git: 1fe029273d1e - main - deskutils/bookworm: Add new port

From: Mateusz Piotrowski <0mp_at_FreeBSD.org>
Date: Fri, 15 Oct 2021 06:12:20 UTC
The branch main has been updated by 0mp:

URL: https://cgit.FreeBSD.org/ports/commit/?id=1fe029273d1ecf6dbf40da326cf8388ffda7a949

commit 1fe029273d1ecf6dbf40da326cf8388ffda7a949
Author:     Miguel Gocobachi <miguel@gocobachi.dev>
AuthorDate: 2021-10-15 05:48:23 +0000
Commit:     Mateusz Piotrowski <0mp@FreeBSD.org>
CommitDate: 2021-10-15 06:00:22 +0000

    deskutils/bookworm: Add new port
    
    Bookworm is a simple, focused eBook reader.
    
    Read the books you love without having to worry about the different
    format complexities like epub, pdf, mobi, cbr, etc. This version
    supports EPUB, PDF and Comics (CBR and CBZ) formats with support for
    more formats to follow soon.
    
    WWW: https://babluboy.github.io/bookworm/
    
    Reported by:    0mp
    Differential Revision:  https://reviews.freebsd.org/D25569
---
 deskutils/Makefile                                 |   1 +
 deskutils/bookworm/Makefile                        |  62 ++++
 deskutils/bookworm/distinfo                        |   3 +
 ...ata_com.github.babluboy.bookworm.appdata.xml.in |  41 +++
 .../patch-data_scripts_mobi__lib_mobi__dict.py     | 135 ++++++++
 .../patch-data_scripts_mobi__lib_mobi__html.py     |  95 ++++++
 .../patch-data_scripts_mobi__lib_mobi__index.py    |  91 ++++++
 .../patch-data_scripts_mobi__lib_mobi__k8proc.py   | 142 +++++++++
 .../patch-data_scripts_mobi__lib_mobi__ncx.py      |  98 ++++++
 .../patch-data_scripts_mobi__lib_mobi__opf.py      |  38 +++
 .../patch-data_scripts_mobi__lib_mobi__unpack.py   | 354 +++++++++++++++++++++
 deskutils/bookworm/files/patch-src_bookworm.vala   |  15 +
 deskutils/bookworm/files/patch-src_pdfReader.vala  |  62 ++++
 deskutils/bookworm/files/patch-src_utils.vala      |  38 +++
 deskutils/bookworm/pkg-descr                       |   7 +
 deskutils/bookworm/pkg-plist                       | 223 +++++++++++++
 16 files changed, 1405 insertions(+)

diff --git a/deskutils/Makefile b/deskutils/Makefile
index f5c6e9860b6e..3aab98e229e1 100644
--- a/deskutils/Makefile
+++ b/deskutils/Makefile
@@ -18,6 +18,7 @@
     SUBDIR += bijiben
     SUBDIR += birdtray
     SUBDIR += bitcollider
+    SUBDIR += bookworm
     SUBDIR += cairo-dock
     SUBDIR += cairo-dock-plugins
     SUBDIR += caja-extensions
diff --git a/deskutils/bookworm/Makefile b/deskutils/bookworm/Makefile
new file mode 100644
index 000000000000..f832ab827aee
--- /dev/null
+++ b/deskutils/bookworm/Makefile
@@ -0,0 +1,62 @@
+PORTNAME=	bookworm
+DISTVERSION=	1.1.2
+CATEGORIES=	deskutils
+
+MAINTAINER=	miguel@gocobachi.dev
+COMMENT=	Simple, focused ebook reader
+
+LICENSE=	GPLv3
+LICENSE_FILE=	${WRKSRC}/COPYING
+
+BUILD_DEPENDS=	curl>0:ftp/curl \
+		html2text>0:textproc/html2text \
+		valac:lang/vala
+LIB_DEPENDS=	libappstream-glib.so:devel/appstream-glib \
+		libgee-0.8.so:devel/libgee \
+		libgranite.so:x11-toolkits/granite \
+		libpoppler-glib.so:graphics/poppler-glib \
+		libsoup-2.4.so:devel/libsoup \
+		libwebkit2gtk-4.0.so:www/webkit2-gtk3 \
+		libxml2.so:textproc/libxml2
+RUN_DEPENDS=	bash:shells/bash \
+		pdftohtml:graphics/poppler-utils \
+		unar:archivers/unarchiver \
+		unzip:archivers/unzip
+
+USES=		desktop-file-utils gettext gnome meson pkgconfig \
+		python:build,run shebangfix sqlite
+USE_GITHUB=	yes
+GH_ACCOUNT=	babluboy
+USE_GNOME=	gdkpixbuf2 glib20 gtk30 libxml2 pango
+
+GLIB_SCHEMAS=	com.github.babluboy.bookworm.gschema.xml
+INSTALLS_ICONS=	yes
+SHEBANG_FILES=	data/scripts/com.github.babluboy.bookworm.search.sh \
+		data/scripts/mobi_lib/mobi_dict.py \
+		data/scripts/mobi_lib/mobi_html.py \
+		data/scripts/mobi_lib/mobi_index.py \
+		data/scripts/mobi_lib/mobi_k8proc.py \
+		data/scripts/mobi_lib/mobi_ncx.py \
+		data/scripts/mobi_lib/mobi_opf.py \
+		data/scripts/mobi_lib/mobi_split.py \
+		data/scripts/mobi_lib/mobi_uncompress.py \
+		data/scripts/mobi_lib/mobi_unpack.py \
+		data/scripts/mobi_lib/mobi_utils.py
+
+MESON_BUILD_DIR=	build
+
+PORTDOCS=	README.md
+
+OPTIONS_DEFINE=	DOCS
+
+post-configure:
+	@${MKDIR} ${WRKSRC}/${MESON_BUILD_DIR}
+
+post-install-DOCS-on:
+	@${MKDIR} ${STAGEDIR}${DOCSDIR}
+	${INSTALL_DATA} ${WRKSRC}/README.md ${STAGEDIR}${DOCSDIR}
+
+do-test:
+	cd ${WRKSRC}/${MESON_BUILD_DIR} && ninja test
+
+.include <bsd.port.mk>
diff --git a/deskutils/bookworm/distinfo b/deskutils/bookworm/distinfo
new file mode 100644
index 000000000000..62681da78331
--- /dev/null
+++ b/deskutils/bookworm/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1593395828
+SHA256 (babluboy-bookworm-1.1.2_GH0.tar.gz) = 6d27e55697debfa08f7cc15805413b74c94c55111cdf2d333b306228eccad824
+SIZE (babluboy-bookworm-1.1.2_GH0.tar.gz) = 2102426
diff --git a/deskutils/bookworm/files/patch-data_com.github.babluboy.bookworm.appdata.xml.in b/deskutils/bookworm/files/patch-data_com.github.babluboy.bookworm.appdata.xml.in
new file mode 100644
index 000000000000..d474e0772145
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_com.github.babluboy.bookworm.appdata.xml.in
@@ -0,0 +1,41 @@
+--- data/com.github.babluboy.bookworm.appdata.xml.in.orig	2019-08-10 18:20:51 UTC
++++ data/com.github.babluboy.bookworm.appdata.xml.in
+@@ -14,10 +14,6 @@
+   </description>
+   <screenshots>
+       <screenshot type="default">
+-          <caption>Bookworm Library View</caption>
+-          <image>https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormLibraryView.png</image>
+-      </screenshot>
+-      <screenshot type="default">
+           <caption>Bookworm Reading View</caption>
+           <image>https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormReadingView.png</image>
+       </screenshot>
+@@ -46,16 +42,6 @@
+           </ul>
+         </description>
+      </release>
+-     <release version="1.0.0" date="2018-02-11">
+-      <description>
+-        <p>Right to Left Reading</p>
+-        <ul>
+-            <li>Support for right-to-left script</li>
+-            <li>A shiny new icon and new cover images</li>
+-            <li>Better support for EPUB table of contents</li>
+-        </ul>
+-      </description>
+-    </release>
+     <release version="1.1.0" date="2018-09-30">
+         <description>
+           <p>This release has some new features, fixes and new translations:</p>
+@@ -72,8 +58,8 @@
+             <li>Some minor CSS compatibility with Juno</li>
+           </ul>
+         </description>
+-     </release>
+-     <release version="1.0.0" date="2018-02-11">
++    </release>
++    <release version="1.0.0" date="2018-02-11">
+       <description>
+         <p>Right to Left Reading</p>
+         <ul>
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__dict.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__dict.py
new file mode 100644
index 000000000000..f5695b3b487d
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__dict.py
@@ -0,0 +1,135 @@
+--- data/scripts/mobi_lib/mobi_dict.py.orig	2021-08-16 04:04:05 UTC
++++ data/scripts/mobi_lib/mobi_dict.py
+@@ -27,37 +27,37 @@ class dictSupport:
+ 
+         decodeInflection = True
+         if metaOrthIndex != 0xFFFFFFFF:
+-            print "Info: Document contains orthographic index, handle as dictionary"
++            print("Info: Document contains orthographic index, handle as dictionary")
+             if metaInflIndex == 0xFFFFFFFF:
+                 decodeInflection = False
+             else:
+                 metaInflIndexData = sect.loadSection(metaInflIndex)
+                 metaIndexCount, = struct.unpack_from('>L', metaInflIndexData, 0x18)
+                 if metaIndexCount != 1:
+-                    print "Error: Dictionary contains multiple inflection index sections, which is not yet supported"
++                    print("Error: Dictionary contains multiple inflection index sections, which is not yet supported")
+                     decodeInflection = False
+                 inflIndexData = sect.loadSection(metaInflIndex + 1)
+                 inflNameData = sect.loadSection(metaInflIndex + 1 + metaIndexCount)
+                 tagSectionStart, = struct.unpack_from('>L', metaInflIndexData, 0x04)
+                 inflectionControlByteCount, inflectionTagTable = readTagSection(tagSectionStart, metaInflIndexData)
+                 if DEBUG_DICT:
+-                    print "inflectionTagTable: %s" % inflectionTagTable
++                    print("inflectionTagTable: %s" % inflectionTagTable)
+                 if self.hasTag(inflectionTagTable, 0x07):
+-                    print "Error: Dictionary uses obsolete inflection rule scheme which is not yet supported"
++                    print("Error: Dictionary uses obsolete inflection rule scheme which is not yet supported")
+                     decodeInflection = False
+ 
+             data = sect.loadSection(metaOrthIndex)
+             tagSectionStart, = struct.unpack_from('>L', data, 0x04)
+             controlByteCount, tagTable = readTagSection(tagSectionStart, data)
+             orthIndexCount, = struct.unpack_from('>L', data, 0x18)
+-            print "orthIndexCount is", orthIndexCount
++            print("orthIndexCount is", orthIndexCount)
+             if DEBUG_DICT:
+-                print "orthTagTable: %s" % tagTable
++                print("orthTagTable: %s" % tagTable)
+             hasEntryLength = self.hasTag(tagTable, 0x02)
+             if not hasEntryLength:
+-                print "Info: Index doesn't contain entry length tags"
++                print("Info: Index doesn't contain entry length tags")
+ 
+-            print "Read dictionary index data"
++            print("Read dictionary index data")
+             for i in range(metaOrthIndex + 1, metaOrthIndex + 1 + orthIndexCount):
+                 data = sect.loadSection(i)
+                 idxtPos, = struct.unpack_from('>L', data, 0x14)
+@@ -145,10 +145,10 @@ class dictSupport:
+ 
+             # Make sure that the required tags are available.
+             if 0x05 not in tagMap:
+-                print "Error: Required tag 0x05 not found in tagMap"
++                print("Error: Required tag 0x05 not found in tagMap")
+                 return ""
+             if 0x1a not in tagMap:
+-                print "Error: Required tag 0x1a not found in tagMap"
++                print("Error: Required tag 0x1a not found in tagMap")
+                 return ""
+ 
+             result += "<idx:infl>"
+@@ -230,7 +230,7 @@ class dictSupport:
+                     totalConsumed += consumed
+                     values.append(data)
+                 if totalConsumed != valueBytes:
+-                    print "Error: Should consume %s bytes, but consumed %s" % (valueBytes, totalConsumed)
++                    print("Error: Should consume %s bytes, but consumed %s" % (valueBytes, totalConsumed))
+             tagHashMap[tag] = values
+ 
+         # Test that all bytes have been processed if endPos is given.
+@@ -238,12 +238,12 @@ class dictSupport:
+             # The last entry might have some zero padding bytes, so complain only if non zero bytes are left.
+             for char in entryData[dataStart:endPos]:
+                 if char != chr(0x00):
+-                    print "Warning: There are unprocessed index bytes left: %s" % toHex(entryData[dataStart:endPos])
++                    print("Warning: There are unprocessed index bytes left: %s" % toHex(entryData[dataStart:endPos]))
+                     if DEBUG_DICT:
+-                        print "controlByteCount: %s" % controlByteCount
+-                        print "tagTable: %s" % tagTable
+-                        print "data: %s" % toHex(entryData[startPos:endPos])
+-                        print "tagHashMap: %s" % tagHashMap
++                        print("controlByteCount: %s" % controlByteCount)
++                        print("tagTable: %s" % tagTable)
++                        print("data: %s" % toHex(entryData[startPos:endPos]))
++                        print("tagHashMap: %s" % tagHashMap)
+                     break
+ 
+         return tagHashMap
+@@ -273,10 +273,10 @@ class dictSupport:
+                 position -= offset
+             elif byte > 0x13:
+                 if mode == -1:
+-                    print "Error: Unexpected first byte %i of inflection rule" % byte
++                    print("Error: Unexpected first byte %i of inflection rule" % byte)
+                     return None
+                 elif position == -1:
+-                    print "Error: Unexpected first byte %i of inflection rule" % byte
++                    print("Error: Unexpected first byte %i of inflection rule" % byte)
+                     return None
+                 else:
+                     if mode == 0x01:
+@@ -292,19 +292,19 @@ class dictSupport:
+                         deleted = byteArray.pop(position)
+                         if deleted != char:
+                             if DEBUG_DICT:
+-                                print "0x03: %s %s %s %s" % (mainEntry, toHex(inflectionRuleData[start:end]), char, deleted)
+-                            print "Error: Delete operation of inflection rule failed"
++                                print("0x03: %s %s %s %s" % (mainEntry, toHex(inflectionRuleData[start:end]), char, deleted))
++                            print("Error: Delete operation of inflection rule failed")
+                             return None
+                     elif mode == 0x04:
+                         # Delete at word start
+                         deleted = byteArray.pop(position)
+                         if deleted != char:
+                             if DEBUG_DICT:
+-                                print "0x03: %s %s %s %s" % (mainEntry, toHex(inflectionRuleData[start:end]), char, deleted)
+-                            print "Error: Delete operation of inflection rule failed"
++                                print("0x03: %s %s %s %s" % (mainEntry, toHex(inflectionRuleData[start:end]), char, deleted))
++                            print("Error: Delete operation of inflection rule failed")
+                             return None
+                     else:
+-                        print "Error: Inflection rule mode %x is not implemented" % mode
++                        print("Error: Inflection rule mode %x is not implemented" % mode)
+                         return None
+             elif byte == 0x01:
+                 # Insert at word start
+@@ -327,7 +327,7 @@ class dictSupport:
+                     position = 0
+                 mode = byte
+             else:
+-                print "Error: Inflection rule mode %x is not implemented" % byte
++                print("Error: Inflection rule mode %x is not implemented" % byte)
+                 return None
+         return byteArray.tostring()
+ 
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__html.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__html.py
new file mode 100644
index 000000000000..e02d8e2fcfce
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__html.py
@@ -0,0 +1,95 @@
+--- data/scripts/mobi_lib/mobi_html.py.orig	2021-08-16 04:19:38 UTC
++++ data/scripts/mobi_lib/mobi_html.py
+@@ -23,7 +23,7 @@ class HTMLProcessor:
+     def findAnchors(self, rawtext, indx_data, positionMap):
+         # process the raw text
+         # find anchors...
+-        print "Find link anchors"
++        print("Find link anchors")
+         link_pattern = re.compile(r'''<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>''', re.IGNORECASE)
+         # TEST NCX: merge in filepos from indx
+         pos_links = [int(m.group(1)) for m in link_pattern.finditer(rawtext)]
+@@ -38,7 +38,7 @@ class HTMLProcessor:
+                 positionMap[position] = '<a id="filepos%d" />' % position
+ 
+         # apply dictionary metadata and anchors
+-        print "Insert data into html"
++        print("Insert data into html")
+         pos = 0
+         lastPos = len(rawtext)
+         dataList = []
+@@ -63,7 +63,7 @@ class HTMLProcessor:
+         metadata = self.metadata
+ 
+         # put in the hrefs
+-        print "Insert hrefs into html"
++        print("Insert hrefs into html")
+         # Two different regex search and replace routines.
+         # Best results are with the second so far IMO (DiapDealer).
+ 
+@@ -73,11 +73,11 @@ class HTMLProcessor:
+         srctext = link_pattern.sub(r'''<a href="#filepos\1"\2>''', srctext)
+ 
+         # remove empty anchors
+-        print "Remove empty anchors from html"
++        print("Remove empty anchors from html")
+         srctext = re.sub(r"<a/>",r"", srctext)
+ 
+         # convert image references
+-        print "Insert image references into html"
++        print("Insert image references into html")
+         # split string into image tag pieces and other pieces
+         image_pattern = re.compile(r'''(<img.*?>)''', re.IGNORECASE)
+         image_index_pattern = re.compile(r'''recindex=['"]{0,1}([0-9]+)['"]{0,1}''', re.IGNORECASE)
+@@ -91,7 +91,7 @@ class HTMLProcessor:
+                 imageNumber = int(m.group(1))
+                 imageName = imgnames[imageNumber-1]
+                 if imageName is None:
+-                    print "Error: Referenced image %s was not recognized as a valid image" % imageNumber
++                    print("Error: Referenced image %s was not recognized as a valid image" % imageNumber)
+                 else:
+                     replacement = 'src="images/' + imageName + '"'
+                     tag = re.sub(image_index_pattern, replacement, tag, 1)
+@@ -128,7 +128,7 @@ class XHTMLK8Processor:
+         posfid_index_pattern = re.compile(r'''['"]kindle:pos:fid:([0-9|A-V]+):off:([0-9|A-V]+).*?["']''')
+ 
+         parts = []
+-        print "Building proper xhtml for each file"
++        print("Building proper xhtml for each file")
+         for i in xrange(self.k8proc.getNumberOfParts()):
+             part = self.k8proc.getPart(i)
+             [partnum, dir, filename, beg, end, aidtext] = self.k8proc.getPartInfo(i)
+@@ -227,7 +227,7 @@ class XHTMLK8Processor:
+                             self.used[imageName] = 'used'
+                             tag = re.sub(img_index_pattern, replacement, tag, 1)
+                         else:
+-                            print "Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag)
++                            print("Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag))
+                     srcpieces[j] = tag
+             flowpart = "".join(srcpieces)
+ 
+@@ -246,13 +246,13 @@ class XHTMLK8Processor:
+                         self.used[imageName] = 'used'
+                         tag = re.sub(url_img_index_pattern, replacement, tag, 1)
+                     else:
+-                        print "Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag)
++                        print("Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag))
+                 # process links to fonts
+                 for m in re.finditer(font_index_pattern, tag):
+                     fontNumber = fromBase32(m.group(1))
+                     fontName = self.imgnames[fontNumber-1]
+                     if fontName is None:
+-                        print "Error: Referenced font %s was not recognized as a valid font in %s" % (fontNumber, tag)
++                        print("Error: Referenced font %s was not recognized as a valid font in %s" % (fontNumber, tag))
+                     else:
+                         replacement = '"../Fonts/' + fontName + '"'
+                         tag = re.sub(font_index_pattern, replacement, tag, 1)
+@@ -345,7 +345,7 @@ class XHTMLK8Processor:
+                             self.used[imageName] = 'used'
+                             tag = re.sub(img_index_pattern, replacement, tag, 1)
+                         else:
+-                            print "Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag)
++                            print("Error: Referenced image %s was not recognized as a valid image in %s" % (imageNumber, tag))
+                     srcpieces[j] = tag
+             part = "".join(srcpieces)
+             # store away modified version
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__index.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__index.py
new file mode 100644
index 000000000000..a5d8eb193918
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__index.py
@@ -0,0 +1,91 @@
+--- data/scripts/mobi_lib/mobi_index.py.orig	2021-08-16 04:22:14 UTC
++++ data/scripts/mobi_lib/mobi_index.py
+@@ -32,15 +32,15 @@ class MobiIndex:
+             tagSectionStart = idxhdr['len']
+             controlByteCount, tagTable = readTagSection(tagSectionStart, data)
+             if DEBUG:
+-                print "IndexCount is", IndexCount
+-                print "TagTable: %s" % tagTable
++                print("IndexCount is", IndexCount)
++                print("TagTable: %s" % tagTable)
+             for i in range(idx + 1, idx + 1 + IndexCount):
+                 data = sect.loadSection(i)
+                 hdrinfo = self.parseINDXHeader(data)
+                 idxtPos = hdrinfo['start']
+                 entryCount = hdrinfo['count']
+                 if DEBUG:
+-                    print idxtPos, entryCount
++                    print(idxtPos, entryCount)
+                 # loop through to build up the IDXT position starts
+                 idxPositions = []
+                 for j in range(entryCount):
+@@ -57,8 +57,8 @@ class MobiIndex:
+                     tagMap = self.getTagMap(controlByteCount, tagTable, data, startPos+1+textLength, endPos)
+                     outtbl.append([text, tagMap])
+                     if DEBUG:
+-                        print tagMap
+-                        print text
++                        print(tagMap)
++                        print(text)
+         return outtbl, ctoc_text
+ 
+     def getTagMap(self, controlByteCount, tagTable, entryData, startPos, endPos):
+@@ -118,19 +118,19 @@ class MobiIndex:
+                     totalConsumed += consumed
+                     values.append(data)
+                 if totalConsumed != valueBytes:
+-                    print "Error: Should consume %s bytes, but consumed %s" % (valueBytes, totalConsumed)
++                    print("Error: Should consume %s bytes, but consumed %s" % (valueBytes, totalConsumed))
+             tagHashMap[tag] = values
+         # Test that all bytes have been processed if endPos is given.
+         if endPos is not None and dataStart != endPos:
+             # The last entry might have some zero padding bytes, so complain only if non zero bytes are left.
+             for char in entryData[dataStart:endPos]:
+                 if char != chr(0x00):
+-                    print "Warning: There are unprocessed index bytes left: %s" % toHex(entryData[dataStart:endPos])
++                    print("Warning: There are unprocessed index bytes left: %s" % toHex(entryData[dataStart:endPos]))
+                     if DEBUG:
+-                        print "controlByteCount: %s" % controlByteCount
+-                        print "tagTable: %s" % tagTable
+-                        print "data: %s" % toHex(entryData[startPos:endPos])
+-                        print "tagHashMap: %s" % tagHashMap
++                        print("controlByteCount: %s" % controlByteCount)
++                        print("tagTable: %s" % tagTable)
++                        print("data: %s" % toHex(entryData[startPos:endPos]))
++                        print("tagHashMap: %s" % tagHashMap)
+                     break
+ 
+         return tagHashMap
+@@ -154,7 +154,7 @@ class MobiIndex:
+     def parseINDXHeader(self, data):
+         "read INDX header"
+         if not data[:4] == 'INDX':
+-            print "Warning: index section is not INDX"
++            print("Warning: index section is not INDX")
+             return False
+         words = (
+                 'len', 'nul1', 'type', 'gen', 'start', 'count', 'code',
+@@ -166,10 +166,10 @@ class MobiIndex:
+         for n in range(num):
+             header[words[n]] = values[n]
+         if DEBUG:
+-            print "parsed INDX header:"
++            print("parsed INDX header:")
+             for n in words:
+-                print n, "%X" % header[n],
+-            print
++                print(n, "%X" % header[n])
++            print("")
+         return header
+ 
+     def readCTOC(self, txtdata):
+@@ -187,7 +187,7 @@ class MobiIndex:
+             name = txtdata[offset:offset+ilen]
+             offset += ilen
+             if DEBUG:
+-                print "name length is ", ilen
+-                print idx_offs, name
++                print("name length is ", ilen)
++                print(idx_offs, name)
+             ctoc_data[idx_offs] = name
+         return ctoc_data
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__k8proc.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__k8proc.py
new file mode 100644
index 000000000000..3c198a5d1216
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__k8proc.py
@@ -0,0 +1,142 @@
+--- data/scripts/mobi_lib/mobi_k8proc.py.orig	2021-08-16 04:24:52 UTC
++++ data/scripts/mobi_lib/mobi_k8proc.py
+@@ -33,11 +33,11 @@ class K8Processor:
+                 sections = header[0x0c:]
+                 self.fdsttbl = struct.unpack_from('>%dL' % (num_sections*2), sections, 0)[::2] + (0xfffffff, )
+             else:
+-                print "Error: K8 Mobi with Missing FDST info"
++                print("Error: K8 Mobi with Missing FDST info")
+         if self.DEBUG:
+-            print "\nFDST Section Map:  %d entries" % len(self.fdsttbl)
++            print("\nFDST Section Map:  %d entries" % len(self.fdsttbl))
+             for j in xrange(len(self.fdsttbl)):
+-                print "  %d - %0x" % (j, self.fdsttbl[j])
++                print("  %d - %0x" % (j, self.fdsttbl[j]))
+ 
+         # read/process skeleton index info to create the skeleton table
+         skeltbl = []
+@@ -50,10 +50,10 @@ class K8Processor:
+                 fileptr += 1
+         self.skeltbl = skeltbl
+         if self.DEBUG:
+-            print "\nSkel Table:  %d entries" % len(self.skeltbl)
+-            print "table: filenum, skeleton name, div tbl record count, start position, length"
++            print("\nSkel Table:  %d entries" % len(self.skeltbl))
++            print("table: filenum, skeleton name, div tbl record count, start position, length")
+             for j in xrange(len(self.skeltbl)):
+-                print self.skeltbl[j]
++                print(self.skeltbl[j])
+ 
+         # read/process the div index to create to <div> (and <p>) table
+         divtbl = []
+@@ -66,10 +66,10 @@ class K8Processor:
+                 divtbl.append([int(text), ctocdata, tagMap[3][0], tagMap[4][0], tagMap[6][0], tagMap[6][1]])
+         self.divtbl = divtbl
+         if self.DEBUG:
+-            print "\nDiv (Fragment) Table: %d entries" % len(self.divtbl)
+-            print "table: file position, link id text, file num, sequence number, start position, length"
++            print("\nDiv (Fragment) Table: %d entries" % len(self.divtbl))
++            print("table: file position, link id text, file num, sequence number, start position, length")
+             for j in xrange(len(self.divtbl)):
+-                print self.divtbl[j]
++                print(self.divtbl[j])
+ 
+         # read / process other index <guide> element of opf
+         othtbl = []
+@@ -88,10 +88,10 @@ class K8Processor:
+                 othtbl.append([ref_type, ref_title, fileno])
+         self.othtbl = othtbl
+         if self.DEBUG:
+-            print "\nOther (Guide) Table: %d entries" % len(self.othtbl)
+-            print "table: ref_type, ref_title, divtbl entry number"
++            print("\nOther (Guide) Table: %d entries" % len(self.othtbl))
++            print("table: ref_type, ref_title, divtbl entry number")
+             for j in xrange(len(self.othtbl)):
+-                print self.othtbl[j]
++                print(self.othtbl[j])
+ 
+ 
+     def buildParts(self, rawML):
+@@ -103,7 +103,7 @@ class K8Processor:
+             if end == 0xffffffff:
+                 end = len(rawML)
+                 if self.DEBUG:
+-                    print "splitting rawml starting at %d and ending at %d into flow piece %d" % (start, end, j)
++                    print("splitting rawml starting at %d and ending at %d into flow piece %d" % (start, end, j))
+             self.flows.append(rawML[start:end])
+ 
+         # the first piece represents the xhtml text
+@@ -114,7 +114,7 @@ class K8Processor:
+         # *without* destroying any file position information needed for later href processing
+         # and create final list of file separation start: stop points and etc in partinfo
+         if self.DEBUG:
+-            print "\nRebuilding flow piece 0: the main body of the ebook"
++            print("\nRebuilding flow piece 0: the main body of the ebook")
+         self.parts = []
+         self.partinfo = []
+         divptr = 0
+@@ -125,8 +125,8 @@ class K8Processor:
+             for i in range(divcnt):
+                 [insertpos, idtext, filenum, seqnum, startpos, length] = self.divtbl[divptr]
+                 if self.DEBUG:
+-                    print "    moving div/frag %d starting at %d of length %d" % (divptr, startpos, length)
+-                    print "        inside of skeleton number %d at postion %d" %  (skelnum, insertpos)
++                    print("    moving div/frag %d starting at %d of length %d" % (divptr, startpos, length))
++                    print("        inside of skeleton number %d at postion %d" %  (skelnum, insertpos))
+                 if i == 0:
+                     aidtext = idtext[12:-2]
+                     filename = 'part%04d.xhtml' % filenum
+@@ -198,14 +198,14 @@ class K8Processor:
+             self.flowinfo.append([type, format, dir, fname])
+         
+         if self.DEBUG:
+-            print "\nFlow Map:  %d entries" % len(self.flowinfo)
++            print("\nFlow Map:  %d entries" % len(self.flowinfo))
+             for fi in self.flowinfo:
+-                print fi
+-            print "\n"
++                print(fi)
++            print("\n")
+ 
+-            print "\nXHTML File Part Position Information: %d entries" % len(self.partinfo)
++            print("\nXHTML File Part Position Information: %d entries" % len(self.partinfo))
+             for pi in self.partinfo:
+-                print pi
++                print(pi)
+ 
+         if False:  # self.DEBUG:
+             # dump all of the locations of the aid tags used in TEXT
+@@ -214,12 +214,12 @@ class K8Processor:
+             #    [^>]* means match any amount of chars except for  '>' char
+             #    [^'"] match any amount of chars except for the quote character
+             #    \s* means match any amount of whitespace
+-            print "\npositions of all aid= pieces"
++            print("\npositions of all aid= pieces")
+             id_pattern = re.compile(r'''<[^>]*\said\s*=\s*['"]([^'"]*)['"][^>]*>''',re.IGNORECASE)
+             for m in re.finditer(id_pattern, rawML):
+-                print "%0x %s %0x" % (m.start(), m.group(1), fromBase32(m.group(1)))
++                print("%0x %s %0x" % (m.start(), m.group(1), fromBase32(m.group(1))))
+                 [filename, partnum, start, end] = self.getFileInfo(m.start())
+-                print "   in  %d %0x %0x" % (partnum, start, end)
++                print("   in  %d %0x %0x" % (partnum, start, end))
+ 
+         return
+ 
+@@ -300,7 +300,7 @@ class K8Processor:
+         n = len(idtbl)
+         if n == 0:
+             if self.DEBUG:
+-                print "Found no id in the textblock, link must be to top of file"
++                print("Found no id in the textblock, link must be to top of file")
+             return ''
+         # if npos is before first id= inside a tag, return the first
+         if npos < idtbl[0][0] :
+@@ -315,7 +315,7 @@ class K8Processor:
+                 tgt = r-1
+                 break
+         if self.DEBUG:
+-            print pos, npos, idtbl[tgt]
++            print(pos, npos, idtbl[tgt])
+         return idtbl[tgt][1]
+ 
+ 
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__ncx.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__ncx.py
new file mode 100644
index 000000000000..efc028ed0cd2
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__ncx.py
@@ -0,0 +1,98 @@
+--- data/scripts/mobi_lib/mobi_ncx.py.orig	2021-08-16 04:25:11 UTC
++++ data/scripts/mobi_lib/mobi_ncx.py
+@@ -34,8 +34,8 @@ class ncxExtract:
+         if self.ncxidx != 0xffffffff:
+             outtbl, ctoc_text = self.mi.getIndexData(self.ncxidx)
+             if DEBUG_NCX:
+-                print ctoc_text
+-                print outtbl
++                print(ctoc_text)
++                print(outtbl)
+             num = 0
+             for [text, tagMap] in outtbl:
+                 tmp = {
+@@ -68,16 +68,16 @@ class ncxExtract:
+                             tmp['kind'] = ctoc_text.get(fieldvalue, 'Unknown Kind')
+                 indx_data.append(tmp)
+                 if DEBUG_NCX:
+-                    print "record number: ", num
+-                    print "name: ", tmp['name'],
+-                    print "position", tmp['pos']," length: ", tmp['len']
+-                    print "text: ", tmp['text']
+-                    print "kind: ", tmp['kind']
+-                    print "heading level: ", tmp['hlvl']
+-                    print "parent:", tmp['parent']
+-                    print "first child: ",tmp['child1']," last child: ", tmp['childn']
+-                    print "pos_fid is ", tmp['pos_fid']
+-                    print "\n\n"
++                    print("record number: ", num)
++                    print("name: ", tmp['name'])
++                    print("position", tmp['pos']," length: ", tmp['len'])
++                    print("text: ", tmp['text'])
++                    print("kind: ", tmp['kind'])
++                    print("heading level: ", tmp['hlvl'])
++                    print("parent:", tmp['parent'])
++                    print("first child: ",tmp['child1']," last child: ", tmp['childn'])
++                    print("pos_fid is ", tmp['pos_fid'])
++                    print("\n\n")
+                 num += 1
+             num += 1
+         self.indx_data = indx_data
+@@ -118,10 +118,10 @@ class ncxExtract:
+         #recursive part
+         def recursINDX(max_lvl=0, num=0, lvl=0, start=-1, end=-1):
+             if start>len(indx_data) or end>len(indx_data):
+-                print "Warning: missing INDX child entries", start, end, len(indx_data)
++                print("Warning: missing INDX child entries", start, end, len(indx_data))
+                 return ''
+             if DEBUG_NCX:
+-                print "recursINDX lvl %d from %d to %d" % (lvl, start, end)
++                print("recursINDX lvl %d from %d to %d" % (lvl, start, end))
+             xml = ''
+             if start <= 0:
+                 start = 0
+@@ -155,13 +155,13 @@ class ncxExtract:
+         header = ncx_header % (ident, max_lvl + 1, title)
+         ncx =  header + body + ncx_footer
+         if not len(indx_data) == num:
+-            print "Warning: different number of entries in NCX", len(indx_data), num
++            print("Warning: different number of entries in NCX", len(indx_data), num)
+         return ncx
+ 
+     def writeNCX(self, metadata):
+         # build the xml
+         self.isNCX = True
+-        print "Write ncx"
++        print("Write ncx")
+         htmlname = os.path.basename(self.files.outbase)
+         htmlname += '.html'
+         xml = self.buildNCX(htmlname, metadata['Title'][0], metadata['UniqueID'][0])
+@@ -202,10 +202,10 @@ class ncxExtract:
+         #recursive part
+         def recursINDX(max_lvl=0, num=0, lvl=0, start=-1, end=-1):
+             if start>len(indx_data) or end>len(indx_data):
+-                print "Warning: missing INDX child entries", start, end, len(indx_data)
++                print("Warning: missing INDX child entries", start, end, len(indx_data))
+                 return ''
+             if DEBUG_NCX:
+-                print "recursINDX lvl %d from %d to %d" % (lvl, start, end)
++                print("recursINDX lvl %d from %d to %d" % (lvl, start, end))
+             xml = ''
+             if start <= 0:
+                 start = 0
+@@ -244,13 +244,13 @@ class ncxExtract:
+         header = ncx_header % (ident, max_lvl + 1, title)
+         ncx =  header + body + ncx_footer
+         if not len(indx_data) == num:
+-            print "Warning: different number of entries in NCX", len(indx_data), num
++            print("Warning: different number of entries in NCX", len(indx_data), num)
+         return ncx
+ 
+     def writeK8NCX(self, ncx_data, metadata):
+         # build the xml
+         self.isNCX = True
+-        print "Write K8 ncx"
++        print("Write K8 ncx")
+         xml = self.buildK8NCX(ncx_data, metadata['Title'][0], metadata['UniqueID'][0])
+         bname = 'toc.ncx'
+         ncxname = os.path.join(self.files.k8oebps,bname)
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__opf.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__opf.py
new file mode 100644
index 000000000000..cd27f3646c95
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__opf.py
@@ -0,0 +1,38 @@
+--- data/scripts/mobi_lib/mobi_opf.py.orig	2021-08-16 04:25:19 UTC
++++ data/scripts/mobi_lib/mobi_opf.py
+@@ -21,7 +21,7 @@ class OPFProcessor:
+ 
+     def writeOPF(self, has_obfuscated_fonts=False):
+         # write out the metadata as an OEB 1.0 OPF file
+-        print "Write opf"
++        print("Write opf")
+         metadata = self.metadata
+ 
+         META_TAGS = ['Drm Server Id', 'Drm Commerce Id', 'Drm Ebookbase Book Id', 'ASIN', 'ThumbOffset', 'Fake Cover',
+@@ -100,7 +100,7 @@ class OPFProcessor:
+             imageNumber = int(metadata['CoverOffset'][0])
+             self.covername = self.imgnames[imageNumber]
+             if self.covername is None:
+-                print "Error: Cover image %s was not recognized as a valid image" % imageNumber
++                print("Error: Cover image %s was not recognized as a valid image" % imageNumber)
+             else:
+                 if self.isK8:
+                     data.append('<meta name="cover" content="cover_img" />\n')
+@@ -126,7 +126,7 @@ class OPFProcessor:
+             priceList = metadata['Price']
+             currencyList = metadata['Currency']
+             if len(priceList) != len(currencyList):
+-                print "Error: found %s price entries, but %s currency entries."
++                print("Error: found %s price entries, but %s currency entries.")
+             else:
+                 for i in range(len(priceList)):
+                     data.append('<SRP Currency="'+currencyList[i]+'">'+priceList[i]+'</SRP>\n')
+@@ -137,7 +137,7 @@ class OPFProcessor:
+             imageNumber = int(metadata['ThumbOffset'][0])
+             imageName = self.imgnames[imageNumber]
+             if imageName is None:
+-                print "Error: Cover Thumbnail image %s was not recognized as a valid image" % imageNumber
++                print("Error: Cover Thumbnail image %s was not recognized as a valid image" % imageNumber)
+             else:
+                 if self.isK8:
+                     data.append('<meta name="Cover ThumbNail Image" content="'+ 'Images/'+imageName+'" />\n')
diff --git a/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__unpack.py b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__unpack.py
new file mode 100644
index 000000000000..dd3ca576da73
--- /dev/null
+++ b/deskutils/bookworm/files/patch-data_scripts_mobi__lib_mobi__unpack.py
@@ -0,0 +1,354 @@
+--- data/scripts/mobi_lib/mobi_unpack.py.orig	2021-08-16 04:42:50 UTC
++++ data/scripts/mobi_lib/mobi_unpack.py
+@@ -256,7 +256,7 @@ class MobiHeader:
+         self.header = self.sect.loadSection(self.start)
+         self.records, = struct.unpack_from('>H', self.header, 0x8)
+         self.length, self.type, self.codepage, self.unique_id, self.version = struct.unpack('>LLLLL', self.header[20:40])
+-        print "Mobi Version: ", self.version
++        print("Mobi Version: ", self.version)
+ 
+         # codec
+         self.codec = 'windows-1252'
+@@ -266,18 +266,18 @@ class MobiHeader:
+         }
+         if self.codepage in codec_map.keys():
+             self.codec = codec_map[self.codepage]
+-        print "Codec: ", self.codec
++        print("Codec: ", self.codec)
+ 
+         # title
+         toff, tlen = struct.unpack('>II', self.header[0x54:0x5c])
+         tend = toff + tlen
+         self.title=self.header[toff:tend]
+-        print "Title: ", self.title
++        print("Title: ", self.title)
+ 
+         # set up for decompression/unpacking
+         compression, = struct.unpack_from('>H', self.header, 0x0)
+         if compression == 0x4448:
+-            print "Huffdic compression"
++            print("Huffdic compression")
+             reader = HuffcdicReader()
+             huffoff, huffnum = struct.unpack_from('>LL', self.header, 0x70)
+             huffoff = huffoff + self.start
+@@ -286,10 +286,10 @@ class MobiHeader:
+                 reader.loadCdic(self.sect.loadSection(huffoff+i))
+             self.unpack = reader.unpack
+         elif compression == 2:
+-            print "Palmdoc compression"
++            print("Palmdoc compression")
+             self.unpack = PalmdocReader().unpack
+         elif compression == 1:
+-            print "No compression"
++            print("No compression")
+             self.unpack = UncompressedReader().unpack
+         else:
+             raise unpackException('invalid compression type: 0x%4x' % compression)
+@@ -376,14 +376,14 @@ class MobiHeader:
+                 self.fdst += self.start
+ 
+         if DEBUG:
+-            print "firstaddl %0x" % self.firstaddl
+-            print "ncxidx %0x" % self.ncxidx
+-            print "exth flags %0x" % exth_flag
++            print("firstaddl %0x" % self.firstaddl)
++            print("ncxidx %0x" % self.ncxidx)
++            print("exth flags %0x" % exth_flag)
+             if self.version == 8 or self.start != 0:
+-                print "skelidx %0x" % self.skelidx
+-                print "dividx %0x" % self.dividx
+-                print "othidx %0x" % self.othidx
+-                print "fdst %0x" % self.fdst
++                print("skelidx %0x" % self.skelidx)
++                print("dividx %0x" % self.dividx)
++                print("othidx %0x" % self.othidx)
++                print("fdst %0x" % self.fdst)
+ 
+         # NOTE: See DumpMobiHeader.py for a complete set of header fields
+ 
+@@ -464,7 +464,7 @@ class MobiHeader:
+                         trailers += 1
+                     flags = flags >> 1
+         # get raw mobi markup languge
+-        print "Unpack raw markup language"
++        print("Unpack raw markup language")
+         dataList = []
+         # offset = 0
+         for i in xrange(1, self.records+1):
+@@ -542,7 +542,7 @@ class MobiHeader:
+             else:
+                 metadata[name].append(value)
+                 if DEBUG:
+-                    print "multiple values: metadata[%s]=%s" % (name, metadata[name])
++                    print("multiple values: metadata[%s]=%s" % (name, metadata[name]))
+         _length, num_items = struct.unpack('>LL', extheader[4:12])
+         extheader = extheader[12:]
+         pos = 0
+@@ -564,12 +564,12 @@ class MobiHeader:
+                     value, = struct.unpack('>L',content)
+                     addValue(name, str(value))
+                 else:
+-                    print "Error: Value for %s has unexpected size of %s" % (name, size)
++                    print("Error: Value for %s has unexpected size of %s" % (name, size))
+             elif id in id_map_hexstrings.keys():
+                 name = id_map_hexstrings[id]
+                 addValue(name, content.encode('hex'))
+             else:
+-                print "Warning: Unknown metadata with id %s found" % id
++                print("Warning: Unknown metadata with id %s found" % id)
+                 name = str(id) + ' (hex)'
+                 addValue(name, content.encode('hex'))
+             pos += size
+@@ -600,11 +600,11 @@ def process_all_mobi_headers(files, sect, mhlst, K8Bou
+     for mh in mhlst:
+ 
+         if mh.isK8():
+-            print "\n\nProcessing K8 format Ebook ..."
++            print("\n\nProcessing K8 format Ebook ...")
+         elif mh.isPrintReplica():
+-            print "\nProcessing PrintReplica (.azw4) format Ebook ..."
++            print("\nProcessing PrintReplica (.azw4) format Ebook ...")
+         else:
+-            print "\nProcessing Mobi format Ebook ..."
++            print("\nProcessing Mobi format Ebook ...")
+ 
+         if DEBUG:
+             # write out raw mobi header data
+@@ -624,8 +624,8 @@ def process_all_mobi_headers(files, sect, mhlst, K8Bou
+         metadata['Codec'] = [mh.codec]
+         metadata['UniqueID'] = [str(mh.unique_id)]
+         if DEBUG:
+-            print "MetaData from EXTH"
+-            print metadata
++            print("MetaData from EXTH")
++            print(metadata)
+ 
+         # save the raw markup language
+         rawML = mh.getRawML()
+@@ -643,12 +643,12 @@ def process_all_mobi_headers(files, sect, mhlst, K8Bou
+ 
+         # process additional sections that represent images, resources, fonts, and etc
+         # build up a list of image names to use to postprocess the rawml
+-        print "Unpacking images, resources, fonts, etc"
++        print("Unpacking images, resources, fonts, etc")
+         firstaddl = mh.getfirstAddl()
+         if DEBUG:
+-            print "firstaddl is ", firstaddl
+-            print "num_sections is ", sect.num_sections
+-            print "K8Boundary is ", K8Boundary
++            print("firstaddl is ", firstaddl)
++            print("num_sections is ", sect.num_sections)
++            print("K8Boundary is ", K8Boundary)
+         beg = firstaddl
+         end = sect.num_sections
+         if firstaddl < K8Boundary:
+@@ -656,12 +656,12 @@ def process_all_mobi_headers(files, sect, mhlst, K8Bou
+         obfuscate_data = []
+         for i in xrange(beg, end):
+             if DEBUG:
+-                print "Section is ", i
++                print("Section is ", i)
+             data = sect.loadSection(i)
+             type = data[0:4]
+             if type in ["FLIS", "FCIS", "FDST", "DATP"]:
+                 if DEBUG:
+-                    print 'First 4 bytes: %s' % toHex(data[0:4])
++                    print('First 4 bytes: %s' % toHex(data[0:4]))
+                     fname = "%05d" % (1+i-beg)
+                     fname = type + fname
+                     if mh.isK8():
+@@ -669,13 +669,13 @@ def process_all_mobi_headers(files, sect, mhlst, K8Bou
+                     fname += '.dat'
+                     outname= os.path.join(files.outdir, fname)
+                     file(outname, 'wb').write(data)
+-                    print "Skipping ", type, " section"
++                    print("Skipping ", type, " section")
+                 imgnames.append(None)
+                 continue
+             elif type == "SRCS":
*** 561 LINES SKIPPED ***