Anime Studio Document File Format
Anime Studio Document File Format
● Introduction ● Document Overview
○ Zip Container ○ JSON Project Document (Project.animeproj)
● Document Structure ○ Document Header ○ Styles ○ Layer Comps ○ Camera Movements and Timeline Markers ○ Layer Definitions ○ Document View State (optional preference)
● How to find what you are looking for from the old format within the new format? ● How to edit the new format using a text editor ● How to edit the new document format with a scripting tool ● Tools for Beautifying JSON ● Recommended JSON editors ● Python Scripts
1
Introduction With Anime Studio 11.0, the product team released a brand new document file format. The older .anme format was replaced with a new JSONbased format (file extension: .anime). Using a standard format like JSON made it easier to maintain a format that is both backward and forward compatible as newer versions of Anime Studio are released. It can do that because the JSON format can organize data in a specific yet flexible hierarchy. This hierarchy can allow us to add, and in some cases delete data values without breaking the file format. In order for the document to save space on user's storage devices, the document format is also compressed in a container using the Zip file format. Since JSON is a text based format, Zip dramatically reduces its size on disk. One of the reasons we picked both JSON and Zip is they are both popular and widely used. That makes it easier for users to be able to use third party libraries and scripting languages to manage and manipulate the document if they feel inclined. For example, the scripting language Python has both JSON parsing and Zip compression decompression builtin. Using a small bit of scripting, it is quite easy to open, query, modify, and save an existing document. We'll show an example of this later in the document. One note about the pre11.0 document format. Anime Studio will continue to read the old format for the foreseeable future. We realize there is a lot of content out there. While the old format is considered deprecated, the application should continue to read and convert documents as old as Anime Studio 6.
2
Document Overview If you were to try to open up a .anime file in a text editor or hex editor, you most likely wouldn't understand what you were looking at. This is because the .anime format is using the Zip compressed file format as a container for the real data of the document.
Zip Container Zip compression shrinks down the size of the data by looking for duplicated items in the document and replacing it with binary data that is significantly smaller than the original information. Since JSON is a text based format, the amount of compression we get is rather dramatic. We've seen document data reduce in size on disk from 250 MB down to about 4 MB! If you were to rename your MyAnimeDocument.anime file to MyAnimeDocument.zip, you could expand the .anime file using any Zip decompression tool (eg StuffIt, WinZip, WinRAR, etc). One or two files should be created.
■ Project.animeproj the main project file ■ preview.jpg the project thumbnail image
Project.animeproj is the JSON file that defines all the main data about your Anime Studio document including layers and styles. The whole next section will be about describing this file. preview.jpg is the optional thumbnail that is created of the project canvas when the document was saved. This file is used by the thumbnail previewer on Windows and the QuickLook plugin on Mac. The reason the thumbnail is optionally in the archive is the image file will not be created if thumbnail creation is disabled in the Documents preferences. (See "Document thumbnail preview size")
JSON Project Document (Project.animeproj) In order to understand how the Anime Studio project document works, it is necessary to review some of the basics of JSON. Below is an example of the first lines of the project document. This document has been beautified to add whitespace and color.
3
JSON is a keyvalue based hierarchical file format. This means that you associate data values from the document by using a specific key. If you want to reference a particular bit of information in the document, you just have to search for its key. (In the screenshot above, the keys are in blue.) If you would like to find in the document what date the document was created, you'd just search the JSON for the "created_date" key. The data value would be the time stamp. Keyvalue pairs together are considered "properties" of the format.
JSON Key/Value
"created_date": "Fri Jan 22 17:59:49 2016"
The hierarchical part of JSON means that you can associate one or more pieces of document data by making it be a "parent" or "child" of that data. For example, Anime Studio
4
uses layers to define objects on the canvas (eg Vector, Image, Switch, etc). A vector layer may be described by many properties such as points, lines, curves. Those drawing types are in turn are described by even deeper properties such as their location on the canvas and how they can be animated. The JSON file format keeps track of all those properties by establishing a parent/child relationship between them. For example, this is a pseudohierarchical representation for a line in a vector layer: All Layers > Vector Layer > Line > Line Properties With JSON, you can also group related data into objects and lists. Objects use { } syntax and lists use [ ] syntax. An object groups properties together that are related into a unified concept. For example, if you wanted to define a background color based on it's RGBA values, you may want to define it this way:
Background Color Object
"back_color": { "r": 234, "g": 234, "b": 234, "a": 255 },
From the definition it becomes clear which properties are considered red, green, blue, or alpha by looking at its keys. A list describes properties, objects, or even other lists that are usually the same type. The advantage of lists is that they are not fixed sizes. So you can define as many items as you need. Here is how you may want to define a list of layer comps:
Layer Comps Defined in a List
"layercomps": ["Background","Character 1", "Character 2", ... ]
(Note that the example above isn't exactly how layer comps are defined in the .anime file format. Properties were removed for brevity.) Objects and lists are what make JSON a really powerful part of the format because to add new data, you just have to add a new property or object. Parsers that read and understand JSON typically ignore information they don't understand. So let's say we add in Anime Studio 15.0 a new property with a key of "my_new_animation_feature". When Anime Studio 11.0 opens that file, it just ignores that feature. This is good news if the new feature was not an integral feature of the document. If it is an important feature, you will have to use Anime Studio 15.0 to make use of it.
5
Document Structure Now that we have JSON basics under control, let's start going through some of the guts of the format. Within the JSON document there are a number of different sections:
■ Document Header ■ Camera Movements and Timeline Markers ■ Styles ■ Layer Comps ■ Layer Definitions ■ Document View State
Each section mostly correlates to the old .anme format. The big difference is that in the old format not all items used a key to define its value. On the surface, that means that the new JSON file format is a bit bigger and a bit more verbose than the old format. The positive is it typically makes it much more easier to find the data values you are looking for. For those who wish to understand more about each of the document sections, they will be described in more detail below.
Document Header The document header contains general or projectwide information about the document. It lives at the very top of the document. In the example below you can see that the header includes version information, a creation date, as well as a comment the application writes. There are three versions specified in the header:
● major_version ● version ● rev_version
Anime Studio can use each of these version numbers in different ways. "major_version" is the major document version. We hope this never happens, but if we have to change the file format so radically so that old versions of our software can't read it, this number will increase. "version" is the document version. It is used mostly to let the application know that this document is newer or older than what the current application can open. If the version number is newer, a warning will be given to the user that the document has been created with a newer version of the document. Finally, rev_version is just a revision number. This revision number is used mostly for internal purposes. The "project_data" object contains properties that are specific to this project. Most of these properties come directly from the Project Settings dialog (File>Project Settings) in the application.
● width/height The project width and height in pixels (eg 720p or 480p) of the export ● start_frame/end_frame The starting and ending frame of the animation ● fps frames per second of the animation ● back_color background color of the canvas ● depth_of_field/focus_distance/focus_range/focus_blur depth of field properties
Document Header
6
"mime_type": "application/xvnd.lm_mohodoc", "version": 1021, "major_version": 1, "rev_version": 0, "comment": "Created in Moho version 12.0, Copyright © 19992016 Smith Micro Software, Inc.", "created_date": "Fri Jan 22 17:59:49 2016", "project_data": { "width": 852, "height": 480, "start_frame": 1, "end_frame": 48, "fps": 24, "back_color": { "r": 234, "g": 234, "b": 234, "a": 255 }, "noise_grain": 0, "pixelation": 0, "antialiasing": true, "depth_sort": false, "distance_sort": false, "depth_of_field": false, "focus_distance": 2, "focus_range": 1, "focus_blur": 0.050625, "global_render_style_fill_style": 0, "global_render_style_line_style": 0, "global_render_style_layer_style": 0, "global_render_style_minimize_randomness": true, "stereo_mode": 0, "stereo_separation": 1, "extra_swf_frame": false, "color_palette": "Basic Colors.png", "soundtrack": "" }
Styles All the global styles of the document are defined in this section. For each style there is a:
● name ● unique identifier ● line and fill color ● brush name ● brush properties
7
Styles
"styles": [ { "type": "Style", "name": "Dragon_Tongue_Shading", "uuid": "0ff99f9b299b44979f736c38dda5e952", "define_fill_color": true, "fill_color": { ... } "define_line_width": true, "line_width": 0.001389, "define_line_col": true, "line_color": { ... } "line_caps": 1, "brush_name": "Brush504.png", "brush_align": false, "brush_jitter": 6.283185, "brush_spacing": 0, "brush_angle_drift": 0, "brush_randomize": false, "brush_merged_alpha": false, "brush_tint": true }, { "type": "Style", "name": "Dragon_Wing_Dark", ... } }
Layer Comps Layer comps are used to focus on certain layers when composing or rendering. You can manage the creation, deletion, or updating of layer comps in the Layers palette. Layer comps in the document are defined by three items:
■ Name ■ Unique identifier (UUID) ■ A list of layer IDs
8
Layer Comps
"layercomps": [ { "name": "Background", "uuid": "B85FA8AD2CB143C489E53B86138CAC40", "layer_ids": [ "DE907C68CD6C4353A5C74D416E208BA7" ] }, { "name": "Character 1", "uuid": "ABC86A51B35443E99D2C5064D8DAD0B0", "layer_ids": [ "11A8AB8BF7A64E7B987CF08F546E95EF", "75BAC57EC083470FB81209CF8F2AE032", "DE907C68CD6C4353A5C74D416E208BA7" ] },
... ],
The "layer_ids" key specifies a list of layer IDs that are UUIDs. In the layers section of the document, each layer has a unique layer ID.
Camera Movements and Timeline Markers Camera movements and timeline markers are defined in the "animated_values" object.
Camera Movements and Timeline Markers
"animated_values": { "camera_track": { ... }, "camera_zoom": { ... }, "camera_roll": { ... }, "camera_pan_tilt": { ... }, "timeline_markers": { ... } },
Layer Definitions The layers section of the document is undoubtably the largest section of any .anime document. Quite simply, it defines all of the layers and their attributes. Here are the current layer types:
■ Vector Layer (aka Mesh Layer) ■ Image Layer (includes image and movie types) ■ Audio Layer
9
■ Group Layer ■ Switch Layer ■ Bone Layer ■ Particle Layer ■ Note Layer ■ Text Layer ■ Patch Layer ■ Poser Layer (aka Mesh 3D Layer)
Each of the layers in the Project.animeproj JSON file will have numerous properties associated with it. In fact it is too many to name in this document. There are some common properties like type, name, and unique ID, but then there will also be a number of layer specific properties. For example, an image layer requires that there be a path to the image file (eg MyPhoto.png)
Layers
"layers": [ { "type": "ImageLayer", "name": "Background", ... }, { "type": "MeshLayer", "name": "Character 1", ... }, ... }
Document View State (optional preference) In Anime Studio 11, there is a new checkbox in the Documents Preferences to save the document view state within the document. Enabling "Save and restore view settings in the document" will add a new "documentviewstate" object. Documents that don't save the view state, will just use the default values when they are opened. An example view state property that can be saved with the document happens when you select View>Show Rule of Thirds in the main menu. Each time you open that document, the canvas will show the Rule of Thirds grid.
10
Document View State
"documentviewstate": { "DocState_viewportSetting": 1, "DocState_gridOn": false, "DocState_gridStyle": 0, "DocState_gridSize": 20, "DocState_gridSnappingOn": true, "DocState_showOutputOnly": false, "DocState_showVideoSafeZones": false, "DocState_showRuleOfThirds": true, "DocState_viewPortLeftWidthPct": 1, "DocState_viewPortTopWidthPct": 1, "DocState_zoom0": 0.9, "DocState_viewOffset0": { ... }, "DocState_rotation0": 0, "DocState_stereo0": false, "DocState_enableOutsideView0": false, "DocState_outsideViewPitch0": 0, "DocState_outsideViewYaw0": 0, "DocState_outsideViewRadius0": 0, "DocState_outsideViewAttn0": { ... }, ... "DocState_playStart": 1, "DocState_playEnd": 1 }
How to find what you are looking for from the old format within the new format? The old .anme document format that was used in Anime Studio 10 and earlier was a custom format. While the format was text based, there were some format issues that made it difficult to add new properties without breaking the format. Since you just had to drag and drop a .anme file into a text editor, it was relatively trivial to make changes if you knew some basics about the format. The good news is that the new document format tries to mimic the sections of the old format. There was a Document Header, Layer Comp, Style, and Layer section to the document. If you have an older .anme file, opened it Anime Studio 11, converted the document to .anime and than compared the documents sidebyside, a lot would match up. The main big difference is every property now must have a key associated with values. That means that it should be easier to find a certain value in the document if you know it's key. So if you want to change a value of an Image Layer, all you'd need to do is search for "ImageLayer" in the JSON file. Find the specific image layer if there is more than one. Then you just find the property that you want to change. For example, if you wanted to enable the Toon Effect on that image layer, you'd continue your search and look for the key "toon_effect".
11
The keys may make the file format be more verbose looking, but the few times we have had to open up a document to check out a property, we find it easier to really drill down into what specific properties are. There are two areas were it will actually be easier to understand in the new format than the old format.
1. Finding specific layers 2. Finding type information about that layer.
In the old format, layers were defined by an ID number. You'd used to have to search through the file and where a new layer was starting you had to know that a Image Layer started with 2 and than you could search for this:
layer_type 2 { ... }
Here were all the ID numbers:
■ Vector Layer = 1 ■ Image Layer = 2 ■ Group Layer = 3 ■ Bone Layer = 4 ■ Switch Layer = 5 ■ Particle Layer = 6 ■ Note Layer = 7 ■ Poser/3D Layer = 8 ■ Audio Layer = 9 ■ Patch Layer = 10 ■ Text Layer = 11
In the new format each layer has a "type" key that has one of the following values: ● Vector Layer = "MeshLayer" ● Image Layer = "ImageLayer" ● Group Layer = "GroupLayer" ● Bone Layer = "BoneLayer" ● Switch Layer = "SwitchLayer" ● Particle Layer = "ParticleLayer" ● Note Layer = "NoteLayer" ● Poser/3D Layer = "Mesh3DLayer" ● Audio Layer = "AudioLayer" ● Patch Layer = "PatchLayer" ● Text Layer = "TextLayer"
No more searching by layer ID. Once you are in the JSON object for the layer, you then just navigate down to set its property. This should be relatively easy for top level properties of the layer (eg "name", "path"). Note that most of the layers make use of complex properties. For
12
example, a vector layer may be made up 100s of shapes. Those shapes may have 100s of points. Editing one of these points may be extremely difficult using just the document file.
How to edit the new format using a text editor 1. Copy the Anime Studio document from MyAnimeDocument.anime to a new file
named MyAnimeDocument.zip. 2. Expand the .zip file using a decompression tool like StuffIt, WinZip, WinRAR, or the
Zip mechanism built into your machine. 3. There should be two files that are expanded: Project.animeproj and preview.jpg 4. Beautify the Project.animeproj file. (See below) 5. Open the Project.animeproj file in a text editor. 6. Make your edits to the text file 7. Zip the Project.animeproj using a compression tool like above. Make sure to choose
the .zip format, not 7z or RAR. 8. Name the archive MyModfiedAnimeDocument.zip 9. Once zipped, rename the document to MyModifiedAnimeDocument.anime. 10. Try and open in Anime Studio.
In step 4 you will most likely need to "beautify" or "pretty" up the Project.animeproj file before it is edited in the Text Editor. In order to save space on the user's computer, the Project.animeproj file had all unnecessary whitespace characters (spaces, tabs, new lines) removed by Anime Studio when saved. Beautifying means that you are adding all that back in just so that it will be easy to edit. There are a number of tools that will do this for you. In the section below, there will be links to beautifiers. The sample code provided below also show how to beautify programmatically. There is no need to remove all of the whitespace/newline characters after making your edits in step 6. The next time that the document is saved, the whitespace will again be removed. In step 7, it is not necessary to Zip the preview.jpg file. This is purely optional. The thumbnail preview will be regenerated the next time the document is saved. Also in step 7, keep in mind that some Zip tools use their own Zip format for compression (eg WinZip). Make sure you specify in the application preferences the most "portable" version of the .zip format. That is usually LZ compression.
How to edit the new document format with a scripting tool Editing the new document format with a scripting tool follows the same general path as manually editing from the section previous. The difference is you can automate the Zip compression/decompression as well as the browsing/editing of the JSON. At the bottom of this document there are two Python scripts that you can use as an example. Other scripting languages have support for Zip and JSON, either through builtin support or by using a library.
13
Scripting Pseudo Code
// Unzip the .anime file ... // Open the Project.animeproj file ... // Use the JSON parser to look for 1 or more keys ... // Print/Edit the values for the key(s) ... // Close the Project.animeproj file ... // Zip the Project.animeproj back to the original .anime file ...
Tools for Beautifying JSON With AnimeStudio removing all of the whitespace and newline characters from the document, it will be necessary to beautify to text to make it easier to search for keys and values. Here are a list of tools we use within Smith Micro to help beautify. JQ JSON Query Utility Commandline tool for Mac or Windows BBEdit or TextWrangler JSON Text Filter Beautifier (Mac only) JS Beautifier (Web) JSON Beautifier & Formatter (Web) JSON Beautifier (Web)
Recommended JSON editors JSON editors can help you visualize a JSON file by syntax coloring and collapsing lists and objects. JSON Editor Online (Web) Notepad++ with JSTool plugin (Windows) Xcode (Mac) BBEdit or TextWrangler (Mac) Keep in mind that JSON is just text. So it can be opened with your favorite text editor. It might not have syntax coloring, but it can get the job done.
14
Python Scripts Below are two Python 2.x scripts. The first one just extracts 1 or more .anime files to JSON. MyAnimeDocument.anime become MyAnimeDocument.json.
Extract to JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#!/usr/bin/env python """Extract To JSON Takes 1 or more .anime file and converts it to JSON extract_to_json.py <.anime files> """ import os import os.path import json import shut import sys import zipfile def ExtractToJSON(animepath, beautify = False): if (not os.path.exists(animepath)): print 'ERROR: %s does not exist' % animepath return # Get the folder and the file name for the path folder, animefilename = os.path.split(animepath) print 'Extracting from ' + animefilename + '...' # Open the .anime file with zipfile.ZipFile(animepath) as zip_file: # Iterate over all of the items in the zip file for item in zip_file.namelist(): filename = os.path.basename(item) # skip everything except the .animeproj file, which is JSON if filename != 'Project.animeproj': continue # Create the extracted .json path name filename, ext = os.path.splitext(animefilename) filename += '.json' jsonpath = os.path.join(folder, filename) # Load the JSON from the zip file and dump it beautified to the .json path source = zip_file.open(item) if (beautify): json_obj = json.load(source) json.dump(json_obj, file(jsonpath, "wb"), sort_keys=False) else: shutil.copyfileobj(source, file(jsonpath, "wb"))
15
42 43 44 45 46 47 48 49 50 51 52 53 54 55
def Usage(): print '%s <.anime files> Extract .anime to JSON' % os.path.basename(sys.argv[0]) def perform(): numArgs = len(sys.argv) if numArgs == 1: Usage() return for animepath in sys.argv[1:]: ExtractToJSON(os.path.normpath(animepath)) if __name__ == '__main__': perform()
16
This second script allows you to list, extract or update an existing .anime without even opening the file.
Anime Util Manipulate a .anime file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#!/usr/bin/env python """anime_util Manipulate a .anime file Commands are list, extract, and update. list prints the paths of image and audio layers. extract saves the Project.animeproj to a desired location. update replaces existing paths with the one supplied. anime_util.py h |command| for help. """ import zipfile import json import argparse import os import sys def extractProject(animeFile): """Return the Project.animeproj extracted from a zip file as a string.""" zf = zipfile.ZipFile(animeFile) for filename in [ 'Project.animeproj' ]: data = zf.read(filename) return data def writeProject(animeFile, projectData): """Write data back into Project.animeproj in a zip file""" zf = zipfile.ZipFile(animeFile, mode="w") zf.writestr('Project.animeproj', projectData) def printLayerInfo(layer): """Print the path of an image or audio layer.""" if layer.has_key('image_path'): print "image:", layer['image_path'] if layer.has_key('audio_path'): print "audio:", layer['audio_path'] def updateLayerInfo(layer, update): """Replace the path of an image or audio layer.""" if layer.has_key('image_path'): path = layer['image_path'] layer['image_path'] = path.replace(update[0], update[1]) if layer.has_key('audio_path'): path = layer['audio_path'] layer['audio_path'] = path.replace(update[0], update[1])
17
45 46 47 48 49 50 51 52
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
def isContainerLayer(layer): """Return True if the layer is a group or switch layer.""" type = layer['type'] return type == "GroupLayer" or type == "SwitchLayer" def iterateContainerLayer(groupLayer, update=None):
"""Iterate a group or switch layer descending into sub groups and updating or printing layers otherwise.""" for layer in groupLayer: if isContainerLayer(layer): iterateContainerLayer(layer['layers'], update) else: if update: updateLayerInfo(layer, update) else: printLayerInfo(layer) def iterateLayers(path, update=None): """Iterate the root project layers.""" projectPath = path projectData = extractProject(projectPath) if len(projectData) > 0: jsonData = json.loads(projectData) layers = jsonData['layers'] iterateContainerLayer(layers, update) if update: writeProject(projectPath, json.dumps(jsonData)) iterateContainerLayer(layers) def extractProjectFile(animeFile, projectFilepath = None): """Iterate Project.animeproj to projectFilePath.""" jsonData = extractProject(animeFile) if len(jsonData) > 0: jsonData = json.loads(jsonData) if projectFilepath: output = file(projectFilepath, 'wb') else: output = file('Project.animeproj', 'wb') json.dump(jsonData, output, sort_keys=False, indent=4) else: parser.error("Project.animeproj is empty") def perform():
18
94
95 96
97 98 99
100 101
102
103 104
105 106
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
parser = argparse.ArgumentParser(description="Open a .anime file and print and/or update the paths of image and audio layers") subParsers = parser.add_subparsers(help="commands", dest="command")
parser_list = subParsers.add_parser("list", help="list image and audio paths in Anime Studio file (.anime).") parser_list.add_argument('path', help="path to Anime Studio file")
parser_extract = subParsers.add_parser("extract", help="extract Project.animeproj from Anime Studio file (.anime).") parser_extract.add_argument('path', help="path to Anime Studio file")
parser_extract.add_argument('projectFile', metavar='project file', help="path to save Anime Studio project file")
parser_extract.add_argument('b', 'beautify', action="store_true", help="beautify the project file")
parser_update = subParsers.add_parser("update", help="update image and audio paths in Anime Studio file (.anime).") parser_update.add_argument('existing', help="existing path(s) in file") parser_update.add_argument('new', help="path to replace existing path(s) with") parser_update.add_argument('path', help="path to Anime Studio file") args = parser.parse_args() path = os.path.abspath(args.path) if not os.path.exists(path): parser.error("%s does not exist." % path) if not path.endswith(".anime"): parser.error("%s is not an Anime Studio (.anime) file." % path) updateInfo = None if args.command == "update": updateInfo = (args.existing, args.new) if args.command == "extract": extractProjectFile(path, os.path.abspath(args.projectFile)) else: iterateLayers(path, update=updateInfo) if __name__ == '__main__': perform()
19