Document Model
Subforge uses a single internal document model for all formats. This model is intentionally ASS-first and keeps richer data even when converting to simpler formats.
Core types
SubtitleDocument
ts
interface SubtitleDocument {
info: ScriptInfo
styles: Map<string, Style>
events: SubtitleEvent[]
comments: Comment[]
fonts?: EmbeddedData[]
graphics?: EmbeddedData[]
regions?: VTTRegion[]
}SubtitleEvent
ts
interface SubtitleEvent {
id: number
start: number
end: number
layer: number
style: string
actor: string
marginL: number
marginR: number
marginV: number
effect?: string
region?: string
text: string
segments: TextSegment[]
image?: ImageData
vobsub?: VobSubMeta
pgs?: PGSMeta
dirty: boolean
}ts
interface ImageData {
format: 'rle' | 'png' | 'raw' | 'indexed'
width: number
height: number
x?: number
y?: number
data: Uint8Array
palette?: number[]
}
interface VobSubMeta {
forced: boolean
originalIndex: number
}
interface PGSMeta {
compositionNumber: number
windowId: number
}Notes:
effectis the raw ASS/SSA Effect field when present.regionholds TTML region references.imageholds bitmap data for image-based formats.vobsubandpgshold format metadata for bitmap subtitles.- For bitmap formats,
segmentscan be empty even whenimageis present.
Colors
Colors use packed 32-bit integers in the format 0xAABBGGRR. Use core utilities:
ts
import { rgba, fromRGBA, Colors } from 'subforge/core'
const red = rgba(255, 0, 0, 0)
const { r, g, b, a } = fromRGBA(red)Text and segments
textholds the original line text (with tags if present).segmentshold parsed inline styles and effects.- If
dirtyisfalse, serializers can keep the originaltext. - If you edit
segments, setdirty = trueso serializers regeneratetext.
Helpers
ts
import {
createDocument,
createEvent,
createKaraokeEvent,
cloneDocument
} from 'subforge/core'
const doc = createDocument()
doc.events.push(createEvent(1000, 3000, 'Hello'))
const karaoke = createKaraokeEvent(0, 3000, [
{ text: 'Hel', duration: 500 },
{ text: 'lo', duration: 500 }
])