empira / PDFsharp

PDFsharp and MigraDoc Foundation for .NET 6 and .NET Framework
https://docs.pdfsharp.net/
Other
514 stars 125 forks source link

Exception when pointing multiple PdfFileSpecification to a single PdfEmbeddedFileStream #71

Open julienrffr opened 10 months ago

julienrffr commented 10 months ago

In 6.1.0-preview-1 (and in 6.0.0 as well),

I'd like to have multiple file attachments elements (with different names) all pointing to the same PdfEmbeddedFileStream file (in order to save space and not embed same binaries multiple times).

But I get an exception doing that due to the line: https://github.com/empira/PDFsharp/blob/b28bc3265e0a15302c04b7b91da0d915a591e167/src/foundation/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFileSpecification.cs#L31

Indeed, in the constructor of PdfFileSpecification, it tries to add the stream to the internals catalog, without cheking if the stream already has a reference.

I think this line should be under a if condition, checking if the stream already has a reference. Another solution would be to provide a constructor that does not attach the new PdfFileSpecification to any Document.

What would be your approach?

vivien7512 commented 10 months ago

In the PDF 1.4 specifications, https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.4.pdf page 149 , we can read

"A given external file may be referenced from more than one file specification. Therefore, when embedding a file with a given name, it is necessary to check for other occurrences of the same name as the value associated with the corresponding key in other file specification dictionaries."

image

So when you create a new PdfFileSpecification that references a PdfEmbeddedFileStream, you mustn't add PdfEmbeddedFileStream in the Internals dictionnary without checking whether it already exists in there. To do this properly, if 2 PdfFileSpecifications share the same PdfEmbeddedFileStream, they must have the same reference.

Maybe you can do something like

//PDFsharp\src\PdfSharp\Pdf.Advanced\PdfFileSpecification.cs
` Elements.SetName(Keys.Type, "/Filespec");

 Elements.SetString(Keys.F, _name);
 Elements.SetString(Keys.UF, _name);

 var embeddedFileDictionary = new PdfDictionary(Owner);
//NEW LINE 
 if (_embeddedFileStream.Reference == null)
     Owner.Internals.AddObject(_embeddedFileStream);
 embeddedFileDictionary.Elements.SetReference(Keys.F, _embeddedFileStream.Reference);
 embeddedFileDictionary.Elements.SetReference(Keys.UF, _embeddedFileStream.Reference);

 Elements.SetObject(Keys.EF, embeddedFileDictionary);`