pandoc2review 1.2.0

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.
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)