def check_padding(self, style, stylizer): txt = '' left_padding_pts = 0 left_margin_pts = 0 if 'padding-left' in style.cssdict( ) and style['padding-left'] != 'auto': left_padding_pts = unit_convert(style['padding-left'], style.width, style.fontSize, stylizer.profile.dpi) if 'margin-left' in style.cssdict() and style['margin-left'] != 'auto': left_margin_pts = unit_convert(style['margin-left'], style.width, style.fontSize, stylizer.profile.dpi) left = left_margin_pts + left_padding_pts emleft = min(int(round(left / stylizer.profile.fbase)), self.MAX_EM) if emleft >= 1: txt += '(' * emleft right_padding_pts = 0 right_margin_pts = 0 if 'padding-right' in style.cssdict( ) and style['padding-right'] != 'auto': right_padding_pts = unit_convert(style['padding-right'], style.width, style.fontSize, stylizer.profile.dpi) if 'margin-right' in style.cssdict( ) and style['margin-right'] != 'auto': right_margin_pts = unit_convert(style['margin-right'], style.width, style.fontSize, stylizer.profile.dpi) right = right_margin_pts + right_padding_pts emright = min(int(round(right / stylizer.profile.fbase)), self.MAX_EM) if emright >= 1: txt += ')' * emright return txt
def _unit_convert(self, value, base=None, font=None): 'Return value in pts' if base is None: base = self.width if not font and font != 0: font = self.fontSize return unit_convert(value, base, font, self._profile.dpi, body_font_size=self._stylizer.body_font_size)
def whitespace(tag): lm = ti = 0.0 if tag.tag == 'p': ti = unit_convert('1.5em', 12, 500, 166) if tag.tag == 'blockquote': lm = unit_convert('2em', 12, 500, 166) lm = self.left_margins.get(tag, lm) ti = self.text_indents.get(tag, ti) try: lm = float(lm) except Exception: lm = 0.0 try: ti = float(ti) except Exception: ti = 0.0 return lm + ti
def store_page_margins(self): self.opts._stored_page_margins = {} for item, stylizer in self.stylizers.items(): margins = self.opts._stored_page_margins[item.href] = {} for prop, val in stylizer.page_rule.items(): p, w = prop.partition('-')[::2] if p == 'margin': margins[w] = unit_convert( val, stylizer.profile.width_pts, stylizer.body_font_size, stylizer.profile.dpi, body_font_size=stylizer.body_font_size)
def upshift_markup(self, root, image_name_map=None): self.log.debug('Converting style information to CSS...') image_name_map = image_name_map or {} size_map = { 'xx-small': '0.5', 'x-small': '1', 'small': '2', 'medium': '3', 'large': '4', 'x-large': '5', 'xx-large': '6', } def barename(x): return x.rpartition(':')[-1] mobi_version = self.book_header.mobi_version for x in root.xpath('//ncx'): x.getparent().remove(x) svg_tags = [] forwardable_anchors = [] pagebreak_anchors = [] BLOCK_TAGS = {'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'p'} for i, tag in enumerate(root.iter(etree.Element)): tag.attrib.pop('xmlns', '') for x in tag.attrib: if ':' in x: del tag.attrib[x] if tag.tag and barename(tag.tag) == 'svg': svg_tags.append(tag) if tag.tag and barename(tag.tag.lower()) in \ ('country-region', 'place', 'placetype', 'placename', 'state', 'city', 'street', 'address', 'content', 'form'): tag.tag = 'div' if tag.tag in ('content', 'form') else 'span' for key in tag.attrib.keys(): tag.attrib.pop(key) continue styles, attrib = [], tag.attrib if 'style' in attrib: style = attrib.pop('style').strip() if style: styles.append(style) if 'height' in attrib: height = attrib.pop('height').strip() if ( height and '<' not in height and '>' not in height and re.search(r'\d+', height)): if tag.tag in ('table', 'td', 'tr'): pass elif tag.tag == 'img': tag.set('height', height) else: if tag.tag == 'div' and not tag.text and \ (not tag.tail or not tag.tail.strip()) and \ not len(list(tag.iterdescendants())): # Paragraph spacer # Insert nbsp so that the element is never # discarded by a renderer tag.text = '\u00a0' # nbsp styles.append('height: %s' % self.ensure_unit(height)) else: styles.append('margin-top: %s' % self.ensure_unit(height)) if 'width' in attrib: width = attrib.pop('width').strip() if width and re.search(r'\d+', width): if tag.tag in ('table', 'td', 'tr'): pass elif tag.tag == 'img': tag.set('width', width) else: ewidth = self.ensure_unit(width) styles.append('text-indent: %s' % ewidth) try: ewidth_val = unit_convert(ewidth, 12, 500, 166) self.text_indents[tag] = ewidth_val except Exception: pass if width.startswith('-'): styles.append('margin-left: %s' % self.ensure_unit(width[1:])) try: ewidth_val = unit_convert(ewidth[1:], 12, 500, 166) self.left_margins[tag] = ewidth_val except Exception: pass if 'align' in attrib: align = attrib.pop('align').strip() if align: align = align.lower() if align == 'baseline': styles.append('vertical-align: '+align) else: styles.append('text-align: %s' % align) if tag.tag == 'hr': if mobi_version == 1: tag.tag = 'div' styles.append('page-break-before: always') styles.append('display: block') styles.append('margin: 0') elif tag.tag == 'i': tag.tag = 'span' tag.attrib['class'] = 'italic' elif tag.tag == 'u': tag.tag = 'span' tag.attrib['class'] = 'underline' elif tag.tag == 'b': tag.tag = 'span' tag.attrib['class'] = 'bold' elif tag.tag == 'font': sz = tag.get('size', '').lower() try: float(sz) except ValueError: if sz in list(size_map.keys()): attrib['size'] = size_map[sz] elif tag.tag == 'img': recindex = None for attr in self.IMAGE_ATTRS: recindex = attrib.pop(attr, None) or recindex if recindex is not None: try: recindex = int(recindex) except Exception: pass else: attrib['src'] = ('images/' + image_name_map.get(recindex, '%05d.jpg' % recindex)) for attr in ('width', 'height'): if attr in attrib: val = attrib[attr] if val.lower().endswith('em'): try: nval = float(val[:-2]) # Assume this was set using the Kindle profile nval *= 16 * (168.451/72) attrib[attr] = "%dpx" % int(nval) except Exception: del attrib[attr] elif val.lower().endswith('%'): del attrib[attr] elif tag.tag == 'pre': if not tag.text: tag.tag = 'div' if (attrib.get('class', None) == 'mbp_pagebreak' and tag.tag == 'div' and 'filepos-id' in attrib): pagebreak_anchors.append(tag) if 'color' in attrib: styles.append('color: ' + attrib.pop('color')) if 'bgcolor' in attrib: styles.append('background-color: ' + attrib.pop('bgcolor')) if 'filepos-id' in attrib: attrib['id'] = attrib.pop('filepos-id') if 'name' in attrib and attrib['name'] != attrib['id']: attrib['name'] = attrib['id'] if 'filepos' in attrib: filepos = attrib.pop('filepos') try: attrib['href'] = "#filepos%d" % int(filepos) except ValueError: pass if (tag.tag == 'a' and attrib.get('id', '').startswith('filepos') and not tag.text and len(tag) == 0 and (tag.tail is None or not tag.tail.strip()) and getattr(tag.getnext(), 'tag', None) in BLOCK_TAGS): # This is an empty anchor immediately before a block tag, move # the id onto the block tag instead forwardable_anchors.append(tag) if styles: ncls = None rule = '; '.join(styles) for sel, srule in self.tag_css_rules.items(): if srule == rule: ncls = sel break if ncls is None: ncls = 'calibre_%d' % i self.tag_css_rules[ncls] = rule cls = attrib.get('class', '') cls = cls + (' ' if cls else '') + ncls attrib['class'] = cls for tag in svg_tags: images = tag.xpath('descendant::img[@src]') parent = tag.getparent() if images and hasattr(parent, 'find'): index = parent.index(tag) for img in images: img.getparent().remove(img) img.tail = img.text = None parent.insert(index, img) if hasattr(parent, 'remove'): parent.remove(tag) for tag in pagebreak_anchors: anchor = tag.attrib['id'] del tag.attrib['id'] if 'name' in tag.attrib: del tag.attrib['name'] p = tag.getparent() a = p.makeelement('a') a.attrib['id'] = anchor p.insert(p.index(tag)+1, a) if getattr(a.getnext(), 'tag', None) in BLOCK_TAGS: forwardable_anchors.append(a) for tag in forwardable_anchors: block = tag.getnext() tag.getparent().remove(tag) if 'id' in block.attrib: tag.tail = block.text block.text = None block.insert(0, tag) else: block.attrib['id'] = tag.attrib['id'] # WebKit fails to navigate to anchors located on <br> tags for br in root.xpath('/body/br[@id]'): br.tag = 'div'