3

I use Adobe Illustrator extensively and automate it with AppleScript. I recently made a children's toy where I need to expand letter font objects (so I can cut them with the laser cutter) and offset the expanded objects by 0.6mm (so I can cut a mortise with the CNC router for the letters to fit into). The design for the letter P looks like this with the orange part to become the laser-cutter letter and the black stroke to become the mortise. I also add pictures of the result with the letter outside and inside the mortise.

Design of letter type with expansion and path offset Acrylic laser cut letter outside the CNC-cut mortise Acrylic laser cut letter inside the CNC-cut mortise

I automated this with GUI scripting in Illustrator (see appendix below) and want to code this with an open source library so it is more accessible to anyone who wants to make such a toy with custom letters.

I have tried SVGwrite as suggested here, but I was unable to expand type. I found no answers whether Cairo would allow type expansion and path offset. One solution is to use Inkscape, but I would rather keep the code self-contained by importing a library rather than requiring the installation of an external program.

Do you know of a Python library that allows type expansion and path offset?

Appendix: Here is the only solution I found to automate this. I use AppleScript and Illustrator was with GUI interaction:

-- Code to interact with Illustrator via GUI
property ai_process : "Adobe Illustrator"
on clickMenuBarItemAndItem(menuBarItem, menuItem)
    tell application "System Events"
        tell application process ai_process
            tell menu bar 1
                tell menu bar item menuBarItem
                    tell menu 1
                        tell menu item menuItem
                            click
                        end tell
                    end tell
                end tell
            end tell
        end tell
    end tell
end clickMenuBarItemAndItem

-- Code to expand object
on expand()
    -- no expand command in API, so expand with GUI (this becomes a group)
    my clickMenuBarItemAndItem("Object", "Expand...")

    tell application "System Events"
        tell application process ai_process

            delay 1
            key code 36 -- enter
        end tell
    end tell
end expand


-- Code to offset path
my clickMenuBarItemAndTwoItems("Object", "Path", "Offset path...")
tell application "System Events"
    tell application "Adobe Illustrator" to activate
    keystroke "0.6mm"
    delay 0.1
    key code 36 -- enter
    delay 0.1
end tell
8
  • 1
    As a workaround, in Python script you can use fonts preliminary converted to SVG format, there are online converters, for example everythingfonts.com/ttf-to-svg
    – emax
    Commented Apr 13, 2018 at 11:46
  • 1
    I'm not sure you want a library for Ilustrator specificaly but, give a look at rsvg, svgpathtools, both libraries to manipulate SVG images, maybe you can even take the process out of illustrator with time. Commented Apr 14, 2018 at 8:39
  • @emax: regarding ttf-to-svg, rendering a font as SVG is not my bottleneck, as I can use svgwriteto render fonts in SVG like this: import svgwrite dwg = svgwrite.Drawing('test.svg', size = ("300px", "400px")) letter = "P" dwg.add(dwg.text(letter, insert = (60, 220), fill = "black", style = ("font-size:192pt;font-family:Arial Rounded MT Bold;"))) dwg.save()
    – emonigma
    Commented Apr 21, 2018 at 15:01
  • @emax: and ttf-to-svg failed to convert my font with a message "Failed to convert the file from ttf to svg." What I need is to expand the <text> tag into paths and offset those paths by 3mm.
    – emonigma
    Commented Apr 21, 2018 at 15:08
  • 1
    For offsetting the path: did you people consider the shapely library? shapely.readthedocs.io/en/stable/…
    – Ideogram
    Commented Apr 24, 2021 at 15:42

3 Answers 3

3

I think this is difficult in Python and I used Inkscape, which can expand font objects (called "object to path"), add stroke to paths, and allows running commands from the command-line. For example, this Python script expands and offsets the letter P:

import os
import subprocess

# Set directory
filepath = os.path.expanduser('~/letter.svg')
character = "P"

# Initial plain SVG with the letter
letter_text = ('<text ' +
               'fill="black" '
               + ('style="'
                  + (
                      ("font-size:67px;"
                      + "font-family:Arial Rounded MT Bold;"
                      + "stroke:black;"
                      + "stroke-width:2px;"
                       # round linejoin avoids protruding shards in lower case cursive o and z
                      + "stroke-linejoin:round;"
                  )
                  + '"')
               + (' x="10" y="100" ')
               + ">" + character + "</text>")
               )

svg_text = ('<?xml version="1.0" encoding="utf-8" ?><svg height="200px" width="200px"><defs />' +
            letter_text +
            "</svg>")


# Write to disk
with open(filepath, "w+") as f:
    f.write(svg_text)


# Object to path (Inkscape) or expand (Illustrator)
# https://stackoverflow.com/questions/15203650/programmatically-convert-svg-shapes-to-paths-lineto-moveto
# You'll need the path to inkscape, e.g.
# /Applications/Inkscape.app/Contents/Resources/bin/inkscape
# or /usr/local/bin/inkscape
inkscape_command = ("/usr/local/bin/inkscape -f '%s'" % filepath +
                    " --verb EditSelectAll " +
                    " --verb ObjectToPath " +
                    " --verb FileSave " +
                    " --verb FileQuit")

#shell = True is important in this call, otherwise it fails
subprocess.check_output(inkscape_command, shell = True) 

The result is this vector graph:

Vector graph of expanded and offset letter

Inkscape also allows writing extensions in Python, so maybe that could be a solution in Python.

Inkscape can offset paths (called "offset path", "outset path", or "dynamic offset object"), but it has issues mentioned in this thread, so I prefer doing it with stroke width.

0
2

I found this beautiful node library: outline-stroke/outline-stroke-cli

It was possible to convert it into a standalone packages, as described here. Download of the linux binary is also there.

How to use?

# Download and install
wget https://cloud.roder.casa/s/m9Njjpe5Y5jkH4X/download -O outline-stroke-cli-1.1.1.tar.xz
tar -Jxvf outline-stroke-cli-1.1.1.tar.xz

Example test.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="160" height="140" viewBox="0 0 160 140" version="1.1">  
    <line x1="40" x2="120" y1="20" y2="20" stroke="black" stroke-width="20" stroke-linecap="butt"/>
    <line x1="40" x2="120" y1="60" y2="60" stroke="black" stroke-width="20" stroke-linecap="square"/>
    <line x1="40" x2="120" y1="100" y2="100" stroke="black" stroke-width="20" stroke-linecap="round"/>
</svg>
# Usage
./outline-stroke-cli-1.1.1/outline-stroke-cli-linux test.svg  # prints output to stdout
./outline-stroke-cli-1.1.1/outline-stroke-cli-linux test.svg -o out.svg  # create output file

Result:

<svg xmlns="http://www.w3.org/2000/svg" width="160" height="140" viewBox="0 0 160 140" version="1.1">
    <path d="M 40 20 L 40 30 80 30 L 120 30 120 20 L 120 10 80 10 L 40 10 40 20 M 30 60 L 30 70 80 70 L 130 70 130 60 L 130 50 80 50 L 30 50 30 60 M 35.110 91.396 C 28.408 95.110, 28.628 105.687, 35.472 108.805 C 39.199 110.503, 120.801 110.503, 124.528 108.805 C 127.394 107.499, 130 103.306, 130 100 C 130 96.694, 127.394 92.501, 124.528 91.195 C 120.561 89.387, 38.400 89.572, 35.110 91.396 " stroke="none" fill="black" fill-rule="evenodd"/>
</svg>
1

For offsetting paths (and some other graphics/geometry opperations), you could use the Shapely library:

https://shapely.readthedocs.io/en/stable/manual.html#constructive-methods

Its' main focus is GIS / Map applications, but it can surely be used for graphics as well.

Note though, that offsets are interpolated using straight line segments instead of Beziers.

__

EDIT: example picture from the Shapely manual:

Offset methods on Shapely: dilation and erosion

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.