def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level=9, jpeg_optimized=True, jpeg_progressive=False): ''' Serialize image to bytestring in the specified format. :param compression_quality: is for JPEG and goes from 0 to 100. 100 being lowest compression, highest image quality :param png_compression_level: is for PNG and goes from 0-9. 9 being highest compression. :param jpeg_optimized: Turns on the 'optimize' option for libjpeg which losslessly reduce file size :param jpeg_progressive: Turns on the 'progressive scan' option for libjpeg which allows JPEG images to be downloaded in streaming fashion ''' ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) fmt = fmt.upper() is_jpeg = fmt in ('JPG', 'JPEG') w = QImageWriter(buf, fmt.encode('ascii')) if is_jpeg: if img.hasAlphaChannel(): img = blend_image(img) # QImageWriter only gained the following options in Qt 5.5 if jpeg_optimized and hasattr(QImageWriter, 'setOptimizedWrite'): w.setOptimizedWrite(True) if jpeg_progressive and hasattr(QImageWriter, 'setProgressiveScanWrite'): w.setProgressiveScanWrite(True) w.setQuality(compression_quality) elif fmt == 'PNG': cl = min(9, max(0, png_compression_level)) w.setQuality(10 * (9-cl)) if not w.write(img): raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString()) return ba.data()
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 = bytes(ba.data()) except Exception as e: self.exception = e self.traceback = traceback.format_exc() finally: self.loop.exit(0)
def image_and_format_from_data(data): 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 __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 scale_image(data, width=60, height=80, compression_quality=70, as_png=False, preserve_aspect_ratio=True): ''' Scale an image, returning it as either JPEG or PNG data (bytestring). Transparency is alpha blended with white when converting to JPEG. Is thread safe and does not require a QApplication. ''' # We use Qt instead of ImageMagick here because ImageMagick seems to use # some kind of memory pool, causing memory consumption to sky rocket. if isinstance(data, QImage): img = data else: img = QImage() if not img.loadFromData(data): raise ValueError('Could not load image for thumbnail generation') if preserve_aspect_ratio: scaled, nwidth, nheight = fit_image(img.width(), img.height(), width, height) if scaled: img = img.scaled(nwidth, nheight, Qt.KeepAspectRatio, Qt.SmoothTransformation) else: if img.width() != width or img.height() != height: img = img.scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) if not as_png and img.hasAlphaChannel(): nimg = QImage(img.size(), QImage.Format_RGB32) nimg.fill(Qt.white) p = QPainter(nimg) p.drawImage(0, 0, img) p.end() img = nimg ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) fmt = 'PNG' if as_png else 'JPEG' if not img.save(buf, fmt, quality=compression_quality): raise ValueError('Failed to export thumbnail image to: ' + fmt) return img.width(), img.height(), ba.data()
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(QBuffer.WriteOnly) icon.pixmap(32).save(buf, 'PNG') return dbus.ByteArray(bytes((ba.data())))
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 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 = bytes(ba.data()) buf.close() return ret
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 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 bytes(ba.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 pixmapToByteArray(self, pix): ''' @param: pix QPixmap @return: QByteArray ''' bytes_ = QByteArray() buffer_ = QBuffer(bytes_) buffer_.open(QIODevice.WriteOnly) if pix.save(buffer_, 'PNG'): return buffer_.buffer().toBase64() return QByteArray()
def _setReply(self, job, contentType, content): ''' @param: job QWebEngineUrlRequestJob @param: contentType QByteArray @param: content QByteArray ''' buf = QBuffer(job) buf.open(QIODevice.ReadWrite) buf.write(content) buf.seek(0) job.reply(contentType, buf)
def send_reply(rq, mime_type, data): if sip.isdeleted(rq): return # make the buf a child of rq so that it is automatically deleted when # rq is deleted buf = QBuffer(parent=rq) buf.open(QIODevice.OpenModeFlag.WriteOnly) # we have to copy data into buf as it will be garbage # collected by python buf.write(data) buf.seek(0) buf.close() rq.reply(mime_type.encode('ascii'), buf)
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 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 [_f for _f in re.split('[, ]', view_box) if _f] ] 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 image_to_data(img, compression_quality=95, fmt='JPEG'): ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) fmt = fmt.upper() if img.hasAlphaChannel() and fmt in 'JPEG JPG'.split(): nimg = QImage(img.size(), QImage.Format_RGB32) nimg.fill(Qt.white) p = QPainter(nimg) p.drawImage(0, 0, img) p.end() img = nimg if not img.save(buf, fmt, quality=compression_quality): raise ValueError('Failed to export image as ' + fmt) return ba.data()
def ins_image(self, image, fmt, width, height, insert_space=True): bytes_ = QByteArray() buffer = QBuffer(bytes_) buffer.open(QIODevice.WriteOnly) image.save(buffer, fmt) buffer.close() base64 = bytes_.toBase64().data().decode(encoding="utf-8") s = (f'<img width="{width}" height="{height}" ' f'src="data:image/{fmt};base64,{base64}"') self._text_edit_cursor.insertHtml(s) if insert_space: self._text_edit_cursor.insertText(" ")
def _saveIconsToDatabase(self): with self._iconCacheMutex: for url, img in self._iconBuffer: ba = QByteArray() buff = QBuffer(ba) buff.open(QIODevice.WriteOnly) img.save(buff, 'PNG') # QByteArray encodedUrl = encodeUrl(url) self._urlImageCache.pop(encodedUrl, None) IconsDbModel.insert(icon=buff.data(), url=encodedUrl.data().decode()) \ .on_conflict('replace') \ .execute() self._iconBuffer.clear()
def pixmap_to_data(pixmap, format='JPEG', quality=None): ''' Return the QPixmap pixmap as a string saved in the specified format. ''' if quality is None: if format.upper() == "PNG": # For some reason on windows with Qt 5.6 using a quality of 90 # generates invalid PNG data. Many other quality values work # but we use -1 for the default quality which is most likely to # work quality = -1 else: quality = 90 ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) pixmap.save(buf, format, quality=quality) return bytes(ba.data())
def to_png(bmp): # ImageMagick does not convert some bmp files correctly, while Qt does, # so try Qt first. See for instance: # https://bugs.launchpad.net/calibre/+bug/934167 # ImageMagick bug report: # http://www.imagemagick.org/discourse-server/viewtopic.php?f=3&t=20350 from PyQt5.Qt import QImage, QByteArray, QBuffer i = QImage() if i.loadFromData(bmp): ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) i.save(buf, 'png') return bytes(ba.data()) from calibre.utils.magick import Image img = Image() img.load(bmp) return img.export('png')
def image_to_data( img, compression_quality=95, fmt="JPEG", png_compression_level=9, jpeg_optimized=True, jpeg_progressive=False ): """ Serialize image to bytestring in the specified format. :param compression_quality: is for JPEG and goes from 0 to 100. 100 being lowest compression, highest image quality :param png_compression_level: is for PNG and goes from 0-9. 9 being highest compression. :param jpeg_optimized: Turns on the 'optimize' option for libjpeg which losslessly reduce file size :param jpeg_progressive: Turns on the 'progressive scan' option for libjpeg which allows JPEG images to be downloaded in streaming fashion """ fmt = fmt.upper() ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) if fmt == "GIF": w = QImageWriter(buf, b"PNG") w.setQuality(90) if not w.write(img): raise ValueError("Failed to export image as " + fmt + " with error: " + w.errorString()) from PIL import Image im = Image.open(BytesIO(ba.data())) buf = BytesIO() im.save(buf, "gif") return buf.getvalue() is_jpeg = fmt in ("JPG", "JPEG") w = QImageWriter(buf, fmt.encode("ascii")) if is_jpeg: if img.hasAlphaChannel(): img = blend_image(img) # QImageWriter only gained the following options in Qt 5.5 if jpeg_optimized and hasattr(QImageWriter, "setOptimizedWrite"): w.setOptimizedWrite(True) if jpeg_progressive and hasattr(QImageWriter, "setProgressiveScanWrite"): w.setProgressiveScanWrite(True) w.setQuality(compression_quality) elif fmt == "PNG": cl = min(9, max(0, png_compression_level)) w.setQuality(10 * (9 - cl)) if not w.write(img): raise ValueError("Failed to export image as " + fmt + " with error: " + w.errorString()) 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 pixmap_to_data(pixmap): ba = QByteArray() buf = QBuffer(ba) buf.open(QBuffer.WriteOnly) pixmap.save(buf, 'PNG') return bytearray(ba.data())
def pixmap_to_data(pixmap): ba = QByteArray() buf = QBuffer(ba) buf.open(QIODevice.OpenModeFlag.WriteOnly) pixmap.save(buf, 'PNG') return bytes(bytearray(ba.data()))