def adv_search_string(self): mk = self.matchkind.currentIndex() if mk == CONTAINS_MATCH: self.mc = '' elif mk == EQUALS_MATCH: self.mc = '=' else: self.mc = '~' all, any, phrase, none = map(lambda x: unicode_type(x.text()), (self.all, self.any, self.phrase, self.none)) all, any, none = map(self.tokens, (all, any, none)) phrase = phrase.strip() all = ' and '.join(all) any = ' or '.join(any) none = ' and not '.join(none) ans = '' if phrase: ans += '"%s"'%phrase if all: ans += (' and ' if ans else '') + all if none: ans += (' and not ' if ans else 'not ') + none if any: ans += (' or ' if ans else '') + any return ans
def __init__(self, sfnt): for table in (b'head', b'hhea', b'hmtx', b'cmap', b'OS/2', b'post', b'name'): if table not in sfnt: raise UnsupportedFont('This font has no %s table'%table) self.sfnt = sfnt self.head = self.sfnt[b'head'] hhea = self.sfnt[b'hhea'] hhea.read_data(self.sfnt[b'hmtx']) self.ascent = hhea.ascender self.descent = hhea.descender self.bbox = (self.head.x_min, self.head.y_min, self.head.x_max, self.head.y_max) self._advance_widths = hhea.advance_widths self.cmap = self.sfnt[b'cmap'] self.units_per_em = self.head.units_per_em self.os2 = self.sfnt[b'OS/2'] self.os2.read_data() self.post = self.sfnt[b'post'] self.post.read_data() self.names = get_all_font_names(self.sfnt[b'name'].raw, raw_is_table=True) self.is_otf = 'CFF ' in self.sfnt.tables self._sig = hash(self.sfnt[b'name'].raw) # Metrics for embedding in PDF pdf_scale = self.pdf_scale = lambda x:int(round(x*1000./self.units_per_em)) self.pdf_ascent, self.pdf_descent = map(pdf_scale, (self.os2.typo_ascender, self.os2.typo_descender)) self.pdf_bbox = tuple(map(pdf_scale, self.bbox)) self.pdf_capheight = pdf_scale(getattr(self.os2, 'cap_height', self.os2.typo_ascender)) self.pdf_avg_width = pdf_scale(self.os2.average_char_width) self.pdf_stemv = 50 + int((self.os2.weight_class / 65.0) ** 2)
def __init__(self, feeds): list.__init__(self, [f for f in feeds if len(f.articles) > 0]) found_articles = set() duplicates = set() def in_set(s, a): for x in s: if a.is_same_as(x): return x return None print('#feeds', len(self)) print(list(map(len, self))) for f in self: dups = [] for a in f: first = in_set(found_articles, a) if first is not None: dups.append(a) duplicates.add((first, f)) else: found_articles.add(a) for x in dups: f.articles.remove(x) self.duplicates = duplicates print(len(duplicates)) print(list(map(len, self)))
def convert_color_for_font_tag(val): rgba = parse_color_string(unicode_type(val or '')) if rgba is None or rgba == 'currentColor': return val clamp = lambda x: min(x, max(0, x), 1) rgb = map(clamp, rgba[:3]) return '#' + ''.join(map(lambda x:'%02x' % int(x * 255), rgb))
def __init__(self, img, opts, log, idc): Element.__init__(self) self.opts, self.log = opts, log self.id = next(idc) self.top, self.left, self.width, self.height, self.iwidth, self.iheight = \ map(float, map(img.get, ('top', 'left', 'rwidth', 'rheight', 'iwidth', 'iheight'))) self.src = img.get('src') self.bottom = self.top + self.height self.right = self.left + self.width
def apply_color_space(self, color, pattern, stroke=False): wl = self.current_page.write_line if color is not None and pattern is None: wl(' '.join(map(fmtnum, color)) + (' RG' if stroke else ' rg')) elif color is None and pattern is not None: wl('/Pattern %s /%s %s'%('CS' if stroke else 'cs', pattern, 'SCN' if stroke else 'scn')) elif color is not None and pattern is not None: col = ' '.join(map(fmtnum, color)) wl('/PCSp %s %s /%s %s'%('CS' if stroke else 'cs', col, pattern, 'SCN' if stroke else 'scn'))
def main(args): import sys, time from calibre import prints parser = option_parser() opts, args = parser.parse_args(args) if len(args) < 4 or len(args) > 4: parser.print_help() raise SystemExit(1) iff, off, chars = args[1:] with open(iff, 'rb') as f: orig = f.read() chars = [x for x in chars.split(',')] individual, ranges = set(), set() def not_single(c): if len(c) > 1: prints(c, 'is not a single character', file=sys.stderr) raise SystemExit(1) def conv_code(c): if c.upper()[:2] in ('U+', '0X'): c = int(c[2:], 16) return safe_chr(int(c)) for c in chars: if '-' in c: parts = [x.strip() for x in c.split('-')] if len(parts) != 2: prints('Invalid range:', c, file=sys.stderr) raise SystemExit(1) if opts.codes: parts = tuple(map(conv_code, parts)) tuple(map(not_single, parts)) ranges.add(tuple(parts)) else: if opts.codes: c = conv_code(c) not_single(c) individual.add(c) st = time.time() sf, old_stats, new_stats = subset(orig, individual, ranges) taken = time.time() - st reduced = (len(sf)/len(orig)) * 100 def sz(x): return '%gKB'%(len(x)/1024.) print_stats(old_stats, new_stats) prints('Original size:', sz(orig), 'Subset size:', sz(sf), 'Reduced to: %g%%'%(reduced)) prints('Subsetting took %g seconds'%taken) with open(off, 'wb') as f: f.write(sf) prints('Subset font written to:', off)
def get(ctx, rd, what, book_id, library_id): book_id, rest = book_id.partition('_')[::2] try: book_id = int(book_id) except Exception: raise HTTPNotFound('Book with id %r does not exist' % book_id) db = get_db(ctx, rd, library_id) if db is None: raise HTTPNotFound('Library %r not found' % library_id) with db.safe_read_lock: if not ctx.has_id(rd, db, book_id): raise BookNotFound(book_id, db) library_id = db.server_library_id # in case library_id was None if what == 'thumb': sz = rd.query.get('sz') w, h = 60, 80 if sz is None: if rest: try: w, h = map(int, rest.split('_')) except Exception: pass elif sz == 'full': w = h = None elif 'x' in sz: try: w, h = map(int, sz.partition('x')[::2]) except Exception: pass else: try: w = h = int(sz) except Exception: pass return cover(ctx, rd, library_id, db, book_id, width=w, height=h) elif what == 'cover': return cover(ctx, rd, library_id, db, book_id) elif what == 'opf': mi = db.get_metadata(book_id, get_cover=False) rd.outheaders['Content-Type'] = 'application/oebps-package+xml; charset=UTF-8' rd.outheaders['Last-Modified'] = http_date(timestampfromdt(mi.last_modified)) return metadata_to_opf(mi) elif what == 'json': from calibre.srv.ajax import book_to_json data, last_modified = book_to_json(ctx, rd, db, book_id) rd.outheaders['Last-Modified'] = http_date(timestampfromdt(last_modified)) return json(ctx, rd, get, data) else: try: return book_fmt(ctx, rd, library_id, db, book_id, what.lower()) except NoSuchFormat: raise HTTPNotFound('No %s format for the book %r' % (what.lower(), book_id))
def do_list(fields, data, opts): from calibre.utils.terminal import geometry, ColoredStream separator = ' ' widths = list(map(lambda x: 0, fields)) for i in data: for j, field in enumerate(fields): widths[j] = max(widths[j], max(len(field), len(unicode_type(i[field])))) screen_width = geometry()[0] if not screen_width: screen_width = 80 field_width = screen_width // len(fields) base_widths = list(map(lambda x: min(x + 1, field_width), widths)) while sum(base_widths) < screen_width: adjusted = False for i in range(len(widths)): if base_widths[i] < widths[i]: base_widths[i] += min( screen_width - sum(base_widths), widths[i] - base_widths[i] ) adjusted = True break if not adjusted: break widths = list(base_widths) titles = map( lambda x, y: '%-*s%s' % (x - len(separator), y, separator), widths, fields ) with ColoredStream(sys.stdout, fg='green'): prints(''.join(titles)) wrappers = list(map(lambda x: TextWrapper(x - 1), widths)) for record in data: text = [ wrappers[i].wrap(unicode_type(record[field])) for i, field in enumerate(fields) ] lines = max(map(len, text)) for l in range(lines): for i, field in enumerate(text): ft = text[i][l] if l < len(text[i]) else '' filler = '%*s' % (widths[i] - len(ft) - 1, '') print(ft.encode('utf-8') + filler.encode('utf-8'), end=separator) print()
def find_identical_books(mi, data): author_map, aid_map, title_map, lang_map = data found_books = None for a in mi.authors: author_ids = author_map.get(icu_lower(a)) if author_ids is None: return set() books_by_author = {book_id for aid in author_ids for book_id in aid_map.get(aid, ())} if found_books is None: found_books = books_by_author else: found_books &= books_by_author if not found_books: return set() ans = set() titleq = fuzzy_title(mi.title) for book_id in found_books: title = title_map.get(book_id, '') if fuzzy_title(title) == titleq: ans.add(book_id) langq = tuple(filter(lambda x: x and x != 'und', map(canonicalize_lang, mi.languages or ()))) if not langq: return ans def lang_matches(book_id): book_langq = lang_map.get(book_id) return not book_langq or langq == book_langq return {book_id for book_id in ans if lang_matches(book_id)}
def __init__(self, accounts, subjects, aliases={}, tags={}): QAbstractTableModel.__init__(self) self.accounts = accounts self.subjects = subjects self.aliases = aliases self.tags = tags self.sorted_on = (0, True) self.account_order = list(self.accounts) self.do_sort() self.headers = [_('Email'), _('Formats'), _('Subject'), _('Auto send'), _('Alias'), _('Auto send only tags')] self.default_font = QFont() self.default_font.setBold(True) self.default_font = (self.default_font) self.tooltips =[None] + list(map(textwrap.fill, [_('Formats to email. The first matching format will be sent.'), _('Subject of the email to use when sending. When left blank ' 'the title will be used for the subject. Also, the same ' 'templates used for "Save to disk" such as {title} and ' '{author_sort} can be used here.'), '<p>'+_('If checked, downloaded news will be automatically ' 'mailed to this email address ' '(provided it is in one of the listed formats and has not been filtered by tags).'), _('Friendly name to use for this email address'), _('If specified, only news with one of these tags will be sent to' ' this email address. All news downloads have their title as a' ' tag, so you can use this to easily control which news downloads' ' are sent to this email address.') ]))
def __init__(self, toc_table): strings = [] for entry in toc_table: strings.append(entry['label']) aut = entry.get('author', None) if aut: strings.append(aut) desc = entry.get('description', None) if desc: strings.append(desc) kind = entry.get('kind', None) if kind: strings.append(kind) self.cncx = CNCX(strings) try: largest = max(x['index'] for x in toc_table) except ValueError: largest = 0 fmt = '%0{0}X'.format(max(2, len('%X' % largest))) def to_entry(x): ans = {} for f in ('offset', 'length', 'depth', 'pos_fid', 'parent', 'first_child', 'last_child'): if f in x: ans[f] = x[f] for f in ('label', 'description', 'author', 'kind'): if f in x: ans[f] = self.cncx[x[f]] return (fmt % x['index'], ans) self.entries = list(map(to_entry, toc_table))
def read_rules(self): try: self.compiled_rules = tuple(map(compile_rule, gprefs.get('add_filter_rules', ()))) except Exception: self.compiled_rules = () import traceback traceback.print_exc()
def get_newest_version(): try: icon_theme_name = json.loads(I('icon-theme.json', data=True))['name'] except Exception: icon_theme_name = '' headers={ 'CALIBRE-VERSION':__version__, 'CALIBRE-OS': ('win' if iswindows else 'osx' if isosx else 'oth'), 'CALIBRE-INSTALL-UUID': prefs['installation_uuid'], 'CALIBRE-ICON-THEME': icon_theme_name, } try: version = get_https_resource_securely(URL, headers=headers) except ssl.SSLError as err: if getattr(err, 'reason', None) != 'CERTIFICATE_VERIFY_FAILED': raise # certificate verification failed, since the version check contains no # critical information, ignore and proceed # We have to do this as if the calibre CA certificate ever # needs to be revoked, then we wont be able to do version checks version = get_https_resource_securely(URL, headers=headers, cacerts=None) try: version = version.decode('utf-8').strip() except UnicodeDecodeError: version = u'' ans = NO_CALIBRE_UPDATE m = re.match(unicode(r'(\d+)\.(\d+).(\d+)$'), version) if m is not None: ans = tuple(map(int, (m.group(1), m.group(2), m.group(3)))) return ans
def __init__(self, raw): fields = raw.lstrip().replace(b'\x1b\x1b\x1b', b'\x1b').split(b'\x1b') self.title = fix_punct(fields[0].decode('cp950', 'replace')) self.num_records = int(fields[1]) self.chapter_titles = list(map( lambda x: fix_punct(x.decode('cp950', 'replace').rstrip(b'\x00')), fields[2:]))
def apply_theme(self, theme): self.theme = theme pal = self.palette() pal.setColor(pal.Base, theme_color(theme, 'Normal', 'bg')) pal.setColor(pal.AlternateBase, theme_color(theme, 'CursorLine', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'Normal', 'fg')) pal.setColor(pal.Highlight, theme_color(theme, 'Visual', 'bg')) pal.setColor(pal.HighlightedText, theme_color(theme, 'Visual', 'fg')) self.setPalette(pal) self.tooltip_palette = pal = QPalette() pal.setColor(pal.ToolTipBase, theme_color(theme, 'Tooltip', 'bg')) pal.setColor(pal.ToolTipText, theme_color(theme, 'Tooltip', 'fg')) self.line_number_palette = pal = QPalette() pal.setColor(pal.Base, theme_color(theme, 'LineNr', 'bg')) pal.setColor(pal.Text, theme_color(theme, 'LineNr', 'fg')) pal.setColor(pal.BrightText, theme_color(theme, 'LineNrC', 'fg')) self.match_paren_format = theme_format(theme, 'MatchParen') font = self.font() ff = tprefs['editor_font_family'] if ff is None: ff = default_font_family() font.setFamily(ff) font.setPointSize(tprefs['editor_font_size']) self.tooltip_font = QFont(font) self.tooltip_font.setPointSize(font.pointSize() - 1) self.setFont(font) self.highlighter.apply_theme(theme) w = self.fontMetrics() self.number_width = max(map(lambda x:w.width(unicode_type(x)), range(10))) self.size_hint = QSize(self.expected_geometry[0] * w.averageCharWidth(), self.expected_geometry[1] * w.height()) self.highlight_color = theme_color(theme, 'HighlightRegion', 'bg') self.highlight_cursor_line() self.completion_popup.clear_caches(), self.completion_popup.update()
def init_columns(self, defaults=False): # Set up columns self.opt_columns.blockSignals(True) self.model = model = self.gui.library_view.model() colmap = list(model.column_map) state = self.columns_state(defaults) self.hidden_cols = state['hidden_columns'] positions = state['column_positions'] colmap.sort(key=lambda x: positions[x]) self.opt_columns.clear() db = model.db self.field_metadata = db.field_metadata self.opt_columns.setColumnCount(4) item = QTableWidgetItem(_('Column header')) self.opt_columns.setHorizontalHeaderItem(0, item) item = QTableWidgetItem(_('Lookup name')) self.opt_columns.setHorizontalHeaderItem(1, item) item = QTableWidgetItem(_('Type')) self.opt_columns.setHorizontalHeaderItem(2, item) item = QTableWidgetItem(_('Description')) self.opt_columns.setHorizontalHeaderItem(3, item) self.opt_columns.setRowCount(len(colmap)) self.column_desc = dict(map(lambda x:(CreateCustomColumn.column_types[x]['datatype'], CreateCustomColumn.column_types[x]['text']), CreateCustomColumn.column_types)) for row, col in enumerate(colmap): self.setup_row(self.field_metadata, row, col) self.restore_geometry() self.opt_columns.cellDoubleClicked.connect(self.row_double_clicked) self.opt_columns.blockSignals(False)
def split_txt(txt, epub_split_size_kb=0): ''' Ensure there are split points for converting to EPUB. A misdetected paragraph type can result in the entire document being one giant paragraph. In this case the EPUB parser will not be able to determine where to split the file to accommodate the EPUB file size limitation and will fail. ''' # Takes care if there is no point to split if epub_split_size_kb > 0: if isinstance(txt, unicode_type): txt = txt.encode('utf-8') length_byte = len(txt) # Calculating the average chunk value for easy splitting as EPUB (+2 as a safe margin) chunk_size = long_type(length_byte / (int(length_byte / (epub_split_size_kb * 1024)) + 2)) # if there are chunks with a superior size then go and break parts = txt.split(b'\n\n') lengths = tuple(map(len, parts)) if lengths and max(lengths) > chunk_size: txt = b'\n\n'.join([ split_string_separator(line, chunk_size) for line in parts ]) if isbytestring(txt): txt = txt.decode('utf-8') return txt
def drop_event(self, event, mime_data): mime = 'application/calibre+from_library' if mime_data.hasFormat(mime): self.dropped_ids = tuple(map(int, str(mime_data.data(mime)).split())) QTimer.singleShot(1, self.do_drop) return True return False
def parse_text_assertion(self, raw, ans): oraw = raw if not raw.startswith('['): return oraw raw = raw[1:] ta = {} m, raw = self.do_match(self.ta1_pat, raw) if m is not None: before, after = m.groups() ta['before'] = self.unescape(before) if after is not None: ta['after'] = self.unescape(after) else: m, raw = self.do_match(self.ta2_pat, raw) if m is not None: ta['after'] = self.unescape(m.group(1)) # parse parameters m, raw = self.do_match(self.parameters_pat, raw) if m is not None: params = {} for name, value in zip(m.captures(1), m.captures(2)): params[name] = tuple(map(self.unescape, self.csv_pat.match(value).captures(1))) if params: ta['params'] = params if not raw.startswith(']'): return oraw # no closing ] or extra content in the assertion if ta: ans['text_assertion'] = ta return raw[1:]
def do_test(self): filename = self.value.strip() allowed = filter_filename(map(compile_rule, self.rules), filename) if allowed is None: self.result.setText(_('The filename %s did not match any rules') % filename) else: self.result.setText(_('The filename {0} will be {1}').format(filename, _('added' if allowed else 'ignored')))
def init_search_box_mixin(self): self.search.initialize('main_search_history', colorize=True, help_text=_('Search (For advanced search click the gear icon to the left)')) self.search.cleared.connect(self.search_box_cleared) # Queued so that search.current_text will be correct self.search.changed.connect(self.search_box_changed, type=Qt.QueuedConnection) self.search.focus_to_library.connect(self.focus_to_library) self.advanced_search_toggle_action.triggered.connect(self.do_advanced_search) self.search.clear() self.search.setMaximumWidth(self.width()-150) self.action_focus_search = QAction(self) shortcuts = list( map(lambda x:unicode_type(x.toString(QKeySequence.PortableText)), QKeySequence.keyBindings(QKeySequence.Find))) shortcuts += ['/', 'Alt+S'] self.keyboard.register_shortcut('start search', _('Start search'), default_keys=shortcuts, action=self.action_focus_search) self.action_focus_search.triggered.connect(self.focus_search_box) self.addAction(self.action_focus_search) self.search.setStatusTip(re.sub(r'<\w+>', ' ', unicode_type(self.search.toolTip()))) self.set_highlight_only_button_icon() self.highlight_only_button.clicked.connect(self.highlight_only_clicked) tt = _('Enable or disable search highlighting.') + '<br><br>' tt += config.help('highlight_search_matches') self.highlight_only_button.setToolTip(tt) self.highlight_only_action = ac = QAction(self) self.addAction(ac), ac.triggered.connect(self.highlight_only_clicked) self.keyboard.register_shortcut('highlight search results', _('Highlight search results'), action=self.highlight_only_action)
def refresh_ids(self, ids): self.cache.clear_caches(book_ids=ids) try: return list(map(self.id_to_index, ids)) except ValueError: pass return None
def tokenize(self): """Main class for handling other methods. Reads the file \ , uses method self.sub_reg to make basic substitutions,\ and process tokens by itself""" # read with open_for_read(self.__file) as read_obj: input_file = read_obj.read() # process simple replacements and split giving us a correct list # remove '' and \n in the process tokens = self.__sub_reg_split(input_file) # correct unicode tokens = map(self.__unicode_process, tokens) # remove empty items created by removing \uc tokens = list(filter(lambda x: len(x) > 0, tokens)) # write with open_for_write(self.__write_to) as write_obj: write_obj.write('\n'.join(tokens)) # Move and copy copy_obj = copy.Copy(bug_handler=self.__bug_handler) if self.__copy: copy_obj.copy_file(self.__write_to, "tokenize.data") copy_obj.rename(self.__write_to, self.__file) os.remove(self.__write_to)
def metadata_extensions(): # Set of all known book extensions + OPF (the OPF is used to read metadata, # but not actually added) global _metadata_extensions if _metadata_extensions is None: _metadata_extensions = frozenset(map(unicode, BOOK_EXTENSIONS)) | {'opf'} return _metadata_extensions
def generate_catalog(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) < 2: rows = range(self.gui.library_view.model().rowCount(QModelIndex())) ids = list(map(self.gui.library_view.model().id, rows)) if not ids: return error_dialog(self.gui, _('No books selected'), _('No books selected for catalog generation'), show=True) db = self.gui.library_view.model().db dbspec = {} for id in ids: dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)} # Calling gui2.tools:generate_catalog() ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager, db) if ret is None: return func, args, desc, out, sync, title = ret fmt = os.path.splitext(out)[1][1:].upper() job = self.gui.job_manager.run_job( self.Dispatcher(self.catalog_generated), func, args=args, description=desc) job.catalog_file_path = out job.fmt = fmt job.catalog_sync, job.catalog_title = sync, title self.gui.status_bar.show_message(_('Generating %s catalog...')%fmt)
def commit(self, urn=None): urn = self.current_urn if urn is None else urn if not self.detail_box.isVisible() or urn is None: return True if self.account.isVisible(): un, pw = map(unicode_type, (self.username.text(), self.password.text())) un, pw = un.strip(), pw.strip() if not un and not pw and self.schedule.isChecked(): if not getattr(self, 'subscription_optional', False): error_dialog(self, _('Need username and password'), _('You must provide a username and/or password to ' 'use this news source.'), show=True) return False if un or pw: self.recipe_model.set_account_info(urn, un, pw) else: self.recipe_model.clear_account_info(urn) if self.schedule.isChecked(): schedule_type, schedule = \ self.schedule_stack.currentWidget().schedule self.recipe_model.schedule_recipe(urn, schedule_type, schedule) else: self.recipe_model.un_schedule_recipe(urn) add_title_tag = self.add_title_tag.isChecked() keep_issues = u'0' if self.keep_issues.isEnabled(): keep_issues = unicode_type(self.keep_issues.value()) custom_tags = unicode_type(self.custom_tags.text()).strip() custom_tags = [x.strip() for x in custom_tags.split(',')] self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags, keep_issues) return True
def periodical(self): ''' TAGX block for the Primary index header of a periodical ''' list(map(self.add_tag, (1, 2, 3, 4, 5, 21, 22, 23, 0, 69, 70, 71, 72, 73, 0))) return self.header(2) + bytes(self.byts)
def sort_q_values(header_val): 'Get sorted items from an HTTP header of type: a;q=0.5, b;q=0.7...' if not header_val: return [] def item(x): e, r = x.partition(';')[::2] p, v = r.partition('=')[::2] q = 1.0 if p == 'q' and v: try: q = max(0.0, min(1.0, float(v.strip()))) except Exception: pass return e.strip(), q return tuple(map(itemgetter(0), sorted(map(item, parse_http_list(header_val)), key=itemgetter(1), reverse=True)))
def create_cover_markup(self, img, preserve_aspect_ratio, width, height): self.count += 1 makeelement, namespaces = self.document_relationships.namespace.makeelement, self.document_relationships.namespace.namespaces if preserve_aspect_ratio: if img.width >= img.height: ar = img.height / img.width height = ar * width else: ar = img.width / img.height width = ar * height root = etree.Element('root', nsmap=namespaces) ans = makeelement(root, 'w:drawing', append=False) parent = makeelement(ans, 'wp:anchor', **{'dist'+edge:'0' for edge in 'LRTB'}) parent.set('simplePos', '0'), parent.set('relativeHeight', '1'), parent.set('behindDoc',"0"), parent.set('locked', "0") parent.set('layoutInCell', "1"), parent.set('allowOverlap', '1') makeelement(parent, 'wp:simplePos', x='0', y='0') makeelement(makeelement(parent, 'wp:positionH', relativeFrom='page'), 'wp:align').text = 'center' makeelement(makeelement(parent, 'wp:positionV', relativeFrom='page'), 'wp:align').text = 'center' width, height = map(pt_to_emu, (width, height)) makeelement(parent, 'wp:extent', cx=unicode_type(width), cy=unicode_type(height)) makeelement(parent, 'wp:effectExtent', l='0', r='0', t='0', b='0') makeelement(parent, 'wp:wrapTopAndBottom') self.create_docx_image_markup(parent, 'cover.jpg', _('Cover'), img.rid, width, height) return ans
def merge_multiple_html_heads_and_bodies(root, log=None): heads, bodies = xpath(root, '//h:head'), xpath(root, '//h:body') if not (len(heads) > 1 or len(bodies) > 1): return root for child in root: root.remove(child) head = root.makeelement(XHTML('head')) body = root.makeelement(XHTML('body')) for h in heads: for x in h: head.append(x) for b in bodies: for x in b: body.append(x) tuple(map(root.append, (head, body))) if log is not None: log.warn('Merging multiple <head> and <body> sections') return root
def test_ssl(self): 'Test serving over SSL' address = '127.0.0.1' with TemporaryDirectory('srv-test-ssl') as tdir: cert_file, key_file, ca_file = map(lambda x:os.path.join(tdir, x), 'cka') create_server_cert(address, ca_file, cert_file, key_file, key_size=2048) ctx = ssl.create_default_context(cafile=ca_file) with TestServer( lambda data:(data.path[0] + data.read().decode('utf-8')), ssl_certfile=cert_file, ssl_keyfile=key_file, listen_on=address, port=0) as server: conn = http_client.HTTPSConnection(address, server.address[1], context=ctx) conn.request('GET', '/test', 'body') r = conn.getresponse() self.ae(r.status, http_client.OK) self.ae(r.read(), b'testbody') cert = conn.sock.getpeercert() subject = dict(x[0] for x in cert['subject']) self.ae(subject['commonName'], address)
def __init__(self, plugin): self.name = plugin['index_name'] self.qname = plugin.get('name', self.name) self.forum_link = plugin['thread_url'] self.zip_url = SERVER + plugin['file'] self.installed_version = None self.description = plugin['description'] self.donation_link = plugin['donate'] self.available_version = tuple(plugin['version']) self.release_date = datetime.datetime(*tuple( map(int, re.split(r'\D', plugin['last_modified'])))[:6]).date() self.calibre_required_version = tuple( plugin['minimum_calibre_version']) self.author = plugin['author'] self.platforms = plugin['supported_platforms'] self.uninstall_plugins = plugin['uninstall'] or [] self.has_changelog = plugin['history'] self.is_deprecated = plugin['deprecated']
def split_text(self, text, root, size): self.log.debug('\t\t\tSplitting text of length: %d' % len(text)) rest = text.replace('\r', '') parts = re.split('\n\n', rest) self.log.debug('\t\t\t\tFound %d parts' % len(parts)) if max(map(len, parts)) > size: raise SplitError( 'Cannot split as file contains a <pre> tag ' 'with a very large paragraph', root) ans = [] buf = '' for part in parts: if len(buf) + len(part) < size: buf += '\n\n' + part else: ans.append(buf) buf = part return ans
def save_to_disk(self, checked, single_dir=False, single_format=None, rows=None, write_opf=None, save_cover=None): if rows is None: rows = self.gui.current_view().selectionModel().selectedRows() if not rows or len(rows) == 0: return error_dialog(self.gui, _('Cannot save to disk'), _('No books selected'), show=True) path = choose_dir(self.gui, 'save to disk dialog', _('Choose destination directory')) if not path: return dpath = os.path.abspath(path).replace('/', os.sep)+os.sep lpath = self.gui.library_view.model().db.library_path.replace('/', os.sep)+os.sep if dpath.startswith(lpath): return error_dialog(self.gui, _('Not allowed'), _('You are trying to save files into the calibre ' 'library. This can cause corruption of your ' 'library. Save to disk is meant to export ' 'files from your calibre library elsewhere.'), show=True) if self.gui.current_view() is self.gui.library_view: from calibre.gui2.save import Saver from calibre.library.save_to_disk import config opts = config().parse() if single_format is not None: opts.formats = single_format # Special case for Kindle annotation files if single_format.lower() in ['mbp','pdr','tan']: opts.to_lowercase = False opts.save_cover = False opts.write_opf = False opts.template = opts.send_template opts.single_dir = single_dir if write_opf is not None: opts.write_opf = write_opf if save_cover is not None: opts.save_cover = save_cover book_ids = set(map(self.gui.library_view.model().id, rows)) Saver(book_ids, self.gui.current_db, opts, path, parent=self.gui, pool=self.gui.spare_pool()) else: paths = self.gui.current_view().model().paths(rows) self.gui.device_manager.save_books( Dispatcher(self.books_saved), paths, path)
def load(self, path_to_zip_file): if not os.access(path_to_zip_file, os.R_OK): raise PluginNotFound('Cannot access %r'%path_to_zip_file) with zipfile.ZipFile(path_to_zip_file) as zf: plugin_name = self._locate_code(zf, path_to_zip_file) try: ans = None plugin_module = 'calibre_plugins.%s'%plugin_name m = sys.modules.get(plugin_module, None) if m is not None: reload(m) else: m = importlib.import_module(plugin_module) plugin_classes = [] for obj in itervalues(m.__dict__): if isinstance(obj, type) and issubclass(obj, Plugin) and \ obj.name != 'Trivial Plugin': plugin_classes.append(obj) if not plugin_classes: raise InvalidPlugin('No plugin class found in %s:%s'%( as_unicode(path_to_zip_file), plugin_name)) if len(plugin_classes) > 1: plugin_classes.sort(key=lambda c:(getattr(c, '__module__', None) or '').count('.')) ans = plugin_classes[0] if ans.minimum_calibre_version > numeric_version: raise InvalidPlugin( 'The plugin at %s needs a version of calibre >= %s' % (as_unicode(path_to_zip_file), '.'.join(map(unicode_type, ans.minimum_calibre_version)))) if platform not in ans.supported_platforms: raise InvalidPlugin( 'The plugin at %s cannot be used on %s' % (as_unicode(path_to_zip_file), platform)) return ans except: with self._lock: del self.loaded_plugins[plugin_name] raise
def custom_dictionaries(reread=False): global _custom if _custom is None or reread: dics = [] for lc in glob.glob(os.path.join(config_dir, 'dictionaries', '*/locales')): locales = list(filter(None, open(lc, 'rb').read().decode('utf-8').splitlines())) try: name, locale, locales = locales[0], locales[1], locales[1:] except IndexError: continue base = os.path.dirname(lc) ploc = parse_lang_code(locale) if ploc.countrycode is None: continue dics.append(Dictionary( ploc, frozenset(filter(lambda x:x.countrycode is not None, map(parse_lang_code, locales))), os.path.join(base, '%s.dic' % locale), os.path.join(base, '%s.aff' % locale), False, name, os.path.basename(base))) _custom = frozenset(dics) return _custom
def metadata_from_path(cls, path): def check_unicode(txt): if not isinstance(txt, unicode_type): txt = txt.decode(filesystem_encoding, 'replace') txt = txt.replace('_', ' ') return txt mi = cls.metadata_from_formats([path]) if (mi.title==_('Unknown') or mi.authors==[_('Unknown')]) \ and '#' in mi.title: fn = os.path.splitext(os.path.basename(path))[0] match = cls.JETBOOK_FILE_NAME_PATTERN.match(fn) if match is not None: mi.title = check_unicode(match.group('title')) authors = string_to_authors(match.group('authors')) mi.authors = list(map(check_unicode, authors)) return mi
def serialize_node(node): return { 'title': node.data(0, Qt.DisplayRole), 'toc_node': node.data(0, Qt.UserRole), 'icon': node.data(0, Qt.DecorationRole), 'tooltip': node.data(0, Qt.ToolTipRole), 'is_selected': node.isSelected(), 'is_expanded': node.isExpanded(), 'children': list( map(serialize_node, (node.child(i) for i in range(node.childCount())))), }
def create_skeleton(opts, namespaces=None): namespaces = namespaces or DOCXNamespace().namespaces def w(x): return '{%s}%s' % (namespaces['w'], x) dn = {k:v for k, v in iteritems(namespaces) if k in {'w', 'r', 'm', 've', 'o', 'wp', 'w10', 'wne', 'a', 'pic'}} E = ElementMaker(namespace=dn['w'], nsmap=dn) doc = E.document() body = E.body() doc.append(body) width, height = page_size(opts) width, height = int(20 * width), int(20 * height) def margin(which): val = page_margin(opts, which) return w(which), str(int(val * 20)) body.append(E.sectPr( E.pgSz(**{w('w'):str(width), w('h'):str(height)}), E.pgMar(**dict(map(margin, 'left top right bottom'.split()))), E.cols(**{w('space'):'720'}), E.docGrid(**{w('linePitch'):"360"}), )) dn = {k:v for k, v in iteritems(namespaces) if k in tuple('wra') + ('wp',)} E = ElementMaker(namespace=dn['w'], nsmap=dn) styles = E.styles( E.docDefaults( E.rPrDefault( E.rPr( E.rFonts(**{w('asciiTheme'):"minorHAnsi", w('eastAsiaTheme'):"minorEastAsia", w('hAnsiTheme'):"minorHAnsi", w('cstheme'):"minorBidi"}), E.sz(**{w('val'):'22'}), E.szCs(**{w('val'):'22'}), E.lang(**{w('val'):'en-US', w('eastAsia'):"en-US", w('bidi'):"ar-SA"}) ) ), E.pPrDefault( E.pPr( E.spacing(**{w('after'):"0", w('line'):"276", w('lineRule'):"auto"}) ) ) ) ) return doc, styles, body
def create_indexing_data(spine, toc): if not toc: return f = partial(IndexEntry, spine) index_entries = list( map(f, (t for t in toc.flat() if t is not toc), (i - 1 for i, t in enumerate(toc.flat()) if t is not toc))) index_entries.sort(key=attrgetter('sort_key')) [i.find_end(index_entries) for i in index_entries] ie = namedtuple('IndexEntry', 'entry start_anchor end_anchor') for spine_pos, spine_item in enumerate(spine): for i in index_entries: if i.end_spine_pos < spine_pos or i.spine_pos > spine_pos: continue # Does not touch this file start = i.anchor if i.spine_pos == spine_pos else None end = i.end_anchor if i.spine_pos == spine_pos else None spine_item.index_entries.append(ie(i, start, end))
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))
def remove_links_to(container, predicate): ''' predicate must be a function that takes the arguments (name, href, fragment=None) and returns True iff the link should be removed ''' from calibre.ebooks.oeb.base import iterlinks, OEB_DOCS, OEB_STYLES, XPath, XHTML stylepath = XPath('//h:style') styleattrpath = XPath('//*[@style]') changed = set() for name, mt in iteritems(container.mime_map): removed = False if mt in OEB_DOCS: root = container.parsed(name) for el, attr, href, pos in iterlinks(root, find_links_in_css=False): hname = container.href_to_name(href, name) frag = href.partition('#')[-1] if predicate(hname, href, frag): if attr is None: el.text = None else: if el.tag == XHTML('link') or el.tag == XHTML('img'): extract(el) else: del el.attrib[attr] removed = True for tag in stylepath(root): if tag.text and (tag.get('type') or 'text/css').lower() == 'text/css': sheet = container.parse_css(tag.text) if remove_links_in_sheet(partial(container.href_to_name, base=name), sheet, predicate): tag.text = css_text(sheet) removed = True for tag in styleattrpath(root): style = tag.get('style') if style: style = container.parse_css(style, is_declaration=True) if remove_links_in_declaration(partial(container.href_to_name, base=name), style, predicate): removed = True tag.set('style', css_text(style)) elif mt in OEB_STYLES: removed = remove_links_in_sheet(partial(container.href_to_name, base=name), container.parsed(name), predicate) if removed: changed.add(name) tuple(map(container.dirty, changed)) return changed
def init_search_box_mixin(self): self.search.initialize( 'main_search_history', colorize=True, help_text=_( 'Search (For advanced search click the gear icon to the left)') ) self.search.cleared.connect(self.search_box_cleared) # Queued so that search.current_text will be correct self.search.changed.connect(self.search_box_changed, type=Qt.ConnectionType.QueuedConnection) self.search.focus_to_library.connect(self.focus_to_library) self.advanced_search_toggle_action.triggered.connect( self.do_advanced_search) self.search.clear() self.search.setMaximumWidth(self.width() - 150) self.action_focus_search = QAction(self) shortcuts = list( map( lambda x: unicode_type( x.toString(QKeySequence.SequenceFormat.PortableText)), QKeySequence.keyBindings(QKeySequence.StandardKey.Find))) shortcuts += ['/', 'Alt+S'] self.keyboard.register_shortcut('start search', _('Start search'), default_keys=shortcuts, action=self.action_focus_search) self.action_focus_search.triggered.connect(self.focus_search_box) self.addAction(self.action_focus_search) self.search.setStatusTip( re.sub(r'<\w+>', ' ', unicode_type(self.search.toolTip()))) self.set_highlight_only_button_icon() self.highlight_only_button.clicked.connect(self.highlight_only_clicked) tt = _('Enable or disable search highlighting.') + '<br><br>' tt += config.help('highlight_search_matches') self.highlight_only_button.setToolTip(tt) self.highlight_only_action = ac = QAction(self) self.addAction(ac), ac.triggered.connect(self.highlight_only_clicked) self.keyboard.register_shortcut('highlight search results', _('Highlight search results'), action=self.highlight_only_action)
def builtin_dictionaries(): global _builtins if _builtins is None: dics = [] for lc in glob.glob( os.path.join(P('dictionaries', allow_user_override=False), '*/locales')): locales = list( filter(None, open(lc, 'rb').read().decode('utf-8').splitlines())) locale = locales[0] base = os.path.dirname(lc) dics.append( Dictionary(parse_lang_code(locale), frozenset(map(parse_lang_code, locales)), os.path.join(base, '%s.dic' % locale), os.path.join(base, '%s.aff' % locale), True, None, None)) _builtins = frozenset(dics) return _builtins
def parse_nav(container, nav_name): root = container.parsed(nav_name) toc_root = TOC() toc_root.lang = toc_root.uid = None et = '{%s}type' % EPUB_NS for nav in root.iterdescendants(XHTML('nav')): if nav.get(et) == 'toc': ol = first_child(nav, XHTML('ol')) if ol is not None: process_nav_node(container, ol, toc_root, nav_name) for h in nav.iterchildren( *map(XHTML, 'h1 h2 h3 h4 h5 h6'.split())): text = etree.tostring( h, method='text', encoding='unicode', with_tail=False) or h.get('title') if text: toc_root.toc_title = text break break return toc_root
def dropMimeData(self, md, action, row, column, parent): if action != Qt.MoveAction or not md.hasFormat( 'application/calibre_charcode_indices' ) or row < 0 or column != 0: return False indices = list( map( int, bytes(md.data('application/calibre_charcode_indices')).decode( 'ascii').split(','))) codes = [self.chars[x] for x in indices] for x in indices: self.chars[x] = None for x in reversed(codes): self.chars.insert(row, x) self.beginResetModel() self.chars = [x for x in self.chars if x is not None] self.endResetModel() tprefs['charmap_favorites'] = list(self.chars) return True
def apply_settings(self): self.keyboard.finalize() self.setDockNestingEnabled(tprefs['nestable_dock_widgets']) for v, h in product(('top', 'bottom'), ('left', 'right')): p = 'dock_%s_%s' % (v, h) pref = tprefs[p] or tprefs.defaults[p] area = getattr( Qt, '%sDockWidgetArea' % capitalize({ 'vertical': h, 'horizontal': v }[pref])) self.setCorner( getattr(Qt, '%s%sCorner' % tuple(map(capitalize, (v, h)))), area) self.preview.apply_settings() self.live_css.apply_theme() for bar in (self.global_bar, self.tools_bar, self.plugins_bar): bar.setIconSize( QSize(tprefs['toolbar_icon_size'], tprefs['toolbar_icon_size']))
def test_qt(self): 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 the QT_PLUGIN_PATH env var is set. 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 Application os.environ.pop('DISPLAY', None) has_headless = isosx or islinux app = Application([], headless=has_headless) self.assertGreaterEqual(len(QFontDatabase().families()), 5, 'The QPA headless plugin is not able to locate enough system fonts via fontconfig') if has_headless: from calibre.ebooks.covers import create_cover create_cover('xxx', ['yyy']) na = QNetworkAccessManager() self.assertTrue(hasattr(na, 'sslErrors'), 'Qt not compiled with openssl') from PyQt5.QtWebKitWidgets import QWebView if iswindows: from PyQt5.Qt import QtWin QtWin QWebView() del QWebView del na del app
def write_widths(self, objects): glyphs = sorted(self.used_glyphs|{0}) widths = {g:self.metrics.pdf_scale(w) for g, w in zip(glyphs, self.metrics.glyph_widths(glyphs))} counter = Counter() for g, w in widths.iteritems(): counter[w] += 1 most_common = counter.most_common(1)[0][0] self.descendant_font['DW'] = most_common widths = {g:w for g, w in widths.iteritems() if w != most_common} groups = Array() for k, g in groupby(enumerate(widths.iterkeys()), lambda i_x:i_x[0]-i_x[1]): group = list(map(itemgetter(1), g)) gwidths = [widths[g] for g in group] if len(set(gwidths)) == 1 and len(group) > 1: w = (min(group), max(group), gwidths[0]) else: w = (min(group), Array(gwidths)) groups.extend(w) self.descendant_font['W'] = objects.add(groups)
def refresh_ids(self, db, ids): ''' Refresh the data in the cache for books identified by ids. Returns a list of affected rows or None if the rows are filtered. ''' for id in ids: try: self._data[id] = CacheRow(db, self.composites, self.datetimes, db.conn.get('SELECT * from meta2 WHERE id=?', (id,))[0], self.series_col, self.series_sort_col) self._data[id].append(db.book_on_device_string(id)) self._data[id].append(self.marked_ids_dict.get(id, None)) self._data[id].append(None) self._uuid_map[self._data[id][self._uuid_column_index]] = id except IndexError: return None try: return list(map(self.row, ids)) except ValueError: pass return None
def template(self): if not self.color or not self.conditions: return None conditions = [x for x in map(self.apply_condition, self.conditions) if x is not None] conditions = (',\n' + ' '*9).join(conditions) if len(self.conditions) > 1: return dedent('''\ program: {sig} test(and( {conditions} ), '{color}', ''); ''').format(sig=self.signature, conditions=conditions, color=self.color) else: return dedent('''\ program: {sig} test({conditions}, '{color}', ''); ''').format(sig=self.signature, conditions=conditions, color=self.color)
class ChunkIndex(Index): tag_types = tuple(map(TagMeta, ( ('cncx_offset', 2, 1, 1, 0), ('file_number', 3, 1, 2, 0), ('sequence_number', 4, 1, 4, 0), ('geometry', 6, 2, 8, 0), EndTagTable ))) def __init__(self, chunk_table): self.cncx = CNCX(c.selector for c in chunk_table) self.entries = [ ('%010d'%c.insert_pos, { 'cncx_offset':self.cncx[c.selector], 'file_number':c.file_number, 'sequence_number':c.sequence_number, 'geometry':(c.start_pos, c.length), }) for c in chunk_table ]
def iterusbdevices(): buf = None pat = devid_pat() for dev_list, devinfo in DeviceSet(guid=None, enumerator='USB', flags=DIGCF_PRESENT | DIGCF_ALLCLASSES).devices(): buf, devid = get_device_registry_property(dev_list, byref(devinfo), buf=buf) if devid: devid = devid[0].lower() m = pat.search(devid) if m is None: yield USBDevice(None, None, None, devid, devinfo.DevInst) else: try: vid, pid, bcd = map(parse_hex, m.group(1, 2, 3)) except Exception: yield USBDevice(None, None, None, devid, devinfo.DevInst) else: yield USBDevice(vid, pid, bcd, devid, devinfo.DevInst)
def load_huff(self, huff): if huff[0:8] != b'HUFF\x00\x00\x00\x18': raise MobiError('Invalid HUFF header') off1, off2 = struct.unpack_from(b'>LL', huff, 8) def dict1_unpack(v): codelen, term, maxcode = v&0x1f, v&0x80, v>>8 assert codelen != 0 if codelen <= 8: assert term maxcode = ((maxcode + 1) << (32 - codelen)) - 1 return (codelen, term, maxcode) self.dict1 = tuple(map(dict1_unpack, struct.unpack_from(b'>256L', huff, off1))) dict2 = struct.unpack_from(b'>64L', huff, off2) self.mincode, self.maxcode = (), () for codelen, mincode in enumerate((0,) + dict2[0::2]): self.mincode += (mincode << (32 - codelen), ) for codelen, maxcode in enumerate((0,) + dict2[1::2]): self.maxcode += (((maxcode + 1) << (32 - codelen)) - 1, ) self.dictionary = []
def text_to_regex(text): has_leading = text.lstrip() != text has_trailing = text.rstrip() != text if text and not text.strip(): return r'\s+' ans = [] for wpart in spat.split(text.strip()): if not wpart.strip(): ans.append(r'\s+') else: for part in qpat.split(wpart): r = quote_map.get(part) if r is not None: ans.append('[' + r + ']') else: part = invisible_chars.join(map(regex.escape, part)) ans.append(part) if has_leading: ans.insert(0, r'\s+') if has_trailing: ans.append(r'\s+') return ''.join(ans)
def commit(self, urn=None): urn = self.current_urn if urn is None else urn if not self.detail_box.isVisible() or urn is None: return True if self.account.isVisible(): un, pw = map(unicode_type, (self.username.text(), self.password.text())) un, pw = un.strip(), pw.strip() if not un and not pw and self.schedule.isChecked(): if not getattr(self, 'subscription_optional', False): error_dialog( self, _('Need username and password'), _('You must provide a username and/or password to ' 'use this news source.'), show=True) return False if un or pw: self.recipe_model.set_account_info(urn, un, pw) else: self.recipe_model.clear_account_info(urn) if self.schedule.isChecked(): schedule_type, schedule = \ self.schedule_stack.currentWidget().schedule self.recipe_model.schedule_recipe(urn, schedule_type, schedule) else: self.recipe_model.un_schedule_recipe(urn) add_title_tag = self.add_title_tag.isChecked() keep_issues = '0' if self.keep_issues.isEnabled(): keep_issues = unicode_type(self.keep_issues.value()) custom_tags = unicode_type(self.custom_tags.text()).strip() custom_tags = [x.strip() for x in custom_tags.split(',')] self.recipe_model.customize_recipe(urn, add_title_tag, custom_tags, keep_issues) return True
def update_found(self, calibre_version, number_of_plugin_updates, force=False, no_show_popup=False): self.last_newest_calibre_version = calibre_version has_calibre_update = calibre_version != NO_CALIBRE_UPDATE and calibre_version[0] > 0 has_plugin_updates = number_of_plugin_updates > 0 self.plugin_update_found(number_of_plugin_updates) version_url = as_hex_unicode(msgpack_dumps((calibre_version, number_of_plugin_updates))) calibre_version = u'.'.join(map(unicode_type, calibre_version)) if not has_calibre_update and not has_plugin_updates: self.status_bar.update_label.setVisible(False) return if has_calibre_update: plt = u'' if has_plugin_updates: plt = ngettext(' (one plugin update)', ' ({} plugin updates)', number_of_plugin_updates).format(number_of_plugin_updates) msg = (u'<span style="color:green; font-weight: bold">%s: ' u'<a href="update:%s">%s%s</a></span>') % ( _('Update found'), version_url, calibre_version, plt) else: plt = ngettext('updated plugin', 'updated plugins', number_of_plugin_updates) msg = (u'<a href="update:%s">%d %s</a>')%(version_url, number_of_plugin_updates, plt) self.status_bar.update_label.setText(msg) self.status_bar.update_label.setVisible(True) if has_calibre_update: if (force or (config.get('new_version_notification') and not is_version_notified(calibre_version))): if not no_show_popup: self._update_notification__ = UpdateNotification(calibre_version, number_of_plugin_updates, parent=self) self._update_notification__.show() elif has_plugin_updates: if force: from calibre.gui2.dialogs.plugin_updater import (PluginUpdaterDialog, FILTER_UPDATE_AVAILABLE) d = PluginUpdaterDialog(self, initial_filter=FILTER_UPDATE_AVAILABLE) d.exec_() if d.do_restart: self.quit(restart=True)
def add_anchors_markup(root, uuid, anchors): body = last_tag(root) div = body.makeelement( XHTML('div'), id=uuid, style= 'display:block !important; page-break-before: always !important; break-before: always !important; white-space: pre-wrap !important' ) div.text = '\n\n' body.append(div) def a(anchor): a = div.makeelement( XHTML('a'), href='#' + anchor, style= 'min-width: 10px !important; min-height: 10px !important; border: solid 1px !important;' ) a.text = a.tail = ' ' div.append(a) tuple(map(a, anchors)) a(uuid)
def get_covers(themes, callback, num_of_workers=8): items = Queue() tuple(map(items.put, themes)) def run(): while True: try: metadata = items.get_nowait() except Empty: return try: cdata = get_cover(metadata) except Exception as e: import traceback traceback.print_exc() callback(metadata, e) else: callback(metadata, cdata) for w in xrange(num_of_workers): t = Thread(name='IconThemeCover', target=run) t.daemon = True t.start()