1 /******************************************************************************
2 Objects.c
3
4 Change Control: DDMMYYYY
5 Michael Still File created 03062000
6
7 Purpose:
8 Panda is based on the concept of objects. This file contains all of
9 methods needed to maintain the objects that we have.
10
11 Copyright (C) Michael Still 2000 - 2002
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 2 of the License, or
16 (at your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
26 ******************************************************************************/
27
28 #if defined _WINDOWS
29 #include "stdafx.h"
30
31 #include "contrib/libz/zlib.h"
32 #else
33 #include <panda/constants.h>
34 #include <panda/functions.h>
35
36 #include <zlib.h>
37 #endif
38
39 /******************************************************************************
40 DOCBOOK START
41
42 FUNCTION panda_newobject
43 PURPOSE create a new object
44
45 SYNOPSIS START
46 #include<panda/constants.h>
47 #include<panda/functions.h>
48 panda_object *panda_newobject (panda_pdf *output, int type);
49 SYNOPSIS END
50
51 DESCRIPTION <command>PANDA INTERNAL</command>. Create a new object within the PDF document output. The types of object are panda_object_normal and panda_object_placeholder -- the difference is that a placeholder object wont be written out when the PDF is dumped to disc. This is an internal function call and would only be need by API users if they are creating support for object types not currently supported by <command>Panda</command>. If this is the case, then these users are encouraged to submit their code changes back to mikal@stillhq.com for inclusion with the next release of <command>Panda</command>, and are reminded of their obligations under the GNU GPL.
52
53 RETURNS A pointer to the new object (a panda_object *)
54
55 EXAMPLE START
56 #include<panda/constants.h>
57 #include<panda/functions.h>
58
59 panda_pdf *document;
60 panda_object *obj;
61
62 panda_init();
63
64 document = panda_open("filename.pdf", "w");
65 obj = panda_newobject(document, panda_object_normal);
66 EXAMPLE END
67 SEEALSO panda_freeobject, panda_writeobject, panda_traverseobjects, panda_addchild
68 DOCBOOK END
69 ******************************************************************************/
70
71 panda_object *
panda_newobject(panda_pdf * doc,int type)72 panda_newobject (panda_pdf * doc, int type)
73 {
74 int dictno;
75 char *dbkey, *dbdata;
76
77 // Do all the magic needed to create an object
78 panda_object *created;
79
80 // Get some memory
81 created = (panda_object *) panda_xmalloc (sizeof (panda_object));
82 doc->totalObjectNumber++;
83
84 // Initialise the object number
85 if (type == panda_placeholder)
86 created->number = -doc->nextPHObjectNumber++;
87 else
88 created->number = doc->nextObjectNumber++;
89
90 // Create the dictionary for this object in the database
91 dictno = panda_adddict (doc);
92 dbkey = panda_xsnprintf ("obj-%d-dictno", created->number);
93 dbdata = panda_xsnprintf ("%d", dictno);
94 panda_dbwrite (doc, dbkey, dbdata);
95 panda_xfree (dbkey);
96 panda_xfree (dbdata);
97
98 // We have no children at the moment
99 created->children = (panda_child *) panda_xmalloc (sizeof (panda_child));
100
101 ((panda_child *) created->children)->next = NULL;
102 created->cachedLastChild = NULL;
103
104 // By default this object is not a pages object
105 created->isPages = panda_false;
106 created->isPlaceholder =
107 type == panda_placeholder ? panda_true : panda_false;
108
109 #if defined DEBUG
110 printf ("Created object %d\n", created->number);
111 #endif
112
113 // We don't have any streams at the moment
114 created->binarystream = NULL;
115 created->layoutstream = NULL;
116
117 // There is no font defined
118 created->currentSetFont = NULL;
119
120 // We are not in text mode within the layout stream at the start
121 created->textmode = panda_false;
122
123 // We are by default not a contents object
124 created->isContents = panda_false;
125
126 if (type == panda_placeholder)
127 {
128 return created;
129 }
130
131 // New objects have a generation number of 0 by definition
132 created->generation = 0;
133
134 // Add this new object to the end of the linked list that we use to append
135 // the xref table onto the end of the PDF
136 doc->xrefTail->me = created;
137
138 // Make space for the next one
139 doc->xrefTail->next = panda_xmalloc (sizeof (panda_xref));
140 doc->xrefTail->next->next = NULL;
141 doc->xrefTail = doc->xrefTail->next;
142
143 // Set the value of the local and cascaded properties for this object to
144 // defaults (all off)
145 memset (created->localproperties, panda_false, panda_object_property_max);
146 memset (created->cascadeproperties, panda_false, panda_object_property_max);
147
148 // The compression level is a little different
149 created->localproperties[panda_object_property_compress_level] =
150 created->cascadeproperties[panda_object_property_compress_level] =
151 panda_default_compress_level;
152
153 // Return
154 return created;
155 }
156
157 // Create a new dictionary
158 int
panda_adddict(panda_pdf * document)159 panda_adddict (panda_pdf * document)
160 {
161 char *dbkey, *dbdata;
162 int max;
163
164 // Determine how many dictionaries we currently have
165 #if defined DEBUG
166 printf ("Determining how many dictionaries are currently defined\n");
167 #endif
168
169 if ((dbdata = panda_dbread (document, "dict-count")) == NULL)
170 {
171 max = 0;
172 }
173 else
174 {
175 max = atoi (dbdata);
176 panda_xfree (dbdata);
177 }
178
179 // Increment that count
180 dbdata = panda_xsnprintf ("%d", ++max);
181 panda_dbwrite (document, "dict-count", dbdata);
182 panda_xfree (dbdata);
183 return max;
184 }
185
186 // The value we return here is the database key that the dictionary value
187 // was stored at, if this value is used as the name of a later key, then
188 // this implies that we are adding a subdictionary. The valuetype for this
189 // first key should have been panda_dictvalue. No longer return a value...
190 void
panda_adddictitem(panda_pdf * document,panda_object * input,char * name,int valueType,...)191 panda_adddictitem (panda_pdf * document, panda_object * input, char *name,
192 int valueType, ...)
193 {
194 // Add an item to the dictionary in the object
195 int dictno, dictelem;
196 char *dbdata, *dbkey, *dictdata, *retval;
197 va_list argPtr;
198 panda_object *obj;
199
200 #if defined DEBUG
201 printf ("Adddictitem called for object numbered %d\n", input->number);
202 #endif
203
204 // Get this object's dictionary number
205 dictno = panda_getobjdictno (document, input);
206
207 // Gain access like variable
208 va_start (argPtr, valueType);
209
210 // Before we can find where to put the data, we need to turn the
211 // data into something which we can store
212 switch (valueType)
213 {
214 case panda_objectarrayvalue:
215 obj = (panda_object *) va_arg (argPtr, panda_object *);
216 dictdata = panda_xsnprintf ("%d %d R", obj->number, obj->generation);
217 break;
218
219 case panda_textvalue:
220 dictdata = panda_xsnprintf ("/%s", (char *) va_arg (argPtr, char *));
221 break;
222
223 case panda_literaltextvalue:
224 dictdata = panda_xsnprintf ("%s", (char *) va_arg (argPtr, char *));
225 break;
226
227 case panda_brackettedtextvalue:
228 dictdata = panda_xsnprintf ("(%s)", (char *) va_arg (argPtr, char *));
229 break;
230
231 case panda_integervalue:
232 dictdata = panda_xsnprintf ("%d", (int) va_arg (argPtr, int));
233 break;
234
235 case panda_objectvalue:
236 obj = (panda_object *) va_arg (argPtr, panda_object *);
237 dictdata = panda_xsnprintf ("%d %d R", obj->number, obj->generation);
238 break;
239
240 case panda_booleanvalue:
241 dictdata = panda_xsnprintf ("%s",
242 (((int) va_arg (argPtr, int)) ==
243 panda_true) ? "true" : "false");
244 break;
245
246 case panda_doublevalue:
247 dictdata = panda_xsnprintf ("%f", (double) va_arg (argPtr, double));
248 break;
249 }
250
251 // Store in the dictionary
252 dictelem = panda_getdictelem (document, dictno, name);
253 retval = panda_adddictiteminternal (document, dictno, dictelem,
254 name, valueType, dictdata);
255 panda_xfree (dictdata);
256 panda_xfree (retval);
257 }
258
259 // Insert data into the dictionary at a given location
260 char *
panda_adddictiteminternal(panda_pdf * document,int passeddictno,int dictelem,char * name,int valueType,char * value)261 panda_adddictiteminternal (panda_pdf * document, int passeddictno,
262 int dictelem, char *name, int valueType,
263 char *value)
264 {
265 char *dbkey, *dbdata, *dbdata2, *dbname, *tempname, *namebit, *therest,
266 *retval;
267 int dictno, pdictelem, pdictno;
268
269 #if defined DEBUG
270 printf ("Add dictionary item internally (name %s, dict %d, element %d)\n",
271 name, passeddictno, dictelem);
272 #endif
273
274 // We need to determine if the key we have been handed really refers to
275 // a subdictionary
276 tempname = panda_xsnprintf ("%s", name);
277 namebit = strtok (tempname, "/");
278 therest = strtok (NULL, "");
279
280 // If this is a dictionary reference, then we should do that instead
281 if ((therest != NULL) && (strlen (therest) > 0))
282 {
283 // Get the dictionary number for this sub item
284 dbkey = panda_finddictiteminternal (document, passeddictno, namebit);
285 if (dbkey != NULL)
286 {
287 dbdata = panda_dbread (document, dbkey);
288 panda_xfree (dbkey);
289 }
290 else
291 {
292 dbdata = NULL;
293 }
294
295 if (dbdata != NULL)
296 {
297 dictno = atoi (dbdata);
298 panda_xfree (dbdata);
299 }
300 else
301 {
302 #if defined DEBUG
303 printf ("Needed to create a new subdictionary\n");
304 #endif
305
306 // We need to add the dictionary item here
307 pdictno = panda_adddict (document);
308 dbdata = panda_xsnprintf ("%d", pdictno);
309 retval = panda_adddictiteminternal (document, passeddictno,
310 panda_getdictelem (document,
311 passeddictno,
312 namebit),
313 namebit, panda_dictionaryvalue,
314 dbdata);
315
316 panda_xfree (dbdata);
317 panda_xfree (retval);
318 dictno = pdictno;
319 }
320
321 #if defined DEBUG
322 printf ("Create a subdictionary\n");
323 #endif
324
325 // We need the element number for the dictionary
326 dictelem = panda_getdictelem (document, dictno, therest);
327 retval = panda_adddictiteminternal (document, dictno, dictelem,
328 therest, valueType, value);
329 panda_xfree (tempname);
330
331 #if defined DEBUG
332 printf ("Rolling up sudictionary creation\n");
333 #endif
334
335 return retval;
336 }
337 else
338 dictno = passeddictno;
339
340 // Otherwise, we do some stuff
341 #if defined DEBUG
342 printf ("Storing a dictionary item: dict = %d, element = %d\n",
343 dictno, dictelem);
344 #endif
345
346 dbkey = panda_xsnprintf ("dict-%d-%d-name", dictno, dictelem);
347 panda_dbwrite (document, dbkey, name);
348 panda_xfree (dbkey);
349
350 dbkey = panda_xsnprintf ("dict-%d-%d-type", dictno, dictelem);
351 dbdata = panda_xsnprintf ("%d", valueType);
352 panda_dbwrite (document, dbkey, dbdata);
353 panda_xfree (dbkey);
354 panda_xfree (dbdata);
355
356 // If this is a panda_objectarrayvalue, then we append to the end
357 dbkey = panda_xsnprintf ("dict-%d-%d-value", dictno, dictelem);
358 if (valueType == panda_objectarrayvalue)
359 {
360 dbdata = panda_dbread (document, dbkey);
361
362 if (dbdata != NULL)
363 dbdata2 = panda_xsnprintf ("%s %s", dbdata, value);
364 else
365 dbdata2 = panda_xsnprintf ("%s", value);
366 }
367 else
368 dbdata2 = panda_xsnprintf ("%s", value);
369
370 panda_dbwrite (document, dbkey, dbdata2);
371
372 panda_xfree (tempname);
373 panda_xfree (dbkey);
374 panda_xfree (dbdata2);
375
376 // todo_mikal: I think there should be a free here, dmalloc disagrees
377 //if(dbdata != NULL)
378 // free(dbdata);
379
380 #if defined DEBUG
381 printf ("Finished inserting dictionary item\n");
382 #endif
383
384 return panda_xsnprintf ("dict-%d-%d-", dictno, dictelem);
385 }
386
387 // Get the dictionary number associated with an object
388 int
panda_getobjdictno(panda_pdf * document,panda_object * input)389 panda_getobjdictno (panda_pdf * document, panda_object * input)
390 {
391 int dictno;
392 char *dbkey, *dbdata;
393
394 #if defined DEBUG
395 printf ("Getting the dictionary number for the object numbered %d\n",
396 input->number);
397 #endif
398
399 // We need to get the dictionary number for this object
400 dbkey = panda_xsnprintf ("obj-%d-dictno", input->number);
401 if ((dbdata = panda_dbread (document, dbkey)) == NULL)
402 {
403 panda_error (panda_true, "Object lacks a dictionary");
404 }
405 panda_xfree (dbkey);
406
407 // Now we can get the count of the current entries in the dictionary
408 dictno = atoi (dbdata);
409 panda_xfree (dbdata);
410
411 if (dictno == 0)
412 panda_error (panda_true, "Object does not have a dictionary\n");
413
414 return dictno;
415 }
416
417 // Find the dictionary element we are looking for
418 int
panda_getdictelem(panda_pdf * document,int dictno,char * name)419 panda_getdictelem (panda_pdf * document, int dictno, char *name)
420 {
421 int count;
422 char *dbkey, *dbdata;
423
424 #if defined DEBUG
425 printf ("Searching for a dictionary element named %s in dictionary %d\n",
426 name, dictno);
427 #endif
428
429 if (dictno == 0)
430 panda_error (panda_true, "Invalid dictionary number\n");
431
432 count = 0;
433 do
434 {
435 dbkey = panda_xsnprintf ("dict-%d-%d-name", dictno, count);
436 dbdata = panda_dbread (document, dbkey);
437
438 // Do the names match?
439 if ((dbdata != NULL) && (strcmp (name, dbdata) == 0))
440 {
441 return count;
442 }
443
444 panda_xfree (dbkey);
445 panda_xfree (dbdata);
446 count++;
447
448 #if defined DEBUG
449 if (count > 1000)
450 panda_error (panda_true, "Counter too big!\n");
451 #endif
452 }
453 while (dbdata != NULL);
454
455 // -1 because of the increment above
456 return count - 1;
457 }
458
459 // Find a given dictionary item, and return the database key for the value
460 // element
461 char *
panda_finddictitem(panda_pdf * document,panda_object * input,char * name)462 panda_finddictitem (panda_pdf * document, panda_object * input, char *name)
463 {
464 int dictno;
465 char *dbdata, *dbkey;
466
467 #if defined DEBUG
468 printf ("Finding a dictionary item for the object numbered %d\n",
469 input->number);
470 #endif
471
472 // We need to get the dictionary number for this object
473 dictno = panda_getobjdictno (document, input);
474 return panda_finddictiteminternal (document, dictno, name);
475 }
476
477 // Internal version of the find
478 char *
panda_finddictiteminternal(panda_pdf * document,int dictno,char * name)479 panda_finddictiteminternal (panda_pdf * document, int dictno, char *name)
480 {
481 char *dbdata, *dbkey;
482 int count;
483
484 #if defined DEBUG
485 printf ("Searching for %s in dictionary %d\n", name, dictno);
486 #endif
487
488 // We can now go through the dictionary elements and see if we are
489 // clobbering this one
490 count = 0;
491 do
492 {
493 dbkey = panda_xsnprintf ("dict-%d-%d-name", dictno, count);
494 dbdata = panda_dbread (document, dbkey); // NULL here is ok
495
496 // Do the names match?
497 if ((dbdata != NULL) && (strcmp (name, dbdata) == 0))
498 {
499 panda_xfree (dbdata);
500 panda_xfree (dbkey);
501 dbkey = panda_xsnprintf ("dict-%d-%d-value", dictno, count);
502 return dbkey;
503 }
504
505 panda_xfree (dbkey);
506 panda_xfree (dbdata);
507
508 #if defined DEBUG
509 if (count > 1000)
510 panda_error (panda_true, "Counter too big!\n");
511 #endif
512
513 count++;
514 }
515 while (dbdata != NULL);
516
517 return NULL;
518 }
519
520 /******************************************************************************
521 DOCBOOK START
522
523 FUNCTION panda_freeobject
524 PURPOSE free the memory used by a previously created object
525
526 SYNOPSIS START
527 #include<panda/constants.h>
528 #include<panda/functions.h>
529 void panda_freeobject (panda_pdf *output, panda_object *);
530 SYNOPSIS END
531
532 DESCRIPTION <command>PANDA INTERNAL</command>. This function call frees the memory used by an object. It is used during the cleanup process prior to finishing closing a PDF document. Even if a user of the API has added object types to their code, they should not have to call this function, as their objects should be added to the object tree via <command>panda_addchild</command>() to ensure they are written to disc by <command>panda_writeobject</command>() having been traversed by <command>panda_traverseobjects</command>() at PDF close time.
533
534 RETURNS None
535
536 EXAMPLE START
537 #include<panda/constants.h>
538 #include<panda/functions.h>
539
540 panda_pdf *document;
541 panda_object *obj;
542
543 panda_init();
544
545 document = panda_open("filename.pdf", "w");
546 obj = panda_newobject(document, panda_object_normal);
547 panda_freeobject(document, obj)l
548 EXAMPLE END
549 SEEALSO panda_newobject, panda_writeobject, panda_traverseobjects, panda_addchild
550 DOCBOOK END
551 ******************************************************************************/
552
553 void
panda_freeobject(panda_pdf * output,panda_object * freeVictim)554 panda_freeobject (panda_pdf * output, panda_object * freeVictim)
555 {
556 panda_freeobjectactual (output, freeVictim, panda_true, panda_true);
557 }
558
559 void
panda_freetempobject(panda_pdf * output,panda_object * freeVictim,int freedict)560 panda_freetempobject (panda_pdf * output, panda_object * freeVictim,
561 int freedict)
562 {
563 panda_freeobjectactual (output, freeVictim, freedict, panda_true);
564 }
565
566 void
panda_freeobjectactual(panda_pdf * output,panda_object * freeVictim,int freedict,int freekids)567 panda_freeobjectactual (panda_pdf * output, panda_object * freeVictim,
568 int freedict, int freekids)
569 {
570 #if defined DEBUG
571 printf ("Freeing object number %d\n", freeVictim->number);
572 #endif
573
574 // Skip placeholder objects
575 if (freeVictim->number != -1)
576 {
577 #if defined DEBUG
578 printf ("Actually freeing this object (not a placeholder)\n");
579 #endif
580
581 panda_xfree (freeVictim->layoutstream);
582 panda_xfree (freeVictim->binarystream);
583
584 panda_xfree (freeVictim->currentSetFont);
585 }
586
587 if (freekids == panda_true)
588 panda_xfree (freeVictim->children);
589
590 panda_xfree (freeVictim);
591 }
592
593 /******************************************************************************
594 DOCBOOK START
595
596 FUNCTION panda_writeobject
597 PURPOSE write a given object to disc
598
599 SYNOPSIS START
600 #include<panda/constants.h>
601 #include<panda/functions.h>
602 void panda_writeobject (panda_pdf *output, object *obj);
603 SYNOPSIS END
604
605 DESCRIPTION <command>PANDA INTERNAL</command>. Writes all objects created with <command>panda_newobject</command>(), that have been added to the object tree with <command>panda_addchild</command>() via a call to <command>panda_traverseobjects</command>().
606
607 RETURNS None
608
609 EXAMPLE START
610 #include<panda/constants.h>
611 #include<panda/functions.h>
612
613 panda_pdf *document;
614 panda_object *obj;
615
616 panda_init();
617
618 document = panda_open("filename.pdf", "w");
619 obj = panda_newobject(document, panda_object_normal);
620 panda_writeobject(document, obj);
621 EXAMPLE END
622 SEEALSO panda_newobject, panda_freeobject, panda_traverseobjects, panda_addchild
623 DOCBOOK END
624 ******************************************************************************/
625
626 void
panda_writeobject(panda_pdf * output,panda_object * dumpTarget)627 panda_writeobject (panda_pdf * output, panda_object * dumpTarget)
628 {
629 // Do all that is needed to dump a PDF object to disk
630 unsigned long count, compressedLen;
631 char *compressed;
632 int compressResult, level;
633
634 #if defined DEBUG
635 printf ("Writing object number %d\n", dumpTarget->number);
636 #endif
637
638 // We don't dump place holder objects
639 if (dumpTarget->isPlaceholder != panda_true)
640 {
641 // Remember the byte offset that was the start of this object -- this is
642 // needed for the XREF later
643 dumpTarget->byteOffset = output->byteOffset;
644
645 /*************************************************************************
646 Do we have a layoutstream? If we do, work out the length of the stream
647 and save that as a dictionary element.
648
649 We also handle binarystreams here.
650
651 *************************************************************************/
652
653 #if defined DEBUG
654 printf ("Layoutstream is %s\n",
655 (dumpTarget->layoutstream == NULL) ? "NULL" : "not NULL");
656 printf ("Binarystream is %s\n",
657 (dumpTarget->binarystream == NULL) ? "NULL" : "not NULL");
658 #endif
659
660 if (dumpTarget->layoutstream != NULL)
661 {
662 #if defined DEBUG
663 printf ("Processing the layoutstream\n");
664 #endif
665 dumpTarget->layoutstreamLength = strlen (dumpTarget->layoutstream);
666
667 // We determine if the stream is to be compressed, and then
668 // overwrite the old stream with the compressed one (and save
669 // the length).
670
671 if ((dumpTarget->cascadeproperties
672 [panda_object_property_compress] == panda_true) ||
673 (dumpTarget->localproperties
674 [panda_object_property_compress] == panda_true))
675 {
676
677 #if defined DEBUG
678 printf ("Compression enabled for this object\n");
679 #endif
680
681 // See zlib.h
682 compressedLen = dumpTarget->layoutstreamLength * 1.2 + 12;
683 compressed = panda_xmalloc (compressedLen);
684
685 // Determine what compression level to use
686 if (dumpTarget->localproperties
687 [panda_object_property_compress_level] !=
688 panda_default_compress_level)
689 level = dumpTarget->localproperties
690 [panda_object_property_compress_level];
691 else
692 level = dumpTarget->cascadeproperties
693 [panda_object_property_compress_level];
694
695 if (((compressResult = compress2 (compressed, &compressedLen,
696 dumpTarget->layoutstream,
697 dumpTarget->
698 layoutstreamLength,
699 level)) == Z_OK)
700 && (compressedLen < dumpTarget->layoutstreamLength))
701 {
702 printf ("Compressed is %d [obj %d]\n", compressedLen,
703 dumpTarget->number);
704
705 // The compression worked (no error returned)
706 panda_adddictitem (output, dumpTarget, "Filter",
707 panda_textvalue, "FlateDecode");
708
709 // Now we need to make the stream use the compressed one,
710 // and record the length
711 panda_xfree (dumpTarget->layoutstream);
712 dumpTarget->layoutstream = compressed;
713 dumpTarget->layoutstreamLength = compressedLen;
714 }
715 }
716
717 #if defined DEBUG
718 printf ("Writing out the length of the stream\n");
719 #endif
720
721 panda_adddictitem (output, dumpTarget, "Length", panda_integervalue,
722 dumpTarget->layoutstreamLength);
723
724 #if defined DEBUG
725 printf ("Finished with layoutstream\n");
726 #endif
727 }
728
729 // We cannot have a layoutstream and a binary stream in the same object
730 else if (dumpTarget->binarystream != NULL)
731 {
732 panda_adddictitem (output, dumpTarget, "Length", panda_integervalue,
733 dumpTarget->binarystreamLength);
734 }
735
736 // Make sure we deal with empty content streams probably
737 else if (dumpTarget->isContents == panda_true)
738 {
739 #if defined DEBUG
740 printf ("Forcing output of content stream\n");
741 #endif
742
743 panda_adddictitem (output, dumpTarget, "Length", panda_integervalue,
744 0);
745 dumpTarget->layoutstream =
746 (char *) panda_xmalloc (sizeof (char) * 2);
747 dumpTarget->layoutstream = panda_xsnprintf (" ");
748 dumpTarget->layoutstreamLength = 1;
749 }
750
751 // We are going to dump the named object (and only the named object) to
752 // disk
753 panda_printf (output, "%d %d obj\n",
754 dumpTarget->number, dumpTarget->generation);
755
756 #if defined DEBUG
757 printf ("Writing out the dictionary\n");
758 #endif
759
760 panda_writedictionary (output, dumpTarget);
761
762 // Do we have a layoutstream?
763 if (dumpTarget->layoutstream != NULL)
764 {
765 panda_print (output, "stream\n");
766
767 for (count = 0; count < dumpTarget->layoutstreamLength; count++)
768 panda_putc (output, dumpTarget->layoutstream[count]);
769
770 panda_print (output, "\nendstream\n");
771 }
772
773 // Do we have a binary stream? We cannot have both cause how would be
774 // differentiate?
775 else if (dumpTarget->binarystream != NULL)
776 {
777 panda_print (output, "stream\n");
778
779 for (count = 0; count < dumpTarget->binarystreamLength; count++)
780 panda_putc (output, dumpTarget->binarystream[count]);
781
782 panda_print (output, "\nendstream\n");
783 }
784
785 panda_print (output, "endobj\n");
786 }
787 }
788
789 void
panda_writedictionary(panda_pdf * output,panda_object * obj)790 panda_writedictionary (panda_pdf * output, panda_object * obj)
791 {
792 int dictno;
793
794 #if defined DEBUG
795 printf ("Writing out the dictionary items for the object numbered %d\n",
796 obj->number);
797 #endif
798
799 // Recursively write the dictionary out (including sub-dictionaries)
800 dictno = panda_getobjdictno (output, obj);
801 panda_writedictionaryinternal (output, dictno, 1);
802 }
803
804 // Dump a specific dictionary to the PDF file
805 void
panda_writedictionaryinternal(panda_pdf * output,int dictno,int depth)806 panda_writedictionaryinternal (panda_pdf * output, int dictno, int depth)
807 {
808 int count, dcount;
809 char *dbkey, *dbname = NULL, *dbvalue, *dbtype;
810
811 #if defined DEBUG
812 printf ("Internal call to write the dictionary\n");
813 #endif
814
815 panda_print (output, "<<\n");
816
817 // We can now go through the dictionary elements and see if we are
818 // clobbering this one
819 count = 0;
820 do
821 {
822 panda_xfree (dbname);
823
824 dbkey = panda_xsnprintf ("dict-%d-%d-name", dictno, count);
825 dbname = panda_dbread (output, dbkey);
826 panda_xfree (dbkey);
827
828 dbkey = panda_xsnprintf ("dict-%d-%d-value", dictno, count);
829 dbvalue = panda_dbread (output, dbkey);
830 panda_xfree (dbkey);
831
832 dbkey = panda_xsnprintf ("dict-%d-%d-type", dictno, count);
833 dbtype = panda_dbread (output, dbkey);
834 panda_xfree (dbkey);
835
836 if (dbname != NULL)
837 {
838 for (dcount = 0; dcount < depth; dcount++)
839 panda_printf (output, "\t");
840 panda_printf (output, "/%s ", dbname);
841
842 if (atoi (dbtype) == panda_dictionaryvalue)
843 panda_writedictionaryinternal (output, atoi (dbvalue), depth + 1);
844 else if (atoi (dbtype) == panda_objectarrayvalue)
845 panda_printf (output, "[%s]\n", dbvalue);
846 else
847 panda_printf (output, "%s\n", dbvalue);
848
849 panda_xfree (dbtype);
850 panda_xfree (dbvalue);
851 }
852
853 #if defined DEBUG
854 if (count > 1000)
855 panda_error (panda_true, "Counter too big!\n");
856 #endif
857
858 count++;
859 }
860 while (dbname != NULL);
861
862 for (dcount = 0; dcount < depth - 1; dcount++)
863 panda_print (output, "\t");
864 panda_print (output, ">>\n");
865 }
866
867
868 /******************************************************************************
869 DOCBOOK START
870
871 FUNCTION panda_addchild
872 PURPOSE add an object to the object tree
873
874 SYNOPSIS START
875 #include<panda/constants.h>
876 #include<panda/functions.h>
877 void panda_addchild (panda_object *parent, panda_object *child);
878 SYNOPSIS END
879
880 DESCRIPTION <command>PANDA INTERNAL</command>. Once an object has been created with <command>panda_newobject</command>() it is added to the object tree with this call. This ensures it is written out to disc via <command>panda_writeobject</command>() when <command>panda_close</command>() is called.
881
882 RETURNS None
883
884 EXAMPLE START
885 #include<panda/constants.h>
886 #include<panda/functions.h>
887
888 panda_pdf *document;
889 panda_object *obj, *obj2;
890
891 panda_init();
892
893 document = panda_open("filename.pdf", "w");
894 obj = panda_newobject(document, panda_object_normal);
895 obj2 = panda_newobject(document, panda_object_normal);
896 panda_addhild(obj, obj2);
897 EXAMPLE END
898 SEEALSO panda_newobject, panda_freeobject, panda_writeobject, panda_traverseobjects
899 DOCBOOK END
900 ******************************************************************************/
901
902 void
panda_addchild(panda_object * parentObj,panda_object * childObj)903 panda_addchild (panda_object * parentObj, panda_object * childObj)
904 {
905 panda_child *currentChild = parentObj->children;
906
907 // Find the end of the list of children
908 if (parentObj->cachedLastChild != NULL)
909 currentChild = parentObj->cachedLastChild;
910
911 while (currentChild->next != NULL)
912 currentChild = currentChild->next;
913
914 // Make a new end
915 currentChild->next = (panda_child *) panda_xmalloc (sizeof (panda_child));
916 currentChild->next->next = NULL;
917
918 // Make me be the child object
919 currentChild->me = childObj;
920
921 // Cascade the relevant properties
922 memcpy (childObj->cascadeproperties, parentObj->cascadeproperties,
923 panda_object_property_max);
924
925 // Cache it
926 parentObj->cachedLastChild = currentChild;
927 }
928
929 /******************************************************************************
930 DOCBOOK START
931
932 FUNCTION panda_traverseobjects
933 PURPOSE traverse the PDF object tree and perform an operation
934
935 SYNOPSIS START
936 #include<panda/constants.h>
937 #include<panda/functions.h>
938 void panda_traverseobjects (panda_pdf * output, panda_object * startObject, int direction, traverseFunct function);
939 SYNOPSIS END
940
941 DESCRIPTION <command>PANDA INTERNAL</command>. This function traverses the object tree created by <command>panda_addchild</command>() and repeatedly calls the function defined as a callback. The traversal has a direction as defined by: panda_up (bottom up) or panda_down (top down). This call is commonly used by <command>panda_close</command> to call <command>panda_writeobject</command>() and <command>panda_freeobject</command>(). API users might also find it useful, although I wouldn't know why.
942
943 RETURNS None
944
945 EXAMPLE START
946 #include<panda/constants.h>
947 #include<panda/functions.h>
948
949 panda_pdf *document;
950
951 panda_init();
952
953 document = panda_open("filename.pdf", "w");
954
955 ... create a whole bunch of objects and add them to the tree ...
956 panda_traverseobjects(document, document->catalog, panda_down, panda_writeobject);
957 EXAMPLE END
958 SEEALSO panda_newobject, panda_freeobject, panda_writeobject, panda_addchild
959 DOCBOOK END
960 ******************************************************************************/
961
962 void
panda_traverseobjects(panda_pdf * output,panda_object * dumpTarget,int direction,traverseFunct function)963 panda_traverseobjects (panda_pdf * output, panda_object * dumpTarget,
964 int direction, traverseFunct function)
965 {
966 // Write out an object and all of it's children. This may be done with
967 // recursive calls and writeobject()
968 panda_child *currentChild;
969
970 #if defined DEBUG
971 printf ("Cleaning up object number %d\n", dumpTarget->number);
972 #endif
973
974 // No children
975 if (((panda_child *) dumpTarget->children)->next == NULL)
976 {
977 function (output, dumpTarget);
978 return;
979 }
980
981 // Otherwise, for me and then for all children
982 if (direction == panda_down)
983 function (output, dumpTarget);
984
985 currentChild = dumpTarget->children;
986 while (currentChild->next != NULL)
987 {
988 panda_traverseobjects (output, currentChild->me, direction, function);
989 currentChild = currentChild->next;
990 }
991
992 if (direction == panda_up)
993 function (output, dumpTarget);
994 }
995
996 /******************************************************************************
997 DOCBOOK START
998
999 FUNCTION panda_setobjectproperty
1000 PURPOSE set a property value for an object
1001
1002 SYNOPSIS START
1003 #include<panda/constants.h>
1004 #include<panda/functions.h>
1005 void panda_setobjectproperty (panda_object *target, int scope, int propid, int propval);
1006 SYNOPSIS END
1007
1008 DESCRIPTION Properties are a way of specifing things about objects. These properties can have either a cascade scope (they affect all subsequently created objects that are children of that object) -- <command>panda_scope_cascade</command>, or local (they only occur for that object) -- <command>panda_scope_local</command>. Possible properties are defined in the <command>panda_const_properties</command> manual page.
1009
1010 RETURNS None
1011
1012 EXAMPLE START
1013 #include<panda/constants.h>
1014 #include<panda/functions.h>
1015
1016 panda_pdf *document;
1017 panda_object *obj;
1018
1019 panda_init();
1020
1021 document = panda_open("filename.pdf", "w");
1022 obj = panda_newobject(document, panda_object_normal);
1023 panda_setproperty(obj, panda_scope_cascade, panda_object_property_compress, panda_true);
1024
1025 EXAMPLE END
1026 SEEALSO panda_newobject, panda_const_properties
1027 DOCBOOK END
1028 ******************************************************************************/
1029
1030 void
panda_setproperty(panda_object * target,int scope,int key,int value)1031 panda_setproperty (panda_object * target, int scope, int key, int value)
1032 {
1033 #if defined DEBUG
1034 printf ("Set a property for object %d %d\n", target->number,
1035 target->generation);
1036 #endif
1037
1038 if ((key < 0) || (key > panda_object_property_max))
1039 {
1040 #if defined DEBUG
1041 printf ("Undefined key value specifiec\n");
1042 #endif
1043 return;
1044 }
1045
1046 switch (scope)
1047 {
1048 case panda_scope_cascade:
1049 #if defined DEBUG
1050 printf ("Cascade\n");
1051 #endif
1052 target->cascadeproperties[key] = value;
1053 break;
1054
1055 case panda_scope_local:
1056 #if defined DEBUG
1057 printf ("Local\n");
1058 #endif
1059 target->localproperties[key] = value;
1060 break;
1061
1062 default:
1063 // Do nothing -- Windows requires the semi colon for some reason
1064 ;
1065 }
1066 }
1067