-
Notifications
You must be signed in to change notification settings - Fork 0
/
attr-color.lua
336 lines (297 loc) · 8.46 KB
/
attr-color.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
--[==============================[
# attr-color.lua
A Pandoc filter which sets text/background/frame color(s) on
Span and Link elements based on Pandoc attributes.
## Version
This is version 2020121418 of the filter.
## Usage
See https://github.com/bpj/pandoc-attr-color
## License
This software is Copyright (c) 2020 by Benct Philip Jonsson.
This is free software, licensed under:
The MIT (X11) License
The MIT License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to
whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall
be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT
WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
--]==============================]
-- This sets the defaults in case metadata/environment variables are unset
-- Feel free to change these in your local copy if you can find a good reason.
local config = {
format = FORMAT, -- (string) defaults to Pandoc's current output format
keep_attrs = false, -- must be a boolean
fallback_format = 'html', -- string
}
-- local dump = require"pl.pretty".dump -- for debugging
-- assert with format string
local function assertf (val, msg, ...)
if val then return val end
error(msg:format(...))
end
-- Fall back to another value if a value is nil
local function if_nil (a,b)
if nil == a then return b else return a end
end
-- Get the keys of a table as an array
local function keys_of (tab)
local keys = {}
for k in pairs(tab) do
keys[#keys+1] = k
end
return keys
end
local rrggbb_keys = {'rr','gg','bb'}
local rgb_keys = {'r','g','b'}
local color_pats = {
{ keys = rrggbb_keys, pat = '^%#(%x%x)(%x%x)(%x%x)$' },
{ keys = rgb_keys, pat = '^%#(%x)(%x)(%x)$' },
{ keys = {'name'}, pat = '^(%a%w*)$' },
}
local color_attrs = {'fg','bg','fr'}
local latex_fmt = {
fcolorbox = {
pre = '\\fcolorbox@(fr)@(bg){',
post = '}',
},
colorbox = {
pre = '\\colorbox@(bg){',
post = '}',
},
-- This is a 'pseudocommand',
-- see https://tex.stackexchange.com/a/22928
-- We might wish to define it but then the filter or
-- the user will have to modify header-includes, so no!
colorfbox = {
pre = '{\\colorlet{curcolor}{.}\\color@(fr)\\fbox{\\color{curcolor}',
post = '}}',
},
-- Variation on colorfbox with a custom text/fg color to avoid redundancy
-- although there will be some redundancy if the frame/text colors are
-- the same, but can't check for that because User may say `{fr=blue fg="#00f"}`
-- and knowing the values of named colors is too much work.
textcolorfbox = {
pre = '{\\color@(fr)\\fbox{',
post = '}}',
},
textcolor = {
pre = '\\textcolor@(fg){',
post = '}',
},
}
local css_fmt = {
fg = 'color: @(fg);',
bg = 'background-color: @(bg);',
fr = 'border: 0.01em solid @(fr);',
}
local css_pad = {
bg = true,
fr = true,
}
local function color2latex (name,value)
local color
for _,p in ipairs(color_pats) do
local match = { value:match(p.pat) }
if #match > 0 then
for i,k in ipairs(p.keys) do
match[k] = match[i]
end
color = match
break
end
end
assertf(
color, 'Bad value for attribute %s: "%s"', name, value
)
if color.r then
for _,x in ipairs(rgb_keys) do
color[x .. x] = color[x] .. color[x]
end
end
if color.rr then
for _,xx in ipairs(rrggbb_keys) do
color[xx] = color[xx]:upper()
end
color.name = color.rr .. color.gg .. color.bb
color.model = '[HTML]'
else
color.model = "" -- named color
end
return color.model .. '{' .. color.name .. '}'
end
-- Interpolate table values into a string by replacing
-- placeholders of the form `@(KEY)` with the value of key KEY
local function interp (str, tab)
local function _subst (k)
return assertf(tab[k], "No value for key: %s", k)
end
return str:gsub('%@%(([^()]+)%)', _subst)
end
-- Generate the xcolor commands to 'wrap' around
-- the span contents according to which color attributes we found
local function make_latex (colors)
-- The return values are two strings of raw LaTeX
-- to put before and after the span contents.
-- We always include a \strut to make sure that
-- the box(es) be one \baselineskip high!
local coms, pre, post = {}, {},{"\\strut"}
if colors.fr then -- if frame
if colors.bg then -- if also bground
coms[#coms+1] = 'fcolorbox'
elseif colors.fg then
coms[#coms+1] = 'textcolorfbox'
else
coms[#coms+1] = 'colorfbox'
end
elseif colors.bg then
coms[#coms+1] = 'colorbox'
end
if colors.fg then
coms[#coms+1] = 'textcolor'
end
for _,com in ipairs(coms) do
local com = assertf(
latex_fmt[com],
"No format for command: %s", com
)
pre[#pre+1] = interp(com.pre, colors)
post[#post+1] = interp(com.post, colors)
end
pre = table.concat(pre)
post = table.concat(post)
return pre, post
end
local function add_style (elem, colors)
local style = {}
for k,v in pairs(css_pad) do
if colors[k] then
style[#style+1] = 'padding: 0.1em;'
break
end
end
for _,attr in ipairs(color_attrs) do
if colors[attr] then
style[#style+1] = interp(css_fmt[attr], colors)
end
end
if #style > 0 then
style = table.concat(style, " ")
if elem.attributes.style then
elem.attributes.style = elem.attributes.style:gsub(';?%s*$', "; ")
else
elem.attributes.style = ""
end
elem.attributes.style = elem.attributes.style .. style
return elem
end
return nil
end
-- TODO: support ConTeXt if I can find out how to do frame and background on the fly.
formats = {
latex = {
name = 'latex',
make_color = color2latex,
make_markup = make_latex,
},
html = {
name = 'html',
modify_elem = add_style,
},
}
-- for _,key in ipairs{'html4', 'html5'} do
-- formats[key] = formats[html]
-- end
local function color_span (elem)
local format = formats[config.format] or formats[config.fallback_format]
if not format then return nil end
local colors
for _,name in ipairs(color_attrs) do
value = elem.attributes[name]
if value then
colors = colors or {}
if format.make_color then
colors[name] = format.make_color(name,value)
else
colors[name] = value
end
if not config.keep_attrs then
elem.attributes[name] = nil
end
end
end
if colors then
if format.make_markup then
local pre, post = format.make_markup(colors)
return{
pandoc.RawInline(format.name, pre),
elem,
pandoc.RawInline(format.name, post),
}
elseif format.modify_elem then
return format.modify_elem(elem, colors)
end
end
return nil
end
local str2bool = {
['true'] = true,
['false'] = false,
}
local function get_config (meta)
for _,key in ipairs(keys_of(config)) do
local dflt = config[key]
meta_key = "attr_color_" .. key
env = "PDC_ATTR_COLOR_" .. key:upper()
val = if_nil(meta[meta_key], os.getenv(env))
if nil ~= val then
if 'table' == type(val) then
val = pandoc.utils.stringify(val)
end
if 'boolean' == type(dflt) then
val = str2bool[tostring(val):lower()]
assertf(
('boolean' == type(val)),
"Expected meta.%s or env %s to be 'true' or 'false'", meta_key, env
)
end
config[key] = val
end
end
return nil
end
-- -- for debugging without pandoc
-- local read = require"pl.pretty".read
-- for line in io.lines() do
-- colors = assert(read(line))
-- for _,name in ipairs(color_attrs) do
-- if colors[name] then
-- colors[name] = color2latex(name, colors[name])
-- end
-- end
-- print(line)
-- print( make_latex(colors) )
-- end
return {
{ Meta = get_config },
{ Span = color_span,
Link = color_span,
},
}