В вашем коде есть ряд проблем. Сначала я дам вам рабочую версию, а затем go в отношении проблем в вашем коде.
Рабочая версия (с важным ограничением)
Вы можете объединить два файла PDF, указанных в MemoryStream
экземпляров masterStream
и componentStream
и получить результат в том же MemoryStream
экземпляре masterStream
следующим образом:
using (MemoryStream temporaryStream = new MemoryStream())
{
masterStream.Position = 0;
componentStream.Position = 0;
using (PdfDocument combinedDocument = new PdfDocument(new PdfReader(masterStream), new PdfWriter(temporaryStream)))
using (PdfDocument componentDocument = new PdfDocument(new PdfReader(componentStream)))
{
componentDocument.CopyPagesTo(1, componentDocument.GetNumberOfPages(), combinedDocument);
}
byte[] temporaryBytes = temporaryStream.ToArray();
masterStream.Position = 0;
masterStream.Capacity = temporaryBytes.Length;
masterStream.Write(temporaryBytes, 0, temporaryBytes.Length);
masterStream.Position = 0;
}
Ограничение заключается в том, что вы должны иметь создание экземпляра masterStream
с возможностью расширения емкость ; класс MemoryStream
имеет ряд конструкторов, только некоторые из которых создают такой расширяемый экземпляр, в то время как другие создают неизменяемые экземпляры. Подробнее см. здесь .
Проблемы в вашей концепции и коде
Объединение файлов PDF не приводит к действительному объединенному PDF
Вы описываете свою концепцию как это
идея состоит в том, что, когда каждый компонент PDF создается, байты компонента PDF добавляются в основной поток
Это не работает, хотя Формат PDF не позволяет объединять PDF-файлы, просто объединяя их. В частности, (активные) объекты в PDF-файле имеют номер идентификатора, который должен быть уникальным в PDF-файле, конкатенация приведет к созданию файла с неуникальными идентификаторами объекта; PDF-файлы содержат структуры перекрестных ссылок, которые сопоставляют каждый идентификатор объекта с его смещением от начала файла, при объединении все эти смещения будут неправильными для добавленных PDF-файлов; кроме того, в PDF должен быть один root объект, из которого прямые или косвенные ссылки на другие объекты, конкатенация приведет к множеству root объектов.
Запись и немедленная перезапись
В вашем коде у вас есть
masterStream.WriteTo(masterMemoryStream);
// Read from master stream (ie. all existing components)
masterMemoryStream.Position = 0;
using (iText.Kernel.Pdf.PdfWriter masterPdfWriter = new iText.Kernel.Pdf.PdfWriter(masterMemoryStream))
Здесь вы пишете содержимое от masterStream
до masterMemoryStream
, затем устанавливаете позицию masterMemoryStream
в начало и создаете экземпляр PdfWriter
, который начинает писать там. Т.е. ваша оригинальная копия masterStream
содержимого будет перезаписана, конечно, не то, что вы хотели.
Использование MemoryStream.GetBuffer
MemoryStream.GetBuffer
не только возвращает данные, записанные в MemoryStream
по конструкции, но весь буфер; т. е. может быть много tra sh байтов после фактического PDF в том, что вы извлекаете здесь
updated = masterMemoryStream.GetBuffer();
Это может привести к тому, что процессоры PDF, пытающиеся обработать полученные PDF-файлы, не смогут открыть файл: PDFs у них указатель на последние перекрестные ссылки в их конце, поэтому, если у вас есть tra sh байт после фактического конца вашего PDF, процессоры PDF могут не найти этот указатель.
PS
Как выяснилось в комментариях, приведенный выше код отлично работает в случае постоянно растущей длины потока (что обычно происходит в случае использования под рукой), но в целом нужно ограничить размер потока перед записью нового контента, например, так :
...
masterStream.Position = 0;
masterStream.SetLength(temporaryBytes.Length); // <<<<
masterStream.Capacity = temporaryBytes.Length;
...