Your message dated Sat, 16 May 2026 10:23:16 +0000
with message-id <[email protected]>
and subject line Released with 13.5
has caused the Debian Bug report #1127428,
regarding trixie-pu: package calibre/8.5.0+ds-1+deb13u2
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1127428: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1127428
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:calibre
User: [email protected]
Usertags: pu

[ Reason ]
Fix these CVEs.
* CVE-2026-25635
* CVE-2026-25636
* CVE-2026-25731

[ Impact ]
* CVE-2026-25635: path traversal vulnerability
* CVE-2026-25636: path traversal vulnerability
* CVE-2026-25731: Server-Side Template Injection (SSTI) vulnerability

[ Tests ]
Automated build-time test was successful.

[ Risks ]
Not well tested on real machine.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Backport and apply upstream fixes.

[ Other info ]
Fixes are examine from online:
  https://github.com/debian-
calibre/calibre/compare/debian/8.5.0+ds-1+deb13u1...debian/trixie
diff -Nru calibre-8.5.0+ds/debian/calibre.install 
calibre-8.5.0+ds/debian/calibre.install
--- calibre-8.5.0+ds/debian/calibre.install     2025-09-22 00:51:57.000000000 
+0900
+++ calibre-8.5.0+ds/debian/calibre.install     2026-02-08 23:10:50.000000000 
+0900
@@ -7,7 +7,6 @@
 #
 usr/lib/calibre/odf
 usr/lib/calibre/qt
-usr/lib/calibre/templite
 usr/lib/calibre/tinycss
 usr/lib/calibre/css_selectors
 usr/lib/calibre/polyglot
diff -Nru calibre-8.5.0+ds/debian/changelog calibre-8.5.0+ds/debian/changelog
--- calibre-8.5.0+ds/debian/changelog   2025-11-09 16:06:24.000000000 +0900
+++ calibre-8.5.0+ds/debian/changelog   2026-02-08 23:19:22.000000000 +0900
@@ -1,3 +1,12 @@
+calibre (8.5.0+ds-1+deb13u2) trixie; urgency=medium
+
+  * CVE-2026-25635: CHM Input: Ignore internal files that have paths that end 
up outside the container
+  * CVE-2026-25636: DRYer
+  * CVE-2026-25731: ZIP Output: Change the template engine used for HTML 
templating from templite to Mustache, for greater safety and performance. Note 
that this is a breaking change if you use custom templates with ZIP output.
+  * Use pystache instead of templite to fix CVE-2026-25731
+
+ -- YOKOTA Hiroshi <[email protected]>  Sun, 08 Feb 2026 23:19:22 +0900
+
 calibre (8.5.0+ds-1+deb13u1) trixie; urgency=medium
 
   * Fix CVE-2025-64486
diff -Nru calibre-8.5.0+ds/debian/control calibre-8.5.0+ds/debian/control
--- calibre-8.5.0+ds/debian/control     2025-11-09 16:06:24.000000000 +0900
+++ calibre-8.5.0+ds/debian/control     2026-02-08 23:06:45.000000000 +0900
@@ -72,6 +72,7 @@
     python3-pyqt6.qttexttospeech,
     python3-pyqt6.qtwebengine,
     python3-pyqtbuild,
+    python3-pystache,
     python3-pyzstd,
     python3-regex,
     python3-requests-toolbelt,
diff -Nru calibre-8.5.0+ds/debian/patches/series 
calibre-8.5.0+ds/debian/patches/series
--- calibre-8.5.0+ds/debian/patches/series      2025-11-09 16:06:24.000000000 
+0900
+++ calibre-8.5.0+ds/debian/patches/series      2026-02-08 23:06:17.000000000 
+0900
@@ -80,3 +80,6 @@
 pykakasi/0080-Revert-Fix-a-regression-that-caused-incorrect-Englis.patch
 0081-Revert-Update-7zip-wrapper-code-for-removal-of-read-.patch
 upstream/0082-Fix-CVE-2025-64486.patch
+upstream/0083-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
+upstream/0084-CVE-2026-25636-DRYer.patch
+upstream/0085-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
diff -Nru 
calibre-8.5.0+ds/debian/patches/upstream/0083-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
 
calibre-8.5.0+ds/debian/patches/upstream/0083-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
--- 
calibre-8.5.0+ds/debian/patches/upstream/0083-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
    1970-01-01 09:00:00.000000000 +0900
+++ 
calibre-8.5.0+ds/debian/patches/upstream/0083-CVE-2026-25635-CHM-Input-Ignore-internal-files-that-.patch
    2026-02-08 23:06:17.000000000 +0900
@@ -0,0 +1,81 @@
+From: Kovid Goyal <[email protected]>
+Date: Wed, 4 Feb 2026 09:39:54 +0530
+Subject: CVE-2026-25635: CHM Input: Ignore internal files that have paths
+ that end up outside the container
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-32vh-whvh-9fxr
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/9739232fcb029ac15dfe52ccd4fdb4a07ebb6ce9
+
+Also, allow extraction of long filenames
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ src/calibre/ebooks/chm/reader.py | 33 ++++++++++++++++-----------------
+ 1 file changed, 16 insertions(+), 17 deletions(-)
+
+diff --git a/src/calibre/ebooks/chm/reader.py 
b/src/calibre/ebooks/chm/reader.py
+index d456171..397ef02 100644
+--- a/src/calibre/ebooks/chm/reader.py
++++ b/src/calibre/ebooks/chm/reader.py
+@@ -15,6 +15,7 @@ from calibre.constants import filesystem_encoding, iswindows
+ from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString
+ from calibre.ebooks.chardet import xml_to_unicode
+ from calibre.ebooks.metadata.toc import TOC
++from calibre.utils.filenames import make_long_path_useable
+ from polyglot.builtins import as_unicode
+ 
+ 
+@@ -181,37 +182,35 @@ class CHMReader(CHMFile):
+ 
+     def ExtractFiles(self, output_dir=os.getcwd(), debug_dump=False):
+         html_files = set()
++        base = output_dir = os.path.abspath(output_dir)
++        if not base.endswith(os.sep):
++            base += os.sep
+         for path in self.Contents():
+-            fpath = path
+-            lpath = os.path.join(output_dir, fpath)
++            fpath = path.partition(';')[0]  # fix file names with ";<junk>" 
at the end, see _reformat()
++            fpath = fpath.replace('/', os.sep)
++            lpath = os.path.abspath(os.path.join(output_dir, fpath))
++            if os.path.commonprefix((lpath, base)) != base:
++                self.log.warn(f'{path!r} outside container, skipping')
++                continue
+             self._ensure_dir(lpath)
+             try:
+                 data = self.GetFile(path)
+             except:
+                 self.log.exception(f'Failed to extract {path} from CHM, 
ignoring')
+                 continue
+-            if lpath.find(';') != -1:
+-                # fix file names with ";<junk>" at the end, see _reformat()
+-                lpath = lpath.split(';')[0]
++            with open(make_long_path_useable(lpath), 'wb') as f:
++                f.write(data)
+             try:
+-                with open(lpath, 'wb') as f:
+-                    f.write(data)
+-                try:
+-                    if 'html' in guess_mimetype(path)[0]:
+-                        html_files.add(lpath)
+-                except:
+-                    pass
++                if 'html' in guess_mimetype(os.path.basename(lpath))[0]:
++                    html_files.add(lpath)
+             except:
+-                if iswindows and len(lpath) > 250:
+-                    self.log.warn(f'{path!r} filename too long, skipping')
+-                    continue
+-                raise
++                pass
+ 
+         if debug_dump:
+             import shutil
+             shutil.copytree(output_dir, os.path.join(debug_dump, 
'debug_dump'))
+         for lpath in html_files:
+-            with open(lpath, 'r+b') as f:
++            with open(make_long_path_useable(lpath), 'r+b') as f:
+                 data = f.read()
+                 data = self._reformat(data, lpath)
+                 if isinstance(data, str):
diff -Nru 
calibre-8.5.0+ds/debian/patches/upstream/0084-CVE-2026-25636-DRYer.patch 
calibre-8.5.0+ds/debian/patches/upstream/0084-CVE-2026-25636-DRYer.patch
--- calibre-8.5.0+ds/debian/patches/upstream/0084-CVE-2026-25636-DRYer.patch    
1970-01-01 09:00:00.000000000 +0900
+++ calibre-8.5.0+ds/debian/patches/upstream/0084-CVE-2026-25636-DRYer.patch    
2026-02-08 23:06:17.000000000 +0900
@@ -0,0 +1,37 @@
+From: Kovid Goyal <[email protected]>
+Date: Mon, 2 Feb 2026 11:25:09 +0530
+Subject: CVE-2026-25636: DRYer
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-8r26-m7j5-hm29
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/9484ea82c6ab226c18e6ca5aa000fa16de598726
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ src/calibre/ebooks/conversion/plugins/epub_input.py | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/src/calibre/ebooks/conversion/plugins/epub_input.py 
b/src/calibre/ebooks/conversion/plugins/epub_input.py
+index 2505169..edfebee 100644
+--- a/src/calibre/ebooks/conversion/plugins/epub_input.py
++++ b/src/calibre/ebooks/conversion/plugins/epub_input.py
+@@ -66,15 +66,17 @@ class EPUBInput(InputFormatPlugin):
+ 
+         try:
+             root = etree.parse(encfile)
++            base = os.path.dirname(encfile)
++            container_base = os.path.dirname(base)
+             for em in root.xpath('descendant::*[contains(name(), 
"EncryptionMethod")]'):
+                 algorithm = em.get('Algorithm', '')
+                 if algorithm not in {ADOBE_OBFUSCATION, IDPF_OBFUSCATION}:
+                     return False
+                 cr = em.getparent().xpath('descendant::*[contains(name(), 
"CipherReference")]')[0]
+                 uri = cr.get('URI')
+-                path = os.path.abspath(os.path.join(os.path.dirname(encfile), 
'..', *uri.split('/')))
++                path = os.path.abspath(os.path.join(base, '..', 
*uri.split('/')))
+                 tkey = (key if algorithm == ADOBE_OBFUSCATION else idpf_key)
+-                if (tkey and os.path.exists(path)):
++                if (tkey and is_existing_subpath(path, container_base)):
+                     self._encrypted_font_uris.append(uri)
+                     decrypt_font(tkey, path, algorithm)
+             return True
diff -Nru 
calibre-8.5.0+ds/debian/patches/upstream/0085-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
 
calibre-8.5.0+ds/debian/patches/upstream/0085-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
--- 
calibre-8.5.0+ds/debian/patches/upstream/0085-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
    1970-01-01 09:00:00.000000000 +0900
+++ 
calibre-8.5.0+ds/debian/patches/upstream/0085-CVE-2026-25731-ZIP-Output-Change-the-template-engine.patch
    2026-02-08 23:06:17.000000000 +0900
@@ -0,0 +1,588 @@
+From: Kovid Goyal <[email protected]>
+Date: Thu, 5 Feb 2026 14:21:25 +0530
+Subject: CVE-2026-25731: ZIP Output: Change the template engine used for HTML
+ templating from templite to Mustache,
+ for greater safety and performance. Note that this is a breaking change if
+ you use custom templates with ZIP output.
+
+Forwarded: not-needed
+Bug: 
https://github.com/kovidgoyal/calibre/security/advisories/GHSA-xrh9-w7qx-3gcc
+Origin: backport, 
https://github.com/kovidgoyal/calibre/commit/f0649b27512e987b95fcab2e1e0a3bcdafc23379
+
+Signed-off-by: YOKOTA Hiroshi <[email protected]>
+---
+ COPYRIGHT                                          |  6 --
+ pyproject.toml                                     |  5 +-
+ resources/templates/html_export_default.mustache   | 70 ++++++++++++++++
+ resources/templates/html_export_default.tmpl       | 74 -----------------
+ .../templates/html_export_default_index.mustache   | 55 +++++++++++++
+ resources/templates/html_export_default_index.tmpl | 61 --------------
+ .../ebooks/conversion/plugins/html_output.py       | 61 ++++++++------
+ src/templite/__init__.py                           | 96 ----------------------
+ 8 files changed, 164 insertions(+), 264 deletions(-)
+ create mode 100644 resources/templates/html_export_default.mustache
+ delete mode 100644 resources/templates/html_export_default.tmpl
+ create mode 100644 resources/templates/html_export_default_index.mustache
+ delete mode 100644 resources/templates/html_export_default_index.tmpl
+ delete mode 100644 src/templite/__init__.py
+
+diff --git a/COPYRIGHT b/COPYRIGHT
+index a44d756..09e1308 100644
+--- a/COPYRIGHT
++++ b/COPYRIGHT
+@@ -12,12 +12,6 @@ Files: resources/rapydscript/*
+ Copyright: Various
+ License: BSD
+ 
+-Files: src/templite/*
+-Copyright: Copyright (c) 2009 joonis new media, Thimo Kraemer
+-License: GPL-2+
+- The full text of the GPL is distributed as in
+- /usr/share/common-licenses/GPL-2 on Debian systems.
+-
+ Files: src/calibre/devices/bambook/*
+ Copyright: 2010, Li Fanxi
+ License: GPL-3
+diff --git a/pyproject.toml b/pyproject.toml
+index b3697bd..f7ef5cb 100644
+--- a/pyproject.toml
++++ b/pyproject.toml
+@@ -26,7 +26,6 @@ exclude = [
+     "setup/linux-installer.py",
+     "src/css_selectors/*",
+     "src/polyglot/*",
+-    "src/templite/*",
+     "src/tinycss/*",
+ ]
+ preview = true
+@@ -71,7 +70,7 @@ unfixable = ['PIE794', 'ISC001']
+ detect-same-package = true
+ extra-standard-library = ["aes", "elementmaker", "encodings"]
+ known-first-party = ["calibre_extensions", "calibre_plugins", "polyglot"]
+-known-third-party = ["odf", "qt", "templite", "tinycss", "css_selectors"]
++known-third-party = ["odf", "qt", "tinycss", "css_selectors"]
+ relative-imports-order = "closest-to-furthest"
+ split-on-trailing-comma = false
+ section-order = ['__python__', "future", "standard-library", "third-party", 
"first-party", "local-folder"]
+@@ -189,7 +188,6 @@ skip = [
+     "./setup/linux-installer.py",
+     "./src/css_selectors/*",
+     "./src/polyglot/*",
+-    "./src/templite/*",
+     "./src/tinycss/*",
+     "./src/unicode_names/*",
+ ]
+@@ -205,7 +203,6 @@ exclude = [
+     "src/calibre/gui2/store/stores/",
+     "src/css_selectors/",
+     "src/polyglot/",
+-    "src/templite/",
+     "src/tinycss/",
+ ]
+ 
+diff --git a/resources/templates/html_export_default.mustache 
b/resources/templates/html_export_default.mustache
+new file mode 100644
+index 0000000..1c8691a
+--- /dev/null
++++ b/resources/templates/html_export_default.mustache
+@@ -0,0 +1,70 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
++<html xmlns="http://www.w3.org/1999/xhtml";>
++<head>
++{{{head_content}}}
++
++<link href="{{css_link}}" type="text/css" rel="stylesheet" />
++
++</head>
++<body>
++
++<div class="calibreMeta">
++  <div class="calibreMetaTitle">
++  {{#meta.titles}}
++      {{#is_first}}
++              <h1><a href="{{toc_url}}">{{title}}</a> </h1>
++      {{/is_first}}
++      {{^is_first}}
++              <div class="calibreMetaSubtitle">{{title}}</div>
++      {{/is_first}}
++  {{/meta.titles}}
++  </div>
++  <div class="calibreMetaAuthor">{{meta.creators}}</div>
++</div>
++
++<div class="calibreMain">
++
++  <div class="calibreEbookContent">
++    {{#has_link}}
++      <div class="calibreEbNavTop">
++          {{#prev_link}}
++          <a href="{{prev_link}}" class="calibreAPrev">{{prev_page}}</a>
++              {{/prev_link}}
++        {{^prev_link}}
++          <a href="{{toc_url}}" class="calibreAPrev">{{prev_page}}</a>
++              {{/prev_link}}
++              {{#next_link}}
++          <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++              {{/next_link}}
++      </div>
++      {{/has_link}}
++
++      {{{ebook_content}}}
++  </div>
++
++  {{#has_toc}}
++  <div class="calibreToc">
++    <h2><a href="{{toc_url}}">{{table_of_contents}}</a></h2>
++      {{{toc}}}
++  </div>
++  {{/has_toc}}
++
++  <div class="calibreEbNav">
++      {{#prev_link}}
++              <a href="{{prev_link}}" class="calibreAPrev">{{prev_page}}</a>
++      {{/prev_link}}
++      {{^prev_link}}
++              <a href="{{toc_url}}" class="calibreAPrev">{{prev_page}}</a>
++      {{/prev_link}}
++
++    <a href="{{toc_url}}" class="calibreAHome">{{start}}</a>
++
++      {{#next_link}}
++              <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++      {{/next_link}}
++  </div>
++
++</div>
++
++</body>
++</html>
+diff --git a/resources/templates/html_export_default.tmpl 
b/resources/templates/html_export_default.tmpl
+deleted file mode 100644
+index 7aac247..0000000
+--- a/resources/templates/html_export_default.tmpl
++++ /dev/null
+@@ -1,74 +0,0 @@
+-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+-<html xmlns="http://www.w3.org/1999/xhtml";>
+-<head>
+-${head_content}$
+-
+-<link href="${cssLink}$" type="text/css" rel="stylesheet" />
+-
+-</head>
+-<body>
+-
+-<div class="calibreMeta">
+-  <div class="calibreMetaTitle">
+-  ${pos1=1}$
+-  ${for title in meta.titles():}$
+-    ${if pos1:}$
+-    <h1>
+-      <a href="${tocUrl}$">${print(title)}$</a>
+-    </h1>
+-    ${:else:}$
+-    <div class="calibreMetaSubtitle">${print(title)}$</div>
+-    ${:endif}$
+-    ${pos1=0}$
+-  ${:endfor}$
+-  </div>
+-  <div class="calibreMetaAuthor">
+-    ${print(', '.join(meta.creators()))}$
+-  </div>
+-</div>
+-
+-<div class="calibreMain">
+-
+-  <div class="calibreEbookContent">
+-    ${if prevLink or nextLink:}$
+-      <div class="calibreEbNavTop">
+-        ${if prevLink:}$
+-          <a href="${prevLink}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-        ${:else:}$
+-          <a href="${tocUrl}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-        ${:endif}$
+-
+-        ${if nextLink:}$
+-          <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-        ${:endif}$
+-      </div>
+-    ${:endif}$
+-
+-    ${ebookContent}$
+-  </div>
+-
+-  ${if has_toc:}$
+-  <div class="calibreToc">
+-    <h2><a href="${tocUrl}$">${print( _('Table of contents'))}$</a></h2>
+-    ${print(toc())}$
+-  </div>
+-  ${:endif}$
+-
+-  <div class="calibreEbNav">
+-    ${if prevLink:}$
+-      <a href="${prevLink}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-    ${:else:}$
+-      <a href="${tocUrl}$" class="calibreAPrev">${print(_('previous 
page'))}$</a>
+-    ${:endif}$
+-
+-    <a href="${tocUrl}$" class="calibreAHome">${print(_('start'))}$</a>
+-
+-    ${if nextLink:}$
+-      <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-    ${:endif}$
+-  </div>
+-
+-</div>
+-
+-</body>
+-</html>
+diff --git a/resources/templates/html_export_default_index.mustache 
b/resources/templates/html_export_default_index.mustache
+new file mode 100644
+index 0000000..aa1bc4d
+--- /dev/null
++++ b/resources/templates/html_export_default_index.mustache
+@@ -0,0 +1,55 @@
++<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
++<html xmlns="http://www.w3.org/1999/xhtml";>
++<head>
++<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
++
++<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"; />
++<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/"; />
++
++<title>{{meta.creators}} - {{meta.first_title}}</title>
++
++{{#meta.items}}
++  <meta name="DC.{{name}}" content="{{value}}" />
++{{/meta.items}}
++
++<link href="{{css_link}}" type="text/css" rel="stylesheet" />
++</head>
++<body>
++
++<div class="calibreMeta">
++  <div class="calibreMetaTitle">
++  {{#meta.titles}}
++      {{#is_first}}
++              <h1><a href="{{toc_url}}">{{title}}</a> </h1>
++      {{/is_first}}
++      {{^is_first}}
++              <div class="calibreMetaSubtitle">{{title}}</div>
++      {{/is_first}}
++  {{/meta.titles}}
++  </div>
++  <div class="calibreMetaAuthor">{{meta.creators}}</div>
++</div>
++
++<div class="calibreMain">
++  <div class="calibreEbookContent">
++    {{#has_toc}}
++      <div class="calibreTocIndex">
++        <h2>{{table_of_contents}}</h2>
++              {{{toc}}}
++      </div>
++    {{/has_toc}}
++    {{^has_toc}}
++        <h2>{{no_toc}}</h2>
++        <div><strong><a 
href="{{next_link}}">{{begin_to_read}}</a></strong></div>
++    {{/has_toc}}
++  </div>
++
++  <div class="calibreEbNav">
++    {{#next_link}}
++      <a href="{{next_link}}" class="calibreANext">{{next_page}}</a>
++    {{/next_link}}
++  </div>
++</div>
++
++</body>
++</html>
+diff --git a/resources/templates/html_export_default_index.tmpl 
b/resources/templates/html_export_default_index.tmpl
+deleted file mode 100644
+index f0665ad..0000000
+--- a/resources/templates/html_export_default_index.tmpl
++++ /dev/null
+@@ -1,61 +0,0 @@
+-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+-<html xmlns="http://www.w3.org/1999/xhtml";>
+-<head>
+-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+-
+-<link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"; />
+-<link rel="schema.DCTERMS" href="http://purl.org/dc/terms/"; />
+-
+-<title>${print(', '.join(meta.creators()))}$ - ${print(next(meta.titles())); 
print(meta.titles().close())}$</title>
+-
+-${for item in meta:}$
+-  <meta ${print('name="DC.'+item['name']+'"')}$ 
${print('content="'+item['value']+'"')}$ />
+-${:endfor}$
+-
+-<link href="${cssLink}$" type="text/css" rel="stylesheet" />
+-</head>
+-<body>
+-
+-<div class="calibreMeta">
+-  <div class="calibreMetaTitle">
+-  ${pos1=1}$
+-  ${for title in meta.titles():}$
+-    ${if pos1:}$
+-    <h1>
+-      <a href="${tocUrl}$">${print(title)}$</a>
+-    </h1>
+-    ${:else:}$
+-    <div class="calibreMetaSubtitle">${print(title)}$</div>
+-    ${:endif}$
+-    ${pos1=0}$
+-  ${:endfor}$
+-  </div>
+-  <div class="calibreMetaAuthor">
+-    ${print(', '.join(meta.creators()))}$
+-  </div>
+-</div>
+-
+-<div class="calibreMain">
+-  <div class="calibreEbookContent">
+-
+-    ${if has_toc:}$
+-      <div class="calibreTocIndex">
+-        <h2>${print(_('Table of contents'))}$</h2>
+-        ${toc}$
+-      </div>
+-    ${:else:}$
+-        <h2>${print(_('No table of contents present'))}$</h2>
+-        <div><strong><a href="${nextLink}$">${print(_('begin to 
read'))}$</a></strong></div>
+-    ${:endif}$
+-
+-  </div>
+-
+-  <div class="calibreEbNav">
+-    ${if nextLink:}$
+-      <a href="${nextLink}$" class="calibreANext">${print(_('next 
page'))}$</a>
+-    ${:endif}$
+-  </div>
+-</div>
+-
+-</body>
+-</html>
+diff --git a/src/calibre/ebooks/conversion/plugins/html_output.py 
b/src/calibre/ebooks/conversion/plugins/html_output.py
+index ea64c70..5573d04 100644
+--- a/src/calibre/ebooks/conversion/plugins/html_output.py
++++ b/src/calibre/ebooks/conversion/plugins/html_output.py
+@@ -27,13 +27,13 @@ class HTMLOutput(OutputFormatPlugin):
+ 
+     options = {
+         OptionRecommendation(name='template_css',
+-            help=_('CSS file used for the output instead of the default 
file')),
++            help=_('CSS file used for the output instead of the default 
CSS.')),
+ 
+         OptionRecommendation(name='template_html_index',
+-            help=_('Template used for generation of the HTML index file 
instead of the default file')),
++            help=_('Template used for generation of the HTML index file 
instead of the default template. In Mustache format.')),
+ 
+         OptionRecommendation(name='template_html',
+-            help=_('Template used for the generation of the HTML contents of 
the book instead of the default file')),
++            help=_('Template used for the generation of the HTML contents of 
the book instead of the default template. In Mustache format.')),
+ 
+         OptionRecommendation(name='extract_to',
+             help=_('Extract the contents of the generated ZIP file to the '
+@@ -85,8 +85,8 @@ class HTMLOutput(OutputFormatPlugin):
+                 xml_declaration=False)
+ 
+     def convert(self, oeb_book, output_path, input_plugin, opts, log):
++        import pystache
+         from lxml import etree
+-        from templite import Templite
+ 
+         from calibre.ebooks.html.meta import EasyMeta
+         from calibre.utils import zipfile
+@@ -97,7 +97,7 @@ class HTMLOutput(OutputFormatPlugin):
+             with open(opts.template_html_index, 'rb') as f:
+                 template_html_index_data = f.read()
+         else:
+-            template_html_index_data = 
P('templates/html_export_default_index.tmpl', data=True)
++            template_html_data = P('templates/html_export_default.mustache', 
data=True)
+ 
+         if opts.template_html is not None:
+             with open(opts.template_html, 'rb') as f:
+@@ -111,9 +111,10 @@ class HTMLOutput(OutputFormatPlugin):
+         else:
+             template_css_data = P('templates/html_export_default.css', 
data=True)
+ 
+-        template_html_index_data = template_html_index_data.decode('utf-8')
+-        template_html_data = template_html_data.decode('utf-8')
++        template_html_index = 
pystache.parse(template_html_index_data.decode('utf-8'))
++        template_html = pystache.parse(template_html_data.decode('utf-8'))
+         template_css_data = template_css_data.decode('utf-8')
++        has_toc = bool(oeb_book.toc.count())
+ 
+         self.log  = log
+         self.opts = opts
+@@ -130,18 +131,31 @@ class HTMLOutput(OutputFormatPlugin):
+         css_path = output_dir+os.sep+'calibreHtmlOutBasicCss.css'
+         with open(css_path, 'wb') as f:
+             f.write(template_css_data.encode('utf-8'))
++        meta_dict = {
++            'titles': [{'title': x, 'is_first': i == 0} for i, x in 
enumerate(meta.titles())],
++            'creators': authors_to_string(tuple(meta.creators())),
++            'items': list(meta),
++        }
++        meta_dict['first_title'] = meta_dict['titles'][0]['title'] if 
meta_dict['titles'] else ''
++        basic_template_vars = {
++                'meta': meta_dict, 'has_toc': has_toc,
++                'table_of_contents':  _('Table of contents'), 'no_toc': _('No 
table of contents present'),
++                'begin_to_read': _('begin to read'), 'start': _('start'),
++                'prev_page': _('previous page'), 'next_page': _('next page'),
++        }
+ 
+         with open(output_file, 'wb') as f:
+-            html_toc = self.generate_html_toc(oeb_book, output_file, 
output_dir)
+-            templite = Templite(template_html_index_data)
+             nextLink = oeb_book.spine[0].href
+             nextLink = relpath(output_dir+os.sep+nextLink, 
dirname(output_file))
+             cssLink = relpath(abspath(css_path), dirname(output_file))
+             tocUrl = relpath(output_file, dirname(output_file))
+-            t = templite.render(has_toc=bool(oeb_book.toc.count()),
+-                    toc=html_toc, meta=meta, nextLink=nextLink,
+-                    tocUrl=tocUrl, cssLink=cssLink,
+-                    firstContentPageLink=nextLink)
++            toc_as_html = self.generate_html_toc(oeb_book, output_file, 
output_dir) if has_toc else ''
++            v = basic_template_vars.copy()
++            v.update({
++                'toc': toc_as_html, 'css_link': cssLink, 'toc_url': tocUrl, 
'next_link': nextLink,
++                'first_content_page_link': nextLink,
++            })
++            t = pystache.render(template_html_index, v)
+             if isinstance(t, str):
+                 t = t.encode('utf-8')
+             f.write(t)
+@@ -197,17 +211,18 @@ class HTMLOutput(OutputFormatPlugin):
+                 firstContentPageLink = oeb_book.spine[0].href
+ 
+                 # render template
+-                templite = Templite(template_html_data)
+-
+                 def toc():
+-                    return self.generate_html_toc(oeb_book, path, output_dir)
+-                t = templite.render(ebookContent=ebook_content,
+-                        prevLink=prevLink, nextLink=nextLink,
+-                        has_toc=bool(oeb_book.toc.count()), toc=toc,
+-                        tocUrl=tocUrl, head_content=head_content,
+-                        meta=meta, cssLink=cssLink,
+-                        firstContentPageLink=firstContentPageLink)
+-
++                    return
++                toc_as_html = self.generate_html_toc(oeb_book, path, 
output_dir) if has_toc else ''
++                v = basic_template_vars.copy()
++                v.update({
++                    'has_link': prevLink or nextLink,
++                    'prev_link': prevLink, 'next_link': nextLink, 'toc_url': 
tocUrl,
++                    'head_content': head_content, 'ebook_content': 
ebook_content,
++                    'css_link': cssLink, 'toc': toc_as_html,
++                    'first_content_page_link': firstContentPageLink,
++                })
++                t = pystache.render(template_html, v)
+                 # write html to file
+                 with open(path, 'wb') as f:
+                     f.write(t.encode('utf-8'))
+diff --git a/src/templite/__init__.py b/src/templite/__init__.py
+deleted file mode 100644
+index 8723d0d..0000000
+--- a/src/templite/__init__.py
++++ /dev/null
+@@ -1,96 +0,0 @@
+-#!/usr/bin/env python
+-#
+-#       Templite+
+-#       A light-weight, fully functional, general purpose templating engine
+-#
+-#       Copyright (c) 2009 joonis new media
+-#       Author: Thimo Kraemer <[email protected]>
+-#
+-#       Based on Templite - Tomer Filiba
+-#       http://code.activestate.com/recipes/496702/
+-#
+-#       This program is free software; you can redistribute it and/or modify
+-#       it under the terms of the GNU General Public License as published by
+-#       the Free Software Foundation; either version 2 of the License, or
+-#       (at your option) any later version.
+-#
+-#       This program is distributed in the hope that it will be useful,
+-#       but WITHOUT ANY WARRANTY; without even the implied warranty of
+-#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-#       GNU General Public License for more details.
+-#
+-#       You should have received a copy of the GNU General Public License
+-#       along with this program; if not, write to the Free Software
+-#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+-#       MA 02110-1301, USA.
+-#
+-
+-import re
+-import sys
+-
+-from polyglot.builtins import unicode_type
+-
+-
+-class Templite:
+-    auto_emit = re.compile(r'''(^['"])|(^[a-zA-Z0-9_[\]'"]+$)''')
+-
+-    def __init__(self, template, start='${', end='}$'):
+-        if len(start) != 2 or len(end) != 2:
+-            raise ValueError('each delimiter must be two characters long')
+-        delimiter = re.compile('%s(.*?)%s' % (re.escape(start), 
re.escape(end)), re.DOTALL)
+-        offset = 0
+-        tokens = []
+-        for i, part in enumerate(delimiter.split(template)):
+-            part = part.replace('\\'.join(list(start)), start)
+-            part = part.replace('\\'.join(list(end)), end)
+-            if i % 2 == 0:
+-                if not part:
+-                    continue
+-                part = part.replace('\\', '\\\\').replace('"', '\\"')
+-                part = '\t' * offset + 'emit("""%s""")' % part
+-            else:
+-                part = part.rstrip()
+-                if not part:
+-                    continue
+-                if part.lstrip().startswith(':'):
+-                    if not offset:
+-                        raise SyntaxError('no block statement to terminate: 
${%s}$' % part)
+-                    offset -= 1
+-                    part = part.lstrip()[1:]
+-                    if not part.endswith(':'):
+-                        continue
+-                elif self.auto_emit.match(part.lstrip()):
+-                    part = 'emit(%s)' % part.lstrip()
+-                lines = part.splitlines()
+-                margin = min(len(l) - len(l.lstrip()) for l in lines if 
l.strip())
+-                part = '\n'.join('\t' * offset + l[margin:] for l in lines)
+-                if part.endswith(':'):
+-                    offset += 1
+-            tokens.append(part)
+-        if offset:
+-            raise SyntaxError('%i block statement(s) not terminated' % offset)
+-        self.__code = compile('\n'.join(tokens), '<templite %r>' % 
template[:20], 'exec')
+-
+-    def render(self, __namespace=None, **kw):
+-        """
+-        renders the template according to the given namespace.
+-        __namespace - a dictionary serving as a namespace for evaluation
+-        **kw - keyword arguments which are added to the namespace
+-        """
+-        namespace = {}
+-        if __namespace:
+-            namespace.update(__namespace)
+-        if kw:
+-            namespace.update(kw)
+-        namespace['emit'] = self.write
+-
+-        __stdout = sys.stdout
+-        sys.stdout = self
+-        self.__output = []
+-        eval(self.__code, namespace)
+-        sys.stdout = __stdout
+-        return ''.join(self.__output)
+-
+-    def write(self, *args):
+-        for a in args:
+-            self.__output.append(unicode_type(a))

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 13.5

This update has been released as part of Debian 13.5.

--- End Message ---

Reply via email to