19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
...
613
614
615
616
617
618
619
620
621
|
local lines = function(...)
local s = ss.strac()
for _, v in pairs{...} do s(v) end
return s
end
local function gsan(str)
local tocodepoint = function(ch)
return string.format('\\[u%04X]', utf8.codepoint(ch))
end
str = str:gsub('(["\'\\])',tocodepoint)
return str
end
local gtxt = ss.declare {
ident = 'groff-text';
mk = function() return {
lines = {};
................................................................................
spans = self.spans;
}
end;
blocks = {};
prop = {};
block = function(self)
local sub = self:clone()
sub.spans = {}
sub.blocks = nil
sub.span = function(me, ln)
local p = ss.clone(me.prop)
p.txt = ln
p.block = sub
p.origin = me.origin
table.insert(me.spans, p)
return p
................................................................................
local blockRenderers = {}
blockRenderers['horiz-rule'] = function(rc, b, sec)
rc.prop.margin = { top = 0.3 }
rc.prop.underline = 0.1
end
function blockRenderers.label(rc, b, sec)
if ct.sec.is(b.captions) then
local sizes = {36,24,12,8,4,2}
local margins = {0,3}
local dedents = {2.5,1.3,0.8,0.4}
local uls = {3,1.5,0.5,0.25}
rc.prop.dsz = sizes[b.captions.depth] or 10
rc.prop.underline = uls[b.captions.depth]
rc.prop.bold = b.captions.depth > 3
rc.prop.margin = {
top = margins[b.captions.depth] or 1;
bottom = 0.1;
}
rc.prop.vassure = rc.prop.dsz+70;
rc.prop.indent = -(dedents[b.captions.depth] or 0)
rc.prop.chtitle = collectText(rc, b.spans, b.spec):compile()
if b.captions.depth == 1 then
rc.prop.breakBefore = true
end
rs.renderSpans(rc, b.spans, b, sec)
else
ss.bug 'tried to render label for an unknown object type':throw()
end
end
function blockRenderers.paragraph(rc, b, sec)
rs.renderSpans(rc, b.spans, b, sec)
end
function rs.renderBlock(rc, b, sec, outerBlockRenderContext)
if blockRenderers[b.kind] then
local rcc = rc:block()
blockRenderers[b.kind](rcc, b, sec)
end
end
................................................................................
if ln.dsz then
defer:req('ps +' .. tostring(0 - ln.dsz) .. 'p')
else
defer:req'ps'
end
end
for i,s in pairs(b.spans) do
rs.emitSpan(gtxt, s)
end
if ln.margin then
if ln.margin.bottom then
gtxt:req(string.format('sp %sm', ln.margin.bottom))
end
end
defer:flush()
if not ln.margin then gtxt:brk() end
end
local ir = {}
for i, sec in ipairs(doc.secorder) do
if sec.kind == 'ordinary' then
local rc = mkrc()
for j, b in ipairs(sec.blocks) do
rs.renderBlock(rc, b, sec)
end
table.insert(ir, {blocks = rc.blocks, src = sec})
end
end
local gd = gtxt()
for i, s in ipairs(ir) do
for j, b in ipairs(s.blocks) do
rs.emitBlock(gd,b)
end
end
................................................................................
top = s.depth
doctitle = collectText(mkrc():block(), s.heading_node.spans, s.heading_node, s):compile()
end
end
end
macs('.ds doctitle '..doctitle)
return macs:compile'\n' .. '\n' .. gd:compile()
end
|
|
>
>
>
|
>
>
|
<
>
>
>
>
>
>
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
<
>
>
|
|
|
|
|
|
|
|
|
|
>
>
>
|
>
>
|
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
...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
...
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
...
677
678
679
680
681
682
683
684
685
686
687
|
local lines = function(...)
local s = ss.strac()
for _, v in pairs{...} do s(v) end
return s
end
local function gsan(str)
-- groff does not support UTF-8
local ascii = {}
for p,c in utf8.codes(str) do
if c > 0x7F or c == 0x27 or c == 0x22 or c == 0x5C then
table.insert(ascii, string.format('\\[u%04X]', c))
else
table.insert(ascii, utf8.char(c))
end
end
str = table.concat(ascii)
str = str:gsub('\t','\\t') -- tabs are sometimes syntactically meaningful
return str
end
local gtxt = ss.declare {
ident = 'groff-text';
mk = function() return {
lines = {};
................................................................................
spans = self.spans;
}
end;
blocks = {};
prop = {};
block = function(self)
local sub = self:clone()
sub.parent = self -- needed for blocks that contain blocks
sub.spans = {}
sub.blocks = nil
sub.block = nil
sub.span = function(me, ln)
local p = ss.clone(me.prop)
p.txt = ln
p.block = sub
p.origin = me.origin
table.insert(me.spans, p)
return p
................................................................................
local blockRenderers = {}
blockRenderers['horiz-rule'] = function(rc, b, sec)
rc.prop.margin = { top = 0.3 }
rc.prop.underline = 0.1
end
function blockRenderers.label(rc, b, sec)
if ct.sec.is(b.captions) then
local visDepth = b.captions.depth + (b.origin.docDepth or 0)
local sizes = {36,24,12,8,4,2}
local margins = {0,3}
local dedents = {2.5,1.3,0.8,0.4}
local uls = {3,1.5,0.5,0.25}
rc.prop.dsz = sizes[visDepth] or 10
rc.prop.underline = uls[visDepth]
rc.prop.bold = visDepth > 3
rc.prop.margin = {
top = margins[visDepth] or 1;
bottom = 0.1;
}
rc.prop.vassure = rc.prop.dsz+70;
rc.prop.indent = -(dedents[visDepth] or 0)
rc.prop.chtitle = collectText(rc, b.spans, b.spec):compile()
if visDepth == 1 then
rc.prop.breakBefore = true
end
rs.renderSpans(rc, b.spans, b, sec)
else
ss.bug 'tried to render label for an unknown object type':throw()
end
end
function blockRenderers.paragraph(rc, b, sec)
rs.renderSpans(rc, b.spans, b, sec)
end
function blockRenderers.macro(rc, b, sec)
local rc = rc.parent:clone()
rs.renderDoc(rc, b.doc)
end
function blockRenderers.quote(rc, b, sec)
local rc = rc.parent:clone()
rc.prop.indent = (rc.prop.indent or 0) + 1
local added = rs.renderDoc(rc, b.doc)
-- select last block of last section and increase bottom margin
local ap = added[#added].blocks
ap = ap[#ap].prop
if ap.margin then
if ap.margin.bottom then
ap.margin.bottom = ap.margin.bottom + 1.1
else
ap.margin.bottom = 1.1
end
else
ap.margin = {bottom = 1.1}
end
end
function blockRenderers.table(rc, b, sec)
function rc:begin(g)
g:req 'TS'
local aligns = {}
for i, c in ipairs(b.rows[1]) do
aligns[i] = ({
left = 'l';
center = 'c';
right = 'r';
})[c.align] or 'l'
end
table.insert(aligns, '.')
g:txt(table.concat(aligns, ' ') .. '\n')
local rc_hdr = rc:clone()
rc_hdr.prop.bold = true
for ri, r in ipairs(b.rows) do
for ci, c in ipairs(r) do
local sp = collect(c.header and rc_hdr or rc, c.spans, b, sec)
for si, s in ipairs(sp) do rs.emitSpan(g,s) end
g:raw '\t'
end
if ri ~= #b.rows then g:raw '\n' end
end
g:req 'TE'
end
end
function rs.renderBlock(rc, b, sec, outerBlockRenderContext)
if blockRenderers[b.kind] then
local rcc = rc:block()
blockRenderers[b.kind](rcc, b, sec)
end
end
................................................................................
if ln.dsz then
defer:req('ps +' .. tostring(0 - ln.dsz) .. 'p')
else
defer:req'ps'
end
end
if b.begin then b:begin(gtxt) end
if b.spans then
for i,s in pairs(b.spans) do
rs.emitSpan(gtxt, s)
end
end
if b.complete then b:complete(gtxt) end
if ln.margin then
if ln.margin.bottom then
gtxt:req(string.format('sp %sm', ln.margin.bottom))
end
end
defer:flush()
if not ln.margin then gtxt:brk() end
end
function rs.renderDoc(gctx, doc, ir) ir = ir or {}
for i, sec in ipairs(doc.secorder) do
if sec.kind == 'ordinary' then
local rc = gctx and gctx:clone() or mkrc()
for j, b in ipairs(sec.blocks) do
rs.renderBlock(rc, b, sec)
end
table.insert(ir, {blocks = rc.blocks, src = sec})
end
end
return ir
end
local ir = rs.renderDoc(nil, doc)
local gd = gtxt()
for i, s in ipairs(ir) do
for j, b in ipairs(s.blocks) do
rs.emitBlock(gd,b)
end
end
................................................................................
top = s.depth
doctitle = collectText(mkrc():block(), s.heading_node.spans, s.heading_node, s):compile()
end
end
end
macs('.ds doctitle '..doctitle)
return macs:compile'\n' .. '\n' .. gd:compile() .. '\n'
-- if the document doesn't end with the character \n, groff will bitch
-- and moan in certain circumstances
end
|