| 1 |
from robofab.world import RFont |
|---|
| 2 |
from fontTools.pens.basePen import BasePen |
|---|
| 3 |
from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect |
|---|
| 4 |
from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds |
|---|
| 5 |
from robofab.pens.filterPen import _estimateCubicCurveLength, _getCubicPoint |
|---|
| 6 |
import math |
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
__all__ = ["AngledMarginPen", "getAngledMargins", |
|---|
| 11 |
"setAngledLeftMargin", "setAngledRightMargin", |
|---|
| 12 |
"centerAngledMargins"] |
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
class AngledMarginPen(BasePen): |
|---|
| 17 |
""" |
|---|
| 18 |
Angled Margin Pen |
|---|
| 19 |
|
|---|
| 20 |
Pen to calculate the margins as if the margin lines were slanted |
|---|
| 21 |
according to the font.info.italicAngle. |
|---|
| 22 |
|
|---|
| 23 |
Notes: |
|---|
| 24 |
- this pen works on the on-curve points, and approximates the distance to curves. |
|---|
| 25 |
- results will be float. |
|---|
| 26 |
- when used in FontLab, the resulting margins may be slightly |
|---|
| 27 |
different from the values originally set, due to rounding errors. |
|---|
| 28 |
- similar to what RoboFog used to do. |
|---|
| 29 |
- RoboFog had a special attribute for "italicoffset", horizontal |
|---|
| 30 |
shift of all glyphs. This is missing in Robofab. |
|---|
| 31 |
""" |
|---|
| 32 |
def __init__(self, glyphSet, width, italicAngle): |
|---|
| 33 |
BasePen.__init__(self, glyphSet) |
|---|
| 34 |
self.width = width |
|---|
| 35 |
self._angle = math.radians(90+italicAngle) |
|---|
| 36 |
self.maxSteps = 100 |
|---|
| 37 |
self.margin = None |
|---|
| 38 |
self._left = None |
|---|
| 39 |
self._right = None |
|---|
| 40 |
self._start = None |
|---|
| 41 |
self.currentPt = None |
|---|
| 42 |
|
|---|
| 43 |
def _getAngled(self, pt): |
|---|
| 44 |
r = (g.width + (pt[1] / math.tan(self._angle)))-pt[0] |
|---|
| 45 |
l = pt[0]-((pt[1] / math.tan(self._angle))) |
|---|
| 46 |
if self._right is None: |
|---|
| 47 |
self._right = r |
|---|
| 48 |
else: |
|---|
| 49 |
self._right = min(self._right, r) |
|---|
| 50 |
if self._left is None: |
|---|
| 51 |
self._left = l |
|---|
| 52 |
else: |
|---|
| 53 |
self._left = min(self._left, l) |
|---|
| 54 |
|
|---|
| 55 |
self.margin = self._left, self._right |
|---|
| 56 |
|
|---|
| 57 |
def _moveTo(self, pt): |
|---|
| 58 |
self._start = self.currentPt = pt |
|---|
| 59 |
|
|---|
| 60 |
def _addMoveTo(self): |
|---|
| 61 |
if self._start is None: |
|---|
| 62 |
return |
|---|
| 63 |
self._start = self.currentPt = None |
|---|
| 64 |
|
|---|
| 65 |
def _lineTo(self, pt): |
|---|
| 66 |
self._addMoveTo() |
|---|
| 67 |
self._getAngled(pt) |
|---|
| 68 |
|
|---|
| 69 |
def _curveToOne(self, pt1, pt2, pt3): |
|---|
| 70 |
step = 1.0/self.maxSteps |
|---|
| 71 |
factors = range(0, self.maxSteps+1) |
|---|
| 72 |
for i in factors: |
|---|
| 73 |
pt = _getCubicPoint(i*step, self.currentPt, pt1, pt2, pt3) |
|---|
| 74 |
self._getAngled(pt) |
|---|
| 75 |
self.currentPt = pt3 |
|---|
| 76 |
|
|---|
| 77 |
def _qCurveToOne(self, bcp, pt): |
|---|
| 78 |
self._addMoveTo() |
|---|
| 79 |
|
|---|
| 80 |
self._getAngled(pt) |
|---|
| 81 |
self.currentPt = pt3 |
|---|
| 82 |
|
|---|
| 83 |
def getAngledMargins(glyph, font): |
|---|
| 84 |
"""Get the angled margins for this glyph.""" |
|---|
| 85 |
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) |
|---|
| 86 |
glyph.draw(pen) |
|---|
| 87 |
return pen.margin |
|---|
| 88 |
|
|---|
| 89 |
def setAngledLeftMargin(glyph, font, value): |
|---|
| 90 |
"""Set the left angled margin to value, adjusted for font.info.italicAngle.""" |
|---|
| 91 |
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) |
|---|
| 92 |
g.draw(pen) |
|---|
| 93 |
isLeft, isRight = pen.margin |
|---|
| 94 |
glyph.leftMargin += value-isLeft |
|---|
| 95 |
|
|---|
| 96 |
def setAngledRightMargin(glyph, font, value): |
|---|
| 97 |
"""Set the right angled margin to value, adjusted for font.info.italicAngle.""" |
|---|
| 98 |
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) |
|---|
| 99 |
g.draw(pen) |
|---|
| 100 |
isLeft, isRight = pen.margin |
|---|
| 101 |
glyph.rightMargin += value-isRight |
|---|
| 102 |
|
|---|
| 103 |
def centerAngledMargins(glyph, font): |
|---|
| 104 |
"""Center the glyph on angled margins.""" |
|---|
| 105 |
pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) |
|---|
| 106 |
g.draw(pen) |
|---|
| 107 |
isLeft, isRight = pen.margin |
|---|
| 108 |
setAngledLeftMargin(glyph, font, (isLeft+isRight)*.5) |
|---|
| 109 |
setAngledRightMargin(glyph, font, (isLeft+isRight)*.5) |
|---|
| 110 |
|
|---|
| 111 |
def guessItalicOffset(glyph, font): |
|---|
| 112 |
"""Guess the italic offset based on the margins of a symetric glyph. |
|---|
| 113 |
For instance H or I. |
|---|
| 114 |
""" |
|---|
| 115 |
l, r = getAngledMargins(glyph, font) |
|---|
| 116 |
return l - (l+r)*.5 |
|---|
| 117 |
|
|---|
| 118 |
|
|---|
| 119 |
if __name__ == "__main__": |
|---|
| 120 |
|
|---|
| 121 |
|
|---|
| 122 |
from robofab.world import CurrentFont, CurrentGlyph |
|---|
| 123 |
g = CurrentGlyph() |
|---|
| 124 |
f = CurrentFont() |
|---|
| 125 |
|
|---|
| 126 |
print "margins!", getAngledMargins(g, f) |
|---|
| 127 |
|
|---|
| 128 |
m = 50 |
|---|
| 129 |
setAngledLeftMargin(g, f, m) |
|---|
| 130 |
setAngledRightMargin(g, f, m) |
|---|
| 131 |
g.update() |
|---|
| 132 |
|
|---|