lpdf-lmt.lua /size: 87 Kb    last modification: 2020-07-01 14:35
1
if
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
-- The code below was originally in back-lpd.lua but it makes more sense in
10
-- this namespace. I will rename variables.
11
--
12
-- There is no way that a lua based backend can compete with the original one
13
-- for relative simple text runs. And we're talking seconds here on say 500
14
-- pages with paragraphs alternativng between three fonts and colors. But such
15
-- documents are rare so in practice we are quite okay, especially because in
16
-- ConTeXt we can gain quite a bit elsewhere. So, when we loose 30% on such
17
-- simple documents, we break even on for instance the manual, and gain 30% on
18
-- Thomas's turture test (also for other reasons). But .. who knows what magic
19
-- I can cook up in due time.
20 21
-- If you consider this complex, watch:
22
--
23
-- https://www.youtube.com/watch?v=6H-cAzfB2qo
24
--
25
-- or in distractionmode:
26
--
27
-- https://www.youtube.com/watch?v=TYuTE_1jvvE
28
-- https://www.youtube.com/watch?v=nnicGKX3lvM
29
--
30
-- For the moment we have to support the built in backend as well as the alternative. So
31
-- the next interface is suboptimal and will change at some time. At that moment I will
32
-- also optimize and extend.
33 34
local
type
,
next
,
unpack
,
tonumber
=
type
,
next
,
unpack
,
tonumber
35
local
char
,
rep
,
find
=
string
.
char
,
string
.
rep
,
string
.
find
36
local
formatters
,
splitupstring
=
string
.
formatters
,
string
.
splitup
37
local
band
,
extract
=
bit32
.
band
,
bit32
.
extract
38
local
concat
,
sortedhash
=
table
.
concat
,
table
.
sortedhash
39
local
setmetatableindex
=
table
.
setmetatableindex
40
local
loaddata
=
io
.
loaddata
41 42
local
bpfactor
=
number
.
dimenfactors
.
bp
43 44
local
md5HEX
=
md5
.
HEX
45
local
osuuid
=
os
.
uuid
46
local
zlibcompress
=
(
xzip
or
zlib
)
.
compress
47 48
local
nuts
=
nodes
.
nuts
49
local
tonut
=
nodes
.
tonut
50 51
local
getdata
=
nuts
.
getdata
52
local
getsubtype
=
nuts
.
getsubtype
53
local
getwhd
=
nuts
.
getwhd
54
local
flushlist
=
nuts
.
flush_list
55 56
local
pdfincludeimage
=
lpdf
.
includeimage
57
local
pdfgetfontname
=
lpdf
.
getfontname
58
local
pdfgetfontobjnumber
=
lpdf
.
getfontobjnumber
59 60
local
pdfreserveobject
=
lpdf
.
reserveobject
61
local
pdfpagereference
=
lpdf
.
pagereference
62
local
pdfflushobject
=
lpdf
.
flushobject
63
local
pdfreference
=
lpdf
.
reference
64
local
pdfdictionary
=
lpdf
.
dictionary
65
local
pdfarray
=
lpdf
.
array
66
local
pdfconstant
=
lpdf
.
constant
67
local
pdfflushstreamobject
=
lpdf
.
flushstreamobject
68
local
pdfliteral
=
lpdf
.
literal
-- not to be confused with a whatsit!
69 70
local
pdf_pages
=
pdfconstant
(
"
Pages
"
)
71
local
pdf_page
=
pdfconstant
(
"
Page
"
)
72
local
pdf_xobject
=
pdfconstant
(
"
XObject
"
)
73
local
pdf_form
=
pdfconstant
(
"
Form
"
)
74 75
local
fonthashes
=
fonts
.
hashes
76
local
characters
=
fonthashes
.
characters
77
local
descriptions
=
fonthashes
.
descriptions
78
local
parameters
=
fonthashes
.
parameters
79
local
properties
=
fonthashes
.
properties
80 81
local
report
=
logs
.
reporter
(
"
backend
"
)
82 83
-- used variables
84 85
local
pdf_h
,
pdf_v
86
local
need_tm
,
need_tf
,
cur_tmrx
,
cur_factor
,
cur_f
,
cur_e
87
local
need_width
,
need_mode
,
done_width
,
done_mode
88
local
mode
89
local
f_pdf_cur
,
f_pdf
,
fs_cur
,
fs
,
f_cur
90
local
tj_delta
,
cw
91
local
usedfonts
,
usedxforms
,
usedximages
,
usedxgroups
92
local
getxformname
,
getximagename
93
local
boundingbox
,
shippingmode
,
objectnumber
94
local
tmrx
,
tmry
,
tmsx
,
tmsy
,
tmtx
,
tmty
95
local
cmrx
,
cmry
,
cmsx
,
cmsy
,
cmtx
,
cmty
96 97
local
function
usefont
(
t
,
k
)
-- a bit redundant hash
98
local
v
=
pdfgetfontname
(
k
)
99
t
[
k
]
=
v
100
return
v
101
end
102 103
local
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
-- nullfont
123
f_pdf
=
0
-- nullfont
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
-- usedxgroups = { }
135
boundingbox
=
specification
.
boundingbox
136
end
137 138
-- buffer
139 140
local
buffer
=
lua
.
newtable
(
1024
,
0
)
-- { }
141
local
b
=
0
142 143
local
function
reset_buffer
(
)
144
b
=
0
145
end
146 147
-- fonts
148 149
local
fontcharacters
150
local
fontdescriptions
151
local
fontparameters
152
local
fontproperties
153
local
usedcharacters
=
setmetatableindex
(
"
table
"
)
154
local
pdfcharacters
155 156
local
horizontalmode
=
true
157
----- widefontmode = true
158
local
scalefactor
=
1
159
local
threshold
=
655360
160
local
tjfactor
=
100
/
65536
161 162
lpdf
.
usedcharacters
=
usedcharacters
163 164
local
function
updatefontstate
(
font
)
165
fontcharacters
=
characters
[
font
]
166
fontdescriptions
=
descriptions
[
font
]
167
fontparameters
=
parameters
[
font
]
168
fontproperties
=
properties
[
font
]
169
local
size
=
fontparameters
.
size
-- or bad news
170
local
designsize
=
fontparameters
.
designsize
or
size
171
pdfcharacters
=
usedcharacters
[
font
]
172
horizontalmode
=
fontparameters
.
writingmode
~
=
"
vertical
"
173
-- widefontmode = fontproperties.encodingbytes == 2
174
scalefactor
=
(
designsize
/
size
)
*
tjfactor
175
local
fthreshold
=
fontproperties
.
threshold
176
threshold
=
(
fthreshold
and
(
size
*
fthreshold
/
100
)
)
or
655360
177
end
178 179
-- helpers
180 181
local
f_cm
=
formatters
[
"
%.6N %.6N %.6N %.6N %.6N %.6N cm
"
]
182
local
f_tm
=
formatters
[
"
%.6N %.6N %.6N %.6N %.6N %.6N Tm
"
]
183 184
local
saved_text_pos_v
=
0
185
local
saved_text_pos_h
=
0
186 187
local
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
"
195
end
196 197
local
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
"
210
end
211 212
local
saved_chararray_pos_h
213
local
saved_chararray_pos_v
214 215
local
saved_b
=
0
216 217
local
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
"
225
end
226 227
local
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
"
234
end
235 236
local
function
begin_charmode
(
)
237
b
=
b
+
1
;
buffer
[
b
]
=
"
<
"
238
mode
=
"
char
"
239
end
240 241
local
function
end_charmode
(
)
242
b
=
b
+
1
;
buffer
[
b
]
=
"
>
"
243
mode
=
"
chararray
"
244
end
245 246
local
function
calc_pdfpos
(
h
,
v
)
247
-- mostly char
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
265
end
266 267
local
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
274
end
275 276
local
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
305
end
306 307
local
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
312
end
313 314
-- these dummy returns makes using them a bit faster
315 316
local
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
323
end
324 325
local
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
-- if mode == "chararray" then
334
return
end_chararray
(
)
335
end
336
end
337
end
338 339
local
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
352
end
353 354
local
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
"
367
end
368 369
-- characters
370 371
local
flushcharacter
do
372 373
local
round
=
math
.
round
374 375
-- across pages ... todo: clean up because we don't need to pass the font
376
-- as fontparameters already has checked / set it we can also have a variable
377
-- for it so
378 379
local
naturalwidth
=
nil
380 381
-- local naturalwidths = setmetatableindex(function(t,font)
382
-- local d = descriptions[font]
383
-- local c = characters[font]
384
-- local f = parameters[font].hfactor
385
-- local v = setmetatableindex(function(t,char)
386
-- local w
387
-- local e = d and d[char]
388
-- if e then
389
-- w = e.width
390
-- if w then
391
-- w = w * f
392
-- end
393
-- end
394
-- if not w then
395
-- e = c[char]
396
-- if e then
397
-- w = e.width or 0
398
-- end
399
-- end
400
-- if not w then
401
-- w = 0
402
-- end
403
-- t[char] = w
404
-- return w
405
-- end)
406
-- t[font] = v
407
-- return v
408
-- end)
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
]
-- cache
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
-- kind of special:
464
if
format
=
=
"
opentype
"
or
format
=
=
"
type1
"
then
465
fs
=
fs
*
1000
/
fontparameters
.
units
-- can we avoid this ?
466
end
467
--
468
naturalwidth
=
naturalwidths
[
font
]
469
end
470 471
-- This only saves a little on hz because there we switch a lot of
472
-- instances.
473 474
-- local lastslant, lastextend, lastsqueeze, lastformat, lastsize, lastwidth, lastmode, lastused, lastfont
475 476
-- local function setup_fontparameters(font,factor,f,e)
477
-- if font ~= lastfont then
478
-- lastslant = fontparameters.slantfactor or 0
479
-- lastextend = fontparameters.extendfactor or 1
480
-- lastsqueeze = fontparameters.squeezefactor or 1
481
-- lastformat = fontproperties.format
482
-- lastsize = fontparameters.size * bpfactor
483
-- if format == "opentype" or format == "type1" then
484
-- lastsize = lastsize * 1000 / fontparameters.units -- can we avoid this ?
485
-- end
486
-- lastwidth = fontparameters.width
487
-- lastmode = fontparameters.mode
488
-- lastused = usedfonts[font] -- cache
489
-- lastfont = font
490
-- end
491
-- local expand = 1 + factor / 1000000
492
-- if e then
493
-- tmrx = expand * lastextend * e
494
-- else
495
-- tmrx = expand * lastextend
496
-- end
497
-- tmsy = lastslant
498
-- tmry = lastsqueeze
499
-- need_width = lastwidth
500
-- need_mode = lastmode
501
-- f_cur = lastfont
502
-- f_pdf = lastused
503
-- cur_factor = factor
504
-- cur_f = f
505
-- cur_e = e
506
-- tj_delta = 0
507
-- if f then
508
-- fs = lastsize * f
509
-- else
510
-- fs = lastsize
511
-- end
512
-- end
513 514
local
f_width
=
formatters
[
"
%.6N w
"
]
515
local
f_mode
=
formatters
[
"
%i Tr
"
]
-- can be hash
516
local
f_font
=
formatters
[
"
/F%i %.6N Tf
"
]
-- can be hash
517 518
local
s_width
=
"
0 w
"
519
local
s_mode
=
"
0 Tr
"
520 521
local
function
set_font
(
)
522
-- if need_width and need_width ~= 0 then
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
-- if need_mode and need_mode ~= 0 then
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
)
-- we already have this somewhere
560
if
k
<
256
then
-- maybe 512
561
-- not sparse in this range
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
)
-- we already have this somewhere
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
)
-- ,naturalwidth,width)
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
-- if trace_threshold then
592
-- report(
593
-- "font %i, char %C, factor %i, naturalwidth %p, move %l, tm %l, hpos %p, delta %p, threshold %p, cw %p",
594
-- font,char,factor,naturalwidth[char],move,need_tm,pos_h,tj_delta,threshold,cw
595
-- )
596
-- end
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
)
-- or f_skip(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 657
end
658 659
-- literals
660 661
local
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
-- pdf_set_pos(pdf_h,pdf_v)
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
-- pdf_set_pos(pdf_h,pdf_v)
756
end
757
b
=
b
+
1
;
buffer
[
b
]
=
str
758
end
759
end
760
end
)
761 762
end
763 764
-- grouping & orientation
765 766
local
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
-- local h = pos_h - t[1]
789
-- local v = pos_v - t[2]
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
)
-- for the moment
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
"
-- 90
868
elseif
orientation
=
=
2
then
869
b
=
b
+
1
;
buffer
[
b
]
=
"
-1 0 0 -1 0 0 cm
"
-- 180
870
elseif
orientation
=
=
3
then
871
b
=
b
+
1
;
buffer
[
b
]
=
"
0 1 -1 0 0 0 cm
"
-- 270
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
-- pushorientation = function(orientation,pos_h,pos_v,pos_r)
882
-- flushsave(false,pos_h,pos_v)
883
-- if orientation == 1 then
884
-- flushsetmatrix("0 -1 1 0",pos_h,pos_v)
885
-- elseif orientation == 2 then
886
-- flushsetmatrix("-1 0 0 -1",pos_h,pos_v)
887
-- elseif orientation == 3 then
888
-- flushsetmatrix("0 1 -1 0",pos_h,pos_v)
889
-- end
890
-- end
891 892
-- poporientation = function(orientation,pos_h,pos_v,pos_r)
893
-- flushrestore(false,pos_h,pos_v)
894
-- end
895 896
end
897 898
-- rules
899 900
local
flushedxforms
=
{
}
-- actually box resources but can also be direct
901
local
localconverter
=
nil
-- will be set
902 903
local
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
-- Historically the index is an object which is kind of bad.
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
-- or dimension
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
)
-- newboxrule
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
-- a bit of a mess: index is now objnum but that has to change to a proper index
1029
-- ... an engine inheritance
1030 1031
local
function
flushpdfxform
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1032
-- object properties
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
-- or: wd, ht, dp = r.width, r.height, r.depth
1043
-- sanity check
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
-- calculate scale
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
-- flush the reference
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
-- place image also used in vf but we can use a different one if we need it
1067 1068
local
imagetypes
=
images
.
types
-- pdf png jpg jp2 jbig2 stream memstream
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
-- Groups are flushed immediately but we can decide to make them into a
1092
-- specific whatsit ... but not now. We could hash them if needed when
1093
-- we use lot sof them in mp ... but not now.
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
-- todo: access via driver in mlib-pps
1121 1122
-- end of experiment
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
-- we never set it ... so just use rotation then
1131
local
index
=
getprop
(
current
,
"
index
"
)
or
0
1132
local
kind
,
1133
xorigin
,
1134
yorigin
,
1135
xsize
,
1136
ysize
,
1137
rotation
,
-- transform / orientation / rotation : it's a mess (i need to redo this)
1138
objnum
,
1139
groupref
=
pdfincludeimage
(
index
)
-- needs to be sorted out, bad name (no longer mixed anyway)
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
-- tricky: xsize and ysize swapped
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
-- if kind == img_png then
1154
-- -- if groupref > 0 and img_page_group_val == 0 then
1155
-- -- img_page_group_val = groupref
1156
-- -- end
1157
-- end
1158
rx
,
ry
=
bpfactor
,
bpfactor
1159
end
1160 1161
if
band
(
transform
,
7
)
>
3
then
1162
-- mirror
1163
rx
,
tx
=
-
rx
,
-
tx
1164
end
1165
local
t
=
band
(
transform
+
rotation
,
3
)
1166
if
t
=
=
0
then
1167
-- nothing
1168
elseif
t
=
=
1
then
1169
-- rotation over 90 degrees (counterclockwise)
1170
rx
,
sx
,
sy
,
ry
,
tx
,
ty
=
0
,
rx
,
-
ry
,
0
,
-
ty
,
tx
1171
elseif
t
=
=
2
then
1172
-- rotation over 180 degrees (counterclockwise)
1173
rx
,
ry
,
tx
,
ty
=
-
rx
,
-
ry
,
-
tx
,
-
ty
1174
elseif
t
=
=
3
then
1175
-- rotation over 270 degrees (counterclockwise)
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
-- no transform
1196
elseif
t
=
=
1
then
1197
-- rotation over 90 degrees (counterclockwise)
1198
tx
=
tx
+
width
1199
elseif
t
=
=
2
then
1200
-- rotation over 180 degrees (counterclockwise)
1201
tx
=
tx
+
width
1202
ty
=
ty
+
total
1203
elseif
t
=
=
3
then
1204
-- rotation over 270 degrees (counterclockwise)
1205
ty
=
ty
+
total
1206
end
1207 1208
-- a flaw in original, can go:
1209
--
1210
-- if img_page_group_val == 0 then
1211
-- img_page_group_val = group_ref
1212
-- end
1213 1214
usedximages
[
index
]
=
objnum
-- hm
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
-- used in vf characters
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
-- to be sorted out
1247
-- local ty = pos_v - depth
1248
local
ty
=
pos_v
-- we assume that depth is dealt with in the caller (for now)
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
-- For the moment we need this hack because the engine checks the 'image'
1266
-- command in virtual fonts (so we use lua instead).
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
)
-- so we pass direction
1282
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1283
return
1284
end
1285 1286
pdf_goto_pagemode
(
)
1287 1288
-- local saved_b = b
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
-- buffer[saved_b] = concat(buffer," ",saved_b,b)
1320
-- b = saved_b
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 1348
end
1349 1350
--- basics
1351 1352
local
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
,
-- original number, only for diagnostics
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
-- mapping can be a hash so:
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
-- We can have this, but then via codeinjections etc. Later.
1413 1414
-- function structures.pages.swapthem()
1415
-- local n = lpdf.getnofpages()
1416
-- local t = { }
1417
-- for i=1,n do
1418
-- t[i] = i
1419
-- end
1420
-- for i=2,math.odd(n) and n or (n-1),2 do
1421
-- t[i] = i+1
1422
-- t[i+1] = i
1423
-- end
1424
-- lpdf.setpageorder(t)
1425
-- end
1426 1427
wrapup
=
function
(
driver
)
1428 1429
-- hook (to reshuffle pages)
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
-- flush page tree
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
-- ugly
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 1501
end
1502 1503
pdf_h
,
pdf_v
=
0
,
0
1504 1505
local
function
initialize
(
driver
,
details
)
1506
reset_variables
(
details
)
1507
reset_buffer
(
)
1508
end
1509 1510
-- This will all move and be merged and become less messy.
1511 1512
-- todo: more clever resource management: a bit tricky as we can inject
1513
-- stuff in the page stream
1514 1515
local
compact
=
false
1516 1517
do
1518 1519
-- This is more a convenience feature and it might even be not entirely robust.
1520
-- It removes redundant color directives which makes the page stream look a bit
1521
-- nicer (also when figuring out issues). I might add more here but there is
1522
-- some additional overhead involved so runtime can be impacted.
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
----- p_nl = S("\n\r")^1
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
----- p_cl = (p_ds * (P("rg") + P("g") + P("k") + P("RG") + P("G") + P("K")))^1
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
-- transparencies and colors before Q
1546
+
p_no_tr
*
p_no_cl
*
p_do_tr
*
p_do_cl
-- transparencies and colors before others
1547
+
p_no_cl
*
p_do_cl
-- successive colors
1548
+
p_no_tr
*
p_do_tr
-- successive transparencies
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 1572
end
1573 1574
local
flushdeferred
-- defined later
1575 1576
local
level
=
0
1577 1578
local
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
(
)
-- for now
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
)
)
-- we can overload for testing
1608
end
1609
end
1610 1611
-- messy: use real indexes for both ... so we need to change some in the
1612
-- full luatex part
1613 1614
if
next
(
usedxforms
)
or
next
(
usedximages
)
or
next
(
usedxgroups
)
then
1615
xforms
=
pdfdictionary
{
}
1616
for
k
in
sortedhash
(
usedxforms
)
do
1617
-- xforms[f_form(k)] = pdfreference(k)
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
-- finish_pdfpage_callback(shippingmode == "page")
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
(
)
-- for now here
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
-- pageattributes.Resources = pdfreference(pdfflushobject(pageresources))
1663
pageattributes
.
MediaBox
=
bbox
1664
pageattributes
.
Parent
=
nil
-- precalculate
1665
pageattributes
.
Group
=
nil
-- todo
1666 1667
-- resources can be indirect
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
-- todo: additional = resources
1717 1718
local
boxresources
=
lpdf
.
collectedresources
{
serialize
=
false
}
1719
boxresources
.
Font
=
fonts
1720
boxresources
.
XObject
=
xforms
1721 1722
-- todo: maybe share them
1723
-- wrapper.Resources = pdfreference(pdfflushobject(boxresources))
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
-- pdfflushstreamobject(content,wrapper,false,objectnumber)
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
]
-- specification.objnum
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 1760
end
1761 1762
updaters
.
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
}
1769
end
)
1770 1771
updaters
.
register
(
"
backend.update
"
,
function
(
)
1772
local
saveboxresource
=
tex
.
boxresources
.
save
1773
--
1774
-- also in lpdf-res .. brrr .. needs fixing
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
1780
end
)
1781 1782
-- now comes the pdf file handling
1783 1784
local
objects
=
{
}
1785
local
streams
=
{
}
-- maybe just parallel to objects (no holes)
1786
local
nofobjects
=
0
1787
local
offset
=
0
1788
local
f
=
false
1789
local
flush
=
false
1790
local
threshold
=
40
-- also #("/Filter /FlateDecode") (compression threshold)
1791
local
objectstream
=
true
1792
local
compress
=
true
1793
local
cache
=
false
1794
local
info
=
"
"
1795
local
catalog
=
"
"
1796
local
lastdeferred
=
false
1797
local
majorversion
=
1
1798
local
minorversion
=
7
1799
local
trailerid
=
true
1800 1801
directives
.
register
(
"
backend.pdf.threshold
"
,
function
(
v
)
1802
if
v
then
1803
threshold
=
tonumber
(
v
)
or
40
1804
else
1805
threshold
=
-1000
1806
end
1807
end
)
1808 1809
local
f_object
=
formatters
[
"
%i 0 obj\010%s\010endobj\010
"
]
1810
local
f_stream_n_u
=
formatters
[
"
%i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1811
local
f_stream_n_c
=
formatters
[
"
%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1812
local
f_stream_d_u
=
formatters
[
"
%i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1813
local
f_stream_d_c
=
formatters
[
"
%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1814
local
f_stream_d_r
=
formatters
[
"
%i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010
"
]
1815 1816
----- f_object_b = formatters["%i 0 obj\010"]
1817
local
f_stream_b_n_u
=
formatters
[
"
%i 0 obj\010<< /Length %i >>\010stream\010
"
]
1818
local
f_stream_b_n_c
=
formatters
[
"
%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010
"
]
1819
local
f_stream_b_d_u
=
formatters
[
"
%i 0 obj\010<< %s /Length %i >>\010stream\010
"
]
1820
local
f_stream_b_d_c
=
formatters
[
"
%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010
"
]
1821
local
f_stream_b_d_r
=
formatters
[
"
%i 0 obj\010<< %s >>\010stream\010
"
]
1822 1823
----- s_object_e = "\010endobj\010"
1824
local
s_stream_e
=
"
\010endstream\010endobj\010
"
1825 1826
do
1827 1828
local
function
setinfo
(
)
end
-- we get it
1829
local
function
setcatalog
(
)
end
-- we get it
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
-- needs to be sorted out
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 1868
end
1869 1870
local
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
-- uncompressed
1878
local
maxcount
=
0xFF
1879 1880
addtocache
=
function
(
n
,
str
)
1881
local
size
=
#
str
1882
if
size
=
=
0
then
1883
-- todo: message
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
-- can have a comment n 0 obj as in luatex
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
(
)
-- references cannot be stored
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 1942
end
1943 1944
local
function
pdfreserveobj
(
)
1945
nofobjects
=
nofobjects
+
1
1946
objects
[
nofobjects
]
=
false
1947
return
nofobjects
1948
end
1949 1950
local
pages
=
table
.
setmetatableindex
(
function
(
t
,
k
)
1951
local
v
=
pdfreserveobj
(
)
1952
t
[
k
]
=
v
1953
return
v
1954
end
)
1955 1956
local
function
getpageref
(
n
)
1957
return
pages
[
n
]
1958
end
1959 1960
local
function
refobj
(
)
1961
-- not needed, as we have auto-delay
1962
end
1963 1964
local
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
1983
end
1984 1985
local
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
2047
end
2048 2049
flushdeferred
=
function
(
)
-- was forward defined
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
2061
end
2062 2063
-- n = pdf.obj([n,] objtext)
2064
-- n = pdf.obj([n,] "file", filename)
2065
-- n = pdf.obj([n,] "stream", streamtext [, attrtext])
2066
-- n = pdf.obj([n,] "streamfile", filename [, attrtext])
2067
--
2068
-- n = pdf.obj {
2069
-- type = <string>, -- raw|stream
2070
-- immediate = <boolean>,
2071
-- objnum = <number>,
2072
-- attr = <string>,
2073
-- compresslevel = <number>,
2074
-- objcompression = <boolean>,
2075
-- file = <string>,
2076
-- string = <string>,
2077
-- nolength = <boolean>,
2078
-- }
2079 2080
local
function
obj
(
a
,
b
,
c
,
d
)
2081
local
kind
--, immediate
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
-- raw | stream
2087
-- immediate = a.immediate
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
-- kind = "raw"
2124
data
=
loaddata
(
b
)
2125
elseif
a
=
=
"
streamfile
"
then
2126
kind
=
"
stream
"
2127
data
=
loaddata
(
b
)
2128
else
2129
data
=
"
"
-- invalid object
2130
end
2131
attr
=
c
2132
else
2133
-- kind = "raw"
2134
data
=
a
2135
end
2136
end
2137
if
not
objnum
then
2138
nofobjects
=
nofobjects
+
1
2139
objnum
=
nofobjects
2140
end
2141
-- todo: immediate
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
2150
end
2151 2152
updaters
.
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
2160
end
)
2161 2162
-- In lua 5.4 the methods are now moved one metalevel deeper so we need to get them
2163
-- from mt.__index instead. (I did get that at first.) It makes for a slightly (imo)
2164
-- nicer interface but no real gain in speed as we don't flush that often.
2165 2166
local
openfile
,
closefile
do
2167 2168
-- I used to do <space><lf> but then figured out that when I open and save a file in a mode
2169
-- that removes trailing spaces, the xref becomes invalid. The problem was then that a
2170
-- reconstruction of the file by a viewer gives weird effects probably because percent symbols
2171
-- gets interpreted then. Thanks to Ross Moore for noticing this side effect!
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
-- local n = 0
2199
-- f = {
2200
-- write = function(self,s)
2201
-- n = n + 1 f[n] = s
2202
-- end,
2203
-- close = function(self)
2204
-- f = concat(f)
2205
-- io.savedata(filename,f)
2206
-- f = false
2207
-- end,
2208
-- }
2209
else
2210
f
=
io
.
open
(
filename
,
"
wb
"
)
2211
if
not
f
then
2212
-- message
2213
os
.
exit
(
)
2214
end
2215
-- f:setvbuf("full",64*1024)
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
-- local b = "%\xCC\xD5\xC1\xD4\xC5\xD8\xD0\xC4\xC6\010" -- LUATEXPDF (+128)
2222
local
b
=
"
%\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010
"
-- CONTEXTPDF (+128)
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
-- + 1
2268
--
2269
-- combine these three in one doesn't really give less code so
2270
-- we go for the efficient ones
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 2435
end
2436 2437
-- For the moment we overload it here, although back-fil.lua eventually will
2438
-- be merged with back-pdf as it's pdf specific, or maybe back-imp-pdf or so.
2439 2440
updaters
.
register
(
"
backend.update.pdf
"
,
function
(
)
2441 2442
-- We overload img but at some point it will even go away, so we just
2443
-- reimplement what we need in context. This will change completely i.e.
2444
-- we will drop the low level interface!
2445 2446
local
codeinjections
=
backends
.
pdf
.
codeinjections
2447 2448
local
imagetypes
=
images
.
types
-- pdf png jpg jp2 jbig2 stream memstream
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
-- We cannot set type in native img so we need this hack or
2510
-- otherwise we need to patch too much. Better that i write
2511
-- a wrapper then. Anyway, it has to be done better: a key that
2512
-- tells either or not to scale by xsize/ysize when flushing.
2513
--
2514
if
not
specification
.
type
then
2515
local
kind
=
specification
.
kind
2516
if
kind
then
2517
-- take that one
2518
elseif
attr
and
find
(
attr
,
"
BBox
"
)
then
2519
kind
=
img_stream
2520
else
2521
-- hack: a bitmap
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
-- better create a real 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
-- newimagerule
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
-- we need the original ones, not the 'rotated' ones
2567
local
ysize
=
bbox
[
4
]
-
yorigin
-- we need the original ones, not the 'rotated' ones
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
-- determines scaling type
2572
return
2573
kind
,
2574
xorigin
,
yorigin
,
2575
xsize
,
ysize
,
2576
transform
,
2577
objnum
,
2578
groupref
2579
end
2580
end
2581 2582
end
)
2583 2584
updaters
.
register
(
"
backend.update.lpdf
"
,
function
(
)
2585 2586
-- todo: an md5 or sha2 hash can save space
2587
-- todo: make a type 3 font instead
2588
-- todo: move to lpdf namespace
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
-- pdf.print or pdf.literal
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
-- We need to have a way to close such a pdf ... esp for fonts.
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 2678
end
)
2679 2680
-- The driver.
2681 2682
do
2683 2684
local
isfile
=
lfs
.
isfile
2685
local
removefile
=
os
.
remove
2686
local
renamefile
=
os
.
rename
2687
-- local copyfile = file.copy
2688
-- local addsuffix = file.addsuffix
2689
local
texgetbox
=
tex
.
getbox
2690 2691
local
pdfname
=
nil
2692
local
converter
=
nil
2693
local
useddriver
=
nil
-- a bit of a hack
2694 2695
local
function
outputfilename
(
driver
)
2696
return
pdfname
2697
end
2698 2699
-- todo: prevent twice
2700 2701
local
function
prepare
(
driver
)
2702
if
not
environment
.
initex
then
2703
-- install new functions in pdf namespace
2704
updaters
.
apply
(
"
backend.update.pdf
"
)
2705
-- install new functions in lpdf namespace
2706
updaters
.
apply
(
"
backend.update.lpdf
"
)
2707
-- adapt existing shortcuts to lpdf namespace
2708
updaters
.
apply
(
"
backend.update.tex
"
)
2709
-- adapt existing shortcuts to tex namespace
2710
updaters
.
apply
(
"
backend.update
"
)
2711
--
2712
-- if rawget(pdf,"setforcefile") then
2713
-- pdf.setforcefile(false) -- default anyway
2714
-- end
2715
--
2716
-- pdfname = file.addsuffix(tex.jobname,"pdf")
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 2797
end
2798