Replace base64-encoded images with PGM files
Surprisingly I found Preview.app can still display PGM images. While somewhat legacy, it's a super straightforward format to (de)serialize. The images are scaled to 50x50 px and only consume 5KB in total. Makes more sense to human being than the previous base64-encoded zlib compressed data, plus we don't have to add pillow as a dependency.
This commit is contained in:
parent
072b4c8db0
commit
f87f75a437
BIN
Tests/pens/data/test_even_odd_fill.pgm
Normal file
BIN
Tests/pens/data/test_even_odd_fill.pgm
Normal file
Binary file not shown.
BIN
Tests/pens/data/test_non_zero_fill.pgm
Normal file
BIN
Tests/pens/data/test_non_zero_fill.pgm
Normal file
Binary file not shown.
@ -1,4 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
import os
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from fontTools.pens.freetypePen import FreeTypePen
|
from fontTools.pens.freetypePen import FreeTypePen
|
||||||
@ -8,6 +9,15 @@ except ImportError:
|
|||||||
|
|
||||||
from fontTools.misc.transform import Scale, Offset
|
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):
|
def draw_cubic(pen):
|
||||||
pen.moveTo((50, 0))
|
pen.moveTo((50, 0))
|
||||||
pen.lineTo((50, 500))
|
pen.lineTo((50, 500))
|
||||||
@ -32,6 +42,23 @@ def star(pen):
|
|||||||
pen.lineTo((800, -200))
|
pen.lineTo((800, -200))
|
||||||
pen.closePath()
|
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:
|
# Assume the buffers are equal when PSNR > 38dB. See also:
|
||||||
# Peak signal-to-noise ratio
|
# Peak signal-to-noise ratio
|
||||||
# https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
|
# https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
|
||||||
@ -45,27 +72,12 @@ def psnr(b1, b2):
|
|||||||
@unittest.skipUnless(FREETYPE_PY_AVAILABLE, "freetype-py not installed")
|
@unittest.skipUnless(FREETYPE_PY_AVAILABLE, "freetype-py not installed")
|
||||||
class FreeTypePenTest(unittest.TestCase):
|
class FreeTypePenTest(unittest.TestCase):
|
||||||
def test_draw(self):
|
def test_draw(self):
|
||||||
import base64, zlib
|
|
||||||
ZLIB_B64_BIN = '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'
|
|
||||||
pen = FreeTypePen(None)
|
pen = FreeTypePen(None)
|
||||||
draw_cubic(pen)
|
box(pen)
|
||||||
width, height = 500, 500
|
width, height = 500, 500
|
||||||
buf1, _ = pen.buffer(width=width, height=height)
|
buf1, _ = pen.buffer(width=width, height=height)
|
||||||
buf2 = zlib.decompress(base64.b64decode(ZLIB_B64_BIN))
|
buf2 = b'\xff' * width * height
|
||||||
self.assertEqual(len(buf1), len(buf2))
|
self.assertEqual(buf1, buf2)
|
||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
|
||||||
|
|
||||||
def test_scale(self):
|
|
||||||
import base64, zlib
|
|
||||||
ZLIB_B64_BIN = 'eJy91r8NgkAUx/FLMHECCLMQC5gGK3UB4gJEXABhAbTAJXAJ7GkoBIornrQif/w2vvo+yXH37vFTqi/5rKYs8jhwDDVdMlp15ttM9NVFE2ZSiLShCYVI5VIhekeFSLKmQhIsZLixZaFdKqQyqZAQi9amQiIsOpsK8bHIsKgNKsTBIsAixiLHosCixKLB4vWHXfEv56fLb5B3Ce5E3u1XRQV+tXwy4OnDJxyeopUFhfYUFHsFRaqgSOGfUx+G65cSgPcNZlPGyRoBM0nmjJJMfdv+mpaa5+N+OW5W44vfouHQiw=='
|
|
||||||
pen = FreeTypePen(None)
|
|
||||||
draw_cubic(pen)
|
|
||||||
t = Scale(0.1, 0.1)
|
|
||||||
width, height = t.transformPoint((500, 500))
|
|
||||||
buf1, size = pen.buffer(width=width, height=height, transform=t)
|
|
||||||
buf2 = zlib.decompress(base64.b64decode(ZLIB_B64_BIN))
|
|
||||||
self.assertEqual(size, (50, 50))
|
|
||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
pen = FreeTypePen(None)
|
pen = FreeTypePen(None)
|
||||||
@ -80,29 +92,27 @@ class FreeTypePenTest(unittest.TestCase):
|
|||||||
self.assertEqual(pen.cbox, (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):
|
def test_non_zero_fill(self):
|
||||||
import base64, zlib
|
|
||||||
ZLIB_B64_BIN = 'eJzt2L0NglAUhmESTZxA4yzGAqfRSl3AuABRF/BnAbTAJXAJ7GkoBAqKo4WNhbk3OZ4vknzvAk/g/nC5QcAYY4wxxhhrRfJZmaXJfjXqWBrving6tDZe1dufKV8NkSrqmxsieWhvSDO3N0SOPXtDjgBD9K/LbTShvSG5dgp7GBIBjEq54n0M2QKMWvcgXoZMAUYMMArVR8vPkBHAWAGMPcBIAEYKMDKAUQKMB8BAvCvEmCPmLmINIvYSwJ6I2NvPGuJ/vrWIMwPg7IM4wwHOovnA3GgmSsLDWGgJt3FSE07jZP7P2Sz1gusOQD3cLqPaaCety6h3xncyxWVmd7dU3m/Xw3rc/R3AGGOMsbb3BMrP0Is='
|
|
||||||
pen = FreeTypePen(None)
|
pen = FreeTypePen(None)
|
||||||
draw_cubic(pen)
|
star(pen)
|
||||||
t = Scale(0.1, 0.1)
|
t = Scale(0.05, 0.05)
|
||||||
width, height = t.transformPoint((1000, 1000))
|
width, height = t.transformPoint((1000, 1000))
|
||||||
t = t.translate(0, 200)
|
t = t.translate(0, 200)
|
||||||
buf1, size = pen.buffer(width=width, height=height, transform=t, evenOdd=False)
|
buf1, size1 = pen.buffer(width=width, height=height, transform=t, evenOdd=False)
|
||||||
buf2 = zlib.decompress(base64.b64decode(ZLIB_B64_BIN))
|
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_non_zero_fill.pgm'))
|
||||||
self.assertEqual(len(buf1), len(buf2))
|
self.assertEqual(len(buf1), len(buf2))
|
||||||
|
self.assertEqual(size1, size2)
|
||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
||||||
|
|
||||||
def test_even_odd_fill(self):
|
def test_even_odd_fill(self):
|
||||||
import base64, zlib
|
|
||||||
ZLIB_B64_BIN = 'eJzt2L0NglAUhmESTZxA4yzGAqfRSl3AuABRF/BnAbTAJXAJ7GkoBAqKo4WNhbk3OZ4vknzvAk/g/nC5QcAYY4wxxhhrRfJZmaXJfjXqWBrving6tDZe1dufKV8NkSrqmxsieWhvSDO3N0SOPXtDjgBD9K/LbTShvSG5dgp7GBIBjEq54n0M2QKMWvcgXoZMAUYMMArVR8vPkBHAWAGMPcBIAEYKMDKAUQKMB8BAvCvEmCPmLmINIvYSwJ6I2NvPGuJ/vrWIMwPg7IM4wwHOovnA3GgmSsLDWGgJt3FSE07jZP7P2Sz1gusOQD3cLqPaaCety6h3xncyxWVmd7dU3m/Xw3rc/R3AGGOMsbb3BMrP0Is='
|
|
||||||
pen = FreeTypePen(None)
|
pen = FreeTypePen(None)
|
||||||
draw_cubic(pen)
|
star(pen)
|
||||||
t = Scale(0.1, 0.1)
|
t = Scale(0.05, 0.05)
|
||||||
width, height = t.transformPoint((1000, 1000))
|
width, height = t.transformPoint((1000, 1000))
|
||||||
t = t.translate(0, 200)
|
t = t.translate(0, 200)
|
||||||
buf1, size = pen.buffer(width=width, height=height, transform=t, evenOdd=True)
|
buf1, size1 = pen.buffer(width=width, height=height, transform=t, evenOdd=True)
|
||||||
buf2 = zlib.decompress(base64.b64decode(ZLIB_B64_BIN))
|
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_even_odd_fill.pgm'))
|
||||||
self.assertEqual(len(buf1), len(buf2))
|
self.assertEqual(len(buf1), len(buf2))
|
||||||
|
self.assertEqual(size1, size2)
|
||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
||||||
|
|
||||||
def test_cubic_vs_quadratic(self):
|
def test_cubic_vs_quadratic(self):
|
||||||
@ -116,15 +126,14 @@ class FreeTypePenTest(unittest.TestCase):
|
|||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
||||||
|
|
||||||
def test_contain(self):
|
def test_contain(self):
|
||||||
import base64, zlib
|
|
||||||
ZLIB_B64_BIN = 'eJyVlKtvAkEQh5dHCEG0vQTbJgjOIVqB7VW0BoUsCQqHLElP4VCkV8sfQBpcqzCYYjEIDClNEFiaSxCEEB7bO/aWO2Bn2fmp22G+3ONjhhBxdB34AUzlBUt0v5GAtlpd4YgCpc84okXpBwqI2pTaUQxhUCf3GMJyiTcMMXKJHwSg010Q2iuMQGjvMkJdu7ZihLr2AvWirL3FCVXtrnAWVe0G3UdRu+UTIu2lBUVlUSJ3YwwwvnXuorXVgba2e7BQdaPWv6mG+Ms8/akA08fA+9/0zgO964NPFmucAxqx489cnMv650WBmcwvDIwyQteXXxDweQH9P8y1qH9tQv1OHkSEIQEIeT8FLCnAJzwY+bTzCQ9GPu2FU+DMtLdEhGza/QkPRjbthgiQTntgwodD/1qy5Ef7pnokUt8f4CWv85ZZ3j3mZ/wMLnlvpdNBmp3TA68ALnlPeDPBC4kmq0DamfBlOVgrL90apH0nfJI9LGYnEu2u8E7yuJrsgNod4dta+LQerm0B7Qa1c+Kb52yxdqufEgOEpPpC7WYcAgiJv/rX/4vPJ4U='
|
|
||||||
pen = FreeTypePen(None)
|
pen = FreeTypePen(None)
|
||||||
star(pen)
|
star(pen)
|
||||||
t = Scale(0.05, 0.05)
|
t = Scale(0.05, 0.05)
|
||||||
width, height = 0, 0
|
width, height = 0, 0
|
||||||
buf1, size = pen.buffer(width=width, height=height, transform=t, contain=True)
|
buf1, size1 = pen.buffer(width=width, height=height, transform=t, contain=True)
|
||||||
buf2 = zlib.decompress(base64.b64decode(ZLIB_B64_BIN))
|
buf2, size2 = load_pgm(os.path.join(DATA_DIR, 'test_non_zero_fill.pgm'))
|
||||||
self.assertEqual(len(buf1), len(buf2))
|
self.assertEqual(len(buf1), len(buf2))
|
||||||
|
self.assertEqual(size1, size2)
|
||||||
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
self.assertGreater(psnr(buf1, buf2), PSNR_THRESHOLD)
|
||||||
|
|
||||||
def test_none_width(self):
|
def test_none_width(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user