pandoc2review 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lua/review.lua CHANGED
@@ -25,7 +25,7 @@ local footnotes = {}
25
25
 
26
26
  -- internal
27
27
  local metadata = nil
28
- local stringify = (require "pandoc.utils").stringify
28
+ local stringify = (require("pandoc.utils")).stringify
29
29
  local inline_commands = {
30
30
  -- processed if given as classes of Span elements
31
31
  -- true if syntax is `@<command>{string}`
@@ -48,7 +48,7 @@ local inline_commands = {
48
48
  title = true,
49
49
  chapref = true,
50
50
  list = true,
51
- img = true,
51
+ img = true,
52
52
  table = true,
53
53
  eq = true,
54
54
  hd = true,
@@ -83,21 +83,19 @@ local function log(s)
83
83
  end
84
84
 
85
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
86
+ if not s:match("[{}]") then
87
+ return "{" .. s .. "}"
88
+ end
89
+ if not s:match("%$") then
90
+ return "$" .. s .. "$"
99
91
  end
100
- return "{" .. s .. "}"
92
+
93
+ -- use % for regexp escape
94
+ if s:match("|") then
95
+ -- give up. escape } by \}
96
+ return "{" .. s:gsub("}", "\\}") .. "}"
97
+ end
98
+ return "|" .. s .. "|"
101
99
  end
102
100
 
103
101
  local function format_inline(fmt, s)
@@ -105,31 +103,18 @@ local function format_inline(fmt, s)
105
103
  end
106
104
 
107
105
  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
106
+ return ({ AlignRight = "right", AlignCenter = "center" })[align] or ""
117
107
  end
118
108
 
119
109
  function Blocksep()
120
110
  return "\n\n"
121
111
  end
122
112
 
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"))
113
+ function Doc(body, meta, variables)
114
+ if #footnotes == 0 then
115
+ return body
131
116
  end
132
- return table.concat(buffer, "\n")
117
+ return table.concat({ body, "", table.concat(footnotes, "\n") }, "\n")
133
118
  end
134
119
 
135
120
  function Str(s)
@@ -145,11 +130,7 @@ function LineBreak()
145
130
  end
146
131
 
147
132
  function SoftBreak(s)
148
- if (metadata.softbreak) then
149
- return " "
150
- else
151
- return "<P2RBR/>"
152
- end
133
+ return metadata.softbreak and " " or "<P2RBR/>"
153
134
  end
154
135
 
155
136
  function Plain(s)
@@ -161,13 +142,7 @@ function Para(s)
161
142
  end
162
143
 
163
144
  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 ""
145
+ return attr[key] or ""
171
146
  end
172
147
 
173
148
  local function attr_classes(attr)
@@ -180,7 +155,7 @@ local function attr_classes(attr)
180
155
  end
181
156
 
182
157
  local function attr_scale(attr, key) -- a helper for CaptionedImage
183
- scale = attr_val(attr, key)
158
+ local scale, count = attr_val(attr, key), 0
184
159
  if (scale == "") or (key == "scale") then
185
160
  return scale
186
161
  end
@@ -194,28 +169,27 @@ local function attr_scale(attr, key) -- a helper for CaptionedImage
194
169
  return tonumber(scale) / 100
195
170
  end
196
171
 
197
- function Header(level, s, attr)
198
- local headmark = ""
199
- for i = 1, level do
200
- headmark = headmark .. "="
172
+ local function class_header(classes)
173
+ -- Re:VIEW's behavior
174
+ for _, cls in pairs({ "column", "nonum", "nodisp", "notoc" }) do
175
+ if classes[cls] then
176
+ return string.format("[%s]", cls)
177
+ end
201
178
  end
202
179
 
203
- local classes = attr_classes(attr)
180
+ -- Pandoc's behavior
181
+ if classes.unnumbered then
182
+ return classes.unlisted and "[notoc]" or "[nonum]"
183
+ end
204
184
 
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
185
+ -- None
186
+ return ""
187
+ end
188
+
189
+ function Header(level, s, attr)
190
+ local headmark = string.rep("=", level) .. class_header(attr_classes(attr))
191
+
192
+ if (config.use_header_id == "true") and attr.id ~= "" and attr.id ~= s then
219
193
  headmark = headmark .. "{" .. attr.id .. "}"
220
194
  end
221
195
 
@@ -223,17 +197,13 @@ function Header(level, s, attr)
223
197
  end
224
198
 
225
199
  function HorizontalRule()
226
- if (config.use_hr == "true") then
227
- return "//hr"
228
- else
229
- return ""
230
- end
200
+ return config.use_hr == "true" and "//hr" or ""
231
201
  end
232
202
 
233
203
  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")
204
+ return s:gsub("\n+(//beginchild)\n+", "\n\n%1\n\n")
205
+ :gsub("\n+(//endchild)\n+", "\n\n%1\n\n")
206
+ :gsub("\n+(//endchild)\n*$", "\n\n%1")
237
207
  end
238
208
 
239
209
  function BulletList(items)
@@ -279,39 +249,33 @@ end
279
249
  function CodeBlock(s, attr)
280
250
  local classes = attr_classes(attr)
281
251
 
282
- local command = nil
283
- for k,v in pairs({cmd = "cmd", source = "source", quote = "source"}) do
252
+ local command = "list" -- default
253
+ for k, v in pairs({ cmd = "cmd", source = "source", quote = "source" }) do
284
254
  if classes[k] then
285
255
  command = v
286
256
  break
287
257
  end
288
258
  end
289
- command = command or "list"
290
-
291
- is_list = command == "list"
292
259
 
260
+ local is_list = command == "list"
293
261
 
294
- local num = (is_list == false) and "" or (
295
- (classes["numberLines"] or classes["number-lines"] or classes["num"]) and
296
- "num" or ""
297
- )
262
+ local num = (is_list and (classes["numberLines"] or classes["number-lines"] or classes["num"])) and "num" or ""
298
263
 
299
264
  local firstlinenum = ""
300
265
  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"
266
+ for _, key in ipairs({ "startFrom", "start-from", "firstlinenum" }) do
267
+ if attr[key] then
268
+ firstlinenum = "//firstlinenum[" .. attr[key] .. "]\n"
305
269
  break
306
270
  end
307
271
  end
308
272
  end
309
273
 
310
274
  local lang = ""
311
- local not_lang = {numberLines = true, num = true, em = true, source = true}
275
+ local not_lang = { numberLines = true, num = true, em = true, source = true }
312
276
  not_lang["number-lines"] = true
313
277
  if is_list or (command == "source") then
314
- for key,_ in pairs(classes) do
278
+ for key, _ in pairs(classes) do
315
279
  if not_lang[key] ~= true then
316
280
  lang = "[" .. key .. "]"
317
281
  break
@@ -322,9 +286,9 @@ function CodeBlock(s, attr)
322
286
  local caption = (command == "cmd") and "" or attr_val(attr, "caption")
323
287
  local identifier = ""
324
288
  local em = is_list and classes["em"] and "em" or ""
325
- if (caption ~= "") then
289
+ if caption ~= "" then
326
290
  if is_list and (em == "") then
327
- if (attr.id ~= "") then
291
+ if attr.id ~= "" then
328
292
  identifier = "[" .. attr.id .. "]"
329
293
  else
330
294
  list_num = list_num + 1
@@ -341,11 +305,7 @@ function CodeBlock(s, attr)
341
305
  end
342
306
  end
343
307
 
344
- return (
345
- firstlinenum ..
346
- "//" .. em .. command .. num .. identifier .. caption .. lang ..
347
- "{\n" .. s .. "\n//}"
348
- )
308
+ return (firstlinenum .. "//" .. em .. command .. num .. identifier .. caption .. lang .. "{\n" .. s .. "\n//}")
349
309
  end
350
310
 
351
311
  function LineBlock(s)
@@ -354,7 +314,7 @@ function LineBlock(s)
354
314
  end
355
315
 
356
316
  function Link(s, src, tit)
357
- if (src == s) then
317
+ if src == s then
358
318
  return format_inline("href", src)
359
319
  else
360
320
  return format_inline("href", src .. "," .. s)
@@ -411,7 +371,7 @@ function Table(caption, aligns, widths, headers, rows)
411
371
  end
412
372
  local tmp = {}
413
373
  for i, h in pairs(headers) do
414
- align = html_align(aligns[i])
374
+ local align = html_align(aligns[i])
415
375
  if (config.use_table_align == "true") and (align ~= "") then
416
376
  h = format_inline("dtp", "table align=" .. align) .. h
417
377
  end
@@ -421,8 +381,8 @@ function Table(caption, aligns, widths, headers, rows)
421
381
  add("--------------")
422
382
  for _, row in pairs(rows) do
423
383
  tmp = {}
424
- for i, c in pairs(row) do
425
- align = html_align(aligns[i])
384
+ for i, c in pairs(row) do
385
+ local align = html_align(aligns[i])
426
386
  if (config.use_table_align == "true") and (align ~= "") then
427
387
  c = format_inline("dtp", "table align=" .. align) .. c
428
388
  end
@@ -435,13 +395,6 @@ function Table(caption, aligns, widths, headers, rows)
435
395
  return table.concat(buffer, "\n")
436
396
  end
437
397
 
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
398
  function CaptionedImage(s, src, tit, attr)
446
399
  local path = "[" .. s:gsub("%.%w+$", ""):gsub("^images/", "") .. "]"
447
400
 
@@ -451,7 +404,7 @@ function CaptionedImage(s, src, tit, attr)
451
404
  if scale == "" then
452
405
  local width = attr_scale(attr, "width")
453
406
  local height = attr_scale(attr, "height")
454
- if (width ~= "") then
407
+ if width ~= "" then
455
408
  if (height ~= "") and (width ~= height) then
456
409
  log("WARNING: Image width and height must be same. Using width.\n")
457
410
  end
@@ -464,26 +417,15 @@ function CaptionedImage(s, src, tit, attr)
464
417
  scale = "[scale=" .. scale .. "]"
465
418
  end
466
419
 
467
- local command = "//image"
468
- local caption = ""
469
- if (tit == "") then
470
- command = "//indepimage"
471
- else
472
- caption = "[" .. tit .. "]"
473
- end
420
+ local command = tit == "" and "//indepimage" or "//image"
421
+ local caption = tit == "" and "" or ("[" .. tit .. "]")
474
422
 
475
- return (
476
- command .. path .. caption .. scale .. "{" .. comment .. "\n//}"
477
- )
423
+ return (command .. path .. caption .. scale .. "{" .. comment .. "\n//}")
478
424
  end
479
425
 
480
426
  function Image(s, src, tit, attr)
481
427
  -- Re:VIEW @<icon> ignores caption and title
482
- if attr.is_figure then
483
- return CaptionedImage(src, s, tit, attr)
484
- end
485
- local id = string.gsub(src, "%.%w+$", "")
486
- id = string.gsub(id, "^images/", "")
428
+ local id = src:gsub("%.%w+$", ""):gsub("^images/", "")
487
429
  return format_inline("icon", id)
488
430
  end
489
431
 
@@ -499,12 +441,7 @@ function Cite(s, cs)
499
441
  end
500
442
 
501
443
  function Quoted(quotetype, s)
502
- if (quotetype == "SingleQuote") then
503
- return SingleQuoted(s)
504
- end
505
- if (quotetype == "DoubleQuote") then
506
- return DoubleQuoted(s)
507
- end
444
+ return _G[quotetype](s)
508
445
  end
509
446
 
510
447
  function SingleQuoted(s)
@@ -520,13 +457,9 @@ function SmallCaps(s)
520
457
  end
521
458
 
522
459
  function Div(s, attr)
523
- local blankline = attr_val(attr, "blankline")
524
- if blankline ~= "" then
525
- local buffer = {}
526
- for i = 1, tonumber(blankline) do
527
- table.insert(buffer, "//blankline")
528
- end
529
- return table.concat(buffer, "\n")
460
+ local blankline = tonumber(attr.blankline)
461
+ if blankline then
462
+ return string.rep("//blankline\n", blankline):gsub("\n$", "")
530
463
  end
531
464
 
532
465
  local classes = attr_classes(attr)
@@ -540,15 +473,11 @@ function Div(s, attr)
540
473
  end
541
474
 
542
475
  if classes["review-internal"] then
543
- s, _ = s:gsub(
544
- "%]{<P2RREMOVEBELOW/>\n", "]{"
545
- ):gsub(
546
- "\n<P2RREMOVEABOVE/>//}", "//}"
547
- )
476
+ s, _ = s:gsub("%]{<P2RREMOVEBELOW/>\n", "]{"):gsub("\n<P2RREMOVEABOVE/>//}", "//}")
548
477
  return s
549
478
  end
550
479
 
551
- for cls,_ in pairs(classes) do
480
+ for cls, _ in pairs(classes) do
552
481
  s = "//" .. cls .. "{\n" .. s .. "\n//}"
553
482
  end
554
483
  return s
@@ -557,7 +486,7 @@ end
557
486
  function Span(s, attr)
558
487
  -- ruby and kw with a supplement
559
488
  local a = ""
560
- for _, cmd in ipairs({"ruby", "kw"}) do
489
+ for _, cmd in ipairs({ "ruby", "kw" }) do
561
490
  a = attr_val(attr, cmd)
562
491
  if a ~= "" then
563
492
  s = format_inline(cmd, s .. "," .. a)
@@ -575,31 +504,31 @@ function Span(s, attr)
575
504
  end
576
505
 
577
506
  function RawInline(format, text)
578
- if (format == "review") then
507
+ if format == "review" then
579
508
  return text
580
509
  end
581
510
 
582
- if (metadata.hideraw) then
511
+ if metadata.hideraw then
583
512
  return ""
584
513
  end
585
514
 
586
- if (format == "tex") then
515
+ if format == "tex" then
587
516
  return format_inline("embed", "|latex|" .. text)
588
517
  else
589
- return format_inline("embed", "|" .. format .. "|", text)
518
+ return format_inline("embed", "|" .. format .. "|" .. text)
590
519
  end
591
520
  end
592
521
 
593
522
  function RawBlock(format, text)
594
- if (format == "review") then
523
+ if format == "review" then
595
524
  return text
596
525
  end
597
526
 
598
- if (metadata.hideraw) then
527
+ if metadata.hideraw then
599
528
  return ""
600
529
  end
601
530
 
602
- if (format == "tex") then
531
+ if format == "tex" then
603
532
  return "//embed[latex]{\n" .. text .. "\n//}"
604
533
  else
605
534
  return "//embed[" .. format .. "]{\n" .. text .. "\n//}"
@@ -607,18 +536,18 @@ function RawBlock(format, text)
607
536
  end
608
537
 
609
538
  local function configure()
610
- try_catch {
539
+ try_catch({
611
540
  try = function()
612
541
  metadata = PANDOC_DOCUMENT.meta
613
542
  end,
614
543
  catch = function(error)
615
544
  log("Due to your pandoc version is too old, config.yml loader is disabled.\n")
616
- end
617
- }
545
+ end,
546
+ })
618
547
 
619
- if (metadata) then
548
+ if metadata then
620
549
  -- Load config from YAML
621
- for k,v in pairs(config) do
550
+ for k, _ in pairs(config) do
622
551
  if metadata[k] ~= nil then
623
552
  config[k] = stringify(metadata[k])
624
553
  end
@@ -626,23 +555,225 @@ local function configure()
626
555
  end
627
556
  end
628
557
 
629
- if PANDOC_VERSION >= "3.0.0" then
630
- -- NOTE: A wrapper to support Pandoc >= 3.0 https://pandoc.org/custom-writers.html#changes-in-pandoc-3.0
631
- function Writer (doc, opts)
632
- PANDOC_DOCUMENT = doc
633
- PANDOC_WRITER_OPTIONS = opts
634
- configure()
635
- return pandoc.write_classic(doc, opts)
636
- end
637
- else
558
+ setmetatable(_G, {
559
+ __index = function(_, key)
560
+ log(string.format("WARNING: Undefined function '%s'\n", key))
561
+ return function()
562
+ return ""
563
+ end
564
+ end,
565
+ })
566
+
567
+ if PANDOC_VERSION < "3.0.0" then
638
568
  configure()
569
+ return
639
570
  end
640
571
 
641
- local meta = {}
642
- meta.__index =
643
- function(_, key)
644
- log(string.format("WARNING: Undefined function '%s'\n", key))
645
- return function() return "" end
572
+ Blocks = setmetatable({}, {
573
+ __index = function(_, key)
574
+ error("NotImplementedError: Blocks.%" .. tostring(key))
575
+ end,
576
+ })
577
+
578
+ Inlines = setmetatable({}, {
579
+ __index = function(_, key)
580
+ error("NotImplementedError: Inlines.%" .. tostring(key))
581
+ end,
582
+ })
583
+
584
+ local tidy_attr = function(el)
585
+ local ret = {}
586
+ for k, v in pairs(el.attr.attributes) do
587
+ ret[k] = v
588
+ end
589
+ ret.id = el.identifier or ""
590
+ ret.class = el.classes and table.concat(el.classes, " ")
591
+ return ret
592
+ end
593
+
594
+ local concat = pandoc.layout.concat
595
+
596
+ local function render(...)
597
+ local x = pandoc.layout.render(...):gsub("\n+$", "")
598
+ return x
599
+ end
600
+
601
+ local function inlines(els)
602
+ local buff = {}
603
+ for _, el in ipairs(els) do
604
+ table.insert(buff, Inlines[el.tag](el))
605
+ end
606
+ return concat(buff)
607
+ end
608
+
609
+ local function blocks(els, sep)
610
+ local buff = {}
611
+ for _, el in ipairs(els) do
612
+ table.insert(buff, Blocks[el.tag](el))
646
613
  end
614
+ return concat(buff, sep)
615
+ end
616
+
617
+ Inlines.Str = function(el)
618
+ return Str(el.text)
619
+ end
647
620
 
648
- setmetatable(_G, meta)
621
+ for _, v in pairs({ "Space", "LineBreak", "SoftBreak" }) do
622
+ Inlines[v] = _G[v]
623
+ end
624
+
625
+ Blocks.HorizontalRule = function(_)
626
+ return HorizontalRule() .. "\n"
627
+ end
628
+
629
+ Blocks.Plain = function(el)
630
+ return inlines(el.content) .. "\n"
631
+ end
632
+
633
+ Blocks.Para = function(el)
634
+ if #el.content == 1 and el.content[1].tag == "Image" then
635
+ local img = el.content[1]
636
+ return CaptionedImage(img.src, img.title, render(inlines(img.caption)), tidy_attr(img)) .. "\n"
637
+ end
638
+ return inlines(el.content) .. "\n"
639
+ end
640
+
641
+ Blocks.Header = function(el)
642
+ return Header(el.level, render(inlines(el.content)), tidy_attr(el)) .. "\n"
643
+ end
644
+
645
+ local function render_blocks(blks, sep)
646
+ local ret = {}
647
+ for _, v in pairs(blks) do
648
+ table.insert(ret, render(blocks(v, sep or "\n")))
649
+ end
650
+ return ret
651
+ end
652
+
653
+ Blocks.BulletList = function(el)
654
+ return BulletList(render_blocks(el.content)) .. "\n"
655
+ end
656
+
657
+ Blocks.OrderedList = function(el)
658
+ return OrderedList(render_blocks(el.content), el.start) .. "\n"
659
+ end
660
+
661
+ Blocks.DefinitionList = function(el)
662
+ local items = {}
663
+ for _, v in pairs(el.content) do
664
+ local term = render(inlines(v[1]))
665
+ table.insert(items, { [term] = render_blocks(v[2]) })
666
+ end
667
+ return DefinitionList(items) .. "\n"
668
+ end
669
+
670
+ Blocks.BlockQuote = function(el)
671
+ return BlockQuote(render(blocks(el.content, "\n"))) .. "\n"
672
+ end
673
+
674
+ Blocks.CodeBlock = function(el)
675
+ return CodeBlock(el.text, tidy_attr(el)) .. "\n"
676
+ end
677
+
678
+ Blocks.LineBlock = function(el)
679
+ local lines = {}
680
+ for _, v in pairs(el.content) do
681
+ table.insert(lines, render(inlines(v)))
682
+ end
683
+ return LineBlock(lines) .. "\n"
684
+ end
685
+
686
+ Inlines.Link = function(el)
687
+ return Link(render(inlines(el.content)), el.target, el.title)
688
+ end
689
+
690
+ Inlines.Code = function(el)
691
+ return Code(el.text, tidy_attr(el))
692
+ end
693
+
694
+ for _, k in pairs({ "Emph", "Strong", "Strikeout", "Underline", "Subscript", "Superscript", "SmallCaps" }) do
695
+ Inlines[k] = function(el)
696
+ return _G[k](render(inlines(el.content)))
697
+ end
698
+ end
699
+
700
+ Inlines.Math = function(el)
701
+ return _G[el.mathtype](el.text)
702
+ end
703
+
704
+ Blocks.Table = function(el)
705
+ local tbl = pandoc.utils.to_simple_table(el)
706
+ local headers = render_blocks(tbl.headers)
707
+ local rows = {}
708
+ for _, row in pairs(tbl.rows) do
709
+ table.insert(rows, render_blocks(row, ""))
710
+ end
711
+ return Table(render(inlines(tbl.caption)), tbl.aligns, tbl.widths, headers, rows) .. "\n"
712
+ end
713
+
714
+ Inlines.Image = function(el)
715
+ return Image(render(inlines(el.caption)), el.src, el.title, tidy_attr(el))
716
+ end
717
+
718
+ Inlines.Note = function(el)
719
+ return Note(render(blocks(el.content, "\n")))
720
+ end
721
+
722
+ Inlines.Cite = function(el)
723
+ return Cite(render(inlines(el.content)))
724
+ end
725
+
726
+ Inlines.Quoted = function(el)
727
+ return Quoted(el.quotetype, render(inlines(el.content)))
728
+ end
729
+
730
+ Blocks.Div = function(el)
731
+ return Div(render(blocks(el.content, "\n")), tidy_attr(el)) .. "\n"
732
+ end
733
+
734
+ Inlines.Span = function(el)
735
+ return Span(render(inlines(el.content)), tidy_attr(el))
736
+ end
737
+
738
+ Inlines.RawInline = function(el)
739
+ return RawInline(el.format, el.text)
740
+ end
741
+
742
+ Blocks.RawBlock = function(el)
743
+ return RawBlock(el.format, el.text) .. "\n"
744
+ end
745
+
746
+ Blocks.Figure = function(el)
747
+ if #el.content > 1 or #el.content[1].content > 1 or el.content[1].content[1].tag ~= "Image" then
748
+ error("NotImplementedError: current implementation assumes Figure contains only a single image.")
749
+ -- because Pandoc 3.1.4 does not support Pandoc's markdown cotaining Figure with multiple images...
750
+ end
751
+
752
+ local img = el.content[1].content[1]
753
+
754
+ return CaptionedImage(img.src, img.title, render(inlines(img.caption)), tidy_attr(img)) .. "\n"
755
+ end
756
+
757
+ function Writer(doc, opts)
758
+ PANDOC_DOCUMENT = doc
759
+ PANDOC_WRITER_OPTIONS = opts
760
+ configure()
761
+
762
+ if metadata.classicwriter then
763
+ if pandoc.write_classic then
764
+ return pandoc.write_classic(
765
+ doc:walk({
766
+ Figure = function(el)
767
+ return pandoc.RawBlock("review", render(Blocks.Figure(el)))
768
+ end,
769
+ }),
770
+ opts
771
+ )
772
+ end
773
+ log("WARNING: pandoc.write_classic is defunct. Using modern writer")
774
+ end
775
+
776
+ -- body should keep trailing new lines but remove one if there are footnotes for the backward-compatibility
777
+ local body = pandoc.layout.render(pandoc.layout.concat({ blocks(doc.blocks, "\n") }))
778
+ return Doc(#footnotes == 0 and body or body:gsub("\n$", ""))
779
+ end