TexturePacker's custom exporter format
Custom Exporters have saved me dozens of hours. I can't function without TexturePacker. Richard Jarell
TexturePacker comes with a lot of different data output formats - both generic like JSON and XML but also platform specific like Cocos2D, Sparrow and others.
Sometimes this is not enough - because a game framework needs its own format - or because it simply does not fit your needs.
The simple solution for you is to create your own custom format. It's easier than you may think, just read the instructions below.
Modifying an existing exporter
Many of the exporters are available in the TexturePacker folder:
- Windows users look under C:/Program Files/CodeAndWeb/TexturePacker/resources/exporters
- Mac users go to /Applications/TexturePacker.app/Contents/Resources/exporters
The plain exporter may be a good starting point. Make sure to duplicate the exporter folder to somewhere outside TexturePacker's installation path and rename the folder and the exporter contents as shown below!
Creating a new exporter from scratch
That's of course possible too... but modifying an existing exporter is the recommended way.
Preparations before creating your own exporter
First, create a directory - it's the Exporter Directory that will contain all your custom exporters - e.g. my-TP-Exporters .
Now create a new subdirectory inside that directory - name it like the exporter you want to create - e.g. testexporter .
Your folder structure should now look like this:
Set the path to your exporter directory my-TP-Exporters in TexturePacker's preferences and then quit TexturePacker.
Structure of an exporter
An exporter consists of 2 or more files:
-
exporter.xml - the file describing the capabilities of the exporter
-
template.txt - the template for the output file format
Optional:
- filter.qs - one (or more) file(s) to extend the template file with JavaScript
The exporter description file
Create an exporter.xml inside your testexporter directory, or use an exporter.xml of an existing exporter as starting point
Here's an example file, containing descriptions for all supported elements:
<exporter version="1.0"> <!-- file format version, must be 1.0 -->
<!-- Identifier for the exporter - should be unique.
This value is stored when the exporter is referenced from a saved file (.tps).
It is also used to select the exporter from command line.
Change this if you've duplicated an existing exporter -->
<name>testexporter</name>
<!-- Old exporter identifiers (optional); if a tps file with such an id is opened,
its id is automatically changed to the one specified in <name> -->
<nameAlias>otherexporter</nameAlias>
<!-- Name of the exporter that is shown in the framework selector dialog -->
<!-- Change this if you duplicated an existing exporter -->
<displayName>TestExporter</displayName>
<!-- A description of the exporter -->
<description>My first custom exporter for TexturePacker</description>
<!-- Exporter version -->
<version>1.0</version>
<files>
<!-- A list of data files which should be generated during the publish process -->
<file>
<!-- name of this file type; used in the tps file
and by the commandline client: for each MYDATA file
a commandline option "- -MYDATA-file" is evaluated
(remark: write the option without the blank between the "-" signs,
it's required in the XML comment) -->
<name>mydata</name>
<!-- human-readable name (used in the TexturePacker GUI) -->
<displayName>MyDataFile</displayName>
<!-- file extension, without '.' -->
<fileExtension>txt</fileExtension>
<!-- description what the file contains,
used for tooltips in the GUI (optional) -->
<description>My txt custom exporter for TexturePacker</description>
<!-- name of the template file -->
<template>testexporter.txt</template>
<!-- this file is optional, i.e. if the user does not specify an
output file name, the file will not be generated;
(optional, default is 'false') -->
<optional>false</optional>
<!-- specifies for which scaling variant this data file is generated:
all: for each variant (default)
first: only for the first variant
last: only for the last one -->
<hasSeparateAutoSDFiles>all</hasSeparateAutoSDFiles>
<!-- create a file for each multipack sheet
(optional, default is 'true') -->
<hasSeparateMultiPackFiles>true</hasSeparateMultiPackFiles>
</file>
<!-- add more <file> ... </file> tags if you need more than one output file -->
</files>
<!-- the following elements show/hide the corresponding input fields -->
<!-- in the TexturePacker user interface -->
<!-- mandatory flags: -->
<!-- Set this to true if the target framework supports trimmed sprites (sprites with
transparency removed). TexturePacker delivers the offsets and size of the
trimmed sprite to the template system. The user can still choose to disable
trimming from the user interface. If supportsTrimming is set to false the option
to enable trimming is not available in the user interface. -->
<supportsTrimming>true</supportsTrimming>
<!-- Set this to true if the target framework supports rotated sprites. The user can
still decide not to enable rotated sprites from the user interface.
If supportsRotation is set to false the option to enable sprite rotation is
not available in the user interface. -->
<supportsRotation>true</supportsRotation>
<!-- The direction in which a sprite is rotated - if TexturePacker decides
to rotate a sprite:
cw: clockwise rotation
ccw: counterclockwise rotation
The amount of rotation is always 90°. Only required for supportsRotation=true -->
<rotationDirection>cw</rotationDirection>
<!-- If enabled TexturePacker allows the user to choose non-power-of-2 sizes for sprite
sheets. Otherwise, only power-of-2 (POT) sizes are enabled in the user interface. -->
<supportsNPOT>true</supportsNPOT>
<!-- the following flags are optional: -->
<!-- framework supports tightly packed textures, i.e. it can extract sprites
from a sheet using polygon paths instead of bounding boxes (default=false) -->
<supportsPolygonPacking>true</supportsPolygonPacking>
<!-- target framework supports pivot point settings (default=false) -->
<supportsPivotPoint>false</supportsPivotPoint>
<!-- target framework accepts sprite names without file name extension. (default=false) -->
<supportsTrimSpriteNames>true</supportsTrimSpriteNames>
<!-- framework can read in border values for 9-patch scaling of sprites (default=false) -->
<supportsScale9>true</supportsScale9>
<!-- enables the "Auto-detect animations" checkbox in the UI; the implementation of
an animation detection must be done by the exporter (default=false) -->
<supportsAnimations>true</supportsAnimations>
<!-- origin of the coordinate system of a sprite in the target framework,
needed for pivot point placement. (default=TopLeft) -->
<spriteOrigin>TopLeft</spriteOrigin> <!-- TopLeft or BottomLeft -->
<!-- ensure that width/height of generated texture is multiple of this factor (default=1) -->
<textureSizeFactor>1</textureSizeFactor>
<!-- ensures that the center offset caused by trimming has integral
coordinates (default=false)-->
<ensureIntegerCenterOffset>false</ensureIntegerCenterOffset>
<!-- You can specify default values for the following TexturePacker settings.
They are used if a new project is created or if the data format of
an existing project is changed and "Update to recommended values"
is checked. The complete <defaults> block is optional. -->
<defaults>
<extrude>0</extrude>
<shapePadding>1</shapePadding>
<borderPadding>1</borderPadding>
<trimMode>Polygon</trimMode> <!-- None, Trim, Crop, CropKeepPos, Polygon -->
<sizeConstraints>POT</sizeConstraints> <!-- POT, WordAligned, AnySize -->
<writePivotPoints>true</writePivotPoints> <!-- true, false -->
<defaultPivotPoint>0.0,0.0</defaultPivotPoint>
</defaults>
<!-- TexturePacker's "scaling variants" dialog provides a set of presets.
These presets can be overwritten by exporter-specific ones.
You can define them with an optional <variantPresets> block as shown here: -->
<variantPresets>
<variantPreset>
<name>HD / SD</name>
<variant>
<scale>1.0</scale>
<extension>-2x</extension>
<filter></filter> <!-- optional -->
</variant>
<variant>
<scale>0.5</scale>
<extension></extension>
<filter></filter>
</variant>
<forceIdenticalLayout>true</forceIdenticalLayout>
</variantPreset>
</variantPresets>
<!-- The following <properties> block shows how to define custom exporter properties.
For each property TexturePacker displays an input field in the UI.
The values entered there are stored in the TPS file and passed to the
exporter when publishing. There they can be used in the template file. -->
<properties>
<property>
<!-- Property name, used in tps file + exporter template: -->
<name>sprite-prefix</name>
<!-- Property type, supported values: string, bool -->
<type>string</type>
<!-- Default value for new projects -->
<default>img_</default>
<!-- Name and tooltip text used for the input field in the UI: -->
<displayName>Sprite prefix</displayName>
<toolTip>Prefix for the sprite's name.</toolTip>
<!-- Only displayed if advanced settings are expanded (small triangle
in front of Data settings) -->
<isAdvanced>true</isAdvanced>
</property>
<!-- more <property>s can be defined here -->
</properties>
</exporter>
The template file
The template file contains a prototype of the data file to write with some additional markup to format the text, build loops and decisions.
If you copied the data from the example file above, you should now create a testexporter.txt in the same directory as the exporter.xml. The file extension of the template file does not matter. However, it might be useful to give it the same extension as the Data file to enable syntax highlighting in your favorite text editor.
TexturePacker uses the Grantlee template engine - which is similar to Django templates. A basic description of the language is available from here: TexturePacker & PhysicsEditor Template Language.
This example template file will write a data file containing a list of sprites with frame coordinates, a flag for rotation and the source coordinates:
// Created with TexturePacker (https://www.codeandweb.com/texturepacker)
//
// Sprite sheet: {{ texture.fullName }} ({{ texture.size.width }} x {{ texture.size.height }})
//
// {{ smartUpdateKey }}
sprites = [
{% for sprite in allSprites %}
{{ sprite.trimmedName }} = [
frame = [
{{ sprite.frameRect.x }},
{{ sprite.frameRect.y }},
{{ sprite.frameRect.width }},
{{ sprite.frameRect.height }}
],
rotated = {{ sprite.rotated }},
source = [
{{ sprite.cornerOffset.x }},
{{ sprite.cornerOffset.y }},
{{ sprite.untrimmedSize.width }},
{{ sprite.untrimmedSize.height }}
]
] {% if not forloop.last %}, {% endif %}
{% endfor %}
]
Using values
Values are surrounded by {{
and }}
, values inside a
structure are separated by .
.
Filters can be used to format values - e.g. {{x|floatformat:3}}
formats
the floating point value with 3 digits.
Control structures
if {% if condition %} ... {% else %} ... {% endif %}
- conditional
execution of one branch in the file.
for {% for object in objectList %} ... {% endfor %}
- loop over a list of
elements.
Values and data types
The variables that can be used in the template file are described in detail in the table below:
Element | Type | Info |
---|---|---|
smartUpdateKey | STRING | hash string over all file dates, used to speed up export |
dataAbsoluteFileName | STRING | absolute file name of the current data file |
settings | SETTINGS | contains tps file settings |
variantIndex | INT | 0 for hasSeparateAutoSDFiles=firstvariantCount-1 for hasSeparateAutoSDFiles=last[0..variantCount-1] for hasSeparateAutoSDFiles=all |
multiPackIndex | INT | -1 for hasSeparateMultiPackFiles=false[0..textureCount-1] for multipack textures |
variantParams | VARIANTPARAMS | settings adapted for the current scaling variant |
allResults | RESULT[] | |
texture | TEXTURE | current texture (only available if hasSeparateMultiPackFiles=true) |
sprites | SPRITE[] | sprites (without aliases) of the current texture (or of complete result, if hasSeparateMultiPackFiles=false) |
allSprites | SPRITE[] | sprites (including aliases) of the current texture (or of complete result, if hasSeparateMultiPackFiles=false) |
exporterProperties.MYPROP | STRING or BOOL | Contains the value of the custom exporter property MYPROP . Exporter properties can defined in the exporter description file, using the <property> element. |
tp | HASH | Contains all elements described above. If you want to write your exporter in Javascript you can use this hash to pass all settings to a Javascript function. |
The template file is loaded with a set of parameters created during the packing process. The values can be referenced using the template language.
smartUpdateKey
When publishing a spritesheet, TexturePacker checks if the Data file already exists.
If the file is found, its contents are read, and a text sequence is searched for -
$TexturePacker:SmartUpdate:....hashvalue....$
.
This value is a hash calculated from all file dates, sizes, and the parameters used
to create the texture. If the calculated hash is identical to the current state of
the files, no output is created, which speeds up batch operations.
To use this smart update feature, add smartUpdateKey
somewhere in the
template file. Ensure that the value is placed within a comment or another
suitable location to avoid confusing the module reading the data.
sprites and allSprites
The sprites
list contains unique sprites only, excluding alias sprites
(sprites that contain the same image). To reference alias sprites,
you can access a sprite's .aliasList
property. On the other hand,
allSprites
contains a list of all sprites, including alias sprites.
In most cases, using allSprites
is more convenient than sprites
.
The standard way to iterate over the sprites is using
{% for sprite in allSprites %}
...
{% endfor %}
SETTINGS
This data type holds .tps file settings.
Element | Type | Info |
---|---|---|
autoSDSettings[0..variantCount-1] | AUTOSDSETTINGS[] | Scaling variant settings |
ditherType | none-nn none-linear fs fs-alpha atkinson atkinson-alpha | for more info see Dithering |
textureFormat | STRING | texture format |
outputFormat | STRING | output format |
contentProtection.key | STRING | 32 hex digits |
tpsName | STRING | name of the tps file |
allowRotation | BOOL | rotation enabled |
flipPVR | BOOL | flip PVR |
forceIdenticalLayout | BOOL | force layout to be identical for all variants |
multiPack | BOOL | multipack enabled |
trimSpriteNames | BOOL | trim sprite names |
autoAliasEnabled | BOOL | automatic detection of duplicate sprites enabled |
premultiplyAlpha | BOOL | premultiply alpha enabled |
reduceBorderArtifacts | BOOL | reduce border artifacts enabled |
fixedTextureSize | SIZE | have a fixed texture size |
dataFormat | STRING | data format |
textureSubPath | STRING | texture sub path |
borderPadding | INT | border padding |
shapePadding | INT | shape padding |
commonDivisorX | INT | common divisor X |
commonDivisorY | INT | common divisor Y |
backgroundColor | INT | background color |
writePivotPoints | BOOL | user has enabled pivot points |
autodetectAnimations | BOOL | user has activated animation detection |
AUTOSDSETTINGS
This data type holds AutoSD settings.
Element | Type | Info |
---|---|---|
scale | FLOAT | scaling factor |
extension | STRING | variant name |
spriteFilter | STRING | sprite filter |
acceptFractionalValues | BOOL | accept fractional values if no common factor can be found |
maxTextureSize | SIZE | maximum texture size |
RESULT
Structure containing a packing result.
Element | Type | Info |
---|---|---|
textures | TEXTURE[] | array of all textures |
nonFittingSprites | STRING[] | array containing names of non-fitting sprites |
TEXTURE
This data type holds a texture with all its parameters.
Element | Type | Info |
---|---|---|
size | SIZE | The width/height of the Texture file in pixels. |
trimmedName | STRING | The name of the Texture file - without the extension. |
fullName | STRING | The name of the Texture file with the extension. |
absoluteFileName | STRING | The absolute path to the Texture file. |
normalMapFileName | STRING | The path of the corresponding normal map sheet, relative to texture file. |
normalMapAbsoluteFileName | STRING | The absolute path of the corresponding normal map sheet. |
sprites[0..n] | SPRITE[] | The sprites on the current texture, without aliases. |
allSprites[0..m] | SPRITE[] | All sprites on current texture, including aliases. |
area | INT | Area of the texture in pixels (i.e. width*height). |
SPRITE
This data type holds a sprite with all its parameters.
Element | Type | Info |
---|---|---|
fileData | FILEDATA | sprite file data |
size | SIZE | The width/height of the sprite in pixels. |
frameRect | RECT | frame rectangle, pixel coordinates |
frameRectWithoutRotation | RECT | frame rectangle, pixel coordinates without rotation |
frameRectRel | RECT | frame rectangle, relative uv coordinates (0..1) |
sourceRect | RECT | source rectangle |
cornerOffset | POINT | corner offset |
untrimmedSize | SIZE | untrimmed size of sprite |
trimmed | BOOL | true if sprite is trimmed |
rotated | BOOL | true if sprite is rotated |
isSolid | BOOL | true if sprite has no transparent pixels |
trimmedName | STRING | The name of the sprite file - without the extension. |
fullName | STRING | The name of the sprite file with the extension. |
name | STRING | trimmedName or fullName, depending on Trim sprite names flag. |
aliasList[0..n] | SPRITE[] | contains the list of identical sprites |
centerOffset | POINT | Center offset |
pivotPoint | POINT | x and y coordinates of the Pivot Point |
pivotPointNorm | POINT | Pivot point coordinates, relative to the sprite size |
vertices | POINT[] | Polygon points approximating the outline of the sprite. Sprite coordinate system. |
verticesUV | POINT[] | Polygon points approximating the outline of the sprite. Texture coordinate system. |
triangleIndices | INT[] | Triangulation of the sprite's polygon. The values in this list are indices in the vertices / verticesUV array. |
scale9Enabled | BOOL | User enabled 9-patch scaling for this sprite |
scale9Borders | RECT | The 9-patch borders. |
scale9Paddings | RECT | The 9-patch paddings. |
scale | FLOAT | The sprite's scaling factor as set in Sprite settings. Does not take global/variant scale in account. |
FILEDATA
This data type holds a file.
Element | Type | Info |
---|---|---|
absoluteFileName | STRING | absolute path of the file |
fileSize | FLOAT | size of the file |
created | DATE | creation date of the file |
lastModified | DATE | last modified date of the file |
VARIANTPARAMS
This data type holds the settings adapted for the current scaling variant.
Element | Type | Info |
---|---|---|
scale | FLOAT | Scaling factor |
shapePadding | INT | shape padding in pixels |
borderPadding | INT | border padding in pixels |
commonDivisorX | INT | Common Divisor X |
commonDivisorY | INT | Common Divisor Y |
extrude | INT | Extrude in pixels |
baseFactor | INT | Common base factor of all scaling variants |
maxTextureSize | SIZE | Maximum texture size in pixels |
fixedTextureSize | SIZE | Fixed texture size in pixels |
RECT (Rectangle)
Element | Type | Info |
---|---|---|
x | INT | x coordinate |
y | INT | y coordinate |
width | INT | width in pixels |
height | INT | height in pixels |
POINT
Element | Type | Info |
---|---|---|
x | INT | x coordinate |
y | INT | y coordinate |
SIZE
Element | Type | Info |
---|---|---|
width | INT | width in pixels |
height | INT | height in pixels |
Enhancing the exporter with JavaScript
In some cases a pure template system is not enough - e.g. if you need to do some calculations. This can be achieved by enhancing the system with JavaScript.
For this, create 2 directories inside the exporter's folder: grantlee and grantlee/0.2 :
In the 0.2 folder place your JavaScript file. The extension must be .qs .
Contents of the JavaScript file
Inside the file a filter can be registered. A filter is a function that receives a value, processes it, and returns it to the template which adds the value to the output file.
The following example registers a filter called makecssselector
which takes
a string, replaces all occurrences of -hover with :hover , and returns the
result.
// the filter function
var MakeSelectorFilter = function(input)
{
var input = input.rawString();
return input.replace("-hover",":hover");
};
// the filter name
MakeSelectorFilter.filterName = "makecssselector";
// register the filter
Library.addFilter("MakeSelectorFilter");
It is important to convert the function parameter to a regular string if you want to use
JavaScript functions like split
, replace
etc. You can do this by calling .rawString()
on the parameter.
The function's result must be a string. If you want to use the function to calculate
conditions you can return an empty string ""
in case of false and a string with content "true"
.
Use {% if variable|testfunction %}
to test the condition from the template.
Loading and using the filter
Loading a filter is easy. Just add the following line to the template:
{% load makecssselector %}
The value must be the same as the file name.
Using the filter is done by appending the filter's name to a value:
{{sprite.trimmedName|makecssselector}}
Tricks
Since a filter only takes a single parameter, some tricks can be used to perform calculations with multiple parameters:
Use one filter to set a global variable, use another to perform the calculation:
width = 1;
var SetWidth = function (input) {
width = input; // store the value
return ""; // return empty string
};
SetWidth.filterName = "setWidth";
Library.addFilter("SetWidth");
var MakeRelX = function (input) {
return String(input / width); // perform calculation, return result as string
};
MakeRelX.filterName = "makeRelX";
Library.addFilter("MakeRelX");
This is how you can use it:
{{texture.size.width|setWidth}} -- set the width
{{sprite.frameRect.x|makeRelX}} -- calculate x/width
Javascript-only exporters
If you prefer to write the complete exporter code in Javascript, you can use the following line of code to pass the entire TexturePacker configuration and results to a filter function:
{% load testexporter %}{{tp|exportData|safe}}
In the JavaScript file, you have the option to construct the data file content
using string operations or, for example, use JSON.stringify()
to serialize
a JavaScript object as JSON.
The following example demonstrates the latter approach:
var exportSprites = function(sprites)
{
var result = {};
for (var i = 0; i < sprites.length; i++)
{
var s = sprites[i];
var frame = [ s.frameRect.x, s.frameRect.y, s.frameRect.width, s.frameRect.height ];
result[s.trimmedName] = frame;
}
return result;
}
var exportTextures = function(root)
{
var textures = root.allResults[root.variantIndex].textures;
var result = {};
for (var i = 0; i < textures.length; i++)
{
var textureName = textures[i].fullName;
var sprites = exportSprites(textures[i].allSprites)
result[textureName] = sprites;
}
return result;
}
var exportData = function(root)
{
var doc = exportTextures(root);
return JSON.stringify(doc, null, "\t");
}
exportData.filterName = "exportData";
Library.addFilter("exportData");