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
/>.
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
*/
35 void append_dest_name
(PDF pdf
, char
*s
, int n
)
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
;
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
)
60 char
*ss
= tokenlist_to_cstring
(id
, true
, NULL);
61 formatted_warning
("pdf backend", "ignoring duplicate destination with the name '%s'",ss
);
63 formatted_warning
("pdf backend", "ignoring duplicate destination with the num '%d'",id
);
65 /* no longer the annoying context
*/
69 void do_dest
(PDF pdf
, halfword p
, halfword parent_box
, scaledpos cur
)
71 scaledpos pos
= pdf-
>posstruct-
>pos
;
74 if
(global_shipping_mode
== SHIPPING_FORM
)
75 normal_error
("pdf backend", "destinations cannot be inside an xform");
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
));
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
)) {
92 set_rect_dimens
(pdf
, p
, parent_box
, cur
, alt_rule
, pdf_dest_margin
);
94 pdf_ann_left
(p
) = pos.h
;
95 pdf_ann_top
(p
) = pos.v
;
101 set_rect_dimens
(pdf
, p
, parent_box
, cur
, alt_rule
, pdf_dest_margin
);
103 pdf_ann_top
(p
) = pos.v
;
108 set_rect_dimens
(pdf
, p
, parent_box
, cur
, alt_rule
, pdf_dest_margin
);
110 pdf_ann_left
(p
) = pos.h
;
116 set_rect_dimens
(pdf
, p
, parent_box
, cur
, alt_rule
, pdf_dest_margin
);
121 void write_out_pdf_mark_destinations
(PDF pdf
)
124 if
((k
= get_page_resources_list
(pdf
, obj_type_dest
)) != NULL) {
126 if
(is_obj_written
(pdf
, k-
>info
)) {
127 normal_error
("pdf backend","destination has been already written (this shouldn't happen)");
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) {
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
)) {
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
) {
148 pdf_print_int
(pdf
, pdf_dest_xyz_zoom
(i
) / 1000);
150 pdf_print_int
(pdf
, (pdf_dest_xyz_zoom
(i
) % 1000));
155 pdf_add_name
(pdf
, "Fit");
158 pdf_add_name
(pdf
, "FitH");
159 pdf_add_bp
(pdf
, pdf_ann_top
(i
));
162 pdf_add_name
(pdf
, "FitV");
163 pdf_add_bp
(pdf
, pdf_ann_left
(i
));
166 pdf_add_name
(pdf
, "FitB");
169 pdf_add_name
(pdf
, "FitBH");
170 pdf_add_bp
(pdf
, pdf_ann_top
(i
));
173 pdf_add_name
(pdf
, "FitBV");
174 pdf_add_bp
(pdf
, pdf_ann_left
(i
));
177 pdf_add_name
(pdf
, "FitR");
178 pdf_add_rect_spec
(pdf
, i
);
181 normal_error
("pdf backend", "unknown dest type");
185 if
(pdf_dest_named_id
(i
) > 0)
195 void scan_pdfdest
(PDF pdf
)
201 q
= cur_list.tail_field
;
202 new_whatsit
(pdf_dest_node
);
203 if
(scan_keyword
("num")) {
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);
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")) {
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
);
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
);
243 normal_error
("pdf backend", "destination type missing");
245 /* Scan an optional space
*/
247 if
(cur_cmd
!= spacer_cmd
)
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
);
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
;
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
);
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
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
) */
303 if
(pdf-
>dest_names_ptr
== 0) {
306 sort_dest_names
(pdf
);
309 l
= pdf_create_obj
(pdf
, obj_type_others
, 0); /* create a new node
*/
311 b
= l
; /* first in this level
*/
312 if
(names_head
== 0) {
316 set_obj_link
(pdf
, 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
);
325 set_obj_start
(pdf
, l
, pdf-
>dest_names
[k
].objname
);
326 pdf_add_name
(pdf
, "Names");
327 pdf_begin_array
(pdf
);
329 pdf_add_string
(pdf
, pdf-
>dest_names
[k
].objname
);
330 pdf_add_ref
(pdf
, pdf-
>dest_names
[k
].objnum
);
333 } while
(j
!= name_tree_kids_max
&& k != pdf->dest_names_ptr);
335 set_obj_stop
(pdf
, l
, pdf-
>dest_names
[k
- 1].objname
); /* for later
*/
336 if
(k
== pdf-
>dest_names_ptr
) {
343 set_obj_start
(pdf
, l
, obj_start
(pdf
, k
));
344 pdf_add_name
(pdf
, "Kids");
345 pdf_begin_array
(pdf
);
348 set_obj_stop
(pdf
, l
, obj_stop
(pdf
, k
));
349 k
= obj_link
(pdf
, k
);
351 } while
(j
!= name_tree_kids_max
&& k != b
352 && obj_link(pdf, k) != 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
));
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
);
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");