summaryrefslogtreecommitdiff
path: root/tex/context/base/lpdf-epa.lua
blob: 61d57b8d361c2c90f06237822d55882a0eccde60 (plain)
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
if not modules then modules = { } end modules ['lpdf-epa'] = {
    version   = 1.001,
    comment   = "companion to lpdf-epa.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- This is a rather experimental feature and the code will probably
-- change.

local type, tonumber = type, tonumber
local format, gsub = string.format, string.gsub
local formatters = string.formatters

----- lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns

local trace_links    = false  trackers.register("figures.links", function(v) trace_links = v end)
local report_link    = logs.reporter("backend","merging")

local backends       = backends
local lpdf           = lpdf
local context        = context

local variables      = interfaces.variables
local codeinjections = backends.pdf.codeinjections
----- urlescaper     = lpegpatterns.urlescaper
----- utftohigh      = lpegpatterns.utftohigh
local escapetex      = characters.filters.utf.private.escape

local layerspec = { -- predefining saves time
    "epdflinks"
}

local function makenamespace(filename)
    return format("lpdf-epa-%s-",file.removesuffix(file.basename(filename)))
end

local function add_link(x,y,w,h,destination,what)
    x = x .. "bp"
    y = y .. "bp"
    w = w .. "bp"
    h = h .. "bp"
    if trace_links then
        report_link("destination %a, type %a, dx %s, dy %s, wd %s, ht %s",destination,what,x,y,w,h)
    end
    local locationspec = { -- predefining saves time
        x      = x,
        y      = y,
        preset = "leftbottom",
    }
    local buttonspec = {
        width  = w,
        height = h,
        offset = variables.overlay,
        frame  = trace_links and variables.on or variables.off,
    }
    context.setlayer (
        layerspec,
        locationspec,
        function() context.button ( buttonspec, "", { destination } ) end
     -- context.nested.button(buttonspec, "", { destination }) -- time this
    )
end

local function link_goto(x,y,w,h,document,annotation,pagedata,namespace)
    local a = annotation.A
    if a then
        local destination = a.D -- [ 18 0 R /Fit ]
        local what = "page"
        if type(destination) == "string" then
            local destinations = document.destinations
            local wanted = destinations[destination]
            destination = wanted and wanted.D
            if destination then what = "named" end
        end
        local pagedata = destination and destination[1]
        if pagedata then
            local destinationpage = pagedata.number
            if destinationpage then
                add_link(x,y,w,h,namespace .. destinationpage,what)
            end
        end
    end
end

local function link_uri(x,y,w,h,document,annotation)
    local url = annotation.A.URI
    if url then
     -- url = lpegmatch(urlescaper,url)
     -- url = lpegmatch(utftohigh,url)
        url = escapetex(url)
        add_link(x,y,w,h,formatters["url(%s)"](url),"url")
    end
end

local function link_file(x,y,w,h,document,annotation)
    local a = annotation.A
    if a then
        local filename = a.F
        if filename then
            filename = escapetex(filename)
            local destination = a.D
            if not destination then
                add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
            elseif type(destination) == "string" then
                add_link(x,y,w,h,formatters["%s::%s"](filename,destination),"file (named)")
            else
                destination = destination[1] -- array
                if tonumber(destination) then
                    add_link(x,y,w,h,formatters["%s::page(%s)"](filename,destination),"file (page)")
                else
                    add_link(x,y,w,h,formatters["file(%s)"](filename),"file")
                end
            end
        end
    end
end

function codeinjections.mergereferences(specification)
    if figures and not specification then
        specification = figures and figures.current()
        specification = specification and specification.status
    end
    if specification then
        local fullname = specification.fullname
        local document = lpdf.epdf.load(fullname)
        if document then
            local pagenumber  = specification.page    or 1
            local xscale      = specification.yscale  or 1
            local yscale      = specification.yscale  or 1
            local size        = specification.size    or "crop" -- todo
            local pagedata    = document.pages[pagenumber]
            local annotations = pagedata and pagedata.Annots
            if annotations and annotations.n > 0 then
                local namespace = format("lpdf-epa-%s-",file.removesuffix(file.basename(fullname)))
                local reference = namespace .. pagenumber
                local mediabox = pagedata.MediaBox
                local llx, lly, urx, ury = mediabox[1], mediabox[2], mediabox[3], mediabox[4]
                local width, height = xscale * (urx - llx), yscale * (ury - lly) -- \\overlaywidth, \\overlayheight
                context.definelayer( { "epdflinks" }, { height = height.."bp" , width = width.."bp" })
                for i=1,annotations.n do
                    local annotation = annotations[i]
                    if annotation then
                        local subtype = annotation.Subtype
                        local rectangle = annotation.Rect
                        local a_llx, a_lly, a_urx, a_ury = rectangle[1], rectangle[2], rectangle[3], rectangle[4]
                        local x, y = xscale * (a_llx -   llx), yscale * (a_lly -   lly)
                        local w, h = xscale * (a_urx - a_llx), yscale * (a_ury - a_lly)
                        if subtype  == "Link" then
                            local a = annotation.A
                            if a then
                                local linktype = a.S
                                if linktype == "GoTo" then
                                    link_goto(x,y,w,h,document,annotation,pagedata,namespace)
                                elseif linktype == "GoToR" then
                                    link_file(x,y,w,h,document,annotation)
                                elseif linktype == "URI" then
                                    link_uri(x,y,w,h,document,annotation)
                                elseif trace_links then
                                    report_link("unsupported link annotation %a",linktype)
                                end
                            else
                                report_link("mising link annotation")
                            end
                        elseif trace_links then
                            report_link("unsupported annotation %a",subtype)
                        end
                    elseif trace_links then
                        report_link("broken annotation, index %a",i)
                    end
                end
                context.flushlayer { "epdflinks" }
             -- context("\\gdef\\figurereference{%s}",reference) -- global
                context.setgvalue("figurereference",reference) -- global
                if trace_links then
                    report_link("setting figure reference to %a",reference)
                end
                specification.reference = reference
                return namespace
            end
        end
    end
    return ""-- no namespace, empty, not nil
end

function codeinjections.mergeviewerlayers(specification)
    -- todo: parse included page for layers
    if true then
        return
    end
    if not specification then
        specification = figures and figures.current()
        specification = specification and specification.status
    end
    if specification then
        local fullname = specification.fullname
        local document = lpdf.epdf.load(fullname)
        if document then
            local namespace = format("lpdf:epa:%s:",file.removesuffix(file.basename(fullname)))
            local layers = document.layers
            if layers then
                for i=1,layers.n do
                    local layer = layers[i]
                    if layer then
                        local tag = namespace .. gsub(layer," ",":")
                        local title = tag
                        if trace_links then
                            report_link("using layer %a",tag)
                        end
                        attributes.viewerlayers.define { -- also does some cleaning
                            tag       = tag, -- todo: #3A or so
                            title     = title,
                            visible   = variables.start,
                            editable  = variables.yes,
                            printable = variables.yes,
                        }
                        codeinjections.useviewerlayer(tag)
                    elseif trace_links then
                        report_link("broken layer, index %a",i)
                    end
                end
            end
        end
    end
end