Monday, October 20, 2008

Adobe XFDF

Adobe Xml Forms Data Format is an xml format that allows you to fill PDF forms.  I used this in my D&D character generator to allow for easy creation of a printable character sheet in PDF form.

I started with an editable PDF form.  Each field in an editable form has a name of some sort, and if you have access to Adobe LiveCycle Designer, you can view the fields as XML.  All you have to do is open the editable PDF in LiveCycle Designer and click View->XML Source.  This will give you a long XML list detailing all the fields in your form.

UPDATE 2008-11-13: I got an email from someone recently who was creating his editable form using LiveCycle Designer.  It turns out that if you use LiveCycle Designer to build your form, it won't work with XFDF.  If you want to use XFDF, you have to create the form using Acrobat.  LiveCycle Designer forms require that you use XDP (which I have never used).  You can read a bit more about the incompatibilities here.

To specify the values to go into an editable PDF, you create an XFDF file.  The basic format is this:

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<f href="http://www.lukerymarz.com/files/CharacterSheet.pdf"/>
<fields>
  <field name="name">
    <value>null</value>
  </field>
  <field name="level">
    <value>1</value>
  </field>
  <field name="class">
    <value>Warlock</value>
  </field>
  <field name="race">
    <value>Tiefling</value>
  </field>
   ...
   ...
   ...
</fields>
</xfdf>

You put this in a text file with an xfdf extension and when you open it, it will reference the pdf in the <f> field and fill in the specified fields.  

So how do you get from the XML LiveCycle Designer gives you to the actual xfdf data.  Well, I didn't want to type a bunch of repetitive lines for each field in the form, so I wrote a little tool to take the LiveCycle xml and spit out ActionScript.  My tool opens the LiveCycle XML, parses each field, and provides a listing with the field type.  It then converts each field to ActionScript code that will append to a string in a my output function.  All I did then was copy the ActionScript code, fill in the specific piece of data for each field (for example, currentCharacter.name for the "name" field), and the output was done.  

There are two caveats I ran into, and the first has to do with spaces.  For each field, the name of that field in the LiveCycle XML has an underscore where a space is.  In the XFDF XML you generate, those underscores need to be replaced with an actual space, otherwise your field won't get filled in.  

The second caveat has to do with arrays.  For example, a part of the ActionScript code my tool generated looks like this:

xfdf += '<field name="class_feature.0"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.1"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.2"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.3"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.4"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.5"><value>' + Database.Data.currentCharacter. + '</value></field>';
xfdf += '<field name="class_feature.6"><value>' + Database.Data.currentCharacter. + '</value></field>';


This is an array of class features.  You'd think you could just go through the list, creating:

  <field name="class feature.0">
    <value>sample feature 1</value>
  </field>
  <field name="class feature.1">
    <value>sample feature 2</value>
  </field>

But that is incorrect.  Since this is an array, you have to represent it as one in the xfdf.  The actual output would look like this:

  <field name="class feature">
    <field name="0">
      <value>Eldritch Blast:</value>
    </field>
    <field name="1">
      <value>Edlritch Pact: Choose Fey Pact, Infernal Pact, or Star Pact.</value>
    </field>
    <field name="2">
      <value>Prime Shot: If you are closest to your target, +1 to attack rolls</value>
    </field>
  </field>

And that's it.  If you reference a pdf on a website, you can send only the xfdf to your user, and as long as they have access to your website, when they open the xfdf, Adobe will take care of the rest.  Pretty neat, once you get past the catches.

Saturday, October 4, 2008

XML schemas, Excel, and getting what you want

In my character generator, there is a lot of data.  There's races, classes, skill, feats, and powers.  On top of that, they're not static lists.  Wizards of the Coast (and other people) are always adding and expanding on everything.  With that in mind, I knew I needed to have all that data extensible in my character generator.  XML is the obvious choice, especially since Flex is so good at parsing it.

But how was I to get the data in the PHB into XML in the easiest way possible?  Microsoft Excel and XML Schemas is the answer.  The basic idea is that you enter all your data into Microsoft Excel as a big list.  Then you tell Excel to export it as XML.  That XML is then fed into the Flex app.  

XML schemas tell Excel how to format the XML you export.  With an XML schema, you're defining the basic format of your XML (in XML, ironically).  There's an article here that gives a good way to create a simple Xml Schema Definition (XSD) using Excel (see step 6, specifically).  That will give you the schema, and then you can attach that to your Excel spreadsheet and have Excel export XML for you.

But there's a HUGE caveat.  Excel cannot handle (that is, export, import, deal with at all) XML with a list of lists.  This looks something like this:

<feats>
  <feat name="a">
    <prerequisites>
      <prerequisite name="x"/>
      <prerequisite name="y"/>
    </prerequisites>
  </feat>
  <feat name="b">
    <prerequisites>
      <prerequisite name="p"/>
      <prerequisite name="q"/>
    </prerequisites>
  </feat>
</feats>

We've got a list of feats, each containing a list of prerequisites.  The issue, specifically, is that we have many elements.  Now, if you think about this, it makes sense.  Excel allows you to easily edit a two dimensional array of data.  More than that and it gets complicated.

The solution I went with to get around this (because I require being able to add and remove from various lists of data, and it makes the XML easier to read) is to make use of the really good XML functionality in Flex.  My solution works like this:

1.  enter you data in Excel.  any time you have a list of things, enter them as a single cell and separate each "thing" with a comma.
2.  export to a simple xml format.
3.  feed the xml into a Flex app that takes the XML and converts any comma separated lists to a proper XML list.

A sample of this is here.  My flex app takes a list of feats I exported from Excel (shown on the left side), and then prints the proper XML for me to save to a file (shown on the right side).  There's now an extra step if I update my Excel spreadsheet, but it's better than having to edit XML all day long.

Unfortunately, I don't know of a way to get Flex to do magic things with XML schemas, so the dream app of being able to give one XML format and export another is still off my radar.  Also, you are effectively writing your XML schema in Flex code, so if you need to make a schema change, it's instead a Flex code change.  This is not necessarily bad.  I feel more comfortable making changes in code instead of in fancy XML schema editors.

Now, I've heard that Altova makes a really nice application that can handle lists of lists, but I haven't tried it (and the price is outside my budget).  Something for next time, I suppose.  For now, I've got a "good enough" solution for tricky XML, and it will be easy to update in the future if I need to.