Skip to content

Proposal: Smart Margins #766

Open
Open
@typesupply

Description

The left, right, top, bottom margins rely on glyph bounds, which is fine for most work. There are times when it would be nice to be able to specify the y (in the case of left, right) where we want the measurement to be taken instead of the bounding box. I started writing this for a tool, but then realized that it might be useful in fontParts. Here's the idea (for left, right for now, but top, bottom are similar):

  • the y value is defined with a guideline
  • the guideline can be located at the font or glyph level.
  • glyph guidelines override font guidelines.
  • guidelines may be defined for use on both sides, the left or the right by setting the guideline name: margins, margin-left, margin-right`
  • side specific guidelines override both side guidelines.
  • if a side specific guideline is not defined, the base leftMargin or rightMargin value is used.

These would be accessible through properties, just like leftMargin and rightMargin. The names that popped into my head were smartLeftMargin and smartRightMargin but I don't really like the vagueness of smart. It implies magic but there is no magic in this. Maybe something like local*Margin? I don't know...

Here is a quick implementation:

from fontPens.marginPen import MarginPen

def getSmartMargins(glyph):
    font = glyph.font
    both = None
    left = None
    right = None
    # look for glyph level guidelines
    while any((both is None, left is None, right is None)):
        for guideline in glyph.guidelines:
            if guideline.name == "margins":
                both = guideline
            elif guideline.name == "margin-left":
                left = guideline
            elif guideline.name == "margin-right":
                right = guideline
        break
    # use font guidelines if there
    # glyph level guidelines are
    # not defined
    buffer = 10
    if all((font is not None, both is None, left is None, right is None)):
        buffer = font.info.unitsPerEm * 0.01
        while any((both is None, left is None, right is None)):
            for guideline in glyph.guidelines:
                if all((both is None, guideline.name == "margins")):
                    both = guideline
                elif all((left is None, guideline.name == "margin-left")):
                    left = guideline
                elif all((right is None, guideline.name == "margin-right")):
                    left = guideline
            break
    # calculate the left
    leftMargin = 0
    if all((left is None, both is None)):
        leftMargin = glyph.leftMargin
    elif any((glyph.contours, glyph.components)):
        if left is not None:
            y = left.y
        elif both is not None:
            y = both.y
        pen = MarginPen(glyphSet=glyph.layer, value=y)
        glyph.draw(pen)
        leftMargin = pen.getMargins()[0]
    # calculate the right
    rightMargin = 0
    if all((right is None, both is None)):
        rightMargin = glyph.rightMargin
    elif any((glyph.contours, glyph.components)):
        if right is not None:
            y = right.y
        elif both is not None:
            y = both.y
        pen = MarginPen(glyphSet=glyph.layer, value=y)
        glyph.draw(pen)
        rightMargin = glyph.width - pen.getMargins()[1]
    # done
    return (leftMargin, rightMargin)

def setSmartLeftMargin(glyph, value):
    oldValue = getSmartMargins(glyph)[0]
    diff = value - oldValue
    glyph.leftMargin += diff

def setSmartRightMargin(glyph, value):
    oldValue = getSmartMargins(glyph)[1]
    diff = value - oldValue
    glyph.rightMargin += diff

Would this be a good addition to fontParts or is this something better left to an extension or something like that?

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions