/*
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.files;
using it.stefanochizzolini.clown.tokens;
using System;
namespace it.stefanochizzolini.clown.objects{
/**
<summary>PDF indirect object [PDF:1.6:3.2.9].</summary>
*/
public class PdfIndirectObject
: PdfObject,
IPdfIndirectObject
{
#region static
#region fields
private const string UsageFree = "f";
private const string UsageInUse = "n";
#endregion
#endregion
#region dynamic
#region fields
private PdfDataObject dataObject;
private File file;
private bool original;
private PdfReference reference;
private XRefEntry xrefEntry;
#endregion
#region constructors
/**
<param name="file">Associated file.</param>
<param name="dataObject">
<para>Data object associated to the indirect object.</para>
<ul>
<li>It MUST be null if the indirect object is original (i.e. coming from an existing file)
or free.</li>
<li>It MUST be NOT null if the indirect object is new and in-use.</li>
</ul>
</param>
<param name="xrefEntry">
<para>Cross-reference entry associated to the indirect object.</para>
<ul>
<li>If the indirect object is new, its offset field MUST be set to 0 (zero).</li>
</ul>
</param>
*/
internal PdfIndirectObject(
File file,
PdfDataObject dataObject,
XRefEntry xrefEntry
)
{
this.file = file;
this.dataObject = dataObject;
this.xrefEntry = xrefEntry;
this.original = (xrefEntry.Offset != 0);
this.reference = new PdfReference(
this,
xrefEntry.Number,
xrefEntry.Generation
);
}
#endregion
#region interface
#region public
public File File
{get{return file;}}
public override int GetHashCode(
)
{
/*
NOTE: Uniqueness should be achieved XORring the (local) reference hashcode
with the (global) file hashcode.
NOTE: DO NOT directly invoke reference.GetHashCode() method here as
it would trigger an infinite loop, as it conversely relies on this method.
*/
return reference.ID.GetHashCode() ^ file.GetHashCode();
}
public bool IsInUse(
)
{return (xrefEntry.Usage == XRefEntry.UsageEnum.InUse);}
public bool IsOriginal(
)
{return original;}
public void Update(
)
{
if(original)
{
/*
NOTE: It's expected that DropOriginal() is invoked by IndirectObjects indexer;
such an action is delegated because clients may invoke directly the indexer skipping
this method.
*/
file.IndirectObjects.Update(this);
}
}
#region IPdfIndirectObject
public override object Clone(
File context
)
{return context.IndirectObjects.Add(this);}
public PdfDataObject DataObject
{
get
{
if(dataObject == null)
{
/*
NOTE: indirect data object is null in 2 cases:
1) when the entry is free (no data object at all);
2) when the indirect object hasn't been initialized yet
because it comes from a parsed reference (late-bound data object).
In case 1 data object MUST keep itself null,
while in case 2 data object MUST be initialized.
*/
// Is the entry free [case 1]?
// NOTE: Free entries have NO indirect data object associated with.
if(xrefEntry.Usage == XRefEntry.UsageEnum.Free)
return null;
// In-use entry (late-bound data object [case 2]).
try
{
Parser parser = file.Reader.Parser;
// Retrieve the associated data object among the original objects!
parser.Seek(xrefEntry.Offset);
// Skip indirect-object header!
parser.MoveNext(4);
// Get the indirect data object!
dataObject = parser.ParsePdfObject();
}
catch(Exception e)
{throw e;}
}
return dataObject;
}
set
{
if(xrefEntry.Generation == XRefEntry.GenerationUnreusable)
throw new Exception("Unreusable entry.");
dataObject = value;
xrefEntry.Usage = XRefEntry.UsageEnum.InUse;
Update();
}
}
public void Delete(
)
{
if(file != null)
{
/*
NOTE: It's expected that DropFile() is invoked by IndirectObjects.Remove() method;
such an action is delegated because clients may invoke directly Remove() method,
skipping this method.
*/
file.IndirectObjects.RemoveAt(xrefEntry.Number);
}
}
public PdfIndirectObject IndirectObject
{get{return this;}}
public PdfReference Reference
{get{return reference;}}
#endregion
#endregion
#region internal
internal void DropFile(
)
{file = null;}
internal void DropOriginal(
)
{original = false;}
internal string Usage
{
get
{
switch(xrefEntry.Usage)
{
case XRefEntry.UsageEnum.Free:
return UsageFree;
case XRefEntry.UsageEnum.InUse:
return UsageInUse;
default: // Should NEVER happen.
throw new Exception("Invalid xref usage value.");
}
}
}
internal override void WriteTo(
IOutputStream stream
)
{
// Header.
stream.Write(reference.ID + " obj\n");
// Body.
DataObject.WriteTo(stream);
// Tail.
stream.Write("\nendobj\n");
}
#endregion
#endregion
#endregion
}
}
|