<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Selenium39</title>
    <description>The latest articles on DEV Community by Selenium39 (@selenium39dev).</description>
    <link>https://dev.to/selenium39dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100439%2F3b9b152b-34c5-4fa1-b2cd-b9dab3290a00.jpg</url>
      <title>DEV Community: Selenium39</title>
      <link>https://dev.to/selenium39dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/selenium39dev"/>
    <language>en</language>
    <item>
      <title>A web-to-EPUB pipeline: readable extract, chapter structure, Pandoc</title>
      <dc:creator>Selenium39</dc:creator>
      <pubDate>Sat, 29 Aug 2026 15:37:33 +0000</pubDate>
      <link>https://dev.to/selenium39dev/a-web-to-epub-pipeline-readable-extract-chapter-structure-pandoc-2cda</link>
      <guid>https://dev.to/selenium39dev/a-web-to-epub-pipeline-readable-extract-chapter-structure-pandoc-2cda</guid>
      <description>&lt;p&gt;&lt;code&gt;pandoc -f html -t epub3 https://example.com/post -o post.epub&lt;/code&gt; looks like a complete product. It is not. Pandoc is a typesetter. It will faithfully encode whatever tree you give it, including the cookie banner.&lt;/p&gt;

&lt;p&gt;I run &lt;a href="https://e-ink.me" rel="noopener noreferrer"&gt;E-Ink&lt;/a&gt;, a small web-to-EPUB service. This is the pipeline underneath it, and why the interesting work is not the EPUB zip.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fetch what a reader actually saw
&lt;/h2&gt;

&lt;p&gt;A lot of the pages people want on a Kindle are JS-rendered docs, not static article HTML. A single &lt;code&gt;GET&lt;/code&gt; of the URL returns a shell. You need a rendered DOM (we use Firecrawl for this), not wget. That already puts you in “this costs something” territory, which is why a free-forever scraper that also produces pretty EPUBs is usually lying about one of those adjectives.&lt;/p&gt;

&lt;p&gt;Public pages only. No login cookies, no paywall punching. If the extract is empty, fail loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Extract the document, not the application
&lt;/h2&gt;

&lt;p&gt;Readability-style extraction is the whole game. Keep title, byline, headings, lists, blockquotes, &lt;code&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/code&gt;, figures + captions. Drop nav, footers, related rails, signup modals, comment widgets.&lt;/p&gt;

&lt;p&gt;Two failure modes I still see weekly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under-extract.&lt;/strong&gt; The article is a docs page whose “body” is a tabbed component. You get the first tab and a blank.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-extract.&lt;/strong&gt; You kept the sidebar TOC as if it were chapter one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Markdown is a good intermediate. It is diffable, previewable, and Pandoc eats it. If the Markdown looks wrong, do not bother emitting EPUB. We show a Markdown preview in the UI for exactly this reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Headings → spine, not a wall of &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;EPUB readers live and die by the nav document. A 8,000-word essay with one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; and no &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; is a brick. A docs page that uses &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; for everything produces a TOC that is either empty or 90 entries deep.&lt;/p&gt;

&lt;p&gt;Normalize heading levels against the document you actually got, not against the HTML5 spec the CMS ignored. Promote or demote so the spine has a usable chapter list. This is boring string work and it is more important than your CSS.&lt;/p&gt;

&lt;p&gt;Images: resolve &lt;code&gt;src&lt;/code&gt; / &lt;code&gt;srcset&lt;/code&gt; / &lt;code&gt;data-src&lt;/code&gt;, download, cap dimensions, rewrite to package-relative paths. Lazy-load placeholders are the number one “why is my ebook a gray box” bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pandoc for EPUB3, not for fetching
&lt;/h2&gt;

&lt;p&gt;Once you have a clean Markdown document plus a media folder, Pandoc is the right tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pandoc article.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from&lt;/span&gt; markdown &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--to&lt;/span&gt; epub3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--epub-title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; article.epub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let Pandoc write the zip, the nav, the OPF. Do not hand-roll EPUB unless you enjoy debugging mimetype-must-be-first-and-uncompressed for the tenth time.&lt;/p&gt;

&lt;p&gt;Output formats beyond EPUB are the same document, different writer: PDF, Markdown, or a second pass to MOBI for older Kindles. Newer Kindles take EPUB natively. Do not make MOBI the default in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The public API is that pipeline with a credit meter
&lt;/h2&gt;

&lt;p&gt;Same steps, HTTP in front. Bearer token, one URL (or several), a format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://e-ink.me/api/v1/convert/webpage &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer eink_your_api_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url": "https://e-ink.me", "format": "epub"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; article.epub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Webpage → ebook is 3 credits per URL. File conversion, translation, TTS, and mind maps are separate endpoints on the same key. Docs, OpenAPI, llms.txt, and an MCP manifest live at &lt;a href="https://e-ink.me/en/developers" rel="noopener noreferrer"&gt;e-ink.me/en/developers&lt;/a&gt;. If you want the same calls inside Cursor or Claude Code, there is a skill at &lt;a href="https://github.com/Selenium39/e-ink-skill" rel="noopener noreferrer"&gt;github.com/Selenium39/e-ink-skill&lt;/a&gt; — &lt;code&gt;npx e-ink-skill&lt;/code&gt; and &lt;code&gt;EINK_API_KEY&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS and the e-ink constraints
&lt;/h2&gt;

&lt;p&gt;Ship almost no CSS. E-readers ignore half of it and fight the other half. Relative font sizes, default margins, &lt;code&gt;break-inside: avoid&lt;/code&gt; on pre/code and figures. No &lt;code&gt;position: fixed&lt;/code&gt;. No dark-mode media queries that assume an emissive screen. Code blocks need wrapping or they clip on a 6-inch Kindle; that is a content problem, not a theme problem.&lt;/p&gt;

&lt;p&gt;If you are tempted to embed a webfont: don't, unless you have tested the file on a 2018 Kobo. The reader already has a font. Your job is structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we refuse to do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No session cookies. If the page needs a login, it is not a public article.&lt;/li&gt;
&lt;li&gt;No “print the whole SPA as screenshots.” That is a PDF of a website, not a book.&lt;/li&gt;
&lt;li&gt;No storing the source file as a convenience cache. Uploads live long enough to produce the output, then they are deleted. The in-browser readers do not upload at all.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I did not invent readable extract or Pandoc. I glued them so I could stop sending navbar novels to a Kobo. If you are building your own pipeline, steal the shape: &lt;strong&gt;render → extract → structure → package&lt;/strong&gt;. If you just want the file, paste a URL on the site. New accounts get free credits.&lt;/p&gt;

&lt;p&gt;Questions / broken extracts: &lt;a href="mailto:openminimax@gmail.com"&gt;openminimax@gmail.com&lt;/a&gt;. I am Selenium39.&lt;/p&gt;

</description>
      <category>epub</category>
      <category>pandoc</category>
      <category>ebook</category>
      <category>api</category>
    </item>
  </channel>
</rss>
