Объединение форм с сохранением шрифтов форм в itext7 - PullRequest
0 голосов
/ 30 мая 2020

Я пытаюсь заполнить и объединить несколько форм без выравнивания (необходимо, чтобы они были интерактивными для пользователей). Однако я заметил проблему. У меня есть файлы PDF, содержащие формы, которые я пытаюсь заполнить. Поля формы имеют свои шрифты, установленные в Adobe PDF. Я замечаю, что после объединения форм поля теряют свой исходный шрифт. Вот моя программа.

using iText.Forms;
using iText.Kernel.Pdf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace PdfCombineTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Stream file1;
            Stream file2;
            using (var stream = new FileStream("./pdf-form-1.pdf", FileMode.Open, FileAccess.Read))
            {
                file1 = Program.Fill(stream, new[] { KeyValuePair.Create("Text1", "TESTING"), KeyValuePair.Create("CheckBox1", "Yes") });
            }

            using (var stream = new FileStream("./pdf-form-2.pdf", FileMode.Open, FileAccess.Read))
            {
                file2 = Program.Fill(stream, new[] { KeyValuePair.Create("Text2", "text 2 text") });
            }

            using (Stream output = Program.Combine(new[] { file1, file2 }))
            {
                using (var fileStream = File.Create("./output.pdf"))
                {
                    output.CopyTo(fileStream);
                }
            }
        }

        public static Stream Combine(params Stream[] streams)
        {
            MemoryStream copyStream = new MemoryStream();
            PdfWriter writer = new PdfWriter(copyStream);
            writer.SetSmartMode(true);
            writer.SetCloseStream(false);
            PdfPageFormCopier formCopier = new PdfPageFormCopier();

            using (PdfDocument combined = new PdfDocument(writer))
            {
                combined.InitializeOutlines();

                foreach (var stream in streams)
                {
                    using (PdfDocument document = new PdfDocument(new PdfReader(stream)))
                    {
                        document.CopyPagesTo(1, document.GetNumberOfPages(), combined, formCopier);
                    }
                }
            }

            copyStream.Seek(0, SeekOrigin.Begin);
            return copyStream;
        }

        public static Stream Fill(Stream inputStream, IEnumerable<KeyValuePair<string, string>> keyValuePairs)
        {
            MemoryStream outputStream = new MemoryStream();
            PdfWriter writer = new PdfWriter(outputStream);
            writer.SetCloseStream(false);

            using (PdfDocument document = new PdfDocument(new PdfReader(inputStream), writer))
            {
                PdfAcroForm acroForm = PdfAcroForm.GetAcroForm(document, true);
                acroForm.SetGenerateAppearance(true);
                IDictionary<string, iText.Forms.Fields.PdfFormField> fields = acroForm.GetFormFields();


                foreach (var kvp in keyValuePairs)
                {
                    fields[kvp.Key].SetValue(kvp.Value);
                }
            }

            outputStream.Seek(0, SeekOrigin.Begin);
            return outputStream;
        }
    }
}

После нескольких часов отладки я заметил, что PdfPageFormCopier исключает ресурсы по умолчанию, которые содержат шрифты, при объединении полей формы. Есть ли способ обойти это? Проект, над которым я сейчас работаю, выполняет этот процесс в ItextSharp и работает по назначению. Однако мы планируем перейти на iText7.

Вот ссылки на некоторые образцы PDF-файлов, которые я сделал. Я не могу загрузить фактические PDF-файлы, с которыми я работаю, но они отображают ту же проблему.

https://www.dropbox.com/s/pukt91d4xe8gmmo/pdf-form-1.pdf?dl=0 https://www.dropbox.com/s/c52x6bc99gnrvo6/pdf-form-2.pdf?dl=0

1 Ответ

0 голосов
/ 03 июня 2020

Итак, моим решением было изменить класс PdfPageFormCopier из iText. Основная проблема заключается в функции ниже.

        public virtual void Copy(PdfPage fromPage, PdfPage toPage) {
            if (documentFrom != fromPage.GetDocument()) {
                documentFrom = fromPage.GetDocument();
                formFrom = PdfAcroForm.GetAcroForm(documentFrom, false);
            }
            if (documentTo != toPage.GetDocument()) {
                documentTo = toPage.GetDocument();
                formTo = PdfAcroForm.GetAcroForm(documentTo, true);
            }
            if (formFrom == null) {
                return;
            }
            //duplicate AcroForm dictionary
            IList<PdfName> excludedKeys = new List<PdfName>();
            excludedKeys.Add(PdfName.Fields);
            excludedKeys.Add(PdfName.DR);
            PdfDictionary dict = formFrom.GetPdfObject().CopyTo(documentTo, excludedKeys, false);
            formTo.GetPdfObject().MergeDifferent(dict);
            IDictionary<String, PdfFormField> fieldsFrom = formFrom.GetFormFields();
            if (fieldsFrom.Count <= 0) {
                return;
            }
            IDictionary<String, PdfFormField> fieldsTo = formTo.GetFormFields();
            IList<PdfAnnotation> annots = toPage.GetAnnotations();
            foreach (PdfAnnotation annot in annots) {
                if (!annot.GetSubtype().Equals(PdfName.Widget)) {
                    continue;
                }
                CopyField(toPage, fieldsFrom, fieldsTo, annot);
            }
        }

Конкретно здесь строка.

excludedKeys.Add(PdfName.DR);

Если вы пройдете код в функции CopyField (), в конечном итоге вы закончите PdfFormField класс. Вы можете увидеть конструктор ниже.

        public PdfFormField(PdfDictionary pdfObject)
            : base(pdfObject) {
            EnsureObjectIsAddedToDocument(pdfObject);
            SetForbidRelease();
            RetrieveStyles();
        }

Функция RetrieveStyles () попытается установить шрифт для поля на основе внешнего вида по умолчанию. Однако это не сработает. Из-за функции ниже.

        private PdfFont ResolveFontName(String fontName) {
            PdfDictionary defaultResources = (PdfDictionary)GetAcroFormObject(PdfName.DR, PdfObject.DICTIONARY);
            PdfDictionary defaultFontDic = defaultResources != null ? defaultResources.GetAsDictionary(PdfName.Font) : 
                null;
            if (fontName != null && defaultFontDic != null) {
                PdfDictionary daFontDict = defaultFontDic.GetAsDictionary(new PdfName(fontName));
                if (daFontDict != null) {
                    return GetDocument().GetFont(daFontDict);
                }
            }
            return null;
        }

Вы видите, что он пытается увидеть, существует ли шрифт в ресурсах по умолчанию, которые были явно исключены в классе PdfPageFormCopier. Он никогда не найдет шрифт.

Итак, я решил создать собственный класс, реализующий интерфейс IPdfPageExtraCopier. Я скопировал код из класса PdfPageFormCopier и удалил одну строку, исключая ресурсы по умолчанию. Затем я использую свой собственный класс копировального устройства в своем коде. Не самое красивое решение, но оно работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...