KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > it > stefanochizzolini > clown > tokens > Writer


1 /*
2   Copyright © 2006 Stefano Chizzolini. http://clown.stefanochizzolini.it
3
4   Contributors:
5     * Stefano Chizzolini (original code developer, info@stefanochizzolini.it):
6       contributed code is Copyright © 2006 by Stefano Chizzolini.
7
8   This file should be part of the source code distribution of "PDF Clown library"
9   (the Program): see the accompanying README files for more info.
10
11   This Program is free software; you can redistribute it and/or modify it under
12   the terms of the GNU General Public License as published by the Free Software
13   Foundation; either version 2 of the License, or (at your option) any later version.
14
15   This Program is distributed in the hope that it will be useful, but WITHOUT ANY
16   WARRANTY, either expressed or implied; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
18
19   You should have received a copy of the GNU General Public License along with this
20   Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
21
22   Redistribution and use, with or without modification, are permitted provided that such
23   redistributions retain the above copyright notice, license and disclaimer, along with
24   this list of conditions.
25 */

26
27 package it.stefanochizzolini.clown.tokens;
28
29 import it.stefanochizzolini.clown.bytes.IOutputStream;
30 import it.stefanochizzolini.clown.files.File;
31 import it.stefanochizzolini.clown.files.IndirectObjects;
32 import it.stefanochizzolini.clown.objects.PdfDictionary;
33 import it.stefanochizzolini.clown.objects.PdfIndirectObject;
34 import it.stefanochizzolini.clown.objects.PdfInteger;
35 import it.stefanochizzolini.clown.objects.PdfLong;
36 import it.stefanochizzolini.clown.objects.PdfName;
37 import it.stefanochizzolini.clown.objects.PdfReference;
38 import java.io.EOFException JavaDoc;
39 import java.util.Map JavaDoc;
40
41 /**
42   PDF file writer.
43 */

44 public class Writer
45 {
46   // <class>
47
// <dynamic>
48
// <fields>
49
private File file;
50   private IOutputStream stream;
51   // </fields>
52

53   // <constructors>
54
/**
55     <h3>Remarks</h3>
56     <p>For internal use only.</p>
57   */

58   public Writer(
59     IOutputStream stream,
60     File file
61     )
62   {
63     this.stream = stream;
64     this.file = file;
65   }
66   // </constructors>
67

68   // <interface>
69
public IOutputStream getStream(
70     )
71   {return stream;}
72
73   /**
74     Serializes the PDF file compactly [PDF:1.6:3.4].
75   */

76   public void writeStandard(
77     )
78   {
79     StringBuilder JavaDoc xrefBuilder = new StringBuilder JavaDoc();
80     int xrefSize = file.getIndirectObjects().size();
81     int offset;
82
83     // Header [PDF:1.6:3.4.1].
84
{
85       offset = stream.write(
86         "%PDF-1.6\r"
87           + "%âãÏÓ\r" // Arbitrary binary characters (code >= 128) for ensuring proper behavior of file transfer applications.
88
);
89     }
90
91     // Body [PDF:1.6:3.4.2].
92
{
93       /*
94         NOTE: A compact xref table comprises just one section composed by just one subsection.
95         NOTE: As xref-table free entries MUST be arrayed as a linked list,
96         it's needed to cache intermingled in-use entries in order to properly render
97         the object number of the next free entry inside the previous one.
98       */

99       StringBuilder JavaDoc xrefInUseBlockBuilder = new StringBuilder JavaDoc();
100       IndirectObjects indirectObjects = file.getIndirectObjects();
101       PdfReference freeReference = indirectObjects.get(0).getReference(); // Initialized to the first free entry.
102
for(
103         int index = 1;
104         index < xrefSize;
105         index++
106         )
107       {
108         PdfIndirectObject indirectObject = indirectObjects.get(index);
109         // Is the object entry in use?
110
if(indirectObject.isInUse()) // In-use entry.
111
{
112           // Indirect object.
113
// Append to the xref table its xref!
114
xrefInUseBlockBuilder.append(
115             indirectObject.getReference().getCrossReference(offset)
116             );
117           // Serialize its content!
118
offset += indirectObject.writeTo(stream);
119         }
120         else // Free entry.
121
{
122           // Flush current xref-table cache!
123
xrefBuilder.append(
124             freeReference.getCrossReference(index)
125               + xrefInUseBlockBuilder.toString()
126             );
127           // Initialize next xref-table subsection!
128
xrefInUseBlockBuilder.setLength(0);
129           freeReference = indirectObject.getReference();
130         }
131       }
132       // Flush current xref-table cache!
133
xrefBuilder.append(
134         freeReference.getCrossReference(0)
135           + xrefInUseBlockBuilder.toString()
136         );
137     }
138
139     // XRef table (unique section) [PDF:1.6:3.4.3]...
140
{
141       // ...header.
142
stream.write(
143         "xref\r"
144           + "0 " + xrefSize + "\r"
145         );
146       // ...body.
147
stream.write(xrefBuilder.toString());
148     }
149
150     // Trailer [PDF:1.6:3.4.4]...
151
{
152       // ...header.
153
stream.write("trailer\r");
154       // ...body.
155
// Update the counter!
156
PdfDictionary trailer = file.getTrailer();
157       trailer.put(PdfName.Size,new PdfInteger(xrefSize));
158       // Serialize the contents!
159
trailer.writeTo(stream);
160       // ...tail.
161
stream.write(
162         "\r"
163           + "startxref\r"
164           + offset + "\r"
165           + "%%EOF"
166         );
167     }
168   }
169
170   /**
171     Serializes the PDF file as incremental update [PDF:1.6:3.4.5].
172   */

173   public void writeIncremental(
174     )
175   {
176     StringBuilder JavaDoc xrefBuilder = new StringBuilder JavaDoc();
177     int xrefSize = file.getIndirectObjects().size();
178     int offset;
179     Parser parser = file.getReader().getParser();
180
181     // Original content.
182
offset = stream.write(parser.getStream());
183
184     // Body update.
185
{
186       /*
187         NOTE: incremental xref table comprises multiple sections each one composed by multiple
188         subsections.
189       */

190       // Insert modified indirect objects.
191
StringBuilder JavaDoc xrefSubBuilder = new StringBuilder JavaDoc(); // xref-table subsection builder.
192
int xrefSubCount = 0; // xref-table subsection counter.
193
int prevKey = 0; // Previous-entry object number.
194
for(
195         Map.Entry JavaDoc<Integer JavaDoc,PdfIndirectObject> indirectObjectEntry : file.getIndirectObjects().getModifiedObjects().entrySet()
196         )
197       {
198         // Is the object in the current subsection?
199
/*
200           NOTE: to belong to the current subsection, the object entry MUST be contiguous with the
201           previous (1° condition) or the iteration has to have been just started (2° condition).
202        */

203         if(indirectObjectEntry.getKey() - prevKey == 1 || prevKey == 0) // Current subsection continues.
204
{
205           xrefSubCount++;
206         }
207         else // Current subsection terminates.
208
{
209           // Flush current xref-table subsection!
210
xrefBuilder.append(
211             (prevKey - xrefSubCount + 1) + " " + xrefSubCount + "\r"
212               + xrefSubBuilder.toString()
213             );
214           // Initialize next xref-table subsection!
215
xrefSubBuilder.setLength(0);
216           xrefSubCount = 1;
217         }
218
219         prevKey = indirectObjectEntry.getKey();
220
221         // Modified indirect object.
222
if(indirectObjectEntry.getValue().isInUse()) // In-use entry.
223
{
224           // Append to the current xref-table subsection its xref!
225
xrefSubBuilder.append(
226             indirectObjectEntry.getValue().getReference().getCrossReference(offset)
227             );
228           // Serialize its content!
229
offset += indirectObjectEntry.getValue().writeTo(stream);
230         }
231         else // Free entry.
232
{
233           // Append to the current xref-table subsection its xref!
234
/*
235           NOTE: We purposely neglect the linked list of free entries (see IndirectObjects.remove(int)),
236           so that this entry links directly back to object number 0, having a generation number of
237           65535 (not reusable) [PDF:1.6:3.4.3].
238           */

239           xrefSubBuilder.append(
240             indirectObjectEntry.getValue().getReference().getCrossReference(0)
241             );
242         }
243       }
244       // Flush current xref-table subsection!
245
xrefBuilder.append(
246         (prevKey - xrefSubCount + 1) + " " + xrefSubCount + "\r"
247           + xrefSubBuilder.toString()
248         );
249     }
250
251     // XRef-table last section...
252
{
253       // ...header.
254
stream.write("xref\r");
255       // ...body.
256
stream.write(xrefBuilder.toString());
257     }
258
259     // Updated trailer...
260
try
261     {
262       // ...header.
263
stream.write("trailer\r");
264       // ...body.
265
// Update the entries!
266
PdfDictionary trailer = file.getTrailer();
267       trailer.put(PdfName.Size,new PdfInteger(xrefSize));
268       trailer.put(PdfName.Prev,new PdfLong(parser.retrieveXRefOffset()));
269       // Serialize the contents!
270
trailer.writeTo(stream);
271       // ...tail.
272
stream.write(
273         "\r"
274           + "startxref\r"
275           + offset + "\r"
276           + "%%EOF"
277         );
278     }
279     catch(Exception JavaDoc e)
280     {
281       // Propagate the exception!
282
throw new RuntimeException JavaDoc(e);
283     }
284   }
285   // </interface>
286
// </dynamic>
287
// </class>
288
}
Popular Tags