如何防止替换注释更改位置和文本大小?

时间:2016-11-30 00:40:30

标签: c# pdf itext

我正在使用iTextSharp将Typewriter注释替换为具有相同内容和位置的文本框,但是一些结果文本框最终位于不同文本大小的不同位置,尽管它们{{1}中的数据似乎完全相同}}。

问题在于,当我在this sample PDF上创建这些新注释,然后在Adobe Acrobat XI中查看PDF时,新文本框会出现以下问题:

  • 他们已经从原来的位置(靠近箭头)移动到页面下方

  • 文字大小已改变

  • 右键单击新文本框时,没有属性

我怀疑所有3个问题都是由于我正在创建新文本框的一个潜在问题。

当我检查hashMap /RecthashMap的{​​{1}}密钥时,它的原始annot具有相同的矩形坐标,所以我不知道理解为什么有些注释最终会被取代。

这是我使用现有Typewriter注释数据创建新文本框的代码。请注意,您需要将freeTextAnnotinputPath设置为PDF的实际位置及其目标路径:

outputPath
编辑:似乎问题的一部分可能出在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值会发生变化。 E.g:

/Rect致电之前

AddAnnotation()
电话结束后

{[2401, 408.56, 2445.64, 693]}

因此,{[1899, 2445.64, 2183.44, 2401]} link to source)第1463-1493行的以下代码可能有责任,我正在研究这种可能性:

PdfStamper.AddAnnotation()

1 个答案:

答案 0 :(得分:7)

原因

您自己找到了注释位置和尺寸变化的原因:

  

似乎问题的一部分可能出在pdfStamper.AddAnnotation(annot,1)的调用中,因为annot键的/Rect值在此次调用后发生了变化。< / p>      

...来自PdfStamper.AddAnnotation()的代码(链接到源代码),第1463-1493行,负责

实际上,如果页面被旋转,该代码会更改注释矩形。

这背后的基本原理是,对于旋转页面,iText会尝试解除将旋转和翻译添加到绘制直立文本所需的页面内容的负担,并使坐标系原点位于用户页面的左下角。 ;因此,用户根本不必处理页面轮换。因此,它也适用于注释。

对于页面内容,PdfStamper属性RotateContents默认为true,如果明确不希望进行此轮换和转换,则允许将其关闭。不幸的是,注释没有类似的属性,它们的位置和大小总是得到纠正&#34;,即旋转和翻译。

解决方法

由于页面旋转是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();

添加注释后,注释就会存在。

缺少属性

您还观察到:

  

右键单击新文本框时没有可用的属性

这是因为您没有更改意图( 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;
}

另一种方法

是否有一个特定原因可以替换原始注释而不是简单地编辑它? E.g:

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)))
        { }
    }

}