105105from otf import getTtfsFromOtfs
106106from enum import Enum
107107
108+ class PageType (Enum ):
109+ Unknown = 0 # this must be an error
110+ Normal = 1
111+ SingleSide = 2 # don't quite know what this is yet
112+ Cover = 3 # front / back cover
113+ EmptyPage = 4 # the intentional blanks inside the front and back covers
114+
115+ def __str__ (self ):
116+ return self .name # to print the enum name without the class
117+
108118class ProductStyle (Enum ):
109119 AlbumSingleSide = 1 # normal for albums, we divide the cewe 2 page bundle to single pages
110120 AlbumDoubleSide = 2 # any album when --keepdoublepages is set
@@ -188,6 +198,7 @@ def isAlbumDoubleSide(ps: ProductStyle):
188198 "ALB82" : reportlab .lib .pagesizes .A4 ,
189199 "ALB98" : reportlab .lib .pagesizes .A4 , # unittest, L 20.5cm x 27.0cm
190200 "ALB32" : (300 * reportlab .lib .pagesizes .mm , 300 * reportlab .lib .pagesizes .mm ), # album XL, 30 x 30 cm
201+ "ALB17" : (205 * reportlab .lib .pagesizes .mm , 205 * reportlab .lib .pagesizes .mm ), # album kvadratisk, 20.5 x 20.5 cm
191202 "ALB69" : (5400 / 100 / 2 * reportlab .lib .units .cm , 3560 / 100 * reportlab .lib .units .cm ),
192203 # add other page sizes here
193204 "MEM3" : (300 * reportlab .lib .pagesizes .mm , 300 * reportlab .lib .pagesizes .mm ) # memory game cards 6x6cm
@@ -303,7 +314,7 @@ def getPageElementForPageNumber(fotobook, pageNumber):
303314# This is only used for the <background .../> tags. The stock backgrounds use this element.
304315def processBackground (backgroundTags , bg_notFoundDirList , cewe_folder , backgroundLocations ,
305316 productstyle , pagetype , pdf , ph , pw ):
306- if pagetype == "emptypage" : # don't draw background for the empty pages. That is page nr. 1 and pageCount-1.
317+ if pagetype == PageType . EmptyPage : # don't draw background for the empty pages. That is page nr. 1 and pageCount-1.
307318 return
308319 if backgroundTags is not None and len (backgroundTags ) > 0 :
309320 # look for a tag that has an alignment attribute
@@ -336,15 +347,19 @@ def processBackground(backgroundTags, bg_notFoundDirList, cewe_folder, backgroun
336347 try :
337348 bgPath = ""
338349 bgPath = findFileInDirs ([bg + '.bmp' , bg + '.webp' , bg + '.jpg' ], backgroundLocations )
339- areaWidth = pw
340- if isAlbumDoubleSide (productstyle ):
341- areaWidth = pw / 2.
350+
351+ # Feb 2025, removed single/double side differentiation here, which basically cured
352+ # issue https://github.com/bash0/cewe2pdf/issues/198. I can only imagine that we have
353+ # tried to cure single/double page problems with "partial fixes" through the years
354+ # without understanding or testing all the cases. It is true that this solution does
355+ # not show the "forced empty pages" on the inside of the covers as white, a la CEWE,
356+ # but rather uses the background colour of the cover page. This whiteness is what
357+ # issue 198 is really complaining about, and I agree with it. Cover page colour is
358+ # a better choice when we are creating a pdf.
342359 areaHeight = ph
360+ areaWidth = pw
361+ areaXOffset = 0
343362
344- if isAlbumDoubleSide (productstyle ) and backgroundTag .get ('alignment' ) == "3" :
345- ax = areaWidth
346- else :
347- ax = 0
348363 logging .debug (f"Reading background file: { bgPath } " )
349364 # webp doesn't work with PIL.Image.open in Anaconda 5.3.0 on Win10
350365 imObj = PIL .Image .open (bgPath )
@@ -356,7 +371,7 @@ def processBackground(backgroundTags, bg_notFoundDirList, cewe_folder, backgroun
356371 memFileHandle .seek (0 )
357372 # im = imread(bgpath) #does not work with 1-bit images
358373 pdf .drawImage (ImageReader (
359- memFileHandle ), f * ax , 0 , width = f * areaWidth , height = f * areaHeight )
374+ memFileHandle ), f * areaXOffset , 0 , width = f * areaWidth , height = f * areaHeight )
360375 # pdf.drawImage(ImageReader(bgpath), f * ax, 0, width=f * aw, height=f * ah)
361376 except Exception :
362377 if bgPath not in bg_notFoundDirList :
@@ -1070,19 +1085,19 @@ def insertClipartFile(fileName:str, colorreplacements, transx, areaWidth, areaHe
10701085
10711086def processElements (additional_fonts , fotobook , imagedir ,
10721087 productstyle , mcfBaseFolder , oddpage , page , pageNumber , pagetype , pdf , ph , pw , lastpage ):
1073- if isAlbumDoubleSide (productstyle ) and oddpage == 0 and pagetype == 'normal' and not lastpage :
1088+ if isAlbumDoubleSide (productstyle ) and pagetype == PageType . Normal and not oddpage and not lastpage :
10741089 # if we are in double-page mode, all the images are drawn by the odd pages.
10751090 return
10761091
1077- # switch pack to the page element for the even page to get the elements
1078- if isAlbumProduct (productstyle ) and pagetype == 'normal' and oddpage == 1 :
1092+ # the mcf file really comes in "bundles" of two pages, so for odd pages we switch back to
1093+ # the page element for the preceding even page to get the elements
1094+ if isAlbumProduct (productstyle ) and pagetype == PageType .Normal and oddpage :
10791095 page = getPageElementForPageNumber (fotobook , 2 * floor (pageNumber / 2 ))
10801096
10811097 for area in page .findall ('area' ):
10821098 areaPos = area .find ('position' )
10831099 areaLeft = float (areaPos .get ('left' ).replace (',' , '.' ))
1084- # old python 2 code: aleft = float(area.get('left').replace(',', '.'))
1085- if pagetype != 'singleside' or len (area .findall ('imagebackground' )) == 0 :
1100+ if pagetype != PageType .SingleSide or len (area .findall ('imagebackground' )) == 0 :
10861101 if oddpage and isAlbumSingleSide (productstyle ):
10871102 # shift double-page content from other page
10881103 areaLeft -= pw
@@ -1092,7 +1107,7 @@ def processElements(additional_fonts, fotobook, imagedir,
10921107 areaRot = float (areaPos .get ('rotation' ))
10931108
10941109 # check if the image is on current page at all
1095- if pagetype == 'normal' and isAlbumSingleSide (productstyle ):
1110+ if pagetype == PageType . Normal and isAlbumSingleSide (productstyle ):
10961111 if oddpage :
10971112 # the right edge of image is beyond the left page border
10981113 if (areaLeft + areaWidth ) < 0 :
@@ -1130,17 +1145,14 @@ def processElements(additional_fonts, fotobook, imagedir,
11301145def parseInputPage (fotobook , cewe_folder , mcfBaseFolder , backgroundLocations , imagedir , pdf ,
11311146 page , pageNumber , pageCount , pagetype , productstyle , oddpage ,
11321147 bg_notFoundDirList , additional_fonts , lastpage ):
1133- logging .info (f"parsing page { page .get ('pagenr' )} of { pageCount } " )
1148+ logging .info (f"Pdf page { pageNumber } ( { pagetype } ): parsing pagenr { page .get ('pagenr' )} of { pageCount } " )
11341149
11351150 bundlesize = page .find ("./bundlesize" )
11361151 if bundlesize is not None :
11371152 pw = float (bundlesize .get ('width' ))
11381153 ph = float (bundlesize .get ('height' ))
1139-
1140- # reduce the page width to a single page width,
1141- # if we want to have single pages.
11421154 if isAlbumSingleSide (productstyle ):
1143- pw = pw / 2
1155+ pw = pw / 2 # reduce the page width to a single page width for single sided
11441156 else :
11451157 # Assume A4 page size
11461158 pw = 2100
@@ -1149,7 +1161,7 @@ def parseInputPage(fotobook, cewe_folder, mcfBaseFolder, backgroundLocations, im
11491161
11501162 # process background
11511163 # look for all "<background...> tags.
1152- # the preceeding designElementIDs tag only match the same
1164+ # the preceding designElementIDs tag only match the same
11531165 # number for the background attribute if it is a original
11541166 # stock image, without filters.
11551167 backgroundTags = page .findall ('background' )
@@ -1158,6 +1170,7 @@ def parseInputPage(fotobook, cewe_folder, mcfBaseFolder, backgroundLocations, im
11581170 # all elements (images, text,..) for even and odd pages are defined on the even page element!
11591171 processElements (additional_fonts , fotobook , imagedir , productstyle , mcfBaseFolder , oddpage , page , pageNumber , pagetype , pdf , ph , pw , lastpage )
11601172
1173+
11611174def getBaseClipartLocations (baseFolder ):
11621175 # create a tuple of places (folders) where background resources would be found by default
11631176 baseClipartLocations = (
@@ -1167,6 +1180,7 @@ def getBaseClipartLocations(baseFolder):
11671180 )
11681181 return baseClipartLocations
11691182
1183+
11701184def readClipArtConfigXML (baseFolder , keyaccountFolder ):
11711185 """Parse the configuration XML file and generate a dictionary of designElementId to fileName
11721186 currently only cliparts_default.xml is supported !"""
@@ -1673,7 +1687,12 @@ def convertMcf(albumname, keepDoublePages: bool, pageNumbers=None, mcfxTmpDir=No
16731687 if articleConfigElement is None :
16741688 logging .error (f'{ albumname } is an old version. Open it in the album editor and save before retrying the pdf conversion. Exiting.' )
16751689 sys .exit (1 )
1676- pageCount = int (articleConfigElement .get ('normalpages' )) + 2 # maximum number of pages
1690+ pageCount = int (articleConfigElement .get ('normalpages' )) + 2
1691+ # The normalpages attribute in the mcf is the number of "usable" inside pages, excluding the front and back covers and the blank inside
1692+ # cover pages. Add 2 so that pagecount represents the actual number of printed pdf pages we expect in the normal single sided
1693+ # pdf print (a basic album is 26 inside pages, plus front and back cover, i.e. 28). If we use keepDoublePages, then we'll
1694+ # actually be producing 2 more (the inside covers) but halving the number of final output pdf pages, making 15 double pages.
1695+ # There is also a totalpages attribute in the mcf, but oddly in my files it is 3 more than the normalpages value. Why not 4 more?
16771696 imagedir = fotobook .get ('imagedir' )
16781697
16791698 # generate a list of available clip-arts
@@ -1696,20 +1715,29 @@ def convertMcf(albumname, keepDoublePages: bool, pageNumbers=None, mcfxTmpDir=No
16961715 pdf = canvas .Canvas (outputFileName , pagesize = pagesize )
16971716 pdf .setTitle (albumTitle )
16981717
1699- for n in range (pageCount ):
1718+ def IsBackCover (n ):
1719+ return n == (pageCount - 1 )
1720+
1721+ for n in range (pageCount ): # starting at 0
17001722 try :
1701- if isAlbumProduct (productstyle ) and ((n == 0 ) or (n == pageCount - 1 )): # clearest like this so pylint: disable=consider-using-in
1702- pageNumber = 0
1723+ pagetype = PageType .Unknown
1724+ # The <page> sections all have a pagenr attribute. The normal pages run from pagenr 1 to pagenr 26 while there are
1725+ # actually FIVE page elements with pagenr 0 in an album file, 4 coming before pagenr 1 and 1 after pagenr 26
1726+ if isAlbumProduct (productstyle ) and ((n == 0 ) or IsBackCover (n )): # clearest like this so pylint: disable=consider-using-in
17031727 fullcoverpages = [i for i in
17041728 fotobook .findall ("./page[@pagenr='0'][@type='FULLCOVER']" )
17051729 + fotobook .findall ("./page[@pagenr='0'][@type='fullcover']" )
17061730 if i .find ("./area" ) is not None ]
1707- if len (fullcoverpages ) == 1 : # in a well-formed album there are two fullcover pages, but only one with an area
1731+ if len (fullcoverpages ) == 1 :
1732+ # in a well-formed album there are two fullcover pages, but only one with an area, which we
1733+ # have just found. That fullcover "page 0" (bundle) contains all the stuff for the back cover
1734+ # on the left side, and the front cover on the right sight (and the spine, as it happens)
17081735 page = fullcoverpages [0 ]
17091736 oddpage = (n == 0 ) # bool assign is clearer with parens so pylint: disable=superfluous-parens
1710- pagetype = 'cover'
1737+ pagetype = PageType .Cover
1738+ pageNumber = n
17111739 # for double-page-layout: the last page is already the left side of the book cover. So skip rendering the last page
1712- if (isAlbumDoubleSide (productstyle ) and ( n == ( pageCount - 1 ) )):
1740+ if (isAlbumDoubleSide (productstyle ) and IsBackCover ( n )):
17131741 page = None
17141742 else :
17151743 logging .warning ("Cannot locate a cover page, is this really an album?" )
@@ -1733,24 +1761,27 @@ def convertMcf(albumname, keepDoublePages: bool, pageNumbers=None, mcfxTmpDir=No
17331761 # Look for the the first page and set it up for processing
17341762 realFirstPageList = fotobook .findall ("./page[@pagenr='1'][@type='normalpage']" )
17351763 if len (realFirstPageList ) > 0 and (pageNumbers is None or 0 in pageNumbers ):
1736- # we need to do run parseInputPage twico for one output page in the PDF.
1764+ # for this special page we need to do run parseInputPage twice for one output page in the PDF.
17371765 # The background needs to be drawn first, or it would obscure any other other elements.
1738- pagetype = 'singleside'
1766+ pagetype = PageType . SingleSide
17391767 lastpage = False
17401768 parseInputPage (fotobook , cewe_folder , mcfBaseFolder , backgroundLocations , imagedir , pdf ,
17411769 realFirstPageList [0 ], pageNumber , pageCount , pagetype , productstyle , oddpage ,
17421770 bg_notFoundDirList , additional_fonts , lastpage )
1743- pagetype = 'emptypage'
1771+ pagetype = PageType . EmptyPage
17441772 else :
17451773 pageNumber = n
17461774 oddpage = (pageNumber % 2 ) == 1
17471775 page = getPageElementForPageNumber (fotobook , n )
1748- pagetype = 'normal'
1776+ pagetype = PageType . Normal
17491777
17501778 if pageNumbers is not None and pageNumber not in pageNumbers :
17511779 continue
17521780
17531781 if page is not None :
1782+ if pagetype == PageType .Unknown :
1783+ logging .error (f'Unable to deduce page type for page { pageNumber } ' )
1784+ continue
17541785 lastpage = (n == pageCount - 2 ) # bool assign is clearer with parens so pylint: disable=superfluous-parens
17551786 parseInputPage (fotobook , cewe_folder , mcfBaseFolder , backgroundLocations , imagedir , pdf ,
17561787 page , pageNumber , pageCount , pagetype , productstyle , oddpage ,
@@ -1761,11 +1792,10 @@ def convertMcf(albumname, keepDoublePages: bool, pageNumbers=None, mcfxTmpDir=No
17611792 # it would be neat to duplicate the page for MemoryCard products but showPage
17621793 # empties the canvas so the user must just print the pdf file twice!
17631794 pdf .showPage ()
1764- elif (isAlbumSingleSide (productstyle )
1765- or ((not (oddpage is False and pagetype == 'normal' ))
1766- and (not (n == (pageCount - 1 ) and pagetype == 'cover' ))
1767- )):
1768- # If we're creating a AlbumDoubleSide then we only output after the odd pages
1795+ elif isAlbumSingleSide (productstyle ):
1796+ pdf .showPage ()
1797+ elif oddpage or (pagetype == PageType .Cover and not IsBackCover (n )):
1798+ # We're creating a AlbumDoubleSide so we only output after the odd pages
17691799 pdf .showPage ()
17701800
17711801 except Exception as pageex :
0 commit comments