/*
Copyright 2006,2008 Stefano Chizzolini. http://clown.stefanochizzolini.it
Contributors:
* Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it)
This file should be part of the source code distribution of "PDF Clown library"
(the Program): see the accompanying README files for more info.
This Program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later version.
This Program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY, either expressed or implied; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
You should have received a copy of the GNU General Public License along with this
Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
Redistribution and use, with or without modification, are permitted provided that such
redistributions retain the above copyright notice, license and disclaimer, along with
this list of conditions.
*/
using it.stefanochizzolini.clown.bytes;
using it.stefanochizzolini.clown.documents;
using it.stefanochizzolini.clown.files;
using it.stefanochizzolini.clown.objects;
using System;
using System.IO;
namespace it.stefanochizzolini.clown.tokens{
/**
<summary>PDF file reader.</summary>
*/
public class Reader : IDisposable
{
#region dynamic
#region fields
private Parser parser;
#endregion
#region constructors
internal Reader(
IInputStream stream,
files.File file
)
{this.parser = new Parser(stream,file);}
#endregion
#region interface
#region public
public override int GetHashCode(
)
{return parser.GetHashCode();}
public Parser Parser
{get{return parser;}}
public PdfDictionary ReadTrailer(
)
{
// Get the offset of the last xref-table section!
long xrefOffset = parser.RetrieveXRefOffset();
// Go to the start of the last xref-table section!
parser.Seek(xrefOffset); parser.MoveNext();
if(!parser.Token.Equals("xref"))
throw new FileFormatException("xref keyword not found.",parser.Position);
// Searching the start of the last trailer...
while(true)
{
parser.MoveNext();
if(parser.TokenType == TokenTypeEnum.Keyword)
break;
parser.MoveNext();
int count = (int)parser.Token;
parser.Skip(count * 20);
}
if(!parser.Token.Equals("trailer"))
throw new FileFormatException("trailer keyword not found.",parser.Position);
// Get the last trailer!
parser.MoveNext();
return (PdfDictionary)parser.ParsePdfObject();
}
/**
<summary>Retrieves the xref-table.</summary>
<returns>The xref-table entries array.</returns>
*/
public XRefEntry[] ReadXRefTable(
PdfDictionary trailer
)
{
// 1. XRef-table.
// Get the xref-table size!
PdfInteger xrefTableSize = (PdfInteger)trailer[PdfName.Size];
// Allocate the xref-table array!
XRefEntry[] xrefEntries = new XRefEntry[xrefTableSize.RawValue];
// 2. Last xref-table section.
// Move to the start of the last xref-table section!
parser.Seek(parser.RetrieveXRefOffset());
// Parse the last xref-table section!
ReadXRefSection(xrefEntries);
// 3. Previous xref-table sections.
while(true)
{
// 1. Previous xref-table section.
// Get the previous xref-table section offset!
PdfInteger prevXRefOffset = (PdfInteger)trailer[PdfName.Prev];
if(prevXRefOffset == null)
break;
// Move to the start of the previous xref-table section!
parser.Seek(prevXRefOffset.RawValue);
// Parse the previous xref-table section!
ReadXRefSection(xrefEntries);
// 2. Previous trailer.
// Skip 'trailer' keyword!
parser.MoveNext();
// Get the previous trailer!
trailer = (PdfDictionary)parser.ParsePdfObject();
}
return xrefEntries;
}
public string ReadVersion(
)
{return parser.RetrieveVersion();}
#region IDisposable
public void Dispose(
)
{
if(parser != null)
{
parser.Dispose();
parser = null;
}
GC.SuppressFinalize(this);
}
#endregion
#endregion
#region protected
protected void ReadXRefSection(
XRefEntry[] xrefEntries
)
{
// Reach the start of the xref-table section!
parser.MoveNext();
if(!((string)parser.Token).Equals("xref"))
throw new FileFormatException("xref keyword not found.",parser.Position);
// Loop sequentially across the subsections inside the current xref-table section.
while(true)
{
/*
NOTE: Each iteration of this loop block represents the scanning
of one subsection.
We get its bounds (first and last object numbers within its range)
and then collect its entries.
*/
// 1. First object number.
parser.MoveNext();
// Have we reached the end of the xref-table section?
if((parser.TokenType == TokenTypeEnum.Keyword)
&& ((string)parser.Token).Equals("trailer"))
break;
// Is the current token type different from the expected one?
if(parser.TokenType != TokenTypeEnum.Integer)
throw new FileFormatException("Neither object number of the first object in this xref subsection nor end of xref section found.",parser.Position);
// Get the object number of the first object in this xref-table subsection!
int startObjectNumber = (int)parser.Token;
// 2. Last object number.
parser.MoveNext();
if(parser.TokenType != TokenTypeEnum.Integer)
throw new FileFormatException("Number of entries in this xref subsection not found.",parser.Position);
// Get the object number of the last object in this xref-table subsection!
int endObjectNumber = (int)parser.Token + startObjectNumber;
// 3. xref-table subsection entries.
for(
int index = startObjectNumber;
index < endObjectNumber;
index++
)
{
// Is the entry undefined?
if(xrefEntries[index].Usage == XRefEntry.UsageEnum.Undefined) // Undefined entry.
{
// 1. Get the indirect object offset!
parser.MoveNext();
int offset = (int)parser.Token;
// 2. Get the object generation number!
parser.MoveNext();
int generation = (int)parser.Token;
// 3. Get the usage tag!
parser.MoveNext();
String usageToken = (string)parser.Token;
XRefEntry.UsageEnum usage;
switch(usageToken)
{
case "n":
usage = XRefEntry.UsageEnum.InUse;
break;
case "f":
usage = XRefEntry.UsageEnum.Free;
break;
default:
throw new FileFormatException("Invalid xref entry.",parser.Position);
}
// 4. Entry initialization.
xrefEntries[index] = new XRefEntry(
index,
generation,
offset,
usage
);
}
else // Already-defined entry.
{
// Skip to the next entry!
parser.MoveNext(3);
}
}
}
}
#endregion
#endregion
#endregion
}
}
|