8000 Add FreeType-based Pen for rasterisation by takaakifuji · Pull Request #2494 · fonttools/fonttools · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add FreeType-based Pen for rasterisation #2494

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

Merged
merged 25 commits into from
Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e2fad4d
Add FreeType-based Pen for rasterisation
takaakifuji Jan 6, 2022
55dca28
Remove execute permission
takaakifuji Jan 6, 2022
46c580f
Fix import in unit test
takaakifuji Jan 6, 2022
10a3ca8
Use freetype-py instead
takaakifuji Jan 6, 2022
288d907
Stop trying to cache modules pointlessly
takaakifuji Jan 6, 2022
f7c29e8
Change arguments for positioning and dimension
takaakifuji Jan 7, 2022
e4c1deb
Add documentation
takaakifuji Jan 7, 2022
dd72f7c
Fix missing call to superclass __init__
takaakifuji Jan 10, 2022
bf45aec
Fix typo
takaakifuji Jan 10, 2022
e3694b6
Fix another typo in docstring
takaakifuji Jan 10, 2022
9a9d0f0
Remove save() method
takaakifuji Jan 10, 2022
d0dbbbd
Rename to freetypePen/FreeTypePen
takaakifuji Jan 10, 2022
0e88026
Remove unnecessary import in test
takaakifuji Jan 10, 2022
2c0ab2a
Relax assertions for rendering tests
takaakifuji Jan 10, 2022
d399e93
Rename even_odd to evenOdd
takaakifuji Jan 10, 2022
3f26a13
Add freetype-py to requirements.txt
takaakifuji Jan 10, 2022
184a615
Opt out of running doctest
takaakifuji Jan 10, 2022
42bc125
Add reference for PSNR
takaakifuji Jan 10, 2022
6f7ef4a
Prefer single transform matrix over offset/scale
takaakifuji Jan 11, 2022
e3bbf39
Move evenOdd to end in kwargs
takaakifuji Jan 11, 2022
072b4c8
Fit to contents when image size omitted
takaakifuji Jan 11, 2022
f87f75a
Replace base64-encoded images with PGM files
takaakifuji Jan 11, 2022
d181eca
Fix warnings when generating docs
takaakifuji Jan 11, 2022
6c5faa6
Add description to requirements section
takaakifuji Jan 12, 2022
c0c354e
Add name to acknowledgements
takaakifuji Jan 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/source/pens/freetypePen.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
###########
freetypePen
###########

.. automodule:: fontTools.pens.freetypePen
:inherited-members:
:members:
:undoc-members:
1 change: 1 addition & 0 deletions Doc/source/pens/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pens
cocoaPen
cu2quPen
filterPen
freetypePen
momentsPen
perimeterPen
pointInsidePen
Expand Down
354 changes: 354 additions & 0 deletions Lib/fontTools/pens/freetypePen.py

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ are required to unlock the extra features named "ufo", etc.
* `reportlab <https://pypi.python.org/pypi/reportlab>`__: Python toolkit
for generating PDFs and graphics.

- ``Lib/fontTools/pens/freetypePen.py``

Pen to drawing glyphs with FreeType as raster images, requires:

* `freetype-py <https://pypi.python.org/pypi/freetype-py>`__: Python binding
for the FreeType library.

How to make a new release
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -238,9 +245,9 @@ aschmitz, Olivier Berten, Samyak Bhuta, Erik van Blokland, Petr van Blokland,
Jelle Bosma, Sascha Brawer, Tom Byrer, Antonio Cavedoni, Frédéric
Coiffier, Vincent Connare, David Corbett, Simon Cozens, Dave Crossland,
Simon Daniels, Peter Dekkers, Behdad Esfahbod, Behnam Esfahbod, Hannes
Famira, Sam Fishman, Matt Fontaine, Yannis Haralambous, Greg Hitchcock,
Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye, Jack
Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal
Famira, Sam Fishman, Matt Fontaine, Takaaki Fuji, Yannis Haralambous, Greg
Hitchcock, Jeremie Hornus, Khaled Hosny, John Hudson, Denis Moyogo Jacquerye,
Ja 8000 ck Jansen, Tom Kacvinsky, Jens Kutilek, Antoine Leca, Werner Lemberg, Tal
Leming, Peter Lofting, Cosimo Lupo, Masaya Nakamura, Dave Opstad,
Laurence Penney, Roozbeh Pournader, Garret Rieger, Read Roberts, Guido
van Rossum, Just van Rossum, Andreas Seidel, Georg Seifert, Chris
Expand Down
Binary file added Tests/pens/data/test_even_odd_fill.pgm
Binary file not shown.
Binary file added Tests/pens/data/test_non_zero_fill.pgm
Binary file not shown.
159 changes: 159 additions & 0 deletions Tests/pens/freetypePen_test.py
9E81
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import unittest
import os

try:
from fontTools.pens.freetypePen import FreeTypePen
FREETYPE_PY_AVAILABLE = True
except ImportError:
FREETYPE_PY_AVAILABLE = False

from fontTools.misc.transform import Scale, Offset

DATA_DIR = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data')

def box(pen):
pen.moveTo((0, 0))
pen.lineTo((0, 500))
pen.lineTo((500, 500))
pen.lineTo((500, 0))
pen.closePath()

def draw_cubic(pen):
pen.moveTo((50, 0))
pen.lineTo((50, 500))
pen.lineTo((200, 500))
pen.curveTo((350, 500), (450, 400), (450, 250))
pen.curveTo((450, 100), (350, 0), (200, 0))
pen.closePath()

def draw_quadratic(pen):
pen.moveTo((50, 0))
pen.lineTo((50, 500))
pen.lineTo((200, 500))
pen.qCurveTo((274, 500), (388, 438), (450, 324), (450, 250))
pen.qCurveTo((450, 176), (388, 62), (274, 0), (200, 0))
pen.closePath()

def star(pen):
pen.moveTo((0, 420))
pen.lineTo((1000, 420))
pen.lineTo((200, -200))
pen.lineTo((500, 800))
pen.lineTo((800, -200))
pen.closePath()

# For the PGM format, see the following resources:
# https://en.wikipedia.org/wiki/Netpbm
# http://netpbm.sourceforge.net/doc/pgm.html
def load_pgm(filename):
with open(filename, 'rb') as fp:
assert fp.readline() == 'P5\n'.encode()
w, h = (int(c) for c in fp.readline().decode().rstrip().split(' '))
assert fp.readline() == '255\n'.encode()
return fp.read(), (w, h)

def save_pgm(filename, buf, size):
with open(filename, 'wb') as fp:
fp.write('P5\n'.encode())
fp.write('{0:d} {1:d}\n'.format(*size).encode())
fp.write('255\n'.encode())
fp.write(buf)

# Assume the buffers are equal when PSNR > 38dB. See also:
# Peak signal-to-noise ratio
# https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
PSNR_THRESHOLD = 38.0

def psnr(b1, b2):
import math
mse = sum((c1-c2) * (c1-c2) for c1, c2 in zip(b1, b2)) / float(len(b1))
return 10.0 * math.log10((255.0 ** 2) / float(mse)) if mse > 0 else math.inf

@unittest.skipUnless(FREETYPE_PY_AVAILABLE, "freetype-py not installed")
class FreeTypePenTest(unittest.TestCase):
def test_draw(self):
pen = FreeTypePen(None)
box(pen)
width, height = 500, 500
buf1, _ = pen.buffer(width=width, height=height)
buf2 = b'\xff' * width * height
self.assertEqual(buf1, buf2)

def test_empty(self):
pen = FreeTypePen(None)
width, height = 500, 500
buf, size = pen.buffer(width=width, height=height)
self.assertEqual(b'\0' * size[0] * size[1], buf)

def test_bbox_and_cbox(self):
pen = FreeTypePen(None)
draw_cubic(pen)
self.assertEqual(pen.bbox, (50.0, 0.0, 450.0, 500.0))
self.assertEqual(pen.cbox, (50.0, 0.0, 450.0, 500.0))

def test_non_zero_fill(self):
pen = FreeTypePen(None)
star(pen)
t = Scale(0.05, 0.05)
width, height = t.transformPoint((1000, 1000))
t = t.translate(0, 200)
buf1, size1 = pen.buffer(width=width, height=height, transform=t, evenOdd=False)
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_non_zero_fill.pgm'))
self.assertEqual(len(buf1), len(buf2))
self.assertEqual(size1, size2)
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)

def test_even_odd_fill(self):
pen = FreeTypePen(None)
star(pen)
t = Scale(0.05, 0.05)
width, height = t.transformPoint((1000, 1000))
t = t.translate(0, 200)
buf1, size1 = pen.buffer(width=width, height=height, transform=t, evenOdd=True)
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_even_odd_fill.pgm'))
self.assertEqual(len(buf1), len(buf2))
self.assertEqual(size1, size2)
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)

def test_cubic_vs_quadratic(self):
pen1, pen2 = FreeTypePen(None), FreeTypePen(None)
draw_cubic(pen1)
draw_quadratic(pen2)
width, height = 500, 500
buf1, _ = pen1.buffer(width=width, height=height)
buf2, _ = pen2.buffer(width=width, height=height)
self.assertEqual(len(buf1), len(buf2))
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)

def test_contain(self):
pen = FreeTypePen(None)
star(pen)
t = Scale(0.05, 0.05)
width, height = 0, 0
buf1, size1 = pen.buffer(width=width, height=height, transform=t, contain=True)
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_non_zero_fill.pgm'))
self.assertEqual(len(buf1), len(buf2))
self.assertEqual(size1, size2)
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)

def test_none_width(self):
pen = FreeTypePen(None)
star(pen)
width, height = None, 1000
buf1, size = pen.buffer(width=width, height=height, transform=Offset(0, 200))
buf2, _ = pen.buffer(width=1000, height=height, transform=Offset(0, 200))
self.assertEqual(size, (1000, 1000))
self.assertEqual(buf1, buf2)

def test_none_height(self):
pen = FreeTypePen(None)
star(pen)
width, height = 1000, None
buf1, size = pen.buffer(width=width, height=height)
buf2, _ = pen.buffer(width=width, height=1000, transform=Offset(0, 200))
self.assertEqual(size, (1000, 1000))
self.assertEqual(buf1, buf2)

if __name__ == '__main__':
import sys
sys.exit(unittest.main())
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ skia-pathops==0.7.2; platform_python_implementation != "PyPy"
# this is only required to run Tests/cu2qu/{ufo,cli}_test.py
ufoLib2==0.13.0
pyobjc==8.1; sys_platform == "darwin"
freetype-py==2.2.0
0