1 /*@z48.c:PDF back end@********************************************************/
2 /* */
3 /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39) */
4 /* COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston */
5 /* */
6 /* Jeffrey H. Kingston (jeff@it.usyd.edu.au) */
7 /* School of Information Technologies */
8 /* The University of Sydney 2006 */
9 /* AUSTRALIA */
10 /* */
11 /* This PDF Back End module written by Vincent Tan, March 1998. */
12 /* */
13 /* This program is free software; you can redistribute it and/or modify */
14 /* it under the terms of the GNU General Public License as published by */
15 /* the Free Software Foundation; either Version 3, or (at your option) */
16 /* any later version. */
17 /* */
18 /* This program is distributed in the hope that it will be useful, */
19 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
20 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
21 /* GNU General Public License for more details. */
22 /* */
23 /* You should have received a copy of the GNU General Public License */
24 /* along with this program; if not, write to the Free Software */
25 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */
26 /* */
27 /* FILE: z48.c */
28 /* MODULE: PDF back end */
29 /* EXTERNS: PDFFile_Init(), PDFFile_BeginFontEncoding(), */
30 /* PDFFile_EndFontEncoding(), PDFFile_Cleanup(), */
31 /* PDFPage_Init(), PDFPage_Cleanup(), PDFPage_Write(), */
32 /* PDFPage_Push(), PDFPage_Pop(), PDFPage_Scale(), */
33 /* PDFPage_Translate(), PDFPage_Rotate(), PDFPage_SetVars(), */
34 /* PDFPage_WriteGraphic(), PDFPage_PrintUnderline(), */
35 /* PDFFont_AddFont(), PDFFont_Set(), PDFText_OpenXY(), */
36 /* PDFText_OpenX(), PDFText_Open(), PDFText_Kern(), */
37 /* PDFText_Close(), PDFHasValidTextMatrix() */
38 /* */
39 /*****************************************************************************/
40 #include "externs.h"
41
42 /* ANSI headers */
43 #include <ctype.h>
44 #include <math.h>
45 #ifndef M_PI
46 #define M_PI 3.14159265358979323846
47 #endif
48 #include <time.h>
49
50 /* zlib headers: define PDF_COMPRESSION = 0 if you don't have zlib library */
51 #if PDF_COMPRESSION
52 #include "zlib.h"
53 #endif
54
Assert(BOOLEAN condition,FILE_POS * inFilePos)55 static void Assert(BOOLEAN condition, FILE_POS *inFilePos)
56 {
57 if (!condition) /* allows me to set a breakpoint here */
58 assert(condition, inFilePos);
59 }
60
61 /* #define's and typedefs */
62 #undef USE_MATRICES
63 #undef _CALC_LARGEST_PAGE_OBJECT_
64
65 enum {
66 kBase14FontCount = 14, /* there are 14 base PDF fonts */
67 kBufferSize = 1024 /* size of buffer for skipping non-marking commands */
68 };
69
70 #if PDF_COMPRESSION
71 enum {
72 kRawOutputBufferSize = 4096, /* arbitrary choice */
73 kCompressedOutputBufferSize = 4096 /* arbitrary choice */
74 };
75 #endif
76
77 enum {
78 kNumberOfObjectsPerBlock = 256, /* arbitrary choice */
79 kNumberOfPagesPerBlock = 64 /* arbitrary choice */
80 };
81
82 typedef enum {
83 kFitNoChange = 0, /* special default case */
84 kFit, /* [ /Fit ]: fit the page to the window */
85 kFitH, /* [ /FitH top ]: fit the width of the page to window; */
86 /* top specifies y-coord of the top edge of the window */
87 kFitV, /* [ /FitV left ]: fit the height of the page to the */
88 /* window. left specifies x-coord of left edge of win. */
89 kFitR, /* [ /FitR left bottom right top ]: fit the rectangle */
90 /* specified by left bottom right top in the window. */
91 /* If the height (top - bottom) and width (right-left) */
92 /* imply different zoom factors, the numerically */
93 /* smaller zoom factor is used, to ensure that the */
94 /* specified rectangle fits in the window */
95 kFitB, /* [ /FitB ]: fit the page's bounding box to window */
96 kFitBH, /* [ /FitBH top ]: fit the width of the page's bound. */
97 /* box to the window. top specifies the y-coordinate */
98 /* of the top edge of the window */
99 kFitBV, /* [ /FitBV left ]: fit the height of the page's */
100 /* bounding box to the window. left specifies the */
101 /* x-coordinate of the left edge of the window */
102
103 kNumberOfDestLinkOptions
104 } PDF_LINK_DEST_OPTION;
105
106 enum eUnitsKeywords {
107 k_in = 0,
108 k_cm,
109 k_pt,
110 k_em,
111 k_loutf,
112 k_loutv,
113 k_louts,
114 kNumberOfUnitKeywords
115 };
116
117 enum eGraphicsKeywords {
118 k_xsize = 0,
119 k_ysize,
120 k_xmark,
121 k_ymark,
122 kNumberOfGraphicsKeywords
123 };
124
125 enum eArithmeticKeywords {
126 k_add = 0,
127 k_sub,
128 k_mul,
129 k_div,
130 k_sin,
131 k_cos,
132 k_pick,
133 kNumberOfArithmeticKeywords
134 };
135
136 typedef enum {
137 k_link_source = 0, /* source of a link to an internal document target */
138 k_link_external, /* source of a link to an external document target */
139 k_link_URI, /* source of a link to an (external) URI target */
140 k_link_target, /* internal document target */
141 k_link_target_for_export, /* external document target */
142 kNumberOfLinkKeywords
143 } PDF_LINK_KEYWORD;
144
145 enum {
146 k_author = 0,
147 k_title,
148 k_subject,
149 k_keywords,
150 kNumberOfDocInfoKeywords
151 };
152
153
154 /* basic types */
155 #ifdef USE_MATRICES
156 typedef double t_matrix[9];
157 #endif
158
159 typedef char t_tempbuf[512];
160 typedef unsigned int PDF_OBJECT_NUM;
161 typedef PDF_OBJECT_NUM PDF_PAGE_OBJECT_NUM; /* an object number that can */
162 /* refer ONLY to page object */
163 typedef unsigned int PDF_FONT_NUM;
164 typedef unsigned int PDF_PAGE_NUM;
165 typedef unsigned int PDF_FILE_OFFSET;
166
167
168 /* font list */
169 struct t_font_list_entry {
170 struct t_font_list_entry *m_next_font_entry;
171 FULL_CHAR *m_PDF_font_name;
172 FULL_CHAR *m_short_font_name;
173 FULL_CHAR *m_actual_font_name;
174 PDF_OBJECT_NUM m_font_encoding_obj; /* valid for entire PDF file */
175 PDF_OBJECT_NUM m_pdf_object_number; /* valid for entire PDF file */
176 BOOLEAN m_font_resource_in_pdf; /* TRUE when PDF file has */
177 /* /Type /Font resource */
178 BOOLEAN m_in_use; /* used on a per-page basis */
179 };
180
181 typedef struct t_font_list_entry t_font_list_entry, *t_font_list_entry_ptr;
182
183
184 /* offsets of all objects (for xref list) */
185 typedef PDF_FILE_OFFSET t_offset_array[kNumberOfObjectsPerBlock];
186
187 struct t_offset_block {
188 struct t_offset_block *m_next_block;
189 t_offset_array m_block;
190 };
191
192 typedef struct t_offset_block t_offset_block, *t_offset_block_ptr;
193
194
195 /* for /Pages object */
196 typedef PDF_PAGE_OBJECT_NUM t_page_array[kNumberOfPagesPerBlock];
197
198 struct t_page_block {
199 struct t_page_block *m_next_block;
200 t_page_array m_block;
201 };
202
203 typedef struct t_page_block t_page_block, *t_page_block_ptr;
204
205
206 /* for font encodings */
207 struct t_font_encoding_entry {
208 struct t_font_encoding_entry* m_next_entry;
209 PDF_OBJECT_NUM m_object_num;
210 FULL_CHAR *m_font_encoding;
211 };
212
213 typedef struct t_font_encoding_entry
214 t_font_encoding_entry, *t_font_encoding_entry_ptr;
215
216
217 /* for qsave/qrestore [see PDFPage_Push()] */
218 struct t_qsave_entry {
219 struct t_qsave_entry *m_next_entry;
220 int m_page_h_origin, m_page_v_origin;
221 float m_page_h_scale_factor, m_page_v_scale_factor;
222 };
223
224 typedef struct t_qsave_entry t_qsave_entry, *t_qsave_entry_ptr;
225
226
227 /* for qsave/qrestore [see PDFPage_Push()] */
228 struct t_qsave_marking_entry {
229 struct t_qsave_marking_entry* m_next_entry;
230 unsigned int m_buffer_pos;
231 };
232
233 typedef struct t_qsave_marking_entry t_qsave_marking_entry, *t_qsave_marking_entry_ptr;
234
235
236 /* target of link annotations */
237 struct t_target_annot_entry {
238 struct t_target_annot_entry* m_next_entry;
239
240 /* all of the following are always defined */
241 FULL_CHAR *m_name;
242 PDF_PAGE_OBJECT_NUM m_page_object_num;
243
244 /* these are in PDF's default user space coordinates */
245 int m_ll_x;
246 int m_ll_y;
247 int m_ur_x;
248 int m_ur_y;
249
250 BOOLEAN m_for_export;
251 };
252
253 typedef struct t_target_annot_entry t_target_annot_entry, *t_target_annot_entry_ptr;
254
255 /* source of link annotations */
256 struct t_source_annot_entry {
257 struct t_source_annot_entry* m_next_entry;
258
259 t_target_annot_entry* m_target; /* if is a link and this is NULL then */
260 /* the link is a fwd link and remains */
261 /* unresolvable until the page is */
262 /* encountered - instead, the m_name */
263 /* field is defined; m_target will be */
264 /* NULL for URI type links */
265
266 FULL_CHAR *m_name; /* this string is defined if m_target */
267 /* is NULL otherwise it is null */
268 /* for URI links, this contains the */
269 /* URI to link to */
270 FULL_CHAR *m_file_spec; /* only defined for link_type == */
271 /* k_link_external */
272
273 /* all of the following are always defined */
274 /* these are in PDF's default user space coordinates */
275 int m_ll_x;
276 int m_ll_y;
277 int m_ur_x;
278 int m_ur_y;
279
280 PDF_OBJECT_NUM m_this_object_num; /* obj num of this "/Type /Annot" obj */
281 PDF_PAGE_OBJECT_NUM m_this_page_object_num; /* obj num of the page that */
282 /* this annot lies in */
283 PDF_LINK_DEST_OPTION m_dest_option;
284 PDF_LINK_KEYWORD m_link_type;
285
286 BOOLEAN m_written_to_PDF_file;
287 };
288
289 typedef struct t_source_annot_entry t_source_annot_entry, *t_source_annot_entry_ptr;
290
291
292 /* matrices */
293 #ifdef USE_MATRICES
294 struct t_matrix_entry {
295 struct t_matrix_entry* m_next_entry;
296 t_matrix m_matrix;
297 };
298
299 typedef struct t_matrix_entry t_matrix_entry, *t_matrix_entry_ptr;
300 #endif
301
302
303 /* statics */
304
305 /* general */
306 static BOOLEAN g_PDF_debug;
307
308 /* objects */
309 static PDF_OBJECT_NUM g_next_objnum;
310 static t_offset_block_ptr g_obj_offset_list; /* first block */
311 static t_offset_block_ptr g_cur_obj_offset_block;
312
313 /* fonts */
314 static t_font_list_entry_ptr g_font_list; /* backwards */
315 static t_font_encoding_entry_ptr g_font_encoding_list; /* backwards */
316
317 /* pages */
318 static PDF_PAGE_NUM g_page_count; /* current page num, */
319 /* starting at 1 */
320 static PDF_PAGE_OBJECT_NUM g_page_object_num; /* obj num of current*/
321 /* "/Type /Page" obj,*/
322 /* corr. to page */
323 /* num g_page_count */
324 static t_page_block_ptr g_page_block_list; /* first block */
325 static t_page_block_ptr g_cur_page_block;
326 static PDF_OBJECT_NUM g_pages_root;
327
328 /* document */
329 static int g_doc_h_bound;
330 static int g_doc_v_bound;
331 static FULL_CHAR* g_doc_author;
332 static FULL_CHAR* g_doc_title;
333 static FULL_CHAR* g_doc_subject;
334 static FULL_CHAR* g_doc_keywords;
335
336 /* link annotations */
337 static t_target_annot_entry_ptr g_target_annot_list;
338 static BOOLEAN g_has_exported_targets;
339
340 /* globals for each page */
341 /* these indicate what kind of content the page has */
342 static BOOLEAN g_page_uses_fonts;
343 static BOOLEAN g_page_has_text;
344 static BOOLEAN g_page_has_graphics;
345
346 /* these are only defined when the page has some content */
347 static PDF_OBJECT_NUM g_page_contents_obj_num;
348 static PDF_OBJECT_NUM g_page_length_obj_num;
349 static PDF_FILE_OFFSET g_page_start_offset;
350
351 /* valid after a PDF_Push and PDF_Pop */
352 static t_qsave_entry_ptr g_qsave_stack;
353
354 static t_qsave_marking_entry_ptr g_qsave_marking_stack; /* implemented as a */
355 /* linked list; pts */
356 /* to top of stack */
357 static BOOLEAN g_in_buffering_mode;
358 static char g_buffer[kBufferSize]; /* this buffer is used*/
359 /* for removing redundant operations */
360 static unsigned int g_buffer_pos;
361
362 /* valid after a link annotation has been defined */
363 static t_source_annot_entry_ptr g_source_annot_list;
364
365 #ifdef USE_MATRICES
366 static t_matrix g_cur_matrix;
367 static t_matrix_entry_ptr g_matrix_stack;
368 #endif
369
370 /* track these values in case they are ever required */
371 static float g_page_h_scale_factor, g_page_v_scale_factor;
372 static int g_page_h_origin, g_page_v_origin;
373 static int g_page_line_width;
374
375 /* magic keywords (actually they will appear in Lout documents as "__in", "__cm", etc.) */
376 static char *g_unit_keywords[kNumberOfUnitKeywords] =
377 {
378 "in", "cm", "pt", "em", "loutf", "loutv", "louts" /* MUST be followed by a fp number */
379 };
380
381 static char *g_graphic_keywords[kNumberOfGraphicsKeywords] =
382 {
383 "xsize", "ysize", "xmark", "ymark" /* like macros, these expand to the actual value */
384 };
385
386 static char *g_arithmetic_keywords[kNumberOfArithmeticKeywords] =
387 {
388 /* syntax: "__mul(x, y)" emits (x * y) to 2 decimal places */
389 /* */
390 /* Notes: */
391 /* */
392 /* sin and cos expect their arguments in degrees */
393 /* */
394 /* for negation, use "__sub(0, arg)" */
395 /* */
396 /* __pick(i, expr1, expr2, expr3...) picks the ith expr from the */
397 /* list of expr the "," are optional (if they are not used, you */
398 /* should separate values with whitespace) */
399
400 "add", "sub", "mul", "div", "sin", "cos", "pick" /* like macros, these expand to the actual value */
401 };
402
403 static char *g_link_keywords[kNumberOfLinkKeywords] =
404 {
405 /* syntax: "__link_source=<<name_of_target_link [dest_link_option]>>" */
406 /* */
407 /* example: "__link_source=<<chapter6>>" */
408 /* example: "__link_source=<<part7 __FitH>>" */
409
410 "link_source=<<",
411
412 /* syntax: "__link_external=<<name_of_target_link __link_to=file_spec>>"*/
413 /* syntax: "__link_external=<<name_of_target_link __link_to=<< /FS /URL /F (url)>>>>" */
414 /* */
415 /* ** note the special format required for URL links ** */
416 /* */
417 /* example: "__link_external=<<chapter6 __link_to=/usr/bin/file.pdf>>" */
418 /* example: "__link_external=<<chapter6 __link_to=<< /FS /URL /F */
419 /* (ftp://ftp.cs.usyd.edu.au/jeff/lout/user.pdf) >>>>" */
420
421 "link_external=<<",
422
423 /* syntax: "__link_URI=<<URL>>" */
424 /* */
425 /* example: "__link_URI=<<http://www.adobe.com>>" */
426
427 "link_URI=<<",
428
429 /* syntax: "__link_target=<<name_of_target_link>>" where */
430 /* name_of_target_link is in this PDF file; name_of_target_link CANNOT */
431 /* be accessed by external documents in links */
432 /* */
433 /* example: "__link_target=<<my_internal_target>>" */
434
435 "link_target=<<",
436
437 /* syntax: "__link_target_for_export=<<name_of_target_link>>" where */
438 /* name_of_target_link is in this file; name_of_target_link can be */
439 /* accessed by external documents in links */
440 /* */
441 /* example: "__link_target_for_export=<<my_exported_target>>" */
442
443 "link_target_for_export=<<"
444 };
445
446 static char *g_dest_link_options[kNumberOfDestLinkOptions] =
447 {
448 /* see PDF_LINK_DEST_OPTION for descriptions of the meanings of these */
449 "__FitNoChange",
450 "__Fit",
451 "__FitH",
452 "__FitV",
453 "__FitR",
454 "__FitB",
455 "__FitBH",
456 "__FitBV"
457 };
458
459 static char* g_external_file_spec_keyword[1] =
460 {
461 "__link_to="
462 };
463
464 static char* g_doc_info_keywords[kNumberOfDocInfoKeywords] =
465 {
466 "author=", "title=", "subject=", "keywords="
467 };
468
469 static int g_units[kNumberOfUnitKeywords];
470
471 static int g_graphics_vars[kNumberOfGraphicsKeywords];
472
473 /* text state */
474 static BOOLEAN g_TJ_pending;
475 static BOOLEAN g_ET_pending;
476 static BOOLEAN g_valid_text_matrix; /* true when BT...ET block open */
477
478
479 /* expressions */
480 static int g_expr_depth = 0;
481 static int g_expr_index;
482 static t_tempbuf g_expr;
483
484 /* links */
485 static int g_link_depth = 0;
486 static int g_link_index;
487 static t_tempbuf g_link;
488 static PDF_LINK_KEYWORD g_link_keyword;
489
490 /* the 14 base fonts */
491 static char *g_standard_base_14_fonts[kBase14FontCount] = {
492 "Courier",
493 "Courier-Bold",
494 "Courier-Oblique",
495 "Courier-BoldOblique",
496 "Helvetica",
497 "Helvetica-Bold",
498 "Helvetica-Oblique",
499 "Helvetica-BoldOblique",
500 "Symbol",
501 "Times",
502 "Times-Bold",
503 "Times-Italic",
504 "Times-BoldItalic",
505 "ZapfDingbats"
506 };
507
508
509 #if PDF_COMPRESSION
510 static BOOLEAN g_apply_compression;
511 static z_stream g_comp_stream; /* zlib compression stream */
512 static unsigned char* g_raw_buffer_ptr;
513
514 /* compression buffers */
515 static unsigned char g_raw_output[kRawOutputBufferSize];
516 static unsigned char g_compressed_output[kCompressedOutputBufferSize];
517 #endif
518
519 /* for calculating largest page object */
520 #ifdef _CALC_LARGEST_PAGE_OBJECT_
521 static PDF_FILE_OFFSET g_max_page_length = 0;
522 #endif
523
PDFHasValidTextMatrix(void)524 BOOLEAN PDFHasValidTextMatrix(void) /* this is called from z24.c */
525 {
526 return g_valid_text_matrix;
527 }
528
529
530 /*****************************************************************************/
531 /* */
532 /* t_offset_block_ptr PDFObject_FindOffsetBlock(PDF_OBJECT_NUM in_obj_num) */
533 /* */
534 /* Find the offset block for the given object number. */
535 /* */
536 /*****************************************************************************/
537
PDFObject_FindOffsetBlock(PDF_OBJECT_NUM in_obj_num,unsigned int * out_block_pos)538 static t_offset_block_ptr PDFObject_FindOffsetBlock(PDF_OBJECT_NUM in_obj_num,
539 unsigned int* out_block_pos)
540 {
541 int wanted_block_num = (in_obj_num - 1) / kNumberOfObjectsPerBlock;
542 int block_pos = (in_obj_num - 1) % kNumberOfObjectsPerBlock;
543 t_offset_block_ptr the_block = g_obj_offset_list;
544
545 Assert((in_obj_num > 0) && (in_obj_num < g_next_objnum), no_fpos);
546
547 /* find block */
548 while (wanted_block_num != 0) {
549 Assert(the_block != NULL, no_fpos);
550 the_block = the_block->m_next_block;
551 wanted_block_num--;
552 }
553 Assert(the_block != NULL, no_fpos);
554
555 if (out_block_pos != NULL)
556 *out_block_pos = block_pos;
557 return the_block;
558 }
559
560
561 /*****************************************************************************/
562 /* */
563 /* PDF_OBJECT_NUM PDFObject_New(FILE* in_fp) */
564 /* */
565 /* Return the next available object number. */
566 /* */
567 /*****************************************************************************/
568
PDFObject_New()569 static PDF_OBJECT_NUM PDFObject_New(/* FILE* in_fp */)
570 {
571 int wanted_block_num = (g_next_objnum - 1) / kNumberOfObjectsPerBlock;
572 int block_pos = (g_next_objnum - 1) % kNumberOfObjectsPerBlock;
573 t_offset_block_ptr the_block = g_cur_obj_offset_block;
574
575 /* if first obj in a block then allocate the block */
576 if (block_pos == 0)
577 {
578 the_block = (t_offset_block_ptr) malloc(sizeof(t_offset_block));
579 if (the_block == NULL)
580 Error(48, 1, "PDFObject_New: run out of memory", FATAL, no_fpos);
581 if (wanted_block_num == 0) /* if first block in file */
582 {
583 Assert(g_obj_offset_list == NULL, no_fpos);
584 g_obj_offset_list = the_block;
585 }
586 else
587 {
588 Assert(g_cur_obj_offset_block != NULL, no_fpos);
589 g_cur_obj_offset_block->m_next_block = the_block;
590 }
591 the_block->m_next_block = NULL; /* don't forget to init this! */
592 g_cur_obj_offset_block = the_block;
593 }
594 else
595 {
596 Assert(the_block != NULL, no_fpos);
597 }
598
599 /* initialise the offset of this object to zero (it hasn't been written */
600 /* to the PDF file yet) */
601 the_block->m_block[block_pos] = 0; /* ftell(in_fp); */
602
603 return g_next_objnum++;
604 }
605
606
607 /*****************************************************************************/
608 /* */
609 /* void PDFObject_WriteRef(FILE* in_fp, PDF_OBJECT_NUM in_object_number) */
610 /* */
611 /* Return the next available object number and write a reference to it. */
612 /* */
613 /*****************************************************************************/
614
PDFObject_WriteRef(FILE * in_fp,PDF_OBJECT_NUM in_object_number)615 static void PDFObject_WriteRef(FILE* in_fp, PDF_OBJECT_NUM in_object_number)
616 {
617 fprintf(in_fp, "%u 0 R", in_object_number);
618 }
619
620
621 /*****************************************************************************/
622 /* */
623 /* void PDFObject_WriteObj(FILE* in_fp, PDF_OBJECT_NUM in_object_number) */
624 /* */
625 /* Write the object's definition (and remember its file position). */
626 /* */
627 /*****************************************************************************/
628
PDFObject_WriteObj(FILE * in_fp,PDF_OBJECT_NUM in_object_number)629 static void PDFObject_WriteObj(FILE* in_fp, PDF_OBJECT_NUM in_object_number)
630 {
631 unsigned int block_pos;
632 t_offset_block_ptr block =
633 PDFObject_FindOffsetBlock(in_object_number, &block_pos);
634 Assert(block->m_block[block_pos]==0, no_fpos); /* offset shd be "unknown" */
635 block->m_block[block_pos] = ftell(in_fp); /* remember offset for xref */
636
637 fprintf(in_fp, "%u 0 obj\n", in_object_number);
638 }
639
640 /*****************************************************************************/
641 /* */
642 /* PDF_OBJECT_NUM PDFObject_WriteNewObj(FILE* in_fp) */
643 /* */
644 /* Return the next available object number and write its definition. */
645 /* */
646 /*****************************************************************************/
647
PDFObject_WriteNewObj(FILE * in_fp)648 static PDF_OBJECT_NUM PDFObject_WriteNewObj(FILE* in_fp)
649 {
650 PDF_OBJECT_NUM next_ref = PDFObject_New(/* in_fp */);
651 PDFObject_WriteObj(in_fp, next_ref);
652 return next_ref;
653 }
654
655
656 /*****************************************************************************/
657 /* */
658 /* PDF_OBJECT_NUM PDFObject_WriteNextRef(FILE* in_fp) */
659 /* */
660 /* Return the next available object number and write a reference to it. */
661 /* */
662 /*****************************************************************************/
663
664 /* ***
665 static PDF_OBJECT_NUM PDFObject_WriteNextRef(FILE* in_fp)
666 {
667 PDF_OBJECT_NUM next_ref = PDFObject_New(in_fp);
668 PDFObject_WriteRef(in_fp, next_ref);
669 return next_ref;
670 }
671 *** */
672
673
674 /*****************************************************************************/
675 /* */
676 /* void PDFFile_BeginFontEncoding(FILE* in_fp, const char* in_encoding_name) */
677 /* */
678 /* Begin font encoding. */
679 /* */
680 /*****************************************************************************/
681
PDFFile_BeginFontEncoding(FILE * in_fp,const char * in_encoding_name)682 void PDFFile_BeginFontEncoding(FILE* in_fp, const char* in_encoding_name)
683 {
684 PDF_OBJECT_NUM encoding_num;
685 t_font_encoding_entry_ptr encoding_entry;
686
687 /* TO FILL IN: create entry in font-encoding list and add this encoding */
688 if (g_PDF_debug)
689 fprintf(in_fp, "%%\n%% font encoding '%s':\n%%\n", in_encoding_name);
690
691 encoding_num = PDFObject_WriteNewObj(in_fp);
692 fputs("<<\n/Type /Encoding\n/Differences [ 0\n", in_fp);
693
694 /* add font encoding to list of encodings (assume we don't get passed the */
695 /* same encoding more than once) */
696 encoding_entry =
697 (t_font_encoding_entry_ptr) malloc(sizeof(t_font_encoding_entry));
698 if (encoding_entry == NULL)
699 Error(48, 2, "PDFFile_BeginFontEncoding: run out of memory",FATAL,no_fpos);
700
701 encoding_entry->m_font_encoding =
702 (FULL_CHAR*) malloc(strlen(in_encoding_name) + 1);
703 if (encoding_entry->m_font_encoding == NULL)
704 Error(48, 3, "PDFFile_BeginFontEncoding: out of memory", FATAL, no_fpos);
705
706 encoding_entry->m_next_entry = g_font_encoding_list;
707 encoding_entry->m_object_num = encoding_num;
708 strcpy((char*) encoding_entry->m_font_encoding, (char*) in_encoding_name);
709 g_font_encoding_list = encoding_entry;
710 }
711
712
713 /*****************************************************************************/
714 /* */
715 /* void PDFFile_EndFontEncoding(FILE* in_fp) */
716 /* */
717 /* End of font encoding. */
718 /* */
719 /*****************************************************************************/
720
PDFFile_EndFontEncoding(FILE * in_fp)721 void PDFFile_EndFontEncoding(FILE* in_fp)
722 {
723 fputs("]\n>>\nendobj\n", in_fp);
724 }
725
726
727 /*****************************************************************************/
728 /* */
729 /* const FULL_CHAR* PDFFont_FindListEntry(const FULL_CHAR* in_real_font_name)*/
730 /* */
731 /* Try to find the font list entry with the specified real font name; */
732 /* return the entry's reference if found else return NULL. */
733 /* */
734 /*****************************************************************************/
735
736 static t_font_list_entry_ptr
PDFFont_FindListEntry(const FULL_CHAR * in_real_font_name)737 PDFFont_FindListEntry(const FULL_CHAR* in_real_font_name)
738 {
739 t_font_list_entry_ptr entry = g_font_list;
740
741 while (entry != NULL) {
742 if (strcmp((char*)in_real_font_name, (char*)entry->m_actual_font_name)==0)
743 break;
744 entry = entry->m_next_font_entry;
745 }
746 return entry;
747 }
748
749
750 /*****************************************************************************/
751 /* */
752 /* const FULL_CHAR* */
753 /* PDFFont_FindListEntry_Short(const FULL_CHAR* in_short_font_name) */
754 /* */
755 /* Try to find the font list entry with the specified real font name; */
756 /* return the entry's reference if found else return NULL. */
757 /* */
758 /*****************************************************************************/
759
760 static t_font_list_entry_ptr
PDFFont_FindListEntry_Short(const FULL_CHAR * in_short_font_name)761 PDFFont_FindListEntry_Short(const FULL_CHAR* in_short_font_name)
762 {
763 t_font_list_entry_ptr entry = g_font_list;
764
765 while (entry != NULL) {
766 if (strcmp((char*)in_short_font_name, (char*)entry->m_short_font_name)==0)
767 break;
768 entry = entry->m_next_font_entry;
769 }
770 return entry;
771 }
772
773
774 /*****************************************************************************/
775 /* */
776 /* const t_font_list_entry_ptr PDFFont_NewListEntry( */
777 /* const FULL_CHAR* in_short_font_name, const FULL_CHAR* in_real_font_name)*/
778 /* */
779 /* Create a new font entry and return the short name of the font. */
780 /* */
781 /*****************************************************************************/
782
783 static t_font_list_entry_ptr
PDFFont_NewListEntry(const FULL_CHAR * in_short_font_name,const FULL_CHAR * in_real_font_name,PDF_OBJECT_NUM in_font_encoding_obj)784 PDFFont_NewListEntry(const FULL_CHAR* in_short_font_name,
785 const FULL_CHAR* in_real_font_name,
786 PDF_OBJECT_NUM in_font_encoding_obj)
787 {
788 PDF_FONT_NUM next_font_num = 0;
789 t_font_list_entry_ptr new_entry = g_font_list;
790 /* t_font_list_entry_ptr last_font_list_entry = NULL; */
791
792 /* find next available font number */
793 {
794 while (new_entry != NULL) {
795 next_font_num++;
796 new_entry = new_entry->m_next_font_entry;
797 }
798 }
799
800 /* make a new font list entry */
801 {
802 char PDF_font_name[64] = "/F";
803 char num[32];
804
805 new_entry = (t_font_list_entry_ptr) malloc(sizeof(t_font_list_entry));
806 if (new_entry == NULL)
807 Error(48, 4, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
808
809 sprintf(num, "%u", next_font_num);
810 strcat(PDF_font_name, num);
811
812 new_entry->m_PDF_font_name =
813 (FULL_CHAR*) malloc(strlen((char*) PDF_font_name) + 1);
814 if (new_entry->m_PDF_font_name == NULL)
815 Error(48, 5, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
816 strcpy((char*) new_entry->m_PDF_font_name, PDF_font_name);
817
818 new_entry->m_short_font_name =
819 (FULL_CHAR*) malloc(strlen((char*) in_short_font_name) + 1);
820 if (new_entry->m_short_font_name == NULL)
821 Error(48, 6, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
822 strcpy((char*) new_entry->m_short_font_name, (char*) in_short_font_name);
823
824 new_entry->m_actual_font_name =
825 (FULL_CHAR*) malloc(strlen((char*) in_real_font_name) + 1);
826 if (new_entry->m_actual_font_name == NULL)
827 Error(48, 7, "PDFFont_NewListEntry: run out of memory", FATAL, no_fpos);
828 strcpy((char*) new_entry->m_actual_font_name, (char*) in_real_font_name);
829
830 new_entry->m_font_encoding_obj = in_font_encoding_obj;
831
832 new_entry->m_pdf_object_number = 0; /* don't give this font resource an */
833 /* object number until needed */
834 /* new_entry->m_in_use = TRUE; */ /* should be cleared after each page */
835 /* g_page_uses_fonts = TRUE; */
836 new_entry->m_font_resource_in_pdf = FALSE; /* not in PDF file yet */
837
838 new_entry->m_next_font_entry = g_font_list;
839 g_font_list = new_entry;
840 }
841 debug1(DPD, D, "new PDF font entry with short name %s",
842 new_entry->m_short_font_name);
843 return new_entry;
844 }
845
846
847 /*****************************************************************************/
848 /* */
849 /* const t_font_list_entry_ptr PDFGetFont(const char* in_real_font_name) */
850 /* */
851 /* Return the reference of a font entry. Never returns NULL. */
852 /* */
853 /*****************************************************************************/
854 /*
855 static t_font_list_entry_ptr PDFGetFont(const FULL_CHAR* in_real_font_name)
856 {
857 t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
858 if (entry == NULL)
859 entry = PDFFont_NewListEntry(in_real_font_name);
860 return entry;
861 }
862 */
863
864 /*****************************************************************************/
865 /* */
866 /* PDFFont_WriteObjectRef(FILE* in_fp, t_font_list_entry_ptr in_font_entry) */
867 /* */
868 /* Write a reference to the object to the file. */
869 /* */
870 /*****************************************************************************/
871
PDFFont_WriteObjectRef(FILE * in_fp,const t_font_list_entry * in_font_entry)872 static void PDFFont_WriteObjectRef(FILE* in_fp,
873 const t_font_list_entry* in_font_entry)
874 {
875 Assert(in_font_entry->m_pdf_object_number != 0, no_fpos);
876 PDFObject_WriteRef(in_fp, in_font_entry->m_pdf_object_number);
877 }
878
879
880 /*****************************************************************************/
881 /* */
882 /* void PDFFont_WriteObject(FILE* in_fp, */
883 /* t_font_list_entry_ptr in_font_entry) */
884 /* */
885 /* Write a reference to the object to the file. */
886 /* */
887 /*****************************************************************************/
888
PDFFont_WriteObject(FILE * in_fp,t_font_list_entry_ptr in_font_entry)889 static void PDFFont_WriteObject(FILE* in_fp, t_font_list_entry_ptr in_font_entry)
890 {
891 if (in_font_entry->m_pdf_object_number == 0)
892 in_font_entry->m_pdf_object_number = PDFObject_New(/* in_fp */);
893 PDFObject_WriteObj(in_fp, in_font_entry->m_pdf_object_number);
894 }
895
896
897 /*****************************************************************************/
898 /* */
899 /* BOOLEAN PDFFont_IsOneOfTheBase14Fonts(const FULL_CHAR* in_real_font_name)*/
900 /* */
901 /* Returns true if given font is one of the base 14 fonts. */
902 /* */
903 /*****************************************************************************/
904
PDFFont_IsOneOfTheBase14Fonts(const FULL_CHAR * in_real_font_name)905 static BOOLEAN PDFFont_IsOneOfTheBase14Fonts(const FULL_CHAR* in_real_font_name)
906 {
907 int i;
908 for (i = 0; i < kBase14FontCount; i++)
909 if (strcmp(g_standard_base_14_fonts[i], (char*) in_real_font_name) == 0)
910 return TRUE;
911 return FALSE;
912 }
913
914
915 /*****************************************************************************/
916 /* */
917 /* PDFFont_WriteFontResource(FILE* in_fp, */
918 /* t_font_list_entry_ptr in_font_entry) */
919 /* */
920 /* Writes out the PDF idea of a Font resource. */
921 /* */
922 /*****************************************************************************/
923
PDFFont_WriteFontResource(FILE * in_fp,t_font_list_entry_ptr in_font_entry)924 static void PDFFont_WriteFontResource(FILE* in_fp,
925 t_font_list_entry_ptr in_font_entry)
926 {
927 if (! in_font_entry->m_font_resource_in_pdf)
928 {
929 in_font_entry->m_font_resource_in_pdf = TRUE;
930
931 if (g_PDF_debug)
932 fprintf(in_fp, "%%\n%% declare use of font %s:\n%%\n",
933 in_font_entry->m_actual_font_name);
934
935 PDFFont_WriteObject(in_fp, in_font_entry);
936 fputs("<<\n/Type /Font\n/Subtype /Type1\n", in_fp);
937 fprintf(in_fp, "/Name %s\n", (char*) in_font_entry->m_PDF_font_name);
938 fprintf(in_fp, "/BaseFont /%s\n", (char*) in_font_entry->m_actual_font_name);
939 if (! PDFFont_IsOneOfTheBase14Fonts(in_font_entry->m_actual_font_name))
940 {
941 /* ***
942 fputs("/FirstChar 0"\n, in_fp); - we don't do first chars (yet)
943 fputs("/LastChar 255\n", in_fp); - we don't do last chars (yet)
944 fputs("/Widths ", in_fp); - we don't do last chars (yet)
945 fputs("/FontDescriptor ", in_fp); - we don't do font descriptors (yet)
946 *** */
947 }
948
949 if (in_font_entry->m_font_encoding_obj != 0)
950 {
951 fputs("/Encoding ", in_fp);
952 PDFObject_WriteRef(in_fp, in_font_entry->m_font_encoding_obj);
953 fputs("\n", in_fp);
954 }
955
956 /* ***
957 else
958 Error(48, 8, "PDFFont_WriteFontResource: a font has no encoding",
959 WARN, no_fpos);
960 *** */
961 fputs(">>\nendobj\n", in_fp);
962 }
963 }
964
965
966 /*****************************************************************************/
967 /* */
968 /* PDFFont_WriteFontResource_name(FILE* in_fp, */
969 /* const FULL_CHAR* in_real_font_name) */
970 /* */
971 /* Writes out the PDF idea of a Font resource. */
972 /* */
973 /*****************************************************************************/
974
975 /* ***
976 static void PDFFont_WriteFontResource_name(FILE* in_fp,
977 const FULL_CHAR* in_real_font_name)
978 {
979 t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
980 Assert(entry != NULL, no_fpos);
981 PDFFont_WriteFontResource(in_fp, entry);
982 }
983 *** */
984
985
986 /*****************************************************************************/
987 /* */
988 /* const FULL_CHAR* PDFGetPDFFontName(const FULL_CHAR* in_real_font_name) */
989 /* */
990 /* Return the short name of a font. */
991 /* */
992 /*****************************************************************************/
993
994 /* ***
995 static const FULL_CHAR* PDFGetPDFFontName(const FULL_CHAR* in_real_font_name)
996 {
997 t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
998 Assert(entry != NULL, no_fpos);
999 return entry->m_PDF_font_name;
1000 }
1001 *** */
1002
1003
1004 /*****************************************************************************/
1005 /* */
1006 /* PDF_OBJECT_NUM PDFFont_FindFontEncoding(FULL_CHAR* in_font_encoding_name) */
1007 /* */
1008 /* Return the object number of a given font encoding. */
1009 /* */
1010 /*****************************************************************************/
1011
PDFFont_FindFontEncoding(const FULL_CHAR * in_font_encoding_name)1012 static PDF_OBJECT_NUM PDFFont_FindFontEncoding(
1013 const FULL_CHAR* in_font_encoding_name)
1014 {
1015 t_font_encoding_entry_ptr entry = g_font_encoding_list;
1016
1017 /* these two lines are Uwe patch Jul 18, 2000 */
1018 if (in_font_encoding_name == NULL)
1019 return 0;
1020
1021 while (entry != NULL)
1022 {
1023 if (strcmp((char*)in_font_encoding_name, (char*)entry->m_font_encoding)==0)
1024 break;
1025 entry = entry->m_next_entry;
1026 }
1027 return (entry != NULL) ? entry->m_object_num : 0;
1028 }
1029
1030
1031 /*****************************************************************************/
1032 /* */
1033 /* void PDFFont_AddFont( */
1034 /* FILE* in_fp, const FULL_CHAR* in_short_font_name, */
1035 /* const FULL_CHAR* in_real_font_name) */
1036 /* */
1037 /* Add this font to the list of fonts that the document uses. Also remember */
1038 /* that this font is "in use" (output when page resources are written). */
1039 /* */
1040 /*****************************************************************************/
1041
PDFFont_AddFont(FILE * in_fp,const FULL_CHAR * in_short_font_name,const FULL_CHAR * in_real_font_name,const FULL_CHAR * in_font_encoding_name)1042 void PDFFont_AddFont(FILE* in_fp, const FULL_CHAR* in_short_font_name,
1043 const FULL_CHAR* in_real_font_name, const FULL_CHAR* in_font_encoding_name)
1044 {
1045 t_font_list_entry_ptr entry = PDFFont_FindListEntry(in_real_font_name);
1046 debug4(DPD, D, "PDFFont_AddFont(-, %s, %s, %s) [new = %s]",
1047 in_short_font_name, in_real_font_name,
1048 (in_font_encoding_name != (FULL_CHAR *) NULL ? in_font_encoding_name : ""),
1049 bool(entry == NULL));
1050 /* *** this attempted bug fix by Jeff K. problem may be multiple font
1051 entries for the same font
1052 if (entry == NULL)
1053 *** */
1054 if (TRUE)
1055 entry = PDFFont_NewListEntry(in_short_font_name, in_real_font_name,
1056 PDFFont_FindFontEncoding(in_font_encoding_name));
1057
1058 /* ***
1059 entry->m_in_use = TRUE;
1060 g_page_uses_fonts = TRUE;
1061 *** */
1062 }
1063
1064
1065 /*****************************************************************************/
1066 /* */
1067 /* void PDFPage_SetVars(int xsize, int ysize, int xmark, int ymark, */
1068 /* int loutf, int loutv, int louts) */
1069 /* */
1070 /* Writes a string to the page's stream. */
1071 /* */
1072 /*****************************************************************************/
1073
PDFPage_SetVars(int xsize,int ysize,int xmark,int ymark,int loutf,int loutv,int louts)1074 void PDFPage_SetVars(int xsize, int ysize, int xmark, int ymark,
1075 int loutf, int loutv, int louts)
1076 {
1077 g_graphics_vars[k_xsize] = xsize;
1078 g_graphics_vars[k_ysize] = ysize;
1079 g_graphics_vars[k_xmark] = xmark;
1080 g_graphics_vars[k_ymark] = ymark;
1081
1082 g_units[k_loutf] = loutf;
1083 g_units[k_loutv] = loutv;
1084 g_units[k_louts] = louts;
1085 }
1086
1087
1088 /*****************************************************************************/
1089 /* */
1090 /* void PDFPage_FlushCompressedBuffer(FILE* in_fp) */
1091 /* */
1092 /* Flushes the compressed output buffer to the page's stream. */
1093 /* */
1094 /*****************************************************************************/
1095
1096 #if PDF_COMPRESSION
PDFPage_FlushCompressedBuffer(FILE * in_fp)1097 static void PDFPage_FlushCompressedBuffer(FILE* in_fp)
1098 {
1099 int err;
1100
1101 Assert(g_apply_compression, no_fpos);
1102 do {
1103 err = deflate(&g_comp_stream, Z_FINISH);
1104
1105 fwrite(g_compressed_output,
1106 sizeof(g_compressed_output) - g_comp_stream.avail_out, 1, in_fp);
1107 g_comp_stream.next_out = g_compressed_output;
1108 g_comp_stream.avail_out = sizeof(g_compressed_output);
1109 } while (err == Z_OK);
1110
1111 if (err != Z_STREAM_END)
1112 Error(48, 9, "PDFPage_FlushCompressedBuffer: zlib error occurred",
1113 FATAL, no_fpos);
1114
1115 err = deflateEnd(&g_comp_stream);
1116 if (err != Z_OK)
1117 Error(48, 10, "PDFPage_FlushCompressedBuffer: zlib error occurred",
1118 FATAL, no_fpos);
1119 }
1120
1121
1122 /*****************************************************************************/
1123 /* */
1124 /* void PDFPage_FlushRawBuffer(FILE* in_fp) */
1125 /* */
1126 /* Attempts to compress the raw buffer; also flushes the compressed output */
1127 /* buffer to the page's stream if it is full. */
1128 /* */
1129 /*****************************************************************************/
1130
PDFPage_FlushRawBuffer(FILE * in_fp)1131 static void PDFPage_FlushRawBuffer(FILE* in_fp)
1132 {
1133 int err;
1134
1135 /* compress the raw buffer */
1136 Assert(g_apply_compression, no_fpos);
1137 g_comp_stream.next_in = g_raw_output;
1138 g_comp_stream.avail_in = (uInt) (g_raw_buffer_ptr - g_raw_output);
1139 Assert(g_comp_stream.avail_out != 0, no_fpos);
1140
1141 /* always compress to the point where the raw buffer is empty */
1142 do {
1143 err = deflate(&g_comp_stream, Z_NO_FLUSH);
1144 /* bug fix from newman-andy@yale.edu Feb 23 2000 if (err != Z_OK) */
1145 if ( err != Z_OK && g_comp_stream.avail_in != 0 )
1146 Error(48, 11, "PDFPage_FlushRawBuffer: zlib error occurred",FATAL,no_fpos);
1147
1148 /* IF compressed output buffer is full THEN flush it to disk and reset it */
1149 if (g_comp_stream.avail_out == 0)
1150 {
1151 if (fwrite(g_compressed_output, sizeof(g_compressed_output), 1, in_fp)!=1)
1152 Error(48, 12, "PDFPage_FlushRawBuffer: write error occurred",
1153 FATAL, no_fpos);
1154 g_comp_stream.next_out = g_compressed_output;
1155 g_comp_stream.avail_out = sizeof(g_compressed_output);
1156 }
1157 } while (g_comp_stream.avail_in != 0);
1158
1159 /* reset raw buffer for next call */
1160 g_raw_buffer_ptr = g_raw_output;
1161 }
1162 #endif
1163
1164
1165 /*****************************************************************************/
1166 /* */
1167 /* void PDFPage_WriteStream(FILE* in_fp, char* in_str) */
1168 /* */
1169 /* Writes a string to the page's stream. */
1170 /* */
1171 /*****************************************************************************/
1172
PDFPage_WriteStream(FILE * in_fp,char * in_str)1173 static void PDFPage_WriteStream(FILE* in_fp, char* in_str)
1174 {
1175 if (*in_str == 0)
1176 return;
1177
1178 #if PDF_COMPRESSION
1179 if (g_apply_compression)
1180 {
1181 unsigned int total = strlen(in_str);
1182 char *ptr = in_str;
1183
1184 while (total != 0)
1185 {
1186 unsigned int len = total;
1187 BOOLEAN needToFlush =
1188 ((g_raw_buffer_ptr + len) > (g_raw_output + sizeof(g_raw_output)));
1189 if (needToFlush)
1190 len = g_raw_output + sizeof(g_raw_output) - g_raw_buffer_ptr;
1191 memcpy(g_raw_buffer_ptr, ptr, len);
1192
1193 ptr += len;
1194 g_raw_buffer_ptr += len;
1195 total -= len;
1196
1197 /* IF need to flush raw buffer THEN do so */
1198 if (needToFlush) PDFPage_FlushRawBuffer(in_fp);
1199 } /* while still have bytes to process */
1200 }
1201 else
1202 #endif
1203 fputs(in_str, in_fp);
1204 }
1205
1206
1207 /*****************************************************************************/
1208 /* */
1209 /* void PDFPage_Begin(FILE* in_fp) */
1210 /* */
1211 /* Begins the page's stream. */
1212 /* */
1213 /*****************************************************************************/
1214
PDFPage_Begin(FILE * in_fp)1215 static void PDFPage_Begin(FILE* in_fp)
1216 {
1217 if (g_page_contents_obj_num == 0)
1218 {
1219 t_tempbuf str;
1220
1221 if (g_PDF_debug)
1222 fprintf(in_fp, "%%\n%% page %u's contents:\n%%\n", g_page_count);
1223
1224 g_page_contents_obj_num = PDFObject_WriteNewObj(in_fp);
1225 g_page_length_obj_num = PDFObject_New(/* in_fp */);
1226 fputs("<< /Length ", in_fp);
1227 PDFObject_WriteRef(in_fp, g_page_length_obj_num);
1228 #if PDF_COMPRESSION
1229 if (g_apply_compression) fputs(" /Filter /FlateDecode", in_fp);
1230 #endif
1231 fputs(" >>\nstream\n", in_fp);
1232 g_page_start_offset = ftell(in_fp);
1233
1234 #if PDF_COMPRESSION
1235 if (g_apply_compression)
1236 {
1237 int err;
1238
1239 g_raw_buffer_ptr = g_raw_output;
1240 g_comp_stream.zalloc = (alloc_func) Z_NULL;
1241 g_comp_stream.zfree = (free_func) Z_NULL;
1242 g_comp_stream.opaque = (voidpf) Z_NULL;
1243
1244 err = deflateInit(&g_comp_stream, Z_DEFAULT_COMPRESSION);
1245 if (err != Z_OK)
1246 Error(48, 13, "PDFPage_Begin: zlib error occurred", FATAL, no_fpos);
1247
1248 g_comp_stream.next_out = g_compressed_output;
1249 g_comp_stream.avail_out = sizeof(g_compressed_output);
1250 }
1251 #endif
1252
1253 #ifndef USE_MATRICES
1254 sprintf(str, "%.2f 0 0 %.2f 0 0 cm\n",
1255 g_page_h_scale_factor, g_page_v_scale_factor);
1256 PDFPage_WriteStream(in_fp, str);
1257 #endif
1258 sprintf(str, "%u w\n", g_page_line_width);
1259 PDFPage_WriteStream(in_fp, str);
1260 }
1261 }
1262
1263
1264 /*****************************************************************************/
1265 /* */
1266 /* void PDFPage_FlushBuffer(FILE* in_fp) */
1267 /* */
1268 /* Flush the buffer to the page's stream and turn off buffering mode. */
1269 /* */
1270 /*****************************************************************************/
1271
PDFPage_FlushBuffer(FILE * in_fp)1272 static void PDFPage_FlushBuffer(FILE* in_fp)
1273 {
1274 if (g_in_buffering_mode)
1275 {
1276 g_in_buffering_mode = FALSE;
1277
1278 /* empty the stack since it's no longer needed */
1279 while (g_qsave_marking_stack != NULL)
1280 {
1281 t_qsave_marking_entry_ptr entry = g_qsave_marking_stack;
1282 g_qsave_marking_stack = entry->m_next_entry;
1283 free(entry);
1284 }
1285
1286 /* output the buffer */
1287 PDFPage_WriteStream(in_fp, g_buffer);
1288 }
1289 }
1290
1291
1292 /*****************************************************************************/
1293 /* */
1294 /* PDF_FILE_OFFSET PDFPage_End(FILE* in_fp) */
1295 /* */
1296 /* Ends the page's stream. */
1297 /* */
1298 /*****************************************************************************/
1299
PDFPage_End(FILE * in_fp)1300 static PDF_FILE_OFFSET PDFPage_End(FILE* in_fp)
1301 {
1302
1303 /* if page has no marks on it then write out an empty stream */
1304 if (g_in_buffering_mode)
1305 {
1306 g_buffer_pos = 0;
1307 g_buffer[0] = '\0'; /* force empty string to be written */
1308 PDFPage_FlushBuffer(in_fp); /* all I really want is to empty the stack */
1309 }
1310
1311 /* IF applying compression THEN first flush the raw buffer and then flush */
1312 /* the compressed buffer (must be performed in that order!) */
1313 #if PDF_COMPRESSION
1314 if (g_apply_compression)
1315 {
1316 if ((g_raw_buffer_ptr > g_raw_output) && (g_raw_buffer_ptr[-1] == '\n'))
1317 g_raw_buffer_ptr--; /* remove last linefeed */
1318
1319 PDFPage_FlushRawBuffer(in_fp);
1320 PDFPage_FlushCompressedBuffer(in_fp);
1321 fputs("\n", in_fp);
1322 }
1323 #endif
1324
1325 /* close page's stream */
1326 Assert(g_page_contents_obj_num != 0, no_fpos);
1327 {
1328 PDF_FILE_OFFSET page_length = ftell(in_fp) - g_page_start_offset;
1329
1330 /* close page's stream */
1331 fputs("endstream\nendobj\n", in_fp);
1332 return page_length;
1333 }
1334 return 0;
1335 }
1336
1337
1338 /*****************************************************************************/
1339 /* */
1340 /* void PDFPage_Write(FILE* in_fp, char* in_str) */
1341 /* */
1342 /* Writes a string to the page's stream. */
1343 /* */
1344 /*****************************************************************************/
1345
PDFPage_Write(FILE * in_fp,char * in_str)1346 void PDFPage_Write(FILE* in_fp, char* in_str)
1347 {
1348 if (*in_str == 0)
1349 return;
1350
1351 PDFPage_Begin(in_fp); /* write page content's hdr "<< /Length >> stream"...*/
1352
1353 /* IF trying to remove redundant operations THEN */
1354 if (g_in_buffering_mode)
1355 {
1356
1357 /* if buffer will overflow then turn off buffering and flush buffer */
1358 unsigned int len = strlen(in_str);
1359 if ( (g_buffer_pos + len) > (kBufferSize-1) ) /* -1 for NULL char */
1360 {
1361 PDFPage_FlushBuffer(in_fp);
1362 PDFPage_WriteStream(in_fp, in_str);
1363 }
1364 else
1365 {
1366 strcpy(g_buffer + g_buffer_pos, in_str); /* save into buffer */
1367 g_buffer_pos += len;
1368 }
1369 }
1370 else
1371 {
1372 if (g_TJ_pending)
1373 {
1374 g_TJ_pending = FALSE; /* clear it */
1375 PDFPage_WriteStream(in_fp, ")]TJ\n");
1376 }
1377
1378 if (g_ET_pending)
1379 {
1380 g_ET_pending = FALSE; /* clear it */
1381 PDFPage_WriteStream(in_fp, "ET\n");
1382 g_valid_text_matrix = FALSE; /* Td is not allowed */
1383 }
1384
1385 PDFPage_WriteStream(in_fp, in_str);
1386 }
1387 }
1388
1389
1390 /*****************************************************************************/
1391 /* */
1392 /* PDFPage_Push(FILE* in_fp) */
1393 /* */
1394 /* Saves the current graphics state. */
1395 /* */
1396 /*****************************************************************************/
1397
PDFPage_Push(FILE * in_fp)1398 void PDFPage_Push(FILE* in_fp)
1399 {
1400
1401 /* push origin coords */
1402 {
1403 t_qsave_entry_ptr entry = (t_qsave_entry_ptr) malloc(sizeof(t_qsave_entry));
1404 if (entry == NULL)
1405 Error(48, 14, "PDFPage_Push: run out of memory", FATAL, no_fpos);
1406
1407 entry->m_page_h_origin = g_page_h_origin;
1408 entry->m_page_v_origin = g_page_v_origin;
1409 /* entry->m_page_h_scale_factor = g_page_h_scale_factor; */
1410 /* entry->m_page_v_scale_factor = g_page_v_scale_factor; */
1411 entry->m_next_entry = g_qsave_stack;
1412 g_qsave_stack = entry;
1413 }
1414
1415 /* if buffering text */
1416 if (g_in_buffering_mode)
1417 {
1418
1419 /* push current state */
1420 t_qsave_marking_entry_ptr entry =
1421 (t_qsave_marking_entry_ptr) malloc(sizeof(t_qsave_marking_entry));
1422 if (entry == NULL)
1423 Error(48, 15, "PDFPage_Push: run out of memory", FATAL, no_fpos);
1424
1425 entry->m_next_entry = g_qsave_marking_stack; /* next-to-top-of-stack */
1426 entry->m_buffer_pos = g_buffer_pos;
1427 g_qsave_marking_stack = entry; /* new TOS */
1428 /* g_in_buffering_mode = TRUE; */
1429 }
1430
1431 /* write out push op */
1432 PDFPage_Write(in_fp, "q\n");
1433 }
1434
1435
1436 /*****************************************************************************/
1437 /* */
1438 /* PDFPage_Pop(FILE* in_fp) */
1439 /* */
1440 /* Restores the current graphics state. */
1441 /* */
1442 /*****************************************************************************/
1443
PDFPage_Pop(FILE * in_fp)1444 void PDFPage_Pop(FILE* in_fp)
1445 {
1446
1447 /* pop origin coords */
1448 {
1449 t_qsave_entry_ptr entry = g_qsave_stack;
1450
1451 g_page_h_origin = entry->m_page_h_origin;
1452 g_page_v_origin = entry->m_page_v_origin;
1453 /* g_page_h_scale_factor = entry->m_page_h_scale_factor; */
1454 /* g_page_v_scale_factor = entry->m_page_v_scale_factor; */
1455
1456 g_qsave_stack = entry->m_next_entry;
1457
1458 free(entry);
1459 }
1460
1461 /* if no marks on page since last push (and thus there should be a stack) */
1462 if (g_in_buffering_mode)
1463 {
1464
1465 /* pop state: behave as if the q...Q never existed */
1466 t_qsave_marking_entry_ptr entry = g_qsave_marking_stack;
1467
1468 Assert(entry != NULL, no_fpos);
1469
1470 g_qsave_marking_stack = entry->m_next_entry; /* new TOS */
1471 g_buffer_pos = entry->m_buffer_pos;
1472 g_buffer[g_buffer_pos] = '\0'; /* chop off unwanted text */
1473
1474 free(entry);
1475 }
1476 else
1477 {
1478 Assert(g_qsave_marking_stack == NULL, no_fpos);
1479 PDFPage_Write(in_fp, "\nQ\n");
1480 }
1481 }
1482
1483
1484 /*****************************************************************************/
1485 /* */
1486 /* PDFFont_Set(FILE* in_fp, FULL_LENGTH in_font_size, */
1487 /* FULL_CHAR* in_font_name) */
1488 /* */
1489 /* Sets the font name and size for subsequent text write statements. */
1490 /* */
1491 /*****************************************************************************/
1492
PDFFont_Set(FILE * in_fp,FULL_LENGTH in_font_size,FULL_CHAR * in_short_font_name)1493 void PDFFont_Set(FILE* in_fp, FULL_LENGTH in_font_size,
1494 FULL_CHAR *in_short_font_name)
1495 {
1496 t_tempbuf str;
1497
1498 t_font_list_entry_ptr entry = PDFFont_FindListEntry_Short(in_short_font_name);
1499 if( entry == NULL )
1500 {
1501 Error(48, 42, "cannot find font entry for name %s", FATAL, no_fpos,
1502 in_short_font_name);
1503 }
1504 /* Assert(entry != NULL, no_fpos); */
1505 #ifdef USE_MATRICES
1506 sprintf(str, "%s %u Tf\n", entry->m_PDF_font_name,
1507 (int) (g_page_v_scale_factor * in_font_size));
1508 #else
1509 sprintf(str, "%s %u Tf\n", entry->m_PDF_font_name, in_font_size);
1510 /* g_text_font_size_in_ems = g_page_v_scale_factor * in_font_size; */
1511 #endif
1512
1513 #if 1
1514
1515 /* font changes can occur within BT...ET blocks, so temporarily turn off */
1516 /* g_ET_pending. I do it this way so that the qsave_marking_stack */
1517 /* optimisation can still be applied (this avoids output such as */
1518 /* "/F0 240 Tf /F0 240 Tf /F1 600 Tf" and instead produces "") */
1519 if (g_TJ_pending)
1520 {
1521 g_TJ_pending = FALSE; /* clear it */
1522 PDFPage_WriteStream(in_fp, ")]TJ\n");
1523 }
1524
1525 {
1526 BOOLEAN cur_ET_pending = g_ET_pending;
1527
1528 g_ET_pending = FALSE; /* clear it */
1529 PDFPage_Write(in_fp, str);
1530 g_ET_pending = cur_ET_pending; /* restore it */
1531 }
1532 #else
1533 /* font changes can occur within BT...ET blocks, so bypass PDFPage_Write() */
1534 PDFPage_WriteStream(in_fp, str);
1535 #endif
1536 entry->m_in_use = TRUE;
1537 g_page_uses_fonts = TRUE;
1538 }
1539
1540
1541 /*****************************************************************************/
1542 /* */
1543 /* void PDFText_RMove(FILE* in_fp, int hdelta, int vdelta) */
1544 /* */
1545 /* Offsets text pen by the given offsets. */
1546 /* */
1547 /*****************************************************************************/
1548
1549 /* ***
1550 void PDFText_RMove(FILE* in_fp, int hdelta, int vdelta)
1551 {
1552 t_tempbuf str;
1553
1554 g_tx_hpos += hdelta;
1555 g_tx_vpos += vdelta;
1556 #if 1
1557 sprintf(str, "ET\n1 0 0 1 %d %d cm\nBT\n", hdelta, vdelta);
1558 #else
1559 sprintf(str, "1 0 0 1 %d %d Tm\n", g_tx_hpos, g_tx_vpos);
1560 #endif
1561 PDFPage_Write(in_fp, str);
1562 }
1563 *** */
1564
1565
1566 /*****************************************************************************/
1567 /* */
1568 /* void PDFText_MoveTo(FILE* in_fp, int hpos, int vpos) */
1569 /* */
1570 /* Move text pen to the given coords. */
1571 /* */
1572 /*****************************************************************************/
1573
1574 /* ***
1575 static void PDFText_MoveTo(FILE* in_fp, int hpos, int vpos)
1576 {
1577 g_tx_hpos = 0;
1578 g_tx_vpos = 0;
1579 #if 1
1580 PDFText_RMove(in_fp, hpos, vpos);
1581 #else
1582 {
1583 t_tempbuf str;
1584 sprintf(str, "1 0 0 1 %d %d Tm\n", hpos, vpos);
1585 PDFPage_Write(in_fp, str);
1586 }
1587 #endif
1588 }
1589 *** */
1590
1591
1592 /*****************************************************************************/
1593 /* */
1594 /* void PDFText_OpenString(FILE* in_fp) */
1595 /* */
1596 /* Open TJ block */
1597 /* */
1598 /*****************************************************************************/
1599
PDFText_OpenString(FILE * in_fp)1600 static void PDFText_OpenString(FILE* in_fp)
1601 {
1602 if (g_TJ_pending)
1603 g_TJ_pending = FALSE; /* clear it */
1604 else
1605 PDFPage_Write(in_fp, "[(");
1606 }
1607
1608
1609 /*****************************************************************************/
1610 /* */
1611 /* void PDFText_MoveToXYAndOpen(FILE* in_fp, int hpos, int vpos) */
1612 /* */
1613 /* Move text pen to the given coords. */
1614 /* */
1615 /*****************************************************************************/
1616
PDFText_MoveToXYAndOpen(FILE * in_fp,int hpos,int vpos)1617 static void PDFText_MoveToXYAndOpen(FILE* in_fp, int hpos, int vpos)
1618 {
1619 #if 1
1620 t_tempbuf str;
1621 sprintf(str, "1 0 0 1 %d %d Tm\n", hpos, vpos);
1622 PDFPage_Write(in_fp, str);
1623 #else
1624 PDFText_MoveTo(in_fp, hpos, vpos);
1625 #endif
1626 PDFText_OpenString(in_fp);
1627 }
1628
1629
1630 /*****************************************************************************/
1631 /* */
1632 /* void PDFText_MoveToXAndOpen(FILE* in_fp, int hpos, int vpos) */
1633 /* */
1634 /* Move text pen to the given coords. */
1635 /* */
1636 /*****************************************************************************/
1637
PDFText_MoveToXAndOpen(FILE * in_fp,int hpos)1638 static void PDFText_MoveToXAndOpen(FILE* in_fp, int hpos)
1639 {
1640 #if 1
1641 t_tempbuf str;
1642 sprintf(str, "%d 0 Td\n", hpos);
1643 PDFPage_Write(in_fp, str);
1644 #else
1645 PDFText_MoveTo(in_fp, hpos, vpos);
1646 #endif
1647 PDFText_OpenString(in_fp);
1648 }
1649
1650
1651 /*****************************************************************************/
1652 /* */
1653 /* void PDFText_OpenBT(FILE* in_fp) */
1654 /* */
1655 /* Opens a text object at the given coords. */
1656 /* */
1657 /*****************************************************************************/
1658
PDFText_OpenBT(FILE * in_fp)1659 static void PDFText_OpenBT(FILE* in_fp)
1660 {
1661 PDFPage_FlushBuffer(in_fp); /* about to mark page: flush buffered PDF */
1662
1663 g_page_has_text = TRUE;
1664
1665 if (g_TJ_pending)
1666 {
1667 g_TJ_pending = FALSE; /* clear it */
1668 PDFPage_WriteStream(in_fp, ")]TJ\n");
1669 }
1670
1671 if (g_ET_pending)
1672 g_ET_pending = FALSE; /* clear it */
1673 else
1674 {
1675 PDFPage_Write(in_fp, "BT\n");
1676 g_valid_text_matrix = TRUE; /* Td is allowed */
1677 }
1678 }
1679
1680
1681 /*****************************************************************************/
1682 /* */
1683 /* void PDFText_OpenXY(FILE* in_fp, int hpos, int vpos) */
1684 /* */
1685 /* Opens a text object at the given coords. */
1686 /* */
1687 /*****************************************************************************/
1688
PDFText_OpenXY(FILE * in_fp,int hpos,int vpos)1689 void PDFText_OpenXY(FILE* in_fp, int hpos, int vpos)
1690 {
1691 PDFText_OpenBT(in_fp);
1692 PDFText_MoveToXYAndOpen(in_fp, hpos, vpos);
1693 }
1694
1695
1696 /*****************************************************************************/
1697 /* */
1698 /* void PDFText_OpenX(FILE* in_fp, int hpos) */
1699 /* */
1700 /* Opens a text object at the given coords. */
1701 /* */
1702 /*****************************************************************************/
1703
PDFText_OpenX(FILE * in_fp,int hpos)1704 void PDFText_OpenX(FILE* in_fp, int hpos)
1705 {
1706 PDFText_OpenBT(in_fp);
1707 PDFText_MoveToXAndOpen(in_fp, hpos);
1708 }
1709
1710
1711 /*****************************************************************************/
1712 /* */
1713 /* void PDFText_Open(FILE* in_fp) */
1714 /* */
1715 /* Opens a text object. */
1716 /* */
1717 /*****************************************************************************/
1718
PDFText_Open(FILE * in_fp)1719 void PDFText_Open(FILE* in_fp)
1720 {
1721 if (g_TJ_pending)
1722 {
1723 g_TJ_pending = FALSE; /* clear it */
1724 Assert(g_ET_pending == TRUE, no_fpos);
1725 g_ET_pending = FALSE; /* clear it */
1726 }
1727 else
1728 {
1729 PDFText_OpenBT(in_fp);
1730 PDFText_OpenString(in_fp);
1731 }
1732 }
1733
1734
1735 /*****************************************************************************/
1736 /* */
1737 /* void PDFText_Kern(FILE* in_fp, int in_kern) */
1738 /* */
1739 /* Apply kerning to a text string. */
1740 /* */
1741 /* Note: in_kern is in 1/1000 of font size */
1742 /* */
1743 /*****************************************************************************/
1744
PDFText_Kern(FILE * in_fp,int in_kern)1745 void PDFText_Kern(FILE* in_fp, int in_kern)
1746 {
1747 t_tempbuf str;
1748
1749 /* sprintf(str, ")%d(", -in_kern * 1000 / g_text_font_size_in_ems); */
1750 sprintf(str, ")%d(", -in_kern);
1751 PDFPage_Write(in_fp, str);
1752 }
1753
1754
1755 /*****************************************************************************/
1756 /* */
1757 /* void PDFText_Close(FILE* in_fp) */
1758 /* */
1759 /* Closes a previously opened text object. */
1760 /* */
1761 /*****************************************************************************/
1762
PDFText_Close(FILE * in_fp)1763 void PDFText_Close(FILE* in_fp)
1764 {
1765 /* PDFPage_Begin(in_fp); - shouldn't be needed */
1766 Assert(g_page_contents_obj_num != 0, no_fpos);
1767
1768 g_TJ_pending = TRUE;
1769 /* PDFPage_Write(in_fp, ")] TJ\n"); */
1770 g_ET_pending = TRUE;
1771 }
1772
1773
1774 #ifdef USE_MATRICES
1775
1776 /*****************************************************************************/
1777 /* */
1778 /* void PDF_Matrix_XY(double* in_out_x, double* in_out_y) */
1779 /* */
1780 /* Returns (x, y) after applying the current matrix: */
1781 /* */
1782 /* [ a b 0 ] */
1783 /* [ x y 1 ] x [ c d 0 ] = [ ax+cy+e bx+dy+f 1 ] */
1784 /* [ e f 1 ] */
1785 /* */
1786 /*****************************************************************************/
1787
PDF_Matrix_XY(double * in_out_x,double * in_out_y)1788 static void PDF_Matrix_XY(double* in_out_x, double* in_out_y)
1789 {
1790 double result_x, result_y;
1791
1792 result_x = g_cur_matrix[0] * *in_out_x + g_cur_matrix[3] * *in_out_y +
1793 g_cur_matrix[6];
1794 result_y = g_cur_matrix[1] * *in_out_x + g_cur_matrix[4] * *in_out_y +
1795 g_cur_matrix[7];
1796 *in_out_x = result_x;
1797 *in_out_y = result_y;
1798 }
1799
1800
1801 /*****************************************************************************/
1802 /* */
1803 /* PDF_Matrix_Mul(t_matrix in_left, t_matrix in_right, t_matrix out_result) */
1804 /* */
1805 /* Multiplies the given matrices. */
1806 /* */
1807 /* [ a b 0 ] [ g h 0 ] [ ag+bi ah+bj 0 ] */
1808 /* [ c d 0 ] x [ i j 0 ] = [ cg+di ch+dj 0 ] */
1809 /* [ e f 1 ] [ k l 1 ] [ eg+fi+k eh+fj+l 1 ] */
1810 /* */
1811 /*****************************************************************************/
1812
PDF_Matrix_Mul(t_matrix in_left,t_matrix in_right,t_matrix out_result)1813 static void PDF_Matrix_Mul(t_matrix in_left, t_matrix in_right,
1814 t_matrix out_result)
1815 {
1816 t_matrix result;
1817 result[0] = in_left[0] * in_right[0] + in_left[1] * in_right[3];
1818 result[1] = in_left[0] * in_right[1] + in_left[1] * in_right[4];
1819 result[2] = 0;
1820 result[3] = in_left[3] * in_right[0] + in_left[4] * in_right[3];
1821 result[4] = in_left[3] * in_right[1] + in_left[4] * in_right[4];
1822 result[5] = 0;
1823 result[6] = in_left[6] * in_right[0] + in_left[7] * in_right[3] + in_right[6];
1824 result[7] = in_left[6] * in_right[1] + in_left[7] * in_right[4] + in_right[7];
1825 result[8] = 1;
1826
1827 memcpy(out_result, result, sizeof(t_matrix));
1828 }
1829 #endif
1830
1831
1832 /*****************************************************************************/
1833 /* */
1834 /* void PDFPage_Scale(float in_h_scale_factor, float in_v_scale_factor) */
1835 /* */
1836 /* Changes CTM by scale factor: */
1837 /* */
1838 /* [ sh 0 0 ] */
1839 /* [ 0 sv 0 ] */
1840 /* [ 0 0 1 ] */
1841 /* */
1842 /*****************************************************************************/
1843
PDFPage_Scale(FILE * in_fp,float in_h_scale_factor,float in_v_scale_factor)1844 void PDFPage_Scale(FILE* in_fp, float in_h_scale_factor, float in_v_scale_factor)
1845 {
1846 #ifdef USE_MATRICES
1847 t_matrix m = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
1848 m[0] = in_h_scale_factor;
1849 m[4] = in_v_scale_factor;
1850
1851 PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
1852 #else
1853 t_tempbuf str;
1854
1855 sprintf(str, "%.2f 0 0 %.2f 0 0 cm\n", in_h_scale_factor, in_v_scale_factor);
1856 PDFPage_Write(in_fp, str);
1857 #endif
1858 g_page_h_scale_factor *= in_h_scale_factor;
1859 g_page_v_scale_factor *= in_v_scale_factor;
1860 }
1861
1862
1863 /*****************************************************************************/
1864 /* */
1865 /* void PDFPage_Rotate(FILE* in_fp, float in_angle_in_radians) */
1866 /* */
1867 /* Changes CTM by rotation factor. */
1868 /* */
1869 /* [ cos a sin a 0 ] */
1870 /* [ -sin a cos a 0 ] */
1871 /* [ 0 0 1 ] */
1872 /* */
1873 /*****************************************************************************/
1874
PDFPage_Rotate(FILE * in_fp,float in_angle_in_radians)1875 void PDFPage_Rotate(FILE* in_fp, float in_angle_in_radians)
1876 {
1877 float cos_radians = cos(in_angle_in_radians);
1878 float sin_radians = sin(in_angle_in_radians);
1879 #ifdef USE_MATRICES
1880 t_matrix m = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
1881 m[0] = m[4] = cos_radians;
1882 m[1] = sin_radians;
1883 m[3] = -sin_radians;
1884 PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
1885 #else
1886 t_tempbuf str;
1887
1888 sprintf(str, "%.2f %.2f %.2f %.2f 0 0 cm\n", cos_radians, sin_radians,
1889 -sin_radians, cos_radians);
1890 PDFPage_Write(in_fp, str);
1891 #endif
1892 }
1893
1894
1895 /*****************************************************************************/
1896 /* */
1897 /* void PDFPage_Translate(FILE* in_fp, float in_delta_h, float in_delta_v) */
1898 /* */
1899 /* Changes CTM by translation: */
1900 /* */
1901 /* [ 1 0 0 ] */
1902 /* [ 0 1 0 ] */
1903 /* [ dh dv 1 ] */
1904 /* */
1905 /*****************************************************************************/
1906
PDFPage_Translate(FILE * in_fp,float in_delta_h,float in_delta_v)1907 void PDFPage_Translate(FILE* in_fp, float in_delta_h, float in_delta_v)
1908 {
1909 #ifdef USE_MATRICES
1910 t_matrix m = { 1, 0, 0, 0, 1, 0, 0, 0, 1 };
1911 m[6] = in_delta_h;
1912 m[7] = in_delta_v;
1913 PDF_Matrix_Mul(m, g_cur_matrix, g_cur_matrix);
1914 #else
1915 t_tempbuf str;
1916
1917 sprintf(str, "1 0 0 1 %.2f %.2f cm\n", in_delta_h, in_delta_v);
1918 PDFPage_Write(in_fp, str);
1919 #endif
1920 g_page_h_origin += in_delta_h;
1921 g_page_v_origin += in_delta_v;
1922 }
1923
1924
1925 /*****************************************************************************/
1926 /* */
1927 /* void PDFTargetAnnot_New(FULL_CHAR* in_annot_name, ...) */
1928 /* */
1929 /* Create a new target annotation entry. */
1930 /* */
1931 /*****************************************************************************/
1932
PDFTargetAnnot_New(FULL_CHAR * in_annot_name,unsigned int in_annot_name_length,int in_ll_x,int in_ll_y,int in_ur_x,int in_ur_y,BOOLEAN in_for_export)1933 static void PDFTargetAnnot_New(FULL_CHAR* in_annot_name,
1934 unsigned int in_annot_name_length, int in_ll_x, int in_ll_y, int in_ur_x,
1935 int in_ur_y, BOOLEAN in_for_export)
1936 {
1937 t_target_annot_entry_ptr entry =
1938 (t_target_annot_entry_ptr) malloc(sizeof(t_target_annot_entry));
1939
1940 if (entry == NULL)
1941 Error(48, 16, "PDFTargetAnnot_New: run out of memory", FATAL, no_fpos);
1942
1943 entry->m_name = (FULL_CHAR*) malloc(in_annot_name_length + 1);
1944 if (entry->m_name == NULL)
1945 Error(48, 17, "PDFTargetAnnot_New: run out of memory", FATAL, no_fpos);
1946 memcpy(entry->m_name, in_annot_name, in_annot_name_length);
1947 entry->m_name[in_annot_name_length] = '\0';
1948
1949 Assert(g_page_contents_obj_num != 0, no_fpos);
1950 entry->m_page_object_num = g_page_object_num;
1951
1952 entry->m_ll_x = in_ll_x;
1953 entry->m_ll_y = in_ll_y;
1954 entry->m_ur_x = in_ur_x;
1955 entry->m_ur_y = in_ur_y;
1956 entry->m_for_export = in_for_export;
1957
1958 entry->m_next_entry = g_target_annot_list;
1959 g_target_annot_list = entry;
1960
1961 if (in_for_export)
1962 g_has_exported_targets = in_for_export;
1963 }
1964
1965
1966 /*****************************************************************************/
1967 /* */
1968 /* t_target_annot_entry_ptr PDFTargetAnnot_Find(FULL_CHAR* in_annot_name) */
1969 /* */
1970 /* Finds an annotation. Returns NULL if not found. */
1971 /* */
1972 /*****************************************************************************/
1973
PDFTargetAnnot_Find(FULL_CHAR * in_annot_name)1974 static t_target_annot_entry_ptr PDFTargetAnnot_Find(FULL_CHAR* in_annot_name)
1975 {
1976 t_target_annot_entry_ptr entry = g_target_annot_list;
1977
1978 /* this takes O(n) time; may need re-implementing if speed is a factor */
1979 while (entry != NULL)
1980 {
1981 if (strcmp((char*) in_annot_name, (char*) entry->m_name) == 0)
1982 break;
1983 entry = entry->m_next_entry;
1984 }
1985
1986 return entry;
1987 }
1988
1989
1990 /*****************************************************************************/
1991 /* */
1992 /* PDFSourceAnnot_Write(FILE* in_fp, */
1993 /* t_source_annot_entry_ptr in_source_entry) */
1994 /* */
1995 /* Write an annot which specifies the source and target of the link. */
1996 /* */
1997 /*****************************************************************************/
1998
PDFSourceAnnot_Write(FILE * in_fp,t_source_annot_entry_ptr in_entry)1999 static void PDFSourceAnnot_Write(FILE* in_fp, t_source_annot_entry_ptr in_entry)
2000 {
2001 t_target_annot_entry_ptr target;
2002
2003 Assert(in_entry != NULL, no_fpos);
2004
2005 target = in_entry->m_target;
2006
2007 /* if it is an unresolved forward link then exit */
2008 if ( (in_entry->m_link_type == k_link_source) && (target == NULL) )
2009 return;
2010
2011 /* green light: write it out */
2012 if (g_PDF_debug)
2013 {
2014 fprintf(in_fp, "%%\n%% annotation in page object # %u to %s:\n%%\n",
2015 in_entry->m_this_page_object_num, in_entry->m_target->m_name);
2016 }
2017
2018 PDFObject_WriteObj(in_fp, in_entry->m_this_object_num);
2019 fprintf(in_fp, "<<\n/Type /Annot\n/Subtype /Link\n"
2020 /* this is what Adobe does (it's also more flexible) */
2021 "/Rect [ %d %d %d %d ]\n/Border [ 0 0 0 ]\n",
2022 /* "/BS << /Type /Border /S /U >>\n" */
2023 /* border appearance is "underline" */
2024 in_entry->m_ll_x, in_entry->m_ll_y, in_entry->m_ur_x, in_entry->m_ur_y);
2025
2026 switch (in_entry->m_link_type)
2027 {
2028 case k_link_source:
2029
2030 fprintf(in_fp, "/Dest [ ");
2031 PDFObject_WriteRef(in_fp, in_entry->m_target->m_page_object_num);
2032 switch (in_entry->m_dest_option)
2033 {
2034 case kFitNoChange:
2035
2036 fprintf(in_fp, " /XYZ null null null");
2037 /* NB NO BREAK */
2038
2039 case kFit:
2040
2041 fprintf(in_fp, " /Fit");
2042 break;
2043
2044
2045 case kFitH:
2046
2047 /* [ /FitH top ]: fit the width of the page to the window; top */
2048 /* specifies the y-coordinate of the top edge of the window */
2049 fprintf(in_fp, " /FitH %u", target->m_ur_y);
2050 break;
2051
2052
2053 case kFitV:
2054
2055 /* [ /FitV left ]: fit the height of the page to the window; */
2056 /* left specifies the x-coordinate of the left edge of the window */
2057 fprintf(in_fp, " /FitV %u", target->m_ll_x);
2058 break;
2059
2060
2061 case kFitR:
2062
2063 /* [ /FitR left bottom right top ]: fit the rectangle specified */
2064 /* by left bottom right top in the window. If the height (top - */
2065 /* bottom) and width (right - left) imply different zoom factors, */
2066 /* the numerically smaller zoom factor is used, to ensure that */
2067 /* the specified rectangle fits in the window */
2068 fprintf(in_fp, " /FitR %u %u %u %u", target->m_ll_x, target->m_ll_y,
2069 target->m_ur_x, target->m_ur_y);
2070 break;
2071
2072
2073 case kFitB:
2074
2075 /* [ /FitB ]: fit the page's bounding box to the window */
2076 fprintf(in_fp, " /FitB");
2077 break;
2078
2079
2080 case kFitBH:
2081
2082 /* [ /FitBH top ]: fit the width of the page's bounding box to */
2083 /* the window. top specifies the y-coord of top edge of window */
2084 fprintf(in_fp, " /FitBH %u", target->m_ur_y);
2085 break;
2086
2087
2088 case kFitBV:
2089
2090 /* [ /FitBV left ]: fit the height of the page' bounding box to */
2091 /* the window. left specifies the x-coordinate of the left edge */
2092 /* of the window */
2093 fprintf(in_fp, " /FitBV %u", target->m_ll_x);
2094 break;
2095
2096
2097 default:
2098
2099 Error(48, 18, "PDFSourceAnnot_Write: invalid link dest option",
2100 FATAL, no_fpos);
2101 }
2102 fprintf(in_fp, " ]\n");
2103 break;
2104
2105
2106 case k_link_external:
2107
2108 #if 1 /* required wrapper for URLs is now in the Lout libraries */
2109 fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
2110 /* <= split across lines for LONG file specs */
2111 "(%s) >>\n", in_entry->m_name, in_entry->m_file_spec);
2112 #else
2113 if (in_entry->m_file_spec[0] != '<')
2114 {
2115 /* note: destination/target is specified as a string, as is file spec */
2116 fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
2117 /* <= split across lines for LONG file specs */
2118 "(%s) >>\n", in_entry->m_name, in_entry->m_file_spec);
2119 }
2120 else
2121 {
2122 /* if file spec starts with '<' then URL, eg <http://www.adobe.com> */
2123 Assert(in_entry->m_file_spec[strlen((char*) in_entry->m_file_spec)-1]
2124 == '>', no_fpos);
2125 fprintf(in_fp, "/A << /Type /Action /S /GoToR /D (%s) /F\n"
2126 /* <= split across lines for LONG file specs */
2127 "<< /FS /URL /F (%s) >> >>\n", in_entry->m_name, in_entry->m_file_spec);
2128 }
2129 #endif
2130 break;
2131
2132
2133 case k_link_URI:
2134
2135 fprintf(in_fp, "/A << /Type /Action /S /URI /URI\n"
2136 /* <= split across lines for LONG URI's */
2137 "(%s) >>\n", in_entry->m_name);
2138 break;
2139
2140
2141 case k_link_target:
2142 case k_link_target_for_export:
2143 case kNumberOfLinkKeywords:
2144
2145 break;
2146 }
2147
2148 fprintf(in_fp, ">>\nendobj\n");
2149 in_entry->m_written_to_PDF_file = TRUE;
2150 }
2151
2152
2153 /*****************************************************************************/
2154 /* */
2155 /* void PDFSourceAnnot_New(FULL_CHAR* in_annot_name) */
2156 /* */
2157 /* Create an entry in the g_source_annot_list which links to in_annot_name. */
2158 /* */
2159 /*****************************************************************************/
2160
2161 static t_source_annot_entry_ptr
PDFSourceAnnot_New(PDF_LINK_KEYWORD in_link_type,FULL_CHAR * in_annot_name,unsigned int in_annot_name_length,int in_ll_x,int in_ll_y,int in_ur_x,int in_ur_y,PDF_LINK_DEST_OPTION in_link_dest_option)2162 PDFSourceAnnot_New(PDF_LINK_KEYWORD in_link_type, FULL_CHAR* in_annot_name,
2163 unsigned int in_annot_name_length, int in_ll_x, int in_ll_y, int in_ur_x,
2164 int in_ur_y, PDF_LINK_DEST_OPTION in_link_dest_option)
2165 {
2166 t_target_annot_entry_ptr target = NULL;
2167 t_source_annot_entry_ptr entry =
2168 (t_source_annot_entry_ptr) malloc(sizeof(t_source_annot_entry));
2169
2170 if (entry == NULL)
2171 Error(48, 19, "PDFSourceAnnot_New: run out of memory", FATAL, no_fpos);
2172
2173 entry->m_ll_x = in_ll_x;
2174 entry->m_ll_y = in_ll_y;
2175 entry->m_ur_x = in_ur_x;
2176 entry->m_ur_y = in_ur_y;
2177
2178 entry->m_this_object_num = PDFObject_New(/* in_fp */);
2179 entry->m_this_page_object_num = g_page_object_num;
2180 entry->m_link_type = in_link_type;
2181 Assert((in_link_dest_option >= kFitNoChange) &&
2182 (in_link_dest_option <= kFitBV), no_fpos);
2183 entry->m_dest_option = in_link_dest_option;
2184 entry->m_file_spec = NULL;
2185 entry->m_written_to_PDF_file = FALSE;
2186
2187 if (in_link_type == k_link_source)
2188 target = PDFTargetAnnot_Find(in_annot_name);
2189
2190 if (target != NULL)
2191 {
2192 entry->m_target = target;
2193 entry->m_name = NULL;
2194 }
2195 else
2196 {
2197 entry->m_target = NULL; /* fwd link */
2198 entry->m_name = (FULL_CHAR*) malloc(in_annot_name_length + 1);
2199 if (entry->m_name == NULL)
2200 Error(48, 20, "PDFSourceAnnot_New: run out of memory", FATAL, no_fpos);
2201 memcpy(entry->m_name, in_annot_name, in_annot_name_length);
2202 entry->m_name[in_annot_name_length] = '\0';
2203 }
2204
2205 entry->m_next_entry = g_source_annot_list;
2206 g_source_annot_list = entry;
2207
2208 return entry;
2209 }
2210
2211
2212 /*****************************************************************************/
2213 /* */
2214 /* PDFSourceAnnot_Dispose(t_source_annot_entry_ptr in_source_annot) */
2215 /* */
2216 /* Dispose of a source annot entry; returns the next entry in the list. */
2217 /* */
2218 /*****************************************************************************/
2219
2220 static t_source_annot_entry_ptr
PDFSourceAnnot_Dispose(t_source_annot_entry_ptr in_source_annot)2221 PDFSourceAnnot_Dispose(t_source_annot_entry_ptr in_source_annot)
2222 {
2223 t_source_annot_entry_ptr next_entry = in_source_annot->m_next_entry;
2224
2225 if (in_source_annot->m_name != NULL)
2226 free(in_source_annot->m_name);
2227 if (in_source_annot->m_file_spec != NULL)
2228 free(in_source_annot->m_file_spec);
2229 free(in_source_annot);
2230 return next_entry;
2231 }
2232
2233
2234 /*****************************************************************************/
2235 /* */
2236 /* float PDFPage_GetFloat(FULL_CHAR* in_str) */
2237 /* */
2238 /* Outputs an appropriate PDF string for drawing a graphic element. */
2239 /* */
2240 /*****************************************************************************/
2241
PDFPage_GetFloat(FULL_CHAR * in_str,float * out_value)2242 static FULL_CHAR *PDFPage_GetFloat(FULL_CHAR* in_str, float* out_value)
2243 {
2244 if (sscanf((char*) in_str, "%f", out_value) == 1)
2245 {
2246 /* skip (presumed) floating point number: [ ]*[+|-][0-9.]* */
2247 while (isspace(*in_str))
2248 in_str++;
2249 if ( (*in_str == '-') || (*in_str == '+') )
2250 in_str++;
2251 while (isdigit(*in_str) || (*in_str == '.'))
2252 in_str++;
2253 }
2254 else Error(48, 21, "PDFPage_GetFloat: unable to evaluate number for Lout graphic keyword processing",
2255 FATAL, no_fpos);
2256 return in_str;
2257 }
2258
2259
2260 /*****************************************************************************/
2261 /* */
2262 /* int PDFKeyword_Find(int in_number_of_array_elements, */
2263 /* char* in_keyword_array[], FULL_CHAR* in_str) */
2264 /* */
2265 /* Return index into keyword array if an element matches the given string. */
2266 /* Returns -1 if not found. */
2267 /* */
2268 /*****************************************************************************/
2269
PDFKeyword_Find(int in_number_of_array_elements,char * in_keyword_array[],FULL_CHAR * in_str)2270 static int PDFKeyword_Find(int in_number_of_array_elements,
2271 char* in_keyword_array[], FULL_CHAR* in_str)
2272 {
2273 unsigned int i;
2274
2275 /* look for keyword */
2276 for (i = 0; i < in_number_of_array_elements; i++)
2277 {
2278 unsigned int len = strlen(in_keyword_array[i]);
2279
2280 if (memcmp(in_keyword_array[i], in_str, len) == 0)
2281 break;
2282 }
2283
2284 return (i < in_number_of_array_elements) ? i : -1;
2285 }
2286
2287
2288 /*****************************************************************************/
2289 /* */
2290 /* FULL_CHAR *PDFPage_ProcessGraphicsKeyword(FULL_CHAR* charPtr, int i) */
2291 /* */
2292 /* Processes a link keyword. */
2293 /* */
2294 /*****************************************************************************/
2295
2296 #if 0 /* this function is no longer used */
2297
2298 static FULL_CHAR *PDFPage_ProcessGraphicsKeyword(FULL_CHAR* charPtr, int i,
2299 char** strPtr)
2300 {
2301 float value;
2302
2303 /* if need be, expand this later to a full blown expression evaluator (ugh) */
2304 switch (*charPtr)
2305 {
2306 case '+':
2307
2308 Assert(FALSE, no_fpos);
2309 charPtr = PDFPage_GetFloat(++charPtr, &value);
2310 sprintf(*strPtr, "%.2f", g_graphics_vars[i] + value);
2311 break;
2312
2313
2314 case '-':
2315
2316 Assert(FALSE, no_fpos);
2317 charPtr = PDFPage_GetFloat(++charPtr, &value);
2318 sprintf(*strPtr, "%.2f", g_graphics_vars[i] - value);
2319 break;
2320
2321
2322 case '*':
2323
2324 Assert(FALSE, no_fpos);
2325 charPtr = PDFPage_GetFloat(++charPtr, &value);
2326 sprintf(*strPtr, "%.2f", g_graphics_vars[i] * value);
2327 break;
2328
2329
2330 case '/':
2331
2332 Assert(FALSE, no_fpos);
2333 charPtr = PDFPage_GetFloat(++charPtr, &value);
2334 Assert(value != 0, no_fpos); /* not great since value is a float... */
2335 sprintf(*strPtr, "%.2f", g_graphics_vars[i] / value);
2336 break;
2337
2338
2339 default:
2340
2341 sprintf(*strPtr, "%d", g_graphics_vars[i]);
2342 break;
2343 }
2344 *strPtr += strlen(*strPtr);
2345 return charPtr;
2346 }
2347
2348 #endif
2349
2350
2351 /*****************************************************************************/
2352 /* */
2353 /* void PDFPage_ProcessLinkKeyword(void) */
2354 /* */
2355 /* Processes a link keyword. */
2356 /* */
2357 /*****************************************************************************/
2358
PDFPage_ProcessLinkKeyword(void)2359 static void PDFPage_ProcessLinkKeyword(void)
2360 {
2361 FULL_CHAR* charPtr = (FULL_CHAR*) g_link;
2362 PDF_LINK_KEYWORD keyword = g_link_keyword;
2363 unsigned int link_len = 0;
2364 FULL_CHAR* link_name = charPtr;
2365
2366 /* scan for name of link; scan until end of string or until ' __' reached */
2367 /* (scan for name of link; scan until end of string or whitespace reached) */
2368 #if 1
2369
2370 FULL_CHAR* parm = NULL;
2371 debug1(DPD, D, "PDFPage_ProcessLinkKeyword(g_link = %s", g_link);
2372
2373 while ((*charPtr != '\0') &&
2374 !(isspace(charPtr[0]) && (charPtr[1] == '_') && (charPtr[2] == '_')))
2375 {
2376 link_len++;
2377 charPtr++;
2378 }
2379
2380 if (*charPtr != '\0')
2381 parm = ++charPtr;
2382
2383 while (*charPtr != '\0')
2384 charPtr++;
2385 #else
2386 while ((*charPtr != '\0') && ! isspace(*charPtr))
2387 {
2388 link_len++;
2389 charPtr++;
2390 }
2391 #endif
2392 if (link_len == 0)
2393 Error(48, 22, "PDFPage_ProcessLinkKeyword: empty link-name / URI; ignored.",
2394 WARN, no_fpos);
2395 else
2396 {
2397
2398 /* see documentaton for @Graphic for the meaning of the x, y parms */
2399 /* translate the object's box into PDF's default user space */
2400 int ll_x = g_page_h_origin * g_page_h_scale_factor;
2401 int ll_y = g_page_v_origin * g_page_v_scale_factor;
2402 int ur_x = (g_page_h_origin + g_graphics_vars[k_xsize]) * g_page_h_scale_factor;
2403 int ur_y = (g_page_v_origin + g_graphics_vars[k_ysize]) * g_page_v_scale_factor;
2404
2405 /* remove this block later (it produces debugging output): */
2406 #if 0
2407 {
2408 t_tempbuf strz = "PDFPage_ProcessLinkKeyword: ";
2409 switch (keyword)
2410 {
2411 case k_link_source:
2412
2413 strcat(strz, "link_source =");
2414 break;
2415
2416
2417 case k_link_external:
2418
2419 strcat(strz, "link_external =");
2420 break;
2421
2422
2423 case k_link_URI:
2424
2425 strcat(strz, "link_URI =");
2426 break;
2427
2428
2429 case k_link_target:
2430
2431 strcat(strz, "link_target =");
2432 break;
2433
2434
2435 case k_link_target_for_export:
2436
2437 strcat(strz, "link_target_for_export=");
2438 break;
2439 }
2440 strcat(strz, (char*) link_name);
2441 fprintf(stderr, "%s", strz);
2442 /* Err or(48, 23, strz, WARN, no_fpos); */
2443 }
2444 #endif
2445 switch (keyword)
2446 {
2447 case k_link_source:
2448
2449 {
2450 int j;
2451
2452 /* if there is a dest option specified then get it */
2453 if (parm != NULL)
2454 {
2455 j = PDFKeyword_Find(kNumberOfDestLinkOptions, g_dest_link_options,
2456 charPtr);
2457 if (j >= 0) /* note signed comparison */
2458 charPtr += strlen(g_dest_link_options[j]);
2459 else
2460 {
2461 j = (int) kFitNoChange; /* default */
2462 /* must consume the rest of the string */
2463 while (*charPtr != '\0')
2464 charPtr++;
2465 link_len = charPtr - link_name;
2466 }
2467 }
2468 else
2469 j = (int) kFitNoChange; /* default */
2470
2471 PDFSourceAnnot_New(keyword, link_name, link_len,
2472 ll_x, ll_y, ur_x, ur_y, (PDF_LINK_DEST_OPTION) j);
2473 break;
2474 }
2475
2476
2477 case k_link_external:
2478 case k_link_URI:
2479
2480 {
2481 t_source_annot_entry_ptr source;
2482 source = PDFSourceAnnot_New(keyword, link_name, link_len, ll_x,
2483 ll_y, ur_x, ur_y, (PDF_LINK_DEST_OPTION) 0 /* doesn't matter */);
2484 if (keyword == k_link_external)
2485 {
2486 int j;
2487
2488 link_len = 0;
2489
2490 if (parm != NULL)
2491 {
2492 j = PDFKeyword_Find(1, g_external_file_spec_keyword, parm);
2493 if (j == 0)
2494 {
2495 parm += strlen(g_external_file_spec_keyword[0]);
2496 link_len = strlen((char*) parm);
2497 #if 0
2498 /* scan for name of file spec; scan until end of string or */
2499 /* until whitespace reached */
2500 link_name = charPtr;
2501 while ((*charPtr != '\0') && ! isspace(*charPtr))
2502 {
2503 link_len++;
2504 charPtr++;
2505 }
2506 #endif
2507 }
2508 }
2509
2510 if (link_len == 0)
2511 Error(48, 24, "PDFPage_ProcessLinkKeyword: empty file spec",
2512 FATAL, no_fpos);
2513 else
2514 {
2515 source->m_file_spec = (FULL_CHAR*) malloc(link_len + 1);
2516 if (source->m_file_spec == NULL)
2517 Error(48, 25, "PDFPage_ProcessLinkKeyword: out of memory",
2518 FATAL, no_fpos);
2519 #if 1
2520 strcpy((char*) source->m_file_spec, (char*) parm);
2521 #else
2522 memcpy(source->m_file_spec, link_name, link_len);
2523 source->m_file_spec[link_len] = '\0';
2524 #endif
2525 }
2526 }
2527 break;
2528 }
2529
2530
2531 case k_link_target:
2532 case k_link_target_for_export:
2533
2534 PDFTargetAnnot_New(link_name, link_len, ll_x, ll_y, ur_x, ur_y,
2535 keyword == k_link_target_for_export);
2536 break;
2537
2538
2539 case kNumberOfLinkKeywords:
2540 break;
2541
2542 } /* switch */
2543 } /* else */
2544
2545 debug0(DPD, D, "PDFPage_ProcessLinkKeyword returning");
2546 /* return charPtr; */
2547 } /* PDFPage_ProcessLinkKeyword */
2548
2549
2550 /*****************************************************************************/
2551 /* */
2552 /* FULL_CHAR* PDFPage_ProcessDocInfoKeyword(FULL_CHAR* charPtr, int i) */
2553 /* */
2554 /* Processes a document info keyword. */
2555 /* */
2556 /*****************************************************************************/
2557
PDFPage_ProcessDocInfoKeyword(FULL_CHAR * charPtr,int i)2558 static FULL_CHAR *PDFPage_ProcessDocInfoKeyword(FULL_CHAR* charPtr, int i)
2559 {
2560 switch (i)
2561 {
2562 case k_author:
2563
2564 if (g_doc_author != NULL)
2565 free(g_doc_author);
2566 g_doc_author = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
2567 if (g_doc_author == NULL)
2568 Error(48, 26, "PDFPage_ProcessDocInfoKeyword: no memory for __author=",
2569 WARN, no_fpos);
2570 else
2571 strcpy((char*) g_doc_author, (char*) charPtr);
2572 break;
2573
2574
2575 case k_title:
2576
2577 if (g_doc_title != NULL)
2578 free(g_doc_title);
2579 g_doc_title = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
2580 if (g_doc_title == NULL)
2581 Error(48, 27, "PDFPage_ProcessDocInfoKeyword: no memory for __title=",
2582 WARN, no_fpos);
2583 else
2584 strcpy((char*) g_doc_title, (char*) charPtr);
2585 break;
2586
2587
2588 case k_subject:
2589
2590 if (g_doc_subject != NULL)
2591 free(g_doc_subject);
2592 g_doc_subject = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
2593 if (g_doc_subject == NULL)
2594 Error(47, 28, "PDFPage_ProcessDocInfoKeyword: no memory for __subject=",
2595 WARN, no_fpos);
2596 else
2597 strcpy((char*) g_doc_subject, (char*) charPtr);
2598 break;
2599
2600
2601 case k_keywords:
2602 if (g_doc_keywords != NULL)
2603 free(g_doc_keywords);
2604 g_doc_keywords = (FULL_CHAR*) malloc(strlen((char*) charPtr) + 1);
2605 if (g_doc_keywords == NULL)
2606 Error(48, 29, "PDFPage_ProcessDocInfoKeyword: no memory for __keywords=",
2607 WARN, no_fpos);
2608 else
2609 strcpy((char*) g_doc_keywords, (char*) charPtr);
2610 break;
2611
2612 }
2613 return (charPtr + strlen((char*) charPtr));
2614 }
2615
2616
2617 /*****************************************************************************/
2618 /* */
2619 /* void PDFPage_EvalExpr(char* inExpr) */
2620 /* */
2621 /* Evaluate collected expression in the given expression buffer. */
2622 /* */
2623 /*****************************************************************************/
2624
PDFPage_EvalExpr(char * inExpr,float * outValue)2625 static char *PDFPage_EvalExpr(char* inExpr, float* outValue)
2626 {
2627 int i;
2628 char* chp = inExpr;
2629
2630 while (isspace( (int) *chp)) /* ignore leading white space */
2631 chp++;
2632
2633 while (*chp == '_')
2634 chp++;
2635
2636 while (*chp == '+') /* ignore unary + */
2637 chp++;
2638
2639 if (isdigit((int) *chp) || (*chp == '.'))
2640 {
2641 chp = (char*) PDFPage_GetFloat((FULL_CHAR*) chp, outValue);
2642 }
2643 else if (*chp == '-') /* handle unary negation */
2644 {
2645 float val;
2646
2647 chp = PDFPage_EvalExpr(++chp, &val);
2648 *outValue = -val;
2649 }
2650 else
2651 {
2652 i = PDFKeyword_Find(kNumberOfArithmeticKeywords,
2653 g_arithmetic_keywords, (FULL_CHAR*) chp);
2654 if (i >= 0)
2655 {
2656 float val1, val2;
2657
2658 chp += strlen(g_arithmetic_keywords[i]);
2659
2660 while (isspace( (int) *chp))
2661 chp++;
2662 if (*chp != '(')
2663 Error(48, 30, "PDFPage_EvalExpr: '(' expected", FATAL, no_fpos);
2664 chp = PDFPage_EvalExpr(++chp, &val1);
2665 if ( (i <= k_div) || (i == k_pick) )
2666 {
2667 int count;
2668
2669 if (i == k_pick)
2670 {
2671 count = floor(val1);
2672 Assert(count != 0, no_fpos);
2673 }
2674 else
2675 count = 1;
2676
2677 if (*chp != ',')
2678 Error(48, 31, "PDFPage_EvalExpr: ',' expected", FATAL, no_fpos);
2679
2680 do {
2681 chp = PDFPage_EvalExpr(++chp, &val2);
2682 if ((count != 1) && (*chp == ','))
2683 ++chp;
2684 } while (--count != 0);
2685 }
2686 if (*chp != ')')
2687 Error(48, 32, "PDFPage_EvalExpr: ')' expected", FATAL, no_fpos);
2688 ++chp;
2689 switch (i)
2690 {
2691
2692 case k_add:
2693 *outValue = val1 + val2;
2694 break;
2695
2696
2697 case k_sub:
2698
2699 *outValue = val1 - val2;
2700 break;
2701
2702
2703 case k_mul:
2704
2705 *outValue = val1 * val2;
2706 break;
2707
2708
2709 case k_div:
2710
2711 Assert(val2 != 0, no_fpos); /* not great since value is a float... */
2712 *outValue = val1 / val2;
2713 break;
2714
2715
2716 case k_sin:
2717
2718 *outValue = sin((double) val1 * (double) M_PI / (double) 180.0);
2719 break;
2720
2721
2722 case k_cos:
2723
2724 *outValue = cos((double) val1 * (double) M_PI / (double) 180.0);
2725 break;
2726
2727
2728 case k_pick:
2729
2730 *outValue = val2;
2731 break;
2732 }
2733 }
2734 else
2735 {
2736 i = PDFKeyword_Find(kNumberOfGraphicsKeywords, g_graphic_keywords,
2737 (FULL_CHAR*) chp);
2738 if (i >= 0)
2739 {
2740 chp += strlen(g_graphic_keywords[i]);
2741 *outValue = g_graphics_vars[i];
2742 }
2743 else
2744 {
2745 i = PDFKeyword_Find(kNumberOfUnitKeywords, g_unit_keywords, (FULL_CHAR*) chp);
2746 if (i >= 0)
2747 {
2748 chp += strlen(g_unit_keywords[i]);
2749 *outValue = g_units[i];
2750 }
2751 else
2752 {
2753 Error(48, 33, "PDFPage_EvalExpr: __add, __sub, __mul, __div, or a unit keyword was expected",
2754 FATAL, no_fpos);
2755 }
2756 }
2757 }
2758 }
2759 return chp;
2760 }
2761
2762
2763 /*****************************************************************************/
2764 /* */
2765 /* FULL_CHAR *PDFPage_CollectExpr(FULL_CHAR* charPtr) */
2766 /* */
2767 /* Collect expression into the expression buffer. */
2768 /* */
2769 /*****************************************************************************/
2770
PDFPage_CollectExpr(FULL_CHAR * charPtr,BOOLEAN * outHasResult,float * outResult)2771 static FULL_CHAR *PDFPage_CollectExpr(FULL_CHAR* charPtr, BOOLEAN* outHasResult,
2772 float* outResult)
2773 {
2774 *outHasResult = FALSE;
2775 while (*charPtr != 0)
2776 {
2777 char ch;
2778
2779 if ( g_expr_index >= sizeof(g_expr) )
2780 Error(48, 34, "PDFPage_CollectExpr: expression too long (max. 512 chars)",
2781 FATAL, no_fpos);
2782
2783 g_expr[g_expr_index++] = ch = *charPtr++;
2784 if (ch == '(')
2785 g_expr_depth++;
2786 else if (ch == ')')
2787 {
2788 Assert(g_expr_depth != 0, no_fpos);
2789 g_expr_depth--;
2790 if (g_expr_depth == 0)
2791 {
2792 g_expr[g_expr_index] = '\0'; /* terminate the string */
2793 (char*) PDFPage_EvalExpr(g_expr, outResult);
2794 *outHasResult = TRUE;
2795 break; /* exit while */
2796 }
2797 }
2798 }
2799 return charPtr;
2800 }
2801
2802
2803 /*****************************************************************************/
2804 /* */
2805 /* FULL_CHAR *PDFPage_CollectLink(FULL_CHAR* charPtr) */
2806 /* */
2807 /* Collect link into the link buffer. */
2808 /* */
2809 /*****************************************************************************/
2810
PDFPage_CollectLink(FULL_CHAR * charPtr)2811 static FULL_CHAR *PDFPage_CollectLink(FULL_CHAR* charPtr
2812 /*, BOOLEAN* outHasResult, float* outResult*/)
2813 {
2814 debug1(DPD, D, "PDFPage_CollectLink(\"%s\")", charPtr);
2815 while (*charPtr != 0)
2816 {
2817 char ch;
2818
2819 if ( g_link_index >= sizeof(g_link) )
2820 Error(48, 35, "PDFPage_CollectLink: link too long (max. 512 chars)",
2821 FATAL, no_fpos);
2822
2823 g_link[g_link_index++] = ch = *charPtr++;
2824 if ((ch == '<') && (*charPtr == '<'))
2825 {
2826 g_link[g_link_index++] = *charPtr++;
2827 g_link_depth++;
2828 }
2829 else if ((ch == '>') && (*charPtr == '>'))
2830 {
2831 Assert(g_link_depth != 0, no_fpos);
2832
2833 g_link_depth--;
2834 if (g_link_depth == 0)
2835 {
2836 /* I don't want the outermost '<<' '>>' pair */
2837 g_link[--g_link_index] = '\0'; /* terminate the string */
2838
2839 PDFPage_ProcessLinkKeyword();
2840
2841 charPtr++;
2842 break; /* exit while */
2843 }
2844 else
2845 g_link[g_link_index++] = *charPtr++;
2846 }
2847 }
2848 debug0(DPD, D, "PDFPage_CollectLink returning");
2849 return charPtr;
2850 }
2851
2852
2853 /*****************************************************************************/
2854 /* */
2855 /* void PDFPage_WriteGraphic(FILE* in_fp, FULL_CHAR* in_str) */
2856 /* */
2857 /* Outputs an appropriate PDF string for drawing a graphic element. */
2858 /* */
2859 /*****************************************************************************/
2860
PDFPage_WriteGraphic(FILE * in_fp,FULL_CHAR * in_str)2861 void PDFPage_WriteGraphic(FILE* in_fp, FULL_CHAR* in_str)
2862 {
2863 t_tempbuf str;
2864
2865 FULL_CHAR *charPtr = in_str;
2866 char *strPtr = str;
2867
2868 if (*charPtr == 0)
2869 return;
2870
2871 /* if in collecting an expression mode then collect until terminating ')' */
2872 if (g_expr_depth != 0)
2873 {
2874 BOOLEAN hasResult;
2875 float value;
2876
2877 charPtr = PDFPage_CollectExpr(charPtr, &hasResult, &value);
2878 if (hasResult)
2879 {
2880 sprintf(strPtr, "%.2f", value);
2881 strPtr += strlen(strPtr);
2882 }
2883 }
2884
2885 /* if in collecting-a-link mode then collect until terminating '>>' */
2886 if (g_link_depth != 0)
2887 charPtr = PDFPage_CollectLink(charPtr);
2888
2889 /* scan the string for '__' otherwise output it */
2890 while (*charPtr != 0)
2891 {
2892 int i;
2893 float value;
2894
2895 Assert(strPtr < (str + sizeof(t_tempbuf)), no_fpos);
2896
2897 /* look for "__" (double underline) */
2898 if ( (charPtr[0] == '_') && (charPtr[1] == '_') )
2899 {
2900 charPtr += 2;
2901
2902 /* "in", "cm", "pt", "em", "loutf", "loutv", "louts" */
2903 #if 0
2904 i = PDFKeyword_Find(kNumberOfUnitKeywords, g_unit_keywords, charPtr);
2905 if (i >= 0)
2906 {
2907 Assert(FALSE, no_fpos);
2908 charPtr += strlen(g_unit_keywords[i]); /* skip keyword */
2909 charPtr = PDFPage_GetFloat(charPtr, &value); /* get value */
2910 sprintf(strPtr, "%.2f", g_units[i] * value); /* output it */
2911 strPtr += strlen(strPtr);
2912 }
2913 else
2914 #endif
2915 {
2916
2917 /* "xsize", "ysize", "xmark", "ymark" */
2918 i = PDFKeyword_Find(kNumberOfGraphicsKeywords, g_graphic_keywords, charPtr);
2919 if (i >= 0)
2920 {
2921 charPtr += strlen(g_graphic_keywords[i]);
2922 #if 1
2923 sprintf(strPtr, "%d", g_graphics_vars[i]);
2924 strPtr += strlen(strPtr);
2925 #else
2926 charPtr = PDFPage_ProcessGraphicsKeyword(charPtr, i, &strPtr);
2927 #endif
2928 }
2929 else
2930 {
2931 /* "link_source=<<", "link_target=<<", "link_target_for_export=<<", "link_URI=<<" */
2932 i = PDFKeyword_Find(kNumberOfLinkKeywords, g_link_keywords, charPtr);
2933 if (i >= 0)
2934 {
2935 charPtr += strlen(g_link_keywords[i]);
2936 #if 1
2937 while (isspace(*charPtr))
2938 charPtr++;
2939
2940 g_link_index = 0;
2941 g_link_depth++;
2942 g_link_keyword = (PDF_LINK_KEYWORD) i;
2943 charPtr = PDFPage_CollectLink(charPtr);
2944 #else
2945 charPtr = PDFPage_ProcessLinkKeyword(charPtr, (PDF_LINK_KEYWORD) i);
2946 #endif
2947 } /* if */
2948 else
2949 {
2950
2951 /* "author=", "title=", "subject=", "keywords=" */
2952 i = PDFKeyword_Find(kNumberOfDocInfoKeywords, g_doc_info_keywords, charPtr);
2953 if (i >= 0)
2954 {
2955 charPtr += strlen(g_doc_info_keywords[i]);
2956 charPtr = PDFPage_ProcessDocInfoKeyword(charPtr, i);
2957 }
2958 else
2959 {
2960
2961 /* "add" "sub" "mul" "div", "sin", "cos" */
2962 i = PDFKeyword_Find(kNumberOfArithmeticKeywords, g_arithmetic_keywords, charPtr);
2963 if (i >= 0)
2964 {
2965 strcpy(g_expr, g_arithmetic_keywords[i]);
2966 charPtr += strlen(g_arithmetic_keywords[i]);
2967 while (isspace(*charPtr))
2968 charPtr++;
2969 if (*charPtr != '(')
2970 Error(48, 36, "PDFPage_WriteGraphic: '(' expected", FATAL, no_fpos);
2971
2972 strcat(g_expr, "(");
2973 g_expr_index = strlen(g_expr);
2974 g_expr_depth++;
2975 {
2976 BOOLEAN hasResult;
2977
2978 charPtr = PDFPage_CollectExpr(++charPtr, &hasResult, &value);
2979 if (hasResult)
2980 {
2981 sprintf(strPtr, "%.2f", value);
2982 strPtr += strlen(strPtr);
2983 }
2984 }
2985 }
2986 else
2987 {
2988 /* alert user in case there was a spelling mistake */
2989 Error(48, 37, "PDFPage_WriteGraphic: '__' encountered while processing @Graphic", WARN, no_fpos);
2990 *strPtr++ = '_';
2991 *strPtr++ = '_';
2992 } /* else */
2993 } /* else */
2994 } /* else */
2995 } /* else */
2996 } /* else */
2997 } /* if */
2998 else
2999 {
3000 *strPtr++ = *charPtr++;
3001 }
3002 }
3003
3004 *strPtr = 0;
3005
3006 PDFPage_FlushBuffer(in_fp); /* this is a marking operation, so flush */
3007 PDFPage_Write(in_fp, str);
3008 }
3009
3010
3011 /*****************************************************************************/
3012 /* */
3013 /* PDFPage_PrintUnderline(FILE* in_fp, int x1, int x2, int y, int thickness) */
3014 /* */
3015 /* Implements underlining (just draws a horizontal line). */
3016 /* */
3017 /*****************************************************************************/
3018
PDFPage_PrintUnderline(FILE * in_fp,int in_x1,int in_x2,int in_y,int in_thickness)3019 void PDFPage_PrintUnderline(FILE* in_fp, int in_x1, int in_x2, int in_y,
3020 int in_thickness)
3021 {
3022 t_tempbuf str;
3023
3024 /* this is a marking operation, so flush and turn off buffering */
3025 PDFPage_FlushBuffer(in_fp);
3026
3027 /* fprintf(out_fp, "/ul { gsave setlinewidth dup 3 1 roll\n"); */
3028 /* fprintf(out_fp, " moveto lineto stroke grestore } bind def\n"); */
3029
3030 sprintf(str, "q %d w %d %d m %d %d l s Q\n",in_thickness,in_x1,in_y,in_x2,in_y);
3031 PDFPage_Write(in_fp, str);
3032 }
3033
3034
3035 /*****************************************************************************/
3036 /* */
3037 /* void PDFPage_Init(FILE* in_fp, float in_scale_factor, int in_line_width) */
3038 /* */
3039 /* Inits the vars for the start of processing a new page. */
3040 /* */
3041 /* [ 0 1 2 ] [ s 0 0 ] */
3042 /* [ 3 4 5 ] = [ 0 s 0 ] */
3043 /* [ 6 7 8 ] [ 0 0 1 ] */
3044 /* */
3045 /*****************************************************************************/
3046
PDFPage_Init(FILE * in_fp,float in_scale_factor,int in_line_width)3047 void PDFPage_Init(FILE* in_fp, float in_scale_factor, int in_line_width)
3048 {
3049
3050 #ifdef USE_MATRICES
3051 g_cur_matrix[0] = g_cur_matrix[4] = in_scale_factor;
3052 g_cur_matrix[1] = g_cur_matrix[2] = g_cur_matrix[3] =
3053 g_cur_matrix[5] = g_cur_matrix[6] = g_cur_matrix[7] = 0;
3054 g_cur_matrix[8] = 1;
3055 g_matrix_stack = NULL;
3056 #endif
3057
3058 /* clear/init page vars */
3059 g_page_uses_fonts = FALSE;
3060 g_page_has_text = FALSE;
3061 g_page_has_graphics = FALSE;
3062
3063 g_page_contents_obj_num = 0; /* undefined */
3064 g_page_length_obj_num = 0; /* undefined */
3065 g_page_start_offset = 0; /* undefined */
3066 /* g_text_font_size_in_ems = 0; */ /* undefined */
3067
3068 g_page_h_scale_factor = g_page_v_scale_factor = in_scale_factor;
3069 g_page_h_origin = g_page_v_origin = 0;
3070 g_page_line_width = in_line_width;
3071
3072 /* ***
3073 g_graphics_vars[k_in] = IN;
3074 g_graphics_vars[k_cm] = CM;
3075 g_graphics_vars[k_pt] = PT;
3076 g_graphics_vars[k_em] = EM;
3077 *** */
3078 g_graphics_vars[k_xsize] = 0; /* undefined */
3079 g_graphics_vars[k_ysize] = 0; /* undefined */
3080 g_graphics_vars[k_xmark] = 0; /* undefined */
3081 g_graphics_vars[k_ymark] = 0; /* undefined */
3082 /* ***
3083 g_graphics_vars[k_loutf] = 0;
3084 g_graphics_vars[k_loutv] = 0;
3085 g_graphics_vars[k_louts] = 0;
3086 *** */
3087
3088 /* No need to touch k_in other constant units */
3089 g_units[k_loutf] = 0; /* undefined */
3090 g_units[k_loutv] = 0; /* undefined */
3091 g_units[k_louts] = 0; /* undefined */
3092
3093 g_ET_pending = FALSE;
3094 g_TJ_pending = FALSE;
3095 g_valid_text_matrix = FALSE; /* Td is not allowed */
3096
3097 /* mark all fonts "not in use" */
3098 {
3099 t_font_list_entry_ptr entry = g_font_list;
3100 while (entry != NULL) {
3101 entry->m_in_use = FALSE; /* set the "in use" state to "not in use" */
3102 entry = entry->m_next_font_entry;
3103 }
3104 }
3105
3106 /* init qsave stack */
3107 g_qsave_stack = NULL;
3108
3109 /* init qsave_marking stack */
3110 g_qsave_marking_stack = NULL;
3111 g_buffer_pos = 0;
3112 /* buffer contains empty string */
3113 g_buffer[0] = '\0';
3114 /* try to chop entire stream if possible! Originally: FALSE; */
3115 /* turn on buffering only AFTER a save request */
3116 g_in_buffering_mode = FALSE;
3117 /* try to chop entire stream if possible! Originally: FALSE; */
3118 /* turn on buffering only AFTER a save request */
3119 g_in_buffering_mode = TRUE;
3120
3121 /* bump page number */
3122 ++g_page_count;
3123 g_page_object_num = PDFObject_New(/* in_fp */);
3124 }
3125
3126
3127 /*****************************************************************************/
3128 /* */
3129 /* void PDFPage_Cleanup(FILE* in_fp) */
3130 /* */
3131 /* Cleans up the processing after a page's contents have been written out. */
3132 /* */
3133 /*****************************************************************************/
3134
PDFPage_Cleanup(FILE * in_fp)3135 void PDFPage_Cleanup(FILE* in_fp)
3136 {
3137 BOOLEAN hasAnnot = FALSE;
3138
3139 Assert(g_qsave_stack == NULL, no_fpos);
3140
3141 /* if page has some content then close its stream object */
3142 if (g_page_contents_obj_num != 0)
3143 {
3144 PDF_FILE_OFFSET page_length = PDFPage_End(in_fp);
3145
3146 #ifdef _CALC_LARGEST_PAGE_OBJECT_
3147 if (page_length > g_max_page_length)
3148 g_max_page_length = page_length;
3149 #endif
3150
3151 /* write page's length object */
3152 if (g_PDF_debug)
3153 fprintf(in_fp, "%%\n%% length object for page %u:\n%%\n", g_page_count);
3154
3155 PDFObject_WriteObj(in_fp, g_page_length_obj_num);
3156 fprintf(in_fp, "%u\nendobj\n", page_length);
3157
3158 /* write out any used font resources */
3159 {
3160 t_font_list_entry_ptr entry = g_font_list;
3161 while (entry != NULL) {
3162 PDFFont_WriteFontResource(in_fp, entry);
3163 entry = entry->m_next_font_entry;
3164 }
3165 }
3166 }
3167
3168 /* write out annotations */
3169 {
3170 t_source_annot_entry_ptr source = g_source_annot_list;
3171
3172 while (source != NULL)
3173 {
3174 if (source->m_this_page_object_num == g_page_object_num)
3175 {
3176
3177 /* even if the annotation(s) cannot be written out now, flag the */
3178 /* fact that this page has annotations */
3179 hasAnnot = TRUE;
3180
3181 /* attempt to write out annotation */
3182 PDFSourceAnnot_Write(in_fp, source);
3183 } /* if annot entry belongs to this page */
3184 source = source->m_next_entry;
3185 } /* while */
3186 }
3187
3188 /* start writing page object ("/Type /Page"); remember its number */
3189 {
3190 unsigned int wanted_block_num = (g_page_count - 1) / kNumberOfPagesPerBlock;
3191 unsigned int block_pos = (g_page_count - 1) % kNumberOfPagesPerBlock;
3192 t_page_block_ptr the_block = g_cur_page_block;
3193
3194 /* if first obj in a block then allocate the block */
3195 if (block_pos == 0)
3196 {
3197 the_block = (t_page_block_ptr) malloc(sizeof(t_page_block));
3198 if (the_block == NULL)
3199 Error(48, 38, "PDFPage_Cleanup: run out of memory", FATAL, no_fpos);
3200 if (wanted_block_num == 0) /* if first block in file */
3201 {
3202 Assert(g_page_block_list == NULL, no_fpos);
3203 g_page_block_list = the_block;
3204 }
3205 else
3206 {
3207 Assert(g_cur_page_block != NULL, no_fpos);
3208 g_cur_page_block->m_next_block = the_block;
3209 }
3210 the_block->m_next_block = NULL; /* don't forget to init this! */
3211 g_cur_page_block = the_block;
3212 }
3213 else
3214 {
3215 Assert(the_block != NULL, no_fpos);
3216 }
3217
3218 /* save object number of this page for later use in the /Pages list */
3219 if (g_PDF_debug)
3220 fprintf(in_fp, "%%\n%% page number %u:\n%%\n", g_page_count);
3221 the_block->m_block[block_pos] = g_page_object_num;
3222 PDFObject_WriteObj(in_fp, g_page_object_num);
3223 /* PDFObject_WriteNewObj(in_fp); */
3224 }
3225
3226 /* write out /Page ID */
3227 fputs("<<\n/Type /Page\n", in_fp);
3228
3229 /* write out page size and orientation */
3230 fprintf(in_fp, "/CropBox [ 0 0 %u %u ]\n",g_doc_h_bound,g_doc_v_bound);
3231
3232 /* write out parent object ref */
3233 fputs("/Parent ", in_fp);
3234 PDFObject_WriteRef(in_fp, g_pages_root);
3235 fputs("\n", in_fp);
3236
3237 /* write out contents object ref (if it exists) */
3238 if (g_page_contents_obj_num != 0)
3239 {
3240 fputs("/Contents ", in_fp);
3241 PDFObject_WriteRef(in_fp, g_page_contents_obj_num);
3242 fputs("\n", in_fp);
3243 }
3244
3245 /* open resources dictionary */
3246 if (g_page_uses_fonts || g_page_has_text || g_page_has_graphics)
3247 fputs("/Resources\n<<\n", in_fp);
3248
3249 /* write out font resources used */
3250 if (g_page_uses_fonts)
3251 {
3252 t_font_list_entry_ptr entry = g_font_list;
3253 fputs("/Font <<", in_fp);
3254 while (entry != NULL) {
3255 if (entry->m_in_use) {
3256 fprintf(in_fp, " %s ", entry->m_PDF_font_name);
3257 PDFFont_WriteObjectRef(in_fp, entry);
3258 }
3259 entry = entry->m_next_font_entry;
3260 }
3261 fputs(" >>\n", in_fp);
3262 }
3263
3264 /* write out any procsets used */
3265 if (g_page_has_text || g_page_has_graphics)
3266 {
3267 fputs("/ProcSet [ /PDF", in_fp);
3268 if (g_page_has_text)
3269 fputs(" /Text", in_fp);
3270 fputs(" ]\n", in_fp);
3271 }
3272
3273 /* close resources dictionary */
3274 if (g_page_uses_fonts || g_page_has_text || g_page_has_graphics)
3275 fputs(">>\n", in_fp);
3276
3277 /* write out annot array */
3278 if (hasAnnot)
3279 {
3280 t_source_annot_entry_ptr entry = g_source_annot_list;
3281 t_source_annot_entry_ptr previous_entry = NULL;
3282
3283 /* write out annotations */
3284 fputs("/Annots [", in_fp);
3285 while (entry != NULL)
3286 {
3287 if (entry->m_this_page_object_num == g_page_object_num)
3288 {
3289 fputs(" ", in_fp);
3290 PDFObject_WriteRef(in_fp, entry->m_this_object_num);
3291
3292 /* if the annotation has just been written out above then delete it */
3293 if (entry->m_written_to_PDF_file)
3294 {
3295 t_source_annot_entry_ptr next_entry = entry->m_next_entry;
3296 if (g_source_annot_list == entry)
3297 g_source_annot_list = next_entry;
3298 if (previous_entry != NULL)
3299 previous_entry->m_next_entry = next_entry;
3300 PDFSourceAnnot_Dispose(entry);
3301 entry = next_entry;
3302 }
3303 else /* annot is a fwd referring one: defer deleting it */
3304 {
3305 previous_entry = entry;
3306 entry = entry->m_next_entry;
3307 }
3308 } /* if annot entry belongs to this page */
3309 else /* annot does not belong to this page; skip it */
3310 {
3311 previous_entry = entry;
3312 entry = entry->m_next_entry;
3313 }
3314 } /* while */
3315 fputs(" ]\n", in_fp);
3316 } /* if */
3317
3318 /* close object */
3319 fputs(">>\nendobj\n", in_fp);
3320 }
3321
3322
3323 /*****************************************************************************/
3324 /* */
3325 /* void PDFFile_Init(FILE* in_fp, int in_h_bound, int in_v_bound) */
3326 /* */
3327 /* Initialize this module. */
3328 /* */
3329 /*****************************************************************************/
3330
PDFFile_Init(FILE * in_fp,int in_h_bound,int in_v_bound,int in_IN,int in_CM,int in_PT,int in_EM)3331 void PDFFile_Init(FILE* in_fp, int in_h_bound, int in_v_bound,
3332 int in_IN, int in_CM, int in_PT, int in_EM)
3333 {
3334 /* write PDF header */
3335 fputs("%PDF-1.2\n", in_fp); /* identifies this as PDF */
3336 fputs("\045\342\343\317\323\n", in_fp); /* 0x25 0xE2 0xE3 0xCF 0xD3 */
3337
3338 /* set debugging status */
3339 #if DEBUG_ON
3340 g_PDF_debug = dbg[DPD].on[D] || dbg[DPD].on[DD] || dbg[DPD].on[DDD];
3341 #else
3342 g_PDF_debug = FALSE;
3343 #endif
3344
3345 #if PDF_COMPRESSION
3346 g_apply_compression = !g_PDF_debug;
3347 #endif
3348
3349 /* objects */
3350 g_next_objnum = 1; /* object numbers start at one */
3351 g_obj_offset_list = NULL;
3352 g_cur_obj_offset_block = NULL;
3353
3354 /* fonts */
3355 g_font_list = NULL;
3356 g_font_encoding_list = NULL;
3357
3358 /* pages */
3359 g_page_count = 0;
3360 g_page_block_list = NULL;
3361 g_cur_page_block = NULL;
3362 g_pages_root = PDFObject_New(/* in_fp */);
3363
3364 /* doc */
3365 g_doc_h_bound = in_h_bound;
3366 g_doc_v_bound = in_v_bound;
3367 g_doc_author = NULL;
3368 g_doc_title = NULL;
3369 g_doc_subject = NULL;
3370 g_doc_keywords = NULL;
3371
3372 /* link annotations */
3373 g_target_annot_list = NULL;
3374 g_has_exported_targets = FALSE;
3375 g_source_annot_list = NULL;
3376
3377 /* units */
3378 g_units[k_in] = in_IN;
3379 g_units[k_cm] = in_CM;
3380 g_units[k_pt] = in_PT;
3381 g_units[k_em] = in_EM;
3382
3383 }
3384
3385
3386 /*****************************************************************************/
3387 /* */
3388 /* void PDFFile_WritePagesObject(FILE* in_fp) */
3389 /* */
3390 /* Cleans up processing after all pages has been written out. */
3391 /* */
3392 /*****************************************************************************/
3393
PDFFile_WritePagesObject(FILE * in_fp)3394 static void PDFFile_WritePagesObject(FILE* in_fp)
3395 {
3396 unsigned int i;
3397 t_page_block_ptr the_block = g_page_block_list;
3398
3399 if (g_PDF_debug)
3400 fprintf(in_fp, "%%\n%% root of pages tree:\n%%\n");
3401
3402 /* write out the root of the Pages tree */
3403 PDFObject_WriteObj(in_fp, g_pages_root);
3404 fputs("<<\n", in_fp);
3405 fputs("/Type /Pages\n", in_fp);
3406 fputs("/Kids [ ", in_fp);
3407 for (i = 0; i < g_page_count; i++)
3408 {
3409 int block_pos = i % kNumberOfPagesPerBlock;
3410 PDFObject_WriteRef(in_fp, the_block->m_block[block_pos]);
3411 if (block_pos == (kNumberOfPagesPerBlock - 1))
3412 {
3413 the_block = the_block->m_next_block;
3414 /* Assert(the_block != NULL, no_fpos); not always true! */
3415 }
3416 fputs(" ", in_fp);
3417 }
3418 fprintf(in_fp, " ]\n/Count %u\n", g_page_count);
3419 /* ***
3420 fprintf(in_fp, "/MediaBox [ 0 0 612 792 ]\n");
3421 fprintf(in_fp, "/MediaBox [ 0 0 %u %u ]\n",g_doc_h_bound,g_doc_v_bound);
3422 *** */
3423 fprintf(in_fp, "/MediaBox [ 0 0 %u %u ]\n", g_doc_h_bound, g_doc_v_bound);
3424 fputs(">>\nendobj\n", in_fp);
3425 }
3426
3427
3428 /*****************************************************************************/
3429 /* */
3430 /* PDF_FILE_OFFSET PDFFile_WriteXREF(FILE* in_fp) */
3431 /* */
3432 /* Writes out the XREF table. */
3433 /* */
3434 /*****************************************************************************/
3435
PDFFile_WriteXREF(FILE * in_fp)3436 static PDF_FILE_OFFSET PDFFile_WriteXREF(FILE* in_fp)
3437 {
3438 int i;
3439 PDF_FILE_OFFSET xref_start;
3440 t_offset_block_ptr the_block = g_obj_offset_list;
3441
3442 if (g_PDF_debug)
3443 fprintf(in_fp, "%%\n%% xref table:\n%%\n");
3444
3445 xref_start = ftell(in_fp);
3446 fputs("xref\n", in_fp);
3447 fprintf(in_fp, "0 %u\n", g_next_objnum);
3448 fputs("0000000000 65535 f \n", in_fp); /* object 0 is a deleted obj */
3449 Assert( (g_next_objnum == 1) || (the_block != NULL), no_fpos);
3450 for (i = 1; i < g_next_objnum; i++) /* write out list of object offsets */
3451 {
3452 int block_pos = (i - 1) % kNumberOfObjectsPerBlock;
3453
3454 /* always write an entry (even if the object doesn't exist) */
3455 fprintf(in_fp, "%010u 00000 n \n", the_block->m_block[block_pos]);
3456
3457 if (the_block->m_block[block_pos] == 0)
3458 {
3459 t_tempbuf str;
3460
3461 strcpy(str, "PDFFile_WriteXREF: undefined object number: ");
3462 sprintf(str + strlen(str), "%u", i);
3463 Error(48, 39, "%s", WARN, no_fpos, str);
3464 }
3465
3466 if (block_pos == (kNumberOfObjectsPerBlock - 1))
3467 {
3468 the_block = the_block->m_next_block;
3469 /* Assert(the_block != NULL, no_fpos); not always true! */
3470 }
3471 }
3472 return xref_start;
3473 }
3474
3475 /*****************************************************************************/
3476 /* */
3477 /* void PDFFile_Cleanup(FILE* in_fp) */
3478 /* */
3479 /* Cleans up processing after all pages has been written out. */
3480 /* */
3481 /*****************************************************************************/
3482
PDFFile_Cleanup(FILE * in_fp)3483 void PDFFile_Cleanup(FILE* in_fp)
3484 {
3485 PDF_FILE_OFFSET xref_start; /* file offset of start of xref table */
3486 PDF_OBJECT_NUM catalog_obj_num;
3487 PDF_OBJECT_NUM info_obj_num;
3488 PDF_OBJECT_NUM dests_obj_num = 0;
3489
3490 /* write out any unresolved link annotations. This could be done earlier */
3491 /* (in fact, it can be done as each new target is defined) but I've */
3492 /* arbitrarily decided to do it here. */
3493 {
3494 t_source_annot_entry_ptr source = g_source_annot_list;
3495
3496 while (source != NULL)
3497 {
3498 t_target_annot_entry_ptr target;
3499
3500 Assert(source->m_target == NULL, no_fpos);
3501 target = PDFTargetAnnot_Find(source->m_name);
3502 if (target != NULL)
3503 {
3504 source->m_target = target;
3505 PDFSourceAnnot_Write(in_fp, source);
3506 }
3507 source = source->m_next_entry;
3508 }
3509 }
3510
3511 /* write out pages object */
3512 PDFFile_WritePagesObject(in_fp);
3513
3514 /* if file has exported targets for links then write out /Dests dictionary */
3515 if (g_has_exported_targets)
3516 {
3517 t_target_annot_entry_ptr entry = g_target_annot_list;
3518
3519 Assert(entry != NULL, no_fpos); /* should be at least an entry! */
3520
3521 /* write PDF 1.1 style /Dests dictionary */
3522 if (g_PDF_debug)
3523 fprintf(in_fp, "%%\n%% /Dests dictionary (exported links):\n%%\n");
3524
3525 dests_obj_num = PDFObject_WriteNewObj(in_fp);
3526 fputs("<<\n", in_fp);
3527
3528 while (entry != NULL)
3529 {
3530 if (entry->m_for_export)
3531 {
3532 fprintf(in_fp, "/%s [ ", entry->m_name);
3533 PDFObject_WriteRef(in_fp, entry->m_page_object_num);
3534 fprintf(in_fp, " /XYZ null null null ]\n");
3535 }
3536 entry = entry->m_next_entry;
3537 }
3538 fputs(">>\nendobj\n", in_fp);
3539 }
3540
3541 /* write out catalog object */
3542 if (g_PDF_debug)
3543 fprintf(in_fp, "%%\n%% catalog:\n%%\n");
3544
3545 catalog_obj_num = PDFObject_WriteNewObj(in_fp);
3546 fputs("<<\n", in_fp);
3547 fputs("/Type /Catalog\n", in_fp);
3548 fputs("/Pages ", in_fp);
3549 PDFObject_WriteRef(in_fp, g_pages_root);
3550 fputs("\n", in_fp);
3551
3552 /* if file has exported targets for links then write out a /Dest dictionary */
3553 if (g_has_exported_targets)
3554 {
3555 fputs("/Dests ", in_fp);
3556 PDFObject_WriteRef(in_fp, dests_obj_num);
3557 fputs("\n", in_fp);
3558 }
3559
3560 /* ***
3561 fputs("/PageMode ", in_fp);
3562 switch ()
3563 {
3564 }
3565 fputs("\n", in_fp);
3566 *** */
3567
3568 fputs(">>\nendobj\n", in_fp);
3569
3570 /* write out info object */
3571 if (g_PDF_debug)
3572 fprintf(in_fp, "%%\n%% Info object:\n%%\n");
3573
3574 /* ***
3575 Author string (Optional) The name of the person who created the document.
3576 CreationDate Date (Optional) The date the document was created.
3577 ModDate Date (Optional) The date the document was last modified.
3578 Creator string (Optional) If the document was converted into a PDF document from another
3579 form, this is the name of the application that created the original document.
3580 Producer string (Optional) The name of the application that converted the document from its native
3581 format to PDF.
3582 Title string (Optional) The document�s title.
3583 Subject string (Optional) The subject of the document.
3584 Keywords string (Optional) Keywords associated with the document.
3585
3586 example:
3587
3588 /Creator (Adobe Illustrator)
3589 /CreationDate (D:19930204080603-08'00')
3590 /Author (Werner Heisenberg)
3591 /Producer (Acrobat Network Distiller 1.0 for Macintosh)
3592 *** */
3593
3594 info_obj_num = PDFObject_WriteNewObj(in_fp);
3595 fputs("<<\n", in_fp);
3596
3597 fprintf(in_fp, "/Creator (%s)\n", LOUT_VERSION);
3598 fprintf(in_fp, "/Producer (%s)\n", LOUT_VERSION);
3599
3600 {
3601 time_t now;
3602 struct tm *date;
3603
3604 /* I will presume that localtime() is Y2K compliant. If it isn't */
3605 /* on your system, feel free to tweak this code. :-) */
3606
3607 now = time( NULL );
3608 date = localtime( &now );
3609 fprintf(in_fp, "/CreationDate (D:%.4d%.2d%.2d%.2d%.2d%.2d)\n",
3610 date->tm_year + 1900, date->tm_mon + 1, date->tm_mday,
3611 date->tm_hour, date->tm_min, date->tm_sec);
3612 }
3613
3614 if (g_doc_author != NULL)
3615 fprintf(in_fp, "/Author (%s)\n", g_doc_author);
3616
3617 if (g_doc_title != NULL)
3618 fprintf(in_fp, "/Title (%s)\n", g_doc_title);
3619
3620 if (g_doc_subject != NULL)
3621 fprintf(in_fp, "/Subject (%s)\n", g_doc_subject);
3622
3623 if (g_doc_keywords != NULL)
3624 fprintf(in_fp, "/Keywords (%s)\n", g_doc_keywords);
3625
3626 fputs(">>\nendobj\n", in_fp);
3627
3628 /* write out xref table */
3629 xref_start = PDFFile_WriteXREF(in_fp);
3630
3631 /* write out trailer */
3632 /* *** uwe: comments can appear in the body only.
3633 if (g_PDF_debug)
3634 fprintf(in_fp, "%%\n%% trailer:\n%%\n");
3635 *** */
3636
3637 fputs("trailer\n<<\n", in_fp);
3638 fprintf(in_fp, "/Size %u\n", g_next_objnum);
3639 fputs("/Root ", in_fp);
3640 PDFObject_WriteRef(in_fp, catalog_obj_num);
3641 fputs("\n/Info ", in_fp);
3642 PDFObject_WriteRef(in_fp, info_obj_num);
3643
3644 fprintf(in_fp, " >>\nstartxref\n%u\n", xref_start);
3645 fputs("%%EOF\n", in_fp);
3646
3647 /* memory deallocation (no need to dispose of the qsave_marking_stack */
3648 /* because it's always empty after a page has been processed) */
3649
3650 while (g_obj_offset_list != NULL)
3651 {
3652 t_offset_block_ptr the_block = g_obj_offset_list;
3653 g_obj_offset_list = the_block->m_next_block;
3654 free(the_block);
3655 }
3656
3657 while (g_font_encoding_list != NULL)
3658 {
3659 t_font_encoding_entry_ptr the_block = g_font_encoding_list;
3660 g_font_encoding_list = the_block->m_next_entry;
3661 free(the_block->m_font_encoding);
3662 free(the_block);
3663 }
3664
3665 while (g_font_list != NULL)
3666 {
3667 t_font_list_entry_ptr the_block = g_font_list;
3668 g_font_list = the_block->m_next_font_entry;
3669 free(the_block->m_PDF_font_name);
3670 free(the_block->m_short_font_name);
3671 free(the_block->m_actual_font_name);
3672 free(the_block);
3673 }
3674
3675 while (g_page_block_list != NULL)
3676 {
3677 t_page_block_ptr the_block = g_page_block_list;
3678 g_page_block_list = the_block->m_next_block;
3679 free(the_block);
3680 }
3681
3682 while (g_source_annot_list != NULL)
3683 {
3684 t_source_annot_entry_ptr entry = g_source_annot_list;
3685
3686 if (entry->m_target == NULL)
3687 {
3688 t_tempbuf str;
3689 strcpy(str, "PDFFile_Cleanup: unresolved link annotation named ");
3690 strcat(str, (char*) entry->m_name);
3691 Error(48, 40, "%s", WARN, no_fpos, str);
3692 }
3693
3694 g_source_annot_list = PDFSourceAnnot_Dispose(entry);
3695 }
3696
3697 while (g_target_annot_list != NULL)
3698 {
3699 t_target_annot_entry_ptr entry = g_target_annot_list;
3700 g_target_annot_list = entry->m_next_entry;
3701 free(entry->m_name);
3702 free(entry);
3703 }
3704
3705 #ifdef _CALC_LARGEST_PAGE_OBJECT_
3706 /* display largest page object */
3707 {
3708 t_tempbuf str;
3709 /* JK sprintf(str, "The largest page object is %u bytes long.", g_max_page_length); */
3710 Error(48, 41, "The largest page object is %u bytes long.", WARN, no_fpos, g_max_page_length);
3711 }
3712 #endif
3713 }
3714