lpdf-lmt.lmt /size: 93 Kb    last modification: 2021-10-28 13:51
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
lpdf-lmt
'
]
=
{
2
version
=
1
.
001
,
3
optimize
=
true
,
4
comment
=
"
companion to lpdf-ini.mkiv
"
,
5
author
=
"
Hans Hagen, PRAGMA-ADE, Hasselt NL
"
,
6
copyright
=
"
PRAGMA ADE / ConTeXt Development Team
"
,
7
license
=
"
see context related readme files
"
8
}
9 10
-- The code below was originally in back-lpd.lua but it makes more sense in this
11
-- namespace. I will rename variables.
12 13
-- There is no way that a lua based backend can compete performance wise with the
14
-- original one for relative simple text runs. And we're talking seconds here on say
15
-- 500 pages with paragraphs alternativng between three fonts and colors. But such
16
-- documents are rare so in practice we are quite okay, especially because in
17
-- ConTeXt we can gain quite a bit elsewhere. So, when we loose 30% on such simple
18
-- documents, we break even on for instance the manual, and gain 30% on Thomas's
19
-- turture test (also for other reasons). But .. who knows what magic I can cook up
20
-- in due time.
21 22
-- If you consider this complex, watch:
23
--
24
-- https://www.youtube.com/watch?v=6H-cAzfB2qo
25
--
26
-- or in distractionmode:
27
--
28
-- https://www.youtube.com/watch?v=TYuTE_1jvvE
29
-- https://www.youtube.com/watch?v=nnicGKX3lvM
30
--
31
-- For the moment we have to support the built in backend as well as the alternative. So
32
-- the next interface is suboptimal and will change at some time. At that moment I will
33
-- also optimize and extend.
34 35
local
type
,
next
,
unpack
,
tonumber
,
rawget
=
type
,
next
,
unpack
,
tonumber
,
rawget
36
local
char
,
rep
,
find
=
string
.
char
,
string
.
rep
,
string
.
find
37
local
formatters
,
splitupstring
=
string
.
formatters
,
string
.
splitup
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
osuuid
=
os
.
uuid
45
local
zlibcompresssize
=
xzip
.
compresssize
46 47
local
nuts
=
nodes
.
nuts
48
local
tonut
=
nodes
.
tonut
49
local
tonode
=
nuts
.
tonode
50 51
local
pdfreference
=
lpdf
.
reference
52
local
pdfdictionary
=
lpdf
.
dictionary
53
local
pdfarray
=
lpdf
.
array
54
local
pdfconstant
=
lpdf
.
constant
55
local
pdfliteral
=
lpdf
.
literal
-- not to be confused with a whatsit!
56 57
local
pdfreserveobject
-- forward reference
58
local
pdfpagereference
-- forward reference
59
local
pdfgetpagereference
-- forward reference
60
local
pdfsharedobject
-- forward reference
61
local
pdfflushobject
-- forward reference
62
local
pdfflushstreamobject
-- forward reference
63
local
pdfdeferredobject
-- forward reference
64
local
pdfimmediateobject
-- forward reference
65 66
local
pdfincludeimage
-- forward reference
67 68
local
pdf_pages
=
pdfconstant
(
"
Pages
"
)
69
local
pdf_page
=
pdfconstant
(
"
Page
"
)
70
local
pdf_xobject
=
pdfconstant
(
"
XObject
"
)
71
local
pdf_form
=
pdfconstant
(
"
Form
"
)
72
local
pdf_pattern
=
pdfconstant
(
"
Pattern
"
)
73 74
local
fonthashes
=
fonts
.
hashes
75
local
characters
=
fonthashes
.
characters
76
local
descriptions
=
fonthashes
.
descriptions
77
local
parameters
=
fonthashes
.
parameters
78
local
properties
=
fonthashes
.
properties
79 80
local
report
=
logs
.
reporter
(
"
backend
"
)
81
local
report_objects
=
logs
.
reporter
(
"
backend
"
,
"
objects
"
)
82
local
report_fonts
=
logs
.
reporter
(
"
backend
"
,
"
fonts
"
)
83 84
local
trace_objects
=
false
trackers
.
register
(
"
backend.objects
"
,
function
(
v
)
trace_objects
=
v
end
)
85
local
trace_details
=
false
trackers
.
register
(
"
backend.details
"
,
function
(
v
)
trace_details
=
v
end
)
86
local
trace_indices
=
false
trackers
.
register
(
"
backend.fonts.details
"
,
function
(
v
)
trace_indices
=
v
end
)
87 88
-- These two tables used a font id as index and will be metatabled in lpdf-emb.lmt:
89 90
local
usedfontnames
=
{
}
91
local
usedfontobjects
=
{
}
92 93
lpdf
.
usedfontnames
=
usedfontnames
94
lpdf
.
usedfontobjects
=
usedfontobjects
95 96
-- experiment:
97 98
local
function
compressdata
(
data
,
size
)
99
local
guess
=
(
(
size
/
/
4096
)
+
1
)
*
2048
100
local
comp
=
zlibcompresssize
(
data
,
guess
,
3
)
101
-- if comp then
102
-- report()
103
-- report("size %i, guess %i, result %i => %s / %s",size,guess,#comp,guess>=#comp and "hit" or "miss")
104
-- report()
105
-- end
106
return
comp
107
end
108 109
-- local function compressdata(data,size)
110
-- return zlibcompress(data,3)
111
-- end
112 113
-- we collect them:
114 115
local
flushers
=
{
}
116 117
-- used variables
118 119
local
pdf_h
,
pdf_v
120
local
need_tm
,
need_tf
,
cur_tmrx
,
cur_factor
,
cur_f
,
cur_e
121
local
need_width
,
need_mode
,
done_width
,
done_mode
122
local
mode
123
local
f_pdf_cur
,
f_pdf
,
fs_cur
,
fs
,
f_cur
,
f_x_scale
,
f_y_scale
124
local
tj_delta
,
cw
125
local
usedfonts
,
usedxforms
,
usedximages
,
usedxgroups
126
local
getxformname
,
getximagename
127
local
boundingbox
,
shippingmode
,
objectnumber
128
local
tmrx
,
tmry
,
tmsx
,
tmsy
,
tmtx
,
tmty
129
local
cmrx
,
cmry
,
cmsx
,
cmsy
,
cmtx
,
cmty
130
local
tmef
131 132
local
function
usefont
(
t
,
k
)
-- a bit redundant hash
133
-- local v = pdfgetfontname(k)
134
local
v
=
usedfontnames
[
k
]
135
t
[
k
]
=
v
136
return
v
137
end
138 139
local
function
reset_variables
(
specification
)
140
pdf_h
,
pdf_v
=
0
,
0
141
cmrx
,
cmry
=
1
.
0
,
1
.
0
142
cmsx
,
cmsy
=
0
.
0
,
0
.
0
143
cmtx
,
cmty
=
0
.
0
,
0
.
0
144
tmrx
,
tmry
=
1
.
0
,
1
.
0
145
tmsx
,
tmsy
=
0
.
0
,
0
.
0
146
tmtx
,
tmty
=
0
.
0
,
0
.
0
147
tmef
=
1
.
0
148
need_tm
=
false
149
need_tf
=
false
150
need_width
=
0
151
need_mode
=
0
152
done_width
=
false
153
done_mode
=
false
154
mode
=
"
page
"
155
shippingmode
=
specification
.
shippingmode
156
objectnumber
=
specification
.
objectnumber
157
cur_tmrx
=
0
.
0
158
f_cur
=
0
159
f_pdf_cur
=
0
-- nullfont
160
f_pdf
=
0
-- nullfont
161
fs_cur
=
0
162
fs
=
0
163
f_x_scale
=
1
.
0
164
f_y_scale
=
1
.
0
165
cur_factor
=
0
166
cur_f
=
false
-- todo: check for nil vs false
167
cur_e
=
false
-- todo: check for nil vs false
168
tj_delta
=
0
.
0
169
cw
=
0
.
0
170
usedfonts
=
setmetatableindex
(
usefont
)
171
usedxforms
=
{
}
172
usedximages
=
{
}
173
-- usedxgroups = { }
174
boundingbox
=
specification
.
boundingbox
175
end
176 177
-- buffer
178 179
local
buffer
=
lua
.
newtable
(
1024
,
0
)
-- { }
180
local
b
=
0
181 182
local
function
reset_buffer
(
)
183
b
=
0
184
end
185 186
-- fonts
187 188
local
fontcharacters
189
local
fontdescriptions
190
local
fontparameters
191
local
fontproperties
192
local
pdfcharacters
193 194
local
getstreamhash
=
fonts
.
handlers
.
otf
.
getstreamhash
195 196
local
usedfontstreams
=
{
}
197 198
local
usedindices
=
setmetatableindex
(
function
(
t
,
k
)
199
local
n
=
0
200
local
v
=
setmetatableindex
(
function
(
tt
,
kk
)
201
if
n
>
=
0xFFFF
then
202
report_fonts
(
"
registering character index: overflow in hash %a, todo: use overflow font
"
)
203
else
204
n
=
n
+
1
205
end
206
if
trace_indices
then
207
report_fonts
(
"
registering character index: hash %a, charindex 0x%05X, slotindex 0x%04X
"
,
k
,
kk
,
n
)
208
end
209
local
vv
=
n
210
tt
[
kk
]
=
vv
211
return
vv
212
end
)
213
t
[
k
]
=
v
214
return
v
215
end
)
216 217
local
usedcharacters
=
setmetatableindex
(
function
(
t
,
k
)
218
local
h
,
d
=
getstreamhash
(
k
)
219
if
trace_indices
then
220
report_fonts
(
"
registering index table: hash %a, fontid %i
"
,
h
,
k
)
221
end
222
usedfontstreams
[
h
]
=
d
223
local
v
=
usedindices
[
h
]
224
t
[
k
]
=
v
225
return
v
226
end
)
227 228
lpdf
.
usedfontstreams
=
usedfontstreams
-- [streamhash] -> fontdata
229
lpdf
.
usedcharacters
=
usedcharacters
-- [fontid] -> indices
230
lpdf
.
usedindices
=
usedindices
-- [streamhash][index] -> realindex (can also be dupindex)
231 232
local
horizontalmode
=
true
233
local
scalefactor
=
1
234
local
threshold
=
655360
235
local
thresfactor
=
100
236
local
tjfactor
=
100
/
65536
237 238
function
flushers
.
updatefontstate
(
font
)
239
fontcharacters
=
characters
[
font
]
240
fontdescriptions
=
descriptions
[
font
]
241
fontparameters
=
parameters
[
font
]
242
fontproperties
=
properties
[
font
]
243
local
size
=
fontparameters
.
size
-- or bad news
244
local
designsize
=
fontparameters
.
designsize
or
size
245
pdfcharacters
=
usedcharacters
[
font
]
246
horizontalmode
=
fontparameters
.
writingmode
~
=
"
vertical
"
247
scalefactor
=
(
designsize
/
size
)
*
tjfactor
248
--
249
local
fthreshold
=
fontproperties
.
threshold
250
threshold
=
(
fthreshold
and
(
size
*
fthreshold
/
100
)
)
or
655360
251
-- when we bolden the threshold should be smaller .. a hack .. i need to redo all this
252
if
(
fontparameters
.
extendfactor
or
1
)
=
=
1
then
253
-- we're probably okay
254
elseif
fontparameters
.
hshift
or
fontparameters
.
vshift
then
255
-- we could be okay
256
else
257
-- some vf magic going on
258
threshold
=
threshold
/
5
259
end
260
end
261 262
-- helpers
263 264
local
f_cm
=
formatters
[
"
%.6N %.6N %.6N %.6N %.6N %.6N cm
"
]
265
local
f_tm
=
formatters
[
"
%.6N %.6N %.6N %.6N %.6N %.6N Tm
"
]
266 267
local
saved_text_pos_v
=
0
268
local
saved_text_pos_h
=
0
269 270
local
function
begin_text
(
)
271
saved_text_pos_h
=
pdf_h
272
saved_text_pos_v
=
pdf_v
273
b
=
b
+
1
;
buffer
[
b
]
=
"
BT
"
274
need_tf
=
true
275
need_width
=
0
276
need_mode
=
0
277
mode
=
"
text
"
278
end
279 280
local
function
end_text
(
)
281
if
done_width
then
282
b
=
b
+
1
;
buffer
[
b
]
=
"
0 w
"
283
done_width
=
false
284
end
285
if
done_mode
then
286
b
=
b
+
1
;
buffer
[
b
]
=
"
0 Tr
"
287
done_mode
=
false
288
end
289
b
=
b
+
1
;
buffer
[
b
]
=
"
ET
"
290
pdf_h
=
saved_text_pos_h
291
pdf_v
=
saved_text_pos_v
292
mode
=
"
page
"
293
end
294 295
local
saved_chararray_pos_h
296
local
saved_chararray_pos_v
297 298
local
saved_b
=
0
299 300
local
function
begin_chararray
(
)
301
saved_chararray_pos_h
=
pdf_h
302
saved_chararray_pos_v
=
pdf_v
303
cw
=
horizontalmode
and
saved_chararray_pos_h
or
-
saved_chararray_pos_v
304
tj_delta
=
0
305
saved_b
=
b
306
b
=
b
+
1
;
buffer
[
b
]
=
"
[
"
307
mode
=
"
chararray
"
308
end
309 310
local
function
end_chararray
(
)
311
b
=
b
+
1
;
buffer
[
b
]
=
"
] TJ
"
312
buffer
[
saved_b
]
=
concat
(
buffer
,
"
"
,
saved_b
,
b
)
313
b
=
saved_b
314
pdf_h
=
saved_chararray_pos_h
315
pdf_v
=
saved_chararray_pos_v
316
mode
=
"
text
"
317
end
318 319
local
function
begin_charmode
(
)
320
b
=
b
+
1
;
buffer
[
b
]
=
"
<
"
321
mode
=
"
char
"
322
end
323 324
local
function
end_charmode
(
)
325
b
=
b
+
1
;
buffer
[
b
]
=
"
>
"
326
mode
=
"
chararray
"
327
end
328 329
local
function
calc_pdfpos
(
h
,
v
)
330
-- mostly char
331
if
mode
=
=
"
page
"
then
332
cmtx
=
h
-
pdf_h
333
cmty
=
v
-
pdf_v
334
return
h
~
=
pdf_h
or
v
~
=
pdf_v
335
elseif
mode
=
=
"
text
"
then
336
tmtx
=
h
-
saved_text_pos_h
337
tmty
=
v
-
saved_text_pos_v
338
return
h
~
=
pdf_h
or
v
~
=
pdf_v
339
elseif
horizontalmode
then
340
tmty
=
v
-
saved_text_pos_v
341
tj_delta
=
cw
-
h
342
return
tj_delta
~
=
0
or
v
~
=
pdf_v
343
else
344
tmtx
=
h
-
saved_text_pos_h
345
tj_delta
=
cw
+
v
346
return
tj_delta
~
=
0
or
h
~
=
pdf_h
347
end
348
end
349 350
local
function
pdf_set_pos
(
h
,
v
)
351
local
move
=
calc_pdfpos
(
h
,
v
)
352
if
move
then
353
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
cmrx
,
cmsx
,
cmsy
,
cmry
,
cmtx
*
bpfactor
,
cmty
*
bpfactor
)
354
pdf_h
=
pdf_h
+
cmtx
355
pdf_v
=
pdf_v
+
cmty
356
end
357
end
358 359
local
function
pdf_reset_pos
(
)
360
if
mode
=
=
"
page
"
then
361
cmtx
=
-
pdf_h
362
cmty
=
-
pdf_v
363
if
pdf_h
=
=
0
and
pdf_v
=
=
0
then
364
return
365
end
366
elseif
mode
=
=
"
text
"
then
367
tmtx
=
-
saved_text_pos_h
368
tmty
=
-
saved_text_pos_v
369
if
pdf_h
=
=
0
and
pdf_v
=
=
0
then
370
return
371
end
372
elseif
horizontalmode
then
373
tmty
=
-
saved_text_pos_v
374
tj_delta
=
cw
375
if
tj_delta
=
=
0
and
pdf_v
=
=
0
then
376
return
377
end
378
else
379
tmtx
=
-
saved_text_pos_h
380
tj_delta
=
cw
381
if
tj_delta
=
=
0
and
pdf_h
=
=
0
then
382
return
383
end
384
end
385
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
cmrx
,
cmsx
,
cmsy
,
cmry
,
cmtx
*
bpfactor
,
cmty
*
bpfactor
)
386
pdf_h
=
pdf_h
+
cmtx
387
pdf_v
=
pdf_v
+
cmty
388
end
389 390
local
function
pdf_set_pos_temp
(
h
,
v
)
391
local
move
=
calc_pdfpos
(
h
,
v
)
392
if
move
then
393
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
cmrx
,
cmsx
,
cmsy
,
cmry
,
cmtx
*
bpfactor
,
cmty
*
bpfactor
)
394
end
395
end
396 397
-- these dummy returns makes using them a bit faster
398 399
local
function
pdf_end_string_nl
(
)
400
if
mode
=
=
"
char
"
then
401
end_charmode
(
)
402
return
end_chararray
(
)
403
elseif
mode
=
=
"
chararray
"
then
404
return
end_chararray
(
)
405
end
406
end
407 408
local
function
pdf_goto_textmode
(
)
409
if
mode
=
=
"
page
"
then
410
pdf_reset_pos
(
)
411
return
begin_text
(
)
412
elseif
mode
~
=
"
text
"
then
413
if
mode
=
=
"
char
"
then
414
end_charmode
(
)
415
return
end_chararray
(
)
416
else
-- if mode == "chararray" then
417
return
end_chararray
(
)
418
end
419
end
420
end
421 422
local
function
pdf_goto_pagemode
(
)
423
if
mode
~
=
"
page
"
then
424
if
mode
=
=
"
char
"
then
425
end_charmode
(
)
426
end_chararray
(
)
427
return
end_text
(
)
428
elseif
mode
=
=
"
chararray
"
then
429
end_chararray
(
)
430
return
end_text
(
)
431
elseif
mode
=
=
"
text
"
then
432
return
end_text
(
)
433
end
434
end
435
end
436 437
local
function
pdf_goto_fontmode
(
)
438
if
mode
=
=
"
char
"
then
439
end_charmode
(
)
440
end_chararray
(
)
441
end_text
(
)
442
elseif
mode
=
=
"
chararray
"
then
443
end_chararray
(
)
444
end_text
(
)
445
elseif
mode
=
=
"
text
"
then
446
end_text
(
)
447
end
448
pdf_reset_pos
(
)
449
mode
=
"
page
"
450
end
451 452
-- characters
453
do
454 455
local
round
=
math
.
round
456 457
-- across pages ... todo: clean up because we don't need to pass the font
458
-- as fontparameters already has checked / set it we can also have a variable
459
-- for it so
460 461
local
naturalwidth
=
nil
462
local
hshift
=
false
463
local
vshift
=
false
464 465
-- The width array uses the original dimensions! This is different from e.g.
466
-- luatex where we have more widths arrays and these reflect the cheated
467
-- widths (goes wrong elsewhere).
468 469
-- when changing this, check math: compact-001.tex (rule width)
470 471
local
naturalwidths
=
setmetatableindex
(
function
(
t
,
font
)
472
local
d
=
descriptions
[
font
]
473
local
c
=
characters
[
font
]
474
local
f
=
parameters
[
font
]
.
hfactor
or
parameters
[
font
]
.
factor
475
local
v
=
setmetatableindex
(
function
(
t
,
char
)
476
local
w
477
local
e
=
c
and
c
[
char
]
478
if
e
then
479
w
=
e
.
width
or
0
480
end
481
if
not
w
then
482
e
=
d
and
d
[
char
]
483
if
e
then
484
w
=
e
.
width
485
if
w
then
486
w
=
w
*
f
487
end
488
end
489
end
490
if
not
w
then
491
w
=
0
492
end
493
t
[
char
]
=
w
494
return
w
495
end
)
496
t
[
font
]
=
v
497
return
v
498
end
)
499 500
local
function
setup_fontparameters
(
font
,
factor
,
f
,
e
,
sx
,
sy
)
501
local
slant
=
fontparameters
.
slantfactor
or
0
502
local
extend
=
fontparameters
.
extendfactor
or
1
503
local
squeeze
=
fontparameters
.
squeezefactor
or
1
504
local
expand
=
1
+
factor
/
1000000
505
local
format
=
fontproperties
.
format
506
if
e
then
507
extend
=
extend
*
e
508
end
509
tmef
=
expand
510
tmrx
=
expand
*
extend
511
tmsy
=
slant
512
tmry
=
squeeze
513
need_width
=
fontparameters
.
width
or
0
514
need_mode
=
fontparameters
.
mode
or
0
515
f_cur
=
font
516
f_pdf
=
usedfonts
[
font
]
-- cache
517
cur_factor
=
factor
518
cur_f
=
f
519
cur_e
=
e
520
tj_delta
=
0
521
cw
=
0
522
f_x_scale
=
1
.
0
523
f_y_scale
=
1
.
0
524
fs
=
fontparameters
.
size
*
bpfactor
525
if
f
then
526
fs
=
fs
*
f
527
end
528
-- kind of special:
529
if
format
=
=
"
opentype
"
or
format
=
=
"
type1
"
then
530
fs
=
fs
*
1000
/
fontparameters
.
units
-- can we avoid this ?
531
end
532
--
533
f_x_scale
=
sx
534
if
f_x_scale
~
=
1
.
0
then
535
tmrx
=
tmrx
*
f_x_scale
536
end
537
f_y_scale
=
sy
538
if
f_y_scale
~
=
1
.
0
then
539
tmry
=
tmry
*
f_y_scale
540
end
541
--
542
naturalwidth
=
naturalwidths
[
font
]
543
--
544
hshift
=
fontparameters
.
hshift
545
vshift
=
fontparameters
.
vshift
546
end
547 548
local
f_width
=
formatters
[
"
%.6N w
"
]
549
local
f_mode
=
formatters
[
"
%i Tr
"
]
-- can be hash
550
local
f_font
=
formatters
[
"
/F%i %.6N Tf
"
]
-- can be hash
551 552
local
s_width
=
"
0 w
"
553
local
s_mode
=
"
0 Tr
"
554 555
local
width_factor
=
72
.
27
/
72000
.
0
556 557
local
function
set_font
(
)
558
-- if need_width and need_width ~= 0 then
559
if
need_width
~
=
0
then
560
b
=
b
+
1
;
buffer
[
b
]
=
f_width
(
width_factor
*
need_width
)
561
done_width
=
true
562
elseif
done_width
then
563
b
=
b
+
1
;
buffer
[
b
]
=
s_width
564
done_width
=
false
565
end
566
-- if need_mode and need_mode ~= 0 then
567
if
need_mode
~
=
0
then
568
b
=
b
+
1
;
buffer
[
b
]
=
f_mode
(
need_mode
)
569
done_mode
=
true
570
elseif
done_mode
then
571
b
=
b
+
1
;
buffer
[
b
]
=
s_mode
572
done_mode
=
false
573
end
574
b
=
b
+
1
;
buffer
[
b
]
=
f_font
(
f_pdf
,
fs
)
575
f_pdf_cur
=
f_pdf
576
fs_cur
=
fs
577
need_tf
=
false
578
need_tm
=
true
579
end
580 581
local
function
set_textmatrix
(
h
,
v
)
582
local
move
=
calc_pdfpos
(
h
,
v
)
583
if
need_tm
or
move
then
584
b
=
b
+
1
;
buffer
[
b
]
=
f_tm
(
tmrx
,
tmsx
,
tmsy
,
tmry
,
tmtx
*
bpfactor
,
tmty
*
bpfactor
)
585
pdf_h
=
saved_text_pos_h
+
tmtx
586
pdf_v
=
saved_text_pos_v
+
tmty
587
need_tm
=
false
588
end
589
cur_tmrx
=
tmrx
590
end
591 592
local
f_hex_4
=
formatters
[
"
%04X
"
]
593
local
f_hex_2
=
formatters
[
"
%02X
"
]
594 595
local
h_hex_4
=
setmetatableindex
(
function
(
t
,
k
)
-- we already have this somewhere
596
if
k
<
256
then
-- maybe 512
597
-- not sparse in this range
598
for
i
=
0
,
255
do
599
t
[
i
]
=
f_hex_4
(
i
)
600
end
601
return
t
[
k
]
602
else
603
local
v
=
f_hex_4
(
k
)
604
t
[
k
]
=
v
605
return
v
606
end
607
end
)
608
local
h_hex_2
=
setmetatableindex
(
function
(
t
,
k
)
-- we already have this somewhere
609
local
v
=
k
<
256
and
f_hex_2
(
k
)
or
"
00
"
610
t
[
k
]
=
v
611
return
v
612
end
)
613 614
-- local trace_threshold = false trackers.register("backends.pdf.threshold", function(v) trace_threshold = v end)
615 616
-- local f_skip = formatters["%.2N"]
617 618
-- I will redo this mess ... we no longer have the mkiv pdf generator that we used in
619
-- luatex (a precursor to lmtx and also for comparison) but only in lmtx now so ...
620
-- time to move on I guess.
621 622
flushers
.
character
=
function
(
current
,
pos_h
,
pos_v
,
pos_r
,
font
,
char
,
data
,
f
,
e
,
factor
,
sx
,
sy
)
-- ,naturalwidth,width)
623
if
sx
~
=
f_x_scale
or
sy
~
=
f_y_scale
or
need_tf
or
font
~
=
f_cur
or
f_pdf
~
=
f_pdf_cur
or
fs
~
=
fs_cur
or
mode
=
=
"
page
"
then
624
pdf_goto_textmode
(
)
625
setup_fontparameters
(
font
,
factor
,
f
,
e
,
sx
,
sy
)
626
set_font
(
)
627
elseif
cur_f
~
=
f
then
-- when ok move up
628
pdf_goto_textmode
(
)
629
setup_fontparameters
(
font
,
factor
,
f
,
e
,
sx
,
sy
)
630
set_font
(
)
631
-- elseif cur_tmrx ~= tmrx or cur_factor ~= factor or cur_f ~= f or cur_e ~= e then
632
elseif
cur_tmrx
~
=
tmrx
or
cur_factor
~
=
factor
or
cur_e
~
=
e
then
633
setup_fontparameters
(
font
,
factor
,
f
,
e
,
sx
,
sy
)
634
need_tm
=
true
635
end
636 637
local
move
=
calc_pdfpos
(
pos_h
,
pos_v
)
638 639
if
trace_threshold
then
640
report_fonts
(
641
"
before: font %i, char %C, factor %i, naturalwidth %p, move %l, tm %l, hpos %p, delta %p, threshold %p, cw %p
"
,
642
font
,
char
,
factor
,
naturalwidth
[
char
]
,
move
,
need_tm
,
pos_h
,
tj_delta
,
threshold
,
cw
643
)
644
end
645 646
if
move
or
need_tm
then
647
if
not
need_tm
then
648
if
horizontalmode
then
649
if
(
saved_text_pos_v
+
tmty
)
~
=
pdf_v
then
650
need_tm
=
true
651
elseif
tj_delta
>
=
threshold
or
tj_delta
<
=
-
threshold
then
652
need_tm
=
true
653
end
654
else
655
if
(
saved_text_pos_h
+
tmtx
)
~
=
pdf_h
then
656
need_tm
=
true
657
elseif
tj_delta
>
=
threshold
or
tj_delta
<
=
-
threshold
then
658
need_tm
=
true
659
end
660
end
661
end
662 663
if
hshift
then
pos_h
=
pos_h
+
hshift
end
664
if
vshift
then
pos_v
=
pos_v
-
vshift
end
665 666
if
need_tm
then
667
pdf_goto_textmode
(
)
668
set_textmatrix
(
pos_h
,
pos_v
)
669
begin_chararray
(
)
670
move
=
calc_pdfpos
(
pos_h
,
pos_v
)
671
end
672
if
move
then
673
local
d
=
tj_delta
*
scalefactor
/
f_x_scale
674
if
d
<
=
-0
.
5
or
d
>
=
0
.
5
then
675
if
mode
=
=
"
char
"
then
676
end_charmode
(
)
677
end
678
b
=
b
+
1
;
buffer
[
b
]
=
round
(
d
)
-- or f_skip(d)
679
end
680
cw
=
cw
-
tj_delta
681
end
682
end
683 684
if
trace_threshold
then
685
report_fonts
(
686
"
after : font %i, char %C, factor %i, naturalwidth %p, move %l, tm %l, hpos %p, delta %p, threshold %p, cw %p
"
,
687
font
,
char
,
factor
,
naturalwidth
[
char
]
,
move
,
need_tm
,
pos_h
,
tj_delta
,
threshold
,
cw
688
)
689
end
690 691
if
mode
=
=
"
chararray
"
then
692
begin_charmode
(
)
693
end
694 695
cw
=
cw
+
naturalwidth
[
char
]
*
tmef
*
f_x_scale
696 697
local
slot
=
pdfcharacters
[
data
.
index
or
char
]
-- registers usage
698 699
b
=
b
+
1
;
buffer
[
b
]
=
font
>
0
and
h_hex_4
[
slot
]
or
h_hex_2
[
slot
]
700 701
end
702 703
flushers
.
fontchar
=
function
(
font
,
char
,
data
)
704
local
dummy
=
usedfonts
[
font
]
705
local
slot
=
pdfcharacters
[
data
.
index
or
char
]
-- registers usage
706
return
dummy
707
end
708 709
end
710 711
-- literals
712 713
local
flushliteral
do
714 715
local
nodeproperties
=
nodes
.
properties
.
data
716
local
literalvalues
=
nodes
.
literalvalues
717 718
local
originliteral_code
=
literalvalues
.
origin
719
local
pageliteral_code
=
literalvalues
.
page
720
local
alwaysliteral_code
=
literalvalues
.
always
721
local
rawliteral_code
=
literalvalues
.
raw
722
local
textliteral_code
=
literalvalues
.
text
723
local
fontliteral_code
=
literalvalues
.
font
724 725
flushliteral
=
function
(
current
,
pos_h
,
pos_v
)
726
local
p
=
nodeproperties
[
current
]
727
if
p
then
728
local
str
=
p
.
data
729
if
str
and
str
~
=
"
"
then
730
local
mode
=
p
.
mode
731
if
mode
=
=
originliteral_code
then
732
pdf_goto_pagemode
(
)
733
pdf_set_pos
(
pos_h
,
pos_v
)
734
elseif
mode
=
=
pageliteral_code
then
735
pdf_goto_pagemode
(
)
736
elseif
mode
=
=
textliteral_code
then
737
pdf_goto_textmode
(
)
738
elseif
mode
=
=
fontliteral_code
then
739
pdf_goto_fontmode
(
)
740
elseif
mode
=
=
alwaysliteral_code
then
-- aka direct
741
pdf_end_string_nl
(
)
742
need_tm
=
true
743
elseif
mode
=
=
rawliteral_code
then
744
pdf_end_string_nl
(
)
745
else
746
report
(
"
invalid literal mode %a when flushing %a
"
,
mode
,
str
)
747
return
748
end
749
b
=
b
+
1
;
buffer
[
b
]
=
str
750
end
751
end
752
end
753 754
flushers
.
literal
=
flushliteral
755 756
function
lpdf
.
print
(
mode
,
str
)
757
-- This only works inside objects, don't change this to flush
758
-- in between. It's different from luatex but okay.
759
if
str
then
760
mode
=
literalvalues
[
mode
]
761
else
762
mode
,
str
=
originliteral_code
,
mode
763
end
764
if
str
and
str
~
=
"
"
then
765
if
mode
=
=
originliteral_code
then
766
pdf_goto_pagemode
(
)
767
-- pdf_set_pos(pdf_h,pdf_v)
768
elseif
mode
=
=
pageliteral_code
then
769
pdf_goto_pagemode
(
)
770
elseif
mode
=
=
textliteral_code
then
771
pdf_goto_textmode
(
)
772
elseif
mode
=
=
fontliteral_code
then
773
pdf_goto_fontmode
(
)
774
elseif
mode
=
=
alwaysliteral_code
then
775
pdf_end_string_nl
(
)
776
need_tm
=
true
777
elseif
mode
=
=
rawliteral_code
then
778
pdf_end_string_nl
(
)
779
else
780
report
(
"
invalid literal mode %a when flushing %a
"
,
mode
,
str
)
781
return
782
end
783
b
=
b
+
1
;
buffer
[
b
]
=
str
784
end
785
end
786 787
end
788 789
-- grouping & orientation
790 791
do
792 793
local
matrices
=
{
}
794
local
positions
=
{
}
795
local
nofpositions
=
0
796
local
nofmatrices
=
0
797 798
local
flushsave
=
function
(
current
,
pos_h
,
pos_v
)
799
nofpositions
=
nofpositions
+
1
800
positions
[
nofpositions
]
=
{
pos_h
,
pos_v
,
nofmatrices
}
801
pdf_goto_pagemode
(
)
802
pdf_set_pos
(
pos_h
,
pos_v
)
803
b
=
b
+
1
;
buffer
[
b
]
=
"
q
"
804
end
805 806
local
flushrestore
=
function
(
current
,
pos_h
,
pos_v
)
807
if
nofpositions
<
1
then
808
return
809
end
810
local
t
=
positions
[
nofpositions
]
811
-- local h = pos_h - t[1]
812
-- local v = pos_v - t[2]
813
if
shippingmode
=
=
"
page
"
then
814
nofmatrices
=
t
[
3
]
815
end
816
pdf_goto_pagemode
(
)
817
pdf_set_pos
(
pos_h
,
pos_v
)
818
b
=
b
+
1
;
buffer
[
b
]
=
"
Q
"
819
nofpositions
=
nofpositions
-
1
820
end
821 822
local
nodeproperties
=
nodes
.
properties
.
data
823 824
local
s_matrix_0
<const>
=
"
1 0 0 1 0 0 cm
"
825
local
f_matrix_2
=
formatters
[
"
%.6N 0 0 %.6N 0 0 cm
"
]
826
local
f_matrix_4
=
formatters
[
"
%.6N %.6N %.6N %.6N 0 0 cm
"
]
827 828
local
flushsetmatrix
=
function
(
current
,
pos_h
,
pos_v
)
829
local
p
=
nodeproperties
[
current
]
830
if
p
then
831
local
m
=
p
.
matrix
832
if
m
then
833
local
rx
,
sx
,
sy
,
ry
=
unpack
(
m
)
834
local
s
835
if
not
rx
then
836
rx
=
1
837
elseif
rx
=
=
0
then
838
rx
=
0
.
0001
839
end
840
if
not
ry
then
841
ry
=
1
842
elseif
ry
=
=
0
then
843
ry
=
0
.
0001
844
end
845
if
not
sx
then
846
sx
=
0
847
end
848
if
not
sy
then
849
sy
=
0
850
end
851
--
852
if
sx
=
=
0
and
sy
=
=
0
then
853
if
rx
=
=
1
and
ry
=
=
1
then
854
s
=
s_matrix_0
855
else
856
s
=
f_matrix_2
(
rx
,
ry
)
857
end
858
else
859
s
=
f_matrix_4
(
rx
,
sx
,
sy
,
ry
)
860
end
861
--
862
if
shippingmode
=
=
"
page
"
then
863
local
tx
=
pos_h
*
(
1
-
rx
)
-
pos_v
*
sy
864
local
ty
=
pos_v
*
(
1
-
ry
)
-
pos_h
*
sx
865
if
nofmatrices
>
0
then
866
local
t
=
matrices
[
nofmatrices
]
867
local
r_x
,
s_x
,
s_y
,
r_y
,
te
,
tf
=
t
[
1
]
,
t
[
2
]
,
t
[
3
]
,
t
[
4
]
,
t
[
5
]
,
t
[
6
]
868
rx
,
sx
=
rx
*
r_x
+
sx
*
s_y
,
rx
*
s_x
+
sx
*
r_y
869
sy
,
ry
=
sy
*
r_x
+
ry
*
s_y
,
sy
*
s_x
+
ry
*
r_y
870
tx
,
ty
=
tx
*
r_x
+
ty
*
s_y
,
tx
*
s_x
+
ty
*
r_y
871
end
872
nofmatrices
=
nofmatrices
+
1
873
matrices
[
nofmatrices
]
=
{
rx
,
sx
,
sy
,
ry
,
tx
,
ty
}
874
end
875
--
876
pdf_goto_pagemode
(
)
877
pdf_set_pos
(
pos_h
,
pos_v
)
878
--
879
b
=
b
+
1
880
buffer
[
b
]
=
s
881
end
882
end
883
end
884 885
flushers
.
setmatrix
=
flushsetmatrix
886
flushers
.
save
=
flushsave
887
flushers
.
restore
=
flushrestore
888 889
function
lpdf
.
hasmatrix
(
)
890
return
nofmatrices
>
0
891
end
892 893
function
lpdf
.
getmatrix
(
)
894
if
nofmatrices
>
0
then
895
return
unpack
(
matrices
[
nofmatrices
]
)
896
else
897
return
1
,
0
,
0
,
1
,
0
,
0
898
end
899
end
900 901
flushers
.
pushorientation
=
function
(
orientation
,
pos_h
,
pos_v
,
pos_r
)
902
pdf_goto_pagemode
(
)
903
pdf_set_pos
(
pos_h
,
pos_v
)
904
b
=
b
+
1
;
buffer
[
b
]
=
"
q
"
905
if
orientation
=
=
1
then
906
b
=
b
+
1
;
buffer
[
b
]
=
"
0 -1 1 0 0 0 cm
"
-- 90
907
elseif
orientation
=
=
2
then
908
b
=
b
+
1
;
buffer
[
b
]
=
"
-1 0 0 -1 0 0 cm
"
-- 180
909
elseif
orientation
=
=
3
then
910
b
=
b
+
1
;
buffer
[
b
]
=
"
0 1 -1 0 0 0 cm
"
-- 270
911
end
912
end
913 914
flushers
.
poporientation
=
function
(
orientation
,
pos_h
,
pos_v
,
pos_r
)
915
pdf_goto_pagemode
(
)
916
pdf_set_pos
(
pos_h
,
pos_v
)
917
b
=
b
+
1
;
buffer
[
b
]
=
"
Q
"
918
end
919 920
--
921 922
flushers
.
startmatrix
=
function
(
current
,
pos_h
,
pos_v
)
923
flushsave
(
current
,
pos_h
,
pos_v
)
924
flushsetmatrix
(
current
,
pos_h
,
pos_v
)
925
end
926 927
flushers
.
stopmatrix
=
function
(
current
,
pos_h
,
pos_v
)
928
flushrestore
(
current
,
pos_h
,
pos_v
)
929
end
930 931
flushers
.
startscaling
=
function
(
current
,
pos_h
,
pos_v
)
932
flushsave
(
current
,
pos_h
,
pos_v
)
933
flushsetmatrix
(
current
,
pos_h
,
pos_v
)
934
end
935 936
flushers
.
stopscaling
=
function
(
current
,
pos_h
,
pos_v
)
937
flushrestore
(
current
,
pos_h
,
pos_v
)
938
end
939 940
flushers
.
startrotation
=
function
(
current
,
pos_h
,
pos_v
)
941
flushsave
(
current
,
pos_h
,
pos_v
)
942
flushsetmatrix
(
current
,
pos_h
,
pos_v
)
943
end
944 945
flushers
.
stoprotation
=
function
(
current
,
pos_h
,
pos_v
)
946
flushrestore
(
current
,
pos_h
,
pos_v
)
947
end
948 949
flushers
.
startmirroring
=
function
(
current
,
pos_h
,
pos_v
)
950
flushsave
(
current
,
pos_h
,
pos_v
)
951
flushsetmatrix
(
current
,
pos_h
,
pos_v
)
952
end
953 954
flushers
.
stopmirroring
=
function
(
current
,
pos_h
,
pos_v
)
955
flushrestore
(
current
,
pos_h
,
pos_v
)
956
end
957 958
flushers
.
startclipping
=
function
(
current
,
pos_h
,
pos_v
)
959
flushsave
(
current
,
pos_h
,
pos_v
)
960
-- lpdf.print("origin",formatters["0 w %s W n"](nodeproperties[current].path))
961
pdf_goto_pagemode
(
)
962
b
=
b
+
1
;
buffer
[
b
]
=
formatters
[
"
0 w %s W n
"
]
(
nodeproperties
[
current
]
.
path
)
963
end
964 965
flushers
.
stopclipping
=
function
(
current
,
pos_h
,
pos_v
)
966
flushrestore
(
current
,
pos_h
,
pos_v
)
967
end
968 969
end
970 971
do
972 973
local
nodeproperties
=
nodes
.
properties
.
data
974 975
flushers
.
setstate
=
function
(
current
,
pos_h
,
pos_v
)
976
local
p
=
nodeproperties
[
current
]
977
if
p
then
978
local
d
=
p
.
data
979
if
d
and
d
~
=
"
"
then
980
pdf_goto_pagemode
(
)
981
b
=
b
+
1
;
buffer
[
b
]
=
d
982
end
983
end
984
end
985 986
end
987 988
-- rules
989 990
local
flushedxforms
=
{
}
-- actually box resources but can also be direct
991
local
localconverter
=
nil
-- will be set
992 993
local
flushimage
do
994 995
local
pdfbackend
=
backends
.
registered
.
pdf
996
local
nodeinjections
=
pdfbackend
.
nodeinjections
997
local
codeinjections
=
pdfbackend
.
codeinjections
998 999
local
newimagerule
=
nuts
.
pool
.
imagerule
1000
local
newboxrule
=
nuts
.
pool
.
boxrule
1001 1002
local
setprop
=
nuts
.
setprop
1003
local
getprop
=
nuts
.
getprop
1004
local
setattrlist
=
nuts
.
setattrlist
1005 1006
local
getwhd
=
nuts
.
getwhd
1007
local
flushlist
=
nuts
.
flushlist
1008
local
getdata
=
nuts
.
getdata
1009 1010
local
rulecodes
=
nodes
.
rulecodes
1011
local
normalrule_code
=
rulecodes
.
normal
1012
local
boxrule_code
=
rulecodes
.
box
1013
local
imagerule_code
=
rulecodes
.
image
1014
local
emptyrule_code
=
rulecodes
.
empty
1015
local
userrule_code
=
rulecodes
.
user
1016
local
overrule_code
=
rulecodes
.
over
1017
local
underrule_code
=
rulecodes
.
under
1018
local
fractionrule_code
=
rulecodes
.
fraction
1019
local
radicalrule_code
=
rulecodes
.
radical
1020
local
outlinerule_code
=
rulecodes
.
outline
1021 1022
local
processrule
=
nodes
.
rules
.
process
1023 1024
local
f_fm
=
formatters
[
"
/Fm%d Do
"
]
1025
local
f_im
=
formatters
[
"
/Im%d Do
"
]
1026
local
f_gr
=
formatters
[
"
/Gp%d Do
"
]
1027 1028
local
s_b
<const>
=
"
q
"
1029
local
s_e
<const>
=
"
Q
"
1030 1031
local
f_v
=
formatters
[
"
[] 0 d 0 J %.6N w 0 0 m %.6N 0 l S
"
]
1032
local
f_h
=
formatters
[
"
[] 0 d 0 J %.6N w 0 0 m 0 %.6N l S
"
]
1033 1034
local
f_f
=
formatters
[
"
0 0 %.6N %.6N re f
"
]
1035
local
f_o
=
formatters
[
"
[] 0 d 0 J 0 0 %.6N %.6N re S
"
]
1036
local
f_w
=
formatters
[
"
[] 0 d 0 J %.6N w 0 0 %.6N %.6N re S
"
]
1037 1038
local
f_b
=
formatters
[
"
%.6N w 0 %.6N %.6N %.6N re f
"
]
1039
local
f_x
=
formatters
[
"
[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S
"
]
1040
local
f_y
=
formatters
[
"
[] 0 d 0 J %.6N w %.6N %.6N %.6N %.6N re S %.6N 0 m %.6N 0 l S
"
]
1041 1042
-- Historically the index is an object which is kind of bad.
1043 1044
local
boxresources
,
n
=
{
}
,
0
1045 1046
getxformname
=
function
(
index
)
1047
local
l
=
boxresources
[
index
]
1048
if
l
then
1049
return
l
.
name
1050
else
1051
report
(
"
no box resource %S
"
,
index
)
1052
end
1053
end
1054 1055
lpdf
.
getxformname
=
getxformname
1056 1057
local
pdfcollectedresources
=
lpdf
.
collectedresources
1058 1059
function
codeinjections
.
saveboxresource
(
box
,
attributes
,
resources
,
immediate
,
kind
,
margin
)
1060
n
=
n
+
1
1061
local
immediate
=
true
1062
local
margin
=
margin
or
0
-- or dimension
1063
local
objnum
=
pdfreserveobject
(
)
1064
local
list
=
tonut
(
type
(
box
)
=
=
"
number
"
and
tex
.
takebox
(
box
)
or
box
)
1065
--
1066
if
resources
=
=
true
then
1067
resources
=
pdfcollectedresources
(
)
1068
end
1069
--
1070
local
width
,
height
,
depth
=
getwhd
(
list
)
1071
--
1072
local
l
=
{
1073
width
=
width
,
1074
height
=
height
,
1075
depth
=
depth
,
1076
margin
=
margin
,
1077
attributes
=
attributes
,
1078
resources
=
resources
,
1079
list
=
nil
,
1080
type
=
kind
,
1081
name
=
n
,
1082
index
=
objnum
,
1083
objnum
=
objnum
,
1084
}
1085
boxresources
[
objnum
]
=
l
1086
if
immediate
then
1087
localconverter
(
list
,
"
xform
"
,
objnum
,
l
)
1088
flushedxforms
[
objnum
]
=
{
true
,
objnum
}
1089
flushlist
(
list
)
1090
else
1091
l
.
list
=
list
1092
end
1093
return
objnum
1094
end
1095 1096
function
nodeinjections
.
useboxresource
(
index
,
wd
,
ht
,
dp
)
1097
local
l
=
boxresources
[
index
]
1098
if
l
then
1099
if
wd
or
ht
or
dp
then
1100
wd
,
ht
,
dp
=
wd
or
0
,
ht
or
0
,
dp
or
0
1101
else
1102
wd
,
ht
,
dp
=
l
.
width
,
l
.
height
,
l
.
depth
1103
end
1104
local
rule
=
newboxrule
(
wd
,
ht
,
dp
)
1105
setattrlist
(
rule
,
true
)
1106
setprop
(
rule
,
"
index
"
,
index
)
1107
return
tonode
(
rule
)
,
wd
,
ht
,
dp
1108
else
1109
report
(
"
no box resource %S
"
,
index
)
1110
end
1111
end
1112 1113
local
function
getboxresourcedimensions
(
index
)
1114
local
l
=
boxresources
[
index
]
1115
if
l
then
1116
return
l
.
width
,
l
.
height
,
l
.
depth
,
l
.
margin
1117
else
1118
report
(
"
no box resource %S
"
,
index
)
1119
end
1120
end
1121 1122
nodeinjections
.
getboxresourcedimensions
=
getboxresourcedimensions
1123 1124
function
codeinjections
.
getboxresourcebox
(
index
)
1125
local
l
=
boxresources
[
index
]
1126
if
l
then
1127
return
l
.
list
1128
end
1129
end
1130 1131
-- a bit of a mess: index is now objnum but that has to change to a proper index
1132
-- ... an engine inheritance
1133 1134
local
function
flushpdfxform
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1135
-- object properties
1136
local
objnum
=
getprop
(
current
,
"
index
"
)
1137
local
name
=
getxformname
(
objnum
)
1138
local
info
=
flushedxforms
[
objnum
]
1139
local
r
=
boxresources
[
objnum
]
1140
if
not
info
then
1141
info
=
{
false
,
objnum
}
1142
flushedxforms
[
objnum
]
=
info
1143
end
1144
local
wd
,
ht
,
dp
=
getboxresourcedimensions
(
objnum
)
1145
-- or: wd, ht, dp = r.width, r.height, r.depth
1146
-- sanity check
1147
local
htdp
=
ht
+
dp
1148
if
wd
=
=
0
or
size_h
=
=
0
or
htdp
=
=
0
or
size_v
=
=
0
then
1149
return
1150
end
1151
-- calculate scale
1152
local
rx
,
ry
=
1
,
1
1153
if
wd
~
=
size_h
or
htdp
~
=
size_v
then
1154
rx
=
size_h
/
wd
1155
ry
=
size_v
/
htdp
1156
end
1157
-- flush the reference
1158
usedxforms
[
objnum
]
=
true
1159
pdf_goto_pagemode
(
)
1160
calc_pdfpos
(
pos_h
,
pos_v
)
1161
tx
=
cmtx
*
bpfactor
1162
ty
=
cmty
*
bpfactor
1163
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1164
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
rx
,
0
,
0
,
ry
,
tx
,
ty
)
1165
b
=
b
+
1
;
buffer
[
b
]
=
f_fm
(
name
)
1166
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1167
end
1168 1169
-- place image also used in vf but we can use a different one if we need it
1170 1171
local
imagetypes
=
images
.
types
-- pdf png jpg jp2 jbig2 stream
1172
local
img_none
=
imagetypes
.
none
1173
local
img_pdf
=
imagetypes
.
pdf
1174
local
img_stream
=
imagetypes
.
stream
1175 1176
local
one_bp
=
65536
*
bpfactor
1177 1178
local
imageresources
,
n
=
{
}
,
0
1179 1180
getximagename
=
function
(
index
)
-- not used
1181
local
l
=
imageresources
[
index
]
1182
if
l
then
1183
return
l
.
name
1184
else
1185
report
(
"
no image resource %S
"
,
index
)
1186
end
1187
end
1188 1189
-- Groups are flushed immediately but we can decide to make them into a
1190
-- specific whatsit ... but not now. We could hash them if needed when
1191
-- we use lot sof them in mp ... but not now.
1192 1193
usedxgroups
=
{
}
1194
local
groups
=
0
1195
local
group
=
nil
1196 1197
local
flushgroup
=
function
(
content
,
bbox
)
1198
if
not
group
then
1199
group
=
pdfdictionary
{
1200
Type
=
pdfconstant
(
"
Group
"
)
,
1201
S
=
pdfconstant
(
"
Transparency
"
)
,
1202
}
1203
end
1204
local
wrapper
=
pdfdictionary
{
1205
Type
=
pdf_xobject
,
1206
Subtype
=
pdf_form
,
1207
FormType
=
1
,
1208
Group
=
group
,
1209
BBox
=
pdfarray
(
bbox
)
,
1210
Resources
=
lpdf
.
collectedresources
{
serialize
=
false
}
,
1211
}
1212
local
objnum
=
pdfflushstreamobject
(
content
,
wrapper
,
false
)
-- why not compressed ?
1213
groups
=
groups
+
1
1214
usedxgroups
[
groups
]
=
objnum
1215
return
f_gr
(
groups
)
1216
end
1217 1218
flushers
.
group
=
flushgroup
1219
lpdf
.
flushgroup
=
flushgroup
-- todo: access via driver in mlib-pps
1220 1221
-- end of experiment
1222 1223
local
function
flushpdfximage
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1224 1225
local
width
,
1226
height
,
1227
depth
=
getwhd
(
current
)
1228
local
total
=
height
+
depth
1229
local
transform
=
getprop
(
current
,
"
transform
"
)
or
0
-- we never set it ... so just use rotation then
1230
local
index
=
getprop
(
current
,
"
index
"
)
or
0
1231
local
kind
,
1232
xorigin
,
1233
yorigin
,
1234
xsize
,
1235
ysize
,
1236
rotation
,
-- transform / orientation / rotation : it's a mess (i need to redo this)
1237
objnum
,
1238
groupref
=
pdfincludeimage
(
index
)
-- needs to be sorted out, bad name (no longer mixed anyway)
1239 1240
if
not
kind
then
1241
report
(
"
invalid image %S
"
,
index
)
1242
return
1243
end
1244 1245
local
rx
,
sx
,
sy
,
ry
,
tx
,
ty
=
1
,
0
,
0
,
1
,
0
,
0
1246 1247
-- tricky: xsize and ysize swapped
1248 1249
if
kind
=
=
img_pdf
or
kind
=
=
img_stream
then
1250
rx
,
ry
,
tx
,
ty
=
1
/
xsize
,
1
/
ysize
,
xorigin
/
xsize
,
yorigin
/
ysize
1251
else
1252
-- if kind == img_png then
1253
-- -- if groupref > 0 and img_page_group_val == 0 then
1254
-- -- img_page_group_val = groupref
1255
-- -- end
1256
-- end
1257
rx
,
ry
=
bpfactor
,
bpfactor
1258
end
1259 1260
if
(
transform
&
7
)
>
3
then
1261
-- mirror
1262
rx
,
tx
=
-
rx
,
-
tx
1263
end
1264
local
t
=
(
transform
+
rotation
)
&
3
1265
if
t
=
=
0
then
1266
-- nothing
1267
elseif
t
=
=
1
then
1268
-- rotation over 90 degrees (counterclockwise)
1269
rx
,
sx
,
sy
,
ry
,
tx
,
ty
=
0
,
rx
,
-
ry
,
0
,
-
ty
,
tx
1270
elseif
t
=
=
2
then
1271
-- rotation over 180 degrees (counterclockwise)
1272
rx
,
ry
,
tx
,
ty
=
-
rx
,
-
ry
,
-
tx
,
-
ty
1273
elseif
t
=
=
3
then
1274
-- rotation over 270 degrees (counterclockwise)
1275
rx
,
sx
,
sy
,
ry
,
tx
,
ty
=
0
,
-
rx
,
ry
,
0
,
ty
,
-
tx
1276
end
1277 1278
rx
=
rx
*
width
1279
sx
=
sx
*
total
1280
sy
=
sy
*
width
1281
ry
=
ry
*
total
1282
tx
=
pos_h
-
tx
*
width
1283
ty
=
pos_v
-
ty
*
total
1284 1285
local
t
=
transform
+
rotation
1286 1287
if
(
transform
&
7
)
>
3
then
1288
t
=
t
+
1
1289
end
1290 1291
t
=
t
&
3
1292 1293
if
t
=
=
0
then
1294
-- no transform
1295
elseif
t
=
=
1
then
1296
-- rotation over 90 degrees (counterclockwise)
1297
tx
=
tx
+
width
1298
elseif
t
=
=
2
then
1299
-- rotation over 180 degrees (counterclockwise)
1300
tx
=
tx
+
width
1301
ty
=
ty
+
total
1302
elseif
t
=
=
3
then
1303
-- rotation over 270 degrees (counterclockwise)
1304
ty
=
ty
+
total
1305
end
1306 1307
-- a flaw in original, can go:
1308
--
1309
-- if img_page_group_val == 0 then
1310
-- img_page_group_val = group_ref
1311
-- end
1312 1313
usedximages
[
index
]
=
objnum
-- hm
1314 1315
pdf_goto_pagemode
(
)
1316 1317
calc_pdfpos
(
tx
,
ty
)
1318 1319
tx
=
cmtx
*
bpfactor
1320
ty
=
cmty
*
bpfactor
1321 1322
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1323
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
rx
,
sx
,
sy
,
ry
,
tx
,
ty
)
1324
b
=
b
+
1
;
buffer
[
b
]
=
f_im
(
index
)
1325
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1326
end
1327 1328
flushimage
=
function
(
index
,
width
,
height
,
depth
,
pos_h
,
pos_v
)
1329 1330
-- used in vf characters
1331 1332
local
total
=
height
+
depth
1333
local
kind
,
1334
xorigin
,
yorigin
,
1335
xsize
,
ysize
,
1336
rotation
,
1337
objnum
,
1338
groupref
=
pdfincludeimage
(
index
)
1339 1340
local
rx
=
width
/
xsize
1341
local
sx
=
0
1342
local
sy
=
0
1343
local
ry
=
total
/
ysize
1344
local
tx
=
pos_h
1345
-- to be sorted out
1346
-- local ty = pos_v - depth
1347
local
ty
=
pos_v
-- we assume that depth is dealt with in the caller (for now)
1348 1349
usedximages
[
index
]
=
objnum
1350 1351
pdf_goto_pagemode
(
)
1352 1353
calc_pdfpos
(
tx
,
ty
)
1354 1355
tx
=
cmtx
*
bpfactor
1356
ty
=
cmty
*
bpfactor
1357 1358
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1359
b
=
b
+
1
;
buffer
[
b
]
=
f_cm
(
rx
,
sx
,
sy
,
ry
,
tx
,
ty
)
1360
b
=
b
+
1
;
buffer
[
b
]
=
f_im
(
index
)
1361
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1362
end
1363 1364
flushers
.
image
=
flushimage
1365 1366
-- For the moment we need this hack because the engine checks the 'image'
1367
-- command in virtual fonts (so we use lua instead).
1368
--
1369
-- These will be replaced by a new more advanced one ... some day ... or
1370
-- never because the next are like the other engines and compensate for
1371
-- small sizes which is needed for inaccurate viewers.
1372 1373
flushers
.
rule
=
function
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
,
subtype
)
1374 1375
if
subtype
=
=
emptyrule_code
then
1376
return
1377
elseif
subtype
=
=
boxrule_code
then
1378
return
flushpdfxform
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1379
elseif
subtype
=
=
imagerule_code
then
1380
return
flushpdfximage
(
current
,
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1381
end
1382
if
subtype
=
=
userrule_code
or
subtype
>
=
overrule_code
and
subtype
<
=
radicalrule_code
then
1383
pdf_goto_pagemode
(
)
1384
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1385
pdf_set_pos_temp
(
pos_h
,
pos_v
)
1386
processrule
(
current
,
size_h
,
size_v
,
pos_r
)
-- so we pass direction
1387
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1388
return
1389
end
1390 1391
pdf_goto_pagemode
(
)
1392 1393
-- local saved_b = b
1394 1395
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1396 1397
local
dim_h
=
size_h
*
bpfactor
1398
local
dim_v
=
size_v
*
bpfactor
1399
local
rule
1400 1401
if
dim_v
<
=
one_bp
then
1402
pdf_set_pos_temp
(
pos_h
,
pos_v
+
0
.
5
*
size_v
)
1403
rule
=
f_v
(
dim_v
,
dim_h
)
1404
elseif
dim_h
<
=
one_bp
then
1405
pdf_set_pos_temp
(
pos_h
+
0
.
5
*
size_h
,
pos_v
)
1406
rule
=
f_h
(
dim_h
,
dim_v
)
1407
else
1408
pdf_set_pos_temp
(
pos_h
,
pos_v
)
1409
if
subtype
=
=
outlinerule_code
then
1410
local
linewidth
=
getdata
(
current
)
1411
if
linewidth
>
0
then
1412
rule
=
f_w
(
linewidth
*
bpfactor
,
dim_h
,
dim_v
)
1413
else
1414
rule
=
f_o
(
dim_h
,
dim_v
)
1415
end
1416
else
1417
rule
=
f_f
(
dim_h
,
dim_v
)
1418
end
1419
end
1420 1421
b
=
b
+
1
;
buffer
[
b
]
=
rule
1422
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1423 1424
-- buffer[saved_b] = concat(buffer," ",saved_b,b)
1425
-- b = saved_b
1426 1427
end
1428 1429
flushers
.
simplerule
=
function
(
pos_h
,
pos_v
,
pos_r
,
size_h
,
size_v
)
1430
pdf_goto_pagemode
(
)
1431 1432
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1433 1434
local
dim_h
=
size_h
*
bpfactor
1435
local
dim_v
=
size_v
*
bpfactor
1436
local
rule
1437 1438
if
dim_v
<
=
one_bp
then
1439
pdf_set_pos_temp
(
pos_h
,
pos_v
+
0
.
5
*
size_v
)
1440
rule
=
f_v
(
dim_v
,
dim_h
)
1441
elseif
dim_h
<
=
one_bp
then
1442
pdf_set_pos_temp
(
pos_h
+
0
.
5
*
size_h
,
pos_v
)
1443
rule
=
f_h
(
dim_h
,
dim_v
)
1444
else
1445
pdf_set_pos_temp
(
pos_h
,
pos_v
)
1446
rule
=
f_f
(
dim_h
,
dim_v
)
1447
end
1448 1449
b
=
b
+
1
;
buffer
[
b
]
=
rule
1450
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1451
end
1452 1453
flushers
.
specialrule
=
function
(
pos_h
,
pos_v
,
pos_r
,
width
,
height
,
depth
,
line
,
outline
,
baseline
)
1454
pdf_goto_pagemode
(
)
1455 1456
b
=
b
+
1
;
buffer
[
b
]
=
s_b
1457 1458
local
width
=
bpfactor
*
width
1459
local
height
=
bpfactor
*
height
1460
local
depth
=
bpfactor
*
depth
1461
local
total
=
height
+
depth
1462
local
line
=
bpfactor
*
line
1463
local
half
=
line
/
2
1464
local
rule
1465 1466
if
outline
then
1467
local
d
=
-
depth
+
half
1468
local
w
=
width
-
line
1469
local
t
=
total
-
line
1470
if
baseline
then
1471
rule
=
f_y
(
line
,
half
,
d
,
w
,
t
,
half
,
w
)
1472
else
1473
rule
=
f_x
(
line
,
half
,
d
,
w
,
t
)
1474
end
1475
else
1476
rule
=
f_b
(
line
,
-
depth
,
width
,
total
)
1477
end
1478
pdf_set_pos_temp
(
pos_h
,
pos_v
)
1479 1480
b
=
b
+
1
;
buffer
[
b
]
=
rule
1481
b
=
b
+
1
;
buffer
[
b
]
=
s_e
1482
end
1483 1484
end
1485 1486
--- basics
1487 1488
local
wrapupdocument
,
registerpage
do
1489 1490
local
pages
=
{
}
1491
local
maxkids
=
10
1492
local
nofpages
=
0
1493
local
pagetag
=
"
unset
"
1494 1495
registerpage
=
function
(
object
)
1496
nofpages
=
nofpages
+
1
1497
local
objnum
=
pdfpagereference
(
nofpages
)
1498
pages
[
nofpages
]
=
{
1499
page
=
nofpages
,
-- original number, only for diagnostics
1500
objnum
=
objnum
,
1501
object
=
object
,
1502
tag
=
pagetag
,
1503
}
1504
end
1505 1506
function
lpdf
.
setpagetag
(
tag
)
1507
pagetag
=
tag
or
"
unset
"
1508
end
1509 1510
function
lpdf
.
getnofpages
(
)
1511
return
nofpages
1512
end
1513 1514
function
lpdf
.
getpagetags
(
)
1515
local
list
=
{
}
1516
for
i
=
1
,
nofpages
do
1517
list
[
i
]
=
pages
[
i
]
.
tag
1518
end
1519
return
list
1520
end
1521 1522
function
lpdf
.
setpageorder
(
mapping
)
1523
-- mapping can be a hash so:
1524
local
list
=
table
.
sortedkeys
(
mapping
)
1525
local
n
=
#
list
1526
if
n
=
=
nofpages
then
1527
local
done
=
{
}
1528
local
hash
=
{
}
1529
for
i
=
1
,
n
do
1530
local
order
=
mapping
[
list
[
i
]
]
1531
if
hash
[
order
]
then
1532
report
(
"
invalid page order, duplicate entry %i
"
,
order
)
1533
return
1534
elseif
order
<
1
or
order
>
nofpages
then
1535
report
(
"
invalid page order, no page %i
"
,
order
)
1536
return
1537
else
1538
done
[
i
]
=
pages
[
order
]
1539
hash
[
order
]
=
true
1540
end
1541
end
1542
pages
=
done
1543
else
1544
report
(
"
invalid page order, %i entries expected
"
,
nofpages
)
1545
end
1546
end
1547 1548
-- We can have this, but then via codeinjections etc. Later.
1549 1550
-- function structures.pages.swapthem()
1551
-- local n = lpdf.getnofpages()
1552
-- local t = { }
1553
-- for i=1,n do
1554
-- t[i] = i
1555
-- end
1556
-- for i=2,math.odd(n) and n or (n-1),2 do
1557
-- t[i] = i+1
1558
-- t[i+1] = i
1559
-- end
1560
-- lpdf.setpageorder(t)
1561
-- end
1562 1563
wrapupdocument
=
function
(
driver
)
1564 1565
-- hook (to reshuffle pages)
1566
local
pagetree
=
{
}
1567
local
parent
=
nil
1568
local
minimum
=
0
1569
local
maximum
=
0
1570
local
current
=
0
1571
if
#
pages
>
1
.
5
*
maxkids
then
1572
repeat
1573
local
plist
,
pnode
1574
if
current
=
=
0
then
1575
plist
,
minimum
=
pages
,
1
1576
elseif
current
=
=
1
then
1577
plist
,
minimum
=
pagetree
,
1
1578
else
1579
plist
,
minimum
=
pagetree
,
maximum
+
1
1580
end
1581
maximum
=
#
plist
1582
if
maximum
>
minimum
then
1583
local
kids
1584
for
i
=
minimum
,
maximum
do
1585
local
p
=
plist
[
i
]
1586
if
not
pnode
or
#
kids
=
=
maxkids
then
1587
kids
=
pdfarray
(
)
1588
parent
=
pdfreserveobject
(
)
1589
pnode
=
pdfdictionary
{
1590
objnum
=
parent
,
1591
Type
=
pdf_pages
,
1592
Kids
=
kids
,
1593
Count
=
0
,
1594
}
1595
pagetree
[
#
pagetree
+
1
]
=
pnode
1596
end
1597
kids
[
#
kids
+
1
]
=
pdfreference
(
p
.
objnum
)
1598
pnode
.
Count
=
pnode
.
Count
+
(
p
.
Count
or
1
)
1599
p
.
Parent
=
pdfreference
(
parent
)
1600
end
1601
end
1602
current
=
current
+
1
1603
until
maximum
=
=
minimum
1604
-- flush page tree
1605
for
i
=
1
,
#
pagetree
do
1606
local
entry
=
pagetree
[
i
]
1607
local
objnum
=
entry
.
objnum
1608
entry
.
objnum
=
nil
1609
pdfflushobject
(
objnum
,
entry
)
1610
end
1611
else
1612
-- ugly
1613
local
kids
=
pdfarray
(
)
1614
local
list
=
pdfdictionary
{
1615
Type
=
pdf_pages
,
1616
Kids
=
kids
,
1617
Count
=
nofpages
,
1618
}
1619
parent
=
pdfreserveobject
(
)
1620
for
i
=
1
,
nofpages
do
1621
local
page
=
pages
[
i
]
1622
kids
[
#
kids
+
1
]
=
pdfreference
(
page
.
objnum
)
1623
page
.
Parent
=
pdfreference
(
parent
)
1624
end
1625
pdfflushobject
(
parent
,
list
)
1626
end
1627
for
i
=
1
,
nofpages
do
1628
local
page
=
pages
[
i
]
1629
local
object
=
page
.
object
1630
object
.
Parent
=
page
.
Parent
1631
pdfflushobject
(
page
.
objnum
,
object
)
1632
end
1633
lpdf
.
addtocatalog
(
"
Pages
"
,
pdfreference
(
parent
)
)
1634 1635
end
1636 1637
end
1638 1639
pdf_h
,
pdf_v
=
0
,
0
1640 1641
local
function
initialize
(
driver
,
details
)
1642
reset_variables
(
details
)
1643
reset_buffer
(
)
1644
end
1645 1646
-- This will all move and be merged and become less messy.
1647 1648
-- todo: more clever resource management: a bit tricky as we can inject
1649
-- stuff in the page stream
1650 1651
local
compact
=
false
1652 1653
do
1654 1655
-- This is more a convenience feature and it might even be not entirely robust.
1656
-- It removes redundant color directives which makes the page stream look a bit
1657
-- nicer (also when figuring out issues). I might add more here but there is
1658
-- some additional overhead involved so runtime can be impacted.
1659 1660
local
P
,
R
,
S
,
Cs
,
lpegmatch
=
lpeg
.
P
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
Cs
,
lpeg
.
match
1661 1662
local
p_ds
=
(
R
(
"
09
"
)
+
S
(
"
.
"
)
)
^
1
1663
----- p_nl = S("\n\r")^1
1664
local
p_nl
=
S
(
"
\n
"
)
^
1
1665
local
p_eg
=
P
(
"
Q
"
)
1666 1667
local
p_cl
=
p_ds
*
(
P
(
"
rg
"
)
+
P
(
"
g
"
)
+
P
(
"
k
"
)
)
*
p_ds
*
(
P
(
"
RG
"
)
+
P
(
"
G
"
)
+
P
(
"
K
"
)
)
1668
----- p_cl = (p_ds * (P("rg") + P("g") + P("k") + P("RG") + P("G") + P("K")))^1
1669
local
p_tr
=
P
(
"
/Tr
"
)
*
p_ds
*
P
(
"
gs
"
)
1670 1671
local
p_no_cl
=
(
p_cl
*
p_nl
)
/
"
"
1672
local
p_no_tr
=
(
p_tr
*
p_nl
)
/
"
"
1673
local
p_no_nl
=
1
-
p_nl
1674 1675
local
p_do_cl
=
p_cl
*
p_nl
1676
local
p_do_tr
=
p_tr
*
p_nl
1677 1678
local
p_do_eg
=
p_eg
*
p_nl
1679 1680
local
pattern
=
Cs
(
(
1681
(
p_no_cl
+
p_no_tr
)
^
0
*
p_do_eg
-- transparencies and colors before Q
1682
+
p_no_tr
*
p_no_cl
*
p_do_tr
*
p_do_cl
-- transparencies and colors before others
1683
+
p_no_cl
*
p_do_cl
-- successive colors
1684
+
p_no_tr
*
p_do_tr
-- successive transparencies
1685
+
p_no_nl
^
1
1686
+
1
1687
)
^
1
)
1688 1689
local
oldsize
=
0
1690
local
newsize
=
0
1691 1692
directives
.
register
(
"
pdf.compact
"
,
function
(
v
)
1693
compact
=
v
and
function
(
s
)
1694
oldsize
=
oldsize
+
#
s
1695
s
=
lpegmatch
(
pattern
,
s
)
or
s
1696
newsize
=
newsize
+
#
s
1697
return
s
1698
end
1699
end
)
1700 1701
statistics
.
register
(
"
pdf pagestream
"
,
function
(
)
1702
if
oldsize
~
=
newsize
then
1703
return
string
.
format
(
"
old size: %i, new size %i
"
,
oldsize
,
newsize
)
1704
end
1705
end
)
1706 1707 1708
end
1709 1710
local
flushdeferred
-- defined later
1711 1712
local
level
=
0
1713 1714
local
finalize
do
1715 1716
local
f_font
=
formatters
[
"
F%d
"
]
1717 1718
local
f_form
=
formatters
[
"
Fm%d
"
]
1719
local
f_group
=
formatters
[
"
Gp%d
"
]
1720
local
f_image
=
formatters
[
"
Im%d
"
]
1721 1722
finalize
=
function
(
driver
,
details
)
1723 1724
level
=
level
+
1
1725 1726
pdf_goto_pagemode
(
)
-- for now
1727 1728
local
objnum
=
details
.
objnum
1729
local
specification
=
details
.
specification
1730 1731
local
content
=
concat
(
buffer
,
"
\n
"
,
1
,
b
)
1732 1733
if
compact
then
1734
content
=
compact
(
content
)
1735
end
1736 1737
local
fonts
=
nil
1738
local
xforms
=
nil
1739 1740
if
next
(
usedfonts
)
then
1741
fonts
=
pdfdictionary
{
}
1742
for
k
,
v
in
next
,
usedfonts
do
1743
-- fonts[f_font(v)] = pdfreference(pdfgetfontobjectnumber(k)) -- we can overload for testing
1744
fonts
[
f_font
(
v
)
]
=
pdfreference
(
usedfontobjects
[
k
]
)
-- we can overload for testing
1745
end
1746
end
1747 1748
-- messy: use real indexes for both ... so we need to change some in the
1749
-- full luatex part
1750 1751
if
next
(
usedxforms
)
or
next
(
usedximages
)
or
next
(
usedxgroups
)
then
1752
xforms
=
pdfdictionary
{
}
1753
for
k
in
sortedhash
(
usedxforms
)
do
1754
xforms
[
f_form
(
getxformname
(
k
)
)
]
=
pdfreference
(
k
)
1755
end
1756
for
k
,
v
in
sortedhash
(
usedximages
)
do
1757
xforms
[
f_image
(
k
)
]
=
pdfreference
(
v
)
1758
end
1759
for
k
,
v
in
sortedhash
(
usedxgroups
)
do
1760
xforms
[
f_group
(
k
)
]
=
pdfreference
(
v
)
1761
end
1762
end
1763 1764
reset_buffer
(
)
1765 1766
-- finish_pdfpage_callback(shippingmode == "page")
1767 1768
if
shippingmode
=
=
"
page
"
then
1769 1770
local
pageproperties
=
lpdf
.
getpageproperties
(
)
1771 1772
local
pageresources
=
pageproperties
.
pageresources
1773
local
pageattributes
=
pageproperties
.
pageattributes
1774
local
pagesattributes
=
pageproperties
.
pagesattributes
1775 1776
pageresources
.
Font
=
fonts
1777
pageresources
.
XObject
=
xforms
1778
pageresources
.
ProcSet
=
lpdf
.
procset
(
)
1779 1780
local
bbox
=
pdfarray
{
1781
boundingbox
[
1
]
*
bpfactor
,
1782
boundingbox
[
2
]
*
bpfactor
,
1783
boundingbox
[
3
]
*
bpfactor
,
1784
boundingbox
[
4
]
*
bpfactor
,
1785
}
1786 1787
local
contentsobj
=
pdfflushstreamobject
(
content
,
false
,
true
)
1788 1789
pageattributes
.
Type
=
pdf_page
1790
pageattributes
.
Contents
=
pdfreference
(
contentsobj
)
1791
pageattributes
.
Resources
=
pageresources
1792
-- pageattributes.Resources = pdfreference(pdfflushobject(pageresources))
1793
-- pageattributes.MediaBox = bbox
1794
pageattributes
.
MediaBox
=
pdfsharedobject
(
bbox
)
1795
pageattributes
.
Parent
=
nil
-- precalculate
1796
pageattributes
.
Group
=
nil
-- todo
1797 1798
-- resources can be indirect
1799 1800
registerpage
(
pageattributes
)
1801 1802
lpdf
.
finalizepage
(
true
)
1803 1804
local
TrimBox
=
pageattributes
.
TrimBox
1805
local
CropBox
=
pageattributes
.
CropBox
1806
local
BleedBox
=
pageattributes
.
BleedBox
1807 1808
-- Indirect objects don't work in all viewers.
1809 1810
if
TrimBox
then
pageattributes
.
TrimBox
=
pdfsharedobject
(
TrimBox
)
end
1811
if
CropBox
then
pageattributes
.
CropBox
=
pdfsharedobject
(
CropBox
)
end
1812
if
BleedBox
then
pageattributes
.
BleedBox
=
pdfsharedobject
(
BleedBox
)
end
1813 1814
else
1815 1816
local
xformtype
=
specification
.
type
or
0
1817
local
margin
=
specification
.
margin
or
0
1818
local
attributes
=
specification
.
attributes
or
"
"
1819
local
resources
=
specification
.
resources
or
"
"
1820 1821
local
wrapper
=
nil
1822 1823
if
xformtype
=
=
0
then
1824
wrapper
=
pdfdictionary
{
1825
Type
=
pdf_xobject
,
1826
Subtype
=
pdf_form
,
1827
FormType
=
1
,
1828
BBox
=
nil
,
1829
Matrix
=
nil
,
1830
Resources
=
nil
,
1831
}
1832
else
1833
wrapper
=
pdfdictionary
{
1834
BBox
=
nil
,
1835
Matrix
=
nil
,
1836
Resources
=
nil
,
1837
}
1838
end
1839
if
xformtype
=
=
0
or
xformtype
=
=
1
or
xformtype
=
=
3
then
1840
wrapper
.
BBox
=
pdfarray
{
1841
-
margin
*
bpfactor
,
1842
-
margin
*
bpfactor
,
1843
(
boundingbox
[
3
]
+
margin
)
*
bpfactor
,
1844
(
boundingbox
[
4
]
+
margin
)
*
bpfactor
,
1845
}
1846
end
1847
if
xformtype
=
=
0
or
xformtype
=
=
2
or
xformtype
=
=
3
then
1848
-- can be shared too
1849
wrapper
.
Matrix
=
pdfarray
{
1
,
0
,
0
,
1
,
0
,
0
}
1850
end
1851 1852
local
patterns
=
true
1853 1854
if
attributes
.
Type
and
attributes
.
Type
=
=
pdf_pattern
then
1855
patterns
=
false
1856
end
1857 1858
local
boxresources
=
lpdf
.
collectedresources
{
1859
patterns
=
patterns
,
1860
serialize
=
false
,
1861
}
1862
boxresources
.
Font
=
fonts
1863
boxresources
.
XObject
=
xforms
1864 1865
-- todo: maybe share them
1866
-- wrapper.Resources = pdfreference(pdfflushobject(boxresources))
1867 1868
if
resources
~
=
"
"
then
1869
boxresources
=
boxresources
+
resources
1870
end
1871
if
attributes
~
=
"
"
then
1872
wrapper
=
wrapper
+
attributes
1873
end
1874 1875
wrapper
.
Resources
=
next
(
boxresources
)
and
boxresources
or
nil
1876
wrapper
.
ProcSet
=
lpdf
.
procset
(
)
1877 1878
pdfflushstreamobject
(
content
,
wrapper
,
true
,
specification
.
objnum
)
1879 1880
end
1881 1882
for
objnum
in
sortedhash
(
usedxforms
)
do
1883
local
f
=
flushedxforms
[
objnum
]
1884
if
f
[
1
]
=
=
false
then
1885
f
[
1
]
=
true
1886
local
objnum
=
f
[
2
]
-- specification.objnum
1887
local
specification
=
boxresources
[
objnum
]
1888
local
list
=
specification
.
list
1889
localconverter
(
list
,
"
xform
"
,
f
[
2
]
,
specification
)
1890
end
1891
end
1892 1893
pdf_h
,
pdf_v
=
0
,
0
1894 1895
if
level
=
=
1
then
1896
flushdeferred
(
)
1897
end
1898
level
=
level
-
1
1899 1900
end
1901 1902
end
1903 1904
-- now comes the pdf file handling
1905 1906
local
objects
=
{
}
1907
local
streams
=
{
}
-- maybe just parallel to objects (no holes)
1908
local
nofobjects
=
0
1909
local
offset
=
0
1910
local
f
=
false
1911
local
flush
=
false
1912
local
threshold
=
40
-- also #("/Filter /FlateDecode") (compression threshold)
1913
local
objectstream
=
true
1914
local
compress
=
true
1915
local
cache
=
false
1916
local
info
=
"
"
1917
local
catalog
=
"
"
1918
local
lastdeferred
=
false
1919
local
majorversion
=
1
1920
local
minorversion
=
7
1921 1922
directives
.
register
(
"
backend.pdf.threshold
"
,
function
(
v
)
1923
if
v
then
1924
threshold
=
tonumber
(
v
)
or
40
1925
else
1926
threshold
=
-1000
1927
end
1928
end
)
1929 1930
local
f_object
=
formatters
[
"
%i 0 obj\010%s\010endobj\010
"
]
1931
local
f_stream_n_u
=
formatters
[
"
%i 0 obj\010<< /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1932
local
f_stream_n_c
=
formatters
[
"
%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1933
local
f_stream_d_u
=
formatters
[
"
%i 0 obj\010<< %s /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1934
local
f_stream_d_c
=
formatters
[
"
%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010%s\010endstream\010endobj\010
"
]
1935
local
f_stream_d_r
=
formatters
[
"
%i 0 obj\010<< %s >>\010stream\010%s\010endstream\010endobj\010
"
]
1936 1937
----- f_object_b = formatters["%i 0 obj\010"]
1938
local
f_stream_b_n_u
=
formatters
[
"
%i 0 obj\010<< /Length %i >>\010stream\010
"
]
1939
local
f_stream_b_n_c
=
formatters
[
"
%i 0 obj\010<< /Filter /FlateDecode /Length %i >>\010stream\010
"
]
1940
local
f_stream_b_d_u
=
formatters
[
"
%i 0 obj\010<< %s /Length %i >>\010stream\010
"
]
1941
local
f_stream_b_d_c
=
formatters
[
"
%i 0 obj\010<< %s /Filter /FlateDecode /Length %i >>\010stream\010
"
]
1942
local
f_stream_b_d_r
=
formatters
[
"
%i 0 obj\010<< %s >>\010stream\010
"
]
1943 1944
----- s_object_e <const> = "\010endobj\010"
1945
local
s_stream_e
<const>
=
"
\010endstream\010endobj\010
"
1946 1947
do
1948 1949
-- Versions can be set but normally are managed by the official standards. When possible
1950
-- reading and writing should look at these values.
1951 1952
function
lpdf
.
setversion
(
major
,
minor
)
1953
majorversion
=
tonumber
(
major
)
or
majorversion
1954
minorversion
=
tonumber
(
minor
)
or
minorversion
1955
end
1956 1957
function
lpdf
.
getversion
(
major
,
minor
)
1958
return
majorversion
,
minorversion
1959
end
1960 1961
function
lpdf
.
majorversion
(
)
return
majorversion
end
1962
function
lpdf
.
minorversion
(
)
return
minorversion
end
1963 1964
-- It makes no sense to support levels so we only enable and disable and stick to level 3
1965
-- which is both fast and efficient.
1966 1967
local
frozen
=
false
1968
local
clevel
=
3
1969
local
olevel
=
1
1970 1971
function
lpdf
.
setcompression
(
level
,
objectlevel
,
freeze
)
1972
if
not
frozen
then
1973
compress
=
level
and
level
~
=
0
and
true
or
false
1974
objectstream
=
objectlevel
and
objectlevel
~
=
0
and
true
or
false
1975
frozen
=
freeze
1976
end
1977
end
1978 1979
function
lpdf
.
getcompression
(
)
1980
return
compress
and
olevel
or
0
,
objectstream
and
clevel
or
0
1981
end
1982 1983
function
lpdf
.
compresslevel
(
)
1984
return
compress
and
olevel
or
0
1985
end
1986 1987
function
lpdf
.
objectcompresslevel
(
)
1988
return
objectstream
and
clevel
or
0
1989
end
1990 1991
if
environment
.
arguments
.
nocompression
then
1992
lpdf
.
setcompression
(
0
,
0
,
true
)
1993
end
1994 1995
end
1996 1997
local
addtocache
,
flushcache
,
cache
do
1998 1999
local
data
,
d
=
{
}
,
0
2000
local
list
,
l
=
{
}
,
0
2001
local
coffset
=
0
2002
local
indices
=
{
}
2003 2004
local
maxsize
=
32
*
1024
-- uncompressed
2005
local
maxcount
=
0xFF
2006 2007
addtocache
=
function
(
n
,
str
)
2008
local
size
=
#
str
2009
if
size
=
=
0
then
2010
-- todo: message
2011
return
2012
end
2013
if
coffset
+
size
>
maxsize
or
d
=
=
maxcount
then
2014
flushcache
(
)
2015
end
2016
if
d
=
=
0
then
2017
nofobjects
=
nofobjects
+
1
2018
objects
[
nofobjects
]
=
false
2019
streams
[
nofobjects
]
=
indices
2020
cache
=
nofobjects
2021
end
2022
objects
[
n
]
=
-
cache
2023
indices
[
n
]
=
d
2024
d
=
d
+
1
2025
-- can have a comment n 0 obj as in luatex
2026
data
[
d
]
=
str
2027
l
=
l
+
1
;
list
[
l
]
=
n
2028
l
=
l
+
1
;
list
[
l
]
=
coffset
2029
coffset
=
coffset
+
size
+
1
2030
end
2031 2032
local
p_ObjStm
=
pdfconstant
(
"
ObjStm
"
)
2033 2034
flushcache
=
function
(
)
-- references cannot be stored
2035
if
l
>
0
then
2036
list
=
concat
(
list
,
"
"
)
2037
data
[
0
]
=
list
2038
data
=
concat
(
data
,
"
\010
"
,
0
,
d
)
2039
local
strobj
=
pdfdictionary
{
2040
Type
=
p_ObjStm
,
2041
N
=
d
,
2042
First
=
#
list
+
1
,
2043
}
2044
objects
[
cache
]
=
offset
2045
local
fb
2046
if
compress
then
2047
local
size
=
#
data
2048
local
comp
=
compressdata
(
data
,
size
)
2049
if
comp
and
#
comp
<
size
then
2050
data
=
comp
2051
fb
=
f_stream_b_d_c
2052
else
2053
fb
=
f_stream_b_d_u
2054
end
2055
else
2056
fb
=
f_stream_b_d_u
2057
end
2058
local
s
=
#
data
2059
local
b
=
fb
(
cache
,
strobj
(
)
,
s
)
2060
local
e
=
s_stream_e
2061
flush
(
f
,
b
)
2062
flush
(
f
,
data
)
2063
flush
(
f
,
e
)
2064
offset
=
offset
+
#
b
+
s
+
#
e
2065
data
,
d
=
{
}
,
0
2066
list
,
l
=
{
}
,
0
2067
coffset
=
0
2068
indices
=
{
}
2069
end
2070
end
2071 2072
end
2073 2074
do
2075 2076
local
names
=
{
}
2077
local
cache
=
{
}
2078
local
nofpages
=
0
2079 2080
local
texgetcount
=
tex
.
getcount
2081 2082
pdfreserveobject
=
function
(
name
)
2083
nofobjects
=
nofobjects
+
1
2084
objects
[
nofobjects
]
=
false
2085
if
name
then
2086
names
[
name
]
=
nofobjects
2087
if
trace_objects
then
2088
report_objects
(
"
reserving number %a under name %a
"
,
nofobjects
,
name
)
2089
end
2090
elseif
trace_objects
then
2091
report_objects
(
"
reserving number %a
"
,
nofobjects
)
2092
end
2093
return
nofobjects
2094
end
2095 2096
pdfpagereference
=
function
(
n
,
complete
)
-- true | false | nil | n [true,false]
2097
if
n
=
=
true
or
not
n
then
2098
complete
=
n
2099
n
=
texgetcount
(
"
realpageno
"
)
2100
end
2101
if
n
>
nofpages
then
2102
nofpages
=
n
2103
end
2104
local
r
=
pdfgetpagereference
(
n
)
2105
return
complete
and
pdfreference
(
r
)
or
r
2106
end
2107 2108
lpdf
.
reserveobject
=
pdfreserveobject
2109
lpdf
.
pagereference
=
pdfpagereference
2110 2111
function
lpdf
.
lastreferredpage
(
)
2112
return
nofpages
2113
end
2114 2115
function
lpdf
.
nofpages
(
)
-- this will change: document nofpages
2116
return
structures
.
pages
.
nofpages
2117
end
2118 2119
function
lpdf
.
object
(
...
)
2120
pdfdeferredobject
(
...
)
2121
end
2122 2123
function
lpdf
.
delayedobject
(
data
,
n
)
2124
if
n
then
2125
pdfdeferredobject
(
n
,
data
)
2126
else
2127
n
=
pdfdeferredobject
(
data
)
2128
end
2129
-- pdfreferenceobject(n)
2130
return
n
2131
end
2132 2133
pdfflushobject
=
function
(
name
,
data
)
2134
if
data
then
2135
local
named
=
names
[
name
]
2136
if
named
then
2137
if
not
trace_objects
then
2138
elseif
trace_details
then
2139
report_objects
(
"
flushing data to reserved object with name %a, data: %S
"
,
name
,
data
)
2140
else
2141
report_objects
(
"
flushing data to reserved object with name %a
"
,
name
)
2142
end
2143
return
pdfimmediateobject
(
named
,
tostring
(
data
)
)
2144
else
2145
if
not
trace_objects
then
2146
elseif
trace_details
then
2147
report_objects
(
"
flushing data to reserved object with number %s, data: %S
"
,
name
,
data
)
2148
else
2149
report_objects
(
"
flushing data to reserved object with number %s
"
,
name
)
2150
end
2151
return
pdfimmediateobject
(
name
,
tostring
(
data
)
)
2152
end
2153
else
2154
if
trace_objects
and
trace_details
then
2155
report_objects
(
"
flushing data: %S
"
,
name
)
2156
end
2157
return
pdfimmediateobject
(
tostring
(
name
)
)
2158
end
2159
end
2160 2161
pdfflushstreamobject
=
function
(
data
,
dict
,
compressed
,
objnum
)
-- default compressed
2162
if
trace_objects
then
2163
report_objects
(
"
flushing stream object of %s bytes
"
,
#
data
)
2164
end
2165
local
dtype
=
type
(
dict
)
2166
local
kind
=
compressed
=
=
"
raw
"
and
"
raw
"
or
"
stream
"
2167
local
nolength
=
nil
2168
if
compressed
=
=
"
raw
"
then
2169
compressed
=
false
2170
nolength
=
true
2171
-- data = string.formatters["<< %s >>stream\n%s\nendstream"](attr,data)
2172
end
2173
return
pdfdeferredobject
{
2174
objnum
=
objnum
,
2175
immediate
=
true
,
2176
nolength
=
nolength
,
2177
-- compresslevel = compressed == false and 0 or nil,
2178
compresslevel
=
compressed
,
2179
type
=
"
stream
"
,
2180
string
=
data
,
2181
attr
=
(
dtype
=
=
"
string
"
and
dict
)
or
(
dtype
=
=
"
table
"
and
dict
(
)
)
or
nil
,
2182
}
2183
end
2184 2185
function
lpdf
.
flushstreamfileobject
(
filename
,
dict
,
compressed
,
objnum
)
-- default compressed
2186
if
trace_objects
then
2187
report_objects
(
"
flushing stream file object %a
"
,
filename
)
2188
end
2189
local
dtype
=
type
(
dict
)
2190
return
pdfdeferredobject
{
2191
objnum
=
objnum
,
2192
immediate
=
true
,
2193
-- compresslevel = compressed == false and 0 or nil,
2194
compresslevel
=
compressed
,
2195
type
=
"
stream
"
,
2196
file
=
filename
,
2197
attr
=
(
dtype
=
=
"
string
"
and
dict
)
or
(
dtype
=
=
"
table
"
and
dict
(
)
)
or
nil
,
2198
}
2199
end
2200 2201
local
shareobjectcache
,
shareobjectreferencecache
=
{
}
,
{
}
2202 2203
function
lpdf
.
shareobject
(
content
)
2204
if
content
=
=
nil
then
2205
-- invalid object not created
2206
else
2207
content
=
tostring
(
content
)
2208
local
o
=
shareobjectcache
[
content
]
2209
if
not
o
then
2210
o
=
pdfimmediateobject
(
content
)
2211
shareobjectcache
[
content
]
=
o
2212
end
2213
return
o
2214
end
2215
end
2216 2217
pdfsharedobject
=
function
(
content
)
2218
if
content
=
=
nil
then
2219
-- invalid object not created
2220
else
2221
content
=
tostring
(
content
)
2222
local
r
=
shareobjectreferencecache
[
content
]
2223
if
not
r
then
2224
local
o
=
shareobjectcache
[
content
]
2225
if
not
o
then
2226
o
=
pdfimmediateobject
(
content
)
2227
shareobjectcache
[
content
]
=
o
2228
end
2229
r
=
pdfreference
(
o
)
2230
shareobjectreferencecache
[
content
]
=
r
2231
end
2232
return
r
2233
end
2234
end
2235 2236
lpdf
.
flushobject
=
pdfflushobject
2237
lpdf
.
flushstreamobject
=
pdfflushstreamobject
2238
lpdf
.
shareobjectreference
=
pdfsharedobject
2239
lpdf
.
sharedobject
=
pdfsharedobject
2240 2241
end
2242 2243
local
pages
=
table
.
setmetatableindex
(
function
(
t
,
k
)
2244
local
v
=
pdfreserveobject
(
)
2245
t
[
k
]
=
v
2246
return
v
2247
end
)
2248 2249
pdfgetpagereference
=
function
(
n
)
2250
return
pages
[
n
]
2251
end
2252 2253
lpdf
.
getpagereference
=
pdfgetpagereference
2254 2255
local
function
flushnormalobj
(
data
,
n
)
2256
if
not
n
then
2257
nofobjects
=
nofobjects
+
1
2258
n
=
nofobjects
2259
end
2260
data
=
f_object
(
n
,
data
)
2261
if
level
=
=
0
then
2262
objects
[
n
]
=
offset
2263
offset
=
offset
+
#
data
2264
flush
(
f
,
data
)
2265
else
2266
if
not
lastdeferred
then
2267
lastdeferred
=
n
2268
elseif
n
<
lastdeferred
then
2269
lastdeferred
=
n
2270
end
2271
objects
[
n
]
=
data
2272
end
2273
return
n
2274
end
2275 2276
local
function
flushstreamobj
(
data
,
n
,
dict
,
comp
,
nolength
)
2277
if
not
data
then
2278
report
(
"
no data for %S
"
,
dict
)
2279
return
2280
end
2281
if
not
n
then
2282
nofobjects
=
nofobjects
+
1
2283
n
=
nofobjects
2284
end
2285
local
size
=
#
data
2286
if
comp
~
=
false
then
2287
comp
=
compress
and
size
>
threshold
2288
end
2289
if
level
=
=
0
then
2290
local
b
=
nil
2291
local
e
=
s_stream_e
2292
if
nolength
then
2293
b
=
f_stream_b_d_r
(
n
,
dict
)
-- raw object, already treated
2294
else
2295
if
comp
then
2296
local
compdata
=
compressdata
(
data
,
size
)
2297
if
compdata
then
2298
local
compsize
=
#
compdata
2299
if
compsize
<
=
size
-
threshold
then
2300
data
=
compdata
2301
size
=
compsize
2302
else
2303
comp
=
false
2304
end
2305
else
2306
comp
=
false
2307
end
2308
end
2309
if
comp
then
2310
b
=
dict
and
f_stream_b_d_c
(
n
,
dict
,
size
)
or
f_stream_b_n_c
(
n
,
size
)
2311
else
2312
b
=
dict
and
f_stream_b_d_u
(
n
,
dict
,
size
)
or
f_stream_b_n_u
(
n
,
size
)
2313
end
2314
end
2315
flush
(
f
,
b
)
2316
flush
(
f
,
data
)
2317
flush
(
f
,
e
)
2318
objects
[
n
]
=
offset
2319
offset
=
offset
+
#
b
+
size
+
#
e
2320
else
2321
if
nolength
then
2322
data
=
f_stream_d_r
(
n
,
dict
,
data
)
-- raw object, already treated
2323
else
2324
if
comp
then
2325
local
compdata
=
compressdata
(
data
,
size
)
2326
if
compdata
then
2327
local
compsize
=
#
compdata
2328
if
compsize
<
=
size
-
threshold
then
2329
data
=
compdata
2330
size
=
compsize
2331
else
2332
comp
=
false
2333
end
2334
else
2335
comp
=
false
2336
end
2337
end
2338
if
comp
then
2339
data
=
dict
and
f_stream_d_c
(
n
,
dict
,
size
,
data
)
or
f_stream_n_c
(
n
,
size
,
data
)
2340
else
2341
data
=
dict
and
f_stream_d_u
(
n
,
dict
,
size
,
data
)
or
f_stream_n_u
(
n
,
size
,
data
)
2342
end
2343
end
2344
if
not
lastdeferred
then
2345
lastdeferred
=
n
2346
elseif
n
<
lastdeferred
then
2347
lastdeferred
=
n
2348
end
2349
objects
[
n
]
=
data
2350
end
2351
return
n
2352
end
2353 2354
flushdeferred
=
function
(
)
-- was forward defined
2355
if
lastdeferred
then
2356
for
n
=
lastdeferred
,
nofobjects
do
2357
local
o
=
objects
[
n
]
2358
if
type
(
o
)
=
=
"
string
"
then
2359
objects
[
n
]
=
offset
2360
offset
=
offset
+
#
o
2361
flush
(
f
,
o
)
2362
end
2363
end
2364
lastdeferred
=
false
2365
end
2366
end
2367 2368
pdfimmediateobject
=
function
(
a
,
b
,
c
,
d
)
2369
local
kind
--, immediate
2370
local
objnum
,
data
,
attr
,
filename
2371
local
compresslevel
,
objcompression
,
nolength
2372
local
argtype
=
type
(
a
)
2373
if
argtype
=
=
"
table
"
then
2374
kind
=
a
.
type
-- raw | stream
2375
-- immediate = a.immediate
2376
objnum
=
a
.
objnum
2377
attr
=
a
.
attr
2378
compresslevel
=
a
.
compresslevel
2379
objcompression
=
a
.
objcompression
2380
filename
=
a
.
file
2381
data
=
a
.
string
or
a
.
stream
or
"
"
2382
nolength
=
a
.
nolength
2383
if
kind
=
=
"
stream
"
then
2384
if
filename
then
2385
data
=
loaddata
(
filename
)
or
"
"
2386
end
2387
elseif
kind
=
=
"
raw
"
then
2388
if
filename
then
2389
data
=
loaddata
(
filename
)
or
"
"
2390
end
2391
elseif
kind
=
=
"
file
"
then
2392
kind
=
"
raw
"
2393
data
=
filename
and
loaddata
(
filename
)
or
"
"
2394
elseif
kind
=
=
"
streamfile
"
then
2395
kind
=
"
stream
"
2396
data
=
filename
and
loaddata
(
filename
)
or
"
"
2397
end
2398
else
2399
if
argtype
=
=
"
number
"
then
2400
objnum
=
a
2401
a
,
b
,
c
=
b
,
c
,
d
2402
else
2403
nofobjects
=
nofobjects
+
1
2404
objnum
=
nofobjects
2405
end
2406
if
b
then
2407
if
a
=
=
"
stream
"
then
2408
kind
=
"
stream
"
2409
data
=
b
2410
elseif
a
=
=
"
file
"
then
2411
-- kind = "raw"
2412
data
=
loaddata
(
b
)
2413
elseif
a
=
=
"
streamfile
"
then
2414
kind
=
"
stream
"
2415
data
=
loaddata
(
b
)
2416
else
2417
data
=
"
"
-- invalid object
2418
end
2419
attr
=
c
2420
else
2421
-- kind = "raw"
2422
data
=
a
2423
end
2424
end
2425
if
not
objnum
then
2426
nofobjects
=
nofobjects
+
1
2427
objnum
=
nofobjects
2428
end
2429
-- todo: immediate
2430
if
kind
=
=
"
stream
"
then
2431
flushstreamobj
(
data
,
objnum
,
attr
,
compresslevel
,
nolength
)
-- nil == auto
2432
elseif
objectstream
and
objcompression
~
=
false
then
2433
addtocache
(
objnum
,
data
)
2434
else
2435
flushnormalobj
(
data
,
objnum
)
2436
end
2437
return
objnum
2438
end
2439 2440
pdfdeferredobject
=
pdfimmediateobject
2441 2442
lpdf
.
deferredobject
=
pdfimmediateobject
2443
lpdf
.
immediateobject
=
pdfimmediateobject
2444 2445
-- In lua 5.4 the methods are now moved one metalevel deeper so we need to get them
2446
-- from mt.__index instead. (I did get that at first.) It makes for a slightly (imo)
2447
-- nicer interface but no real gain in speed as we don't flush that often.
2448 2449
local
openfile
,
closefile
do
2450 2451
-- I used to do <space><lf> but then figured out that when I open and save a file in a mode
2452
-- that removes trailing spaces, the xref becomes invalid. The problem was then that a
2453
-- reconstruction of the file by a viewer gives weird effects probably because percent symbols
2454
-- gets interpreted then. Thanks to Ross Moore for noticing this side effect!
2455 2456
local
f_used
=
formatters
[
"
%010i 00000 n\013\010
"
]
2457
local
f_link
=
formatters
[
"
%010i 00000 f\013\010
"
]
2458
local
f_first
=
formatters
[
"
%010i 65535 f\013\010
"
]
2459 2460
local
f_pdf_tag
=
formatters
[
"
%%PDF-%i.%i\010
"
]
2461
local
f_xref
=
formatters
[
"
xref\0100 %i\010
"
]
2462
local
f_trailer_id
=
formatters
[
"
trailer\010<< %s /ID [ <%s> <%s> ] >>\010startxref\010%i\010%%%%EOF
"
]
2463
local
f_trailer_no
=
formatters
[
"
trailer\010<< %s >>\010startxref\010%i\010%%%%EOF
"
]
2464
local
f_startxref
=
formatters
[
"
startxref\010%i\010%%%%EOF
"
]
2465 2466
local
inmemory
=
false
2467
local
close
=
false
2468
local
update
=
false
2469 2470
-- local banner <const> = "%\xCC\xD5\xC1\xD4\xC5\xD8\xD0\xC4\xC6\010" -- LUATEXPDF (+128)
2471
local
banner
<const>
=
"
%\xC3\xCF\xCE\xD4\xC5\xD8\xD4\xD0\xC4\xC6\010
"
-- CONTEXTPDF (+128)
2472 2473 2474
-- local removefile = os.remove
2475 2476
openfile
=
function
(
filename
)
2477
if
inmemory
then
2478
local
n
=
0
2479
f
=
{
}
2480
flush
=
function
(
f
,
s
)
2481
n
=
n
+
1
f
[
n
]
=
s
2482
-- offset = offset + #s
2483
end
2484
close
=
function
(
f
)
2485
f
=
concat
(
f
)
2486
io
.
savedata
(
filename
,
f
)
2487
f
=
false
2488
end
2489
update
=
function
(
f
,
s
)
2490
f
[
1
]
=
s
2491
end
2492
-- local n = 0
2493
-- f = {
2494
-- write = function(self,s)
2495
-- n = n + 1 f[n] = s
2496
-- end,
2497
-- close = function(self)
2498
-- f = concat(f)
2499
-- io.savedata(filename,f)
2500
-- f = false
2501
-- end,
2502
-- }
2503
else
2504
f
=
io
.
open
(
filename
,
"
wb
"
)
2505
if
not
f
then
2506
report
(
)
2507
report
(
"
quitting because file %a cannot be opened for writing
"
,
filename
)
2508
report
(
)
2509
os
.
exit
(
)
2510
end
2511
-- f:setvbuf("full",64*1024)
2512
local
m
=
getmetatable
(
f
)
2513
flush
=
m
.
write
or
m
.
__index
.
write
2514
close
=
m
.
close
or
m
.
__index
.
close
2515
update
=
function
(
f
,
s
)
2516
f
:
seek
(
"
set
"
,
0
)
2517
f
:
write
(
s
)
2518
end
2519
end
2520
local
version
=
f_pdf_tag
(
majorversion
,
minorversion
)
2521
flush
(
f
,
version
)
2522
flush
(
f
,
banner
)
2523
offset
=
offset
+
#
version
+
#
banner
2524
end
2525 2526
closefile
=
function
(
abort
)
2527
if
abort
then
2528
close
(
f
)
2529
if
not
environment
.
arguments
.
nodummy
then
2530
f
=
io
.
open
(
abort
,
"
wb
"
)
2531
if
f
then
2532
local
name
=
resolvers
.
findfile
(
"
context-lmtx-error.pdf
"
)
2533
if
name
then
2534
local
data
=
io
.
loaddata
(
name
)
2535
if
data
then
2536
f
:
write
(
data
)
2537
f
:
close
(
)
2538
return
2539
end
2540
end
2541
f
:
close
(
)
2542
end
2543
end
2544
os
.
remove
(
abort
)
2545
else
2546
local
xrefoffset
=
offset
2547
local
lastfree
=
0
2548
local
noffree
=
0
2549
local
catalog
=
lpdf
.
getcatalog
(
)
2550
local
info
=
lpdf
.
getinfo
(
)
2551
local
trailerid
=
lpdf
.
gettrailerid
(
)
2552
if
objectstream
then
2553
flushdeferred
(
)
2554
flushcache
(
)
2555
--
2556
xrefoffset
=
offset
2557
--
2558
nofobjects
=
nofobjects
+
1
2559
objects
[
nofobjects
]
=
offset
-- + 1
2560
--
2561
-- combine these three in one doesn't really give less code so
2562
-- we go for the efficient ones
2563
--
2564
local
nofbytes
=
4
2565
local
c1
,
c2
,
c3
,
c4
2566
if
offset
<
=
0xFFFF
then
2567
nofbytes
=
2
2568
for
i
=
1
,
nofobjects
do
2569
local
o
=
objects
[
i
]
2570
if
not
o
then
2571
noffree
=
noffree
+
1
2572
else
2573
local
strm
=
o
<
0
2574
if
strm
then
2575
o
=
-
o
2576
end
2577
c1
=
(
o
>
>
8
)
&
0xFF
2578
c2
=
(
o
>
>
0
)
&
0xFF
2579
if
strm
then
2580
objects
[
i
]
=
char
(
2
,
c1
,
c2
,
streams
[
o
]
[
i
]
)
2581
else
2582
objects
[
i
]
=
char
(
1
,
c1
,
c2
,
0
)
2583
end
2584
end
2585
end
2586
if
noffree
>
0
then
2587
for
i
=
nofobjects
,
1
,
-1
do
2588
local
o
=
objects
[
i
]
2589
if
not
o
then
2590
local
f1
=
(
lastfree
>
>
8
)
&
0xFF
2591
local
f2
=
(
lastfree
>
>
0
)
&
0xFF
2592
objects
[
i
]
=
char
(
0
,
f1
,
f2
,
0
)
2593
lastfree
=
i
2594
end
2595
end
2596
end
2597
elseif
offset
<
=
0xFFFFFF
then
2598
nofbytes
=
3
2599
for
i
=
1
,
nofobjects
do
2600
local
o
=
objects
[
i
]
2601
if
not
o
then
2602
noffree
=
noffree
+
1
2603
else
2604
local
strm
=
o
<
0
2605
if
strm
then
2606
o
=
-
o
2607
end
2608
c1
=
(
o
>
>
16
)
&
0xFF
2609
c2
=
(
o
>
>
8
)
&
0xFF
2610
c3
=
(
o
>
>
0
)
&
0xFF
2611
if
strm
then
2612
objects
[
i
]
=
char
(
2
,
c1
,
c2
,
c3
,
streams
[
o
]
[
i
]
)
2613
else
2614
objects
[
i
]
=
char
(
1
,
c1
,
c2
,
c3
,
0
)
2615
end
2616
end
2617
end
2618
if
noffree
>
0
then
2619
for
i
=
nofobjects
,
1
,
-1
do
2620
local
o
=
objects
[
i
]
2621
if
not
o
then
2622
local
f1
=
(
lastfree
>
>
16
)
&
0xFF
2623
local
f2
=
(
lastfree
>
>
8
)
&
0xFF
2624
local
f3
=
(
lastfree
>
>
0
)
&
0xFF
2625
objects
[
i
]
=
char
(
0
,
f1
,
f2
,
f3
,
0
)
2626
lastfree
=
i
2627
end
2628
end
2629
end
2630
else
2631
nofbytes
=
4
2632
for
i
=
1
,
nofobjects
do
2633
local
o
=
objects
[
i
]
2634
if
not
o
then
2635
noffree
=
noffree
+
1
2636
else
2637
local
strm
=
o
<
0
2638
if
strm
then
2639
o
=
-
o
2640
end
2641
c1
=
(
o
>
>
24
)
&
0xFF
2642
c2
=
(
o
>
>
16
)
&
0xFF
2643
c3
=
(
o
>
>
8
)
&
0xFF
2644
c4
=
(
o
>
>
0
)
&
0xFF
2645
if
strm
then
2646
objects
[
i
]
=
char
(
2
,
c1
,
c2
,
c3
,
c4
,
streams
[
o
]
[
i
]
)
2647
else
2648
objects
[
i
]
=
char
(
1
,
c1
,
c2
,
c3
,
c4
,
0
)
2649
end
2650
end
2651
end
2652
if
noffree
>
0
then
2653
for
i
=
nofobjects
,
1
,
-1
do
2654
local
o
=
objects
[
i
]
2655
if
not
o
then
2656
local
f1
=
(
lastfree
>
>
24
)
&
0xFF
2657
local
f2
=
(
lastfree
>
>
16
)
&
0xFF
2658
local
f3
=
(
lastfree
>
>
8
)
&
0xFF
2659
local
f4
=
(
lastfree
>
>
0
)
&
0xFF
2660
objects
[
i
]
=
char
(
0
,
f1
,
f2
,
f3
,
f4
,
0
)
2661
lastfree
=
i
2662
end
2663
end
2664
end
2665
end
2666
objects
[
0
]
=
rep
(
"
\0
"
,
1
+
nofbytes
+
1
)
2667
local
data
=
concat
(
objects
,
"
"
,
0
,
nofobjects
)
2668
local
size
=
#
data
2669
local
xref
=
pdfdictionary
{
2670
Type
=
pdfconstant
(
"
XRef
"
)
,
2671
Size
=
nofobjects
+
1
,
2672
W
=
pdfarray
{
1
,
nofbytes
,
1
}
,
2673
Root
=
catalog
,
2674
Info
=
info
,
2675
ID
=
trailerid
and
pdfarray
{
pdfliteral
(
trailerid
,
true
)
,
pdfliteral
(
trailerid
,
true
)
}
or
nil
,
2676
}
2677
local
fb
2678
if
compress
then
2679
local
comp
=
compressdata
(
data
,
size
)
2680
if
comp
then
2681
data
=
comp
2682
size
=
#
data
2683
fb
=
f_stream_b_d_c
2684
else
2685
fb
=
f_stream_b_d_u
2686
end
2687
else
2688
fb
=
f_stream_b_d_u
2689
end
2690
flush
(
f
,
fb
(
nofobjects
,
xref
(
)
,
size
)
)
2691
flush
(
f
,
data
)
2692
flush
(
f
,
s_stream_e
)
2693
flush
(
f
,
f_startxref
(
xrefoffset
)
)
2694
else
2695
flushdeferred
(
)
2696
xrefoffset
=
offset
2697
flush
(
f
,
f_xref
(
nofobjects
+
1
)
)
2698
local
trailer
=
pdfdictionary
{
2699
Size
=
nofobjects
+
1
,
2700
Root
=
catalog
,
2701
Info
=
info
,
2702
}
2703
for
i
=
1
,
nofobjects
do
2704
local
o
=
objects
[
i
]
2705
if
o
then
2706
objects
[
i
]
=
f_used
(
o
)
2707
end
2708
end
2709
for
i
=
nofobjects
,
1
,
-1
do
2710
local
o
=
objects
[
i
]
2711
if
not
o
then
2712
objects
[
i
]
=
f_link
(
lastfree
)
2713
lastfree
=
i
2714
end
2715
end
2716
objects
[
0
]
=
f_first
(
lastfree
)
2717
flush
(
f
,
concat
(
objects
,
"
"
,
0
,
nofobjects
)
)
2718
trailer
.
Size
=
nofobjects
+
1
2719
if
trailerid
then
2720
flush
(
f
,
f_trailer_id
(
trailer
(
)
,
trailerid
,
trailerid
,
xrefoffset
)
)
2721
else
2722
flush
(
f
,
f_trailer_no
(
trailer
(
)
,
xrefoffset
)
)
2723
end
2724
end
2725
update
(
f
,
f_pdf_tag
(
majorversion
,
minorversion
)
)
2726
close
(
f
)
2727
end
2728
io
.
flush
(
)
2729
closefile
=
function
(
)
end
2730
end
2731 2732
end
2733 2734
-- For the moment we overload it here, although back-fil.lua eventually will
2735
-- be merged with back-pdf as it's pdf specific, or maybe back-imp-pdf or so.
2736 2737
do
2738 2739
-- We overload img but at some point it will even go away, so we just
2740
-- reimplement what we need in context. This will change completely i.e.
2741
-- we will drop the low level interface!
2742 2743
local
pdfbackend
=
backends
.
registered
.
pdf
2744
local
nodeinjections
=
pdfbackend
.
nodeinjections
2745
local
codeinjections
=
pdfbackend
.
codeinjections
2746 2747
local
imagetypes
=
images
.
types
-- pdf png jpg jp2 jbig2 stream
2748
local
img_none
=
imagetypes
.
none
2749 2750
local
newimagerule
=
nuts
.
pool
.
imagerule
2751
local
setattrlist
=
nuts
.
setattrlist
2752
local
setprop
=
nuts
.
setprop
2753 2754
local
report_images
=
logs
.
reporter
(
"
backend
"
,
"
images
"
)
2755 2756
local
lastindex
=
0
2757
local
indices
=
{
}
2758 2759
local
bpfactor
=
number
.
dimenfactors
.
bp
2760 2761
function
codeinjections
.
newimage
(
specification
)
2762
return
specification
2763
end
2764 2765
function
codeinjections
.
copyimage
(
original
)
2766
return
setmetatableindex
(
original
)
2767
end
2768 2769
function
codeinjections
.
scanimage
(
specification
)
2770
-- placeholder, doesn't give back dimensions etc but will be plugged in
2771
return
specification
2772
end
2773 2774
local
function
embedimage
(
specification
)
2775
if
specification
then
2776
lastindex
=
lastindex
+
1
2777
index
=
lastindex
2778
specification
.
index
=
index
2779
local
xobject
=
pdfdictionary
{
}
2780
if
not
specification
.
notype
then
2781
xobject
.
Type
=
pdf_xobject
2782
xobject
.
Subtype
=
pdf_form
2783
xobject
.
FormType
=
1
2784
end
2785
local
bbox
=
specification
.
bbox
2786
if
bbox
and
not
specification
.
nobbox
then
2787
xobject
.
BBox
=
pdfarray
{
2788
bbox
[
1
]
*
bpfactor
,
2789
bbox
[
2
]
*
bpfactor
,
2790
bbox
[
3
]
*
bpfactor
,
2791
bbox
[
4
]
*
bpfactor
,
2792
}
2793
end
2794
xobject
=
xobject
+
specification
.
attr
2795
if
bbox
and
not
specification
.
width
then
2796
specification
.
width
=
bbox
[
3
]
2797
end
2798
if
bbox
and
not
specification
.
height
then
2799
specification
.
height
=
bbox
[
4
]
2800
end
2801
local
dict
=
xobject
(
)
2802
--
2803
nofobjects
=
nofobjects
+
1
2804
local
objnum
=
nofobjects
2805
local
nolength
=
specification
.
nolength
2806
local
stream
=
specification
.
stream
or
specification
.
string
2807
--
2808
-- We cannot set type in native img so we need this hack or
2809
-- otherwise we need to patch too much. Better that i write
2810
-- a wrapper then. Anyway, it has to be done better: a key that
2811
-- tells either or not to scale by xsize/ysize when flushing.
2812
--
2813
if
not
specification
.
type
then
2814
local
kind
=
specification
.
kind
2815
if
kind
then
2816
-- take that one
2817
elseif
attr
and
find
(
attr
,
"
BBox
"
)
then
2818
kind
=
img_stream
2819
else
2820
-- hack: a bitmap
2821
kind
=
img_none
2822
end
2823
specification
.
type
=
kind
2824
specification
.
kind
=
kind
2825
end
2826
flushstreamobj
(
stream
,
objnum
,
dict
,
compresslevel
,
nolength
)
2827
specification
.
objnum
=
objnum
2828
specification
.
rotation
=
specification
.
rotation
or
0
2829
specification
.
orientation
=
specification
.
orientation
or
0
2830
specification
.
transform
=
specification
.
transform
or
0
2831
specification
.
stream
=
nil
2832
specification
.
attr
=
nil
2833
specification
.
type
=
specification
.
kind
or
specification
.
type
or
img_none
2834
indices
[
index
]
=
specification
-- better create a real specification
2835
return
specification
2836
end
2837
end
2838 2839
codeinjections
.
embedimage
=
embedimage
2840 2841
function
codeinjections
.
wrapimage
(
specification
)
2842
--
2843
local
index
=
specification
.
index
2844
if
not
index
then
2845
embedimage
(
specification
)
2846
end
2847
--
2848
local
n
=
newimagerule
(
2849
specification
.
width
or
0
,
2850
specification
.
height
or
0
,
2851
specification
.
depth
or
0
2852
)
2853
setattrlist
(
n
,
true
)
2854
setprop
(
n
,
"
index
"
,
specification
.
index
)
2855
return
tonode
(
n
)
2856
end
2857 2858
pdfincludeimage
=
function
(
index
)
2859
local
specification
=
indices
[
index
]
2860
if
specification
then
2861
local
bbox
=
specification
.
bbox
2862
local
xorigin
=
bbox
[
1
]
2863
local
yorigin
=
bbox
[
2
]
2864
local
xsize
=
bbox
[
3
]
-
xorigin
-- we need the original ones, not the 'rotated' ones
2865
local
ysize
=
bbox
[
4
]
-
yorigin
-- we need the original ones, not the 'rotated' ones
2866
local
transform
=
specification
.
transform
or
0
2867
local
objnum
=
specification
.
objnum
or
pdfreserveobject
(
)
2868
local
groupref
=
nil
2869
local
kind
=
specification
.
kind
or
specification
.
type
or
img_none
-- determines scaling type
2870
return
2871
kind
,
2872
xorigin
,
yorigin
,
2873
xsize
,
ysize
,
2874
transform
,
2875
objnum
,
2876
groupref
2877
end
2878
end
2879 2880
lpdf
.
includeimage
=
pdfincludeimage
2881 2882
end
2883 2884
-- The driver.
2885 2886
do
2887 2888
-- local addsuffix = file.addsuffix
2889
local
texgetbox
=
tex
.
getbox
2890 2891
local
pdfname
=
nil
2892
local
converter
=
nil
2893
local
useddriver
=
nil
-- a bit of a hack
2894 2895
local
function
outputfilename
(
driver
)
2896
return
pdfname
2897
end
2898 2899
-- local outputfilename ; do -- old todo usedname in ^^
2900
-- local filename = nil
2901
-- outputfilename = function(driver,usedname)
2902
-- if usedname and usedname ~= "" then
2903
-- filename = addsuffix(usedname,"pdf")
2904
-- elseif not filename or filename == "" then
2905
-- filename = addsuffix(tex.jobname,"pdf")
2906
-- end
2907
-- return filename
2908
-- end
2909
-- end
2910 2911
-- todo: prevent twice
2912 2913
local
function
prepare
(
driver
)
2914
if
not
environment
.
initex
then
2915
--
2916
backends
.
initialize
(
"
pdf
"
)
-- also does bindings
2917
--
2918
pdfname
=
tex
.
jobname
.
.
"
.pdf
"
2919
openfile
(
pdfname
)
2920
--
2921
luatex
.
registerstopactions
(
1
,
function
(
)
2922
if
pdfname
then
2923
lpdf
.
finalizedocument
(
)
2924
closefile
(
)
2925
pdfname
=
nil
2926
end
2927
end
)
2928
--
2929
luatex
.
registerpageactions
(
1
,
function
(
)
2930
if
pdfname
then
2931
lpdf
.
finalizepage
(
true
)
2932
end
2933
end
)
2934
--
2935
lpdf
.
registerdocumentfinalizer
(
wrapupdocument
,
nil
,
"
wrapping up
"
)
2936
--
2937
statistics
.
register
(
"
result saved in file
"
,
function
(
)
2938
local
outputfilename
=
environment
.
outputfilename
or
environment
.
jobname
or
tex
.
jobname
or
"
<unset>
"
2939
outputfilename
=
string
.
gsub
(
outputfilename
,
"
^%./+
"
,
"
"
)
-- todo: make/use a helper
2940
return
string
.
format
(
"
%s.%s, compresslevel %s, objectcompresslevel %s
"
,
outputfilename
,
"
pdf
"
,
lpdf
.
getcompression
(
)
)
2941
end
)
2942
--
2943
luatex
.
registerstopactions
(
function
(
)
2944
if
pdfname
then
2945
local
r
=
lpdf
.
lastreferredpage
(
)
-- somehow referenced
2946
local
s
=
lpdf
.
getnofpages
(
)
-- in page tree, saved in file
2947
local
t
=
lpdf
.
nofpages
(
)
-- in tuc file
2948
if
r
>
s
then
2949
report
(
)
2950
report
(
"
referred pages: %i, saved pages %i, pages from tuc file: %i, possible corrupt file
"
,
r
,
s
,
t
)
2951
report
(
)
2952
end
2953
end
2954
end
)
2955
end
2956
converter
=
drivers
.
converters
.
lmtx
2957
useddriver
=
driver
2958
end
2959 2960
local
function
wrapup
(
driver
)
2961
if
pdfname
then
2962
closefile
(
)
2963
pdfname
=
nil
2964
end
2965
end
2966 2967
local
function
cleanup
(
driver
)
2968
if
pdfname
then
2969
closefile
(
pdfname
)
2970
pdfname
=
nil
2971
end
2972
end
2973 2974
local
function
convert
(
driver
,
boxnumber
)
2975
converter
(
driver
,
texgetbox
(
boxnumber
)
,
"
page
"
)
2976
end
2977 2978
localconverter
=
function
(
...
)
2979
converter
(
useddriver
,
...
)
2980
end
2981 2982
drivers
.
install
{
2983
name
=
"
pdf
"
,
2984
flushers
=
flushers
,
2985
actions
=
{
2986
prepare
=
prepare
,
2987
wrapup
=
wrapup
,
2988
cleanup
=
cleanup
,
2989
--
2990
initialize
=
initialize
,
2991
convert
=
convert
,
2992
finalize
=
finalize
,
2993
--
2994
outputfilename
=
outputfilename
,
2995
}
,
2996
}
2997 2998
end
2999