-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathconversionSetup.py
More file actions
191 lines (158 loc) · 9.31 KB
/
Copy pathconversionSetup.py
File metadata and controls
191 lines (158 loc) · 9.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""Prepare the input, configuration, and rendering resources for one conversion."""
# This module deliberately gathers the many resources required for one
# conversion into one named result. Splitting the preparation function or its
# dataclass purely to satisfy Pylint's default size limits would obscure that
# relationship. Broad catches are also intentional: the legacy configuration
# file is optional, and malformed album input needs a user-facing error.
# pylint: disable=bare-except,broad-exception-caught,too-many-instance-attributes,too-many-locals,too-many-statements
import configparser
import logging
import os
import os.path
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from lxml import etree
from ceweInfo import CeweInfo
from clipArt import readClipArtConfigXML
from configUtils import getConfigurationInt
from conversionState import ConversionState
from extraLoggers import mustsee
from fontHandling import findAndRegisterFonts
from lineScales import LineScales
from mcfx import unpackMcfx
from pathutils import findFileInDirs
@dataclass
class ConversionSetup:
"""Input and read-only resources resolved before the PDF canvas is created.
Values here describe the album and its configured rendering environment.
Data discovered while rendering belongs in :class:`ConversionState`
instead, so it is clear which values are safe to share with all pages.
"""
cewe_folder: str # CEWE installation/data root, from cewe_folder.txt or cewe2pdf.ini.
key_account_folder: str | None # Account-specific CEWE data directory when one can be determined.
configuration: configparser.ConfigParser | None # Merged INI configuration, or None when the legacy cewe_folder.txt path was used.
default_config_section: Any # The DEFAULT INI section, passed to renderers needing an individual setting.
background_locations: tuple[str, ...] # Ordered directories searched for CEWE page-background images.
clipart_files: dict[int, str] # Extra clipart ID-to-file mappings configured in the INI file.
clipart_paths: tuple[str, ...] # Clipart XML/resource search paths resolved from the CEWE installation.
passepartout_folders: tuple[str, ...] # Ordered directories searched when building the passepartout index.
mcf_xml_name: str # Actual XML file to parse: the source MCF, or data.mcf unpacked from MCFX.
mcf_base_folder: str # Folder containing mcf_xml_name, used to resolve album image references.
unpacked_folder: str | None # TemporaryDirectory returned when an MCFX archive was unpacked; otherwise None.
album_base_folder: str # Original album location, used to find its optional configuration file.
fotobook: Any # Root <fotobook> XML element used by the page-processing stage.
album_title: str # Human-readable album name, used as the PDF document title.
available_fonts: Any # Font faces successfully registered with ReportLab for this conversion.
line_scales: LineScales # Default and per-font line-spacing rules read from the INI configuration.
image_resolution: int # Target DPI for ordinary images.
background_resolution: int # Target DPI for page-background images.
def prepareConversion(albumname, mcfxTmpDir, appDataDir, state: ConversionState) -> ConversionSetup: # noqa: C901
"""Read an album and resolve the configuration and resources it requires."""
albumTitle, dummy = os.path.splitext(os.path.basename(albumname))
# Check for the archive format introduced around CEWE 7.3.
mcfxFormat = albumname.endswith('.mcfx')
if mcfxFormat:
albumPathObj = Path(albumname).resolve()
unpackedFolder, mcfxmlname = unpackMcfx(albumPathObj, mcfxTmpDir)
else:
unpackedFolder = None
mcfxmlname = albumname
# The original album folder locates configuration; the MCF folder locates images.
albumBaseFolder = str(Path(albumname).resolve().parent)
mcfPathObj = Path(mcfxmlname).resolve()
mcfBaseFolder = str(mcfPathObj.parent)
# Read as binary so the XML parser retains the file's UTF-8 encoding.
try:
with open(mcfxmlname, 'rb') as mcffile:
mcf = etree.parse(mcffile)
except Exception as exception:
invalidmsg = f'Cannot open mcf file {mcfxmlname}'
if mcfxFormat:
invalidmsg += f' (unpacked from {albumname})'
logging.error(f'{invalidmsg}: {repr(exception)}')
sys.exit(1)
fotobook = mcf.getroot()
CeweInfo.ensureAcceptableAlbumMcf(fotobook, albumname, mcfxmlname, mcfxFormat)
clipartFiles = {}
passepartoutFolders = tuple[str]()
defaultConfigSection = None
configuration = None
imageResolution = 150
backgroundResolution = 150
# Prefer the legacy cewe_folder.txt file when it is present. Otherwise,
# read the current-directory INI first and the album INI second, so the
# album-specific values override the current-directory defaults.
try:
configFolderFileName = findFileInDirs(
'cewe_folder.txt',
(albumBaseFolder, os.path.curdir, os.path.dirname(os.path.realpath(__file__))))
with open(configFolderFileName, 'r') as ceweFile: # pylint: disable=unspecified-encoding
ceweFolder = ceweFile.read().strip()
CeweInfo.checkCeweFolder(ceweFolder)
keyAccountNumber = CeweInfo.getKeyAccountNumber(ceweFolder)
keyAccountFolder = CeweInfo.getKeyAccountDataFolder(keyAccountNumber)
backgroundLocations = CeweInfo.getBaseBackgroundLocations(ceweFolder, keyAccountFolder)
except: # noqa: E722
logging.info('Trying cewe2pdf.ini from current directory and from the album directory.')
configuration = configparser.ConfigParser()
filesread = configuration.read(['cewe2pdf.ini', os.path.join(albumBaseFolder, 'cewe2pdf.ini')])
if len(filesread) < 1:
logging.error('You must create cewe_folder.txt or cewe2pdf.ini to specify the cewe_folder')
sys.exit(1)
mustsee.info(f'Using configuration files, in order: {str(filesread)}')
defaultConfigSection = configuration['DEFAULT']
if 'cewe_folder' not in defaultConfigSection:
logging.error('You must create cewe_folder.txt or modify cewe2pdf.ini to define cewe_folder')
sys.exit(1)
ceweFolder = defaultConfigSection['cewe_folder'].strip()
CeweInfo.checkCeweFolder(ceweFolder)
keyAccountNumber = CeweInfo.getKeyAccountNumber(ceweFolder, defaultConfigSection)
CeweInfo.SetEnvironmentVariables(ceweFolder, keyAccountNumber)
keyAccountFolder = CeweInfo.getKeyAccountDataFolder(keyAccountNumber, defaultConfigSection)
baseBackgroundLocations = CeweInfo.getBaseBackgroundLocations(ceweFolder, keyAccountFolder)
extraBackgroundFolders = defaultConfigSection.get('extraBackgroundFolders', '').splitlines()
backgroundLocations = baseBackgroundLocations + tuple(
os.path.expandvars(folder) for folder in extraBackgroundFolders if folder)
extraClipArts = defaultConfigSection.get('extraClipArts', '').splitlines()
for extraClipArt in extraClipArts:
if not extraClipArt:
continue
definition = os.path.expandvars(extraClipArt).split(',')
if len(definition) == 2:
clipartFiles[int(definition[0])] = definition[1].strip()
configuredPassepartoutFolders = defaultConfigSection.get('passepartoutFolders', '').splitlines()
configuredPassepartoutFolders.append(ceweFolder)
passepartoutFolders = tuple(
os.path.expandvars(folder) for folder in configuredPassepartoutFolders if folder)
imageResolution = getConfigurationInt(defaultConfigSection, 'pdfImageResolution', '150', 100)
backgroundResolution = getConfigurationInt(defaultConfigSection, 'pdfBackgroundResolution', '150', 100)
mustsee.info(f'Using image resolution {imageResolution}, background resolution {backgroundResolution}')
lineScales = LineScales(defaultConfigSection)
if keyAccountFolder is not None:
passepartoutFolders += CeweInfo.getCewePassepartoutFolders(ceweFolder, keyAccountFolder)
availableFonts = findAndRegisterFonts(defaultConfigSection, appDataDir, albumBaseFolder, ceweFolder, state)
clipartPaths = readClipArtConfigXML(ceweFolder, keyAccountFolder, clipartFiles)
# Use names here rather than relying on ConversionSetup's declaration
# order. The dataclass is intentionally grouped for readability above,
# and its fields should be freely rearrangeable without changing values.
return ConversionSetup(
cewe_folder=ceweFolder,
key_account_folder=keyAccountFolder,
configuration=configuration,
default_config_section=defaultConfigSection,
background_locations=backgroundLocations,
clipart_files=clipartFiles,
clipart_paths=clipartPaths,
passepartout_folders=passepartoutFolders,
mcf_xml_name=mcfxmlname,
mcf_base_folder=mcfBaseFolder,
unpacked_folder=unpackedFolder,
album_base_folder=albumBaseFolder,
fotobook=fotobook,
album_title=albumTitle,
available_fonts=availableFonts,
line_scales=lineScales,
image_resolution=imageResolution,
background_resolution=backgroundResolution)