230def write_opf_file(path, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml, listofSpreads):
231
232
233
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
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
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
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
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
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
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
382
383
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
417
418
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
445
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
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
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")
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")
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
513
514 opfSpine = opfFile.createElement("spine")
515
516 opfSpine.setAttribute("toc", "ncx")
517
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
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
539 props.append("rendition:page-spread-center")
540
541
542
543
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
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