@@ -27,7 +27,7 @@ use std::sync::{Arc, Mutex};
2727
2828use fleischwolf_core:: { DoclingDocument , Node } ;
2929
30- pub use mets:: convert_mets_gbs;
30+ pub use mets:: { convert_mets_gbs, convert_mets_gbs_with_options } ;
3131pub use pdfium_backend:: { PdfDocument , PdfPage , TextCell } ;
3232
3333/// Errors from the PDF backend. Detailed and surfaced (never silently skipped).
@@ -90,11 +90,15 @@ struct Worker {
9090}
9191
9292impl Worker {
93- fn load ( intra : usize ) -> Result < Self , PdfError > {
93+ fn load ( intra : usize , no_table_former : bool ) -> Result < Self , PdfError > {
9494 Ok ( Self {
9595 layout : layout:: LayoutModel :: load_with ( intra) . map_err ( PdfError :: Layout ) ?,
9696 ocr : None ,
97- tables : tableformer:: TableFormer :: load_with ( intra) ,
97+ tables : if no_table_former {
98+ None
99+ } else {
100+ tableformer:: TableFormer :: load_with ( intra)
101+ } ,
98102 } )
99103 }
100104
@@ -208,6 +212,9 @@ pub struct Pipeline {
208212 target_workers : usize ,
209213 /// Page count at/above which the parallel pool is worth its load cost.
210214 parallel_min : usize ,
215+ /// Skip loading/running TableFormer; table regions fall back to geometric
216+ /// reconstruction. See [`Pipeline::no_table_former`].
217+ no_table_former : bool ,
211218}
212219
213220impl Pipeline {
@@ -220,13 +227,25 @@ impl Pipeline {
220227 pool : Vec :: new ( ) ,
221228 target_workers : pdf_worker_count ( ) ,
222229 parallel_min : pdf_parallel_min ( ) ,
230+ no_table_former : false ,
223231 } )
224232 }
225233
234+ /// Skip loading and running the TableFormer table-structure model. Table
235+ /// regions still get emitted, but reconstructed geometrically from cell
236+ /// positions instead of via the ONNX model's predicted structure — faster
237+ /// (no model load, no per-table inference) at the cost of table fidelity.
238+ /// No effect if a worker is already loaded; set this before the first
239+ /// conversion.
240+ pub fn no_table_former ( mut self , disable : bool ) -> Self {
241+ self . no_table_former = disable;
242+ self
243+ }
244+
226245 /// The full-intra serial worker, loaded on first use.
227246 fn primary ( & mut self ) -> Result < & mut Worker , PdfError > {
228247 if self . primary . is_none ( ) {
229- self . primary = Some ( Worker :: load ( intra_threads ( ) ) ?) ;
248+ self . primary = Some ( Worker :: load ( intra_threads ( ) , self . no_table_former ) ?) ;
230249 }
231250 Ok ( self . primary . as_mut ( ) . unwrap ( ) )
232251 }
@@ -507,9 +526,10 @@ impl Pipeline {
507526 return Ok ( ( ) ) ;
508527 }
509528 let intra = pdf_intra ( ) ;
529+ let no_table_former = self . no_table_former ;
510530 let loaded: Vec < Result < Worker , PdfError > > = std:: thread:: scope ( |s| {
511531 let handles: Vec < _ > = ( 0 ..need)
512- . map ( |_| s. spawn ( move || Worker :: load ( intra) ) )
532+ . map ( |_| s. spawn ( move || Worker :: load ( intra, no_table_former ) ) )
513533 . collect ( ) ;
514534 handles. into_iter ( ) . map ( |h| h. join ( ) . unwrap ( ) ) . collect ( )
515535 } ) ;
@@ -567,16 +587,53 @@ pub fn convert(
567587 password : Option < & str > ,
568588 name : & str ,
569589) -> Result < DoclingDocument , PdfError > {
570- Pipeline :: new ( ) ?. convert ( bytes, password, name)
590+ convert_with_options ( bytes, password, name, false )
591+ }
592+
593+ /// Like [`convert`], but optionally skips loading/running TableFormer (see
594+ /// [`Pipeline::no_table_former`]).
595+ pub fn convert_with_options (
596+ bytes : & [ u8 ] ,
597+ password : Option < & str > ,
598+ name : & str ,
599+ no_table_former : bool ,
600+ ) -> Result < DoclingDocument , PdfError > {
601+ Pipeline :: new ( ) ?
602+ . no_table_former ( no_table_former)
603+ . convert ( bytes, password, name)
571604}
572605
573606/// Convenience one-shot image conversion (loads the pipeline per call).
574607pub fn convert_image ( bytes : & [ u8 ] , name : & str ) -> Result < DoclingDocument , PdfError > {
575- Pipeline :: new ( ) ?. convert_image ( bytes, name)
608+ convert_image_with_options ( bytes, name, false )
609+ }
610+
611+ /// Like [`convert_image`], but optionally skips loading/running TableFormer (see
612+ /// [`Pipeline::no_table_former`]).
613+ pub fn convert_image_with_options (
614+ bytes : & [ u8 ] ,
615+ name : & str ,
616+ no_table_former : bool ,
617+ ) -> Result < DoclingDocument , PdfError > {
618+ Pipeline :: new ( ) ?
619+ . no_table_former ( no_table_former)
620+ . convert_image ( bytes, name)
576621}
577622
578623/// Convert pre-segmented pages (image + already-known text cells, e.g. METS/hOCR
579624/// scans) through the shared layout + assembly pipeline.
580625pub fn convert_pages ( pages : Vec < PdfPage > , name : & str ) -> Result < DoclingDocument , PdfError > {
581- Pipeline :: new ( ) ?. process_pages ( pages, name)
626+ convert_pages_with_options ( pages, name, false )
627+ }
628+
629+ /// Like [`convert_pages`], but optionally skips loading/running TableFormer (see
630+ /// [`Pipeline::no_table_former`]).
631+ pub fn convert_pages_with_options (
632+ pages : Vec < PdfPage > ,
633+ name : & str ,
634+ no_table_former : bool ,
635+ ) -> Result < DoclingDocument , PdfError > {
636+ Pipeline :: new ( ) ?
637+ . no_table_former ( no_table_former)
638+ . process_pages ( pages, name)
582639}
0 commit comments