diff --git a/src/calibre/db/notes/exim.py b/src/calibre/db/notes/exim.py index b7a95a62aa..6c3db1bd0e 100644 --- a/src/calibre/db/notes/exim.py +++ b/src/calibre/db/notes/exim.py @@ -25,6 +25,10 @@ def parse_html(raw): def export_note(note_doc: str, get_resource) -> str: root = parse_html(note_doc) + return html.tostring(expand_note_resources(root, get_resource), encoding='unicode') + + +def expand_note_resources(root, get_resource): for img in root.xpath('//img[@src]'): img.attrib.pop('data-pre-import-src', None) try: @@ -38,8 +42,6 @@ def export_note(note_doc: str, get_resource) -> str: img.set('src', data_url(guess_type(x['name'])[0], x['data'])) img.set('data-filename', x['name']) - return html.tostring(root, encoding='unicode') - def import_note(shtml: str | bytes, basedir: str, add_resource) -> tuple[str, str, set[str]]: shtml = xml_to_unicode(shtml, strip_encoding_pats=True, assume_utf8=True)[0] diff --git a/src/calibre/ebooks/oeb/transforms/jacket.py b/src/calibre/ebooks/oeb/transforms/jacket.py index 0add5bfb72..91d21f7ab3 100644 --- a/src/calibre/ebooks/oeb/transforms/jacket.py +++ b/src/calibre/ebooks/oeb/transforms/jacket.py @@ -375,6 +375,13 @@ def render_jacket(mi, output_profile, else: val = comments_to_html(val) args[dkey] = val + elif dt == 'composite': + val = val or '' + # if the column is marked as containing html, use it + # unchanged. Otherwise treat it as a comment. + if not m.get('display', {}).get('contains_html', False): + val = comments_to_html(val) + args[dkey] = val else: args[dkey] = escape(val) args[dkey+'_label'] = escape(display_name) diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 20c85e5404..85254bfe35 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -20,11 +20,13 @@ from contextlib import suppress from datetime import datetime, timedelta from enum import Enum, auto from functools import partial +from lxml import html from math import ceil, floor, modf, trunc from calibre import human_readable, prepare_string_for_xml, prints from calibre.constants import DEBUG from calibre.db.constants import DATA_DIR_NAME, DATA_FILE_PATTERN +from calibre.db.notes.exim import parse_html, expand_note_resources from calibre.ebooks.metadata import title_sort from calibre.utils.config import tweaks from calibre.utils.date import UNDEFINED_DATE, format_date, now, parse_date @@ -2581,14 +2583,34 @@ class BuiltinGetNote(BuiltinFormatterFunction): def evaluate(self, formatter, kwargs, mi, locals, field_name, field_value, plain_text): db = self.get_database(mi).new_api try: + note = '' item_id = db.get_item_id(field_name, field_value) if item_id is not None: - note = db.notes_data_for(field_name, item_id) - if note is not None: - if plain_text: + note_data = db.notes_data_for(field_name, item_id) + if note_data is not None: + if plain_text == '1': return note['searchable_text'].partition('\n')[2] - return note['doc'] - return '' + # Return the full HTML of the note, including all images as + # data: URLs. Reason: non-exported note html contains + # "calres://" URLs for images. These images won't render + # outside the context of the library where the note "lives". + # For example, they don't work in book jackets and book + # details from a different library. They also don't work in + # tooltips. + + # This code depends on the note being wrapped in
tags + # by parse_html. The body is changed to a