1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
local M = { }
local alloc = require 'minim-alloc'
local cb = require 'minim-callbacks'
-- the standard table design
local function init_resources()
return {
ExtGState = { },
ColorSpace = { },
Pattern = { },
Shading = { },
}
end
-- the module will use optex’s functions if present
M.self_destruct = pdf.add_page_resource or false
-- resource_data is a table that can contain any data. only the following keys
-- are used by this module:
--
-- _entry_ internal value; do not use
-- value value in the resource dictionary
-- write() called for writing the resource to the pdf; returns value
--
-- of the last two, only one needs be present; if the value is missing it will
-- be set at first use to the result of write().
--
local resource_data = init_resources() -- name ↦ { [write = ... | value = ...], ... }
--
function M.add_resource(kind, name, resource)
resource_data[kind][name] = resource
end
function M.get_resource(kind, name)
return resource_data[kind][name]
end
function M.use_resource(kind, name)
local res = resource_data[kind][name]
if not res then
return alloc.err('Unknown %s resource: %s', kind, name)
end
res.value = res.value or res:write()
if not res._entry_ then -- first-use
res._entry_ = string.format('%s %s', alloc.pdf_name(name), res.value)
if M.self_destruct then
M.self_destruct(kind, name, res.value)
end
end
return res._entry_
end
-- global resources are mainly for pgf compatibility: it contains entries to
-- the resource dictionaries that will be added for every page.
--
local global_resources = init_resources() -- name ↦ '/Key <value>'
--
function M.add_global_resource(kind, name, value)
local r = global_resources[kind]
if M.self_destruct then
M.self_destruct(kind, name, value)
elseif name == nil then -- used for pgf compatibility
if value ~= '' then
r[''] = r[''] and string.format('%s %s', global_resources[kind][''], value) or value
end
else
r[name] = string.format('%s %s', alloc.pdf_name(name), value)
end
end
-- we can now provide optex’s interface
if not M.self_destruct then
pdf.add_page_resource = M.add_global_resource
end
-- for nonglobal resources, every use must be marked with a late_lua node. from
-- those, the M.use_resouce() function will be called automatically.
--
local page_resources = init_resources() -- name ↦ '/Key <value>'
--
function _with_pdf_resource_(kind, name) -- global, for use in latelua
page_resources[kind][name] = M.use_resource(kind, name)
end
function M.use_resource_node(kind, name)
local n = node.new('whatsit', 'late_lua')
n.data = string.format('_with_pdf_resource_("%s", "%s")', kind, name)
return n
end
alloc.luadef('withpdfresource', function()
node.write(M.use_resource_node(token.scan_string(), token.scan_string()))
end, 'protected')
-- construction and caching of resource dictionaries.
--
local previous_dicts = init_resources() -- pdf dict ↦ objnum
--
local function make_resource_entry(kind)
local arr = { }
for _, entry in pairs(page_resources[kind]) do table.insert(arr, entry) end
for _, entry in pairs(global_resources[kind]) do table.insert(arr, entry) end
if #arr == 0 then return false end
table.sort(arr)
arr[0] = '<<'; table.insert(arr, '>>')
local dict = table.concat(arr, ' ', 0)
local objnum = previous_dicts[kind][dict]
if not objnum then
objnum = pdf.obj(dict)
pdf.refobj(objnum)
previous_dicts[kind][dict] = objnum
end
return string.format('/%s %d 0 R', kind, objnum)
end
local removal_regex = {
ExtGState = ' */ExtGState *%d+ +0 *R',
ColorSpace = ' */ColorSpace *%d+ +0 *R',
Pattern = ' */Pattern *%d+ +0 *R',
Shading = ' */Shading *%d+ +0 *R' }
local function update_page_resources(shippingout)
if M.self_destruct then return end
local resources = shippingout and pdf.getpageresources() or pdf.getxformresources() or ''
for kind, used in pairs(page_resources) do
resources = string.gsub(resources, removal_regex[kind], '')
local entry = make_resource_entry(kind)
if entry then
resources = resources == '' and entry or string.format('%s %s', resources, entry)
end
end
-- update page resources
if shippingout then
pdf.setpageresources(resources)
else
pdf.setxformresources(resources)
end
page_resources = init_resources()
end
cb.register('finish_pdfpage', update_page_resources)
-- for pgf compatibility, reroute the pgf resource registration functions to
-- our global resource pool.
--
alloc.luadef('minim:pgf:fix:luaside', function()
alloc.luadef('pgf@sys@addpdfresource@extgs@plain', function()
M.add_global_resource('ExtGState', nil, token.scan_string())
end)
alloc.luadef('pgf@sys@addpdfresource@patterns@plain', function()
M.add_global_resource('Pattern', nil, token.scan_string())
end)
alloc.luadef('pgf@sys@addpdfresource@colorspaces@plain', function()
M.add_global_resource('ColorSpace', nil, token.scan_string())
end)
end)
return M
|