Documentation
¶
Overview ¶
Package pdf converts bracket XLSX workbooks into grouped, print-ready PDFs.
The pipeline mirrors square_prep/lc2026/xlsx_to_pdf.py: each XLSX is rendered to a full PDF by LibreOffice (soffice) headless, the per-sheet bookmarks LibreOffice emits are read to map sheet names to page ranges, and the wanted sheets are extracted and merged into grouped output PDFs.
LibreOffice (soffice) is a runtime dependency. It is NOT bundled. Callers detect availability with LocateSoffice and surface an actionable error when it is absent rather than failing deep in the pipeline.
Index ¶
- Variables
- func ExtractPages(srcPath string, ranges []SheetRange, outPath string) error
- func LocateSoffice() (string, error)
- func MergePDFs(inPaths []string, outPath string) error
- func PageCount(pdfPath string) (int, error)
- func StampPageNumbers(inPath, outPath string) error
- type Converter
- type Generator
- type Group
- type SheetRange
- type SourceWorkbook
Constants ¶
This section is empty.
Variables ¶
var ErrSofficeNotFound = errors.New("LibreOffice (soffice) not found")
ErrSofficeNotFound is returned when no LibreOffice binary can be located. Callers map this to an actionable user-facing message (HTTP 503 in the web app) telling the operator to install LibreOffice or pull the -pdf image.
var Groups = []Group{ { Type: "registration", Output: "print_registration.pdf", Description: "Registration (data sheets)", Sheets: []sheetSpec{exact(helper.SheetData)}, }, { Type: "names", Output: "print_names_to_print.pdf", Description: "Names to Print", Sheets: []sheetSpec{prefix(helper.SheetNamesToPrint)}, InsertTitle: true, A3Landscape: true, }, { Type: "tags", Output: "print_tags.pdf", Description: "Tags", Sheets: []sheetSpec{prefix(helper.SheetTags)}, InsertTitle: true, SkipTeamWorkbooks: true, }, { Type: "pools-trees", Output: "print_pools_and_trees.pdf", Description: "Pool Draw + Trees (participant booklet)", Sheets: []sheetSpec{exact(helper.SheetPoolDraw), prefix(helper.SheetTree)}, PageNumbers: true, }, { Type: "full-bracket", Output: "print_full_bracket.pdf", Description: "Full bracket (pools, matches, trees)", Sheets: []sheetSpec{ exact(helper.SheetPoolDraw), exact(helper.SheetPoolMatches), exact(helper.SheetEliminationMatches), prefix(helper.SheetTree), }, PageNumbers: true, }, }
Groups is the canonical set of output PDFs, mirroring the PDF_GROUPS config in square_prep/lc2026/xlsx_to_pdf.py. Sheet selectors use the Sheet* constants from internal/helper/constants.go (compile-time drift safety). Names/Tags/Tree match by prefix because the generator splits them into suffixed physical sheets (e.g. "Names to Print A", "Tree 1").
Functions ¶
func ExtractPages ¶
func ExtractPages(srcPath string, ranges []SheetRange, outPath string) error
ExtractPages writes a new PDF at outPath containing only the given inclusive 1-indexed page ranges from srcPath, in the order supplied.
func LocateSoffice ¶
LocateSoffice resolves the LibreOffice binary using, in order:
- $LIBREOFFICE_PATH (if set and executable)
- "soffice" on $PATH
- a platform-specific list of well-known install locations
It returns ErrSofficeNotFound (wrapped) when none resolve.
func StampPageNumbers ¶
StampPageNumbers writes a copy of inPath to outPath with a "N / M" footer stamped on every page. It returns an error if the PDF has no pages.
Types ¶
type Converter ¶
type Converter struct {
// contains filtered or unexported fields
}
Converter runs soffice headless conversions. The zero value is not usable; construct with NewConverter.
func NewConverter ¶
NewConverter locates LibreOffice and returns a Converter. It returns ErrSofficeNotFound (wrapped) when no binary is available, so callers can branch on errors.Is(err, ErrSofficeNotFound).
func (*Converter) ConvertToPDF ¶
ConvertToPDF renders srcPath (an XLSX or HTML file) to a PDF in outDir using soffice headless, and returns the resulting PDF path. Calls are serialized process-wide and bounded by convertTimeout. A dedicated, isolated user profile is passed via -env:UserInstallation so a concurrent desktop LibreOffice (or another server) does not collide on the shared profile.
func (*Converter) SofficePath ¶
SofficePath returns the resolved soffice binary path.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator drives the XLSX→PDF pipeline. Construct with NewGenerator.
func NewGenerator ¶
NewGenerator locates LibreOffice and returns a Generator, or ErrSofficeNotFound (wrapped) when soffice is unavailable.
func (*Generator) GenerateAll ¶
func (g *Generator) GenerateAll(ctx context.Context, sources []SourceWorkbook, outDir string) (map[string]string, error)
GenerateAll produces every group's PDF from the given workbooks into outDir, returning a map of group type → output path. Each workbook is converted to a full PDF exactly once and reused across groups. A group that yields no pages (e.g. tags when only team workbooks are present) is skipped and omitted from the result map. Output files are written atomically (temp file → rename).
type Group ¶
type Group struct {
// Type is the CLI/web selector (e.g. "registration", "names").
Type string
// Output is the default output filename.
Output string
// Description is a human-readable label for progress output.
Description string
// Sheets are the sheet selectors, applied in order, for each workbook.
Sheets []sheetSpec
// InsertTitle prepends a per-tournament title page before each file's pages.
InsertTitle bool
// A3Landscape renders the title page in A3 landscape (for Names to Print).
A3Landscape bool
// SkipTeamWorkbooks excludes team-registration workbooks (no individual tags).
SkipTeamWorkbooks bool
// PageNumbers stamps "N / M" footers on the final merged PDF.
PageNumbers bool
}
Group describes one output PDF: which sheets to pull from each source workbook, whether to prepend a per-tournament title page, which source files to skip, and whether to stamp page numbers on the merged result.
func GroupByType ¶
GroupByType returns the group with the given Type selector, or false.
type SheetRange ¶
type SheetRange struct {
Sheet string
PageFrom int // 1-indexed, inclusive
PageThru int // 1-indexed, inclusive
}
SheetRange is a sheet's inclusive 1-indexed page span within a full PDF.
func SheetRanges ¶
func SheetRanges(pdfPath string) ([]SheetRange, error)
SheetRanges reads the per-sheet bookmarks LibreOffice embeds in a converted PDF and returns one SheetRange per sheet, in document order.
pdfcpu leaves PageThru==0 on the final bookmark because it has no successor to bound it; we patch any bookmark whose PageThru < PageFrom to end at the last page of the document. Without this the final sheet's pages are dropped.
type SourceWorkbook ¶
type SourceWorkbook struct {
// Path is the absolute path to the .xlsx file.
Path string
// Title is the human-readable tournament name used on title pages. When
// empty, the filename stem is used.
Title string
// IsTeam marks a team-registration workbook; excluded from the Tags group.
IsTeam bool
}
SourceWorkbook is one input XLSX with the metadata the pipeline needs.