/* Copyright 2006-2011 Taco Hoekwater This file is part of LuaTeX. LuaTeX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with LuaTeX; if not, see . */ #include "ptexlib.h" #define PAGES_TREE_KIDSMAX 10 static struct avl_table *divert_list_tree = NULL; typedef struct pages_entry_ { /*tex The object number of this |/Pages| object. */ int objnum; /*tex The total number of all pages below. */ int number_of_pages; /*tex The number of direct kid objects. */ int number_of_kids; /*tex The array of kid object numbers. */ int kids[PAGES_TREE_KIDSMAX]; struct pages_entry_ *next; } pages_entry; typedef struct divert_list_entry_ { int divnum; pages_entry *first; pages_entry *last; } divert_list_entry; static int comp_divert_list_entry(const void *pa, const void *pb, void *p) { (void) p; if (((const divert_list_entry *) pa)->divnum > ((const divert_list_entry *) pb)->divnum) return 1; if (((const divert_list_entry *) pa)->divnum < ((const divert_list_entry *) pb)->divnum) return -1; return 0; } static pages_entry *new_pages_entry(PDF pdf) { int i; pages_entry *p = xtalloc(1, pages_entry); p->number_of_pages = p->number_of_kids = 0; for (i = 0; i < PAGES_TREE_KIDSMAX; i++) p->kids[i] = 0; p->next = NULL; p->objnum = pdf_create_obj(pdf, obj_type_pages, 0); return p; } static divert_list_entry *new_divert_list_entry(void) { divert_list_entry *d; d = xtalloc(1, divert_list_entry); d->first = d->last = NULL; return d; } static void ensure_list_tree(void) { if (divert_list_tree == NULL) { divert_list_tree = avl_create(comp_divert_list_entry, NULL, &avl_xallocator); } } static divert_list_entry *get_divert_list(int divnum) { divert_list_entry *d, tmp; void **aa; tmp.divnum = divnum; d = (divert_list_entry *) avl_find(divert_list_tree, &tmp); if (d == NULL) { d = new_divert_list_entry(); d->divnum = divnum; /*tex The next bit of code can actually be removed. */ aa = avl_probe(divert_list_tree, d); if (aa==NULL) { normal_error("pdf backend","page list lookup error"); } } return d; } /*tex |pdf_do_page_divert| returns the current |/Parent| object number. */ int pdf_do_page_divert(PDF pdf, int objnum, int divnum) { divert_list_entry *d; pages_entry *p; /*tex Initialize the tree. */ ensure_list_tree(); /*tex Make sure we have a list for this diversion. */ d = get_divert_list(divnum); if (d->first == NULL || d->last->number_of_kids == PAGES_TREE_KIDSMAX) { /*tex Append a new |pages_entry|. */ p = new_pages_entry(pdf); if (d->first == NULL) d->first = p; else d->last->next = p; d->last = p; } p = d->last; p->kids[p->number_of_kids++] = objnum; p->number_of_pages++; return p->objnum; } static void movelist(divert_list_entry * d, divert_list_entry * dto) { if (d != NULL && d->first != NULL && d->divnum != dto->divnum) { /*tex No undivert of empty list or into self. */ if (dto->first == NULL) dto->first = d->first; else dto->last->next = d->first; dto->last = d->last; /*tex One could as well remove this |divert_list_entry|. */ d->first = d->last = NULL; } } /*tex Undivert from diversion |divnum| into diversion |curdivnum|. */ void pdf_do_page_undivert(int divnum, int curdivnum) { divert_list_entry *d, *dto, tmp; struct avl_traverser t; /*tex Initialize the tree. */ ensure_list_tree(); /*tex Find the diversion |curdivnum| list where diversion |divnum| should go. */ dto = get_divert_list(curdivnum); if (divnum == 0) { /*tex Zero is a special case: undivert {\em all} lists. */ avl_t_init(&t, divert_list_tree); for (d = avl_t_first(&t, divert_list_tree); d != NULL; d = avl_t_next(&t)) movelist(d, dto); } else { tmp.divnum = divnum; d = (divert_list_entry *) avl_find(divert_list_tree, &tmp); movelist(d, dto); } } /*tex Write a |/Pages| object. */ static void write_pages(PDF pdf, pages_entry * p, int parent) { int i; int pages_attributes ; pdf_begin_obj(pdf, p->objnum, OBJSTM_ALWAYS); pdf_begin_dict(pdf); pdf_dict_add_name(pdf, "Type", "Pages"); if (parent == 0) { /*tex It's root. Lookup the attributes once. */ pages_attributes = pdf_pages_attr; if (pages_attributes != null) { pdf_print_toks(pdf, pages_attributes); pdf_out(pdf, ' '); } print_pdf_table_string(pdf, "pagesattributes"); pdf_out(pdf, ' '); } else { pdf_dict_add_ref(pdf, "Parent", parent); } pdf_dict_add_int(pdf, "Count", (int) p->number_of_pages); pdf_add_name(pdf, "Kids"); pdf_begin_array(pdf); for (i = 0; i < p->number_of_kids; i++) { pdf_add_ref(pdf, (int) p->kids[i]); } pdf_end_array(pdf); pdf_end_dict(pdf); pdf_end_obj(pdf); } /*tex Loop over all |/Pages| objects, output them, create their parents, recursing bottom up, return the |/Pages| root object number. */ static int output_pages_list(PDF pdf, pages_entry * pe) { pages_entry *p, *q, *r; if (pe->next == NULL) { /*tex Everything fits into one |pages_entry|. */ write_pages(pdf, pe, 0); return pe->objnum; } /*tex One level higher needed. */ q = r = new_pages_entry(pdf); for (p = pe; p != NULL; p = p->next) { if (q->number_of_kids == PAGES_TREE_KIDSMAX) { q->next = new_pages_entry(pdf); q = q->next; } q->kids[q->number_of_kids++] = p->objnum; q->number_of_pages += p->number_of_pages; write_pages(pdf, p, q->objnum); } /*tex Recurse through next higher level. */ return output_pages_list(pdf, r); } int output_pages_tree(PDF pdf) { divert_list_entry *d; /*tex Concatenate all diversions into diversion 0. */ pdf_do_page_undivert(0, 0); /*tex Get diversion 0. */ d = get_divert_list(0); return output_pages_list(pdf, d->first); }