OCR Engine Integration#
Docling's OCR subsystem is fully plugin-driven: every OCR engine — built-in or third-party — is registered through the same factory and entry-point mechanism. The core components are:
BaseOcrModel— abstract base class every engine must subclass.OcrFactory— a thinBaseFactory[BaseOcrModel]specialization that uses"ocr_engines"as its plugin attribute name.BaseFactory— the generic plugin loading, filtering, and registration logic shared by all Docling factory types (layout, table-structure, picture-description).get_ocr_factory()— the@lru_cache-decorated accessor that lazily constructs theOcrFactoryand loads all plugins.
get_ocr_factory(allow_external_plugins)
└─ OcrFactory
└─ BaseFactory.load_from_plugins()
└─ PluginManager (pluggy) → discovers "docling" entry points
└─ calls ocr_engines() → returns list of engine classes
└─ BaseFactory.register(cls, ...) ← maps OcrOptions subclass → model class
Abstract Interface (BaseOcrModel)#
Every custom OCR engine must implement two abstract members :
| Member | Signature | Purpose |
|---|---|---|
__call__ | (conv_res, page_batch) → Iterable[Page] | Yield pages after populating page.cells with TextCell objects |
get_options_type | classmethod → type[OcrOptions] | Return the OcrOptions subclass that uniquely identifies this engine |
BaseOcrModel also inherits BasePageModel and BaseModelWithOptions . Its constructor signature is :
def __init__(self, *, enabled: bool, artifacts_path: Path | None,
options: OcrOptions, accelerator_options: AcceleratorOptions)
BaseOcrModel provides ready-made helpers that implementations should call:
get_ocr_rects(page)— computes which page regions need OCR based onOcrOptions.mode.post_process_cells(ocr_cells, page, conv_res)— merges OCR-generatedTextCellobjects with existing PDF text cells and writes the result back intopage.parsed_page.
See NemotronOcrModel for a production example: it subclasses BaseOcrModel, declares get_options_type returning NemotronOcrOptions , and delegates region detection + post-processing entirely to the base class methods.
Options Class#
Each engine needs a paired OcrOptions subclass. The kind class variable on the options class is the string selector used by the factory and the CLI:
class YourOcrOptions(OcrOptions):
kind: ClassVar[str] = "your_engine" # unique identifier
# engine-specific fields...
All option classes inherit force_full_page_ocr and bitmap_area_threshold from OcrOptions. The kind string must be globally unique — duplicates raise a ValueError at registration time .
Plugin Registration#
Entry-point declaration#
Register your package under the "docling" entry-point group :
# pyproject.toml
[project.entry-points."docling"]
your_plugin_name = "your_package.module"
See the official plugin docs for Poetry v1 / setup.cfg / setup.py equivalents.
Registration function#
Expose an ocr_engines() function in the referenced module :
def ocr_engines():
return {"ocr_engines": [YourOcrModel]}
The function name and dict key must both be "ocr_engines" — this is the plugin_attr_name wired into OcrFactory.__init__. Docling's own built-in engines follow the identical pattern in defaults.py, which registers 8 engines including EasyOcrModel, TesseractOcrCliModel, NemotronOcrModel, and OcrAutoModel.
The built-in entry point (docling_defaults) is declared in pyproject.toml under [project.entry-points.docling] and points to docling.models.plugins.defaults.
How factory loading works#
BaseFactory.load_from_plugins() creates a PluginManager, loads all setuptools entry points under "docling" , and for each discovered plugin:
- Checks the module name against the
"docling."prefix . External modules are skipped unlessallow_external_plugins=True. - Calls
ocr_engines()on the module . - Calls
register(cls, ...)for each returned class, mappingcls.get_options_type()→cls.
Enabling a Third-Party Engine at Runtime#
External plugins (modules not starting with "docling.") are disabled by default for security . Enable them explicitly :
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
pipeline_options = PdfPipelineOptions()
pipeline_options.allow_external_plugins = True # unlock external plugins
pipeline_options.ocr_options = YourOcrOptions() # your options class selects your engine
doc_converter = DocumentConverter(
format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=pipeline_options)}
)
CLI equivalent :
docling --allow-external-plugins --ocr-engine=your_engine
Important: get_ocr_factory is @lru_cache-keyed on allow_external_plugins , so this flag must be set before the first pipeline is constructed.
To enumerate all registered engines at runtime (including external plugins):
from docling.models.factories import get_ocr_factory
print(get_ocr_factory(allow_external_plugins=True).registered_kind)
Key Source Files#
| File | Purpose |
|---|---|
docling/models/base_ocr_model.py | BaseOcrModel — abstract interface and shared helpers |
docling/models/factories/ocr_factory.py | OcrFactory — thin factory subclass for OCR |
docling/models/factories/base_factory.py | BaseFactory — generic plugin loading, filtering, registration |
docling/models/factories/__init__.py | get_ocr_factory() — cached factory accessor |
docling/models/plugins/defaults.py | Built-in ocr_engines() registration with all 8 engines |
docs/concepts/plugins.md | Official plugin authoring guide |
docling/models/stages/ocr/nemotron_ocr_model.py | Production reference implementation of BaseOcrModel |
docling/datamodel/pipeline_options.py | OcrOptions base and all built-in options classes |