def generate_cover(mi, prefs=None, as_qimage=False): ensure_app() load_builtin_fonts() prefs = prefs or cprefs prefs = {k: prefs.get(k) for k in cprefs.defaults} prefs = Prefs(**prefs) color_theme = random.choice(load_color_themes(prefs)) style = random.choice(load_styles(prefs))(color_theme, prefs) title, subtitle, footer = format_text(mi, prefs) img = QImage(prefs.cover_width, prefs.cover_height, QImage.Format_ARGB32) title_block, subtitle_block, footer_block = layout_text( prefs, img, title, subtitle, footer, img.height() // 3, style) p = QPainter(img) rect = QRect(0, 0, img.width(), img.height()) colors = style(p, rect, color_theme, title_block, subtitle_block, footer_block) for block, color in zip((title_block, subtitle_block, footer_block), colors): p.setPen(color) block.draw(p) p.end() img.setText('Generated cover', '%s %s' % (__appname__, __version__)) if as_qimage: return img return pixmap_to_data(img)
def main(args=sys.argv): opts, args = create_option_parser().parse_args(args) if opts.auto_reload: if getattr(opts, 'daemonize', False): raise SystemExit( 'Cannot specify --auto-reload and --daemonize at the same time') from calibre.srv.auto_reload import auto_reload, NoAutoReload try: from calibre.utils.logging import default_log return auto_reload(default_log, listen_on=opts.listen_on) except NoAutoReload as e: raise SystemExit(error_message(e)) ensure_single_instance() if opts.userdb: opts.userdb = os.path.abspath(os.path.expandvars(os.path.expanduser(opts.userdb))) connect(opts.userdb, exc_class=SystemExit).close() if opts.manage_users: try: manage_users_cli(opts.userdb) except (KeyboardInterrupt, EOFError): raise SystemExit(_('Interrupted by user')) raise SystemExit(0) libraries = args[1:] for lib in libraries: if not lib or not LibraryDatabase.exists_at(lib): raise SystemExit(_('There is no calibre library at: %s') % lib) libraries = libraries or load_gui_libraries() if not libraries: if not prefs['library_path']: raise SystemExit(_('You must specify at least one calibre library')) libraries = [prefs['library_path']] opts.auto_reload_port = int(os.environ.get('CALIBRE_AUTORELOAD_PORT', 0)) opts.allow_console_print = 'CALIBRE_ALLOW_CONSOLE_PRINT' in os.environ if opts.log and os.path.isdir(opts.log): raise SystemExit('The --log option must point to a file, not a directory') if opts.access_log and os.path.isdir(opts.access_log): raise SystemExit('The --access-log option must point to a file, not a directory') server = Server(libraries, opts) if getattr(opts, 'daemonize', False): if not opts.log and not iswindows: raise SystemExit( 'In order to daemonize you must specify a log file, you can use /dev/stdout to log to screen even as a daemon' ) daemonize() if opts.pidfile: with lopen(opts.pidfile, 'wb') as f: f.write(unicode_type(os.getpid()).encode('ascii')) signal.signal(signal.SIGTERM, lambda s, f: server.stop()) if not getattr(opts, 'daemonize', False) and not iswindows: signal.signal(signal.SIGHUP, lambda s, f: server.stop()) # Needed for dynamic cover generation, which uses Qt for drawing from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts() try: server.serve_forever() finally: shutdown_delete_service()
def main(args=sys.argv): opts, args = create_option_parser().parse_args(args) ensure_single_instance() if opts.userdb: opts.userdb = os.path.abspath(os.path.expandvars(os.path.expanduser(opts.userdb))) connect(opts.userdb, exc_class=SystemExit).close() if opts.manage_users: try: manage_users_cli(opts.userdb) except (KeyboardInterrupt, EOFError): raise SystemExit(_('Interrupted by user')) raise SystemExit(0) libraries = args[1:] for lib in libraries: if not lib or not LibraryDatabase.exists_at(lib): raise SystemExit(_('There is no calibre library at: %s') % lib) libraries = libraries or load_gui_libraries() if not libraries: if not prefs['library_path']: raise SystemExit(_('You must specify at least one calibre library')) libraries = [prefs['library_path']] if opts.auto_reload: if getattr(opts, 'daemonize', False): raise SystemExit( 'Cannot specify --auto-reload and --daemonize at the same time') from calibre.srv.auto_reload import auto_reload, NoAutoReload try: from calibre.utils.logging import default_log return auto_reload(default_log, listen_on=opts.listen_on) except NoAutoReload as e: raise SystemExit(e.message) opts.auto_reload_port = int(os.environ.get('CALIBRE_AUTORELOAD_PORT', 0)) opts.allow_console_print = 'CALIBRE_ALLOW_CONSOLE_PRINT' in os.environ if opts.log and os.path.isdir(opts.log): raise SystemExit('The --log option must point to a file, not a directory') if opts.access_log and os.path.isdir(opts.access_log): raise SystemExit('The --access-log option must point to a file, not a directory') server = Server(libraries, opts) if getattr(opts, 'daemonize', False): if not opts.log and not iswindows: raise SystemExit( 'In order to daemonize you must specify a log file, you can use /dev/stdout to log to screen even as a daemon' ) daemonize() if opts.pidfile: with lopen(opts.pidfile, 'wb') as f: f.write(str(os.getpid())) signal.signal(signal.SIGTERM, lambda s, f: server.stop()) if not getattr(opts, 'daemonize', False) and not iswindows: signal.signal(signal.SIGHUP, lambda s, f: server.stop()) # Needed for dynamic cover generation, which uses Qt for drawing from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts() try: server.serve_forever() finally: shutdown_delete_service()
def main(args=sys.argv): opts, args = create_option_parser().parse_args(args) if opts.manage_users: try: manage_users(opts.userdb) except (KeyboardInterrupt, EOFError): raise SystemExit(_('Interrupted by user')) raise SystemExit(0) libraries = args[1:] for lib in libraries: if not lib or not LibraryDatabase.exists_at(lib): raise SystemExit(_('There is no calibre library at: %s') % lib) if not libraries: if not prefs['library_path']: raise SystemExit( _('You must specify at least one calibre library')) libraries = [prefs['library_path']] if opts.auto_reload: if opts.daemonize: raise SystemExit( 'Cannot specify --auto-reload and --daemonize at the same time' ) from calibre.srv.auto_reload import auto_reload, NoAutoReload try: from calibre.utils.logging import default_log return auto_reload(default_log) except NoAutoReload as e: raise SystemExit(e.message) opts.auto_reload_port = int(os.environ.get('CALIBRE_AUTORELOAD_PORT', 0)) server = Server(libraries, opts) if opts.daemonize: if not opts.log and not iswindows: raise SystemExit( 'In order to daemonize you must specify a log file, you can use /dev/stdout to log to screen even as a daemon' ) daemonize() if opts.pidfile: with lopen(opts.pidfile, 'wb') as f: f.write(str(os.getpid())) signal.signal(signal.SIGTERM, lambda s, f: server.stop()) if not opts.daemonize and not iswindows: signal.signal(signal.SIGHUP, lambda s, f: server.stop()) # Needed for dynamic cover generation, which uses Qt for drawing from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts() server.serve_forever()
def overlay_image(img, canvas=None, left=0, top=0): if canvas is None: canvas = QImage(img.size(), QImage.Format_RGB32) canvas.fill(Qt.white) if imageops is None: # This is for people running from source who have not updated the # binary and so do not have the imageops module from PyQt5.Qt import QPainter from calibre.gui2 import ensure_app ensure_app() p = QPainter(canvas) p.drawImage(left, top, img) p.end() else: imageops.overlay(img, canvas, left, top) return canvas
def main(args=sys.argv): opts, args=create_option_parser().parse_args(args) if opts.manage_users: try: manage_users(opts.userdb) except (KeyboardInterrupt, EOFError): raise SystemExit(_('Interrupted by user')) raise SystemExit(0) libraries=args[1:] for lib in libraries: if not lib or not LibraryDatabase.exists_at(lib): raise SystemExit(_('There is no calibre library at: %s') % lib) if not libraries: if not prefs['library_path']: raise SystemExit(_('You must specify at least one calibre library')) libraries=[prefs['library_path']] if opts.auto_reload: if opts.daemonize: raise SystemExit('Cannot specify --auto-reload and --daemonize at the same time') from calibre.srv.auto_reload import auto_reload, NoAutoReload try: from calibre.utils.logging import default_log return auto_reload(default_log, listen_on=opts.listen_on) except NoAutoReload as e: raise SystemExit(e.message) opts.auto_reload_port=int(os.environ.get('CALIBRE_AUTORELOAD_PORT', 0)) opts.allow_console_print = 'CALIBRE_ALLOW_CONSOLE_PRINT' in os.environ server=Server(libraries, opts) if opts.daemonize: if not opts.log and not iswindows: raise SystemExit('In order to daemonize you must specify a log file, you can use /dev/stdout to log to screen even as a daemon') daemonize() if opts.pidfile: with lopen(opts.pidfile, 'wb') as f: f.write(str(os.getpid())) signal.signal(signal.SIGTERM, lambda s,f: server.stop()) if not opts.daemonize and not iswindows: signal.signal(signal.SIGHUP, lambda s,f: server.stop()) # Needed for dynamic cover generation, which uses Qt for drawing from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts() server.serve_forever()
def run_tests(find_tests=find_tests): from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts() # needed for dynamic cover generation parser = argparse.ArgumentParser() parser.add_argument( 'name', nargs='?', default=None, help= 'The name of the test to run, for e.g. writing.WritingTest.many_many_basic or .many_many_basic for a shortcut' ) args = parser.parse_args() if args.name and args.name.startswith('.'): tests = find_tests() q = args.name[1:] if not q.startswith('test_'): q = 'test_' + q ans = None try: for suite in tests: for test in suite._tests: if test.__class__.__name__ == 'ModuleImportFailure': raise Exception('Failed to import a test module: %s' % test) for s in test: if s._testMethodName == q: ans = s raise StopIteration() except StopIteration: pass if ans is None: print('No test named %s found' % args.name) raise SystemExit(1) tests = ans else: tests = unittest.defaultTestLoader.loadTestsFromName( args.name) if args.name else find_tests() r = unittest.TextTestRunner r.resultclass = TestResult r(verbosity=4).run(tests)
def generate_cover(mi, prefs=None, as_qimage=False): ensure_app() load_builtin_fonts() prefs = prefs or cprefs prefs = {k:prefs.get(k) for k in cprefs.defaults} prefs = Prefs(**prefs) color_theme = random.choice(load_color_themes(prefs)) style = random.choice(load_styles(prefs))(color_theme, prefs) title, subtitle, footer = format_text(mi, prefs) img = QImage(prefs.cover_width, prefs.cover_height, QImage.Format_ARGB32) title_block, subtitle_block, footer_block = layout_text( prefs, img, title, subtitle, footer, img.height() // 3, style) p = QPainter(img) rect = QRect(0, 0, img.width(), img.height()) colors = style(p, rect, color_theme, title_block, subtitle_block, footer_block) for block, color in zip((title_block, subtitle_block, footer_block), colors): p.setPen(color) block.draw(p) p.end() img.setText('Generated cover', '%s %s' % (__appname__, __version__)) if as_qimage: return img return pixmap_to_data(img)
def init_environment(): ensure_app() load_builtin_fonts()
def test_qt(self): from PyQt5.QtCore import QTimer from PyQt5.QtWidgets import QApplication from PyQt5.QtWebEngineWidgets import QWebEnginePage from PyQt5.QtGui import QImageReader, QFontDatabase from PyQt5.QtNetwork import QNetworkAccessManager from calibre.utils.img import image_from_data, image_to_data, test # Ensure that images can be read before QApplication is constructed. # Note that this requires QCoreApplication.libraryPaths() to return the # path to the Qt plugins which it always does in the frozen build, # because Qt is patched to know the layout of the calibre application # package. On non-frozen builds, it should just work because the # hard-coded paths of the Qt installation should work. If they do not, # then it is a distro problem. fmts = set(map(lambda x: x.data().decode('utf-8'), QImageReader.supportedImageFormats())) # no2to3 testf = {'jpg', 'png', 'svg', 'ico', 'gif'} self.assertEqual(testf.intersection(fmts), testf, "Qt doesn't seem to be able to load some of its image plugins. Available plugins: %s" % fmts) data = P('images/blank.png', allow_user_override=False, data=True) img = image_from_data(data) image_from_data(P('catalog/mastheadImage.gif', allow_user_override=False, data=True)) for fmt in 'png bmp jpeg'.split(): d = image_to_data(img, fmt=fmt) image_from_data(d) # Run the imaging tests test() from calibre.gui2 import ensure_app, destroy_app display_env_var = os.environ.pop('DISPLAY', None) try: ensure_app() self.assertGreaterEqual(len(QFontDatabase().families()), 5, 'The QPA headless plugin is not able to locate enough system fonts via fontconfig') from calibre.ebooks.covers import create_cover create_cover('xxx', ['yyy']) na = QNetworkAccessManager() self.assertTrue(hasattr(na, 'sslErrors'), 'Qt not compiled with openssl') if iswindows: from PyQt5.Qt import QtWin QtWin p = QWebEnginePage() def callback(result): callback.result = result if hasattr(print_callback, 'result'): QApplication.instance().quit() def print_callback(result): print_callback.result = result if hasattr(callback, 'result'): QApplication.instance().quit() p.runJavaScript('1 + 1', callback) p.printToPdf(print_callback) QTimer.singleShot(5000, lambda: QApplication.instance().quit()) QApplication.instance().exec_() test_flaky = ismacos and not is_ci if not test_flaky: self.assertEqual(callback.result, 2, 'Simple JS computation failed') self.assertIn(b'Skia/PDF', bytes(print_callback.result), 'Print to PDF failed') del p del na destroy_app() del QWebEnginePage finally: if display_env_var is not None: os.environ['DISPLAY'] = display_env_var
def setUpModule(): # Needed for cover generation from calibre.gui2 import ensure_app, load_builtin_fonts ensure_app(), load_builtin_fonts()
def create_cover_page(top_lines, bottom_lines, display_image, options, image_path, output_format='jpg'): from calibre.gui2 import ensure_app ensure_app() (width, height) = options.get(cfg.KEY_SIZE, (590, 750)) margins = options.get(cfg.KEY_MARGINS) (top_mgn, bottom_mgn, left_mgn, right_mgn, image_mgn) = ( margins['top'], margins['bottom'], margins['left'], margins['right'], margins['image']) left_mgn = min([left_mgn, (width / 2) - 10]) left_text_margin = left_mgn if left_mgn > 0 else 10 right_mgn = min([right_mgn, (width / 2) - 10]) right_text_margin = right_mgn if right_mgn > 0 else 10 colors = options[cfg.KEY_COLORS] bgcolor, border_color, fill_color, stroke_color = ( colors['background'], colors['border'], colors['fill'], colors['stroke']) if not options.get(cfg.KEY_COLOR_APPLY_STROKE, False): stroke_color = None auto_reduce_font = options.get(cfg.KEY_FONTS_AUTOREDUCED, False) borders = options[cfg.KEY_BORDERS] (cover_border_width, image_border_width) = ( borders['coverBorder'], borders['imageBorder']) is_background_image = options.get(cfg.KEY_BACKGROUND_IMAGE, False) if image_path: if not os.path.exists(image_path) or os.path.getsize(image_path) == 0: display_image = is_background_image = False canvas = create_canvas(width - cover_border_width * 2, height - cover_border_width * 2, bgcolor) if cover_border_width > 0: canvas = add_border(canvas, cover_border_width, border_color, bgcolor) if is_background_image: logo = Image() logo.open(image_path) outer_margin = 0 if cover_border_width == 0 else cover_border_width logo.size = (width - outer_margin * 2, height - outer_margin * 2) left = top = outer_margin canvas.compose(logo, int(left), int(top)) top = top_mgn if len(top_lines) > 0: for line in top_lines: twand = create_colored_text_wand(line, fill_color, stroke_color) top = draw_sized_text( canvas, twand, line, top, left_text_margin, right_text_margin, auto_reduce_font) top += line.bottom_margin top -= top_lines[-1].bottom_margin if len(bottom_lines) > 0: # Draw this on a fake canvas so can determine the space required fake_canvas = create_canvas(width, height, bgcolor) footer_height = 0 for line in bottom_lines: line.twand = create_colored_text_wand( line, fill_color, stroke_color) footer_height = draw_sized_text( fake_canvas, line.twand, line, footer_height, left_text_margin, right_text_margin, auto_reduce_font) footer_height += line.bottom_margin footer_height -= bottom_lines[-1].bottom_margin footer_top = height - footer_height - bottom_mgn bottom = footer_top # Re-use the text wand from previously which we will have adjusted the # font size on for line in bottom_lines: bottom = draw_sized_text( canvas, line.twand, line, bottom, left_text_margin, right_text_margin, auto_reduce_font=False) bottom += line.bottom_margin available = (width - (left_mgn + right_mgn), int(footer_top - top) - (image_mgn * 2)) else: available = (width - (left_mgn + right_mgn), int(height - top) - bottom_mgn - (image_mgn * 2)) if not is_background_image and display_image and available[1] > 40: logo = Image() logo.open(image_path) lwidth, lheight = logo.size available = (available[0] - image_border_width * 2, available[1] - image_border_width * 2) scaled, lwidth, lheight = fit_image(lwidth, lheight, *available) if not scaled and options.get(cfg.KEY_RESIZE_IMAGE_TO_FIT, False): scaled, lwidth, lheight = scaleup_image( lwidth, lheight, *available) if scaled: logo.size = (lwidth, lheight) if image_border_width > 0: logo = add_border(logo, image_border_width, border_color, bgcolor) left = int(max(0, (width - lwidth) / 2.)) top = top + image_mgn + ((available[1] - lheight) / 2.) canvas.compose(logo, int(left), int(top)) return canvas.export(output_format)