-
-
Notifications
You must be signed in to change notification settings - Fork 4
Custom Provider How To
This guide will try to explain the way I create custom providers. I will use the PDF provider as an example.
These are the skills you need to take into account when creating providers
You will need to use the browser developer tools extensively. Every browser has their own way of accessing them, so inform yourself on how to do that. During this guide you will mostly use the JavaScript console.
You will need some basic understanding of JavaScript syntax, and control structures.
THis is a list of tools I usually use while making providers
This is the most important piece. You need to have access to a FoundryVTT instance, preferably one which can be used for development purposes. Ideally you run one from your development workstation, so you can easily access the files with your editor.
Make sure to have the actor-export module installed and activated
Make sure you have plenty of actors to test your code with. The more you can test the better. If you are in need of some, I can (probably) provide.
You need to have a decent code editor, and there are quite a lot of (good) ones out there. Typically I use VSCodium, and I use a couple of extensions to make my life easier, most notably Code Spell Checker, ESLint, Prettier and Prettier ESLint.
For some operations I need to know pixel distances (when adding textboxes and images), which is where this tool comes in. I use Gimp for these kinds of things.
Browser PDF viewers are ok. But when I need to check PDF document properties, like fonts used, it's good to have a decent PDF viewer around to get to the bottom of documents. I use Okular.
Make sure you have authenticated to your Foundry instance using the Gamemaster credentials.
Head over to the Custom provider page.
Assuming we want to work on a provider for a pdf file, start off with the following template:
import { pdfProvider } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/providers/PDFProvider.js';
const mapper = new pdfProvider(actor);
mapper.debugProvider = true;
export { mapper };Make sure to replace https://<replace with your hostname> with the correct details.
mapper.debugProvider = true; will enable actor-export to print debug information about your pdf. At the time of this writing, you will get the size of your PDF in pixels, if you use the above code as-is:
actor-export | PDF | blah.pdf contains 106 form fields
actor-export | PDF | fields: Array(106) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]
actor-export | PDF | blah.pdf page 0 WxH dimensions: 841.89 x 595.28 pixelsThis shows the PDF blah.pdf contains 106 form fields, has 1 page, and it's dimension in pixels is 841.89 x 595.28
The fields Array object can be inspected to see which fields are available:
actor-export | PDF | fields:
Array(106) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]
[0…99]
0: Object { name: "CharacterName", type: "PDFTextField" }
1: Object { name: "Background", type: "PDFTextField" }
2: Object { name: "Ancestry", type: "PDFTextField" }
...
97: Object { name: "isWounded", type: "PDFCheckBox" }
98: Object { name: "Equipment", type: "PDFTextField" }
99: Object { name: "XP", type: "PDFTextField" }
[100…105]
length: 106
<prototype>: Array []Each form field has a type, and currently, only PDFTextField and PDFCheckBox are supported (because I haven't encountered any other, yet). PDFTextField expects text, while PDFCheckBox expects a boolean (true or false).
FoundryVTT represents a character as an actor which is also exported for your convenience so you can walk the object to find the properties your are looking for.
typing actor.exportToJSON() in the JavaScript console will prompt you to save the actor as a json file on your computer. very practical to look for properties.
Alternatively, you can use any of the helper classes for specific systems I have created.
| ✏️ | 2 helper classes are available for your convenience: pf2e and dnd5e |
|---|
The advantage of using these helper classes is that you do not need to keep track of any changes in the actor schema, I do.
If the owner(s) of a game system decide to change something, I will update the helper character, fixing all sheets depending on that particular property of the actor.
If a specific property is missing, or wrong, let me know.
Assuming we will use the pf2e helper, we need to update the code to:
import { pdfProvider } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/providers/PDFProvider.js';
import { pf2eHelper } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/helpers/PF2eHelper.js';
const mapper = new pdfProvider(actor);
mapper.debugProvider = true;
const character = pf2eHelper.getActorObject(game, actor);
export { mapper };I've added
import { pf2eHelper } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/helpers/PF2eHelper.js';
to import the necessary code for the helper, and
const character = pf2eHelper.getActorObject(game, actor);
to create an object with the information prepared for you. This will also cause your JavaScript Console to get an additional line:
actor-export | character: Object { actor: {…}, game: {…}, spellcastingTraditions: (4) […], spellcastingTypes: (2) […] }Which provides you a direct interface to all properties the helper offers you.
This section serves as an example for PDF form fields.
From the list of obtained PDF form fields, you can now start creating a provider mapping: assign values to the form fields.
This is done using the field method.
In short, you need to provide the following according to the wiki:
Store form field information, text for text fields, booleans for checkboxes
Kind: instance method of
pdfProvider
Param Type Description file stringthe pdf filename to apply the image to ('all' means all PDFs) name stringThe name of the field to reference value string|boolean|PromiseThe value to be applied to the field options ObjectOptional data, functions for the field options.parseValue functionfunction to parse the value after resolving the value Promise
imagine we want to add the character's name to the PDF form field named CharacterName, we can write the following:
mapper.field('all', 'CharacterName', actor.name);
using the raw actor data or with the helper:
mapper.field('all', 'CharacterName', character.name);
Rinse and repeat for all fields.
In this case our code becomes:
import { pdfProvider } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/providers/PDFProvider.js';
import { pf2eHelper } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/helpers/PF2eHelper.js';
const mapper = new pdfProvider(actor);
mapper.debugProvider = true;
const character = pf2eHelper.getActorObject(game, actor);
mapper.field('all', 'CharacterName', character.name);
mapper.field('all', 'OwnerName', character.ownerName);
/* and so on */
export { mapper };Optionally you can add free text to your PDF at arbitrary locations. The advantage is that you do not need to 'extend' PDFs with form fields, which can be cumbersome, and cause your PDF to grow (considerably) in size.
You need to figure out where to place your text, and I do that using Gimp.
Import the PDF into your image editor, using the dimensions provided by the debug output of your custom provider. Now the available pages are available as different layers, and I can easily find x and y coordinates, and width and height.
Adding free text is more complicated, as you need to format your text as well. There are a couple of methods which will help you out, but this can be more tricky that just form fields.
Here's an example of adding the character name as free text, using the textBox method:
import { pdfProvider } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/providers/PDFProvider.js';
import { pf2eHelper } from 'https://<replace with your hostname>/modules/actor-export/scripts/lib/helpers/PF2eHelper.js';
const mapper = new pdfProvider(actor);
mapper.defaultFont('MarkerFelt.ttf', 12, 14);
mapper.defaultFontColor('#01579b');
mapper.debugProvider = true;
const character = pf2eHelper.getActorObject(game, actor);
mapper.textBox('character name', 'all', 0, 240, 39, 125, 14, character.name);
export { mapper };I've added
mapper.defaultFont('MarkerFelt.ttf', 12, 14);
To specify the default font (MarkerFelt.ttf) to use, fontsize (12px) and line height (14px). Currently, only 'MarkerFelt.ttf' is available.
and:
mapper.defaultFontColor('#01579b');
To specify the default font color. It's HTML-style color, so you can change this easily
and finally:
mapper.textBox('character name', 'all', 0, 240, 39, 125, 14, character.name);
To write the character name on page 0 (first page) at coordinates 240,39 (x,y) in a box 125px wide by 14px high.
You're not limited to just 1 font (wel actually, you are at this time), and size, there are a lot of options you can specify on a per entry basis:
let option = { size: 16, lineHeight: 18, halign: 'center' }; mapper.textBox('character name', 'all', 0, 240, 39, 125, 14, character.name, option);
would place the same text at the same location, but with bigger font size, and it would horizontally justify it in the 125x14px box.
using the image method, you can add images to a particular page at a particular location:
mapper.image('all', 2, 29, 39, actor.img, 178, 265);This adds the actor's image to page 2 (third page) at coordinates 29,39, and will not exceed the square which is 178x265px.