Marker converts PDFs to markdown, JSON, and HTML quickly and accurately.
- Supports a wide range of documents
- Supports all languages
- Removes headers/footers/other artifacts
- Formats tables and code blocks
- Extracts and saves images along with the markdown
- Converts equations to latex
- Easily extensible with your own formatting and logic
- Works on GPU, CPU, or MPS
Marker is a pipeline of deep learning models:
- Extract text, OCR if necessary (heuristics, surya)
- Detect page layout and find reading order (surya)
- Clean and format each block (heuristics, texify. tabled)
- Combine blocks and postprocess complete text
It only uses models where necessary, which improves speed and accuracy.
File type | Markdown | JSON | |
---|---|---|---|
Think Python | Textbook | View | View |
Switch Transformers | arXiv paper | View | View |
Multi-column CNN | arXiv paper | View | View |
The above results are with marker setup so it takes ~7GB of VRAM on an A10.
See below for detailed speed and accuracy benchmarks, and instructions on how to run your own benchmarks.
I want marker to be as widely accessible as possible, while still funding my development/training costs. Research and personal usage is always okay, but there are some restrictions on commercial usage.
The weights for the models are licensed cc-by-nc-sa-4.0
, but I will waive that for any organization under $5M USD in gross revenue in the most recent 12-month period AND under $5M in lifetime VC/angel funding raised. You also must not be competitive with the Datalab API. If you want to remove the GPL license requirements (dual-license) and/or use the weights commercially over the revenue limit, check out the options here.
There's a hosted API for marker available here:
- Supports PDFs, word documents, and powerpoints
- 1/4th the price of leading cloud-based competitors
- High uptime (99.99%), quality, and speed (around 15 seconds to convert a 250 page PDF)
Discord is where we discuss future development.
PDF is a tricky format, so marker will not always work perfectly. Here are some known limitations that are on the roadmap to address:
- Marker will only convert block equations
- Tables are not always formatted 100% correctly - multiline cells are sometimes split into multiple rows.
- Forms are not converted optimally
- Very complex layouts, with nested tables and forms, may not work
You'll need python 3.10+ and PyTorch. You may need to install the CPU version of torch first if you're not using a Mac or a GPU machine. See here for more details.
Install with:
pip install marker-pdf
First, some configuration:
- Your torch device will be automatically detected, but you can override this. For example,
TORCH_DEVICE=cuda
. - Some PDFs, even digital ones, have bad text in them. Set the
force_ocr
flag on the CLI or via configuration to ensure your PDF runs through OCR.
I've included a streamlit app that lets you interactively try marker with some basic options. Run it with:
pip install streamlit
marker_gui
marker_single /path/to/file.pdf
Options:
--output_dir PATH
: Directory where output files will be saved. Defaults to the value specified in settings.OUTPUT_DIR.--debug
: Enable debug mode for additional logging and diagnostic information.--output_format [markdown|json|html]
: Specify the format for the output results.--page_range TEXT
: Specify which pages to process. Accepts comma-separated page numbers and ranges. Example:--page_range "0,5-10,20"
will process pages 0, 5 through 10, and page 20.--force_ocr
: Force OCR processing on the entire document, even for pages that might contain extractable text.--processors TEXT
: Override the default processors by providing their full module paths, separated by commas. Example:--processors "module1.processor1,module2.processor2"
--config_json PATH
: Path to a JSON configuration file containing additional settings.--languages TEXT
: Optionally specify which languages to use for OCR processing. Accepts a comma-separated list. Example:--languages "eng,fra,deu"
for English, French, and German.config --help
: List all available builders, processors, and converters, and their associated configuration. These values can be used to build a JSON configuration file for additional tweaking of marker defaults.
The list of supported languages for surya OCR is here. If you don't need OCR, marker can work with any language.
marker /path/to/input/folder --workers 4
marker
supports all the same options frommarker_single
above.--workers
is the number of conversion workers to run simultaneously. This is set to 5 by default, but you can increase it to increase throughput, at the cost of more CPU/GPU usage. Marker will use 5GB of VRAM per worker at the peak, and 3.5GB average.
NUM_DEVICES=4 NUM_WORKERS=15 marker_chunk_convert ../pdf_in ../md_out
NUM_DEVICES
is the number of GPUs to use. Should be2
or greater.NUM_WORKERS
is the number of parallel processes to run on each GPU.
See the PdfConverter
class at marker/converters/pdf.py
function for additional arguments that can be passed.
from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.output import text_from_rendered
converter = PdfConverter(
artifact_dict=create_model_dict(),
)
rendered = converter("FILEPATH")
text, _, images = text_from_rendered(rendered)
rendered
will be a pydantic basemodel with different properties depending on the output type requested. With markdown output (default), you'll have the properties markdown
, metadata
, and images
. For json output, you'll have children
, block_type
, and metadata
.
You can also pass configuration using the ConfigParser
:
from marker.converters.pdf import PdfConverter
from marker.models import create_model_dict
from marker.config.parser import ConfigParser
config = {
"output_format": "json",
"ADDITIONAL_KEY": "VALUE"
}
config_parser = ConfigParser(config)
converter = PdfConverter(
config=config_parser.generate_config_dict(),
artifact_dict=create_model_dict(),
processor_list=config_parser.get_processors(),
renderer=config_parser.get_renderer()
)
rendered = converter("FILEPATH")
Markdown output will include:
- image links (images will be saved in the same folder)
- formatted tables
- embedded LaTeX equations (fenced with
$$
) - Code is fenced with triple backticks
- Superscripts for footnotes
HTML output is similar to markdown output:
- Images are included via
img
tags - equations are fenced with
<math>
tags - code is in
pre
tags
JSON output will be organized in a tree-like structure, with the leaf nodes being blocks. Examples of leaf nodes are a single list item, a paragraph of text, or an image.
The output will be a list, with each list item representing a page. Each page is considered a block in the internal marker schema. There are different types of blocks to represent different elements.
Pages have the keys:
id
- unique id for the block.block_type
- the type of block. The possible block types can be seen inmarker/schema/__init__.py
. As of this writing, they are ["Line", "Span", "FigureGroup", "TableGroup", "ListGroup", "PictureGroup", "Page", "Caption", "Code", "Figure", "Footnote", "Form", "Equation", "Handwriting", "TextInlineMath", "ListItem", "PageFooter", "PageHeader", "Picture", "SectionHeader", "Table", "Text", "TableOfContents", "Document"]html
- the HTML for the page. Note that this will have recursive references to children. Thecontent-ref
tags must be replaced with the child content if you want the full html. You can see an example of this atmarker/renderers/__init__.py:BaseRender.extract_block_html
.polygon
- the 4-corner polygon of the page, in (x1,y1), (x2,y2), (x3, y3), (x4, y4) format. (x1,y1) is the top left, and coordinates go clockwise.children
- the child blocks.
The child blocks have two additional keys:
section_hierarchy
- indicates the sections that the block is part of.1
indicates an h1 tag,2
an h2, and so on.images
- base64 encoded images. The key will be the block id, and the data will be the encoded image.
Note that child blocks of pages can have their own children as well (a tree structure).
{
"id": "/page/10/Page/366",
"block_type": "Page",
"html": "<content-ref src='/page/10/SectionHeader/0'></content-ref><content-ref src='/page/10/SectionHeader/1'></content-ref><content-ref src='/page/10/Text/2'></content-ref><content-ref src='/page/10/Text/3'></content-ref><content-ref src='/page/10/Figure/4'></content-ref><content-ref src='/page/10/SectionHeader/5'></content-ref><content-ref src='/page/10/SectionHeader/6'></content-ref><content-ref src='/page/10/TextInlineMath/7'></content-ref><content-ref src='/page/10/TextInlineMath/8'></content-ref><content-ref src='/page/10/Table/9'></content-ref><content-ref src='/page/10/SectionHeader/10'></content-ref><content-ref src='/page/10/Text/11'></content-ref>",
"polygon": [[0.0, 0.0], [612.0, 0.0], [612.0, 792.0], [0.0, 792.0]],
"children": [
{
"id": "/page/10/SectionHeader/0",
"block_type": "SectionHeader",
"html": "<h1>Supplementary Material for <i>Subspace Adversarial Training</i> </h1>",
"polygon": [
[217.845703125, 80.630859375], [374.73046875, 80.630859375],
[374.73046875, 107.0],
[217.845703125, 107.0]
],
"children": null,
"section_hierarchy": {
"1": "/page/10/SectionHeader/1"
},
"images": {}
},
...
]
}
All output formats will return a metadata dictionary, with the following fields:
{
"table_of_contents": [
{
"title": "Introduction",
"heading_level": 1,
"page_id": 0,
"polygon": [...]
}
], // computed PDF table of contents
"page_stats": [
{
"page_id": 0,
"text_extraction_method": "pdftext",
"block_counts": [("Span", 200), ...]
},
...
]
}
Marker is easy to extend. The core units of marker are:
Providers
, atmarker/providers
. These provide information from a source file, like a PDF.Builders
, atmarker/builders
. These generate the initial document blocks and fill in text, using info from the providers.Processors
, atmarker/processors
. These process specific blocks, for example the table formatter is a processor.Renderers
, atmarker/renderers
. These use the blocks to render output.Schema
, atmarker/schema
. The classes for all the block types.Converters
, atmarker/converters
. They run the whole end to end pipeline.
To customize processing behavior, override the processors
. To add new output formats, write a new renderer
. For additional input formats, write a new provider.
Processors and renderers can be directly passed into the base PDFConverter
, so you can specify your own custom processing easily.
There is a very simple API server you can run like this:
pip install -U uvicorn fastapi python-multipart
marker_server --port 8001
This will start a fastapi server that you can access at localhost:8001
. You can go to localhost:8001/docs
to see the endpoint options.
You can send requests like this:
import requests
import json
post_data = {
'filepath': 'FILEPATH',
# Add other params here
}
requests.post("http://localhost:8001/marker", data=json.dumps(post_data)).json()
Note that this is not a very robust API, and is only intended for small-scale use. If you want to use this server, but want a more robust conversion option, you can use the hosted Datalab API.
There are some settings that you may find useful if things aren't working the way you expect:
- Make sure to set
force_ocr
if you see garbled text - this will re-OCR the document. TORCH_DEVICE
- set this to force marker to use a given torch device for inference.- If you're getting out of memory errors, decrease worker count. You can also try splitting up long PDFs into multiple files.
Pass the debug
option to activate debug mode. This will save images of each page with detected layout and text, as well as output a json file with additional bounding box information.
Benchmarking PDF extraction quality is hard. I've created a test set by finding books and scientific papers that have a pdf version and a latex source. I convert the latex to text, and compare the reference to the output of text extraction methods. It's noisy, but at least directionally correct.
Speed
Method | Average Score | Time per page | Time per document |
---|---|---|---|
marker | 0.625115 | 0.234184 | 21.545 |
Accuracy
Method | thinkpython.pdf | switch_trans.pdf | thinkdsp.pdf | crowd.pdf | thinkos.pdf | multicolcnn.pdf |
---|---|---|---|---|---|---|
marker | 0.720347 | 0.592002 | 0.70468 | 0.515082 | 0.701394 | 0.517184 |
Peak GPU memory usage during the benchmark is 6GB
for marker. Benchmarks were run on an A10.
Throughput
Marker takes about 6GB of VRAM on average per task, so you can convert 8 documents in parallel on an A6000.
You can benchmark the performance of marker on your machine. Install marker manually with:
git clone https://github.com/VikParuchuri/marker.git
poetry install
Download the benchmark data here and unzip. Then run the overall benchmark like this:
python benchmarks/overall.py data/pdfs data/references report.json
This work would not have been possible without amazing open source models and datasets, including (but not limited to):
- Surya
- Texify
- Pypdfium2/pdfium
- DocLayNet from IBM
Thank you to the authors of these models and datasets for making them available to the community!