def find_usage_in(self, elem, inherited_style, ff_rules): style = elem_style(self.style_rules, elem.get('class', '') or '', inherited_style) for child in elem: self.find_usage_in(child, style, ff_rules) has_font, existing = used_font(style, ff_rules) if not has_font: return if existing is None: in_book = used_font(style, self.embedded_fonts)[1] if in_book is None: # Try to find the font in the system added = self.embed_font(style) if added is not None: ff_rules.append(added) self.embedded_fonts.append(added) else: # TODO: Create a page rule from the book rule (cannot use it # directly as paths might be different) item = in_book['item'] sheet = self.parser.parseString(in_book['rule'].cssText, validate=False) rule = sheet.cssRules[0] page_sheet = self.get_page_sheet() href = page_sheet.abshref(item.href) rule.style.setProperty('src', 'url(%s)' % href) ff_rules.append(find_font_face_rules(sheet, self.oeb)[0]) page_sheet.data.insertRule(rule, len(page_sheet.data.cssRules))
def find_usage_in(self, elem, inherited_style, ff_rules): style = elem_style(self.style_rules, elem.get('class', '') or '', inherited_style) for child in elem: self.find_usage_in(child, style, ff_rules) has_font, existing = used_font(style, ff_rules) if not has_font: return if existing is None: in_book = used_font(style, self.embedded_fonts)[1] if in_book is None: # Try to find the font in the system added = self.embed_font(style) if added is not None: ff_rules.append(added) self.embedded_fonts.append(added) else: # TODO: Create a page rule from the book rule (cannot use it # directly as paths might be different) item = in_book['item'] sheet = self.parser.parseString(in_book['rule'].cssText, validate=False) rule = sheet.cssRules[0] page_sheet = self.get_page_sheet() href = page_sheet.abshref(item.href) rule.style.setProperty('src', 'url(%s)' % href) ff_rules.append(find_font_face_rules(sheet, self.oeb)[0]) page_sheet.data.insertRule(rule, len(page_sheet.data.cssRules))
def serialize(self, text_styles, fonts, embed_relationships, font_data_map): makeelement = self.namespace.makeelement font_families, seen = set(), set() for ts in text_styles: if ts.font_family: lf = ts.font_family.lower() if lf not in seen: seen.add(lf) font_families.add(ts.font_family) family_map = {} for family in sorted(font_families): family_map[family] = makeelement(fonts, 'w:font', w_name=family) embedded_fonts = [] for item in self.oeb.manifest: if item.media_type in OEB_STYLES and hasattr( item.data, 'cssRules'): embedded_fonts.extend(find_font_face_rules(item, self.oeb)) num = 0 face_map = defaultdict(set) rel_map = {} for ef in embedded_fonts: ff = ef['font-family'][0] if ff not in font_families: continue num += 1 bold = ef['weight'] > 400 italic = ef['font-style'] != 'normal' tag = 'Regular' if bold or italic: tag = 'Italic' if bold and italic: tag = 'BoldItalic' elif bold: tag = 'Bold' if tag in face_map[ff]: continue face_map[ff].add(tag) font = family_map[ff] key = uuid4() item = ef['item'] rid = rel_map.get(item) if rid is None: rel_map[item] = rid = 'rId%d' % num fname = 'fonts/font%d.odttf' % num makeelement(embed_relationships, 'Relationship', Id=rid, Type=self.namespace.names['EMBEDDED_FONT'], Target=fname) font_data_map['word/' + fname] = obfuscate_font_data( item.data, key) makeelement(font, 'w:embed' + tag, r_id=rid, w_fontKey='{%s}' % key.urn.rpartition(':')[-1].upper(), w_subsetted="true" if self.opts.subset_embedded_fonts else "false")
def find_embedded_fonts(self): ''' Find all @font-face rules and extract the relevant info from them. ''' self.embedded_fonts = [] for item in self.oeb.manifest: if not hasattr(item.data, 'cssRules'): continue self.embedded_fonts.extend(find_font_face_rules(item, self.oeb))
def find_embedded_fonts(self): ''' Find all @font-face rules and extract the relevant info from them. ''' self.embedded_fonts = [] for item in self.oeb.manifest: if not hasattr(item.data, 'cssRules'): continue self.embedded_fonts.extend(find_font_face_rules(item, self.oeb))
def embed_font(self, style): ff = [ unicode(f) for f in style.get('font-family', []) if unicode(f).lower() not in { 'serif', 'sansserif', 'sans-serif', 'fantasy', 'cursive', 'monospace' } ] if not ff: return ff = ff[0] if ff in self.warned or ff == 'inherit': return try: fonts = font_scanner.fonts_for_family(ff) except NoFonts: self.log.warn('Failed to find fonts for family:', ff, 'not embedding') self.warned.add(ff) return try: weight = int(style.get('font-weight', '400')) except (ValueError, TypeError, AttributeError): w = style['font-weight'] if w not in self.warned2: self.log.warn('Invalid weight in font style: %r' % w) self.warned2.add(w) return for f in fonts: if f['weight'] == weight and f['font-style'] == style.get( 'font-style', 'normal') and f['font-stretch'] == style.get( 'font-stretch', 'normal'): self.log('Embedding font %s from %s' % (f['full_name'], f['path'])) data = font_scanner.get_font_data(f) name = f['full_name'] ext = 'otf' if f['is_otf'] else 'ttf' name = ascii_filename(name).replace(' ', '-').replace( '(', '').replace(')', '') fid, href = self.oeb.manifest.generate(id=u'font', href=u'fonts/%s.%s' % (name, ext)) item = self.oeb.manifest.add(fid, href, guess_type('dummy.' + ext)[0], data=data) item.unload_data_from_memory() page_sheet = self.get_page_sheet() href = page_sheet.relhref(item.href) css = '''@font-face { font-family: "%s"; font-weight: %s; font-style: %s; font-stretch: %s; src: url(%s) }''' % ( f['font-family'], f['font-weight'], f['font-style'], f['font-stretch'], href) sheet = self.parser.parseString(css, validate=False) page_sheet.data.insertRule(sheet.cssRules[0], len(page_sheet.data.cssRules)) return find_font_face_rules(sheet, self.oeb)[0]
def process_item(self, item, sheets): ff_rules = [] self.current_item = item self.page_sheet = None for sheet in sheets: if 'page_css' in sheet.id: ff_rules.extend(find_font_face_rules(sheet, self.oeb)) self.page_sheet = sheet base = {'font-family':['serif'], 'font-weight': '400', 'font-style':'normal', 'font-stretch':'normal'} for body in item.data.xpath('//*[local-name()="body"]'): self.find_usage_in(body, base, ff_rules)
def process_item(self, item, sheets): ff_rules = [] self.current_item = item self.page_sheet = None for sheet in sheets: if 'page_css' in sheet.id: ff_rules.extend(find_font_face_rules(sheet, self.oeb)) self.page_sheet = sheet base = {'font-family':['serif'], 'font-weight': '400', 'font-style':'normal', 'font-stretch':'normal'} for body in item.data.xpath('//*[local-name()="body"]'): self.find_usage_in(body, base, ff_rules)
def serialize(self, text_styles, fonts, embed_relationships, font_data_map): makeelement = self.namespace.makeelement font_families, seen = set(), set() for ts in text_styles: if ts.font_family: lf = ts.font_family.lower() if lf not in seen: seen.add(lf) font_families.add(ts.font_family) family_map = {} for family in sorted(font_families): family_map[family] = makeelement(fonts, 'w:font', w_name=family) embedded_fonts = [] for item in self.oeb.manifest: if item.media_type in OEB_STYLES and hasattr(item.data, 'cssRules'): embedded_fonts.extend(find_font_face_rules(item, self.oeb)) num = 0 face_map = defaultdict(set) rel_map = {} for ef in embedded_fonts: ff = ef['font-family'][0] if ff not in font_families: continue num += 1 bold = ef['weight'] > 400 italic = ef['font-style'] != 'normal' tag = 'Regular' if bold or italic: tag = 'Italic' if bold and italic: tag = 'BoldItalic' elif bold: tag = 'Bold' if tag in face_map[ff]: continue face_map[ff].add(tag) font = family_map[ff] key = uuid4() item = ef['item'] rid = rel_map.get(item) if rid is None: rel_map[item] = rid = 'rId%d' % num fname = 'fonts/font%d.odttf' % num makeelement(embed_relationships, 'Relationship', Id=rid, Type=self.namespace.names['EMBEDDED_FONT'], Target=fname) font_data_map['word/' + fname] = obfuscate_font_data(item.data, key) makeelement(font, 'w:embed' + tag, r_id=rid, w_fontKey='{%s}' % key.urn.rpartition(':')[-1].upper(), w_subsetted="true" if self.opts.subset_embedded_fonts else "false")
def do_embed(f): data = font_scanner.get_font_data(f) name = f['full_name'] ext = 'otf' if f['is_otf'] else 'ttf' name = ascii_filename(name).replace(' ', '-').replace('(', '').replace(')', '') fid, href = self.oeb.manifest.generate(id=u'font', href=u'fonts/%s.%s'%(name, ext)) item = self.oeb.manifest.add(fid, href, guess_type('dummy.'+ext)[0], data=data) item.unload_data_from_memory() page_sheet = self.get_page_sheet() href = page_sheet.relhref(item.href) css = '''@font-face { font-family: "%s"; font-weight: %s; font-style: %s; font-stretch: %s; src: url(%s) }''' % ( f['font-family'], f['font-weight'], f['font-style'], f['font-stretch'], href) sheet = self.parser.parseString(css, validate=False) page_sheet.data.insertRule(sheet.cssRules[0], len(page_sheet.data.cssRules)) return find_font_face_rules(sheet, self.oeb)[0]
def do_embed(f): data = font_scanner.get_font_data(f) name = f['full_name'] ext = 'otf' if f['is_otf'] else 'ttf' name = ascii_filename(name).replace(' ', '-').replace('(', '').replace(')', '') fid, href = self.oeb.manifest.generate(id=u'font', href=u'fonts/%s.%s'%(name, ext)) item = self.oeb.manifest.add(fid, href, guess_type('dummy.'+ext)[0], data=data) item.unload_data_from_memory() page_sheet = self.get_page_sheet() href = page_sheet.relhref(item.href) css = '''@font-face { font-family: "%s"; font-weight: %s; font-style: %s; font-stretch: %s; src: url(%s) }''' % ( f['font-family'], f['font-weight'], f['font-style'], f['font-stretch'], href) sheet = self.parser.parseString(css, validate=False) page_sheet.data.insertRule(sheet.cssRules[0], len(page_sheet.data.cssRules)) return find_font_face_rules(sheet, self.oeb)[0]
def embed_font(self, style): ff = [unicode(f) for f in style.get('font-family', []) if unicode(f).lower() not in { 'serif', 'sansserif', 'sans-serif', 'fantasy', 'cursive', 'monospace'}] if not ff: return ff = ff[0] if ff in self.warned or ff == 'inherit': return try: fonts = font_scanner.fonts_for_family(ff) except NoFonts: self.log.warn('Failed to find fonts for family:', ff, 'not embedding') self.warned.add(ff) return try: weight = int(style.get('font-weight', '400')) except (ValueError, TypeError, AttributeError): w = style['font-weight'] if w not in self.warned2: self.log.warn('Invalid weight in font style: %r' % w) self.warned2.add(w) return for f in fonts: if f['weight'] == weight and f['font-style'] == style.get('font-style', 'normal') and f['font-stretch'] == style.get('font-stretch', 'normal'): self.log('Embedding font %s from %s' % (f['full_name'], f['path'])) data = font_scanner.get_font_data(f) name = f['full_name'] ext = 'otf' if f['is_otf'] else 'ttf' name = ascii_filename(name).replace(' ', '-').replace('(', '').replace(')', '') fid, href = self.oeb.manifest.generate(id=u'font', href=u'fonts/%s.%s'%(name, ext)) item = self.oeb.manifest.add(fid, href, guess_type('dummy.'+ext)[0], data=data) item.unload_data_from_memory() page_sheet = self.get_page_sheet() href = page_sheet.relhref(item.href) css = '''@font-face { font-family: "%s"; font-weight: %s; font-style: %s; font-stretch: %s; src: url(%s) }''' % ( f['font-family'], f['font-weight'], f['font-style'], f['font-stretch'], href) sheet = self.parser.parseString(css, validate=False) page_sheet.data.insertRule(sheet.cssRules[0], len(page_sheet.data.cssRules)) return find_font_face_rules(sheet, self.oeb)[0]