OmniGraffle API: Get Text Data From Shapes

I recently had an exercise where I needed to extract all the text from an OmniGraffle document. The task at hand was to check a beautifully designed organisation chart against a spreadsheet list of staff. Doing this task manually was going to take far too long.

There had to be a better way of extracting the information I needed from the document: and thankfully there was!

OmniGraffle have a new feature labelled Omni-Automation for OmniGraffle that allows you to write JavaScript code and create/modify objects on the canvas.

While the documentation is still a work in progress, there’s enough information to do what I needed to extract from the OmniGraffle canvas.

Extract Information From OmniGraffle Document

First, open the OmniGraffle document, and then click on the Automation menu item, then on Show Console .

This will open up a console window similar to what most users writing JavaScript code in a browser console window see. At the bottom of the console window is a prompt > .

Omnigraffle Console Window
The console will look like this when you’ve performed some operations in it.

Here you will enter your commands similar to what you would do with a Python script.

To check you are operating on the right OmniGraffle document, next to the console prompt enter the following command:

> document.name

"My OmniGraffle Document.graffle"

Next, you should see the file name of the OmniGraffle document you want to operate on. If you don’t then make sure the OmniGraffle document is in the active window and go back to the instructions above on opening the documents’ console window ( Automation > Show Console ).

Get Canvas

Now that you have the right document opened, the next step is selecting the right canvas where the information you’re seeking is located. You should be able to see a list of all the canvases in the left-sidebar of the opened OmniGraffle document.

Run the following command to get a list of all the canvases available in the active document:

> canvases

[[object Canvas]]

In my case, there was only one canvas object.

Get Shapes

Once you have the right canvas it’s now a simple matter of extracting the shapes.

> canvases[0].shapes

[[object Shape], [object Shape], [object Shape], [object Shape], [object Shape], [object Shape], [object Shape], [object Shape], [object Shape], [object Shape], …] (96) 

As you can see there are a lot of shapes on this OmniGraffle document! I can inspect the properties of each shape by calling an element within this array, the first element returns the following properties:

> canvases[0].shapes[0]

[object Shape] {actionURL: null, alignsEdgesToGrid: true, allowsConnections: true, automationAction: [], autosizing: [object TextAutosizing: Full], blendColor: null, blendFraction: 0, connectedLines: [], cornerRadius: 0, fillColor: null, fillType: null, flippedHorizontally: false, flippedVertically: false, fontName: "HelveticaNeue-Italic", geometry: [object Rect: (1082.2039999999997, 1924.535888671875, 146.09200000000055, 63.23199462890625)], gradientAngle: 90, gradientCenter: [object Point: (0.0, 0.0)], gradientColor: [object Color], id: 223, image: null, imageOffset: [object Point: (0.0, 0.0)], imageOpacity: 0, imagePage: 0, imageScale: [object Size: (0.0, 0.0)], imageSizing: [object ImageSizing: Manual], incomingLines: [], layer: [object Layer], locked: false, magnets: [], name: null, notes: "", outgoingLines: [], plasticCurve: null, plasticHighlightAngle: null, rotation: 0, shadowColor: null, shadowFuzziness: 3, shadowVector: [object Point: (0.0, 2.0)], shape: "Rectangle", shapeControlPoints: [[object Point: (1082.2039999999997, 1924.535888671875)], [object Point: (1082.2039999999997, 1924.535888671875)], [object Point: (1228.2960000000003, 1924.535888671875)], [object Point: (1228.2960000000003, 1924.535888671875)], [object Point: (1228.2960000000003, 1924.535888671875)], [object Point: (1228.2960000000003, 1987.7678833007812)], [object Point: (1228.2960000000003, 1987.7678833007812)], [object Point: (1228.2960000000003, 1987.7678833007812)], [object Point: (1082.2039999999997, 1987.7678833007812)], [object Point: (1082.2039999999997, 1987.7678833007812)], …], shapeVertices: [[object Point: (1082.2039999999997, 1924.535888671875)], [object Point: (1228.2960000000003, 1924.535888671875)], [object Point: (1228.2960000000003, 1987.7678833007812)], [object Point: (1082.2039999999997, 1987.7678833007812)]], strokeCap: [object LineCap: Round], strokeColor: null, strokeJoin: [object LineJoin: Round], strokePattern: [object StrokeDash: Solid], strokeThickness: 1, strokeType: null, text: "THIS IS WHAT I NEED TO EXTRACT FROM EACH SHAPE OBJECT", textAlongPathGlyphAnchor: 0, textColor: [object Color], textFlow: [object TextFlow: Resize], textGeometry: [object Rect: (1087.2039976196284, 1929.535888671875, 136.09200000000055, 53.23199462890625)], textHorizontalAlignment: [object HorizontalTextAlignment: Center], textHorizontalPadding: 5, textRotation: 0, textRotationIsRelative: true, textSize: 11, textUnitRect: [object Rect: (0.0, 0.0, 1.0, 1.0)], textVerticalPadding: 5, textVerticalPlacement: [object VerticalTextPlacement: Middle], textWraps: false, tripleBlend: false, userData: [object Object]} = $4

When I found the property I needed, the text property in the Shape object, it was a simple matter of using a JavaScript array method to obtain this information.

I thought the map function would work best in getting the information first, before then outputting it into a meaningful use:

> result = canvases[0].shapes.map( function( shape ) {
     return shape.text;
  });

["THIS IS WHAT I NEED TO EXTRACT FROM EACH SHAPE OBJECT", "Employee 1", "Employee 2", "Employee 3", "Employee 4", "Employee 5", …] (96)

To copy and paste the data out of OmniGraffle console window and into the spreadsheet I needed to compare the data, I joined the resulting array as one long comma delimited string for each element in the array which produced the following result in the console window:

> result.join()

"THIS IS WHAT I NEED TO EXTRACT FROM EACH SHAPE OBJECT,Employee 1,Employee 2,Employee 3,..."

Summary

OmniGraffle’s API is quite easy to navigate and use, at least for my use case as detailed above. It has me wondering whether I can go the other way by designing the organisational chart using code, rather than having someone spend the time designing it up.

To extract the information you need from OmniGraffle make sure you are operating on the right file, then just loop through each canvas and the respective shape property on each canvas. From the array of Shapes you can further loop through each element and extract the property desired.

Photo of author
Ryan Sheehy
Ryan has been dabbling in code since the late '90s when he cut his teeth exploring VBA in Excel. Having his eyes opened with the potential of automating repetitive tasks, he expanded to Python and then moved over to scripting languages such as HTML, CSS, Javascript and PHP.