Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Smart Margins #766

Open
typesupply opened this issue Nov 7, 2024 · 5 comments
Open

Proposal: Smart Margins #766

typesupply opened this issue Nov 7, 2024 · 5 comments

Comments

@typesupply
Copy link
Member

typesupply commented Nov 7, 2024

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?

@typesupply
Copy link
Member Author

Any thoughts @benkiel, @LettError , @typemytype?

@LettError
Copy link
Member

I really like the idea of being able to approach margin values at a specific height. But that means there needs to be a way to record those heights somewhere in the glyph. Are guides the best medium? These can be angled - is that useful, or complicated?

Also how would this relate to angledMargins? In an italic, with italic margins set?

Would a y value, stored in glyph.lib suffice?

(I don't like glyph.lib solutions, there is an impermanence to them)

@justvanrossum
Copy link
Contributor

It could also be a named anchor, and you'd ignore the x value.

@typesupply
Copy link
Member Author

Anchor or guidelines would work. My first thought was anchor but I switched to guidelines because they are already visualized in editors with a line. Extensions could add visualization to anchors (something like a line from the anchor to the intersection in the outline). If we use guides, I'd document that angles should be ignored.

I tried to think about angled margins but my brain melted. I'm sure we can figure it out though.

@typoman
Copy link

typoman commented Nov 28, 2024

FWIW, I was thinking how having same functionality for spacing Arabic glyphs a while ago. When I set the margin for many glyphs, a lot of time I want the reference for the margin to be taken from the same height for most glyphs and not the margin that is based on the glyph bounds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants