pdf2epub 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +130 -0
- package/bin/pdf2epub.js +14 -0
- package/lib/cli.js +129 -0
- package/lib/paths.js +44 -0
- package/lib/python.js +76 -0
- package/lib/setup.js +152 -0
- package/package.json +50 -0
- package/vendor/pdf2epub/LICENSE +9 -0
- package/vendor/pdf2epub/README.md +217 -0
- package/vendor/pdf2epub/UPSTREAM.json +8 -0
- package/vendor/pdf2epub/main.py +118 -0
- package/vendor/pdf2epub/modules/__init__.py +0 -0
- package/vendor/pdf2epub/modules/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/pdf2epub/modules/__pycache__/mark2epub.cpython-313.pyc +0 -0
- package/vendor/pdf2epub/modules/__pycache__/pdf2md.cpython-313.pyc +0 -0
- package/vendor/pdf2epub/modules/mark2epub.py +696 -0
- package/vendor/pdf2epub/modules/pdf2md.py +172 -0
- package/vendor/pdf2epub/modules/postprocessing/__init__.py +0 -0
- package/vendor/pdf2epub/modules/postprocessing/prompt.txt +41 -0
- package/vendor/pdf2epub/modules/postprocessing/template.py +177 -0
- package/vendor/pdf2epub/requirements.txt +8 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# PDF2EPUB 📚
|
|
2
|
+
|
|
3
|
+
Convert PDF files to nicely structured Markdown and EPUB format with intelligent layout detection.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 📖 Smart layout detection for books and academic papers
|
|
8
|
+
- 🔍 Advanced text extraction and OCR capabilities
|
|
9
|
+
- 📊 Table detection and formatting
|
|
10
|
+
- 🖼️ Image extraction and optimization
|
|
11
|
+
- 📝 Clean markdown output with preserved structure
|
|
12
|
+
- 📱 EPUB generation with customizable styling
|
|
13
|
+
- 🌍 Multi-language support
|
|
14
|
+
- 🚀 GPU acceleration support (NVIDIA & AMD)
|
|
15
|
+
- 🍎 Apple Silicon support
|
|
16
|
+
|
|
17
|
+
## 🛠️ Dependencies
|
|
18
|
+
|
|
19
|
+
- Python 3.10–3.14 (3.13 recommended, see below)
|
|
20
|
+
- PyTorch (with CUDA/ROCm support for GPU acceleration)
|
|
21
|
+
- marker-pdf==1.10.2
|
|
22
|
+
- transformers==4.57.6
|
|
23
|
+
- markdown==3.10.2
|
|
24
|
+
- latex2mathml==3.81.0
|
|
25
|
+
|
|
26
|
+
### ⚠️ Python version
|
|
27
|
+
|
|
28
|
+
Python 3.13 is recommended.
|
|
29
|
+
|
|
30
|
+
`marker-pdf` constrains `Pillow<11.0.0`, and Pillow only ships Python 3.14
|
|
31
|
+
wheels from 11.3.0 onward. On Python 3.10–3.13 every dependency installs as a
|
|
32
|
+
prebuilt wheel. On 3.14, pip has to build Pillow from source instead: this
|
|
33
|
+
works, but it is slower and requires a working C toolchain plus the image
|
|
34
|
+
library headers Pillow links against. On Debian/Ubuntu install them first:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
sudo apt install libjpeg-dev zlib1g-dev libtiff-dev libfreetype6-dev libwebp-dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Without these headers the install fails with
|
|
41
|
+
`RequiredDependencyException: The headers or library files could not be found for jpeg`.
|
|
42
|
+
|
|
43
|
+
## 💻 Installation
|
|
44
|
+
|
|
45
|
+
1. Create and activate a virtual environment.
|
|
46
|
+
|
|
47
|
+
On Linux/Mac:
|
|
48
|
+
```bash
|
|
49
|
+
python3.13 -m venv .venv
|
|
50
|
+
source .venv/bin/activate
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
On Windows:
|
|
54
|
+
```powershell
|
|
55
|
+
py -3.13 -m venv .venv
|
|
56
|
+
.venv\Scripts\activate
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
2. Install Python dependencies (this installs PyTorch as well):
|
|
60
|
+
```bash
|
|
61
|
+
pip install -r requirements.txt
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
3. GPU acceleration (optional):
|
|
65
|
+
|
|
66
|
+
PyTorch is installed as a dependency in step 2. On Apple Silicon that wheel
|
|
67
|
+
already supports MPS, so no further action is needed. For a specific CUDA or
|
|
68
|
+
ROCm build, reinstall PyTorch using the selector at
|
|
69
|
+
[pytorch.org/get-started/locally](https://pytorch.org/get-started/locally/).
|
|
70
|
+
For example, for AMD GPUs with ROCm:
|
|
71
|
+
```bash
|
|
72
|
+
pip uninstall torch torchvision torchaudio
|
|
73
|
+
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
4. Verify GPU support:
|
|
77
|
+
```python
|
|
78
|
+
import torch
|
|
79
|
+
print(torch.__version__) # PyTorch version
|
|
80
|
+
print(torch.cuda.is_available()) # Should return True for NVIDIA
|
|
81
|
+
print(torch.backends.mps.is_available()) # Should return True for Apple Silicon
|
|
82
|
+
print(torch.version.hip) # Should print ROCm version for AMD
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 🐳 Docker
|
|
86
|
+
|
|
87
|
+
A CPU-only image can be built from the included `Dockerfile`:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
docker build -t pdf2epub .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Run it with your PDFs mounted at `/data` and a model cache volume (marker-pdf
|
|
94
|
+
downloads its models on first run):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
docker run -it --rm \
|
|
98
|
+
-v "$(pwd)":/data \
|
|
99
|
+
-v pdf2epub-models:/models \
|
|
100
|
+
pdf2epub input.pdf
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`-it` is required for EPUB generation because metadata is prompted
|
|
104
|
+
interactively; with `--skip-epub` it can run non-interactively.
|
|
105
|
+
|
|
106
|
+
Tagged releases are also published to
|
|
107
|
+
`ghcr.io/overcuriousity/pdf2epub` by the Docker workflow.
|
|
108
|
+
|
|
109
|
+
## 🚀 Usage
|
|
110
|
+
|
|
111
|
+
### Basic Usage
|
|
112
|
+
|
|
113
|
+
Convert a single PDF file:
|
|
114
|
+
```bash
|
|
115
|
+
python main.py input.pdf
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Convert all PDFs in a directory:
|
|
119
|
+
```bash
|
|
120
|
+
python main.py input_directory/
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
EPUB generation prompts interactively for metadata (title, author, language,
|
|
124
|
+
and so on; press Enter to accept each default). It therefore needs a terminal —
|
|
125
|
+
run it non-interactively and it will fail with `EOFError`. Use `--skip-epub` to
|
|
126
|
+
produce only markdown without any prompts.
|
|
127
|
+
|
|
128
|
+
### Advanced Options
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
python main.py [input_path] [output_path] [options]
|
|
132
|
+
|
|
133
|
+
Options:
|
|
134
|
+
--max-pages INT Maximum number of pages to process
|
|
135
|
+
--start-page INT Page number to start from
|
|
136
|
+
--skip-epub Skip EPUB generation, only create markdown
|
|
137
|
+
--skip-md Skip markdown generation, use existing markdown files
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
If `input_path` is omitted, all PDFs in `./input/` are processed.
|
|
141
|
+
|
|
142
|
+
### Examples
|
|
143
|
+
|
|
144
|
+
Process a specific range of pages:
|
|
145
|
+
```bash
|
|
146
|
+
python main.py book.pdf --start-page 10 --max-pages 50
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Convert to markdown only:
|
|
150
|
+
```bash
|
|
151
|
+
python main.py thesis.pdf --skip-epub
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Output Structure
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
output_directory/
|
|
158
|
+
├── document_name/
|
|
159
|
+
│ ├── document_name.md
|
|
160
|
+
│ ├── document_name.epub
|
|
161
|
+
│ ├── document_name_metadata.json
|
|
162
|
+
│ └── images/
|
|
163
|
+
│ ├── image1.png
|
|
164
|
+
│ ├── image2.jpg
|
|
165
|
+
│ └── ...
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 🤝 Contributing
|
|
169
|
+
|
|
170
|
+
Contributions are welcome! Here's how you can help:
|
|
171
|
+
|
|
172
|
+
1. Fork the repository
|
|
173
|
+
2. Create a new branch for your feature
|
|
174
|
+
3. Commit your changes
|
|
175
|
+
4. Push to your branch
|
|
176
|
+
5. Create a Pull Request
|
|
177
|
+
|
|
178
|
+
Please ensure your code follows the existing style and includes appropriate tests.
|
|
179
|
+
|
|
180
|
+
### Development Setup
|
|
181
|
+
|
|
182
|
+
1. Clone the repository:
|
|
183
|
+
```bash
|
|
184
|
+
git clone https://github.com/overcuriousity/pdf2epub.git
|
|
185
|
+
cd pdf2epub
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
2. Create a virtual environment:
|
|
189
|
+
```bash
|
|
190
|
+
python -m venv venv
|
|
191
|
+
source venv/bin/activate # Linux/Mac
|
|
192
|
+
venv\Scripts\activate # Windows
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
3. Install development dependencies:
|
|
196
|
+
```bash
|
|
197
|
+
pip install -r requirements.txt
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## 📄 License
|
|
201
|
+
|
|
202
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
203
|
+
|
|
204
|
+
## 🐛 Known Issues
|
|
205
|
+
|
|
206
|
+
- Some image embedding might need manual adjustment
|
|
207
|
+
- Some complex mathematical equations might not be perfectly converted
|
|
208
|
+
- Certain PDF layouts with multiple columns may require manual adjustment
|
|
209
|
+
- Font detection might be imperfect in some cases
|
|
210
|
+
|
|
211
|
+
## 🙏 Acknowledgments
|
|
212
|
+
|
|
213
|
+
This project builds upon several excellent open-source libraries:
|
|
214
|
+
- [marker-pdf](https://github.com/VikParuchuri/marker) for PDF processing
|
|
215
|
+
- [mark2epub](https://github.com/AlexPof/mark2epub) for markdown conversion
|
|
216
|
+
- [PyTorch](https://pytorch.org/) for GPU acceleration
|
|
217
|
+
- [Transformers](https://huggingface.co/transformers) for advanced text processing
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repo": "overcuriousity/pdf2epub",
|
|
3
|
+
"ref": "v1.1.0",
|
|
4
|
+
"commit": "5eae669d1c897b4735f369fb2ff4086237db3a81",
|
|
5
|
+
"commitDate": "2026-07-16T18:17:11Z",
|
|
6
|
+
"url": "https://github.com/overcuriousity/pdf2epub/tree/5eae669d1c897b4735f369fb2ff4086237db3a81",
|
|
7
|
+
"syncedAt": "2026-09-07T09:36:39.940Z"
|
|
8
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import modules.pdf2md as pdf2md
|
|
6
|
+
import modules.mark2epub as mark2epub
|
|
7
|
+
import torch
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
if torch.cuda.is_available():
|
|
13
|
+
print("CUDA is available. Using GPU for processing.")
|
|
14
|
+
elif torch.backends.mps.is_available():
|
|
15
|
+
print("MPS is available. Using Apple Silicon for processing.")
|
|
16
|
+
else:
|
|
17
|
+
print("CUDA is not available. Using CPU for processing.")
|
|
18
|
+
|
|
19
|
+
parser = argparse.ArgumentParser(
|
|
20
|
+
description='Convert PDF files to EPUB format via Markdown'
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
'input_path',
|
|
24
|
+
nargs='?',
|
|
25
|
+
type=str,
|
|
26
|
+
help='Path to input PDF file or directory (default: ./input/*.pdf)'
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
'output_path',
|
|
30
|
+
nargs='?',
|
|
31
|
+
type=str,
|
|
32
|
+
help='Path to output directory (default: directory named after PDF)'
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
'--max-pages',
|
|
36
|
+
type=int,
|
|
37
|
+
default=None,
|
|
38
|
+
help='Maximum number of pages to process'
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
'--start-page',
|
|
42
|
+
type=int,
|
|
43
|
+
default=None,
|
|
44
|
+
help='Page number to start from'
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
'--skip-epub',
|
|
48
|
+
action='store_true',
|
|
49
|
+
help='Skip EPUB generation, only create markdown'
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
'--skip-md',
|
|
53
|
+
action='store_true',
|
|
54
|
+
help='Skip markdown generation, use existing markdown files'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
args = parser.parse_args()
|
|
58
|
+
|
|
59
|
+
# Get input path
|
|
60
|
+
input_path = Path(args.input_path) if args.input_path else pdf2md.get_default_input_dir()
|
|
61
|
+
|
|
62
|
+
# Get queue of PDFs to process
|
|
63
|
+
queue = pdf2md.add_pdfs_to_queue(input_path)
|
|
64
|
+
print(f"Found {len(queue)} PDF files to process")
|
|
65
|
+
|
|
66
|
+
# Process each PDF
|
|
67
|
+
failed = []
|
|
68
|
+
for pdf_path in queue:
|
|
69
|
+
print(f"\nProcessing: {pdf_path.name}")
|
|
70
|
+
|
|
71
|
+
# Get output directory for this PDF
|
|
72
|
+
if args.output_path:
|
|
73
|
+
output_path = Path(args.output_path)
|
|
74
|
+
markdown_dir = output_path / pdf_path.stem
|
|
75
|
+
else:
|
|
76
|
+
markdown_dir = pdf2md.get_default_output_dir(pdf_path)
|
|
77
|
+
output_path = markdown_dir.parent
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
# Check if markdown directory exists when skipping MD generation
|
|
81
|
+
if args.skip_md:
|
|
82
|
+
if not markdown_dir.exists():
|
|
83
|
+
print(f"Error: Markdown directory not found: {markdown_dir}", file=sys.stderr)
|
|
84
|
+
failed.append(pdf_path.name)
|
|
85
|
+
continue
|
|
86
|
+
print(f"Using existing markdown files from: {markdown_dir}")
|
|
87
|
+
|
|
88
|
+
# Convert PDF to Markdown unless skipped
|
|
89
|
+
if not args.skip_md:
|
|
90
|
+
print("Converting PDF to Markdown...")
|
|
91
|
+
pdf2md.convert_pdf(
|
|
92
|
+
str(pdf_path),
|
|
93
|
+
markdown_dir,
|
|
94
|
+
args.max_pages,
|
|
95
|
+
args.start_page,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Convert Markdown to EPUB unless skipped
|
|
99
|
+
if not args.skip_epub:
|
|
100
|
+
print("Converting Markdown to EPUB...")
|
|
101
|
+
mark2epub.convert_to_epub(markdown_dir, output_path)
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"Error processing {pdf_path.name}: {str(e)}", file=sys.stderr)
|
|
105
|
+
failed.append(pdf_path.name)
|
|
106
|
+
continue
|
|
107
|
+
|
|
108
|
+
if failed:
|
|
109
|
+
print(
|
|
110
|
+
f"\nFailed to process {len(failed)} of {len(queue)} PDF file(s):",
|
|
111
|
+
file=sys.stderr,
|
|
112
|
+
)
|
|
113
|
+
for name in failed:
|
|
114
|
+
print(f" - {name}", file=sys.stderr)
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
if __name__ == '__main__':
|
|
118
|
+
main()
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|