Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ragaeeb/kokokor/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The TextBlock type represents a processed text unit that has been assembled from raw OCR observations and enriched with semantic metadata about its role within the document.
TextBlock extends the base Observation type with additional metadata flags that guide formatting and structure reconstruction.

Type Definition

type TextBlock = Observation & {
  isCentered?: boolean;
  isFootnote?: boolean;
  isHeading?: boolean;
  isPoetic?: boolean;
};
Reference: src/types.ts:389

Base Observation Type

Every TextBlock includes the fundamental properties from Observation:
type Observation = {
  bbox: BoundingBox;  // Position and dimensions
  text: string;       // Recognized text content
};

type BoundingBox = {
  x: number;          // Left edge position (pixels)
  y: number;          // Top edge position (pixels)
  width: number;      // Width (pixels)
  height: number;     // Height (pixels)
};
Reference: src/types.ts:170, src/types.ts:11

Metadata Properties

isCentered

Indicates whether the text is centered on the page with adequate whitespace on both sides.
Text is considered centered when it meets both conditions:
  1. Center Point Alignment: The text’s center is within tolerance of the page center
  2. Sufficient Margins: Adequate whitespace exists on both left and right sides
const pageCenter = imageWidth / 2;
const tolPx = imageWidth * options.centerToleranceRatio; // Default: 5%
const centerX = bbox.x + bbox.width / 2;

const isCenterPointCentered = Math.abs(centerX - pageCenter) <= tolPx;

const leftMargin = bbox.x;
const rightMargin = imageWidth - (bbox.x + bbox.width);
const minMargin = imageWidth * options.minMarginRatio; // Default: 10%

const hasSufficientMargins = leftMargin >= minMargin && rightMargin >= minMargin;

return isCenterPointCentered && hasSufficientMargins;
Reference: src/utils/layout.ts:36
  • Document titles and headings
  • Poetry and verse content
  • Section headers
  • Epigraphs or quotes
{
  centerToleranceRatio: 0.05,  // 5% of page width
  minMarginRatio: 0.1,         // 10% of page width
}

isFootnote

Indicates whether the text appears below horizontal line separators and should be treated as footnote content.
Footnotes are identified by their position relative to horizontal lines:
// Internal: Finds the last horizontal separator line
const footerLineY = findLastHorizontalSeparator(
  options.rectangles || [],
  options.horizontalLines || [],
  options.pixelTolerance
);

if (footerLineY !== undefined && o.bbox.y > footerLineY) {
  e.isFootnote = true;
}
The internal algorithm:
  • Filters out horizontal lines contained within rectangles (headers)
  • Excludes lines very close to the top edge (artifacts)
  • Returns the Y-coordinate of the last qualifying line
  • Academic citations
  • Reference notes
  • Supplementary information
  • Translation notes
Footnotes are processed separately from body text:
const bodyBlocks = groupProseToParagraphs(
  textLines.filter((t) => !t.isFootnote),
  verticalJumpFactor,
  widthTolerance
);

const footerBlocks = groupProseToParagraphs(
  textLines.filter((t) => t.isFootnote),
  verticalJumpFactor,
  widthTolerance
);
Reference: src/utils/paragraphs.ts:238

isHeading

Indicates whether the text represents a heading, typically identified by visual presentation.
Headings are identified when text is contained within rectangular borders:
const isObservationInsideRectangle = options.rectangles?.some(
  (rectangle) => isBoundingBoxContained(
    o.bbox,
    rectangle,
    options.pixelTolerance
  )
);

if (isObservationInsideRectangle) {
  e.isHeading = true;
}
Reference: src/utils/paragraphs.ts:122The containment check includes pixel tolerance for OCR inaccuracies:
const outerLeft = outer.x - tolerance;
const outerRight = outer.x + outer.width + tolerance;
const outerTop = outer.y - tolerance;
const outerBottom = outer.y + outer.height + tolerance;

return innerLeft >= outerLeft &&
       innerRight <= outerRight &&
       innerTop >= outerTop &&
       innerBottom <= outerBottom;
Reference: src/utils/layout.ts:116
Headings receive special formatting with blank lines after them:
if (t.isHeading) {
  return [t.text, ''];  // Add blank line after heading
}
Reference: src/index.ts:20Output:
Chapter Title

First paragraph of chapter...
  • Chapter titles
  • Section headings
  • Boxed announcements
  • Highlighted content

isPoetic

Indicates whether the text is identified as poetry or verse content that should preserve line breaks.
Poetry detection uses multiple coordinated heuristics:
1

Paired Hemistichs

Two short, balanced lines that appear to be halves of a verse
if (group.length === 2) {
  return isPoetryPair(group[0], group[1], imageWidth, options);
}
2

Wide Poetic Lines

Single centered lines with low word density
if (group.length === 1 && minWidthRatioForMerged !== null) {
  return isWidePoeticLine(
    group[0],
    imageWidth,
    avgProseWordDensity,
    options
  );
}
Reference: src/utils/poetry.ts:365See Poetry Detection for detailed algorithms.
Poetic lines are never merged into paragraphs:
for (const line of textLines) {
  if (line.isPoetic) {
    // Add directly to result without merging
    result.push(line);
  } else {
    // Accumulate for paragraph grouping
    current.push(line);
  }
}
Reference: src/utils/paragraphs.ts:204
  • Classical poetry
  • Quranic verses
  • Song lyrics
  • Formatted verse content

Real-World Examples

Example 1: Centered Heading

{
  bbox: { x: 298, y: 50, width: 286, height: 24 },
  text: "Chapter One",
  isCentered: true,
  isHeading: false,
  isFootnote: false,
  isPoetic: false
}
Analysis:
  • Page width: 960px
  • Text center: 298 + 286/2 = 441px
  • Page center: 960/2 = 480px
  • Difference: 39px (4% of page width) ✓
  • Left margin: 298px (31% of page width) ✓
  • Right margin: 376px (39% of page width) ✓

Example 2: Poetry Pair (Hemistichs)

// First hemistich
{
  bbox: { x: 150, y: 200, width: 220, height: 18 },
  text: "في البدء كانت الكلمة",
  isCentered: true,
  isPoetic: true
}

// Second hemistich
{
  bbox: { x: 430, y: 200, width: 210, height: 18 },
  text: "والكلمة عند الله",
  isCentered: false,  // Individual part not centered
  isPoetic: true
}

// Combined output: "في البدء كانت الكلمة والكلمة عند الله"
Detection:
  • Similar widths: 220px vs 210px (5% difference) ✓
  • Word counts: 3 vs 3 (equal) ✓
  • Vertical gap: 0px (same line) ✓
  • Combined centering: (150 + 640) / 2 = 395px, center = 400px ✓

Example 3: Footnote

{
  bbox: { x: 50, y: 1050, width: 400, height: 16 },
  text: "1. See reference on page 42",
  isCentered: false,
  isFootnote: true,
  isHeading: false,
  isPoetic: false
}
Detection:
  • Last horizontal line Y: 1000px
  • Text Y position: 1050px (below line) ✓

Example 4: Regular Prose

{
  bbox: { x: 50, y: 300, width: 720, height: 20 },
  text: "This is a regular paragraph of prose text that spans most of the page width.",
  isCentered: false,   // Left-aligned
  isFootnote: false,   // In main body
  isHeading: false,    // Not in rectangle
  isPoetic: false      // High word density, not centered
}

Using Metadata in Processing

Conditional Formatting

function formatBlock(block: TextBlock): string {
  let output = block.text;

  if (block.isHeading) {
    output = `\n# ${output}\n`;
  }

  if (block.isPoetic) {
    output = `> ${output}`;  // Quote formatting
  }

  if (block.isFootnote) {
    output = `[^]: ${output}`;
  }

  return output;
}

Filtering

// Extract only main body content
const bodyText = paragraphs
  .filter(p => !p.isFootnote && !p.isPoetic)
  .map(p => p.text)
  .join('\n\n');

// Extract only poetry
const poems = lines
  .filter(l => l.isPoetic)
  .map(l => l.text);

// Extract all headings for TOC
const tableOfContents = lines
  .filter(l => l.isHeading)
  .map(l => l.text);

Export to Different Formats

function toMarkdown(blocks: TextBlock[]): string {
  return blocks.map(block => {
    if (block.isHeading) {
      return `## ${block.text}\n`;
    }
    if (block.isPoetic) {
      return `> ${block.text}  `; // Two spaces for line break
    }
    if (block.isFootnote) {
      return `<sup>${block.text}</sup>`;
    }
    return block.text;
  }).join('\n');
}

Metadata Interaction

Multiple Flags

A TextBlock can have multiple metadata flags:
{
  text: "Centered Poetic Line",
  isCentered: true,   // ✓ Visually centered
  isPoetic: true,     // ✓ Identified as poetry
  isFootnote: false,
  isHeading: false
}

Priority Rules

When multiple flags are present, formatting follows this priority:
  1. isHeading (highest priority - structural)
  2. isPoetic (preserve line breaks)
  3. isFootnote (section separation)
  4. isCentered (visual hint)

API Reference

Observation Type

Base type for OCR text observations

BoundingBox Type

Position and dimension properties

ReconstructResult

Complete pipeline output structure

Pipeline Overview

How TextBlocks flow through processing

Next Steps

Poetry Detection

Deep dive into poetry identification

Processing Pipeline

Understand the three-stage pipeline

Build docs developers (and LLMs) love