001: /*
002: Copyright © 2006 Stefano Chizzolini. http://clown.stefanochizzolini.it
003:
004: Contributors:
005: * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
006: contributed code is Copyright © 2006 by Stefano Chizzolini.
007:
008: This file should be part of the source code distribution of "PDF Clown library"
009: (the Program): see the accompanying README files for more info.
010:
011: This Program is free software; you can redistribute it and/or modify it under
012: the terms of the GNU General Public License as published by the Free Software
013: Foundation; either version 2 of the License, or (at your option) any later version.
014:
015: This Program is distributed in the hope that it will be useful, but WITHOUT ANY
016: WARRANTY, either expressed or implied; without even the implied warranty of
017: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
018:
019: You should have received a copy of the GNU General Public License along with this
020: Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
021:
022: Redistribution and use, with or without modification, are permitted provided that such
023: redistributions retain the above copyright notice, license and disclaimer, along with
024: this list of conditions.
025: */
026:
027: package it.stefanochizzolini.clown.tokens;
028:
029: import it.stefanochizzolini.clown.bytes.IInputStream;
030: import it.stefanochizzolini.clown.files.File;
031: import it.stefanochizzolini.clown.objects.PdfDictionary;
032: import it.stefanochizzolini.clown.objects.PdfInteger;
033: import it.stefanochizzolini.clown.objects.PdfName;
034: import java.io.EOFException;
035:
036: /**
037: PDF file reader.
038: */
039: public class Reader {
040: // <class>
041: // <dynamic>
042: // <fields>
043: private Parser parser;
044:
045: // </fields>
046:
047: // <constructors>
048: /**
049: <h3>Remarks</h3>
050: <p>For internal use only.</p>
051: */
052: public Reader(IInputStream stream, File file) {
053: this .parser = new Parser(stream, file);
054: }
055:
056: // </constructors>
057:
058: // <interface>
059: // <public>
060: public int hashCode() {
061: return parser.hashCode();
062: }
063:
064: public Parser getParser() {
065: return parser;
066: }
067:
068: public PdfDictionary readTrailer() throws FileFormatException {
069: // Get the offset of the last xref-table section!
070: long xrefOffset = parser.retrieveXRefOffset();
071: // Go to the start of the last xref-table section!
072: parser.seek(xrefOffset);
073: parser.moveNext();
074: if (!((String) parser.getToken()).equals("xref"))
075: throw new FileFormatException("'xref' keyword not found.",
076: parser.getPosition());
077:
078: // Searching the start of the last trailer...
079: while (true) {
080: parser.moveNext();
081: if (parser.getTokenType() == TokenTypeEnum.Keyword)
082: break;
083: parser.moveNext();
084: int count = (Integer) parser.getToken();
085: parser.skip(count * 20);
086: }
087: if (!((String) parser.getToken()).equals("trailer"))
088: throw new FileFormatException(
089: "'trailer' keyword not found.", parser
090: .getPosition());
091:
092: // Get the last trailer!
093: parser.moveNext();
094: return (PdfDictionary) parser.parsePdfObject();
095: }
096:
097: /**
098: Retrieves the xref-table.
099: @return The xref-table entries array.
100: */
101: public XRefEntry[] readXRefTable(PdfDictionary trailer)
102: throws FileFormatException {
103: // 1. XRef-table.
104: // Get the xref-table size!
105: PdfInteger xrefTableSize = (PdfInteger) trailer
106: .get(PdfName.Size);
107: // Allocate the xref-table array!
108: XRefEntry[] xrefEntries = new XRefEntry[xrefTableSize
109: .getValue()];
110:
111: // 2. Last xref-table section.
112: // Move to the start of the last xref-table section!
113: parser.seek(parser.retrieveXRefOffset());
114: // Parse the last xref-table section!
115: readXRefSection(xrefEntries);
116:
117: // 3. Previous xref-table sections.
118: while (true) {
119: // 1. Previous xref-table section.
120: // Get the previous xref-table section offset!
121: PdfInteger prevXRefOffset = (PdfInteger) trailer
122: .get(PdfName.Prev);
123: if (prevXRefOffset == null)
124: break;
125: // Move to the start of the previous xref-table section!
126: parser.seek(prevXRefOffset.getValue());
127: // Parse the previous xref-table section!
128: readXRefSection(xrefEntries);
129:
130: // 2. Previous trailer.
131: // Skip 'trailer' keyword!
132: parser.moveNext();
133: // Get the previous trailer!
134: trailer = (PdfDictionary) parser.parsePdfObject();
135: }
136:
137: return xrefEntries;
138: }
139:
140: public String readVersion() throws FileFormatException {
141: return parser.retrieveVersion();
142: }
143:
144: // </public>
145:
146: // <protected>
147: protected void readXRefSection(XRefEntry[] xrefEntries)
148: throws FileFormatException {
149: // Reach the start of the xref-table section!
150: parser.moveNext();
151: if (!((String) parser.getToken()).equals("xref"))
152: throw new FileFormatException("'xref' keyword not found.",
153: parser.getPosition());
154:
155: // Loop sequentially across the subsections inside the current xref-table section.
156: while (true) {
157: /*
158: NOTE: Each iteration of this loop block represents the scanning
159: of one subsection.
160: We get its bounds (first and last object numbers within its range)
161: and then collect its entries.
162: */
163: // 1. First object number.
164: parser.moveNext();
165: // Have we reached the end of the xref-table section?
166: if ((parser.getTokenType() == TokenTypeEnum.Keyword)
167: && ((String) parser.getToken()).equals("trailer"))
168: break;
169: // Is the current token type different from the expected one?
170: if (parser.getTokenType() != TokenTypeEnum.Integer)
171: throw new FileFormatException(
172: "Neither object number of the first object in this xref subsection nor end of xref section found.",
173: parser.getPosition());
174: // Get the object number of the first object in this xref-table subsection!
175: int startObjectNumber = (Integer) parser.getToken();
176:
177: // 2. Last object number.
178: parser.moveNext();
179: if (parser.getTokenType() != TokenTypeEnum.Integer)
180: throw new FileFormatException(
181: "Number of entries in this xref subsection not found.",
182: parser.getPosition());
183: // Get the object number of the last object in this xref-table subsection!
184: int endObjectNumber = (Integer) parser.getToken()
185: + startObjectNumber;
186:
187: // 3. xref-table subsection entries.
188: for (int index = startObjectNumber; index < endObjectNumber; index++) {
189: // Is the entry undefined?
190: if (xrefEntries[index] == null) // Undefined entry.
191: {
192: // 1. Get the indirect object offset!
193: parser.moveNext();
194: int offset = (Integer) parser.getToken();
195: // 2. Get the object generation number!
196: parser.moveNext();
197: int generation = (Integer) parser.getToken();
198: // 3. Get the usage tag!
199: parser.moveNext();
200: String usageToken = (String) parser.getToken();
201: XRefEntryUsageEnum usage;
202: if (usageToken.equals("n"))
203: usage = XRefEntryUsageEnum.InUse;
204: else if (usageToken.equals("f"))
205: usage = XRefEntryUsageEnum.Free;
206: else
207: throw new FileFormatException(
208: "Invalid xref entry.", parser
209: .getPosition());
210:
211: // 4. Entry initialization.
212: xrefEntries[index] = new XRefEntry(index,
213: generation, offset, usage);
214: } else // Already-defined entry.
215: {
216: // Skip to the next entry!
217: parser.moveNext(3);
218: }
219: }
220: }
221: }
222: // </protected>
223: // </interface>
224: // </dynamic>
225: // </class>
226: }
|