def __call__(self, ok): from PyQt5.Qt import QImage, QPainter, QByteArray, QBuffer try: if not ok: raise RuntimeError('Rendering of HTML failed.') de = self.page.mainFrame().documentElement() pe = de.findFirst('parsererror') if not pe.isNull(): raise ParserError(pe.toPlainText()) image = QImage(self.page.viewportSize(), QImage.Format_ARGB32) image.setDotsPerMeterX(96 * (100 / 2.54)) image.setDotsPerMeterY(96 * (100 / 2.54)) painter = QPainter(image) self.page.mainFrame().render(painter) painter.end() ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) image.save(buf, 'JPEG') self.data = str(ba.data()) except Exception as e: self.exception = e self.traceback = traceback.format_exc() finally: self.loop.exit(0)
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) osname = 'windows' if iswindows else ('macos' if isosx else 'linux') # DO NOT change the user agent as it is used to workaround # Qt bugs see workaround_qt_bug() in ajax.pyj ua = 'calibre-viewer {} {}'.format(__version__, osname) ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_viewer prints('Compiling viewer code...') compile_viewer() js = P('viewer.js', data=True, allow_user_override=False) translations_json = get_translations_data() or b'null' js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1) insert_scripts(ans, create_script('viewer.js', js)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-editor-preview ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_editor compile_editor() js = P('editor.js', data=True, allow_user_override=False) cparser = P('csscolorparser.js', data=True, allow_user_override=False) dark_mode_css = P('dark_mode.css', data=True, allow_user_override=False).decode('utf-8') insert_scripts( ans, create_script('csscolorparser.js', cparser), create_script('editor.js', js), create_script('dark-mode.js', ''' (function() { var settings = JSON.parse(navigator.userAgent.split('|')[1]); var dark_css = CSS; function apply_body_colors(event) { if (document.documentElement) { if (settings.bg) document.documentElement.style.backgroundColor = settings.bg; if (settings.fg) document.documentElement.style.color = settings.fg; } if (document.body) { if (settings.bg) document.body.style.backgroundColor = settings.bg; if (settings.fg) document.body.style.color = settings.fg; } } function apply_css() { var css = ''; if (settings.link) css += 'html > body :link, html > body :link * { color: ' + settings.link + ' !important; }'; if (settings.is_dark_theme) { css += dark_css; } var style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); apply_body_colors(); } apply_body_colors(); document.addEventListener("DOMContentLoaded", apply_css); })(); '''.replace('CSS', json.dumps(dark_mode_css), 1), injection_point=QWebEngineScript.DocumentCreation)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.FullScreenSupportEnabled, False) s.setAttribute(s.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def __getstate__(self): result = dict(self.__dict__) result['url'] = result['url'].toEncoded() data = QByteArray() ds = QDataStream(data, QIODevice.WriteOnly) ds.writeQVariant(self.icon) result['icon'] = data.data() return result
def image_and_format_from_data(data): ' Create an image object from the specified data which should be a bytsestring and also return the format of the image ' ba = QByteArray(data) buf = QBuffer(ba) buf.open(QBuffer.ReadOnly) r = QImageReader(buf) fmt = bytes(r.format()).decode('utf-8') return r.read(), fmt
def icon_to_dbus_menu_icon(icon, size=32): if icon.isNull(): return None ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) icon.pixmap(32).save(buf, 'PNG') return dbus.ByteArray(ba)
def icon_to_dbus_menu_icon(icon, size=32): if icon.isNull(): return None ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) icon.pixmap(32).save(buf, 'PNG') return dbus.ByteArray(bytes((ba.data())))
def create_profile(parent=None, private=False): from .vise_scheme import UrlSchemeHandler from .url_intercept import Interceptor if parent is None: parent = QApplication.instance() if private: from .downloads import download_requested ans = QWebEngineProfile(parent) ans.downloadRequested.connect(download_requested) else: ans = QWebEngineProfile(appname, parent) ans.setCachePath(os.path.join(cache_dir, appname, 'cache')) safe_makedirs(ans.cachePath()) ans.setPersistentStoragePath( os.path.join(cache_dir, appname, 'storage')) safe_makedirs(ans.persistentStoragePath()) # TODO: Enable spellchecking when # https://bugreports.qt.io/browse/QTBUG-58512 is implemented. # See https://doc.qt.io/qt-5/qtwebengine-webenginewidgets-spellchecker-example.html for how to create the Qt .bdic files ua = ' '.join(x for x in ans.httpUserAgent().split() if 'QtWebEngine' not in x) ans.setHttpUserAgent(ua) ans.setRequestInterceptor(Interceptor(ans)) try: insert_scripts(ans, client_script()) except FileNotFoundError as err: if '-client.js' in str(err): raise SystemExit( 'You need to compile the rapydscript parts of vise before running it. Install rapydscript-ng and run the build script' ) raise ans.url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(b'vise'), ans.url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.FullScreenSupportEnabled, True) s.setAttribute(s.LinksIncludedInFocusChain, False) from .config import font_families, font_sizes for ftype, family in font_families().items(): if ftype != 'default' and family: ftype = ftype.replace('-', '').capitalize().replace( 'serif', 'Serif') + 'Font' ftype = getattr(s, ftype, None) if ftype: s.setFontFamily(ftype, family) for ftype, sz in font_sizes().items(): if sz > 0: ftype = { 'minimum': 'Minimum', 'minimum-logical': 'MinimumLogical', 'default-size': 'Default', 'default-monospace-size': 'DefaultFixed' }.get(ftype) if ftype: ftype = getattr(s, ftype + 'FontSize') s.setFontSize(ftype, sz) return ans
def loadResource(self, rtype, qurl): if qurl.isLocalFile(): path = qurl.toLocalFile() try: with lopen(path, 'rb') as f: data = f.read() except EnvironmentError: if path.rpartition('.')[-1].lower() in {'jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'}: return QByteArray(bytearray.fromhex( '89504e470d0a1a0a0000000d49484452' '000000010000000108060000001f15c4' '890000000a49444154789c6300010000' '0500010d0a2db40000000049454e44ae' '426082')) else: return QByteArray(data) else: return QTextBrowser.loadResource(self, rtype, qurl)
def __setstate__(self, state): for key, val in state.items(): if key == 'url': self.__dict__[key] = QUrl.fromEncoded(val) elif key == 'icon': ds = QDataStream(QByteArray(val)) self.__dict__[key] = ds.readQVariant() else: self.__dict__[key] = val
def get_welcome_html(): if not hasattr(get_welcome_html, 'html'): d = get_data('welcome.html').decode('utf-8') ic = get_data('images/vise.svg') d = d.replace( 'VISE_ICON', 'data:image/svg+xml;base64,' + base64.standard_b64encode(ic).decode('ascii')) get_welcome_html.html = QByteArray(d.encode('utf-8')) return get_welcome_html.html
def image_to_data(image): # {{{ ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) if not image.save(buf, CACHE_FORMAT): raise EncodeError('Failed to encode thumbnail') ret = ba.data() buf.close() return ret
def pixmap_to_data(pixmap, format='JPEG', quality=90): ''' Return the QPixmap pixmap as a string saved in the specified format. ''' ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) pixmap.save(buf, format, quality=quality) return bytes(ba.data())
def add_image(self, img, cache_key): ref = self.get_image(cache_key) if ref is not None: return ref fmt = img.format() image = QImage(img) if (image.depth() == 1 and img.colorTable().size() == 2 and img.colorTable().at(0) == QColor(Qt.black).rgba() and img.colorTable().at(1) == QColor(Qt.white).rgba()): if fmt == QImage.Format_MonoLSB: image = image.convertToFormat(QImage.Format_Mono) fmt = QImage.Format_Mono else: if (fmt != QImage.Format_RGB32 and fmt != QImage.Format_ARGB32): image = image.convertToFormat(QImage.Format_ARGB32) fmt = QImage.Format_ARGB32 w = image.width() h = image.height() d = image.depth() if fmt == QImage.Format_Mono: bytes_per_line = (w + 7) >> 3 data = image.constBits().asstring(bytes_per_line * h) return self.write_image(data, w, h, d, cache_key=cache_key) has_alpha = False soft_mask = None if fmt == QImage.Format_ARGB32: tmask = image.constBits().asstring(4*w*h)[self.alpha_bit::4] sdata = bytearray(tmask) vals = set(sdata) vals.discard(255) # discard opaque pixels has_alpha = bool(vals) if has_alpha: # Blend image onto a white background as otherwise Qt will render # transparent pixels as black background = QImage(image.size(), QImage.Format_ARGB32_Premultiplied) background.fill(Qt.white) painter = QPainter(background) painter.drawImage(0, 0, image) painter.end() image = background ba = QByteArray() buf = QBuffer(ba) image.save(buf, 'jpeg', 94) data = ba.data() if has_alpha: soft_mask = self.write_image(tmask, w, h, 8) return self.write_image(data, w, h, 32, dct=True, soft_mask=soft_mask, cache_key=cache_key)
def clear(self): self.title = '' self.url = QUrl() self.icon = QIcon() self.history = QByteArray() self.isPinned = False self.zoomLevel = 1 self.parentTab = -1 self.childTabs = [] self.sessionData = {}
def __getstate__(self): data = QByteArray() stream = QDataStream(data, QIODevice.WriteOnly) stream.writeQVariant(self.windows) stream.writeInt(self._s_restoreDataVersion) stream.writeQVariant(self.crashedSession) stream.writeQVariant(self.closedWindows) return data
def to_png(bmp): from PyQt5.Qt import QImage, QByteArray, QBuffer i = QImage() if not i.loadFromData(bmp): raise ValueError('Invalid image data') ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) i.save(buf, 'png') return ba.data()
def rasterize_svg(self, elem, width=0, height=0, format='PNG'): view_box = elem.get('viewBox', elem.get('viewbox', None)) sizes = None logger = self.oeb.logger if view_box is not None: try: box = [float(x) for x in filter(None, re.split('[, ]', view_box))] sizes = [box[2]-box[0], box[3] - box[1]] except (TypeError, ValueError, IndexError): logger.warn('SVG image has invalid viewBox="%s", ignoring the viewBox' % view_box) else: for image in elem.xpath('descendant::*[local-name()="image" and ' '@height and contains(@height, "%")]'): logger.info('Found SVG image height in %, trying to convert...') try: h = float(image.get('height').replace('%', ''))/100. image.set('height', str(h*sizes[1])) except: logger.exception('Failed to convert percentage height:', image.get('height')) data = QByteArray(xml2str(elem, with_tail=False)) svg = QSvgRenderer(data) size = svg.defaultSize() if size.width() == 100 and size.height() == 100 and sizes: size.setWidth(sizes[0]) size.setHeight(sizes[1]) if width or height: size.scale(width, height, Qt.KeepAspectRatio) logger.info('Rasterizing %r to %dx%d' % (elem, size.width(), size.height())) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(QColor("white").rgb()) painter = QPainter(image) svg.render(painter) painter.end() array = QByteArray() buffer = QBuffer(array) buffer.open(QIODevice.WriteOnly) image.save(buffer, format) return str(array)
def rasterize_external(self, elem, style, item, svgitem): width = style['width'] height = style['height'] width = (width / 72) * self.profile.dpi height = (height / 72) * self.profile.dpi data = QByteArray(str(svgitem)) svg = QSvgRenderer(data) size = svg.defaultSize() size.scale(width, height, Qt.KeepAspectRatio) key = (svgitem.href, size.width(), size.height()) if key in self.images: href = self.images[key] else: logger = self.oeb.logger logger.info('Rasterizing %r to %dx%d' % (svgitem.href, size.width(), size.height())) image = QImage(size, QImage.Format_ARGB32_Premultiplied) image.fill(QColor("white").rgb()) painter = QPainter(image) svg.render(painter) painter.end() array = QByteArray() buffer = QBuffer(array) buffer.open(QIODevice.WriteOnly) image.save(buffer, 'PNG') data = str(array) manifest = self.oeb.manifest href = os.path.splitext(svgitem.href)[0] + '.png' id, href = manifest.generate(svgitem.id, href) manifest.add(id, href, PNG_MIME, data=data) self.images[key] = href elem.tag = XHTML('img') for attr in elem.attrib: if attr not in KEEP_ATTRS: del elem.attrib[attr] elem.attrib['src'] = item.relhref(href) if elem.text: elem.attrib['alt'] = elem.text elem.text = None for child in elem: elem.remove(child)
def __init__(self, webTab=None): self.title = '' self.url = QUrl() self.icon = QIcon() self.history = QByteArray() self.isPinned = False self.zoomLevel = 1 self.parentTab = -1 self.childTabs = [] self.sessionData = {} if webTab: self.setWebTab(webTab)
def historyData(self): ''' @return QByteArray ''' if self.isRestored(): historyArray = QByteArray() stream = QDataStream(historyArray, QIODevice.WriteOnly) history = self._webView.history() stream << history return historyArray else: return self._savedTab.history
def mimeData(self, indices): mimeData = QMimeData() encodedData = QByteArray() stream = QDataStream(encodedData, QIODevice.WriteOnly) for index in indices: if index.column() == 1: d = QVariant(self.data(index, Qt.DecorationRole)) else: d = QVariant(self.data(index, Qt.DisplayRole).toString()) stream << d mimeData.setData('application/target.tableitem.creepy', encodedData) return mimeData
def handle_embedded_fonts(self): ''' On windows, Qt uses GDI which does not support OpenType (CFF) fonts, so we need to nuke references to OpenType fonts. Qt's directwrite text backend is not mature. Also make sure all fonts are embeddable. ''' from calibre.ebooks.oeb.base import urlnormalize from calibre.utils.fonts.utils import remove_embed_restriction from PyQt5.Qt import QByteArray, QRawFont font_warnings = set() processed = set() is_cff = {} 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: try: s = rule.style src = s.getProperty('src').propertyValue[0].uri except: continue path = item.abshref(src) ff = self.oeb.manifest.hrefs.get(urlnormalize(path), None) if ff is None: continue raw = nraw = ff.data if path not in processed: processed.add(path) try: nraw = remove_embed_restriction(raw) except: continue if nraw != raw: ff.data = nraw self.oeb.container.write(path, nraw) if iswindows: if path not in is_cff: f = QRawFont(QByteArray(nraw), 12) is_cff[path] = f.isValid() and len( f.fontTable('head')) == 0 if is_cff[path]: if path not in font_warnings: font_warnings.add(path) self.log.warn( 'CFF OpenType fonts are not supported on windows, ignoring: %s' % path) remove.add(i) for i in sorted(remove, reverse=True): item.data.cssRules.pop(i)
def return_data_to_clients(self, client_id, data): try: for client_socket in self.clients: return_data_string = 'Client {} sent: {}'.format(client_id, data) data_byte_array =QByteArray() stream =QDataStream(data_byte_array, QIODevice.WriteOnly) stream.setVersion(QDataStream.Qt_5_9) stream.writeUInt32(0) stream.writeQString(return_data_string) client_socket.write(data_byte_array) except Exception as e: print(e)
def __init__(self, parent=None): super().__init__(parent) self._ui = uic.loadUi('mc/other/ClearPrivateData.ui', self) self._ui.buttonBox.setFocus() self._ui.history.clicked.connect(self._historyClicked) self._ui.clear.clicked.connect(self._dialogAccepted) self._ui.optimizeDb.clicked.connect(self._optimizeDb) self._ui.editCookies.clicked.connect(self._showCookieManager) settings = Settings() settings.beginGroup('ClearPrivateData') self._restoreState(settings.value('state', QByteArray())) settings.endGroup()
def loadResource(self, rtype, qurl): if self.base_url: if qurl.isRelative(): qurl = self.base_url.resolved(qurl) if qurl.isLocalFile(): path = qurl.toLocalFile() try: with lopen(path, 'rb') as f: data = f.read() except EnvironmentError: pass else: return QByteArray(data)
def encode_jpeg(file_path, quality=80): from calibre.utils.speedups import ReadOnlyFileBuffer quality = max(0, min(100, int(quality))) exe = get_exe_path('cjpeg') cmd = [exe] + '-optimize -progressive -maxmemory 100M -quality'.split() + [str(quality)] img = QImage() if not img.load(file_path): raise ValueError('%s is not a valid image file' % file_path) ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) if not img.save(buf, 'PPM'): raise ValueError('Failed to export image to PPM') return run_optimizer(file_path, cmd, as_filter=True, input_data=ReadOnlyFileBuffer(ba.data()))
def load_fonts(self, lrf, load_substitutions=True): font_map = {} for font in lrf.font_map: fdata = QByteArray(lrf.font_map[font].data) id = QFontDatabase.addApplicationFontFromData(fdata) if id != -1: font_map[font] = [str(i) for i in QFontDatabase.applicationFontFamilies(id)][0] if load_substitutions: base = P('fonts/liberation/*.ttf') for f in glob.glob(base): QFontDatabase.addApplicationFont(f) self.font_loader = FontLoader(font_map, self.dpi)
def save(self, save_proc): if self._text.isModified(): self._text.setModified(False) if self._cfg.get("TextEditor/PlainText", 0): txt = str(self._text.toPlainText()) if self._cfg.get("TextEditor/ReplaceTabWithSpace", 0): cnt = self._cfg.get("TextEditor/CountSpaceInTab", 1) txt = txt.replace("\t", " " * cnt) res = save_proc(txt) else: res = save_proc(str(self._text.toHtml(encoding=QByteArray()))) if res is not None: self._text.setModified(True) self.change(self._text_edit_cursor)
def magick_to_qimage(img): fmt = get_pixel_map() # ImageMagick can only output raw data in some formats that can be # read into QImage directly, if the QImage format is not one of those, use # PNG if fmt in {'RGBA', 'BGRA'}: w, h = img.size img.depth = 8 # QImage expects 8bpp raw = img.export(fmt) i = QImage(raw, w, h, QImage.Format_ARGB32) del raw # According to the documentation, raw is supposed to not be deleted, but it works, so make it explicit return i else: raw = img.export('PNG') return QImage.fromData(QByteArray(raw), 'PNG')