ooxml

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 21, 2026 License: EPL-2.0 Imports: 20 Imported by: 0

Documentation

Overview

Package ooxml provides the preservation-first representation used by the renderer. It deliberately models XML rather than a subset of WordprocessingML.

Index

Constants

View Source
const (
	WordDrawingNamespace     = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
	DrawingNamespace         = "http://schemas.openxmlformats.org/drawingml/2006/main"
	PictureNamespace         = "http://schemas.openxmlformats.org/drawingml/2006/picture"
	Word2010Namespace        = "http://schemas.microsoft.com/office/word/2010/wordml"
	Word2010DrawingNamespace = "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
)
View Source
const (

	// The defaults are deliberately conservative for server-side template
	// processing. Callers which intentionally handle larger packages can use
	// OpenPackageWithLimits with application-specific values.
	DefaultMaxPackageEntries         = 4096
	DefaultMaxXMLPartBytes           = 64 << 20
	DefaultMaxMaterializedPartBytes  = 256 << 20
	DefaultMaxMaterializedTotalBytes = 512 << 20
	DefaultMaxPackageOutputBytes     = 512 << 20
	DefaultMaxXMLNodes               = 1_000_000
	DefaultMaxXMLAttributes          = 2_000_000
	DefaultMaxXMLTextBytes           = 128 << 20
	DefaultMaxXMLDepth               = 1024
)
View Source
const (
	XMLNamespace   = "http://www.w3.org/XML/1998/namespace"
	XMLNSNamespace = "http://www.w3.org/2000/xmlns/"
	WordNamespace  = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
	// StrictWordNamespace is the ISO/IEC 29500 Strict WordprocessingML
	// namespace. V2 treats Strict and Transitional Word nodes equivalently for
	// semantic matching while retaining their original namespace declarations.
	StrictWordNamespace = "http://purl.oclc.org/ooxml/wordprocessingml/main"
)
View Source
const (
	PackageRelationshipsNamespace = "http://schemas.openxmlformats.org/package/2006/relationships"
	HyperlinkRelationshipType     = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
)
View Source
const ContentTypesNamespace = "http://schemas.openxmlformats.org/package/2006/content-types"
View Source
const OfficeRelationshipsNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
View Source
const StrictOfficeRelationshipsNamespace = "http://purl.oclc.org/ooxml/officeDocument/relationships"

Variables

View Source
var ErrDigitalSignatureUnsupported = errors.New("OPC digital signatures are not supported")

ErrDigitalSignatureUnsupported reports an OPC package carrying digital signature metadata. go-stencil deliberately fails closed because rendering can invalidate package signatures and it does not perform cryptographic signature verification.

View Source
var ErrNotZIPPackage = errors.New("not a ZIP package")

ErrNotZIPPackage reports that input contains no ZIP container signature or end-of-central-directory record. Other errors from PreflightPackageWithLimits describe a ZIP-shaped package that is invalid or exceeds configured limits.

Functions

func AdaptOfficeRelationshipType

func AdaptOfficeRelationshipType(relationshipType, namespace string) string

AdaptOfficeRelationshipType aligns an office-document relationship type URI with a destination Strict/Transitional relationship namespace.

func AdaptOfficeRelationshipsNamespace

func AdaptOfficeRelationshipsNamespace(nodes []*Node, namespace string)

AdaptOfficeRelationshipsNamespace aligns detached relationship-bearing attributes with the concrete Strict/Transitional profile of a story.

func AdaptWordprocessingNamespace

func AdaptWordprocessingNamespace(nodes []*Node, namespace string)

AdaptWordprocessingNamespace makes detached nodes conform to the concrete Strict/Transitional profile of their destination story. Lexical prefixes are retained; explicit namespace declarations are updated with the nodes.

func CanonicalPartName

func CanonicalPartName(name string) string

CanonicalPartName returns the comparison key used by OPC package lookups. OPC part names are ASCII-case-insensitive; the fixed [Content_Types].xml package item remains case-sensitive because it is not an ordinary part.

func DeclareUsedNamespaces

func DeclareUsedNamespaces(nodes []*Node)

DeclareUsedNamespaces makes each top-level element self-contained after it is detached from its original XML document and inserted into another part.

func IsOfficeRelationshipsNamespace

func IsOfficeRelationshipsNamespace(namespace string) bool

IsOfficeRelationshipsNamespace reports whether namespace is either the Transitional or Strict office-document relationships namespace.

func IsSemanticNonTextParagraphContent

func IsSemanticNonTextParagraphContent(node *Node) bool

IsSemanticNonTextParagraphContent reports whether an element carries paragraph content that is not represented by a visible w:t node. Besides reference/range markers, this covers the complete semantic EG_RunInnerContent family. Property and wrapper elements deliberately do not qualify on their own; their descendants are inspected independently.

func IsWordprocessingMLNamespace

func IsWordprocessingMLNamespace(namespace string) bool

IsWordprocessingMLNamespace reports whether namespace is either the Transitional or Strict WordprocessingML namespace.

func Marshal

func Marshal(root *Node) ([]byte, error)

Marshal serializes the stored lexical names and attribute order. It does not pretty-print because whitespace is meaningful in Word text nodes.

func NormalizeRevisions

func NormalizeRevisions(root *Node) bool

NormalizeRevisions accepts tracked revisions in-place. Insertions and move-to ranges are promoted, deletions and move-from ranges are removed, and property change histories are discarded. The function reports whether it changed the tree.

func PreflightPackageWithLimits

func PreflightPackageWithLimits(data []byte, limits PackageLimits) error

PreflightPackageWithLimits validates the ZIP directory and cheap OPC metadata before callers enter archive/zip or decompress any part. It is intended for lazy package detection paths which must retain deferred XML parsing while still enforcing entry, output and declared XML-size limits.

func RejectDigitalSignaturesWithLimits

func RejectDigitalSignaturesWithLimits(data []byte, limits PackageLimits) error

RejectDigitalSignaturesWithLimits inspects only package names, content-type metadata, and relationship metadata. It does not validate a signature and never removes signature artifacts.

func SemanticEqual

func SemanticEqual(left, right *Node) bool

SemanticEqual compares parsed XML by expanded names and content. Namespace declaration spelling, prefixes and attribute order do not affect XML semantics and are therefore ignored.

func StoryRelationshipsNamespace

func StoryRelationshipsNamespace(root *Node) string

StoryRelationshipsNamespace returns the concrete office-document relationships namespace declared by a story, if present.

func ValidatePackage

func ValidatePackage(data []byte) error

ValidatePackage verifies OPC and WordprocessingML invariants that are not covered by XML well-formedness alone. It is intended for tests, diagnostics and explicit acceptance checks rather than the render hot path.

func ValidatePackageWithLimits

func ValidatePackageWithLimits(data []byte, limits PackageLimits) error

ValidatePackageWithLimits validates a package using the same resource limit model as OpenPackageWithLimits.

func WordprocessingNamespace

func WordprocessingNamespace(root *Node) string

WordprocessingNamespace returns the concrete WordprocessingML namespace used by root. This deliberately preserves whether the destination story is Strict or Transitional.

Types

type Attribute

type Attribute struct {
	Name                   Name
	Value                  string
	IsNamespaceDeclaration bool
}

Attribute is an ordered XML attribute. Namespace declarations are kept as normal attributes with IsNamespaceDeclaration set.

type DigitalSignatureError

type DigitalSignatureError struct {
	Part      string
	Indicator string
}

DigitalSignatureError identifies the package metadata which caused a fail-closed digital-signature rejection.

func (*DigitalSignatureError) Error

func (e *DigitalSignatureError) Error() string

func (*DigitalSignatureError) Unwrap

func (e *DigitalSignatureError) Unwrap() error

type Name

type Name struct {
	Prefix       string
	NamespaceURI string
	Local        string
}

Name stores both the lexical prefix and the resolved namespace name. Prefix is retained for round-tripping; NamespaceURI is used for matching.

func (Name) String

func (n Name) String() string

String returns the lexical qualified name.

type Node

type Node struct {
	Kind       NodeKind
	ID         uint64
	Name       Name
	Attributes []Attribute
	Children   []*Node
	Data       string
	Target     string
}

Node is a lossless, ordered XML node for the information relevant to OOXML. IDs are stable across DeepClone so compiled render plans never depend on Go pointers.

func CloneDetachedStructuralRange

func CloneDetachedStructuralRange(nodes []*Node) ([]*Node, error)

CloneDetachedStructuralRange validates and deep-clones complete sibling nodes without assigning destination-local identifiers. The returned nodes are detached and owned by the caller. AdoptStructuralRange must be called exactly once after any render-time transformations and before insertion into a destination part.

func Parse

func Parse(data []byte) (*Node, error)

Parse reads XML through RawToken so lexical prefixes and ordered attributes are retained. Names are additionally resolved against a namespace stack.

func ParseFragment

func ParseFragment(fragment string) ([]*Node, error)

ParseFragment parses a well-formed OOXML fragment using the standard Word namespaces. Prefixes outside this set must be declared by the fragment.

func (*Node) Attribute

func (n *Node) Attribute(namespace, local string) (string, bool)

Attribute returns the first attribute with the requested expanded name.

func (*Node) DeepClone

func (n *Node) DeepClone() *Node

DeepClone creates an independent tree while retaining stable node IDs.

func (*Node) DeepCloneWithIndex

func (n *Node) DeepCloneWithIndex() (*Node, *NodeIndex)

DeepCloneWithIndex creates an independent tree and builds a render-local node/parent index in the same traversal. The index points only into the cloned tree.

func (*Node) FindID

func (n *Node) FindID(id uint64) *Node

FindID returns the node with id, if present.

func (*Node) Is

func (n *Node) Is(namespace, local string) bool

Is reports whether n is an element with the expanded name namespace/local.

func (*Node) Walk

func (n *Node) Walk(visit func(*Node) bool)

Walk visits n and all descendants in document order. Returning false skips the descendants of the current node.

type NodeIndex

type NodeIndex struct {
	// contains filtered or unexported fields
}

NodeIndex provides constant-time stable-ID and parent lookups for one tree. It is intentionally render-local: callers must rebuild it after structural mutations that add or remove nodes.

func NewNodeIndex

func NewNodeIndex(root *Node) *NodeIndex

NewNodeIndex indexes root and all descendants in document order.

func (*NodeIndex) FindID

func (i *NodeIndex) FindID(id uint64) *Node

FindID returns the indexed node with id, if present.

func (*NodeIndex) MaxID

func (i *NodeIndex) MaxID() uint64

MaxID returns the largest stable node ID in the indexed tree.

func (*NodeIndex) Parent

func (i *NodeIndex) Parent(id uint64) *Node

Parent returns the indexed node's parent. The tree root and unknown IDs have no parent.

type NodeKind

type NodeKind uint8

NodeKind identifies an XML node type.

const (
	DocumentNode NodeKind = iota
	ElementNode
	TextNode
	CommentNode
	ProcInstNode
	DirectiveNode
)

func (NodeKind) String

func (k NodeKind) String() string

type NodeLocation

type NodeLocation struct {
	NodeID uint64
	Path   []XMLPathStep
}

NodeLocation locates a node in the immutable story tree.

type Package

type Package struct {
	Parts []*Part
	// contains filtered or unexported fields
}

Package is an ordered OPC package. It does not expose a map as the primary representation because ZIP entry order is preservation-sensitive.

func OpenPackage

func OpenPackage(data []byte) (*Package, error)

OpenPackage opens a DOCX using conservative server-safe limits.

func OpenPackageWithLimits

func OpenPackageWithLimits(data []byte, limits PackageLimits) (*Package, error)

OpenPackageWithLimits parses supported story parts eagerly. Other entries retain their exact compressed stream and are decompressed only when their content is requested. The package keeps references into data; callers must not modify it afterwards.

func (*Package) AddBinaryPart

func (p *Package) AddBinaryPart(name string, content []byte) (*Part, error)

AddBinaryPart appends generated non-XML content to the package.

func (*Package) AddXMLPart

func (p *Package) AddXMLPart(name string, tree *Node) (*Part, error)

AddXMLPart appends a newly generated XML part. OPC relationship parts do not need a content-type override when the package has the standard rels default.

func (*Package) CloneForRender

func (p *Package) CloneForRender(mutableParts map[string]bool) (*Package, map[string]*NodeIndex)

CloneForRender copies package/part metadata but deep-clones only the named mutable trees. Trees not listed in mutableParts remain shared and must be treated as immutable by the caller. The returned indexes point into cloned mutable trees only.

func (*Package) DeepClone

func (p *Package) DeepClone() *Package

DeepClone creates a render-local package. Immutable original and compressed bytes are shared; mutable trees and headers are copied.

func (*Package) DeepCloneWithIndexes

func (p *Package) DeepCloneWithIndexes() (*Package, map[string]*NodeIndex)

DeepCloneWithIndexes creates a render-local package and an ID/parent index for every cloned XML tree. Clean non-tree parts are shared as immutable data.

func (*Package) EffectiveContentType

func (p *Package) EffectiveContentType(partName string) (string, error)

EffectiveContentType returns the content type declared for partName, preferring an exact Override over the extension Default.

func (*Package) EnsureContentType

func (p *Package) EnsureContentType(partName, contentType string) error

EnsureContentType preserves an explicit content type while retaining the compact conventional Default entry when the extension has that exact type.

func (*Package) EnsureContentTypeDefault

func (p *Package) EnsureContentTypeDefault(partName string) error

EnsureContentTypeDefault adds a Default entry for a generated part's extension when it is not already declared.

func (*Package) EnsureContentTypeOverride

func (p *Package) EnsureContentTypeOverride(partName, contentType string) error

EnsureContentTypeOverride adds an Override entry for a generated OOXML part unless the part is already declared.

func (*Package) Marshal

func (p *Package) Marshal() ([]byte, error)

Marshal writes the entries in their original order. Clean entries reuse the original compressed stream through CreateRaw. Dirty entries are compressed up front so their local headers contain CRC and sizes; Office rejects stored entries that rely on streaming ZIP data descriptors.

func (*Package) Part

func (p *Package) Part(name string) (*Part, bool)

Part returns a package entry by its ASCII-case-insensitive OPC name. The fixed [Content_Types].xml package item remains case-sensitive.

func (*Package) Relationships

func (p *Package) Relationships(sourcePart string) (*RelationshipSet, error)

Relationships opens or creates the part-local relationship set.

func (*Package) ReplacePart

func (p *Package) ReplacePart(name string, content []byte) (*Part, error)

ReplacePart stores generated bytes for an existing part or appends a new part. It is intended for non-story resource XML and binary package parts.

func (*Package) StoryPartNames

func (p *Package) StoryPartNames() []string

StoryPartNames returns the main document and every header/footer story in a stable order. Relationship-discovered names are included even when they do not use Word's conventional headerN.xml/footerN.xml naming scheme.

type PackageLimits

type PackageLimits struct {
	MaxEntries                int
	MaxXMLPartBytes           uint64
	MaxMaterializedPartBytes  uint64
	MaxMaterializedTotalBytes uint64
	MaxOutputBytes            uint64
	MaxXMLNodes               uint64
	MaxXMLAttributes          uint64
	MaxXMLTextBytes           uint64
	MaxXMLDepth               uint64
}

PackageLimits bounds work which requires decompression. Clean entries that are only copied through do not count towards the materialized-byte limits.

type PackageValidationError

type PackageValidationError struct {
	Issues []ValidationIssue
	Cause  error
}

PackageValidationError contains every detected integrity violation.

func (*PackageValidationError) Error

func (e *PackageValidationError) Error() string

func (*PackageValidationError) Unwrap

func (e *PackageValidationError) Unwrap() error

type Paragraph

type Paragraph struct {
	NodeID uint64
	Path   []XMLPathStep
	Text   []TextReference
}

Paragraph is one independently compilable text scope. Nested paragraphs, for example inside a textbox, are indexed separately.

func IndexParagraphCompact

func IndexParagraphCompact(paragraphNode *Node) (Paragraph, error)

IndexParagraphCompact indexes one WordprocessingML paragraph without traversing or wrapping its containing story. Nested paragraphs, such as those in textboxes, remain independent text scopes and are not included.

func (Paragraph) CompileTemplateSpans

func (p Paragraph) CompileTemplateSpans() []TemplateSpan

CompileTemplateSpans finds template ranges across any number of visible w:t nodes. Positions are retained in node-local rune offsets for precise edits.

func (Paragraph) String

func (p Paragraph) String() string

String concatenates the visible w:t values in document order.

type Part

type Part struct {
	Name        string
	Header      zip.FileHeader
	Original    []byte
	Tree        *Node
	Dirty       bool
	Replacement []byte
	// contains filtered or unexported fields
}

Part is one ordered OPC ZIP entry. Original is populated for eagerly parsed story parts; other clean content is available through ReadOnlyBytes or Bytes. Original, lazy backing and rawCompressed are immutable; Tree is populated only for supported story parts.

func (*Part) Bytes

func (p *Part) Bytes() ([]byte, error)

Bytes returns the current uncompressed part content.

func (*Part) IsStory

func (p *Part) IsStory() bool

IsStory reports whether this part participates in the first V2 story pass.

func (*Part) MarkDirty

func (p *Part) MarkDirty()

MarkDirty records that Tree rather than Original must be serialized.

func (*Part) ReadOnlyBytes

func (p *Part) ReadOnlyBytes() ([]byte, error)

ReadOnlyBytes returns the current uncompressed part content without copying immutable binary storage. Callers must not modify the returned slice.

type RelationshipSet

type RelationshipSet struct {
	SourcePart string
	Part       *Part
	// contains filtered or unexported fields
}

RelationshipSet mutates only the .rels part belonging to SourcePart.

func (*RelationshipSet) Add

func (r *RelationshipSet) Add(relationshipType, target, targetMode string) string

Add appends a relationship with a collision-free part-local rId.

func (r *RelationshipSet) AddHyperlink(target string) string

AddHyperlink adds an external hyperlink relationship.

func (*RelationshipSet) Ensure

func (r *RelationshipSet) Ensure(relationshipType, target, targetMode string) string

Ensure returns an existing matching relationship ID or appends one.

type Story

type Story struct {
	PartName   string
	RootID     uint64
	Paragraphs []Paragraph
	Locations  map[uint64]NodeLocation
	// contains filtered or unexported fields
}

Story indexes visible WordprocessingML text while retaining stable Node IDs.

func IndexStory

func IndexStory(partName string, document *Node) (*Story, error)

IndexStory builds the common document/header/footer story index. It traverses unknown wrappers, hyperlinks, content controls and textbox content naturally.

func IndexStoryCompact

func IndexStoryCompact(partName string, document *Node) (*Story, error)

IndexStoryCompact indexes the text scopes needed by the renderer without materializing diagnostic XML paths or a location map for every node. Use IndexStory when callers need source locations for validation or diagnostics.

func (*Story) HasSemanticNonTextContent

func (s *Story) HasSemanticNonTextContent(paragraphID uint64) bool

HasSemanticNonTextContent reports whether the paragraph carries fields, references, ranges, drawings, or other semantic content not represented by visible w:t nodes. The sparse set is populated only during story indexing.

type TemplateSpan

type TemplateSpan struct {
	Raw         string
	Start       TextPosition
	End         TextPosition
	GlobalStart int
	GlobalEnd   int
	StartPath   []XMLPathStep
	RunIndex    int
	Malformed   bool
}

TemplateSpan is a raw {{...}} range compiled from a paragraph's visible text. End is exclusive. Malformed spans extend to the end of the paragraph.

type TextPosition

type TextPosition struct {
	TextNodeID uint64
	RuneOffset int
}

TextPosition addresses a rune boundary within one visible text node.

type TextReference

type TextReference struct {
	TextElementID uint64
	TextNodeID    uint64
	RunIndex      int
	Text          string
	Path          []XMLPathStep
}

TextReference identifies visible content from one w:t element.

type UniqueIDAllocator

type UniqueIDAllocator struct {
	// contains filtered or unexported fields
}

UniqueIDAllocator owns the part-local identifiers assigned to structural clones. It is render-local and therefore requires no synchronization.

func NewUniqueIDAllocator

func NewUniqueIDAllocator(root *Node) *UniqueIDAllocator

NewUniqueIDAllocator scans the complete part before any clones are added.

func (*UniqueIDAllocator) AdoptOneToOneStructuralRange

func (a *UniqueIDAllocator) AdoptOneToOneStructuralRange(nodes []*Node) error

AdoptOneToOneStructuralRange assigns destination-local identifiers to a structural range produced by splitting an existing range in place. Unlike AdoptStructuralRange, it accepts comment, permission, and custom XML range markers because the caller is transferring every split segment exactly once rather than duplicating arbitrary content. Range identifiers are therefore intentionally preserved. Only internal node IDs and duplicated paragraph or drawing hex IDs are made destination-local; paired bookmark IDs/names and field references must remain unchanged when their other endpoint lives outside the paragraph being split.

func (*UniqueIDAllocator) AdoptStructuralRange

func (a *UniqueIDAllocator) AdoptStructuralRange(nodes []*Node) error

AdoptStructuralRange assigns destination-local IDs to detached nodes and remaps identifiers in place. The caller transfers ownership of nodes; they must not be shared with a prepared tree or another render. Compared with CloneStructuralRange this avoids a redundant deep clone for freshly parsed HTML/XML and already render-local DOCX fragment nodes.

func (*UniqueIDAllocator) CloneStructuralRange

func (a *UniqueIDAllocator) CloneStructuralRange(nodes []*Node) ([]*Node, error)

CloneStructuralRange clones complete sibling nodes, assigns fresh internal IDs and remaps OOXML identifiers that must be unique within a part.

func (*UniqueIDAllocator) CloneStructuralRangeWithIndex

func (a *UniqueIDAllocator) CloneStructuralRangeWithIndex(nodes []*Node) ([]*Node, *NodeIndex, error)

CloneStructuralRangeWithIndex clones a sibling range and returns an index that resolves both the source IDs used by compiled render plans and the new IDs assigned to the clones. This lets loop bodies reuse their prepared plans without reparsing the cloned XML on every iteration.

func (*UniqueIDAllocator) ReserveNodeIDFloor

func (a *UniqueIDAllocator) ReserveNodeIDFloor(floor uint64)

ReserveNodeIDFloor prevents subsequently cloned internal nodes from using IDs at or below floor. This is useful when a prepared-tree index is available but the live render tree may have already removed its highest-ID nodes before the allocator is first needed. It deliberately affects only internal Node IDs; OOXML-visible identifiers are still derived from the live tree scan.

type ValidationIssue

type ValidationIssue struct {
	Part    string
	Message string
}

ValidationIssue describes one package-integrity violation that can make Word reject or repair a generated document.

type XMLPathStep

type XMLPathStep struct {
	NamespaceURI string
	LocalName    string
	SiblingIndex int
}

XMLPathStep is a stable, namespace-aware structural location.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL