Exemplo n.º 1
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        from calibre.utils.podofo import get_podofo
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        self.podofo = get_podofo()
        self.doc = self.podofo.PDFDoc()

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self._render_html,
                type=Qt.QueuedConnection)
        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(x,
                    Qt.ScrollBarAlwaysOff)
        self.render_queue = []
        self.combine_queue = []
        self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts')

        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc
Exemplo n.º 2
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing|
                    QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self.render_html,
                type=Qt.QueuedConnection)
        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(x,
                    Qt.ScrollBarAlwaysOff)
        self.report_progress = lambda x, y: x
        self.current_section = ''
        self.current_tl_section = ''
Exemplo n.º 3
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        # Turn off hinting in WebKit (requires a patched build of QtWebKit)
        os.environ['CALIBRE_WEBKIT_NO_HINTING'] = '1'
        try:
            must_use_qt()
            load_builtin_fonts()

            self.oeb = oeb_book
            self.input_plugin, self.opts, self.log = input_plugin, opts, log
            self.output_path = output_path
            from calibre.ebooks.oeb.base import OPF, OPF2_NS
            from lxml import etree
            from io import BytesIO
            package = etree.Element(OPF('package'),
                attrib={'version': '2.0', 'unique-identifier': 'dummy'},
                nsmap={None: OPF2_NS})
            from calibre.ebooks.metadata.opf2 import OPF
            self.oeb.metadata.to_opf2(package)
            self.metadata = OPF(BytesIO(etree.tostring(package))).to_book_metadata()
            self.cover_data = None

            if input_plugin.is_image_collection:
                log.debug('Converting input as an image collection...')
                self.convert_images(input_plugin.get_images())
            else:
                log.debug('Converting input as a text based book...')
                self.convert_text(oeb_book)
        finally:
            os.environ.pop('CALIBRE_WEBKIT_NO_HINTING', None)
Exemplo n.º 4
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        from calibre.utils.podofo import get_podofo
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        self.podofo = get_podofo()
        self.doc = self.podofo.PDFDoc()

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing
                                 | QPainter.TextAntialiasing
                                 | QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self._render_html,
                                       type=Qt.QueuedConnection)
        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(
                x, Qt.ScrollBarAlwaysOff)
        self.render_queue = []
        self.combine_queue = []
        self.tmp_path = PersistentTemporaryDirectory(u'_pdf_output_parts')

        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc
Exemplo n.º 5
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        self.mathjax_dir = P('mathjax', allow_user_override=False)
        current_log(log)
        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing
                                 | QPainter.TextAntialiasing
                                 | QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self.render_html,
                                       type=Qt.QueuedConnection)
        self.view.loadProgress.connect(self.load_progress)
        self.ignore_failure = None
        self.hang_check_timer = t = QTimer(self)
        t.timeout.connect(self.hang_check)
        t.setInterval(1000)

        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(
                x, Qt.ScrollBarAlwaysOff)
        self.report_progress = lambda x, y: x
        self.current_section = ''
        self.current_tl_section = ''
Exemplo n.º 6
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        from calibre.ebooks.oeb.transforms.split import Split
        # Turn off hinting in WebKit (requires a patched build of QtWebKit)
        os.environ['CALIBRE_WEBKIT_NO_HINTING'] = '1'
        self.filtered_font_warnings = set()
        try:
            # split on page breaks, as the JS code to convert page breaks to
            # column breaks will not work because of QWebSettings.LocalContentCanAccessFileUrls
            Split()(oeb_book, opts)
            must_use_qt()
            load_builtin_fonts()

            self.oeb = oeb_book
            self.input_plugin, self.opts, self.log = input_plugin, opts, log
            self.output_path = output_path
            from calibre.ebooks.oeb.base import OPF, OPF2_NS
            from lxml import etree
            from io import BytesIO
            package = etree.Element(OPF('package'),
                attrib={'version': '2.0', 'unique-identifier': 'dummy'},
                nsmap={None: OPF2_NS})
            from calibre.ebooks.metadata.opf2 import OPF
            self.oeb.metadata.to_opf2(package)
            self.metadata = OPF(BytesIO(etree.tostring(package))).to_book_metadata()
            self.cover_data = None

            if input_plugin.is_image_collection:
                log.debug('Converting input as an image collection...')
                self.convert_images(input_plugin.get_images())
            else:
                log.debug('Converting input as a text based book...')
                self.convert_text(oeb_book)
        finally:
            os.environ.pop('CALIBRE_WEBKIT_NO_HINTING', None)
Exemplo n.º 7
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        # Turn off hinting in WebKit (requires a patched build of QtWebKit)
        os.environ['CALIBRE_WEBKIT_NO_HINTING'] = '1'
        try:
            must_use_qt()
            load_builtin_fonts()

            self.oeb = oeb_book
            self.input_plugin, self.opts, self.log = input_plugin, opts, log
            self.output_path = output_path
            from calibre.ebooks.oeb.base import OPF, OPF2_NS
            from lxml import etree
            from io import BytesIO
            package = etree.Element(OPF('package'),
                                    attrib={
                                        'version': '2.0',
                                        'unique-identifier': 'dummy'
                                    },
                                    nsmap={None: OPF2_NS})
            from calibre.ebooks.metadata.opf2 import OPF
            self.oeb.metadata.to_opf2(package)
            self.metadata = OPF(BytesIO(
                etree.tostring(package))).to_book_metadata()
            self.cover_data = None

            if input_plugin.is_image_collection:
                log.debug('Converting input as an image collection...')
                self.convert_images(input_plugin.get_images())
            else:
                log.debug('Converting input as a text based book...')
                self.convert_text(oeb_book)
        finally:
            os.environ.pop('CALIBRE_WEBKIT_NO_HINTING', None)
Exemplo n.º 8
0
    def __init__(self, container, do_embed=False):
        self.container = container
        self.log = self.logger = container.log
        self.do_embed = do_embed
        must_use_qt()
        self.parser = CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css'))
        self.first_letter_pat = regex.compile(r'^[\p{Ps}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]+', regex.VERSION1 | regex.UNICODE)
        self.capitalize_pat = regex.compile(r'[\p{L}\p{N}]', regex.VERSION1 | regex.UNICODE)

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(self.log)
        self.view.setPage(self.page)
        self.page.setViewportSize(QSize(1200, 1600))

        self.view.loadFinished.connect(self.collect,
                type=Qt.QueuedConnection)

        self.render_queue = list(container.spine_items)
        self.font_stats = {}
        self.font_usage_map = {}
        self.font_spec_map = {}
        self.font_rule_map = {}
        self.all_font_rules = {}

        QTimer.singleShot(0, self.render_book)

        if self.loop.exec_() == 1:
            raise Exception('Failed to gather statistics from book, see log for details')
Exemplo n.º 9
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        must_use_qt()
        load_builtin_fonts()

        self.oeb = oeb_book
        self.input_plugin, self.opts, self.log = input_plugin, opts, log
        self.output_path = output_path
        from calibre.ebooks.oeb.base import OPF, OPF2_NS
        from lxml import etree
        from io import BytesIO
        package = etree.Element(OPF('package'),
                                attrib={
                                    'version': '2.0',
                                    'unique-identifier': 'dummy'
                                },
                                nsmap={None: OPF2_NS})
        from calibre.ebooks.metadata.opf2 import OPF
        self.oeb.metadata.to_opf2(package)
        self.metadata = OPF(BytesIO(
            etree.tostring(package))).to_book_metadata()
        self.cover_data = None

        if input_plugin.is_image_collection:
            log.debug('Converting input as an image collection...')
            self.convert_images(input_plugin.get_images())
        else:
            log.debug('Converting input as a text based book...')
            self.convert_text(oeb_book)
Exemplo n.º 10
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts

        must_use_qt()
        load_builtin_fonts()

        self.oeb = oeb_book
        self.input_plugin, self.opts, self.log = input_plugin, opts, log
        self.output_path = output_path
        from calibre.ebooks.oeb.base import OPF, OPF2_NS
        from lxml import etree
        from io import BytesIO

        package = etree.Element(
            OPF("package"), attrib={"version": "2.0", "unique-identifier": "dummy"}, nsmap={None: OPF2_NS}
        )
        from calibre.ebooks.metadata.opf2 import OPF

        self.oeb.metadata.to_opf2(package)
        self.metadata = OPF(BytesIO(etree.tostring(package))).to_book_metadata()
        self.cover_data = None

        if input_plugin.is_image_collection:
            log.debug("Converting input as an image collection...")
            self.convert_images(input_plugin.get_images())
        else:
            log.debug("Converting input as a text based book...")
            self.convert_text(oeb_book)
Exemplo n.º 11
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        self.mathjax_dir = P('mathjax', allow_user_override=False)
        current_log(log)
        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self.render_html,
                type=Qt.QueuedConnection)
        self.view.loadProgress.connect(self.load_progress)
        self.ignore_failure = None
        self.hang_check_timer = t = QTimer(self)
        t.timeout.connect(self.hang_check)
        t.setInterval(1000)

        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(x,
                    Qt.ScrollBarAlwaysOff)
        self.report_progress = lambda x, y: x
        self.current_section = ''
        self.current_tl_section = ''
Exemplo n.º 12
0
    def __init__(self, container, do_embed=False):
        self.container = container
        self.log = self.logger = container.log
        self.do_embed = do_embed
        must_use_qt()
        self.parser = CSSParser(loglevel=logging.CRITICAL, log=logging.getLogger('calibre.css'))
        self.first_letter_pat = regex.compile(r'^[\p{Ps}\p{Ps}\p{Pe}\p{Pi}\p{Pf}\p{Po}]+', regex.VERSION1 | regex.UNICODE)

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(self.log)
        self.view.setPage(self.page)
        self.page.setViewportSize(QSize(1200, 1600))

        self.view.loadFinished.connect(self.collect,
                type=Qt.QueuedConnection)

        self.render_queue = list(container.spine_items)
        self.font_stats = {}
        self.font_usage_map = {}
        self.font_spec_map = {}
        self.font_rule_map = {}
        self.all_font_rules = {}

        QTimer.singleShot(0, self.render_book)

        if self.loop.exec_() == 1:
            raise Exception('Failed to gather statistics from book, see log for details')
Exemplo n.º 13
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()
        QObject.__init__(self)

        self.logger = self.log = log
        current_log(log)
        self.opts = opts
        self.cover_data = cover_data
        self.paged_js = None
        self.toc = toc

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(opts, self.log)
        self.view.setPage(self.page)
        self.view.setRenderHints(QPainter.Antialiasing
                                 | QPainter.TextAntialiasing
                                 | QPainter.SmoothPixmapTransform)
        self.view.loadFinished.connect(self.render_html,
                                       type=Qt.QueuedConnection)
        for x in (Qt.Horizontal, Qt.Vertical):
            self.view.page().mainFrame().setScrollBarPolicy(
                x, Qt.ScrollBarAlwaysOff)
        self.report_progress = lambda x, y: x
        self.current_section = ''
        self.current_tl_section = ''
Exemplo n.º 14
0
def main():
    must_use_qt()
    load_builtin_fonts()
    renderer = Renderer()
    renderer.setUrl(QUrl.fromLocalFile(sys.argv[-1]))
    renderer.loadFinished.connect(renderer.do_print)
    QApplication.instance().exec_()
    print('Output written to:', OUTPUT)
Exemplo n.º 15
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()

        self.logger = self.log = log
        self.opts = opts
        self.cover_data = cover_data
        self.toc = toc
Exemplo n.º 16
0
    def __init__(self, opts, log, cover_data=None, toc=None):
        from calibre.gui2 import must_use_qt
        must_use_qt()

        self.logger = self.log = log
        self.opts = opts
        self.cover_data = cover_data
        self.toc = toc
Exemplo n.º 17
0
def verify_theme(report):
    must_use_qt()
    report.bad = bad = {}
    for name, path in iteritems(report.name_map):
        reader = QImageReader(os.path.join(report.path, path))
        img = reader.read()
        if img.isNull():
            bad[name] = reader.errorString()
    return bool(bad)
Exemplo n.º 18
0
def verify_theme(report):
    must_use_qt()
    report.bad = bad = {}
    for name, path in report.name_map.iteritems():
        reader = QImageReader(os.path.join(report.path, path))
        img = reader.read()
        if img.isNull():
            bad[name] = reader.errorString()
    return bool(bad)
Exemplo n.º 19
0
 def __init__(self):
     must_use_qt()
     QWebEnginePage.__init__(self, create_profile(), QApplication.instance())
     self.titleChanged.connect(self.title_changed)
     secure_webengine(self.settings())
     self.console_messages = []
     self.ready = False
     self.working = False
     self.pending = None
     self.setHtml('')
Exemplo n.º 20
0
    def __init__(
            self,
            # Logging. If None, uses a default log, which does not output
            # debugging info
            log=None,
            # Receives a string and returns True/False. By default, returns
            # True for all strings
            confirm_callback=None,

            # Prompt callback. Receives a msg string and a default value
            # string. Should return the user input value or None if the user
            # canceled the prompt. By default returns None.
            prompt_callback=None,

            # User agent to be used
            user_agent=USER_AGENT,

            # The size (in MB) of the on disk cache. Note that because the disk
            # cache cannot be shared between different instances, we currently
            # use a temporary dir for the cache, which is deleted on
            # program exit. Set to zero to disable cache.
            disk_cache_size=50,

            # Enable Inspect element functionality
            enable_developer_tools=False,

            # Verbosity
            verbosity=0,

            # The default timeout (in seconds)
            default_timeout=30,

            # If True, do not connect to the X server on linux
            headless=True):
        must_use_qt(headless=headless)
        QObject.__init__(self)
        FormsMixin.__init__(self)

        if log is None:
            log = ThreadSafeLog()
        if verbosity:
            log.filter_level = log.DEBUG
        self.log = log
        self.default_timeout = default_timeout

        self.page = WebPage(log,
                            confirm_callback=confirm_callback,
                            prompt_callback=prompt_callback,
                            user_agent=user_agent,
                            enable_developer_tools=enable_developer_tools,
                            parent=self)
        self.nam = NetworkAccessManager(log,
                                        disk_cache_size=disk_cache_size,
                                        parent=self)
        self.page.setNetworkAccessManager(self.nam)
Exemplo n.º 21
0
def run_rapydscript_tests():
    from PyQt5.Qt import QApplication, QEventLoop
    from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript

    from calibre.gui2 import must_use_qt
    from calibre.gui2.webengine import secure_webengine
    must_use_qt()
    base = base_dir()
    rapydscript_dir = os.path.join(base, 'src', 'pyj')
    fname = os.path.join(rapydscript_dir, 'test.pyj')
    with lopen(fname, 'rb') as f:
        js = compile_fast(f.read(), fname)

    def create_script(src, name):
        s = QWebEngineScript()
        s.setName(name)
        s.setInjectionPoint(QWebEngineScript.DocumentReady)
        s.setWorldId(QWebEngineScript.ApplicationWorld)
        s.setRunsOnSubFrames(False)
        s.setSourceCode(src)
        return s

    class Tester(QWebEnginePage):
        def __init__(self):
            QWebEnginePage.__init__(self)
            self.titleChanged.connect(self.title_changed)
            secure_webengine(self)
            self.scripts().insert(create_script(js, 'test-rapydscript.js'))
            self.setHtml('<p>initialize')
            self.working = True

        def title_changed(self, title):
            if title == 'initialized':
                self.titleChanged.disconnect()
                self.runJavaScript('window.main()',
                                   QWebEngineScript.ApplicationWorld,
                                   self.callback)

        def spin_loop(self):
            while self.working:
                QApplication.instance().processEvents(
                    QEventLoop.ExcludeUserInputEvents)
            return self.result

        def callback(self, result):
            self.result = result
            self.working = False

        def javaScriptConsoleMessage(self, level, msg, line_num, source_id):
            print(msg, file=sys.stderr if level > 0 else sys.stdout)

    tester = Tester()
    result = tester.spin_loop()
    raise SystemExit(int(result))
Exemplo n.º 22
0
def load_icon_for_file(path: str, as_data=False, size=ICON_SIZE):
    try:
        hicon = winutil.get_icon_for_file(path)
    except Exception:
        return
    must_use_qt()
    pmap = hicon_to_pixmap(hicon)
    if not pmap.isNull():
        if pmap.width() != size:
            pmap = pmap.scaled(size, size, aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)
        return pixmap_to_data(pmap) if as_data else pmap
Exemplo n.º 23
0
    def __init__(self,
            # Logging. If None, uses a default log, which does not output
            # debugging info
            log=None,
            # Receives a string and returns True/False. By default, returns
            # True for all strings
            confirm_callback=None,

            # Prompt callback. Receives a msg string and a default value
            # string. Should return the user input value or None if the user
            # canceled the prompt. By default returns None.
            prompt_callback=None,

            # User agent to be used
            user_agent=USER_AGENT,

            # The size (in MB) of the on disk cache. Note that because the disk
            # cache cannot be shared between different instances, we currently
            # use a temporary dir for the cache, which is deleted on
            # program exit. Set to zero to disable cache.
            disk_cache_size=50,

            # Enable Inspect element functionality
            enable_developer_tools=False,

            # Verbosity
            verbosity=0,

            # The default timeout (in seconds)
            default_timeout=30,

            # If True, do not connect to the X server on linux
            headless=True
        ):
        must_use_qt(headless=headless)
        QObject.__init__(self)
        FormsMixin.__init__(self)

        if log is None:
            log = ThreadSafeLog()
        if verbosity:
            log.filter_level = log.DEBUG
        self.log = log
        self.default_timeout = default_timeout

        self.page = WebPage(log, confirm_callback=confirm_callback,
                prompt_callback=prompt_callback, user_agent=user_agent,
                enable_developer_tools=enable_developer_tools,
                parent=self)
        self.nam = NetworkAccessManager(log, disk_cache_size=disk_cache_size, parent=self)
        self.page.setNetworkAccessManager(self.nam)
Exemplo n.º 24
0
def main(path_to_html, tdir, image_format='jpeg'):
    if image_format not in ('jpeg', 'png'):
        raise ValueError('Image format must be either jpeg or png')
    must_use_qt()
    path_to_html = os.path.abspath(path_to_html)
    os.chdir(tdir)
    renderer = Render()
    renderer.start_load(path_to_html)
    ret = QApplication.instance().exec_()
    if ret == 0:
        page_images('rendered.pdf', image_format=image_format)
        ext = {'jpeg': 'jpg'}.get(image_format, image_format)
        atomic_rename('page-images-1.' + ext, 'rendered.' + image_format)
    return ret == 0
Exemplo n.º 25
0
    def __init__(
            self,
            # Logging. If None, uses a default log, which does not output
            # debugging info
            log=None,
            # Receives a string and returns True/False. By default, returns
            # True for all strings
            confirm_callback=None,

            # Prompt callback. Receives a msg string and a default value
            # string. Should return the user input value or None if the user
            # canceled the prompt. By default returns None.
            prompt_callback=None,

            # User agent to be used
            user_agent=USER_AGENT,

            # If True a disk cache is used
            use_disk_cache=True,

            # Enable Inspect element functionality
            enable_developer_tools=False,

            # Verbosity
            verbosity=0,

            # The default timeout (in seconds)
            default_timeout=30):
        must_use_qt()
        QObject.__init__(self)
        FormsMixin.__init__(self)

        if log is None:
            log = ThreadSafeLog()
        if verbosity:
            log.filter_level = log.DEBUG
        self.log = log
        self.default_timeout = default_timeout

        self.page = WebPage(log,
                            confirm_callback=confirm_callback,
                            prompt_callback=prompt_callback,
                            user_agent=user_agent,
                            enable_developer_tools=enable_developer_tools,
                            parent=self)
        self.nam = NetworkAccessManager(log,
                                        use_disk_cache=use_disk_cache,
                                        parent=self)
        self.page.setNetworkAccessManager(self.nam)
Exemplo n.º 26
0
def read_icon(handle, icon):
    must_use_qt()
    resource = win32api.LoadResource(handle, win32con.RT_ICON, icon.id)
    pixmap = QPixmap()
    pixmap.loadFromData(resource)
    hicon = None
    if pixmap.isNull():
        if icon.width > 0 and icon.height > 0:
            hicon = ctypes.windll.user32.CreateIconFromResourceEx(
                resource, len(resource), True, 0x00030000, icon.width, icon.height, win32con.LR_DEFAULTCOLOR)
        else:
            hicon = win32gui.CreateIconFromResource(resource, True)
        pixmap = hicon_to_pixmap(hicon).copy()
        win32gui.DestroyIcon(hicon)
    return pixmap
Exemplo n.º 27
0
def read_icon(handle, icon):
    must_use_qt()
    resource = win32api.LoadResource(handle, win32con.RT_ICON, icon.id)
    pixmap = QPixmap()
    pixmap.loadFromData(resource)
    hicon = None
    if pixmap.isNull():
        if icon.width > 0 and icon.height > 0:
            hicon = ctypes.windll.user32.CreateIconFromResourceEx(
                resource, len(resource), True, 0x00030000, icon.width, icon.height, win32con.LR_DEFAULTCOLOR)
        else:
            hicon = win32gui.CreateIconFromResource(resource, True)
        pixmap = hicon_to_pixmap(hicon).copy()
        win32gui.DestroyIcon(hicon)
    return pixmap
Exemplo n.º 28
0
def get_pdf_printer(opts, for_comic=False, output_file_name=None):  # {{{
    from calibre.gui2 import must_use_qt
    must_use_qt()

    printer = QPrinter(QPrinter.HighResolution)
    custom_size = get_custom_size(opts)
    if isosx and not for_comic:
        # On OSX, the native engine can only produce a single page size
        # (usually A4). The Qt engine on the other hand produces image based
        # PDFs. If we set a custom page size using QSizeF the native engine
        # produces unreadable output, so we just ignore the custom size
        # settings.
        printer.setPaperSize(paper_size(opts.paper_size))
    else:
        if opts.output_profile.short_name == 'default' or \
                opts.output_profile.width > 9999 or opts.override_profile_size:
            if custom_size is None:
                printer.setPaperSize(paper_size(opts.paper_size))
            else:
                printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]),
                                     unit(opts.unit))
        else:
            w = opts.output_profile.comic_screen_size[0] if for_comic else \
                    opts.output_profile.width
            h = opts.output_profile.comic_screen_size[1] if for_comic else \
                    opts.output_profile.height
            dpi = opts.output_profile.dpi
            printer.setPaperSize(QSizeF(float(w) / dpi,
                                        float(h) / dpi), QPrinter.Inch)

    if for_comic:
        # Comic pages typically have their own margins, or their background
        # color is not white, in which case the margin looks bad
        printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
    else:
        printer.setPageMargins(opts.margin_left, opts.margin_top,
                               opts.margin_right, opts.margin_bottom,
                               QPrinter.Point)
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setFullPage(for_comic)
    if output_file_name:
        printer.setOutputFileName(output_file_name)
    if isosx and not for_comic:
        # Ensure we are not generating enormous image based PDFs
        printer.setOutputFormat(QPrinter.NativeFormat)

    return printer
Exemplo n.º 29
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        must_use_qt()
        load_builtin_fonts()

        self.oeb = oeb_book
        self.input_plugin, self.opts, self.log = input_plugin, opts, log
        self.output_path = output_path
        self.metadata = oeb_book.metadata
        self.cover_data = None

        if input_plugin.is_image_collection:
            log.debug('Converting input as an image collection...')
            self.convert_images(input_plugin.get_images())
        else:
            log.debug('Converting input as a text based book...')
            self.convert_text(oeb_book)
Exemplo n.º 30
0
def load_icon_resource_as_pixmap(icon_resource, size=ICON_SIZE):
    if not icon_resource:
        return
    parts = tuple(filter(None, re.split(r',([-0-9]+$)', icon_resource)))
    if len(parts) != 2:
        return
    module, index = parts
    index = int(index)
    if module.startswith('"') and module.endswith('"'):
        module = split_commandline(module)[0]
    hmodule = winutil.load_library(
        module, winutil.LOAD_LIBRARY_AS_DATAFILE
        | winutil.LOAD_LIBRARY_AS_IMAGE_RESOURCE)
    icons = winutil.load_icons(hmodule, index)
    pixmaps = []
    must_use_qt()
    for icon_data, icon_handle in icons:
        pixmap = QPixmap()
        pixmap.loadFromData(icon_data)
        if pixmap.isNull() and bool(icon_handle):
            pixmap = hicon_to_pixmap(icon_handle)
        if pixmap.isNull():
            continue
        pixmaps.append(pixmap)
    if not pixmaps:
        return

    def area(p):
        return p.width() * p.height()

    pixmaps.sort(key=area)
    q = size * size
    for pmap in pixmaps:
        if area(pmap) >= q:
            if area(pmap) == q:
                return pmap
            return pmap.scaled(
                size,
                size,
                aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio,
                transformMode=Qt.TransformationMode.SmoothTransformation)
    return pixmaps[-1].scaled(
        size,
        size,
        aspectRatioMode=Qt.AspectRatioMode.KeepAspectRatio,
        transformMode=Qt.TransformationMode.SmoothTransformation)
Exemplo n.º 31
0
    def convert(self, oeb_book, output_path, input_plugin, opts, log):
        from calibre.gui2 import must_use_qt, load_builtin_fonts
        must_use_qt()
        load_builtin_fonts()

        self.oeb = oeb_book
        self.input_plugin, self.opts, self.log = input_plugin, opts, log
        self.output_path = output_path
        self.metadata = oeb_book.metadata
        self.cover_data = None

        if input_plugin.is_image_collection:
            log.debug('Converting input as an image collection...')
            self.convert_images(input_plugin.get_images())
        else:
            log.debug('Converting input as a text based book...')
            self.convert_text(oeb_book)
Exemplo n.º 32
0
    def __init__(self,
            # Logging. If None, uses a default log, which does not output
            # debugging info
            log=None,
            # Receives a string and returns True/False. By default, returns
            # True for all strings
            confirm_callback=None,

            # Prompt callback. Receives a msg string and a default value
            # string. Should return the user input value or None if the user
            # canceled the prompt. By default returns None.
            prompt_callback=None,

            # User agent to be used
            user_agent=USER_AGENT,

            # If True a disk cache is used
            use_disk_cache=True,

            # Enable Inspect element functionality
            enable_developer_tools=False,

            # Verbosity
            verbosity=0,

            # The default timeout (in seconds)
            default_timeout=30
        ):
        must_use_qt()
        QObject.__init__(self)
        FormsMixin.__init__(self)

        if log is None:
            log = ThreadSafeLog()
        if verbosity:
            log.filter_level = log.DEBUG
        self.log = log
        self.default_timeout = default_timeout

        self.page = WebPage(log, confirm_callback=confirm_callback,
                prompt_callback=prompt_callback, user_agent=user_agent,
                enable_developer_tools=enable_developer_tools,
                parent=self)
        self.nam = NetworkAccessManager(log, use_disk_cache=use_disk_cache, parent=self)
        self.page.setNetworkAccessManager(self.nam)
Exemplo n.º 33
0
    def specialize_options(self, log, opts, input_fmt):
        # Ensure Qt is setup to be used with WebEngine
        # specialize_options is called early enough in the pipeline
        # that hopefully no Qt application has been constructed as yet
        from qt.webengine import QWebEngineUrlScheme
        from qt.webengine import QWebEnginePage  # noqa
        from calibre.gui2 import must_use_qt
        from calibre.constants import FAKE_PROTOCOL
        scheme = QWebEngineUrlScheme(FAKE_PROTOCOL.encode('ascii'))
        scheme.setSyntax(QWebEngineUrlScheme.Syntax.Host)
        scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme)
        QWebEngineUrlScheme.registerScheme(scheme)
        must_use_qt()
        self.input_fmt = input_fmt

        if opts.pdf_use_document_margins:
            # Prevent the conversion pipeline from overwriting document margins
            opts.margin_left = opts.margin_right = opts.margin_top = opts.margin_bottom = -1
Exemplo n.º 34
0
def get_pdf_printer(opts, for_comic=False, output_file_name=None):  # {{{
    from calibre.gui2 import must_use_qt
    must_use_qt()

    printer = QPrinter(QPrinter.HighResolution)
    custom_size = get_custom_size(opts)
    if isosx and not for_comic:
        # On OSX, the native engine can only produce a single page size
        # (usually A4). The Qt engine on the other hand produces image based
        # PDFs. If we set a custom page size using QSizeF the native engine
        # produces unreadable output, so we just ignore the custom size
        # settings.
        printer.setPaperSize(paper_size(opts.paper_size))
    else:
        if opts.output_profile.short_name == 'default' or \
                opts.output_profile.width > 9999 or opts.override_profile_size:
            if custom_size is None:
                printer.setPaperSize(paper_size(opts.paper_size))
            else:
                printer.setPaperSize(QSizeF(custom_size[0], custom_size[1]), unit(opts.unit))
        else:
            w = opts.output_profile.comic_screen_size[0] if for_comic else \
                    opts.output_profile.width
            h = opts.output_profile.comic_screen_size[1] if for_comic else \
                    opts.output_profile.height
            dpi = opts.output_profile.dpi
            printer.setPaperSize(QSizeF(float(w) / dpi, float(h) / dpi), QPrinter.Inch)

    if for_comic:
        # Comic pages typically have their own margins, or their background
        # color is not white, in which case the margin looks bad
        printer.setPageMargins(0, 0, 0, 0, QPrinter.Point)
    else:
        printer.setPageMargins(opts.margin_left, opts.margin_top,
                opts.margin_right, opts.margin_bottom, QPrinter.Point)
    printer.setOutputFormat(QPrinter.PdfFormat)
    printer.setFullPage(for_comic)
    if output_file_name:
        printer.setOutputFileName(output_file_name)
    if isosx and not for_comic:
        # Ensure we are not generating enormous image based PDFs
        printer.setOutputFormat(QPrinter.NativeFormat)

    return printer
Exemplo n.º 35
0
def simple_load_icon(module, index, as_data=False, size=ICON_SIZE):
    ' Use the win32 API ExtractIcon to load the icon. This restricts icon size to 32x32, but has less chance of failing '
    try:
        large_icons, small_icons = win32gui.ExtractIconEx(module, index, 10)
    except pywintypes.error as err:
        if err.winerror != winerror.ERROR_FILE_NOT_FOUND:
            raise
        prints('File %r does not exist, cannot load icon' % module)
        return
    icons = large_icons + small_icons
    try:
        if icons:
            must_use_qt()
            pixmap = copy_to_size(QtWin.fromHICON(icons[0]), size=size)
            if as_data:
                return pixmap_to_data(pixmap)
            return QIcon(pixmap)
    finally:
        tuple(map(win32gui.DestroyIcon, icons))
Exemplo n.º 36
0
def simple_load_icon(module, index, as_data=False, size=ICON_SIZE):
    ' Use the win32 API ExtractIcon to load the icon. This restricts icon size to 32x32, but has less chance of failing '
    try:
        large_icons, small_icons = win32gui.ExtractIconEx(module, index, 10)
    except pywintypes.error as err:
        if err.winerror != winerror.ERROR_FILE_NOT_FOUND:
            raise
        prints('File %r does not exist, cannot load icon' % module)
        return
    icons = large_icons + small_icons
    try:
        if icons:
            must_use_qt()
            pixmap = copy_to_size(QtWin.fromHICON(icons[0]), size=size)
            if as_data:
                return pixmap_to_data(pixmap)
            return QIcon(pixmap)
    finally:
        tuple(map(win32gui.DestroyIcon, icons))
Exemplo n.º 37
0
    def __init__(self, container):
        self.container = container
        self.log = self.logger = container.log
        must_use_qt()

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(self.log)
        self.view.setPage(self.page)
        self.page.setViewportSize(QSize(1200, 1600))

        self.view.loadFinished.connect(self.collect, type=Qt.QueuedConnection)

        self.render_queue = list(container.spine_items)
        self.font_stats = {}

        QTimer.singleShot(0, self.render_book)

        if self.loop.exec_() == 1:
            raise Exception(
                'Failed to gather statistics from book, see log for details')
Exemplo n.º 38
0
    def __init__(self, container):
        self.container = container
        self.log = self.logger = container.log
        must_use_qt()

        self.loop = QEventLoop()
        self.view = QWebView()
        self.page = Page(self.log)
        self.view.setPage(self.page)
        self.page.setViewportSize(QSize(1200, 1600))

        self.view.loadFinished.connect(self.collect,
                type=Qt.QueuedConnection)

        self.render_queue = list(container.spine_items)
        self.font_stats = {}

        QTimer.singleShot(0, self.render_book)

        if self.loop.exec_() == 1:
            raise Exception('Failed to gather statistics from book, see log for details')
Exemplo n.º 39
0
def worker_main(source):
    QLoggingCategory.setFilterRules('''\
qt.webenginecontext.info=false
''')
    from calibre.gui2 import must_use_qt

    from .simple_backend import SimpleScraper
    must_use_qt()
    s = SimpleScraper(source)
    for line in sys.stdin.buffer:
        line = line.strip()
        if source == 'test':
            print(line.decode('utf-8'), file=sys.stderr)
        try:
            cmd, rest = line.split(b':', 1)
        except Exception:
            continue
        if cmd == b'EXIT':
            raise SystemExit(int(rest))
        if cmd == b'FETCH':
            try:
                d = json.loads(rest)
                html = s.fetch(QUrl.fromEncoded(d['url'].encode('utf-8')),
                               timeout=float(d['timeout']))
            except Exception as e:
                import traceback
                result = {
                    'ok': False,
                    'tb': traceback.format_exc(),
                    'err': str(e)
                }
            else:
                with PersistentTemporaryFile(
                        suffix='-scraper-result.html') as t:
                    t.write(html.encode('utf-8'))
                result = {'ok': True, 'html_file': t.name}
            print(json.dumps(result), flush=True)
Exemplo n.º 40
0
def run_rapydscript_tests():
    from urllib.parse import parse_qs
    from qt.core import QApplication, QByteArray, QEventLoop, QUrl
    from qt.webengine import (
        QWebEnginePage, QWebEngineProfile, QWebEngineScript, QWebEngineUrlRequestJob,
        QWebEngineUrlScheme, QWebEngineUrlSchemeHandler
    )

    from calibre.constants import FAKE_HOST, FAKE_PROTOCOL
    from calibre.gui2 import must_use_qt
    from calibre.gui2.viewer.web_view import send_reply
    from calibre.gui2.webengine import secure_webengine, insert_scripts, create_script
    must_use_qt()
    scheme = QWebEngineUrlScheme(FAKE_PROTOCOL.encode('ascii'))
    scheme.setSyntax(QWebEngineUrlScheme.Syntax.Host)
    scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme)
    QWebEngineUrlScheme.registerScheme(scheme)

    base = base_dir()
    rapydscript_dir = os.path.join(base, 'src', 'pyj')
    fname = os.path.join(rapydscript_dir, 'test.pyj')
    with lopen(fname, 'rb') as f:
        js = compile_fast(f.read(), fname)

    class UrlSchemeHandler(QWebEngineUrlSchemeHandler):

        def __init__(self, parent=None):
            QWebEngineUrlSchemeHandler.__init__(self, parent)
            self.allowed_hosts = (FAKE_HOST,)
            self.registered_data = {}

        def requestStarted(self, rq):
            if bytes(rq.requestMethod()) != b'GET':
                return self.fail_request(rq, QWebEngineUrlRequestJob.Error.RequestDenied)
            url = rq.requestUrl()
            host = url.host()
            if host not in self.allowed_hosts:
                return self.fail_request(rq)
            q = parse_qs(url.query())
            if not q:
                return self.fail_request(rq)
            mt = q.get('mime-type', ('text/plain',))[0]
            data = q.get('data', ('',))[0].encode('utf-8')
            send_reply(rq, mt, data)

        def fail_request(self, rq, fail_code=None):
            if fail_code is None:
                fail_code = QWebEngineUrlRequestJob.Error.UrlNotFound
            rq.fail(fail_code)
            print(f"Blocking FAKE_PROTOCOL request: {rq.requestUrl().toString()}", file=sys.stderr)

    class Tester(QWebEnginePage):

        def __init__(self):
            profile = QWebEngineProfile(QApplication.instance())
            profile.setHttpUserAgent('calibre-tester')
            insert_scripts(profile, create_script('test-rapydscript.js', js, on_subframes=False))
            url_handler = UrlSchemeHandler(profile)
            profile.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
            QWebEnginePage.__init__(self, profile, None)
            self.titleChanged.connect(self.title_changed)
            secure_webengine(self)
            self.setHtml('<p>initialize', QUrl(f'{FAKE_PROTOCOL}://{FAKE_HOST}/index.html'))
            self.working = True

        def title_changed(self, title):
            if title == 'initialized':
                self.titleChanged.disconnect()
                self.runJavaScript('window.main()', QWebEngineScript.ScriptWorldId.ApplicationWorld, self.callback)

        def spin_loop(self):
            while self.working:
                QApplication.instance().processEvents(QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
            return self.result

        def callback(self, result):
            self.result = result
            self.working = False

        def javaScriptConsoleMessage(self, level, msg, line_num, source_id):
            print(msg, file=sys.stderr if level > 0 else sys.stdout)

    tester = Tester()
    result = tester.spin_loop()
    raise SystemExit(int(result))
Exemplo n.º 41
0
 def __init__(self):
     from calibre.gui2 import must_use_qt
     must_use_qt()
Exemplo n.º 42
0
    def handle_embedded_fonts(self):
        '''
        Because of QtWebKit's inability to handle embedded fonts correctly, we
        remove the embedded fonts and make them available system wide instead.
        If you ever move to Qt WebKit 2.3+ then this will be unnecessary.
        '''
        from calibre.ebooks.oeb.base import urlnormalize
        from calibre.gui2 import must_use_qt
        from calibre.utils.fonts.utils import get_font_names, remove_embed_restriction
        from PyQt4.Qt import QFontDatabase, QByteArray

        # First find all @font-face rules and remove them, adding the embedded
        # fonts to Qt
        family_map = {}
        for item in list(self.oeb.manifest):
            if not hasattr(item.data, 'cssRules'): continue
            remove = set()
            for i, rule in enumerate(item.data.cssRules):
                if rule.type == rule.FONT_FACE_RULE:
                    remove.add(i)
                    try:
                        s = rule.style
                        src = s.getProperty('src').propertyValue[0].uri
                        font_family = s.getProperty('font-family').propertyValue[0].value
                    except:
                        continue
                    path = item.abshref(src)
                    ff = self.oeb.manifest.hrefs.get(urlnormalize(path), None)
                    if ff is None:
                        continue

                    raw = ff.data
                    self.oeb.manifest.remove(ff)
                    try:
                        raw = remove_embed_restriction(raw)
                    except:
                        continue
                    must_use_qt()
                    QFontDatabase.addApplicationFontFromData(QByteArray(raw))
                    try:
                        family_name = get_font_names(raw)[0]
                    except:
                        family_name = None
                    if family_name:
                        family_map[icu_lower(font_family)] = family_name

            for i in sorted(remove, reverse=True):
                item.data.cssRules.pop(i)

        # Now map the font family name specified in the css to the actual
        # family name of the embedded font (they may be different in general).
        for item in self.oeb.manifest:
            if not hasattr(item.data, 'cssRules'): continue
            for i, rule in enumerate(item.data.cssRules):
                if rule.type != rule.STYLE_RULE: continue
                ff = rule.style.getProperty('font-family')
                if ff is None: continue
                val = ff.propertyValue
                for i in xrange(val.length):
                    k = icu_lower(val[i].value)
                    if k in family_map:
                        val[i].value = family_map[k]
Exemplo n.º 43
0
def render_svg(filepath):
    must_use_qt(headless=False)
    pngpath = filepath[:-4] + '.png'
    i = QImage(filepath)
    i.save(pngpath)
Exemplo n.º 44
0
 def __init__(self, base_css=''):
     self.base_css = base_css
     from calibre.gui2 import must_use_qt
     must_use_qt()
Exemplo n.º 45
0
 def __init__(self, base_css=''):
     self.base_css = base_css
     from calibre.gui2 import must_use_qt
     must_use_qt()
Exemplo n.º 46
0
 def __init__(self):
     from calibre.gui2 import must_use_qt
     must_use_qt()
Exemplo n.º 47
0
def render_svg(filepath):
    must_use_qt(headless=False)
    pngpath = filepath[:-4] + '.png'
    i = QImage(filepath)
    i.save(pngpath)
Exemplo n.º 48
0
def compiler():
    from calibre_lzma.xz import decompress
    ans = getattr(compiler, 'ans', None)
    if ans is not None:
        return ans
    from calibre import walk
    from calibre.gui2 import must_use_qt
    from calibre.gui2.webengine import secure_webengine
    from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript
    from PyQt5.Qt import QApplication, QEventLoop
    must_use_qt()

    buf = BytesIO()
    decompress(P(COMPILER_PATH, data=True, allow_user_override=False), buf)

    base = base_dir()
    rapydscript_dir = os.path.join(base, 'src', 'pyj')
    cache_path = os.path.join(module_cache_dir(),
                              'embedded-compiler-write-cache.json')

    def create_vfs():
        ans = {}
        for x in walk(rapydscript_dir):
            if x.endswith('.pyj'):
                r = os.path.relpath(x, rapydscript_dir).replace('\\', '/')
                with open(x, 'rb') as f:
                    ans['__stdlib__/' + r] = f.read().decode('utf-8')
        return ans

    def vfs_script():
        try:
            with open(cache_path, 'rb') as f:
                write_cache = f.read().decode('utf-8')
        except Exception:
            write_cache = '{}'

        return '''
(function() {
"use strict";
var vfs = VFS;

function read_file_sync(name) {
    var ans = vfs[name];
    if (typeof ans === "string") return ans;
    ans = write_cache[name];
    if (typeof ans === "string") return ans;
    return null;
}

function write_file_sync(name, data) {
    write_cache[name] = data;
}

RapydScript.virtual_file_system = {
    'read_file_sync': read_file_sync,
    'write_file_sync': write_file_sync
};

window.compiler = RapydScript.create_embedded_compiler();
document.title = 'compiler initialized';
})();
'''.replace(
            'VFS',
            json.dumps(create_vfs()) + ';\n' + 'window.write_cache = ' +
            write_cache, 1)

    def create_script(src, name):
        s = QWebEngineScript()
        s.setName(name)
        s.setInjectionPoint(QWebEngineScript.DocumentReady)
        s.setWorldId(QWebEngineScript.ApplicationWorld)
        s.setRunsOnSubFrames(True)
        s.setSourceCode(src)
        return s

    class Compiler(QWebEnginePage):
        def __init__(self):
            QWebEnginePage.__init__(self)
            self.errors = []
            secure_webengine(self)
            script = buf.getvalue().decode('utf-8')
            script += '\n\n;;\n\n' + vfs_script()
            self.scripts().insert(create_script(script, 'rapydscript.js'))
            self.setHtml('<p>initialize')
            while self.title() != 'compiler initialized':
                self.spin_loop()

        def spin_loop(self):
            QApplication.instance().processEvents(
                QEventLoop.ExcludeUserInputEvents)

        def javaScriptConsoleMessage(self, level, msg, line_num, source_id):
            if level:
                self.errors.append(msg)
            else:
                print('{}:{}:{}'.format(source_id, line_num, msg))

        def __call__(self, src, options):
            self.compiler_result = null = object()
            self.errors = []
            self.working = True
            options['basedir'] = '__stdlib__'
            options['write_name'] = True
            options['keep_docstrings'] = False
            src = 'var js = window.compiler.compile({}, {}); [js, window.write_cache]'.format(
                *map(json.dumps, (src, options)))
            self.runJavaScript(src, QWebEngineScript.ApplicationWorld,
                               self.compilation_done)
            while self.working:
                self.spin_loop()
            if self.compiler_result is null or self.compiler_result is None:
                raise CompileFailure(
                    'Failed to compile rapydscript code with error: ' +
                    '\n'.join(self.errors))
            write_cache = self.compiler_result[1]
            with open(cache_path, 'wb') as f:
                f.write(as_bytes(json.dumps(write_cache)))
            return self.compiler_result[0]

        def eval(self, js):
            self.compiler_result = null = object()
            self.errors = []
            self.working = True
            self.runJavaScript(js, QWebEngineScript.ApplicationWorld,
                               self.compilation_done)
            while self.working:
                self.spin_loop()
            if self.compiler_result is null:
                raise CompileFailure('Failed to eval JS with error: ' +
                                     '\n'.join(self.errors))
            return self.compiler_result

        def compilation_done(self, js):
            self.working = False
            self.compiler_result = js

    compiler.ans = Compiler()
    return compiler.ans