Skip to content

Commit

Permalink
Merge pull request #1810 from googlefonts/ds-bg-image-convert-jpeg-to…
Browse files Browse the repository at this point in the history
…-png-issue1809

[background image] [designspace] Convert JPEG to PNG with Pillow as part of putBackgroundImage()
  • Loading branch information
justvanrossum authored Nov 18, 2024
2 parents 7418eca + d7b98df commit 378f0c8
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies = [
"ufomerge>=1.8.0",
"unicodedata2>=15.1.0",
"skia-pathops>=0.8.0.post1",
"pillow>=11.0.0",
]
dynamic = ["version"]
requires-python = ">=3.10"
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ aiohttp==3.11.2
cattrs==24.1.2
fonttools[ufo,unicode]==4.55.0
glyphsLib==6.9.5
pillow==11.0.0
pyyaml==6.0.2
ufomerge==1.8.2
unicodedata2==15.1.0
Expand Down
20 changes: 19 additions & 1 deletion src/fontra/backends/designspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,7 @@ async def getBackgroundImage(self, imageIdentifier: str) -> ImageData | None:

async def putBackgroundImage(self, imageIdentifier: str, data: ImageData) -> None:
if data.type != ImageType.PNG:
raise NotImplementedError("convert image to PNG")
data = convertImageData(data, ImageType.PNG)

imageInfo = self._imageMapping.reverse.get(imageIdentifier)
if imageInfo is None:
Expand Down Expand Up @@ -2240,3 +2240,21 @@ def mergeKernGroups(
mergedGroups[groupName] = gA + [n for n in gB if n not in gASet]

return mergedGroups


def convertImageData(data, type):
import io

from PIL import Image

image = Image.open(io.BytesIO(data.data))
if image.mode == "RGBA" and type == ImageType.JPEG:
# from https://stackoverflow.com/questions/9166400/convert-rgba-png-to-rgb-with-pil
image.load() # required for image.split()
imageJPEG = Image.new("RGB", image.size, (255, 255, 255))
imageJPEG.paste(image, mask=image.split()[3]) # 3 is the alpha channel
image = imageJPEG

outFile = io.BytesIO()
image.save(outFile, type)
return ImageData(type=type, data=outFile.getvalue())
31 changes: 30 additions & 1 deletion test-py/test_backends_designspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,20 @@

from fontra.backends import getFileSystemBackend, newFileSystemBackend
from fontra.backends.copy import copyFont
from fontra.backends.designspace import DesignspaceBackend, UFOBackend
from fontra.backends.designspace import DesignspaceBackend, UFOBackend, convertImageData
from fontra.backends.null import NullBackend
from fontra.core.classes import (
Anchor,
Axes,
BackgroundImage,
CrossAxisMapping,
FontAxis,
FontInfo,
FontSource,
GlyphAxis,
GlyphSource,
Guideline,
ImageType,
Layer,
LineMetric,
OpenTypeFeatures,
Expand Down Expand Up @@ -431,6 +433,33 @@ async def test_putGlyph_with_backgroundImage_new_font(testFont, tmpdir):
assert 0, "expected backgroundImage"


async def test_putBackgroundImage_JPEG(writableTestFont):
glyph = await writableTestFont.getGlyph("C")
for layerName, layer in glyph.layers.items():
bgImage = layer.glyph.backgroundImage
if bgImage is not None:
break

imageDataPNG = await writableTestFont.getBackgroundImage(bgImage.identifier)
imageDataJPEG = convertImageData(imageDataPNG, ImageType.JPEG)
assert imageDataJPEG.type == ImageType.JPEG

glyphName = "D"
imageIdentifier = str(uuid.uuid4())
bgImage = BackgroundImage(identifier=imageIdentifier)

glyphD = await writableTestFont.getGlyph(glyphName)
firstLayerName = list(glyphD.layers.keys())[0]
layer = glyphD.layers[firstLayerName]
layer.glyph.backgroundImage = bgImage

await writableTestFont.putGlyph(glyphName, glyphD, [ord("D")])
await writableTestFont.putBackgroundImage(imageIdentifier, imageDataJPEG)

imageRoundtripped = await writableTestFont.getBackgroundImage(imageIdentifier)
assert imageRoundtripped.type == ImageType.PNG


async def test_putAxes(writableTestFont):
axes = await writableTestFont.getAxes()
axes.axes.append(
Expand Down

0 comments on commit 378f0c8

Please sign in to comment.