<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Calibre on OHTLY Blog</title>
    <link>https://blog.ohtly.com/tags/calibre/</link>
    <description>Recent content in Calibre on OHTLY Blog</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Tue, 28 Jul 2026 14:28:50 +0800</lastBuildDate>
    <atom:link href="https://blog.ohtly.com/tags/calibre/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>转换 EPUB 为 PDF 并前置目录页</title>
      <link>https://blog.ohtly.com/posts/2026-07-28-epub-to-pdf-with-toc-front/</link>
      <pubDate>Tue, 28 Jul 2026 14:16:09 +0800</pubDate>
      <guid>https://blog.ohtly.com/posts/2026-07-28-epub-to-pdf-with-toc-front/</guid>
      <description>&lt;p&gt;Calibre 的 &lt;code&gt;--pdf-add-toc&lt;/code&gt; 会把目录页插在 PDF 末尾，方便打印但不适合电子阅读。EPUB 自带的目录书签完整，但同样落在尾部。&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--use-auto-toc&lt;/code&gt; 用 XPath 自动生成目录，效果不稳定，经常只匹配到少量标题。&lt;/p&gt;&#xA;&lt;h2 id=&#34;方案&#34;&gt;&#xA;  方案&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#%e6%96%b9%e6%a1%88&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;链接到标题&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;链接到标题&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;两步处理：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Calibre 转换（不加 &lt;code&gt;--pdf-add-toc&lt;/code&gt;，保留 EPUB 自带目录）&lt;/li&gt;&#xA;&lt;li&gt;PyMuPDF 脚本把末尾目录页移到正文前方&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ebook-convert input.epub output.pdf&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;生成的文件目录页在最后两页，用以下脚本移动到第 3 页之后：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; fitz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;src &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;某书_with_toc.pdf&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dst &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;某书_with_toc_moved.pdf&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;doc &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fitz&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;open(src)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;total &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; len(doc)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;toc_start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; total &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;# 目录页起始位置&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 页面映射：旧页码 → 新页码&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;old_to_new &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;idx &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; old &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;):           &lt;span style=&#34;color:#75715e&#34;&gt;# 前3页：空白、扉页、格言页&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    old_to_new[old] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; idx; idx &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; old &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(toc_start, total):  &lt;span style=&#34;color:#75715e&#34;&gt;# 目录页&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    old_to_new[old] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; idx; idx &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; old &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; range(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, toc_start):      &lt;span style=&#34;color:#75715e&#34;&gt;# 正文&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    old_to_new[old] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; idx; idx &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; fitz&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;open()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;insert_pdf(doc, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)              &lt;span style=&#34;color:#75715e&#34;&gt;# 前3页&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;insert_pdf(doc, toc_start, total&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)  &lt;span style=&#34;color:#75715e&#34;&gt;# 目录页&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;insert_pdf(doc, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, toc_start&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)     &lt;span style=&#34;color:#75715e&#34;&gt;# 正文&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 修正书签页码&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;toc &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;get_toc()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_toc &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [[lvl, title, old_to_new[page&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; lvl, title, page &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; toc]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set_toc(new_toc)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_doc&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;save(dst)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;结果&#34;&gt;&#xA;  结果&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#%e7%bb%93%e6%9e%9c&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;链接到标题&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;链接到标题&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;目录页位于扉页/格言页之后，正文之前&lt;/li&gt;&#xA;&lt;li&gt;书签数量不变，页码自动修正，点击跳转正确&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;注意事项&#34;&gt;&#xA;  注意事项&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a1%b9&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;链接到标题&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;链接到标题&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;--use-auto-toc&lt;/code&gt; 的 &lt;code&gt;--level1-toc=&amp;quot;//h:h1&amp;quot;&lt;/code&gt; 等 XPath 对 EPUB 内部结构依赖大，优先用自带目录&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;set_toc&lt;/code&gt; 需在 &lt;code&gt;insert_pdf&lt;/code&gt; 完成后调用，页码为 1-based&lt;/li&gt;&#xA;&lt;li&gt;如果目录页不在末尾，需先确认 &lt;code&gt;toc_start&lt;/code&gt; 位置（通过 &lt;code&gt;get_toc()&lt;/code&gt; 或观察页内容）&lt;/li&gt;&#xA;&lt;/ul&gt;</description>
    </item>
  </channel>
</rss>
