UNPKG

kokokor

Version:

A lightweight TypeScript library designed to reconstruct paragraphs from OCRed inputs.

558 lines 21.2 kB
//#region src/types.d.ts /** * Represents a rectangular bounding box with position and dimensions. * Used to define the location and size of text elements and structural components * within a document coordinate system. * * The coordinate system typically uses: * - Origin (0,0) at the top-left corner of the document * - X-axis extending rightward (for LTR text) or leftward (for RTL text after normalization) * - Y-axis extending downward */ type BoundingBox = Size & { /** * The x-coordinate of the top-left corner of the bounding box in pixels. * This coordinate may be normalized depending on text direction: * - For LTR (left-to-right) text: represents distance from left edge * - For RTL (right-to-left) text: may be adjusted during processing */ x: number; /** * The y-coordinate of the top-left corner of the bounding box in pixels. * Represents the distance from the top edge of the document. */ y: number; }; /** * Configuration options for determining if text content is centered on a page. * These settings control the tolerance and margin requirements for center detection, * which is important for identifying titles, headings, and poetic content. */ type CenteringOptions = { /** * The tolerance for center point alignment as a ratio of image width. * This determines how precisely text must be centered to be considered "centered". * * For example: * - 0.05 means the observation's center can be within 5% of the page width * from the true center and still be considered centered * - 0.02 would require more precise centering (within 2%) * - 0.1 would allow looser centering requirements (within 10%) * * @default 0.05 */ readonly centerToleranceRatio: number; /** * The minimum margin required on each side as a ratio of image width. * This ensures that "centered" text has adequate whitespace around it. * * For example: * - 0.1 means there must be at least 10% of the page width as whitespace * on both the left and right sides of the observation * - 0.2 would require larger margins (20% on each side) * - 0.05 would allow tighter margins (5% on each side) * * @default 0.1 */ readonly minMarginRatio: number; }; /** * Configuration options for the main text line mapping function. * * This type combines all the configuration needed to process OCR observations * into structured text lines, including poetry detection, centering analysis, * and layout element detection. */ type MapObservationsToTextLinesOptions = CenteringOptions & { /** * Optional array of horizontal line elements detected in the document. * These are typically used to identify sections, headers, footers, or decorative elements. * When provided, text appearing below these lines may be classified as footnotes. */ horizontalLines?: BoundingBox[]; /** Are the coordinates from a RTL language? If it is we would flip the x-axis. */ isRTL?: boolean; /** * Optional fixed line height factor for grouping observations into lines. * If not provided, the system will compute an adaptive factor based on document analysis. * * Typical values: * - 0.5: Very tight line grouping * - 1.0: Standard line height * - 1.5: Generous line spacing tolerance */ lineHeightFactor?: number; /** * Optional logging function for debugging and monitoring the text processing pipeline. * When provided, the system will output detailed information about its decisions * and intermediate processing steps. * * @param message - Primary log message * @param args - Additional arguments for formatting or context */ log?: (message: string, ...args: any[]) => void; /** * Additional vertical tolerance in pixels (at 72 DPI) for line grouping. * This value is automatically scaled based on the document's actual DPI. * * Higher values make line grouping more permissive (more text on same line). * Lower values make line grouping stricter (more separate lines). * * @default 5 pixels at 72 DPI */ pixelTolerance?: number; /** * Configuration options for poetry detection algorithms. * If not provided, default poetry detection settings will be used. * Set to null or undefined to disable poetry detection entirely. */ poetryDetectionOptions?: Partial<PoetryDetectionOptions>; /** * Delimiter used when merging a detected poetry pair (hemistichs) into a single line. * * Example: `" ... "` formats a pair as `صدر ... عجز`. * * @default " " */ poetryPairDelimiter?: string; /** * Optional array of rectangular elements detected in the document. * These are typically used to identify text boxes, highlighted sections, or headers. * When provided, text within these rectangles may be classified as headings. */ rectangles?: BoundingBox[]; }; /** * Options for grouping text lines into paragraphs. * * These options replace positional arguments with named fields so callers can * tune behavior without relying on parameter ordering. */ type ParagraphOptions = { /** * Factor for detecting paragraph breaks based on vertical spacing. * * Higher values make break detection stricter. * * @default 2 */ verticalJumpFactor?: number; /** * Threshold for identifying short lines that indicate paragraph endings. * * Lower values mark fewer lines as "short". * * @default 0.85 */ widthTolerance?: number; }; /** * Represents a basic text observation extracted from OCR processing. * * This is the fundamental unit of OCR output, containing both the recognized * text content and its precise location within the document. Observations * typically represent individual words or short phrases as identified by * the OCR engine. */ type Observation = { /** * The bounding box defining the exact position and dimensions of the text * within the document coordinate system. This includes: * - Position (x, y) of the top-left corner * - Dimensions (width, height) of the text area */ bbox: BoundingBox; /** * The recognized text content of the observation. * This is the actual text string extracted by the OCR engine, * which may include punctuation, numbers, or special characters. */ text: string; }; /** * Canonical page metadata required for reconstruction. */ type PageContext = { /** * Page width in pixels. */ width: number; /** * Page height in pixels. */ height: number; /** * Horizontal DPI. */ dpiX: number; /** * Vertical DPI. */ dpiY: number; }; /** * Optional layout primitives extracted from a page. */ type LayoutElements = { /** * Horizontal separators used for footnote boundary detection. */ horizontalLines?: BoundingBox[]; /** * Rectangles used for heading/boxed-content detection. */ rectangles?: BoundingBox[]; }; /** * Input payload for one-shot paragraph reconstruction. */ type ReconstructInput = { /** * OCR observations to reconstruct. */ observations: Observation[]; /** * Page geometry and DPI context. */ page: PageContext; /** * Optional structural layout hints. */ layout?: LayoutElements; }; /** * Optional knobs for one-shot paragraph reconstruction. */ type ReconstructOptions = { /** * Line-detection options. */ line?: Partial<MapObservationsToTextLinesOptions>; /** * Paragraph-detection options. */ paragraph?: ParagraphOptions; /** * Text formatting options. */ format?: { /** * Optional symbol to insert before first footnote. */ footerSymbol?: string; }; }; /** * Configuration options to fine-tune the poetry detection algorithm. * * Poetry detection uses multiple heuristics to identify poetic content: * 1. **Paired hemistichs**: Two short lines that appear to be halves of a verse * 2. **Merged lines**: Single lines with distinctive spacing/density characteristics * 3. **Centering**: Lines that are centered with appropriate margins * * These options allow fine-tuning of each heuristic to balance precision and recall * for different types of documents and poetry styles. */ type PoetryDetectionOptions = Partial<CenteringOptions> & { /** * Maximum allowed vertical gap between observations to be considered a poetry pair. * This controls how close two lines must be to be considered hemistichs (halves of a verse). * * The gap is measured as a ratio of the average height of the two observations: * - 2.0 means the gap can be up to 200% of the average line height * - 1.5 would require closer spacing (150% of average height) * - 3.0 would allow wider spacing (300% of average height) * * @default 2.0 (200% of average height) */ maxVerticalGapRatio: number; /** * For merged lines heuristic: The minimum width a line must have to be considered. * This prevents very short lines (like page numbers) from being analyzed for poetry. * * Specified as a ratio of the image width: * - 0.6 means the line must span at least 60% of the page width * - 0.4 would include shorter lines (40% of page width) * - 0.8 would require longer lines (80% of page width) * * @default 0.6 (60% of image width) */ minWidthRatioForMerged: null | number; /** * The minimum number of words a line must contain to be considered poetry. * This helps filter out noise like page numbers, single-word labels, or artifacts. * * Higher values reduce false positives but may miss short poetic lines. * Lower values increase sensitivity but may include more noise. * * @default 2 */ minWordCount: number; /** * For paired lines heuristic: How similar in width two hemistichs must be. * This determines whether two lines are balanced enough to be verse halves. * * The similarity check: `|width1 - width2| / average(width1, width2) < ratio` * - 0.4 means widths can differ by up to 40% of their average * - 0.2 would require more similar widths (20% difference) * - 0.6 would allow more variation (60% difference) * * @default 0.4 (40% width difference allowed) */ pairWidthSimilarityRatio: number; /** * For paired lines heuristic: How similar in word count two hemistichs must be. * This ensures that verse halves have balanced content length. * * The similarity check: `|count1 - count2| / max(count1, count2) < ratio` * - 0.5 means word counts can differ by up to 50% of the larger count * - 0.3 would require more similar counts (30% difference) * - 0.7 would allow more variation (70% difference) * * @default 0.5 (50% word count difference allowed) */ pairWordCountSimilarityRatio: number; /** * For merged lines heuristic: Word density threshold for identifying poetry. * Poetry typically has lower word density (more spacing) than prose. * * A line is considered poetic if its density (words per pixel) is less than * this ratio multiplied by the average prose density of the document. * * - 0.8 means poetic lines should have ≤80% of average prose density * - 0.9 would require density closer to prose (≤90%) * - 0.7 would require sparser text (≤70% of prose density) * * @default 0.8 (80% of average prose density) */ wordDensityComparisonRatio: number; }; /** * Represents the dimensions of a rectangular area. * Used as a base type for defining size properties in bounding boxes and other geometric shapes. */ type Size = { /** * The height of the rectangular area in pixels. */ readonly height: number; /** * The width of the rectangular area in pixels. */ readonly width: number; }; /** * A processed text block reconstructed from raw OCR observations. * * TextBlock represents a higher-level text unit (typically a line or paragraph) * that has been assembled from individual OCR observations and enriched with * semantic information about its role and characteristics within the document. * * This type extends the basic Observation with additional metadata that helps * in document structure analysis, formatting preservation, and content classification. */ type TextBlock = Observation & { /** * Indicates whether the text is centered on the page. * * This is determined by analyzing the text's position relative to page margins * and ensuring adequate whitespace on both sides. Centered text often indicates: * - Document titles and headings * - Poetry or verse content * - Section headers * - Epigraphs or quotes * * The centering detection uses configurable tolerance and margin ratios * to account for slight misalignments and varying document layouts. */ isCentered?: boolean; /** * Indicates whether this text is identified as a footnote. * * Footnotes are typically detected by their position relative to horizontal * line elements in the document. Text appearing below the last significant * horizontal line is often classified as footnote content. This classification * helps in: * - Separating main content from supplementary information * - Proper document structure reconstruction * - Academic and formal document processing */ isFootnote?: boolean; /** * Indicates whether the text represents a heading or title. * * Headings are often identified by their visual presentation, such as: * - Being enclosed within rectangular borders or boxes * - Having distinctive spacing or positioning * - Being centered or specially formatted * * This classification helps in: * - Document outline generation * - Hierarchical content structuring * - Navigation and indexing */ isHeading?: boolean; /** * Indicates whether this text is identified as a line of poetry or verse. * * Poetic content is detected using multiple heuristics including: * - Line length and spacing patterns * - Word density analysis * - Centering and alignment characteristics * - Paired hemistich detection * * Poetic lines receive special treatment during processing: * - They are not merged into standard paragraphs * - Line breaks are preserved as semantically significant * - Spacing and formatting are maintained more strictly * * This is crucial for preserving the artistic and structural integrity * of poems, verses, and other formatted literary content. */ isPoetic?: boolean; }; /** * Output payload from one-shot paragraph reconstruction. */ type ReconstructResult = { /** * Intermediate line-level blocks. */ lines: TextBlock[]; /** * Final paragraph-level blocks. */ paragraphs: TextBlock[]; /** * Formatted plain text output. */ text: string; }; //#endregion //#region src/utils/grouping.d.ts /** * Merges the group of observations into a single one. * @param group The group of observations to merge. * @param delimiter Text delimiter used when concatenating observations. * @returns A single observation with the text of the group concatenated as well as the bounding box adjusted to fit all of the contents. */ declare const mergeObservations: <T extends Observation>(group: T[], delimiter?: string) => T; //#endregion //#region src/utils/layout.d.ts /** * Filters out horizontal lines that are contained within any of the provided rectangles. * * This is useful for removing header/footer lines that appear within document sections * while preserving lines that mark true document boundaries or section separators. * * @param rectangles - Array of rectangles to check containment against * @param horizontalLines - Array of horizontal lines to filter * @param tolerance - Pixel tolerance for boundary checking (default: 5) * @returns Array of horizontal lines that are NOT contained within any rectangle */ declare const filterHorizontalLinesOutsideRectangles: (rectangles: BoundingBox[], horizontalLines: BoundingBox[], tolerance?: number) => BoundingBox[]; /** * Converts bounding box coordinates from array format to object format. * Transforms [x1, y1, x2, y2] coordinates to {x, y, width, height} format. * * @param box - Array containing [x1, y1, x2, y2] coordinates where (x1,y1) is top-left and (x2,y2) is bottom-right * @returns Bounding box object with x, y, width, and height properties */ declare const mapMatrixToBoundingBox: (box: [number, number, number, number]) => { height: number; width: number; x: number; y: number; }; //#endregion //#region src/utils/marking.d.ts /** * Calculates the DPI (dots per inch) based on image dimensions and original PDF size. * * This utility function helps determine the resolution at which a PDF was rasterized * by comparing the resulting image dimensions with the original PDF page dimensions. * The DPI values are essential for proper scaling of pixel-based tolerances and * measurements throughout the document processing pipeline. * * @param imageSize - Dimensions of the rasterized image in pixels * @param pdfSize - Original dimensions of the PDF page in points (1/72 inch) * @returns Object containing x and y DPI values * * @example * ```typescript * const imageSize = { width: 2480, height: 3508 }; * const pdfSize = { width: 595, height: 842 }; // A4 page in points * const dpi = calculateDPI(imageSize, pdfSize); * // Result: { x: 300, y: 300 } for a 300 DPI scan * ``` */ declare const calculateDPI: (imageSize: Size, pdfSize: Size) => { x: number; y: number; }; //#endregion //#region src/utils/options.d.ts /** * Resolves optional overrides against a default options object. * * This is a shallow merge helper intended for option bags where * top-level keys are merged and nested objects are handled explicitly * by the caller when needed. */ declare const resolveWithDefaults: <T extends object>(defaults: T, overrides?: Partial<T>) => T; //#endregion //#region src/utils/paragraphs.d.ts /** * Preprocesses observations by filtering noise, flipping coordinates for RTL text, * and normalizing x-coordinates for proper alignment. * * @param observations - Array of text observations to preprocess * @param imageWidth - Total width of the document/image in pixels * @param dpiX - Horizontal DPI for coordinate normalization * @param options - Optional logging configuration * @returns Preprocessed observations ready for line grouping */ declare const flipAndAlignObservations: (observations: Observation[], imageWidth: number, dpiX: number, options?: Partial<Pick<MapObservationsToTextLinesOptions, "isRTL" | "log">>) => Observation[]; /** * Converts OCR observations into structured text lines with metadata. * * Groups observations into lines based on vertical proximity, applies centering detection, * identifies headings (text within rectangles), footnotes (text below horizontal lines), * and poetic content. Also performs poetry detection to preserve poetic formatting. * * @param observations - Array of OCR text observations * @param page - Page dimensions and DPI information. * @param opts - Configuration options for text line processing * @returns Array of text blocks with metadata (centering, headings, footnotes, poetry) */ declare const mapObservationsToTextLines: (observations: Observation[], page: PageContext, opts?: Partial<MapObservationsToTextLinesOptions>) => TextBlock[]; /** * Groups text lines into coherent paragraphs, handling both prose and poetry. * * Prose lines are grouped into paragraphs based on vertical spacing and line width patterns. * Poetic lines are preserved individually to maintain their formatting. * Processes body content and footnotes separately. * * @param textLines - Array of text lines to group into paragraphs * @param options - Object-based paragraph detection settings. * @returns Array of text blocks representing complete paragraphs */ declare const mapTextLinesToParagraphs: (textLines: TextBlock[], options?: ParagraphOptions) => TextBlock[]; //#endregion //#region src/index.d.ts /** * Formats an array of text blocks into a readable string with proper paragraph breaks. * * @param textBlocks - Array of text blocks to format * @param footerSymbol - Optional symbol to insert before the first footnote * @returns Formatted text string with proper line breaks and spacing */ declare const formatTextBlocks: (textBlocks: TextBlock[], footerSymbol?: string) => string; /** * One-shot API for OCR paragraph reconstruction. * * Converts observations into lines, groups lines into paragraphs, then formats text. */ declare const reconstructParagraphs: (input: ReconstructInput, options?: ReconstructOptions) => ReconstructResult; //#endregion export { BoundingBox, CenteringOptions, LayoutElements, MapObservationsToTextLinesOptions, Observation, PageContext, ParagraphOptions, PoetryDetectionOptions, ReconstructInput, ReconstructOptions, ReconstructResult, Size, TextBlock, calculateDPI, filterHorizontalLinesOutsideRectangles, flipAndAlignObservations, formatTextBlocks, mapMatrixToBoundingBox, mapObservationsToTextLines, mapTextLinesToParagraphs, mergeObservations, reconstructParagraphs, resolveWithDefaults }; //# sourceMappingURL=index.d.ts.map