Как запретить замене аннотации изменять положение и размер текста?

Я использую iTextSharp, чтобы заменить аннотации пишущей машинки текстовыми полями с одинаковым содержимым и позицией, но некоторые из полученных текстовых полей оказываются в разных позициях с разными размерами текста, несмотря на то, что в их данных, по-видимому, одни и те же данные hashMap,

Проблема заключается в том, что когда я создаю эти новые аннотации для этого образца PDF, а затем просматриваю PDF в Adobe Acrobat XI, у новых текстовых полей возникают следующие проблемы:

  • Они переместились из своего исходного положения (рядом со стрелками) на нижнюю страницу

  • Текст изменился в размере

  • Если щелкнуть правой кнопкой мыши новое текстовое поле, свойства недоступны

Я подозреваю, что все 3 проблемы связаны с одной основной проблемой, с которой я создаю новое Textbox.

Когда я проверяю /Rect ключ в hashMap за annot, он имеет те же координаты прямоугольника в том же порядке, что и оригинал freeTextAnnot, поэтому я не понимаю, почему некоторые аннотации заканчиваются смещением.

Вот мой код для создания новых текстовых полей с существующими данными аннотации пишущей машинки. Обратите внимание, что вам нужно установить inputPath а также outputPath к фактическому местоположению PDF и его пути назначения:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using iTextSharp;
using iTextSharp.text.pdf;
using System.IO;

namespace PDFConverterTester
{
    public class PdfModifierTester
    {
        public void testWithPaths()
        {
            //set to location of test PDF
            string inputPath = @"C:\InputPath\Before Conversion Dummy.pdf";
            //set to destination of new PDF
            string outputPath = @"C:\OutputPath\Before Conversion Dummy.pdf";
            test(inputPath, outputPath);
        }

        public void test(string inputPath, string outputPath)
        {
            PdfReader pdfReader = new PdfReader(inputPath);
            PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(outputPath, FileMode.Create));
            //get the PdfDictionary of the 1st page
            PdfDictionary pageDict = pdfReader.GetPageN(1);
            //get annotation array
            PdfArray annotArray = pageDict.GetAsArray(PdfName.ANNOTS);
            //iterate through annotation array
            int size = annotArray.Size;
            for (int i = size - 1; i >= 0; i--)
            {
                PdfDictionary dict = annotArray.GetAsDict(i);
                PdfName ITName = dict.GetAsName(new PdfName("IT"));
                if (ITName != null)
                {
                    if (ITName.Equals(new PdfName("FreeTextTypewriter")))
                    {
                        PdfAnnotation annot = copyToNewAnnotation_SSCCE(dict,pdfStamper);
                        pdfStamper.AddAnnotation(annot, 1);
                        annotArray.Remove(i);
                    }
                }
            }
            pdfStamper.Close();
            pdfReader.Close();
        }

        private PdfAnnotation copyToNewAnnotation_SSCCE(PdfDictionary freeTextAnnot,PdfStamper pdfStamper)
        {

            //need Rectangle for CreateFreeText()
            iTextSharp.text.Rectangle rect;
            PdfArray rectArray = freeTextAnnot.GetAsArray(PdfName.RECT);
            if (rectArray == null)
            {
                rect = null;
            }
            else
            {
                rect = new iTextSharp.text.Rectangle(getFloat(rectArray, 0),
                                                                          getFloat(rectArray, 1),
                                                                          getFloat(rectArray, 2),
                                                                          getFloat(rectArray, 3));
            }
            //create new annotation
            PdfContentByte pcb = new PdfContentByte(pdfStamper.Writer);
            PdfAnnotation annot = PdfAnnotation.CreateFreeText(pdfStamper.Writer, rect, "", pcb);
            //make array of all possible PdfName keys in dictionary for freeTextAnnot
            string pdfNames = "AP,BS,C,CA,CL,CONTENTS,CREATIONDATE,DA,DS,F,IT,LE,M,NM,P,POPUP,Q,RC,RD,ROTATE,SUBJ,SUBTYPE,T,TYPE";
            string[] pdfNameArray = pdfNames.Split(',');
            //iterate through key array copying key-value pairs to new annotation
            foreach (string pdfName in pdfNameArray)
            {
                //get value for this PdfName
                PdfName key = new PdfName(pdfName);
                PdfObject obj = freeTextAnnot.Get(key);
                //if returned value is null, maybe key is case-sensitive
                if (obj == null)
                {
                    //try with first letter only capitalized, e.g. "Contents" instead of "CONTENTS"
                    string firstCappdfName = char.ToUpper(pdfName[0]) + pdfName.Substring(1);
                    key = new PdfName(firstCappdfName);
                    obj = freeTextAnnot.Get(key);
                }
                //set key-value pair in new annotation
                annot.Put(key, obj);
            }
            //change annotation type to Textbox
            annot.Put(PdfName.SUBTYPE, PdfName.FREETEXT);
            annot.Put(new PdfName("Subj"), new PdfString("Textbox"));

            //set a default blank border
            annot.Put(PdfName.BORDER, new PdfBorderArray(0, 0, 0));

            return annot;


        }

        private float getFloat(PdfArray arr, int index)
        {
            return float.Parse(arr[index].ToString());
        }
    }
}

РЕДАКТИРОВАТЬ: Кажется, что часть проблемы может быть в призыве к pdfStamper.AddAnnotation(annot,1), так как annotзначения для /Rect Смена ключа после этого звонка. Например:

до AddAnnotation() вызов:

{[2401, 408.56, 2445.64, 693]}

после звонка:

{[1899, 2445.64, 2183.44, 2401]}

Так что, возможно, следующий код из PdfStamper.AddAnnotation() ( ссылка на источник), строки 1463-1493, несет ответственность, в настоящее время я изучаю эту возможность:

if (!annot.IsUsed()) {
                        PdfRectangle rect = (PdfRectangle)annot.Get(PdfName.RECT);
                        if (rect != null && (rect.Left != 0 || rect.Right != 0 || rect.Top != 0 || rect.Bottom != 0)) {
                            int rotation = reader.GetPageRotation(pageN);
                            Rectangle pageSize = reader.GetPageSizeWithRotation(pageN);
                            switch (rotation) {
                                case 90:
                                    annot.Put(PdfName.RECT, new PdfRectangle(
                                        pageSize.Top - rect.Top,
                                        rect.Right,
                                        pageSize.Top - rect.Bottom,
                                        rect.Left));
                                    break;
                                case 180:
                                    annot.Put(PdfName.RECT, new PdfRectangle(
                                        pageSize.Right - rect.Left,
                                        pageSize.Top - rect.Bottom,
                                        pageSize.Right - rect.Right,
                                        pageSize.Top - rect.Top));
                                    break;
                                case 270:
                                    annot.Put(PdfName.RECT, new PdfRectangle(
                                        rect.Bottom,
                                        pageSize.Right - rect.Left,
                                        rect.Top,
                                        pageSize.Right - rect.Right));
                                    break;
                            }
                        }
                    }
                }

1 ответ

Решение

Причина

Вы сами нашли причину изменения положения и размеров аннотации:

Кажется, что часть проблемы может быть в вызове pdfStamper.AddAnnotation(annot,1), так как annotзначения для /Rect Смена ключа после этого звонка.

... код из PdfStamper.AddAnnotation() (ссылка на источник), строки 1463-1493, отвечает

Действительно, этот код изменяет прямоугольник аннотации, если страница поворачивается.

Обоснование этого заключается в том, что для повернутых страниц iText пытается поднять бремя добавления поворота и перевода к содержимому страницы, необходимое для рисования вертикального текста и создания источника системы координат в левом нижнем углу страницы на плечах пользователей, чтобы пользователям вообще не нужно заниматься ротацией страниц. Следовательно, это также делает для аннотаций.

Для содержимого страницы есть PdfStamper имущество RotateContents по умолчанию true что позволяет отключить это, если кто-то явно не хочет этого вращения и перевода. К сожалению, для аннотаций подобного свойства нет, их позиции и размеры всегда "исправляются", то есть поворачиваются и переводятся.

Обходной путь

Поскольку вращение страницы является триггером для iText для поворота и перемещения прямоугольника, можно просто удалить вращение страницы, прежде чем манипулировать аннотациями, и добавить его снова позже:

PdfDictionary pageDict = pdfReader.GetPageN(1);
// hide the page rotation
PdfNumber rotation = pageDict.GetAsNumber(PdfName.ROTATE);
pageDict.Remove(PdfName.ROTATE);
//get annotation array
PdfArray annotArray = pageDict.GetAsArray(PdfName.ANNOTS);
//iterate through annotation array
int size = annotArray.Size;
for (int i = size - 1; i >= 0; i--)
{
    ...
}
// add page rotation again if required
if (rotation != null)
    pageDict.Put(PdfName.ROTATE, rotation);
pdfStamper.Close();

С этим дополнением аннотации остаются на месте.

Недостающие свойства

Вы также заметили:

Если щелкнуть правой кнопкой мыши новое текстовое поле, свойства недоступны

Это связано с тем, что вы не изменили запись intent (IT), поэтому они по-прежнему содержали FreeTextTypewriter, поэтому Adobe Reader не уверен, что это за объект, и поэтому не предлагает диалоговое окно свойств. Если вы также измените намерение:

//change annotation type to Textbox
annot.Put(PdfName.SUBTYPE, PdfName.FREETEXT);
annot.Put(new PdfName("IT"), PdfName.FREETEXT); // <======
annot.Put(new PdfName("Subj"), new PdfString("Textbox"));

вы получите диалог свойств.

Как в сторону

Ваш метод getFloat сначала вызвал самые странные изменения в системах координат для меня, потому что моя локаль не использует точки в качестве десятичного разделителя.

Я изменил это на это, чтобы сделать его независимым от локали:

private float getFloat(PdfArray arr, int index)
{
    return arr.GetAsNumber(index).FloatValue;
}

Альтернативный подход

Есть ли конкретная причина, по которой вы заменяете оригинальную аннотацию вместо простого ее редактирования? Например:

public void AlternativeReplaceFreetextByTextbox(string InputPath, string OutputPath)
{
    PdfName IT = new PdfName("IT");
    PdfName FREETEXTTYPEWRITER = new PdfName("FreeTextTypewriter");
    using (PdfReader Reader = new PdfReader(InputPath))
    {
        PdfDictionary Page = Reader.GetPageN(1);
        PdfArray Annotations = Page.GetAsArray(PdfName.ANNOTS);
        foreach (PdfObject Object in Annotations)
        {
            PdfDictionary Annotation = (PdfDictionary)PdfReader.GetPdfObject(Object);
            PdfName Intent = Annotation.GetAsName(IT);
            if (FREETEXTTYPEWRITER.Equals(Intent))
            {
                // change annotation type to Textbox
                Annotation.Put(IT, PdfName.FREETEXT);
            }
        }

        using (PdfStamper Stamper = new PdfStamper(Reader, new FileStream(OutputPath, FileMode.Create)))
        { }
    }

}
Другие вопросы по тегам