beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / pdf / pdfpagetree.w
blob76d008c43596dfe12fe967a64169d5f6f25100b4
1 % pdfpagetree.w
3 % Copyright 2006-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 @* Page diversions.
26 @ @c
27 # define PAGES_TREE_KIDSMAX 10
29 static struct avl_table *divert_list_tree = NULL;
31 typedef struct pages_entry_ {
32 int objnum; /* object number of this /Pages object */
33 int number_of_pages; /* total number of all pages below */
34 int number_of_kids; /* number of direct kid objects */
35 int kids[PAGES_TREE_KIDSMAX]; /* array of kid object numbers */
36 struct pages_entry_ *next;
37 } pages_entry;
39 typedef struct divert_list_entry_ {
40 int divnum;
41 pages_entry *first;
42 pages_entry *last;
43 } divert_list_entry;
45 static int comp_divert_list_entry(const void *pa, const void *pb, void *p)
47 (void) p;
48 if (((const divert_list_entry *) pa)->divnum > ((const divert_list_entry *) pb)->divnum)
49 return 1;
50 if (((const divert_list_entry *) pa)->divnum < ((const divert_list_entry *) pb)->divnum)
51 return -1;
52 return 0;
55 @ @c
56 static pages_entry *new_pages_entry(PDF pdf)
58 int i;
59 pages_entry *p = xtalloc(1, pages_entry);
60 p->number_of_pages = p->number_of_kids = 0;
61 for (i = 0; i < PAGES_TREE_KIDSMAX; i++)
62 p->kids[i] = 0;
63 p->next = NULL;
64 p->objnum = pdf_create_obj(pdf, obj_type_pages, 0);
65 return p;
68 @ @c
69 static divert_list_entry *new_divert_list_entry(void)
71 divert_list_entry *d;
72 d = xtalloc(1, divert_list_entry);
73 d->first = d->last = NULL;
74 return d;
77 @ @c
78 static void ensure_list_tree(void)
80 if (divert_list_tree == NULL) {
81 divert_list_tree = avl_create(comp_divert_list_entry, NULL, &avl_xallocator);
85 @ @c
86 static divert_list_entry *get_divert_list(int divnum)
88 divert_list_entry *d, tmp;
89 void **aa;
90 tmp.divnum = divnum;
91 d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
92 if (d == NULL) {
93 d = new_divert_list_entry();
94 d->divnum = divnum;
95 /* the next bit of code can actually be removed */
96 aa = avl_probe(divert_list_tree, d);
97 if (aa==NULL) {
98 normal_error("pdf backend","page list lookup error");
101 return d;
104 @ |pdf_do_page_divert()| returns the current /Parent object number
106 int pdf_do_page_divert(PDF pdf, int objnum, int divnum)
108 divert_list_entry *d;
109 pages_entry *p;
110 /* initialize the tree */
111 ensure_list_tree();
112 /* make sure we have a list for this diversion */
113 d = get_divert_list(divnum);
114 if (d->first == NULL || d->last->number_of_kids == PAGES_TREE_KIDSMAX) {
115 /* append a new |pages_entry| */
116 p = new_pages_entry(pdf);
117 if (d->first == NULL)
118 d->first = p;
119 else
120 d->last->next = p;
121 d->last = p;
123 p = d->last;
124 p->kids[p->number_of_kids++] = objnum;
125 p->number_of_pages++;
126 return p->objnum;
129 @ @c
130 static void movelist(divert_list_entry * d, divert_list_entry * dto)
132 if (d != NULL && d->first != NULL && d->divnum != dto->divnum) {
133 /* no undivert of empty list or into self */
134 if (dto->first == NULL)
135 dto->first = d->first;
136 else
137 dto->last->next = d->first;
138 dto->last = d->last;
139 /* one could as well remove this |divert_list_entry| */
140 d->first = d->last = NULL;
144 @ undivert from diversion |divnum| into diversion |curdivnum|
146 void pdf_do_page_undivert(int divnum, int curdivnum)
148 divert_list_entry *d, *dto, tmp;
149 struct avl_traverser t;
150 /* initialize the tree */
151 ensure_list_tree();
152 /* find the diversion |curdivnum| list where diversion |divnum| should go */
153 dto = get_divert_list(curdivnum);
154 if (divnum == 0) {
155 /* 0 = special case: undivert {\it all\/} lists */
156 avl_t_init(&t, divert_list_tree);
157 for (d = avl_t_first(&t, divert_list_tree); d != NULL;
158 d = avl_t_next(&t))
159 movelist(d, dto);
160 } else {
161 tmp.divnum = divnum;
162 d = (divert_list_entry *) avl_find(divert_list_tree, &tmp);
163 movelist(d, dto);
167 @ write a /Pages object
170 static void write_pages(PDF pdf, pages_entry * p, int parent)
172 int i;
173 int pages_attributes ;
174 pdf_begin_obj(pdf, p->objnum, OBJSTM_ALWAYS);
175 pdf_begin_dict(pdf);
176 pdf_dict_add_name(pdf, "Type", "Pages");
177 if (parent == 0) {
178 /* it's root */
179 pages_attributes = pdf_pages_attr; /* lookup once */
180 if (pages_attributes != null) {
181 pdf_print_toks(pdf, pages_attributes);
182 pdf_out(pdf, ' ');
184 print_pdf_table_string(pdf, "pagesattributes");
185 pdf_out(pdf, ' ');
186 } else
187 pdf_dict_add_ref(pdf, "Parent", parent);
188 pdf_dict_add_int(pdf, "Count", (int) p->number_of_pages);
189 pdf_add_name(pdf, "Kids");
190 pdf_begin_array(pdf);
191 for (i = 0; i < p->number_of_kids; i++)
192 pdf_add_ref(pdf, (int) p->kids[i]);
193 pdf_end_array(pdf);
194 pdf_end_dict(pdf);
195 pdf_end_obj(pdf);
198 @ loop over all /Pages objects, output them, create their parents,
199 recursing bottom up, return the /Pages root object number
202 static int output_pages_list(PDF pdf, pages_entry * pe)
204 pages_entry *p, *q, *r;
205 if (pe->next == NULL) {
206 /* everything fits into one |pages_entry| */
207 write_pages(pdf, pe, 0); /* /Pages root found */
208 return pe->objnum;
210 q = r = new_pages_entry(pdf); /* one level higher needed */
211 for (p = pe; p != NULL; p = p->next) {
212 if (q->number_of_kids == PAGES_TREE_KIDSMAX) {
213 q->next = new_pages_entry(pdf);
214 q = q->next;
216 q->kids[q->number_of_kids++] = p->objnum;
217 q->number_of_pages += p->number_of_pages;
218 write_pages(pdf, p, q->objnum);
220 return output_pages_list(pdf, r); /* recurse through next higher level */
223 @ @c
224 int output_pages_tree(PDF pdf)
226 divert_list_entry *d;
227 pdf_do_page_undivert(0, 0); /* concatenate all diversions into diversion 0 */
228 d = get_divert_list(0); /* get diversion 0 */
229 return output_pages_list(pdf, d->first);