def create_text_arc(text, font_size, font=None, bgcolor='#ffffff'): if isinstance(text, unicode): text = text.encode('utf-8') canvas = create_canvas(300, 300, bgcolor) tw = create_text_wand(font_size, font_path=font) m = canvas.font_metrics(tw, text) canvas = create_canvas(int(m.text_width)+20, int(m.text_height*3.5), bgcolor) canvas.annotate(tw, 0, 0, 0, text) canvas.distort("ArcDistortion", [120], True) canvas.trim(0) return canvas
def create_cover(report, icons=(), cols=5, size=60, padding=8): icons = icons or tuple(default_cover_icons(cols)) rows = int(math.ceil(len(icons) / cols)) canvas = create_canvas(cols * (size + padding), rows * (size + padding), '#eeeeee') y = -size - padding // 2 x = 0 for i, icon in enumerate(icons): if i % cols == 0: y += padding + size x = padding // 2 else: x += size + padding if report and icon in report.name_map: ipath = os.path.join(report.path, report.name_map[icon]) else: ipath = I(icon, allow_user_override=False) img = Image() with open(ipath, 'rb') as f: img.load(f.read()) scaled, nwidth, nheight = fit_image(img.size[0], img.size[1], size, size) img.size = nwidth, nheight dx = (size - nwidth) // 2 canvas.compose(img, x + dx, y) return canvas.export('JPEG')
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg', preserve_aspect_ratio=True, compression_quality=70): img = Image() img.load(data) owidth, oheight = img.size if width is None: width = owidth if height is None: height = oheight if not preserve_aspect_ratio: scaled = owidth > width or oheight > height nwidth = width nheight = height else: scaled, nwidth, nheight = fit_image(owidth, oheight, width, height) if scaled: img.size = (nwidth, nheight) canvas = create_canvas(img.size[0], img.size[1], bgcolor) canvas.compose(img) data = image_to_data(canvas.img, compression_quality=compression_quality) return (canvas.size[0], canvas.size[1], data)
def test_magick(): from calibre.utils.magick import create_canvas i = create_canvas(100, 100) from calibre.gui2.tweak_book.editor.canvas import qimage_to_magick, magick_to_qimage img = magick_to_qimage(i) i = qimage_to_magick(img) print ('magick OK!')
def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0, border_color="#ffffff", fmt="jpg"): img = Image() img.load(img_data) lwidth, lheight = img.size canvas = create_canvas(lwidth + left + right, lheight + top + bottom, border_color) canvas.compose(img, left, top) return canvas.export(fmt)
def test_magick(): from calibre.utils.magick import create_canvas i = create_canvas(100, 100) from calibre.gui2.tweak_book.editor.canvas import qimage_to_magick, magick_to_qimage img = magick_to_qimage(i) i = qimage_to_magick(img) fprint('magick OK!')
def add_borders_to_image(img_data, left=0, top=0, right=0, bottom=0, border_color='#ffffff', fmt='jpg'): img = Image() img.load(img_data) lwidth, lheight = img.size canvas = create_canvas(lwidth+left+right, lheight+top+bottom, border_color) canvas.compose(img, left, top) return canvas.export(fmt)
def create_cover_page(top_lines, logo_path, width=590, height=750, bgcolor='#ffffff', output_format='jpg', texture_data=None, texture_opacity=1.0): ''' Create the standard calibre cover page and return it as a byte string in the specified output_format. ''' canvas = create_canvas(width, height, bgcolor) if texture_data and hasattr(canvas, 'texture'): texture = Image() texture.load(texture_data) texture.set_opacity(texture_opacity) canvas.texture(texture) bottom = 10 for line in top_lines: twand = create_text_wand(line.font_size, font_path=line.font_path) bottom = draw_centered_text(canvas, twand, line.text, bottom) bottom += line.bottom_margin bottom -= top_lines[-1].bottom_margin foot_font = tweaks['generate_cover_foot_font'] if not foot_font: foot_font = P('fonts/liberation/LiberationMono-Regular.ttf') vanity = create_text_arc(__appname__ + ' ' + __version__, 24, font=foot_font, bgcolor='#00000000') lwidth, lheight = vanity.size left = int(max(0, (width - lwidth) / 2.)) top = height - lheight - 10 canvas.compose(vanity, left, top) available = (width, int(top - bottom) - 20) if available[1] > 40: logo = Image() logo.open(logo_path) lwidth, lheight = logo.size scaled, lwidth, lheight = fit_image(lwidth, lheight, *available) if scaled: logo.size = (lwidth, lheight) left = int(max(0, (width - lwidth) / 2.)) top = bottom + 10 extra = int((available[1] - lheight) / 2.0) if extra > 0: top += extra canvas.compose(logo, left, top) return canvas.export(output_format)
def create_cover_page( top_lines, logo_path, width=590, height=750, bgcolor="#ffffff", output_format="jpg", texture_data=None, texture_opacity=1.0, ): """ Create the standard calibre cover page and return it as a byte string in the specified output_format. """ canvas = create_canvas(width, height, bgcolor) if texture_data and hasattr(canvas, "texture"): texture = Image() texture.load(texture_data) texture.set_opacity(texture_opacity) canvas.texture(texture) bottom = 10 for line in top_lines: twand = create_text_wand(line.font_size, font_path=line.font_path) bottom = draw_centered_text(canvas, twand, line.text, bottom) bottom += line.bottom_margin bottom -= top_lines[-1].bottom_margin foot_font = P("fonts/liberation/LiberationMono-Regular.ttf") vanity = create_text_arc(__appname__ + " " + __version__, 24, font=foot_font, bgcolor="#00000000") lwidth, lheight = vanity.size left = int(max(0, (width - lwidth) / 2.0)) top = height - lheight - 10 canvas.compose(vanity, left, top) available = (width, int(top - bottom) - 20) if available[1] > 40: logo = Image() logo.open(logo_path) lwidth, lheight = logo.size scaled, lwidth, lheight = fit_image(lwidth, lheight, *available) if scaled: logo.size = (lwidth, lheight) left = int(max(0, (width - lwidth) / 2.0)) top = bottom + 10 extra = int((available[1] - lheight) / 2.0) if extra > 0: top += extra canvas.compose(logo, left, top) return canvas.export(output_format)
def thumbnail(data, width=120, height=120, bgcolor='#ffffff', fmt='jpg', preserve_aspect_ratio=True, compression_quality=70): img = Image() img.load(data) owidth, oheight = img.size if not preserve_aspect_ratio: scaled = owidth > width or oheight > height nwidth = width nheight = height else: scaled, nwidth, nheight = fit_image(owidth, oheight, width, height) if scaled: img.size = (nwidth, nheight) canvas = create_canvas(img.size[0], img.size[1], bgcolor) canvas.compose(img) if fmt == 'jpg': canvas.set_compression_quality(compression_quality) return (canvas.size[0], canvas.size[1], canvas.export(fmt))
def extract_content(self, output_dir): # Each text record is independent (unless the continuation # value is set in the previous record). Put each converted # text recored into a separate file. We will reference the # home.html file as the first file and let the HTML input # plugin assemble the order based on hyperlinks. with CurrentDir(output_dir): for uid, num in self.uid_text_secion_number.items(): self.log.debug('Writing record with uid: %s as %s.html' % (uid, uid)) with open('%s.html' % uid, 'wb') as htmlf: html = u'<html><body>' section_header, section_data = self.sections[num] if section_header.type == DATATYPE_PHTML: html += self.process_phtml( section_data.data, section_data.header.paragraph_offsets) elif section_header.type == DATATYPE_PHTML_COMPRESSED: d = self.decompress_phtml(section_data.data) html += self.process_phtml( d, section_data.header.paragraph_offsets).decode( self.get_text_uid_encoding(section_header.uid), 'replace') html += '</body></html>' htmlf.write(html.encode('utf-8')) # Images. # Cache the image sizes in case they are used by a composite image. image_sizes = {} if not os.path.exists(os.path.join(output_dir, 'images/')): os.makedirs(os.path.join(output_dir, 'images/')) with CurrentDir(os.path.join(output_dir, 'images/')): # Single images. for uid, num in self.uid_image_section_number.items(): section_header, section_data = self.sections[num] if section_data: idata = None if section_header.type == DATATYPE_TBMP: idata = section_data elif section_header.type == DATATYPE_TBMP_COMPRESSED: if self.header_record.compression == 1: idata = decompress_doc(section_data) elif self.header_record.compression == 2: idata = zlib.decompress(section_data) try: with TemporaryFile(suffix='.palm') as itn: with open(itn, 'wb') as itf: itf.write(idata) im = Image() im.read(itn) image_sizes[uid] = im.size im.set_compression_quality(70) im.save('%s.jpg' % uid) self.log.debug( 'Wrote image with uid %s to images/%s.jpg' % (uid, uid)) except Exception as e: self.log.error( 'Failed to write image with uid %s: %s' % (uid, e)) else: self.log.error( 'Failed to write image with uid %s: No data.' % uid) # Composite images. # We're going to use the already compressed .jpg images here. for uid, num in self.uid_composite_image_section_number.items(): try: section_header, section_data = self.sections[num] # Get the final width and height. width = 0 height = 0 for row in section_data.layout: row_width = 0 col_height = 0 for col in row: if col not in image_sizes: raise Exception('Image with uid: %s missing.' % col) im = Image() im.read('%s.jpg' % col) w, h = im.size row_width += w if col_height < h: col_height = h if width < row_width: width = row_width height += col_height # Create a new image the total size of all image # parts. Put the parts into the new image. canvas = create_canvas(width, height) y_off = 0 for row in section_data.layout: x_off = 0 largest_height = 0 for col in row: im = Image() im.read('%s.jpg' % col) canvas.compose(im, x_off, y_off) w, h = im.size x_off += w if largest_height < h: largest_height = h y_off += largest_height canvas.set_compression_quality(70) canvas.save('%s.jpg' % uid) self.log.debug( 'Wrote composite image with uid %s to images/%s.jpg' % (uid, uid)) except Exception as e: self.log.error( 'Failed to write composite image with uid %s: %s' % (uid, e)) # Run the HTML through the html processing plugin. from calibre.customize.ui import plugin_for_input_format html_input = plugin_for_input_format('html') for opt in html_input.options: setattr(self.options, opt.option.name, opt.recommended_value) self.options.input_encoding = 'utf-8' odi = self.options.debug_pipeline self.options.debug_pipeline = None # Determine the home.html record uid. This should be set in the # reserved values in the metadata recored. home.html is the first # text record (should have hyper link references to other records) # in the document. try: home_html = self.header_record.home_html if not home_html: home_html = self.uid_text_secion_number.items()[0][0] except: raise Exception('Could not determine home.html') # Generate oeb from html conversion. oeb = html_input.convert(open('%s.html' % home_html, 'rb'), self.options, 'html', self.log, {}) self.options.debug_pipeline = odi return oeb
def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None, return_data=False, compression_quality=90, minify_to=None, grayscale=False): ''' Saves image in data to path, in the format specified by the path extension. Removes any transparency. If there is no transparency and no resize and the input and output image formats are the same, no changes are made. :param data: Image data as bytestring or Image object :param compression_quality: The quality of the image after compression. Number between 1 and 100. 1 means highest compression, 100 means no compression (lossless). :param bgcolor: The color for transparent pixels. Must be specified in hex. :param resize_to: A tuple (width, height) or None for no resizing :param minify_to: A tuple (width, height) to specify maximum target size. :param grayscale: If True, the image is grayscaled will be resized to fit into this target size. If None the value from the tweak is used. ''' changed = False img = _data_to_image(data) orig_fmt = normalize_format_name(img.format) fmt = os.path.splitext(path)[1] fmt = normalize_format_name(fmt[1:]) if grayscale: img.type = "GrayscaleType" changed = True if resize_to is not None: img.size = (resize_to[0], resize_to[1]) changed = True owidth, oheight = img.size nwidth, nheight = tweaks[ 'maximum_cover_size'] if minify_to is None else minify_to scaled, nwidth, nheight = fit_image(owidth, oheight, nwidth, nheight) if scaled: img.size = (nwidth, nheight) changed = True if img.has_transparent_pixels(): canvas = create_canvas(img.size[0], img.size[1], bgcolor) canvas.compose(img) img = canvas changed = True if not changed: changed = fmt != orig_fmt ret = None if return_data: ret = data if changed or isinstance(ret, Image): if hasattr(img, 'set_compression_quality') and fmt == 'jpg': img.set_compression_quality(compression_quality) ret = img.export(fmt) else: if changed or isinstance(ret, Image): if hasattr(img, 'set_compression_quality') and fmt == 'jpg': img.set_compression_quality(compression_quality) img.save(path) else: with lopen(path, 'wb') as f: f.write(data) return ret
def save_cover_data_to(data, path, bgcolor='#ffffff', resize_to=None, return_data=False, compression_quality=90, minify_to=None, grayscale=False): ''' Saves image in data to path, in the format specified by the path extension. Removes any transparency. If there is no transparency and no resize and the input and output image formats are the same, no changes are made. :param data: Image data as bytestring or Image object :param compression_quality: The quality of the image after compression. Number between 1 and 100. 1 means highest compression, 100 means no compression (lossless). :param bgcolor: The color for transparent pixels. Must be specified in hex. :param resize_to: A tuple (width, height) or None for no resizing :param minify_to: A tuple (width, height) to specify maximum target size. :param grayscale: If True, the image is grayscaled will be resized to fit into this target size. If None the value from the tweak is used. ''' changed = False img = _data_to_image(data) orig_fmt = normalize_format_name(img.format) fmt = os.path.splitext(path)[1] fmt = normalize_format_name(fmt[1:]) if grayscale: img.type = "GrayscaleType" changed = True if resize_to is not None: img.size = (resize_to[0], resize_to[1]) changed = True owidth, oheight = img.size nwidth, nheight = tweaks['maximum_cover_size'] if minify_to is None else minify_to scaled, nwidth, nheight = fit_image(owidth, oheight, nwidth, nheight) if scaled: img.size = (nwidth, nheight) changed = True if img.has_transparent_pixels(): canvas = create_canvas(img.size[0], img.size[1], bgcolor) canvas.compose(img) img = canvas changed = True if not changed: changed = fmt != orig_fmt ret = None if return_data: ret = data if changed or isinstance(ret, Image): if hasattr(img, 'set_compression_quality') and fmt == 'jpg': img.set_compression_quality(compression_quality) ret = img.export(fmt) else: if changed or isinstance(ret, Image): if hasattr(img, 'set_compression_quality') and fmt == 'jpg': img.set_compression_quality(compression_quality) img.save(path) else: with lopen(path, 'wb') as f: f.write(data) return ret
def extract_content(self, output_dir): # Each text record is independent (unless the continuation # value is set in the previous record). Put each converted # text recored into a separate file. We will reference the # home.html file as the first file and let the HTML input # plugin assemble the order based on hyperlinks. with CurrentDir(output_dir): for uid, num in self.uid_text_secion_number.items(): self.log.debug('Writing record with uid: %s as %s.html' % (uid, uid)) with open('%s.html' % uid, 'wb') as htmlf: html = u'<html><body>' section_header, section_data = self.sections[num] if section_header.type == DATATYPE_PHTML: html += self.process_phtml(section_data.data, section_data.header.paragraph_offsets) elif section_header.type == DATATYPE_PHTML_COMPRESSED: d = self.decompress_phtml(section_data.data) html += self.process_phtml(d, section_data.header.paragraph_offsets).decode(self.get_text_uid_encoding(section_header.uid), 'replace') html += '</body></html>' htmlf.write(html.encode('utf-8')) # Images. # Cache the image sizes in case they are used by a composite image. image_sizes = {} if not os.path.exists(os.path.join(output_dir, 'images/')): os.makedirs(os.path.join(output_dir, 'images/')) with CurrentDir(os.path.join(output_dir, 'images/')): # Single images. for uid, num in self.uid_image_section_number.items(): section_header, section_data = self.sections[num] if section_data: idata = None if section_header.type == DATATYPE_TBMP: idata = section_data elif section_header.type == DATATYPE_TBMP_COMPRESSED: if self.header_record.compression == 1: idata = decompress_doc(section_data) elif self.header_record.compression == 2: idata = zlib.decompress(section_data) try: with TemporaryFile(suffix='.palm') as itn: with open(itn, 'wb') as itf: itf.write(idata) im = Image() im.read(itn) image_sizes[uid] = im.size im.set_compression_quality(70) im.save('%s.jpg' % uid) self.log.debug('Wrote image with uid %s to images/%s.jpg' % (uid, uid)) except Exception as e: self.log.error('Failed to write image with uid %s: %s' % (uid, e)) else: self.log.error('Failed to write image with uid %s: No data.' % uid) # Composite images. # We're going to use the already compressed .jpg images here. for uid, num in self.uid_composite_image_section_number.items(): try: section_header, section_data = self.sections[num] # Get the final width and height. width = 0 height = 0 for row in section_data.layout: row_width = 0 col_height = 0 for col in row: if col not in image_sizes: raise Exception('Image with uid: %s missing.' % col) im = Image() im.read('%s.jpg' % col) w, h = im.size row_width += w if col_height < h: col_height = h if width < row_width: width = row_width height += col_height # Create a new image the total size of all image # parts. Put the parts into the new image. canvas = create_canvas(width, height) y_off = 0 for row in section_data.layout: x_off = 0 largest_height = 0 for col in row: im = Image() im.read('%s.jpg' % col) canvas.compose(im, x_off, y_off) w, h = im.size x_off += w if largest_height < h: largest_height = h y_off += largest_height canvas.set_compression_quality(70) canvas.save('%s.jpg' % uid) self.log.debug('Wrote composite image with uid %s to images/%s.jpg' % (uid, uid)) except Exception as e: self.log.error('Failed to write composite image with uid %s: %s' % (uid, e)) # Run the HTML through the html processing plugin. from calibre.customize.ui import plugin_for_input_format html_input = plugin_for_input_format('html') for opt in html_input.options: setattr(self.options, opt.option.name, opt.recommended_value) self.options.input_encoding = 'utf-8' odi = self.options.debug_pipeline self.options.debug_pipeline = None # Determine the home.html record uid. This should be set in the # reserved values in the metadata recored. home.html is the first # text record (should have hyper link references to other records) # in the document. try: home_html = self.header_record.home_html if not home_html: home_html = self.uid_text_secion_number.items()[0][0] except: raise Exception('Could not determine home.html') # Generate oeb from html conversion. oeb = html_input.convert(open('%s.html' % home_html, 'rb'), self.options, 'html', self.log, {}) self.options.debug_pipeline = odi return oeb
def test_magick(): from calibre.utils.magick import create_canvas, qimage_to_magick i = create_canvas(100, 100) img = i.to_qimage() i = qimage_to_magick(img) fprint('magick OK!')