Skip to content

The ObjectsContext Object

gal kahana edited this page Dec 10, 2018 · 8 revisions

The ObjectsContext object (and layer) is responsible for writing simple PDF objects. Objects such as strings, dates, numbers, dictionaries and streams. As such it is a very important object to become familiar with, for someone who wishes to extend the PDF library. with it you can create new objects, start a dictionary or stream and allocate object IDs for later referencing. A true delight. (i know, i’m using it a lot. obviously).
you can find its header file here

So here are some of its methods.

Free context writing

if you really want to go out of the management of the library and write just free code into the PDF file use these methods:

IByteWriterWithPosition* StartFreeContext()
void EndFreeContext()

StartFreeContext will provide you a stream pointer that you can use for writing buffers directly to the stream.
use EndFreeContext to mark for the library that you are finished with writing free buffers, and things can go back to normal. yeah, as might be implied using any library command while writing to the free context is not recommended.

Object registry

The IndirectObjectsReferenceRegistry object can be retrieved via the GetInDirectObjectsRegistry method of the ObjectsContext. The object manages the usage of PDF Indirect object IDs in the PDF file. it is the object that if finally used for creating the Xref table. It is very useful if you want to allocate a free object ID to be used in the create of a new object, when extending the library (supported objects do such allocation automatically and don’t require the user to allocate the ID for them).
To allocate a new Id use the AllocateNewObjectID method of the IndirectObjectsReferenceRegistry object.
In PDF file modification scenarios you can use the DeleteObject method in order to delete an object from the modified PDF model.

Objects writing

simple objects may be written using the ObjectsContext commands. all such commands take as a second parameter a token separator. the Token separator determines how the next object will be separated from this one. options are – space, next line or none. The following lists the relevant commands:

  1. void WriteName(const string& inName,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write a name to the file. the first parameter is the name to write. the method will prefix the name with a forward slash (/) symbol.
  2. void WriteInteger(long long inIntegerToken,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write an integer to the file.
  3. void WriteLiteralString(const string& inString,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Writes a string to the file. will surround the string with parenthesis.
  4. void WriteHexString(const string& inString,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write a hex string to the file. will surround the string with angel brackets.
  5. void WriteIndirectObjectReference(ObjectIDType inIndirectObjectID,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write an object reference to the file, e.g. “1 0 R”, for object ID 1. ObjectIDType is a type used for
    object IDs. you can normally retrieve an object ID from relevant objects in the library. you can also allocate new object IDs using the IndirectObjectsReferenceRegistry object (see above).
  6. void WriteDouble(double inDoubleToken,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write a double number to the file.
  7. void WriteBoolean(bool inBooleanToken,ETokenSeparator inSeparate = eTokenSeparatorSpace)
    Write a boolean object to the file. true values will be written as “true”, false values will be written as “false”.
  8. void WriteKeyword(const string& inKeyword)
    Write a keyword. A keyword is a PDF command. normally it is the last one in the line, and hence the method will automatically move to the next line. an example to a keyword is “obj” in the statement “1 0 obj”
  9. void WriteComment(const string& inCommentText)
    Write a comment line to the file. The text will be prefixed by a percent mark (%). the method will move to the next line after the comment text is written.

Dictionary writing

The following methods are used for writing dictionaries:
DictionaryContext* StartDictionary()
EStatusCode EndDictionary(DictionaryContext* inDictionaryContext)

Dictionaries are objects that contain key-value pairs. When you use the StartDictionary to start a dictionary it will return a DictionaryContext object. The dictionary context object allows you to write keys and value. while writing in the dictionary context it is possible to use the ObjectsContext where the DictionaryContext object does not provide (it does have some method duplication with the ObjectsContext that also take care of indentation). The DictionaryContext object make sure that you don’t define a key more than once, by failing a key write if such a key already exists.
Use EndDictionary to finish writing the dictionary.

it is possible to call StartDictionary in the context of a dictionary. this will create a nested dictionary. using the dictionary objects writing commands will maintain proper indentation. The EndDictionary command will make sure that you are not ending the dictionary which is not “now” in context. it will fail if the wrong dictionary is ended.

Array writing

The following methods are used for writing arrays:
void StartArray()
void EndArray(ETokenSeparator inSeparate = eTokenSepratorNone)

StartArray simply places a left bracket and separates with a space. EndArray places a right bracket and the requested separator.

Indirect Objects Writing

the following method handle objects writing:
ObjectIDType StartNewIndirectObject()
StartNewIndirectObject(ObjectIDType inObjectID)
void EndIndirectObject()

Indirect objects are used throughout the PDF file for defining objects that may later be used by reference from other objects.
The two overloads of StartNewIndirectObject starts the writing of an object by placing an “obj” keyword, e.g. “1 0 obj”. The override receiving an ObjectIDType will use this object ID as the starting object ID. this ID was probably initially allocated with the IndirectObjectsReferenceRegistry object described above. The overload that returns an Object ID simply start an object and allocate an ID which is returned to the user for later referencing.

EndIndirectObject is used for finishing an object write, by placing the “endobj” keyword.

For scenarios where modification is used (through ModifyPDF method of PDFWriter class), it is also possibly to manipulate the modified document objects. If you want to update an existing object use this method to start the object instead of StartNewIndirectObject:
void StartModifiedIndirectObject(ObjectIDType inObjectID)

when done wiht the object use EndIndirectObject as is the case for new objects.

Streams objects writing

The following methods are used for writing PDF Stream objects
void SetCompressStreams(bool inCompressStreams)
PDFStream* StartPDFStream(DictionaryContext* inStreamDictionary=NULL)
PDFStream* StartUnfilteredPDFStream(DictionaryContext* inStreamDictionary=NULL)
void EndPDFStream(PDFStream* inStream)

SetCompressStreams allows you to determine whether streams are to be compressed by the library or not. by default (as set by the PDFWriter configuration object) all streams are to be compressed with Flate (zlib) compression. if set to “false”, compression will be avoided.

StartPDFStream starts a PDF stream object represented by a PDFStream object. The PDFStream object has a few methods of interest. the one most important ones are GetWriteStream() and FinalizeStreamWrite(). The former returns the stream object to which you can write buffers, and the latter Finalizes a stream write, and calculates its length.
You would normally just use GetWriteStream from the PDFStream object, and for actually finalizing it use the ObjectsContext EndPDFStream method. This method will write the end stream required content such as the ending keyword and the extent (length) object.

Important – Streams MUST always be written inside an indirect object definition. So make sure to call StartNewIndirectObject (or when modifying, StartModifiedIndirectObject), before starting the stream, and make sure you are clear of any object writing at the time. Also, EndPDFStream will take care of ending the indirect object (mainly to write another object, the length), so DO NOT call EndIndirectObject after calling EndPDFStream. don’t worry about it – the stream indirect will be ended internally.

Use StartUnfilteredPDFStream if you want to write a particular stream without compressing it. This is useful when you are writing an already compressed stream and don’t need flate compression. in this case make sure to add the decompression filter to the stream dictionary.

Note that both StartPDFStream and StartUnfilteredPDFStream receive an optional DictionaryContext object. This dictionary marks the stream dictionary, when the user wishes to add more keys than just the basic stream keys. this is useful when writing complex objects which are more than just Streams, like Image XObjects or Form XObjects.

Extensibility

void SetObjectsContextExtender(IObjectsContextExtender* inExtender)

ObjectsContext offers some method of extensibility for stream handling. Extensibility in ObjectsContext is aimed for extending the compression features as provided by the library.
To extend ObjectsContext use the SetObjectsContextExtender method. This method sets ObjectsContex with an extender object implementing the IObjectsContextExtender interface (you can also extend the ObjectsContextExtenderAdapter adapter class to implement only part of the methods).

The IObjectContextExtender interface includes 3 methods:

  1. virtual bool OverridesStreamCompression()
    use this method to signal ObjectsContext that compression should be taken care of by the extender class. if returns true then every time compression is required the other extender methods will be used to provide an alternative stream, which should implement the required compression.
  2. virtual IByteWriter* GetCompressionWriteStream(IByteWriterWithPosition* inOutputStream)
    GetCompressionWriteStream is called when a PDFStream Object is created, in case compression was required, and OverridesStreamCompression returns true. Input is the stream to write to. the expected output is the stream to use instead for writing. the stream implementation should include the required compression.
  3. virtual void FinalizeCompressedStreamWrite(IByteWriter* inCompressedStream)
    FinalizeCompressedStreamWrite is called when the PDFStream is finalized. it recieves as input the compressed stream that was returned from GetCompressionWriteStream. The implementation should finalize the compression stream write. Any remaning footer buffers should be written now. This would allow the PDFStream to calculate the extent on the actual write stream (given as input for GetCompressionWriteStream).
Clone this wiki locally