1

我有一个带有 AcroFields 的 PDF。有些字段是多行字段。我需要用给定的文本填充 AcroFields,并用“*”(或预定义的字符/字符串)填充字段中的任何剩余空间。我可以使用 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;
}
4

1 回答 1

3

考虑一个包含三个多行文本字段的表单:

在此处输入图像描述

对于第一个字段,我们定义了字体大小 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,则字体将以文本始终适合的方式进行调整。如果字体有实际值,我们可以或多或少地计算出我们需要的行数和“列”数:

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,我们将定义用于计算前导的因子(即两行之间的空间)。

我们还询问该字段的尺寸。然后我们计算可以将多少行放入高度 ( rows) 以及我们可以将多少次String " *"放入字段矩形的宽度 ( columns)。如果我们将 和 相乘columnsrows我们会得到一个近似值,即我们必须添加多少次" *"才能获得适当的填充物。如果我们有太多的填充物也没关系:如果定义了字体,所有不适合的文本都将被删除。

你可以在这里找到完整的例子:MultiLineFieldCount

我们采用multiline.pdf格式,该getInformation()方法返回以下信息:

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()方法造成的。高估列数总比低估它好...

于 2015-11-19T17:31:50.887 回答