diff --git a/CHANGELOG.md b/CHANGELOG.md index 75b8f79..a367b75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,13 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- -## [Unreleased] +## [1.26.11] — 2026-09-10 + +### Added +- **Comprehensive OCR Element & Grounding Recognition.** Full support for layout-aware vision OCR models (Baidu Unlimited-OCR, DeepSeek-OCR, DeepSeek-OCR-2, PaddleOCR). + - Explicit element parsing for Headlines, Paragraphs, Sidenotes/Sidebars, Page Numbers/Folios, Images/Figures, Graphics/Charts/Maps/Treemaps/Diagrams, Tables, and Code blocks. + - Native support for grounding coordinate tokens (`<|ref|>...<|/ref|>`, `<|det|>...<|/det|>`, single/double brackets, normalized `0..1000`/`0..1` scale, and pixel space). +- **Ingestion Preview & Visual Thumbnail Cards.** The live ingestion preview formats non-text visual regions (`[CHART]`, `[MAP]`, `[TABLE]`, `[SIDENOTE]`, `[CODE]`) with clean badges instead of blank gaps, and the magazine review sidebar renders interactive cropped image thumbnails (`mag-block-thumb`) for detected visual figures. --- diff --git a/VERSION b/VERSION index 63d92c7..fabf663 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.26.10 +1.26.11 diff --git a/core/magazine.py b/core/magazine.py index 6b378e4..3b78933 100644 --- a/core/magazine.py +++ b/core/magazine.py @@ -738,75 +738,124 @@ def _require_fitz(): # engine invents) is treated as ambiguous body copy and still goes through # classify_blocks() for finer distinctions (byline, deck, ...). _VISION_LABEL_TYPES = { - "footer": BLOCK_FURNITURE, - "header": BLOCK_FURNITURE, + # Headlines / Titles / Headers + "title": BLOCK_TITLE, + "headline": BLOCK_TITLE, + "article_headline": BLOCK_TITLE, + "main_title": BLOCK_TITLE, + "heading": BLOCK_HEADING, + "header": BLOCK_HEADING, + "section_heading": BLOCK_HEADING, + "subheading": BLOCK_HEADING, + "subtitle": BLOCK_HEADING, + "deck": BLOCK_HEADING, + + # Paragraphs / Text / Body + "paragraph": BLOCK_BODY, + "body": BLOCK_BODY, + "content": BLOCK_BODY, + "list": BLOCK_BODY, + "list_item": BLOCK_BODY, + "author": BLOCK_BYLINE, + "byline": BLOCK_BYLINE, + + # Sidenotes / Sidebars / Quotes / Footnotes + "sidenote": BLOCK_SIDEBAR, + "sidebar": BLOCK_SIDEBAR, + "aside_text": BLOCK_SIDEBAR, + "aside": BLOCK_SIDEBAR, + "footnote": BLOCK_SIDEBAR, + "footnotes": BLOCK_SIDEBAR, + "note": BLOCK_SIDEBAR, + "commentary": BLOCK_SIDEBAR, + "quote": BLOCK_PULLQUOTE, + "pullquote": BLOCK_PULLQUOTE, + + # Page numbers / Furniture / Folios / Sources "page_number": BLOCK_FURNITURE, + "pagenumber": BLOCK_FURNITURE, + "page": BLOCK_FURNITURE, + "header_text": BLOCK_FURNITURE, + "footer": BLOCK_FURNITURE, + "running_header": BLOCK_FURNITURE, + "running_footer": BLOCK_FURNITURE, + "folio": BLOCK_FURNITURE, + "head": BLOCK_FURNITURE, "source": BLOCK_FURNITURE, "sources": BLOCK_FURNITURE, "citation": BLOCK_FURNITURE, "citations": BLOCK_FURNITURE, - "footnote": BLOCK_FURNITURE, - "footnotes": BLOCK_FURNITURE, - "title": BLOCK_TITLE, - "heading": BLOCK_HEADING, - "caption": BLOCK_CAPTION, - "image_caption": BLOCK_CAPTION, - "table_caption": BLOCK_CAPTION, - "headline": BLOCK_TITLE, - "article_headline": BLOCK_TITLE, - "paragraph": BLOCK_BODY, - "section_heading": BLOCK_HEADING, - "author": BLOCK_BYLINE, - "byline": BLOCK_BYLINE, - "sidenote": BLOCK_SIDEBAR, - "quote": BLOCK_PULLQUOTE, - "pullquote": BLOCK_PULLQUOTE, + + # Images / Figures / Photos "image": BLOCK_FIGURE, "figure": BLOCK_FIGURE, "picture": BLOCK_FIGURE, + "photo": BLOCK_FIGURE, + "illustration": BLOCK_FIGURE, + "non_text": BLOCK_FIGURE, + "nontext": BLOCK_FIGURE, + "drawing": BLOCK_FIGURE, + + # Graphics / Charts / Diagrams / Maps / Treemaps "chart": BLOCK_CHART, "graphic": BLOCK_CHART, "infographic": BLOCK_CHART, "statistics": BLOCK_CHART, "diagram": BLOCK_CHART, + "map": BLOCK_CHART, + "treemap": BLOCK_CHART, + "plot": BLOCK_CHART, + "flowchart": BLOCK_CHART, + + # Tables "table": BLOCK_TABLE, - "formula": BLOCK_CODE, - "list": BLOCK_BODY, + "grid": BLOCK_TABLE, + "matrix": BLOCK_TABLE, + + # Code / Formulas / Math "code": BLOCK_CODE, "code_block": BLOCK_CODE, + "formula": BLOCK_CODE, + "equation": BLOCK_CODE, + "math": BLOCK_CODE, + + # Captions + "caption": BLOCK_CAPTION, + "image_caption": BLOCK_CAPTION, + "figure_caption": BLOCK_CAPTION, + "table_caption": BLOCK_CAPTION, + + # Ads "advertisement": BLOCK_AD, "advert": BLOCK_AD, "ad": BLOCK_AD, - "aside_text": BLOCK_SIDEBAR, } # One label + its four-number bbox, anchoring a region; content runs until -# the next such marker. Confirmed live against Baidu Unlimited-OCR's actual -# output shape: "text [241, 90, 588, 249]Battery technology is entering...". -_VISION_REGION = re.compile(r"(?m)^(\w+)\s*\[\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\]") -# Native Baidu Unlimited-OCR document-parsing output. The model card emits -# one marker per detected region, such as -# ``<|det|>table [0,0,999,999]<|/det|>...``. Its categories are precisely -# what we need to retain charts/tables/images as visual blocks instead of -# treating their axis labels as article prose. +# the next such marker. Tolerates DeepSeek-OCR / Unlimited-OCR / PaddleOCR shapes +# like "text [241, 90, 588, 249]", "<|ref|>title<|/ref|><|det|>[[x0, y0, x1, y1]]<|/det|>", etc. +_VISION_REGION = re.compile( + r"(?m)" + r"(?:<\|ref\|>\s*(?P[^<]+?)\s*<\|/ref\|>\s*)?" + r"(?:<\|det\|>\s*)?" + r"(?P