我正在使用iTextSharp将Typewriter注释替换为具有相同内容和位置的文本框,但是一些生成的文本框最终会在不同的位置使用不同的文本大小,尽管看起来它们的数据完全相同hashMap.
问题是,当我在此示例PDF上创建这些新注释,然后在Adobe Acrobat XI中查看PDF时,新文本框会出现以下问题:
它们已经从原始位置(与箭头相邻)移动到页面下方
文字的大小已经改变
右键单击新文本框时,没有可用的属性
我怀疑所有3个问题都是由于我正在创建新文本框的一个潜在问题.
当我检查for中的/Rect键时,它具有与原始键相同的矩形坐标,因此我不明白为什么某些注释最终会被置换.hashMapannotfreeTextAnnot
这是我使用现有Typewriter注释数据创建新Textbox的代码.请注意,您需要设置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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:似乎问题的一部分可能在调用中pdfStamper.AddAnnotation(annot,1),因为在调用之后键annot的值/Rect发生变化.例如:
AddAnnotation()通话前:
{[2401, 408.56, 2445.64, 693]}
Run Code Online (Sandbox Code Playgroud)
通话后:
{[1899, 2445.64, 2183.44, 2401]}
Run Code Online (Sandbox Code Playgroud)
所以也许以下代码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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
您自己找到了注释位置和尺寸变化的原因:
似乎问题的一部分可能在调用中
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();
Run Code Online (Sandbox Code Playgroud)
通过此添加,注释保持不变.
你还观察到:
右键单击新文本框时,没有可用的属性
这是因为您没有更改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"));
Run Code Online (Sandbox Code Playgroud)
你会得到属性对话框.
您的方法getFloat首先在我的坐标系中引起了最奇怪的变化,因为我的语言环境不使用点作为小数分隔符.
我将其更改为此以使其与区域设置无关:
private float getFloat(PdfArray arr, int index)
{
return arr.GetAsNumber(index).FloatValue;
}
Run Code Online (Sandbox Code Playgroud)
是否有一个特定原因可以替换原始注释而不是简单地编辑它?例如:
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)))
{ }
}
}
Run Code Online (Sandbox Code Playgroud)