我正在尝试重命名子类的复选框。假设复选框的名称是MyForm.Check1.page0。我跑的时候:
reader.AcroField.RenameField("MyForm.Check1.page0", "MyForm.Check1.newName");
该复选框仅重命名为“newName”。子类信息被删除。我从文档中得到了子类无法更改的信息,但这是出乎意料的。
根据文件:
“重命名字段。只能重命名名称的最后部分 例如,如果原始字段是“ab.cd.ef”,则只有“ef”部分可以 更名“。
这是漫长的一天,但我会读到这意味着您可以将子类“ab.cd.ef”的字段重命名为“ab.cd.yz”,而不是重命名为“ab.cd” .ef“to”ab.cd.yz“你最终会得到一个名为”yz“的字段。
GitHub上的I found the source class对我来说它看起来像个错误。来自GitHub:
public bool RenameField(String oldName, String newName) {
int idx1 = oldName.LastIndexOf('.') + 1;
int idx2 = newName.LastIndexOf('.') + 1;
<snip>
Item item = fields[oldName];
newName = newName.Substring(idx2);
我认为问题在于最后一行。如果这是设计的话,对我来说似乎很奇怪。我想我可以解决它,但它再次看起来很奇怪。
修改 的
我已经复制并清理了代码并制作了显示此问题的sample command line tool。您只需下载副本并更改PDF的路径即可。这一切都来自一个更大的应用程序,因此它作为一个测试应用程序有点臃肿,但它比尝试重写所有内容更快。有些代码有点草率,因为它正在进行中。我还遗漏了一些与此问题无关的代码(即我插入的JavaScript代码)。
如果您更喜欢DropBox以外的传送机制,请告诉我。
我也在粘贴下面.cs文件的详细信息。您应该只能将其粘贴到C#项目中,它应该可以工作。您将需要一个带有文本字段的PDF,其名称设置为“TableStartPosition”,或者在FillCoverPage方法中调整fieldPositions对象并更新硬编码路径。
运行应用程序时,会添加一些复选框,这些复选框被分配名称“InkSaver.chk2.pageX”(其中X是数字)。但是在运行结束时,复选框只是命名为“pageX”。您可以在RenameFields()方法中查看代码。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace RenameFieldTest
{
class Program
{
Stream _pdfTemplateStream;
MemoryStream _pdfResultStream;
PdfReader _pdfTemplateReader;
PdfStamper _pdfResultStamper;
static void Main(string[] args)
{
Program p = new Program();
try
{
p.RunTest();
}
catch (Exception f)
{
Console.WriteLine(f.Message);
Console.ReadLine();
}
}
internal void RunTest()
{
FileStream fs = File.OpenRead(@"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\CoverPage.pdf");
_pdfTemplateStream = fs;
_pdfResultStream = new MemoryStream();
//PDFTemplateStream = new FileStream(_templatePath, FileMode.Open);
_pdfTemplateReader = new PdfReader(_pdfTemplateStream);
_pdfResultStamper = new PdfStamper(_pdfTemplateReader, _pdfResultStream);
#region setup objects
List<CustomCategory> Categories = new List<CustomCategory>();
CustomCategory c1 = new CustomCategory();
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[2]);
Categories.Add(c1);
CustomCategory c2 = new CustomCategory();
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[0]);
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
Categories.Add(c2);
List<CustomObject> Items = new List<CustomObject>();
CustomObject co1 = new CustomObject();
co1.Category = c1;
co1.Title = "Object 1";
Items.Add(co1);
CustomObject co2 = new CustomObject();
co2.Category = c2;
co2.Title = "Object 2";
Items.Add(co2);
#endregion
FillCoverPage(Items);
_pdfResultStamper.Close();
_pdfTemplateReader.Close();
List<MemoryStream> pdfStreams = new List<MemoryStream>();
pdfStreams.Add(new MemoryStream(_pdfResultStream.ToArray()));
MergePdfs(@"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\Outfile.pdf", pdfStreams);
_pdfResultStream.Dispose();
_pdfTemplateStream.Dispose();
}
internal void FillCoverPage(List<CustomObject> Items)
{
//Before we start we need to figure out where to start adding the table
var fieldPositions = _pdfResultStamper.AcroFields.GetFieldPositions("TableStartPosition");
if (fieldPositions == null)
{ throw new Exception("Could not find the TableStartPosition field. Unable to determine point of origin for the table!"); }
_pdfResultStamper.AcroFields.RemoveField("TableStartPosition");
var fieldPosition = fieldPositions[0];
// Get the position of the field
var targetPosition = fieldPosition.position;
//First, get all the available card sizes
List<string> availableSizes = CustomCategory.AvailableSizes;
//Generate a table with the number of available card sizes + 1 for the device name
PdfPTable table = new PdfPTable(availableSizes.Count + 1);
float[] columnWidth = new float[availableSizes.Count + 1];
for (int y = 0; y < columnWidth.Length; y++)
{
if (y == 0)
{ columnWidth[y] = 320; }
else
{ columnWidth[y] = 120; }
}
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
PdfContentByte canvas;
List<PdfFormField> checkboxes = new List<PdfFormField>();
//Build the header row
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
//Insert the global check boxes
PdfPCell[] globalRow = new PdfPCell[availableSizes.Count + 1];
Phrase tPhrase = new Phrase("Select/Unselect All");
PdfPCell tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
tCell.AddElement(tPhrase);
globalRow[0] = tCell;
for (int x = 0; x < availableSizes.Count; x++)
{
tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.Global.chk{0}", availableSizes[x].Replace(".", ""));
//f.FieldName = fieldName;
string js = string.Format("hideAll(event.target, '{0}');", availableSizes[x].Replace(".", ""));
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
globalRow[x + 1] = tCell;
checkboxes.Add(f);
}
table.Rows.Add(new PdfPRow(globalRow));
int status = 0;
int pageNum = 1;
for (int itemIndex = 0; itemIndex < Items.Count; itemIndex++)
{
tCell = new PdfPCell();
Phrase p = new Phrase(Items[itemIndex].Title);
tCell.AddElement(p);
tCell.HorizontalAlignment = Element.ALIGN_LEFT;
PdfPCell[] cells = new PdfPCell[availableSizes.Count + 1];
cells[0] = tCell;
for (int availCardSizeIndex = 0; availCardSizeIndex < availableSizes.Count; availCardSizeIndex++)
{
if (Items[itemIndex].Category.CategorySizesInUse.Contains(availableSizes[availCardSizeIndex]))
{
string str = availableSizes[availCardSizeIndex];
tCell = new PdfPCell();
tCell.PaddingLeft = 10f;
tCell.PaddingRight = 10f;
cells[availCardSizeIndex + 1] = tCell;
cells[availCardSizeIndex].HorizontalAlignment = Element.ALIGN_CENTER;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.chk{0}.{1}", availableSizes[availCardSizeIndex].Replace(".", ""), itemIndex + 1);
//f.FieldName = fieldName; <-- This causes the checkbox to be double-named (i.e. InkSaver.Global.chk0.InkSaver.Global.chk0
string js = string.Format("hideCardSize(event.target, {0}, '{1}');", itemIndex + 1, availableSizes[availCardSizeIndex]);
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
checkboxes.Add(f);
}
else
{
//Add a blank cell
tCell = new PdfPCell();
cells[availCardSizeIndex + 1] = tCell;
}
}
//Test if the column text will fit
table.Rows.Add(new PdfPRow(cells));
canvas = _pdfResultStamper.GetUnderContent(pageNum);
ColumnText ct2 = new ColumnText(canvas);
ct2.AddElement(new PdfPTable(table));
ct2.Alignment = Element.ALIGN_LEFT;
ct2.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
status = ct2.Go(true);
if ((status != ColumnText.NO_MORE_TEXT) || (itemIndex == (Items.Count - 1)))
{
ColumnText ct3 = new ColumnText(canvas);
ct3.AddElement(table);
ct3.Alignment = Element.ALIGN_LEFT;
ct3.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
ct3.Go();
foreach (PdfFormField f in checkboxes)
{
_pdfResultStamper.AddAnnotation(f, pageNum);
}
checkboxes.Clear();
if (itemIndex < (Items.Count - 1))
{
pageNum++;
_pdfResultStamper.InsertPage(pageNum, _pdfTemplateReader.GetPageSize(1));
table = new PdfPTable(availableSizes.Count + 1);
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
}
}
}
}
private PdfPCell[] GetTableHeaderRow(List<string> AvailableSizes)
{
PdfPCell[] sizeHeaders = new PdfPCell[AvailableSizes.Count + 1];
Phrase devName = new Phrase("Device Name");
PdfPCell deviceHeader = new PdfPCell(devName);
deviceHeader.HorizontalAlignment = Element.ALIGN_CENTER;
deviceHeader.BackgroundColor = BaseColor.GRAY;
sizeHeaders[0] = deviceHeader;
for (int x = 0; x < AvailableSizes.Count; x++)
{
PdfPCell hCell = new PdfPCell(new Phrase(AvailableSizes[x]));
hCell.HorizontalAlignment = Element.ALIGN_CENTER;
hCell.BackgroundColor = BaseColor.GRAY;
sizeHeaders[x + 1] = hCell;
}
return sizeHeaders;
}
public void MergePdfs(string filePath, List<MemoryStream> pdfStreams)
{
//Create output stream
FileStream outStream = new FileStream(filePath, FileMode.Create);
if (pdfStreams.Count > 0)
{
try
{
int PriceCardCounter = 0;
//Create Main reader
PdfReader reader = new PdfReader(pdfStreams[0]);
//rename fields in the PDF. This is required because PDF's cannot have more than one field with the same name
RenameFields(reader, PriceCardCounter++);
//Create main writer
PdfCopyFields Writer = new PdfCopyFields(outStream);
//Open document for writing
////Add pages
Writer.AddDocument(reader);
//For each additional pdf after first combine them into main document
foreach (var PdfStream in pdfStreams.Skip(1))
{
PdfReader reader2 = new PdfReader(PdfStream);
//rename PDF fields
RenameFields(reader2, PriceCardCounter++);
// Add content
Writer.AddDocument(reader2);
}
Writer.Close();
}
finally
{
foreach (var Strm in pdfStreams)
{
try { if (null != Strm) Strm.Dispose(); }
catch { }
}
outStream.Close();
}
}
}
private void RenameFields(PdfReader reader, int PriceCardID)
{
int tempPageNum = 1;
//rename all fields
foreach (string field in reader.AcroFields.Fields.Keys)
{
if (((reader.AcroFields.GetFieldType(field) == 1) || (reader.AcroFields.GetFieldType(field) == 2)) && (field.StartsWith("InkSaver")))
{
//This is a InkSaver button, set the name so its subclassed
string classPath;
if (reader.AcroFields.GetFieldType(field) == 2)
{
classPath = field.Substring(0, field.LastIndexOf("."));
if (field.StartsWith("InkSaver.chk"))
{
int a = field.LastIndexOf(".");
string sub = field.Substring(a + 1, (field.Length - a - 1));
int pageNum = int.Parse(sub);
int realPageNum = pageNum + tempPageNum;//PostProcessing.Instance.CoverPageLength;
PriceCardID = realPageNum;
}
}
else
{
classPath = field.Substring(0, field.LastIndexOf("."));
}
string newID = classPath + ".page" + PriceCardID.ToString();
bool ret = reader.AcroFields.RenameField(field, newID);
}
else
{
reader.AcroFields.RenameField(field, field + "_" + PriceCardID.ToString());// field + Guid.NewGuid().ToString("N"));
}
}
}
}
public class ChildFieldEvent : IPdfPCellEvent
{
protected PdfWriter writer;
protected PdfFormField parent;
protected string checkBoxName;
internal ChildFieldEvent(PdfWriter writer, PdfFormField parent, string CheckBoxName)
{
this.writer = writer;
this.parent = parent;
this.checkBoxName = CheckBoxName;
}
public void CellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] cb)
{
createCheckboxField(rect);
}
private void createCheckboxField(Rectangle rect)
{
RadioCheckField bt = new RadioCheckField(this.writer, rect, this.checkBoxName, "Yes");
bt.CheckType = RadioCheckField.TYPE_SQUARE;
bt.Checked = true;
this.parent.AddKid(bt.CheckField);
}
}
}
答案 0 :(得分:1)
也许作者没有完全解释它,但我认为确实存在一个错误。
我们假设我们有一个带有AcroForm的PDF文档,其中包含两个字段:name
和owner.name
。现在,我们要将owner.name
重命名为owner.name1
,将name
重命名为name1
。第一个将成功,但现场注册表(AcroFields.fields)中的名称现在是name1
,而不是owner.name1
按预期(在owner.name
之前)。然后,重命名秒会失败,因为字段注册表中已存在name1
。
它仅影响注册表,生成的PDF文档中的字段名称是正确的。
这里是Java iText的关键代码片段:
// snippet from com.itextpdf.text.pdf.AcroFields.renameField
int idx2 = newName.lastIndexOf('.') + 1;
// cut the last part from the original name
newName = newName.substring(idx2);
PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE);
// problem: only the last part will be registered, this must
// be IMO the (original) whole name including the dots
fields.put(newName, item);
答案 1 :(得分:0)
没有子类的字段。字段名称中的点指的是层次结构。例如:如果您有一个名为person
的字段。此字段可以包含name
和address
等子项。这些子字段的完全限定名称将为person.name
和person.address
。地址又可以包含子字段,例如street
,city
和country
,从而生成完全限定的名称,例如person.address.street
,person.address.city
和{{1 }}
您可以重命名字段,例如person.address.country
,但不能更改层次结构,因为PDF中的任何位置都不存在完全限定名称。因此,如果您的字段具有完全限定的名称street
,则只能重命名person.address.street
部分。例如:您可以将street
重命名为person.address.street
。您无法将person.address.line1
重命名为person.address.street
,因为这会更改表单的结构。
文档说的是您可以重命名字段名称,但不能更改字段结构。
你说某事感觉像是一个错误,但我没有看到什么是错的。文档表明person.street
是可以更改的完全限定名称的唯一部分。没有任何地方说你可以将ef
更改为ab.cd.ef
,因为这意味着你必须重写一个由不同PDF字典组成的结构树,而不是在一个字典中重命名一个键的值。 / p>
顺便说一句,这也在my book中解释过。
<强>附录:强>
我创建了一个名为RenameField的简单示例。它需要一个表单(subscribe.pdf)并创建一个新表单(subscribe_renamed.pdf)。区别?我们已将xy
重命名为"personal.loginname"
。将"personal.login"
重命名为"personal.loginname"
是不可能的,因为这需要更改层次结构(只是测试它,你会发现它不起作用)。