获取多行AcroField的行数

时间:2015-11-19 15:37:30

标签: itext multiline pad acrofields

我有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;
}

1 个答案:

答案 0 :(得分:2)

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

enter image description here

对于第一个字段,我们定义了字体大小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();
}

结果如下:

enter image description here

如您所见,我们添加的填充*的数量取决于字段级别定义的字体大小。如果字体大小为0,则将调整字体以使文本始终适合。如果字体具有实际值,我们可以或多或少地计算行数和&#34;列&#34;我们需要:

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()方法。最好过高估计列数而不是低估它......