EmbedPDF
DocsVueHeadlessPluginsLayout Analysis

Layout Analysis Plugin

⚠️

This plugin is experimental. Running deep learning models in the browser is resource-intensive — on many devices the browser tab may become very slow or even crash, particularly on mobile or lower-end hardware. As models get smaller and browser APIs improve, stability and performance will continue to get better.

The Layout Analysis Plugin brings AI-powered document understanding directly into your PDF viewer. It uses ONNX Runtime to run deep learning models locally in the browser, detecting layout elements like text blocks, tables, images, headings, formulas, and more.

Because all inference happens on-device, no data ever leaves the browser. This makes it ideal for applications that handle sensitive or confidential documents. Models are loaded on demand from HuggingFace CDN (or a custom server) and cached locally using the browser’s Cache API.

The plugin optionally supports table structure analysis, which identifies rows, columns, and cells within detected tables. All results are mapped to PDF page coordinates for precise overlay rendering.

Installation

This plugin depends on the AI Manager and Render plugins. You also need to install onnxruntime-web, which provides the ONNX Runtime that powers inference in the browser.

npm install @embedpdf/plugin-layout-analysis @embedpdf/plugin-ai-manager @embedpdf/ai @embedpdf/plugin-render onnxruntime-web
ℹ️

For server-side inference, install onnxruntime-node instead of onnxruntime-web.

Registration

First create an AI runtime, then register the AI Manager and Layout Analysis plugins.

import { createPluginRegistration } from '@embedpdf/core' // ... other imports import { RenderPluginPackage } from '@embedpdf/plugin-render/vue' import { createAiRuntime } from '@embedpdf/ai/web' import { AiManagerPluginPackage } from '@embedpdf/plugin-ai-manager/vue' import { LayoutAnalysisPluginPackage } from '@embedpdf/plugin-layout-analysis/vue' // Create the AI runtime (loads ONNX Runtime on demand) const aiRuntime = createAiRuntime({ backend: 'auto', // tries WebGPU first, falls back to WASM cache: true, // caches models in the browser Cache API }) const plugins = [ // ... other essential plugins createPluginRegistration(DocumentManagerPluginPackage, { /* ... */ }), createPluginRegistration(ViewportPluginPackage), createPluginRegistration(RenderPluginPackage), // Required dependency // Register the AI manager with the runtime createPluginRegistration(AiManagerPluginPackage, { runtime: aiRuntime, }), // Register and configure layout analysis createPluginRegistration(LayoutAnalysisPluginPackage, { layoutThreshold: 0.35, // confidence threshold for layout detections tableStructureThreshold: 0.8, // confidence threshold for table elements tableStructure: false, // enable table structure analysis }), ]

Usage

The plugin provides the <LayoutAnalysisLayer /> component for displaying results, the useLayoutAnalysis composable for document-scoped reactive state, and the useLayoutAnalysisCapability composable for direct API access.

The useLayoutAnalysis Composable

The useLayoutAnalysis(documentId) composable is the primary way to interact with the plugin from Vue. It returns reactive refs and scoped methods for a specific document.

<script setup lang="ts"> import { useLayoutAnalysis } from '@embedpdf/plugin-layout-analysis/vue' const props = defineProps<{ documentId: string }>() const la = useLayoutAnalysis(() => props.documentId) </script> <template> <div> <button @click="la.provides.value?.setLayoutOverlayVisible(!la.layoutOverlayVisible.value)"> Toggle Layout Overlay </button> <button @click="la.provides.value?.setTableStructureEnabled(!la.tableStructureEnabled.value)"> Toggle Table Analysis </button> </div> </template>

Analyzing Pages

Use the scope to trigger analysis. The returned task supports progress tracking (including model download progress) and cancellation.

The plugin implements smart caching — pages that have already been analyzed are skipped automatically. Pass { force: true } to re-analyze regardless.

<script setup lang="ts"> import { ref } from 'vue' import { useLayoutAnalysis } from '@embedpdf/plugin-layout-analysis/vue' const props = defineProps<{ documentId: string }>() const la = useLayoutAnalysis(() => props.documentId) const status = ref('idle') function handleAnalyze() { if (!la.scope.value) return const task = la.scope.value.analyzeAllPages() task.onProgress((p) => { if (p.stage === 'downloading-model') { status.value = `Downloading model: ${((p.loaded / p.total) * 100).toFixed(0)}%` } else if (p.stage === 'creating-session') { status.value = 'Initializing model...' } else if (p.stage === 'page-complete') { status.value = `Page ${p.completed}/${p.total} complete` } else { status.value = p.stage } }) task.wait( (result) => { const totalBlocks = result.pages.reduce((sum, p) => sum + p.blocks.length, 0) status.value = `Done — ${totalBlocks} blocks detected` }, (error) => { status.value = error.type === 'abort' ? 'Cancelled' : error.reason.message }, ) } </script> <template> <button @click="handleAnalyze">{{ status }}</button> </template>

Cancellation

The task returned by analyzePage and analyzeAllPages can be cancelled at any time by calling abort():

const task = la.scope.value.analyzeAllPages() // Later, to cancel: task.abort({ type: 'no-document', message: 'Cancelled by user' })

When aborted, the error callback receives error.type === 'abort' and the page status resets to idle.

Displaying Results

Place the <LayoutAnalysisLayer /> component on top of the <RenderLayer /> inside your <Scroller />’s default slot. It draws color-coded bounding boxes for each detected layout element with labels and confidence scores. When table structure analysis is enabled, table rows, columns, and cells are rendered as dashed overlays.

The layer reads thresholds, overlay visibility, and selected block state directly from the plugin state — no props needed beyond documentId and pageIndex.

<script setup lang="ts"> import { LayoutAnalysisLayer } from '@embedpdf/plugin-layout-analysis/vue' import { RenderLayer } from '@embedpdf/plugin-render/vue' </script> <template> <Scroller :document-id="activeDocumentId"> <template #default="{ page }"> <RenderLayer :document-id="activeDocumentId" :page-index="page.pageIndex" /> <LayoutAnalysisLayer :document-id="activeDocumentId" :page-index="page.pageIndex" /> </template> </Scroller> </template>

Live Example

This example shows a full-featured layout analysis viewer with controls for analyzing pages, toggling layout and table structure overlays independently, adjusting confidence thresholds with sliders, and enabling/disabling table structure analysis at runtime. The model (~130 MB) is downloaded on first use and cached for subsequent runs.

API Reference

Configuration (LayoutAnalysisPluginConfig)

OptionTypeDescription
layoutThresholdnumberMinimum confidence score for a layout detection to be displayed.
Default: 0.35
tableStructureThresholdnumberMinimum confidence score for a table structure element to be displayed.
Default: 0.8
tableStructurebooleanIf true, enables table structure analysis at startup. Can be toggled at runtime via setTableStructureEnabled().
Default: false
autoAnalyzebooleanIf true, automatically analyzes pages as they load.
Default: false
renderScalenumberScale factor for rendering pages before analysis. Higher values improve detection accuracy at the cost of memory.
Default: 2.0

Component: <LayoutAnalysisLayer />

Renders bounding boxes over a PDF page for detected layout elements and table structure. Thresholds, overlay visibility, and selected block state are read directly from the plugin state.

PropTypeDescription
documentIdstring(Required) The ID of the document.
pageIndexnumber(Required) The page index to display layout results for.
scalenumberZoom scale multiplier. Falls back to the document’s current scale.

Composable: useLayoutAnalysis(documentId)

Document-scoped composable that returns reactive refs for plugin state and methods.

Parameters

ParameterTypeDescription
documentIdMaybeRefOrGetter<string>The document ID to track (can be a ref, computed, getter, or plain value).

Returns

PropertyTypeDescription
pagesComputedRef<Record<number, PageAnalysisState>>Per-page analysis state for the document.
layoutOverlayVisibleComputedRef<boolean>Whether layout block overlays are shown.
tableStructureOverlayVisibleComputedRef<boolean>Whether table structure overlays are shown.
tableStructureEnabledComputedRef<boolean>Whether table structure analysis is enabled.
layoutThresholdComputedRef<number>Current layout detection confidence threshold.
tableStructureThresholdComputedRef<number>Current table structure confidence threshold.
selectedBlockIdComputedRef<string | null>The UUID of the currently selected block.
scopeComputedRef<LayoutAnalysisScope | null>Document-scoped methods (analyzePage, analyzeAllPages, etc.).
providesRef<LayoutAnalysisCapability>Full plugin capability with global methods.

Composable: useLayoutAnalysisCapability()

Low-level composable that returns the full plugin capability directly.

LayoutAnalysisCapability Methods

MethodDescription
analyzePage(pageIndex, options?)Analyzes a single page. Skips if already analyzed unless { force: true }. Returns a Task with progress and abort support.
analyzeAllPages(options?)Analyzes all pages sequentially. Skips already-analyzed pages unless { force: true }. Returns a Task with per-page progress.
getPageLayout(pageIndex)Returns the cached PageLayout for a page, or null if not yet analyzed.
getPageTextRuns(pageIndex)Returns raw text runs from the PDF engine for a page (font, size, color, text content).
forDocument(id)Returns a document-scoped LayoutAnalysisScope.
onPageLayoutChangeEvent hook fired when a page’s layout results change.
onStateChangeEvent hook fired when any plugin state changes (thresholds, visibility, etc.).
setLayoutOverlayVisible(visible)Toggles visibility of layout block overlays.
setTableStructureOverlayVisible(visible)Toggles visibility of table structure overlays.
setTableStructureEnabled(enabled)Enables or disables table structure analysis. When enabled, the next analysis run includes the table structure pass.
setLayoutThreshold(threshold)Updates the layout detection confidence threshold.
setTableStructureThreshold(threshold)Updates the table structure confidence threshold.
selectBlock(blockId)Selects a block by UUID, or null to deselect.
clearPageResults(documentId, pageIndex)Clears cached results for a single page (forces re-analysis on next run).
clearAllResults(documentId)Clears cached results for all pages in a document.

Progress Stages (PageAnalysisProgress)

The onProgress callback receives one of these stages during analyzePage:

StageFieldsDescription
renderingpageIndexRendering the page to an image for the model.
downloading-modelpageIndex, loaded, totalDownloading the AI model from the server (first run only).
creating-sessionpageIndexInitializing the ONNX inference session after download.
layout-detectionpageIndexRunning layout detection inference.
table-structurepageIndex, tableIndex, tableCountAnalyzing table structure (if enabled).
mapping-coordinatespageIndexMapping results to PDF page coordinates.

When using analyzeAllPages, an additional page-complete stage is emitted with pageIndex, completed, and total fields.

Result: LayoutBlock

Each detected element in the PageLayout.blocks array has these fields:

FieldTypeDescription
idstringGlobally unique UUID for this detection.
classIdnumberNumeric class ID from the model.
labelstringHuman-readable label (e.g., text, table, image, header).
scorenumberConfidence score from 0 to 1.
rectRectBounding box in PDF page coordinates (points).
readingOrdernumberEstimated reading order index.

Result: TableStructureElement

When table structure analysis is enabled, each table block in PageLayout.tableStructures maps to an array of these elements:

FieldTypeDescription
classIdnumberNumeric class ID (e.g., 0 = table, 1 = column, 2 = row).
labelstringHuman-readable label (table, table_column, table_row, table_column_header, table_projected_row_header, table_spanning_cell).
scorenumberConfidence score from 0 to 1.
rectRectBounding box in PDF page coordinates (points).
Last updated on February 18, 2026

Need Help?

Join our community for support, discussions, and to contribute to EmbedPDF's development.