Documentation
¶
Overview ¶
Package opc reads an Open Packaging Conventions container: the ZIP, the content types and the relationships that docx, xlsx, pptx and xps are all built on.
It is the layer below a format reader. What it knows is how to find a part and what a part says it is; what the parts MEAN is WordprocessingML's business, or SpreadsheetML's, or PresentationML's.
Nothing here is found by its path ¶
⛔ The main part of a package is reached through a RELATIONSHIP, never by guessing a filename. Measured on files LibreOffice wrote, the one relationship type that names it points at three different places:
.docx → word/document.xml .xlsx → xl/workbook.xml .pptx → ppt/presentation.xml
A reader that hardcoded "word/document.xml" would work on most Word documents and on nothing else, and would still fail on the Word documents whose producer chose another name — which the standard allows.
Index ¶
- Constants
- func Build(parts []NewPart, rels map[string][]Rel) ([]byte, error)
- func SameRelType(a, b string) bool
- func TooLarge(err error) bool
- type Limits
- type NewPart
- type Package
- func (p *Package) ContentType(name string) string
- func (p *Package) Has(name string) bool
- func (p *Package) Main() (string, error)
- func (p *Package) Part(name string) ([]byte, error)
- func (p *Package) Parts() []string
- func (p *Package) RelOfType(part, relType string) (Rel, bool, error)
- func (p *Package) Rels(part string) ([]Rel, error)
- func (p *Package) Resolve(part string, r Rel) (string, error)
- type Rel
Constants ¶
const ( // RelOfficeDocument names the package's main part: the Word document, the // Excel workbook, the PowerPoint presentation. RelOfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" // RelCoreProperties names the Dublin Core metadata part. RelCoreProperties = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" )
The relationship types this package knows by name. A type is the whole URI, and it is compared with SameRelType rather than with ==.
Variables ¶
This section is empty.
Functions ¶
func Build ¶
Build writes an OPC container: the parts, the content types that say what each is, and the relationships that say how anything is found.
rels maps a part name to the relationships that start AT it. The empty name is the package's own, which goes in _rels/.rels and is where a reader starts — see Package.Main. The relationships parts are written for you, in the place Package.Rels looks for them, because that place is a rule and not a choice.
⛔ It refuses rather than writing a container that cannot be read back:
a part name with ".." in it which escapes the package
two parts with the same name where the second would be unreachable
[Content_Types].xml, or a .rels which this writes itself, and a second
copy would contradict the first
a relationship from a part that is not in the package, which is a
dangling edge nothing can resolve
no package relationships because a package nothing points into has
no main part, and every reader starts there
Each of those is a container that opens and then fails to say anything, and the failure turns up in whatever program the file was written for rather than here.
func SameRelType ¶
SameRelType says whether two relationship type URIs mean the same thing.
⛔ It is NOT a string comparison, and this is measured rather than cautious. In one .xlsx that LibreOffice wrote, the core-properties relationship type came out as
http://schemas.openxmlformats.org/officedocument/2006/relationships/...
with "officedocument" in lower case, while the .docx and .pptx from the same program, the same version and the same minute wrote "officeDocument". A producer is inconsistent about the case of its own URIs, so a reader that compares them exactly works on two files out of three.
Types ¶
type Limits ¶
type Limits struct {
// MaxPartBytes is how large one part may be, opened. Zero means 64 MiB.
MaxPartBytes int64
// MaxPackageBytes is how large the file itself may be. Zero means 256 MiB.
MaxPackageBytes int64
// MaxUncompressedBytes is how large every part may come to TOGETHER. Zero
// means 384 MiB.
//
// ⛔ One part under the ceiling says nothing about a thousand of them,
// which is the shape of bomb a per-part limit alone invites: a package of
// eight thousand parts each a byte under the limit.
MaxUncompressedBytes int64
// MaxParts is how many entries the ZIP may hold. Zero means 8192.
//
// It is checked before the parts are indexed, because that index is an
// allocation made from a count the file chose.
MaxParts int
// MaxRels is how many relationships one part may declare. Zero means 4096.
MaxRels int
}
Limits bound what a package may ask for before anything is allocated.
An OPC container is a ZIP, and a ZIP says how big it will be once opened. ⛔ What it says is a number in a FILE: it costs the author of that file nothing to claim four gigabytes, and a reader that believes it allocates them. Every ceiling here is checked against the DECLARED size first, so a refusal happens before a byte is inflated, and then the read itself is bounded again in case the declaration was a lie in the other direction.
The zero value asks for the defaults, which are generous for real documents and small for hostile ones. They are the same ceilings go-odf/odf arrived at after measuring what a bomb costs: a 67 206-byte spreadsheet whose content.xml inflates to 64 MiB parsed without complaint and held 222.9 MiB — a thousandfold on disk, and better than three times the inflated size in the heap, because the bytes are read, decoded as UTF-8, then kept as runs.
type NewPart ¶
type NewPart struct {
// Name is the part name, with or without a leading slash — "/" is how the
// content types part spells one and the ZIP does not, and a caller should
// not have to remember which this is.
Name string
// ContentType is what the content types part will say this is. It may be
// empty for a part whose extension already has a Default: every .rels and
// every .xml part does, which is why most packages override only a few.
ContentType string
// Data is the part's bytes.
Data []byte
}
A NewPart is one part of a package being built: where it goes, what it is, and what is in it.
type Package ¶
type Package struct {
// contains filtered or unexported fields
}
A Package is an OPC container opened for reading. It is safe for concurrent reads.
func (*Package) ContentType ¶
ContentType says what a part declares itself to be: its Override if it has one, otherwise the Default for its extension, otherwise "".
func (*Package) Main ¶
Main returns the name of the package's main part: the Word document, the Excel workbook, the PowerPoint presentation.
⛔ This is the whole reason this package exists. The part is found through the officeDocument relationship in _rels/.rels, and the three formats put it in three different places — word/document.xml, xl/workbook.xml, ppt/presentation.xml — so a path cannot be guessed even if a producer always chose the usual one, which it need not.
func (*Package) Part ¶
Part returns a part's bytes.
The lookup is exact first and case-insensitive second, because OPC compares part names without regard to case while a ZIP does not: a package whose [Content_Types].xml says "/word/Document.xml" and whose entry is "word/document.xml" is one file, and both names have to find it.
func (*Package) RelOfType ¶
RelOfType returns the first relationship of a part with the given type.
⛔ "First", and the order is the file's. A package may name two parts with the same relationship type — several slides, several worksheets — and for those the caller wants Package.Rels and all of them. This is for the types of which there is one: the main part, the core properties.
func (*Package) Rels ¶
Rels returns the relationships of a part. An empty name asks for the package's own, at _rels/.rels.
A part with no relationships part has no relationships, which is not an error: most parts of most packages have none.
func (*Package) Resolve ¶
Resolve turns a relationship's target into a part name of this package.
A target is relative to the DIRECTORY of the part that declared it — from word/document.xml, "styles.xml" is word/styles.xml and "../docProps/app.xml" is docProps/app.xml. A target beginning with a slash is from the root of the package instead.
⛔ An external target is an error rather than a path. "http://example.com/x" cleaned into a part name becomes "http:/example.com/x", which is a lookup that fails for a reason that has nothing to do with what went wrong.
type Rel ¶
type Rel struct {
ID string
Type string
Target string
// External is true when Target is a URI rather than a part of this
// package. ⛔ It must be checked before Target is used as a part name: an
// external target is a link to the web, and treating it as a path is how a
// reader ends up asking the package for "https:/example.com/x".
External bool
}
A Rel is one relationship: a named edge from a part (or from the package) to somewhere else.