获取多行 AcroField 的行数
Get row count of a multiline AcroField
我有一个带有 AcroFields 的 PDF。一些字段是多行字段。我需要用给定的文本填充 AcroFields,并用“*”(或预定义的 character/string)填充字段中剩余的任何 space。我可以使用 iText 添加文本,但不知道如何计算要添加的正确填充量。
请你建议我如何做到这一点。谢谢。
我写的代码是:
public string CreatePdf(IDictionary<FieldKey, string> dictionary, string template, string saveAs)
{
// Create new PDF from template
using (PdfReader reader = new PdfReader(template))
using (FileStream stream = new FileStream(saveAs, FileMode.Create, FileAccess.ReadWrite))
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
// Populate PDF fields from dictionary
AcroFields formFields = stamper.AcroFields;
foreach (KeyValuePair<FieldKey, string> dataField in dictionary)
{
switch (dataField.Key.FieldType)
{
case FieldType.Text:
string fieldValue = dataField.Value;
// Add filler if a filler is set
if (!String.IsNullOrWhiteSpace(dataField.Key.FillRight))
{
fieldValue = GetTextWithFiller(formFields, dataField.Key.FieldName, fieldValue, dataField.Key.FillRight);
}
// Text field
if (!formFields.SetField(dataField.Key.FieldName, fieldValue))
throw new InvalidDataException(String.Format("Invalid Template Field: {0} in Template: {1}", dataField.Key.FieldName, template));
break;
case FieldType.Image:
// Image field
PlaceImage(formFields, dataField);
break;
case FieldType.Barcode2Of5:
// 2 of 5 Barcode
PlaceBarcode2Of5(dataField.Value, stamper);
break;
case FieldType.Barcode128:
// 2 of 5 Barcode
PlaceBarcode128(dataField.Value, stamper);
break;
default:
throw new InvalidDataException(String.Format("Invalid data filed type : {0}", dataField.Key.FieldType));
}
}
// Save PDF
reader.RemoveUnusedObjects();
stamper.FormFlattening = true;
stamper.Close();
}
return saveAs;
}
以及获取填充物的方法,它没有像我预期的那样工作:
private static string GetTextWithFiller(AcroFields fields, string fieldName, string text, string filler)
{
// Get the size of the rectangle that defines the field
AcroFields.FieldPosition fieldPosition = fields.GetFieldPositions(fieldName)[0];
Rectangle rect = fieldPosition.position;
// Get field font
PdfDictionary merged = fields.GetFieldItem(fieldName).GetMerged(0);
TextField textField = new TextField(null, null, null);
fields.DecodeGenericDictionary(merged, textField);
Font fieldFont = new Font(textField.Font);
Chunk whatWeHave = new Chunk(text, fieldFont);
float textWidth = whatWeHave.GetWidthPoint();
// See how far the text field is filled with give text
float textEndPoint = rect.Left + textWidth;
float rectBottom = rect.Bottom;
float rectRight = rect.Right;
float rectTop = rect.Top;
// How many rows to fill
int textRows = Convert.ToInt32(rect.Height / fieldFont.CalculatedSize);
float totalCharactersWeCanFit = rect.Width * textRows;
if (textWidth < totalCharactersWeCanFit)
{
// Get the width of filler character
Chunk fillCharWidth = new Chunk(filler, fieldFont);
// Available gap
float gap = totalCharactersWeCanFit - textWidth;
// How much filler required
int fillAmount = Convert.ToInt32(gap / fillCharWidth.GetWidthPoint());
// Fill with filler
StringBuilder tempString = new StringBuilder();
tempString.Append(text);
for (int n = 0; n < fillAmount; ++n)
{
tempString.Append(filler);
}
text = tempString.ToString();
}
return text;
}
考虑一个包含三个多行文本字段的表单:
对于第一个字段,我们定义了一个字体大小0(这也是你做的);对于第二个字段,我们定义了一个字体 12;对于第三个字段,我们定义了一个字体 6.
现在让我们填写并拼合表格:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
StringBuilder sb = new StringBuilder();
for (String name : form.getFields().keySet()) {
int n = getInformation(form, name);
for (int i = 0; i < n; i++) {
sb.append(" *");
}
String filler = sb.toString();
form.setField(name, name + filler);
}
stamper.setFormFlattening(true);
stamper.close();
reader.close();
}
结果如下所示:
如您所见,我们作为填充符添加的 *
的数量取决于在字段级别定义的字体大小。如果字体大小为 0,则字体将以文本始终适合的方式进行调整。如果字体有实际值,我们可以或多或少地计算行数,"columns" 我们需要:
public int getInformation(AcroFields form, String name) {
form.getFieldItem(name);
AcroFields.Item item = form.getFieldItem(name);
PdfDictionary dict = item.getMerged(0);
PdfString da = dict.getAsString(PdfName.DA);
Object[] da_values = AcroFields.splitDAelements(da.toUnicodeString());
if (da_values == null) {
System.out.println("No default appearance");
}
BaseFont bf = null;
String font = (String)da_values[AcroFields.DA_FONT];
if (font != null) {
PdfDictionary dr = dict.getAsDict(PdfName.DR);
if (dr != null) {
PdfDictionary fontDict = dr.getAsDict(PdfName.FONT);
bf = BaseFont.createFont((PRIndirectReference)fontDict.get(new PdfName(font)));
}
}
if (bf == null) {
System.out.println("No BaseFont");
}
else {
System.out.println("Basefont: " + bf.getPostscriptFontName());
System.out.println("Size: " + da_values[AcroFields.DA_SIZE]);
Float size = (Float)da_values[AcroFields.DA_SIZE];
if (size == 0)
return 1000;
Rectangle rect = form.getFieldPositions(name).get(0).position;
float factor = bf.getFontDescriptor(BaseFont.BBOXURY, 1) - bf.getFontDescriptor(BaseFont.BBOXLLY, 1);
int rows = Math.round(rect.getHeight() / (size * factor) + 0.5f);
int columns = Math.round(rect.getWidth() / bf.getWidthPoint(" *", size) + 0.5f);
System.out.println("height: " + rect.getHeight() + "; width: " + rect.getWidth());
System.out.println("rows: " + rows + "; columns: " + columns);
return rows * columns;
}
return 1000;
}
首先我们获取字体,以便我们可以创建一个 BaseFont
对象。然后我们得到字体大小并使用存储在 BaseFont
中的信息,我们将定义用于计算行距的因子(即两行之间的 space)。
我们还询问了字段的尺寸。然后我们计算可以将多少行放入字段矩形的高度 (rows
) 中,以及将 String " *"
放入字段矩形的宽度 (columns
) 中。如果我们将 columns
和 rows
相乘,我们将得到一个近似值,即我们必须添加 " *"
多少次才能获得适当的填充符。如果我们有太多填充物也没关系:如果定义了字体,所有不适合的文本都将被删除。
您可以在此处找到完整示例:MultiLineFieldCount
我们采用 multiline.pdf 形式和 getInformation()
方法 returns 此信息:
Basefont: Helvetica
Size: 0.0
Basefont: Helvetica
Size: 6.0
height: 86.0; width: 108.0
rows: 13; columns: 27
Basefont: Helvetica
Size: 12.0
height: 86.0; width: 107.999985
rows: 7; columns: 14
关于第一个字段,我们不能说太多,因为字体大小为 0。在您的示例中也是如此。如果字体为 0,则您的问题无法回答。您无法计算出有多少 *
适合该字段,因为您不知道将用于呈现 *
.
的字体大小
如果字体大小为12,我们可以容纳7行14列(我们在屏幕截图中看到7行13列)。如果字体大小为6,我们可以放14行27列(我们在截图中看到的是14行26列)。
多出一列是因为我们使用了ceil()
方法。高估列数总比低估列数好...
我有一个带有 AcroFields 的 PDF。一些字段是多行字段。我需要用给定的文本填充 AcroFields,并用“*”(或预定义的 character/string)填充字段中剩余的任何 space。我可以使用 iText 添加文本,但不知道如何计算要添加的正确填充量。
请你建议我如何做到这一点。谢谢。
我写的代码是:
public string CreatePdf(IDictionary<FieldKey, string> dictionary, string template, string saveAs)
{
// Create new PDF from template
using (PdfReader reader = new PdfReader(template))
using (FileStream stream = new FileStream(saveAs, FileMode.Create, FileAccess.ReadWrite))
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
// Populate PDF fields from dictionary
AcroFields formFields = stamper.AcroFields;
foreach (KeyValuePair<FieldKey, string> dataField in dictionary)
{
switch (dataField.Key.FieldType)
{
case FieldType.Text:
string fieldValue = dataField.Value;
// Add filler if a filler is set
if (!String.IsNullOrWhiteSpace(dataField.Key.FillRight))
{
fieldValue = GetTextWithFiller(formFields, dataField.Key.FieldName, fieldValue, dataField.Key.FillRight);
}
// Text field
if (!formFields.SetField(dataField.Key.FieldName, fieldValue))
throw new InvalidDataException(String.Format("Invalid Template Field: {0} in Template: {1}", dataField.Key.FieldName, template));
break;
case FieldType.Image:
// Image field
PlaceImage(formFields, dataField);
break;
case FieldType.Barcode2Of5:
// 2 of 5 Barcode
PlaceBarcode2Of5(dataField.Value, stamper);
break;
case FieldType.Barcode128:
// 2 of 5 Barcode
PlaceBarcode128(dataField.Value, stamper);
break;
default:
throw new InvalidDataException(String.Format("Invalid data filed type : {0}", dataField.Key.FieldType));
}
}
// Save PDF
reader.RemoveUnusedObjects();
stamper.FormFlattening = true;
stamper.Close();
}
return saveAs;
}
以及获取填充物的方法,它没有像我预期的那样工作:
private static string GetTextWithFiller(AcroFields fields, string fieldName, string text, string filler)
{
// Get the size of the rectangle that defines the field
AcroFields.FieldPosition fieldPosition = fields.GetFieldPositions(fieldName)[0];
Rectangle rect = fieldPosition.position;
// Get field font
PdfDictionary merged = fields.GetFieldItem(fieldName).GetMerged(0);
TextField textField = new TextField(null, null, null);
fields.DecodeGenericDictionary(merged, textField);
Font fieldFont = new Font(textField.Font);
Chunk whatWeHave = new Chunk(text, fieldFont);
float textWidth = whatWeHave.GetWidthPoint();
// See how far the text field is filled with give text
float textEndPoint = rect.Left + textWidth;
float rectBottom = rect.Bottom;
float rectRight = rect.Right;
float rectTop = rect.Top;
// How many rows to fill
int textRows = Convert.ToInt32(rect.Height / fieldFont.CalculatedSize);
float totalCharactersWeCanFit = rect.Width * textRows;
if (textWidth < totalCharactersWeCanFit)
{
// Get the width of filler character
Chunk fillCharWidth = new Chunk(filler, fieldFont);
// Available gap
float gap = totalCharactersWeCanFit - textWidth;
// How much filler required
int fillAmount = Convert.ToInt32(gap / fillCharWidth.GetWidthPoint());
// Fill with filler
StringBuilder tempString = new StringBuilder();
tempString.Append(text);
for (int n = 0; n < fillAmount; ++n)
{
tempString.Append(filler);
}
text = tempString.ToString();
}
return text;
}
考虑一个包含三个多行文本字段的表单:
对于第一个字段,我们定义了一个字体大小0(这也是你做的);对于第二个字段,我们定义了一个字体 12;对于第三个字段,我们定义了一个字体 6.
现在让我们填写并拼合表格:
public void manipulatePdf(String src, String dest) throws DocumentException, IOException {
PdfReader reader = new PdfReader(src);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
AcroFields form = stamper.getAcroFields();
StringBuilder sb = new StringBuilder();
for (String name : form.getFields().keySet()) {
int n = getInformation(form, name);
for (int i = 0; i < n; i++) {
sb.append(" *");
}
String filler = sb.toString();
form.setField(name, name + filler);
}
stamper.setFormFlattening(true);
stamper.close();
reader.close();
}
结果如下所示:
如您所见,我们作为填充符添加的 *
的数量取决于在字段级别定义的字体大小。如果字体大小为 0,则字体将以文本始终适合的方式进行调整。如果字体有实际值,我们可以或多或少地计算行数,"columns" 我们需要:
public int getInformation(AcroFields form, String name) {
form.getFieldItem(name);
AcroFields.Item item = form.getFieldItem(name);
PdfDictionary dict = item.getMerged(0);
PdfString da = dict.getAsString(PdfName.DA);
Object[] da_values = AcroFields.splitDAelements(da.toUnicodeString());
if (da_values == null) {
System.out.println("No default appearance");
}
BaseFont bf = null;
String font = (String)da_values[AcroFields.DA_FONT];
if (font != null) {
PdfDictionary dr = dict.getAsDict(PdfName.DR);
if (dr != null) {
PdfDictionary fontDict = dr.getAsDict(PdfName.FONT);
bf = BaseFont.createFont((PRIndirectReference)fontDict.get(new PdfName(font)));
}
}
if (bf == null) {
System.out.println("No BaseFont");
}
else {
System.out.println("Basefont: " + bf.getPostscriptFontName());
System.out.println("Size: " + da_values[AcroFields.DA_SIZE]);
Float size = (Float)da_values[AcroFields.DA_SIZE];
if (size == 0)
return 1000;
Rectangle rect = form.getFieldPositions(name).get(0).position;
float factor = bf.getFontDescriptor(BaseFont.BBOXURY, 1) - bf.getFontDescriptor(BaseFont.BBOXLLY, 1);
int rows = Math.round(rect.getHeight() / (size * factor) + 0.5f);
int columns = Math.round(rect.getWidth() / bf.getWidthPoint(" *", size) + 0.5f);
System.out.println("height: " + rect.getHeight() + "; width: " + rect.getWidth());
System.out.println("rows: " + rows + "; columns: " + columns);
return rows * columns;
}
return 1000;
}
首先我们获取字体,以便我们可以创建一个 BaseFont
对象。然后我们得到字体大小并使用存储在 BaseFont
中的信息,我们将定义用于计算行距的因子(即两行之间的 space)。
我们还询问了字段的尺寸。然后我们计算可以将多少行放入字段矩形的高度 (rows
) 中,以及将 String " *"
放入字段矩形的宽度 (columns
) 中。如果我们将 columns
和 rows
相乘,我们将得到一个近似值,即我们必须添加 " *"
多少次才能获得适当的填充符。如果我们有太多填充物也没关系:如果定义了字体,所有不适合的文本都将被删除。
您可以在此处找到完整示例:MultiLineFieldCount
我们采用 multiline.pdf 形式和 getInformation()
方法 returns 此信息:
Basefont: Helvetica
Size: 0.0
Basefont: Helvetica
Size: 6.0
height: 86.0; width: 108.0
rows: 13; columns: 27
Basefont: Helvetica
Size: 12.0
height: 86.0; width: 107.999985
rows: 7; columns: 14
关于第一个字段,我们不能说太多,因为字体大小为 0。在您的示例中也是如此。如果字体为 0,则您的问题无法回答。您无法计算出有多少 *
适合该字段,因为您不知道将用于呈现 *
.
如果字体大小为12,我们可以容纳7行14列(我们在屏幕截图中看到7行13列)。如果字体大小为6,我们可以放14行27列(我们在截图中看到的是14行26列)。
多出一列是因为我们使用了ceil()
方法。高估列数总比低估列数好...