pandoc2review 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/pandoc.yml +18 -0
  3. data/.gitignore +47 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE +339 -0
  6. data/README.md +105 -0
  7. data/Rakefile +13 -0
  8. data/exe/pandoc2review +12 -0
  9. data/lib/pandoc2review.rb +99 -0
  10. data/lua/filters.lua +163 -0
  11. data/lua/review.lua +620 -0
  12. data/markdown-format.ja.md +721 -0
  13. data/pandoc2review.gemspec +29 -0
  14. data/samples/format.md +276 -0
  15. data/samples/reviewsample/.gitignore +154 -0
  16. data/samples/reviewsample/Gemfile +4 -0
  17. data/samples/reviewsample/Rakefile +3 -0
  18. data/samples/reviewsample/catalog.yml +10 -0
  19. data/samples/reviewsample/ch01.md +38 -0
  20. data/samples/reviewsample/ch02.re +3 -0
  21. data/samples/reviewsample/config-ebook.yml +6 -0
  22. data/samples/reviewsample/config.yml +20 -0
  23. data/samples/reviewsample/images/cover-a5.ai +5836 -16
  24. data/samples/reviewsample/images/cover.jpg +0 -0
  25. data/samples/reviewsample/images/pandoc2review.png +0 -0
  26. data/samples/reviewsample/lib/tasks/review.rake +128 -0
  27. data/samples/reviewsample/lib/tasks/z01_pandoc2review.rake +69 -0
  28. data/samples/reviewsample/sty/README.md +168 -0
  29. data/samples/reviewsample/sty/gentombow.sty +769 -0
  30. data/samples/reviewsample/sty/jsbook.cls +2072 -0
  31. data/samples/reviewsample/sty/jumoline.sty +310 -0
  32. data/samples/reviewsample/sty/plistings.sty +326 -0
  33. data/samples/reviewsample/sty/review-base.sty +530 -0
  34. data/samples/reviewsample/sty/review-custom.sty +1 -0
  35. data/samples/reviewsample/sty/review-jsbook.cls +503 -0
  36. data/samples/reviewsample/sty/review-style.sty +49 -0
  37. data/samples/reviewsample/sty/reviewmacro.sty +15 -0
  38. data/samples/reviewsample/style.css +494 -0
  39. metadata +128 -0
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ desc 'Run tests'
5
+ task :test, :target do |_, argv|
6
+ if argv[:target].nil?
7
+ ruby('test/run_test.rb')
8
+ else
9
+ ruby('test/run_test.rb', "--pattern=#{argv[:target]}")
10
+ end
11
+ end
12
+
13
+ task :default => :test
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright 2020-2021 Kenshi Muto
4
+
5
+ require 'pathname'
6
+ bindir = Pathname.new(__FILE__).realpath.dirname
7
+ $LOAD_PATH.unshift((bindir + '../lib').realpath)
8
+
9
+ require 'pandoc2review'
10
+
11
+ p2r = Pandoc2ReVIEW.new
12
+ p2r.main
@@ -0,0 +1,99 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright 2020-2021 Kenshi Muto
3
+ require 'optparse'
4
+ require 'unicode/eaw'
5
+ require 'pathname'
6
+ require 'open3'
7
+
8
+ class Pandoc2ReVIEW
9
+ def main
10
+ luadir = ((Pathname.new(__FILE__)).realpath.dirname + '../lua').realpath
11
+ parse_args
12
+
13
+ ARGV.each do |file|
14
+ unless File.exist?(file)
15
+ puts "#{file} not exist. skip."
16
+ next
17
+ end
18
+ args = ['pandoc', '-t', File.join(luadir, 'review.lua'), '--lua-filter', File.join(luadir, 'filters.lua')]
19
+
20
+ if file =~ /\.md$/i
21
+ args += ['-f', 'markdown-auto_identifiers-smart+east_asian_line_breaks']
22
+
23
+ if @disableeaw
24
+ args += ['-M', "softbreak:true"]
25
+ end
26
+
27
+ if @hideraw
28
+ args += ['-M', "hideraw:true"]
29
+ end
30
+ end
31
+
32
+ if @heading
33
+ args += ["--shift-heading-level-by=#{@heading}"]
34
+ end
35
+
36
+ args.push(file)
37
+
38
+ stdout, stderr, status = Open3.capture3(*args)
39
+ unless status.success?
40
+ STDERR.puts stderr
41
+ exit 1
42
+ end
43
+ print modify_result(stdout)
44
+ end
45
+ end
46
+
47
+ def parse_args
48
+ @heading = nil
49
+ @disableeaw = nil
50
+ @hideraw = nil
51
+ opts = OptionParser.new
52
+ opts.banner = 'Usage: pandoc2review [option] file [file ...]'
53
+ opts.version = '1.2'
54
+
55
+ opts.on('--help', 'Prints this message and quit.') do
56
+ puts opts.help
57
+ exit 0
58
+ end
59
+ opts.on('--shiftheading num', 'Add <num> to heading level.') do |v|
60
+ @heading = v
61
+ end
62
+ opts.on('--disable-eaw', "Disable compositing a paragraph with Ruby's EAW library.") do
63
+ @disableeaw = true
64
+ end
65
+ opts.on('--hideraw', "Hide raw inline/block with no review format specified.") do
66
+ @hideraw = true
67
+ end
68
+
69
+ opts.parse!(ARGV)
70
+ if ARGV.size != 1
71
+ puts opts.help
72
+ exit 0
73
+ end
74
+ end
75
+
76
+ def modify_result(s)
77
+ s.gsub('<P2RBR/>') do
78
+ tail = $`[-1]
79
+ head = $'[0]
80
+ return '' if tail.nil? || head.nil?
81
+
82
+ space = ' '
83
+ if %i[F W H].include?(Unicode::Eaw.property(tail)) &&
84
+ %i[F W H].include?(Unicode::Eaw.property(head)) &&
85
+ tail !~ /\p{Hangul}/ && head !~ /\p{Hangul}/
86
+ space = ''
87
+ end
88
+
89
+ if (%i[F W H].include?(Unicode::Eaw.property(tail)) &&
90
+ tail !~ /\p{Hangul}/) ||
91
+ (%i[F W H].include?(Unicode::Eaw.property(head)) &&
92
+ head !~ /\p{Hangul}/)
93
+ space = ''
94
+ end
95
+
96
+ space
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,163 @@
1
+ local function review_inline(x)
2
+ return pandoc.RawInline("review", x)
3
+ end
4
+
5
+ local beginchild = {pandoc.Plain(review_inline("//beginchild"))}
6
+ local endchild = {pandoc.Plain(review_inline("//endchild"))}
7
+
8
+ local function markdown(text)
9
+ return(pandoc.read(text, "markdown").blocks[1].content)
10
+ end
11
+
12
+ local function support_blankline(constructor)
13
+ --[[
14
+ Returns a function that splits a block into blocks separated by a Div
15
+ element which defines blank lines.
16
+ The Re:VIEW Lua writer subsequently transforms the Div element into
17
+ `//blankline` commands.
18
+ ]]
19
+ local construct = constructor or pandoc.Para
20
+ return function(x)
21
+ local blocks = {construct({})}
22
+ local i = 1
23
+ local n_break = 0
24
+ local content = blocks[i].content
25
+
26
+ for j, elem in ipairs(x.content) do
27
+ if elem.tag == "LineBreak" then
28
+ -- Count the repeated number of LineBreak
29
+ n_break = n_break + 1
30
+ elseif n_break == 1 then
31
+ -- Do nothing if LineBreak is not repeated
32
+ table.insert(content, pandoc.LineBreak())
33
+ table.insert(content, elem)
34
+ n_break = 0
35
+ elseif n_break > 1 then
36
+ -- Convert LineBreak's into //blankline commands
37
+ table.insert(blocks, pandoc.Div({}, {blankline = n_break - 1}))
38
+ table.insert(blocks, construct({elem}))
39
+ i = i + 2
40
+ content = blocks[i].content
41
+ n_break = 0
42
+ else
43
+ -- Do nothing on elements other than LineBreak
44
+ table.insert(content, elem)
45
+ end
46
+ end
47
+
48
+ return blocks
49
+ end
50
+ end
51
+
52
+ local function nestablelist(elem)
53
+ --[[
54
+ Support items with multiple blocks in
55
+ BulletList, OrderedList, and DefinitionList.
56
+ ]]
57
+ for _, block in ipairs(elem.content) do
58
+ local second = block[2]
59
+ if second then
60
+ if second.tag == "BulletList" then
61
+ table.insert(second.content, 1, beginchild)
62
+ elseif second.tag then
63
+ table.insert(block, 2, pandoc.BulletList(beginchild))
64
+ else
65
+ for _,definition in ipairs(second) do
66
+ if definition[2] then
67
+ table.insert(definition, 2, pandoc.BulletList(beginchild))
68
+ table.insert(definition, pandoc.BulletList(endchild))
69
+ end
70
+ end
71
+ end
72
+
73
+ local last = block[#block]
74
+ if last.tag == "BulletList" then
75
+ table.insert(last.content, endchild)
76
+ elseif last.tag then
77
+ table.insert(block, pandoc.BulletList(endchild))
78
+ end
79
+ end
80
+ end
81
+ return elem
82
+ end
83
+
84
+ local function support_strong(child)
85
+ --[[
86
+ Returns a function that converts `***text***` as Span with the strong class
87
+ (i.e., `[text]{.strong}`).
88
+ Pandoc treats `***` as Emph wrapped by Strong, but is not documented.
89
+ This filter also supports the inverse order just for sure.
90
+
91
+ The Lua writer, review.lua, further converts the text to `@strong{text}`
92
+ ]]
93
+ return function(elem)
94
+ if (#elem.content == 1) and (elem.content[1].tag == child) then
95
+ return pandoc.Span(elem.content[1].content, {class = 'strong'})
96
+ end
97
+ end
98
+ end
99
+
100
+ local function caption_div(div)
101
+ local class = div.classes[1]
102
+ local caption = div.attributes.caption
103
+
104
+ if ((#div.content == 1) and
105
+ (#div.content[1].content == 1) and
106
+ (div.content[1].content[1].tag == "Math") and
107
+ (div.identifier)) then
108
+ class = "texequation[" .. div.identifier .. "]"
109
+ local math_text = (div.content[1].content[1].text
110
+ ):gsub("^\n+", ""):gsub("\n+$", "")
111
+
112
+ if caption == nil then
113
+ return pandoc.RawBlock(
114
+ "review",
115
+ "//" .. class .. "{\n" .. math_text .. "\n//}"
116
+ )
117
+ end
118
+
119
+ div.content = {pandoc.RawBlock("review", math_text)}
120
+ end
121
+
122
+ if class == nil then
123
+ return nil
124
+ end
125
+
126
+ if caption then
127
+ local begin = pandoc.Para(markdown(caption))
128
+ table.insert(begin.content, 1, review_inline("//" .. class .. "["))
129
+ table.insert(begin.content, review_inline("]{<P2RREMOVEBELOW/>"))
130
+ table.insert(div.content, 1, begin)
131
+ table.insert(div.content, pandoc.RawBlock("review", "<P2RREMOVEABOVE/>//}"))
132
+ div.classes = {"review-internal"}
133
+ return div
134
+ end
135
+ end
136
+
137
+ local function noindent(para)
138
+ first = para.content[1]
139
+
140
+ if ((first.tag == "RawInline") and
141
+ (first.format == "tex") and
142
+ (first.text:match("^\\noindent%s*"))) then
143
+ para.content[1] = review_inline("//noindent\n")
144
+ if para.content[2].tag == "SoftBreak" then
145
+ table.remove(para.content, 2)
146
+ end
147
+ end
148
+
149
+ return para
150
+ end
151
+
152
+ return {
153
+ {Emph = support_strong("Strong")},
154
+ {Strong = support_strong("Emph")},
155
+ {Plain = support_blankline(pandoc.Plain)},
156
+ {Para = support_blankline(pandoc.Para)},
157
+ {Para = noindent},
158
+ -- blankline must be processed before lists
159
+ {BulletList = nestablelist},
160
+ {OrderedList = nestablelist},
161
+ {DefinitionList = nestablelist},
162
+ {Div = caption_div}
163
+ }
@@ -0,0 +1,620 @@
1
+ -- -*- coding: utf-8 -*-
2
+ -- Re:VIEW Writer for Pandoc
3
+ -- Copyright 2020 Kenshi Muto
4
+
5
+ -- config
6
+ local config = {
7
+ use_header_id = "true",
8
+ use_hr = "true",
9
+ use_table_align = "true",
10
+
11
+ bold = "b",
12
+ italic = "i",
13
+ code = "tt",
14
+ strike = "del",
15
+ underline = "u",
16
+ lineblock = "source", --- XXX: Re:VIEW doesn't provide poem style by default
17
+ }
18
+
19
+ -- counter
20
+ local table_num = 0
21
+ local list_num = 0
22
+ local fig_num = 0
23
+ local note_num = 0
24
+ local footnotes = {}
25
+
26
+ -- internal
27
+ local metadata = nil
28
+ local stringify = (require "pandoc.utils").stringify
29
+ local inline_commands = {
30
+ -- processed if given as classes of Span elements
31
+ -- true if syntax is `@<command>{string}`
32
+ --- formats
33
+ kw = true,
34
+ bou = true,
35
+ ami = true,
36
+ u = true,
37
+ b = true,
38
+ i = true,
39
+ strong = true,
40
+ em = true,
41
+ tt = true,
42
+ tti = true,
43
+ ttb = true,
44
+ code = true,
45
+ tcy = true,
46
+ --- ref
47
+ chap = true,
48
+ title = true,
49
+ chapref = true,
50
+ list = true,
51
+ img = true,
52
+ table = true,
53
+ eq = true,
54
+ hd = true,
55
+ column = true,
56
+ --- others
57
+ ruby = false,
58
+ br = false,
59
+ uchar = true,
60
+ href = false,
61
+ icon = true,
62
+ m = true,
63
+ w = true,
64
+ wb = true,
65
+ raw = false,
66
+ embed = false,
67
+ idx = true,
68
+ hidx = true,
69
+ balloon = true,
70
+ }
71
+
72
+ local function try_catch(what)
73
+ -- ref: http://bushimichi.blogspot.com/2016/11/lua-try-catch.html
74
+ local status, result = pcall(what.try)
75
+ if not status then
76
+ what.catch(result)
77
+ end
78
+ return result
79
+ end
80
+
81
+ local function log(s)
82
+ io.stderr:write(s)
83
+ end
84
+
85
+ local function surround_inline(s)
86
+ if (string.match(s, "{") or string.match(s, "}")) then
87
+ if (string.match(s, "%$")) then -- use % for regexp escape
88
+ if (string.match(s, "|")) then
89
+ -- give up. escape } by \}
90
+ return "{" .. string.gsub(s, "}", "\\}") .. "}"
91
+ else
92
+ -- surround by ||
93
+ return "|" .. s .. "|"
94
+ end
95
+ else
96
+ -- surround by $$
97
+ return "$" .. s .. "$"
98
+ end
99
+ end
100
+ return "{" .. s .. "}"
101
+ end
102
+
103
+ local function format_inline(fmt, s)
104
+ return string.format("@<%s>%s", fmt, surround_inline(s))
105
+ end
106
+
107
+ local function html_align(align)
108
+ if align == "AlignLeft" then
109
+ return ""
110
+ elseif align == "AlignRight" then
111
+ return "right"
112
+ elseif align == "AlignCenter" then
113
+ return "center"
114
+ else
115
+ return ""
116
+ end
117
+ end
118
+
119
+ function Blocksep()
120
+ return "\n\n"
121
+ end
122
+
123
+ function Doc(body, metadata, variables)
124
+ local buffer = {}
125
+ local function add(s)
126
+ table.insert(buffer, s)
127
+ end
128
+ add(body)
129
+ if (#footnotes > 0) then
130
+ add("\n" .. table.concat(footnotes, "\n"))
131
+ end
132
+ return table.concat(buffer, "\n")
133
+ end
134
+
135
+ function Str(s)
136
+ return s
137
+ end
138
+
139
+ function Space()
140
+ return " "
141
+ end
142
+
143
+ function LineBreak()
144
+ return "@<br>{}"
145
+ end
146
+
147
+ function SoftBreak(s)
148
+ if (metadata.softbreak) then
149
+ return " "
150
+ else
151
+ return "<P2RBR/>"
152
+ end
153
+ end
154
+
155
+ function Plain(s)
156
+ return s
157
+ end
158
+
159
+ function Para(s)
160
+ return s
161
+ end
162
+
163
+ local function attr_val(attr, key)
164
+ local attr_table = {}
165
+ for k, v in pairs(attr) do
166
+ if (k == key and v and v ~= "") then
167
+ return v
168
+ end
169
+ end
170
+ return ""
171
+ end
172
+
173
+ local function attr_classes(attr)
174
+ local classes = {}
175
+
176
+ for cls in attr_val(attr, "class"):gmatch("[^%s]+") do
177
+ classes[cls] = true
178
+ end
179
+ return classes
180
+ end
181
+
182
+ local function attr_scale(attr, key) -- a helper for CaptionedImage
183
+ scale = attr_val(attr, key)
184
+ if (scale == "") or (key == "scale") then
185
+ return scale
186
+ end
187
+
188
+ scale, count = scale:gsub("%%$", "")
189
+ if count == 0 then
190
+ log("WARNING: Units must be % for `" .. key .. "` of Image. Ignored.\n")
191
+ return ""
192
+ end
193
+
194
+ return tonumber(scale) / 100
195
+ end
196
+
197
+ function Header(level, s, attr)
198
+ local headmark = ""
199
+ for i = 1, level do
200
+ headmark = headmark .. "="
201
+ end
202
+
203
+ local classes = attr_classes(attr)
204
+
205
+ headmark = headmark .. (
206
+ -- Re:VIEW's behavior
207
+ classes["column"] and "[column]" or (
208
+ classes["nonum"] and "[nonum]" or (
209
+ classes["nodisp"] and "[nodisp]" or (
210
+ classes["notoc"] and "[notoc]" or (
211
+ -- Pandoc's behavior
212
+ classes["unnumbered"] and (
213
+ classes["unlisted"] and "[notoc]" or "[nonum]") or (
214
+ -- None
215
+ "")))))
216
+ )
217
+
218
+ if ((config.use_header_id == "true") and attr.id ~= "" and attr.id ~= s) then
219
+ headmark = headmark .. "{" .. attr.id .. "}"
220
+ end
221
+
222
+ return headmark .. " " .. s
223
+ end
224
+
225
+ function HorizontalRule()
226
+ if (config.use_hr == "true") then
227
+ return "//hr"
228
+ else
229
+ return ""
230
+ end
231
+ end
232
+
233
+ local function lint_list(s)
234
+ return s:gsub("\n+(//beginchild)\n+", '\n\n%1\n\n'
235
+ ):gsub("\n+(//endchild)\n+", '\n\n%1\n\n'
236
+ ):gsub("\n+(//endchild)\n*$", "\n\n%1")
237
+ end
238
+
239
+ function BulletList(items)
240
+ local buffer = {}
241
+ for _, item in pairs(items) do
242
+ if (item == "//beginchild") or (item == "//endchild") then
243
+ table.insert(buffer, item)
244
+ else
245
+ table.insert(buffer, " * " .. item)
246
+ end
247
+ end
248
+ return lint_list(table.concat(buffer, "\n"))
249
+ end
250
+
251
+ function OrderedList(items, start)
252
+ local buffer = {}
253
+ local n = start
254
+ for _, item in pairs(items) do
255
+ if (item == "//beginchild") or (item == "//endchild") then
256
+ table.insert(buffer, item)
257
+ else
258
+ table.insert(buffer, " " .. n .. ". " .. item)
259
+ n = n + 1
260
+ end
261
+ end
262
+ return lint_list(table.concat(buffer, "\n"))
263
+ end
264
+
265
+ function DefinitionList(items)
266
+ local buffer = {}
267
+ for _, item in pairs(items) do
268
+ for k, v in pairs(item) do
269
+ table.insert(buffer, " : " .. k .. "\n\t" .. table.concat(v, "\n"))
270
+ end
271
+ end
272
+ return lint_list(table.concat(buffer, "\n") .. "\n")
273
+ end
274
+
275
+ function BlockQuote(s)
276
+ return "//quote{\n" .. s .. "\n//}"
277
+ end
278
+
279
+ function CodeBlock(s, attr)
280
+ local classes = attr_classes(attr)
281
+
282
+ local command = nil
283
+ for k,v in pairs({cmd = "cmd", source = "source", quote = "source"}) do
284
+ if classes[k] then
285
+ command = v
286
+ break
287
+ end
288
+ end
289
+ command = command or "list"
290
+
291
+ is_list = command == "list"
292
+
293
+
294
+ local num = (is_list == false) and "" or (
295
+ (classes["numberLines"] or classes["number-lines"] or classes["num"]) and
296
+ "num" or ""
297
+ )
298
+
299
+ local firstlinenum = ""
300
+ if is_list and (num == "num") then
301
+ for _, key in ipairs({"startFrom", "start-from", "firstlinenum"}) do
302
+ firstlinenum = attr_val(attr, key)
303
+ if firstlinenum ~= "" then
304
+ firstlinenum = "//firstlinenum[" .. firstlinenum .. "]\n"
305
+ break
306
+ end
307
+ end
308
+ end
309
+
310
+ local lang = ""
311
+ local not_lang = {numberLines = true, num = true, em = true, source = true}
312
+ not_lang["number-lines"] = true
313
+ if is_list or (command == "source") then
314
+ for key,_ in pairs(classes) do
315
+ if not_lang[key] ~= true then
316
+ lang = "[" .. key .. "]"
317
+ break
318
+ end
319
+ end
320
+ end
321
+
322
+ local caption = (command == "cmd") and "" or attr_val(attr, "caption")
323
+ local identifier = ""
324
+ local em = is_list and classes["em"] and "em" or ""
325
+ if (caption ~= "") then
326
+ if is_list and (em == "") then
327
+ if (attr.id ~= "") then
328
+ identifier = "[" .. attr.id .. "]"
329
+ else
330
+ list_num = list_num + 1
331
+ identifier = "[list" .. list_num .. "]"
332
+ end
333
+ end
334
+ caption = "[" .. caption .. "]"
335
+ else
336
+ if is_list then
337
+ em = "em"
338
+ end
339
+ if lang ~= "" then
340
+ caption = "[" .. caption .. "]"
341
+ end
342
+ end
343
+
344
+ return (
345
+ firstlinenum ..
346
+ "//" .. em .. command .. num .. identifier .. caption .. lang ..
347
+ "{\n" .. s .. "\n//}"
348
+ )
349
+ end
350
+
351
+ function LineBlock(s)
352
+ -- | block
353
+ return "//" .. config.lineblock .. "{\n" .. table.concat(s, "\n") .. "\n//}"
354
+ end
355
+
356
+ function Link(s, src, tit)
357
+ if (src == s) then
358
+ return format_inline("href", src)
359
+ else
360
+ return format_inline("href", src .. "," .. s)
361
+ end
362
+ end
363
+
364
+ function Code(s, attr)
365
+ -- ignore attr
366
+ return format_inline(config.code, s)
367
+ end
368
+
369
+ function Emph(s)
370
+ return format_inline(config.italic, s)
371
+ end
372
+
373
+ function Strong(s)
374
+ return format_inline(config.bold, s)
375
+ end
376
+
377
+ function Strikeout(s)
378
+ return format_inline(config.strike, s)
379
+ end
380
+
381
+ function Underline(s)
382
+ return format_inline(config.underline, s)
383
+ end
384
+
385
+ function Subscript(s)
386
+ return format_inline("sub", s)
387
+ end
388
+
389
+ function Superscript(s)
390
+ return format_inline("sup", s)
391
+ end
392
+
393
+ function InlineMath(s)
394
+ return format_inline("m", s)
395
+ end
396
+
397
+ function DisplayMath(s)
398
+ return format_inline("m", "\\displaystyle{}" .. s)
399
+ end
400
+
401
+ function Table(caption, aligns, widths, headers, rows)
402
+ local buffer = {}
403
+ local function add(s)
404
+ table.insert(buffer, s)
405
+ end
406
+ if caption ~= "" then
407
+ table_num = table_num + 1
408
+ add("//table[table" .. table_num .. "][" .. caption .. "]{")
409
+ else
410
+ add("//table{")
411
+ end
412
+ local tmp = {}
413
+ for i, h in pairs(headers) do
414
+ align = html_align(aligns[i])
415
+ if (config.use_table_align == "true") and (align ~= "") then
416
+ h = format_inline("dtp", "table align=" .. align) .. h
417
+ end
418
+ table.insert(tmp, h)
419
+ end
420
+ add(table.concat(tmp, "\t"))
421
+ add("--------------")
422
+ for _, row in pairs(rows) do
423
+ tmp = {}
424
+ for i, c in pairs(row) do
425
+ align = html_align(aligns[i])
426
+ if (config.use_table_align == "true") and (align ~= "") then
427
+ c = format_inline("dtp", "table align=" .. align) .. c
428
+ end
429
+ table.insert(tmp, c)
430
+ end
431
+ add(table.concat(tmp, "\t"))
432
+ end
433
+ add("//}")
434
+
435
+ return table.concat(buffer, "\n")
436
+ end
437
+
438
+ function Image(s, src, tit)
439
+ -- Re:VIEW @<icon> ignores caption and title
440
+ local id = string.gsub(src, "%.%w+$", "")
441
+ id = string.gsub(id, "^images/", "")
442
+ return format_inline("icon", id)
443
+ end
444
+
445
+ function CaptionedImage(s, src, tit, attr)
446
+ local path = "[" .. s:gsub("%.%w+$", ""):gsub("^images/", "") .. "]"
447
+
448
+ local comment = src:gsub("^fig:", ""):gsub("(.+)", "\n%1")
449
+
450
+ local scale = attr_scale(attr, "scale")
451
+ if scale == "" then
452
+ local width = attr_scale(attr, "width")
453
+ local height = attr_scale(attr, "height")
454
+ if (width ~= "") then
455
+ if (height ~= "") and (width ~= height) then
456
+ log("WARNING: Image width and height must be same. Using width.\n")
457
+ end
458
+ scale = width
459
+ else
460
+ scale = height
461
+ end
462
+ end
463
+ if scale ~= "" then
464
+ scale = "[scale=" .. scale .. "]"
465
+ end
466
+
467
+ local command = "//image"
468
+ local caption = ""
469
+ if (tit == "") then
470
+ command = "//indepimage"
471
+ else
472
+ caption = "[" .. tit .. "]"
473
+ end
474
+
475
+ return (
476
+ command .. path .. caption .. scale .. "{" .. comment .. "\n//}"
477
+ )
478
+ end
479
+
480
+ function Note(s)
481
+ note_num = note_num + 1
482
+ table.insert(footnotes, "//footnote[fn" .. note_num .. "][" .. s .. "]")
483
+ return format_inline("fn", "fn" .. note_num)
484
+ end
485
+
486
+ function Cite(s, cs)
487
+ -- use @ as is.
488
+ return s
489
+ end
490
+
491
+ function Quoted(quotetype, s)
492
+ if (quotetype == "SingleQuote") then
493
+ return SingleQuoted(s)
494
+ end
495
+ if (quotetype == "DoubleQuote") then
496
+ return DoubleQuoted(s)
497
+ end
498
+ end
499
+
500
+ function SingleQuoted(s)
501
+ return "'" .. s .. "'"
502
+ end
503
+
504
+ function DoubleQuoted(s)
505
+ return '"' .. s .. '"'
506
+ end
507
+
508
+ function SmallCaps(s)
509
+ return "◆→SMALLCAPS:" .. s .. "←◆"
510
+ end
511
+
512
+ function Div(s, attr)
513
+ local blankline = attr_val(attr, "blankline")
514
+ if blankline ~= "" then
515
+ local buffer = {}
516
+ for i = 1, tonumber(blankline) do
517
+ table.insert(buffer, "//blankline")
518
+ end
519
+ return table.concat(buffer, "\n")
520
+ end
521
+
522
+ local classes = attr_classes(attr)
523
+
524
+ if next(classes) == nil then
525
+ return "//{\n" .. s .. "\n//}"
526
+ end
527
+
528
+ if classes["review-internal"] then
529
+ s, _ = s:gsub(
530
+ "%]{<P2RREMOVEBELOW/>\n", "]{"
531
+ ):gsub(
532
+ "\n<P2RREMOVEABOVE/>//}", "//}"
533
+ )
534
+ return s
535
+ end
536
+
537
+ for cls,_ in pairs(classes) do
538
+ s = "//" .. cls .. "{\n" .. s .. "\n//}"
539
+ end
540
+ return s
541
+ end
542
+
543
+ function Span(s, attr)
544
+ -- ruby and kw with a supplement
545
+ local a = ""
546
+ for _, cmd in ipairs({"ruby", "kw"}) do
547
+ a = attr_val(attr, cmd)
548
+ if a ~= "" then
549
+ s = format_inline(cmd, s .. "," .. a)
550
+ end
551
+ end
552
+
553
+ -- inline format
554
+ for cmd in attr_val(attr, "class"):gmatch("[^%s]+") do
555
+ if inline_commands[cmd] then
556
+ s = format_inline(cmd, s)
557
+ end
558
+ end
559
+
560
+ return s
561
+ end
562
+
563
+ function RawInline(format, text)
564
+ if (format == "review") then
565
+ return text
566
+ end
567
+
568
+ if (metadata.hideraw) then
569
+ return ""
570
+ end
571
+
572
+ if (format == "tex") then
573
+ return format_inline("embed", "|latex|" .. text)
574
+ else
575
+ return format_inline("embed", "|" .. format .. "|", text)
576
+ end
577
+ end
578
+
579
+ function RawBlock(format, text)
580
+ if (format == "review") then
581
+ return text
582
+ end
583
+
584
+ if (metadata.hideraw) then
585
+ return ""
586
+ end
587
+
588
+ if (format == "tex") then
589
+ return "//embed[latex]{\n" .. text .. "\n//}"
590
+ else
591
+ return "//embed[" .. format .. "]{\n" .. text .. "\n//}"
592
+ end
593
+ end
594
+
595
+ try_catch {
596
+ try = function()
597
+ metadata = PANDOC_DOCUMENT.meta
598
+ end,
599
+ catch = function(error)
600
+ log("Due to your pandoc version is too old, config.yml loader is disabled.\n")
601
+ end
602
+ }
603
+
604
+ if (metadata) then
605
+ -- Load config from YAML
606
+ for k,v in pairs(config) do
607
+ if metadata[k] ~= nil then
608
+ config[k] = stringify(metadata[k])
609
+ end
610
+ end
611
+ end
612
+
613
+ local meta = {}
614
+ meta.__index =
615
+ function(_, key)
616
+ log(string.format("WARNING: Undefined function '%s'\n", key))
617
+ return function() return "" end
618
+ end
619
+
620
+ setmetatable(_G, meta)