Overview
Comment: | add beginnings of groff renderer, document more planned syntaxes |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
9215a9c850343f45b4116f6270b8487b |
User & Date: | lexi on 2021-12-27 13:20:36 |
Other Links: | manifest | tags |
Context
2021-12-29
| ||
12:19 | continue iterating on groff renderer; add headings, basic formatting, beginnings of a footnote and link system, colors check-in: 7ba2577283 user: lexi tags: trunk | |
2021-12-27
| ||
13:20 | add beginnings of groff renderer, document more planned syntaxes check-in: 9215a9c850 user: lexi tags: trunk | |
06:06 | add hue spread check-in: 560e69cc54 user: lexi tags: trunk | |
Changes
Modified cli.lua from [9f14981767] to [ad6ab18d31].
216 216 input.stream = file 217 217 input.src.file = args[1] 218 218 end 219 219 220 220 return main(input, outp, log, mode, suggestions, vars, extrule) 221 221 end 222 222 223 -local ok, e = pcall(entry_cli) 224 --- local ok, e = true, entry_cli() 223 +-- local ok, e = pcall(entry_cli) 224 +local ok, e = true, entry_cli() 225 225 if not ok then 226 226 local str = 'translation failure' 227 227 if ss.exn.is(e) then 228 228 str = e.kind.desc 229 229 end 230 230 local color = false 231 231 if log:seek() == nil then
Modified cortav.ct from [0c4afc6088] to [4ed3bc7476].
115 115 * [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides. 116 116 117 117 ##onspans styled text 118 118 most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. 119 119 120 120 * strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color. 121 121 * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics 122 +* custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism. 122 123 * literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace 123 124 * variable {obj $|styled-text}: indicates that its text is a stand-in that will be replaced with what it names. generally rendered in italic monospace, ideally of a different color 124 125 * underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text [!is] distinct from links, but underlining non-links is still a violation of convention. 125 126 * strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion 126 127 * insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body. 127 128 ** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work 128 129 * link \[>[!ref] [!styled-text]\]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link. 129 130 * footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism]] will insert a link with the next [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. 130 131 * superscript {obj '|[$styled-text]} 131 132 * subscript {obj ,|[$styled-text]} 132 -* raw \[\\[`raw-text]\]: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket 133 +* raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket 133 134 * raw literal \[$\\[!raw-text]\]: shorthand for [\[$[\…]]] 134 135 * macro [`\{[!name] [!arguments]\}]: invokes a [>ex.mac macro], specified with a reference 135 136 * argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer. 136 137 * raw argument {obj ##|var}: like above, but does not evaluate [$var]. 137 138 * term {obj &|name}, {span &|name|[$expansion]}: quotes a defined term with a link to its definition, optionally with a custom expansion of the term (for instance, to expand the first use of an acronym) 138 139 * inline image {obj &@|name}: shows a small image or other object inline. the unicode character [`🖼] can also be used instead of [`&@]. 139 140 * unicode codepoint {obj U+|hex-integer}: inserts an arbitrary UCS codepoint in the output, specified by [$hex-integer]. lowercase [`u] is also legal. ................................................................................ 237 238 * {def cortav.page} the number of the page currently being rendered 238 239 * {def cortav.id} the identifier of the renderer 239 240 * {def cortav.hash} the SHA3 hash of the source file being rendered 240 241 def: [*[#1]]: 241 242 242 243 on systems with environment variables, these may be accessed as context variables by prefixing their name with [`env.]. 243 244 244 -different renderers may provide context in different ways, such as from command line options or a context file. any predefined variables should carry an appropriate prefix to prevent conflation. 245 +different renderers may provide context in different ways, such as from command line options or a context file. any predefined variables should carry an appropriate prefix to prevent conflation. 245 246 246 247 ##fonts fonts 247 248 for output backends that support font specification, cortav provides a sophisticated font management system by means of the [!font stack]. 248 249 249 250 when a document parse begins, the font stack is empty (unless a default font has already been loaded by an intent file). 250 251 when the font stack is empty, cortav does not include font specifications in its output, and thus will use whatever the default of the various rendering programs is. 251 252 ................................................................................ 256 257 #^fonts 257 258 %% we then define each font as a resource 258 259 @serif 259 260 src: auto font name:Alegreya 260 261 embed font/ttf file:project-fonts/alegreya.ttf 261 262 link font/woff2 file:/assets/font/alegreya.woff2 262 263 auto font name:Times New Roman 264 + auto font dit:TR/bold=TRB/italic=TRI/bold,italic=TRBI 263 265 @sans 264 266 src: link font name:Alegreya Sans 265 267 link font name:Open Sans 266 268 link font name:sans-serif 267 269 ~~~ 268 270 269 271 here we have defined two font families, [`fonts.serif] and [`fonts.sans]. each contains a list of references to fonts which will be tried in order. for example, this could be translated into the following CSS: ................................................................................ 343 345 344 346 &$cursive-quote Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident 345 347 346 348 %% without affecting the overall font context. in fact, since 'cursive-quote' creates 347 349 %% its context using 'dup', it would import all font specifications besides 'body' 348 350 %% from the environment it is invoked in 349 351 ~~~ 352 + 353 +you may have noticed the rather odd bit at the end of our font definition, with the [`dit] URI. the reasons for this are tragic. groff, while delightful, has a thoroughly antiquated understanding of fonts, and doesn't support normal font formats like truetype. groff ships with a limited number of fonts in its own format, identified by obscurantist letter code ([`HBI] is "Helvetica Bold Italic", for instance) and lacking normal metadata. for this reason, you'll have to tell cortav how you want your fonts translated. 354 + 355 +it is possible to use modern fonts with groff, but to do that you'll have to convert and install them, which is outside the scope of this document. however, even if you do this, you should specify a fallback font (if possible) so that people rendering your document on other machines still get somewhat sensible output. 356 + 357 +the syntax of a [`dit] specification is [`dit:[$regular]], where [$regular] specifies the name of the regular font. this can be followed by any number of variant specifications [`/[$variant]=[$name]], where [$variant] is one of the tags described in the [>fonts-sty custom] style section, and [$name] is the name of a DIT font. so the URI in the example names a font [`T] with bold [`TB], italic [`TI], and bold-italic [`TBI]. 358 + 359 +the [`groff] backend does do a little magic to make this mess more bearable, however. some of groff's built-in fonts can be accessed by a [`name] URI instead of having to construct them by hand with a [`dit] URI -- the backend hardcodes metadata for these fonts so that documents can render somewhat intellgibly in groff even if the original author did not make special provisions for this. the groff fonts accessible by [`name] are: 360 +* Times New Roman 361 +* Helvetica 362 +* Courier 363 +* Bookman 364 +additionally, as a shortcut, if the regular, bold, italic, and bold-italic variants of a DIT font have the predictable pattern of [`[$X]], [`[$X]B], [`[$X]I], [`[$X]BI] (which many do), you can simply write the URI [`dit:[$X]] and cortav will infer the rest. so the example above could be rewritten as [`dit:T] to exactly the same effect. 365 + 366 +###fonts-sty custom styles 367 +sometimes you want to be able to issue more specific formatting instructions than "italic" or "bold". cortav provides a simple [!custom style] mechanism to allow this. a custom style is simply a reference that binds a name to a sequence of space-separated formatting directives. these directives include: 368 +* [`regular]: applies the regulat form of the font, overriding any previous set styles 369 +* [`medium]: applies a weight of the font between [`regular] and [`bold], defaulting to [`regular] if one is not available 370 +* [`demibold]: applies a weight of the font between [`regular] and [`bold], defaulting to [`bold] if one is not available 371 +* [`bold]: applies the usual "bold" weight of the font 372 +* [`dense]: applies the heaviest available weight of th the font. usually the same as [`bold] 373 +* [`light]: applies the usual "light" weight of the font. most fonts do not have a light weight, so this will be the same as [`regular]. 374 +* [`thin]: applies the slimmest available weight of the font. usually the same as [`light]. 375 +* [`underline]: underlines the text 376 +* [`strike]: strikes the text out 377 +* [`italic]: applies a slanted variant of the font 378 +* [`oblique]: applies the most slanted variant of the font available. usually the same as [`italic] 379 +* [`font=[$id]]: switches to font [$id] for the duration of the span. [$id] must be the ID of a resource defining a font. 380 +* [`[$ext].[$prop]=[$word]]: attaches extra information for use by formatting extensions. [$ext] must be the ID of the extension. 381 + 382 +once a custom style is defined, you can make use of it using the [` \[.[$id] [$styled-text]\]] span notation, where [$id] is the identifier of the reference containing your style. for instance, to define and use a style named [`important] that [^pls-no specifies a dense, underlined variant of font [`impact]] and applies the CSS class [`blink] when rendered with the [`html] backend: 383 + pls-no: please do not do this 384 +~~~cortav 385 +this paragraph contains some [.important truly important] information. 386 + important: dense underline font=impact html.class=blink 387 +~~~ 388 +you should always give your styles semantic names where practicable, instead of simply describing their graphical characteristics. this is good practice in general, but especially because your document will be renderable to different formats with different characteristics, and what makes text look important on a manpage in the terminal may be quite different from how it looks in a webpage or PDF. 350 389 351 390 ##dir directives 352 391 d: [`%[*[##1]]] 353 392 * {d author} encodes document authorship. multiple author directives can be issued to add additional coauthors 354 393 * {d cols} specifies the number of columns the next object should be rendered with 355 394 * {d include} transcludes another file 356 395 * {d import} reads in the contents of another file as an embeddable section
Modified desk/cortav.xml from [b82e1b14f3] to [d0eba8f9ef].
119 119 <DetectChar attribute='Span Cue' char='~' context='#pop!span-del' /> 120 120 121 121 <AnyChar attribute='Span Cue' String='`$+🔒' context='#pop!span' /> 122 122 <StringDetect attribute='Span Cue' String='→' context='#pop!ref' /> 123 123 <StringDetect attribute='Span Cue' String='🔗' context='#pop!ref' /> 124 124 <DetectChar attribute='Span Cue' char='>' context='#pop!ref' /> 125 125 <DetectChar attribute='Span Cue' char='^' context='#pop!ref' /> 126 + <DetectChar attribute='Span Cue' char='"' context='#pop!ref' /> 126 127 <DetectChar attribute='Span Cue' char='&' context='#pop!ref' /> 127 128 <DetectChar attribute='Span Cue' char='#' context='#pop!var-ref' /> 128 129 <DetectChar attribute='Span Cue' char='\' context='#pop!flat-span' /> 129 130 <Detect2Chars attribute='Comment' char='%' char1='%' context='#pop!inline-comment' /> 130 131 <Detect2Chars attribute='Critical Directive Cue' char='%' char1='!' context='#pop!inline-directive' /> 131 132 <DetectChar attribute='Directive Cue' char='%' context='#pop!inline-directive' /> 132 133 </context>
Modified ext/toc.lua from [35da212f52] to [e8a2cf6308].
82 82 } 83 83 ol.toc > ol > li { 84 84 list-style: decimal; 85 85 } 86 86 ol.toc > li > ol > li > ol > li { 87 87 list-style: enclosed; 88 88 } 89 + ol.toc > li > ol > li > ol > li > ol > li { 90 + list-style: lower-roman; 91 + } 89 92 } 90 93 ]] 91 94 92 95 ct.ext.install { 93 96 id = 'toc'; 94 97 desc = 'provides a table of contents for HTML renderer plus generic fallback'; 95 98 version = ss.version {0,1; 'devel'};
Added render/groff.lua version [a43bfa19e3].
1 +-- [ʞ] render/groff.lua 2 +-- ~ lexi hale <lexi@hale.su> 3 +-- 🄯 AGPLv3 4 +-- ? renders cortav to groff source code, for creating pdfs, 5 +-- dvis, manapages, and html files that are grievously 6 +-- inferior compared to our own illustrious direct-html 7 +-- renderer. 8 +-- > cortav -m render:format groff 9 + 10 +local ct = require 'cortav' 11 +local ss = require 'sirsem' 12 + 13 +local tcat = function(a,b) 14 + for i,v in ipairs(b) do 15 + table.insert(a, b) 16 + end 17 + return a 18 +end 19 +local lines = function(...) 20 + local s = ss.strac() 21 + for _, v in pairs{...} do s(v) end 22 + return s 23 +end 24 + 25 +function ct.render.groff(doc, opts) 26 + -- rs contains state specific to this render job 27 + -- that modules will need access to 28 + local rs = {}; 29 + rs.macsets = { 30 + strike = { 31 + '.de ST'; 32 + [[.nr ww \w'\\$1']]; 33 + [[\Z@\v'-.25m'\l'\\n[ww]u'@\\$1']]; 34 + '..'; 35 + }; 36 + } 37 + rs.macsNeeded = { 38 + order = {}; 39 + count = 0; 40 + } 41 + function rs.macAdd(id) 42 + if rs.macsets[id] then 43 + rs.macsNeeded.count = macsNeeded.count + 1 44 + rs.macsNeeded.order[rs.macsNeeded.count] = id 45 + return true 46 + else return false end 47 + end 48 + local job = doc:job('render_groff',nil,rs) 49 + 50 + -- the way this module works is we build up a table for each block 51 + -- of individual strings paired with attributes that say how they 52 + -- should be rendered. we then iterate over the table, applying 53 + -- formats as need be, and inserting blanks after each block 54 + 55 + local spanRenderers = {} 56 + function spanRenderers.format(rc, s, b, sec) 57 + local rcc = rc:clone() 58 + if s.style == 'strong' then 59 + rcc.prop.bold = true 60 + elseif s.style == 'emph' then 61 + rcc.prop.emph = true 62 + elseif s.style == 'strike' then 63 + rcc.prop.strike = true 64 + rs.macAdd 'strike' 65 + elseif s.style == 'insert' then 66 + end 67 + rs.renderSpans(rcc, s.spans, b, sec) 68 + end; 69 + 70 + function rs.renderSpans(rc, sp, b, sec) 71 + for i, v in ipairs(sp) do 72 + if type(v) == 'string' then 73 + rc:add(v) 74 + elseif spanRenderers[v.kind] then 75 + spanRenderers[v.kind](rc, v, b, sec) 76 + end 77 + end 78 + end 79 + 80 + local blockRenderers = {} 81 + function blockRenderers.paragraph(rc, b, sec) 82 + rs.renderSpans(rc, b.spans, b, sec) 83 + end 84 + function rs.renderBlock(b, sec) 85 + local rc = { 86 + clone = function(self) 87 + return { 88 + clone = self.clone; 89 + lines = self.lines; 90 + prop = ss.clone(self.prop); 91 + mk = self.mk; 92 + add = self.add; 93 + } 94 + end; 95 + lines = {}; 96 + prop = {}; 97 + mk = function(self, ln) 98 + local p = ss.clone(self.prop) 99 + p.txt = ln 100 + return p 101 + end; 102 + add = function(self, ln) 103 + table.insert(self.lines, self:mk(ln)) 104 + end; 105 + } 106 + if blockRenderers[b.kind] then 107 + blockRenderers[b.kind](rc, b, sec) 108 + end 109 + return rc.lines 110 + end 111 + 112 + function rs.emitLine(ln) 113 + local q = ss.strac() 114 + if ln.dsz then 115 + q('\\ps +' .. tostring(ln.dsz)) 116 + elseif ln.sz then 117 + q('\\ps ' .. tostring(ln.dsz)) 118 + end 119 + 120 + if ln.bold and ln.emph then 121 + q '\\f(BI' 122 + elseif ln.bold then 123 + q '\\fB' 124 + elseif ln.emph then 125 + q '\\fI' 126 + end 127 + 128 + 129 + q(ln.txt) 130 + 131 + if ln.bold or ln.emph then 132 + q'\\f[]' 133 + end 134 + 135 + if ln.dsz then 136 + q('.ps -' .. tostring(ln.dsz)) 137 + elseif ln.sz then 138 + q '.ps' 139 + end 140 + return q 141 + end 142 + 143 + local ir = {} 144 + for i, sec in ipairs(doc.secorder) do 145 + if sec.kind == 'ordinary' then 146 + local blks = {} 147 + for j, b in ipairs(sec.blocks) do 148 + local r = rs.renderBlock(b, sec) 149 + if r then table.insert(blks, r) end 150 + end 151 + table.insert(ir, blks) 152 + end 153 + end 154 + 155 + local rd = ss.strac() 156 + for i, s in ipairs(ir) do 157 + for j, b in ipairs(s) do 158 + for z, l in ipairs(b) do 159 + rd(rs.emitLine(l)) 160 + end 161 + rd'\n' 162 + end 163 + end 164 + 165 + local macs = ss.strac() 166 + for _, m in pairs(rs.macsNeeded.order) do 167 + for _, ln in pairs(m) do macs(ln) end 168 + end 169 + return macs:compile'\n' .. rd:compile'' 170 +end
Modified render/html.lua from [35b47400ac] to [c1f1be8e43].
1 +-- [ʞ] render/html.lua 2 +-- ~ lexi hale <lexi@hale.su> 3 +-- 🄯 AGPLv3 4 +-- ? renders cortav to beautiful, highly customizable 5 +-- webpages full of css trickery to make them look 6 +-- good both on a screen and when printed. 7 +-- > cortav -m render:format html 8 + 1 9 local ct = require 'cortav' 2 10 local ss = require 'sirsem' 3 11 4 12 -- install rendering function for html 5 13 function ct.render.html(doc, opts) 6 14 local doctitle = opts['title'] 7 15 local f = string.format ................................................................................ 298 306 abbr = [[ 299 307 abbr[title] { cursor: help; } 300 308 ]]; 301 309 editors_markup = [[]]; 302 310 block_code_listing = [[ 303 311 figure.listing { 304 312 font-family: monospace; 313 + font-size: 85%; 305 314 background: @tone(0.05 20); 306 315 color: @tone(1 20); 307 316 padding: 0; 308 317 margin: 0.3em 0; 309 318 counter-reset: line-number; 310 319 position: relative; 311 320 border: 1px solid @tone(1 20);
Modified sirsem.lua from [dc1f0ae1fb] to [4b787982a7].
885 885 end 886 886 else 887 887 me:react(sym) 888 888 end 889 889 end; 890 890 }; 891 891 } 892 + 893 +-- convenience buffer for holding strings under 894 +-- construction, accumulating and compiling then in 895 +-- as quick a way as lua permits 896 +ss.strac = ss.declare { 897 + ident = 'string-accumulator'; 898 + mk = function() return { 899 + strs = {}; 900 + strc = 0; 901 + plain = true; 902 + } end; 903 + call = function(self, s, ...) 904 + if s == nil then return end 905 + self.strc = self.strc + 1 906 + self.strs[self.strc] = s 907 + if type(s) ~= 'string' then self.plain = false end 908 + self(...) 909 + end; 910 + cast = { 911 + string = function(self) 912 + return self:compile() 913 + end; 914 + }; 915 + fns = { 916 + compile = function(self, delim) 917 + if self.plain then 918 + return table.concat(self.strs, delim) 919 + end 920 + local tbl = {} 921 + local function delve(a) 922 + for i=1,a.strc do 923 + local s = a.strs[i] 924 + if type(s) == 'string' then 925 + table.insert(tbl, s) 926 + elseif ss.strac.is(s) then 927 + delve(s) 928 + elseif s ~= nil then 929 + table.insert(tbl, tostring(s)) 930 + end 931 + end 932 + end 933 + delve(self) 934 + return table.concat(tbl, delim) 935 + end; 936 + wrap = function(self,a,b) 937 + table.insert(self.strs, 1, a) 938 + table.insert(self.strs, b) 939 + end; 940 + }; 941 +}