Search code examples
pdfclown

Get font and size of an acroform field


I need to get access to the font style and size of an acroform text field. I have access to the Field object via pdfclown but can't work out how to access the font. Anyone know how to do this?


Solution

  • I'm glad to announce you that today I committed to the PDF Clown's repository the fully-functional implementation of the FormFlattener, both on the current branch (0.1.2-Fix, rev 129, 131, 133) and on trunk (rev 130, 132, 134): I suggest you to download directly from that SVN repo to get the latest features & fixes.

    Otherwise, in case you stick with the published release (0.1.2.0), here it is the relevant code (again, I recommend the committed version as it's more refined). First of all, we need some adjustment:

    1) in XObject.java replace the wrap method:

    public static XObject wrap(
      PdfDirectObject baseObject
      )
    {
      if(baseObject == null)
        return null;
    
      PdfName subtype = (PdfName)((PdfStream)baseObject.resolve()).getHeader().get(PdfName.Subtype);
      if(PdfName.Form.equals(subtype))
        return FormXObject.wrap(baseObject);
      else if(PdfName.Image.equals(subtype))
        return ImageXObject.wrap(baseObject);
      else
        return null;
    }
    

    2) in FormXObject.java replace the wrap method:

    public static FormXObject wrap(
      PdfDirectObject baseObject
      )
    {
      if(baseObject == null)
        return null;
    
      PdfDictionary header = ((PdfStream)PdfObject.resolve(baseObject)).getHeader();
      PdfName subtype = (PdfName)header.get(PdfName.Subtype);
      /*
        NOTE: Sometimes the form stream's header misses the mandatory Subtype entry; therefore, here
        we force integrity for convenience (otherwise, content resource allocation may fail, for 
        example in case of Acroform flattening).
      */
      if(subtype == null && header.containsKey(PdfName.BBox))
      {header.put(PdfName.Subtype, PdfName.Form);}
      else if(!subtype.equals(PdfName.Form))
        return null;
    
      return new FormXObject(baseObject);
    }
    

    Then we can define the actual form flattener:

    3) add FormFlattener.java to org.pdfclown.tools package:

    package org.pdfclown.tools;
    
    import java.awt.geom.Point2D;
    import java.awt.geom.Rectangle2D;
    import java.util.EnumSet;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.pdfclown.documents.Document;
    import org.pdfclown.documents.Page;
    import org.pdfclown.documents.PageAnnotations;
    import org.pdfclown.documents.contents.xObjects.FormXObject;
    import org.pdfclown.documents.interaction.annotations.Annotation.FlagsEnum;
    import org.pdfclown.documents.interaction.annotations.Widget;
    import org.pdfclown.documents.interaction.forms.Field;
    import org.pdfclown.documents.interaction.forms.Fields;
    import org.pdfclown.documents.interaction.forms.Form;
    import org.pdfclown.objects.PdfArray;
    import org.pdfclown.objects.PdfDictionary;
    import org.pdfclown.objects.PdfDirectObject;
    import org.pdfclown.objects.PdfName;
    import org.pdfclown.objects.PdfObjectWrapper;
    import org.pdfclown.objects.PdfReference;
    import org.pdfclown.tools.PageStamper;
    import org.pdfclown.util.math.geom.Dimension;
    
    public class FormFlattener
    {
      private boolean hiddenRendered;
      private boolean nonPrintableRendered;
    
      /**
        Replaces the Acroform fields with their corresponding graphics representation.
    
        @param document Document to flatten.
      */
      public void flatten(
        Document document
        )
      {
        Map<PdfDirectObject,PageStamper> pageStampers = new HashMap<PdfDirectObject,PageStamper>();
        Form form = document.getForm();
        Fields formFields = form.getFields();
        for(Field field : formFields.values())
        {
          for(Widget widget : field.getWidgets())
          {
            Page widgetPage = widget.getPage();
            EnumSet<FlagsEnum> flags = widget.getFlags();
            // Is the widget to be rendered?
            if((!flags.contains(FlagsEnum.Hidden) || hiddenRendered)
              && (flags.contains(FlagsEnum.Print) || nonPrintableRendered))
            {
              // Stamping the current state appearance of the widget...
              PdfName widgetCurrentState = (PdfName)widget.getBaseDataObject().get(PdfName.AS);
              FormXObject widgetCurrentAppearance = widget.getAppearance().getNormal().get(widgetCurrentState);
              if(widgetCurrentAppearance != null)
              {
                PageStamper widgetStamper = pageStampers.get(widgetPage.getBaseObject());
                if(widgetStamper == null)
                {pageStampers.put(widgetPage.getBaseObject(), widgetStamper = new PageStamper(widgetPage));}
    
                Rectangle2D widgetBox = widget.getBox();
                widgetStamper.getForeground().showXObject(widgetCurrentAppearance, new Point2D.Double(widgetBox.getX(), widgetBox.getY()), new Dimension(widgetBox.getWidth(), widgetBox.getHeight()));
              }
            }
    
            // Removing the widget from the page annotations...
            PageAnnotations widgetPageAnnotations = widgetPage.getAnnotations();
            widgetPageAnnotations.remove(widget);
            if(widgetPageAnnotations.isEmpty())
            {
              widgetPage.getBaseDataObject().put(PdfName.Annots, null);
              widgetPageAnnotations.delete();
            }
    
            // Removing the field references relating the widget...
            PdfDictionary fieldPartDictionary = widget.getBaseDataObject();
            while (fieldPartDictionary != null)
            {
              PdfDictionary parentFieldPartDictionary = (PdfDictionary)fieldPartDictionary.resolve(PdfName.Parent);
    
              PdfArray kidsArray;
              if(parentFieldPartDictionary != null)
              {kidsArray = (PdfArray)parentFieldPartDictionary.resolve(PdfName.Kids);}
              else
              {kidsArray = formFields.getBaseDataObject();}
    
              kidsArray.remove(fieldPartDictionary.getReference());
              fieldPartDictionary.getReference().delete();
    
              if(!kidsArray.isEmpty())
                break;
    
              fieldPartDictionary = parentFieldPartDictionary;
            }
          }
        }
        if(formFields.isEmpty())
        {
          // Removing the Acroform root...
          document.setForm(null);
          form.delete();
        }
        for(PageStamper pageStamper : pageStampers.values())
        {pageStamper.flush();}
      }
    
      /**
        Gets whether hidden fields have to be rendered.
      */
      public boolean isHiddenRendered(
        )
      {return hiddenRendered;}
    
      /**
        Gets whether non-printable fields have to be rendered.
      */
      public boolean isNonPrintableRendered(
        )
      {return nonPrintableRendered;}
    
      /**
        @see #isHiddenRendered()
      */
      public FormFlattener setHiddenRendered(
        boolean value
        )
      {
        hiddenRendered = value;
        return this;
      }
    
      /**
        @see #isNonPrintableRendered()
      */
      public FormFlattener setNonPrintableRendered(
        boolean value
        )
      {
        nonPrintableRendered = value;
        return this;
      }
    }
    

    And this is an example using it:

    import java.io.IOException;
    
    import org.pdfclown.documents.Document;
    import org.pdfclown.files.File;
    import org.pdfclown.tools.FormFlattener;
    
    File file = null;
    try
    {
      // 1. Opening the PDF file...
      {
        try
        {file = new File(myFilePath);}
        catch(Exception e)
        {throw new RuntimeException(myFilePath + " file access error.",e);}
      }
      Document document = file.getDocument();
    
      // 2. Flatten the form!
      FormFlattener formFlattener = new FormFlattener();
      formFlattener.flatten(document);
    
      // 3. Serialize the PDF file!
      try
      {file.save(SerializationModeEnum.Standard);}
      catch(Exception e)
      {
        System.out.println("File writing failed: " + e.getMessage());
        e.printStackTrace();
      }
    }
    finally
    {
      // 4. Closing the PDF file...
      if(file != null)
      {
        try
        {file.close();}
        catch(IOException e)
        {/* NOOP */}
      }
    }
    

    To keep yourself up-to-date with the activity about the project, you can follow PDF Clown on its site (pdfclown.org) and through its twitter stream (https://twitter.com/pdfclown).