Krita Source Code Documentation
Loading...
Searching...
No Matches
comics_project_management_tools.exporters.CPMT_EPUB_exporter Namespace Reference

Functions

 export (configDictionary={}, projectURL=str(), pagesLocationList=[], pageData=[])
 
 write_nav_file (path, configDictionary, htmlFiles, listOfNavItems)
 
 write_ncx_file (path, configDictionary, htmlFiles, listOfNavItems)
 
 write_opf_file (path, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads)
 
 write_region_nav_file (path, configDictionary, htmlFiles, regions=[])
 

Detailed Description

SPDX-FileCopyrightText: 2018 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>

This file is part of the Comics Project Management Tools(CPMT).

SPDX-License-Identifier: GPL-3.0-or-later

Function Documentation

◆ export()

comics_project_management_tools.exporters.CPMT_EPUB_exporter.export ( configDictionary = {},
projectURL = str(),
pagesLocationList = [],
pageData = [] )

Definition at line 27 of file CPMT_EPUB_exporter.py.

27def export(configDictionary = {}, projectURL = str(), pagesLocationList = [], pageData = []):
28 path = Path(os.path.join(projectURL, configDictionary["exportLocation"]))
29 exportPath = path / "EPUB-files"
30 metaInf = exportPath / "META-INF"
31 oebps = exportPath / "OEBPS"
32 imagePath = oebps / "Images"
33 # Don't write empty folders. Epubcheck doesn't like that.
34 # stylesPath = oebps / "Styles"
35 textPath = oebps / "Text"
36
37 if exportPath.exists() is False:
38 exportPath.mkdir()
39 metaInf.mkdir()
40 oebps.mkdir()
41 imagePath.mkdir()
42 # stylesPath.mkdir()
43 textPath.mkdir()
44
45 # Due the way EPUB verifies, the mimetype needs to be packaged in first.
46 # Due the way zips are constructed, the only way to ensure that is to
47 # Fill the zip as we go along...
48
49 # Use the project name if there's no title to avoid sillyness with unnamed zipfiles.
50 title = configDictionary["projectName"]
51 if "title" in configDictionary.keys():
52 title = str(configDictionary["title"]).replace(" ", "_")
53
54 # Get the appropriate path.
55 url = str(path / str(title + ".epub"))
56
57 # Create a zip file.
58 epubArchive = zipfile.ZipFile(url, mode="w", compression=zipfile.ZIP_STORED)
59
60 mimetype = open(str(Path(exportPath / "mimetype")), mode="w")
61 mimetype.write("application/epub+zip")
62 mimetype.close()
63
64 # Write to zip.
65 epubArchive.write(Path(exportPath / "mimetype"), Path("mimetype"))
66
67 container = QDomDocument()
68 cRoot = container.createElement("container")
69 cRoot.setAttribute("version", "1.0")
70 cRoot.setAttribute("xmlns", "urn:oasis:names:tc:opendocument:xmlns:container")
71 container.appendChild(cRoot)
72 rootFiles = container.createElement("rootfiles")
73 rootfile = container.createElement("rootfile")
74 rootfile.setAttribute("full-path", "OEBPS/content.opf")
75 rootfile.setAttribute("media-type", "application/oebps-package+xml")
76 rootFiles.appendChild(rootfile)
77 cRoot.appendChild(rootFiles)
78
79 containerFileName = str(Path(metaInf / "container.xml"))
80
81 containerFile = open(containerFileName, 'w', newline="", encoding="utf-8")
82 containerFile.write(container.toString(indent=2))
83 containerFile.close()
84
85 # Write to zip.
86 epubArchive.write(containerFileName, os.path.relpath(containerFileName, str(exportPath)))
87
88 # copyimages to images
89 pagesList = []
90 if len(pagesLocationList)>0:
91 if "cover" in configDictionary.keys():
92 coverNumber = configDictionary["pages"].index(configDictionary["cover"])
93 else:
94 coverNumber = 0
95 for p in pagesLocationList:
96 if os.path.exists(p):
97 shutil.copy2(p, str(imagePath))
98 filename = str(Path(imagePath / os.path.basename(p)))
99 pagesList.append(filename)
100 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
101 if len(pagesLocationList) >= coverNumber:
102 coverpageurl = pagesList[coverNumber]
103 else:
104 print("CPMT: Couldn't find the location for the epub images.")
105 return False
106
107 # for each image, make an xhtml file
108
109 htmlFiles = []
110 listOfNavItems = {}
111 listofSpreads = []
112 regions = []
113 for i in range(len(pagesList)):
114 pageName = "Page" + str(i) + ".xhtml"
115 doc = QDomDocument()
116 html = doc.createElement("html")
117 doc.appendChild(html)
118 html.setAttribute("xmlns", "http://www.w3.org/1999/xhtml")
119 html.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops")
120
121 # The viewport is a prerequisite to get pre-paginated
122 # layouts working. We'll make the layout the same size
123 # as the image.
124
125 head = doc.createElement("head")
126 viewport = doc.createElement("meta")
127 viewport.setAttribute("name", "viewport")
128
129 img = QImage()
130 img.load(pagesLocationList[i])
131 w = img.width()
132 h = img.height()
133
134 widthHeight = "width="+str(w)+", height="+str(h)
135
136 viewport.setAttribute("content", widthHeight)
137 head.appendChild(viewport)
138 html.appendChild(head)
139
140 # Here, we process the region navigation data to percentages
141 # because we have access here to the width and height of the viewport.
142
143 data = pageData[i]
144 transform = data["transform"]
145 for v in data["vector"]:
146 pointsList = []
147 dominantColor = QColor(Qt.GlobalColor.white)
148 listOfColors = []
149 for point in v["boundingBox"]:
150 offset = QPointF(transform["offsetX"], transform["offsetY"])
151 pixelPoint = QPointF(point.x() * transform["resDiff"], point.y() * transform["resDiff"])
152 newPoint = pixelPoint - offset
153 x = max(0, min(w, int(newPoint.x() * transform["scaleWidth"])))
154 y = max(0, min(h, int(newPoint.y() * transform["scaleHeight"])))
155 listOfColors.append(img.pixelColor(QPointF(x, y).toPoint()))
156 pointsList.append(QPointF((x/w)*100, (y/h)*100))
157 regionType = "panel"
158 if "text" in v.keys():
159 regionType = "text"
160 if len(listOfColors)>0:
161 dominantColor = listOfColors[-1]
162 listOfColors = listOfColors[:-1]
163 for color in listOfColors:
164 dominantColor.setRedF(0.5*(dominantColor.redF()+color.redF()))
165 dominantColor.setGreenF(0.5*(dominantColor.greenF()+color.greenF()))
166 dominantColor.setBlueF(0.5*(dominantColor.blueF()+color.blueF()))
167 region = {}
168 bounds = QPolygonF(pointsList).boundingRect()
169 region["points"] = bounds
170 region["type"] = regionType
171 region["page"] = str(Path(textPath / pageName))
172 region["primaryColor"] = dominantColor.name()
173 regions.append(region)
174
175 # We can also figureout here whether the page can be seen as a table of contents entry.
176
177 if "acbf_title" in data["keys"]:
178 listOfNavItems[str(Path(textPath / pageName))] = data["title"]
179
180 # Or spreads...
181
182 if "epub_spread" in data["keys"]:
183 listofSpreads.append(str(Path(textPath / pageName)))
184
185 body = doc.createElement("body")
186
187 img = doc.createElement("img")
188 img.setAttribute("src", os.path.relpath(pagesList[i], str(textPath)))
189 img.setAttribute("width", "100%")
190 body.appendChild(img)
191
192 html.appendChild(body)
193
194 filename = str(Path(textPath / pageName))
195 docFile = open(filename, 'w', newline="", encoding="utf-8")
196 docFile.write(doc.toString(indent=2))
197 docFile.close()
198
199 if pagesList[i] == coverpageurl:
200 coverpagehtml = os.path.relpath(filename, str(oebps))
201 htmlFiles.append(filename)
202
203 # Write to zip.
204 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
205
206 # metadata
207
208 filename = write_opf_file(oebps, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads)
209 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
210
211 filename = write_region_nav_file(oebps, configDictionary, htmlFiles, regions)
212 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
213
214 # toc
215 filename = write_nav_file(oebps, configDictionary, htmlFiles, listOfNavItems)
216 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
217
218 filename = write_ncx_file(oebps, configDictionary, htmlFiles, listOfNavItems)
219 epubArchive.write(filename, os.path.relpath(filename, str(exportPath)))
220
221 epubArchive.close()
222
223 return True
224
225"""
226Write OPF metadata file
227"""
228
229

References comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_nav_file(), comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_ncx_file(), comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_opf_file(), and comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_region_nav_file().

◆ write_nav_file()

comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_nav_file ( path,
configDictionary,
htmlFiles,
listOfNavItems )

Definition at line 663 of file CPMT_EPUB_exporter.py.

663def write_nav_file(path, configDictionary, htmlFiles, listOfNavItems):
664 navDoc = QDomDocument()
665 navRoot = navDoc.createElement("html")
666 navRoot.setAttribute("xmlns", "http://www.w3.org/1999/xhtml")
667 navRoot.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops")
668 navDoc.appendChild(navRoot)
669
670 head = navDoc.createElement("head")
671 title = navDoc.createElement("title")
672 title.appendChild(navDoc.createTextNode("Table of Contents"))
673 head.appendChild(title)
674 navRoot.appendChild(head)
675
676 body = navDoc.createElement("body")
677 navRoot.appendChild(body)
678
679 # The Table of Contents
680
681 toc = navDoc.createElement("nav")
682 toc.setAttribute("epub:type", "toc")
683 oltoc = navDoc.createElement("ol")
684 li = navDoc.createElement("li")
685 anchor = navDoc.createElement("a")
686 anchor.setAttribute("href", os.path.relpath(htmlFiles[0], str(path)))
687 anchor.appendChild(navDoc.createTextNode("Start"))
688 li.appendChild(anchor)
689 oltoc.appendChild(li)
690 for fileName in listOfNavItems.keys():
691 li = navDoc.createElement("li")
692 anchor = navDoc.createElement("a")
693 anchor.setAttribute("href", os.path.relpath(fileName, str(path)))
694 anchor.appendChild(navDoc.createTextNode(listOfNavItems[fileName]))
695 li.appendChild(anchor)
696 oltoc.appendChild(li)
697
698 toc.appendChild(oltoc)
699 body.appendChild(toc)
700
701 # The Pages List.
702
703 pageslist = navDoc.createElement("nav")
704 pageslist.setAttribute("epub:type", "page-list")
705 olpages = navDoc.createElement("ol")
706
707 entry = 1
708 for i in range(len(htmlFiles)):
709 li = navDoc.createElement("li")
710 anchor = navDoc.createElement("a")
711 anchor.setAttribute("href", os.path.relpath(htmlFiles[1], str(path)))
712 anchor.appendChild(navDoc.createTextNode(str(i)))
713 li.appendChild(anchor)
714 olpages.appendChild(li)
715 pageslist.appendChild(olpages)
716
717 body.appendChild(pageslist)
718
719
720
721 navFile = open(str(Path(path / "nav.xhtml")), 'w', newline="", encoding="utf-8")
722 navFile.write(navDoc.toString(indent=2))
723 navFile.close()
724 return str(Path(path / "nav.xhtml"))
725
726"""
727Write a NCX file.
728
729This is the same as the navigation document above, but then
730for 2.0 backward compatibility.
731"""
732

◆ write_ncx_file()

comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_ncx_file ( path,
configDictionary,
htmlFiles,
listOfNavItems )

Definition at line 733 of file CPMT_EPUB_exporter.py.

733def write_ncx_file(path, configDictionary, htmlFiles, listOfNavItems):
734 tocDoc = QDomDocument()
735 ncx = tocDoc.createElement("ncx")
736 ncx.setAttribute("version", "2005-1")
737 ncx.setAttribute("xmlns", "http://www.daisy.org/z3986/2005/ncx/")
738 tocDoc.appendChild(ncx)
739
740 tocHead = tocDoc.createElement("head")
741
742 # NCX also has some meta values that are in the head.
743 # They are shared with the opf metadata document.
744
745 uuid = str(configDictionary["uuid"])
746 uuid = uuid.strip("{")
747 uuid = uuid.strip("}")
748 metaID = tocDoc.createElement("meta")
749 metaID.setAttribute("content", uuid)
750 metaID.setAttribute("name", "dtb:uid")
751 tocHead.appendChild(metaID)
752 metaDepth = tocDoc.createElement("meta")
753 metaDepth.setAttribute("content", str(1))
754 metaDepth.setAttribute("name", "dtb:depth")
755 tocHead.appendChild(metaDepth)
756 metaTotal = tocDoc.createElement("meta")
757 metaTotal.setAttribute("content", str(len(htmlFiles)))
758 metaTotal.setAttribute("name", "dtb:totalPageCount")
759 tocHead.appendChild(metaTotal)
760 metaMax = tocDoc.createElement("meta")
761 metaMax.setAttribute("content", str(len(htmlFiles)))
762 metaMax.setAttribute("name", "dtb:maxPageNumber")
763 tocHead.appendChild(metaDepth)
764 ncx.appendChild(tocHead)
765
766 docTitle = tocDoc.createElement("docTitle")
767 text = tocDoc.createElement("text")
768 if "title" in configDictionary.keys():
769 text.appendChild(tocDoc.createTextNode(str(configDictionary["title"])))
770 else:
771 text.appendChild(tocDoc.createTextNode("Comic with no Name"))
772 docTitle.appendChild(text)
773 ncx.appendChild(docTitle)
774
775 # The navmap is a table of contents.
776
777 navmap = tocDoc.createElement("navMap")
778 navPoint = tocDoc.createElement("navPoint")
779 navPoint.setAttribute("id", "navPoint-1")
780 navPoint.setAttribute("playOrder", "1")
781 navLabel = tocDoc.createElement("navLabel")
782 navLabelText = tocDoc.createElement("text")
783 navLabelText.appendChild(tocDoc.createTextNode("Start"))
784 navLabel.appendChild(navLabelText)
785 navContent = tocDoc.createElement("content")
786 navContent.setAttribute("src", os.path.relpath(htmlFiles[0], str(path)))
787 navPoint.appendChild(navLabel)
788 navPoint.appendChild(navContent)
789 navmap.appendChild(navPoint)
790 entry = 1
791 for fileName in listOfNavItems.keys():
792 entry +=1
793 navPointT = tocDoc.createElement("navPoint")
794 navPointT.setAttribute("id", "navPoint-"+str(entry))
795 navPointT.setAttribute("playOrder", str(entry))
796 navLabelT = tocDoc.createElement("navLabel")
797 navLabelTText = tocDoc.createElement("text")
798 navLabelTText.appendChild(tocDoc.createTextNode(listOfNavItems[fileName]))
799 navLabelT.appendChild(navLabelTText)
800 navContentT = tocDoc.createElement("content")
801 navContentT.setAttribute("src", os.path.relpath(fileName, str(path)))
802 navPointT.appendChild(navLabelT)
803 navPointT.appendChild(navContentT)
804 navmap.appendChild(navPointT)
805 ncx.appendChild(navmap)
806
807 # The pages list on the other hand just lists all pages.
808
809 pagesList = tocDoc.createElement("pageList")
810 navLabelPages = tocDoc.createElement("navLabel")
811 navLabelPagesText = tocDoc.createElement("text")
812 navLabelPagesText.appendChild(tocDoc.createTextNode("Pages"))
813 navLabelPages.appendChild(navLabelPagesText)
814 pagesList.appendChild(navLabelPages)
815 for i in range(len(htmlFiles)):
816 pageTarget = tocDoc.createElement("pageTarget")
817 pageTarget.setAttribute("type", "normal")
818 pageTarget.setAttribute("id", "page-"+str(i))
819 pageTarget.setAttribute("value", str(i))
820 navLabelPagesTarget = tocDoc.createElement("navLabel")
821 navLabelPagesTargetText = tocDoc.createElement("text")
822 navLabelPagesTargetText.appendChild(tocDoc.createTextNode(str(i+1)))
823 navLabelPagesTarget.appendChild(navLabelPagesTargetText)
824 pageTarget.appendChild(navLabelPagesTarget)
825 pageTargetContent = tocDoc.createElement("content")
826 pageTargetContent.setAttribute("src", os.path.relpath(htmlFiles[i], str(path)))
827 pageTarget.appendChild(pageTargetContent)
828 pagesList.appendChild(pageTarget)
829 ncx.appendChild(pagesList)
830
831 # Save the document.
832
833 docFile = open(str(Path(path / "toc.ncx")), 'w', newline="", encoding="utf-8")
834 docFile.write(tocDoc.toString(indent=2))
835 docFile.close()
836 return str(Path(path / "toc.ncx"))

◆ write_opf_file()

comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_opf_file ( path,
configDictionary,
htmlFiles,
pagesList,
coverpageurl,
coverpagehtml,
listofSpreads )

Definition at line 230 of file CPMT_EPUB_exporter.py.

230def write_opf_file(path, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads):
231
232 # marc relators
233 # This has several entries removed to reduce it to the most relevant entries.
234 marcRelators = {"abr":i18n("Abridger"), "acp":i18n("Art copyist"), "act":i18n("Actor"), "adi":i18n("Art director"), "adp":i18n("Adapter"), "ann":i18n("Annotator"), "ant":i18n("Bibliographic antecedent"), "arc":i18n("Architect"), "ard":i18n("Artistic director"), "art":i18n("Artist"), "asn":i18n("Associated name"), "ato":i18n("Autographer"), "att":i18n("Attributed name"), "aud":i18n("Author of dialog"), "aut":i18n("Author"), "bdd":i18n("Binding designer"), "bjd":i18n("Bookjacket designer"), "bkd":i18n("Book designer"), "bkp":i18n("Book producer"), "blw":i18n("Blurb writer"), "bnd":i18n("Binder"), "bpd":i18n("Bookplate designer"), "bsl":i18n("Bookseller"), "cll":i18n("Calligrapher"), "clr":i18n("Colorist"), "cns":i18n("Censor"), "cov":i18n("Cover designer"), "cph":i18n("Copyright holder"), "cre":i18n("Creator"), "ctb":i18n("Contributor"), "cur":i18n("Curator"), "cwt":i18n("Commentator for written text"), "drm":i18n("Draftsman"), "dsr":i18n("Designer"), "dub":i18n("Dubious author"), "edt":i18n("Editor"), "etr":i18n("Etcher"), "exp":i18n("Expert"), "fnd":i18n("Funder"), "ill":i18n("Illustrator"), "ilu":i18n("Illuminator"), "ins":i18n("Inscriber"), "lse":i18n("Licensee"), "lso":i18n("Licensor"), "ltg":i18n("Lithographer"), "mdc":i18n("Metadata contact"), "oth":i18n("Other"), "own":i18n("Owner"), "pat":i18n("Patron"), "pbd":i18n("Publishing director"), "pbl":i18n("Publisher"), "prt":i18n("Printer"), "sce":i18n("Scenarist"), "scr":i18n("Scribe"), "spn":i18n("Sponsor"), "stl":i18n("Storyteller"), "trc":i18n("Transcriber"), "trl":i18n("Translator"), "tyd":i18n("Type designer"), "tyg":i18n("Typographer"), "wac":i18n("Writer of added commentary"), "wal":i18n("Writer of added lyrics"), "wam":i18n("Writer of accompanying material"), "wat":i18n("Writer of added text"), "win":i18n("Writer of introduction"), "wpr":i18n("Writer of preface"), "wst":i18n("Writer of supplementary textual content")}
235
236 # opf file
237 opfFile = QDomDocument()
238 opfRoot = opfFile.createElement("package")
239 opfRoot.setAttribute("version", "3.0")
240 opfRoot.setAttribute("unique-identifier", "BookId")
241 opfRoot.setAttribute("xmlns", "http://www.idpf.org/2007/opf")
242 opfRoot.setAttribute("prefix", "rendition: http://www.idpf.org/vocab/rendition/#")
243 opfFile.appendChild(opfRoot)
244
245 opfMeta = opfFile.createElement("metadata")
246 opfMeta.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/")
247 opfMeta.setAttribute("xmlns:dcterms", "http://purl.org/dc/terms/")
248
249 # EPUB metadata requires a title, language and uuid
250
251 langString = "en-US"
252 if "language" in configDictionary.keys():
253 langString = str(configDictionary["language"]).replace("_", "-")
254
255 bookLang = opfFile.createElement("dc:language")
256 bookLang.appendChild(opfFile.createTextNode(langString))
257 opfMeta.appendChild(bookLang)
258
259 bookTitle = opfFile.createElement("dc:title")
260 if "title" in configDictionary.keys():
261 bookTitle.appendChild(opfFile.createTextNode(str(configDictionary["title"])))
262 else:
263 bookTitle.appendChild(opfFile.createTextNode("Comic with no Name"))
264 opfMeta.appendChild(bookTitle)
265
266 # Generate series title and the like here too.
267 if "seriesName" in configDictionary.keys():
268 bookTitle.setAttribute("id", "main")
269
270 refine = opfFile.createElement("meta")
271 refine.setAttribute("refines", "#main")
272 refine.setAttribute("property", "title-type")
273 refine.appendChild(opfFile.createTextNode("main"))
274 opfMeta.appendChild(refine)
275
276 refine2 = opfFile.createElement("meta")
277 refine2.setAttribute("refines", "#main")
278 refine2.setAttribute("property", "display-seq")
279 refine2.appendChild(opfFile.createTextNode("1"))
280 opfMeta.appendChild(refine2)
281
282 seriesTitle = opfFile.createElement("dc:title")
283 seriesTitle.appendChild(opfFile.createTextNode(str(configDictionary["seriesName"])))
284 seriesTitle.setAttribute("id", "series")
285 opfMeta.appendChild(seriesTitle)
286
287 refineS = opfFile.createElement("meta")
288 refineS.setAttribute("refines", "#series")
289 refineS.setAttribute("property", "title-type")
290 refineS.appendChild(opfFile.createTextNode("collection"))
291 opfMeta.appendChild(refineS)
292
293 refineS2 = opfFile.createElement("meta")
294 refineS2.setAttribute("refines", "#series")
295 refineS2.setAttribute("property", "display-seq")
296 refineS2.appendChild(opfFile.createTextNode("2"))
297 opfMeta.appendChild(refineS2)
298
299 if "seriesNumber" in configDictionary.keys():
300 refineS3 = opfFile.createElement("meta")
301 refineS3.setAttribute("refines", "#series")
302 refineS3.setAttribute("property", "group-position")
303 refineS3.appendChild(opfFile.createTextNode(str(configDictionary["seriesNumber"])))
304 opfMeta.appendChild(refineS3)
305
306 uuid = str(configDictionary["uuid"])
307 uuid = uuid.strip("{")
308 uuid = uuid.strip("}")
309
310 # Append the id, and assign it as the bookID.
311 uniqueID = opfFile.createElement("dc:identifier")
312 uniqueID.appendChild(opfFile.createTextNode("urn:uuid:"+uuid))
313 uniqueID.setAttribute("id", "BookId")
314 opfMeta.appendChild(uniqueID)
315
316 if "authorList" in configDictionary.keys():
317 authorEntry = 0
318 for authorE in range(len(configDictionary["authorList"])):
319 authorDict = configDictionary["authorList"][authorE]
320 authorType = "dc:creator"
321 if "role" in authorDict.keys():
322 # This determines if someone was just a contributor, but might need a more thorough version.
323 if str(authorDict["role"]).lower() in ["editor", "assistant editor", "proofreader", "beta", "patron", "funder"]:
324 authorType = "dc:contributor"
325 author = opfFile.createElement(authorType)
326 authorName = []
327 if "last-name" in authorDict.keys():
328 authorName.append(authorDict["last-name"])
329 if "first-name" in authorDict.keys():
330 authorName.append(authorDict["first-name"])
331 if "initials" in authorDict.keys():
332 authorName.append(authorDict["initials"])
333 if "nickname" in authorDict.keys():
334 authorName.append("(" + authorDict["nickname"] + ")")
335 author.appendChild(opfFile.createTextNode(", ".join(authorName)))
336 author.setAttribute("id", "cre" + str(authorE))
337 opfMeta.appendChild(author)
338 if "role" in authorDict.keys():
339 role = opfFile.createElement("meta")
340 role.setAttribute("refines", "#cre" + str(authorE))
341 role.setAttribute("scheme", "marc:relators")
342 role.setAttribute("property", "role")
343 roleString = str(authorDict["role"])
344 if roleString in marcRelators.values() or roleString in marcRelators.keys():
345 i = list(marcRelators.values()).index(roleString)
346 roleString = list(marcRelators.keys())[i]
347 else:
348 roleString = "oth"
349 role.appendChild(opfFile.createTextNode(roleString))
350 opfMeta.appendChild(role)
351 refine = opfFile.createElement("meta")
352 refine.setAttribute("refines", "#cre"+str(authorE))
353 refine.setAttribute("property", "display-seq")
354 refine.appendChild(opfFile.createTextNode(str(authorE+1)))
355 opfMeta.appendChild(refine)
356
357 if "publishingDate" in configDictionary.keys():
358 date = opfFile.createElement("dc:date")
359 date.appendChild(opfFile.createTextNode(configDictionary["publishingDate"]))
360 opfMeta.appendChild(date)
361
362 #Creation date
363 modified = opfFile.createElement("meta")
364 modified.setAttribute("property", "dcterms:modified")
365 modified.appendChild(opfFile.createTextNode(QDateTime.currentDateTimeUtc().toString(Qt.DateFormat.ISODate)))
366 opfMeta.appendChild(modified)
367
368 if "source" in configDictionary.keys():
369 if len(configDictionary["source"])>0:
370 source = opfFile.createElement("dc:source")
371 source.appendChild(opfFile.createTextNode(configDictionary["source"]))
372 opfMeta.appendChild(source)
373
374 description = opfFile.createElement("dc:description")
375 if "summary" in configDictionary.keys():
376 description.appendChild(opfFile.createTextNode(configDictionary["summary"]))
377 else:
378 description.appendChild(opfFile.createTextNode("There was no summary upon generation of this file."))
379 opfMeta.appendChild(description)
380
381 # Type can be dictionary or index, or one of those edupub thingies. Not necessary for comics.
382 # typeE = opfFile.createElement("dc:type")
383 # opfMeta.appendChild(typeE)
384
385 if "publisherName" in configDictionary.keys():
386 publisher = opfFile.createElement("dc:publisher")
387 publisher.appendChild(opfFile.createTextNode(configDictionary["publisherName"]))
388 opfMeta.appendChild(publisher)
389
390
391 if "isbn-number" in configDictionary.keys():
392 isbnnumber = configDictionary["isbn-number"]
393
394 if len(isbnnumber)>0:
395 publishISBN = opfFile.createElement("dc:identifier")
396 publishISBN.appendChild(opfFile.createTextNode(str("urn:isbn:") + isbnnumber))
397 opfMeta.appendChild(publishISBN)
398
399 if "license" in configDictionary.keys():
400
401 if len(configDictionary["license"])>0:
402 rights = opfFile.createElement("dc:rights")
403 rights.appendChild(opfFile.createTextNode(configDictionary["license"]))
404 opfMeta.appendChild(rights)
405
406 """
407 Not handled
408 Relation - This is for whether the work has a relationship with another work.
409 It could be fanart, but also adaptation, an academic work, etc.
410 Coverage - This is for the time/place that the work covers. Typically to determine
411 whether an academic work deals with a certain time period or place.
412 For comics you could use this to mark historical comics, but other than
413 that we'd need a much better ui to define this.
414 """
415
416 # These are all dublin core subjects.
417 # 3.1 defines the ability to use an authority, but that
418 # might be a bit too complicated right now.
419
420 if "genre" in configDictionary.keys():
421 genreListConf = configDictionary["genre"]
422 if isinstance(configDictionary["genre"], dict):
423 genreListConf = configDictionary["genre"].keys()
424 for g in genreListConf:
425 subject = opfFile.createElement("dc:subject")
426 subject.appendChild(opfFile.createTextNode(g))
427 opfMeta.appendChild(subject)
428 if "characters" in configDictionary.keys():
429 for name in configDictionary["characters"]:
430 char = opfFile.createElement("dc:subject")
431 char.appendChild(opfFile.createTextNode(name))
432 opfMeta.appendChild(char)
433 if "format" in configDictionary.keys():
434 for formatF in configDictionary["format"]:
435 f = opfFile.createElement("dc:subject")
436 f.appendChild(opfFile.createTextNode(formatF))
437 opfMeta.appendChild(f)
438 if "otherKeywords" in configDictionary.keys():
439 for key in configDictionary["otherKeywords"]:
440 word = opfFile.createElement("dc:subject")
441 word.appendChild(opfFile.createTextNode(key))
442 opfMeta.appendChild(word)
443
444 # Pre-pagination and layout
445 # Comic are always prepaginated.
446
447 elLayout = opfFile.createElement("meta")
448 elLayout.setAttribute("property", "rendition:layout")
449 elLayout.appendChild(opfFile.createTextNode("pre-paginated"))
450 opfMeta.appendChild(elLayout)
451
452 # We should figure out if the pages are portrait or not...
453 elOrientation = opfFile.createElement("meta")
454 elOrientation.setAttribute("property", "rendition:orientation")
455 elOrientation.appendChild(opfFile.createTextNode("portrait"))
456 opfMeta.appendChild(elOrientation)
457
458 elSpread = opfFile.createElement("meta")
459 elSpread.setAttribute("property", "rendition:spread")
460 elSpread.appendChild(opfFile.createTextNode("landscape"))
461 opfMeta.appendChild(elSpread)
462
463 opfRoot.appendChild(opfMeta)
464
465 # Manifest
466
467 opfManifest = opfFile.createElement("manifest")
468 toc = opfFile.createElement("item")
469 toc.setAttribute("id", "ncx")
470 toc.setAttribute("href", "toc.ncx")
471 toc.setAttribute("media-type", "application/x-dtbncx+xml")
472 opfManifest.appendChild(toc)
473
474 region = opfFile.createElement("item")
475 region.setAttribute("id", "regions")
476 region.setAttribute("href", "region-nav.xhtml")
477 region.setAttribute("media-type", "application/xhtml+xml")
478 region.setAttribute("properties", "data-nav") # Set the propernavmap to use this later)
479 opfManifest.appendChild(region)
480
481 nav = opfFile.createElement("item")
482 nav.setAttribute("id", "nav")
483 nav.setAttribute("href", "nav.xhtml")
484 nav.setAttribute("media-type", "application/xhtml+xml")
485 nav.setAttribute("properties", "nav") # Set the propernavmap to use this later)
486 opfManifest.appendChild(nav)
487
488 ids = 0
489 for p in pagesList:
490 item = opfFile.createElement("item")
491 item.setAttribute("id", "img"+str(ids))
492 ids +=1
493 item.setAttribute("href", os.path.relpath(p, str(path)))
494 item.setAttribute("media-type", "image/png")
495 if os.path.basename(p) == os.path.basename(coverpageurl):
496 item.setAttribute("properties", "cover-image")
497 opfManifest.appendChild(item)
498
499
500 ids = 0
501 for p in htmlFiles:
502 item = opfFile.createElement("item")
503 item.setAttribute("id", "p"+str(ids))
504 ids +=1
505 item.setAttribute("href", os.path.relpath(p, str(path)))
506 item.setAttribute("media-type", "application/xhtml+xml")
507 opfManifest.appendChild(item)
508
509
510 opfRoot.appendChild(opfManifest)
511
512 # Spine
513
514 opfSpine = opfFile.createElement("spine")
515 # this sets the table of contents to use the ncx file
516 opfSpine.setAttribute("toc", "ncx")
517 # Reading Direction:
518
519 spreadRight = True
520 direction = 0
521 if "readingDirection" in configDictionary.keys():
522 if configDictionary["readingDirection"] == "rightToLeft":
523 opfSpine.setAttribute("page-progression-direction", "rtl")
524 spreadRight = False
525 direction = 1
526 else:
527 opfSpine.setAttribute("page-progression-direction", "ltr")
528
529 # Here we'd need to switch between the two and if spread keywrod use neither but combine with spread-none
530
531 ids = 0
532 for p in htmlFiles:
533 item = opfFile.createElement("itemref")
534 item.setAttribute("idref", "p"+str(ids))
535 ids +=1
536 props = []
537 if p in listofSpreads:
538 # Put this one in the center.
539 props.append("rendition:page-spread-center")
540
541 # Reset the spread boolean.
542 # It needs to point at the first side after the spread.
543 # So ltr -> spread-left, rtl->spread-right
544 if direction == 0:
545 spreadRight = False
546 else:
547 spreadRight = True
548 else:
549 if spreadRight:
550 props.append("page-spread-right")
551 spreadRight = False
552 else:
553 props.append("page-spread-left")
554 spreadRight = True
555 item.setAttribute("properties", " ".join(props))
556 opfSpine.appendChild(item)
557 opfRoot.appendChild(opfSpine)
558
559 # Guide
560
561 opfGuide = opfFile.createElement("guide")
562 if coverpagehtml is not None and coverpagehtml.isspace() is False and len(coverpagehtml) > 0:
563 item = opfFile.createElement("reference")
564 item.setAttribute("type", "cover")
565 item.setAttribute("title", "Cover")
566 item.setAttribute("href", coverpagehtml)
567 opfGuide.appendChild(item)
568 opfRoot.appendChild(opfGuide)
569
570 docFile = open(str(Path(path / "content.opf")), 'w', newline="", encoding="utf-8")
571 docFile.write(opfFile.toString(indent=2))
572 docFile.close()
573 return str(Path(path / "content.opf"))
574
575"""
576Write a region navmap file.
577"""
578

◆ write_region_nav_file()

comics_project_management_tools.exporters.CPMT_EPUB_exporter.write_region_nav_file ( path,
configDictionary,
htmlFiles,
regions = [] )

Definition at line 579 of file CPMT_EPUB_exporter.py.

579def write_region_nav_file(path, configDictionary, htmlFiles, regions = []):
580 navDoc = QDomDocument()
581 navRoot = navDoc.createElement("html")
582 navRoot.setAttribute("xmlns", "http://www.w3.org/1999/xhtml")
583 navRoot.setAttribute("xmlns:epub", "http://www.idpf.org/2007/ops")
584 navDoc.appendChild(navRoot)
585
586 head = navDoc.createElement("head")
587 title = navDoc.createElement("title")
588 title.appendChild(navDoc.createTextNode("Region Navigation"))
589 head.appendChild(title)
590 navRoot.appendChild(head)
591
592 body = navDoc.createElement("body")
593 navRoot.appendChild(body)
594
595 nav = navDoc.createElement("nav")
596 nav.setAttribute("epub:type", "region-based")
597 nav.setAttribute("prefix", "ahl: http://idpf.org/epub/vocab/ahl")
598 body.appendChild(nav)
599
600 # Let's write the panels and balloons down now.
601
602 olPanels = navDoc.createElement("ol")
603 for region in regions:
604 if region["type"] == "panel":
605 pageName = os.path.relpath(region["page"], str(path))
606 print("accessing panel")
607 li = navDoc.createElement("li")
608 li.setAttribute("epub:type", "panel")
609
610 anchor = navDoc.createElement("a")
611 bounds = region["points"]
612 anchor.setAttribute("href", pageName+"#xywh=percent:"+str(bounds.x())+","+str(bounds.y())+","+str(bounds.width())+","+str(bounds.height()))
613
614 if len(region["primaryColor"])>0:
615 primaryC = navDoc.createElement("meta")
616 primaryC.setAttribute("property","ahl:primary-color")
617 primaryC.setAttribute("content", region["primaryColor"])
618 anchor.appendChild(primaryC)
619
620 li.appendChild(anchor)
621 olBalloons = navDoc.createElement("ol")
622
623 """
624 The region nav spec specifies that we should have text-areas/balloons as a refinement on
625 the panel.
626 For each panel, we'll check if there's balloons/text-areas inside, and we'll do that by
627 checking whether the center point is inside the panel because some comics have balloons
628 that overlap the gutters.
629 """
630 for balloon in regions:
631 if balloon["type"] == "text" and balloon["page"] == region["page"] and bounds.contains(balloon["points"].center()):
632 liBalloon = navDoc.createElement("li")
633 liBalloon.setAttribute("epub:type", "text-area")
634
635 anchorBalloon = navDoc.createElement("a")
636 BBounds = balloon["points"]
637 anchorBalloon.setAttribute("href", pageName+"#xywh=percent:"+str(BBounds.x())+","+str(BBounds.y())+","+str(BBounds.width())+","+str(BBounds.height()))
638
639 liBalloon.appendChild(anchorBalloon)
640 olBalloons.appendChild(liBalloon)
641
642 if olBalloons.hasChildNodes():
643 li.appendChild(olBalloons)
644 olPanels.appendChild(li)
645 nav.appendChild(olPanels)
646
647 navFile = open(str(Path(path / "region-nav.xhtml")), 'w', newline="", encoding="utf-8")
648 navFile.write(navDoc.toString(indent=2))
649 navFile.close()
650 return str(Path(path / "region-nav.xhtml"))
651
652"""
653Write XHTML nav file.
654
655This is virtually the same as the NCX file, except that
656the navigation document can be styled, and is what 3.1 and
6573.2 expect as a primary navigation document.
658
659This function will both create a table of contents, using the
660"acbf_title" feature, as well as a regular pageslist.
661"""
662