1% pdflink.w
2%
3% Copyright 2009-2012 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25@ @c
26#define pdf_link_margin          dimen_par(pdf_link_margin_code)
27
28@ To implement nested link annotations, we need a stack to hold copy of
29|pdf_start_link_node|'s that are being written out, together with their box
30nesting level.
31
32@c
33void push_link_level(PDF pdf, halfword p)
34{
35    if (pdf->link_stack_ptr >= pdf_max_link_level)
36        overflow("pdf link stack size", pdf_max_link_level);
37    assert(((type(p) == whatsit_node) && (subtype(p) == pdf_start_link_node)));
38    pdf->link_stack_ptr++;
39    pdf->link_stack[pdf->link_stack_ptr].nesting_level = cur_s;
40    pdf->link_stack[pdf->link_stack_ptr].link_node = copy_node_list(p);
41    pdf->link_stack[pdf->link_stack_ptr].ref_link_node = p;
42}
43
44@ @c
45void pop_link_level(PDF pdf)
46{
47    assert(pdf->link_stack_ptr > 0);
48    flush_node_list(pdf->link_stack[pdf->link_stack_ptr].link_node);
49    pdf->link_stack_ptr--;
50}
51
52@ @c
53void do_link(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
54{
55    scaled_whd alt_rule;
56    if (type(p) == vlist_node)
57        pdf_error("ext4", "\\pdfstartlink ended up in vlist");
58    if (global_shipping_mode == SHIPPING_FORM)
59        pdf_error("ext4", "link annotations cannot be inside an XForm");
60    assert(type(parent_box) == hlist_node);
61    if (is_obj_scheduled(pdf, pdf_link_objnum(p)))
62        pdf_link_objnum(p) = pdf_create_obj(pdf, obj_type_others, 0);
63    push_link_level(pdf, p);
64    alt_rule.wd = width(p);
65    alt_rule.ht = height(p);
66    alt_rule.dp = depth(p);
67    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
68    obj_annot_ptr(pdf, pdf_link_objnum(p)) = p; /* the reference for the pdf annot object must be set here */
69    addto_page_resources(pdf, obj_type_link, pdf_link_objnum(p));
70}
71
72@ @c
73void end_link(PDF pdf, halfword p)
74{
75    halfword q;
76    scaledpos pos = pdf->posstruct->pos;
77    if (type(p) == vlist_node)
78        pdf_error("ext4", "\\pdfendlink ended up in vlist");
79    if (pdf->link_stack_ptr < 1)
80        pdf_error("ext4",
81                  "pdf link_stack empty, \\pdfendlink used without \\pdfstartlink?");
82    if (pdf->link_stack[pdf->link_stack_ptr].nesting_level != cur_s)
83        pdf_error("ext4",
84                  "\\pdfendlink ended up in different nesting level than \\pdfstartlink");
85
86    /* N.B.: test for running link must be done on |link_node| and not |ref_link_node|,
87       as |ref_link_node| can be set by |do_link| or |append_link| already */
88
89    if (is_running(width(pdf->link_stack[pdf->link_stack_ptr].link_node))) {
90        q = pdf->link_stack[pdf->link_stack_ptr].ref_link_node;
91        if (global_shipping_mode == SHIPPING_PAGE && matrixused()) {
92            matrixrecalculate(pos.h + pdf_link_margin);
93            pdf_ann_left(q) = getllx() - pdf_link_margin;
94            pdf_ann_top(q) = pdf->page_size.v - getury() - pdf_link_margin;
95            pdf_ann_right(q) = geturx() + pdf_link_margin;
96            pdf_ann_bottom(q) = pdf->page_size.v - getlly() + pdf_link_margin;
97        } else {
98            switch (pdf->posstruct->dir) {
99            case dir_TLT:
100                pdf_ann_right(q) = pos.h + pdf_link_margin;
101                break;
102            case dir_TRT:
103                pdf_ann_left(q) = pos.h - pdf_link_margin;
104                break;
105            case dir_LTL:
106            case dir_RTT:
107                pdf_ann_bottom(q) = pos.v - pdf_link_margin;
108                break;
109            default:
110                assert(0);
111            }
112        }
113    }
114    pop_link_level(pdf);
115}
116
117@ For ``running'' annotations we must append a new node when the end of
118annotation is in other box than its start. The new created node is identical to
119corresponding whatsit node representing the start of annotation,  but its
120|info| field is |max_halfword|. We set |info| field just before destroying the
121node, in order to use |flush_node_list| to do the job.
122
123
124@ Append a new pdf annot to |pdf_link_list|.
125
126@c
127void append_link(PDF pdf, halfword parent_box, scaledpos cur, small_number i)
128{
129    halfword p;
130    int k;
131    scaled_whd alt_rule;
132    assert(type(parent_box) == hlist_node);
133    p = copy_node(pdf->link_stack[(int) i].link_node);
134    pdf->link_stack[(int) i].ref_link_node = p;
135    subtype(p) = pdf_link_data_node;    /* this node is not a normal link node */
136    alt_rule.wd = width(p);
137    alt_rule.ht = height(p);
138    alt_rule.dp = depth(p);
139    set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_link_margin);
140    k = pdf_create_obj(pdf, obj_type_others, 0);
141    obj_annot_ptr(pdf, k) = p;
142    addto_page_resources(pdf, obj_type_link, k);
143}
144
145@ @c
146void scan_startlink(PDF pdf)
147{
148    int k;
149    halfword r;
150    if (abs(cur_list.mode_field) == vmode)
151        pdf_error("ext1", "\\pdfstartlink cannot be used in vertical mode");
152    k = pdf_create_obj(pdf, obj_type_others, 0);
153    new_annot_whatsit(pdf_start_link_node);
154    set_pdf_link_attr(cur_list.tail_field, null);
155    if (scan_keyword("attr")) {
156        scan_pdf_ext_toks();
157        set_pdf_link_attr(cur_list.tail_field, def_ref);
158    }
159    r = scan_action(pdf);
160    set_pdf_link_action(cur_list.tail_field, r);
161    set_pdf_link_objnum(cur_list.tail_field, k);
162    pdf_last_link = k;
163    /* N.B.: although it is possible to set |obj_annot_ptr(k) := tail| here, it
164       is not safe if nodes are later copied/destroyed/moved; a better place
165       to do this is inside |do_link|, when the whatsit node is written out */
166}
167