SDK Reference

Data structures and types for plugin development

Overview

The Plugin SDK provides standardized data structures for plugin output. Using the SDK ensures your plugin's output matches what FileGrind expects.

Available SDKs

Rust fgnd-plugin-sdk Reference implementation
Go fgnd-plugin-sdk-go Go module
Objective-C FGNDPluginSDK Swift Package

Core types

  • FileMetadata - Document metadata (title, authors, page count)
  • DocumentOutline - Table of contents / hierarchical structure
  • GroundChips - Extracted text content by page
  • ExtractionInfo - Metadata about the extraction process
  • CapManifest - Plugin manifest with capabilities

FileMetadata

Represents metadata extracted from a document. Used by the extract-metadata capability.

Fields

FileMetadata {
    // Required
    file_path: String,          // Path to source file
    file_size_bytes: u64,       // File size
    document_type: String,      // "pdf", "epub", "xyz"

    // Common metadata
    title: Option<String>,
    authors: Vec<String>,
    contributors: Vec<String>,
    keywords: Vec<String>,

    // Dates (ISO 8601 format)
    creation_date: Option<String>,
    modification_date: Option<String>,

    // Content info
    page_count: Option<usize>,
    chapter_count: Option<usize>,
    word_count: Option<usize>,
    character_count: Option<usize>,

    // Format-specific
    mime_type: Option<String>,
    encoding: Option<String>,
    format_version: Option<String>,
    language: Option<String>,

    // PDF-specific
    pdf_version: Option<String>,
    has_forms: bool,
    is_encrypted: bool,
    is_linearized: bool,
    attachment_count: usize,

    // EPUB-specific
    epub_version: Option<String>,
    publisher: Option<String>,
    publication_date: Option<String>,
    rights: Option<String>,
    has_drm: bool,

    // Extensible
    extended_metadata: HashMap<String, Value>,
}

Rust usage

use fgnd_plugin_sdk::FileMetadata;

// Create with required fields
let mut metadata = FileMetadata::new(
    "/path/to/file.xyz".to_string(),
    "xyz".to_string(),
    1024 * 1024,  // 1 MB
);

// Set optional fields
metadata.title = Some("Document Title".into());
metadata.add_author("Author One");
metadata.add_author("Author Two");
metadata.add_keyword("tag1");
metadata.page_count = Some(42);

// Add custom fields
metadata.set_extended("custom_field", json!("value"));
metadata.set_extended("nested", json!({"key": "value"}));

// Serialize to JSON
let json = serde_json::to_string_pretty(&metadata)?;

JSON output

{
  "file_path": "/path/to/file.xyz",
  "file_size_bytes": 1048576,
  "document_type": "xyz",
  "title": "Document Title",
  "authors": ["Author One", "Author Two"],
  "keywords": ["tag1"],
  "page_count": 42,
  "extended_metadata": {
    "custom_field": "value",
    "nested": {"key": "value"}
  }
}

DocumentOutline

Represents a hierarchical table of contents. Used by the extract-outline capability.

Fields

DocumentOutline {
    source_file: String,
    document_title: Option<String>,
    document_type: String,
    total_pages: usize,
    entries: Vec<OutlineEntry>,
    has_outline: bool,
    extraction_info: ExtractionInfo,
}

OutlineEntry {
    title: String,
    level: usize,                    // 0 = top level
    page: Option<usize>,             // 1-indexed page number
    source_ref: Option<String>,      // Anchor, ID, or filename
    children: Vec<OutlineEntry>,     // Nested entries
}

Rust usage

use fgnd_plugin_sdk::{DocumentOutline, OutlineEntry, ExtractionInfo};

// Create outline
let mut outline = DocumentOutline::new(
    "/path/to/file.xyz",
    "xyz",
    100,  // total pages
);
outline.document_title = Some("Book Title".into());

// Add entries with nesting
let mut chapter1 = OutlineEntry::new("Chapter 1: Introduction", 0)
    .with_page(1);

let section1 = OutlineEntry::new("1.1 Background", 1)
    .with_page(3);
let section2 = OutlineEntry::new("1.2 Overview", 1)
    .with_page(8);

chapter1.add_child(section1);
chapter1.add_child(section2);
outline.add_entry(chapter1);

let chapter2 = OutlineEntry::new("Chapter 2: Methods", 0)
    .with_page(15);
outline.add_entry(chapter2);

// Set extraction info
outline.extraction_info = ExtractionInfo::new("myplugin", "1.0.0");

JSON output

{
  "source_file": "/path/to/file.xyz",
  "document_title": "Book Title",
  "document_type": "xyz",
  "total_pages": 100,
  "has_outline": true,
  "entries": [
    {
      "title": "Chapter 1: Introduction",
      "level": 0,
      "page": 1,
      "children": [
        {
          "title": "1.1 Background",
          "level": 1,
          "page": 3,
          "children": []
        },
        {
          "title": "1.2 Overview",
          "level": 1,
          "page": 8,
          "children": []
        }
      ]
    },
    {
      "title": "Chapter 2: Methods",
      "level": 0,
      "page": 15,
      "children": []
    }
  ],
  "extraction_info": {
    "extractor_name": "myplugin",
    "extractor_version": "1.0.0"
  }
}

GroundChips

Contains extracted text content organized by page. Used by the grind capability.

Fields

GroundChips {
    source_file: String,
    document_title: Option<String>,
    document_type: String,
    total_pages: usize,
    pages: Vec<FileChip>,
    extraction_info: ExtractionInfo,
}

FileChip {
    order_index: usize,              // 1-indexed page number
    text_content: String,
    source_ref: Option<String>,      // Section name or anchor
    word_count: Option<usize>,       // Auto-calculated
    character_count: Option<usize>,
}

Rust usage

use fgnd_plugin_sdk::{GroundChips, FileChip};

// Create pages container
let mut pages = GroundChips::new("/path/to/file.xyz", "xyz");

// Add pages
let page1 = FileChip::new_with_text(
    1,
    "This is the content of page 1. It contains multiple sentences.".into(),
);
pages.add_chip(page1);

let page2 = FileChip::new_with_text(
    2,
    "Page 2 has different content...".into(),
);
pages.add_chip(page2);

// Word/character counts are calculated automatically
let total_words = pages.total_word_count();
let total_chars = pages.total_character_count();

JSON output

{
  "source_file": "/path/to/file.xyz",
  "document_type": "xyz",
  "total_pages": 2,
  "pages": [
    {
      "order_index": 1,
      "text_content": "This is the content of page 1...",
      "word_count": 11,
      "character_count": 65
    },
    {
      "order_index": 2,
      "text_content": "Page 2 has different content...",
      "word_count": 5,
      "character_count": 31
    }
  ],
  "extraction_info": {
    "extractor_name": "myplugin",
    "extractor_version": "1.0.0"
  }
}

ExtractionInfo

Metadata about the extraction process itself. Include this in DocumentOutline and GroundChips output.

Fields

ExtractionInfo {
    extractor_name: String,      // Plugin name
    extractor_version: String,   // Plugin version
    extracted_at: Option<String>, // ISO 8601 timestamp
    warnings: Vec<String>,       // Non-fatal issues
}

Rust usage

use fgnd_plugin_sdk::ExtractionInfo;

let mut info = ExtractionInfo::new("myplugin", "1.0.0");
info.extracted_at = Some(chrono::Utc::now().to_rfc3339());
info.add_warning("Page 3 contained unreadable text");
info.add_warning("OCR confidence low for images");

CapManifest

The plugin manifest returned by the manifest command. Describes the plugin and its capabilities.

Fields

CapManifest {
    name: String,
    version: String,
    description: String,
    authors: Vec<String>,
    caps: Vec<Cap>,
}

Rust usage

use capns::{Cap, CapManifest, CapUrnBuilder};

// Build capabilities
let extract_metadata_cap = Cap::new(
    CapUrnBuilder::new()
        .tag("action", "extract")
        .tag("format", "xyz")
        .tag("target", "metadata")
        .build()?,
    "Extract XYZ Metadata".into(),
    "extract-metadata".into(),
);

// Create manifest
let manifest = CapManifest::new(
    "myplugin".into(),
    env!("CARGO_PKG_VERSION").into(),
    "Processes XYZ files".into(),
    vec![extract_metadata_cap],
).with_author("Your Name".into());

Standard Caps

The SDK provides helpers to create standard capability definitions.

Standard capability URNs

// Extract metadata
cap:op=extract;format={ext};target=metadata

// Extract outline
cap:op=extract;format={ext};target=outline

// Grind
cap:op=extract;format={ext};target=pages

// Generate thumbnail
cap:op=generate;format={ext};target=thumbnail

Rust helpers

use capns::{
    extract_metadata_cap,
    extract_outline_cap,
    grind_cap,
    generate_thumbnail_cap,
};

// These fetch full capability definitions from the registry
let cap = extract_metadata_cap(registry.clone(), "xyz").await?;
let cap = extract_outline_cap(registry.clone(), "xyz").await?;
let cap = grind_cap(registry.clone(), "xyz").await?;
let cap = generate_thumbnail_cap(registry.clone(), "xyz").await?;

Go constants

import sdk "github.com/fgnd/fgnd-plugin-sdk-go"

sdk.StandardCaps.ExtractMetadata  // "extract-metadata"
sdk.StandardCaps.ExtractOutline   // "extract-outline"
sdk.StandardCaps.Grind     // "grind"
sdk.StandardCaps.GenerateThumbnail // "generate-thumbnail"

Go SDK

The Go SDK provides the same data structures with Go-native APIs.

Installation

go get github.com/fgnd/fgnd-plugin-sdk-go

Usage

package main

import (
    "encoding/json"
    "fmt"
    sdk "github.com/fgnd/fgnd-plugin-sdk-go"
)

func main() {
    // Create metadata
    metadata := sdk.NewFileMetadata("/path/to/file.xyz", "xyz", 1024)
    metadata.Title = "Document Title"
    metadata.AddAuthor("Author Name")
    metadata.PageCount = 42

    // Create outline
    outline := sdk.NewDocumentOutline("/path/to/file.xyz", "xyz", 100)
    entry := sdk.NewOutlineEntry("Chapter 1", 0).WithPage(1)
    outline.AddEntry(entry)

    // Create pages
    pages := sdk.NewGroundChips("/path/to/file.xyz", "xyz")
    page := sdk.NewFileChipWithText(1, "Content...")
    pages.AddPage(page)

    // Serialize
    output, _ := json.MarshalIndent(metadata, "", "  ")
    fmt.Println(string(output))
}

Plugin registry

import sdk "github.com/fgnd/fgnd-plugin-sdk-go"

// Create registry
registry := sdk.NewPluginRegistry()

// Check if capability is available
caller, err := registry.Can("cap:op=extract;format=xyz;target=metadata")
if err != nil {
    log.Fatal(err)
}

// Call the capability
response, err := caller.Call(ctx, []string{"/path/to/file.xyz"}, nil, nil)
if err != nil {
    log.Fatal(err)
}

// Parse response
var metadata sdk.FileMetadata
response.AsType(&metadata)

Objective-C SDK

The Objective-C SDK provides Swift-compatible types for macOS plugins.

Installation

// Package.swift
dependencies: [
    .package(url: "https://github.com/fgnd/fgnd-plugin-sdk-objc", from: "1.0.0")
]

Swift usage

import FGNDPluginSDK

// Create metadata
let metadata = FGNDDocumentMetadata()
metadata.filePath = "/path/to/file.xyz"
metadata.documentType = "xyz"
metadata.fileSizeBytes = 1024
metadata.title = "Document Title"
metadata.authors.add("Author Name")

// Create outline
let outline = FGNDDocumentOutline()
outline.sourceFile = "/path/to/file.xyz"
outline.documentType = "xyz"
outline.totalPages = 100

let entry = FGNDOutlineEntry()
entry.title = "Chapter 1"
entry.level = 0
entry.page = 1
outline.outlineEntries.add(entry)

// Create pages
let pages = FGNDGroundChips()
pages.sourceFile = "/path/to/file.xyz"
pages.documentType = "xyz"

let page = FGNDFileChip()
page.orderIndex = 1
page.textContent = "Content..."
pages.pages.add(page)

Standard caps

import FGNDPluginSDK

// Get standard cap URN strings
let extractMetadata = FGNDStandardizedCaps.extractMetadata
let extractOutline = FGNDStandardizedCaps.extractOutline
let grind = FGNDStandardizedCaps.grind
let generateThumbnail = FGNDStandardizedCaps.generateThumbnail