Acorn's native file format is used to losslessly store layer data, editable text, layer filters, an optional composite of the image, and various bits of metadata about the image. It's advantage over other common formats such as PNG or JPEG is that it can store all this native information without flattening all the layer data and vector graphics.
But proprietary closed formats are a big drawback, right? Well in Acorn's case it's actually very easy to pull out all the layer and image information because the image file is actually a SQLite database. SQLite is the most used database engine in the world, is open source, and is integrated into just about every programming language.
Identifying an Acorn Image
Acorn images are single files which end with .acorn
.
Inspecting an Acorn Image with SQLite
If you are on macOS, you can use the Terminal app to open and inspect an Acorn file with the following command:
/usr/bin/sqlite3 /path/to/acorn_file.acorn
To look at the image information, you can type .schema
to get a list of tables.
The SQLite Database Schema
| CREATE TABLE image_attributes ( name text, value blob);
CREATE TABLE layers (id text, parent_id text, sequence integer, uti text, name text, data blob);
CREATE TABLE layer_attributes ( id text, name text, value blob);
|
Acorn sets the SQLite application_id to 'Acrn'
, which when viewed as a four byte number is 1097036398
. You can view this with the SQLite statement PRAGMA application_id;
The image_attributes Table
The image_attributes
table is a simple key value store which stores information about the image such as the bits per pixel, the DPI, the size of the image, the color profile, the composite image, and more. Most of the key values are self describing. Here is an example of some of the data in this table:
| sqlite> select * from image_attributes limit 10;
┌─────────────────────┬─────────────┐
│ name │ value │
├─────────────────────┼─────────────┤
│ imageSize │ {902, 1889} │
│ dpi │ {72, 72} │
│ bitsPerPixel │ 32 │
│ bitsPerComponent │ 8 │
│ acorn.showsGrid │ 0 │
│ acorn.gridUsesDots │ 0 │
│ acorn.scalesGrid │ 1 │
│ acorn.gridBlendMode │ normal │
│ acorn.gridSpacing │ 10 │
│ acorn.fileVersion │ 4 │
└─────────────────────┴─────────────┘
|
Since the type of data stored in value
column is binary data, you can actually quickly export the composite of the image with this SQLite command:
SELECT writefile('/tmp/myfile.tiff', value) from image_attributes where name = 'composite'
This will write out the composite of the image as a TIFF file.
Note
The format of the composite of an Acorn file is either TIFF or PNG, depending on what version of Acorn was used to last edit the image and the bit depth of the image. To determine if you're dealing with a PNG or TIFF file, you write out the image and inspect it via the file system, or use the following SQLite query:
select hex(substr(value, 1, 4)) from image_attributes where name = 'composite'
4D4D002A
is the header for TIFF, 89504E47
is the header for PNG.
The layers Table
The layers
table stores all the layer information for the image. As of Acorn 8, there are three types of layers: group, bitmap, and shape (vector).
The id
column is a unique ID to each layer.
The parent_id
column is a reference to the parent group that the layer is a part of. If this value is empty or nil, then the layer is not part of a group.
The sequence
lets the enclosing group know what order the layer appears in.
The uti
column is the format of the layer. There are a number of different formats as described:
com.acorn.group
is a group layer and contains no information for the data column.
com.flyingmeat.acorn.shapelayer
is shape / vector layer, and the data column contains an XML representation of the layer.
public.png
or public.tiff
is a bitmap / raster layer, and the data column contains the encoded layer image. Mask layers also store their layer data this way.
The name
column is a UTF-8 string for the layer name.
And finally the data
column is the actual layer data for the layer.
Note
The format of a mask layer is the same as a bitmap layer. The ID for a mask layer has a prefix of mask-
. So if you have a shape layer with an id
of abc-123
, then the mask for that layer would have an id of mask-abc-123
.
The layer_attributes Table
Similar to the image_attributes
table, the layer_attributes
table contains metadata about a layer. The id
column matches with the id
column of the layers
table, and then the name
and value
columns act as a key value store. Here is an example of a three layered image, with the layer ids of 49a5d80a-78d2-4578-979d-1c8ee9674307
, 945a5bd2-61c3-4e93-a8c1-ad7f85957d33
, and 0cc3af32-d73b-48db-8063-c67a63da9a87
:
| sqlite> select * from layer_attributes;
┌──────────────────────────────────────┬────────────────────────────────────┬──────────────────────────────────────────────────────────────┐
│ id │ name │ value │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ visible │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ locked │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ blendMode │ normal │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ opacity │ 1.0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ acorn.maskLinked │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ acorn.maskEnabled │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ acorn.layerStylesEnabled │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ acorn.styleChainSelectionBlendMode │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ frame │ {{0, 0}, {902, 1889}} │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 49a5d80a-78d2-4578-979d-1c8ee9674307 │ shouldApplyStyleChainToSelection │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ visible │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ locked │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ blendMode │ passThrough │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ opacity │ 1.0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ acorn.maskLinked │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ acorn.maskEnabled │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ acorn.layerStylesEnabled │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ acorn.styleChainSelectionBlendMode │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 945a5bd2-61c3-4e93-a8c1-ad7f85957d33 │ groupIsExpanded │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ visible │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ locked │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ blendMode │ normal │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ opacity │ 1.0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ acorn.maskLinked │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ acorn.maskEnabled │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ acorn.layerStylesEnabled │ 1 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ acorn.styleChainSelectionBlendMode │ 0 │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ acorn.layerStylesList │ <?xml version="1.0" encoding="UTF-8"?> │
│ │ │ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http:/ │
│ │ │ /www.apple.com/DTDs/PropertyList-1.0.dtd"> │
│ │ │ <plist version="1.0"> │
│ │ │ <array> │
│ │ │ <dict> │
│ │ │ <key>class</key> │
│ │ │ <string>TSMerlinFilter</string> │
│ │ │ <key>enabled</key> │
│ │ │ <true/> │
│ │ │ <key>name</key> │
│ │ │ <string>TSGaussianBlurClampFilter</string> │
│ │ │ <key>params</key> │
│ │ │ <dict> │
│ │ │ <key>CIAttributeFilterName</key> │
│ │ │ <string>TSGaussianBlurClampFilter</s │
│ │ │ tring> │
│ │ │ <key>inputOption</key> │
│ │ │ <integer>1</integer> │
│ │ │ <key>inputRadius</key> │
│ │ │ <integer>10</integer> │
│ │ │ </dict> │
│ │ │ </dict> │
│ │ │ </array> │
│ │ │ </plist> │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ frame │ {{34, -99}, {902, 1889}} │
├──────────────────────────────────────┼────────────────────────────────────┼──────────────────────────────────────────────────────────────┤
│ 0cc3af32-d73b-48db-8063-c67a63da9a87 │ shouldApplyStyleChainToSelection │ 0 │
└──────────────────────────────────────┴────────────────────────────────────┴──────────────────────────────────────────────────────────────┘
|
Most of the names are self explanatory. You may notice that the value for the acorn.layerStylesList
is XML data. This is the information for the non-destructive fitlers assigned to this layer.
To export all the PNG layer data to files, you can use the following SQLite statements:
| select writefile('/tmp/' || name || '.png', data) from layers where uti = 'public.png';
select writefile('/tmp/' || name || '.tiff', data) from layers where uti = 'public.tiff';
select writefile('/tmp/' || name || '.xml', data) from layers where uti = 'com.flyingmeat.acorn.shapelayer';
|
You can download a sample Acorn file here: Pizza.acorn