Skip to content

ACTN002: Acorn's File Format

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

1
2
3
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:

1
2
3
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