1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 5# Copyright (c) 2015-2020 Kevin B. Hendricks, and Doug Massay 6# Copyright (c) 2014 Kevin B. Hendricks, John Schember, and Doug Massay 7# All rights reserved. 8# 9# Redistribution and use in source and binary forms, with or without modification, 10# are permitted provided that the following conditions are met: 11# 12# 1. Redistributions of source code must retain the above copyright notice, this list of 13# conditions and the following disclaimer. 14# 15# 2. Redistributions in binary form must reproduce the above copyright notice, this list 16# of conditions and the following disclaimer in the documentation and/or other materials 17# provided with the distribution. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 20# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 22# SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 27# WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29from quickparser import QuickXHTMLParser 30from preferences import JSONPrefs 31from pluginhunspell import HunspellChecker 32 33class ContainerException(Exception): 34 pass 35 36class BookContainer(object): 37 38 def __init__(self, wrapper, debug=False): 39 self._debug = debug 40 self._w = wrapper 41 self.qp = QuickXHTMLParser() 42 self.hspell = HunspellChecker(wrapper.get_hunspell_path()) 43 self.dictionary_dirs = wrapper.get_dictionary_dirs() 44 self._prefs_store = JSONPrefs(wrapper.plugin_dir, wrapper.plugin_name) 45 46 def getPrefs(self): 47 return self._prefs_store 48 49 def savePrefs(self, user_copy): 50 self._prefs_store = user_copy 51 self._prefs_store._commit() 52 53 def launcher_version(self): 54 return self._w.getversion() 55 56 def epub_version(self): 57 return self._w.getepubversion() 58 59 def epub_is_standard(self): 60 return self._w.epub_is_standard() 61 62 @property 63 def sigil_ui_lang(self): 64 if self._w.sigil_ui_lang is None: 65 return 'en' 66 return self._w.sigil_ui_lang 67 68 @property 69 def sigil_spellcheck_lang(self): 70 if self._w.sigil_spellcheck_lang is None: 71 return 'en_US' 72 return self._w.sigil_spellcheck_lang 73 74# OPF Acess and Manipulation Routines 75 76# toc and pagemap access routines 77 78 def gettocid(self): 79 return self._w.gettocid() 80 81 def getpagemapid(self): 82 return self._w.getpagemapid() 83 84# nav access routines 85 86 def getnavid(self): 87 return self._w.getnavid() 88 89# spine get/set and access routines 90 91 def getspine(self): 92 # spine is an ordered list of tuples (id, linear) 93 return self._w.getspine() 94 95 def setspine(self, new_spine): 96 # new_spine must be an ordered list of tuples (id, linear) 97 self._w.setspine(new_spine) 98 99 # New for epub3 100 def getspine_epub3(self): 101 # spine is an ordered list of tuples (id, linear, properties) 102 return self._w.getspine_epub3() 103 104 # New for epub3 105 def setspine_epub3(self, new_spine): 106 # new_spine must be an ordered list of tuples (id, linear, properties (or None)) 107 self._w.setspine_epub3(new_spine) 108 109 # Modified for epub3 110 # Note: for prepend, set pos = 0 111 # for append, set pos = -1 or pos >= current length of spine 112 def spine_insert_before(self, pos, spid, linear, properties=None): 113 self._w.spine_insert_before(pos, spid, linear, properties) 114 115 def getspine_ppd(self): 116 # spine_ppd is utf-8 string of page direction (rtl, ltr, None) 117 return self._w.getspine_ppd() 118 119 def setspine_ppd(self, ppd): 120 # new pagedirection string 121 self._w.setspine_ppd(ppd) 122 123 # New for epub3 124 def setspine_idref_epub3_attributes(self, idref, linear, properties): 125 self._w.setspine_idref_attributes(idref, linear, properties) 126 127 128# guide get/set 129 130 def getguide(self): 131 # guide is an ordered list of tuples (type, title, href) 132 return self._w.guide 133 134 def setguide(self, new_guide): 135 # new_guide must be an ordered list of tupes (type, title, href) 136 self._w.setguide(new_guide) 137 138 139# bindings get/set access routines 140 141 # New for epub3 142 def getbindings_epub3(self): 143 # bindings is an ordered list of tuples (media-type, handler) 144 return self._w.getbindings_epub3() 145 146 # New for epub3 147 def setbindings_epub3(self, new_bindings): 148 # new_bindings is an ordered list of tuples (media-type, handler) 149 self._w.setbindings_epub3(new_bindings) 150 151# metadata get/set 152 153 def getmetadataxml(self): 154 # returns a utf-8 encoded metadata xml fragement 155 return self._w.getmetadataxml() 156 157 def setmetadataxml(self, new_metadata): 158 # new_metadata must be a metadata xml fragmment 159 self._w.setmetadataxml(new_metadata) 160 161# package tag get/set 162 163 def getpackagetag(self): 164 # returns a utf-8 encoded metadata xml fragement 165 return self._w.getpackagetag() 166 167 def setpackagetag(self, new_tag): 168 # new_tag must be a xml package tag 169 self._w.setpackagetag(new_tag) 170 171 172# reading / writing / adding / deleting files in the opf manifest 173 174 def readfile(self, id): 175 # returns the contents of the file with manifest id (text files are utf-8 encoded) 176 return self._w.readfile(id) 177 178 def writefile(self, id, data): 179 # writes data to a currently existing file pointed to by the manifest id 180 self._w.writefile(id, data) 181 182 # Modified for epub3 183 def addfile(self, uniqueid, basename, data, mime=None, properties=None, fallback=None, overlay=None): 184 # creates a new file in the manifest with unique manifest id, basename, data, and mimetype 185 self._w.addfile(uniqueid, basename, data, mime, properties, fallback, overlay) 186 187 def deletefile(self, id): 188 # removes the file associated with that manifest id, removes any existing spine entries as well 189 self._w.deletefile(id) 190 191 # New for epub3 192 def set_manifest_epub3_attributes(self, id, properties=None, fallback=None, overlay=None): 193 # sets the epub3 manifest attrobutes for this manifest id 194 self._w.set_manifest_epub3_attributes(id, properties, fallback, overlay) 195 196# reading / writing / adding / deleting other ebook files that DO NOT exist in the opf manifest 197 198 def readotherfile(self, book_href): 199 # returns the contents of the file pointed to by the ebook href 200 return self._w.readotherfile(book_href) 201 202 def writeotherfile(self, book_href, data): 203 # writes data to a currently existing file pointed to by the ebook href 204 self._w.writeotherfile(book_href, data) 205 206 def addotherfile(self, book_href, data): 207 # creates a new file with desired ebook href 208 self._w.addotherfile(book_href, data) 209 210 def deleteotherfile(self, book_href): 211 # removes file pointed to by the ebook href 212 self._w.deleteotherfile(book_href) 213 214 215# iterators 216 217 def text_iter(self): 218 # yields manifest id, href in spine order plus any non-spine items 219 text_set = set([k for k, v in self._w.id_to_mime.items() if v == 'application/xhtml+xml']) 220 for (id, linear, properties) in self._w.spine: 221 if id in text_set: 222 text_set -= set([id]) 223 href = self._w.id_to_href[id] 224 yield id, href 225 for id in text_set: 226 href = self._w.id_to_href[id] 227 yield id, href 228 229 def css_iter(self): 230 # yields manifest id, href 231 for id in sorted(self._w.id_to_mime): 232 mime = self._w.id_to_mime[id] 233 if mime == 'text/css': 234 href = self._w.id_to_href[id] 235 yield id, href 236 237 def image_iter(self): 238 # yields manifest id, href, and mimetype 239 for id in sorted(self._w.id_to_mime): 240 mime = self._w.id_to_mime[id] 241 if mime.startswith('image'): 242 href = self._w.id_to_href[id] 243 yield id, href, mime 244 245 def font_iter(self): 246 # yields manifest id, href, and mimetype 247 for id in sorted(self._w.id_to_mime): 248 mime = self._w.id_to_mime[id] 249 if 'font-' in mime or 'truetype' in mime or 'opentype' in mime or mime.startswith('font/'): 250 href = self._w.id_to_href[id] 251 yield id, href, mime 252 253 def manifest_iter(self): 254 # yields manifest id, href, and mimetype 255 for id in sorted(self._w.id_to_mime): 256 mime = self._w.id_to_mime[id] 257 href = self._w.id_to_href[id] 258 yield id, href, mime 259 260 # New for epub3 261 def manifest_epub3_iter(self): 262 # yields manifest id, href, mimetype, properties, fallback, media-overlay 263 for id in sorted(self._w.id_to_mime): 264 mime = self._w.id_to_mime[id] 265 href = self._w.id_to_href[id] 266 properties = self._w.id_to_props[id] 267 fallback = self._w.id_to_fall[id] 268 overlay = self._w.id_to_over[id] 269 yield id, href, mime, properties, fallback, overlay 270 271 def spine_iter(self): 272 # yields spine idref, linear(yes,no,None), href in spine order 273 for (id, linear, properties) in self._w.spine: 274 href = self._w.id_to_href[id] 275 yield id, linear, href 276 277 # New for epub3 278 def spine_epub3_iter(self): 279 # yields spine idref, linear(yes,no,None), properties, href in spine order 280 for (id, linear, properties) in self._w.spine: 281 href = self._w.id_to_href[id] 282 yield id, linear, properties, href 283 284 285 def guide_iter(self): 286 # yields guide reference type, title, href, and manifest id of href 287 for (type, title, href) in self._w.guide: 288 thref = href.split('#')[0] 289 id = self._w.href_to_id.get(thref, None) 290 yield type, title, href, id 291 292 # New for epub3 293 def bindings_epub3_iter(self): 294 # yields media-type handler in bindings order 295 for (mtype, handler) in self._w.bindings: 296 handler_href = self._w.id_to_href[handler] 297 yield mtype, handler, handler_href 298 299 300 def media_iter(self): 301 # yields manifest, title, href, and manifest id of href 302 for id in sorted(self._w.id_to_mime): 303 mime = self._w.id_to_mime[id] 304 if mime.startswith('audio') or mime.startswith('video'): 305 href = self._w.id_to_href[id] 306 yield id, href, mime 307 308 def other_iter(self): 309 # yields otherid for each file not in the manifest 310 for book_href in self._w.other: 311 yield book_href 312 313 def selected_iter(self): 314 # yields id type ('other' or 'manifest') and id/otherid for each file selected in the BookBrowser 315 for book_href in self._w.selected: 316 id_type = 'other' 317 id = book_href 318 if book_href in self._w.bookpath_to_id: 319 id_type = 'manifest' 320 id = self._w.bookpath_to_id[book_href] 321 yield id_type, id 322 323 324 # miscellaneous routines 325 326 # build the current opf incorporating all changes to date and return it 327 def get_opf(self): 328 return self._w.build_opf() 329 330 # create your own current copy of all ebook contents in destintation directory 331 def copy_book_contents_to(self, destdir): 332 self._w.copy_book_contents_to(destdir) 333 334 # get path to hunspell dll / library 335 def get_hunspell_library_path(self): 336 return self._w.get_hunspell_path() 337 338 # get a list of the directories that contain Sigil's hunspell dictionaries 339 def get_dictionary_dirs(self): 340 return self._w.get_dictionary_dirs() 341 342 # get status of epub file open inside of Sigil 343 def get_epub_is_modified(self): 344 return self._w.epub_isDirty 345 346 # get path to currently open epub or an inside Sigil or empty string if unsaved 347 def get_epub_filepath(self): 348 return self._w.epub_filepath 349 350 351 # functions for converting from manifest id to href, basename, mimetype etc 352 def href_to_id(self, href, ow=None): 353 return self._w.map_href_to_id(href, ow) 354 355 def id_to_mime(self, id, ow=None): 356 return self._w.map_id_to_mime(id, ow) 357 358 def basename_to_id(self, basename, ow=None): 359 return self._w.map_basename_to_id(basename, ow) 360 361 def id_to_href(self, id, ow=None): 362 return self._w.map_id_to_href(id, ow) 363 364 def href_to_basename(self, href, ow=None): 365 if href is not None: 366 return href.split('/')[-1] 367 return ow 368 369 # New for epub3 370 def id_to_properties(self, id, ow=None): 371 return self._w.map_id_to_properties(id, ow) 372 373 def id_to_fallback(self, id, ow=None): 374 return self._w.map_id_to_fallback(id, ow) 375 376 def id_to_overlay(self, id, ow=None): 377 return self._w.map_id_to_overlay(id, ow) 378 379 380 # New in Sigil 1.1 381 # ---------------- 382 383 # returns "light" or "dark" 384 def colorMode(self): 385 return self._w.colorMode() 386 387 # returns color as css or javascript hex color string #xxxxxx 388 # acccepts the following color roles in a case insensitive manner: 389 # "Window", "Base", "Text", "Highlight", "HighlightedText" 390 def color(self, role): 391 return self._w.color(role) 392 393 394 # New in Sigil 1.0 395 # ---------------- 396 397 # A book path (aka bookpath) is a unique relative path from the 398 # ebook root to a specific file in the epub. As a relative path meant 399 # to be used in an href or src "link", it only uses forward slashes "/" 400 # as path separators. Since all files exist inside the 401 # epub root (folder the epub was unzipped into), bookpaths will NEVER 402 # have or use "./" or "../" ie they are in always in canonical form 403 404 # For example under Sigil pre 1.0, all epubs were put into a standard 405 # structure. Under this standard structure book paths would look like 406 # the following: 407 # OEBPS/content.opf 408 # OEBPS/toc.ncx 409 # OEBPS/Text/Section0001.xhtml 410 # OEBPS/Images/cover.jpg 411 # 412 413 # and src and hrefs always looked like the following: 414 # from Section0001.xhtml to Section0002.xhtml: ../Text/Section0002.xhtml 415 # from Section0001.xhtml to cover.jpg: ../Images/cover.jpg 416 # from content.opf to Section0001.xhtml Text/Section0001.xhtml 417 # from toc.ncx to Section0001.xhtml Text/Section0001.xhtml 418 419 # Under Sigil 1.0 and later, the original epub structure can be preserved 420 # meaning that files like content.opf could be named package.opf, and be placed 421 # almost anyplace inside the epub. This is true for almost all files. 422 423 # So to uniquely identify a file, you need to know the bookpath of the OPF 424 # and the manifest href to the specific file, or the path from the epub 425 # root to the file itself (ie. its bookpath) 426 427 # so the Sigil plugin interface for Sigil 1.0 has been extended to allow 428 # the plugin developer to more easily work with bookpaths, create links 429 # between bookpaths, etc. 430 431 # we will use the terms book_href (or bookhref) interchangeably 432 # with bookpath with the following convention: 433 # - use book_href when working with "other" files outside the manifest 434 # - use bookpath when working with files in the opf manifest 435 # - use either when working with the OPF file as it is at the intersection 436 437 # returns the bookpath/book_href to the opf file 438 def get_opfbookpath(self): 439 return self._w.get_opfbookpath() 440 441 # returns the book path of the folder containing this bookpath 442 def get_startingdir(self, bookpath): 443 return self._w.get_startingdir(bookpath) 444 445 # return a bookpath for the file pointed to by the href 446 # from the specified bookpath starting directory 447 def build_bookpath(self, href, starting_dir): 448 return self._w.build_bookpath(href, starting_dir) 449 450 # returns the href relative path from source bookpath to target bookpath 451 def get_relativepath(self, from_bookpath, to_bookpath): 452 return self._w.get_relativepath(from_bookpath, to_bookpath) 453 454 # adds a new file to the *manifest* with the stated bookpath with the provided 455 # uniqueid, data, (and mediatype if specified) 456 def addbookpath(self, uniqueid, bookpath, data, mime=None): 457 return self._w.addbookpath(uniqueid, bookpath, data, mime) 458 459 # functions for converting from manifest id to bookpath and back 460 def bookpath_to_id(self, bookpath, ow=None): 461 return self._w.map_bookpath_to_id(bookpath, ow) 462 463 def id_to_bookpath(self, id, ow=None): 464 return self._w.map_id_to_bookpath(id, ow) 465 466 # valid groups: Text, Styles, Images, Fonts, Audio, Video, ncx, opf, Misc 467 # returns a sorted folder list of ebook paths for a group 468 def group_to_folders(self, group, ow=None): 469 return self._w.map_group_to_folders(group, ow) 470 471 def mediatype_to_group(self, mediatype, ow=None): 472 return self._w.map_mediatype_to_group(mediatype, ow) 473