def generate_annotation_html(self, bookmark): from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, NavigableString # Returns <div class="user_annotations"> ... </div> last_read_location = bookmark.last_read_location timestamp = datetime.datetime.utcfromtimestamp(bookmark.timestamp) percent_read = bookmark.percent_read ka_soup = BeautifulSoup() dtc = 0 divTag = Tag(ka_soup, 'div') divTag['class'] = 'user_annotations' # Add the last-read location spanTag = Tag(ka_soup, 'span') spanTag['style'] = 'font-weight:bold' if bookmark.book_format == 'pdf': spanTag.insert(0,NavigableString( _("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % \ dict(time=strftime(u'%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read))) else: spanTag.insert(0,NavigableString( _("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % \ dict(time=strftime(u'%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read))) divTag.insert(dtc, spanTag) dtc += 1 divTag.insert(dtc, Tag(ka_soup, 'br')) dtc += 1 if bookmark.user_notes: user_notes = bookmark.user_notes annotations = [] # Add the annotations sorted by location # Italicize highlighted text for location in sorted(user_notes): if user_notes[location]['text']: annotations.append( _('<b>Location %(dl)d • %(typ)s</b><br />%(text)s<br />') % \ dict(dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'], text=(user_notes[location]['text'] if \ user_notes[location]['type'] == 'Note' else \ '<i>%s</i>' % user_notes[location]['text']))) else: if bookmark.book_format == 'pdf': annotations.append( _('<b>Page %(dl)d • %(typ)s</b><br />') % \ dict(dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'])) else: annotations.append( _('<b>Location %(dl)d • %(typ)s</b><br />') % \ dict(dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'])) for annotation in annotations: divTag.insert(dtc, annotation) dtc += 1 ka_soup.insert(0, divTag) return ka_soup
def comments_to_html(comments): ''' Convert random comment text to normalized, xml-legal block of <p>s 'plain text' returns as <p>plain text</p> 'plain text with <i>minimal</i> <b>markup</b>' returns as <p>plain text with <i>minimal</i> <b>markup</b></p> '<p>pre-formatted text</p> returns untouched 'A line of text\n\nFollowed by a line of text' returns as <p>A line of text</p> <p>Followed by a line of text</p> 'A line of text.\nA second line of text.\rA third line of text' returns as <p>A line of text.<br />A second line of text.<br />A third line of text.</p> '...end of a paragraph.Somehow the break was lost...' returns as <p>...end of a paragraph.</p> <p>Somehow the break was lost...</p> Deprecated HTML returns as HTML via BeautifulSoup() ''' if not comments: return u'<p></p>' if not isinstance(comments, unicode): comments = comments.decode(preferred_encoding, 'replace') if comments.lstrip().startswith('<'): # Comment is already HTML do not mess with it return comments if '<' not in comments: comments = prepare_string_for_xml(comments) parts = [u'<p class="description">%s</p>'%x.replace(u'\n', u'<br />') for x in comments.split('\n\n')] return '\n'.join(parts) if sanitize_pat.search(comments) is not None: try: return sanitize_comments_html(comments) except: import traceback traceback.print_exc() return u'<p></p>' # Explode lost CRs to \n\n comments = lost_cr_exception_pat.sub(lambda m: m.group().replace('.', '.\r'), comments) for lost_cr in lost_cr_pat.finditer(comments): comments = comments.replace(lost_cr.group(), '%s%s\n\n%s' % (lost_cr.group(1), lost_cr.group(2), lost_cr.group(3))) comments = comments.replace(u'\r', u'') # Convert \n\n to <p>s comments = comments.replace(u'\n\n', u'<p>') # Convert solo returns to <br /> comments = comments.replace(u'\n', '<br />') # Convert two hyphens to emdash comments = comments.replace('--', '—') soup = BeautifulSoup(comments) result = BeautifulSoup() rtc = 0 open_pTag = False all_tokens = list(soup.contents) for token in all_tokens: if type(token) is NavigableString: if not open_pTag: pTag = Tag(result,'p') open_pTag = True ptc = 0 pTag.insert(ptc,prepare_string_for_xml(token)) ptc += 1 elif type(token) in (CData, Comment, Declaration, ProcessingInstruction): continue elif token.name in ['br', 'b', 'i', 'em', 'strong', 'span', 'font', 'a', 'hr']: if not open_pTag: pTag = Tag(result,'p') open_pTag = True ptc = 0 pTag.insert(ptc, token) ptc += 1 else: if open_pTag: result.insert(rtc, pTag) rtc += 1 open_pTag = False ptc = 0 result.insert(rtc, token) rtc += 1 if open_pTag: result.insert(rtc, pTag) for p in result.findAll('p'): p['class'] = 'description' for t in result.findAll(text=True): t.replaceWith(prepare_string_for_xml(unicode(t))) return result.renderContents(encoding=None)
def generate_annotation_html(self, bookmark): from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, NavigableString # Returns <div class="user_annotations"> ... </div> last_read_location = bookmark.last_read_location timestamp = datetime.datetime.utcfromtimestamp(bookmark.timestamp) percent_read = bookmark.percent_read ka_soup = BeautifulSoup() dtc = 0 divTag = Tag(ka_soup,'div') divTag['class'] = 'user_annotations' # Add the last-read location spanTag = Tag(ka_soup, 'span') spanTag['style'] = 'font-weight:bold' if bookmark.book_format == 'pdf': spanTag.insert(0,NavigableString( _("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % dict( time=strftime(u'%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read))) else: spanTag.insert(0,NavigableString( _("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % dict( time=strftime(u'%x', timestamp.timetuple()), loc=last_read_location, pr=percent_read))) divTag.insert(dtc, spanTag) dtc += 1 divTag.insert(dtc, Tag(ka_soup,'br')) dtc += 1 if bookmark.user_notes: user_notes = bookmark.user_notes annotations = [] # Add the annotations sorted by location # Italicize highlighted text for location in sorted(user_notes): if user_notes[location]['text']: annotations.append( _('<b>Location %(dl)d • %(typ)s</b><br />%(text)s<br />') % dict( dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'], text=(user_notes[location]['text'] if user_notes[location]['type'] == 'Note' else '<i>%s</i>' % user_notes[location]['text']))) else: if bookmark.book_format == 'pdf': annotations.append( _('<b>Page %(dl)d • %(typ)s</b><br />') % dict( dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'])) else: annotations.append( _('<b>Location %(dl)d • %(typ)s</b><br />') % dict( dl=user_notes[location]['displayed_location'], typ=user_notes[location]['type'])) for annotation in annotations: divTag.insert(dtc, annotation) dtc += 1 ka_soup.insert(0,divTag) return ka_soup
def generate_annotation_html(self, bookmark): from calibre.ebooks.BeautifulSoup import BeautifulSoup, Tag, NavigableString # Returns <div class="user_annotations"> ... </div> last_read_location = bookmark.last_read_location timestamp = datetime.datetime.utcfromtimestamp(bookmark.timestamp) percent_read = bookmark.percent_read ka_soup = BeautifulSoup() dtc = 0 divTag = Tag(ka_soup, "div") divTag["class"] = "user_annotations" # Add the last-read location spanTag = Tag(ka_soup, "span") spanTag["style"] = "font-weight:bold" if bookmark.book_format == "pdf": spanTag.insert( 0, NavigableString( _("%(time)s<br />Last Page Read: %(loc)d (%(pr)d%%)") % dict(time=strftime(u"%x", timestamp.timetuple()), loc=last_read_location, pr=percent_read) ), ) else: spanTag.insert( 0, NavigableString( _("%(time)s<br />Last Page Read: Location %(loc)d (%(pr)d%%)") % dict(time=strftime(u"%x", timestamp.timetuple()), loc=last_read_location, pr=percent_read) ), ) divTag.insert(dtc, spanTag) dtc += 1 divTag.insert(dtc, Tag(ka_soup, "br")) dtc += 1 if bookmark.user_notes: user_notes = bookmark.user_notes annotations = [] # Add the annotations sorted by location # Italicize highlighted text for location in sorted(user_notes): if user_notes[location]["text"]: annotations.append( _("<b>Location %(dl)d • %(typ)s</b><br />%(text)s<br />") % dict( dl=user_notes[location]["displayed_location"], typ=user_notes[location]["type"], text=( user_notes[location]["text"] if user_notes[location]["type"] == "Note" else "<i>%s</i>" % user_notes[location]["text"] ), ) ) else: if bookmark.book_format == "pdf": annotations.append( _("<b>Page %(dl)d • %(typ)s</b><br />") % dict(dl=user_notes[location]["displayed_location"], typ=user_notes[location]["type"]) ) else: annotations.append( _("<b>Location %(dl)d • %(typ)s</b><br />") % dict(dl=user_notes[location]["displayed_location"], typ=user_notes[location]["type"]) ) for annotation in annotations: divTag.insert(dtc, annotation) dtc += 1 ka_soup.insert(0, divTag) return ka_soup
def comments_to_html(comments): """ Convert random comment text to normalized, xml-legal block of <p>s 'plain text' returns as <p>plain text</p> 'plain text with <i>minimal</i> <b>markup</b>' returns as <p>plain text with <i>minimal</i> <b>markup</b></p> '<p>pre-formatted text</p> returns untouched 'A line of text\n\nFollowed by a line of text' returns as <p>A line of text</p> <p>Followed by a line of text</p> 'A line of text.\nA second line of text.\rA third line of text' returns as <p>A line of text.<br />A second line of text.<br />A third line of text.</p> '...end of a paragraph.Somehow the break was lost...' returns as <p>...end of a paragraph.</p> <p>Somehow the break was lost...</p> Deprecated HTML returns as HTML via BeautifulSoup() """ if not comments: return u"<p></p>" if not isinstance(comments, unicode): comments = comments.decode(preferred_encoding, "replace") if comments.lstrip().startswith("<"): # Comment is already HTML do not mess with it return comments if "<" not in comments: comments = prepare_string_for_xml(comments) parts = [u'<p class="description">%s</p>' % x.replace(u"\n", u"<br />") for x in comments.split("\n\n")] return "\n".join(parts) if sanitize_pat.search(comments) is not None: try: return sanitize_comments_html(comments) except: import traceback traceback.print_exc() return u"<p></p>" # Explode lost CRs to \n\n comments = lost_cr_exception_pat.sub(lambda m: m.group().replace(".", ".\r"), comments) for lost_cr in lost_cr_pat.finditer(comments): comments = comments.replace( lost_cr.group(), "%s%s\n\n%s" % (lost_cr.group(1), lost_cr.group(2), lost_cr.group(3)) ) comments = comments.replace(u"\r", u"") # Convert \n\n to <p>s comments = comments.replace(u"\n\n", u"<p>") # Convert solo returns to <br /> comments = comments.replace(u"\n", "<br />") # Convert two hyphens to emdash comments = comments.replace("--", "—") soup = BeautifulSoup(comments) result = BeautifulSoup() rtc = 0 open_pTag = False all_tokens = list(soup.contents) for token in all_tokens: if type(token) is NavigableString: if not open_pTag: pTag = Tag(result, "p") open_pTag = True ptc = 0 pTag.insert(ptc, prepare_string_for_xml(token)) ptc += 1 elif type(token) in (CData, Comment, Declaration, ProcessingInstruction): continue elif token.name in ["br", "b", "i", "em", "strong", "span", "font", "a", "hr"]: if not open_pTag: pTag = Tag(result, "p") open_pTag = True ptc = 0 pTag.insert(ptc, token) ptc += 1 else: if open_pTag: result.insert(rtc, pTag) rtc += 1 open_pTag = False ptc = 0 result.insert(rtc, token) rtc += 1 if open_pTag: result.insert(rtc, pTag) for p in result.findAll("p"): p["class"] = "description" for t in result.findAll(text=True): t.replaceWith(prepare_string_for_xml(unicode(t))) return result.renderContents(encoding=None)