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