beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / pdf / pdfdest.w
blob74e8e4b1f135f7bee770b1f49eaba413ad482d83
1 % pdfdest.w
3 % Copyright 2009-2011 Taco Hoekwater <taco@@luatex.org>
5 % This file is part of LuaTeX.
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.
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.
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/>.
20 @ @c
22 #include "ptexlib.h"
24 @ Here we implement subroutines for work with objects and related things. Some of
25 them are used in former parts too, so we need to declare them forward.
28 void init_dest_names(PDF pdf)
30 pdf->dest_names_size = inf_dest_names_size;
31 pdf->dest_names = xmallocarray(dest_name_entry, inf_dest_names_size); /* will grow dynamically */
34 @ @c
35 void append_dest_name(PDF pdf, char *s, int n)
37 int a;
38 if (pdf->dest_names_ptr == sup_dest_names_size)
39 overflow("number of destination names (dest_names_size)",(unsigned) pdf->dest_names_size);
40 if (pdf->dest_names_ptr == pdf->dest_names_size) {
41 a = pdf->dest_names_size / 5;
42 if (pdf->dest_names_size < sup_dest_names_size - a)
43 pdf->dest_names_size = pdf->dest_names_size + a;
44 else
45 pdf->dest_names_size = sup_dest_names_size;
46 pdf->dest_names = xreallocarray(pdf->dest_names, dest_name_entry, (unsigned) pdf->dest_names_size);
48 pdf->dest_names[pdf->dest_names_ptr].objname = xstrdup(s);
49 pdf->dest_names[pdf->dest_names_ptr].objnum = n;
50 pdf->dest_names_ptr++;
53 @ When a destination is created we need to check whether another destination
54 with the same identifier already exists and give a warning if needed.
57 static void warn_dest_dup(int id, small_number byname)
59 if (byname > 0) {
60 char *ss = tokenlist_to_cstring(id, true, NULL);
61 formatted_warning("pdf backend", "ignoring duplicate destination with the name '%s'",ss);
62 } else {
63 formatted_warning("pdf backend", "ignoring duplicate destination with the num '%d'",id);
65 /* no longer the annoying context */
68 @ @c
69 void do_dest(PDF pdf, halfword p, halfword parent_box, scaledpos cur)
71 scaledpos pos = pdf->posstruct->pos;
72 scaled_whd alt_rule;
73 int k;
74 if (global_shipping_mode == SHIPPING_FORM)
75 normal_error("pdf backend", "destinations cannot be inside an xform");
76 if (doing_leaders)
77 return;
78 k = pdf_get_obj(pdf, obj_type_dest, pdf_dest_id(p), pdf_dest_named_id(p));
79 if (obj_dest_ptr(pdf, k) != null) {
80 warn_dest_dup(pdf_dest_id(p), (small_number) pdf_dest_named_id(p));
81 return;
83 obj_dest_ptr(pdf, k) = p;
84 addto_page_resources(pdf, obj_type_dest, k);
85 alt_rule.wd = width(p);
86 alt_rule.ht = height(p);
87 alt_rule.dp = depth(p);
88 /* the different branches for matrixused is somewhat strange and should always be used */
89 switch (pdf_dest_type(p)) {
90 case pdf_dest_xyz:
91 if (matrixused())
92 set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
93 else {
94 pdf_ann_left(p) = pos.h;
95 pdf_ann_top(p) = pos.v;
97 break;
98 case pdf_dest_fith:
99 case pdf_dest_fitbh:
100 if (matrixused())
101 set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
102 else
103 pdf_ann_top(p) = pos.v;
104 break;
105 case pdf_dest_fitv:
106 case pdf_dest_fitbv:
107 if (matrixused())
108 set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
109 else
110 pdf_ann_left(p) = pos.h;
111 break;
112 case pdf_dest_fit:
113 case pdf_dest_fitb:
114 break;
115 case pdf_dest_fitr:
116 set_rect_dimens(pdf, p, parent_box, cur, alt_rule, pdf_dest_margin);
120 @ @c
121 void write_out_pdf_mark_destinations(PDF pdf)
123 pdf_object_list *k;
124 if ((k = get_page_resources_list(pdf, obj_type_dest)) != NULL) {
125 while (k != NULL) {
126 if (is_obj_written(pdf, k->info)) {
127 normal_error("pdf backend","destination has been already written (this shouldn't happen)");
128 } else {
129 int i;
130 i = obj_dest_ptr(pdf, k->info);
131 pdf_begin_obj(pdf, k->info, OBJSTM_ALWAYS);
132 if (pdf_dest_named_id(i) > 0) {
133 pdf_begin_dict(pdf);
134 pdf_add_name(pdf, "D");
136 pdf_begin_array(pdf);
137 pdf_add_ref(pdf, pdf->last_page);
138 switch (pdf_dest_type(i)) {
139 case pdf_dest_xyz:
140 pdf_add_name(pdf, "XYZ");
141 pdf_add_bp(pdf, pdf_ann_left(i));
142 pdf_add_bp(pdf, pdf_ann_top(i));
143 if (pdf_dest_xyz_zoom(i) == null) {
144 pdf_add_null(pdf);
145 } else {
146 if (pdf->cave == 1)
147 pdf_out(pdf, ' ');
148 pdf_print_int(pdf, pdf_dest_xyz_zoom(i) / 1000);
149 pdf_out(pdf, '.');
150 pdf_print_int(pdf, (pdf_dest_xyz_zoom(i) % 1000));
151 pdf->cave = 1;
153 break;
154 case pdf_dest_fit:
155 pdf_add_name(pdf, "Fit");
156 break;
157 case pdf_dest_fith:
158 pdf_add_name(pdf, "FitH");
159 pdf_add_bp(pdf, pdf_ann_top(i));
160 break;
161 case pdf_dest_fitv:
162 pdf_add_name(pdf, "FitV");
163 pdf_add_bp(pdf, pdf_ann_left(i));
164 break;
165 case pdf_dest_fitb:
166 pdf_add_name(pdf, "FitB");
167 break;
168 case pdf_dest_fitbh:
169 pdf_add_name(pdf, "FitBH");
170 pdf_add_bp(pdf, pdf_ann_top(i));
171 break;
172 case pdf_dest_fitbv:
173 pdf_add_name(pdf, "FitBV");
174 pdf_add_bp(pdf, pdf_ann_left(i));
175 break;
176 case pdf_dest_fitr:
177 pdf_add_name(pdf, "FitR");
178 pdf_add_rect_spec(pdf, i);
179 break;
180 default:
181 normal_error("pdf backend", "unknown dest type");
182 break;
184 pdf_end_array(pdf);
185 if (pdf_dest_named_id(i) > 0)
186 pdf_end_dict(pdf);
187 pdf_end_obj(pdf);
189 k = k->link;
194 @ @c
195 void scan_pdfdest(PDF pdf)
197 halfword q;
198 int k;
199 str_number i;
200 scaled_whd alt_rule;
201 q = cur_list.tail_field;
202 new_whatsit(pdf_dest_node);
203 if (scan_keyword("num")) {
204 scan_int();
205 if (cur_val <= 0)
206 normal_error("pdf backend", "num identifier must be positive");
207 if (cur_val > max_halfword)
208 normal_error("pdf backend", "number too big");
209 set_pdf_dest_id(cur_list.tail_field, cur_val);
210 set_pdf_dest_named_id(cur_list.tail_field, 0);
211 } else if (scan_keyword("name")) {
212 scan_toks(false, true);
213 set_pdf_dest_id(cur_list.tail_field, def_ref);
214 set_pdf_dest_named_id(cur_list.tail_field, 1);
215 } else {
216 normal_error("pdf backend", "identifier type missing");
218 if (scan_keyword("xyz")) {
219 set_pdf_dest_type(cur_list.tail_field, pdf_dest_xyz);
220 if (scan_keyword("zoom")) {
221 scan_int();
222 if (cur_val > max_halfword)
223 normal_error("pdf backend", "number too big");
224 set_pdf_dest_xyz_zoom(cur_list.tail_field, cur_val);
225 } else {
226 set_pdf_dest_xyz_zoom(cur_list.tail_field, null);
228 } else if (scan_keyword("fitbh")) {
229 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbh);
230 } else if (scan_keyword("fitbv")) {
231 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitbv);
232 } else if (scan_keyword("fitb")) {
233 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitb);
234 } else if (scan_keyword("fith")) {
235 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fith);
236 } else if (scan_keyword("fitv")) {
237 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitv);
238 } else if (scan_keyword("fitr")) {
239 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fitr);
240 } else if (scan_keyword("fit")) {
241 set_pdf_dest_type(cur_list.tail_field, pdf_dest_fit);
242 } else {
243 normal_error("pdf backend", "destination type missing");
245 /* Scan an optional space */
246 get_x_token();
247 if (cur_cmd != spacer_cmd)
248 back_input();
250 if (pdf_dest_type(cur_list.tail_field) == pdf_dest_fitr) {
251 alt_rule = scan_alt_rule(); /* scans |<rule spec>| to |alt_rule| */
252 set_width(cur_list.tail_field, alt_rule.wd);
253 set_height(cur_list.tail_field, alt_rule.ht);
254 set_depth(cur_list.tail_field, alt_rule.dp);
256 if (pdf_dest_named_id(cur_list.tail_field) != 0) {
257 i = tokens_to_string(pdf_dest_id(cur_list.tail_field));
258 k = find_obj(pdf, obj_type_dest, i, true);
259 flush_str(i);
260 } else {
261 k = find_obj(pdf, obj_type_dest, pdf_dest_id(cur_list.tail_field), false);
263 if ((k != 0) && (obj_dest_ptr(pdf, k) != null)) {
264 warn_dest_dup(pdf_dest_id(cur_list.tail_field),(small_number) pdf_dest_named_id(cur_list.tail_field));
265 flush_node_list(cur_list.tail_field);
266 cur_list.tail_field = q;
267 vlink(q) = null;
271 @ sorts |dest_names| by names
273 static int dest_cmp(const void *a, const void *b)
275 dest_name_entry aa = *(const dest_name_entry *) a;
276 dest_name_entry bb = *(const dest_name_entry *) b;
277 return strcmp(aa.objname, bb.objname);
280 @ @c
281 void sort_dest_names(PDF pdf)
283 qsort(pdf->dest_names, (size_t) pdf->dest_names_ptr, sizeof(dest_name_entry), dest_cmp);
286 @ Output the name tree. The tree nature of the destination list forces the
287 storing of intermediate data in |obj_info| and |obj_aux| fields, which
288 is further uglified by the fact that |obj_tab| entries do not accept char
289 pointers.
292 int output_name_tree(PDF pdf)
294 boolean is_names = true; /* flag for name tree output: is it Names or Kids? */
295 int k = 0; /* index of current child of |l|; if |k < pdf_dest_names_ptr|
296 then this is pointer to |dest_names| array;
297 otherwise it is the pointer to |obj_tab| (object number) */
298 int b = 0;
299 int m, j, l;
300 int dests = 0;
301 int names_head = 0;
302 int names_tail = 0;
303 if (pdf->dest_names_ptr == 0) {
304 goto DONE;
306 sort_dest_names(pdf);
307 while (true) {
308 do {
309 l = pdf_create_obj(pdf, obj_type_others, 0); /* create a new node */
310 if (b == 0)
311 b = l; /* first in this level */
312 if (names_head == 0) {
313 names_head = l;
314 names_tail = l;
315 } else {
316 set_obj_link(pdf, names_tail, l);
317 names_tail = l;
319 set_obj_link(pdf, names_tail, 0);
320 /* Output the current node in this level */
321 pdf_begin_obj(pdf, l, OBJSTM_ALWAYS);
322 pdf_begin_dict(pdf);
323 j = 0;
324 if (is_names) {
325 set_obj_start(pdf, l, pdf->dest_names[k].objname);
326 pdf_add_name(pdf, "Names");
327 pdf_begin_array(pdf);
328 do {
329 pdf_add_string(pdf, pdf->dest_names[k].objname);
330 pdf_add_ref(pdf, pdf->dest_names[k].objnum);
331 j++;
332 k++;
333 } while (j != name_tree_kids_max && k != pdf->dest_names_ptr);
334 pdf_end_array(pdf);
335 set_obj_stop(pdf, l, pdf->dest_names[k - 1].objname); /* for later */
336 if (k == pdf->dest_names_ptr) {
337 is_names = false;
338 k = names_head;
339 b = 0;
342 } else {
343 set_obj_start(pdf, l, obj_start(pdf, k));
344 pdf_add_name(pdf, "Kids");
345 pdf_begin_array(pdf);
346 do {
347 pdf_add_ref(pdf, k);
348 set_obj_stop(pdf, l, obj_stop(pdf, k));
349 k = obj_link(pdf, k);
350 j++;
351 } while (j != name_tree_kids_max && k != b
352 && obj_link(pdf, k) != 0);
353 pdf_end_array(pdf);
354 if (k == b)
355 b = 0;
357 pdf_add_name(pdf, "Limits");
358 pdf_begin_array(pdf);
359 pdf_add_string(pdf, obj_start(pdf, l));
360 pdf_add_string(pdf, obj_stop(pdf, l));
361 pdf_end_array(pdf);
362 pdf_end_dict(pdf);
363 pdf_end_obj(pdf);
364 } while (b != 0);
366 if (k == l) {
367 dests = l;
368 goto DONE;
371 DONE:
372 if ((dests != 0) || (pdf_names_toks != null)) {
373 m = pdf_create_obj(pdf, obj_type_others, 0);
374 pdf_begin_obj(pdf, m, OBJSTM_ALWAYS);
375 pdf_begin_dict(pdf);
376 if (dests != 0)
377 pdf_dict_add_ref(pdf, "Dests", dests);
378 if (pdf_names_toks != null) {
379 pdf_print_toks(pdf, pdf_names_toks);
380 delete_token_ref(pdf_names_toks);
381 pdf_names_toks = null;
383 print_pdf_table_string(pdf, "names");
384 pdf_end_dict(pdf);
385 pdf_end_obj(pdf);
386 return m;
387 } else {
388 return 0;