我正在使用itextsharp用具有相同内容和位置的文本框替换打字机注释,但是某些结果的文本框最终以不同的位置具有不同的文本尺寸他们的hashMap
中相同的数据。
问题是,当我在此示例PDF上创建这些新注释,然后在Adobe Acrobat XI中查看PDF时,新的文本框有以下问题:
-
他们已经从原始位置(与箭头相邻)移至页面上的降低
-
文本大小变化
-
当右键单击
时,没有属性可用
我怀疑所有3个问题都是由于我如何创建新的文本框的一个根本问题。
当我检查hashMap
中的/Rect
键以获取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:InputPathBefore Conversion Dummy.pdf";
//set to destination of new PDF
string outputPath = @"C:OutputPathBefore 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
的CC_9键更改的值。例如:
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)
的呼叫中,因为annot
的/Rect
键更改后进行了此调用。...
PdfStamper.AddAnnotation()
的代码(链接到源),第1463-1493行,负责
的确,该代码在旋转页面时更改注释矩形。
其背后的理由是,对于旋转页面,Itext试图增加旋转和翻译的负担,这样用户根本不必处理页面旋转。因此,它也是为注释。
对于页面内容,PdfStamper
属性 RotateContents
默认为true
,如果一个明确不希望此旋转和翻译,则可以关闭此内容。不幸的是,没有类似的注释属性,它们的位置和大小总是被"纠正",即旋转和翻译。
一个工作
由于页面旋转是旋转和翻译矩形的触发器,因此可以简单地删除页面旋转,然后再处理注释,然后再添加:
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;
}
另一种方法
有没有特定原因替换原始注释而不是简单地编辑它?例如:
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)))
{ }
}
}