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