Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 55 additions & 4 deletions clipart/CLP file format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,58 @@ Decode the hexadecimal string to is binary representation.

Using custom clip-art files
---------------------------
You can use your own .clp files in the CEWE software, but it is not known, if this will work if you really order a photo book with a custom clip-art.
You have been warned, so first copy your .svg or .clp file to the folder Resources\photofun\categories
Both format will work.
Then edit 'cliparts_default.xml' and add <decoration> section while using one of the existing sections as tempalte for your clip-art file. Use the .svg extension even if your real file has as .clp extension. Be sure to change the designElementId="1234" to a number that is not used anywhere else in the software. (Re-)Start the CEWE software.
You can use your own .clp files in the CEWE software, but it is not known if
this will work if you really order a photo book with custom clip-art. You have
been warned.

Older CEWE versions used a catalogue file called 'cliparts_default.xml'. That
file is no longer present in current installations: CEWE scans the resource
folders instead. Downloaded content is normally installed below:

%LOCALAPPDATA%\CEWE\hps\5026\photofun\decorations

Use a unique numeric designElementId, place an XML descriptor and the .clp
files in their own folder below that location, and restart CEWE. The editor
can search for the numeric ID directly. XML descriptors name files with an
'.svg' extension even when the installed files are .clp.

Creating custom passepartouts (masks and frames)
------------------------------------------------
A passepartout is a 'fading' decoration with:

* an XML descriptor;
* a mask .clp, which defines the visible shape of the photograph;
* a frame/clip .clp, which is painted above the photograph; and
* optionally, a preview .clp for CEWE's decoration picker.

For example, the descriptor has this shape:

<decoration type="fading" designElementId="990001" id="example-frame">
<categories><category>category-travel</category></categories>
<fading designElementType="passepartout" file="example-mask.svg"
keepAspectRatio="1" ratio="0.8056">
<clipart file="example-clip.svg" designElementType="clipart" ratio="0.8056"/>
<fotoarea height="0.57" width="0.83" x="0.085" y="0.285"/>
</fading>
</decoration>

There is an important geometry convention. The frame/clip SVG canvas covers
the complete passepartout and must have the aspect ratio declared by 'ratio'.
The mask SVG canvas covers only the photograph aperture and must have the
aspect ratio implied by 'fotoarea', not the full frame. If these canvases are
made square by default, CEWE may display the frame but cewe2pdf will not
interpret the mask and frame consistently.

The helper in this repository writes the simple CLP encoding:

ClpFile.convertSVGtoCLP('example-mask.svg')

Keep the editable SVG source outside the installed resource folder, then place
the resulting .clp files beside the XML descriptor. Validate the resource in
the CEWE editor first. Once accepted, copy the exact resource tree into a
self-contained test fixture, for example:

tests/hps/5026/photofun/decorations/<designElementId>/<resource-name>/

Test INI files can point 'hpsFolder' at 'tests/hps', ensuring that cewe2pdf
uses the fixture rather than a developer's installed CEWE content.
10 changes: 10 additions & 0 deletions clipart/clpfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ def convertToPngInBuffer(self, width:int = None, height:int = None, alpha:int =
if (pixels[i, j] != 0):
pixels[i, j] = alpha
scaledImage.putalpha(alphamask)
else:
# SVGs with transparent pixels arrive as RGBA. Preserve their
# existing antialiased alpha, while applying the decoration's
# opacity to the whole clipart. Previously the alpha argument
# was ignored for this common case, leaving passepartout frames
# opaque when their photographs were transparent.
scaledImage = scaledImage.convert('RGBA')
scaledImage.putalpha(
scaledImage.getchannel('A').point(
lambda value: int(value * alpha / 255)))

if flipX:
scaledImage = scaledImage.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
Expand Down
29 changes: 18 additions & 11 deletions imageareas.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@
from renderContext import RenderContext


def _getDecorationOpacity(area):
"""Return an image area's CEWE decoration opacity as a value from 0 to 1."""
decoration = area.find('decoration')
if decoration is None or decoration.get('alpha') is None:
return 1

alphaText = decoration.get('alpha')
try:
return max(0, min(1, float(alphaText)))
except ValueError:
logging.warning('Ignoring invalid image decoration alpha %r', alphaText)
return 1


def _applyDecorationAlpha(image, area):
"""Apply a CEWE image area's optional decoration opacity to ``image``.

Expand All @@ -27,16 +41,7 @@ def _applyDecorationAlpha(image, area):
source image, a passepartout mask, or a corner mask by scaling rather
than replacing its alpha channel.
"""
decoration = area.find('decoration')
if decoration is None or decoration.get('alpha') is None:
return image

alphaText = decoration.get('alpha')
try:
opacity = max(0, min(1, float(alphaText)))
except ValueError:
logging.warning('Ignoring invalid image decoration alpha %r', alphaText)
return image
opacity = _getDecorationOpacity(area)

if opacity == 1:
return image
Expand Down Expand Up @@ -121,7 +126,9 @@ def processAreaImageTag(imageTag, area, areaHeight, areaRot, areaWidth, imageDir
maskClipartFileName = None
frameDeltaX_mcfunit = 0
frameDeltaY_mcfunit = 0
frameAlpha = 255
# A passepartout has a separately drawn frame as well as the masked photo.
# CEWE applies the image area's decoration opacity to both components.
frameAlpha = int(_getDecorationOpacity(area) * 255)
imageCropWidth_mcfunit = areaWidth
imageCropHeight_mcfunit = areaHeight
if passepartoutId is not None:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667222077696474683d223231342e323522206865696768743d223137322e3539333735222076696577426f783d22302030203231342e3235203137322e3539333735223e0a20203c672066696c6c3d226e6f6e6522207374726f6b652d6c696e656a6f696e3d22726f756e6422207374726f6b652d6c696e656361703d22726f756e64223e0a202020203c7061746820643d224d38332e353537352034342e3837343337355633322e373932383132354338332e353537352032312e3537343231383735203133302e363932352032312e3537343231383735203133302e363932352033322e373932383132355634342e38373433373522207374726f6b653d222331373642383722207374726f6b652d77696474683d22372e32333639333735222f3e0a202020203c7265637420783d2231322e3835352220793d2234342e383734333735222077696474683d223138382e353422206865696768743d223130372e303038313235222072783d2231392e3238323522207374726f6b653d222331373642383722207374726f6b652d77696474683d22372e32333639333735222f3e0a202020203c7061746820643d224d34362e30363337352034392e3138383834333735563134372e35373736353632354d3136382e31383632352034392e3138383834333735563134372e353737363536323522207374726f6b653d222345374138334122207374726f6b652d77696474683d22372e39333933313235222f3e0a202020203c7061746820643d224d3136352e3037372034362e39343239363837354c3138322e313132352034332e313438343337354c3138382e313131352036362e39363535333132354c3137312e342037302e373630303632355a222066696c6c3d222346303646383222207374726f6b653d222341393343353622207374726f6b652d77696474683d22322e30353535363235222f3e0a202020203c7061746820643d224d3136372e3131352034362e36303033313235433136382e31383632352033392e36393635363235203137372e383237352033392e333531333735203138302e333938352034342e3532393138373522207374726f6b653d222346464637443622207374726f6b652d77696474683d22322e3538383930363235222f3e0a20203c2f673e0a3c2f7376673e0a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667222077696474683d223137372e3832373522206865696768743d2239382e33373834333735222076696577426f783d22302030203137372e383237352039382e33373834333735223e0a20203c7265637420783d22302220793d2230222077696474683d223137372e3832373522206865696768743d2239382e33373834333735222072783d2231372e3738323735222066696c6c3d2223303030303030222f3e0a3c2f7376673e0a
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d38223f3e0a3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f737667222077696474683d223231342e323522206865696768743d223137322e3539333735222076696577426f783d22302030203231342e3235203137322e3539333735223e0a20203c7265637420783d2231382e32313132352220793d2234392e3138383834333735222077696474683d223137372e3832373522206865696768743d2239382e33373834333735222072783d2231302e323635363235222066696c6c3d2223443745454632222f3e0a20203c672066696c6c3d226e6f6e6522207374726f6b652d6c696e656a6f696e3d22726f756e6422207374726f6b652d6c696e656361703d22726f756e64223e0a202020203c7061746820643d224d38332e353537352034342e3837343337355633322e373932383132354338332e353537352032312e3537343231383735203133302e363932352032312e3537343231383735203133302e363932352033322e373932383132355634342e38373433373522207374726f6b653d222331373642383722207374726f6b652d77696474683d22372e32333639333735222f3e0a202020203c7265637420783d2231322e3835352220793d2234342e383734333735222077696474683d223138382e353422206865696768743d223130372e303038313235222072783d2231392e3238323522207374726f6b653d222331373642383722207374726f6b652d77696474683d22372e32333639333735222f3e0a202020203c7061746820643d224d34362e30363337352034392e3138383834333735563134372e35373736353632354d3136382e31383632352034392e3138383834333735563134372e353737363536323522207374726f6b653d222345374138334122207374726f6b652d77696474683d22372e39333933313235222f3e0a202020203c7061746820643d224d3136352e3037372034362e39343239363837354c3138322e313132352034332e313438343337354c3138382e313131352036362e39363535333132354c3137312e342037302e373630303632355a222066696c6c3d222346303646383222207374726f6b653d222341393343353622207374726f6b652d77696474683d22322e30353535363235222f3e0a202020203c7061746820643d224d3136372e3131352034362e36303033313235433136382e31383632352033392e36393635363235203137372e383237352033392e333531333735203138302e333938352034342e3532393138373522207374726f6b653d222346464637443622207374726f6b652d77696474683d22322e3538383930363235222f3e0a20203c2f673e0a3c2f7376673e0a
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<decorations>
<decoration type="fading" designElementId="990001" id="codex-travel-frame">
<categories>
<category>category-travel</category>
</categories>
<fading designElementType="passepartout" file="codex-travel-frame-mask.svg" keepAspectRatio="1" ratio="0.8056">
<clipart file="codex-travel-frame-clip.svg" designElementType="clipart" ratio="0.8056"/>
<fotoarea height="0.57" width="0.83" x="0.085" y="0.285"/>
</fading>
</decoration>
</decorations>
17 changes: 16 additions & 1 deletion tests/testClipart/test_drawClipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,28 @@
configureTestImportPaths(__file__)
from datetime import datetime
from pikepdf import Pdf, PdfImage
from PIL import Image

from compare_pdf import ComparePDF, ShowDiffsStyle # type: ignore
from cewe2pdf import convertMcf # type: ignore
from clipart.clpfile import ClpFile

from testutils import getLatestResultFile


def test_rgbaClipartOpacityPreservesExistingAlpha(monkeypatch):
"""Decoration opacity scales an SVG's existing antialiased alpha."""
clipart = ClpFile()
monkeypatch.setattr(
clipart, 'rasterSvgData',
lambda width, height: Image.new('RGBA', (1, 1), (10, 20, 30, 200)))

clipart.convertToPngInBuffer(1, 1, alpha=76)
renderedImage = Image.open(clipart.pngMemFile)

assert renderedImage.getpixel((0, 0)) == (10, 20, 30, 59)


def tryToBuildBook(inFile, outFile, latestResultFile, keepDoublePages):
if os.path.exists(outFile) == True:
os.remove(outFile)
Expand Down Expand Up @@ -90,4 +105,4 @@ def test_testDrawClipart(main=False):

if __name__ == '__main__':
#only executed when this file is run directly.
test_testDrawClipart(main=True)
test_testDrawClipart(main=True)
Binary file not shown.
Loading
Loading