Quick Start
This guide walks you through Xberg’s core API: extract, extract_batch,
ExtractInput, and the ExtractionResult envelope. Install your binding first if you haven’t:
Installation.
TypeScript users: @xberg-io/xberg for Node.js, @xberg-io/xberg-wasm for browsers and edge runtimes — see Language Support.
Your First Extraction
Section titled “Your First Extraction”Pass an ExtractInput with kind = "uri" to extract a local path, file:// URI,
or HTTP(S) URL. extract returns an ExtractionResult with a results list:
#include "xberg.h"#include <stdio.h>
int main(void) { XBERGExtractionConfig *config = xberg_extraction_config_default(); XBERGExtractInput *input = xberg_extract_input_from_json("{\"kind\":\"uri\",\"uri\":\"document.pdf\"}");
XBERGExtractionResult *output = xberg_extract(input, config); if (!output) { fprintf(stderr, "extraction failed: %s\n", xberg_last_error_context()); xberg_extract_input_free(input); xberg_extraction_config_free(config); return 1; }
char *json = xberg_extraction_result_to_json(output); puts(json);
xberg_free_string(json); xberg_extraction_result_free(output); xberg_extract_input_free(input); xberg_extraction_config_free(config); return 0;}using Xberg;
var output = await XbergConverter.ExtractAsync( ExtractInput.FromUri("document.pdf"), ExtractionConfig.Default());
Console.WriteLine(output.Results[0].Content);import 'package:xberg/xberg.dart';
final output = await Xberg.extract( const ExtractInput( kind: ExtractInputKind.uri, uri: 'document.pdf', ),);
print(output.results.first.content);package main
import ( "fmt" "log"
"github.com/xberg-io/xberg")
func main() { kind := xberg.ExtractInputKindURI uri := "document.pdf"
output, err := xberg.Extract( xberg.ExtractInput{Kind: &kind, URI: &uri}, xberg.ExtractionConfig{}, ) if err != nil { log.Fatal(err) }
fmt.Println(output.Results[0].Content)}import io.xberg.ExtractInput;import io.xberg.ExtractInputKind;import io.xberg.ExtractionConfig;import io.xberg.Xberg;
var input = ExtractInput.builder() .withKind(ExtractInputKind.Uri) .withUri("document.pdf") .build();
var output = Xberg.extract(input, ExtractionConfig.builder().build());System.out.println(output.results().get(0).content());import io.xberg.ExtractInputimport io.xberg.ExtractInputKindimport io.xberg.ExtractionConfigimport io.xberg.Xberg
val output = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), ExtractionConfig(),)
println(output.results.first().content)import asyncio
from xberg import ExtractInput, extract
async def main() -> None: output = await extract(ExtractInput(kind="uri", uri="document.pdf"))
print(output.results[0].content) print(f"Results: {output.summary.results}")
asyncio.run(main())require "xberg"
output = Xberg.extract(Xberg::ExtractInput.new(uri: "document.pdf"))
puts output.results.first.contentputs "Results: #{output.summary.results}"use xberg::{extract, ExtractInput, ExtractionConfig};
let config = ExtractionConfig::default();let output = extract(ExtractInput::from_uri("document.pdf"), &config).await?;
println!("{}", output.results[0].content);println!("Results: {}", output.summary.results);import Xberg
let input = #"{"kind":"uri","uri":"document.pdf"}"#let output = try await extract(input, "{}")
print("Results: \(output.summary().results())")input = %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}
{:ok, output} = Xberg.extract(input: input, config: nil)
IO.inspect(output.summary)import { ExtractInputKind, extract } from "@xberg-io/xberg";
const output = await extract({ kind: "uri", uri: "document.pdf",});
console.log(output.results[0].content);console.log(`Results: ${output.summary.results}`);import { ExtractInputKind, extract, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();
const bytes = new Uint8Array( await fetch("/document.pdf").then((response) => response.arrayBuffer()),);
const output = await extract({ kind: "bytes", bytes, mimeType: "application/pdf", filename: "document.pdf",});
console.log(output.results[0].content);const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { const output_json = try xberg.extract( "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}", "{}", ); defer std.heap.c_allocator.free(output_json);
try std.io.getStdOut().writer().print("{s}\n", .{output_json});}# Extract to stdoutxberg extract document.pdf
# Save to file using shell redirectionxberg extract document.pdf > output.txt
# Extract with JSON format (includes metadata)xberg extract document.pdf --format jsonHandle Errors
Section titled “Handle Errors”Wrap extractions in error handling before going further. Xberg raises specific exceptions for missing files, parse failures, and OCR problems:
#include "xberg.h"#include <stdio.h>#include <stdlib.h>
int main(void) { XBERGExtractionConfig *config = xberg_extraction_config_default();
/* Pass an unsupported MIME type to trigger an error. */ XBERGExtractInput *input = xberg_extract_input_from_bytes(NULL, 0, "application/x-unknown", NULL); if (!input) { int32_t code = xberg_last_error_code(); const char *message = xberg_last_error_context(); /* message is valid until the next FFI call on this thread — copy if needed. */ fprintf(stderr, "error %d: %s\n", code, message ? message : "(no message)"); xberg_extraction_config_free(config); return code != 0 ? code : 1; }
XBERGExtractionResult *result = xberg_extract(input, config); if (!result) { int32_t code = xberg_last_error_code(); const char *message = xberg_last_error_context(); fprintf(stderr, "error %d: %s\n", code, message ? message : "(no message)"); xberg_extract_input_free(input); xberg_extraction_config_free(config); return code != 0 ? code : 1; }
char *content = xberg_extraction_result_results(result); printf("%s\n", content ? content : "(empty)"); xberg_free_string(content);
xberg_extract_input_free(input); xberg_extraction_result_free(result); xberg_extraction_config_free(config); return 0;}using Xberg;
try{ var result = (await XbergConverter.ExtractAsync(ExtractInput.FromUri("missing.pdf"), ExtractionConfig.Default())).Results[0]; Console.WriteLine(result.Content);}catch (ValidationException ex){ Console.Error.WriteLine($"Validation error: {ex.Message}");}catch (IoException ex){ Console.Error.WriteLine($"IO error: {ex.Message}"); throw;}catch (XbergException ex){ Console.Error.WriteLine($"Extraction failed: {ex.Message}"); throw;}import 'package:xberg/xberg.dart';
Future<void> main() async { try { final result = await XbergBridge.extract(ExtractInput(kind: ExtractInputKind.uri, uri: 'document.pdf'), config: ExtractionConfig()); print(result.results[0].content); } on Exception catch (e) { // flutter_rust_bridge converts every XbergError variant // (Io / UnsupportedFormat / Parsing / MissingDependency, ...) // into a Dart exception whose message preserves the original context. print('Extraction failed: $e'); }}package main
import ( "errors" "log"
"github.com/xberg-io/xberg")
func main() { input := xberg.ExtractInputFromURI("missing.pdf") result, err := xberg.Extract(*input, xberg.ExtractionConfig{}) if err != nil { if errors.Is(err, xberg.ErrIo) { log.Printf("file not found: %v", err) } else if errors.Is(err, xberg.ErrUnsupportedFormat) { log.Printf("unsupported format: %v", err) } else { log.Printf("extraction error: %v", err) } return }
println("Content:", result.Results[0].Content)}import io.xberg.Xberg;import io.xberg.ExtractInputKind;import io.xberg.ExtractedDocument;import io.xberg.ExtractionConfig;import io.xberg.XbergRsException;import java.nio.file.Paths;
try { ExtractionConfig config = ExtractionConfig.builder().build(); var resultOutput = Xberg.extract( io.xberg.ExtractInput.builder() .withKind(io.xberg.ExtractInputKind.Uri) .withUri("missing.pdf") .build(), config ); ExtractedDocument result = resultOutput.results().get(0); System.out.println(result.content());} catch (XbergRsException e) { System.err.println("Extraction failed: " + e.getMessage()); System.err.println("Error code: " + e.getCode());}import io.xberg.*
fun main() { val config = ExtractionConfig.builder().build() try { val resultOutput = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), config, ) val result = resultOutput.results.first() println(result.content) } catch (e: XbergRsException) { System.err.println("Extraction failed: ${e.message}") System.err.println("Error code: ${e.code}") } catch (e: Exception) { System.err.println("Unexpected error: ${e.message}") }}import asynciofrom xberg import ExtractInput, extract, ExtractionConfigfrom xberg import ( XbergError, ParsingError, OcrError, ValidationError,)
async def main() -> None: try: result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig()) print(f"Extracted {len(result.results[0].content)} characters") except FileNotFoundError as e: print(f"File not found: {e}") except ParsingError as e: print(f"Failed to parse document: {e}") except OcrError as e: print(f"OCR processing failed: {e}") except XbergError as e: print(f"Extraction error: {e}")
try: config: ExtractionConfig = ExtractionConfig() pdf_bytes: bytes = b"%PDF-1.4\n" result = await extract( ExtractInput(kind="bytes", bytes=pdf_bytes, mime_type="application/pdf", filename="document.pdf"), config, ) print(f"Extracted: {result.results[0].content[:100]}") except ValidationError as e: print(f"Invalid configuration: {e}") except OcrError as e: print(f"OCR failed: {e}") except XbergError as e: print(f"Extraction failed: {e}")
asyncio.run(main())require 'xberg'
begin input = Xberg::ExtractInput.new(uri: 'missing.pdf') config = Xberg::ExtractionConfig.new result = Xberg.extract(input, config) puts result.results.first.contentrescue RuntimeError => e # All extraction errors are raised as RuntimeError # Check error message for specific error details case e.message when /validation/i puts "Validation error: #{e.message}" when /io|not found/i puts "IO error: #{e.message}" raise else puts "Extraction failed: #{e.message}" raise endenduse xberg::{extract, ExtractionConfig, ExtractInput, XbergError};
#[tokio::main]async fn main() { let config = ExtractionConfig::default(); match extract(ExtractInput::from_uri("document.pdf"), &config).await { Ok(output) => println!("{}", output.results[0].content), Err(XbergError::Io(e)) => eprintln!("File error: {e}"), Err(XbergError::UnsupportedFormat(mime)) => { eprintln!("Unsupported format: {mime}"); } Err(XbergError::Parsing { message, .. }) => { eprintln!("Corrupt or invalid document: {message}"); } Err(XbergError::MissingDependency(dep)) => { eprintln!("Missing dependency — install {dep}"); } Err(e) => eprintln!("Extraction failed: {e}"), }}import Foundationimport Xbergimport RustBridge
// The Swift binding throws `RustString` (not `XbergError`) for every// failure surfaced from the Rust core. The string preserves the original// error variant name and message (e.g. "UnsupportedFormat: ...",// "MissingDependency: ...", "Parsing: ...") so callers can pattern-match// on the prefix or simply print the message.do { let config = try extractionConfigFromJson("{}") let input = try extractInputFromJson(#"{"kind":"uri","uri":"document.pdf"}"#) let resultOutput = try await extract(input: input, config: config) let result = resultOutput.results().get(index: 0)! print(result.content.toString())} catch let error as RustString { let message = error.toString() if message.contains("UnsupportedFormat") { print("Unsupported format: \(message)") } else if message.contains("MissingDependency") { print("Install the required dependency: \(message)") } else if message.contains("Parsing") { print("Corrupt or invalid document: \(message)") } else if message.contains("Io") { print("File error: \(message)") } else { print("Extraction failed: \(message)") }} catch { print("Unexpected error: \(error)")}# Example: Handling extraction errorscase Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}, config: nil) do {:ok, output} -> result = List.first(output.results) IO.puts("Successfully extracted content") IO.puts("Content length: #{byte_size(result.content)} characters")
{:error, reason} -> IO.puts("Extraction failed: #{reason}")end
# Example: Handling with custom error messageresult = Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "nonexistent.pdf"}, config: nil)
case result do {:ok, output} -> data = List.first(output.results) IO.puts("File processed successfully") {:error, error} -> IO.puts("Error details: #{inspect(error)}")end
# Example: Extract with pattern matchingcase Xberg.extract(input: %Xberg.ExtractInput{kind: :bytes, bytes: <<>>, mime_type: "application/pdf"}, config: nil) do {:ok, output} -> result = List.first(output.results) IO.puts("Content: #{result.content}") {:error, msg} when is_binary(msg) -> IO.puts("Validation error: #{msg}") {:error, reason} -> IO.puts("Unknown error: #{inspect(reason)}")endimport { extract } from "@xberg-io/xberg";
try { const output = await extract({ kind: "uri", uri: "missing.pdf", }); console.log(output.results[0].content);} catch (error: unknown) { if (error instanceof Error) { console.error(`Extraction failed: ${error.message}`); } throw error;}import { initWasm, extract } from "@xberg-io/xberg-wasm";
await initWasm();
const fileInput = document.getElementById("file") as HTMLInputElement;const file = fileInput.files?.[0];
if (file) { try { const bytes = new Uint8Array(await file.arrayBuffer()); const result = await extract({ kind: "bytes", bytes, mimeType: file.type || "application/pdf" }, undefined); console.log(`Extracted: ${result.content.length} characters`); } catch (error) { const message = error instanceof Error ? error.message : String(error); console.error("Extraction failed:", message); }}const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { const config_json = "{}"; const input_json = "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}"; const output_json = xberg.extract(input_json, config_json) catch |err| { const stderr = std.io.getStdErr().writer(); switch (err) { error.Io => try stderr.print("File error\n", .{}), error.UnsupportedFormat => try stderr.print("Unsupported format\n", .{}), error.Parsing => try stderr.print("Corrupt or invalid document\n", .{}), error.MissingDependency => try stderr.print("Missing dependency — install required backend\n", .{}), error.Ocr => try stderr.print("OCR processing failed\n", .{}), error.OutOfMemory => try stderr.print("Out of memory\n", .{}), else => try stderr.print("Extraction failed: {s}\n", .{@errorName(err)}), } if (xberg._last_error()) |context| { try stderr.print(" context: {s}\n", .{context}); } return; }; defer std.heap.c_allocator.free(output_json);
const stdout = std.io.getStdOut().writer(); try stdout.print("{s}\n", .{output_json});}OCR for Scanned Documents
Section titled “OCR for Scanned Documents”Xberg runs OCR automatically when it detects an image or scanned PDF. You can also force OCR on any document:
#include "xberg.h"#include <stdio.h>
int main(void) { const char *config_json = "{" "\"ocr\": {\"tesseract\": {\"language\": \"eng\"}}" "}";
XBERGExtractionConfig *config = xberg_extraction_config_from_json(config_json); if (!config) { fprintf(stderr, "config parse failed (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); return 1; }
XBERGExtractInput *input = xberg_extract_input_from_uri("scanned.png"); if (!input) { fprintf(stderr, "Failed to create input (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); xberg_extraction_config_free(config); return 1; }
XBERGExtractionResult *result = xberg_extract(input, config); if (result) { char *results = xberg_extraction_result_results(result); if (results) { printf("OCR results: %s\n", results); } xberg_free_string(results); } else { fprintf(stderr, "OCR error (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); }
xberg_extract_input_free(input); xberg_extraction_result_free(result); xberg_extraction_config_free(config); return 0;}using Xberg;
var config = new ExtractionConfig{ ForceOcr = true, Ocr = new OcrConfig { Backend = "tesseract", Language = "eng", },};
var result = (await XbergConverter.ExtractAsync(ExtractInput.FromUri("scanned.pdf"), config)).Results[0];Console.WriteLine(result.Content);Console.WriteLine(result.DetectedLanguages);import 'package:xberg/xberg.dart';
Future<void> main() async { const config = ExtractionConfig( useCache: true, enableQualityProcessing: true, forceOcr: true, disableOcr: false, ocr: OcrConfig( enabled: true, backend: 'tesseract', language: ['eng'], autoRotate: false, vlmFallback: VlmFallbackPolicy.disabled(), ), resultFormat: ResultFormat.unified, outputFormat: OutputFormat.plain(), includeDocumentStructure: false, maxArchiveDepth: 3, useLayoutForMarkdown: false, url: UrlExtractionConfig( mode: UrlExtractionMode.auto, allowLocalFileInputs: true, allowFileUris: true, ), );
const input = ExtractInput( kind: ExtractInputKind.uri, uri: 'scanned.pdf', ); final output = await XbergBridge.extract(input, config: config); final document = output.results.first;
print(document.content);}package main
import ( "log"
"github.com/xberg-io/xberg/packages/go")
func main() { cfg := xberg.ExtractionConfig{ Ocr: &xberg.OcrConfig{ Backend: "tesseract", Language: "eng", }, }
input := xberg.ExtractInputFromURI("scanned.pdf") result, err := xberg.Extract(*input, cfg) if err != nil { log.Fatalf("extract failed: %v", err) } log.Println(len(result.Results[0].Content))}import io.xberg.ExtractInput;import io.xberg.ExtractInputKind;import io.xberg.ExtractedDocument;import io.xberg.ExtractionConfig;import io.xberg.ExtractionResult;import io.xberg.OcrConfig;import io.xberg.Xberg;import io.xberg.XbergRsException;import java.util.List;
public class Main { public static void main(String[] args) { try { ExtractionConfig config = ExtractionConfig.builder() .withForceOcr(true) .withOcr(OcrConfig.builder() .withBackend("tesseract") .withLanguage(List.of("eng")) .build()) .build();
ExtractInput input = ExtractInput.builder() .withKind(ExtractInputKind.Uri) .withUri("scanned.pdf") .build();
ExtractionResult output = Xberg.extract(input, config); ExtractedDocument document = output.results().get(0); System.out.println(document.content()); } catch (XbergRsException e) { System.err.println("Extraction failed: " + e.getMessage()); } }}import io.xberg.*import java.util.Optional
fun main() { val ocr = OcrConfig.builder() .withBackend("tesseract") .withLanguage("eng") .build()
val config = ExtractionConfig.builder() .withOcr(Optional.of(ocr)) .build()
val resultOutput = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "scanned.pdf"), config, ) val result = resultOutput.results.first() println(result.content)}import asynciofrom xberg import ExtractInput, extract, ExtractionConfig, OcrConfig
async def main() -> None: config: ExtractionConfig = ExtractionConfig( ocr=OcrConfig(backend="tesseract", language="eng") )
result = await extract(ExtractInput(uri="scanned.pdf"), config)
content: str = result.results[0].content preview: str = content[:100] total_length: int = len(content)
print(f"Extracted content (preview): {preview}") print(f"Total characters: {total_length}")
asyncio.run(main())require 'xberg'
ocr_config = Xberg::OcrConfig.new( backend: 'tesseract', language: 'eng')
config = Xberg::ExtractionConfig.new(ocr: ocr_config)input = Xberg::ExtractInput.new(uri: 'scanned.pdf')result = Xberg.extract(input, config)puts result.results.first.contentuse xberg::{extract, ExtractionConfig, ExtractInput, OcrConfig};
#[tokio::main]async fn main() -> xberg::Result<()> { let config = ExtractionConfig { ocr: Some(OcrConfig { backend: "tesseract".to_string(), language: "eng".to_string(), ..Default::default() }), ..Default::default() };
let output = extract(ExtractInput::from_uri("scanned.pdf"), &config).await?; println!("{}", output.results[0].content); Ok(())}import Foundationimport Xbergimport RustBridge
let configJson = """{ "ocr": { "backend": "tesseract", "language": "eng" }}"""
let config = try extractionConfigFromJson(configJson)let input = try extractInputFromJson(#"{"kind":"uri","uri":"scanned.pdf"}"#)let resultOutput = try await extract(input: input, config: config)let result = resultOutput.results().get(index: 0)!
print(result.content.toString())alias Xberg.ExtractionConfig
config = %ExtractionConfig{ ocr: %{"enabled" => true, "backend" => "tesseract"}}
{:ok, output} = Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "scanned_document.pdf"}, config: config)
result = List.first(output.results)content = result.contentIO.puts("OCR Extracted content:")IO.puts(content)IO.puts("Metadata: #{inspect(result.metadata)}")import { extract } from "@xberg-io/xberg";
const config = { ocr: { backend: "tesseract", language: ["eng"], },};
const output = await extract({ kind: "uri", uri: "scanned.pdf" }, config);console.log(output.results[0].content);import { enableOcr, extract, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();await enableOcr();
const fileInput = document.getElementById("file") as HTMLInputElement;const file = fileInput.files?.[0];
if (file) { const result = await extract( { kind: "bytes", bytes: file, mimeType: file.type }, { ocr: { backend: "xberg-tesseract", language: "eng", }, }, ); console.log(result.content);}import { enableOcr, extract, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();await enableOcr(); // Uses native xberg-tesseract backend
const result = await extract( { kind: "uri", uri: "./scanned_document.png" }, { ocr: { backend: "xberg-tesseract", language: "eng", }, },);console.log(result.content);const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator();
const config_json = \\{ \\ "ocr": { \\ "backend": "tesseract", \\ "language": "eng" \\ } \\} ;
const input_json = "{\"kind\":\"uri\",\"uri\":\"scanned.pdf\"}"; const output_json = try xberg.extract(input_json, config_json); defer std.heap.c_allocator.free(output_json);
const owned = try allocator.dupe(u8, output_json); defer allocator.free(owned);
const stdout = std.io.getStdOut().writer(); try stdout.print("{s}\n", .{owned});}xberg extract scanned.pdf --ocr trueProcess Multiple Inputs
Section titled “Process Multiple Inputs”Pass a list of ExtractInput values to extract_batch. Mix kind = "uri" and
kind = "bytes" inputs when needed:
#include "xberg.h"#include <stdio.h>
int main(void) { const char *inputs_json = "[" "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}," "{\"kind\":\"bytes\",\"bytes\":[72,101,108,108,111]," "\"mime_type\":\"text/plain\",\"filename\":\"note.txt\"}" "]";
XBERGExtractionConfig *config = xberg_extraction_config_default(); XBERGExtractionResult *output = xberg_extract_batch(inputs_json, config); if (!output) { fprintf(stderr, "batch extraction failed: %s\n", xberg_last_error_context()); xberg_extraction_config_free(config); return 1; }
char *json = xberg_extraction_result_to_json(output); puts(json);
xberg_free_string(json); xberg_extraction_result_free(output); xberg_extraction_config_free(config); return 0;}using Xberg;
var output = await XbergConverter.ExtractBatchAsync( new List<ExtractInput> { ExtractInput.FromUri("document.pdf"), ExtractInput.FromBytes( System.Text.Encoding.UTF8.GetBytes("Hello from memory"), "text/plain", "note.txt" ), }, ExtractionConfig.Default());
foreach (var result in output.Results){ Console.WriteLine(result.Content);}import 'dart:convert';import 'package:xberg/xberg.dart';
final output = await Xberg.extractBatch([ const ExtractInput( kind: ExtractInputKind.uri, uri: 'document.pdf', ), ExtractInput( kind: ExtractInputKind.bytes, bytes: utf8.encode('Hello from memory'), mimeType: 'text/plain', filename: 'note.txt', ),]);
for (final result in output.results) { print(result.content);}package main
import ( "fmt" "log"
"github.com/xberg-io/xberg")
func main() { uriKind := xberg.ExtractInputKindURI bytesKind := xberg.ExtractInputKindBytes uri := "document.pdf" mimeType := "text/plain" filename := "note.txt"
output, err := xberg.ExtractBatch( []xberg.ExtractInput{ {Kind: &uriKind, URI: &uri}, { Kind: &bytesKind, Bytes: []byte("Hello from memory"), MimeType: &mimeType, Filename: &filename, }, }, xberg.ExtractionConfig{}, ) if err != nil { log.Fatal(err) }
for _, result := range output.Results { fmt.Println(result.Content) }}import io.xberg.ExtractInput;import io.xberg.ExtractInputKind;import io.xberg.ExtractionConfig;import io.xberg.Xberg;import java.nio.charset.StandardCharsets;import java.util.List;
var inputs = List.of( ExtractInput.builder() .withKind(ExtractInputKind.Uri) .withUri("document.pdf") .build(), ExtractInput.builder() .withKind(ExtractInputKind.Bytes) .withBytes("Hello from memory".getBytes(StandardCharsets.UTF_8)) .withMimeType("text/plain") .withFilename("note.txt") .build());
var output = Xberg.extractBatch(inputs, ExtractionConfig.builder().build());for (var result : output.results()) { System.out.println(result.content());}import io.xberg.ExtractInputimport io.xberg.ExtractInputKindimport io.xberg.ExtractionConfigimport io.xberg.Xberg
val output = Xberg.extractBatch( listOf( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), ExtractInput( kind = ExtractInputKind.BYTES, bytes = "Hello from memory".toByteArray(), mimeType = "text/plain", filename = "note.txt", ), ), ExtractionConfig(),)
output.results.forEach { result -> println(result.content)}import asyncio
from xberg import ExtractInput, extract_batch
async def main() -> None: inputs = [ ExtractInput(kind="uri", uri="document.pdf"), ExtractInput( kind="bytes", bytes=b"Hello from memory", mime_type="text/plain", filename="note.txt", ), ]
output = await extract_batch(inputs)
for result in output.results: print(result.results[0].content[:200])
asyncio.run(main())require "xberg"
inputs = [ Xberg::ExtractInput.new(kind: Xberg::ExtractInputKind::Uri, uri: "document.pdf"), Xberg::ExtractInput.new( kind: Xberg::ExtractInputKind::Bytes, bytes: "Hello from memory", mime_type: "text/plain", filename: "note.txt" )]
output = Xberg.extract_batch(inputs)
output.results.each do |result| puts result.contentenduse xberg::{extract_batch, ExtractInput, ExtractionConfig};
let config = ExtractionConfig::default();let inputs = vec![ ExtractInput::from_uri("document.pdf"), ExtractInput::from_bytes( b"Hello from memory".to_vec(), "text/plain", Some("note.txt".to_string()), ),];
let output = extract_batch(inputs, &config).await?;
for result in output.results { println!("{}", result.content);}import Xberg
let inputs = [ try extractInputFromJson(#"{"kind":"uri","uri":"document.pdf"}"#), try extractInputFromJson( #"{"kind":"bytes","bytes":[72,101,108,108,111],"mime_type":"text/plain","filename":"note.txt"}"# ),]
let output = try await extractBatch(inputs, "{}")print("Results: \(output.summary().results())")inputs = [ %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}, %Xberg.ExtractInput{ kind: :bytes, bytes: "Hello from memory", mime_type: "text/plain", filename: "note.txt" }]
{:ok, output} = Xberg.extract_batch(inputs: inputs, config: nil)
Enum.each(output.results, fn result -> IO.puts(result.content)end)import { extractBatch } from "@xberg-io/xberg";
const output = await extractBatch([ { kind: "uri", uri: "document.pdf" }, { kind: "bytes", bytes: Buffer.from("Hello from memory"), mimeType: "text/plain", filename: "note.txt", },]);
for (const result of output.results) { console.log(result.content.slice(0, 200));}import { extractBatch, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();
const pdfBytes = new Uint8Array( await fetch("/document.pdf").then((response) => response.arrayBuffer()),);
const output = await extractBatch([ { kind: "bytes", bytes: pdfBytes, mimeType: "application/pdf", filename: "document.pdf", }, { kind: "bytes", bytes: new TextEncoder().encode("Hello from memory"), mimeType: "text/plain", filename: "note.txt", },]);
for (const result of output.results) { console.log(result.content);}const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { const inputs_json = "[" ++ "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}," ++ "{\"kind\":\"bytes\",\"bytes\":[72,101,108,108,111]," ++ "\"mime_type\":\"text/plain\",\"filename\":\"note.txt\"}" ++ "]";
const output_json = try xberg.extract_batch(inputs_json, "{}"); defer std.heap.c_allocator.free(output_json);
try std.io.getStdOut().writer().print("{s}\n", .{output_json});}# Process multiple filesxberg extract doc1.pdf doc2.docx doc3.pptx
# Use glob patternsxberg extract documents/**/*.pdfRead Document Metadata
Section titled “Read Document Metadata”Every ExtractionResult contains document metadata in results. Each
ExtractedDocument includes format-specific metadata: page count for PDFs, sheet
names for Excel, dimensions for images:
#include "xberg.h"#include <stdio.h>
int main(void) { XBERGExtractInput *input = xberg_extract_input_from_uri("document.pdf"); if (!input) { fprintf(stderr, "Failed to create input (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); return 1; }
XBERGExtractionResult *result = xberg_extract(input, NULL); if (!result) { fprintf(stderr, "extraction failed (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); xberg_extract_input_free(input); return 1; }
char *results_json = xberg_extraction_result_results(result); if (results_json) { printf("Results: %s\n", results_json); } xberg_free_string(results_json);
char *full_json = xberg_extraction_result_to_json(result); if (full_json) { printf("Full result: %s\n", full_json); } xberg_free_string(full_json);
xberg_extract_input_free(input); xberg_extraction_result_free(result); return 0;}using Xberg;
var config = new ExtractionConfig{ PdfOptions = new PdfConfig { ExtractMetadata = true }};
var result = (await XbergConverter.ExtractAsync(ExtractInput.FromUri("document.pdf"), config)).Results[0];
if (result.Metadata?.Format.Pdf != null){ var pdfMeta = result.Metadata.Format.Pdf; Console.WriteLine($"Pages: {pdfMeta.PageCount}"); Console.WriteLine($"Author: {pdfMeta.Author}"); Console.WriteLine($"Title: {pdfMeta.Title}");}
var htmlResult = (await XbergConverter.ExtractAsync(ExtractInput.FromUri("page.html"), config)).Results[0];if (htmlResult.Metadata?.Format.Html != null){ var htmlMeta = htmlResult.Metadata.Format.Html; Console.WriteLine($"Title: {htmlMeta.Title}"); Console.WriteLine($"Description: {htmlMeta.Description}");
// Access keywords as array if (htmlMeta.Keywords != null && htmlMeta.Keywords.Count > 0) { Console.WriteLine($"Keywords: {string.Join(", ", htmlMeta.Keywords)}"); }
// Access canonical URL (renamed from canonical) if (htmlMeta.CanonicalUrl != null) { Console.WriteLine($"Canonical URL: {htmlMeta.CanonicalUrl}"); }
// Access Open Graph fields from dictionary if (htmlMeta.OpenGraph != null && htmlMeta.OpenGraph.Count > 0) { if (htmlMeta.OpenGraph.ContainsKey("image")) Console.WriteLine($"Open Graph Image: {htmlMeta.OpenGraph["image"]}"); if (htmlMeta.OpenGraph.ContainsKey("title")) Console.WriteLine($"Open Graph Title: {htmlMeta.OpenGraph["title"]}"); if (htmlMeta.OpenGraph.ContainsKey("type")) Console.WriteLine($"Open Graph Type: {htmlMeta.OpenGraph["type"]}"); }
// Access Twitter Card fields from dictionary if (htmlMeta.TwitterCard != null && htmlMeta.TwitterCard.Count > 0) { if (htmlMeta.TwitterCard.ContainsKey("card")) Console.WriteLine($"Twitter Card Type: {htmlMeta.TwitterCard["card"]}"); if (htmlMeta.TwitterCard.ContainsKey("creator")) Console.WriteLine($"Twitter Creator: {htmlMeta.TwitterCard["creator"]}"); }
// Access new fields if (htmlMeta.Language != null) Console.WriteLine($"Language: {htmlMeta.Language}");
if (htmlMeta.TextDirection != null) Console.WriteLine($"Text Direction: {htmlMeta.TextDirection}");
// Access headers if (htmlMeta.Headers != null && htmlMeta.Headers.Count > 0) Console.WriteLine($"Headers: {string.Join(", ", htmlMeta.Headers.Select(h => h.Text))}");
// Access links if (htmlMeta.Links != null && htmlMeta.Links.Count > 0) { foreach (var link in htmlMeta.Links) Console.WriteLine($"Link: {link.Href} ({link.Text})"); }
// Access images if (htmlMeta.Images != null && htmlMeta.Images.Count > 0) Console.WriteLine($"Images: {string.Join(", ", htmlMeta.Images.Select(i => i.Src))}");
// Access structured data if (htmlMeta.StructuredData != null && htmlMeta.StructuredData.Count > 0) Console.WriteLine($"Structured Data items: {htmlMeta.StructuredData.Count}");}import 'package:xberg/xberg.dart';
Future<void> main() async { final result = await XbergBridge.extract(ExtractInput(kind: ExtractInputKind.uri, uri: 'document.pdf'), config: ExtractionConfig());
final metadata = result.metadata;
if (metadata.title != null) { print('Title: ${metadata.title}'); } if (metadata.subject != null) { print('Subject: ${metadata.subject}'); } if (metadata.authors != null) { print('Authors: ${metadata.authors!.join(', ')}'); } if (metadata.keywords != null) { print('Keywords: ${metadata.keywords!.join(', ')}'); } if (metadata.language != null) { print('Language: ${metadata.language}'); } if (metadata.createdAt != null) { print('Created: ${metadata.createdAt}'); } if (metadata.modifiedAt != null) { print('Modified: ${metadata.modifiedAt}'); } if (metadata.extractionDurationMs != null) { print('Extraction took: ${metadata.extractionDurationMs} ms'); }
for (final entry in metadata.additional.entries) { print('Additional[${entry.key}]: ${entry.value}'); }}package main
import ( "fmt" "log" "strings"
"github.com/xberg-io/xberg/packages/go")
func main() { input := xberg.ExtractInputFromURI("document.pdf") result, err := xberg.Extract(*input, xberg.ExtractionConfig{}) if err != nil { log.Fatalf("extract pdf: %v", err) }
// Access PDF metadata if pdf, ok := result.Results[0].Metadata.PdfMetadata(); ok { if pdf.PageCount != nil { fmt.Printf("Pages: %d\n", *pdf.PageCount) } if pdf.Author != nil { fmt.Printf("Author: %s\n", *pdf.Author) } if pdf.Title != nil { fmt.Printf("Title: %s\n", *pdf.Title) } }
// Access HTML metadata htmlInput := xberg.ExtractInputFromURI("page.html") htmlResult, err := xberg.Extract(*htmlInput, xberg.ExtractionConfig{}) if err != nil { log.Fatalf("extract html: %v", err) } if html, ok := htmlResult.Results[0].Metadata.HTMLMetadata(); ok { if html.Title != nil { fmt.Printf("Title: %s\n", *html.Title) } if html.Description != nil { fmt.Printf("Description: %s\n", *html.Description) }
// Access keywords as array if len(html.Keywords) > 0 { fmt.Printf("Keywords: %s\n", strings.Join(html.Keywords, ", ")) }
// Access canonical URL (renamed from canonical) if html.CanonicalURL != nil { fmt.Printf("Canonical URL: %s\n", *html.CanonicalURL) }
// Access Open Graph fields from map if len(html.OpenGraph) > 0 { if image, ok := html.OpenGraph["image"]; ok { fmt.Printf("Open Graph Image: %s\n", image) } if ogTitle, ok := html.OpenGraph["title"]; ok { fmt.Printf("Open Graph Title: %s\n", ogTitle) } if ogType, ok := html.OpenGraph["type"]; ok { fmt.Printf("Open Graph Type: %s\n", ogType) } }
// Access Twitter Card fields from map if len(html.TwitterCard) > 0 { if card, ok := html.TwitterCard["card"]; ok { fmt.Printf("Twitter Card Type: %s\n", card) } if creator, ok := html.TwitterCard["creator"]; ok { fmt.Printf("Twitter Creator: %s\n", creator) } }
// Access new fields if html.Language != nil { fmt.Printf("Language: %s\n", *html.Language) }
if html.TextDirection != nil { fmt.Printf("Text Direction: %s\n", *html.TextDirection) }
// Access headers if len(html.Headers) > 0 { headers := make([]string, len(html.Headers)) for i, h := range html.Headers { headers[i] = h.Text } fmt.Printf("Headers: %s\n", strings.Join(headers, ", ")) }
// Access links if len(html.Links) > 0 { for _, link := range html.Links { fmt.Printf("Link: %s (%s)\n", link.Href, link.Text) } }
// Access images if len(html.Images) > 0 { for _, image := range html.Images { fmt.Printf("Image: %s\n", image.Src) } }
// Access structured data if len(html.StructuredData) > 0 { fmt.Printf("Structured data items: %d\n", len(html.StructuredData)) } }}import io.xberg.Xberg;import io.xberg.ExtractInputKind;import io.xberg.ExtractedDocument;import io.xberg.Metadata;import io.xberg.XbergException;import java.io.IOException;import java.util.Map;import java.util.List;
public class Main { public static void main(String[] args) { try { var resultOutput = Xberg.extract( io.xberg.ExtractInput.builder() .withKind(io.xberg.ExtractInputKind.Uri) .withUri("document.pdf") .build(), io.xberg.ExtractionConfig.builder().build() ); ExtractedDocument result = resultOutput.results().get(0); // Metadata is flat — format-specific fields are at the top level Metadata metadata = result.getMetadata(); metadata.getTitle().ifPresent(t -> System.out.println("Title: " + t)); metadata.getAuthors().ifPresent(a -> System.out.println("Authors: " + String.join(", ", a))); // Format-specific fields are in the additional map Map<String, Object> extra = metadata.getAdditional(); if (extra.get("page_count") != null) { System.out.println("Pages: " + extra.get("page_count")); } // Access HTML metadata var htmlResultOutput = Xberg.extract( io.xberg.ExtractInput.builder() .withKind(io.xberg.ExtractInputKind.Uri) .withUri("page.html") .build(), io.xberg.ExtractionConfig.builder().build() ); ExtractedDocument htmlResult = htmlResultOutput.results().get(0); Metadata htmlMeta = htmlResult.getMetadata(); htmlMeta.getTitle().ifPresent(t -> System.out.println("Title: " + t)); Map<String, Object> htmlExtra = htmlMeta.getAdditional(); String description = (String) htmlExtra.get("description"); if (description != null) { System.out.println("Description: " + description); } // Access keywords as array htmlMeta.getKeywords().ifPresent(keywords -> System.out.println("Keywords: " + keywords)); // Access canonical URL (renamed from canonical) String canonicalUrl = (String) htmlExtra.get("canonical_url"); if (canonicalUrl != null) { System.out.println("Canonical URL: " + canonicalUrl); } // Access Open Graph fields from map @SuppressWarnings("unchecked") Map<String, String> openGraph = (Map<String, String>) htmlExtra.get("open_graph"); if (openGraph != null) { System.out.println("Open Graph Image: " + openGraph.get("image")); System.out.println("Open Graph Title: " + openGraph.get("title")); System.out.println("Open Graph Type: " + openGraph.get("type")); } // Access Twitter Card fields from map Map<String, String> twitterCard = (Map<String, String>) htmlExtra.get("twitter_card"); if (twitterCard != null) { System.out.println("Twitter Card Type: " + twitterCard.get("card")); System.out.println("Twitter Creator: " + twitterCard.get("creator")); } // Access new fields htmlMeta.getLanguage().ifPresent(l -> System.out.println("Language: " + l)); String textDirection = (String) htmlExtra.get("text_direction"); if (textDirection != null) { System.out.println("Text Direction: " + textDirection); } // Access headers List<Map<String, Object>> headers = (List<Map<String, Object>>) htmlExtra.get("headers"); if (headers != null) { headers.stream() .map(h -> h.get("text")) .forEach(text -> System.out.print(text + ", ")); System.out.println(); } // Access links List<Map<String, Object>> links = (List<Map<String, Object>>) htmlExtra.get("links"); if (links != null) { for (Map<String, Object> link : links) { System.out.println("Link: " + link.get("href") + " (" + link.get("text") + ")"); } } // Access images List<Map<String, Object>> images = (List<Map<String, Object>>) htmlExtra.get("images"); if (images != null) { for (Map<String, Object> image : images) { System.out.println("Image: " + image.get("src")); } } // Access structured data List<Map<String, Object>> structuredData = (List<Map<String, Object>>) htmlExtra.get("structured_data"); if (structuredData != null) { System.out.println("Structured data items: " + structuredData.size()); } } catch (IOException | XbergException e) { System.err.println("Extraction failed: " + e.getMessage()); } }}import io.xberg.*import java.util.Optional
fun main() { val config = ExtractionConfig.builder().build() val resultOutput = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), config, ) val result = resultOutput.results.first()
val metadata = result.metadata metadata.title?.let { println("Title: $it") } metadata.authors?.let { println("Authors: ${it.joinToString(", ")}") }
// Format-specific metadata via discriminated union metadata.format?.pdf?.let { pdf -> pdf.pageCount?.let { println("Pages: $it") } pdf.producer?.let { println("Producer: $it") } pdf.pdfVersion?.let { println("PDF Version: $it") } }
// Access HTML metadata val htmlResultOutput = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "page.html"), config, ) val htmlResult = htmlResultOutput.results.first() htmlResult.metadata.format?.html?.let { html -> html.title?.let { println("Title: $it") } html.description?.let { println("Description: $it") } html.canonicalUrl?.let { println("Canonical URL: $it") } html.language?.let { println("Language: $it") }
// Access keywords list println("Keywords: ${html.keywords}")
// Open Graph fields are exposed as a Map<String, String> html.openGraph["image"]?.let { println("Open Graph Image: $it") } html.openGraph["title"]?.let { println("Open Graph Title: $it") }
// Twitter Card fields as a Map<String, String> html.twitterCard["card"]?.let { println("Twitter Card Type: $it") }
// Headers for (header in html.headers) { println("Header (level ${header.level}): ${header.text}") }
// Links for (link in html.links) { println("Link: ${link.href} (${link.text})") }
// Images for (image in html.images) { println("Image: ${image.src}") }
// Structured data if (html.structuredData.isNotEmpty()) { println("Structured data items: ${html.structuredData.size}") } }}import asynciofrom xberg import ExtractInput, extract, ExtractionConfig
async def main() -> None: result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
# Common fields live directly on Metadata metadata = result.results[0].metadata pdf_metadata = metadata.format.pdf if metadata.format else None if pdf_metadata and pdf_metadata.page_count: print(f"Pages: {pdf_metadata.page_count}") if metadata.title: print(f"Title: {metadata.title}") if metadata.authors: print(f"Authors: {', '.join(metadata.authors)}")
html_result = await extract(ExtractInput(uri="page.html"), ExtractionConfig()) html_metadata = html_result.results[0].metadata html_format = html_metadata.format.html if html_metadata.format else None if html_format: if html_format.title: print(f"Title: {html_format.title}") if html_format.description: print(f"Description: {html_format.description}")
# Access keywords as array if html_format.keywords: print(f"Keywords: {', '.join(html_format.keywords)}")
# Access canonical URL if html_format.canonical_url: print(f"Canonical URL: {html_format.canonical_url}")
# Access Open Graph fields from map if html_format.open_graph: if "image" in html_format.open_graph: print(f"Open Graph Image: {html_format.open_graph['image']}") if "title" in html_format.open_graph: print(f"Open Graph Title: {html_format.open_graph['title']}") if "type" in html_format.open_graph: print(f"Open Graph Type: {html_format.open_graph['type']}")
# Access Twitter Card fields from map if html_format.twitter_card: if "card" in html_format.twitter_card: print(f"Twitter Card Type: {html_format.twitter_card['card']}") if "creator" in html_format.twitter_card: print(f"Twitter Creator: {html_format.twitter_card['creator']}")
if html_format.language: print(f"Language: {html_format.language}")
if html_format.text_direction: print(f"Text Direction: {html_format.text_direction}")
# Access headers if html_format.headers: print(f"Headers: {', '.join(h.text for h in html_format.headers)}")
# Access links if html_format.links: for link in html_format.links: print(f"Link: {link.href} ({link.text})")
# Access images if html_format.images: for image in html_format.images: print(f"Image: {image}")
# Access structured data if html_format.structured_data: print(f"Structured data items: {len(html_format.structured_data)}")
asyncio.run(main())require 'xberg'
input = Xberg::ExtractInput.new(uri: 'document.pdf')config = Xberg::ExtractionConfig.newresult = Xberg.extract(input, config)
# Metadata is flat — format-specific fields are at the top levelmetadata = result.results.first.metadataif metadata['page_count'] puts "Pages: #{metadata['page_count']}"endif metadata['title'] puts "Title: #{metadata['title']}"endif metadata['authors'] puts "Authors: #{metadata['authors'].join(', ')}"end
# Access HTML metadatahtml_input = Xberg::ExtractInput.new(uri: 'page.html')html_result = Xberg.extract(html_input, Xberg::ExtractionConfig.new)metadata = html_result.results.first.metadataif metadata['title'] puts "Title: #{metadata['title']}"endif metadata['description'] puts "Description: #{metadata['description']}"end
# Access keywords as arrayif metadata['keywords'] puts "Keywords: #{metadata['keywords'].join(', ')}"end
# Access canonical URL (renamed from canonical)puts "Canonical URL: #{metadata['canonical_url']}" if metadata['canonical_url']
# Access Open Graph fields from mapopen_graph = metadata['open_graph'] || {}puts "Open Graph Image: #{open_graph['image']}" if open_graph['image']puts "Open Graph Title: #{open_graph['title']}" if open_graph['title']puts "Open Graph Type: #{open_graph['type']}" if open_graph['type']
# Access Twitter Card fields from maptwitter_card = metadata['twitter_card'] || {}puts "Twitter Card Type: #{twitter_card['card']}" if twitter_card['card']puts "Twitter Creator: #{twitter_card['creator']}" if twitter_card['creator']
# Access new fieldsputs "Language: #{metadata['language']}" if metadata['language']puts "Text Direction: #{metadata['text_direction']}" if metadata['text_direction']
# Access headersif metadata['headers'] puts "Headers: #{metadata['headers'].map { |h| h['text'] }.join(', ')}"end
# Access linksif metadata['links'] metadata['links'].each do |link| puts "Link: #{link['href']} (#{link['text']})" endend
# Access imagesif metadata['images'] metadata['images'].each do |image| puts "Image: #{image['src']}" endend
# Access structured dataif metadata['structured_data'] puts "Structured data items: #{metadata['structured_data'].length}"enduse xberg::{extract, ExtractionConfig, ExtractInput};
#[tokio::main]async fn main() -> xberg::Result<()> { let output = extract(ExtractInput::from_uri("document.pdf"), &ExtractionConfig::default()).await?; let result = &output.results[0];
if let Some(pdf_meta) = result.metadata.pdf { if let Some(pages) = pdf_meta.page_count { println!("Pages: {}", pages); } if let Some(author) = pdf_meta.author { println!("Author: {}", author); } if let Some(title) = pdf_meta.title { println!("Title: {}", title); } }
let html_output = extract(ExtractInput::from_uri("page.html"), &ExtractionConfig::default()).await?; let html_result = &html_output.results[0]; if let Some(html_meta) = html_result.metadata.html { if let Some(title) = html_meta.title { println!("Title: {}", title); } if let Some(desc) = html_meta.description { println!("Description: {}", desc); }
// Access keywords array println!("Keywords: {:?}", html_meta.keywords);
// Access canonical URL (renamed from canonical) if let Some(canonical) = html_meta.canonical_url { println!("Canonical URL: {}", canonical); }
// Access Open Graph fields as a map if let Some(og_image) = html_meta.open_graph.get("image") { println!("Open Graph Image: {}", og_image); } if let Some(og_title) = html_meta.open_graph.get("title") { println!("Open Graph Title: {}", og_title); }
// Access Twitter Card fields as a map if let Some(twitter_card) = html_meta.twitter_card.get("card") { println!("Twitter Card Type: {}", twitter_card); }
// Access new fields if let Some(lang) = html_meta.language { println!("Language: {}", lang); }
// Access headers if !html_meta.headers.is_empty() { for header in &html_meta.headers { println!("Header (level {}): {}", header.level, header.text); } }
// Access links if !html_meta.links.is_empty() { for link in &html_meta.links { println!("Link: {} ({})", link.href, link.text); } }
// Access images if !html_meta.images.is_empty() { for image in &html_meta.images { println!("Image: {}", image.src); } }
// Access structured data if !html_meta.structured_data.is_empty() { println!("Structured data items: {}", html_meta.structured_data.len()); } } Ok(())}import Foundationimport Xbergimport RustBridge
let config = try extractionConfigFromJson("{}")let input = try extractInputFromJson(#"{"kind":"uri","uri":"document.pdf"}"#)let resultOutput = try await extract(input: input, config: config)let result = resultOutput.results().get(index: 0)!
let metadata = result.metadata
if let title = metadata.title() { print("Title: \(title.toString())")}if let subject = metadata.subject() { print("Subject: \(subject.toString())")}if let language = metadata.language() { print("Language: \(language.toString())")}if let createdAt = metadata.created_at() { print("Created at: \(createdAt.toString())")}if let modifiedAt = metadata.modified_at() { print("Modified at: \(modifiedAt.toString())")}if let createdBy = metadata.created_by() { print("Created by: \(createdBy.toString())")}if let authors = metadata.authors() { let names = authors.map { $0.toString() } print("Authors: \(names)")}if let keywords = metadata.keywords() { let words = keywords.map { $0.toString() } print("Keywords: \(words)")}if let duration = metadata.extraction_duration_ms() { print("Extraction duration (ms): \(duration)")}if let pages = metadata.pages() { print("Page count: \(pages.total_count())")}{:ok, output} = Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}, config: nil)result = List.first(output.results)# Metadata is flat — format-specific fields are at the top levelmetadata = result.metadataIO.puts("MIME type: #{result.mime_type}")IO.puts("All metadata keys: #{inspect(Map.keys(metadata))}")
# Access PDF metadata directly from the flat mappage_count = metadata["page_count"]if page_count, do: IO.puts("Page count: #{page_count}")
authors = metadata["authors"] || []if authors != [], do: IO.puts("Authors: #{Enum.join(authors, ", ")}")
title = metadata["title"]if title, do: IO.puts("Title: #{title}")
# Access HTML metadata directly from the flat map{:ok, html_output} = Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "page.html"}, config: nil)html_result = List.first(html_output.results)html_meta = html_result.metadata
keywords = html_meta["keywords"] || []if keywords != [], do: IO.puts("Keywords: #{Enum.join(keywords, ", ")}")
description = html_meta["description"]if description, do: IO.puts("Description: #{description}")import { extract } from "@xberg-io/xberg";
const output = await extract({ kind: "uri", uri: "document.pdf",});const result = output.results[0];console.log(`Metadata: ${JSON.stringify(result.metadata)}`);if (result.metadata?.pageCount) { console.log(`Pages: ${result.metadata.pageCount}`);}
const htmlOutput = await extract({ kind: "uri", uri: "page.html",});const htmlResult = htmlOutput.results[0];console.log(`HTML Metadata: ${JSON.stringify(htmlResult.metadata)}`);
const htmlMeta = htmlResult.metadata;if (htmlMeta?.title) { console.log(`Title: ${htmlMeta.title}`);}
// Access keywords as arrayif (htmlMeta?.keywords && htmlMeta.keywords.length > 0) { console.log(`Keywords: ${htmlMeta.keywords.join(", ")}`);}
// Access canonical URL (renamed from canonical)if (htmlMeta?.canonicalUrl) { console.log(`Canonical URL: ${htmlMeta.canonicalUrl}`);}
// Access Open Graph fields from mapif (htmlMeta?.openGraph) { if (htmlMeta.openGraph["image"]) { console.log(`Open Graph Image: ${htmlMeta.openGraph["image"]}`); } if (htmlMeta.openGraph["title"]) { console.log(`Open Graph Title: ${htmlMeta.openGraph["title"]}`); } if (htmlMeta.openGraph["type"]) { console.log(`Open Graph Type: ${htmlMeta.openGraph["type"]}`); }}
// Access Twitter Card fields from mapif (htmlMeta?.twitterCard) { if (htmlMeta.twitterCard["card"]) { console.log(`Twitter Card Type: ${htmlMeta.twitterCard["card"]}`); } if (htmlMeta.twitterCard["creator"]) { console.log(`Twitter Creator: ${htmlMeta.twitterCard["creator"]}`); }}
// Access new fieldsif (htmlMeta?.language) { console.log(`Language: ${htmlMeta.language}`);}
if (htmlMeta?.textDirection) { console.log(`Text Direction: ${htmlMeta.textDirection}`);}
// Access headersif (htmlMeta?.headers && htmlMeta.headers.length > 0) { console.log(`Headers: ${htmlMeta.headers.map((h) => h.text).join(", ")}`);}
// Access linksif (htmlMeta?.links && htmlMeta.links.length > 0) { htmlMeta.links.forEach((link) => { console.log(`Link: ${link.href} (${link.text})`); });}
// Access imagesif (htmlMeta?.images && htmlMeta.images.length > 0) { htmlMeta.images.forEach((image) => { console.log(`Image: ${image.src}`); });}
// Access structured dataif (htmlMeta?.structuredData && htmlMeta.structuredData.length > 0) { console.log(`Structured data items: ${htmlMeta.structuredData.length}`);}import { extract, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();
const fileInput = document.getElementById("file") as HTMLInputElement;const file = fileInput.files?.[0];
if (file) { const result = await extract({ kind: "bytes", bytes: file, mimeType: file.type || "application/octet-stream" }); console.log(`Metadata: ${JSON.stringify(result.metadata)}`);
// Access common metadata fields if (result.metadata.title) { console.log(`Title: ${result.metadata.title}`); }
// Access format-specific metadata const metadata = result.metadata;
// For HTML files if (metadata.html) { const htmlMeta = metadata.html; console.log(`HTML Title: ${htmlMeta.title}`); console.log(`Description: ${htmlMeta.description}`);
// Access keywords as array if (htmlMeta.keywords && htmlMeta.keywords.length > 0) { console.log(`Keywords: ${htmlMeta.keywords.join(", ")}`); }
// Access canonical URL if (htmlMeta.canonical_url) { console.log(`Canonical URL: ${htmlMeta.canonical_url}`); }
// Access Open Graph fields if (htmlMeta.open_graph) { if (htmlMeta.open_graph["title"]) { console.log(`OG Title: ${htmlMeta.open_graph["title"]}`); } if (htmlMeta.open_graph["image"]) { console.log(`OG Image: ${htmlMeta.open_graph["image"]}`); } }
// Access Twitter Card fields if (htmlMeta.twitter_card && htmlMeta.twitter_card["card"]) { console.log(`Twitter Card Type: ${htmlMeta.twitter_card["card"]}`); }
// Access headers if (htmlMeta.headers && htmlMeta.headers.length > 0) { console.log(`Headers: ${htmlMeta.headers.map((h: any) => h.text).join(", ")}`); }
// Access links if (htmlMeta.links && htmlMeta.links.length > 0) { htmlMeta.links.forEach((link: any) => { console.log(`Link: ${link.href} (${link.text})`); }); }
// Access images if (htmlMeta.images && htmlMeta.images.length > 0) { htmlMeta.images.forEach((image: any) => { console.log(`Image: ${image.src}`); }); }
// Access structured data if (htmlMeta.structured_data && htmlMeta.structured_data.length > 0) { console.log(`Structured data items: ${htmlMeta.structured_data.length}`); } }
// PDF-specific fields are at the top level of metadata if (metadata.pageCount) { console.log(`Pages: ${metadata.pageCount}`); } if (metadata.authors && metadata.authors.length > 0) { console.log(`Authors: ${metadata.authors.join(", ")}`); }}const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator();
const config_json = "{}"; const input_json = "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}"; const output_json = try xberg.extract(input_json, config_json); defer std.heap.c_allocator.free(output_json);
var parsed = try std.json.parseFromSlice(std.json.Value, allocator, output_json, .{}); defer parsed.deinit();
const output = parsed.value; if (output != .object) return;
const results_val = output.object.get("results") orelse return; if (results_val != .array or results_val.array.items.len == 0) return; const root = results_val.array.items[0]; if (root != .object) return;
const stdout = std.io.getStdOut().writer();
if (root.object.get("metadata")) |metadata_val| { if (metadata_val != .object) return; const metadata = metadata_val.object;
if (metadata.get("title")) |title_val| { if (title_val == .string) { try stdout.print("Title: {s}\n", .{title_val.string}); } }
if (metadata.get("authors")) |authors_val| { if (authors_val == .array) { for (authors_val.array.items) |author| { if (author == .string) { try stdout.print("Author: {s}\n", .{author.string}); } } } }
if (metadata.get("language")) |language_val| { if (language_val == .string) { try stdout.print("Language: {s}\n", .{language_val.string}); } }
if (metadata.get("created_at")) |created_val| { if (created_val == .string) { try stdout.print("Created: {s}\n", .{created_val.string}); } }
if (metadata.get("pages")) |pages_val| { if (pages_val == .object) { if (pages_val.object.get("total_count")) |total_val| { if (total_val == .integer) { try stdout.print("Pages: {d}\n", .{total_val.integer}); } } } } }}Extract and parse metadata using JSON output:
# Extract with metadata (JSON format includes metadata automatically)xberg extract document.pdf --format json
# Save to file and parse metadataxberg extract document.pdf --format json > result.json
# Print all metadata fieldscat result.json | jq '.metadata'
# Extract HTML metadataxberg extract page.html --format json | jq '.metadata'
# Get specific fieldsxberg extract document.pdf --format json | \ jq '.metadata | {page_count, authors, title}'
# Process multiple filesxberg batch documents/*.pdf --format json > all_metadata.jsonJSON Output Structure:
{ "results": [ { "content": "Extracted text...", "mime_type": "application/pdf", "metadata": { "title": "Document Title", "authors": ["John Doe"], "created_by": "LaTeX with hyperref package", "format_type": "pdf", "page_count": 10 }, "tables": [] } ], "errors": [], "summary": { "inputs": 1, "results": 1, "errors": 0 }}Xberg extracts format-specific metadata for:
- PDF: page count, title, authors (list), creation date, modification date
- HTML: SEO tags, Open Graph, Twitter Card, structured data, headers, links, images
- Excel: sheet count, sheet names
- Email: from, to, CC, BCC, message ID, attachments
- PowerPoint: title, author, description, fonts
- Images: dimensions, format, EXIF data
- Archives: format, file count, file list, sizes
- XML: element count, unique elements
- Text/Markdown: word count, line count, headers, links
See Types Reference for complete metadata reference.
Extract Tables
Section titled “Extract Tables”Tables come back as both structured cells and Markdown. Xberg extracts them from PDFs, spreadsheets, and HTML:
#include "xberg.h"#include <stdio.h>
int main(void) { XBERGExtractInput *input = xberg_extract_input_from_uri("spreadsheet.xlsx"); if (!input) { fprintf(stderr, "Failed to create input (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); return 1; }
XBERGExtractionResult *result = xberg_extract(input, NULL); if (!result) { fprintf(stderr, "extraction failed (code %d): %s\n", xberg_last_error_code(), xberg_last_error_context()); xberg_extract_input_free(input); return 1; }
char *result_json = xberg_extraction_result_to_json(result); if (result_json) { printf("Extraction result (JSON): %s\n", result_json); } else { printf("No extraction result available\n"); } xberg_free_string(result_json);
xberg_extract_input_free(input); xberg_extraction_result_free(result); return 0;}using Xberg;
var result = (await XbergConverter.ExtractAsync(ExtractInput.FromUri("document.pdf"), new ExtractionConfig())).Results[0];
foreach (var table in result.Tables){ Console.WriteLine($"Table with {table.Cells.Count} rows"); Console.WriteLine(table.Markdown);
foreach (var row in table.Cells) { Console.WriteLine(string.Join(" | ", row)); }}import 'package:xberg/xberg.dart';
Future<void> main() async { final result = await XbergBridge.extract(ExtractInput(kind: ExtractInputKind.uri, uri: 'document.pdf'), config: ExtractionConfig());
for (final table in result.results[0].tables) { print('Table on page ${table.pageNumber} with ${table.cells.length} rows'); print(table.markdown);
for (final row in table.cells) { print(row); }
if (table.boundingBox != null) { print('Bounding box: ${table.boundingBox}'); } }}package main
import ( "fmt" "log"
"github.com/xberg-io/xberg/packages/go")
func main() { input := xberg.ExtractInputFromURI("document.pdf") result, err := xberg.Extract(*input, xberg.ExtractionConfig{}) if err != nil { log.Fatalf("extract failed: %v", err) }
// Iterate over tables for _, table := range result.Results[0].Tables { fmt.Printf("Table with %d rows\n", len(table.Cells)) fmt.Println(table.Markdown) // Markdown representation
// Access cells for _, row := range table.Cells { fmt.Println(row) } }}import io.xberg.Xberg;import io.xberg.ExtractInputKind;import io.xberg.ExtractionResult;import io.xberg.ExtractedDocument;import io.xberg.XbergException;import io.xberg.ExtractInput;import io.xberg.ExtractionConfig;import io.xberg.Table;import java.io.IOException;import java.util.List;
public class Main { public static void main(String[] args) { try { ExtractionResult output = Xberg.extract( ExtractInput.builder().withKind(ExtractInputKind.Uri).withUri("document.pdf").build(), ExtractionConfig.builder().build() ); ExtractedDocument result = output.results().get(0); for (Table table : result.tables()) { System.out.println("Table with " + table.cells().size() + " rows"); System.out.println(table.markdown()); for (List<String> row : table.cells()) { System.out.println(row); } } } catch (IOException | XbergException e) { System.err.println("Extraction failed: " + e.getMessage()); } }}import io.xberg.*
fun main() { val config = ExtractionConfig.builder().build() val resultOutput = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), config, ) val result = resultOutput.results.first()
val tables = result.tables ?: emptyList() for (table in tables) { println("Table on page ${table.pageNumber} with ${table.cells.size} rows") println(table.markdown)
for (row in table.cells) { println(row) } }}import asynciofrom xberg import ExtractInput, extract, ExtractionConfig
async def main() -> None: result = await extract(ExtractInput(uri="document.pdf"), ExtractionConfig())
for table in result.results[0].tables: row_count: int = len(table.cells) print(f"Table with {row_count} rows") print(table.markdown) for row in table.cells: print(row)
asyncio.run(main())require 'xberg'
input = Xberg::ExtractInput.new(uri: 'document.pdf')config = Xberg::ExtractionConfig.newresult = Xberg.extract(input, config)
# Iterate over tablesresult.results.first.tables.each do |table| puts "Table with #{table['cells'].length} rows" puts table['markdown'] # Markdown representation
# Access cells table['cells'].each do |row| puts row endenduse xberg::{extract, ExtractionConfig, ExtractInput};
#[tokio::main]async fn main() -> xberg::Result<()> { let output = extract(ExtractInput::from_uri("document.pdf"), &ExtractionConfig::default()).await?; let result = &output.results[0];
for table in &result.tables { println!("Table with {} rows", table.cells.len()); println!("{}", table.markdown);
for row in &table.cells { println!("{:?}", row); } } Ok(())}import Foundationimport Xbergimport RustBridge
let config = try extractionConfigFromJson("{}")let input = try extractInputFromJson(#"{"kind":"uri","uri":"document.pdf"}"#)let resultOutput = try await extract(input: input, config: config)let result = resultOutput.results().get(index: 0)!
let tables = result.tablesprint("Tables: \(tables.count)")
for (index, table) in tables.enumerated() { print("Table \(index) on page \(table.page_number())") print(table.markdown().toString())
if let bbox = table.bounding_box() { print(" Bounding box: \(bbox.toString())") }}{:ok, output} = Xberg.extract(input: %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}, config: nil)result = List.first(output.results)tables = result.tablesIO.puts("Total tables found: #{length(tables)}")
Enum.with_index(tables, 1) |> Enum.each(fn {table, index} -> IO.puts("\n--- Table #{index} ---")
# Access table cells cells = table["cells"] || [] IO.puts("Rows: #{length(cells)}")
# Access table markdown representation markdown = table["markdown"] IO.puts("Markdown representation:") IO.puts(markdown)end)import { ExtractInputKind, extract } from "@xberg-io/xberg";
const output = await extract({ kind: "uri", uri: "document.pdf",});
output.results[0].tables?.forEach((table) => { console.log(`Table with ${table.cells?.length ?? 0} rows`); console.log(table.markdown); table.cells?.forEach((row) => console.log(row.join(" | ")));});import { initWasm, extract } from "@xberg-io/xberg-wasm";
await initWasm();
const fileInput = document.getElementById("file") as HTMLInputElement;const file = fileInput.files?.[0];
if (file) { const bytes = new Uint8Array(await file.arrayBuffer()); const result = await extract({ kind: "bytes", bytes, mimeType: file.type || "application/pdf" }, undefined);
result.tables?.forEach((table) => { console.log(`Table with ${table.cells?.length ?? 0} rows`); if (table.markdown) { console.log(table.markdown); } table.cells?.forEach((row) => console.log(row.join(" | "))); });}const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator();
const config_json = "{}"; const input_json = "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}"; const output_json = try xberg.extract(input_json, config_json); defer std.heap.c_allocator.free(output_json);
var parsed = try std.json.parseFromSlice(std.json.Value, allocator, output_json, .{}); defer parsed.deinit();
const output = parsed.value; if (output != .object) return;
const results_val = output.object.get("results") orelse return; if (results_val != .array or results_val.array.items.len == 0) return; const root = results_val.array.items[0]; if (root != .object) return;
const stdout = std.io.getStdOut().writer();
const tables_val = root.object.get("tables") orelse return; if (tables_val != .array) return;
for (tables_val.array.items) |table| { if (table != .object) continue;
if (table.object.get("cells")) |cells_val| { if (cells_val == .array) { try stdout.print("Table with {d} rows\n", .{cells_val.array.items.len});
for (cells_val.array.items) |row_val| { if (row_val != .array) continue; try stdout.print(" Row:", .{}); for (row_val.array.items) |cell_val| { if (cell_val == .string) { try stdout.print(" [{s}]", .{cell_val.string}); } } try stdout.print("\n", .{}); } } }
if (table.object.get("markdown")) |markdown_val| { if (markdown_val == .string) { try stdout.print("{s}\n", .{markdown_val.string}); } }
if (table.object.get("page_number")) |page_val| { if (page_val == .integer) { try stdout.print("Page: {d}\n", .{page_val.integer}); } } }}Extract and process tables from documents:
# Extract with JSON format (includes tables when detected)xberg extract document.pdf --format json
# Save tables to JSONxberg extract spreadsheet.xlsx --format json > tables.json
# Extract and parse table markdownxberg extract document.pdf --format json | \ jq '.tables[]? | .markdown'
# Get table cellsxberg extract document.pdf --format json | \ jq '.tables[]? | .cells'
# Batch extract tables from multiple filesxberg batch documents/**/*.pdf --format json > all_tables.jsonJSON Table Structure:
{ "results": [ { "content": "...", "tables": [ { "cells": [ ["Name", "Age", "City"], ["Alice", "30", "New York"], ["Bob", "25", "Los Angeles"] ], "markdown": "| Name | Age | City |\\n|------|-----|--------|\\n| Alice | 30 | New York |\\n| Bob | 25 | Los Angeles |" } ] } ], "errors": [], "summary": { "inputs": 1, "results": 1, "errors": 0 }}Going Async
Section titled “Going Async”Use async extraction in web servers, background workers, or anywhere you need non-blocking I/O:
#include "xberg.h"#include <stdio.h>
int main(void) { XBERGExtractionConfig *config = xberg_extraction_config_default(); XBERGExtractInput *input = xberg_extract_input_from_json("{\"kind\":\"uri\",\"uri\":\"document.pdf\"}");
XBERGExtractionResult *output = xberg_extract(input, config); if (!output) { fprintf(stderr, "extraction failed: %s\n", xberg_last_error_context()); xberg_extract_input_free(input); xberg_extraction_config_free(config); return 1; }
char *json = xberg_extraction_result_to_json(output); puts(json);
xberg_free_string(json); xberg_extraction_result_free(output); xberg_extract_input_free(input); xberg_extraction_config_free(config); return 0;}using Xberg;
var output = await XbergConverter.ExtractAsync( ExtractInput.FromUri("document.pdf"), ExtractionConfig.Default());
Console.WriteLine(output.Results[0].Content);import 'package:xberg/xberg.dart';
final output = await Xberg.extract( const ExtractInput( kind: ExtractInputKind.uri, uri: 'document.pdf', ),);
print(output.results.first.content);package main
import ( "fmt" "log"
"github.com/xberg-io/xberg")
func main() { kind := xberg.ExtractInputKindURI uri := "document.pdf"
output, err := xberg.Extract( xberg.ExtractInput{Kind: &kind, URI: &uri}, xberg.ExtractionConfig{}, ) if err != nil { log.Fatal(err) }
fmt.Println(output.Results[0].Content)}import io.xberg.ExtractInput;import io.xberg.ExtractInputKind;import io.xberg.ExtractionConfig;import io.xberg.Xberg;
var input = ExtractInput.builder() .withKind(ExtractInputKind.Uri) .withUri("document.pdf") .build();
var output = Xberg.extract(input, ExtractionConfig.builder().build());System.out.println(output.results().get(0).content());import io.xberg.ExtractInputimport io.xberg.ExtractInputKindimport io.xberg.ExtractionConfigimport io.xberg.Xberg
val output = Xberg.extract( ExtractInput(kind = ExtractInputKind.URI, uri = "document.pdf"), ExtractionConfig(),)
println(output.results.first().content)import asyncio
from xberg import ExtractInput, extract
async def main() -> None: output = await extract(ExtractInput(kind="uri", uri="document.pdf"))
print(output.results[0].content) print(f"Results: {output.summary.results}")
asyncio.run(main())require "xberg"
output = Xberg.extract(Xberg::ExtractInput.new(uri: "document.pdf"))
puts output.results.first.contentputs "Results: #{output.summary.results}"use xberg::{extract, ExtractInput, ExtractionConfig};
let config = ExtractionConfig::default();let output = extract(ExtractInput::from_uri("document.pdf"), &config).await?;
println!("{}", output.results[0].content);println!("Results: {}", output.summary.results);import Xberg
let input = #"{"kind":"uri","uri":"document.pdf"}"#let output = try await extract(input, "{}")
print("Results: \(output.summary().results())")input = %Xberg.ExtractInput{kind: :uri, uri: "document.pdf"}
{:ok, output} = Xberg.extract(input: input, config: nil)
IO.inspect(output.summary)import { ExtractInputKind, extract } from "@xberg-io/xberg";
const output = await extract({ kind: "uri", uri: "document.pdf",});
console.log(output.results[0].content);console.log(`Results: ${output.summary.results}`);import { ExtractInputKind, extract, initWasm } from "@xberg-io/xberg-wasm";
await initWasm();
const bytes = new Uint8Array( await fetch("/document.pdf").then((response) => response.arrayBuffer()),);
const output = await extract({ kind: "bytes", bytes, mimeType: "application/pdf", filename: "document.pdf",});
console.log(output.results[0].content);const std = @import("std");const xberg = @import("xberg");
pub fn main() !void { const output_json = try xberg.extract( "{\"kind\":\"uri\",\"uri\":\"document.pdf\"}", "{}", ); defer std.heap.c_allocator.free(output_json);
try std.io.getStdOut().writer().print("{s}\n", .{output_json});}Next Steps
Section titled “Next Steps”You’ve covered the core API. Go deeper:
- Configuration Guide — OCR backends, chunking, language detection, config files
- Extract from Bytes — Use
ExtractInput(kind="bytes") - OCR Setup — Tesseract, PaddleOCR, Sceptre, Candle, and VLM backends
- Types Reference — Full metadata fields for every format
- Docker Deployment — Run Xberg in containers
- API Reference — Complete API documentation