229def write_opf_file(path, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads):
230
231
232
233 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")}
234
235
236 opfFile = QDomDocument()
237 opfRoot = opfFile.createElement("package")
238 opfRoot.setAttribute("version", "3.0")
239 opfRoot.setAttribute("unique-identifier", "BookId")
240 opfRoot.setAttribute("xmlns", "http://www.idpf.org/2007/opf")
241 opfRoot.setAttribute("prefix", "rendition: http://www.idpf.org/vocab/rendition/#")
242 opfFile.appendChild(opfRoot)
243
244 opfMeta = opfFile.createElement("metadata")
245 opfMeta.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/")
246 opfMeta.setAttribute("xmlns:dcterms", "http://purl.org/dc/terms/")
247
248
249
250 langString = "en-US"
251 if "language" in configDictionary.keys():
252 langString = str(configDictionary["language"]).replace("_", "-")
253
254 bookLang = opfFile.createElement("dc:language")
255 bookLang.appendChild(opfFile.createTextNode(langString))
256 opfMeta.appendChild(bookLang)
257
258 bookTitle = opfFile.createElement("dc:title")
259 if "title" in configDictionary.keys():
260 bookTitle.appendChild(opfFile.createTextNode(str(configDictionary["title"])))
261 else:
262 bookTitle.appendChild(opfFile.createTextNode("Comic with no Name"))
263 opfMeta.appendChild(bookTitle)
264
265
266 if "seriesName" in configDictionary.keys():
267 bookTitle.setAttribute("id", "main")
268
269 refine = opfFile.createElement("meta")
270 refine.setAttribute("refines", "#main")
271 refine.setAttribute("property", "title-type")
272 refine.appendChild(opfFile.createTextNode("main"))
273 opfMeta.appendChild(refine)
274
275 refine2 = opfFile.createElement("meta")
276 refine2.setAttribute("refines", "#main")
277 refine2.setAttribute("property", "display-seq")
278 refine2.appendChild(opfFile.createTextNode("1"))
279 opfMeta.appendChild(refine2)
280
281 seriesTitle = opfFile.createElement("dc:title")
282 seriesTitle.appendChild(opfFile.createTextNode(str(configDictionary["seriesName"])))
283 seriesTitle.setAttribute("id", "series")
284 opfMeta.appendChild(seriesTitle)
285
286 refineS = opfFile.createElement("meta")
287 refineS.setAttribute("refines", "#series")
288 refineS.setAttribute("property", "title-type")
289 refineS.appendChild(opfFile.createTextNode("collection"))
290 opfMeta.appendChild(refineS)
291
292 refineS2 = opfFile.createElement("meta")
293 refineS2.setAttribute("refines", "#series")
294 refineS2.setAttribute("property", "display-seq")
295 refineS2.appendChild(opfFile.createTextNode("2"))
296 opfMeta.appendChild(refineS2)
297
298 if "seriesNumber" in configDictionary.keys():
299 refineS3 = opfFile.createElement("meta")
300 refineS3.setAttribute("refines", "#series")
301 refineS3.setAttribute("property", "group-position")
302 refineS3.appendChild(opfFile.createTextNode(str(configDictionary["seriesNumber"])))
303 opfMeta.appendChild(refineS3)
304
305 uuid = str(configDictionary["uuid"])
306 uuid = uuid.strip("{")
307 uuid = uuid.strip("}")
308
309
310 uniqueID = opfFile.createElement("dc:identifier")
311 uniqueID.appendChild(opfFile.createTextNode("urn:uuid:"+uuid))
312 uniqueID.setAttribute("id", "BookId")
313 opfMeta.appendChild(uniqueID)
314
315 if "authorList" in configDictionary.keys():
316 authorEntry = 0
317 for authorE in range(len(configDictionary["authorList"])):
318 authorDict = configDictionary["authorList"][authorE]
319 authorType = "dc:creator"
320 if "role" in authorDict.keys():
321
322 if str(authorDict["role"]).lower() in ["editor", "assistant editor", "proofreader", "beta", "patron", "funder"]:
323 authorType = "dc:contributor"
324 author = opfFile.createElement(authorType)
325 authorName = []
326 if "last-name" in authorDict.keys():
327 authorName.append(authorDict["last-name"])
328 if "first-name" in authorDict.keys():
329 authorName.append(authorDict["first-name"])
330 if "initials" in authorDict.keys():
331 authorName.append(authorDict["initials"])
332 if "nickname" in authorDict.keys():
333 authorName.append("(" + authorDict["nickname"] + ")")
334 author.appendChild(opfFile.createTextNode(", ".join(authorName)))
335 author.setAttribute("id", "cre" + str(authorE))
336 opfMeta.appendChild(author)
337 if "role" in authorDict.keys():
338 role = opfFile.createElement("meta")
339 role.setAttribute("refines", "#cre" + str(authorE))
340 role.setAttribute("scheme", "marc:relators")
341 role.setAttribute("property", "role")
342 roleString = str(authorDict["role"])
343 if roleString in marcRelators.values() or roleString in marcRelators.keys():
344 i = list(marcRelators.values()).index(roleString)
345 roleString = list(marcRelators.keys())[i]
346 else:
347 roleString = "oth"
348 role.appendChild(opfFile.createTextNode(roleString))
349 opfMeta.appendChild(role)
350 refine = opfFile.createElement("meta")
351 refine.setAttribute("refines", "#cre"+str(authorE))
352 refine.setAttribute("property", "display-seq")
353 refine.appendChild(opfFile.createTextNode(str(authorE+1)))
354 opfMeta.appendChild(refine)
355
356 if "publishingDate" in configDictionary.keys():
357 date = opfFile.createElement("dc:date")
358 date.appendChild(opfFile.createTextNode(configDictionary["publishingDate"]))
359 opfMeta.appendChild(date)
360
361
362 modified = opfFile.createElement("meta")
363 modified.setAttribute("property", "dcterms:modified")
364 modified.appendChild(opfFile.createTextNode(QDateTime.currentDateTimeUtc().toString(Qt.DateFormat.ISODate)))
365 opfMeta.appendChild(modified)
366
367 if "source" in configDictionary.keys():
368 if len(configDictionary["source"])>0:
369 source = opfFile.createElement("dc:source")
370 source.appendChild(opfFile.createTextNode(configDictionary["source"]))
371 opfMeta.appendChild(source)
372
373 description = opfFile.createElement("dc:description")
374 if "summary" in configDictionary.keys():
375 description.appendChild(opfFile.createTextNode(configDictionary["summary"]))
376 else:
377 description.appendChild(opfFile.createTextNode("There was no summary upon generation of this file."))
378 opfMeta.appendChild(description)
379
380
381
382
383
384 if "publisherName" in configDictionary.keys():
385 publisher = opfFile.createElement("dc:publisher")
386 publisher.appendChild(opfFile.createTextNode(configDictionary["publisherName"]))
387 opfMeta.appendChild(publisher)
388
389
390 if "isbn-number" in configDictionary.keys():
391 isbnnumber = configDictionary["isbn-number"]
392
393 if len(isbnnumber)>0:
394 publishISBN = opfFile.createElement("dc:identifier")
395 publishISBN.appendChild(opfFile.createTextNode(str("urn:isbn:") + isbnnumber))
396 opfMeta.appendChild(publishISBN)
397
398 if "license" in configDictionary.keys():
399
400 if len(configDictionary["license"])>0:
401 rights = opfFile.createElement("dc:rights")
402 rights.appendChild(opfFile.createTextNode(configDictionary["license"]))
403 opfMeta.appendChild(rights)
404
405 """
406 Not handled
407 Relation - This is for whether the work has a relationship with another work.
408 It could be fanart, but also adaptation, an academic work, etc.
409 Coverage - This is for the time/place that the work covers. Typically to determine
410 whether an academic work deals with a certain time period or place.
411 For comics you could use this to mark historical comics, but other than
412 that we'd need a much better ui to define this.
413 """
414
415
416
417
418
419 if "genre" in configDictionary.keys():
420 genreListConf = configDictionary["genre"]
421 if isinstance(configDictionary["genre"], dict):
422 genreListConf = configDictionary["genre"].keys()
423 for g in genreListConf:
424 subject = opfFile.createElement("dc:subject")
425 subject.appendChild(opfFile.createTextNode(g))
426 opfMeta.appendChild(subject)
427 if "characters" in configDictionary.keys():
428 for name in configDictionary["characters"]:
429 char = opfFile.createElement("dc:subject")
430 char.appendChild(opfFile.createTextNode(name))
431 opfMeta.appendChild(char)
432 if "format" in configDictionary.keys():
433 for formatF in configDictionary["format"]:
434 f = opfFile.createElement("dc:subject")
435 f.appendChild(opfFile.createTextNode(formatF))
436 opfMeta.appendChild(f)
437 if "otherKeywords" in configDictionary.keys():
438 for key in configDictionary["otherKeywords"]:
439 word = opfFile.createElement("dc:subject")
440 word.appendChild(opfFile.createTextNode(key))
441 opfMeta.appendChild(word)
442
443
444
445
446 elLayout = opfFile.createElement("meta")
447 elLayout.setAttribute("property", "rendition:layout")
448 elLayout.appendChild(opfFile.createTextNode("pre-paginated"))
449 opfMeta.appendChild(elLayout)
450
451
452 elOrientation = opfFile.createElement("meta")
453 elOrientation.setAttribute("property", "rendition:orientation")
454 elOrientation.appendChild(opfFile.createTextNode("portrait"))
455 opfMeta.appendChild(elOrientation)
456
457 elSpread = opfFile.createElement("meta")
458 elSpread.setAttribute("property", "rendition:spread")
459 elSpread.appendChild(opfFile.createTextNode("landscape"))
460 opfMeta.appendChild(elSpread)
461
462 opfRoot.appendChild(opfMeta)
463
464
465
466 opfManifest = opfFile.createElement("manifest")
467 toc = opfFile.createElement("item")
468 toc.setAttribute("id", "ncx")
469 toc.setAttribute("href", "toc.ncx")
470 toc.setAttribute("media-type", "application/x-dtbncx+xml")
471 opfManifest.appendChild(toc)
472
473 region = opfFile.createElement("item")
474 region.setAttribute("id", "regions")
475 region.setAttribute("href", "region-nav.xhtml")
476 region.setAttribute("media-type", "application/xhtml+xml")
477 region.setAttribute("properties", "data-nav")
478 opfManifest.appendChild(region)
479
480 nav = opfFile.createElement("item")
481 nav.setAttribute("id", "nav")
482 nav.setAttribute("href", "nav.xhtml")
483 nav.setAttribute("media-type", "application/xhtml+xml")
484 nav.setAttribute("properties", "nav")
485 opfManifest.appendChild(nav)
486
487 ids = 0
488 for p in pagesList:
489 item = opfFile.createElement("item")
490 item.setAttribute("id", "img"+str(ids))
491 ids +=1
492 item.setAttribute("href", os.path.relpath(p, str(path)))
493 item.setAttribute("media-type", "image/png")
494 if os.path.basename(p) == os.path.basename(coverpageurl):
495 item.setAttribute("properties", "cover-image")
496 opfManifest.appendChild(item)
497
498
499 ids = 0
500 for p in htmlFiles:
501 item = opfFile.createElement("item")
502 item.setAttribute("id", "p"+str(ids))
503 ids +=1
504 item.setAttribute("href", os.path.relpath(p, str(path)))
505 item.setAttribute("media-type", "application/xhtml+xml")
506 opfManifest.appendChild(item)
507
508
509 opfRoot.appendChild(opfManifest)
510
511
512
513 opfSpine = opfFile.createElement("spine")
514
515 opfSpine.setAttribute("toc", "ncx")
516
517
518 spreadRight = True
519 direction = 0
520 if "readingDirection" in configDictionary.keys():
521 if configDictionary["readingDirection"] == "rightToLeft":
522 opfSpine.setAttribute("page-progression-direction", "rtl")
523 spreadRight = False
524 direction = 1
525 else:
526 opfSpine.setAttribute("page-progression-direction", "ltr")
527
528
529
530 ids = 0
531 for p in htmlFiles:
532 item = opfFile.createElement("itemref")
533 item.setAttribute("idref", "p"+str(ids))
534 ids +=1
535 props = []
536 if p in listofSpreads:
537
538 props.append("rendition:page-spread-center")
539
540
541
542
543 if direction == 0:
544 spreadRight = False
545 else:
546 spreadRight = True
547 else:
548 if spreadRight:
549 props.append("page-spread-right")
550 spreadRight = False
551 else:
552 props.append("page-spread-left")
553 spreadRight = True
554 item.setAttribute("properties", " ".join(props))
555 opfSpine.appendChild(item)
556 opfRoot.appendChild(opfSpine)
557
558
559
560 opfGuide = opfFile.createElement("guide")
561 if coverpagehtml is not None and coverpagehtml.isspace() is False and len(coverpagehtml) > 0:
562 item = opfFile.createElement("reference")
563 item.setAttribute("type", "cover")
564 item.setAttribute("title", "Cover")
565 item.setAttribute("href", coverpagehtml)
566 opfGuide.appendChild(item)
567 opfRoot.appendChild(opfGuide)
568
569 docFile = open(str(Path(path / "content.opf")), 'w', newline="", encoding="utf-8")
570 docFile.write(opfFile.toString(indent=2))
571 docFile.close()
572 return str(Path(path / "content.opf"))
573
574"""
575Write a region navmap file.
576"""
577