Question:
Is it possible to embed a file in a PDF document when I only have a stream?
Answer:
Yes it is possible.
All file streams are based on the SDF/COS Object Model and that includes embedded files. If you have access to the stream of a file but not the file on disk you can embed that file within a PDF by creating the proper Obj objects and inserting them into the document.
The following C# pseudo-code mimics the sample shown here under the section How to Embed an XML in a PDF. The difference is that instead of using a file path to create a FileSpec object it mimics the FileSpec object by creating matching low-level Obj objects.
Be sure to replace CreateYourFileStream pseudo-code with your actual code to generate/retrieve your FileStream.
byte[] doc_bytes_invoice_added = null;
using (PDFDoc in_doc = new PDFDoc(input_path + "fish.pdf"))
{
in_doc.InitSecurityHandler();
NameTree files = NameTree.Create(in_doc, "EmbeddedFiles");
// Manually mimic the FileSpec object.
Obj fsDict = doc.CreateIndirectDict();
fsDict.PutName("AFRelationship", "Alternative");
fsDict.PutString("F", input_path + "ZUGFeRD-invoice.xml");
fsDict.PutString("UF", input_path + "ZUGFeRD-invoice.xml");
fsDict.PutText("Desc", "Your file description here");
// Replace 'CreateYourFileStream()' with the actual code to create the stream.
FileStream fs = CreateYourFileStream();
// Create a new MemoryFilter to add your filestream to the document
pdftron.Filters.MemoryFilter memoryFilter = new pdftron.Filters.MemoryFilter((int)fs.Length, false); // false = sink
pdftron.Filters.FilterWriter writer = new pdftron.Filters.FilterWriter(memoryFilter); // helper filter to allow us to write to buffer
int bytes_read = 0;
__byte[] buf = new byte[10 * 1024]; // 10 MiB buffer__
do
{
Console.WriteLine(bytes_read);
bytes_read = fs.Read(buf, 0, buf.Length);
if (bytes_read < buf.Length)
{
for (int i = 0; i < bytes_read; i++)
{
writer.WriteUChar(buf[i]);
}
}
else
{
writer.WriteBuffer(buf);
}
} while (bytes_read > 0);
writer.Flush();
// switch from sink to source
memoryFilter.SetAsInputFilter();
FilterReader fr = new FilterReader(memoryFilter);
// Add a new dict to the main object that will contain the file stream
Obj fsObj = fsDict.PutDict("EF");
Obj fileStreamObj = doc.CreateIndirectStream(fr);
// Add the file stream
fsObj.Put("F", fileStreamObj);
byte[] file1_name = System.Text.Encoding.UTF8.GetBytes("ZUGFeRD-invoice.xml");
Obj collection = in_doc.GetRoot().FindObj("Collection");
if (collection == null) collection = in_doc.GetRoot().PutDict("Collection");
collection.PutName("View", "T");
doc_bytes_invoice_added = in_doc.Save(SDFDoc.SaveOptions.e_incremental);
}