1 /*
2 * tumble: build a PDF file from image files
3 *
4 * Semantic routines for spec file parser
5 * Copyright 2001, 2002, 2003, 2017 Eric Smith <spacewar@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation. Note that permission is
10 * not granted to redistribute this program under the terms of any
11 * other version of the General Public License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21 *
22 * 2009-03-13 [JDB] Add support for blank pages, overlay images, color
23 * mapping, color-key masking, and push/pop of input
24 * contexts.
25 */
26
27
28 #include <stdbool.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33
34 #include "semantics.h"
35
36 #ifdef CTL_LANG
37 #include "parser.tab.h"
38 #endif
39
40 #include "tumble.h"
41
42
43 typedef struct
44 {
45 bool has_page_size;
46 page_size_t page_size;
47
48 bool has_rotation;
49 int rotation;
50
51 bool has_crop;
52 crop_t crop;
53
54 bool has_transparency;
55 rgb_range_t transparency;
56 } input_modifiers_t;
57
58
59 typedef struct input_context_t
60 {
61 struct input_context_t *parent;
62 struct input_context_t *next;
63
64 int image_count; /* how many pages reference this context,
65 including those from subcontexts */
66
67 char *input_file;
68 bool is_blank;
69
70 input_modifiers_t modifiers [INPUT_MODIFIER_TYPE_COUNT];
71 } input_context_t;
72
73
74 typedef struct input_image_t
75 {
76 struct input_image_t *next;
77 input_context_t *input_context;
78 range_t range;
79 } input_image_t;
80
81
82 typedef struct output_context_t
83 {
84 struct output_context_t *parent;
85 struct output_context_t *next;
86
87 int page_count; /* how many pages reference this context,
88 including those from subcontexts */
89
90 char *output_file;
91 pdf_file_attributes_t file_attributes;
92
93 bookmark_t *first_bookmark;
94 bookmark_t *last_bookmark;
95
96 bool has_page_label;
97 page_label_t page_label;
98
99 bool has_colormap;
100 colormap_t colormap;
101 } output_context_t;
102
103
104 typedef struct output_page_t
105 {
106 struct output_page_t *next;
107 output_context_t *output_context;
108 range_t range;
109 bookmark_t *bookmark_list;
110 bool has_overlay;
111 overlay_t overlay;
112 } output_page_t;
113
114
115 #ifdef SEMANTIC_DEBUG
116 #define SDBG(x) printf x
117 #else
118 #define SDBG(x)
119 #endif
120
121
122 extern FILE *yyin;
123 int line; /* line number in spec file */
124
125 int bookmark_level;
126
127 input_context_t *first_input_context;
128 input_context_t *last_input_context;
129
130 input_modifier_type_t current_modifier_context = INPUT_MODIFIER_ALL;
131
132 input_image_t *first_input_image;
133 input_image_t *last_input_image;
134
135 output_context_t *first_output_context;
136 output_context_t *last_output_context;
137
138 output_page_t *first_output_page;
139 output_page_t *last_output_page;
140
141
input_push_context(void)142 void input_push_context (void)
143 {
144 input_context_t *new_input_context;
145
146 new_input_context = malloc (sizeof (input_context_t));
147 if (! new_input_context)
148 {
149 fprintf (stderr, "failed to malloc an input context\n");
150 return;
151 }
152
153 if (last_input_context)
154 {
155 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
156 new_input_context->image_count = 0;
157 }
158 else
159 {
160 memset (new_input_context, 0, sizeof (input_context_t));
161 first_input_context = new_input_context;
162 }
163
164 new_input_context->parent = last_input_context;
165 last_input_context = new_input_context;
166 };
167
input_pop_context(void)168 void input_pop_context (void)
169 {
170 if (! last_input_context)
171 {
172 fprintf (stderr, "failed to pop an input context\n");
173 return;
174 }
175
176 last_input_context = last_input_context->parent;
177 };
178
input_set_modifier_context(input_modifier_type_t type)179 void input_set_modifier_context (input_modifier_type_t type)
180 {
181 current_modifier_context = type;
182 #ifdef SEMANTIC_DEBUG
183 SDBG(("modifier type "));
184 switch (type)
185 {
186 case INPUT_MODIFIER_ALL: SDBG(("all")); break;
187 case INPUT_MODIFIER_ODD: SDBG(("odd")); break;
188 case INPUT_MODIFIER_EVEN: SDBG(("even")); break;
189 default: SDBG(("unknown %d", type));
190 }
191 SDBG(("\n"));
192 #endif /* SEMANTIC_DEBUG */
193 }
194
input_clone(void)195 static void input_clone (void)
196 {
197 input_context_t *new_input_context;
198
199 if (! last_input_context->image_count)
200 return;
201
202 new_input_context = malloc (sizeof (input_context_t));
203 if (! new_input_context)
204 {
205 fprintf (stderr, "failed to malloc an input context\n");
206 return;
207 }
208
209 memcpy (new_input_context, last_input_context, sizeof (input_context_t));
210 new_input_context->image_count = 0;
211 last_input_context->next = new_input_context;
212 last_input_context = new_input_context;
213 }
214
input_set_file(char * name)215 void input_set_file (char *name)
216 {
217 input_clone ();
218 last_input_context->input_file = name;
219 last_input_context->is_blank = (name == NULL);
220 };
221
input_set_rotation(int rotation)222 void input_set_rotation (int rotation)
223 {
224 last_input_context->modifiers [current_modifier_context].has_rotation = 1;
225 last_input_context->modifiers [current_modifier_context].rotation = rotation;
226 SDBG(("rotation %d\n", rotation));
227 }
228
input_set_page_size(page_size_t size)229 void input_set_page_size (page_size_t size)
230 {
231 last_input_context->modifiers [current_modifier_context].has_page_size = 1;
232 last_input_context->modifiers [current_modifier_context].page_size = size;
233 SDBG(("page size %f, %f\n", size.width, size.height));
234 }
235
input_set_transparency(rgb_range_t rgb_range)236 void input_set_transparency (rgb_range_t rgb_range)
237 {
238 last_input_context->modifiers [current_modifier_context].has_transparency = 1;
239 last_input_context->modifiers [current_modifier_context].transparency = rgb_range;
240 }
241
increment_input_image_count(int count)242 static void increment_input_image_count (int count)
243 {
244 input_context_t *context;
245
246 for (context = last_input_context; context; context = context->parent)
247 context->image_count += count;
248 }
249
input_images(range_t range)250 void input_images (range_t range)
251 {
252 input_image_t *new_image;
253 int count = ((range.last - range.first) + 1);
254
255 #ifdef SEMANTIC_DEBUG
256 if (range.first == range.last)
257 SDBG(("image %d\n", range.first));
258 else
259 SDBG(("images %d..%d\n", range.first, range.last));
260 #endif /* SEMANTIC_DEBUG */
261
262 new_image = calloc (1, sizeof (input_image_t));
263 if (! new_image)
264 {
265 fprintf (stderr, "failed to malloc an input image struct\n");
266 return;
267 }
268 if (first_input_image)
269 {
270 last_input_image->next = new_image;
271 last_input_image = new_image;
272 }
273 else
274 {
275 first_input_image = last_input_image = new_image;
276 }
277 new_image->range = range;
278 new_image->input_context = last_input_context;
279 increment_input_image_count (count);
280 }
281
282
output_push_context(void)283 void output_push_context (void)
284 {
285 output_context_t *new_output_context;
286
287 new_output_context = malloc (sizeof (output_context_t));
288 if (! new_output_context)
289 {
290 fprintf (stderr, "failed to malloc an output context\n");
291 return;
292 }
293
294 if (last_output_context)
295 {
296 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
297 new_output_context->page_count = 0;
298 new_output_context->first_bookmark = NULL;
299 new_output_context->last_bookmark = NULL;
300 }
301 else
302 {
303 memset (new_output_context, 0, sizeof (output_context_t));
304 first_output_context = new_output_context;
305 }
306
307 new_output_context->parent = last_output_context;
308 last_output_context = new_output_context;
309 };
310
output_pop_context(void)311 void output_pop_context (void)
312 {
313 if (! last_output_context)
314 {
315 fprintf (stderr, "failed to pop an output context\n");
316 return;
317 }
318
319 last_output_context = last_output_context->parent;
320 };
321
output_clone(void)322 static void output_clone (void)
323 {
324 output_context_t *new_output_context;
325
326 if (! last_output_context->page_count)
327 return;
328
329 new_output_context = malloc (sizeof (output_context_t));
330 if (! new_output_context)
331 {
332 fprintf (stderr, "failed to malloc an output context\n");
333 return;
334 }
335
336 memcpy (new_output_context, last_output_context, sizeof (output_context_t));
337 new_output_context->page_count = 0;
338 last_output_context->next = new_output_context;
339 }
340
output_set_file(char * name)341 void output_set_file (char *name)
342 {
343 output_clone ();
344 last_output_context->output_file = name;
345 last_output_context->file_attributes.author = NULL;
346 last_output_context->file_attributes.creator = NULL;
347 last_output_context->file_attributes.title = NULL;
348 last_output_context->file_attributes.subject = NULL;
349 last_output_context->file_attributes.keywords = NULL;
350 };
351
output_set_author(char * author)352 void output_set_author (char *author)
353 {
354 last_output_context->file_attributes.author = author;
355 }
356
output_set_creator(char * creator)357 void output_set_creator (char *creator)
358 {
359 last_output_context->file_attributes.creator = creator;
360 }
361
output_set_title(char * title)362 void output_set_title (char *title)
363 {
364 last_output_context->file_attributes.title = title;
365 }
366
output_set_subject(char * subject)367 void output_set_subject (char *subject)
368 {
369 last_output_context->file_attributes.subject = subject;
370 }
371
output_set_keywords(char * keywords)372 void output_set_keywords (char *keywords)
373 {
374 last_output_context->file_attributes.keywords = keywords;
375 }
376
output_set_bookmark(char * name)377 void output_set_bookmark (char *name)
378 {
379 bookmark_t *new_bookmark;
380
381 /* As the language is defined (parser.y), a bookmark can only appear
382 at the beginning of a context! */
383 if (last_output_context->page_count)
384 {
385 fprintf (stderr, "internal error, bookmark not at beginning of context\n");
386 exit (2);
387 }
388
389 new_bookmark = calloc (1, sizeof (bookmark_t));
390 if (! new_bookmark)
391 {
392 fprintf (stderr, "failed to calloc a bookmark\n");
393 return;
394 }
395
396 new_bookmark->level = bookmark_level;
397 new_bookmark->name = name;
398 if (last_output_context->first_bookmark)
399 last_output_context->last_bookmark->next = new_bookmark;
400 else
401 last_output_context->first_bookmark = new_bookmark;
402 last_output_context->last_bookmark = new_bookmark;
403 }
404
output_set_page_label(page_label_t label)405 void output_set_page_label (page_label_t label)
406 {
407 output_clone ();
408 last_output_context->has_page_label = 1;
409 last_output_context->page_label = label;
410 }
411
increment_output_page_count(int count)412 static void increment_output_page_count (int count)
413 {
414 output_context_t *context;
415
416 for (context = last_output_context; context; context = context->parent)
417 context->page_count += count;
418 }
419
420
output_pages(range_t range)421 void output_pages (range_t range)
422 {
423 output_page_t *new_page;
424 output_context_t *context;
425 int count = ((range.last - range.first) + 1);
426
427 #ifdef SEMANTIC_DEBUG
428 if (range.first == range.last)
429 SDBG(("page %d\n", range.first));
430 else
431 SDBG(("pages %d..%d\n", range.first, range.last));
432 #endif /* SEMANTIC_DEBUG */
433
434 new_page = calloc (1, sizeof (output_page_t));
435 if (! new_page)
436 {
437 fprintf (stderr, "failed to malloc an output page struct\n");
438 return;
439 }
440 if (first_output_page)
441 {
442 last_output_page->next = new_page;
443 last_output_page = new_page;
444 }
445 else
446 {
447 first_output_page = last_output_page = new_page;
448 }
449 new_page->range = range;
450 new_page->output_context = last_output_context;
451
452 /* transfer bookmarks from context(s) to page */
453 for (context = last_output_context; context; context = context->parent)
454 if (context->first_bookmark)
455 {
456 context->last_bookmark->next = new_page->bookmark_list;
457 new_page->bookmark_list = context->first_bookmark;
458 context->first_bookmark = NULL;
459 context->last_bookmark = NULL;
460 }
461
462 increment_output_page_count (count);
463 }
464
465
output_overlay(overlay_t overlay)466 void output_overlay (overlay_t overlay)
467 {
468 output_pages (last_output_page->range);
469 last_output_page->has_overlay = 1;
470 last_output_page->overlay.left = overlay.left;
471 last_output_page->overlay.top = overlay.top;
472 }
473
output_set_colormap(rgb_t black_color,rgb_t white_color)474 void output_set_colormap (rgb_t black_color, rgb_t white_color)
475 {
476 output_clone ();
477 last_output_context->has_colormap = 1;
478 last_output_context->colormap.black_map = black_color;
479 last_output_context->colormap.white_map = white_color;
480 }
481
yyerror(const char * s)482 void yyerror (const char *s)
483 {
484 fprintf (stderr, "%d: %s\n", line, s);
485 }
486
get_input_filename(input_context_t * context)487 static char *get_input_filename (input_context_t *context)
488 {
489 for (; context; context = context->parent)
490 if ((context->input_file) || (context->is_blank))
491 return (context->input_file);
492 fprintf (stderr, "no input file name found\n");
493 exit (2);
494 }
495
get_input_rotation(input_context_t * context,input_modifier_type_t type,int * rotation)496 static bool get_input_rotation (input_context_t *context,
497 input_modifier_type_t type,
498 int *rotation)
499 {
500 for (; context; context = context->parent)
501 {
502 if (context->modifiers [type].has_rotation)
503 {
504 * rotation = context->modifiers [type].rotation;
505 return (1);
506 }
507 if (context->modifiers [INPUT_MODIFIER_ALL].has_rotation)
508 {
509 * rotation = context->modifiers [INPUT_MODIFIER_ALL].rotation;
510 return (1);
511 }
512 }
513 return (0); /* default */
514 }
515
get_input_transparency(input_context_t * context,input_modifier_type_t type)516 static rgb_range_t *get_input_transparency (input_context_t *context,
517 input_modifier_type_t type)
518 {
519 for (; context; context = context->parent)
520 {
521 if (context->modifiers [type].has_transparency)
522 {
523 return & (context->modifiers [type].transparency);
524 }
525 if (context->modifiers [INPUT_MODIFIER_ALL].has_transparency)
526 {
527 return & (context->modifiers [INPUT_MODIFIER_ALL].transparency);
528 }
529 }
530 return NULL; /* default */
531 }
532
get_input_page_size(input_context_t * context,input_modifier_type_t type,page_size_t * page_size)533 static bool get_input_page_size (input_context_t *context,
534 input_modifier_type_t type,
535 page_size_t *page_size)
536 {
537 for (; context; context = context->parent)
538 {
539 if (context->modifiers [type].has_page_size)
540 {
541 * page_size = context->modifiers [type].page_size;
542 return (1);
543 }
544 if (context->modifiers [INPUT_MODIFIER_ALL].has_page_size)
545 {
546 * page_size = context->modifiers [INPUT_MODIFIER_ALL].page_size;
547 return (1);
548 }
549 }
550 return (0); /* default */
551 }
552
get_output_filename(output_context_t * context)553 static char *get_output_filename (output_context_t *context)
554 {
555 for (; context; context = context->parent)
556 if (context->output_file)
557 return (context->output_file);
558 fprintf (stderr, "no output file found\n");
559 exit (2);
560 }
561
get_output_file_attributes(output_context_t * context)562 static pdf_file_attributes_t *get_output_file_attributes (output_context_t *context)
563 {
564 for (; context; context = context->parent)
565 if (context->output_file)
566 return (& context->file_attributes);
567 fprintf (stderr, "no output file found\n");
568 exit (2);
569 }
570
get_output_page_label(output_context_t * context)571 static page_label_t *get_output_page_label (output_context_t *context)
572 {
573 for (; context; context = context->parent)
574 if (context->has_page_label)
575 return (& context->page_label);
576 return (NULL); /* default */
577 }
578
get_output_colormap(output_context_t * context)579 static colormap_t *get_output_colormap (output_context_t *context)
580 {
581 for (; context; context = context->parent)
582 if (context->has_colormap)
583 return (& context->colormap);
584 return (NULL); /* default */
585 }
586
587
588 #ifdef SEMANTIC_DEBUG
dump_input_tree(void)589 void dump_input_tree (void)
590 {
591 input_image_t *image;
592 int i;
593 char *fn;
594
595 printf ("input images:\n");
596 for (image = first_input_image; image; image = image->next)
597 for (i = image->range.first; i <= image->range.last; i++)
598 {
599 input_modifier_type_t parity = (i % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
600 bool has_rotation, has_page_size;
601 int rotation;
602 page_size_t page_size;
603 rgb_range_t *transparency;
604
605 has_rotation = get_input_rotation (image->input_context,
606 parity,
607 & rotation);
608 has_page_size = get_input_page_size (image->input_context,
609 parity,
610 & page_size);
611 transparency = get_input_transparency (image->input_context, parity);
612 fn = get_input_filename (image->input_context);
613 if (fn)
614 printf ("file '%s' image %d", fn, i);
615 else
616 printf ("blank image %d", i);
617 if (has_rotation)
618 printf (" rotation %d", rotation);
619 if (transparency)
620 printf (" transparency %d..%d, %d..%d, %d..%d",
621 transparency->red.first, transparency->red.last,
622 transparency->green.first, transparency->green.last,
623 transparency->blue.first, transparency->blue.last);
624 if (has_page_size)
625 printf (" size %f, %f", page_size.width, page_size.height);
626 printf ("\n");
627 printf ("context: %08x\n", image->input_context);
628 }
629 }
630
dump_output_tree(void)631 void dump_output_tree (void)
632 {
633 int i;
634 output_page_t *page;
635 bookmark_t *bookmark;
636
637 printf ("output pages:\n");
638 for (page = first_output_page; page; page = page->next)
639 {
640 if (page->bookmark_list)
641 {
642 for (bookmark = page->bookmark_list; bookmark; bookmark = bookmark->next)
643 printf ("bookmark %d \"%s\"\n", bookmark->level, bookmark->name);
644 }
645 for (i = page->range.first; i <= page->range.last; i++)
646 {
647 page_label_t *label = get_output_page_label (page->output_context);
648 colormap_t *colormap = get_output_colormap (page->output_context);
649 printf ("file \"%s\" ", get_output_filename (page->output_context));
650 if (label)
651 {
652 printf ("label ");
653 if (label->prefix)
654 printf ("\"%s\" ", label->prefix);
655 if (label->style)
656 printf ("'%c' ", label->style);
657 }
658 if (colormap)
659 printf ("colormap (%d %d %d) (%d %d %d) ",
660 colormap->black_map.red, colormap->black_map.green, colormap->black_map.blue,
661 colormap->white_map.red, colormap->white_map.green, colormap->white_map.blue);
662 printf ("page %d\n", i);
663 }
664 }
665 }
666 #endif /* SEMANTIC_DEBUG */
667
668
range_count(range_t range)669 static inline int range_count (range_t range)
670 {
671 return ((range.last - range.first) + 1);
672 }
673
674
675 #ifdef CTL_LANG
parse_control_file(char * fn)676 bool parse_control_file (char *fn)
677 {
678 bool result = 0;
679
680 yyin = fopen (fn, "r");
681 if (! yyin)
682 {
683 fprintf (stderr, "can't open spec file '%s'\n", fn);
684 goto fail;
685 }
686
687 line = 1;
688
689 input_push_context (); /* create root input context */
690 input_push_context (); /* create inital input context */
691
692 output_push_context (); /* create root output context */
693 output_push_context (); /* create initial output context */
694
695 yyparse ();
696
697 if (first_input_context->image_count != first_output_context->page_count)
698 {
699 fprintf (stderr, "input image count %d != output page count %d\n",
700 first_input_context->image_count,
701 first_output_context->page_count);
702 goto fail;
703 }
704
705 fprintf (stderr, "%d images specified\n", first_input_context->image_count);
706
707 result = 1;
708
709 #ifdef SEMANTIC_DEBUG
710 dump_input_tree ();
711 dump_output_tree ();
712 #endif /* SEMANTIC_DEBUG */
713
714 fail:
715 if (yyin)
716 fclose (yyin);
717
718 return (result);
719 }
720
omit_label(page_label_t * page_label)721 bool omit_label (page_label_t *page_label)
722 {
723 static page_label_t *last_page_label;
724 bool unneeded;
725
726 unneeded = ( (last_page_label != NULL) &&
727 page_label->prefix &&
728 last_page_label->prefix &&
729 (strcmp (page_label->prefix, last_page_label->prefix) == 0) &&
730 (page_label->style == last_page_label->style) &&
731 (page_label->base == last_page_label->base + 1) );
732
733 last_page_label = page_label;
734
735 return unneeded;
736 }
737
process_controls(void)738 bool process_controls (void)
739 {
740 input_image_t *image = NULL;
741 output_page_t *page = NULL;
742 int i = 0;
743 int p = 0;
744 int page_index = 0;
745 input_attributes_t input_attributes;
746 input_modifier_type_t parity;
747 page_label_t *page_label;
748
749 for (;;)
750 {
751 if ((! image) || (i >= range_count (image->range)))
752 {
753 char *input_fn;
754 if (image)
755 image = image->next;
756 else
757 image = first_input_image;
758 if (! image)
759 return (1); /* done */
760 i = 0;
761 input_fn = get_input_filename (image->input_context);
762 if (verbose)
763 {
764 if (input_fn)
765 fprintf (stderr, "opening input file '%s'\n", input_fn);
766 else
767 fprintf (stderr, "generating blank image\n");
768 }
769 if (! open_input_file (input_fn))
770 {
771 fprintf (stderr, "error opening input file '%s'\n", input_fn);
772 return (0);
773 }
774 }
775
776 if ((! page) || (p >= range_count (page->range)))
777 {
778 char *output_fn;
779 if (page)
780 page = page->next;
781 else
782 page = first_output_page;
783 p = 0;
784 output_fn = get_output_filename (page->output_context);
785 if (verbose)
786 fprintf (stderr, "opening PDF file '%s'\n", output_fn);
787 if (! open_pdf_output_file (output_fn,
788 get_output_file_attributes (page->output_context)))
789 {
790 fprintf (stderr, "error opening PDF file '%s'\n", output_fn);
791 return (0);
792 }
793 }
794
795 parity = ((image->range.first + i) % 2) ? INPUT_MODIFIER_ODD : INPUT_MODIFIER_EVEN;
796
797 memset (& input_attributes, 0, sizeof (input_attributes));
798
799 input_attributes.rotation = 0;
800 input_attributes.has_rotation = get_input_rotation (image->input_context,
801 parity,
802 & input_attributes.rotation);
803
804 input_attributes.has_page_size = get_input_page_size (image->input_context,
805 parity,
806 & input_attributes.page_size);
807
808 input_attributes.transparency = get_input_transparency (image->input_context, parity);
809
810
811 // really an output attribute, but we don't have such an thing
812 input_attributes.colormap = get_output_colormap (page->output_context);
813
814 if (verbose)
815 fprintf (stderr, "processing image %d\n", image->range.first + i);
816
817 if (p)
818 page_label = NULL;
819 else
820 {
821 page_label = get_output_page_label (page->output_context);
822 if (page_label)
823 {
824 page_label->page_index = page_index;
825 page_label->base = page->range.first;
826 page_label->count = range_count (page->range);
827
828 if (omit_label (page_label))
829 page_label = NULL;
830 }
831 }
832
833 if (! process_page (image->range.first + i,
834 input_attributes,
835 p ? NULL : page->bookmark_list,
836 page_label,
837 page->has_overlay ? & page->overlay : NULL,
838 input_attributes.transparency))
839 {
840 fprintf (stderr, "error processing image %d\n", image->range.first + i);
841 return (0);
842 }
843 i++;
844 p++;
845
846 if (! page->has_overlay)
847 page_index++;
848 }
849 }
850 #endif /* CTL_LANG */
851