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&lt;panda/constants.h&gt;
47 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
57 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
528 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
538 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
601 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
611 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
876 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
886 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
937 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
947 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
1004 #include&lt;panda/functions.h&gt;
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&lt;panda/constants.h&gt;
1014 #include&lt;panda/functions.h&gt;
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