Overview
Comment: | beginnings of some support for captions/subtitles, excise dumb ideas from readme and fix typo, black pharaoh but this codebase needs a rewrite |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
435c29db6bd7070b43184954c64101d7 |
User & Date: | lexi on 2024-07-17 22:21:07 |
Other Links: | manifest | tags |
Context
2024-07-17
| ||
22:29 | cleanup check-in: d85fbc448f user: lexi tags: trunk | |
22:21 | beginnings of some support for captions/subtitles, excise dumb ideas from readme and fix typo, black pharaoh but this codebase needs a rewrite check-in: 435c29db6b user: lexi tags: trunk | |
20:10 | add missing subtitle support check-in: 362f9a6647 user: lexi tags: trunk | |
Changes
Modified cortav.ct from [4b83863e2a] to [7452348c5b].
1070 1070 * [*html]: emit HTML and CSS code to typeset the document. [!in progress] 1071 1071 * [*svg]: emit SVG, taking advantage of its precise layout features to produce a nicely formatted and paginated document. pagination can perhaps be accomplished through emitting multiple files (somewhat problematic) or by assigning one layer to each page. [!long term] 1072 1072 * [*groff]: the most important output backend, rivalling [*html]. will allow the document to be typeset in a wide variety of formats, including PDF and manpage. [!in progress] 1073 1073 * [*gemtext]: essentially a downrezzing of cortav to make it readable to Gemini clients 1074 1074 * [*ast]: produces a human- and/or machine-readable dump of the document's syntax tree, to aid in debugging or for interoperation with systems that do not support `cortav` direcly. mode [`ast:repr] wil allow selecting formats for the dump. [`ast:rel] can be [`tree] (the default) to emit a hierarchical representation, or [`flat] to emit an array of nodes that convey hierarchy [^flatdoc by naming one another], rather than being placed inside one another. [`tree] is easier for humans to parse; [`flat] is easier for computers. origin information can be included for each node with the flag [`ast:debug-syms], but be aware this will greatly increase file size. 1075 1075 ** [`tabtree] [!(default)]: a hierarchical tree view, with the number of tabs preceding an item showing its depth in the tree 1076 1076 ** [`sexp] 1077 -** [`binary]: emit a raw binary format that is easier for programs to read. maybe an lmdb or cdb file? 1077 +** [`binary]: emit a raw binary format that is easier for programs to read. maybe a msgpack file? 1078 1078 ** [`json]: obligatory, alas 1079 1079 1080 1080 flatdoc: ~~~flat sexp example output [scheme]~~~ 1081 1081 (nodes 1082 1082 (section (id . "section1") 1083 1083 (anchor "introduction") 1084 1084 (kind . "ordinary")
Modified cortav.lua from [76257c7c34] to [f6f6b80f21].
970 970 if l:sub(1,1) == '.' then l = l:sub(2) end 971 971 return { 972 972 kind = "paragraph"; 973 973 spans = ct.parse_span(l, c); 974 974 } 975 975 end) 976 976 977 -local insert_subtitle = blockwrap(function(l,c) 978 - return { 979 - kind = "subtitle"; 980 - spans = ct.parse_span(l:sub(3), c); 977 +local insert_caption = blockwrap(function(l,c,j,d) 978 + if next(d) == nil then 979 + c:fail 'subtitle in an unlabeled section is meaningless' 980 + end 981 + 982 + local last = d[#d] 983 + local me = { 984 + kind = 'subtitle'; 985 + spans = ct.parse_span(l:sub(3):gsub("^%s+",""), c); 986 + } 987 + 988 + local captionable = { 989 + quote=true, aside=true, 990 + table=true, code=true, 991 + embed=true, link=true, 981 992 } 993 + 994 + if last.kind == 'label' then 995 + me.attach = last; 996 + elseif last.kind == 'subtitle' then 997 + me.attach = last.attach; 998 + elseif captionable[last.kind] then 999 + me.kind = 'label' 1000 + me.captions = last 1001 + last.label_node = me 1002 + else 1003 + c:fail 'subtitle/attribution syntax in improper context' 1004 + end 1005 + 1006 + return me 982 1007 end) 983 1008 984 1009 local function 985 1010 insert_section(skind) return function(l,c,j) 986 1011 local depth, id, t = l:match '^([#§^]+)([^%s]*)%s*(.-)$' 987 1012 if id and id ~= "" then 988 1013 if c.doc.sections[id] then ................................................................................ 1181 1206 ct.ctlseqs = { 1182 1207 {seq = '.', fn = insert_paragraph}; 1183 1208 {seq = '¶', fn = insert_paragraph}; 1184 1209 {seq = '❡', fn = insert_paragraph}; 1185 1210 {seq = '#', fn = insert_section()}; 1186 1211 {seq = '§', fn = insert_section()}; 1187 1212 {seq = '^', fn = insert_section 'namespace'}; 1188 - {seq = '--',fn = insert_subtitle}; 1213 + {seq = '--',fn = insert_caption}; 1189 1214 {seq = '+', fn = insert_table_row}; 1190 1215 {seq = '|', fn = insert_table_row}; 1191 1216 {seq = '│', fn = insert_table_row}; 1192 1217 {seq = '!', fn = function(l,c,j,d) 1193 1218 local last = d[#d] 1194 1219 local txt = l:match '^%s*!%s*(.-)$' 1195 1220 if (not last) or last.kind ~= 'aside' then
Modified render/groff.lua from [599907d26e] to [b6c7f1e8a6].
79 79 sreq = function(me, r) 80 80 me:flush() 81 81 table.insert(me.lines, "'"..r) 82 82 end; 83 83 esc = function(me, e) 84 84 me:raw('\\' .. e) 85 85 end; 86 - draw = function(me, args) 87 - for _,v in ipairs(args) do 86 + draw = function(me, args) 87 + for _,v in ipairs(args) do 88 88 me:esc("D'" .. v .. "'") 89 - end 90 - end; 89 + end 90 + end; 91 91 flush = function(me) 92 92 if me.linbuf ~= nil then 93 93 local line = me.linbuf:compile() 94 94 local first = line:sub(1,1) 95 95 -- make sure our lines aren't accidentally interpreted 96 96 -- as groff requests. groff is kinda hostile to script 97 97 -- generation, huh? ................................................................................ 418 418 end 419 419 420 420 local blockRenderers = {} 421 421 blockRenderers['horiz-rule'] = function(rc, b, sec) 422 422 rc.prop.margin = { top = 0.3 } 423 423 rc.prop.underline = 0.1 424 424 end 425 - function blockRenderers.label(rc, b, sec) 425 + function blockRenderers.label(rc, b, sec) 426 426 if ct.sec.is(b.captions) then 427 427 local visDepth = b.captions.depth + (b.origin.docDepth or 0) 428 428 local sizes = {36,24,12,8,4,2} 429 429 local margins = {0,3} 430 430 local dedents = {2.5,1.3,0.8,0.4} 431 431 local uls = {3,1.5,0.5,0.25} 432 432 rc.prop.dsz = sizes[visDepth] or 10 ................................................................................ 443 443 rc.prop.breakBefore = true 444 444 end 445 445 rs.renderSpans(rc, b.spans, b, sec) 446 446 else 447 447 ss.bug 'tried to render label for an unknown object type':throw() 448 448 end 449 449 end 450 - function blockRenderers.paragraph(rc, b, sec) 450 + function blockRenderers.paragraph(rc, b, sec) 451 + rs.renderSpans(rc, b.spans, b, sec) 452 + end 453 + function blockRenderers.subtitle(rc, b, sec) 454 + rc.prop.dsz = 16 -- TODO base on "parent" label 455 + rc.prop.emph = true 451 456 rs.renderSpans(rc, b.spans, b, sec) 452 457 end 453 - function blockRenderers.macro(rc, b, sec) 458 + function blockRenderers.macro(rc, b, sec) 454 459 local rc = rc.parent:clone() 455 460 rs.renderDoc(rc, b.doc) 456 461 end 457 - function blockRenderers.quote(rc, b, sec) 462 + function blockRenderers.quote(rc, b, sec) 458 463 local rc = rc.parent:clone() 459 464 rc.prop.indent = (rc.prop.indent or 0) + 1 460 465 local added = rs.renderDoc(rc, b.doc) 461 466 -- select last block of last section and increase bottom margin 462 467 local ap = added[#added].blocks 463 468 ap = ap[#ap].prop 464 469 if ap.margin then ................................................................................ 467 472 else 468 473 ap.margin.bottom = 1.1 469 474 end 470 475 else 471 476 ap.margin = {bottom = 1.1} 472 477 end 473 478 end 474 - function blockRenderers.table(rc, b, sec) 479 + function blockRenderers.table(rc, b, sec) 475 480 function rc:begin(g) 476 481 g:req 'TS' 477 482 local aligns = {} 478 483 for i, c in ipairs(b.rows[1]) do 479 484 aligns[i] = ({ 480 485 left = 'l'; 481 486 center = 'c';
Modified render/html.lua from [e689cbb41c] to [e178fd542c].
259 259 subtitle = [[ 260 260 .subtitle { 261 261 color: @tone(0.3 20); 262 262 font-size: 1.2em; 263 263 font-style: italic; 264 264 margin-left: 1em; 265 265 } 266 - blockquote + .subtitle { 267 - &::before { 268 - content: "— "; 269 - } 270 - } 271 266 ]]; 272 267 accent = [[ 273 268 @media screen { 274 269 body { background: @bg; color: @fg } 275 270 a[href] { 276 271 color: @tone(0.7 30); 277 272 text-decoration-color: @tone/0.4(0.7 30); ................................................................................ 313 308 } 314 309 section > aside p:first-child { 315 310 margin: 0; 316 311 } 317 312 section aside + aside { 318 313 margin-top: 0.5em; 319 314 } 320 - ]]; 315 + ]]; 316 + quoteCaption = [[ 317 + blockquote > div.caption { 318 + text-align: right; 319 + font-style: italic; 320 + &::before { 321 + content: "— "; 322 + } 323 + } 324 + ]]; 321 325 code = [[ 322 326 code { 323 327 display: inline-block; 324 328 background: @tone(-1); 325 329 color: @tone(0.7); 326 330 font-family: monospace; 327 331 font-size: 90%; ................................................................................ 1021 1025 return htmlURI(s.uri) 1022 1026 elseif s.mode == 'embed' then 1023 1027 local mime = s.mime:clone() 1024 1028 mime.opts = {} 1025 1029 return string.format('data:%s;base64,%s', mime, ss.str.b64e(s.raw)) 1026 1030 end 1027 1031 end 1028 - --figure out how to embed the given object 1029 1032 local function P(p) -- get prop 1030 1033 if b.props and b.props[p] then 1031 1034 return b.props[p] 1032 1035 end 1033 1036 return obj.props[p] 1034 1037 end 1038 + 1039 + local cap = b.cap or P'desc' or P'detail' 1040 + local capIR = ''; 1041 + if b.label_node then 1042 + local ln = b.label_node 1043 + capIR = sr.htmlSpan(ln.spans, ln, s) 1044 + elseif cap then 1045 + -- the block here should really be the relevant 1046 + -- ref definition if an override caption isn't 1047 + -- specified, but oh well 1048 + capIR = sr.htmlSpan(spanparse( 1049 + cap, b.origin 1050 + ), b, s) 1051 + end 1052 + 1053 + --figure out how to embed the given object 1035 1054 local embedActs = { 1036 1055 {ss.mime'image/*', function(s,ctr) 1037 1056 if s == nil then 1038 1057 return {tag = "picture", nodes = {}} 1039 1058 else 1040 1059 local uri = uriForSource(s) 1041 1060 local fbimg, idx ................................................................................ 1139 1158 goto compatFound 1140 1159 end 1141 1160 end 1142 1161 end 1143 1162 -- nothing found; install fallback link 1144 1163 if fallback then 1145 1164 local lnk = htmlURI(fallback.uri) 1146 - return tag('a', {href=lnk}, 1147 - tag('div',{class=xref}, 1148 - string.format("→ %s [%s]", b.cap or '', tostring(fallback.mime)))) 1165 + return tag('a', {href=lnk}, catenate { 1166 + tag('div',{class=xref}, catenate { 1167 + '→ '; capIR; 1168 + string.format(" [%s]", tostring(fallback.mime)); 1169 + })}) 1149 1170 else 1150 1171 addStyle 'docmeta' 1151 1172 return tag('div',{class="render-warn"}, 1152 1173 'could not embed object type ' .. tostring(obj.srcs.mime)) 1153 1174 end 1154 1175 1155 1176 ::compatFound:: ................................................................................ 1156 1177 local top = rtype[2]() -- create container 1157 1178 for n, src in ipairs(obj.srcs) do 1158 1179 if rtype[1] < src.mime then 1159 1180 rtype[2](src, top) 1160 1181 end 1161 1182 end 1162 1183 local ft = flatten(top) 1163 - local cap = b.cap or P'desc' or P'detail' 1164 1184 if b.mode == 'inline' then 1165 1185 -- TODO insert caption 1166 1186 return ft 1167 1187 else 1168 1188 local prop = {} 1169 1189 if b.mode == 'open' then 1170 1190 prop.open = true 1171 1191 end 1172 1192 return tag('details', prop, catenate { 1173 - tag('summary', {}, 1174 - cap and ( 1175 - -- the block here should really be the relevant 1176 - -- ref definition if an override caption isn't 1177 - -- specified, but oh well 1178 - sr.htmlSpan(spanparse( 1179 - cap, b.origin 1180 - ), b, s) 1181 - ) or ''); 1193 + tag('summary', {}, capIR); 1182 1194 ft; 1183 1195 }) 1184 1196 end 1185 1197 end 1186 1198 1187 1199 function block_renderers.macro(b,s) 1188 1200 local all = renderSubdoc(b.doc) 1189 1201 local cat = catenate(ss.map(flatten,all)) 1190 1202 return tag(nil, {}, cat) 1191 1203 end 1192 1204 1193 1205 function block_renderers.quote(b,s) 1194 1206 local ir = renderSubdoc(b.doc) 1207 + if b.label_node then 1208 + addStyle 'quoteCaption' 1209 + table.insert(ir, tag('div', {class='caption'}, 1210 + sr.htmlSpan(b.label_node.spans, b.label_node, s))) 1211 + end 1195 1212 return tag('blockquote', b.id and {id=getSafeID(b)} or {}, catenate(ss.map(flatten,ir))) 1196 1213 end 1197 1214 1198 1215 return block_renderers 1199 1216 end 1200 1217 1201 1218 local function getRenderers(procs)