├── debian ├── compat ├── source │ └── format ├── changelog ├── rules ├── control └── copyright ├── AUTHORS ├── .bzrignore ├── .gitignore ├── data ├── scripts │ ├── mobi_lib │ │ ├── __init__.py │ │ ├── mobi_dict.pyc │ │ ├── mobi_html.pyc │ │ ├── mobi_nav.pyc │ │ ├── mobi_ncx.pyc │ │ ├── mobi_opf.pyc │ │ ├── unipath.pyc │ │ ├── mobi_cover.pyc │ │ ├── mobi_header.pyc │ │ ├── mobi_index.pyc │ │ ├── mobi_k8proc.pyc │ │ ├── mobi_k8resc.pyc │ │ ├── mobi_split.pyc │ │ ├── mobi_utils.pyc │ │ ├── mobi_pagemap.pyc │ │ ├── mobi_sectioner.pyc │ │ ├── mobi_uncompress.pyc │ │ ├── unpack_structure.pyc │ │ ├── compatibility_utils.pyc │ │ ├── unipath.py │ │ ├── mobi_uncompress.py │ │ ├── mobi_sectioner.py │ │ ├── mobi_pagemap.py │ │ ├── mobi_nav.py │ │ ├── unpack_structure.py │ │ ├── mobi_utils.py │ │ └── mobi_cover.py │ ├── com.github.babluboy.bookworm.search.sh │ ├── com.github.babluboy.bookworm.dictionary.sh │ ├── com.github.babluboy.bookworm.htmlscripts.styles.css │ └── com.github.babluboy.bookworm.htmlscripts.functions.js ├── icons │ ├── 16 │ │ ├── bookworm_rating_1.png │ │ ├── bookworm_rating_2.png │ │ ├── bookworm_rating_3.png │ │ ├── bookworm_rating_4.png │ │ ├── bookworm_rating_5.png │ │ ├── bookworm-width-less.png │ │ ├── bookworm-width-more.png │ │ ├── bookworm-bookmark-active.svg │ │ ├── bookworm-line-height-less.png │ │ ├── bookworm-line-height-more.png │ │ ├── bookworm-help-about-symbolic.svg │ │ ├── bookworm-view-grid-symbolic.svg │ │ ├── bookworm-format-text-larger-symbolic.svg │ │ ├── bookworm-format-text-smaller-symbolic.svg │ │ ├── bookworm-help-info-symbolic.svg │ │ ├── bookworm-view-list-symbolic.svg │ │ ├── bookworm-bookmark-inactive.svg │ │ ├── bookworm-dir-remove.svg │ │ ├── bookworm-list-add.svg │ │ ├── bookworm-list-remove.svg │ │ ├── bookworm-selection-option.svg │ │ ├── bookworm-selection-checked.svg │ │ ├── bookworm-object-select-symbolic.svg │ │ ├── bookworm-go-next.svg │ │ ├── bookworm-go-previous.svg │ │ ├── bookworm-format-justify-left.svg │ │ ├── bookworm-help-about.svg │ │ └── bookworm-format-justify-right.svg │ ├── 24 │ │ ├── bookworm-bookmark-active.png │ │ ├── bookworm-bookmark-inactive.png │ │ └── bookworm-selection-option.svg │ └── default_covers │ │ └── bookworm-placeholder-cover.svg ├── com.github.babluboy.bookworm.contract ├── com.github.babluboy.bookworm.desktop.in ├── meson.build ├── com.github.babluboy.bookworm.gresource.xml ├── com.github.babluboy.bookworm.gschema.xml └── com.github.babluboy.bookworm.appdata.xml.in ├── po ├── meson.build ├── POTFILES └── LINGUAS ├── .editorconfig ├── meson └── post_install.py ├── .travis.yml ├── src ├── meson.build ├── main.vala ├── settings.vala ├── comicsReader.vala └── backgroundTasks.vala ├── .github └── workflows │ └── main.yml ├── com.github.babluboy.bookworm.yml ├── meson.build ├── snapcraft.yaml ├── README.md └── CODE_OF_CONDUCT.md /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Siddhartha Das 2 | -------------------------------------------------------------------------------- /.bzrignore: -------------------------------------------------------------------------------- 1 | *~ 2 | **/*~ 3 | .tables 4 | build/ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | **/*~ 3 | .tables 4 | build/ 5 | .flatpak-builder 6 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai 3 | -------------------------------------------------------------------------------- /data/icons/16/bookworm_rating_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm_rating_1.png -------------------------------------------------------------------------------- /data/icons/16/bookworm_rating_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm_rating_2.png -------------------------------------------------------------------------------- /data/icons/16/bookworm_rating_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm_rating_3.png -------------------------------------------------------------------------------- /data/icons/16/bookworm_rating_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm_rating_4.png -------------------------------------------------------------------------------- /data/icons/16/bookworm_rating_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm_rating_5.png -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_dict.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_dict.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_html.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_html.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_nav.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_nav.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_ncx.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_ncx.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_opf.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_opf.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/unipath.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/unipath.pyc -------------------------------------------------------------------------------- /data/icons/16/bookworm-width-less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm-width-less.png -------------------------------------------------------------------------------- /data/icons/16/bookworm-width-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm-width-more.png -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_cover.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_cover.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_header.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_header.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_index.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_index.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_k8proc.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_k8proc.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_k8resc.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_k8resc.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_split.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_split.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_utils.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_pagemap.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_pagemap.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_sectioner.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_sectioner.pyc -------------------------------------------------------------------------------- /data/icons/16/bookworm-bookmark-active.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm-bookmark-active.svg -------------------------------------------------------------------------------- /data/icons/24/bookworm-bookmark-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/24/bookworm-bookmark-active.png -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_uncompress.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/mobi_uncompress.pyc -------------------------------------------------------------------------------- /data/scripts/mobi_lib/unpack_structure.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/unpack_structure.pyc -------------------------------------------------------------------------------- /data/icons/16/bookworm-line-height-less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm-line-height-less.png -------------------------------------------------------------------------------- /data/icons/16/bookworm-line-height-more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/16/bookworm-line-height-more.png -------------------------------------------------------------------------------- /data/icons/24/bookworm-bookmark-inactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/icons/24/bookworm-bookmark-inactive.png -------------------------------------------------------------------------------- /data/scripts/mobi_lib/compatibility_utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babluboy/bookworm/HEAD/data/scripts/mobi_lib/compatibility_utils.pyc -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext(meson.project_name(), 2 | args: [ 3 | '--directory=' + meson.source_root(), 4 | '--from-code=UTF-8' 5 | ] 6 | ) 7 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | com.github.babluboy.bookworm (1.1.2) bionic; urgency=medium 2 | 3 | * Initial Release. 4 | 5 | -- Siddhartha Das Sun, 21 Oct 2018 04:53:39 -0500 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = tab 8 | indent_style = space 9 | insert_final_newline = true 10 | tab_width = 4 11 | 12 | [{*.yml,*.yaml}] 13 | tab_width = 2 14 | -------------------------------------------------------------------------------- /data/com.github.babluboy.bookworm.contract: -------------------------------------------------------------------------------- 1 | [Contractor Entry] 2 | Name=Bookworm 3 | Description=eBook Reader 4 | MimeType=application/epub+zip;application/pdf;application/x-pdf;application/cbr;application/x-cbz;application/x-mobipocket-ebook;application/fb2+xml;application/fb2+zip; 5 | Exec=com.github.babluboy.bookworm %F 6 | -------------------------------------------------------------------------------- /meson/post_install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | 6 | schemadir = os.path.join(os.environ['MESON_INSTALL_PREFIX'], 'share', 'glib-2.0', 'schemas') 7 | 8 | if not os.environ.get('DESTDIR'): 9 | print('Compiling gsettings schemas...') 10 | subprocess.call(['glib-compile-schemas', schemadir]) 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 10.17.0 7 | 8 | sudo: required 9 | 10 | services: 11 | - docker 12 | 13 | addons: 14 | apt: 15 | sources: 16 | - ubuntu-toolchain-r-test 17 | packages: 18 | - libstdc++-5-dev 19 | 20 | install: 21 | - npm i -g @elementaryos/houston 22 | 23 | script: 24 | - houston ci 25 | -------------------------------------------------------------------------------- /data/scripts/com.github.babluboy.bookworm.search.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #Written by Siddhartha Das (bablu.boy@gmail.com) as part of Bookworm (eBook Reader) 3 | #This script searches through the contents of a book for the user provided text 4 | #html2text utility is used to extract the text from the html content and html entities de-coded 5 | 6 | HTML_CONTENT_TO_BE_SEARCHED=$1 7 | USER_SEARCH_TEXT=$2 8 | html2text -utf8 "$HTML_CONTENT_TO_BE_SEARCHED" | tr '\n' ' ' | grep -E -o -i ".{0,50}$USER_SEARCH_TEXT.{0,50}" 9 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | #export DH_VERBOSE=1 11 | 12 | %: 13 | dh $@ 14 | 15 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | # Replace parameters in source code 2 | conf_data = configuration_data() 3 | conf_data.set('MESON_INSTALL_PREFIX', install_dir) 4 | conf_data.set('MESON_INSTALL_SCRIPTSDIR', scripts_dir) 5 | conf_data.set('MESON_INSTALL_TASKSDIR', tasks_dir) 6 | conf_data.set('MESON_INSTALL_MOBILIBDIR', mobilib_dir) 7 | conf_data.set('MESON_INSTALL_VERSION', version) 8 | constants_config = configure_file( 9 | input: 'constants.vala.in', 10 | output: 'constants.vala', 11 | configuration: conf_data 12 | ) 13 | -------------------------------------------------------------------------------- /data/com.github.babluboy.bookworm.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Bookworm 3 | GenericName=eBook Reader 4 | Comment=Read and manage eBooks 5 | Categories=Office;Viewer; 6 | MimeType=application/epub+zip;application/pdf;application/x-pdf;application/cbr;application/x-cbz;application/fb2+xml;application/fb2+zip; 7 | Exec=com.github.babluboy.bookworm %F 8 | Icon=com.github.babluboy.bookworm 9 | Terminal=false 10 | Type=Application 11 | X-GNOME-Gettext-Domain=bookworm 12 | Keywords=Bookworm;Ebook;Reader;Epub;Mobi;Comic;Cbr;Cbz;Pdf; 13 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-help-about-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-view-grid-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-format-text-larger-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-format-text-smaller-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | src/main.vala 2 | src/bookworm.vala 3 | src/utils.vala 4 | src/constants.vala.in 5 | src/ePubReader.vala 6 | src/book.vala 7 | src/database.vala 8 | src/settings.vala 9 | src/bookinfo.vala 10 | src/headerbar.vala 11 | src/dialog.vala 12 | src/window.vala 13 | src/prefpopover.vala 14 | src/library.vala 15 | src/pdfReader.vala 16 | src/contentHandler.vala 17 | src/comicsReader.vala 18 | src/backgroundTasks.vala 19 | src/mobiReader.vala 20 | src/shortcuts.vala 21 | src/xmlHandler.vala 22 | 23 | data/com.github.babluboy.bookworm.desktop.in 24 | data/com.github.babluboy.bookworm.appdata.xml.in 25 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: com.github.babluboy.bookworm 2 | Section: text 3 | Priority: extra 4 | Maintainer: Siddhartha Das 5 | Build-Depends: debhelper, 6 | gettext, 7 | libgtk-3-dev (>= 3.10), 8 | meson, 9 | valac (>= 0.28.0), 10 | libgee-0.8-dev, 11 | libgranite-dev (>= 0.5), 12 | libsqlite3-dev, 13 | libxml2, 14 | libxml2-dev, 15 | libwebkit2gtk-4.0-dev, 16 | libpoppler-glib-dev, 17 | ninja-build 18 | Standards-Version: 4.1.1 19 | 20 | Package: com.github.babluboy.bookworm 21 | Architecture: any 22 | Depends: ${misc:Depends}, ${shlibs:Depends}, unzip, poppler-utils, unar, html2text, python2, curl, appstream 23 | Description: eBook Reader 24 | A simple and focussed eBook reader 25 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://dep.debian.net/deps/dep5 2 | Upstream-Name: bookworm 3 | Source: https://github.com/babluboy/bookworm 4 | 5 | Files: * 6 | Copyright: 2018 Siddhartha Das 7 | License: GPL-3.0+ 8 | 9 | License: GPL-3.0+ 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | . 15 | This package is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | . 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | . 23 | On Debian systems, the complete text of the GNU General 24 | Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". 25 | 26 | -------------------------------------------------------------------------------- /data/scripts/com.github.babluboy.bookworm.dictionary.sh: -------------------------------------------------------------------------------- 1 | search_word=$1 2 | #check if the input still has spaces i.e. multiple words 3 | case "$search_word" in 4 | *\ * ) 5 | #space found in input - send output warning and quit 6 | echo "Multiple words are not supported, please search for a single word" 7 | exit 8 | ;; 9 | *) 10 | #do nothing - the input has no spaces 11 | ;; 12 | esac 13 | # 14 | # 15 | #The section below is for online dictionary. For offline dictionary, comment this section and uncomment the section below 16 | curl -s http://wordnetweb.princeton.edu/perl/webwn?s="$search_word" | html2text -o /tmp/bookworm_word_search.txt > /dev/null 17 | #split the output to remove unwanted header information i.e anything before **** Noun **** 18 | csplit -s -f /tmp/search_result_ /tmp/bookworm_word_search.txt /"[****]*[****]"/ > /dev/null 19 | cat /tmp/search_result_01 20 | #remove the files created by this script 21 | rm -f /tmp/search_result_00 /tmp/search_result_01 /tmp/bookworm_word_search.txt 22 | # 23 | # 24 | #The section below is for offline dictionary.Ensure dictionary is installed by running the following command: 25 | #sudo apt-get install dictd dict dict-gcide 26 | #Run offline dictionary by enabling the line below: 27 | #dict $search_word 28 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # This workflow will run for any pull request or pushed commit 4 | on: [push, pull_request] 5 | 6 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 7 | jobs: 8 | # This workflow contains a single job called "flatpak" 9 | flatpak: 10 | # The type of runner that the job will run on 11 | runs-on: ubuntu-latest 12 | 13 | # This job runs in a special container designed for building Flatpaks for AppCenter 14 | container: 15 | image: ghcr.io/elementary/flatpak-platform/runtime:6 16 | options: --privileged 17 | 18 | # Steps represent a sequence of tasks that will be executed as part of the job 19 | steps: 20 | # Checks-out your repository under $GITHUB_WORKSPACE, so the job can access it 21 | - uses: actions/checkout@v2 22 | 23 | # Builds your flatpak manifest using the Flatpak Builder action 24 | - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v3 25 | with: 26 | # This is the name of the Bundle file we're building and can be anything you like 27 | bundle: bookworm.flatpak 28 | # This uses your app's RDNN ID 29 | manifest-path: com.github.babluboy.bookworm.yml 30 | 31 | # You can automatically run any of the tests you've created as part of this workflow 32 | run-tests: true 33 | 34 | # These lines specify the location of the elementary Runtime and Sdk 35 | repository-name: appcenter 36 | repository-url: https://flatpak.elementary.io/repo.flatpakrepo 37 | cache-key: "flatpak-builder-${{ github.sha }}" 38 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-help-info-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 17 | 19 | image/svg+xml 20 | 22 | elementary Symbolic Icon Theme 23 | 24 | 25 | 26 | elementary Symbolic Icon Theme 28 | 30 | 33 | 37 | 38 | 39 | 43 | 44 | -------------------------------------------------------------------------------- /src/main.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com) 2 | * 3 | * This file is part of Bookworm and entry point to the 4 | * application with the main method 5 | * 6 | * Bookworm is free software: you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * Bookworm is distributed in the hope that it will be 12 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 14 | * Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with Bookworm. If not, see http://www.gnu.org/licenses/. 18 | */ 19 | BookwormApp.Bookworm application; 20 | public static int main (string[] args) { 21 | Environment.set_variable ("G_MESSAGES_DEBUG", "all", true); 22 | //Get an instance of Bookworm if is running, otherwise create a new instance 23 | application = BookwormApp.Bookworm.getAppInstance (); 24 | //Workaround to get Granite's --about & Gtk's --help working together 25 | if ("--help" in args || "-h" in args || 26 | "--version" in args || 27 | "--discover" in args) 28 | { 29 | return application.processCommandLine (args); 30 | } else { 31 | Gtk.init (ref args); 32 | if ("--debug" in args) { 33 | application.command_line_option_debug = true; 34 | } 35 | if ("--info" in args) { 36 | application.command_line_option_info = true; 37 | } 38 | return application.run (args); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | aa 2 | ab 3 | ae 4 | af 5 | ak 6 | am 7 | an 8 | ar 9 | as 10 | ast 11 | av 12 | ay 13 | az 14 | ba 15 | be 16 | bg 17 | bh 18 | bi 19 | bm 20 | bn 21 | bo 22 | br 23 | bs 24 | ca 25 | ce 26 | ch 27 | ckb 28 | co 29 | cr 30 | cs 31 | cu 32 | cv 33 | cy 34 | da 35 | de 36 | dv 37 | dz 38 | ee 39 | el 40 | en_AU 41 | en_CA 42 | en_GB 43 | eo 44 | es 45 | es_MX 46 | et 47 | eu 48 | fa 49 | ff 50 | fi 51 | fj 52 | fo 53 | fr 54 | fr_CA 55 | fy 56 | ga 57 | gd 58 | gl 59 | gn 60 | gu 61 | gv 62 | ha 63 | he 64 | hi 65 | ho 66 | hr 67 | ht 68 | hu 69 | hy 70 | hz 71 | ia 72 | id 73 | ie 74 | ig 75 | ii 76 | ik 77 | io 78 | is 79 | it 80 | iu 81 | ja 82 | jv 83 | ka 84 | kg 85 | ki 86 | kj 87 | kk 88 | kl 89 | km 90 | kn 91 | ko 92 | kr 93 | ks 94 | ku 95 | kv 96 | kw 97 | ky 98 | la 99 | lb 100 | lg 101 | li 102 | ln 103 | lo 104 | lt 105 | lu 106 | lv 107 | mg 108 | mh 109 | mi 110 | mk 111 | ml 112 | mn 113 | mo 114 | mr 115 | ms 116 | mt 117 | my 118 | na 119 | nb_NO 120 | nd 121 | ne 122 | ng 123 | nl 124 | nn 125 | nr 126 | nv 127 | ny 128 | oc 129 | oj 130 | om 131 | or 132 | os 133 | pa 134 | pi 135 | pl 136 | ps 137 | pt 138 | pt_BR 139 | qu 140 | rm 141 | rn 142 | ro 143 | ru 144 | rue 145 | rw 146 | sa 147 | sc 148 | sd 149 | se 150 | sg 151 | si 152 | sk 153 | sl 154 | sm 155 | sma 156 | sn 157 | so 158 | sq 159 | sr 160 | ss 161 | st 162 | su 163 | sv 164 | sw 165 | ta 166 | te 167 | tg 168 | th 169 | ti 170 | tk 171 | tl 172 | tn 173 | to 174 | tr 175 | ts 176 | tt 177 | tw 178 | ty 179 | ug 180 | uk 181 | ur 182 | uz 183 | ve 184 | vi 185 | vo 186 | wa 187 | wo 188 | xh 189 | yi 190 | yo 191 | za 192 | zh 193 | zh_CN 194 | zh_HK 195 | zh_TW zu 196 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-view-list-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 18 | 19 | 21 | image/svg+xml 22 | 24 | 25 | 26 | 27 | 29 | 49 | 54 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /com.github.babluboy.bookworm.yml: -------------------------------------------------------------------------------- 1 | # This is the same ID that you've used in meson.build and other files 2 | { 3 | "app-id": "com.github.babluboy.bookworm", 4 | 5 | # Instead of manually specifying a long list of build and runtime dependencies, 6 | # we can use a convenient pre-made runtime and SDK. For this example, we'll be 7 | # using the runtime and SDK provided by elementary. 8 | "runtime": "io.elementary.Platform", 9 | "runtime-version": "7.2", 10 | "sdk": "io.elementary.Sdk", 11 | 12 | # This should match the exec line in your .desktop file and usually is the same 13 | # as your app ID 14 | command: com.github.babluboy.bookworm, 15 | 16 | # Here we can specify the kinds of permissions our app needs to run. Since we're 17 | # not using hardware like webcams, making sound, or reading external files, we 18 | # only need permission to draw our app on screen using either X11 or Wayland. 19 | "finish-args":[ 20 | '--share=ipc', 21 | '--socket=fallback-x11', 22 | '--socket=wayland' 23 | ], 24 | 25 | # This section is where you list all the source code required to build your app. 26 | # If we had external dependencies that weren't included in our SDK, we would list 27 | # them here. 28 | "modules": [ 29 | { 30 | "name": "cpython", 31 | "sources": [{ 32 | "type": "archive", 33 | "url": "https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz", 34 | "sha256": "f434053ba1b5c8a5cc597e966ead3c5143012af827fd3f0697d21450bb8d87a6" 35 | }] 36 | }, 37 | { 38 | "name": "poppler", 39 | "buildsystem": "cmake-ninja", 40 | "config-opts": [ 41 | "-DCMAKE_INSTALL_LIBDIR=/app/lib", 42 | "-DCMAKE_INSTALL_INCLUDEDIR=/app/include", 43 | "-DENABLE_BOOST=OFF", 44 | "-DENABLE_LIBOPENJPEG=none" 45 | ], 46 | "sources": [{ 47 | "type": "archive", 48 | "url": "https://poppler.freedesktop.org/poppler-23.04.0.tar.xz", 49 | "sha256": "b6d893dc7dcd4138b9e9df59a13c59695e50e80dc5c2cacee0674670693951a1" 50 | }] 51 | }, 52 | { 53 | "name": "bookworm", 54 | "buildsystem": "meson", 55 | "sources": [{ 56 | "type": "dir", 57 | "path": "." 58 | }] 59 | } 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-bookmark-inactive.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 15 | 17 | image/svg+xml 18 | 20 | elementary Symbolic Icon Theme 21 | 22 | 23 | 24 | elementary Symbolic Icon Theme 26 | 28 | 31 | 35 | 36 | 39 | 43 | 44 | 45 | 50 | 54 | 58 | 61 | 65 | 69 | 73 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | icon_sizes = ['16', '24', '32', '48', '64', '128'] 2 | 3 | foreach i : icon_sizes 4 | install_data( 5 | join_paths('icons', i, meson.project_name() + '.svg'), 6 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i, 'apps') 7 | ) 8 | install_data( 9 | join_paths('icons', i, meson.project_name() + '.svg'), 10 | install_dir: join_paths(get_option('datadir'), 'icons', 'hicolor', i + 'x' + i + '@2', 'apps') 11 | ) 12 | endforeach 13 | 14 | install_data( 15 | join_paths('scripts', meson.project_name() + '.search.sh'), 16 | install_dir: join_paths(scripts_dir, 'tasks'), 17 | install_mode: 'rwxr-xr-x' 18 | ) 19 | 20 | install_data( 21 | join_paths('scripts', meson.project_name() + '.dictionary.sh'), 22 | install_dir: join_paths(scripts_dir, 'tasks'), 23 | install_mode: 'rwxr-xr-x' 24 | ) 25 | 26 | install_subdir( 27 | 'scripts/mobi_lib', 28 | install_dir: scripts_dir, 29 | install_mode: 'rwxr-xr-x' 30 | ) 31 | 32 | i18n.merge_file( 33 | input: meson.project_name() + '.desktop.in', 34 | output: meson.project_name() + '.desktop', 35 | po_dir: join_paths(meson.source_root(), 'po'), 36 | type: 'desktop', 37 | install: true, 38 | install_dir: join_paths(get_option('datadir'), 'applications') 39 | ) 40 | 41 | install_data ( 42 | meson.project_name () + '.gschema.xml', 43 | install_dir: join_paths (get_option ('datadir'), 'glib-2.0', 'schemas') 44 | ) 45 | 46 | i18n.merge_file( 47 | input: meson.project_name() + '.appdata.xml.in', 48 | output: meson.project_name() + '.appdata.xml', 49 | po_dir: join_paths(meson.source_root(), 'po'), 50 | install: true, 51 | install_dir: join_paths(get_option('datadir'), 'metainfo') 52 | ) 53 | 54 | desktop_file_validate = find_program('desktop-file-validate', required:false) 55 | if desktop_file_validate.found() 56 | test ( 57 | 'Validate desktop file', 58 | desktop_file_validate, 59 | args: join_paths(meson.current_build_dir (), meson.project_name() + '.desktop') 60 | ) 61 | endif 62 | appstreamcli = find_program(['appstreamcli', 'appstream-util'], required:false) 63 | if appstreamcli.found() 64 | test ( 65 | 'Validate appdata file', 66 | appstreamcli, 67 | args: ['validate', join_paths(meson.current_build_dir (), meson.project_name() + '.appdata.xml')] 68 | ) 69 | endif 70 | -------------------------------------------------------------------------------- /data/scripts/com.github.babluboy.bookworm.htmlscripts.styles.css: -------------------------------------------------------------------------------- 1 | /* Set the style for the webkit scroll bar (Reading View) */ 2 | ::-webkit-scrollbar { 3 | width: 8px; 4 | height: 7px; 5 | background: $SCROLLBAR_BACKGROUND; 6 | border-radius: 10px; 7 | } 8 | ::-webkit-scrollbar:hover { 9 | background: #7a7a7a; 10 | } 11 | ::-webkit-scrollbar-track { 12 | background: $SCROLLBAR_BACKGROUND; 13 | } 14 | ::-webkit-scrollbar-track:hover { 15 | background: #848484; 16 | } 17 | ::-webkit-scrollbar-track-piece, 18 | ::-webkit-scrollbar-button { 19 | display: none; 20 | } 21 | ::-webkit-scrollbar-thumb { 22 | background-color: $SCROLLBAR_BACKGROUND; 23 | border-radius: 3ex; 24 | border: 2px; 25 | min-height: 40px; 26 | } 27 | ::-webkit-scrollbar-thumb:hover { 28 | background: $SCROLLBAR_BACKGROUND; 29 | border: none; 30 | } 31 | 32 | /* Set style for the whole document */ 33 | *{ 34 | /* Set hyphenation */ 35 | /*-webkit-hyphens: auto; */ 36 | /* Control line spacing */ 37 | line-height: $READING_LINE_HEIGHT%; 38 | /* Control page margins */ 39 | margin:auto !important; 40 | max-width: $READING_WIDTH% !important; 41 | /* Control fonts */ 42 | font-family: $FONT_FAMILY !important; 43 | font-size: $FONT_SIZEpx !important; 44 | /* Control text and background color */ 45 | $TEXT_AND_BACKGROUND_COLOR 46 | /* Control text alignment */ 47 | text-align: $READING_TEXT_ALIGN; 48 | } 49 | 50 | /* Re-size and center cover images for the title page (first or second page)*/ 51 | $TITLE_PAGE_IMAGE { 52 | margin-top: auto !important; 53 | margin-bottom: auto !important; 54 | margin-left: auto !important; 55 | margin-right: auto !important; 56 | width:auto; 57 | height:auto; 58 | max-width:50vh !important; 59 | max-height:70vh !important; 60 | } 61 | 62 | /* Set style for two page view */ 63 | .two_page { 64 | -webkit-column-count: 2; 65 | -webkit-column-gap: 40px; 66 | -webkit-column-rule-style: solid; 67 | column-rule-style: solid; 68 | -webkit-column-rule-width: 1px; 69 | column-rule-color: lightgrey; 70 | } 71 | /* Set style for text highlight */ 72 | span.bookworm_search_result_highlight { 73 | background-color: $HIGHLIGHT_COLOR !important; 74 | } 75 | span.bookworm_annotation_highlight { 76 | background-color: $HIGHLIGHT_COLOR !important; 77 | } 78 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # project name and programming language 2 | project('com.github.babluboy.bookworm', ['vala', 'c'], 3 | version: '1.1.2' 4 | ) 5 | version = '1.1.2' 6 | gnome = import('gnome') 7 | i18n = import('i18n') 8 | 9 | # Set all the install directories 10 | install_dir = join_paths(get_option('prefix'), 'share') 11 | scripts_dir = join_paths(install_dir, meson.project_name(), 'scripts') 12 | tasks_dir = join_paths(scripts_dir, 'tasks') 13 | mobilib_dir = join_paths(scripts_dir, 'mobi_lib') 14 | 15 | asresources = gnome.compile_resources( 16 | 'as-resources', 'data/com.github.babluboy.bookworm.gresource.xml', 17 | source_dir: 'data', 18 | c_name: 'as' 19 | ) 20 | 21 | conf = configuration_data() 22 | conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) 23 | configure_file(output: 'config.h', configuration: conf) 24 | config_h_dir = include_directories('.') 25 | 26 | vala_args = [ 27 | '-g', '--thread' , '--target-glib=2.38', 28 | ] 29 | 30 | add_global_arguments('-DGETTEXT_PACKAGE="@0@"'.format (meson.project_name()), language:'c') 31 | # Replace parameters in source code 32 | subdir('src') 33 | 34 | # Create a new executable, list the files we want to compile, list the dependencies we need, and install 35 | executable( 36 | meson.project_name(), 37 | constants_config, 38 | 'src/main.vala', 39 | 'src/bookworm.vala', 40 | 'src/utils.vala', 41 | 'src/ePubReader.vala', 42 | 'src/book.vala', 43 | 'src/database.vala', 44 | 'src/settings.vala', 45 | 'src/bookinfo.vala', 46 | 'src/headerbar.vala', 47 | 'src/dialog.vala', 48 | 'src/window.vala', 49 | 'src/prefpopover.vala', 50 | 'src/library.vala', 51 | 'src/pdfReader.vala', 52 | 'src/contentHandler.vala', 53 | 'src/comicsReader.vala', 54 | 'src/backgroundTasks.vala', 55 | 'src/mobiReader.vala', 56 | 'src/fb2Reader.vala', 57 | 'src/shortcuts.vala', 58 | 'src/xmlHandler.vala', 59 | asresources, 60 | vala_args: vala_args, 61 | link_args: '-lm', 62 | dependencies: [ 63 | dependency('gtk+-3.0'), 64 | dependency('gee-0.8'), 65 | dependency('webkit2gtk-4.0'), 66 | dependency('poppler-glib'), 67 | dependency('libxml-2.0'), 68 | dependency('granite', version: '>=0.5'), 69 | dependency('sqlite3', version: '>=3.5.9') 70 | ], 71 | install: true 72 | ) 73 | 74 | meson.add_install_script('meson/post_install.py') 75 | subdir('data') 76 | subdir('po') 77 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-dir-remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 14 | 15 | 17 | image/svg+xml 18 | 20 | elementary Symbolic Icon Theme 21 | 22 | 23 | 24 | elementary Symbolic Icon Theme 26 | 28 | 31 | 35 | 36 | 39 | 43 | 44 | 45 | 50 | 54 | 58 | 61 | 65 | 69 | 73 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /data/icons/default_covers/bookworm-placeholder-cover.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 42 | 47 | 54 | 55 | 57 | 58 | 60 | image/svg+xml 61 | 63 | 64 | 65 | 66 | 67 | 72 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: bookworm 2 | version: '1.0.0' 3 | summary: A focused eBook reader 4 | description: | 5 | Read the books you love without having to worry about the different format complexities like epub, pdf, mobi, cbr, etc 6 | Manage your library by tagging and updating metadata on books, to quickly find books using metadata searching and tag based filtering. 7 | This version supports EPUB, MOBI, PRC, PDF and Comics (CBR and CBZ) formats with support for more formats to follow soon. 8 | 9 | icon: data/icons/24/com.github.babluboy.bookworm.svg 10 | grade: devel # must be 'stable' to release into candidate/stable channels 11 | confinement: devmode # use 'strict' once you have the right plugs and slots 12 | 13 | slots: 14 | dbus-bookworm: 15 | interface: dbus 16 | bus: session 17 | name: com.github.babluboy.bookworm 18 | 19 | apps: 20 | bookworm: 21 | command: desktop-launch com.github.babluboy.bookworm 22 | slots: [ dbus-bookworm ] 23 | desktop: usr/share/applications/com.github.babluboy.bookworm.desktop 24 | plugs: [network, home, x11, unity7, gsettings, browser-support] 25 | environment: 26 | LD_LIBRARY_PATH: $SNAP/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0/:$LD_LIBRARY_PATH 27 | 28 | parts: 29 | granite: 30 | plugin: cmake 31 | source: https://github.com/elementary/granite/archive/0.5.tar.gz 32 | source-type: tar 33 | configflags: [-DCMAKE_BUILD_TYPE=Release, -DCMAKE_INSTALL_PREFIX=/usr, -DCMAKE_INSTALL_LIBDIR=/usr/lib] 34 | build-packages: 35 | - build-essential 36 | - libgee-0.8-dev 37 | - libgirepository1.0-dev 38 | - libgtk-3-dev 39 | - cmake 40 | - gobject-introspection 41 | 42 | bookworm: 43 | after: [granite, desktop-gtk3] 44 | source: https://github.com/babluboy/bookworm/archive/1.0.0.tar.gz 45 | source-type: tar 46 | plugin: cmake 47 | configflags: [-DCMAKE_INSTALL_PREFIX=/usr] 48 | build-packages: 49 | - build-essential 50 | - valac 51 | - intltool 52 | - libgee-0.8-dev 53 | - debhelper 54 | - libgtk-3-dev 55 | - libgranite-dev 56 | - libwebkit2gtk-4.0-37 57 | - libwebkit2gtk-4.0-dev 58 | - libsqlite3-dev 59 | - poppler-utils 60 | - libpoppler-glib-dev 61 | - libsoup2.4-dev 62 | - libxml2-dev 63 | stage-packages: 64 | - gnome-keyring 65 | - gobject-introspection 66 | - libgdk-pixbuf2.0-0 67 | - libgee-0.8-2 68 | - libgtk-3-0 69 | - libpango-1.0-0 70 | - libcairo2 71 | - libpangocairo-1.0-0 72 | - libsqlite3-0 73 | - libwebkit2gtk-4.0-37 74 | - libxml2 75 | - dbus-x11 76 | valac: 77 | plugin: autotools 78 | source: http://download.gnome.org/sources/vala/0.38/vala-0.38.8.tar.xz 79 | build-packages: 80 | - flex 81 | - bison 82 | - build-essential 83 | - libglib2.0-dev 84 | - libgraphviz-dev 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bookworm [![Translation status](https://hosted.weblate.org/widgets/bookworm/-/svg-badge.svg)](https://hosted.weblate.org/engage/bookworm/?utm_source=widget) [![Build Status](https://travis-ci.org/babluboy/bookworm.svg?branch=master)](https://travis-ci.org/babluboy/bookworm) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FZP8GK839VGQC) 2 | A simple, focused eBook reader 3 | 4 | Author: Siddhartha Das 5 | 6 | Read the books you love without having to worry about the different format complexities like epub, pdf, mobi, cbr, etc. This version supports EPUB, MOBI, FB2, PDF, FB2 and Comics (CBR and CBZ) formats with support for more formats to follow soon. 7 | 8 | Check the Bookworm website for details on features, shortcuts, installation guides for supported distros : https://babluboy.github.io/bookworm/ 9 | 10 | 11 | ## Building, Testing, and Installation 12 | 13 | You'll need the following dependencies to build and run on Ubuntu/Debian based distros: 14 | * unzip 15 | * poppler-utils 16 | * unar 17 | * html2text 18 | * python2 19 | * libgtk-3-dev 20 | * libgee-0.8-dev 21 | * libgranite-dev 22 | * libsqlite3-dev 23 | * libxml2-dev 24 | * webkit2gtk-4.0 25 | * libwebkit2gtk-4.0-37 26 | * libpoppler-glib-dev 27 | * meson 28 | * valac 29 | * gettext 30 | * curl 31 | * ninja-build 32 | * appstream 33 | 34 | Run the command below to install the above on Ubuntu and Debian distros: 35 | 36 | `sudo apt-get install unzip poppler-utils unar html2text python2.7 libgtk-3-dev libgee-0.8-dev libgranite-dev libsqlite3-dev libxml2-dev webkit2gtk-4.0 libwebkit2gtk-4.0-37 libpoppler-glib-dev meson valac gettext curl ninja-build appstream git` 37 | 38 | Run `git clone to download the source code, meson build` to configure the build environment and run `ninja test` to build 39 | 40 | git clone https://github.com/babluboy/bookworm.git 41 | cd bookworm 42 | meson build --prefix=/usr 43 | mkdir -p build && cd build 44 | ninja 45 | 46 | To install, use `ninja install`, then execute with `com.github.babluboy.bookworm` 47 | 48 | sudo ninja install 49 | com.github.babluboy.bookworm 50 | 51 | 52 | ## Screenshots 53 | 54 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormLibraryView.png) 55 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormReadingView.png) 56 | 57 | Two Page View 58 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/TwoPageView.png) 59 | 60 | Bookworm Dark Mode 61 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/DarkModeLibraryView.png) 62 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/DarkModeReadingView.png) 63 | 64 | Library List View 65 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/LibraryListView.png) 66 | 67 | Bookworm Preferences 68 | ![screenshot](https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/PreferencesDialog.png) 69 | 70 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at bablu.boy@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-list-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 36 | 38 | 39 | 41 | image/svg+xml 42 | 44 | elementary Symbolic Icon Theme 45 | 46 | 47 | 48 | elementary Symbolic Icon Theme 50 | 52 | 55 | 59 | 60 | 63 | 67 | 68 | 69 | 74 | 78 | 82 | 85 | 89 | 93 | 97 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-list-remove.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 36 | 38 | 39 | 41 | image/svg+xml 42 | 44 | elementary Symbolic Icon Theme 45 | 46 | 47 | 48 | elementary Symbolic Icon Theme 50 | 52 | 55 | 59 | 60 | 63 | 67 | 68 | 69 | 74 | 78 | 82 | 85 | 89 | 93 | 97 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/unipath.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | # Copyright (c) 2014 Kevin B. Hendricks, John Schember, and Doug Massay 6 | # All rights reserved. 7 | # 8 | # Redistribution and use in source and binary forms, with or without modification, 9 | # are permitted provided that the following conditions are met: 10 | # 11 | # 1. Redistributions of source code must retain the above copyright notice, this list of 12 | # conditions and the following disclaimer. 13 | # 14 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list 15 | # of conditions and the following disclaimer in the documentation and/or other materials 16 | # provided with the distribution. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 21 | # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 | # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 26 | # WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from __future__ import unicode_literals, division, absolute_import, print_function 29 | from .compatibility_utils import PY2, text_type, binary_type 30 | 31 | import sys 32 | import os 33 | 34 | # utility routines to convert all paths to be full unicode 35 | 36 | # Under Python 2, if a bytestring, try to convert it to unicode using sys.getfilesystemencoding 37 | # Under Python 3, if bytes, try to convert it to unicode using os.fsencode() to decode it 38 | 39 | # Mac OS X and Windows will happily support full unicode paths 40 | # Linux can support full unicode paths but allows arbitrary byte paths which may be inconsistent with unicode 41 | 42 | fsencoding = sys.getfilesystemencoding() 43 | 44 | def pathof(s, enc=fsencoding): 45 | if s is None: 46 | return None 47 | if isinstance(s, text_type): 48 | return s 49 | if isinstance(s, binary_type): 50 | try: 51 | return s.decode(enc) 52 | except: 53 | pass 54 | return s 55 | 56 | def exists(s): 57 | return os.path.exists(pathof(s)) 58 | 59 | def isfile(s): 60 | return os.path.isfile(pathof(s)) 61 | 62 | def isdir(s): 63 | return os.path.isdir(pathof(s)) 64 | 65 | def mkdir(s): 66 | return os.mkdir(pathof(s)) 67 | 68 | def listdir(s): 69 | rv = [] 70 | for file in os.listdir(pathof(s)): 71 | rv.append(pathof(file)) 72 | return rv 73 | 74 | def getcwd(): 75 | if PY2: 76 | return os.getcwdu() 77 | return os.getcwd() 78 | 79 | def walk(top): 80 | top = pathof(top) 81 | rv = [] 82 | for base, dnames, names in os.walk(top): 83 | base = pathof(base) 84 | for name in names: 85 | name = pathof(name) 86 | rv.append(relpath(os.path.join(base, name), top)) 87 | return rv 88 | 89 | def relpath(path, start=None): 90 | return os.path.relpath(pathof(path) , pathof(start)) 91 | 92 | def abspath(path): 93 | return os.path.abspath(pathof(path)) 94 | -------------------------------------------------------------------------------- /src/settings.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com) 2 | * 3 | * This file is part of Bookworm and is used for persisting 4 | * the state of the window and associated user prefferences 5 | * 6 | * Bookworm is free software: you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * Bookworm is distributed in the hope that it will be 12 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 14 | * Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with Bookworm. If not, see http://www.gnu.org/licenses/. 18 | */ 19 | 20 | public class BookwormApp.Settings : Granite.Services.Settings { 21 | private static Settings? instance = null; 22 | 23 | public int window_width { get; set; } 24 | public int window_height { get; set; } 25 | public int pos_x { get; set; } 26 | public int pos_y { get; set; } 27 | public bool window_is_maximized { get; set; } 28 | public double zoom_level { get; set; } 29 | public string reading_profile { get; set; } 30 | public bool is_dark_theme_enabled { get; set; } 31 | public bool is_local_storage_enabled { get; set; } 32 | public bool is_show_library_on_start { get; set; } 33 | public string reading_width { get; set; } 34 | public string reading_line_height { get; set; } 35 | public string text_alignment { get; set; } 36 | public string library_view_mode { get; set; } 37 | public string reading_font_name { get; set; } 38 | public string reading_font_name_family { get; set; } 39 | public int reading_font_size { get; set; } 40 | public string list_of_profile_colors { get; set; } 41 | public string list_of_scan_dirs { get; set; } 42 | public string book_being_read { get; set; } 43 | public bool is_two_page_enabled { get; set; } 44 | public bool is_leaf_over_page_by_edge_enabled { get; set; } 45 | public string current_info_tab { get; set; } 46 | public bool is_fullscreen { get; set; } 47 | public int library_page_items { get; set; } 48 | public SettingsOfShortcuts shortcuts { get; set; } 49 | 50 | public static Settings get_instance () { 51 | if (instance == null) { 52 | instance = new Settings (); 53 | } 54 | return instance; 55 | } 56 | 57 | private Settings () { 58 | base (BookwormApp.Constants.bookworm_id); 59 | this.shortcuts = new SettingsOfShortcuts (); 60 | } 61 | } 62 | 63 | public class BookwormApp.SettingsOfShortcuts : Granite.Services.Settings { 64 | 65 | public SettingsOfShortcuts () { 66 | base.with_path (BookwormApp.Constants.bookworm_shortcuts_id, "/com/github/babluboy/bookworm/shortcuts/"); 67 | } 68 | 69 | public string[] toggle_library_view { get; set; } 70 | public string[] move_library_page_backward { get; set; } 71 | public string[] move_library_page_forward { get; set; } 72 | public string[] return_to_library_view { get; set; } 73 | public string[] move_page_backward { get; set; } 74 | public string[] move_page_forward { get; set; } 75 | public string[] increase_zoom_level { get; set; } 76 | public string[] decrease_zoom_level { get; set; } 77 | public string[] toggle_bookmark { get; set; } 78 | public string[] unfullscreen { get; set; } 79 | public string[] toggle_fullscreen { get; set; } 80 | public string[] close_bookworm_completely { get; set; } 81 | public string[] focus_on_header_search_bar { get; set; } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-selection-option.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 21 | 25 | 29 | 33 | 34 | 43 | 44 | 46 | 47 | 49 | image/svg+xml 50 | 52 | 53 | 54 | 55 | 56 | 60 | 64 | 68 | 72 | 76 | 77 | -------------------------------------------------------------------------------- /data/icons/24/bookworm-selection-option.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 21 | 25 | 26 | 34 | 36 | 40 | 44 | 48 | 52 | 53 | 62 | 63 | 65 | 66 | 68 | image/svg+xml 69 | 71 | 72 | 73 | 74 | 75 | 79 | 83 | 87 | 91 | 95 | 96 | -------------------------------------------------------------------------------- /data/scripts/com.github.babluboy.bookworm.htmlscripts.functions.js: -------------------------------------------------------------------------------- 1 | var is_search_result_found = "false"; 2 | 3 | function setTwoPageView() { 4 | var lengthOfData = document.getElementsByTagName("BODY")[0].innerText.length; 5 | if(lengthOfData > 500){ 6 | document.getElementsByTagName("BODY")[0].className = "two_page"; 7 | } 8 | } 9 | 10 | function overlayAnnotation(annotationText) { 11 | annotationText = decodeURIComponent (annotationText); 12 | //attempt to find and highlight the whole phrase 13 | var originalAnnotationText = annotationText; 14 | findAndReplace(annotationText, ""+annotationText+""); 15 | while(is_search_result_found == "false" && annotationText.length > 2){ 16 | //whole phrase is not found, retry after removing one character from left and right 17 | annotationText = annotationText.slice(1, -1).trim(); 18 | findAndReplace(annotationText, ""+annotationText+""); 19 | } 20 | var resultText = document.getElementsByClassName("bookworm_annotation_highlight")[0]; 21 | resultText.scrollIntoView(); 22 | } 23 | 24 | function highlightText(highlightText) { 25 | highlightText = decodeURIComponent (highlightText); 26 | //attempt to find and highlight the whole phrase 27 | findAndReplace(highlightText, ""+highlightText+""); 28 | while(is_search_result_found == "false" && highlightText.length > 2){ 29 | //whole phrase is not found, retry after removing one character from left and right 30 | highlightText = highlightText.slice(1, -1).trim(); 31 | findAndReplace(highlightText, ""+highlightText+""); 32 | } 33 | //highlight phrase and scroll it to view 34 | var resultText = document.getElementsByClassName("bookworm_search_result_highlight")[0]; 35 | resultText.scrollIntoView(); 36 | } 37 | 38 | function scrollToSearchText(searchText) { 39 | findAndReplace(searchText, ""+searchText+""); 40 | var resultText = document.getElementsByClassName("bookworm_search_result_scroll")[0]; 41 | resultText.scrollIntoView(); 42 | } 43 | 44 | RegExp.escape= function(s) { 45 | return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); 46 | }; 47 | 48 | function findAndReplace(searchText, replacement, searchNode) { 49 | if (!searchText || typeof replacement === 'undefined') { 50 | // Throw error here if you want... 51 | return; 52 | } 53 | var regex = typeof searchText === 'string' ? 54 | new RegExp(RegExp.escape(searchText), 'ig') : searchText, 55 | childNodes = (searchNode || document.body).childNodes, 56 | cnLength = childNodes.length, 57 | excludes = 'html,head,style,title,link,meta,script,object,iframe'; 58 | while (cnLength--) { 59 | var currentNode = childNodes[cnLength]; 60 | if (currentNode.nodeType === 1 && 61 | (excludes + ',').indexOf(currentNode.nodeName.toLowerCase() + ',') === -1) { 62 | arguments.callee(searchText, replacement, currentNode); 63 | } 64 | if (currentNode.nodeType !== 3 || !regex.test(currentNode.data) ) { 65 | continue; 66 | } 67 | var parent = currentNode.parentNode, 68 | frag = (function(){ 69 | var matchedResult = new RegExp(RegExp.escape(searchText), 'ig').exec(currentNode.data); 70 | is_search_result_found = "true"; 71 | replacement = replacement.replace(searchText, matchedResult[0]), 72 | wrap = document.createElement('div'), 73 | frag = document.createDocumentFragment(); 74 | wrap.innerHTML = html; 75 | var html = currentNode.data.replace(regex, replacement), 76 | wrap = document.createElement('div'), 77 | frag = document.createDocumentFragment(); 78 | wrap.innerHTML = html; 79 | while (wrap.firstChild) { 80 | frag.appendChild(wrap.firstChild); 81 | } 82 | return frag; 83 | })(); 84 | parent.insertBefore(frag, currentNode); 85 | parent.removeChild(currentNode); 86 | } 87 | } 88 | function getSelectionText() { 89 | var text = ""; 90 | if (window.getSelection) { 91 | text = window.getSelection().toString(); 92 | } else if (document.selection && document.selection.type != "Control") { 93 | text = document.selection.createRange().text; 94 | } 95 | return text; 96 | } 97 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_uncompress.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import PY2, bchr, lmap, bstr 8 | 9 | if PY2: 10 | range = xrange 11 | 12 | import struct 13 | # note: struct pack, unpack, unpack_from all require bytestring format 14 | # data all the way up to at least python 2.7.5, python 3 okay with bytestring 15 | 16 | 17 | class unpackException(Exception): 18 | pass 19 | 20 | class UncompressedReader: 21 | 22 | def unpack(self, data): 23 | return data 24 | 25 | class PalmdocReader: 26 | 27 | def unpack(self, i): 28 | o, p = b'', 0 29 | while p < len(i): 30 | # for python 3 must use slice since i[p] returns int while slice returns character 31 | c = ord(i[p:p+1]) 32 | p += 1 33 | if (c >= 1 and c <= 8): 34 | o += i[p:p+c] 35 | p += c 36 | elif (c < 128): 37 | o += bchr(c) 38 | elif (c >= 192): 39 | o += b' ' + bchr(c ^ 128) 40 | else: 41 | if p < len(i): 42 | c = (c << 8) | ord(i[p:p+1]) 43 | p += 1 44 | m = (c >> 3) & 0x07ff 45 | n = (c & 7) + 3 46 | if (m > n): 47 | o += o[-m:n-m] 48 | else: 49 | for _ in range(n): 50 | # because of completely ass-backwards decision by python mainters for python 3 51 | # we must use slice for bytes as i[p] returns int while slice returns character 52 | if m == 1: 53 | o += o[-m:] 54 | else: 55 | o += o[-m:-m+1] 56 | return o 57 | 58 | class HuffcdicReader: 59 | q = struct.Struct(b'>Q').unpack_from 60 | 61 | def loadHuff(self, huff): 62 | if huff[0:8] != b'HUFF\x00\x00\x00\x18': 63 | raise unpackException('invalid huff header') 64 | off1, off2 = struct.unpack_from(b'>LL', huff, 8) 65 | 66 | def dict1_unpack(v): 67 | codelen, term, maxcode = v&0x1f, v&0x80, v>>8 68 | assert codelen != 0 69 | if codelen <= 8: 70 | assert term 71 | maxcode = ((maxcode + 1) << (32 - codelen)) - 1 72 | return (codelen, term, maxcode) 73 | self.dict1 = lmap(dict1_unpack, struct.unpack_from(b'>256L', huff, off1)) 74 | 75 | dict2 = struct.unpack_from(b'>64L', huff, off2) 76 | self.mincode, self.maxcode = (), () 77 | for codelen, mincode in enumerate((0,) + dict2[0::2]): 78 | self.mincode += (mincode << (32 - codelen), ) 79 | for codelen, maxcode in enumerate((0,) + dict2[1::2]): 80 | self.maxcode += (((maxcode + 1) << (32 - codelen)) - 1, ) 81 | 82 | self.dictionary = [] 83 | 84 | def loadCdic(self, cdic): 85 | if cdic[0:8] != b'CDIC\x00\x00\x00\x10': 86 | raise unpackException('invalid cdic header') 87 | phrases, bits = struct.unpack_from(b'>LL', cdic, 8) 88 | n = min(1<H').unpack_from 90 | def getslice(off): 91 | blen, = h(cdic, 16+off) 92 | slice = cdic[18+off:18+off+(blen&0x7fff)] 93 | return (slice, blen&0x8000) 94 | self.dictionary += lmap(getslice, struct.unpack_from(bstr('>%dH' % n), cdic, 16)) 95 | 96 | def unpack(self, data): 97 | q = HuffcdicReader.q 98 | 99 | bitsleft = len(data) * 8 100 | data += b"\x00\x00\x00\x00\x00\x00\x00\x00" 101 | pos = 0 102 | x, = q(data, pos) 103 | n = 32 104 | 105 | s = b'' 106 | while True: 107 | if n <= 0: 108 | pos += 4 109 | x, = q(data, pos) 110 | n += 32 111 | code = (x >> n) & ((1 << 32) - 1) 112 | 113 | codelen, term, maxcode = self.dict1[code >> 24] 114 | if not term: 115 | while code < self.mincode[codelen]: 116 | codelen += 1 117 | maxcode = self.maxcode[codelen] 118 | 119 | n -= codelen 120 | bitsleft -= codelen 121 | if bitsleft < 0: 122 | break 123 | 124 | r = (maxcode - code) >> (32 - codelen) 125 | slice, flag = self.dictionary[r] 126 | if not flag: 127 | self.dictionary[r] = None 128 | slice = self.unpack(slice) 129 | self.dictionary[r] = (slice, 1) 130 | s += slice 131 | return s 132 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-selection-checked.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 24 | 26 | 30 | 34 | 35 | 37 | 41 | 45 | 49 | 53 | 54 | 63 | 64 | 66 | 67 | 69 | image/svg+xml 70 | 72 | 73 | 74 | 75 | 76 | 80 | 84 | 88 | 92 | 96 | 97 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-object-select-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 37 | 39 | 41 | 45 | 49 | 53 | 57 | 58 | 67 | 68 | 70 | 71 | 73 | image/svg+xml 74 | 76 | 77 | 78 | 79 | 80 | 84 | 88 | 92 | 96 | 100 | 101 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_sectioner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import PY2, hexlify, bstr, bord, bchar 8 | 9 | import datetime 10 | 11 | if PY2: 12 | range = xrange 13 | 14 | # note: struct pack, unpack, unpack_from all require bytestring format 15 | # data all the way up to at least python 2.7.5, python 3 okay with bytestring 16 | import struct 17 | 18 | from .unipath import pathof 19 | 20 | DUMP = False 21 | """ Set to True to dump all possible information. """ 22 | 23 | class unpackException(Exception): 24 | pass 25 | 26 | 27 | def describe(data): 28 | txtans = '' 29 | hexans = hexlify(data) 30 | for i in data: 31 | if bord(i) < 32 or bord(i) > 127: 32 | txtans += '?' 33 | else: 34 | txtans += bchar(i).decode('latin-1') 35 | return '"' + txtans + '"' + ' 0x'+ hexans 36 | 37 | def datetimefrompalmtime(palmtime): 38 | if palmtime > 0x7FFFFFFF: 39 | pythondatetime = datetime.datetime(year=1904,month=1,day=1)+datetime.timedelta(seconds=palmtime) 40 | else: 41 | pythondatetime = datetime.datetime(year=1970,month=1,day=1)+datetime.timedelta(seconds=palmtime) 42 | return pythondatetime 43 | 44 | 45 | class Sectionizer: 46 | 47 | def __init__(self, filename): 48 | self.data = b'' 49 | with open(pathof(filename), 'rb') as f: 50 | self.data = f.read() 51 | self.palmheader = self.data[:78] 52 | self.palmname = self.data[:32] 53 | self.ident = self.palmheader[0x3C:0x3C+8] 54 | self.num_sections, = struct.unpack_from(b'>H', self.palmheader, 76) 55 | self.filelength = len(self.data) 56 | sectionsdata = struct.unpack_from(bstr('>%dL' % (self.num_sections*2)), self.data, 78) + (self.filelength, 0) 57 | self.sectionoffsets = sectionsdata[::2] 58 | self.sectionattributes = sectionsdata[1::2] 59 | self.sectiondescriptions = ["" for x in range(self.num_sections+1)] 60 | self.sectiondescriptions[-1] = "File Length Only" 61 | return 62 | 63 | def dumpsectionsinfo(self): 64 | print("Section Offset Length UID Attribs Description") 65 | for i in range(self.num_sections): 66 | print("%3d %3X 0x%07X 0x%05X % 8d % 7d %s" % (i,i, self.sectionoffsets[i], self.sectionoffsets[ 67 | i+1] - self.sectionoffsets[i], self.sectionattributes[i]&0xFFFFFF, (self.sectionattributes[i]>>24)&0xFF, self.sectiondescriptions[i])) 68 | print("%3d %3X 0x%07X %s" % 69 | (self.num_sections,self.num_sections, self.sectionoffsets[self.num_sections], self.sectiondescriptions[self.num_sections])) 70 | 71 | def setsectiondescription(self, section, description): 72 | if section < len(self.sectiondescriptions): 73 | self.sectiondescriptions[section] = description 74 | else: 75 | print("Section out of range: %d, description %s" % (section,description)) 76 | 77 | def dumppalmheader(self): 78 | print("Palm Database Header") 79 | print("Database name: " + repr(self.palmheader[:32])) 80 | dbattributes, = struct.unpack_from(b'>H', self.palmheader, 32) 81 | print("Bitfield attributes: 0x%0X" % dbattributes,) 82 | if dbattributes != 0: 83 | print(" (",) 84 | if (dbattributes & 2): 85 | print("Read-only; ",) 86 | if (dbattributes & 4): 87 | print("Dirty AppInfoArea; ",) 88 | if (dbattributes & 8): 89 | print("Needs to be backed up; ",) 90 | if (dbattributes & 16): 91 | print("OK to install over newer; ",) 92 | if (dbattributes & 32): 93 | print("Reset after installation; ",) 94 | if (dbattributes & 64): 95 | print("No copying by PalmPilot beaming; ",) 96 | print(")") 97 | else: 98 | print("") 99 | print("File version: %d" % struct.unpack_from(b'>H', self.palmheader, 34)[0]) 100 | dbcreation, = struct.unpack_from(b'>L', self.palmheader, 36) 101 | print("Creation Date: " + str(datetimefrompalmtime(dbcreation))+ (" (0x%0X)" % dbcreation)) 102 | dbmodification, = struct.unpack_from(b'>L', self.palmheader, 40) 103 | print("Modification Date: " + str(datetimefrompalmtime(dbmodification))+ (" (0x%0X)" % dbmodification)) 104 | dbbackup, = struct.unpack_from(b'>L', self.palmheader, 44) 105 | if dbbackup != 0: 106 | print("Backup Date: " + str(datetimefrompalmtime(dbbackup))+ (" (0x%0X)" % dbbackup)) 107 | print("Modification No.: %d" % struct.unpack_from(b'>L', self.palmheader, 48)[0]) 108 | print("App Info offset: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 52)[0]) 109 | print("Sort Info offset: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 56)[0]) 110 | print("Type/Creator: %s/%s" % (repr(self.palmheader[60:64]), repr(self.palmheader[64:68]))) 111 | print("Unique seed: 0x%0X" % struct.unpack_from(b'>L', self.palmheader, 68)[0]) 112 | expectedzero, = struct.unpack_from(b'>L', self.palmheader, 72) 113 | if expectedzero != 0: 114 | print("Should be zero but isn't: %d" % struct.unpack_from(b'>L', self.palmheader, 72)[0]) 115 | print("Number of sections: %d" % struct.unpack_from(b'>H', self.palmheader, 76)[0]) 116 | return 117 | 118 | def loadSection(self, section): 119 | before, after = self.sectionoffsets[section:section+2] 120 | return self.data[before:after] 121 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-go-next.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 25 | 27 | 31 | 35 | 39 | 43 | 44 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 57 | 61 | 65 | 69 | 73 | 74 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-go-previous.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 25 | 27 | 31 | 35 | 39 | 43 | 44 | 45 | 47 | 48 | 50 | image/svg+xml 51 | 53 | 54 | 55 | 56 | 57 | 61 | 65 | 69 | 73 | 74 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_pagemap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import PY2, unicode_str 8 | 9 | if PY2: 10 | range = xrange 11 | 12 | import struct 13 | # note: struct pack, unpack, unpack_from all require bytestring format 14 | # data all the way up to at least python 2.7.5, python 3 okay with bytestring 15 | 16 | import re 17 | # note: re requites the pattern to be the exact same type as the data to be searched in python3 18 | # but u"" is not allowed for the pattern itself only b"" 19 | 20 | 21 | _TABLE = [('m', 1000), ('cm', 900), ('d', 500), ('cd', 400), ('c', 100), ('xc', 90), ('l', 50), ('xl', 40), ('x', 10), ('ix', 9), ('v', 5), ('iv', 4), ('i', 1)] 22 | 23 | def int_to_roman(i): 24 | parts = [] 25 | num = i 26 | for letter, value in _TABLE: 27 | while value <= num: 28 | num -= value 29 | parts.append(letter) 30 | return ''.join(parts) 31 | 32 | def roman_to_int(s): 33 | result = 0 34 | rnstr = s 35 | for letter, value in _TABLE: 36 | while rnstr.startswith(letter): 37 | result += value 38 | rnstr = rnstr[len(letter):] 39 | return result 40 | 41 | _pattern = r'''\(([^\)]*)\)''' 42 | _tup_pattern = re.compile(_pattern,re.IGNORECASE) 43 | 44 | 45 | def _parseNames(numpages, data): 46 | data = unicode_str(data) 47 | pagenames = [] 48 | pageMap = '' 49 | for i in range(numpages): 50 | pagenames.append(None) 51 | for m in re.finditer(_tup_pattern, data): 52 | tup = m.group(1) 53 | if pageMap != '': 54 | pageMap += ',' 55 | pageMap += '(' + tup + ')' 56 | spos, nametype, svalue = tup.split(",") 57 | # print(spos, nametype, svalue) 58 | if nametype == 'a' or nametype == 'r': 59 | svalue = int(svalue) 60 | spos = int(spos) 61 | for i in range(spos - 1, numpages): 62 | if nametype == 'r': 63 | pname = int_to_roman(svalue) 64 | svalue += 1 65 | elif nametype == 'a': 66 | pname = "%s" % svalue 67 | svalue += 1 68 | elif nametype == 'c': 69 | sp = svalue.find('|') 70 | if sp == -1: 71 | pname = svalue 72 | else: 73 | pname = svalue[0:sp] 74 | svalue = svalue[sp+1:] 75 | else: 76 | print("Error: unknown page numbering type", nametype) 77 | pagenames[i] = pname 78 | return pagenames, pageMap 79 | 80 | 81 | class PageMapProcessor: 82 | 83 | def __init__(self, mh, data): 84 | self.mh = mh 85 | self.data = data 86 | self.pagenames = [] 87 | self.pageoffsets = [] 88 | self.pageMap = '' 89 | self.pm_len = 0 90 | self.pm_nn = 0 91 | self.pn_bits = 0 92 | self.pmoff = None 93 | self.pmstr = '' 94 | print("Extracting Page Map Information") 95 | rev_len, = struct.unpack_from(b'>L', self.data, 0x10) 96 | # skip over header, revision string length data, and revision string 97 | ptr = 0x14 + rev_len 98 | pm_1, self.pm_len, self.pm_nn, self.pm_bits = struct.unpack_from(b'>4H', self.data, ptr) 99 | # print(pm_1, self.pm_len, self.pm_nn, self.pm_bits) 100 | self.pmstr = self.data[ptr+8:ptr+8+self.pm_len] 101 | self.pmoff = self.data[ptr+8+self.pm_len:] 102 | offsize = b">L" 103 | offwidth = 4 104 | if self.pm_bits == 16: 105 | offsize = b">H" 106 | offwidth = 2 107 | ptr = 0 108 | for i in range(self.pm_nn): 109 | od, = struct.unpack_from(offsize, self.pmoff, ptr) 110 | ptr += offwidth 111 | self.pageoffsets.append(od) 112 | self.pagenames, self.pageMap = _parseNames(self.pm_nn, self.pmstr) 113 | 114 | def getPageMap(self): 115 | return self.pageMap 116 | 117 | def getNames(self): 118 | return self.pagenames 119 | 120 | def getOffsets(self): 121 | return self.pageoffsets 122 | 123 | # page-map.xml will be unicode but encoded to utf-8 immediately before being written to a file 124 | def generateKF8PageMapXML(self, k8proc): 125 | pagemapxml = '\n' 126 | for i in range(len(self.pagenames)): 127 | pos = self.pageoffsets[i] 128 | name = self.pagenames[i] 129 | if name is not None and name != "": 130 | [pn, dir, filename, skelpos, skelend, aidtext] = k8proc.getSkelInfo(pos) 131 | idtext = unicode_str(k8proc.getPageIDTag(pos)) 132 | linktgt = unicode_str(filename) 133 | if idtext != '': 134 | linktgt += '#' + idtext 135 | pagemapxml += '\n' % (name, dir, linktgt) 136 | pagemapxml += "\n" 137 | return pagemapxml 138 | 139 | def generateAPNX(self, apnx_meta): 140 | if apnx_meta['format'] == 'MOBI_8': 141 | content_header = '{"contentGuid":"%(contentGuid)s","asin":"%(asin)s","cdeType":"%(cdeType)s","format":"%(format)s","fileRevisionId":"1","acr":"%(acr)s"}' %apnx_meta 142 | else: 143 | content_header = '{"contentGuid":"%(contentGuid)s","asin":"%(asin)s","cdeType":"%(cdeType)s","fileRevisionId":"1"}' % apnx_meta 144 | content_header = content_header.encode('utf-8') 145 | page_header = '{"asin":"%(asin)s","pageMap":"%(pageMap)s"}' % apnx_meta 146 | page_header = page_header.encode('utf-8') 147 | apnx = struct.pack(b'>H',1) + struct.pack(b'>H',1) 148 | apnx += struct.pack(b'>I', 12 + len(content_header)) 149 | apnx += struct.pack(b'>I', len(content_header)) 150 | apnx += content_header 151 | apnx += struct.pack(b'>H', 1) 152 | apnx += struct.pack(b'>H', len(page_header)) 153 | apnx += struct.pack(b'>H', self.pm_nn) 154 | apnx += struct.pack(b'>H', 32) 155 | apnx += page_header 156 | for page in self.pageoffsets: 157 | apnx += struct.pack(b'>L', page) 158 | return apnx 159 | -------------------------------------------------------------------------------- /data/com.github.babluboy.bookworm.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | icons/14/com.github.babluboy.bookworm.svg 6 | 7 | icons/16/com.github.babluboy.bookworm.svg 8 | icons/16/bookworm-go-next.svg 9 | icons/16/bookworm-go-previous.svg 10 | icons/16/bookworm-object-select-symbolic.svg 11 | icons/16/bookworm-list-add.svg 12 | icons/16/bookworm-list-remove.svg 13 | icons/16/bookworm-view-grid-symbolic.svg 14 | icons/16/bookworm-view-list-symbolic.svg 15 | icons/16/bookworm-line-height-more.png 16 | icons/16/bookworm-line-height-less.png 17 | icons/16/bookworm-width-more.png 18 | icons/16/bookworm-width-less.png 19 | icons/16/bookworm_rating_1.png 20 | icons/16/bookworm_rating_2.png 21 | icons/16/bookworm_rating_3.png 22 | icons/16/bookworm_rating_4.png 23 | icons/16/bookworm_rating_5.png 24 | icons/16/bookworm-help-about-symbolic.svg 25 | icons/16/bookworm-help-about.svg 26 | icons/16/bookworm-selection-option.svg 27 | icons/16/bookworm-selection-checked.svg 28 | icons/16/bookworm-insert-image.svg 29 | icons/16/bookworm-help-info-symbolic.svg 30 | icons/16/bookworm-format-text-larger-symbolic.svg 31 | icons/16/bookworm-format-text-smaller-symbolic.svg 32 | icons/16/bookworm-format-justify-left.svg 33 | icons/16/bookworm-format-justify-right.svg 34 | 35 | icons/24/com.github.babluboy.bookworm.svg 36 | icons/24/bookworm-bookmark-inactive.png 37 | icons/24/bookworm-bookmark-active.png 38 | icons/24/bookworm-selection-checked.svg 39 | icons/24/bookworm-selection-option.svg 40 | icons/24/bookworm-open-menu.svg 41 | 42 | icons/32/com.github.babluboy.bookworm.svg 43 | 44 | icons/48/com.github.babluboy.bookworm.svg 45 | 46 | icons/64/com.github.babluboy.bookworm.svg 47 | 48 | icons/128/com.github.babluboy.bookworm.svg 49 | 50 | icons/default_covers/bookworm-placeholder-cover.svg 51 | icons/default_covers/bookworm-default-cover-1.svg 52 | icons/default_covers/bookworm-default-cover-2.svg 53 | icons/default_covers/bookworm-default-cover-3.svg 54 | icons/default_covers/bookworm-default-cover-4.svg 55 | icons/default_covers/bookworm-default-cover-5.svg 56 | 57 | icons/192/com.github.babluboy.bookworm.svg 58 | 59 | icons/256/com.github.babluboy.bookworm.svg 60 | 61 | scripts/com.github.babluboy.bookworm.htmlscripts.functions.js 62 | scripts/com.github.babluboy.bookworm.htmlscripts.styles.css 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /data/com.github.babluboy.bookworm.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0 5 | Save x co-ordinate of window position 6 | 7 | 8 | 0 9 | Save x co-ordinate of window position 10 | 11 | 12 | 1200 13 | The saved width of the window. 14 | 15 | 16 | 800 17 | The saved height of the window. 18 | 19 | 20 | false 21 | Save maximized state of window 22 | 23 | 24 | 0 25 | Save x co-ordinate of window position 26 | 27 | 28 | 0 29 | Save y co-ordinate of window position 30 | 31 | 32 | 1.0 33 | Save Zoom Level of WebView 34 | 35 | 36 | "PROFILE1" 37 | Save the reading profile 38 | 39 | 40 | true 41 | Local storage for extracted contents of books 42 | 43 | 44 | false 45 | Always show the library view on start 46 | 47 | 48 | false 49 | Enable dark theme 50 | 51 | 52 | "1" 53 | Save the page width for reading 54 | 55 | 56 | "150" 57 | Save the page line height for reading 58 | 59 | 60 | "left" 61 | Save the page text alignment for reading 62 | 63 | 64 | "georgia" 65 | Save the choosen font for reading 66 | 67 | 68 | "georgia 12" 69 | Save the choosen font for reading 70 | 71 | 72 | 12 73 | Save the choosen font size for reading 74 | 75 | 76 | false 77 | Enable two page view 78 | 79 | 80 | false 81 | Enable leaf over page by clicking on edge 82 | 83 | 84 | "LIBRARY_MODE_GRID" 85 | Save the library view mode 86 | 87 | 88 | false 89 | Save fullscreen state 90 | 91 | 92 | "#000000,#fbfbfb,#E8ED00,#586e75,#fdf6e3,#87FF2B,#93a1a1,#002b36,#3465A4" 93 | 94 | List of profile colours. Each profiles has three colors - First color is text color, second one is bgcolor and third color is highlight color. The first three sets are customizable and the last two sets are the default light and dark profiles 95 | 96 | 97 | 98 | "" 99 | List of directories to be scanned for new books 100 | 101 | 102 | "" 103 | Save the path of the book being read if the user closes the app while reading 104 | 105 | 106 | "content-list" 107 | Save the last viewed info tab 108 | 109 | 110 | 21 111 | Number of books to show per library page in both grid and list views 112 | 113 | 114 | 115 | 116 | 117 | ["v 4", "V 4"] 118 | toggleLibraryView 119 | 120 | 121 | ["Left 0"] 122 | moveLibraryPageBackward 123 | 124 | 125 | ["Right 0"] 126 | moveLibraryPageForward 127 | 128 | 129 | ["l 4", "L 4"] 130 | returnToLibraryView 131 | 132 | 133 | ["Left 0"] 134 | movePageBackward 135 | 136 | 137 | ["Right 0"] 138 | movePageForward 139 | 140 | 141 | ["plus 4", "plus 5"] 142 | increaseZoomLevel 143 | 144 | 145 | ["minus 4"] 146 | decreaseZoomLevel 147 | 148 | 149 | ["d 4", "D 4"] 150 | toggleBookmark 151 | 152 | 153 | ["Escape 0"] 154 | unfullscreen 155 | 156 | 157 | ["F11 0"] 158 | toggleFullScreen 159 | 160 | 161 | ["q 4", "Q 4"] 162 | closeBookwormCompletely 163 | 164 | 165 | ["f 4", "F 4"] 166 | focusOnHeaderSearchBar 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/comicsReader.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com) 2 | * 3 | * This file is part of Bookworm and is used for parsing comics 4 | * file formats like .cbz, .cbr 5 | * 6 | * Bookworm is free software: you can redistribute it 7 | * and/or modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation, either version 3 of the 9 | * License, or (at your option) any later version. 10 | * 11 | * Bookworm is distributed in the hope that it will be 12 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 14 | * Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with Bookworm. If not, see http://www.gnu.org/licenses/. 18 | */ 19 | using Gee; 20 | public class BookwormApp.comicsReader { 21 | public static BookwormApp.Book parseComicsBook (owned BookwormApp.Book aBook, string comicsFileType) { 22 | info ("[START] [FUNCTION:parseComicsBook] book.location=" + aBook.getBookLocation () + "comicsFileType=" + comicsFileType); 23 | //Only parse the eBook if it has not been parsed already 24 | if (!aBook.getIsBookParsed ()) { 25 | //Extract the content of the comics book based on file type 26 | string extractionLocation = extractComics (aBook.getBookLocation (),comicsFileType); 27 | if ("false" == extractionLocation) { //handle error condition 28 | aBook.setIsBookParsed (false); 29 | aBook.setParsingIssue (BookwormApp.Constants.TEXT_FOR_EXTRACTION_ISSUE); 30 | return aBook; 31 | } else { 32 | aBook.setBookExtractionLocation (extractionLocation); 33 | } 34 | //Store the list of comic book images in the correct order of reading 35 | aBook = getContentList (aBook, extractionLocation); 36 | if (aBook.getBookContentList ().size < 1) { 37 | //No content has been determined for the book 38 | aBook.setIsBookParsed (false); 39 | aBook.setParsingIssue (BookwormApp.Constants.TEXT_FOR_EXTRACTION_ISSUE); 40 | return aBook; 41 | } 42 | } 43 | //Use the file name as book title 44 | if (aBook.getBookTitle () != null && aBook.getBookTitle ().length < 1) { 45 | string bookTitle = File.new_for_path (aBook.getBookLocation ()).get_basename (); 46 | if (bookTitle.last_index_of (".") != -1) { 47 | bookTitle = bookTitle.slice (0, bookTitle.last_index_of (".")); 48 | } 49 | aBook.setBookTitle (bookTitle); 50 | debug ("File name set as Title:" + bookTitle); 51 | } 52 | aBook.setIsBookParsed (true); 53 | info ("[END] [FUNCTION:parseComicsBook]"); 54 | return aBook; 55 | } 56 | 57 | public static string extractComics (string eBookLocation, string comicsFileType) { 58 | info ("[START] [FUNCTION:extractComics] eBookLocation=" + eBookLocation + ", comicsFileType=" + comicsFileType); 59 | string extractionLocation = "false"; 60 | if (BookwormApp.Bookworm.settings == null) { 61 | BookwormApp.Bookworm.settings = BookwormApp.Settings.get_instance (); 62 | } 63 | //create a location for extraction of eBook based on local storage prefference 64 | if (BookwormApp.Bookworm.settings.is_local_storage_enabled) { 65 | extractionLocation = BookwormApp.Bookworm.bookworm_config_path + "/books/" + File.new_for_path (eBookLocation).get_basename (); 66 | } else { 67 | extractionLocation = BookwormApp.Constants.EBOOK_EXTRACTION_LOCATION + File.new_for_path (eBookLocation).get_basename (); 68 | } 69 | //check and create directory for extracting contents of ebook 70 | BookwormApp.Utils.fileOperations ("CREATEDIR", extractionLocation, "", ""); 71 | //extract eBook contents into extraction location 72 | switch (comicsFileType) { 73 | case ".CBR": 74 | BookwormApp.Utils.execute_sync_command ("unar -D -o \"" + extractionLocation + "/images/" + "\" \"" + eBookLocation + "\""); 75 | break; 76 | case ".CBZ": 77 | BookwormApp.Utils.execute_sync_command ("unzip -j -o \"" + eBookLocation + "\" -d \"" + extractionLocation + "/images/" + "\""); 78 | break; 79 | default: 80 | break; 81 | } 82 | info ("[END] [FUNCTION:extractComics] extractionLocation=" + eBookLocation); 83 | return extractionLocation; 84 | } 85 | 86 | public static BookwormApp.Book getContentList (owned BookwormApp.Book aBook, string extractionLocation) { 87 | info ("[START] [FUNCTION:getContentList] book.location=" + aBook.getBookLocation () + "extractionLocation=" + extractionLocation); 88 | //list the content of the extraction folder 89 | string comicContent = BookwormApp.Utils.execute_sync_command ("find \"" + extractionLocation + "/images/" + "\" -type f"); 90 | comicContent = comicContent.replace ("\r", "^^^").replace ("\n", "^^^"); 91 | string[] comicContentList = comicContent.split ("^^^"); 92 | //sort by file names to order the images 93 | GLib.qsort_with_data (comicContentList, sizeof (string), (a, b) => GLib.strcmp (a, b)); 94 | if (comicContentList.length > 1) { 95 | int countOfSections = 1; 96 | StringBuilder htmlFileName = new StringBuilder (); 97 | foreach (string contentLocationPath in comicContentList) { 98 | //TO-DO: Add a better logic to list pages by page number in file name 99 | if (contentLocationPath != null && contentLocationPath.length > 0) { 100 | //create a HTML content with the location of the image 101 | htmlFileName.assign (File.new_for_path (aBook.getBookLocation ()).get_basename () + "_" + countOfSections.to_string () + ".html"); 102 | BookwormApp.Utils.fileOperations ( 103 | "WRITE", extractionLocation, htmlFileName.str, BookwormApp.Constants.COMICS_HTML_TEMPLATE 104 | .replace ("", extractionLocation + "/images/" + contentLocationPath)); 105 | aBook.setBookContentList (extractionLocation + "/" + htmlFileName.str); 106 | //Set the first image as the cover for the comics 107 | if (countOfSections == 1) { 108 | if (!aBook.getIsBookCoverImagePresent ()) { 109 | debug ("setting cover as:" + contentLocationPath); 110 | aBook = BookwormApp.Utils.setBookCoverImage (aBook, contentLocationPath); 111 | } 112 | } 113 | countOfSections++; 114 | } else { 115 | aBook.setIsBookParsed (false); 116 | aBook.setParsingIssue (BookwormApp.Constants.TEXT_FOR_EXTRACTION_ISSUE); 117 | } 118 | } 119 | } 120 | info ("[END] [FUNCTION:getContentList]"); 121 | return aBook; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-format-justify-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 21 | 25 | 26 | 35 | 44 | 53 | 61 | 62 | 64 | 65 | 67 | image/svg+xml 68 | 70 | 71 | 72 | 73 | 74 | 76 | 80 | 84 | 88 | 92 | 96 | 100 | 104 | 108 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_nav.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import unicode_str 8 | import os 9 | from .unipath import pathof 10 | 11 | import re 12 | # note: re requites the pattern to be the exact same type as the data to be searched in python3 13 | # but u"" is not allowed for the pattern itself only b"" 14 | 15 | DEBUG_NAV = False 16 | 17 | FORCE_DEFAULT_TITLE = False 18 | """ Set to True to force to use the default title. """ 19 | 20 | NAVIGATION_FINENAME = 'nav.xhtml' 21 | """ The name for the navigation document. """ 22 | 23 | DEFAULT_TITLE = 'Navigation' 24 | """ The default title for the navigation document. """ 25 | 26 | class NAVProcessor(object): 27 | 28 | def __init__(self, files): 29 | self.files = files 30 | self.navname = NAVIGATION_FINENAME 31 | 32 | def buildLandmarks(self, guidetext): 33 | header = '' 34 | header += ' \n' 41 | 42 | type_map = { 43 | 'cover' : 'cover', 44 | 'title-page' : 'title-page', 45 | # ?: 'frontmatter', 46 | 'text' : 'bodymatter', 47 | # ?: 'backmatter', 48 | 'toc' : 'toc', 49 | 'loi' : 'loi', 50 | 'lot' : 'lot', 51 | 'preface' : 'preface', 52 | 'bibliography' : 'bibliography', 53 | 'index' : 'index', 54 | 'glossary' : 'glossary', 55 | 'acknowledgements' : 'acknowledgements', 56 | 'colophon' : None, 57 | 'copyright-page' : None, 58 | 'dedication' : None, 59 | 'epigraph' : None, 60 | 'foreword' : None, 61 | 'notes' : None 62 | } 63 | 64 | re_type = re.compile(r'\s+type\s*=\s*"(.*?)"', re.I) 65 | re_title = re.compile(r'\s+title\s*=\s*"(.*?)"', re.I) 66 | re_link = re.compile(r'\s+href\s*=\s*"(.*?)"', re.I) 67 | dir_ = os.path.relpath(self.files.k8text, self.files.k8oebps).replace('\\', '/') 68 | 69 | data = '' 70 | references = re.findall(r'', unicode_str(guidetext), re.I) 71 | for reference in references: 72 | mo_type = re_type.search(reference) 73 | mo_title = re_title.search(reference) 74 | mo_link = re_link.search(reference) 75 | if mo_type is not None: 76 | type_ = type_map.get(mo_type.group(1), None) 77 | else: 78 | type_ = None 79 | if mo_title is not None: 80 | title = mo_title.group(1) 81 | else: 82 | title = None 83 | if mo_link is not None: 84 | link = mo_link.group(1) 85 | else: 86 | link = None 87 | 88 | if type_ is not None and title is not None and link is not None: 89 | link = os.path.relpath(link, dir_).replace('\\', '/') 90 | data += element.format(type_, link, title) 91 | if len(data) > 0: 92 | return header + data + footer 93 | else: 94 | return '' 95 | 96 | def buildTOC(self, indx_data): 97 | header = '' 98 | header += ' \n' 101 | 102 | # recursive part 103 | def recursINDX(max_lvl=0, num=0, lvl=0, start=-1, end=-1): 104 | if start>len(indx_data) or end>len(indx_data): 105 | print("Warning (in buildTOC): missing INDX child entries", start, end, len(indx_data)) 106 | return '' 107 | if DEBUG_NAV: 108 | print("recursINDX (in buildTOC) lvl %d from %d to %d" % (lvl, start, end)) 109 | xhtml = '' 110 | if start <= 0: 111 | start = 0 112 | if end <= 0: 113 | end = len(indx_data) 114 | if lvl > max_lvl: 115 | max_lvl = lvl 116 | 117 | indent1 = ' ' * (2 + lvl * 2) 118 | indent2 = ' ' * (3 + lvl * 2) 119 | xhtml += indent1 + '
    \n' 120 | for i in range(start, end): 121 | e = indx_data[i] 122 | htmlfile = e['filename'] 123 | desttag = e['idtag'] 124 | text = e['text'] 125 | if not e['hlvl'] == lvl: 126 | continue 127 | num += 1 128 | if desttag == '': 129 | link = htmlfile 130 | else: 131 | link = '{:s}#{:s}'.format(htmlfile, desttag) 132 | xhtml += indent2 + '
  1. ' 133 | entry = '{:s}'.format(link, text) 134 | xhtml += entry 135 | # recurs 136 | if e['child1'] >= 0: 137 | xhtml += '\n' 138 | xhtmlrec, max_lvl, num = recursINDX(max_lvl, num, lvl + 1, 139 | e['child1'], e['childn'] + 1) 140 | xhtml += xhtmlrec 141 | xhtml += indent2 142 | # close entry 143 | xhtml += '
  2. \n' 144 | xhtml += indent1 + '
\n' 145 | return xhtml, max_lvl, num 146 | 147 | data, max_lvl, num = recursINDX() 148 | if not len(indx_data) == num: 149 | print("Warning (in buildTOC): different number of entries in NCX", len(indx_data), num) 150 | return header + data + footer 151 | 152 | def buildNAV(self, ncx_data, guidetext, title, lang): 153 | print("Building Navigation Document.") 154 | if FORCE_DEFAULT_TITLE: 155 | title = DEFAULT_TITLE 156 | nav_header = '' 157 | nav_header += '\n' 158 | nav_header += ' 2 | 3 | 4 | com.github.babluboy.bookworm 5 | CC0-1.0 6 | bookworm 7 | GPL-3.0+ 8 | Bookworm 9 | A focused eBook reader 10 | 11 |

Read the books you love without having to worry about the different format complexities like EPUB, PDF, MOBI, CBR, etc.

12 |

Manage your library by tagging and updating metadata on books, to quickly find books using metadata searching and tag based filtering.

13 |

This version supports EPUB, MOBI, PRC, PDF, FB2 and comics (CBR and CBZ) formats with support for more formats to follow soon.

14 |
15 | 16 | 17 | Bookworm Library View 18 | https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormLibraryView.png 19 | 20 | 21 | Bookworm Reading View 22 | https://raw.githubusercontent.com/babluboy/bookworm/gh-pages/images/BookwormReadingView.png 23 | 24 | 25 | 26 | com.github.babluboy.bookworm 27 | 28 | 29 | 30 | 31 |

This release has some new features, fixes and new translations:

32 |

New Features:

33 |
    34 |
  • Library is paginated to load large libraries quickly
  • 35 |
  • Support for FB2 Format - .fb2 and .fb2.zip
  • 36 |
37 |

Fixes:

38 |
    39 |
  • Intuitive representation of book removal button on library
  • 40 |
  • Fix for keeping the preference dialog open until all adju ments are completed
  • 41 |
  • Fix for header title changing during open
  • 42 |
  • Fix for handling cancelation of dialog to open an ebook
  • 43 |
  • Fix for toggling between full screen mode with F11
  • 44 |
  • Fix for annotation dialog for books with empty titles
  • 45 |
  • Fix for returning to the page position from the info view
  • 46 |
47 |
48 |
49 | 50 | 51 |

This release has some new features, fixes and new translations:

52 |

New Features:

53 |
    54 |
  • Right click a selected word and look up its meaning in an online dictionary
  • 55 |
  • Added option to customize highlight colour
  • 56 |
57 |

Fixes:

58 |
    59 |
  • Prevent Bookworm from opening links like HTTP, FTP etc. and use the default browser to open them
  • 60 |
  • Fixed an issue where the lines overlapped for the first couple of pages in the book
  • 61 |
  • Use of GResource to speed up loading of icons, scripts, etc.
  • 62 |
  • Some minor CSS compatibility with Juno
  • 63 |
64 |
65 |
66 | 67 | 68 |

Right to Left Reading

69 |
    70 |
  • Support for right-to-left script
  • 71 |
  • A shiny new icon and new cover images
  • 72 |
  • Better support for EPUB table of contents
  • 73 |
74 |
75 |
76 | 77 | 78 |

Annotations and two page views

79 |
80 |
81 | 82 | 83 |

Support for MOBI format and UX improvements

84 |
85 |
86 | 87 | 88 |

UX improvements for list view and other preference customizations

89 |
90 |
91 | 92 | 93 |

Comics support and UX improvements

94 |
95 |
96 | 97 | 98 |

PDF support and UX improvements

99 |
100 |
101 | 102 | 103 |

Initial release with support for EPUB format

104 |
105 |
106 |
107 | Siddhartha Das 108 | https://babluboy.github.io/bookworm 109 | https://github.com/babluboy/bookworm/issues 110 | https://github.com/babluboy/bookworm/wiki 111 | bablu.boy_AT_gmail.com 112 | 113 | #e29ffc 114 | #260063 115 | 2 116 | pk_live_i8u8qHGI7MRdJLNAdPYmewBc 117 | 118 | 119 | none 120 | none 121 | none 122 | none 123 | none 124 | none 125 | none 126 | none 127 | none 128 | none 129 | none 130 | none 131 | none 132 | none 133 | none 134 | none 135 | none 136 | none 137 | none 138 | none 139 | none 140 | none 141 | none 142 | none 143 | none 144 | none 145 | none 146 | 147 |
148 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/unpack_structure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import text_type 8 | 9 | from . import unipath 10 | from .unipath import pathof 11 | 12 | DUMP = False 13 | """ Set to True to dump all possible information. """ 14 | 15 | import os 16 | 17 | import re 18 | # note: re requites the pattern to be the exact same type as the data to be searched in python3 19 | # but u"" is not allowed for the pattern itself only b"" 20 | 21 | import zipfile 22 | import binascii 23 | from .mobi_utils import mangle_fonts 24 | 25 | class unpackException(Exception): 26 | pass 27 | 28 | class ZipInfo(zipfile.ZipInfo): 29 | 30 | def __init__(self, *args, **kwargs): 31 | if 'compress_type' in kwargs: 32 | compress_type = kwargs.pop('compress_type') 33 | super(ZipInfo, self).__init__(*args, **kwargs) 34 | self.compress_type = compress_type 35 | 36 | class fileNames: 37 | 38 | def __init__(self, infile, outdir): 39 | self.infile = infile 40 | self.outdir = outdir 41 | if not unipath.exists(self.outdir): 42 | unipath.mkdir(self.outdir) 43 | self.mobi7dir = os.path.join(self.outdir,'mobi7') 44 | if not unipath.exists(self.mobi7dir): 45 | unipath.mkdir(self.mobi7dir) 46 | self.imgdir = os.path.join(self.mobi7dir, 'Images') 47 | if not unipath.exists(self.imgdir): 48 | unipath.mkdir(self.imgdir) 49 | self.hdimgdir = os.path.join(self.outdir,'HDImages') 50 | if not unipath.exists(self.hdimgdir): 51 | unipath.mkdir(self.hdimgdir) 52 | self.outbase = os.path.join(self.outdir, os.path.splitext(os.path.split(infile)[1])[0]) 53 | 54 | def getInputFileBasename(self): 55 | return os.path.splitext(os.path.basename(self.infile))[0] 56 | 57 | def makeK8Struct(self): 58 | self.k8dir = os.path.join(self.outdir,'mobi8') 59 | if not unipath.exists(self.k8dir): 60 | unipath.mkdir(self.k8dir) 61 | self.k8metainf = os.path.join(self.k8dir,'META-INF') 62 | if not unipath.exists(self.k8metainf): 63 | unipath.mkdir(self.k8metainf) 64 | self.k8oebps = os.path.join(self.k8dir,'OEBPS') 65 | if not unipath.exists(self.k8oebps): 66 | unipath.mkdir(self.k8oebps) 67 | self.k8images = os.path.join(self.k8oebps,'Images') 68 | if not unipath.exists(self.k8images): 69 | unipath.mkdir(self.k8images) 70 | self.k8fonts = os.path.join(self.k8oebps,'Fonts') 71 | if not unipath.exists(self.k8fonts): 72 | unipath.mkdir(self.k8fonts) 73 | self.k8styles = os.path.join(self.k8oebps,'Styles') 74 | if not unipath.exists(self.k8styles): 75 | unipath.mkdir(self.k8styles) 76 | self.k8text = os.path.join(self.k8oebps,'Text') 77 | if not unipath.exists(self.k8text): 78 | unipath.mkdir(self.k8text) 79 | 80 | # recursive zip creation support routine 81 | def zipUpDir(self, myzip, tdir, localname): 82 | currentdir = tdir 83 | if localname != "": 84 | currentdir = os.path.join(currentdir,localname) 85 | list = unipath.listdir(currentdir) 86 | for file in list: 87 | afilename = file 88 | localfilePath = os.path.join(localname, afilename) 89 | realfilePath = os.path.join(currentdir,file) 90 | if unipath.isfile(realfilePath): 91 | myzip.write(pathof(realfilePath), pathof(localfilePath), zipfile.ZIP_DEFLATED) 92 | elif unipath.isdir(realfilePath): 93 | self.zipUpDir(myzip, tdir, localfilePath) 94 | 95 | def makeEPUB(self, usedmap, obfuscate_data, uid): 96 | bname = os.path.join(self.k8dir, self.getInputFileBasename() + '.epub') 97 | # Create an encryption key for Adobe font obfuscation 98 | # based on the epub's uid 99 | if isinstance(uid,text_type): 100 | uid = uid.encode('ascii') 101 | if obfuscate_data: 102 | key = re.sub(br'[^a-fA-F0-9]', b'', uid) 103 | key = binascii.unhexlify((key + key)[:32]) 104 | 105 | # copy over all images and fonts that are actually used in the ebook 106 | # and remove all font files from mobi7 since not supported 107 | imgnames = unipath.listdir(self.imgdir) 108 | for name in imgnames: 109 | if usedmap.get(name,'not used') == 'used': 110 | filein = os.path.join(self.imgdir,name) 111 | if name.endswith(".ttf"): 112 | fileout = os.path.join(self.k8fonts,name) 113 | elif name.endswith(".otf"): 114 | fileout = os.path.join(self.k8fonts,name) 115 | elif name.endswith(".failed"): 116 | fileout = os.path.join(self.k8fonts,name) 117 | else: 118 | fileout = os.path.join(self.k8images,name) 119 | data = b'' 120 | with open(pathof(filein),'rb') as f: 121 | data = f.read() 122 | if obfuscate_data: 123 | if name in obfuscate_data: 124 | data = mangle_fonts(key, data) 125 | open(pathof(fileout),'wb').write(data) 126 | if name.endswith(".ttf") or name.endswith(".otf"): 127 | os.remove(pathof(filein)) 128 | 129 | # opf file name hard coded to "content.opf" 130 | container = '\n' 131 | container += '\n' 132 | container += ' \n' 133 | container += '' 134 | container += ' \n\n' 135 | fileout = os.path.join(self.k8metainf,'container.xml') 136 | with open(pathof(fileout),'wb') as f: 137 | f.write(container.encode('utf-8')) 138 | 139 | if obfuscate_data: 140 | encryption = '\n' 142 | for font in obfuscate_data: 143 | encryption += ' \n' 144 | encryption += ' \n' 145 | encryption += ' \n' 146 | encryption += ' \n' 147 | encryption += ' \n' 148 | encryption += ' \n' 149 | encryption += '\n' 150 | fileout = os.path.join(self.k8metainf,'encryption.xml') 151 | with open(pathof(fileout),'wb') as f: 152 | f.write(encryption.encode('utf-8')) 153 | 154 | # ready to build epub 155 | self.outzip = zipfile.ZipFile(pathof(bname), 'w') 156 | 157 | # add the mimetype file uncompressed 158 | mimetype = b'application/epub+zip' 159 | fileout = os.path.join(self.k8dir,'mimetype') 160 | with open(pathof(fileout),'wb') as f: 161 | f.write(mimetype) 162 | nzinfo = ZipInfo('mimetype', compress_type=zipfile.ZIP_STORED) 163 | nzinfo.external_attr = 0o600 << 16 # make this a normal file 164 | self.outzip.writestr(nzinfo, mimetype) 165 | self.zipUpDir(self.outzip,self.k8dir,'META-INF') 166 | self.zipUpDir(self.outzip,self.k8dir,'OEBPS') 167 | self.outzip.close() 168 | -------------------------------------------------------------------------------- /src/backgroundTasks.vala: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 Siddhartha Das (bablu.boy@gmail.com) 2 | * 3 | * This file is part of Bookworm and performs background 4 | * related tasks like discovering books, cleaning cached data, etc. 5 | * This code does not require a GUI/Display 6 | * This code should be called from a sheduled job as "bookworm --discover" 7 | * 8 | * Bookworm is free software: you can redistribute it 9 | * and/or modify it under the terms of the GNU General Public License as 10 | * published by the Free Software Foundation, either version 3 of the 11 | * License, or (at your option) any later version. 12 | * 13 | * Bookworm is distributed in the hope that it will be 14 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 16 | * Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with Bookworm. If not, see http://www.gnu.org/licenses/. 20 | */ 21 | using Gee; 22 | public class BookwormApp.BackgroundTasks { 23 | public static ArrayList listOfBooks; 24 | public static BookwormApp.Settings settings; 25 | 26 | public static void performTasks () { 27 | initialization (); 28 | //Add any new books from the watched folders 29 | discoverBooks (); 30 | //refresh list of books in DB due to new books being added 31 | listOfBooks = BookwormApp.DB.getBookIDListFromDB (); 32 | //Remove cahched data and cover thumbs which are no longer used 33 | cleanBookCacheContent (); 34 | cleanBookCoverImages (); 35 | } 36 | 37 | public static void initialization () { 38 | settings = BookwormApp.Settings.get_instance (); 39 | //check if the database exists otherwise create database and required tables 40 | BookwormApp.DB.initializeBookWormDB (BookwormApp.Bookworm.bookworm_config_path); 41 | listOfBooks = BookwormApp.DB.getBookIDListFromDB (); 42 | } 43 | 44 | public static void discoverBooks () { 45 | print ("\nStarted process for discovery of books...."); 46 | ArrayList scanDirList = new ArrayList (); 47 | //find the folders to scan from the settings 48 | if (settings != null && settings.list_of_scan_dirs != null && settings.list_of_scan_dirs.length > 1) { 49 | debug (settings.list_of_scan_dirs); 50 | string[] scanDirArray = settings.list_of_scan_dirs.split ("~~"); 51 | foreach (string token in scanDirArray) { 52 | scanDirList.add (token); 53 | } 54 | 55 | if (scanDirList.size > 0) { 56 | //create the find command 57 | StringBuilder findCmd = new StringBuilder ("find "); 58 | foreach (string scanDir in scanDirList) { 59 | if (scanDir != null && scanDir.length > 1) { 60 | findCmd.append ("\"").append (scanDir).append ("\"").append (" "); 61 | } 62 | } 63 | findCmd.append ("! -readable -prune -o -type f \\( -iname \\*.mobi -o -iname \\*.pdf -o -iname \\*.epub -o -iname \\*.cbr -o -iname \\*.cbz \\) -print"); 64 | string findCmdOutput = BookwormApp.Utils.execute_sync_command (findCmd.str); 65 | if (findCmdOutput.contains ("\n")) { 66 | string[] findCmdOutputResults = findCmdOutput.strip ().split ("\n",-1); 67 | foreach (string findResult in findCmdOutputResults) { 68 | bool noMatchFound = true; 69 | foreach (string book in listOfBooks) { 70 | if (book.contains (findResult)) { 71 | noMatchFound = false; 72 | break; 73 | } 74 | } 75 | if (noMatchFound) { 76 | print ("\nAttempting to add book located at:" + findResult); 77 | BookwormApp.Book aBook = new BookwormApp.Book (); 78 | aBook.setBookLocation (findResult); 79 | File eBookFile = File.new_for_path (findResult); 80 | if (eBookFile.query_exists () && eBookFile.query_file_type (0) != FileType.DIRECTORY) { 81 | int bookID = BookwormApp.DB.addBookToDataBase (aBook); 82 | aBook.setBookId (bookID); 83 | aBook.setBookLastModificationDate ((new DateTime.now_utc ().to_unix ()).to_string ()); 84 | aBook.setWasBookOpened (true); 85 | //parse eBook to populate cache and book meta data 86 | aBook = BookwormApp.Bookworm.genericParser (aBook); 87 | if (!aBook.getIsBookParsed ()) { 88 | BookwormApp.DB.removeBookFromDB (aBook); 89 | } else { 90 | BookwormApp.DB.updateBookToDataBase (aBook); 91 | print ("\nSuccessfully added book located at:" + findResult); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | print ("\nCompleted process for discovery of books....\n"); 98 | } 99 | } 100 | } 101 | 102 | public static void cleanBookCacheContent () { 103 | print ("\nStarting to delete un-necessary cache data..."); 104 | //list the folders in the cache 105 | string cacheFolders = BookwormApp.Utils.execute_sync_command ("ls -1 " + BookwormApp.Bookworm.bookworm_config_path + "/books/"); 106 | cacheFolders = cacheFolders.replace ("\r", "^^^").replace ("\n", "^^^"); 107 | string[] cacheFolderList = cacheFolders.split ("^^^"); 108 | //loop through each folder name 109 | bool folderMatched = false; 110 | foreach (string cacheFolder in cacheFolderList) { 111 | folderMatched = false; 112 | cacheFolder = cacheFolder.strip (); 113 | if (cacheFolder == null || cacheFolder.length < 1) { 114 | folderMatched = true; 115 | } 116 | foreach (string bookData in listOfBooks) { 117 | if (cacheFolder != null && cacheFolder.length > 0) { 118 | //check if the folder is part of a book in the library 119 | if ((bookData.split ("::")[1]).index_of (cacheFolder) != -1) { 120 | folderMatched = true; 121 | break; 122 | } 123 | } 124 | } 125 | if (!folderMatched) { 126 | //delete the folder and content if it is not a part of any book in the library 127 | BookwormApp.Utils.execute_sync_command ("rm -Rf \"" + BookwormApp.Bookworm.bookworm_config_path + "/books/" + cacheFolder + "\""); 128 | print ("\nCache Folder deleted:" + cacheFolder); 129 | } 130 | } 131 | } 132 | 133 | public static void cleanBookCoverImages () { 134 | print ("\nStarting to delete un-necessary cover image data..."); 135 | //list the cover images in the cache 136 | string cacheImages = BookwormApp.Utils.execute_sync_command ("ls -1 " + BookwormApp.Bookworm.bookworm_config_path + "/covers/"); 137 | cacheImages = cacheImages.replace ("\r", "^^^").replace ("\n", "^^^"); 138 | string[] cacheImageList = cacheImages.split ("^^^"); 139 | //loop through each cover image in cache 140 | bool imageMatched = false; 141 | foreach (string cacheImage in cacheImageList) { 142 | imageMatched = false; 143 | cacheImage = cacheImage.strip (); 144 | if (cacheImage == null || cacheImage.length < 1) { 145 | imageMatched = true; 146 | } 147 | foreach (string bookData in listOfBooks) { 148 | if (cacheImage != null && cacheImage.length > 0) { 149 | //check if the folder is part of a book in the library 150 | if (cacheImage.index_of ((bookData.split ("::")[0])) != -1) { 151 | imageMatched = true; 152 | break; 153 | } 154 | } 155 | } 156 | if (!imageMatched) { 157 | //delete the folder and content if it is not a part of any book in the library 158 | BookwormApp.Utils.execute_sync_command ("rm -f \"" + BookwormApp.Bookworm.bookworm_config_path + "/covers/" + cacheImage + "\""); 159 | print ("\nCache Image deleted:" + cacheImage); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-help-about.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 24 | 26 | 30 | 34 | 38 | 42 | 43 | 52 | 54 | 58 | 62 | 63 | 65 | 69 | 73 | 77 | 81 | 82 | 84 | 88 | 92 | 93 | 103 | 112 | 113 | 115 | 116 | 118 | image/svg+xml 119 | 121 | 122 | 123 | 124 | 125 | 127 | 131 | 136 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | # flake8: noqa 5 | 6 | from __future__ import unicode_literals, division, absolute_import, print_function 7 | 8 | from .compatibility_utils import PY2, text_type, bchr, bord 9 | 10 | import binascii 11 | 12 | if PY2: 13 | range = xrange 14 | 15 | from itertools import cycle 16 | 17 | def getLanguage(langID, sublangID): 18 | mobilangdict = { 19 | 54 : {0 : 'af'}, # Afrikaans 20 | 28 : {0 : 'sq'}, # Albanian 21 | 1 : {0 : 'ar' , 5 : 'ar-dz' , 15 : 'ar-bh' , 3 : 'ar-eg' , 2 : 'ar-iq', 11 : 'ar-jo' , 13 : 'ar-kw' , 12 : 'ar-lb' , 4: 'ar-ly', 22 | 6 : 'ar-ma' , 8 : 'ar-om' , 16 : 'ar-qa' , 1 : 'ar-sa' , 10 : 'ar-sy' , 7 : 'ar-tn' , 14 : 'ar-ae' , 9 : 'ar-ye'}, 23 | # Arabic, Arabic (Algeria), Arabic (Bahrain), Arabic (Egypt), Arabic 24 | # (Iraq), Arabic (Jordan), Arabic (Kuwait), Arabic (Lebanon), Arabic 25 | # (Libya), Arabic (Morocco), Arabic (Oman), Arabic (Qatar), Arabic 26 | # (Saudi Arabia), Arabic (Syria), Arabic (Tunisia), Arabic (United Arab 27 | # Emirates), Arabic (Yemen) 28 | 43 : {0 : 'hy'}, # Armenian 29 | 77 : {0 : 'as'}, # Assamese 30 | 44 : {0 : 'az'}, # "Azeri (IANA: Azerbaijani) 31 | 45 : {0 : 'eu'}, # Basque 32 | 35 : {0 : 'be'}, # Belarusian 33 | 69 : {0 : 'bn'}, # Bengali 34 | 2 : {0 : 'bg'}, # Bulgarian 35 | 3 : {0 : 'ca'}, # Catalan 36 | 4 : {0 : 'zh' , 3 : 'zh-hk' , 2 : 'zh-cn' , 4 : 'zh-sg' , 1 : 'zh-tw'}, 37 | # Chinese, Chinese (Hong Kong), Chinese (PRC), Chinese (Singapore), Chinese (Taiwan) 38 | 26 : {0 : 'hr', 3 : 'sr'}, # Croatian, Serbian 39 | 5 : {0 : 'cs'}, # Czech 40 | 6 : {0 : 'da'}, # Danish 41 | 19 : {0: 'nl', 1 : 'nl' , 2 : 'nl-be'}, # Dutch / Flemish, Dutch (Belgium) 42 | 9 : {0: 'en', 1 : 'en' , 3 : 'en-au' , 40 : 'en-bz' , 4 : 'en-ca' , 6 : 'en-ie' , 8 : 'en-jm' , 5 : 'en-nz' , 13 : 'en-ph' , 43 | 7 : 'en-za' , 11 : 'en-tt' , 2 : 'en-gb', 1 : 'en-us' , 12 : 'en-zw'}, 44 | # English, English (Australia), English (Belize), English (Canada), 45 | # English (Ireland), English (Jamaica), English (New Zealand), English 46 | # (Philippines), English (South Africa), English (Trinidad), English 47 | # (United Kingdom), English (United States), English (Zimbabwe) 48 | 37 : {0 : 'et'}, # Estonian 49 | 56 : {0 : 'fo'}, # Faroese 50 | 41 : {0 : 'fa'}, # Farsi / Persian 51 | 11 : {0 : 'fi'}, # Finnish 52 | 12 : {0 : 'fr', 1 : 'fr' , 2 : 'fr-be' , 3 : 'fr-ca' , 5 : 'fr-lu' , 6 : 'fr-mc' , 4 : 'fr-ch'}, 53 | # French, French (Belgium), French (Canada), French (Luxembourg), French (Monaco), French (Switzerland) 54 | 55 : {0 : 'ka'}, # Georgian 55 | 7 : {0 : 'de', 1 : 'de' , 3 : 'de-at' , 5 : 'de-li' , 4 : 'de-lu' , 2 : 'de-ch'}, 56 | # German, German (Austria), German (Liechtenstein), German (Luxembourg), German (Switzerland) 57 | 8 : {0 : 'el'}, # Greek, Modern (1453-) 58 | 71 : {0 : 'gu'}, # Gujarati 59 | 13 : {0 : 'he'}, # Hebrew (also code 'iw'?) 60 | 57 : {0 : 'hi'}, # Hindi 61 | 14 : {0 : 'hu'}, # Hungarian 62 | 15 : {0 : 'is'}, # Icelandic 63 | 33 : {0 : 'id'}, # Indonesian 64 | 16 : {0 : 'it', 1 : 'it' , 2 : 'it-ch'}, # Italian, Italian (Switzerland) 65 | 17 : {0 : 'ja'}, # Japanese 66 | 75 : {0 : 'kn'}, # Kannada 67 | 63 : {0 : 'kk'}, # Kazakh 68 | 87 : {0 : 'x-kok'}, # Konkani (real language code is 'kok'?) 69 | 18 : {0 : 'ko'}, # Korean 70 | 38 : {0 : 'lv'}, # Latvian 71 | 39 : {0 : 'lt'}, # Lithuanian 72 | 47 : {0 : 'mk'}, # Macedonian 73 | 62 : {0 : 'ms'}, # Malay 74 | 76 : {0 : 'ml'}, # Malayalam 75 | 58 : {0 : 'mt'}, # Maltese 76 | 78 : {0 : 'mr'}, # Marathi 77 | 97 : {0 : 'ne'}, # Nepali 78 | 20 : {0 : 'no'}, # Norwegian 79 | 72 : {0 : 'or'}, # Oriya 80 | 21 : {0 : 'pl'}, # Polish 81 | 22 : {0 : 'pt', 2 : 'pt' , 1 : 'pt-br'}, # Portuguese, Portuguese (Brazil) 82 | 70 : {0 : 'pa'}, # Punjabi 83 | 23 : {0 : 'rm'}, # "Rhaeto-Romanic" (IANA: Romansh) 84 | 24 : {0 : 'ro'}, # Romanian 85 | 25 : {0 : 'ru'}, # Russian 86 | 59 : {0 : 'sz'}, # "Sami (Lappish)" (not an IANA language code) 87 | # IANA code for "Northern Sami" is 'se' 88 | # 'SZ' is the IANA region code for Swaziland 89 | 79 : {0 : 'sa'}, # Sanskrit 90 | 27 : {0 : 'sk'}, # Slovak 91 | 36 : {0 : 'sl'}, # Slovenian 92 | 46 : {0 : 'sb'}, # "Sorbian" (not an IANA language code) 93 | # 'SB' is IANA region code for 'Solomon Islands' 94 | # Lower Sorbian = 'dsb' 95 | # Upper Sorbian = 'hsb' 96 | # Sorbian Languages = 'wen' 97 | 10 : {0 : 'es' , 4 : 'es' , 44 : 'es-ar' , 64 : 'es-bo' , 52 : 'es-cl' , 36 : 'es-co' , 20 : 'es-cr' , 28 : 'es-do' , 98 | 48 : 'es-ec' , 68 : 'es-sv' , 16 : 'es-gt' , 72 : 'es-hn' , 8 : 'es-mx' , 76 : 'es-ni' , 24 : 'es-pa' , 99 | 60 : 'es-py' , 40 : 'es-pe' , 80 : 'es-pr' , 56 : 'es-uy' , 32 : 'es-ve'}, 100 | # Spanish, Spanish (Mobipocket bug?), Spanish (Argentina), Spanish 101 | # (Bolivia), Spanish (Chile), Spanish (Colombia), Spanish (Costa Rica), 102 | # Spanish (Dominican Republic), Spanish (Ecuador), Spanish (El 103 | # Salvador), Spanish (Guatemala), Spanish (Honduras), Spanish (Mexico), 104 | # Spanish (Nicaragua), Spanish (Panama), Spanish (Paraguay), Spanish 105 | # (Peru), Spanish (Puerto Rico), Spanish (Uruguay), Spanish (Venezuela) 106 | 48 : {0 : 'sx'}, # "Sutu" (not an IANA language code) 107 | # "Sutu" is another name for "Southern Sotho"? 108 | # IANA code for "Southern Sotho" is 'st' 109 | 65 : {0 : 'sw'}, # Swahili 110 | 29 : {0 : 'sv' , 1 : 'sv' , 8 : 'sv-fi'}, # Swedish, Swedish (Finland) 111 | 73 : {0 : 'ta'}, # Tamil 112 | 68 : {0 : 'tt'}, # Tatar 113 | 74 : {0 : 'te'}, # Telugu 114 | 30 : {0 : 'th'}, # Thai 115 | 49 : {0 : 'ts'}, # Tsonga 116 | 50 : {0 : 'tn'}, # Tswana 117 | 31 : {0 : 'tr'}, # Turkish 118 | 34 : {0 : 'uk'}, # Ukrainian 119 | 32 : {0 : 'ur'}, # Urdu 120 | 67 : {0 : 'uz', 2 : 'uz'}, # Uzbek 121 | 42 : {0 : 'vi'}, # Vietnamese 122 | 52 : {0 : 'xh'}, # Xhosa 123 | 53 : {0 : 'zu'}, # Zulu 124 | } 125 | lang = "en" 126 | if langID in mobilangdict: 127 | subdict = mobilangdict[langID] 128 | lang = subdict[0] 129 | if sublangID in subdict: 130 | lang = subdict[sublangID] 131 | return lang 132 | 133 | 134 | def toHex(byteList): 135 | return binascii.hexlify(byteList) 136 | 137 | # returns base32 bytestring 138 | def toBase32(value, npad=4): 139 | digits = b'0123456789ABCDEFGHIJKLMNOPQRSTUV' 140 | num_string=b'' 141 | current = value 142 | while current != 0: 143 | next, remainder = divmod(current, 32) 144 | rem_string = digits[remainder:remainder+1] 145 | num_string = rem_string + num_string 146 | current=next 147 | if num_string == b'': 148 | num_string = b'0' 149 | pad = npad - len(num_string) 150 | if pad > 0: 151 | num_string = b'0' * pad + num_string 152 | return num_string 153 | 154 | 155 | # converts base32 string to value 156 | def fromBase32(str_num): 157 | if isinstance(str_num, text_type): 158 | str_num = str_num.encode('latin-1') 159 | scalelst = [1,32,1024,32768,1048576,33554432,1073741824,34359738368] 160 | value = 0 161 | j = 0 162 | n = len(str_num) 163 | scale = 0 164 | for i in range(n): 165 | c = str_num[n-i-1:n-i] 166 | if c in b'0123456789': 167 | v = ord(c) - ord(b'0') 168 | else: 169 | v = ord(c) - ord(b'A') + 10 170 | if j < len(scalelst): 171 | scale = scalelst[j] 172 | else: 173 | scale = scale * 32 174 | j += 1 175 | if v != 0: 176 | value = value + (v * scale) 177 | return value 178 | 179 | 180 | # note: if decode a bytestring using 'latin-1' (or any other 0-255 encoding) 181 | # in place of ascii you will get a byte to half-word or integer 182 | # one to one mapping of values from 0 - 255 183 | 184 | def mangle_fonts(encryption_key, data): 185 | if isinstance(encryption_key, text_type): 186 | encryption_key = encryption_key.encode('latin-1') 187 | crypt = data[:1024] 188 | key = cycle(iter(map(bord, encryption_key))) 189 | # encrypt = ''.join([chr(ord(x)^key.next()) for x in crypt]) 190 | encrypt = b''.join([bchr(bord(x)^next(key)) for x in crypt]) 191 | return encrypt + data[1024:] 192 | -------------------------------------------------------------------------------- /data/icons/16/bookworm-format-justify-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 21 | 25 | 26 | 34 | 42 | 50 | 58 | 67 | 76 | 85 | 94 | 102 | 111 | 120 | 129 | 130 | 132 | 133 | 135 | image/svg+xml 136 | 138 | 139 | 140 | 141 | 142 | 144 | 148 | 152 | 156 | 160 | 164 | 168 | 172 | 176 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /data/scripts/mobi_lib/mobi_cover.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab 4 | 5 | from __future__ import unicode_literals, division, absolute_import, print_function 6 | 7 | from .compatibility_utils import unicode_str 8 | 9 | from .unipath import pathof 10 | import os 11 | import imghdr 12 | 13 | import struct 14 | # note: struct pack, unpack, unpack_from all require bytestring format 15 | # data all the way up to at least python 2.7.5, python 3 okay with bytestring 16 | 17 | USE_SVG_WRAPPER = True 18 | """ Set to True to use svg wrapper for default. """ 19 | 20 | FORCE_DEFAULT_TITLE = False 21 | """ Set to True to force to use the default title. """ 22 | 23 | COVER_PAGE_FINENAME = 'cover_page.xhtml' 24 | """ The name for the cover page. """ 25 | 26 | DEFAULT_TITLE = 'Cover' 27 | """ The default title for the cover page. """ 28 | 29 | MAX_WIDTH = 4096 30 | """ The max width for the svg cover page. """ 31 | 32 | MAX_HEIGHT = 4096 33 | """ The max height for the svg cover page. """ 34 | 35 | 36 | def get_image_type(imgname, imgdata=None): 37 | imgtype = unicode_str(imghdr.what(pathof(imgname), imgdata)) 38 | 39 | # imghdr only checks for JFIF or Exif JPEG files. Apparently, there are some 40 | # with only the magic JPEG bytes out there... 41 | # ImageMagick handles those, so, do it too. 42 | if imgtype is None: 43 | if imgdata is None: 44 | with open(pathof(imgname), 'rb') as f: 45 | imgdata = f.read() 46 | if imgdata[0:2] == b'\xFF\xD8': 47 | # Get last non-null bytes 48 | last = len(imgdata) 49 | while (imgdata[last-1:last] == b'\x00'): 50 | last-=1 51 | # Be extra safe, check the trailing bytes, too. 52 | if imgdata[last-2:last] == b'\xFF\xD9': 53 | imgtype = "jpeg" 54 | return imgtype 55 | 56 | 57 | def get_image_size(imgname, imgdata=None): 58 | '''Determine the image type of imgname (or imgdata) and return its size. 59 | 60 | Originally, 61 | Determine the image type of fhandle and return its size. 62 | from draco''' 63 | if imgdata is None: 64 | fhandle = open(pathof(imgname), 'rb') 65 | head = fhandle.read(24) 66 | else: 67 | head = imgdata[0:24] 68 | if len(head) != 24: 69 | return 70 | 71 | imgtype = get_image_type(imgname, imgdata) 72 | if imgtype == 'png': 73 | check = struct.unpack(b'>i', head[4:8])[0] 74 | if check != 0x0d0a1a0a: 75 | return 76 | width, height = struct.unpack(b'>ii', head[16:24]) 77 | elif imgtype == 'gif': 78 | width, height = struct.unpack(b'H', fhandle.read(2))[0] - 2 91 | # We are at a SOFn block 92 | fhandle.seek(1, 1) # Skip `precision' byte. 93 | height, width = struct.unpack(b'>HH', fhandle.read(4)) 94 | except Exception: # IGNORE:W0703 95 | return 96 | elif imgtype == 'jpeg' and imgdata is not None: 97 | try: 98 | pos = 0 99 | size = 2 100 | ftype = 0 101 | while not 0xc0 <= ftype <= 0xcf: 102 | pos += size 103 | byte = imgdata[pos:pos+1] 104 | pos += 1 105 | while ord(byte) == 0xff: 106 | byte = imgdata[pos:pos+1] 107 | pos += 1 108 | ftype = ord(byte) 109 | size = struct.unpack(b'>H', imgdata[pos:pos+2])[0] - 2 110 | pos += 2 111 | # We are at a SOFn block 112 | pos += 1 # Skip `precision' byte. 113 | height, width = struct.unpack(b'>HH', imgdata[pos:pos+4]) 114 | pos += 4 115 | except Exception: # IGNORE:W0703 116 | return 117 | else: 118 | return 119 | return width, height 120 | 121 | # XXX experimental 122 | class CoverProcessor(object): 123 | 124 | """Create a cover page. 125 | 126 | """ 127 | def __init__(self, files, metadata, rscnames, imgname=None, imgdata=None): 128 | self.files = files 129 | self.metadata = metadata 130 | self.rscnames = rscnames 131 | self.cover_page = COVER_PAGE_FINENAME 132 | self.use_svg = USE_SVG_WRAPPER # Use svg wrapper. 133 | self.lang = metadata.get('Language', ['en'])[0] 134 | # This should ensure that if the methods to find the cover image's 135 | # dimensions should fail for any reason, the SVG routine will not be used. 136 | [self.width, self.height] = (-1,-1) 137 | if FORCE_DEFAULT_TITLE: 138 | self.title = DEFAULT_TITLE 139 | else: 140 | self.title = metadata.get('Title', [DEFAULT_TITLE])[0] 141 | 142 | self.cover_image = None 143 | if imgname is not None: 144 | self.cover_image = imgname 145 | elif 'CoverOffset' in metadata: 146 | imageNumber = int(metadata['CoverOffset'][0]) 147 | cover_image = self.rscnames[imageNumber] 148 | if cover_image is not None: 149 | self.cover_image = cover_image 150 | else: 151 | print('Warning: Cannot identify the cover image.') 152 | if self.use_svg: 153 | try: 154 | if imgdata is None: 155 | fname = os.path.join(files.imgdir, self.cover_image) 156 | [self.width, self.height] = get_image_size(fname) 157 | else: 158 | [self.width, self.height] = get_image_size(None, imgdata) 159 | except: 160 | self.use_svg = False 161 | width = self.width 162 | height = self.height 163 | if width < 0 or height < 0 or width > MAX_WIDTH or height > MAX_HEIGHT: 164 | self.use_svg = False 165 | return 166 | 167 | def getImageName(self): 168 | return self.cover_image 169 | 170 | def getXHTMLName(self): 171 | return self.cover_page 172 | 173 | def buildXHTML(self): 174 | print('Building a cover page.') 175 | files = self.files 176 | cover_image = self.cover_image 177 | title = self.title 178 | lang = self.lang 179 | 180 | image_dir = os.path.normpath(os.path.relpath(files.k8images, files.k8text)) 181 | image_path = os.path.join(image_dir, cover_image).replace('\\', '/') 182 | 183 | if not self.use_svg: 184 | data = '' 185 | data += '' 186 | data += '