This project is dual-licensed. Please see the License section for comprehensive details.
PDF3MD is a web application designed for efficient conversion of PDF documents into well-structured Markdown and Microsoft Word (DOCX) formats. It features a React-based frontend and a Python Flask backend, providing a seamless user experience with real-time progress updates.
- PDF to Markdown Conversion: Transforms PDF documents into clean, readable Markdown, preserving structural elements.
- Markdown to Word (DOCX) Conversion: Converts user-provided Markdown text to DOCX format using Pandoc for high-fidelity output.
- Multi-File Upload: Supports uploading and processing multiple PDF files simultaneously for PDF to Markdown conversion.
- Drag & Drop Interface: User-friendly file uploads via drag and drop or traditional file selection.
- Real-time Progress Tracking: Detailed status updates during the conversion process for each file.
- File Information Display: Shows original filename, file size, page count, and conversion timestamp.
- Modern and Responsive UI: Intuitive interface designed for ease of use across various devices.
- Frontend: React, Vite
- Backend: Python, Flask
- PDF Processing: PyMuPDF4LLM
- Markdown to DOCX Conversion: Pandoc
The easiest and recommended way to run PDF3MD is using the provided Docker quick start script.
- Docker Engine
- Docker Compose (typically included with Docker Desktop)
This method uses pre-built Docker images from Docker Hub for quick setup. You'll need the docker-compose.yml and docker-start.sh script from the repository.
-
Prepare Required Files:
- Create a directory for your application (e.g.,
mkdir pdf3md-app && cd pdf3md-app). docker-compose.yml: Create a file nameddocker-compose.ymlin this directory and paste the following content into it:services: backend: image: docker.io/learnedmachine/pdf3md-backend:latest container_name: pdf3md-backend ports: - "6201:6201" environment: - PYTHONUNBUFFERED=1 - FLASK_ENV=production - TZ=America/Chicago volumes: - ./pdf3md/temp:/app/temp # Creates a local temp folder for backend processing if needed restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6201/"] interval: 30s timeout: 10s retries: 3 start_period: 40s frontend: image: docker.io/learnedmachine/pdf3md-frontend:latest container_name: pdf3md-frontend ports: - "3000:3000" depends_on: - backend restart: unless-stopped healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/"] interval: 30s timeout: 10s retries: 3 start_period: 40s networks: default: name: pdf3md-network
docker-start.sh: Download thedocker-start.shscript from the pdf3md GitHub repository's main branch and place it in the same directory.- Make the script executable:
chmod +x ./docker-start.sh - (Optional) For development mode using local source code (which requires cloning the full repository), you would also need
docker-compose.dev.ymlfrom the repository.
- Create a directory for your application (e.g.,
-
Start in Production Mode: In the directory where you placed
docker-compose.ymlanddocker-start.sh, run:./docker-start.sh start
This will pull the latest images from Docker Hub and start the application.
- Access Frontend:
http://localhost:3000 - Access Backend API:
http://localhost:6201
- Access Frontend:
-
Start in Development Mode (with hot-reloading):
./docker-start.sh dev
This uses local source code for development if
docker-compose.dev.ymlis present and configured for local mounts. Otherwise, it may behave like production mode depending on the script's logic.- Access Frontend (Vite Dev Server, if using local source):
http://localhost:5173 - Access Backend API:
http://localhost:6201
- Access Frontend (Vite Dev Server, if using local source):
-
Other Useful Script Commands:
./docker-start.sh stop # Stop all services ./docker-start.sh status # Check running services ./docker-start.sh logs # View logs from services ./docker-start.sh rebuild dev example.com # Rebuild development with custom domain ./docker-start.sh help # Display all available script commands
If you prefer to use Docker Compose commands directly with the pre-built images without the docker-start.sh script:
-
Create
docker-compose.yml:- Create a directory for your application (e.g.,
mkdir pdf3md && cd pdf3md). - Create a file named
docker-compose.ymlin this directory and paste the content provided in the section above (under "Using Pre-built Docker Images (Recommended)").
- Create a directory for your application (e.g.,
-
Pull and Start Services: In the directory where you created
docker-compose.yml, run:docker compose pull # Pulls the latest images specified in docker-compose.yml docker compose up -d -
Access Application:
- With default settings: Frontend at
http://localhost:3000, Backend API athttp://localhost:6201 - With custom domain: Frontend at
http://example.com:3000, Backend API athttp://example.com:6201
- With default settings: Frontend at
-
Stop Services:
docker compose down
This setup is for developing the application locally, not using pre-built images for development.
- Clone the Repository:
git clone https://github.com/murtaza-nasir/pdf3md.git cd pdf3md - Start Services:
Use the
docker-compose.dev.ymlfile, which is typically configured to build images locally and mount source code.docker compose -f docker-compose.dev.yml up --build
- Access Application:
- With default settings: Frontend (Vite) at
http://localhost:5173, Backend API athttp://localhost:6201 - With custom domain/IP: Frontend at
http://192.168.1.100:5173, Backend API athttp://192.168.1.100:6201
- With default settings: Frontend (Vite) at
- Stop Services:
docker compose -f docker-compose.dev.yml down
This method involves running the frontend and backend services directly on your machine without Docker, typically for development or if you prefer not to use Docker.
- Clone the Repository:
If you haven't already, clone the repository to get the source code:
git clone https://github.com/murtaza-nasir/pdf3md.git cd pdf3md
- Navigate to the
pdf3mdsub-directory (if you are in the root of the cloned repo):cd pdf3md - Install Python dependencies:
pip install -r requirements.txt - Start the backend server:
python app.py(The backend will be available athttp://localhost:6201)
- In a new terminal, navigate to the
pdf3mdsub-directory (if you are in the root of the cloned repo):cd pdf3md - Install Node.js dependencies:
npm install - Start the frontend development server:
npm run dev(The frontend will be available athttp://localhost:5173)
The pdf3md sub-directory contains scripts for managing both services:
./start_server.sh: Starts both frontend and backend../stop_server.sh: Stops both services. (Ensure these scripts are executable:chmod +x ./start_server.sh ./stop_server.sh)
- Open the PDF3MD application in your web browser.
- Upload one or more PDF files using the drag-and-drop area or by clicking to select files.
- Monitor the real-time progress as each PDF is converted.
- Once a file is processed, the resulting Markdown will be displayed.
- You can then:
- Copy the Markdown text (from PDF to MD conversion).
- In "MD → Word" mode, input Markdown and download the content as a DOCX file (powered by Pandoc).
- Backend Port: The Flask server runs on port
6201by default, configurable inpdf3md/app.py. - Frontend API Proxy: The Vite development server proxies API requests. If the backend port changes, update
pdf3md/vite.config.js. - Environment Variables (Docker):
FLASK_ENV:developmentorproduction.FLASK_DEBUG:1for debug mode.TZ: Timezone configuration for the backend container. You can modify this in the Docker Compose files to match your local timezone (e.g.,America/New_York).
The application is designed with the following network assumptions when using the Docker Compose setup:
- Same Host Access: When running via Docker Compose, both the frontend (port
3000) and backend (port6201) are expected to be accessed from the same host machine (e.g.,http://localhost:3000for the frontend, which will then try to reachhttp://localhost:6201for the backend). - Local Area Network (LAN) Access: If you access the frontend from another device on your LAN (e.g.,
http://<host-ip-address>:3000), the frontend will attempt to connect to the backend athttp://<host-ip-address>:6201. This requires the host machine's firewall to allow incoming connections on port6201from other devices on the LAN. - Limitations: This setup assumes the backend API is always reachable on the same hostname as the frontend, but on port
6201. For more complex deployment scenarios (e.g., different domains/subdomains for frontend and backend, or API gateways), further configuration, potentially involving environment variables for the API base URL in the frontend build, would be necessary. The current Docker setup is primarily optimized for local development and straightforward LAN access.
If you plan to put PDF3MD behind a reverse proxy (e.g., Nginx, Apache, Caddy, Traefik), you need to ensure that requests to your chosen domain are correctly routed to the frontend and backend services.
The frontend application is designed to detect if it's being accessed via a domain name. If so, it will make API requests to the /api path on that same domain. Your reverse proxy must be configured to route these /api/... requests to the backend service (running on port 6201 by default).
Example Nginx Configuration:
Assuming your reverse proxy listens on http://pdf3md.local/ (or your chosen domain):
- Route
/to the frontend service (running on port3000by default). - Route
/api/to the backend service (running on port6201by default).
Here's a sample Nginx location block for routing the API:
location /api/ {
proxy_pass http://<BACKEND_IP_OR_HOSTNAME>:6201/; # Replace with your backend's actual IP/hostname if not localhost from proxy's perspective
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Important: Ensure trailing slashes are correct.
# If proxy_pass has a URI (like / at the end), the part of the original request
# that matches the location block is replaced by this URI.
# If proxy_pass has no URI, the full original request URI is passed.
}Key considerations for your reverse proxy setup:
- Frontend Root: Your reverse proxy should serve the frontend application (from port
3000) at the root of your domain (e.g.,http://pdf3md.local/). - API Path: The frontend will make requests to
http://pdf3md.local/api/.... Your proxy needs to strip/apifrom the path before forwarding to the backend if the backend doesn't expect/apiin its routes (which is the case for PDF3MD by default). Theproxy_pass http://<backend_host>:6201/;(with a trailing slash) typically handles this correctly in Nginx. - WebSocket Support: Not currently used by PDF3MD, but if future features require WebSockets, ensure your proxy is configured to handle them.
- SSL/TLS Termination: It's highly recommended to configure SSL/TLS termination at your reverse proxy.
- CORS: The backend is configured with permissive CORS headers (
Access-Control-Allow-Origin: *). In most reverse proxy setups where the frontend and API are served under the same domain, CORS issues should not arise. However, if you encounter them, ensure your proxy isn't stripping or altering necessary CORS headers.
Adjust the proxy_pass directive to point to the correct address of your backend container as seen by the reverse proxy (e.g., http://localhost:6201 if on the same machine, or http://pdf3md-backend:6201 if using Docker service names in a shared Docker network).
- Port Conflicts: Ensure ports
3000,5173(for dev), and6201are not in use by other applications. Usedocker compose downto stop existing PDF3MD containers. - Script Permissions (Manual Setup): If
start_server.shorstop_server.shfail, make them executable:chmod +x pdf3md/start_server.sh pdf3md/stop_server.sh. - Docker Issues: Ensure Docker is running. Try rebuilding images with
docker compose up --build. - API Connectivity: Verify the backend is running and accessible. Check browser console for errors.
This project is dual-licensed:
-
GNU Affero General Public License v3.0 (AGPLv3)
PDF3MD is offered under the AGPLv3 as its open-source license. You are free to use, modify, and distribute this software under the terms of the AGPLv3. A key condition of the AGPLv3 is that if you run a modified version on a network server and provide access to it for others, you must also make the source code of your modified version available to those users under the AGPLv3.
You must create a file named
LICENSE(orCOPYING) in the root of your repository and paste the full text of the GNU AGPLv3 license into it. Read the full license text carefully to understand your rights and obligations. -
Commercial License
For users or organizations who cannot or do not wish to comply with the terms of the AGPLv3 (for example, if you want to integrate PDF3MD into a proprietary commercial product or service without being obligated to share your modifications under AGPLv3), a separate commercial license is available.
Please contact PDF3MD maintainers for details on obtaining a commercial license.
You must choose one of these licenses under which to use, modify, or distribute this software. If you are using or distributing the software without a commercial license agreement, you must adhere to the terms of the AGPLv3.
- PDF processing powered by PyMuPDF4LLM.
- Markdown to DOCX conversion via Pandoc.
- Frontend developed with React and Vite.
- Backend implemented using Flask.
Feedback, bug reports, and feature suggestions are highly appreciated. Please open an Issue on the GitHub repository.
Note on Future Contributions and CLAs: Should this project begin accepting code contributions from external developers in the future, signing a Contributor License Agreement (CLA) will be required before any pull requests can be merged. This policy ensures that the project maintainer receives the necessary rights to distribute all contributions under both the AGPLv3 and the commercial license options offered. Details on the CLA process will be provided if and when the project formally opens up to external code contributions.