1if not modules then modules = { } end modules [ ' lpdf-lmt ' ] = {
2 version = 1 . 001 ,
3 comment = " companion to lpdf-ini.mkiv " ,
4 author = " Hans Hagen, PRAGMA-ADE, Hasselt NL " ,
5 copyright = " PRAGMA ADE / ConTeXt Development Team " ,
6 license = " see context related readme files "
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
34local type , next , unpack , tonumber = type , next , unpack , tonumber
35local char , rep , find = string . char , string . rep , string . find
36local formatters , splitupstring = string . formatters , string . splitup
37local band , extract = bit32 . band , bit32 . extract
38local concat , sortedhash = table . concat , table . sortedhash
39local setmetatableindex = table . setmetatableindex
40local loaddata = io . loaddata
41
42local bpfactor = number . dimenfactors . bp
43
44local md5HEX = md5 . HEX
45local osuuid = os . uuid
46local zlibcompress = ( xzip or zlib ) . compress
47
48local nuts = nodes . nuts
49local tonut = nodes . tonut
50
51local getdata = nuts . getdata
52local getsubtype = nuts . getsubtype
53local getwhd = nuts . getwhd
54local flushlist = nuts . flush_list
55
56local pdfincludeimage = lpdf . includeimage
57local pdfgetfontname = lpdf . getfontname
58local pdfgetfontobjnumber = lpdf . getfontobjnumber
59
60local pdfreserveobject = lpdf . reserveobject
61local pdfpagereference = lpdf . pagereference
62local pdfflushobject = lpdf . flushobject
63local pdfreference = lpdf . reference
64local pdfdictionary = lpdf . dictionary
65local pdfarray = lpdf . array
66local pdfconstant = lpdf . constant
67local pdfflushstreamobject = lpdf . flushstreamobject
68local pdfliteral = lpdf . literal
69
70local pdf_pages = pdfconstant ( " Pages " )
71local pdf_page = pdfconstant ( " Page " )
72local pdf_xobject = pdfconstant ( " XObject " )
73local pdf_form = pdfconstant ( " Form " )
74
75local fonthashes = fonts . hashes
76local characters = fonthashes . characters
77local descriptions = fonthashes . descriptions
78local parameters = fonthashes . parameters
79local properties = fonthashes . properties
80
81local report = logs . reporter ( " backend " )
82
83
84
85local pdf_h , pdf_v
86local need_tm , need_tf , cur_tmrx , cur_factor , cur_f , cur_e
87local need_width , need_mode , done_width , done_mode
88local mode
89local f_pdf_cur , f_pdf , fs_cur , fs , f_cur
90local tj_delta , cw
91local usedfonts , usedxforms , usedximages , usedxgroups
92local getxformname , getximagename
93local boundingbox , shippingmode , objectnumber
94local tmrx , tmry , tmsx , tmsy , tmtx , tmty
95local cmrx , cmry , cmsx , cmsy , cmtx , cmty
96
97local function usefont ( t , k )
98 local v = pdfgetfontname ( k )
99 t [ k ] = v
100 return v
101end
102
103local function reset_variables ( specification )
104 pdf_h , pdf_v = 0 , 0
105 cmrx , cmry = 1 . 0 , 1 . 0
106 cmsx , cmsy = 0 . 0 , 0 . 0
107 cmtx , cmty = 0 . 0 , 0 . 0
108 tmrx , tmry = 1 . 0 , 1 . 0
109 tmsx , tmsy = 0 . 0 , 0 . 0
110 tmtx , tmty = 0 . 0 , 0 . 0
111 need_tm = false
112 need_tf = false
113 need_width = 0
114 need_mode = 0
115 done_width = false
116 done_mode = false
117 mode = " page "
118 shippingmode = specification . shippingmode
119 objectnumber = specification . objectnumber
120 cur_tmrx = 0 . 0
121 f_cur = 0
122 f_pdf_cur = 0
123 f_pdf = 0
124 fs_cur = 0
125 fs = 0
126 cur_factor = 0
127 cur_f = false
128 cur_e = false
129 tj_delta = 0 . 0
130 cw = 0 . 0
131 usedfonts = setmetatableindex ( usefont )
132 usedxforms = { }
133 usedximages = { }
134
135 boundingbox = specification . boundingbox
136end
137
138
139
140local buffer = lua . newtable ( 1024 , 0 )
141local b = 0
142
143local function reset_buffer ( )
144 b = 0
145end
146
147
148
149local fontcharacters
150local fontdescriptions
151local fontparameters
152local fontproperties
153local usedcharacters = setmetatableindex ( " table " )
154local pdfcharacters
155
156local horizontalmode = true
157
158local scalefactor = 1
159local threshold = 655360
160local tjfactor = 100 / 65536
161
162lpdf . usedcharacters = usedcharacters
163
164local function updatefontstate ( font )
165 fontcharacters = characters [ font ]
166 fontdescriptions = descriptions [ font ]
167 fontparameters = parameters [ font ]
168 fontproperties = properties [ font ]
169 local size = fontparameters . size
170 local designsize = fontparameters . designsize or size
171 pdfcharacters = usedcharacters [ font ]
172 horizontalmode = fontparameters . writingmode ~ = " vertical "
173
174 scalefactor = ( designsize / size ) * tjfactor
175 local fthreshold = fontproperties . threshold
176 threshold = ( fthreshold and ( size * fthreshold / 100 ) ) or 655360
177end
178
179
180
181local f_cm = formatters [ " %.6N %.6N %.6N %.6N %.6N %.6N cm " ]
182local f_tm = formatters [ " %.6N %.6N %.6N %.6N %.6N %.6N Tm " ]
183
184local saved_text_pos_v = 0
185local saved_text_pos_h = 0
186
187local function begin_text ( )
188 saved_text_pos_h = pdf_h
189 saved_text_pos_v = pdf_v
190 b = b + 1 ; buffer [ b ] = " BT "
191 need_tf = true
192 need_width = 0
193 need_mode = 0
194 mode = " text "
195end
196
197local function end_text ( )
198 if done_width then
199 b = b + 1 ; buffer [ b ] = " 0 w "
200 done_width = false
201 end
202 if done_mode then
203 b = b + 1 ; buffer [ b ] = " 0 Tr "
204 done_mode = false
205 end
206 b = b + 1 ; buffer [ b ] = " ET "
207 pdf_h = saved_text_pos_h
208 pdf_v = saved_text_pos_v
209 mode = " page "
210end
211
212local saved_chararray_pos_h
213local saved_chararray_pos_v
214
215local saved_b = 0
216
217local function begin_chararray ( )
218 saved_chararray_pos_h = pdf_h
219 saved_chararray_pos_v = pdf_v
220 cw = horizontalmode and saved_chararray_pos_h or - saved_chararray_pos_v
221 tj_delta = 0
222 saved_b = b
223 b = b + 1 ; buffer [ b ] = " [ "
224 mode = " chararray "
225end
226
227local function end_chararray ( )
228 b = b + 1 ; buffer [ b ] = " ] TJ "
229 buffer [ saved_b ] = concat ( buffer , " " , saved_b , b )
230 b = saved_b
231 pdf_h = saved_chararray_pos_h
232 pdf_v = saved_chararray_pos_v
233 mode = " text "
234end
235
236local function begin_charmode ( )
237 b = b + 1 ; buffer [ b ] = " < "
238 mode = " char "
239end
240
241local function end_charmode ( )
242 b = b + 1 ; buffer [ b ] = " > "
243 mode = " chararray "
244end
245
246local function calc_pdfpos ( h , v )
247
248 if mode = = " page " then
249 cmtx = h - pdf_h
250 cmty = v - pdf_v
251 return h ~ = pdf_h or v ~ = pdf_v
252 elseif mode = = " text " then
253 tmtx = h - saved_text_pos_h
254 tmty = v - saved_text_pos_v
255 return h ~ = pdf_h or v ~ = pdf_v
256 elseif horizontalmode then
257 tmty = v - saved_text_pos_v
258 tj_delta = cw - h
259 return tj_delta ~ = 0 or v ~ = pdf_v
260 else
261 tmtx = h - saved_text_pos_h
262 tj_delta = cw + v
263 return tj_delta ~ = 0 or h ~ = pdf_h
264 end
265end
266
267local function pdf_set_pos ( h , v )
268 local move = calc_pdfpos ( h , v )
269 if move then
270 b = b + 1 ; buffer [ b ] = f_cm ( cmrx , cmsx , cmsy , cmry , cmtx * bpfactor , cmty * bpfactor )
271 pdf_h = pdf_h + cmtx
272 pdf_v = pdf_v + cmty
273 end
274end
275
276local function pdf_reset_pos ( )
277 if mode = = " page " then
278 cmtx = - pdf_h
279 cmty = - pdf_v
280 if pdf_h = = 0 and pdf_v = = 0 then
281 return
282 end
283 elseif mode = = " text " then
284 tmtx = - saved_text_pos_h
285 tmty = - saved_text_pos_v
286 if pdf_h = = 0 and pdf_v = = 0 then
287 return
288 end
289 elseif horizontalmode then
290 tmty = - saved_text_pos_v
291 tj_delta = cw
292 if tj_delta = = 0 and pdf_v = = 0 then
293 return
294 end
295 else
296 tmtx = - saved_text_pos_h
297 tj_delta = cw
298 if tj_delta = = 0 and pdf_h = = 0 then
299 return
300 end
301 end
302 b = b + 1 ; buffer [ b ] = f_cm ( cmrx , cmsx , cmsy , cmry , cmtx * bpfactor , cmty * bpfactor )
303 pdf_h = pdf_h + cmtx
304 pdf_v = pdf_v + cmty
305end
306
307local function pdf_set_pos_temp ( h , v )
308 local move = calc_pdfpos ( h , v )
309 if move then
310 b = b + 1 ; buffer [ b ] = f_cm ( cmrx , cmsx , cmsy , cmry , cmtx * bpfactor , cmty * bpfactor )
311 end
312end
313
314
315
316local function pdf_end_string_nl ( )
317 if mode = = " char " then
318 end_charmode ( )
319 return end_chararray ( )
320 elseif mode = = " chararray " then
321 return end_chararray ( )
322 end
323end
324
325local function pdf_goto_textmode ( )
326 if mode = = " page " then
327 pdf_reset_pos ( )
328 return begin_text ( )
329 elseif mode ~ = " text " then
330 if mode = = " char " then
331 end_charmode ( )
332 return end_chararray ( )
333 else
334 return end_chararray ( )
335 end
336 end
337end
338
339local function pdf_goto_pagemode ( )
340 if mode ~ = " page " then
341 if mode = = " char " then
342 end_charmode ( )
343 end_chararray ( )
344 return end_text ( )
345 elseif mode = = " chararray " then
346 end_chararray ( )
347 return end_text ( )
348 elseif mode = = " text " then
349 return end_text ( )
350 end
351 end
352end
353
354local function pdf_goto_fontmode ( )
355 if mode = = " char " then
356 end_charmode ( )
357 end_chararray ( )
358 end_text ( )
359 elseif mode = = " chararray " then
360 end_chararray ( )
361 end_text ( )
362 elseif mode = = " text " then
363 end_text ( )
364 end
365 pdf_reset_pos ( )
366 mode = " page "
367end
368
369
370
371local flushcharacter do
372
373 local round = math . round
374
375
376
377
378
379 local naturalwidth = nil
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 local naturalwidths = setmetatableindex ( function ( t , font )
411 local d = descriptions [ font ]
412 local c = characters [ font ]
413 local f = parameters [ font ] . hfactor
414 local v = setmetatableindex ( function ( t , char )
415 local w
416 local e = c [ char ]
417 if e then
418 w = e . width or 0
419 end
420 if not w then
421 e = d and d [ char ]
422 if e then
423 w = e . width
424 if w then
425 w = w * f
426 end
427 end
428 end
429 if not w then
430 w = 0
431 end
432 t [ char ] = w
433 return w
434 end )
435 t [ font ] = v
436 return v
437 end )
438
439 local function setup_fontparameters ( font , factor , f , e )
440 local slant = fontparameters . slantfactor or 0
441 local extend = fontparameters . extendfactor or 1
442 local squeeze = fontparameters . squeezefactor or 1
443 local expand = 1 + factor / 1000000
444 local format = fontproperties . format
445 if e then
446 extend = extend * e
447 end
448 tmrx = expand * extend
449 tmsy = slant
450 tmry = squeeze
451 need_width = fontparameters . width or 0
452 need_mode = fontparameters . mode or 0
453 f_cur = font
454 f_pdf = usedfonts [ font ]
455 cur_factor = factor
456 cur_f = f
457 cur_e = e
458 tj_delta = 0
459 fs = fontparameters . size * bpfactor
460 if f then
461 fs = fs * f
462 end
463
464 if format = = " opentype " or format = = " type1 " then
465 fs = fs * 1000 / fontparameters . units
466 end
467
468 naturalwidth = naturalwidths [ font ]
469 end
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
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514 local f_width = formatters [ " %.6N w " ]
515 local f_mode = formatters [ " %i Tr " ]
516 local f_font = formatters [ " /F%i %.6N Tf " ]
517
518 local s_width = " 0 w "
519 local s_mode = " 0 Tr "
520
521 local function set_font ( )
522
523 if need_width ~ = 0 then
524 b = b + 1 ; buffer [ b ] = f_width ( bpfactor * need_width / 1000 )
525 done_width = true
526 elseif done_width then
527 b = b + 1 ; buffer [ b ] = s_width
528 done_width = false
529 end
530
531 if need_mode ~ = 0 then
532 b = b + 1 ; buffer [ b ] = f_mode ( need_mode )
533 done_mode = true
534 elseif done_mode then
535 b = b + 1 ; buffer [ b ] = s_mode
536 done_mode = false
537 end
538 b = b + 1 ; buffer [ b ] = f_font ( f_pdf , fs )
539 f_pdf_cur = f_pdf
540 fs_cur = fs
541 need_tf = false
542 need_tm = true
543 end
544
545 local function set_textmatrix ( h , v )
546 local move = calc_pdfpos ( h , v )
547 if need_tm or move then
548 b = b + 1 ; buffer [ b ] = f_tm ( tmrx , tmsx , tmsy , tmry , tmtx * bpfactor , tmty * bpfactor )
549 pdf_h = saved_text_pos_h + tmtx
550 pdf_v = saved_text_pos_v + tmty
551 need_tm = false
552 end
553 cur_tmrx = tmrx
554 end
555
556 local f_hex_4 = formatters [ " %04X " ]
557 local f_hex_2 = formatters [ " %02X " ]
558
559 local h_hex_4 = setmetatableindex ( function ( t , k )
560 if k < 256 then
561
562 for i = 0 , 255 do
563 t [ i ] = f_hex_4 ( i )
564 end
565 return t [ k ]
566 else
567 local v = f_hex_4 ( k )
568 t [ k ] = v
569 return v
570 end
571 end )
572 local h_hex_2 = setmetatableindex ( function ( t , k )
573 local v = k < 256 and f_hex_2 ( k ) or " 00 "
574 t [ k ] = v
575 return v
576 end )
577
578 local trace_threshold = false trackers . register ( " backends.pdf.threshold " , function ( v ) trace_threshold = v end )
579
580 flushcharacter = function ( current , pos_h , pos_v , pos_r , font , char , data , f , e , factor )
581 if need_tf or font ~ = f_cur or f_pdf ~ = f_pdf_cur or fs ~ = fs_cur or mode = = " page " then
582 pdf_goto_textmode ( )
583 setup_fontparameters ( font , factor , f , e )
584 set_font ( )
585 elseif cur_tmrx ~ = tmrx or cur_factor ~ = factor or cur_f ~ = f or cur_e ~ = e then
586 setup_fontparameters ( font , factor , f , e )
587 need_tm = true
588 end
589 local move = calc_pdfpos ( pos_h , pos_v )
590
591
592
593
594
595
596
597
598 if move or need_tm then
599 if not need_tm then
600 if horizontalmode then
601 if ( saved_text_pos_v + tmty ) ~ = pdf_v then
602 need_tm = true
603 elseif tj_delta > = threshold or tj_delta < = - threshold then
604 need_tm = true
605 end
606 else
607 if ( saved_text_pos_h + tmtx ) ~ = pdf_h then
608 need_tm = true
609 elseif tj_delta > = threshold or tj_delta < = - threshold then
610 need_tm = true
611 end
612 end
613 end
614 if need_tm then
615 pdf_goto_textmode ( )
616 set_textmatrix ( pos_h , pos_v )
617 begin_chararray ( )
618 move = calc_pdfpos ( pos_h , pos_v )
619 end
620 if move then
621 local d = tj_delta * scalefactor
622 if d < = -0 . 5 or d > = 0 . 5 then
623 if mode = = " char " then
624 end_charmode ( )
625 end
626 b = b + 1 ; buffer [ b ] = round ( d )
627 end
628 cw = cw - tj_delta
629 end
630 end
631
632 if mode = = " chararray " then
633 begin_charmode ( )
634 end
635
636 cw = cw + naturalwidth [ char ] * tmrx
637
638 local index = data . index or char
639
640 b = b + 1 ; buffer [ b ] = font > 0 and h_hex_4 [ index ] or h_hex_2 [ index ]
641
642 if not pdfcharacters [ index ] then
643 pdfcharacters [ index ] = true
644 end
645
646 end
647
648 flushfontchar = function ( font , char , data )
649 local dummy = usedfonts [ font ]
650 local index = data . index or char
651 if not pdfcharacters [ index ] then
652 pdfcharacters [ index ] = true
653 end
654 return dummy
655 end
656
657end
658
659
660
661local flushliteral do
662
663 local nodeproperties = nodes . properties . data
664 local literalvalues = nodes . literalvalues
665
666 local originliteral_code = literalvalues . origin
667 local pageliteral_code = literalvalues . page
668 local alwaysliteral_code = literalvalues . always
669 local rawliteral_code = literalvalues . raw
670 local textliteral_code = literalvalues . text
671 local fontliteral_code = literalvalues . font
672
673 flushliteral = function ( current , pos_h , pos_v , mode , str )
674 if mode then
675 if not str then
676 mode , str = originliteral_code , mode
677 elseif mode = = " mode " then
678 mode = literalvalues [ str ]
679 if mode = = originliteral_code then
680 pdf_goto_pagemode ( )
681 pdf_set_pos ( pos_h , pos_v )
682 elseif mode = = pageliteral_code then
683 pdf_goto_pagemode ( )
684 elseif mode = = textliteral_code then
685 pdf_goto_textmode ( )
686 elseif mode = = fontliteral_code then
687 pdf_goto_fontmode ( )
688 elseif mode = = alwaysliteral_code then
689 pdf_end_string_nl ( )
690 need_tm = true
691 elseif mode = = rawliteral_code then
692 pdf_end_string_nl ( )
693 end
694 return
695 else
696 mode = literalvalues [ mode ]
697 end
698 else
699 local p = nodeproperties [ current ]
700 if p then
701 str = p . data
702 mode = p . mode
703 else
704 str , mode = getdata ( current )
705 end
706 end
707 if str and str ~ = " " then
708 if mode = = originliteral_code then
709 pdf_goto_pagemode ( )
710 pdf_set_pos ( pos_h , pos_v )
711 elseif mode = = pageliteral_code then
712 pdf_goto_pagemode ( )
713 elseif mode = = textliteral_code then
714 pdf_goto_textmode ( )
715 elseif mode = = fontliteral_code then
716 pdf_goto_fontmode ( )
717 elseif mode = = alwaysliteral_code then
718 pdf_end_string_nl ( )
719 need_tm = true
720 elseif mode = = rawliteral_code then
721 pdf_end_string_nl ( )
722 else
723 report ( " check literal " )
724 pdf_goto_pagemode ( )
725 pdf_set_pos ( pos_h , pos_v )
726 end
727 b = b + 1 ; buffer [ b ] = str
728 end
729 end
730
731 updaters . register ( " backend.update.pdf " , function ( )
732 function pdf . print ( mode , str )
733 if str then
734 mode = literalvalues [ mode ]
735 else
736 mode , str = originliteral_code , mode
737 end
738 if str and str ~ = " " then
739 if mode = = originliteral_code then
740 pdf_goto_pagemode ( )
741
742 elseif mode = = pageliteral_code then
743 pdf_goto_pagemode ( )
744 elseif mode = = textliteral_code then
745 pdf_goto_textmode ( )
746 elseif mode = = fontliteral_code then
747 pdf_goto_fontmode ( )
748 elseif mode = = alwaysliteral_code then
749 pdf_end_string_nl ( )
750 need_tm = true
751 elseif mode = = rawliteral_code then
752 pdf_end_string_nl ( )
753 else
754 pdf_goto_pagemode ( )
755
756 end
757 b = b + 1 ; buffer [ b ] = str
758 end
759 end
760 end )
761
762end
763
764
765
766local flushsave , flushrestore , flushsetmatrix do
767
768 local matrices = { }
769 local positions = { }
770 local nofpositions = 0
771 local nofmatrices = 0
772
773 local f_matrix = formatters [ " %s 0 0 cm " ]
774
775 flushsave = function ( current , pos_h , pos_v )
776 nofpositions = nofpositions + 1
777 positions [ nofpositions ] = { pos_h , pos_v , nofmatrices }
778 pdf_goto_pagemode ( )
779 pdf_set_pos ( pos_h , pos_v )
780 b = b + 1 ; buffer [ b ] = " q "
781 end
782
783 flushrestore = function ( current , pos_h , pos_v )
784 if nofpositions < 1 then
785 return
786 end
787 local t = positions [ nofpositions ]
788
789
790 if shippingmode = = " page " then
791 nofmatrices = t [ 3 ]
792 end
793 pdf_goto_pagemode ( )
794 pdf_set_pos ( pos_h , pos_v )
795 b = b + 1 ; buffer [ b ] = " Q "
796 nofpositions = nofpositions - 1
797 end
798
799 local function pdf_set_matrix ( str , pos_h , pos_v )
800 if shippingmode = = " page " then
801 local rx , sx , sy , ry = splitupstring ( str , " " )
802 if rx and ry and sx and ry then
803 rx , sx , sy , ry = tonumber ( rx ) , tonumber ( sx ) , tonumber ( sy ) , tonumber ( ry )
804 local tx = pos_h * ( 1 - rx ) - pos_v * sy
805 local ty = pos_v * ( 1 - ry ) - pos_h * sx
806 if nofmatrices > 0 then
807 local t = matrices [ nofmatrices ]
808 local r_x , s_x , s_y , r_y , te , tf = t [ 1 ] , t [ 2 ] , t [ 3 ] , t [ 4 ] , t [ 5 ] , t [ 6 ]
809 rx , sx = rx * r_x + sx * s_y , rx * s_x + sx * r_y
810 sy , ry = sy * r_x + ry * s_y , sy * s_x + ry * r_y
811 tx , ty = tx * r_x + ty * s_y , tx * s_x + ty * r_y
812 end
813 nofmatrices = nofmatrices + 1
814 matrices [ nofmatrices ] = { rx , sx , sy , ry , tx , ty }
815 end
816 end
817 end
818
819 local nodeproperties = nodes . properties . data
820
821 flushsetmatrix = function ( current , pos_h , pos_v )
822 local str
823 if type ( current ) = = " string " then
824 str = current
825 else
826 local p = nodeproperties [ current ]
827 if p then
828 str = p . matrix
829 else
830 str = getdata ( current )
831 end
832 end
833 if str and str ~ = " " then
834 pdf_set_matrix ( str , pos_h , pos_v )
835 pdf_goto_pagemode ( )
836 pdf_set_pos ( pos_h , pos_v )
837 b = b + 1 ; buffer [ b ] = f_matrix ( str )
838 end
839 end
840
841 do
842
843 local function hasmatrix ( )
844 return nofmatrices > 0
845 end
846
847 local function getmatrix ( )
848 if nofmatrices > 0 then
849 return unpack ( matrices [ nofmatrices ] )
850 else
851 return 1 , 0 , 0 , 1 , 0 , 0
852 end
853 end
854
855 updaters . register ( " backend.update.pdf " , function ( )
856 pdf . hasmatrix = hasmatrix
857 pdf . getmatrix = getmatrix
858 end )
859
860 end
861
862 pushorientation = function ( orientation , pos_h , pos_v , pos_r )
863 pdf_goto_pagemode ( )
864 pdf_set_pos ( pos_h , pos_v )
865 b = b + 1 ; buffer [ b ] = " q "
866 if orientation = = 1 then
867 b = b + 1 ; buffer [ b ] = " 0 -1 1 0 0 0 cm "
868 elseif orientation = = 2 then
869 b = b + 1 ; buffer [ b ] = " -1 0 0 -1 0 0 cm "
870 elseif orientation = = 3 then
871 b = b + 1 ; buffer [ b ] = " 0 1 -1 0 0 0 cm "
872 end
873 end
874
875 poporientation = function ( orientation , pos_h , pos_v , pos_r )
876 pdf_goto_pagemode ( )
877 pdf_set_pos ( pos_h , pos_v )
878 b = b + 1 ; buffer [ b ] = " Q "
879 end
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896end
897
898
899
900local flushedxforms = { }
901local localconverter = nil
902
903local flushrule , flushsimplerule , flushimage , flushgroup do
904
905 local rulecodes = nodes . rulecodes
906 local newrule = nodes . pool . rule
907
908 local setprop = nuts . setprop
909 local getprop = nuts . getprop
910
911 local normalrule_code = rulecodes . normal
912 local boxrule_code = rulecodes . box
913 local imagerule_code = rulecodes . image
914 local emptyrule_code = rulecodes . empty
915 local userrule_code = rulecodes . user
916 local overrule_code = rulecodes . over
917 local underrule_code = rulecodes . under
918 local fractionrule_code = rulecodes . fraction
919 local radicalrule_code = rulecodes . radical
920 local outlinerule_code = rulecodes . outline
921
922 local rule_callback = callbacks . functions . process_rule
923
924 local f_fm = formatters [ " /Fm%d Do " ]
925 local f_im = formatters [ " /Im%d Do " ]
926 local f_gr = formatters [ " /Gp%d Do " ]
927
928 local s_b = " q "
929 local s_e = " Q "
930
931 local f_v = formatters [ " [] 0 d 0 J %.6N w 0 0 m %.6N 0 l S " ]
932 local f_h = formatters [ " [] 0 d 0 J %.6N w 0 0 m 0 %.6N l S " ]
933
934 local f_f = formatters [ " 0 0 %.6N %.6N re f " ]
935 local f_o = formatters [ " [] 0 d 0 J 0 0 %.6N %.6N re S " ]
936 local f_w = formatters [ " [] 0 d 0 J %.6N w 0 0 %.6N %.6N re S " ]
937
938
939
940 local boxresources , n = { } , 0
941
942 getxformname = function ( index )
943 local l = boxresources [ index ]
944 if l then
945 return l . name
946 else
947 report ( " no box resource %S " , index )
948 end
949 end
950
951 updaters . register ( " backend.update.pdf " , function ( )
952 pdf . getxformname = getxformname
953 end )
954
955 local function saveboxresource ( box , attributes , resources , immediate , kind , margin )
956 n = n + 1
957 local immediate = true
958 local margin = margin or 0
959 local objnum = pdfreserveobject ( )
960 local list = tonut ( type ( box ) = = " number " and tex . takebox ( box ) or box )
961
962 local width , height , depth = getwhd ( list )
963
964 local l = {
965 width = width ,
966 height = height ,
967 depth = depth ,
968 margin = margin ,
969 attributes = attributes ,
970 resources = resources ,
971 list = nil ,
972 type = kind ,
973 name = n ,
974 index = objnum ,
975 objnum = objnum ,
976 }
977 boxresources [ objnum ] = l
978 if immediate then
979 localconverter ( list , " xform " , objnum , l )
980 flushedxforms [ objnum ] = { true , objnum }
981 flushlist ( list )
982 else
983 l . list = list
984 end
985 return objnum
986 end
987
988 local function useboxresource ( index , wd , ht , dp )
989 local l = boxresources [ index ]
990 if l then
991 if wd or ht or dp then
992 wd , ht , dp = wd or 0 , ht or 0 , dp or 0
993 else
994 wd , ht , dp = l . width , l . height , l . depth
995 end
996 local rule = newrule ( wd , ht , dp )
997 rule . subtype = boxrule_code
998 setprop ( tonut ( rule ) , " index " , index )
999 return rule , wd , ht , dp
1000 else
1001 report ( " no box resource %S " , index )
1002 end
1003 end
1004
1005 local function getboxresourcedimensions ( index )
1006 local l = boxresources [ index ]
1007 if l then
1008 return l . width , l . height , l . depth , l . margin
1009 else
1010 report ( " no box resource %S " , index )
1011 end
1012 end
1013
1014 local function getboxresourcebox ( index )
1015 local l = boxresources [ index ]
1016 if l then
1017 return l . list
1018 end
1019 end
1020
1021 updaters . register ( " backend.update.tex " , function ( )
1022 tex . saveboxresource = saveboxresource
1023 tex . useboxresource = useboxresource
1024 tex . getboxresourcedimensions = getboxresourcedimensions
1025 tex . getboxresourcebox = getboxresourcebox
1026 end )
1027
1028
1029
1030
1031 local function flushpdfxform ( current , pos_h , pos_v , pos_r , size_h , size_v )
1032
1033 local objnum = getprop ( current , " index " )
1034 local name = getxformname ( objnum )
1035 local info = flushedxforms [ objnum ]
1036 local r = boxresources [ objnum ]
1037 if not info then
1038 info = { false , objnum }
1039 flushedxforms [ objnum ] = info
1040 end
1041 local wd , ht , dp = getboxresourcedimensions ( objnum )
1042
1043
1044 local htdp = ht + dp
1045 if wd = = 0 or size_h = = 0 or htdp = = 0 or size_v = = 0 then
1046 return
1047 end
1048
1049 local rx , ry = 1 , 1
1050 if wd ~ = size_h or htdp ~ = size_v then
1051 rx = size_h / wd
1052 ry = size_v / htdp
1053 end
1054
1055 usedxforms [ objnum ] = true
1056 pdf_goto_pagemode ( )
1057 calc_pdfpos ( pos_h , pos_v )
1058 tx = cmtx * bpfactor
1059 ty = cmty * bpfactor
1060 b = b + 1 ; buffer [ b ] = s_b
1061 b = b + 1 ; buffer [ b ] = f_cm ( rx , 0 , 0 , ry , tx , ty )
1062 b = b + 1 ; buffer [ b ] = f_fm ( name )
1063 b = b + 1 ; buffer [ b ] = s_e
1064 end
1065
1066
1067
1068 local imagetypes = images . types
1069 local img_none = imagetypes . none
1070 local img_pdf = imagetypes . pdf
1071 local img_stream = imagetypes . stream
1072 local img_memstream = imagetypes . memstream
1073
1074 local one_bp = 65536 * bpfactor
1075
1076 local imageresources , n = { } , 0
1077
1078 getximagename = function ( index )
1079 local l = imageresources [ index ]
1080 if l then
1081 return l . name
1082 else
1083 report ( " no image resource %S " , index )
1084 end
1085 end
1086
1087 updaters . register ( " backend.update.pdf " , function ( )
1088 pdf . getximagename = getximagename
1089 end )
1090
1091
1092
1093
1094
1095 usedxgroups = { }
1096 local groups = 0
1097 local group = nil
1098
1099 flushgroup = function ( content , bbox )
1100 if not group then
1101 group = pdfdictionary {
1102 Type = pdfconstant ( " Group " ) ,
1103 S = pdfconstant ( " Transparency " ) ,
1104 }
1105 end
1106 local wrapper = pdfdictionary {
1107 Type = pdf_xobject ,
1108 Subtype = pdf_form ,
1109 FormType = 1 ,
1110 Group = group ,
1111 BBox = pdfarray ( bbox ) ,
1112 Resources = lpdf . collectedresources { serialize = false } ,
1113 }
1114 local objnum = pdfflushstreamobject ( content , wrapper , false )
1115 groups = groups + 1
1116 usedxgroups [ groups ] = objnum
1117 return f_gr ( groups )
1118 end
1119
1120 lpdf . flushgroup = flushgroup
1121
1122
1123
1124 local function flushpdfximage ( current , pos_h , pos_v , pos_r , size_h , size_v )
1125
1126 local width ,
1127 height ,
1128 depth = getwhd ( current )
1129 local total = height + depth
1130 local transform = getprop ( current , " transform " ) or 0
1131 local index = getprop ( current , " index " ) or 0
1132 local kind ,
1133 xorigin ,
1134 yorigin ,
1135 xsize ,
1136 ysize ,
1137 rotation ,
1138 objnum ,
1139 groupref = pdfincludeimage ( index )
1140
1141 if not kind then
1142 report ( " invalid image %S " , index )
1143 return
1144 end
1145
1146 local rx , sx , sy , ry , tx , ty = 1 , 0 , 0 , 1 , 0 , 0
1147
1148
1149
1150 if kind = = img_pdf or kind = = img_stream or kind = = img_memstream then
1151 rx , ry , tx , ty = 1 / xsize , 1 / ysize , xorigin / xsize , yorigin / ysize
1152 else
1153
1154
1155
1156
1157
1158 rx , ry = bpfactor , bpfactor
1159 end
1160
1161 if band ( transform , 7 ) > 3 then
1162
1163 rx , tx = - rx , - tx
1164 end
1165 local t = band ( transform + rotation , 3 )
1166 if t = = 0 then
1167
1168 elseif t = = 1 then
1169
1170 rx , sx , sy , ry , tx , ty = 0 , rx , - ry , 0 , - ty , tx
1171 elseif t = = 2 then
1172
1173 rx , ry , tx , ty = - rx , - ry , - tx , - ty
1174 elseif t = = 3 then
1175
1176 rx , sx , sy , ry , tx , ty = 0 , - rx , ry , 0 , ty , - tx
1177 end
1178
1179 rx = rx * width
1180 sx = sx * total
1181 sy = sy * width
1182 ry = ry * total
1183 tx = pos_h - tx * width
1184 ty = pos_v - ty * total
1185
1186 local t = transform + rotation
1187
1188 if band ( transform , 7 ) > 3 then
1189 t = t + 1
1190 end
1191
1192 t = band ( t , 3 )
1193
1194 if t = = 0 then
1195
1196 elseif t = = 1 then
1197
1198 tx = tx + width
1199 elseif t = = 2 then
1200
1201 tx = tx + width
1202 ty = ty + total
1203 elseif t = = 3 then
1204
1205 ty = ty + total
1206 end
1207
1208
1209
1210
1211
1212
1213
1214 usedximages [ index ] = objnum
1215
1216 pdf_goto_pagemode ( )
1217
1218 calc_pdfpos ( tx , ty )
1219
1220 tx = cmtx * bpfactor
1221 ty = cmty * bpfactor
1222
1223 b = b + 1 ; buffer [ b ] = s_b
1224 b = b + 1 ; buffer [ b ] = f_cm ( rx , sx , sy , ry , tx , ty )
1225 b = b + 1 ; buffer [ b ] = f_im ( index )
1226 b = b + 1 ; buffer [ b ] = s_e
1227 end
1228
1229 flushimage = function ( index , width , height , depth , pos_h , pos_v )
1230
1231
1232
1233 local total = height + depth
1234 local kind ,
1235 xorigin , yorigin ,
1236 xsize , ysize ,
1237 rotation ,
1238 objnum ,
1239 groupref = pdfincludeimage ( index )
1240
1241 local rx = width / xsize
1242 local sx = 0
1243 local sy = 0
1244 local ry = total / ysize
1245 local tx = pos_h
1246
1247
1248 local ty = pos_v
1249
1250 usedximages [ index ] = objnum
1251
1252 pdf_goto_pagemode ( )
1253
1254 calc_pdfpos ( tx , ty )
1255
1256 tx = cmtx * bpfactor
1257 ty = cmty * bpfactor
1258
1259 b = b + 1 ; buffer [ b ] = s_b
1260 b = b + 1 ; buffer [ b ] = f_cm ( rx , sx , sy , ry , tx , ty )
1261 b = b + 1 ; buffer [ b ] = f_im ( index )
1262 b = b + 1 ; buffer [ b ] = s_e
1263 end
1264
1265
1266
1267
1268 flushrule = function ( current , pos_h , pos_v , pos_r , size_h , size_v , subtype )
1269
1270 if subtype = = emptyrule_code then
1271 return
1272 elseif subtype = = boxrule_code then
1273 return flushpdfxform ( current , pos_h , pos_v , pos_r , size_h , size_v )
1274 elseif subtype = = imagerule_code then
1275 return flushpdfximage ( current , pos_h , pos_v , pos_r , size_h , size_v )
1276 end
1277 if subtype = = userrule_code or subtype > = overrule_code and subtype < = radicalrule_code then
1278 pdf_goto_pagemode ( )
1279 b = b + 1 ; buffer [ b ] = s_b
1280 pdf_set_pos_temp ( pos_h , pos_v )
1281 rule_callback ( current , size_h , size_v , pos_r )
1282 b = b + 1 ; buffer [ b ] = s_e
1283 return
1284 end
1285
1286 pdf_goto_pagemode ( )
1287
1288
1289
1290 b = b + 1 ; buffer [ b ] = s_b
1291
1292 local dim_h = size_h * bpfactor
1293 local dim_v = size_v * bpfactor
1294 local rule
1295
1296 if dim_v < = one_bp then
1297 pdf_set_pos_temp ( pos_h , pos_v + 0 . 5 * size_v )
1298 rule = f_v ( dim_v , dim_h )
1299 elseif dim_h < = one_bp then
1300 pdf_set_pos_temp ( pos_h + 0 . 5 * size_h , pos_v )
1301 rule = f_h ( dim_h , dim_v )
1302 else
1303 pdf_set_pos_temp ( pos_h , pos_v )
1304 if subtype = = outlinerule_code then
1305 local linewidth = getdata ( current )
1306 if linewidth > 0 then
1307 rule = f_w ( linewidth * bpfactor , dim_h , dim_v )
1308 else
1309 rule = f_o ( dim_h , dim_v )
1310 end
1311 else
1312 rule = f_f ( dim_h , dim_v )
1313 end
1314 end
1315
1316 b = b + 1 ; buffer [ b ] = rule
1317 b = b + 1 ; buffer [ b ] = s_e
1318
1319
1320
1321
1322 end
1323
1324 flushsimplerule = function ( pos_h , pos_v , pos_r , size_h , size_v )
1325 pdf_goto_pagemode ( )
1326
1327 b = b + 1 ; buffer [ b ] = s_b
1328
1329 local dim_h = size_h * bpfactor
1330 local dim_v = size_v * bpfactor
1331 local rule
1332
1333 if dim_v < = one_bp then
1334 pdf_set_pos_temp ( pos_h , pos_v + 0 . 5 * size_v )
1335 rule = f_v ( dim_v , dim_h )
1336 elseif dim_h < = one_bp then
1337 pdf_set_pos_temp ( pos_h + 0 . 5 * size_h , pos_v )
1338 rule = f_h ( dim_h , dim_v )
1339 else
1340 pdf_set_pos_temp ( pos_h , pos_v )
1341 rule = f_f ( dim_h , dim_v )
1342 end
1343
1344 b = b + 1 ; buffer [ b ] = rule
1345 b = b + 1 ; buffer [ b ] = s_e
1346 end
1347
1348end
1349
1350
1351
1352local wrapup , registerpage do
1353
1354 local pages = { }
1355 local maxkids = 10
1356 local nofpages = 0
1357 local pagetag = " unset "
1358
1359 registerpage = function ( object )
1360 nofpages = nofpages + 1
1361 local objnum = pdfpagereference ( nofpages )
1362 pages [ nofpages ] = {
1363 page = nofpages ,
1364 objnum = objnum ,
1365 object = object ,
1366 tag = pagetag ,
1367 }
1368 end
1369
1370 function lpdf . setpagetag ( tag )
1371 pagetag = tag or " unset "
1372 end
1373
1374 function lpdf . getnofpages ( )
1375 return nofpages
1376 end
1377
1378 function lpdf . getpagetags ( )
1379 local list = { }
1380 for i = 1 , nofpages do
1381 list [ i ] = pages [ i ] . tag
1382 end
1383 return list
1384 end
1385
1386 function lpdf . setpageorder ( mapping )
1387
1388 local list = table . sortedkeys ( mapping )
1389 local n = # list
1390 if n = = nofpages then
1391 local done = { }
1392 local hash = { }
1393 for i = 1 , n do
1394 local order = mapping [ list [ i ] ]
1395 if hash [ order ] then
1396 report ( " invalid page order, duplicate entry %i " , order )
1397 return
1398 elseif order < 1 or order > nofpages then
1399 report ( " invalid page order, no page %i " , order )
1400 return
1401 else
1402 done [ i ] = pages [ order ]
1403 hash [ order ] = true
1404 end
1405 end
1406 pages = done
1407 else
1408 report ( " invalid page order, %i entries expected " , nofpages )
1409 end
1410 end
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427 wrapup = function ( driver )
1428
1429
1430 local pagetree = { }
1431 local parent = nil
1432 local minimum = 0
1433 local maximum = 0
1434 local current = 0
1435 if # pages > 1 . 5 * maxkids then
1436 repeat
1437 local plist , pnode
1438 if current = = 0 then
1439 plist , minimum = pages , 1
1440 elseif current = = 1 then
1441 plist , minimum = pagetree , 1
1442 else
1443 plist , minimum = pagetree , maximum + 1
1444 end
1445 maximum = # plist
1446 if maximum > minimum then
1447 local kids
1448 for i = minimum , maximum do
1449 local p = plist [ i ]
1450 if not pnode or # kids = = maxkids then
1451 kids = pdfarray ( )
1452 parent = pdfreserveobject ( )
1453 pnode = pdfdictionary {
1454 objnum = parent ,
1455 Type = pdf_pages ,
1456 Kids = kids ,
1457 Count = 0 ,
1458 }
1459 pagetree [ # pagetree + 1 ] = pnode
1460 end
1461 kids [ # kids + 1 ] = pdfreference ( p . objnum )
1462 pnode . Count = pnode . Count + ( p . Count or 1 )
1463 p . Parent = pdfreference ( parent )
1464 end
1465 end
1466 current = current + 1
1467 until maximum = = minimum
1468
1469 for i = 1 , # pagetree do
1470 local entry = pagetree [ i ]
1471 local objnum = entry . objnum
1472 entry . objnum = nil
1473 pdfflushobject ( objnum , entry )
1474 end
1475 else
1476
1477 local kids = pdfarray ( )
1478 local list = pdfdictionary {
1479 Type = pdf_pages ,
1480 Kids = kids ,
1481 Count = nofpages ,
1482 }
1483 parent = pdfreserveobject ( )
1484 for i = 1 , nofpages do
1485 local page = pages [ i ]
1486 kids [ # kids + 1 ] = pdfreference ( page . objnum )
1487 page . Parent = pdfreference ( parent )
1488 end
1489 pdfflushobject ( parent , list )
1490 end
1491 for i = 1 , nofpages do
1492 local page = pages [ i ]
1493 local object = page . object
1494 object . Parent = page . Parent
1495 pdfflushobject ( page . objnum , object )
1496 end
1497 lpdf . addtocatalog ( " Pages " , pdfreference ( parent ) )
1498
1499 end
1500
1501end
1502
1503pdf_h , pdf_v = 0 , 0
1504
1505local function initialize ( driver , details )
1506 reset_variables ( details )
1507 reset_buffer ( )
1508end
1509
1510
1511
1512
1513
1514
1515local compact = false
1516
1517do
1518
1519
1520
1521
1522
1523
1524 local P , R , S , Cs , lpegmatch = lpeg . P , lpeg . R , lpeg . S , lpeg . Cs , lpeg . match
1525
1526 local p_ds = ( R ( " 09 " ) + S ( " . " ) ) ^ 1
1527
1528 local p_nl = S ( " \n " ) ^ 1
1529 local p_eg = P ( " Q " )
1530
1531 local p_cl = p_ds * ( P ( " rg " ) + P ( " g " ) + P ( " k " ) ) * p_ds * ( P ( " RG " ) + P ( " G " ) + P ( " K " ) )
1532
1533 local p_tr = P ( " /Tr " ) * p_ds * P ( " gs " )
1534
1535 local p_no_cl = ( p_cl * p_nl ) / " "
1536 local p_no_tr = ( p_tr * p_nl ) / " "
1537 local p_no_nl = 1 - p_nl
1538
1539 local p_do_cl = p_cl * p_nl
1540 local p_do_tr = p_tr * p_nl
1541
1542 local p_do_eg = p_eg * p_nl
1543
1544 local pattern = Cs ( (
1545 ( p_no_cl + p_no_tr ) ^ 0 * p_do_eg
1546 + p_no_tr * p_no_cl * p_do_tr * p_do_cl
1547 + p_no_cl * p_do_cl
1548 + p_no_tr * p_do_tr
1549 + p_no_nl ^ 1
1550 + 1
1551 ) ^ 1 )
1552
1553 local oldsize = 0
1554 local newsize = 0
1555
1556 directives . register ( " pdf.compact " , function ( v )
1557 compact = v and function ( s )
1558 oldsize = oldsize + # s
1559 s = lpegmatch ( pattern , s ) or s
1560 newsize = newsize + # s
1561 return s
1562 end
1563 end )
1564
1565 statistics . register ( " pdf pagestream " , function ( )
1566 if oldsize ~ = newsize then
1567 return string . format ( " old size: %i, new size %i " , oldsize , newsize )
1568 end
1569 end )
1570
1571
1572end
1573
1574local flushdeferred
1575
1576local level = 0
1577
1578local finalize do
1579
1580 local f_font = formatters [ " F%d " ]
1581
1582 local f_form = formatters [ " Fm%d " ]
1583 local f_group = formatters [ " Gp%d " ]
1584 local f_image = formatters [ " Im%d " ]
1585
1586 finalize = function ( driver , details )
1587
1588 level = level + 1
1589
1590 pdf_goto_pagemode ( )
1591
1592 local objnum = details . objnum
1593 local specification = details . specification
1594
1595 local content = concat ( buffer , " \n " , 1 , b )
1596
1597 if compact then
1598 content = compact ( content )
1599 end
1600
1601 local fonts = nil
1602 local xforms = nil
1603
1604 if next ( usedfonts ) then
1605 fonts = pdfdictionary { }
1606 for k , v in next , usedfonts do
1607 fonts [ f_font ( v ) ] = pdfreference ( pdfgetfontobjnumber ( k ) )
1608 end
1609 end
1610
1611
1612
1613
1614 if next ( usedxforms ) or next ( usedximages ) or next ( usedxgroups ) then
1615 xforms = pdfdictionary { }
1616 for k in sortedhash ( usedxforms ) do
1617
1618 xforms [ f_form ( getxformname ( k ) ) ] = pdfreference ( k )
1619 end
1620 for k , v in sortedhash ( usedximages ) do
1621 xforms [ f_image ( k ) ] = pdfreference ( v )
1622 end
1623 for k , v in sortedhash ( usedxgroups ) do
1624 xforms [ f_group ( k ) ] = pdfreference ( v )
1625 end
1626 end
1627
1628 reset_buffer ( )
1629
1630
1631
1632 if shippingmode = = " page " then
1633
1634 local pageproperties = lpdf . getpageproperties ( )
1635
1636 local pageresources = pageproperties . pageresources
1637 local pageattributes = pageproperties . pageattributes
1638 local pagesattributes = pageproperties . pagesattributes
1639
1640 pageresources . Font = fonts
1641 pageresources . XObject = xforms
1642 pageresources . ProcSet = lpdf . procset ( )
1643
1644 local xorigin , yorigin , relocated = backends . codeinjections . getpageorigin ( )
1645
1646 local bbox = pdfarray {
1647 ( boundingbox [ 1 ] + xorigin ) * bpfactor ,
1648 ( boundingbox [ 2 ] + yorigin ) * bpfactor ,
1649 ( boundingbox [ 3 ] + xorigin ) * bpfactor ,
1650 ( boundingbox [ 4 ] + yorigin ) * bpfactor ,
1651 }
1652
1653 if relocated then
1654 content = formatters [ " 1 0 0 1 %.6N %.6N cm\n%s " ] ( bbox [ 1 ] , bbox [ 2 ] , content )
1655 end
1656
1657 local contentsobj = pdfflushstreamobject ( content , false , false )
1658
1659 pageattributes . Type = pdf_page
1660 pageattributes . Contents = pdfreference ( contentsobj )
1661 pageattributes . Resources = pageresources
1662
1663 pageattributes . MediaBox = bbox
1664 pageattributes . Parent = nil
1665 pageattributes . Group = nil
1666
1667
1668
1669 registerpage ( pageattributes )
1670
1671 lpdf . finalizepage ( true )
1672
1673 if relocated then
1674 if pageattributes . TrimBox then pageattributes . TrimBox = box end
1675 if pageattributes . CropBox then pageattributes . CropBox = box end
1676 if pageattributes . BleedBox then pageattributes . BleedBox = box end
1677 end
1678
1679 else
1680
1681 local xformtype = specification . type or 0
1682 local margin = specification . margin or 0
1683 local attributes = specification . attributes or " "
1684 local resources = specification . resources or " "
1685
1686 local wrapper = nil
1687
1688 if xformtype = = 0 then
1689 wrapper = pdfdictionary {
1690 Type = pdf_xobject ,
1691 Subtype = pdf_form ,
1692 FormType = 1 ,
1693 BBox = nil ,
1694 Matrix = nil ,
1695 Resources = nil ,
1696 }
1697 else
1698 wrapper = pdfdictionary {
1699 BBox = nil ,
1700 Matrix = nil ,
1701 Resources = nil ,
1702 }
1703 end
1704 if xformtype = = 0 or xformtype = = 1 or xformtype = = 3 then
1705 wrapper . BBox = pdfarray {
1706 - margin * bpfactor ,
1707 - margin * bpfactor ,
1708 ( boundingbox [ 3 ] + margin ) * bpfactor ,
1709 ( boundingbox [ 4 ] + margin ) * bpfactor ,
1710 }
1711 end
1712 if xformtype = = 0 or xformtype = = 2 or xformtype = = 3 then
1713 wrapper . Matrix = pdfarray { 1 , 0 , 0 , 1 , 0 , 0 }
1714 end
1715
1716
1717
1718 local boxresources = lpdf . collectedresources { serialize = false }
1719 boxresources . Font = fonts
1720 boxresources . XObject = xforms
1721
1722
1723
1724
1725 if resources ~ = " " then
1726 boxresources = boxresources + resources
1727 end
1728 if attributes ~ = " " then
1729 wrapper = wrapper + attributes
1730 end
1731
1732 wrapper . Resources = next ( boxresources ) and boxresources or nil
1733 wrapper . ProcSet = lpdf . procset ( )
1734
1735
1736 pdfflushstreamobject ( content , wrapper , false , specification . objnum )
1737
1738 end
1739
1740 for objnum in sortedhash ( usedxforms ) do
1741 local f = flushedxforms [ objnum ]
1742 if f [ 1 ] = = false then
1743 f [ 1 ] = true
1744 local objnum = f [ 2 ]
1745 local specification = boxresources [ objnum ]
1746 local list = specification . list
1747 localconverter ( list , " xform " , f [ 2 ] , specification )
1748 end
1749 end
1750
1751 pdf_h , pdf_v = 0 , 0
1752
1753 if level = = 1 then
1754 flushdeferred ( )
1755 end
1756 level = level - 1
1757
1758 end
1759
1760end
1761
1762updaters . register ( " backend.update.pdf " , function ( )
1763 job . positions . registerhandlers {
1764 getpos = drivers . getpos ,
1765 getrpos = drivers . getrpos ,
1766 gethpos = drivers . gethpos ,
1767 getvpos = drivers . getvpos ,
1768 }
1769end )
1770
1771updaters . register ( " backend.update " , function ( )
1772 local saveboxresource = tex . boxresources . save
1773
1774
1775
1776 backends . codeinjections . registerboxresource = function ( n , offset )
1777 local r = saveboxresource ( n , nil , nil , false , 0 , offset or 0 )
1778 return r
1779 end
1780end )
1781
1782
1783
1784local objects = { }
1785local streams = { }
1786local nofobjects = 0
1787local offset = 0
1788local f = false
1789local flush = false
1790local threshold = 40
1791local objectstream = true
1792local compress = true
1793local cache = false
1794local info = " "
1795local catalog = " "
1796local lastdeferred = false
1797local majorversion = 1
1798local minorversion = 7
1799local trailerid = true
1800
1801directives . register ( " backend.pdf.threshold " , function ( v )
1802 if v then
1803 threshold = tonumber ( v ) or 40
1804 else
1805 threshold = -1000
1806 end
1807end )
1808
1809local f_object = formatters [ " %i 0 obj\010%s\010endobj\010 " ]
1810local f_stream_n_u = formatters [ " %i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010 " ]
1811local f_stream_n_c = formatters [ " %i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010 " ]
1812local f_stream_d_u = formatters [ " %i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010 " ]
1813local f_stream_d_c = formatters [ " %i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010 " ]
1814local f_stream_d_r = formatters [ " %i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010 " ]
1815
1816
1817local f_stream_b_n_u = formatters [ " %i 0 obj\010<< /Length %i >>\010stream\010 " ]
1818local f_stream_b_n_c = formatters [ " %i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010 " ]
1819local f_stream_b_d_u = formatters [ " %i 0 obj\010<< %s /Length %i >>\010stream\010 " ]
1820local f_stream_b_d_c = formatters [ " %i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010 " ]
1821local f_stream_b_d_r = formatters [ " %i 0 obj\010<< %s >>\010stream\010 " ]
1822
1823
1824local s_stream_e = " \010endstream\010endobj\010 "
1825
1826do
1827
1828 local function setinfo ( ) end
1829 local function setcatalog ( ) end
1830
1831 local function settrailerid ( v )
1832 trailerid = v or false
1833 end
1834
1835 local function setmajorversion ( v ) majorversion = tonumber ( v ) or majorversion end
1836 local function setminorversion ( v ) minorversion = tonumber ( v ) or minorversion end
1837
1838 local function getmajorversion ( v ) return majorversion end
1839 local function getminorversion ( v ) return minorversion end
1840
1841 local function setcompresslevel ( v ) compress = v and v ~ = 0 and true or false end
1842 local function setobjcompresslevel ( v ) objectstream = v and v ~ = 0 and true or false end
1843
1844 local function getcompresslevel ( v ) return compress and 3 or 0 end
1845 local function getobjcompresslevel ( v ) return objectstream and 1 or 0 end
1846
1847 local function setpageresources ( ) end
1848 local function setpageattributes ( ) end
1849 local function setpagesattributes ( ) end
1850
1851 updaters . register ( " backend.update.pdf " , function ( )
1852 pdf . setinfo = setinfo
1853 pdf . setcatalog = setcatalog
1854 pdf . settrailerid = settrailerid
1855 pdf . setmajorversion = setmajorversion
1856 pdf . setminorversion = setminorversion
1857 pdf . getmajorversion = getmajorversion
1858 pdf . getminorversion = getminorversion
1859 pdf . setcompresslevel = setcompresslevel
1860 pdf . setobjcompresslevel = setobjcompresslevel
1861 pdf . getcompresslevel = getcompresslevel
1862 pdf . getobjcompresslevel = getobjcompresslevel
1863 pdf . setpageresources = setpageresources
1864 pdf . setpageattributes = setpageattributes
1865 pdf . setpagesattributes = setpagesattributes
1866 end )
1867
1868end
1869
1870local addtocache , flushcache , cache do
1871
1872 local data , d = { } , 0
1873 local list , l = { } , 0
1874 local coffset = 0
1875 local indices = { }
1876
1877 local maxsize = 32 * 1024
1878 local maxcount = 0xFF
1879
1880 addtocache = function ( n , str )
1881 local size = # str
1882 if size = = 0 then
1883
1884 return
1885 end
1886 if coffset + size > maxsize or d = = maxcount then
1887 flushcache ( )
1888 end
1889 if d = = 0 then
1890 nofobjects = nofobjects + 1
1891 objects [ nofobjects ] = false
1892 streams [ nofobjects ] = indices
1893 cache = nofobjects
1894 end
1895 objects [ n ] = - cache
1896 indices [ n ] = d
1897 d = d + 1
1898
1899 data [ d ] = str
1900 l = l + 1 ; list [ l ] = n
1901 l = l + 1 ; list [ l ] = coffset
1902 coffset = coffset + size + 1
1903 end
1904
1905 local p_ObjStm = pdfconstant ( " ObjStm " )
1906
1907 flushcache = function ( )
1908 if l > 0 then
1909 list = concat ( list , " " )
1910 data [ 0 ] = list
1911 data = concat ( data , " \010 " , 0 , d )
1912 local strobj = pdfdictionary {
1913 Type = p_ObjStm ,
1914 N = d ,
1915 First = # list + 1 ,
1916 }
1917 objects [ cache ] = offset
1918 local b = nil
1919 local e = s_stream_e
1920 if compress then
1921 local comp = zlibcompress ( data , 3 )
1922 if comp and # comp < # data then
1923 data = comp
1924 b = f_stream_b_d_c ( cache , strobj ( ) , # data )
1925 else
1926 b = f_stream_b_d_u ( cache , strobj ( ) , # data )
1927 end
1928 else
1929 b = f_stream_b_d_u ( cache , strobj ( ) , # data )
1930 end
1931 flush ( f , b )
1932 flush ( f , data )
1933 flush ( f , e )
1934 offset = offset + # b + # data + # e
1935 data , d = { } , 0
1936 list , l = { } , 0
1937 coffset = 0
1938 indices = { }
1939 end
1940 end
1941
1942end
1943
1944local function pdfreserveobj ( )
1945 nofobjects = nofobjects + 1
1946 objects [ nofobjects ] = false
1947 return nofobjects
1948end
1949
1950local pages = table . setmetatableindex ( function ( t , k )
1951 local v = pdfreserveobj ( )
1952 t [ k ] = v
1953 return v
1954end )
1955
1956local function getpageref ( n )
1957 return pages [ n ]
1958end
1959
1960local function refobj ( )
1961
1962end
1963
1964local function flushnormalobj ( data , n )
1965 if not n then
1966 nofobjects = nofobjects + 1
1967 n = nofobjects
1968 end
1969 data = f_object ( n , data )
1970 if level = = 0 then
1971 objects [ n ] = offset
1972 offset = offset + # data
1973 flush ( f , data )
1974 else
1975 if not lastdeferred then
1976 lastdeferred = n
1977 elseif n < lastdeferred then
1978 lastdeferred = n
1979 end
1980 objects [ n ] = data
1981 end
1982 return n
1983end
1984
1985local function flushstreamobj ( data , n , dict , comp , nolength )
1986 if not data then
1987 report ( " no data for %S " , dict )
1988 return
1989 end
1990 if not n then
1991 nofobjects = nofobjects + 1
1992 n = nofobjects
1993 end
1994 local size = # data
1995 if level = = 0 then
1996 local b = nil
1997 local e = s_stream_e
1998 if nolength then
1999 b = f_stream_b_d_r ( n , dict )
2000 elseif comp ~ = false and compress and size > threshold then
2001 local compdata = zlibcompress ( data , 3 )
2002 if compdata then
2003 local compsize = # compdata
2004 if compsize > size - threshold then
2005 b = dict and f_stream_b_d_u ( n , dict , size ) or f_stream_b_n_u ( n , size )
2006 else
2007 data = compdata
2008 b = dict and f_stream_b_d_c ( n , dict , compsize ) or f_stream_b_n_c ( n , compsize )
2009 end
2010 else
2011 b = dict and f_stream_b_d_u ( n , dict , size ) or f_stream_b_n_u ( n , size )
2012 end
2013 else
2014 b = dict and f_stream_b_d_u ( n , dict , size ) or f_stream_b_n_u ( n , size )
2015 end
2016 flush ( f , b )
2017 flush ( f , data )
2018 flush ( f , e )
2019 objects [ n ] = offset
2020 offset = offset + # b + # data + # e
2021 else
2022 if nolength then
2023 data = f_stream_d_r ( n , dict , data )
2024 elseif comp ~ = false and compress and size > threshold then
2025 local compdata = zlibcompress ( data , 3 )
2026 if compdata then
2027 local compsize = # compdata
2028 if compsize > size - threshold then
2029 data = dict and f_stream_d_u ( n , dict , size , data ) or f_stream_n_u ( n , size , data )
2030 else
2031 data = dict and f_stream_d_c ( n , dict , compsize , compdata ) or f_stream_n_c ( n , compsize , compdata )
2032 end
2033 else
2034 data = dict and f_stream_d_u ( n , dict , size , data ) or f_stream_n_u ( n , size , data )
2035 end
2036 else
2037 data = dict and f_stream_d_u ( n , dict , size , data ) or f_stream_n_u ( n , size , data )
2038 end
2039 if not lastdeferred then
2040 lastdeferred = n
2041 elseif n < lastdeferred then
2042 lastdeferred = n
2043 end
2044 objects [ n ] = data
2045 end
2046 return n
2047end
2048
2049flushdeferred = function ( )
2050 if lastdeferred then
2051 for n = lastdeferred , nofobjects do
2052 local o = objects [ n ]
2053 if type ( o ) = = " string " then
2054 objects [ n ] = offset
2055 offset = offset + # o
2056 flush ( f , o )
2057 end
2058 end
2059 lastdeferred = false
2060 end
2061end
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080local function obj ( a , b , c , d )
2081 local kind
2082 local objnum , data , attr , filename
2083 local compresslevel , objcompression , nolength
2084 local argtype = type ( a )
2085 if argtype = = " table " then
2086 kind = a . type
2087
2088 objnum = a . objnum
2089 attr = a . attr
2090 compresslevel = a . compresslevel
2091 objcompression = a . objcompression
2092 filename = a . file
2093 data = a . string or a . stream or " "
2094 nolength = a . nolength
2095 if kind = = " stream " then
2096 if filename then
2097 data = loaddata ( filename ) or " "
2098 end
2099 elseif kind = = " raw " then
2100 if filename then
2101 data = loaddata ( filename ) or " "
2102 end
2103 elseif kind = = " file " then
2104 kind = " raw "
2105 data = filename and loaddata ( filename ) or " "
2106 elseif kind = = " streamfile " then
2107 kind = " stream "
2108 data = filename and loaddata ( filename ) or " "
2109 end
2110 else
2111 if argtype = = " number " then
2112 objnum = a
2113 a , b , c = b , c , d
2114 else
2115 nofobjects = nofobjects + 1
2116 objnum = nofobjects
2117 end
2118 if b then
2119 if a = = " stream " then
2120 kind = " stream "
2121 data = b
2122 elseif a = = " file " then
2123
2124 data = loaddata ( b )
2125 elseif a = = " streamfile " then
2126 kind = " stream "
2127 data = loaddata ( b )
2128 else
2129 data = " "
2130 end
2131 attr = c
2132 else
2133
2134 data = a
2135 end
2136 end
2137 if not objnum then
2138 nofobjects = nofobjects + 1
2139 objnum = nofobjects
2140 end
2141
2142 if kind = = " stream " then
2143 flushstreamobj ( data , objnum , attr , compresslevel and compresslevel > 0 or nil , nolength )
2144 elseif objectstream and objcompression ~ = false then
2145 addtocache ( objnum , data )
2146 else
2147 flushnormalobj ( data , objnum )
2148 end
2149 return objnum
2150end
2151
2152updaters . register ( " backend.update.pdf " , function ( )
2153 pdf . reserveobj = pdfreserveobj
2154 pdf . getpageref = getpageref
2155 pdf . refobj = refobj
2156 pdf . flushstreamobj = flushstreamobj
2157 pdf . flushnormalobj = flushnormalobj
2158 pdf . obj = obj
2159 pdf . immediateobj = obj
2160end )
2161
2162
2163
2164
2165
2166local openfile , closefile do
2167
2168
2169
2170
2171
2172
2173 local f_used = formatters [ " %010i 00000 n\013\010 " ]
2174 local f_link = formatters [ " %010i 00000 f\013\010 " ]
2175 local f_first = formatters [ " %010i 65535 f\013\010 " ]
2176
2177 local f_pdf = formatters [ " %%PDF-%i.%i\010 " ]
2178 local f_xref = formatters [ " xref\0100 %i\010 " ]
2179 local f_trailer_id = formatters [ " trailer\010<< %s /ID [ <%s> <%s> ] >>\010startxref\010%i\010%%%%EOF " ]
2180 local f_trailer_no = formatters [ " trailer\010<< %s >>\010startxref\010%i\010%%%%EOF " ]
2181 local f_startxref = formatters [ " startxref\010%i\010%%%%EOF " ]
2182
2183 local inmemory = false
2184 local close = false
2185
2186 openfile = function ( filename )
2187 if inmemory then
2188 local n = 0
2189 f = { }
2190 flush = function ( f , s )
2191 n = n + 1 f [ n ] = s
2192 end
2193 close = function ( f )
2194 f = concat ( f )
2195 io . savedata ( filename , f )
2196 f = false
2197 end
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209 else
2210 f = io . open ( filename , " wb " )
2211 if not f then
2212
2213 os . exit ( )
2214 end
2215
2216 local m = getmetatable ( f )
2217 flush = m . write or m . __index . write
2218 close = m . close or m . __index . close
2219 end
2220 local v = f_pdf ( majorversion , minorversion )
2221
2222 local b = " %\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010 "
2223 flush ( f , v )
2224 flush ( f , b )
2225 offset = # v + # b
2226 end
2227
2228 closefile = function ( abort )
2229 if abort then
2230 f : close ( )
2231 if not environment . arguments . nodummy then
2232 f = io . open ( abort , " wb " )
2233 if f then
2234 local name = resolvers . findfile ( " context-lmtx-error.pdf " )
2235 if name then
2236 local data = io . loaddata ( name )
2237 if data then
2238 f : write ( data )
2239 f : close ( )
2240 return
2241 end
2242 end
2243 f : close ( )
2244 end
2245 end
2246 removefile ( abort )
2247 else
2248 local xrefoffset = offset
2249 local lastfree = 0
2250 local noffree = 0
2251 local catalog = lpdf . getcatalog ( )
2252 local info = lpdf . getinfo ( )
2253 if trailerid = = true then
2254 trailerid = md5HEX ( osuuid ( ) )
2255 elseif trailerid and # trailerid > 32 then
2256 trailerid = md5HEX ( trailerid )
2257 else
2258 trailerid = false
2259 end
2260 if objectstream then
2261 flushdeferred ( )
2262 flushcache ( )
2263
2264 xrefoffset = offset
2265
2266 nofobjects = nofobjects + 1
2267 objects [ nofobjects ] = offset
2268
2269
2270
2271
2272 local nofbytes = 4
2273 local c1 , c2 , c3 , c4
2274 if offset < = 0xFFFF then
2275 nofbytes = 2
2276 for i = 1 , nofobjects do
2277 local o = objects [ i ]
2278 if not o then
2279 noffree = noffree + 1
2280 else
2281 local strm = o < 0
2282 if strm then
2283 o = - o
2284 end
2285 c1 = extract ( o , 8 , 8 )
2286 c2 = extract ( o , 0 , 8 )
2287 if strm then
2288 objects [ i ] = char ( 2 , c1 , c2 , streams [ o ] [ i ] )
2289 else
2290 objects [ i ] = char ( 1 , c1 , c2 , 0 )
2291 end
2292 end
2293 end
2294 if noffree > 0 then
2295 for i = nofobjects , 1 , -1 do
2296 local o = objects [ i ]
2297 if not o then
2298 local f1 = extract ( lastfree , 8 , 8 )
2299 local f2 = extract ( lastfree , 0 , 8 )
2300 objects [ i ] = char ( 0 , f1 , f2 , 0 )
2301 lastfree = i
2302 end
2303 end
2304 end
2305 elseif offset < = 0xFFFFFF then
2306 nofbytes = 3
2307 for i = 1 , nofobjects do
2308 local o = objects [ i ]
2309 if not o then
2310 noffree = noffree + 1
2311 else
2312 local strm = o < 0
2313 if strm then
2314 o = - o
2315 end
2316 c1 = extract ( o , 16 , 8 )
2317 c2 = extract ( o , 8 , 8 )
2318 c3 = extract ( o , 0 , 8 )
2319 if strm then
2320 objects [ i ] = char ( 2 , c1 , c2 , c3 , streams [ o ] [ i ] )
2321 else
2322 objects [ i ] = char ( 1 , c1 , c2 , c3 , 0 )
2323 end
2324 end
2325 end
2326 if noffree > 0 then
2327 for i = nofobjects , 1 , -1 do
2328 local o = objects [ i ]
2329 if not o then
2330 local f1 = extract ( lastfree , 16 , 8 )
2331 local f2 = extract ( lastfree , 8 , 8 )
2332 local f3 = extract ( lastfree , 0 , 8 )
2333 objects [ i ] = char ( 0 , f1 , f2 , f3 , 0 )
2334 lastfree = i
2335 end
2336 end
2337 end
2338 else
2339 nofbytes = 4
2340 for i = 1 , nofobjects do
2341 local o = objects [ i ]
2342 if not o then
2343 noffree = noffree + 1
2344 else
2345 local strm = o < 0
2346 if strm then
2347 o = - o
2348 end
2349 c1 = extract ( o , 24 , 8 )
2350 c2 = extract ( o , 16 , 8 )
2351 c3 = extract ( o , 8 , 8 )
2352 c4 = extract ( o , 0 , 8 )
2353 if strm then
2354 objects [ i ] = char ( 2 , c1 , c2 , c3 , c4 , streams [ o ] [ i ] )
2355 else
2356 objects [ i ] = char ( 1 , c1 , c2 , c3 , c4 , 0 )
2357 end
2358 end
2359 end
2360 if noffree > 0 then
2361 for i = nofobjects , 1 , -1 do
2362 local o = objects [ i ]
2363 if not o then
2364 local f1 = extract ( lastfree , 24 , 8 )
2365 local f2 = extract ( lastfree , 16 , 8 )
2366 local f3 = extract ( lastfree , 8 , 8 )
2367 local f4 = extract ( lastfree , 0 , 8 )
2368 objects [ i ] = char ( 0 , f1 , f2 , f3 , f4 , 0 )
2369 lastfree = i
2370 end
2371 end
2372 end
2373 end
2374 objects [ 0 ] = rep ( " \0 " , 1 + nofbytes + 1 )
2375 local data = concat ( objects , " " , 0 , nofobjects )
2376 local xref = pdfdictionary {
2377 Type = pdfconstant ( " XRef " ) ,
2378 Size = nofobjects + 1 ,
2379 W = pdfarray { 1 , nofbytes , 1 } ,
2380 Root = catalog ,
2381 Info = info ,
2382 ID = trailerid and pdfarray { pdfliteral ( trailerid , true ) , pdfliteral ( trailerid , true ) } or nil ,
2383 }
2384 if compress then
2385 local comp = zlibcompress ( data , 3 )
2386 if comp then
2387 data = comp
2388 flush ( f , f_stream_b_d_c ( nofobjects , xref ( ) , # data ) )
2389 else
2390 flush ( f , f_stream_b_d_u ( nofobjects , xref ( ) , # data ) )
2391 end
2392 else
2393 flush ( f , f_stream_b_d_u ( nofobjects , xref ( ) , # data ) )
2394 end
2395 flush ( f , data )
2396 flush ( f , s_stream_e )
2397 flush ( f , f_startxref ( xrefoffset ) )
2398 else
2399 flushdeferred ( )
2400 xrefoffset = offset
2401 flush ( f , f_xref ( nofobjects + 1 ) )
2402 local trailer = pdfdictionary {
2403 Size = nofobjects + 1 ,
2404 Root = catalog ,
2405 Info = info ,
2406 }
2407 for i = 1 , nofobjects do
2408 local o = objects [ i ]
2409 if o then
2410 objects [ i ] = f_used ( o )
2411 end
2412 end
2413 for i = nofobjects , 1 , -1 do
2414 local o = objects [ i ]
2415 if not o then
2416 objects [ i ] = f_link ( lastfree )
2417 lastfree = i
2418 end
2419 end
2420 objects [ 0 ] = f_first ( lastfree )
2421 flush ( f , concat ( objects , " " , 0 , nofobjects ) )
2422 trailer . Size = nofobjects + 1
2423 if trailerid then
2424 flush ( f , f_trailer_id ( trailer ( ) , trailerid , trailerid , xrefoffset ) )
2425 else
2426 flush ( f , f_trailer_no ( trailer ( ) , xrefoffset ) )
2427 end
2428 end
2429 f : close ( )
2430 end
2431 io . flush ( )
2432 closefile = function ( ) end
2433 end
2434
2435end
2436
2437
2438
2439
2440updaters . register ( " backend.update.pdf " , function ( )
2441
2442
2443
2444
2445
2446 local codeinjections = backends . pdf . codeinjections
2447
2448 local imagetypes = images . types
2449 local img_none = imagetypes . none
2450
2451 local rulecodes = nodes . rulecodes
2452
2453 local setprop = nodes . nuts . setprop
2454
2455 local report_images = logs . reporter ( " backend " , " images " )
2456
2457 local lastindex = 0
2458 local indices = { }
2459
2460 local bpfactor = number . dimenfactors . bp
2461 local imagerule_code = rulecodes . image
2462
2463 function codeinjections . newimage ( specification )
2464 return specification
2465 end
2466
2467 function codeinjections . copyimage ( original )
2468 return setmetatableindex ( original )
2469 end
2470
2471 function codeinjections . scanimgage ( specification )
2472 return specification
2473 end
2474
2475 local function embedimage ( specification )
2476 if specification then
2477 lastindex = lastindex + 1
2478 index = lastindex
2479 specification . index = index
2480 local xobject = pdfdictionary { }
2481 if not specification . notype then
2482 xobject . Type = pdf_xobject
2483 xobject . Subtype = pdf_form
2484 xobject . FormType = 1
2485 end
2486 local bbox = specification . bbox
2487 if bbox and not specification . nobbox then
2488 xobject . BBox = pdfarray {
2489 bbox [ 1 ] * bpfactor ,
2490 bbox [ 2 ] * bpfactor ,
2491 bbox [ 3 ] * bpfactor ,
2492 bbox [ 4 ] * bpfactor ,
2493 }
2494 end
2495 xobject = xobject + specification . attr
2496 if bbox and not specification . width then
2497 specification . width = bbox [ 3 ]
2498 end
2499 if bbox and not specification . height then
2500 specification . height = bbox [ 4 ]
2501 end
2502 local dict = xobject ( )
2503
2504 nofobjects = nofobjects + 1
2505 local objnum = nofobjects
2506 local nolength = specification . nolength
2507 local stream = specification . stream or specification . string
2508
2509
2510
2511
2512
2513
2514 if not specification . type then
2515 local kind = specification . kind
2516 if kind then
2517
2518 elseif attr and find ( attr , " BBox " ) then
2519 kind = img_stream
2520 else
2521
2522 kind = img_none
2523 end
2524 specification . type = kind
2525 specification . kind = kind
2526 end
2527 local compress = compresslevel and compresslevel > 0 or nil
2528 flushstreamobj ( stream , objnum , dict , compress , nolength )
2529 specification . objnum = objnum
2530 specification . rotation = specification . rotation or 0
2531 specification . orientation = specification . orientation or 0
2532 specification . transform = specification . transform or 0
2533 specification . stream = nil
2534 specification . attr = nil
2535 specification . type = specification . kind or specification . type or img_none
2536 indices [ index ] = specification
2537 return specification
2538 end
2539 end
2540
2541 codeinjections . embedimage = embedimage
2542
2543 function codeinjections . wrapimage ( specification )
2544
2545 local index = specification . index
2546 if not index then
2547 embedimage ( specification )
2548 end
2549
2550 local width = specification . width or 0
2551 local height = specification . height or 0
2552 local depth = specification . depth or 0
2553
2554 local n = nodes . pool . rule ( width , height , depth )
2555 n . subtype = imagerule_code
2556 setprop ( tonut ( n ) , " index " , specification . index )
2557 return n
2558 end
2559
2560 function pdf . includeimage ( index )
2561 local specification = indices [ index ]
2562 if specification then
2563 local bbox = specification . bbox
2564 local xorigin = bbox [ 1 ]
2565 local yorigin = bbox [ 2 ]
2566 local xsize = bbox [ 3 ] - xorigin
2567 local ysize = bbox [ 4 ] - yorigin
2568 local transform = specification . transform or 0
2569 local objnum = specification . objnum or pdfreserveobj ( )
2570 local groupref = nil
2571 local kind = specification . kind or specification . type or img_none
2572 return
2573 kind ,
2574 xorigin , yorigin ,
2575 xsize , ysize ,
2576 transform ,
2577 objnum ,
2578 groupref
2579 end
2580 end
2581
2582end )
2583
2584updaters . register ( " backend.update.lpdf " , function ( )
2585
2586
2587
2588
2589
2590 local pdfimage = lpdf . epdf . image
2591 local newpdf = pdfimage . new
2592 local openpdf = pdfimage . open
2593 local closepdf = pdfimage . close
2594 local copypage = pdfimage . copy
2595
2596 local embedimage = images . embed
2597
2598 local nofstreams = 0
2599 local topdf = { }
2600 local toidx = { }
2601
2602 local function storedata_s ( pdf )
2603 local idx = toidx [ pdf ]
2604 if not idx then
2605 nofstreams = nofstreams + 1
2606 idx = nofstreams
2607 toidx [ pdf ] = nofstreams
2608 topdf [ idx ] = pdf
2609 end
2610 return idx
2611 end
2612
2613 local function vfimage_s ( id , wd , ht , dp , pos_h , pos_v )
2614 local index = topdf [ id ]
2615 if type ( index ) = = " string " then
2616 local pdfdoc = newpdf ( index , # index )
2617 local image = copypage ( pdfdoc )
2618 local bbox = image . bbox
2619 image . width = bbox [ 3 ] - bbox [ 1 ]
2620 image . height = bbox [ 4 ] - bbox [ 2 ]
2621 embedimage ( image )
2622 index = image . index
2623 topdf [ id ] = index
2624 end
2625
2626 flushimage ( index , wd , ht , dp , pos_h , pos_v )
2627 end
2628
2629 local function storedata_n ( name , page )
2630 local idx = toidx [ pdf ]
2631 if not idx then
2632 nofstreams = nofstreams + 1
2633 idx = nofstreams
2634 toidx [ pdf ] = nofstreams
2635 topdf [ idx ] = pdf
2636 end
2637 return idx
2638 end
2639
2640
2641
2642 local pdfdocs = { }
2643
2644 local function vfimage_n ( name , page , wd , ht , dp , pos_h , pos_v )
2645 local d = pdfdocs [ name ]
2646 if not d then
2647 d = { doc = openpdf ( name ) , pages = { } }
2648 pdfdocs [ name ] = d
2649 end
2650 local index = d . pages [ page ]
2651 if not index then
2652 local image = copypage ( d . doc , page )
2653 local bbox = image . bbox
2654 image . width = bbox [ 3 ] - bbox [ 1 ]
2655 image . height = bbox [ 4 ] - bbox [ 2 ]
2656 embedimage ( image )
2657 index = image . index
2658 d . pages [ page ] = index
2659 end
2660 flushimage ( index , wd , ht , dp , pos_h , pos_v )
2661 end
2662
2663 local function pdfvfimage ( wd , ht , dp , data , name )
2664 if type ( data ) = = " number " then
2665 return { " lua " , function ( font , char , pos_h , pos_v )
2666 vfimage_n ( name , data , wd , ht , dp , pos_h , pos_v )
2667 end }
2668 else
2669 return { " lua " , function ( font , char , pos_h , pos_v )
2670 local id = storedata_s ( data )
2671 vfimage_s ( id , wd , ht , dp , pos_h , pos_v )
2672 end }
2673 end
2674 end
2675
2676 lpdf . vfimage = pdfvfimage
2677
2678end )
2679
2680
2681
2682do
2683
2684 local isfile = lfs . isfile
2685 local removefile = os . remove
2686 local renamefile = os . rename
2687
2688
2689 local texgetbox = tex . getbox
2690
2691 local pdfname = nil
2692 local converter = nil
2693 local useddriver = nil
2694
2695 local function outputfilename ( driver )
2696 return pdfname
2697 end
2698
2699
2700
2701 local function prepare ( driver )
2702 if not environment . initex then
2703
2704 updaters . apply ( " backend.update.pdf " )
2705
2706 updaters . apply ( " backend.update.lpdf " )
2707
2708 updaters . apply ( " backend.update.tex " )
2709
2710 updaters . apply ( " backend.update " )
2711
2712
2713
2714
2715
2716
2717 pdfname = tex . jobname . . " .pdf "
2718 openfile ( pdfname )
2719
2720 luatex . registerstopactions ( 1 , function ( )
2721 if pdfname then
2722 lpdf . finalizedocument ( )
2723 closefile ( )
2724 end
2725 end )
2726
2727 luatex . registerpageactions ( 1 , function ( )
2728 if pdfname then
2729 lpdf . finalizepage ( true )
2730 end
2731 end )
2732
2733 lpdf . registerdocumentfinalizer ( wrapup , nil , " wrapping up " )
2734
2735 end
2736
2737 environment . lmtxmode = CONTEXTLMTXMODE
2738
2739 converter = drivers . converters . lmtx
2740 useddriver = driver
2741 end
2742
2743 local function wrapup ( driver )
2744 if pdfname then
2745 closefile ( )
2746 pdfname = nil
2747 end
2748 end
2749
2750 local function cleanup ( driver )
2751 if pdfname then
2752 closefile ( pdfname )
2753 pdfname = nil
2754 end
2755 end
2756
2757 local function convert ( driver , boxnumber )
2758 converter ( driver , texgetbox ( boxnumber ) , " page " )
2759 end
2760
2761 localconverter = function ( ... )
2762 converter ( useddriver , ... )
2763 end
2764
2765 drivers . install {
2766 name = " pdf " ,
2767 flushers = {
2768 character = flushcharacter ,
2769 fontchar = flushfontchar ,
2770 rule = flushrule ,
2771 simplerule = flushsimplerule ,
2772 pushorientation = pushorientation ,
2773 poporientation = poporientation ,
2774
2775 literal = flushliteral ,
2776 setmatrix = flushsetmatrix ,
2777 save = flushsave ,
2778 restore = flushrestore ,
2779 image = flushimage ,
2780 group = flushgroup ,
2781
2782 updatefontstate = updatefontstate ,
2783 } ,
2784 actions = {
2785 prepare = prepare ,
2786 wrapup = wrapup ,
2787 cleanup = cleanup ,
2788
2789 initialize = initialize ,
2790 convert = convert ,
2791 finalize = finalize ,
2792
2793 outputfilename = outputfilename ,
2794 } ,
2795 }
2796
2797end
2798 |