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.
- checksums.yaml +7 -0
- data/.github/workflows/pandoc.yml +18 -0
- data/.gitignore +47 -0
- data/Gemfile +6 -0
- data/LICENSE +339 -0
- data/README.md +105 -0
- data/Rakefile +13 -0
- data/exe/pandoc2review +12 -0
- data/lib/pandoc2review.rb +99 -0
- data/lua/filters.lua +163 -0
- data/lua/review.lua +620 -0
- data/markdown-format.ja.md +721 -0
- data/pandoc2review.gemspec +29 -0
- data/samples/format.md +276 -0
- data/samples/reviewsample/.gitignore +154 -0
- data/samples/reviewsample/Gemfile +4 -0
- data/samples/reviewsample/Rakefile +3 -0
- data/samples/reviewsample/catalog.yml +10 -0
- data/samples/reviewsample/ch01.md +38 -0
- data/samples/reviewsample/ch02.re +3 -0
- data/samples/reviewsample/config-ebook.yml +6 -0
- data/samples/reviewsample/config.yml +20 -0
- data/samples/reviewsample/images/cover-a5.ai +5836 -16
- data/samples/reviewsample/images/cover.jpg +0 -0
- data/samples/reviewsample/images/pandoc2review.png +0 -0
- data/samples/reviewsample/lib/tasks/review.rake +128 -0
- data/samples/reviewsample/lib/tasks/z01_pandoc2review.rake +69 -0
- data/samples/reviewsample/sty/README.md +168 -0
- data/samples/reviewsample/sty/gentombow.sty +769 -0
- data/samples/reviewsample/sty/jsbook.cls +2072 -0
- data/samples/reviewsample/sty/jumoline.sty +310 -0
- data/samples/reviewsample/sty/plistings.sty +326 -0
- data/samples/reviewsample/sty/review-base.sty +530 -0
- data/samples/reviewsample/sty/review-custom.sty +1 -0
- data/samples/reviewsample/sty/review-jsbook.cls +503 -0
- data/samples/reviewsample/sty/review-style.sty +49 -0
- data/samples/reviewsample/sty/reviewmacro.sty +15 -0
- data/samples/reviewsample/style.css +494 -0
- metadata +128 -0
data/Rakefile
ADDED
@@ -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
|
data/exe/pandoc2review
ADDED
@@ -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
|
data/lua/filters.lua
ADDED
@@ -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
|
+
}
|
data/lua/review.lua
ADDED
@@ -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)
|