mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			125 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/env python2
 | 
						|
# vim:fileencoding=utf-8
 | 
						|
from __future__ import (unicode_literals, division, absolute_import,
 | 
						|
                        print_function)
 | 
						|
 | 
						|
__license__ = 'GPL v3'
 | 
						|
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
						|
 | 
						|
import re
 | 
						|
from PyQt5.Qt import QAction, QInputDialog
 | 
						|
from css_parser.css import CSSRule
 | 
						|
 | 
						|
# The base class that all tools must inherit from
 | 
						|
from calibre.gui2.tweak_book.plugin import Tool
 | 
						|
 | 
						|
from calibre import force_unicode
 | 
						|
from calibre.gui2 import error_dialog
 | 
						|
from calibre.ebooks.oeb.polish.container import OEB_DOCS, OEB_STYLES, serialize
 | 
						|
 | 
						|
 | 
						|
class DemoTool(Tool):
 | 
						|
 | 
						|
    #: Set this to a unique name it will be used as a key
 | 
						|
    name = 'demo-tool'
 | 
						|
 | 
						|
    #: If True the user can choose to place this tool in the plugins toolbar
 | 
						|
    allowed_in_toolbar = True
 | 
						|
 | 
						|
    #: If True the user can choose to place this tool in the plugins menu
 | 
						|
    allowed_in_menu = True
 | 
						|
 | 
						|
    def create_action(self, for_toolbar=True):
 | 
						|
        # Create an action, this will be added to the plugins toolbar and
 | 
						|
        # the plugins menu
 | 
						|
        ac = QAction(get_icons('images/icon.png'), 'Magnify fonts', self.gui)  # noqa
 | 
						|
        if not for_toolbar:
 | 
						|
            # Register a keyboard shortcut for this toolbar action. We only
 | 
						|
            # register it for the action created for the menu, not the toolbar,
 | 
						|
            # to avoid a double trigger
 | 
						|
            self.register_shortcut(ac, 'magnify-fonts-tool', default_keys=('Ctrl+Shift+Alt+D',))
 | 
						|
        ac.triggered.connect(self.ask_user)
 | 
						|
        return ac
 | 
						|
 | 
						|
    def ask_user(self):
 | 
						|
        # Ask the user for a factor by which to multiply all font sizes
 | 
						|
        factor, ok = QInputDialog.getDouble(
 | 
						|
            self.gui, 'Enter a magnification factor', 'Allow font sizes in the book will be multiplied by the specified factor',
 | 
						|
            value=2, min=0.1, max=4
 | 
						|
        )
 | 
						|
        if ok:
 | 
						|
            # Ensure any in progress editing the user is doing is present in the container
 | 
						|
            self.boss.commit_all_editors_to_container()
 | 
						|
            try:
 | 
						|
                self.magnify_fonts(factor)
 | 
						|
            except Exception:
 | 
						|
                # Something bad happened report the error to the user
 | 
						|
                import traceback
 | 
						|
                error_dialog(self.gui, _('Failed to magnify fonts'), _(
 | 
						|
                    'Failed to magnify fonts, click "Show details" for more info'),
 | 
						|
                    det_msg=traceback.format_exc(), show=True)
 | 
						|
                # Revert to the saved restore point
 | 
						|
                self.boss.revert_requested(self.boss.global_undo.previous_container)
 | 
						|
            else:
 | 
						|
                # Show the user what changes we have made, allowing her to
 | 
						|
                # revert them if necessary
 | 
						|
                self.boss.show_current_diff()
 | 
						|
                # Update the editor UI to take into account all the changes we
 | 
						|
                # have made
 | 
						|
                self.boss.apply_container_update_to_gui()
 | 
						|
 | 
						|
    def magnify_fonts(self, factor):
 | 
						|
        # Magnify all font sizes defined in the book by the specified factor
 | 
						|
        # First we create a restore point so that the user can undo all changes
 | 
						|
        # we make.
 | 
						|
        self.boss.add_savepoint('Before: Magnify fonts')
 | 
						|
 | 
						|
        container = self.current_container  # The book being edited as a container object
 | 
						|
 | 
						|
        # Iterate over all style declarations in the book, this means css
 | 
						|
        # stylesheets, <style> tags and style="" attributes
 | 
						|
        for name, media_type in container.mime_map.items():
 | 
						|
            if media_type in OEB_STYLES:
 | 
						|
                # A stylesheet. Parsed stylesheets are css_parser CSSStylesheet
 | 
						|
                # objects.
 | 
						|
                self.magnify_stylesheet(container.parsed(name), factor)
 | 
						|
                container.dirty(name)  # Tell the container that we have changed the stylesheet
 | 
						|
            elif media_type in OEB_DOCS:
 | 
						|
                # A HTML file. Parsed HTML files are lxml elements
 | 
						|
 | 
						|
                for style_tag in container.parsed(name).xpath('//*[local-name="style"]'):
 | 
						|
                    if style_tag.text and style_tag.get('type', None) in {None, 'text/css'}:
 | 
						|
                        # We have an inline CSS <style> tag, parse it into a
 | 
						|
                        # stylesheet object
 | 
						|
                        sheet = container.parse_css(style_tag.text)
 | 
						|
                        self.magnify_stylesheet(sheet, factor)
 | 
						|
                        style_tag.text = serialize(sheet, 'text/css', pretty_print=True)
 | 
						|
                        container.dirty(name)  # Tell the container that we have changed the stylesheet
 | 
						|
                for elem in container.parsed(name).xpath('//*[@style]'):
 | 
						|
                    # Process inline style attributes
 | 
						|
                    block = container.parse_css(elem.get('style'), is_declaration=True)
 | 
						|
                    self.magnify_declaration(block, factor)
 | 
						|
                    elem.set('style', force_unicode(block.getCssText(separator=' '), 'utf-8'))
 | 
						|
 | 
						|
    def magnify_stylesheet(self, sheet, factor):
 | 
						|
        # Magnify all fonts in the specified stylesheet by the specified
 | 
						|
        # factor.
 | 
						|
        for rule in sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE):
 | 
						|
            self.magnify_declaration(rule.style, factor)
 | 
						|
 | 
						|
    def magnify_declaration(self, style, factor):
 | 
						|
        # Magnify all fonts in the specified style declaration by the specified
 | 
						|
        # factor
 | 
						|
        val = style.getPropertyValue('font-size')
 | 
						|
        if not val:
 | 
						|
            return
 | 
						|
        # see if the font-size contains a number
 | 
						|
        num = re.search(r'[0-9.]+', val)
 | 
						|
        if num is not None:
 | 
						|
            num = num.group()
 | 
						|
            val = val.replace(num, '%f' % (float(num) * factor))
 | 
						|
            style.setProperty('font-size', val)
 | 
						|
        # We should also be dealing with the font shorthand property and
 | 
						|
        # font sizes specified as non numbers, but those are left as exercises
 | 
						|
        # for the reader
 |