tipi-markup 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +6 -0
- data/README.tipi +7 -0
- data/bin/tipi +63 -0
- data/lib/tipi/parser.rb +284 -0
- data/lib/tipi/version.rb +3 -0
- data/lib/tipi.rb +10 -0
- data/tipi.gemspec +21 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b87186cc48230699aee8ca1f7994d298d7290debfcb5b139be5602f6e740f174
|
4
|
+
data.tar.gz: ceea3879d123997e59d5aebe8d89128cdb4ef4f5f5f3ba83e63d55e02f824a44
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 992a9db4ed8a971e579b3645a349f5a8c7dfeaed8aae4cb6a4a72c4189547173262ff7e3b9fbc9bfbe9a9975d0ec2b233074e7774b593ea43e3992b371c706dd
|
7
|
+
data.tar.gz: 047da29dc713a72717c0b23d1437b14fe9de287ba373f08ca0280abd514128f958c705b4d9562e63bf78ba53a5f4d10c50f2793bbda3ea8f71dbc1e52ddbeace
|
data/.gitignore
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Generated by Cargo
|
2
|
+
# will have compiled files and executables
|
3
|
+
debug/
|
4
|
+
target/
|
5
|
+
|
6
|
+
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
7
|
+
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
8
|
+
Cargo.lock
|
9
|
+
|
10
|
+
# These are backup files generated by rustfmt
|
11
|
+
**/*.rs.bk
|
12
|
+
|
13
|
+
# MSVC Windows builds of rustc generate these, which store debugging information
|
14
|
+
*.pdb
|
data/Gemfile
ADDED
data/README.tipi
ADDED
data/bin/tipi
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/tipi'
|
4
|
+
require 'wkhtmltopdf_runner'
|
5
|
+
|
6
|
+
def print_usage_and_exit
|
7
|
+
puts "Usage:"
|
8
|
+
puts " ./tipi html input.tipi output.html"
|
9
|
+
puts " ./tipi pdf input.tipi output.pdf"
|
10
|
+
exit(1)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Ensure correct number of arguments
|
14
|
+
if ARGV.length != 3
|
15
|
+
print_usage_and_exit
|
16
|
+
end
|
17
|
+
|
18
|
+
command = ARGV[0]
|
19
|
+
input_file = ARGV[1]
|
20
|
+
output_file = ARGV[2]
|
21
|
+
|
22
|
+
# Validate command
|
23
|
+
unless ['html', 'pdf'].include?(command)
|
24
|
+
puts "Invalid command: #{command}"
|
25
|
+
print_usage_and_exit
|
26
|
+
end
|
27
|
+
|
28
|
+
# Read input file content
|
29
|
+
file_content = File.read(input_file)
|
30
|
+
|
31
|
+
# Process command
|
32
|
+
case command
|
33
|
+
when 'html'
|
34
|
+
html = Tipi.text_to_html(file_content)
|
35
|
+
File.open(output_file, "w") { |file| file.puts(html) }
|
36
|
+
puts "HTML conversion completed."
|
37
|
+
when 'pdf'
|
38
|
+
# Read input file content
|
39
|
+
file_content = File.read(input_file)
|
40
|
+
|
41
|
+
html = Tipi.text_to_html(file_content)
|
42
|
+
File.open('temp.html', "w") { |file| file.puts(html) }
|
43
|
+
|
44
|
+
file_path = 'temp.html'
|
45
|
+
|
46
|
+
# Open the file in read mode
|
47
|
+
File.open(file_path, "r") do |file|
|
48
|
+
# Read the content of the file and store it in a variable
|
49
|
+
file_content = file.read
|
50
|
+
puts(file_content)
|
51
|
+
# Output the content of the file
|
52
|
+
|
53
|
+
string = file_content
|
54
|
+
|
55
|
+
WkhtmltopdfRunner.pdf_from_string(string) do |pdf_file|
|
56
|
+
doc = File.open(output_file, 'w')
|
57
|
+
doc.write(pdf_file.read)
|
58
|
+
doc.close
|
59
|
+
end
|
60
|
+
File.delete('temp.html')
|
61
|
+
end
|
62
|
+
puts "PDF conversion completed."
|
63
|
+
end
|
data/lib/tipi/parser.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Tipi
|
5
|
+
class Parser
|
6
|
+
attr_accessor :allowed_schemes
|
7
|
+
attr_writer :extensions
|
8
|
+
def extensions?; @extensions; end
|
9
|
+
attr_writer :no_escape
|
10
|
+
def no_escape?; @no_escape; end
|
11
|
+
|
12
|
+
def initialize(text, options = {})
|
13
|
+
@allowed_schemes = %w(http https ftp ftps)
|
14
|
+
@text = text
|
15
|
+
@extensions = @no_escape = nil
|
16
|
+
options.each_pair {|k,v| send("#{k}=", v) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_html
|
20
|
+
@out = ''
|
21
|
+
@p = false
|
22
|
+
@stack = []
|
23
|
+
parse_block(@text)
|
24
|
+
@out
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def escape_html(string)
|
30
|
+
CGI::escapeHTML(string)
|
31
|
+
end
|
32
|
+
|
33
|
+
def escape_url(string)
|
34
|
+
CGI::escape(string)
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_tag(tag)
|
38
|
+
@stack.push(tag)
|
39
|
+
@out << '<' << tag << '>'
|
40
|
+
end
|
41
|
+
|
42
|
+
def end_tag
|
43
|
+
@out << '</' << @stack.pop << '>'
|
44
|
+
end
|
45
|
+
|
46
|
+
def toggle_tag(tag, match)
|
47
|
+
if @stack.include?(tag)
|
48
|
+
if @stack.last == tag
|
49
|
+
end_tag
|
50
|
+
else
|
51
|
+
@out << escape_html(match)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
start_tag(tag)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def end_paragraph
|
59
|
+
end_tag while !@stack.empty?
|
60
|
+
@p = false
|
61
|
+
end
|
62
|
+
|
63
|
+
def start_paragraph
|
64
|
+
if @p
|
65
|
+
@out << ' ' if @out[-1] != ?\s
|
66
|
+
else
|
67
|
+
end_paragraph
|
68
|
+
start_tag('p')
|
69
|
+
@p = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def make_local_link(link) #:doc:
|
74
|
+
no_escape? ? link : escape_url(link)
|
75
|
+
end
|
76
|
+
|
77
|
+
def make_direct_link(url) #:doc:
|
78
|
+
url
|
79
|
+
end
|
80
|
+
|
81
|
+
def make_image_link(url) #:doc:
|
82
|
+
url
|
83
|
+
end
|
84
|
+
|
85
|
+
def make_image(uri, alt)
|
86
|
+
if alt
|
87
|
+
'<img src="' << escape_html(uri) << '" alt="' << escape_html(alt) << '"/>'
|
88
|
+
else
|
89
|
+
'<img src="' << escape_html(uri) << '"/>'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def make_headline(level, text)
|
94
|
+
"<h#{level}>" << escape_html(text) << "</h#{level}>"
|
95
|
+
end
|
96
|
+
|
97
|
+
def make_explicit_link(link)
|
98
|
+
begin
|
99
|
+
uri = URI.parse(link)
|
100
|
+
return uri.to_s if uri.scheme && @allowed_schemes.include?(uri.scheme)
|
101
|
+
rescue URI::InvalidURIError
|
102
|
+
end
|
103
|
+
make_local_link(link)
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_inline(str)
|
107
|
+
until str.empty?
|
108
|
+
case str
|
109
|
+
when /\A(\~)?((https?|ftps?):\/\/\S+?)(?=([\,.?!:;"'\)]+)?(\s|$))/
|
110
|
+
str = $'
|
111
|
+
if $1
|
112
|
+
@out << escape_html($2)
|
113
|
+
else
|
114
|
+
if uri = make_direct_link($2)
|
115
|
+
@out << '<a href="' << escape_html(uri) << '">' << escape_html($2) << '</a>'
|
116
|
+
else
|
117
|
+
@out << escape_html($&)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
when /\A\[\[\s*([^|]*?)\s*(\|\s*(.*?))?\s*\]\]/m
|
121
|
+
str = $'
|
122
|
+
link, content = $1, $3
|
123
|
+
if uri = make_explicit_link(link)
|
124
|
+
@out << '<a href="' << escape_html(uri) << '">'
|
125
|
+
if content
|
126
|
+
until content.empty?
|
127
|
+
content = parse_inline_tag(content)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
@out << escape_html(link)
|
131
|
+
end
|
132
|
+
@out << '</a>'
|
133
|
+
else
|
134
|
+
@out << escape_html($&)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
str = parse_inline_tag(str)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def parse_inline_tag(str)
|
143
|
+
case str
|
144
|
+
when /\A\{\{\{(.*?\}*)\}\}\}/
|
145
|
+
@out << '<tt>' << escape_html($1) << '</tt>'
|
146
|
+
when /\A\{\{\s*(.*?)\s*(\|\s*(.*?)\s*)?\}\}/
|
147
|
+
if uri = make_image_link($1)
|
148
|
+
@out << make_image(uri, $3)
|
149
|
+
else
|
150
|
+
@out << escape_html($&)
|
151
|
+
end
|
152
|
+
when /\A([[:alpha:]]|[[:digit:]])+/
|
153
|
+
@out << $&
|
154
|
+
when /\A\s+/
|
155
|
+
@out << ' ' if @out[-1] != ?\s
|
156
|
+
when /\A\*\*/
|
157
|
+
toggle_tag 'strong', $&
|
158
|
+
when /\A\/\//
|
159
|
+
toggle_tag 'em', $&
|
160
|
+
when /\A\\\\/
|
161
|
+
@out << '<br/>'
|
162
|
+
else
|
163
|
+
if @extensions
|
164
|
+
case str
|
165
|
+
when /\A__/
|
166
|
+
toggle_tag 'u', $&
|
167
|
+
when /\A\-\-/
|
168
|
+
toggle_tag 'del', $&
|
169
|
+
when /\A\+\+/
|
170
|
+
toggle_tag 'ins', $&
|
171
|
+
when /\A\^\^/
|
172
|
+
toggle_tag 'sup', $&
|
173
|
+
when /\A\~\~/
|
174
|
+
toggle_tag 'sub', $&
|
175
|
+
when /\A\(R\)/i
|
176
|
+
@out << '®'
|
177
|
+
when /\A\(C\)/i
|
178
|
+
@out << '©'
|
179
|
+
when /\A~([^\s])/
|
180
|
+
@out << escape_html($1)
|
181
|
+
when /./
|
182
|
+
@out << escape_html($&)
|
183
|
+
end
|
184
|
+
else
|
185
|
+
case str
|
186
|
+
when /\A~([^\s])/
|
187
|
+
@out << escape_html($1)
|
188
|
+
when /./
|
189
|
+
@out << escape_html($&)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
return $'
|
194
|
+
end
|
195
|
+
|
196
|
+
def parse_table_row(str)
|
197
|
+
@out << '<tr>'
|
198
|
+
str.scan(/\s*\|(=)?\s*((\[\[.*?\]\]|\{\{.*?\}\}|[^|~]|~.)*)(?=\||$)/) do
|
199
|
+
if !$2.empty? || !$'.empty?
|
200
|
+
@out << ($1 ? '<th>' : '<td>')
|
201
|
+
parse_inline($2) if $2
|
202
|
+
end_tag while @stack.last != 'table'
|
203
|
+
@out << ($1 ? '</th>' : '</td>')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
@out << '</tr>'
|
207
|
+
end
|
208
|
+
|
209
|
+
def make_nowikiblock(input)
|
210
|
+
input.gsub(/^ (?=\}\}\})/, '')
|
211
|
+
end
|
212
|
+
|
213
|
+
def ulol?(x); x == 'ul' || x == 'ol'; end
|
214
|
+
|
215
|
+
def parse_block(str)
|
216
|
+
until str.empty?
|
217
|
+
case str
|
218
|
+
when /\A\{\{\{\r?\n(.*?)\r?\n\}\}\}/m
|
219
|
+
end_paragraph
|
220
|
+
nowikiblock = make_nowikiblock($1)
|
221
|
+
@out << '<pre>' << escape_html(nowikiblock) << '</pre>'
|
222
|
+
when /\A\s*-{4,}\s*$/
|
223
|
+
end_paragraph
|
224
|
+
@out << '<hr/>'
|
225
|
+
when /\A\s*(={1,6})\s*(.*?)\s*=*\s*$(\r?\n)?/
|
226
|
+
end_paragraph
|
227
|
+
level = $1.size
|
228
|
+
@out << make_headline(level, $2)
|
229
|
+
when /\A[ \t]*\|.*$(\r?\n)?/
|
230
|
+
if !@stack.include?('table')
|
231
|
+
end_paragraph
|
232
|
+
start_tag('table')
|
233
|
+
end
|
234
|
+
parse_table_row($&)
|
235
|
+
when /\A\s*$(\r?\n)?/
|
236
|
+
end_paragraph
|
237
|
+
when /\A(\s*([*#]+)\s*(.*?))$(\r?\n)?/
|
238
|
+
line, bullet, item = $1, $2, $3
|
239
|
+
tag = (bullet[0,1] == '*' ? 'ul' : 'ol')
|
240
|
+
if bullet[0,1] == '#' || bullet.size != 2 || @stack.find {|x| ulol?(x) }
|
241
|
+
count = @stack.select { |x| ulol?(x) }.size
|
242
|
+
|
243
|
+
while !@stack.empty? && count > bullet.size
|
244
|
+
count -= 1 if ulol?(@stack.last)
|
245
|
+
end_tag
|
246
|
+
end
|
247
|
+
|
248
|
+
end_tag while !@stack.empty? && @stack.last != 'li'
|
249
|
+
|
250
|
+
if @stack.last == 'li' && count == bullet.size
|
251
|
+
end_tag
|
252
|
+
if @stack.last != tag
|
253
|
+
end_tag
|
254
|
+
count -= 1
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
while count < bullet.size
|
259
|
+
start_tag tag
|
260
|
+
count += 1
|
261
|
+
start_tag 'li' if count < bullet.size
|
262
|
+
end
|
263
|
+
|
264
|
+
@p = true
|
265
|
+
start_tag('li')
|
266
|
+
parse_inline(item)
|
267
|
+
else
|
268
|
+
start_paragraph
|
269
|
+
parse_inline(line)
|
270
|
+
end
|
271
|
+
when /\A([ \t]*\S+.*?)$(\r?\n)?/
|
272
|
+
start_paragraph
|
273
|
+
parse_inline($1)
|
274
|
+
else
|
275
|
+
raise "Parse error at #{str[0,30].inspect}"
|
276
|
+
end
|
277
|
+
#p [$&, $']
|
278
|
+
str = $'
|
279
|
+
end
|
280
|
+
end_paragraph
|
281
|
+
@out
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
data/lib/tipi/version.rb
ADDED
data/lib/tipi.rb
ADDED
data/tipi.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/lib/tipi/version'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'tipi-markup'
|
6
|
+
s.version = Tipi::VERSION
|
7
|
+
s.date = Date.today.to_s
|
8
|
+
|
9
|
+
s.authors = ['Timo Sarkar']
|
10
|
+
s.email = ['timosarkar@duck.com']
|
11
|
+
s.summary = 'authoring markup language'
|
12
|
+
s.description = 'Tipi is a lightweight authoring markup language.'
|
13
|
+
s.extra_rdoc_files = %w(README.tipi)
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.executables = ['tipi']
|
17
|
+
s.require_paths = %w(lib)
|
18
|
+
|
19
|
+
s.homepage = 'http://github.com/sartimo/tipi'
|
20
|
+
s.license = 'Ruby'
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tipi-markup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Timo Sarkar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Tipi is a lightweight authoring markup language.
|
14
|
+
email:
|
15
|
+
- timosarkar@duck.com
|
16
|
+
executables:
|
17
|
+
- tipi
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.tipi
|
21
|
+
files:
|
22
|
+
- ".gitignore"
|
23
|
+
- Gemfile
|
24
|
+
- README.tipi
|
25
|
+
- bin/tipi
|
26
|
+
- lib/tipi.rb
|
27
|
+
- lib/tipi/parser.rb
|
28
|
+
- lib/tipi/version.rb
|
29
|
+
- tipi.gemspec
|
30
|
+
homepage: http://github.com/sartimo/tipi
|
31
|
+
licenses:
|
32
|
+
- Ruby
|
33
|
+
metadata: {}
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubygems_version: 3.3.5
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: authoring markup language
|
53
|
+
test_files: []
|