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!