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

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

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

  • 它们已经从原始位置(与箭头相邻)移动到页面下方

  • 文字的大小已经改变

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

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

当我在hashMap中检查annot/Rect键时,它具有与原始freeTextAnnot相同顺序的相同矩形坐标,所以我不明白为什么某些注释最终会被取代。

这是我使用现有Typewriter注释数据创建新文本框的代码。 请注意,您需要将inputPathoutputPath设置为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, eg "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)的调用中,因为在进行此调用之后, /Rect键的annot值会发生变化。 例如:

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; } } } } 

原因

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

似乎问题的一部分可能在于对pdfStamper.AddAnnotation(annot,1)的调用,因为在进行此调用之后, /Rect键的annot值会发生变化。

…代码来自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))) { } } }