1 /*
2  * tumble: build a PDF file from image files
3  *
4  * PDF routines
5  * Copyright 2001, 2002, 2003, 2017 Eric Smith <spacewar@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.  Note that permission is
10  * not granted to redistribute this program under the terms of any
11  * other version of the General Public License.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
21  *
22  *  2007-05-07 [JDB] Allow embedded nulls in strings by storing as character
23  *                   arrays plus length words.
24  */
25 
26 
27 #include <stdarg.h>
28 #include <stdbool.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "bitblt.h"
35 #include "pdf.h"
36 #include "pdf_util.h"
37 #include "pdf_prim.h"
38 #include "pdf_private.h"
39 
40 
41 struct pdf_array_elem
42 {
43   struct pdf_array_elem *next;
44   struct pdf_obj        *val;
45 };
46 
47 
48 struct pdf_array
49 {
50   struct pdf_array_elem *first;
51   struct pdf_array_elem *last;
52 };
53 
54 
55 struct pdf_dict_entry
56 {
57   struct pdf_dict_entry *next;
58   char                  *key;
59   struct pdf_obj        *val;
60 };
61 
62 
63 struct pdf_dict
64 {
65   struct pdf_dict_entry *first;
66 };
67 
68 
69 struct pdf_stream
70 {
71   struct pdf_obj *stream_dict;
72   struct pdf_obj *length;
73   pdf_stream_write_callback callback;
74   void *app_data;  /* arg to pass to callback */
75   struct pdf_obj *filters;  /* name or array of names */
76   struct pdf_obj *decode_parms;
77 };
78 
79 
80 struct pdf_obj
81 {
82   /* these fields only apply to indirectly referenced objects */
83   struct pdf_obj      *prev;
84   struct pdf_obj      *next;
85   unsigned long       obj_num;
86   unsigned long       obj_gen;
87   long int            file_offset;
88 
89   /* these fields apply to all objects */
90   unsigned long       ref_count;
91   pdf_obj_type        type;
92   union {
93     bool              boolean;
94     char              *name;
95     struct {
96       char            *content;
97       int             length;
98     }                 string;
99     long              integer;
100     double            real;
101     struct pdf_obj    *ind_ref;
102     struct pdf_dict   dict;
103     struct pdf_array  array;
104     struct pdf_stream stream;
105   } val;
106 };
107 
108 
ref(struct pdf_obj * obj)109 struct pdf_obj *ref (struct pdf_obj *obj)
110 {
111   obj->ref_count++;
112   return (obj);
113 }
114 
115 
unref(struct pdf_obj * obj)116 void unref (struct pdf_obj *obj)
117 {
118   if ((--obj->ref_count) == 0)
119     {
120       /* $$$ free the object */
121     }
122 }
123 
124 
pdf_deref_ind_obj(struct pdf_obj * ind_obj)125 struct pdf_obj *pdf_deref_ind_obj (struct pdf_obj *ind_obj)
126 {
127   pdf_assert (ind_obj->type == PT_IND_REF);
128   return (ind_obj->val.ind_ref);
129 }
130 
131 
pdf_set_dict_entry(struct pdf_obj * dict_obj,char * key,struct pdf_obj * val)132 void pdf_set_dict_entry (struct pdf_obj *dict_obj, char *key, struct pdf_obj *val)
133 {
134   struct pdf_dict_entry *entry;
135 
136   if (dict_obj->type == PT_IND_REF)
137     dict_obj = pdf_deref_ind_obj (dict_obj);
138 
139   pdf_assert (dict_obj->type == PT_DICTIONARY);
140 
141   /* replacing existing entry? */
142   for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
143     if (strcmp (entry->key, key) == 0)
144       {
145 	unref (entry->val);
146 	entry->val = ref (val);
147 	return;
148       }
149 
150   /* new entry */
151   entry = pdf_calloc (1, sizeof (struct pdf_dict_entry));
152 
153   entry->next = dict_obj->val.dict.first;
154   dict_obj->val.dict.first = entry;
155 
156   entry->key = pdf_strdup (key);
157   entry->val = ref (val);
158 }
159 
160 
pdf_get_dict_entry(struct pdf_obj * dict_obj,char * key)161 struct pdf_obj *pdf_get_dict_entry (struct pdf_obj *dict_obj, char *key)
162 {
163   struct pdf_dict_entry *entry;
164 
165   if (dict_obj->type == PT_IND_REF)
166     dict_obj = pdf_deref_ind_obj (dict_obj);
167 
168   pdf_assert (dict_obj->type == PT_DICTIONARY);
169 
170   for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
171     if (strcmp (entry->key, key) == 0)
172       return (entry->val);
173 
174   return (NULL);
175 }
176 
177 
pdf_add_array_elem(struct pdf_obj * array_obj,struct pdf_obj * val)178 void pdf_add_array_elem (struct pdf_obj *array_obj, struct pdf_obj *val)
179 {
180   struct pdf_array_elem *elem = pdf_calloc (1, sizeof (struct pdf_array_elem));
181 
182   if (array_obj->type == PT_IND_REF)
183     array_obj = pdf_deref_ind_obj (array_obj);
184 
185   pdf_assert (array_obj->type == PT_ARRAY);
186 
187   elem->val = ref (val);
188 
189   if (! array_obj->val.array.first)
190     array_obj->val.array.first = elem;
191   else
192     array_obj->val.array.last->next = elem;
193 
194   array_obj->val.array.last = elem;
195 }
196 
197 
pdf_add_array_elem_unique(struct pdf_obj * array_obj,struct pdf_obj * val)198 void pdf_add_array_elem_unique (struct pdf_obj *array_obj, struct pdf_obj *val)
199 {
200   struct pdf_array_elem *elem;
201 
202   if (array_obj->type == PT_IND_REF)
203     array_obj = pdf_deref_ind_obj (array_obj);
204 
205   pdf_assert (array_obj->type == PT_ARRAY);
206 
207   for (elem = array_obj->val.array.first; elem; elem = elem->next)
208     if (pdf_compare_obj (val, elem->val) == 0)
209       return;
210 
211   elem = pdf_calloc (1, sizeof (struct pdf_array_elem));
212 
213   elem->val = ref (val);
214 
215   if (! array_obj->val.array.first)
216     array_obj->val.array.first = elem;
217   else
218     array_obj->val.array.last->next = elem;
219 
220   array_obj->val.array.last = elem;
221 }
222 
223 
pdf_new_obj(pdf_obj_type type)224 struct pdf_obj *pdf_new_obj (pdf_obj_type type)
225 {
226   struct pdf_obj *obj = pdf_calloc (1, sizeof (struct pdf_obj));
227   obj->type = type;
228   return (obj);
229 }
230 
231 
pdf_new_bool(bool val)232 struct pdf_obj *pdf_new_bool (bool val)
233 {
234   struct pdf_obj *obj = pdf_new_obj (PT_BOOL);
235   obj->val.boolean = val;
236   return (obj);
237 }
238 
239 
pdf_new_name(char * name)240 struct pdf_obj *pdf_new_name (char *name)
241 {
242   struct pdf_obj *obj = pdf_new_obj (PT_NAME);
243   obj->val.name = pdf_strdup (name);
244   return (obj);
245 }
246 
247 
pdf_new_string(char * str)248 struct pdf_obj *pdf_new_string (char *str)
249 {
250   struct pdf_obj *obj = pdf_new_obj (PT_STRING);
251   obj->val.string.content = pdf_strdup (str);
252   obj->val.string.length = strlen(str);
253   return (obj);
254 }
255 
256 
pdf_new_string_n(char * str,int n)257 struct pdf_obj *pdf_new_string_n (char *str, int n)
258 {
259   struct pdf_obj *obj = pdf_new_obj (PT_STRING);
260   obj->val.string.length = n;
261   obj->val.string.content = pdf_calloc (1,n);
262   memcpy(obj->val.string.content, str, n);
263   return (obj);
264 }
265 
266 
pdf_new_integer(long val)267 struct pdf_obj *pdf_new_integer (long val)
268 {
269   struct pdf_obj *obj = pdf_new_obj (PT_INTEGER);
270   obj->val.integer = val;
271   return (obj);
272 }
273 
274 
pdf_new_real(double val)275 struct pdf_obj *pdf_new_real (double val)
276 {
277   struct pdf_obj *obj = pdf_new_obj (PT_REAL);
278   obj->val.real = val;
279   return (obj);
280 }
281 
282 
pdf_new_stream(pdf_file_handle pdf_file,struct pdf_obj * stream_dict,pdf_stream_write_callback callback,void * app_data)283 struct pdf_obj *pdf_new_stream (pdf_file_handle pdf_file,
284 				struct pdf_obj *stream_dict,
285 				pdf_stream_write_callback callback,
286 				void *app_data)
287 {
288   struct pdf_obj *obj = pdf_new_obj (PT_STREAM);
289 
290   obj->val.stream.stream_dict = stream_dict;
291   obj->val.stream.length = pdf_new_ind_ref (pdf_file, pdf_new_integer (0));
292   pdf_set_dict_entry (obj->val.stream.stream_dict, "Length", obj->val.stream.length);
293 
294   obj->val.stream.callback = callback;
295   obj->val.stream.app_data = app_data;
296   return (obj);
297 }
298 
299 
300 /* $$$ currently limited to one filter per stream */
pdf_stream_add_filter(struct pdf_obj * stream,char * filter_name,struct pdf_obj * decode_parms)301 void pdf_stream_add_filter (struct pdf_obj *stream,
302 			    char *filter_name,
303 			    struct pdf_obj *decode_parms)
304 {
305   if (stream->type == PT_IND_REF)
306     stream = pdf_deref_ind_obj (stream);
307 
308   pdf_assert (stream->type == PT_STREAM);
309 
310   pdf_set_dict_entry (stream->val.stream.stream_dict, "Filter", pdf_new_name (filter_name));
311   if (decode_parms)
312     pdf_set_dict_entry (stream->val.stream.stream_dict, "DecodeParms", decode_parms);
313 }
314 
315 
pdf_new_ind_ref(pdf_file_handle pdf_file,struct pdf_obj * obj)316 struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj)
317 {
318   struct pdf_obj *ind_obj;
319 
320   pdf_assert (obj->type != PT_IND_REF);
321 
322   ind_obj = pdf_new_obj (PT_IND_REF);
323 
324   ind_obj->type = PT_IND_REF;
325   ind_obj->val.ind_ref = obj;
326 
327   /* is there already an indirect reference to this object? */
328   if (! obj->obj_num)
329     {
330       /* no, assign object number/generation and add to linked list */
331       if (! pdf_file->first_ind_obj)
332 	{
333 	  obj->obj_num = 1;
334 	  pdf_file->first_ind_obj = pdf_file->last_ind_obj = obj;
335 	}
336       else
337 	{
338 	  obj->obj_num = pdf_file->last_ind_obj->obj_num + 1;
339 	  pdf_file->last_ind_obj->next = obj;
340 	  obj->prev = pdf_file->last_ind_obj;
341 	  pdf_file->last_ind_obj = obj;
342 	}
343     }
344 
345   return (ind_obj);
346 }
347 
348 
pdf_get_integer(struct pdf_obj * obj)349 long pdf_get_integer (struct pdf_obj *obj)
350 {
351   if (obj->type == PT_IND_REF)
352     obj = pdf_deref_ind_obj (obj);
353 
354   pdf_assert (obj->type == PT_INTEGER);
355 
356   return (obj->val.integer);
357 }
358 
pdf_set_integer(struct pdf_obj * obj,long val)359 void pdf_set_integer (struct pdf_obj *obj, long val)
360 {
361   if (obj->type == PT_IND_REF)
362     obj = pdf_deref_ind_obj (obj);
363 
364   pdf_assert (obj->type == PT_INTEGER);
365 
366   obj->val.integer = val;
367 }
368 
369 
pdf_get_real(struct pdf_obj * obj)370 double pdf_get_real (struct pdf_obj *obj)
371 {
372   if (obj->type == PT_IND_REF)
373     obj = pdf_deref_ind_obj (obj);
374 
375   pdf_assert (obj->type == PT_REAL);
376 
377   return (obj->val.real);
378 }
379 
pdf_set_real(struct pdf_obj * obj,double val)380 void pdf_set_real (struct pdf_obj *obj, double val)
381 {
382   if (obj->type == PT_IND_REF)
383     obj = pdf_deref_ind_obj (obj);
384 
385   pdf_assert (obj->type == PT_REAL);
386 
387   obj->val.real = val;
388 }
389 
390 
pdf_compare_obj(struct pdf_obj * o1,struct pdf_obj * o2)391 int pdf_compare_obj (struct pdf_obj *o1, struct pdf_obj *o2)
392 {
393   if (o1->type == PT_IND_REF)
394     o1 = pdf_deref_ind_obj (o1);
395 
396   if (o2->type == PT_IND_REF)
397     o2 = pdf_deref_ind_obj (o2);
398 
399   pdf_assert (o1->type == o2->type);
400 
401   switch (o1->type)
402     {
403     case PT_INTEGER:
404       if (o1->val.integer < o2->val.integer)
405 	return (-1);
406       if (o1->val.integer > o2->val.integer)
407 	return (1);
408       return (0);
409     case PT_REAL:
410       if (o1->val.real < o2->val.real)
411 	return (-1);
412       if (o1->val.real > o2->val.real)
413 	return (1);
414       return (0);
415     case PT_STRING:
416       {
417 	int l;
418 	l = o1->val.string.length;
419 	if(l > o2->val.string.length)
420 	  l = o2->val.string.length;
421 	l = memcmp (o1->val.string.content, o2->val.string.content, l);
422 	if (l)
423 	  return l;
424 	return o1->val.string.length - o2->val.string.length;
425       }
426     case PT_NAME:
427       return (strcmp (o1->val.name, o2->val.name));
428     default:
429       pdf_fatal ("invalid object type for comparison\n");
430     }
431 }
432 
433 
name_char_needs_quoting(char c)434 static int name_char_needs_quoting (char c)
435 {
436   return ((c < '!')  || (c > '~')  || (c == '/') || (c == '\\') ||
437 	  (c == '(') || (c == ')') || (c == '<') || (c == '>')  ||
438 	  (c == '[') || (c == ']') || (c == '{') || (c == '}')  ||
439 	  (c == '%'));
440 }
441 
442 
pdf_write_name(pdf_file_handle pdf_file,char * s)443 void pdf_write_name (pdf_file_handle pdf_file, char *s)
444 {
445   fprintf (pdf_file->f, "/");
446   while (*s)
447     if (name_char_needs_quoting (*s))
448       fprintf (pdf_file->f, "#%02x", 0xff & *(s++));
449     else
450       fprintf (pdf_file->f, "%c", *(s++));
451   fprintf (pdf_file->f, " ");
452 }
453 
454 
pdf_write_literal_string(pdf_file_handle pdf_file,char * s,int n)455 static int pdf_write_literal_string (pdf_file_handle pdf_file, char *s, int n)
456 {
457   int i, p;
458 
459   if (pdf_file)
460     fprintf (pdf_file->f, "(");
461 
462   for (i = p = 0; n; n--)
463     {
464       int j, k;
465 
466       k = 0;
467 
468       switch (*s)
469       {
470 	case '\\':
471 	  k = 1;
472 	  break;
473 
474 	case '(':
475 	  for (j = k =1; k && j < n; j++)
476 	    k += (s[j] == '(') ? 1 : (s[j] == ')') ? -1 : 0;
477 	  p += !k;
478 	  break;
479 
480 	case ')':
481 	  if (p)
482 	    p--;
483 	  else
484 	    k = 1;
485 	  break;
486       }
487 
488       if (k)
489 	{
490 	  i++;
491 	  if (pdf_file)
492 	    fprintf (pdf_file->f, "\\");
493 	}
494 
495       i++;
496 
497       if (pdf_file)
498 	fprintf (pdf_file->f, "%c", *(s++));
499     }
500 
501   if (pdf_file)
502     fprintf (pdf_file->f, ") ");
503   return i;
504 }
505 
506 
pdf_write_string(pdf_file_handle pdf_file,char * s,int n)507 void pdf_write_string (pdf_file_handle pdf_file, char *s, int n)
508 {
509   if (pdf_write_literal_string (NULL, s, n) < 2 * n)
510     pdf_write_literal_string (pdf_file, s, n);
511   else
512     {
513       fprintf (pdf_file->f, "<");
514 
515       for( ; n--; )
516 	fprintf (pdf_file->f, "%.2X",*(s++));
517       fprintf (pdf_file->f, "> ");
518     }
519 }
520 
521 
pdf_write_real(pdf_file_handle pdf_file,double num)522 void pdf_write_real (pdf_file_handle pdf_file, double num)
523 {
524   /* $$$ not actually good enough, precision needs to be variable,
525      and no exponent is allowed */
526   fprintf (pdf_file->f, "%0f ", num);
527 }
528 
529 
pdf_write_ind_ref(pdf_file_handle pdf_file,struct pdf_obj * ind_obj)530 void pdf_write_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
531 {
532   struct pdf_obj *obj = pdf_deref_ind_obj (ind_obj);
533   fprintf (pdf_file->f, "%ld %ld R ", obj->obj_num, obj->obj_gen);
534 }
535 
536 
pdf_write_array(pdf_file_handle pdf_file,struct pdf_obj * array_obj)537 void pdf_write_array (pdf_file_handle pdf_file, struct pdf_obj *array_obj)
538 {
539   struct pdf_array_elem *elem;
540 
541   pdf_assert (array_obj->type == PT_ARRAY);
542 
543   fprintf (pdf_file->f, "[ ");
544   for (elem = array_obj->val.array.first; elem; elem = elem->next)
545     {
546       pdf_write_obj (pdf_file, elem->val);
547       fprintf (pdf_file->f, " ");
548     }
549   fprintf (pdf_file->f, "] ");
550 }
551 
552 
pdf_write_dict(pdf_file_handle pdf_file,struct pdf_obj * dict_obj)553 void pdf_write_dict (pdf_file_handle pdf_file, struct pdf_obj *dict_obj)
554 {
555   struct pdf_dict_entry *entry;
556 
557   pdf_assert (dict_obj->type == PT_DICTIONARY);
558 
559   fprintf (pdf_file->f, "<<\r\n");
560   for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
561     {
562       pdf_write_name (pdf_file, entry->key);
563       fprintf (pdf_file->f, " ");
564       pdf_write_obj (pdf_file, entry->val);
565       fprintf (pdf_file->f, "\r\n");
566     }
567   fprintf (pdf_file->f, ">>\r\n");
568 }
569 
570 
pdf_stream_write_data(pdf_file_handle pdf_file,struct pdf_obj * stream,char * data,unsigned long len)571 void pdf_stream_write_data (pdf_file_handle pdf_file,
572 			    struct pdf_obj *stream,
573 			    char *data,
574 			    unsigned long len)
575 {
576   while (len)
577     {
578       unsigned long l2 = fwrite (data, 1, len, pdf_file->f);
579       data += l2;
580       len -= l2;
581       if (ferror (pdf_file->f))
582 	pdf_fatal ("error writing stream data\n");
583     }
584 }
585 
586 
pdf_stream_printf(pdf_file_handle pdf_file,struct pdf_obj * stream,char * fmt,...)587 void pdf_stream_printf (pdf_file_handle pdf_file,
588 			struct pdf_obj *stream,
589 			char *fmt, ...)
590 {
591   va_list ap;
592 
593   va_start (ap, fmt);
594   vfprintf (pdf_file->f, fmt, ap);
595   va_end (ap);
596 }
597 
598 
pdf_write_stream(pdf_file_handle pdf_file,struct pdf_obj * stream)599 void pdf_write_stream (pdf_file_handle pdf_file, struct pdf_obj *stream)
600 {
601   unsigned long begin_pos, end_pos;
602 
603   pdf_assert (stream->type == PT_STREAM);
604 
605   pdf_write_dict (pdf_file, stream->val.stream.stream_dict);
606   fprintf (pdf_file->f, "stream\r\n");
607   begin_pos = ftell (pdf_file->f);
608   stream->val.stream.callback (pdf_file,
609 			       stream,
610 			       stream->val.stream.app_data);
611   end_pos = ftell (pdf_file->f);
612 
613   fprintf (pdf_file->f, "\r\nendstream\r\n");
614 
615   pdf_set_integer (stream->val.stream.length, end_pos - begin_pos);
616 }
617 
618 
pdf_write_obj(pdf_file_handle pdf_file,struct pdf_obj * obj)619 void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj)
620 {
621   switch (obj->type)
622     {
623     case PT_NULL:
624       fprintf (pdf_file->f, "null ");
625       break;
626     case PT_BOOL:
627       if (obj->val.boolean)
628 	fprintf (pdf_file->f, "true ");
629       else
630 	fprintf (pdf_file->f, "false ");
631       break;
632     case PT_NAME:
633       pdf_write_name (pdf_file, obj->val.name);
634       break;
635     case PT_STRING:
636       pdf_write_string (pdf_file, obj->val.string.content, obj->val.string.length);
637       break;
638     case PT_INTEGER:
639       fprintf (pdf_file->f, "%ld ", obj->val.integer);
640       break;
641     case PT_REAL:
642       pdf_write_real (pdf_file, obj->val.real);
643       break;
644     case PT_IND_REF:
645       pdf_write_ind_ref (pdf_file, obj);
646       break;
647     case PT_DICTIONARY:
648       pdf_write_dict (pdf_file, obj);
649       break;
650     case PT_ARRAY:
651       pdf_write_array (pdf_file, obj);
652       break;
653     case PT_STREAM:
654       pdf_write_stream (pdf_file, obj);
655       break;
656     default:
657       pdf_fatal ("bad object type\n");
658     }
659 }
660 
661 
pdf_write_ind_obj(pdf_file_handle pdf_file,struct pdf_obj * ind_obj)662 void pdf_write_ind_obj (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
663 {
664   struct pdf_obj *obj;
665 
666   if (ind_obj->type == PT_IND_REF)
667     obj = pdf_deref_ind_obj (ind_obj);
668   else
669     obj = ind_obj;
670 
671   obj->file_offset = ftell (pdf_file->f);
672   fprintf (pdf_file->f, "%ld %ld obj\r\n", obj->obj_num, obj->obj_gen);
673   pdf_write_obj (pdf_file, obj);
674   fprintf (pdf_file->f, "endobj\r\n");
675 }
676 
677 
pdf_write_all_ind_obj(pdf_file_handle pdf_file)678 void pdf_write_all_ind_obj (pdf_file_handle pdf_file)
679 {
680   struct pdf_obj *ind_obj;
681   for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
682     if (! ind_obj->file_offset)
683       pdf_write_ind_obj (pdf_file, ind_obj);
684 }
685 
686 
pdf_write_xref(pdf_file_handle pdf_file)687 unsigned long pdf_write_xref (pdf_file_handle pdf_file)
688 {
689   struct pdf_obj *ind_obj;
690   pdf_file->xref_offset = ftell (pdf_file->f);
691   fprintf (pdf_file->f, "xref\r\n");
692   fprintf (pdf_file->f, "0 %ld\r\n", pdf_file->last_ind_obj->obj_num + 1);
693   fprintf (pdf_file->f, "0000000000 65535 f\r\n");
694   for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
695     fprintf (pdf_file->f, "%010ld 00000 n\r\n", ind_obj->file_offset);
696   return (pdf_file->last_ind_obj->obj_num + 1);
697 }
698 
699 
700 /* this isn't really a PDF primitive data type */
pdf_new_XObject(pdf_page_handle pdf_page,struct pdf_obj * ind_ref)701 char pdf_new_XObject (pdf_page_handle pdf_page, struct pdf_obj *ind_ref)
702 {
703   char XObject_name [4] = "Im ";
704 
705   XObject_name [2] = ++pdf_page->last_XObject_name;
706 
707   if (! pdf_page->XObject_dict)
708     {
709       pdf_page->XObject_dict = pdf_new_obj (PT_DICTIONARY);
710       pdf_set_dict_entry (pdf_page->resources, "XObject", pdf_page->XObject_dict);
711     }
712 
713   pdf_set_dict_entry (pdf_page->XObject_dict, & XObject_name [0], ind_ref);
714 
715   return (pdf_page->last_XObject_name);
716 }
717 
718 
719