Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.git
.github
.gitignore
.ruff_cache
__pycache__
**/__pycache__
docs
input
*.md
!README.md
venv
.venv
Dockerfile
.dockerignore
39 changes: 39 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Docker

on:
push:
tags: ["v*"]
workflow_dispatch:

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v7

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,4 @@ config.yaml

# macOS
.DS_Store
.claude/
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# CPU-only image. For GPU acceleration install a CUDA/ROCm PyTorch build
# instead (see README) or run natively.
FROM python:3.13-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY main.py .
COPY modules/ modules/

# marker-pdf downloads its models on first run; mount a volume at this path
# to cache them across container runs.
ENV HF_HOME=/models
VOLUME /models

# PDFs are read from /data/input and results written next to them by default.
WORKDIR /data

ENTRYPOINT ["python", "/app/main.py"]
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ Python 3.13 is recommended.
`marker-pdf` constrains `Pillow<11.0.0`, and Pillow only ships Python 3.14
wheels from 11.3.0 onward. On Python 3.10–3.13 every dependency installs as a
prebuilt wheel. On 3.14, pip has to build Pillow from source instead: this
works, but it is slower and requires a working C toolchain.
works, but it is slower and requires a working C toolchain plus the image
library headers Pillow links against. On Debian/Ubuntu install them first:

```bash
sudo apt install libjpeg-dev zlib1g-dev libtiff-dev libfreetype6-dev libwebp-dev
```

Without these headers the install fails with
`RequiredDependencyException: The headers or library files could not be found for jpeg`.

## 💻 Installation

Expand Down Expand Up @@ -74,6 +82,30 @@ print(torch.backends.mps.is_available()) # Should return True for Apple Silicon
print(torch.version.hip) # Should print ROCm version for AMD
```

### 🐳 Docker

A CPU-only image can be built from the included `Dockerfile`:

```bash
docker build -t pdf2epub .
```

Run it with your PDFs mounted at `/data` and a model cache volume (marker-pdf
downloads its models on first run):

```bash
docker run -it --rm \
-v "$(pwd)":/data \
-v pdf2epub-models:/models \
pdf2epub input.pdf
```

`-it` is required for EPUB generation because metadata is prompted
interactively; with `--skip-epub` it can run non-interactively.

Tagged releases are also published to
`ghcr.io/overcuriousity/pdf2epub` by the Docker workflow.

## 🚀 Usage

### Basic Usage
Expand Down
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def main():
if torch.cuda.is_available():
print("CUDA is available. Using GPU for processing.")
elif torch.mps.is_available():
elif torch.backends.mps.is_available():
print("MPS is available. Using Apple Silicon for processing.")
else:
print("CUDA is not available. Using CPU for processing.")
Expand Down
15 changes: 10 additions & 5 deletions modules/mark2epub.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ def review_markdown(markdown_path: Path) -> tuple[bool, str]:
response = input("\nWould you like to review the markdown file before conversion? (y/n): ").lower()
if response in ['y', 'yes']:
try:
subprocess.run(['xdg-open' if os.name == 'posix' else 'start', str(markdown_path)], check=True)
if sys.platform == 'darwin':
subprocess.run(['open', str(markdown_path)], check=True)
elif os.name == 'posix':
subprocess.run(['xdg-open', str(markdown_path)], check=True)
else:
os.startfile(str(markdown_path))

while True:
proceed = input("\nPress Enter when you're done editing (or 'q' to abort): ").lower()
Expand Down Expand Up @@ -333,8 +338,8 @@ def get_coverpage_XML(title, authors):
</head>
<body>
<div class="cover">
<h1>{title}</h1>
<p>{authors}</p>
<h1>{xml_escape(title)}</h1>
<p>{xml_escape(authors) if authors else ''}</p>
</div>
</body>
</html>"""
Expand All @@ -355,7 +360,7 @@ def get_TOC_XML(default_css_filenames, markdown_filenames):
for i,md_filename in enumerate(markdown_filenames):
stem = md_filename.split(".")[0]
href = quote("s{:05d}-{}.xhtml".format(i, stem))
toc_xhtml += """<li><a href="{}">{}</a></li>""".format(href, stem)
toc_xhtml += """<li><a href="{}">{}</a></li>""".format(href, xml_escape(stem))
toc_xhtml += """</ol>\n</nav>\n</body>\n</html>"""

return toc_xhtml
Expand All @@ -377,7 +382,7 @@ def get_TOCNCX_XML(markdown_filenames, uid="", title=""):
stem = md_filename.split(".")[0]
src = quote("s{:05d}-{}.xhtml".format(i, stem))
toc_ncx += """<navPoint id="navpoint-{}" playOrder="{}">\n""".format(i, i + 1)
toc_ncx += """<navLabel>\n<text>{}</text>\n</navLabel>""".format(stem)
toc_ncx += """<navLabel>\n<text>{}</text>\n</navLabel>""".format(xml_escape(stem))
toc_ncx += """<content src="{}"/>""".format(src)
toc_ncx += """ </navPoint>"""
toc_ncx += """</navMap>\n</ncx>"""
Expand Down