• Stars
    star
    119
  • Rank 297,930 (Top 6 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated almost 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Parse files from Sketch using pure NodeJS

sketch-node-parser

Parse files from Sketch using pure NodeJS.

This project is just a proof-of-concept, and isn't ready for production use.

Summary

This program shows how to open a Sketch file and parse it into a Javascript object tree. It doesn't try to render the file, and doesn't fully model all of the internal data structures. It is just a proof of concept, showing how an open source Sketch file parser could be created.

How to use

Look at parseExample.js to see how to open and parse a Sketch file.

Reverse engineering Sketch files

Sketch files are SQLite3 databases, with binary blobs that are binary-encoded Property Lists (an OSX format).

To decode the property lists, you need class definitions for the internal classes. The main challenge in parsing Sketch files is figuring out these internal classes.

You can see the existing set of classes in /src/msArchiver/sketchClasses.js. That list is probably incomplete, but it isn't too difficult to add more.

Adding Sketch classes

To add more classes, or fix existing ones, it helps to have a JSON view of the internal property lists. You can create those by running extractJsonFromExample.js.

That creates two files: classes.json and objects.json. The classes.json file is an object with class names as keys, and lists of class properties and example serialized classes as values.

The objects.json file is the internal objects array from the NSKeyedArchiver.

Here's an example of a serialized object in objects.json:

{
  "$class": 64,
  "color": 63,
  "image": 0,
  "fillType": 0,
  "noiseIntensity": 0,
  "patternFillType": 0,
  "patternTileScale": 1,
  "noiseIndex": 0,
  "isEnabled": true
}

Every property is either a primitive (number or boolean) or a reference to another object in the objects array. References look like integers because they reference the index of the object in the objects array. The $class property is special in that it is a reference to the class definition, which is also an object in the objects array. The trick is figuring out which are references and which are primitives. You can use the classes.json file to see examples of all serialized objects of a particular class to make this easier.

This particular example is an MSImmutableStyleFill, which has this definition (from /src/msArchiver/sketchClasses):

sketchClasses.MSImmutableStyleFill = function(obj, archive) {
  this.color = archive.deserializeByRef(obj.color);
  this.image = archive.deserializeByRef(obj.image);
  this.fillType = archive.deserializeByRef(obj.fillType);
  this.noiseIntensity = archive.deserializeByRef(obj.noiseIntensity);
  this.patternFillType = archive.deserializeByRef(obj.patternFillType);
  this.patternTileScale = archive.deserializeByRef(obj.patternTileScale);
  this.isEnabled = obj.isEnabled;
};

Most properties here are references, so archive.deserializeByRef() is called to find that reference and deserialize it. One, isEnabled, is a boolean, so it is copied over directly.

Further Reading

This blog post has an excellent description of how Apple's NSKeyedArchiver serializes objects.