galkahana/PDF-Writer

Streams objects writing problem

tangdouer1005 opened this issue · 2 comments

I now want to write an object like this

8 0 obj
<< /Type /EmbeddedFile /Length 35 >> stream
This is a text file attachment...

endstream 
endobj

I've successfully inserted content into the stream, but I'm having trouble incrementing the key.I used this function

PDFStream* StartPDFStream(DictionaryContext* inStreamDictionary=NULL,bool inForceDirectExtentObject = false);

as it says in the wiki,I seem to be able to pass in a DictionaryContext parameter to accomplish my goal

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.

So I wrote the code like this, but it produces wrong results

DictionaryContext *dictionaryContext = new DictionaryContext(&objectsContext, 0);
dictionaryContext->WriteKey("Type");
dictionaryContext->WriteNameValue("EmbeddedFile");
PDFStream *pdfStream = objectsContext.StartPDFStream(dictionaryContext);
IByteWriter *mwriter = pdfStream->GetWriteStream();
mwriter->Write(inAttachment->FileContent, inAttachment->Lenth);
objectsContext.EndPDFStream(pdfStream);

Here is the generated object, it seems to be missing a symbol '">>".

1 0 obj
<<
	/Type /EmbeddedFile
	/Filter /FlateDecode
	/Length 2 0 R
stream
x淜LJNI� �??
endstream
endobj

I would like to know what is the correct way? Or if you need any other information please let me know, thank you!

Hi,
I think that what you are doing is mostly what should be done. There's some usages of StartPDFStream with a dictionary in PDFWriter itself, that if im comparing to have a slight difference. For exmaple, here's code for writing CFF fonts that uses StartPDFStream in the same manner you're trying to use it:
https://github.com/galkahana/PDF-Writer/blob/master/PDFWriter/CFFEmbeddedFontWriter.cpp#L95

The key difference is that creating the dictionary context is done via the ObjectsContext::StartDictionary method as opposed to creating a new dictionary object directly. so, change your code to this for it to work:

DictionaryContext *dictionaryContext = objectsContext->StartDictionary();
dictionaryContext->WriteKey("Type");
dictionaryContext->WriteNameValue("EmbeddedFile");
PDFStream *pdfStream = objectsContext.StartPDFStream(dictionaryContext);
IByteWriter *mwriter = pdfStream->GetWriteStream();
mwriter->Write(inAttachment->FileContent, inAttachment->Lenth);
objectsContext.EndPDFStream(pdfStream);

The reason for the mishap in the code you ran is that dictionaries must be registered at ObjectsContext in an attempt to make starting and ending dictionary consistent (it makes sure that StartDictionary and EndDictionary are called on the same dictionary in case of nesting). StartPDFStream calls EndDictionary, but since the dictionary in the code sample you provided is NOT registered in ObjectsContext there's a failure and so the dictionary end writing is skipped. StartPDFStream does not seem to check the return status code from EndDictionary (there's only a log if you activate logging) which is why it slilengtly continues to just writing the stream keyword and the rest of the stream. (here's some documentation about StartDictionary/EndDictionary in more documentation about the ObjectsContext - https://github.com/galkahana/PDF-Writer/wiki/The-ObjectsContext-Object#dictionary-writing)

i'm gonna fix StartPDFStream to fail if EndDictionary doesn't end well and return null stream pointer...which would very loudly indicate a problem.

yes! With your help, I made it!
In fact, I tried to use objectsContext.StartDictionary() to create DictionaryContext, but I always used this function objectsContext.EndDictionary(DictionaryContext* inDictionaryContext) by mistake, which would lead to Segmentation fault error.
like this.

        result.second = objectsContext.StartNewIndirectObject();
        DictionaryContext *dictionaryContext = objectsContext.StartDictionary();
        dictionaryContext->WriteKey("Type");
        dictionaryContext->WriteNameValue("EmbeddedFile");
        objectsContext.EndDictionary(dictionaryContext);           // my mistake
        PDFStream *pdfStream = objectsContext.StartPDFStream(dictionaryContext);
        IByteWriter *mwriter = pdfStream->GetWriteStream();
        mwriter->Write(inAttachment->FileContent, inAttachment->Lenth);
        objectsContext.EndPDFStream(pdfStream);

I used to think it was because objectsContext could not create other objects before PDFStream was created.
as it says in the wiki

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.

In fact, I misunderstood wiki.
Now I understand that the reason for my Segmentation fault is that I repeatedly released the DictionaryContext, but I didn't think of this at the time.

thanks!