57 Commits
0.1 ... 0.3.1

Author SHA1 Message Date
BasioMeusPuga
34dcf9f1b4 Fix empty database triggering error at first start 2018-03-23 15:15:42 +05:30
BasioMeusPuga
7931f92335 Application icon and .desktop file
Rearrange modules because of single-version-externally-managed
2018-03-23 00:58:42 +05:30
BasioMeusPuga
42b655862c Partially fix tab close memory leak 2018-03-22 19:06:16 +05:30
BasioMeusPuga
e6eb056ec6 Make context menus more coherent
Update translations
2018-03-22 14:51:33 +05:30
BasioMeusPuga
7f5b6fc349 Multiple UI improvements 2018-03-21 21:19:28 +05:30
BasioMeusPuga
9af175b11f Merge pull request #28 from szymonpk/gentoo-ebuild
Add link to unofficial Gentoo ebuild
2018-03-21 15:31:50 +05:30
BasioMeusPuga
c783e44444 Adjust widgets to screen size
Delete key for the library
2018-03-21 15:28:58 +05:30
BasioMeusPuga
a1dba753e8 Manually added books no longer removed on library refresh
Overhaul database module
2018-03-21 15:04:28 +05:30
Szymon Szypulski
a55a0e7205 Add link to unofficial Gentoo ebuild 2018-03-21 07:31:15 +01:00
BasioMeusPuga
bb8de60efe Fix books in subdirectories getting filtered 2018-03-20 20:36:24 +05:30
BasioMeusPuga
0d8c2b6648 Multiple fixes
Update translations
2018-03-20 13:24:17 +05:30
BasioMeusPuga
64a96d816d Update translations: German 2018-03-20 08:26:35 +05:30
BasioMeusPuga
5a4af54118 Merge pull request #25 from atmaxinger/master
German translation
2018-03-20 08:19:01 +05:30
atmaxinger
4cf18e008d German translation 2018-03-20 00:10:05 +01:00
BasioMeusPuga
50cc52b116 Update README.md 2018-03-20 00:04:05 +05:30
BasioMeusPuga
35f38b9f68 French translation 2018-03-19 23:57:47 +05:30
BasioMeusPuga
c883ba0175 Merge pull request #24 from eclipseo/add_French_translation
Great work! 
Give me a minute to update the binary files.
2018-03-19 23:52:50 +05:30
BasioMeusPuga
39cf03a70e Switch context menu TOC to combobox
Update translations
2018-03-19 23:40:16 +05:30
Robert-André Mauchin
44d88d99bb Add French translation
Signed-off-by: Robert-André Mauchin <zebob.m@gmail.com>
2018-03-19 18:36:10 +01:00
BasioMeusPuga
ca67071e91 Add TOC to context menu in distraction free mode 2018-03-19 19:28:33 +05:30
BasioMeusPuga
b5acce6449 Small fixes 2018-03-19 18:26:43 +05:30
BasioMeusPuga
7bdf01a67e Spanish translation 2018-03-19 17:48:25 +05:30
BasioMeusPuga
aca08827fb Implement save as for comic/pdf view
Account for malformed container.xml for epubs
2018-03-19 01:11:55 +05:30
BasioMeusPuga
d4aaa4dc74 Update README.md 2018-03-19 00:43:18 +05:30
BasioMeusPuga
98daa40bfd Implement internationalization support 2018-03-19 00:11:06 +05:30
BasioMeusPuga
a7df896468 Mark translatable strings 2018-03-18 22:19:19 +05:30
BasioMeusPuga
fd149dcafa Usability improvements
Keyboard shortcuts
Title reporting
Context menu for comic/pdf view
2018-03-18 01:19:04 +05:30
BasioMeusPuga
0bb2e9329f Fix fullscreened widget not finding main window 2018-03-17 12:56:23 +05:30
BasioMeusPuga
89a32bfeda Add toggle for image caching
Remove PyQt5 reference from setup.py
2018-03-17 10:44:02 +05:30
BasioMeusPuga
50089cb57a Remove version requirements 2018-03-17 00:38:09 +05:30
BasioMeusPuga
a62e681223 Fix python-poppler-qt5 version check 2018-03-16 21:10:28 +05:30
BasioMeusPuga
efe52cd3cb Update README.md 2018-03-16 20:30:16 +05:30
BasioMeusPuga
23aff44412 Merge pull request #14 from BasioMeusPuga/pdf
Preliminary pdf support
2018-03-16 20:22:20 +05:30
BasioMeusPuga
e8e3b81871 Put cache refilling on separate thread
Lookout for memory leaks
2018-03-16 20:19:35 +05:30
BasioMeusPuga
03b683e05d Implement caching for QGraphicsView 2018-03-16 19:32:25 +05:30
BasioMeusPuga
5b3759afe6 Preliminary pdf support
Consolidate comicbook modules
Do not write to temp dir for comics any longer
2018-03-16 18:46:38 +05:30
BasioMeusPuga
fc2fcb5361 Add "Last Read" column
Start counting blocks for progress (Not implemented)
2018-03-16 14:46:47 +05:30
BasioMeusPuga
6ee135a52b Blank lines at the end of each chapter
Improve searching
2018-03-15 19:23:02 +05:30
BasioMeusPuga
55545f62e7 Update README.md 2018-03-15 11:55:43 +05:30
BasioMeusPuga
160226c6cd Launching with __main__.py 2018-03-15 11:24:28 +05:30
BasioMeusPuga
5d3ce17447 Fix mobi7 parsing 2018-03-14 20:29:08 +05:30
BasioMeusPuga
03afc6933f Splitting for ePubs with anchor navigation 2018-03-14 19:58:41 +05:30
BasioMeusPuga
c9559daaf6 Fix file duplication
Increase icon size for scan library icon
2018-03-14 11:53:47 +05:30
BasioMeusPuga
3c293a39d3 Embed icon theme 2018-03-14 08:26:34 +05:30
BasioMeusPuga
a87cd24c3d Update README.md 2018-03-13 19:21:59 +05:30
BasioMeusPuga
8564ede48b Small fixes 2018-03-13 19:20:47 +05:30
BasioMeusPuga
e0b20e36dd Merge pull request #11 from eclipseo/fix_for_python3
Convert KindleUnpack/mobiml2xhtml.py to Python 3 syntax
2018-03-13 19:03:42 +05:30
Robert-André Mauchin
c2db6c13b0 Convert KindleUnpack/mobiml2xhtml.py to Python 3 syntax 2018-03-13 03:08:42 +01:00
BasioMeusPuga
dbff4cbcca Update README.md 2018-03-13 00:36:48 +05:30
BasioMeusPuga
ce9ee4ccb2 Account for no images in an epub 2018-03-13 00:24:32 +05:30
BasioMeusPuga
ca3d747136 Revert to manual versioning in the setup.py 2018-03-12 20:36:49 +05:30
BasioMeusPuga
9e5559bbfa Scrollbar visibility when needed
Exception handling in ePub book year
2018-03-12 19:51:01 +05:30
BasioMeusPuga
9e9d7cca90 Update README.md 2018-03-12 19:41:18 +05:30
BasioMeusPuga
05e1655fd9 Fix bugs in setuptools packaging 2018-03-12 19:32:36 +05:30
BasioMeusPuga
f9bcc399e8 Merge pull request #1 from undu/wheel
Enable Python binary paclaging of the code
2018-03-11 01:40:35 +05:30
Pau Ruiz i Safont
1cd6ff6b58 maintenance: use modules properly 2018-03-10 19:21:18 +00:00
Pau Ruiz i Safont
79180885b5 feature: enable python packaging 2018-03-10 19:00:56 +00:00
151 changed files with 12161 additions and 2212 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
__pycache__/
.gitignore
.vscode/
parsers/__pycache__/
books/
Examples/

33
Lector.pro Normal file
View File

@@ -0,0 +1,33 @@
# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2017-2018 BasioMeusPuga
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SOURCES += lector/__main__.py \
lector/definitionsdialog.py \
lector/metadatadialog.py \
lector/models.py \
lector/widgets.py \
lector/library.py \
lector/toolbars.py \
lector/settingsdialog.py \
resources/definitions.py \
resources/settingswindow.py \
resources/metadata.py \
resources/mainwindow.py
TRANSLATIONS += resources/translations/Lector_es.ts \
resources/translations/Lector_fr.ts \
resources/translations/Lector_de.ts \
resources/translations/SAMPLE.ts

View File

@@ -2,21 +2,49 @@
Qt based ebook reader
Currently supports:
* pdf
* epub
* mobi
* azw / azw3 / azw4
* cbr / cbz
Support for a bunch of other formats is coming. Please see the TODO for additional information.
## Requirements
* Qt5
* PyQt5
* python-requests
* python-beautifulsoup4
| Package | Version tested |
| --- | --- |
| Qt5 | 5.10.1 |
| Python | 3.6 |
| PyQt5 | 5.10.1 |
| python-requests | 2.18.4 |
| python-beautifulsoup4 | 4.6.0 |
| poppler-qt5 | 0.61.1 |
| python-poppler-qt5 | 0.24.2 |
poppler-qt5 and python-poppler-qt5 are optional.
## Installation
0. Install dependencies
### Manual
0. Install dependencies - I recommend using your package manager for this.
1. Clone repository
2. Launch with \_\_main\_\_.py
2. Type the following in the root directory:
$ python setup.py build
# python setup.py install
3. OR launch with `lector/__main__.py`
### Available packages
* [AUR](https://aur.archlinux.org/packages/lector-git/)
* [Gentoo (unofficial)](https://bitbucket.org/szymonsz/gen2-overlay/src/master/app-text/lector/)
## Translations
1. There is a `SAMPLE.ts` file in `resources/translations`. Open it in `Qt Linguist`.
2. Pick the language you wish to translate to.
3. Translate relevant strings.
4. Try to resist the urge to include profanity.
5. Save the file as `Lector_<language>` and send it to me, preferably as a pull request.
Oh, please keep the translations short. There's only so much space for UI elements.
## Screenshots
@@ -43,3 +71,14 @@ Currently supports:
### In program dictionary
![alt tag](https://i.imgur.com/Vh9xQUC.png)
## Reporting issues
When reporting issues:
* If you're having trouble with a book while the rest of the application / other books work, please link to a copy of the book itself.
* If nothing is working, please make sure the requirements mentioned above are all installed, and are at least at the version mentioned.
## Attributions
* [KindleUnpack](https://github.com/kevinhendricks/KindleUnpack)
* [rarfile](https://github.com/markokr/rarfile)
* [Papirus icon theme](https://github.com/PapirusDevelopmentTeam/papirus-icon-theme)

24
TODO
View File

@@ -1,4 +1,8 @@
TODO
General:
✓ Internationalization
Application icon
.desktop file
Options:
✓ Automatic library management
✓ Recursive file addition
@@ -24,6 +28,7 @@ TODO
✓ Context menu: Cache, Read, Edit database, delete, Mark read/unread
✓ Information dialog widget
✓ Allow editing of database data through the UI + for Bookmarks
✓ Include (action) icons with the applications
Set focus to newly added file
Reading:
✓ Drop down for TOC
@@ -50,9 +55,13 @@ TODO
✓ Cache next and previous images
✓ Set context menu for definitions and the like
✓ Paragraph indentation
✓ Comic view keyboard shortcuts
✓ Comic view context menu
Adjust key navigation according to viewport dimensions
Search document using QTextCursor?
Comic view keyboard shortcuts
Filetypes:
✓ pdf support
Parse TOC
✓ epub support
✓ Homegrown solution please
✓ cbz, cbr support
@@ -63,9 +72,8 @@ TODO
Other:
✓ Define every widget in code
Bugs:
If there are files open and the database is deleted, TypeErrors result
Cover culling does not occur if some other tab has initial focus
Exiting with Ctrl + Q does not save the cursor position INITIALLY
Slider position change might be acting up
Deselecting all directories in the settings dialog also filters out manually added books
Secondary:
Annotations
@@ -76,7 +84,7 @@ TODO
Goodreads API: Ratings, Read, Recommendations
Get ISBN using python-isbnlib
Pagination
Use embedded fonts
Use embedded fonts + CSS
Scrolling: Smooth / By Line
Spacebar should not cut off lines at the top
Shift to logging instead of print statements
@@ -86,10 +94,6 @@ TODO
Comic view modes
Continuous paging
Double pages
Leave comic images on disk in case tab isn't closed and files are remembered
Give the comic view a 'Save image as...' option
Ignore a / the / numbers for sorting purposes
? Add only one file type if multiple are present
? Plugin system for parsers
? pdf support
? Create emblem per filetype
? Create emblem per filetype

View File

@@ -246,13 +246,13 @@ class MobiMLConverter(object):
# handle case of end tag with no beginning by injecting empty begin tag
taginfo = ('begin', tname, None)
htmlstr += self.processtag(taginfo)
print " - fixed by injecting empty start tag ", tname
print(" - fixed by injecting empty start tag ", tname)
self.path.append(tname)
elif len(self.path) > 1 and tname == self.path[-2]:
# handle case of dangling missing end
taginfo = ('end', self.path[-1], None)
htmlstr += self.processtag(taginfo)
print " - fixed by injecting end tag ", self.path[-1]
print(" - fixed by injecting end tag ", self.path[-1])
self.path.pop()
self.path.pop()
@@ -504,18 +504,18 @@ def main(argv=sys.argv):
infile = argv[1]
try:
print 'Converting Mobi Markup Language to XHTML'
print('Converting Mobi Markup Language to XHTML')
mlc = MobiMLConverter(infile)
print 'Processing ...'
print('Processing ...')
htmlstr, css, cssname = mlc.processml()
outname = infile.rsplit('.',1)[0] + '_converted.html'
file(outname, 'wb').write(htmlstr)
file(cssname, 'wb').write(css)
print 'Completed'
print 'XHTML version of book can be found at: ' + outname
print('Completed')
print('XHTML version of book can be found at: ', outname)
except ValueError, e:
print "Error: %s" % e
except ValueError as e:
print("Error: %s" % e)
return 1
return 0

View File

@@ -17,24 +17,33 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import gc
import sys
import hashlib
import pathlib
from PyQt5 import QtWidgets, QtGui, QtCore
import sorter
import database
# This allows for the program to be launched from the
# dir where it's been copied instead of needing to be
# installed
install_dir = os.path.realpath(__file__)
install_dir = pathlib.Path(install_dir).parents[1]
sys.path.append(str(install_dir))
from resources import mainwindow, resources
from toolbars import LibraryToolBar, BookToolBar
from widgets import Tab
from delegates import LibraryDelegate
from threaded import BackGroundTabUpdate, BackGroundBookAddition, BackGroundBookDeletion
from library import Library
from settings import Settings
from lector import database
from lector import sorter
from lector.toolbars import LibraryToolBar, BookToolBar
from lector.widgets import Tab
from lector.delegates import LibraryDelegate
from lector.threaded import BackGroundTabUpdate, BackGroundBookAddition, BackGroundBookDeletion
from lector.library import Library
from lector.guifunctions import QImageFactory
from lector.settings import Settings
from lector.settingsdialog import SettingsUI
from lector.metadatadialog import MetadataUI
from lector.definitionsdialog import DefinitionsUI
from settingsdialog import SettingsUI
from metadatadialog import MetadataUI
from definitionsdialog import DefinitionsUI
from lector.resources import mainwindow, resources
class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
@@ -42,6 +51,17 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
super(MainUI, self).__init__()
self.setupUi(self)
# Set window icon
self.setWindowIcon(
QtGui.QIcon(':/images/Lector.png'))
# Central Widget - Make borders disappear
self.centralWidget().layout().setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
# Initialize translation function
self._translate = QtCore.QCoreApplication.translate
# Empty variables that will be infested soon
self.settings = {}
self.thread = None # Background Thread
@@ -52,6 +72,13 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.database_path = None
self.active_library_filters = []
# Initialize application
Settings(self).read_settings() # This should populate all variables that need
# to be remembered across sessions
# Initialize icon factory
self.QImageFactory = QImageFactory(self)
# Initialize toolbars
self.libraryToolBar = LibraryToolBar(self)
self.bookToolBar = BookToolBar(self)
@@ -59,12 +86,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Widget declarations
self.libraryFilterMenu = QtWidgets.QMenu()
self.statusMessage = QtWidgets.QLabel()
self.toolbarToggle = QtWidgets.QToolButton()
self.reloadLibrary = QtWidgets.QToolButton()
# Initialize application
Settings(self).read_settings() # This should populate all variables that need
# to be remembered across sessions
self.distractionFreeToggle = QtWidgets.QToolButton()
# self.reloadLibrary = QtWidgets.QToolButton()
self.reloadLibrary = QtWidgets.QPushButton()
# Create the database in case it doesn't exist
database.DatabaseInit(self.database_path)
@@ -89,22 +113,14 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.statusBar.addWidget(self.sorterProgress)
self.sorterProgress.setVisible(False)
# Statusbar - Toolbar Visibility
self.toolbarToggle.setIcon(QtGui.QIcon.fromTheme('visibility'))
self.toolbarToggle.setObjectName('toolbarToggle')
self.toolbarToggle.setToolTip('Toggle toolbar')
self.toolbarToggle.setAutoRaise(True)
self.toolbarToggle.clicked.connect(self.toggle_toolbars)
self.statusBar.addPermanentWidget(self.toolbarToggle)
# THIS IS TEMPORARY
self.guiTest = QtWidgets.QToolButton()
self.guiTest.setIcon(QtGui.QIcon.fromTheme('mail-thread-watch'))
self.guiTest.setObjectName('guiTest')
self.guiTest.setToolTip('Test Function')
self.guiTest.setAutoRaise(True)
self.guiTest.clicked.connect(self.test_function)
self.statusBar.addPermanentWidget(self.guiTest)
# Statusbar + Toolbar Visibility
self.distractionFreeToggle.setIcon(self.QImageFactory.get_image('visibility'))
self.distractionFreeToggle.setObjectName('distractionFreeToggle')
self.distractionFreeToggle.setToolTip(
self._translate('Main_UI', 'Toggle distraction free mode (Ctrl + D)'))
self.distractionFreeToggle.setAutoRaise(True)
self.distractionFreeToggle.clicked.connect(self.toggle_distraction_free)
self.statusBar.addPermanentWidget(self.distractionFreeToggle)
# Application wide temporary directory
self.temp_dir = QtCore.QTemporaryDir()
@@ -119,7 +135,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Toolbar display
# Maybe make this a persistent option
self.settings['show_toolbars'] = True
self.settings['show_bars'] = True
# Library toolbar
self.libraryToolBar.addButton.triggered.connect(self.add_books)
@@ -194,9 +210,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
print('Available parsers: ' + self.available_parsers)
# The library refresh button on the Library tab
self.reloadLibrary.setIcon(QtGui.QIcon.fromTheme('reload'))
self.reloadLibrary.setFlat(True)
self.reloadLibrary.setIcon(self.QImageFactory.get_image('reload'))
self.reloadLibrary.setObjectName('reloadLibrary')
self.reloadLibrary.setAutoRaise(True)
self.reloadLibrary.setToolTip(self._translate('Main_UI', 'Scan library'))
self.reloadLibrary.clicked.connect(self.settingsDialog.start_library_scan)
self.tabWidget.tabBar().setTabButton(
@@ -243,22 +260,35 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.settings['main_window_headers']:
for count, i in enumerate(self.settings['main_window_headers']):
self.tableView.horizontalHeader().resizeSection(count, int(i))
self.tableView.horizontalHeader().resizeSection(4, 1)
self.tableView.horizontalHeader().resizeSection(5, 1)
self.tableView.horizontalHeader().setStretchLastSection(True)
self.tableView.horizontalHeader().sectionClicked.connect(
self.lib_ref.table_proxy_model.sort_table_columns)
self.tableView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.tableView.customContextMenuRequested.connect(self.generate_library_context_menu)
self.tableView.customContextMenuRequested.connect(
self.generate_library_context_menu)
# Keyboard shortcuts
self.ksCloseTab = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+W'), self)
self.ksCloseTab.setContext(QtCore.Qt.ApplicationShortcut)
self.ksCloseTab.activated.connect(self.tab_close)
self.ksDistractionFree = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+D'), self)
self.ksDistractionFree.setContext(QtCore.Qt.ApplicationShortcut)
self.ksDistractionFree.activated.connect(self.toggle_distraction_free)
self.ksOpenFile = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+O'), self)
self.ksOpenFile.setContext(QtCore.Qt.ApplicationShortcut)
self.ksOpenFile.activated.connect(self.add_books)
self.ksExitAll = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+Q'), self)
self.ksExitAll.setContext(QtCore.Qt.ApplicationShortcut)
self.ksExitAll.activated.connect(self.closeEvent)
self.ksCloseTab = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+W'), self)
self.ksCloseTab.setContext(QtCore.Qt.ApplicationShortcut)
self.ksCloseTab.activated.connect(self.tab_close)
self.ksDeletePressed = QtWidgets.QShortcut(QtGui.QKeySequence('Delete'), self)
self.ksDeletePressed.setContext(QtCore.Qt.ApplicationShortcut)
self.ksDeletePressed.activated.connect(self.delete_pressed)
self.listView.setFocus()
self.open_books_at_startup()
@@ -286,12 +316,15 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
file_list = [QtCore.QFileInfo(i).absoluteFilePath() for i in my_args]
books = sorter.BookSorter(
file_list,
'addition',
('addition', 'manual'),
self.database_path,
self.settings['auto_tags'],
self.temp_dir.path())
parsed_books = books.initiate_threads()
if not parsed_books:
return
database.DatabaseFunctions(self.database_path).add_to_database(parsed_books)
self.lib_ref.generate_model('addition', parsed_books, True)
@@ -365,6 +398,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
{'Hash': ''},
'LIKE')
if not all_covers_db:
return
all_covers = {
i[0]: i[1] for i in all_covers_db}
@@ -393,9 +429,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.tabWidget.currentIndex() != 0:
self.tabWidget.widget(self.tabWidget.currentIndex()).add_bookmark()
def test_function(self):
print('Caesar si viveret, ad remum dareris')
def resizeEvent(self, event=None):
if event:
# This implies a vertical resize event only
@@ -411,7 +444,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# First, calculate the number of images per row
i = self.listView.viewport().width() / default_size
rem = i - int(i)
if rem >= .11875 and rem <= .9999:
if rem >= .21875 and rem <= .9999:
num_images = int(i)
else:
num_images = int(i) - 1
@@ -432,14 +465,11 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
return
def add_books(self):
# TODO
# Remember file addition modality
# If a file is added from here, it should not be removed
# from the libary in case of a database refresh
dialog_prompt = self._translate('Main_UI', 'Add books to database')
ebooks_string = self._translate('Main_UI', 'eBooks')
opened_files = QtWidgets.QFileDialog.getOpenFileNames(
self, 'Open file', self.settings['last_open_path'],
f'eBooks ({self.available_parsers})')
self, dialog_prompt, self.settings['last_open_path'],
f'{ebooks_string} ({self.available_parsers})')
if not opened_files[0]:
return
@@ -449,9 +479,9 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.settings['last_open_path'] = os.path.dirname(opened_files[0][0])
self.sorterProgress.setVisible(True)
self.statusMessage.setText('Adding books...')
self.statusMessage.setText(self._translate('Main_UI', 'Adding books...'))
self.thread = BackGroundBookAddition(
opened_files[0], self.database_path, False, self)
opened_files[0], self.database_path, 'manual', self)
self.thread.finished.connect(self.move_on)
self.thread.start()
@@ -471,11 +501,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
return selected_indexes
def delete_books(self, selected_indexes=None):
# TODO
# ? Mirror selection
# Ask if library files are to be excluded from further scans
# Make a checkbox for this
if not selected_indexes:
# Get a list of QItemSelection objects
# What we're interested in is the indexes()[0] in each of them
@@ -506,26 +531,32 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Update the database in the background
self.thread = BackGroundBookDeletion(
delete_hashes, self.database_path, self)
delete_hashes, self.database_path)
self.thread.finished.connect(self.move_on)
self.thread.start()
# Generate a message box to confirm deletion
selected_number = len(selected_indexes)
confirm_deletion = QtWidgets.QMessageBox()
confirm_deletion.setText('Delete %d book(s)?' % selected_number)
deletion_prompt = self._translate(
'Main_UI', f'Delete {selected_number} book(s)?')
confirm_deletion.setText(deletion_prompt)
confirm_deletion.setIcon(QtWidgets.QMessageBox.Question)
confirm_deletion.setWindowTitle('Confirm deletion')
confirm_deletion.setWindowTitle(self._translate('Main_UI', 'Confirm deletion'))
confirm_deletion.setStandardButtons(
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
confirm_deletion.buttonClicked.connect(ifcontinue)
confirm_deletion.show()
confirm_deletion.exec_()
def delete_pressed(self):
if self.tabWidget.currentIndex() == 0:
self.delete_books()
def move_on(self):
self.settingsDialog.okButton.setEnabled(True)
self.settingsDialog.okButton.setToolTip(
'Save changes and start library scan')
self._translate('Main_UI', 'Save changes and start library scan'))
self.reloadLibrary.setEnabled(True)
self.sorterProgress.setVisible(False)
@@ -534,6 +565,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.lib_ref.update_proxymodels()
self.lib_ref.generate_library_tags()
self.statusMessage.setText(
str(self.lib_ref.item_proxy_model.rowCount()) +
self._translate('Main_UI', ' books'))
if not self.settings['perform_culling']:
self.load_all_covers()
@@ -561,7 +596,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.resizeEvent()
self.start_culling_timer()
if self.settings['show_toolbars']:
if self.settings['show_bars']:
self.bookToolBar.hide()
self.libraryToolBar.show()
@@ -569,10 +604,11 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Making the proxy model available doesn't affect
# memory utilization at all. Bleh.
self.statusMessage.setText(
str(self.lib_ref.item_proxy_model.rowCount()) + ' Books')
str(self.lib_ref.item_proxy_model.rowCount()) +
self._translate('Main_UI', ' Books'))
else:
if self.settings['show_toolbars']:
if self.settings['show_bars']:
self.bookToolBar.show()
self.libraryToolBar.hide()
@@ -616,7 +652,10 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.thread.start()
self.tabWidget.widget(tab_index).update_last_accessed_time()
self.tabWidget.removeTab(tab_index)
self.tabWidget.widget(tab_index).deleteLater()
self.tabWidget.widget(tab_index).setParent(None)
gc.collect()
def set_toc_position(self, event=None):
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
@@ -733,7 +772,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
contents = sorter.BookSorter(
file_paths,
'reading',
('reading', None),
self.database_path,
True,
self.temp_dir.path()).initiate_threads()
@@ -742,7 +781,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# New tabs are created here
# Initial position adjustment is carried out by the tab itself
file_data = contents[i]
Tab(file_data, self.tabWidget)
Tab(file_data, self)
if self.settings['last_open_tab'] == 'library':
self.tabWidget.setCurrentIndex(0)
@@ -860,15 +899,19 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
profile_index, current_profile, QtCore.Qt.UserRole)
self.format_contentView()
def modify_comic_view(self):
signal_sender = self.sender().objectName()
def modify_comic_view(self, key_pressed=None):
if key_pressed:
signal_sender = None
else:
signal_sender = self.sender().objectName()
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
self.bookToolBar.fitWidth.setChecked(False)
self.bookToolBar.bestFit.setChecked(False)
self.bookToolBar.originalSize.setChecked(False)
if signal_sender == 'zoomOut':
if signal_sender == 'zoomOut' or key_pressed == QtCore.Qt.Key_Minus:
self.comic_profile['zoom_mode'] = 'manualZoom'
self.comic_profile['padding'] += 50
@@ -876,7 +919,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.comic_profile['padding'] * 2 > current_tab.contentView.viewport().width():
self.comic_profile['padding'] -= 50
if signal_sender == 'zoomIn':
if signal_sender == 'zoomIn' or key_pressed in (QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal):
self.comic_profile['zoom_mode'] = 'manualZoom'
self.comic_profile['padding'] -= 50
@@ -884,18 +927,18 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.comic_profile['padding'] < 0:
self.comic_profile['padding'] = 0
if signal_sender == 'fitWidth':
if signal_sender == 'fitWidth' or key_pressed == QtCore.Qt.Key_W:
self.comic_profile['zoom_mode'] = 'fitWidth'
self.comic_profile['padding'] = 0
self.bookToolBar.fitWidth.setChecked(True)
# Padding in the following cases is decided by
# the image pixmap loaded by the widget
if signal_sender == 'bestFit':
if signal_sender == 'bestFit' or key_pressed == QtCore.Qt.Key_B:
self.comic_profile['zoom_mode'] = 'bestFit'
self.bookToolBar.bestFit.setChecked(True)
if signal_sender == 'originalSize':
if signal_sender == 'originalSize' or key_pressed == QtCore.Qt.Key_O:
self.comic_profile['zoom_mode'] = 'originalSize'
self.bookToolBar.originalSize.setChecked(True)
@@ -905,7 +948,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# TODO
# See what happens if a font isn't installed
current_tab = self.tabWidget.widget(self.tabWidget.currentIndex())
current_tab = self.tabWidget.widget(
self.tabWidget.currentIndex())
try:
current_metadata = current_tab.metadata
@@ -990,19 +1034,24 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
context_menu = QtWidgets.QMenu()
openAction = context_menu.addAction(
QtGui.QIcon.fromTheme('view-readermode'), 'Start reading')
self.QImageFactory.get_image('view-readermode'),
self._translate('Main_UI', 'Start reading'))
editAction = None
if len(selected_indexes) == 1:
editAction = context_menu.addAction(
QtGui.QIcon.fromTheme('edit-rename'), 'Edit')
self.QImageFactory.get_image('edit-rename'),
self._translate('Main_UI', 'Edit'))
deleteAction = context_menu.addAction(
QtGui.QIcon.fromTheme('trash-empty'), 'Delete')
self.QImageFactory.get_image('trash-empty'),
self._translate('Main_UI', 'Delete'))
readAction = context_menu.addAction(
QtGui.QIcon.fromTheme('vcs-normal'), 'Mark read')
QtGui.QIcon(':/images/checkmark.svg'),
self._translate('Main_UI', 'Mark read'))
unreadAction = context_menu.addAction(
QtGui.QIcon.fromTheme('emblem-unavailable'), 'Mark unread')
QtGui.QIcon(':/images/xmark.svg'),
self._translate('Main_UI', 'Mark unread'))
action = context_menu.exec_(self.sender().mapToGlobal(position))
@@ -1099,11 +1148,13 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
checked = [i for i in directory_list if i[3] == QtCore.Qt.Checked]
filter_list = list(map(generate_name, checked))
filter_list.sort()
filter_list.append('Manually Added')
filter_actions = [QtWidgets.QAction(i, self.libraryFilterMenu) for i in filter_list]
filter_list.append(self._translate('Main_UI', 'Manually Added'))
filter_actions = [QtWidgets.QAction(i, self.libraryFilterMenu) for i in filter_list]
filter_all = QtWidgets.QAction('All', self.libraryFilterMenu)
filter_actions.append(filter_all)
for i in filter_actions:
i.setCheckable(True)
i.setChecked(True)
@@ -1135,8 +1186,13 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.lib_ref.update_proxymodels()
def toggle_toolbars(self):
self.settings['show_toolbars'] = not self.settings['show_toolbars']
def toggle_distraction_free(self):
self.settings['show_bars'] = not self.settings['show_bars']
self.statusBar.setVisible(
not self.statusBar.isVisible())
self.tabWidget.tabBar().setVisible(
not self.tabWidget.tabBar().isVisible())
current_tab = self.tabWidget.currentIndex()
if current_tab == 0:
@@ -1146,6 +1202,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
self.bookToolBar.setVisible(
not self.bookToolBar.isVisible())
self.start_culling_timer()
def closeEvent(self, event=None):
if event:
event.ignore()
@@ -1187,6 +1245,18 @@ def main():
app = QtWidgets.QApplication(sys.argv)
app.setApplicationName('Lector') # This is needed for QStandardPaths
# and my own hubris
# Internationalization support
translator = QtCore.QTranslator()
translations_found = translator.load(
QtCore.QLocale.system(), ':/translations/translations_bin/Lector_')
app.installTranslator(translator)
translations_out_string = '(Translations found)'
if not translations_found:
translations_out_string = '(No translations found)'
print(f'Locale: {QtCore.QLocale.system().name()}', translations_out_string)
form = MainUI()
form.show()
form.resizeEvent()

View File

@@ -25,29 +25,70 @@ from PyQt5 import QtCore
class DatabaseInit:
def __init__(self, location_prefix):
os.makedirs(location_prefix, exist_ok=True)
database_path = os.path.join(location_prefix, 'Lector.db')
self.database_path = os.path.join(location_prefix, 'Lector.db')
if not os.path.exists(database_path):
self.database = sqlite3.connect(database_path)
self.books_table_columns = {
'id': 'INTEGER PRIMARY KEY',
'Title': 'TEXT',
'Author': 'TEXT',
'Year': 'INTEGER',
'DateAdded': 'BLOB',
'Path': 'TEXT',
'Position': 'BLOB',
'ISBN': 'TEXT',
'Tags': 'TEXT',
'Hash': 'TEXT',
'LastAccessed': 'BLOB',
'Bookmarks': 'BLOB',
'CoverImage': 'BLOB',
'Addition': 'TEXT'}
self.directories_table_columns = {
'id': 'INTEGER PRIMARY KEY',
'Path': 'TEXT',
'Name': 'TEXT',
'Tags': 'TEXT',
'CheckState': 'INTEGER'}
if os.path.exists(self.database_path):
self.check_database()
else:
self.create_database()
def create_database(self):
# TODO
# Add separate columns for:
# addition mode
self.database.execute(
"CREATE TABLE books \
(id INTEGER PRIMARY KEY, Title TEXT, Author TEXT, Year INTEGER, DateAdded BLOB, \
Path TEXT, Position BLOB, ISBN TEXT, Tags TEXT, Hash TEXT, LastAccessed BLOB,\
Bookmarks BLOB, CoverImage BLOB)")
self.database = sqlite3.connect(self.database_path)
column_string = ', '.join(
[i[0] + ' ' + i[1] for i in self.books_table_columns.items()])
self.database.execute(f"CREATE TABLE books ({column_string})")
# CheckState is the standard QtCore.Qt.Checked / Unchecked
self.database.execute(
"CREATE TABLE directories (id INTEGER PRIMARY KEY, Path TEXT, \
Name TEXT, Tags TEXT, CheckState INTEGER)")
column_string = ', '.join(
[i[0] + ' ' + i[1] for i in self.directories_table_columns.items()])
self.database.execute(f"CREATE TABLE directories ({column_string})")
self.database.commit()
self.database.close()
def check_database(self):
self.database = sqlite3.connect(self.database_path)
database_return = self.database.execute("PRAGMA table_info(books)").fetchall()
database_columns = [i[1] for i in database_return]
# This allows for addition of a column without having to reform the database
commit_required = False
for i in self.books_table_columns.items():
if i[0] not in database_columns:
commit_required = True
print(f'Database: Adding column {i[0]}')
sql_command = f"ALTER TABLE books ADD COLUMN {i[0]} {i[1]}"
self.database.execute(sql_command)
if commit_required:
self.database.commit()
self.database.close()
class DatabaseFunctions:
def __init__(self, location_prefix):
@@ -55,10 +96,6 @@ class DatabaseFunctions:
self.database = sqlite3.connect(database_path)
def set_library_paths(self, data_iterable):
# TODO
# INSERT OR REPLACE is not working
# So this is the old fashion kitchen sink approach
self.database.execute("DELETE FROM directories")
for i in data_iterable:
@@ -67,10 +104,13 @@ class DatabaseFunctions:
tags = i[2]
is_checked = i[3]
if not os.path.exists(path):
continue # Remove invalid paths from the database
sql_command = (
"INSERT OR REPLACE INTO directories (ID, Path, Name, Tags, CheckState)\
VALUES ((SELECT ID FROM directories WHERE Path = ?), ?, ?, ?, ?)")
self.database.execute(sql_command, [path, path, name, tags, is_checked])
"INSERT INTO directories (Path, Name, Tags, CheckState)\
VALUES (?, ?, ?, ?)")
self.database.execute(sql_command, [path, name, tags, is_checked])
self.database.commit()
self.database.close()
@@ -95,6 +135,7 @@ class DatabaseFunctions:
path = i[1]['path']
cover = i[1]['cover_image']
isbn = i[1]['isbn']
addition_mode = i[1]['addition_mode']
tags = i[1]['tags']
if tags:
# Is a list. Needs to be a string
@@ -105,8 +146,9 @@ class DatabaseFunctions:
sql_command_add = (
"INSERT OR REPLACE INTO \
books (Title, Author, Year, DateAdded, Path, ISBN, Tags, Hash, CoverImage) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
books (Title, Author, Year, DateAdded, Path, \
ISBN, Tags, Hash, CoverImage, Addition) \
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
cover_insert = None
if cover:
@@ -115,7 +157,8 @@ class DatabaseFunctions:
self.database.execute(
sql_command_add,
[title, author, year, current_datetime_bin,
path, isbn, tags, book_hash, cover_insert])
path, isbn, tags, book_hash, cover_insert,
addition_mode])
self.database.commit()
self.database.close()
@@ -208,9 +251,10 @@ class DatabaseFunctions:
# target_data is an iterable
if column_name == '*':
self.database.execute('DELETE FROM books')
self.database.execute(
"DELETE FROM books WHERE NOT Addition = 'manual'")
else:
sql_command = f'DELETE FROM books WHERE {column_name} = ?'
sql_command = f"DELETE FROM books WHERE {column_name} = ?"
for i in target_data:
self.database.execute(sql_command, (i,))

View File

@@ -18,16 +18,17 @@
import requests
from PyQt5 import QtWidgets, QtCore, QtGui, QtMultimedia
from resources import definitions
from lector.resources import definitions
class DefinitionsUI(QtWidgets.QDialog, definitions.Ui_Dialog):
def __init__(self, parent):
super(DefinitionsUI, self).__init__()
self.setupUi(self)
self._translate = QtCore.QCoreApplication.translate
self.parent = parent
self.previous_position = None
self.setWindowFlags(
QtCore.Qt.Popup |
@@ -39,6 +40,8 @@ class DefinitionsUI(QtWidgets.QDialog, definitions.Ui_Dialog):
mask = QtGui.QRegion(path.toFillPolygon().toPolygon())
self.setMask(mask)
self.definitionView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.app_id = 'bb7a91f9'
self.app_key = 'fefacdf6775c347b52e9efa2efe642ef'
@@ -109,8 +112,9 @@ class DefinitionsUI(QtWidgets.QDialog, definitions.Ui_Dialog):
html_string += f'<h2><em><strong>{word}</strong></em></h2>\n'
if nothing_found:
nope_string = self._translate('DefinitionsUI', 'No definitions found in')
language = self.parent.settings['dictionary_language'].upper()
html_string += f'<p><em>No definitions found in {language}<em></p>\n'
html_string += f'<p><em>{nope_string} {language}<em></p>\n'
else:
# Word root
html_string += f'<p><em>Word root: <em>{word_root}</p>\n'

View File

@@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from PyQt5 import QtWidgets, QtGui, QtCore
from resources import pie_chart
from lector.resources import pie_chart
class LibraryDelegate(QtWidgets.QStyledItemDelegate):

View File

@@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import zipfile
from urllib.parse import unquote
@@ -29,6 +28,7 @@ class EPUB:
self.filename = filename
self.zip_file = None
self.book = {}
self.book['split_chapters'] = {}
def read_epub(self):
# This is the function that should error out in
@@ -38,7 +38,7 @@ class EPUB:
None, True)
if not contents_path:
return False # No opf was found so processing cannot continue
return False # No (valid) opf was found so processing cannot continue
self.generate_book_metadata(contents_path)
self.parse_toc()
@@ -57,7 +57,8 @@ class EPUB:
try:
this_xml = self.zip_file.read(filename).decode()
except KeyError:
print(str(filename) + ' not found in zip')
short_filename = os.path.basename(self.filename)
print(f'{str(filename)} not found in {short_filename}')
return
root = BeautifulSoup(this_xml, parser)
@@ -75,13 +76,17 @@ class EPUB:
if xml:
root_item = xml.find('rootfile')
return root_item.get('full-path')
else:
possible_filenames = ('content.opf', 'package.opf')
for i in possible_filenames:
presumptive_location = self.get_file_path(i)
if presumptive_location:
return presumptive_location
try:
return root_item.get('full-path')
except AttributeError:
print(f'ePub module: {self.filename} has a malformed container.xml')
return None
possible_filenames = ('content.opf', 'package.opf')
for i in possible_filenames:
presumptive_location = self.get_file_path(i)
if presumptive_location:
return presumptive_location
for i in self.zip_file.filelist:
if os.path.basename(i.filename) == os.path.basename(filename):
@@ -104,7 +109,8 @@ class EPUB:
#______________________________________________________
def generate_book_metadata(self, contents_path):
self.book['title'] = 'Unknown'
self.book['title'] = os.path.splitext(
os.path.basename(self.filename))[0]
self.book['author'] = 'Unknown'
self.book['isbn'] = None
self.book['tags'] = None
@@ -127,7 +133,7 @@ class EPUB:
try:
self.book['year'] = int(self.book['year'][:4])
except (TypeError, KeyError, IndexError):
except (TypeError, KeyError, IndexError, ValueError):
self.book['year'] = 9999
# Get identifier
@@ -170,7 +176,7 @@ class EPUB:
self.get_file_path(cover_href))
if not self.book['cover']:
# If no cover is located the conventioanl way,
# If no cover is located the conventional way,
# we go looking for the largest image in the book
biggest_image_size = 0
biggest_image = None
@@ -215,33 +221,32 @@ class EPUB:
for i in navpoints:
chapter_title = i.find('text').text
chapter_source = i.find('content').get('src')
chapter_source = unquote(chapter_source.split('#')[0])
self.book['navpoint_dict'][chapter_source] = chapter_title
chapter_source_file = unquote(chapter_source.split('#')[0])
if '#' in chapter_source:
try:
self.book['split_chapters'][chapter_source_file].append(
(chapter_source.split('#')[1], chapter_title))
except KeyError:
self.book['split_chapters'][chapter_source_file] = []
self.book['split_chapters'][chapter_source_file].append(
(chapter_source.split('#')[1], chapter_title))
self.book['navpoint_dict'][chapter_source_file] = chapter_title
def parse_chapters(self, temp_dir=None, split_large_xml=False):
no_title_chapter = 0
self.book['book_list'] = []
for i in self.book['chapters_in_order']:
chapter_data = self.read_from_zip(i).decode()
if not split_large_xml:
try:
self.book['book_list'].append(
(self.book['navpoint_dict'][i], chapter_data))
except KeyError:
fallback_title = str(no_title_chapter)
self.book['book_list'].append(
(fallback_title, chapter_data))
no_title_chapter += 1
if i in self.book['split_chapters'] and not split_large_xml:
split_chapters = get_split_content(
chapter_data, self.book['split_chapters'][i])
self.book['book_list'].extend(split_chapters)
cover_path = os.path.join(temp_dir, os.path.basename(self.filename)) + '- cover'
with open(cover_path, 'wb') as cover_temp:
cover_temp.write(self.book['cover'])
self.book['book_list'][0] = (
'Cover', f'<center><img src="{cover_path}" alt="Cover"></center>')
else:
elif split_large_xml:
# https://stackoverflow.com/questions/14444732/how-to-split-a-html-page-to-multiple-pages-using-python-and-beautiful-soup
markup = BeautifulSoup(chapter_data, 'xml')
chapters = []
@@ -264,13 +269,56 @@ class EPUB:
for this_chapter in chapters:
fallback_title = str(no_title_chapter)
self.book['book_list'].append(
(fallback_title, this_chapter))
(fallback_title, this_chapter + ('<br/>' * 8)))
no_title_chapter += 1
else:
try:
self.book['book_list'].append(
(self.book['navpoint_dict'][i], chapter_data + ('<br/>' * 8)))
except KeyError:
fallback_title = str(no_title_chapter)
self.book['book_list'].append(
(fallback_title, chapter_data))
no_title_chapter += 1
def main():
book = EPUB(sys.argv[1])
book.read_epub()
book.parse_chapters()
cover_path = os.path.join(temp_dir, os.path.basename(self.filename)) + '- cover'
if self.book['cover']:
with open(cover_path, 'wb') as cover_temp:
cover_temp.write(self.book['cover'])
if __name__ == '__main__':
main()
try:
self.book['book_list'][0] = (
'Cover', f'<center><img src="{cover_path}" alt="Cover"></center>')
except IndexError:
pass
def get_split_content(chapter_data, split_by):
split_anchors = [i[0] for i in split_by]
chapter_titles = [i[1] for i in split_by]
return_list = []
xml = BeautifulSoup(chapter_data, 'lxml')
xml_string = xml.body.prettify()
for count, i in enumerate(split_anchors):
this_split = xml_string.split(i)
current_chapter = this_split[0]
bs_obj = BeautifulSoup(current_chapter, 'lxml')
# Since tags correspond to data following them, the first
# chunk will be ignored
# As will all empty chapters
if bs_obj.text == '\n' or bs_obj.text == '' or count == 0:
continue
bs_obj_string = str(bs_obj).replace('"&gt;', '', 1) + ('<br/>' * 8)
return_list.append(
(chapter_titles[count - 1], bs_obj_string))
xml_string = this_split[1]
bs_obj = BeautifulSoup(xml_string, 'lxml')
bs_obj_string = str(bs_obj).replace('"&gt;', '', 1) + ('<br/>' * 8)
return_list.append(
(chapter_titles[-1], bs_obj_string))
return return_list

32
lector/guifunctions.py Normal file
View File

@@ -0,0 +1,32 @@
#!usr/bin/env python3
# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2018 BasioMeusPuga
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from PyQt5 import QtGui
from lector.resources import resources
class QImageFactory:
def __init__(self, parent):
self.parent = parent
def get_image(self, image_name):
icon_theme = self.parent.settings['icon_theme']
icon_path = f':/images/{icon_theme}/{image_name}.svg'
this_qicon = QtGui.QIcon(icon_path)
return this_qicon

View File

@@ -20,9 +20,8 @@ import os
import pickle
import pathlib
from PyQt5 import QtGui, QtCore
import database
from models import TableProxyModel, ItemProxyModel
from lector import database
from lector.models import TableProxyModel, ItemProxyModel
class Library:
@@ -31,6 +30,7 @@ class Library:
self.view_model = None
self.item_proxy_model = None
self.table_proxy_model = None
self._translate = QtCore.QCoreApplication.translate
def generate_model(self, mode, parsed_books=None, is_database_ready=True):
if mode == 'build':
@@ -40,7 +40,8 @@ class Library:
books = database.DatabaseFunctions(
self.parent.database_path).fetch_data(
('Title', 'Author', 'Year', 'DateAdded', 'Path',
'Position', 'ISBN', 'Tags', 'Hash', 'LastAccessed'),
'Position', 'ISBN', 'Tags', 'Hash', 'LastAccessed',
'Addition'),
'books',
{'Title': ''},
'LIKE')
@@ -63,7 +64,7 @@ class Library:
books.append([
i[1]['title'], i[1]['author'], i[1]['year'], current_qdatetime,
i[1]['path'], None, i[1]['isbn'], _tags, i[0], None])
i[1]['path'], None, i[1]['isbn'], _tags, i[0], None, i[1]['addition_mode']])
else:
return
@@ -75,7 +76,11 @@ class Library:
author = i[1]
year = i[2]
path = i[4]
addition_mode = i[10]
last_accessed = i[9]
if last_accessed and not isinstance(last_accessed, QtCore.QDateTime):
last_accessed = pickle.loads(last_accessed)
tags = i[7]
if isinstance(tags, list): # When files are added for the first time
@@ -102,7 +107,10 @@ class Library:
except KeyError:
position_perc = None
file_exists = os.path.exists(path)
try:
file_exists = os.path.exists(path)
except UnicodeEncodeError:
print('Error with unicode encoding in the library module')
all_metadata = {
'title': title,
@@ -115,9 +123,12 @@ class Library:
'tags': tags,
'hash': i[8],
'last_accessed': last_accessed,
'addition_mode': addition_mode,
'file_exists': file_exists}
tooltip_string = title + '\nAuthor: ' + author + '\nYear: ' + str(year)
author_string = self._translate('Library', 'Author')
year_string = self._translate('Library', 'Year')
tooltip_string = f'{title} \n{author_string}: {author} \n{year_string}: {str(year)}'
# Additional data can be set using an incrementing
# QtCore.Qt.UserRole
@@ -162,7 +173,8 @@ class Library:
self.parent.listView.setIconSize(s)
self.parent.listView.setModel(self.item_proxy_model)
self.table_proxy_model = TableProxyModel(self.parent.temp_dir.path())
self.table_proxy_model = TableProxyModel(
self.parent.temp_dir.path(), self.parent.tableView.horizontalHeader())
self.table_proxy_model.setSourceModel(self.view_model)
self.table_proxy_model.setSortCaseSensitivity(False)
self.parent.tableView.setModel(self.table_proxy_model)
@@ -180,6 +192,9 @@ class Library:
self.parent.libraryToolBar.searchBar.text())
# ^^^ This isn't needed, but it forces a model update every time the
# text in the line edit changes. So I guess it is needed.
self.table_proxy_model.sort_table_columns(
self.parent.tableView.horizontalHeader().sortIndicatorSection())
self.table_proxy_model.sort_table_columns()
# Item proxy model
self.item_proxy_model.invalidateFilter()
@@ -191,7 +206,8 @@ class Library:
self.parent.libraryToolBar.searchBar.text())
self.parent.statusMessage.setText(
str(self.item_proxy_model.rowCount()) + ' books')
str(self.item_proxy_model.rowCount()) +
self._translate('Library', ' books'))
# TODO
# Allow sorting by type
@@ -227,11 +243,22 @@ class Library:
{'Path': ''},
'LIKE')
if not db_library_directories: # Empty database / table
return
if db_library_directories: # Empty database / table
library_directories = {
i[0]: (i[1], i[2]) for i in db_library_directories}
library_directories = {
i[0]: (i[1], i[2]) for i in db_library_directories}
else:
db_library_directories = database.DatabaseFunctions(
self.parent.database_path).fetch_data(
('Path',),
'books', # THIS CHECKS THE BOOKS TABLE
{'Path': ''},
'LIKE')
library_directories = None
if db_library_directories:
library_directories = {
i[0]: (None, None) for i in db_library_directories}
def get_tags(all_metadata):
path = os.path.dirname(all_metadata['path'])
@@ -243,7 +270,7 @@ class Library:
if directory_name:
directory_name = directory_name.lower()
else:
directory_name = path.rsplit('/')[-1].lower()
directory_name = i.rsplit(os.sep)[-1].lower()
directory_tags = library_directories[i][1]
if directory_tags:
@@ -251,9 +278,13 @@ class Library:
return directory_name, directory_tags
return 'manually added', None
# A file is assigned a 'manually added' tag in case it isn't
# in any designated library directory
added_string = self._translate('Library', 'manually added')
return added_string.lower(), None
# Generate tags for the QStandardItemModel
# This isn't triggered for an empty view model
for i in range(self.view_model.rowCount()):
this_item = self.view_model.item(i, 0)
all_metadata = this_item.data(QtCore.Qt.UserRole + 3)
@@ -267,23 +298,24 @@ class Library:
# All files in unselected directories will have to be removed
# from both of the models
# They will also have to be deleted from the library
valid_paths = set(valid_paths)
# Get all paths
all_paths = set()
for i in range(self.view_model.rowCount()):
item = self.view_model.item(i, 0)
item_metadata = item.data(QtCore.Qt.UserRole + 3)
book_path = item_metadata['path']
all_paths.add(book_path)
invalid_paths = all_paths - valid_paths
invalid_paths = []
deletable_persistent_indexes = []
for i in range(self.view_model.rowCount()):
item = self.view_model.item(i)
path = item.data(QtCore.Qt.UserRole + 3)['path']
if path in invalid_paths:
item_metadata = item.data(QtCore.Qt.UserRole + 3)
book_path = item_metadata['path']
try:
addition_mode = item_metadata['addition_mode']
except KeyError:
addition_mode = 'automatic'
print('Libary: Error setting addition mode for prune')
if (book_path not in valid_paths and
(addition_mode != 'manual' or addition_mode is None)):
invalid_paths.append(book_path)
deletable_persistent_indexes.append(
QtCore.QPersistentModelIndex(item.index()))

View File

@@ -18,16 +18,16 @@
from PyQt5 import QtWidgets, QtCore, QtGui
import database
from resources import metadata
from widgets import PliantQGraphicsScene
from lector import database
from lector.widgets import PliantQGraphicsScene
from lector.resources import metadata
class MetadataUI(QtWidgets.QDialog, metadata.Ui_Dialog):
def __init__(self, parent):
super(MetadataUI, self).__init__()
self.setupUi(self)
self._translate = QtCore.QCoreApplication.translate
self.setWindowFlags(
QtCore.Qt.Popup |
@@ -85,7 +85,7 @@ class MetadataUI(QtWidgets.QDialog, metadata.Ui_Dialog):
graphics_scene.addPixmap(image_pixmap)
self.coverView.setScene(graphics_scene)
def ok_pressed(self, event):
def ok_pressed(self, event=None):
book_item = self.parent.lib_ref.view_model.item(self.book_index.row())
title = self.titleLine.text()
@@ -97,7 +97,9 @@ class MetadataUI(QtWidgets.QDialog, metadata.Ui_Dialog):
except ValueError:
year = self.book_year
tooltip_string = title + '\nAuthor: ' + author + '\nYear: ' + str(year)
author_string = self._translate('MetadataUI', 'Author')
year_string = self._translate('MetadataUI', 'Year')
tooltip_string = f'{title} \n{author_string}: {author} \n{year_string}: {str(year)}'
book_item.setData(title, QtCore.Qt.UserRole)
book_item.setData(author, QtCore.Qt.UserRole + 1)
@@ -123,7 +125,7 @@ class MetadataUI(QtWidgets.QDialog, metadata.Ui_Dialog):
database.DatabaseFunctions(self.database_path).modify_metadata(
database_dict, book_hash)
def cancel_pressed(self, event):
def cancel_pressed(self, event=None):
self.hide()
def generate_display_position(self, mouse_cursor_position):

View File

@@ -19,14 +19,14 @@
import pathlib
from PyQt5 import QtCore, QtWidgets
from resources import pie_chart
from lector.resources import pie_chart
class BookmarkProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(BookmarkProxyModel, self).__init__(parent)
self.parent = parent
self.filter_string = None
self.filter_text = None
def setFilterParams(self, filter_text):
self.filter_text = filter_text
@@ -65,10 +65,20 @@ class ItemProxyModel(QtCore.QSortFilterProxyModel):
class TableProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, temp_dir, parent=None):
def __init__(self, temp_dir, tableViewHeader, parent=None):
super(TableProxyModel, self).__init__(parent)
self.tableViewHeader = tableViewHeader
self._translate = QtCore.QCoreApplication.translate
title_string = self._translate('TableProxyModel', 'Title')
author_string = self._translate('TableProxyModel', 'Author')
year_string = self._translate('TableProxyModel', 'Year')
lastread_string = self._translate('TableProxyModel', 'Last Read')
tags_string = self._translate('TableProxyModel', 'Tags')
self.header_data = [
None, 'Title', 'Author', 'Year', '%', 'Tags']
None, title_string, author_string,
year_string, lastread_string, '%', tags_string]
self.temp_dir = temp_dir
self.filter_text = None
self.active_library_filters = None
@@ -77,16 +87,21 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
1: QtCore.Qt.UserRole, # Title
2: QtCore.Qt.UserRole + 1, # Author
3: QtCore.Qt.UserRole + 2, # Year
4: QtCore.Qt.UserRole + 7, # Position percentage
5: QtCore.Qt.UserRole + 4} # Tags
4: QtCore.Qt.UserRole + 12, # Last read
5: QtCore.Qt.UserRole + 7, # Position percentage
6: QtCore.Qt.UserRole + 4} # Tags
self.common_functions = ProxyModelsCommonFunctions(self)
def columnCount(self, parent):
return 6
return 7
def headerData(self, column, orientation, role):
if role == QtCore.Qt.DisplayRole:
return self.header_data[column]
try:
return self.header_data[column]
except IndexError:
print('Table proxy model: Can\'t find header for column', column)
return 'IndexError'
def flags(self, index):
# Tag editing will take place by way of a right click menu
@@ -97,11 +112,12 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
source_index = self.mapToSource(index)
item = self.sourceModel().item(source_index.row(), 0)
if role == QtCore.Qt.TextAlignmentRole and index.column() == 3:
return QtCore.Qt.AlignHCenter
if role == QtCore.Qt.TextAlignmentRole:
if index.column() in (3, 4):
return QtCore.Qt.AlignHCenter
if role == QtCore.Qt.DecorationRole:
if index.column() == 4:
if index.column() == 5:
return_pixmap = None
file_exists = item.data(QtCore.Qt.UserRole + 5)
@@ -136,11 +152,17 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
return return_pixmap
elif role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
if index.column() in (0, 4): # Cover and Status
if index.column() in (0, 5): # Cover and Status
return QtCore.QVariant()
return item.data(self.role_dictionary[index.column()])
if index.column() == 4:
last_accessed = item.data(self.role_dictionary[index.column()])
if last_accessed:
right_now = QtCore.QDateTime().currentDateTime()
time_diff = last_accessed.msecsTo(right_now)
return self.time_convert(time_diff // 1000)
return item.data(self.role_dictionary[index.column()])
else:
return QtCore.QVariant()
@@ -152,11 +174,28 @@ class TableProxyModel(QtCore.QSortFilterProxyModel):
output = self.common_functions.filterAcceptsRow(row, parent)
return output
def sort_table_columns(self, column):
sorting_order = self.sender().sortIndicatorOrder()
self.sort(0, sorting_order)
self.setSortRole(self.role_dictionary[column])
def sort_table_columns(self, column=None):
column = self.tableViewHeader.sortIndicatorSection()
sorting_order = self.tableViewHeader.sortIndicatorOrder()
self.sort(0, sorting_order)
if column != 0:
self.setSortRole(self.role_dictionary[column])
def time_convert(self, seconds):
seconds = int(seconds)
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
d, h = divmod(h, 24)
if d > 0:
return f'{d}d'
if h > 0:
return f'{h}h'
if m > 0:
return f'{m}m'
else:
return '<1m'
class ProxyModelsCommonFunctions:
def __init__(self, parent_model):

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2017-18 BasioMeusPuga
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# TODO
# Account for files with passwords
import os
import time
import zipfile
from lector.rarfile import rarfile
class ParseCOMIC:
def __init__(self, filename, *args):
self.filename = filename
self.book = None
self.image_list = None
self.book_extension = os.path.splitext(self.filename)
def read_book(self):
try:
if self.book_extension[1] == '.cbz':
self.book = zipfile.ZipFile(
self.filename, mode='r', allowZip64=True)
self.image_list = [i.filename for i in self.book.infolist() if not i.is_dir()]
elif self.book_extension[1] == '.cbr':
self.book = rarfile.RarFile(self.filename)
self.image_list = [i.filename for i in self.book.infolist() if not i.isdir()]
self.image_list.sort()
except: # Specifying no exception here is warranted
print('Cannot parse ' + self.filename)
return
def get_title(self):
title = os.path.basename(self.book_extension[0]).strip(' ')
return title
def get_author(self):
return None
def get_year(self):
creation_time = time.ctime(os.path.getctime(self.filename))
creation_year = creation_time.split()[-1]
return creation_year
def get_cover_image(self):
# The first image in the archive may not be the cover
# It is implied, however, that the first image in order
# will be the cover
return self.book.read(self.image_list[0])
def get_isbn(self):
return None
def get_tags(self):
return None
def get_contents(self):
file_settings = {'images_only': True}
contents = [(f'Page {count + 1}', i) for count, i in enumerate(self.image_list)]
return contents, file_settings

View File

@@ -19,7 +19,7 @@
import os
import zipfile
from ePub.read_epub import EPUB
from lector.ePub.read_epub import EPUB
class ParseEPUB:
@@ -28,9 +28,8 @@ class ParseEPUB:
# Maybe also include book description
self.book_ref = None
self.book = None
self.temp_dir = temp_dir
self.filename = filename
self.file_md5 = file_md5
self.extract_path = os.path.join(temp_dir, file_md5)
def read_book(self):
self.book_ref = EPUB(self.filename)
@@ -59,10 +58,9 @@ class ParseEPUB:
return self.book['tags']
def get_contents(self):
extract_path = os.path.join(self.temp_dir, self.file_md5)
zipfile.ZipFile(self.filename).extractall(extract_path)
zipfile.ZipFile(self.filename).extractall(self.extract_path)
self.book_ref.parse_chapters(temp_dir=self.temp_dir)
self.book_ref.parse_chapters(temp_dir=self.extract_path)
file_settings = {
'images_only': False}
return self.book['book_list'], file_settings

View File

@@ -24,8 +24,8 @@ import sys
import shutil
import zipfile
from ePub.read_epub import EPUB
import KindleUnpack.kindleunpack as KindleUnpack
from lector.ePub.read_epub import EPUB
import lector.KindleUnpack.kindleunpack as KindleUnpack
class ParseMOBI:

105
lector/parsers/pdf.py Normal file
View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
# This file is a part of Lector, a Qt based ebook reader
# Copyright (C) 2018 BasioMeusPuga
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import io
import os
from PyQt5 import QtCore
from bs4 import BeautifulSoup
proceed = True
try:
import popplerqt5
except ImportError:
print('python-poppler-qt5 is not installed. Pdf files will not work.')
proceed = False
class ParsePDF:
def __init__(self, filename, *args):
self.filename = filename
self.book = None
self.metadata = None
def read_book(self):
if not proceed:
return
self.book = popplerqt5.Poppler.Document.load(self.filename)
if not self.book:
return
self.metadata = BeautifulSoup(self.book.metadata(), 'xml')
def get_title(self):
try:
title = self.metadata.find('title').text
return title.replace('\n', '')
except AttributeError:
return os.path.splitext(os.path.basename(self.filename))[0]
def get_author(self):
try:
author = self.metadata.find('creator').text
return author.replace('\n', '')
except AttributeError:
return 'Unknown'
def get_year(self):
try:
year = self.metadata.find('MetadataDate').text
return year.replace('\n', '')
except AttributeError:
return 9999
def get_cover_image(self):
self.book.setRenderHint(
popplerqt5.Poppler.Document.Antialiasing
and popplerqt5.Poppler.Document.TextAntialiasing)
cover_page = self.book.page(0)
cover_image = cover_page.renderToImage(300, 300)
return resize_image(cover_image)
def get_isbn(self):
return None
def get_tags(self):
try:
tags = self.metadata.find('Keywords').text
return tags.replace('\n', '')
except AttributeError:
return None
def get_contents(self):
file_settings = {'images_only': True}
contents = [(f'Page {i + 1}', i) for i in range(self.book.numPages())]
return contents, file_settings
def resize_image(cover_image):
cover_image = cover_image.scaled(
420, 600, QtCore.Qt.IgnoreAspectRatio)
byte_array = QtCore.QByteArray()
buffer = QtCore.QBuffer(byte_array)
buffer.open(QtCore.QIODevice.WriteOnly)
cover_image.save(buffer, 'jpg', 75)
cover_image_final = io.BytesIO(byte_array)
cover_image_final.seek(0)
return cover_image_final.getvalue()

View File

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 7 3 L 7 7 L 3 7 L 3 9 L 7 9 L 7 13 L 9 13 L 9 9 L 13 9 L 13 7 L 9 7 L 9 3 L 7 3 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 4 0.00390625 C 4 0.00390625 3 0.00390625 3 1.0039062 L 3 15.003906 L 8 12.003906 L 13 15.003906 L 13 1.0039062 C 13 1.0039062 13 0.00390625 12 0.00390625 L 4 0.00390625 z M 7 3.0039062 L 9 3.0039062 L 9 5.0039062 L 11 5.0039062 L 11 7.0039062 L 9 7.0039062 L 9 9.0039062 L 7 9.0039062 L 7 7.0039062 L 5 7.0039062 L 5 5.0039062 L 7 5.0039062 L 7 3.0039062 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 703 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 0.390625 L 5.8808594 5.8847656 L 0 6.2011719 L 4.5722656 9.9160156 L 3.0566406 15.607422 L 8 12.40625 L 12.943359 15.607422 L 11.427734 9.9160156 L 16 6.2011719 L 10.119141 5.8847656 L 8 0.390625 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 1 C 7.79297 1.66364 7.5132275 2.3110656 7.2109375 2.9472656 C 5.6704375 6.0974656 3.2599437 8.2540875 3.0273438 10.242188 C 3.0213438 10.271888 3.0052 10.304384 3 10.333984 L 3.0195312 10.339844 C 3.0145313 10.408244 3 10.476722 3 10.544922 C 3 13.005122 5.2386 15 8 15 C 10.7614 15 13 13.005122 13 10.544922 C 13 10.476722 12.985469 10.408214 12.980469 10.339844 L 13 10.333984 C 12.995 10.304484 12.978956 10.271887 12.972656 10.242188 C 12.740106 8.2539875 10.329662 6.0973656 8.7890625 2.9472656 C 8.4867825 2.3110456 8.20702 1.6636 8 1 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 12.210938 1 C 11.998438 1 11.784141 1.0830469 11.619141 1.2480469 L 9.9902344 2.8886719 L 13.109375 6.0078125 L 14.75 4.3789062 C 15.08 4.0489063 15.08 3.5272656 14.75 3.1972656 L 12.800781 1.2480469 C 12.635781 1.0830469 12.423437 1 12.210938 1 z M 8.8691406 4.0078125 L 0.99023438 11.888672 L 0.99023438 15.007812 L 4.109375 15.007812 L 11.990234 7.1289062 L 8.8691406 4.0078125 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 729 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5.9980469 1.0195312 L 5.9980469 7.0195312 L 3.6582031 7.0195312 L 7.9902344 13.324219 L 12.371094 7.0195312 L 9.9980469 7.0195312 L 9.9980469 1.0488281 L 5.9980469 1.0195312 z M 1 14.03125 L 1 16 L 15.005859 16 L 15 14.03125 L 1 14.03125 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5 L 1 7 L 9 7.0039062 L 9 5.0039062 L 1 5 z M 15 5.0039062 L 10 8.0039062 L 15 11.003906 L 15 5.0039062 z M 1 9 L 1 11 L 9 11 L 9 9 L 1 9 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 11.003906 L 6 8.0039062 L 1 5.0039062 z M 7 5.0039062 L 7 7.0039062 L 15 7.0039062 L 15 5.0039062 L 7 5.0039062 z M 15 9 L 7 9.0039062 L 7 11.003906 L 15 11 L 15 9 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 601 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 4 5.0039062 L 4 7.0039062 L 12 7.0039062 L 12 5.0039062 L 4 5.0039062 z M 4 9.0039062 L 4 11.003906 L 12 11.003906 L 12 9.0039062 L 4 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 7.0039062 L 15 7.0039062 L 15 5.0039062 L 1 5.0039062 z M 1 9.0039062 L 1 11.003906 L 15 11.003906 L 15 9.0039062 L 1 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 7.0039062 L 9 7.0039062 L 9 5.0039062 L 1 5.0039062 z M 1 9.0039062 L 1 11.003906 L 9 11.003906 L 9 9.0039062 L 1 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 7 5.0039062 L 7 7.0039062 L 15 7.0039062 L 15 5.0039062 L 7 5.0039062 z M 7 9.0039062 L 7 11.003906 L 15 11.003906 L 15 9.0039062 L 7 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5 3 L 5 4 L 2 4 L 2 12 L 5 12 L 5 13 L 14 13 L 14 10 L 5 10 L 5 11 L 3 11 L 3 9 L 4 9 L 4 7 L 3 7 L 3 5 L 5 5 L 5 6 L 14 6 L 14 3 L 5 3 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5 2 L 5 3 L 2 3 L 2 13 L 5 13 L 5 14 L 14 14 L 14 11 L 5 11 L 5 12 L 3 12 L 3 9 L 4 9 L 4 7 L 3 7 L 3 4 L 5 4 L 5 5 L 14 5 L 14 2 L 5 2 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 7 1 L 2 15 L 4.5 15 L 5.5625 12 L 10.4375 12 L 11.5 15 L 14.28125 15 L 9 1 L 7 1 z M 14 1 A 1 1 0 0 0 13 2 A 1 1 0 0 0 14 3 A 1 1 0 0 0 15 2 A 1 1 0 0 0 14 1 z M 14 4 A 1 1 0 0 0 13 5 A 1 1 0 0 0 14 6 A 1 1 0 0 0 15 5 A 1 1 0 0 0 14 4 z M 8 5 L 9.75 10 L 6.25 10 L 8 5 z M 14 7 A 1 1 0 0 0 13 8 A 1 1 0 0 0 14 9 A 1 1 0 0 0 15 8 A 1 1 0 0 0 14 7 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="m4.5 3a3.5 5 0 0 0 -3.5 5 3.5 5 0 0 0 3.5 5 3.5 5 0 0 0 3.5 -5 3.5 5 0 0 0 -3.5 -5zm3.5 5a3.5 5 0 0 0 3.5 5 3.5 5 0 0 0 3.5 -5 3.5 5 0 0 0 -3.5 -5 3.5 5 0 0 0 -3.5 5zm-3 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2zm7 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 642 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 1.015625 C 4.134 1.015625 1 4.149625 1 8.015625 C 1 11.881625 4.134 15.015625 8 15.015625 C 11.1748 15.015625 13.86145 12.912425 14.71875 10.015625 L 12.5625 10.015625 C 11.78823 11.775125 10.0457 13.015625 8 13.015625 C 5.2386 13.015625 3 10.777025 3 8.015625 C 3 5.254225 5.2386 3.015625 8 3.015625 C 9.3816 3.015625 10.615525 3.59065 11.515625 4.5 L 9.0058594 7.015625 L 15.005859 7.015625 L 15.005859 1.015625 L 12.953125 3.0683594 C 11.683125 1.8033594 9.9339063 1.015625 8.0039062 1.015625 L 8 1.015625 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 859 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3 7 L 3 9 L 13 9 L 13 7 L 3 7 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 378 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 6.4902344 0.99609375 C 3.4613344 0.99609375 0.99023438 3.4706937 0.99023438 6.4960938 C 0.99023438 9.5214938 3.4613344 11.996094 6.4902344 11.996094 C 7.6422344 11.996094 8.7279444 11.638254 9.6152344 11.027344 L 13.302734 14.714844 A 1.0055 1.0055 0 1 0 14.708984 13.277344 L 11.021484 9.5898438 C 11.632274 8.7038438 12.021484 7.6459938 12.021484 6.4960938 C 12.021484 3.4706937 9.5190344 0.99609375 6.4902344 0.99609375 z M 6.4902344 2.9960938 C 8.4376344 2.9960938 9.9902344 4.5508938 9.9902344 6.4960938 C 9.9902344 8.4411937 8.4376344 9.9960938 6.4902344 9.9960938 C 4.5428344 9.9960938 2.9902344 8.4411937 2.9902344 6.4960938 C 2.9902344 4.5508938 4.5428344 2.9960938 6.4902344 2.9960938 z" transform="translate(3 3)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 6.25 1 L 6.0957031 2.84375 A 5.5 5.5 0 0 0 4.4882812 3.7734375 L 2.8125 2.984375 L 1.0625 6.015625 L 2.5839844 7.0722656 A 5.5 5.5 0 0 0 2.5 8 A 5.5 5.5 0 0 0 2.5800781 8.9316406 L 1.0625 9.984375 L 2.8125 13.015625 L 4.484375 12.228516 A 5.5 5.5 0 0 0 6.0957031 13.152344 L 6.2460938 15.001953 L 9.7460938 15.001953 L 9.9003906 13.158203 A 5.5 5.5 0 0 0 11.507812 12.228516 L 13.183594 13.017578 L 14.933594 9.9863281 L 13.412109 8.9296875 A 5.5 5.5 0 0 0 13.496094 8.0019531 A 5.5 5.5 0 0 0 13.416016 7.0703125 L 14.933594 6.0175781 L 13.183594 2.9863281 L 11.511719 3.7734375 A 5.5 5.5 0 0 0 9.9003906 2.8496094 L 9.75 1 L 6.25 1 z M 8 6 A 2 2 0 0 1 10 8 A 2 2 0 0 1 8 10 A 2 2 0 0 1 6 8 A 2 2 0 0 1 8 6 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="m1 1v14h14v-14h-14zm2 2h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm-8 4h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 3.0039062 L 1 5.0039062 L 3 5.0039062 L 3 3.0039062 L 1 3.0039062 z M 5 3.0039062 L 5 5.0039062 L 15 5.0039062 L 15 3.0039062 L 5 3.0039062 z M 1 7.0039062 L 1 9.0039062 L 3 9.0039062 L 3 7.0039062 L 1 7.0039062 z M 5 7.0039062 L 5 9.0039062 L 15 9.0039062 L 15 7.0039062 L 5 7.0039062 z M 1 11.003906 L 1 13.003906 L 3 13.003906 L 3 11.003906 L 1 11.003906 z M 5 11.003906 L 5 13.003906 L 15 13.003906 L 15 11.003906 L 5 11.003906 z" transform="translate(3 3)"/>
</svg>

After

Width:  |  Height:  |  Size: 782 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 6 0.9921875 C 5 0.9921875 5 1.9921875 5 1.9921875 L 2 1.9921875 C 2 1.9921875 1 1.9956938 1 2.9960938 L 1 3.9960938 L 14 3.9921875 L 14 2.9960938 C 14 1.9960938 13 1.9921875 13 1.9921875 L 10 1.9921875 C 10 1.9921875 10 0.9921875 9 0.9921875 L 6 0.9921875 z M 2 4.9960938 L 2 13.996094 C 2.00005 14.519674 2.47642 14.996044 3 14.996094 L 12 14.996094 C 12.52358 14.996044 12.99995 14.519674 13 13.996094 L 13 4.9960938 L 2 4.9960938 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 781 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 2 2.0039062 C 1 2.0039062 1 3.0039062 1 3.0039062 L 1 7.0039062 L 3 7.0039062 L 3 4.0039062 L 6 4.0039062 L 6 2.0039062 L 2 2.0039062 z M 10 2.0039062 L 10 4.0039062 L 13 4.0039062 L 13 7.0039062 L 15 7.0039062 L 15 3.0039062 C 15 2.0039062 14 2.0039062 14 2.0039062 L 10 2.0039062 z M 1 9.0039062 L 1 13.003906 C 1 14.003906 2 14.003906 2 14.003906 L 6 14.003906 L 6 12.003906 L 3 12.003906 L 3 9.0039062 L 1 9.0039062 z M 13 9.0039062 L 13 12.003906 L 10 12.003906 L 10 14.003906 L 14 14.003906 C 14 14.003906 15 14.003906 15 13.003906 L 15 9.0039062 L 13 9.0039062 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 916 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1.25 3.0039062 C 1.1115 3.0039063 1 3.1154062 1 3.2539062 L 1 4.7539062 C 1 4.8924062 1.1115 5.0039062 1.25 5.0039062 L 2.75 5.0039062 C 2.8885 5.0039062 3 4.8924062 3 4.7539062 L 3 3.2539062 C 3 3.1154062 2.8885 3.0039062 2.75 3.0039062 L 1.25 3.0039062 z M 5.25 3.0039062 C 5.1115 3.0039063 5 3.1154062 5 3.2539062 L 5 4.7539062 C 5 4.8924062 5.1115 5.0039062 5.25 5.0039062 L 6.75 5.0039062 C 6.8885 5.0039062 7 4.8924062 7 4.7539062 L 7 3.2539062 C 7 3.1154062 6.8885 3.0039062 6.75 3.0039062 L 5.25 3.0039062 z M 9.25 3.0039062 C 9.1115 3.0039063 9 3.1154062 9 3.2539062 L 9 4.7539062 C 9 4.8924062 9.1115 5.0039062 9.25 5.0039062 L 10.75 5.0039062 C 10.8885 5.0039062 11 4.8924062 11 4.7539062 L 11 3.2539062 C 11 3.1154062 10.8885 3.0039062 10.75 3.0039062 L 9.25 3.0039062 z M 13.25 3.0039062 C 13.1115 3.0039063 13 3.1154062 13 3.2539062 L 13 4.7539062 C 13 4.8924062 13.1115 5.0039062 13.25 5.0039062 L 14.75 5.0039062 C 14.8885 5.0039062 15 4.8924062 15 4.7539062 L 15 3.2539062 C 15 3.1154062 14.8885 3.0039062 14.75 3.0039062 L 13.25 3.0039062 z M 1.25 7.0039062 C 1.1115 7.0039063 1 7.1154063 1 7.2539062 L 1 8.7539062 C 1 8.8924063 1.1115 9.0039062 1.25 9.0039062 L 2.75 9.0039062 C 2.8885 9.0039062 3 8.8924063 3 8.7539062 L 3 7.2539062 C 3 7.1154063 2.8885 7.0039062 2.75 7.0039062 L 1.25 7.0039062 z M 5.25 7.0039062 C 5.1115 7.0039063 5 7.1154063 5 7.2539062 L 5 8.7539062 C 5 8.8924063 5.1115 9.0039062 5.25 9.0039062 L 6.75 9.0039062 C 6.8885 9.0039062 7 8.8924063 7 8.7539062 L 7 7.2539062 C 7 7.1154063 6.8885 7.0039062 6.75 7.0039062 L 5.25 7.0039062 z M 9.25 7.0039062 C 9.1115 7.0039063 9 7.1154063 9 7.2539062 L 9 8.7539062 C 9 8.8924063 9.1115 9.0039062 9.25 9.0039062 L 10.75 9.0039062 C 10.8885 9.0039062 11 8.8924063 11 8.7539062 L 11 7.2539062 C 11 7.1154063 10.8885 7.0039062 10.75 7.0039062 L 9.25 7.0039062 z M 13.25 7.0039062 C 13.1115 7.0039063 13 7.1154063 13 7.2539062 L 13 8.7539062 C 13 8.8924063 13.1115 9.0039062 13.25 9.0039062 L 14.75 9.0039062 C 14.8885 9.0039062 15 8.8924063 15 8.7539062 L 15 7.2539062 C 15 7.1154063 14.8885 7.0039062 14.75 7.0039062 L 13.25 7.0039062 z M 1.25 11.003906 C 1.1115 11.003906 1 11.115406 1 11.253906 L 1 12.753906 C 1 12.892406 1.1115 13.003906 1.25 13.003906 L 2.75 13.003906 C 2.8885 13.003906 3 12.892406 3 12.753906 L 3 11.253906 C 3 11.115406 2.8885 11.003906 2.75 11.003906 L 1.25 11.003906 z M 5.25 11.003906 C 5.1115 11.003906 5 11.115406 5 11.253906 L 5 12.753906 C 5 12.892406 5.1115 13.003906 5.25 13.003906 L 6.75 13.003906 C 6.8885 13.003906 7 12.892406 7 12.753906 L 7 11.253906 C 7 11.115406 6.8885 11.003906 6.75 11.003906 L 5.25 11.003906 z M 9.25 11.003906 C 9.1115 11.003906 9 11.115406 9 11.253906 L 9 12.753906 C 9 12.892406 9.1115 13.003906 9.25 13.003906 L 10.75 13.003906 C 10.8885 13.003906 11 12.892406 11 12.753906 L 11 11.253906 C 11 11.115406 10.8885 11.003906 10.75 11.003906 L 9.25 11.003906 z M 13.25 11.003906 C 13.1115 11.003906 13 11.115406 13 11.253906 L 13 12.753906 C 13 12.892406 13.1115 13.003906 13.25 13.003906 L 14.75 13.003906 C 14.8885 13.003906 15 12.892406 15 12.753906 L 15 11.253906 C 15 11.115406 14.8885 11.003906 14.75 11.003906 L 13.25 11.003906 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 10.029297 1 C 9.8819169 1.003 9.7293425 1.0150094 9.5703125 1.0371094 C 9.0034025 1.1159094 8.3186875 1.3914781 7.5546875 1.7988281 C 6.0140875 0.94567812 4.8137406 0.89374688 3.8066406 1.1542969 C 2.7378406 1.4308069 1.868925 1.8823869 0.515625 1.8417969 L 0 1.8261719 L 0 16 L 15 16 L 15 1.84375 L 14.482422 1.8613281 C 12.965822 1.9165281 12.211922 1.464645 11.232422 1.171875 C 10.865122 1.062075 10.471417 0.99098 10.029297 1 z M 5.21875 1.9941406 C 5.71774 2.0327406 6.2822 2.213495 7 2.609375 L 7 11.333984 C 5.8956 10.692224 4.7902063 10.643969 3.8164062 10.886719 C 3.1632062 11.049539 2.5692 11.237652 2 11.388672 L 2 2.6621094 C 2.8021 2.5141794 3.4740875 2.2830225 4.0546875 2.1328125 C 4.2857975 2.0730125 4.5091312 2.02398 4.7382812 2 C 4.8933012 1.9838 5.05242 1.9813406 5.21875 1.9941406 z M 10.337891 2.0019531 C 10.543791 2.0284531 10.742569 2.079065 10.949219 2.140625 C 11.483649 2.300375 12.1426 2.5531719 13 2.7011719 L 13 11.384766 C 12.43016 11.232366 11.837453 11.042719 11.189453 10.880859 C 10.209613 10.636099 9.0981 10.696078 8 11.367188 L 8 2.6679688 C 8.70328 2.2824487 9.2453875 2.0716725 9.6796875 2.0078125 C 9.9189475 1.9726125 10.131991 1.9756531 10.337891 2.0019531 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 3.0039062 C 6.4492 3.0190063 4.8879094 3.3732319 3.5371094 4.1386719 C 2.9987094 4.4892919 2.3523344 4.9421175 1.8652344 5.3984375 C 1.0987444 6.1488575 0.4427 7.0244062 0 8.0039062 C 1.2149 10.683506 3.8859187 12.6474 6.8242188 12.9375 C 8.7516188 13.15561 10.768591 12.822631 12.462891 11.869141 C 13.001291 11.518521 13.647666 11.065695 14.134766 10.609375 C 14.901256 9.858955 15.5573 8.9834063 16 8.0039062 C 14.785 5.3245062 12.114181 3.3601125 9.1757812 3.0703125 C 8.7859013 3.0248425 8.39251 3.0038963 8 3.0039062 z M 8 5.0019531 L 8 5.0039062 C 9.607 4.9683062 11.0303 6.4057062 11 8.0039062 C 11.0515 9.7703063 9.2909813 11.294844 7.5507812 10.964844 C 5.7931812 10.758504 4.5587188 8.7851344 5.1367188 7.1152344 C 5.5058788 5.8858344 6.7125 4.9866531 8 5.0019531 z M 8 7.0039062 A 1 1 0 0 0 7 8.0039062 A 1 1 0 0 0 8 9.0039062 A 1 1 0 0 0 9 8.0039062 A 1 1 0 0 0 8 7.0039062 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3.1894531 2.0039062 C 2.5267531 2.0039062 2.0019531 2.5527963 2.0019531 3.2226562 L 2.0019531 7.0039062 L 4.0019531 7.0039062 L 4.0019531 4.0039062 L 7.0019531 4.0039062 L 7.0019531 2.0039062 L 3.1894531 2.0039062 z M 9.0019531 2.0039062 L 9.0019531 4.0039062 L 12.001953 4.0039062 L 12.001953 7.0039062 L 14.001953 7.0039062 L 14.001953 3.2226562 C 14.001953 2.5528963 13.477153 2.0039062 12.814453 2.0039062 L 9.0019531 2.0039062 z M 2.0019531 9.0039062 L 2.0019531 12.785156 C 2.0019531 13.454916 2.5267531 14.003906 3.1894531 14.003906 L 7.0019531 14.003906 L 7.0019531 12.003906 L 4.0019531 12.003906 L 4.0019531 9.0039062 L 2.0019531 9.0039062 z M 12.001953 9.0039062 L 12.001953 12.003906 L 9.0019531 12.003906 L 9.0019531 14.003906 L 12.814453 14.003906 C 13.477153 14.003906 14.001953 13.455016 14.001953 12.785156 L 14.001953 9.0039062 L 12.001953 9.0039062 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3.1875 2 C 2.5248 2 2 2.54895 2 3.21875 L 2 7 L 4 7 L 4 4 L 7 4 L 7 2 L 3.1875 2 z M 9 2 L 9 4 L 12 4 L 12 7 L 14 7 L 14 3.21875 C 14 2.54885 13.4755 2 12.8125 2 L 9 2 z M 7 5 L 7 11 L 9 11 L 9 5 L 7 5 z M 6 6 L 4 8 L 6 10 L 6 6 z M 10 6 L 10 10 L 12 8 L 10 6 z M 2 9 L 2 12.78125 C 2 13.45125 2.5248 14 3.1875 14 L 7 14 L 7 12 L 4 12 L 4 9 L 2 9 z M 12 9 L 12 12 L 9 12 L 9 14 L 12.8125 14 C 13.4755 14 14 13.45125 14 12.78125 L 14 9 L 12 9 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 790 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3 2 C 2.446 2 2 2.446 2 3 L 2 13 C 2 13.554 2.446 14 3 14 L 13 14 C 13.554 14 14 13.554 14 13 L 14 3 C 14 2.446 13.554 2 13 2 L 3 2 z M 7 5 L 9 5 L 9 7 L 11 7 L 11 9 L 9 9 L 9 11 L 7 11 L 7 9 L 5 9 L 5 7 L 7 7 L 7 5 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3 2 C 2.446 2 2 2.446 2 3 L 2 13 C 2 13.554 2.446 14 3 14 L 13 14 C 13.554 14 14 13.554 14 13 L 14 3 C 14 2.446 13.554 2 13 2 L 3 2 z M 7 5 L 9 5 L 9 11 L 7 11 L 7 7 L 6 7 L 6 6 C 6 6 7 6 7 5 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 540 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#5c616c; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 3 2 C 2.446 2 2 2.446 2 3 L 2 13 C 2 13.554 2.446 14 3 14 L 13 14 C 13.554 14 14 13.554 14 13 L 14 3 C 14 2.446 13.554 2 13 2 L 3 2 z M 5 7 L 11 7 L 11 9 L 5 9 L 5 7 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 7 3 L 7 7 L 3 7 L 3 9 L 7 9 L 7 13 L 9 13 L 9 9 L 13 9 L 13 7 L 9 7 L 9 3 L 7 3 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 4 0.00390625 C 4 0.00390625 3 0.00390625 3 1.0039062 L 3 15.003906 L 8 12.003906 L 13 15.003906 L 13 1.0039062 C 13 1.0039062 13 0.00390625 12 0.00390625 L 4 0.00390625 z M 7 3.0039062 L 9 3.0039062 L 9 5.0039062 L 11 5.0039062 L 11 7.0039062 L 9 7.0039062 L 9 9.0039062 L 7 9.0039062 L 7 7.0039062 L 5 7.0039062 L 5 5.0039062 L 7 5.0039062 L 7 3.0039062 z" transform="translate(3 3)"/>
</svg>

After

Width:  |  Height:  |  Size: 703 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 0.390625 L 5.8808594 5.8847656 L 0 6.2011719 L 4.5722656 9.9160156 L 3.0566406 15.607422 L 8 12.40625 L 12.943359 15.607422 L 11.427734 9.9160156 L 16 6.2011719 L 10.119141 5.8847656 L 8 0.390625 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 8 1 C 7.79297 1.66364 7.5132275 2.3110656 7.2109375 2.9472656 C 5.6704375 6.0974656 3.2599437 8.2540875 3.0273438 10.242188 C 3.0213438 10.271888 3.0052 10.304384 3 10.333984 L 3.0195312 10.339844 C 3.0145313 10.408244 3 10.476722 3 10.544922 C 3 13.005122 5.2386 15 8 15 C 10.7614 15 13 13.005122 13 10.544922 C 13 10.476722 12.985469 10.408214 12.980469 10.339844 L 13 10.333984 C 12.995 10.304484 12.978956 10.271887 12.972656 10.242188 C 12.740106 8.2539875 10.329662 6.0973656 8.7890625 2.9472656 C 8.4867825 2.3110456 8.20702 1.6636 8 1 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 891 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 12.210938 1 C 11.998438 1 11.784141 1.0830469 11.619141 1.2480469 L 9.9902344 2.8886719 L 13.109375 6.0078125 L 14.75 4.3789062 C 15.08 4.0489063 15.08 3.5272656 14.75 3.1972656 L 12.800781 1.2480469 C 12.635781 1.0830469 12.423437 1 12.210938 1 z M 8.8691406 4.0078125 L 0.99023438 11.888672 L 0.99023438 15.007812 L 4.109375 15.007812 L 11.990234 7.1289062 L 8.8691406 4.0078125 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 729 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5.9980469 1.0195312 L 5.9980469 7.0195312 L 3.6582031 7.0195312 L 7.9902344 13.324219 L 12.371094 7.0195312 L 9.9980469 7.0195312 L 9.9980469 1.0488281 L 5.9980469 1.0195312 z M 1 14.03125 L 1 16 L 15.005859 16 L 15 14.03125 L 1 14.03125 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 586 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5 L 1 7 L 9 7.0039062 L 9 5.0039062 L 1 5 z M 15 5.0039062 L 10 8.0039062 L 15 11.003906 L 15 5.0039062 z M 1 9 L 1 11 L 9 11 L 9 9 L 1 9 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 11.003906 L 6 8.0039062 L 1 5.0039062 z M 7 5.0039062 L 7 7.0039062 L 15 7.0039062 L 15 5.0039062 L 7 5.0039062 z M 15 9 L 7 9.0039062 L 7 11.003906 L 15 11 L 15 9 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 601 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 4 5.0039062 L 4 7.0039062 L 12 7.0039062 L 12 5.0039062 L 4 5.0039062 z M 4 9.0039062 L 4 11.003906 L 12 11.003906 L 12 9.0039062 L 4 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 7.0039062 L 15 7.0039062 L 15 5.0039062 L 1 5.0039062 z M 1 9.0039062 L 1 11.003906 L 15 11.003906 L 15 9.0039062 L 1 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 1 5.0039062 L 1 7.0039062 L 9 7.0039062 L 9 5.0039062 L 1 5.0039062 z M 1 9.0039062 L 1 11.003906 L 9 11.003906 L 9 9.0039062 L 1 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 1 1 L 1 3 L 15 3 L 15 1 L 1 1 z M 7 5.0039062 L 7 7.0039062 L 15 7.0039062 L 15 5.0039062 L 7 5.0039062 z M 7 9.0039062 L 7 11.003906 L 15 11.003906 L 15 9.0039062 L 7 9.0039062 z M 1 13 L 1 15 L 15 15 L 15 13 L 1 13 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5 3 L 5 4 L 2 4 L 2 12 L 5 12 L 5 13 L 14 13 L 14 10 L 5 10 L 5 11 L 3 11 L 3 9 L 4 9 L 4 7 L 3 7 L 3 5 L 5 5 L 5 6 L 14 6 L 14 3 L 5 3 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 5 2 L 5 3 L 2 3 L 2 13 L 5 13 L 5 14 L 14 14 L 14 11 L 5 11 L 5 12 L 3 12 L 3 9 L 4 9 L 4 7 L 3 7 L 3 4 L 5 4 L 5 5 L 14 5 L 14 2 L 5 2 z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 484 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="M 7 1 L 2 15 L 4.5 15 L 5.5625 12 L 10.4375 12 L 11.5 15 L 14.28125 15 L 9 1 L 7 1 z M 14 1 A 1 1 0 0 0 13 2 A 1 1 0 0 0 14 3 A 1 1 0 0 0 15 2 A 1 1 0 0 0 14 1 z M 14 4 A 1 1 0 0 0 13 5 A 1 1 0 0 0 14 6 A 1 1 0 0 0 15 5 A 1 1 0 0 0 14 4 z M 8 5 L 9.75 10 L 6.25 10 L 8 5 z M 14 7 A 1 1 0 0 0 13 8 A 1 1 0 0 0 14 9 A 1 1 0 0 0 15 8 A 1 1 0 0 0 14 7 z" transform="translate(3 3)"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<defs>
<style id="current-color-scheme" type="text/css">
.ColorScheme-Text { color:#d3dae3; } .ColorScheme-Highlight { color:#5294e2; }
</style>
</defs>
<path style="fill:currentColor" class="ColorScheme-Text" d="m4.5 3a3.5 5 0 0 0 -3.5 5 3.5 5 0 0 0 3.5 5 3.5 5 0 0 0 3.5 -5 3.5 5 0 0 0 -3.5 -5zm3.5 5a3.5 5 0 0 0 3.5 5 3.5 5 0 0 0 3.5 -5 3.5 5 0 0 0 -3.5 -5 3.5 5 0 0 0 -3.5 5zm-3 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2zm7 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" transform="translate(4 4)"/>
</svg>

After

Width:  |  Height:  |  Size: 642 B

Some files were not shown because too many files have changed in this diff Show More