Reading txt, csv file data to create shapes

Can’t figure out how to write automation JS to read txt or csv file. The Reason is to create objects programmatically depends on data from those files.
Thanks for any support!

One approach here:

Loading local text file - Automation / OmniGraffle Automation - The Omni Group User Forums

1 Like

Thank you very much for your response!

I’m new to OG and JS (I’ve some experience in .NET C# coding), but nevertheless from approach you’ve shared I couldn’t understand how to use this JXA code from OG JS automation console, the “link below” that could help me with this (from post you recommended) doesn’t work.
Can you help me with direction of further searches?

Part of the link name seems to have changed:

Try this:

1 Like

Thank you very much for your help!

And now I need to solve an another problem. How to run this script from own OG Plug-In script?
Let me describe the whole I need to realise with this plug-in:

  1. To Detect some shape selected (it’s easy to implement)
  2. To Read csv file with specification line by line which contains rows of values (color, size, name, ID, some key-value pairs). Reading is works fine by code you share above (using AppleScript), but how to run it from OG console (plug-in) and then loop reading the string line by line I cannot figure out.
  3. Then Multiply selected shape in step 1 relatively to information from this csv file (set color, size, name, ID, key-value pairs

Can I ask you to write or show short working portion of code of collaboration JXA and OmniJS (OG plug-in script) in plug-in context. Next I’ll be able to understand the logic of such approach.

Thanks a lot!



how do you decide which row in the file is relevant ?

Or is your meaning that a new copy of the selected shape is made for every row in the file ?

(is there another language, in addition to English, that it might be easier to express these things in more directly?)

To do make one new shape per (specification file) line entirely in JXA (Script Editor, top-left language selector set to JavaScript), and given a tab-delimited specification file at ~/spec.txt with contents like:

Expand disclosure triangle to view tab-delimited specifications
alpha	100	50
beta	200	100
gamma	300	150
delta	400	200

and a JXA .scpt file with contents on the lines of:

Expand disclosure triangle to view JavaScript
(() => {
    "use strict";

    const copyFromSpec = graffle =>
        graphics => baseShape => keyNames => spec => {
                values = spec.split(/\t/u),
                intExpected = keyNames.length;

            return values.length < intExpected ? (
                `Expected ${intExpected} fields in ${spec}`
            ) : (() => {
                const [x, y] = baseShape.origin();
                const [w, h] = baseShape.size();

                const shp = graffle.Shape({
                    text: values[0],
                    origin: [parseInt(values[1], 10), y],
                    size: [w, parseInt(values[2], 10)]

                return (

    const main = () => {
            fpSpec = "~/Desktop/spec.txt",
            fieldNames = ["title", "x", "height"];

        return doesFileExist(fpSpec) ? (() => {
                graffle = Application("OmniGraffle"),
                windows =;

            return 0 < windows.length ? (() => {
                    win =,
                    doc = win.document,
                    selection = win.selection();

                return 0 < selection.length ? (() => {
                        baseShape = selection[0],
                        graphics =,
                        xs = lines(readFile(fpSpec));

                    return 0 < xs.length ? (
                    ) : `${fpSpec} is empty.`;
                })() : `Nothing selected in '${}'`;
            })() : "No windows open in OmniGraffle";

        })() : `No file found at ${fpSpec}`;

    // --------------------- GENERIC ---------------------

    // doesFileExist :: FilePath -> IO Bool
    const doesFileExist = fp => {
        const ref = Ref();

        return $.NSFileManager.defaultManager
                .stringByStandardizingPath, ref
            ) && 1 !== ref[0];

    // lines :: String -> [String]
    const lines = s =>
        // A list of strings derived from a single
        // string delimited by newline and or CR.
        0 < s.length ? (
        ) : [];

    // readFile :: FilePath -> IO String
    const readFile = fp => {
        // The contents of a text file at the
        // filepath fp.
            e = $(),
            ns = $.NSString

        return ObjC.unwrap(
            ns.isNil() ? (
            ) : ns

    return main();


Thanks for your code it’s explains a lot for me.
And now I want to figure out how to manipulate using same logic you’ve shared but now doing this with Groups not with simple Shapes.
Even I couldn’t simply to clone existing selected graphic (which is a Group):

simply for experiment passing baseShape argument to push it to graphics

return (
          graphics.push(baseShape or baseShape.Shape or other other code),

Error: cannot to create a class…

There is a function “duplicateTo” but how to call it from js? Is there other approache to clone Groups with parameters from spec.txt?

Thanks for any support!