From 4b3770fc5cdd8293db2059f35da221fdd09dc1a4 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 6 Oct 2026 16:25:29 +0200 Subject: [PATCH] One docs, not two: the site is served at /docs/, so the section cannot be too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every page came out at /docs/docs/… — the content sat in a `docs` section and the site is published at /docs/, so the segment appeared twice: before /docs/docs/working-with-a-file/reading/ after /docs/working-with-a-file/reading/ ⚠ The links were NOT broken, and I nearly said they were. Hugo prefixes the base path onto an absolute link in Markdown, so `](/docs/api/)` was emitted as `href="/docs/docs/api/"` — self-consistent with the doubled shape. A probe of /docs/working-with-a-file/reading/ returned 404 and I read that as "the links are broken"; it only meant that was never a URL the site used. What settled it was a link checker over the built tree: 848 internal links in the shipped build, 0 broken. The tool is `linkcheck`, and it stopped a wrong claim rather than finding a defect — which is the useful half of a control reproducing a known positive and refusing to. So this is a URL-shape change, not a repair. The content moves up one level; Hextra takes its documentation layout from a section called `docs`, so with the content at the root the type is cascaded instead. The separate hero home goes: this site IS the documentation, and the organisation's landing page is a different repository. 796 internal links, 0 broken, 15 pages. --- content/_index.md | 130 +++++++++++++++--- content/{docs => }/api.md | 0 content/docs/_index.md | 120 ---------------- .../{docs => }/working-with-a-file/_index.md | 0 .../working-with-a-file/coediting.md | 0 .../working-with-a-file/extraction.md | 0 .../working-with-a-file/operations.md | 0 .../{docs => }/working-with-a-file/reading.md | 0 .../working-with-a-file/rendering.md | 0 .../working-with-a-file/workbench.md | 0 .../{docs => }/writing-a-document/_index.md | 0 .../writing-a-document/getting-started.md | 0 .../{docs => }/writing-a-document/graphics.md | 0 .../{docs => }/writing-a-document/images.md | 0 .../writing-a-document/text-and-fonts.md | 0 .../{docs => }/writing-a-document/widgets.md | 0 hugo.toml | 18 ++- 17 files changed, 126 insertions(+), 142 deletions(-) rename content/{docs => }/api.md (100%) delete mode 100644 content/docs/_index.md rename content/{docs => }/working-with-a-file/_index.md (100%) rename content/{docs => }/working-with-a-file/coediting.md (100%) rename content/{docs => }/working-with-a-file/extraction.md (100%) rename content/{docs => }/working-with-a-file/operations.md (100%) rename content/{docs => }/working-with-a-file/reading.md (100%) rename content/{docs => }/working-with-a-file/rendering.md (100%) rename content/{docs => }/working-with-a-file/workbench.md (100%) rename content/{docs => }/writing-a-document/_index.md (100%) rename content/{docs => }/writing-a-document/getting-started.md (100%) rename content/{docs => }/writing-a-document/graphics.md (100%) rename content/{docs => }/writing-a-document/images.md (100%) rename content/{docs => }/writing-a-document/text-and-fonts.md (100%) rename content/{docs => }/writing-a-document/widgets.md (100%) diff --git a/content/_index.md b/content/_index.md index c28c94f..5beff7c 100644 --- a/content/_index.md +++ b/content/_index.md @@ -1,24 +1,120 @@ --- -title: go-pdfkit -layout: hextra-home +title: "Overview" +weight: 10 --- -{{< hextra/hero-badge link="https://github.com/go-pdfkit" >}} - Pure Go · CGO=0 · BSD-3-Clause -{{< /hextra/hero-badge >}} +# go-pdfkit documentation -{{< hextra/hero-headline >}} - The whole of PDF in Go, 
with no C anywhere -{{< /hextra/hero-headline >}} +**The whole of PDF in Go, with `CGO_ENABLED=0` and no C anywhere**: read a +file, write one, rearrange it, draw it, read it back as words and pictures, +and edit it with other people. Every library here builds for `GOOS=js/wasm`, +so all of it runs in a browser tab. -{{< hextra/hero-subtitle >}} - Read a file, write one, rearrange it, draw it, read it back as words and 
pictures, and edit it with other people — in a browser tab if you like. -{{< /hextra/hero-subtitle >}} +Fonts are parsed and shaped with +[go-opentype](https://github.com/go-opentype/opentype) and pages are +rasterised by [go-gfx](https://github.com/go-gfx/gfx); nothing outside the Go +standard library and our own pure-Go libraries is required. -{{< hextra/hero-button text="Read the documentation" link="docs" >}} +{{< callout type="info" >}} +**Measured against 118 863 real files** -{{< hextra/feature-grid >}} - {{< hextra/feature-card title="Measured, not asserted" subtitle="Everything here is checked against 118 863 real PDFs — arXiv's figures, Matplotlib, Mathematica, pdfTeX, Ghostscript and Adobe — rather than against files written to pass a test." >}} - {{< hextra/feature-card title="Runs on eight architectures" subtitle="amd64, arm64, riscv64, loong64, ppc64le, s390x, 386 and arm — the whole suite runs on each, big-endian and 32-bit included, and on FreeBSD and OpenBSD too." >}} - {{< hextra/feature-card title="In a browser tab" subtitle="Every library builds for js/wasm. The workbench opens a PDF, rearranges it and saves it back without any of it leaving the machine." >}} -{{< /hextra/feature-grid >}} +Every claim on this site is measured against a corpus of real PDFs — +arXiv's figures, from Matplotlib and Mathematica and pdfTeX and Ghostscript +and Adobe — rather than against files written to pass a test. Each wave of +work is checked by **hashing the pixels of every page before and after** and +putting the biggest changes beside what the operating system's own renderer +draws. That is what found a stroke that came out at half its colour, a font +that took the dots off every *i*, and a colour transform that turned every +plot's paper yellow. +{{< /callout >}} +{{< callout type="info" >}} +**Zero C dependencies** + +No cgo, no bundled `libpoppler`/`libharfbuzz`/`libfreetype`. Glyph +subsetting, font-descriptor metrics and per-glyph advances all come from +[go-opentype](https://github.com/go-opentype/opentype)'s +`Font.SubsetTrueType` / `Font.SubsetCFF` — `pdfkit` keeps no private sfnt +re-parse or subsetter of its own. See +[Text & fonts](/docs/writing-a-document/text-and-fonts/#font-embedding-architecture). +{{< /callout >}} +## The family + +| Repo | What it is | +| --- | --- | +| [`reader`](https://github.com/go-pdfkit/reader) | reads and writes the format itself: objects, cross-reference tables and streams, filters, encryption, the page tree, content streams — and a writer that produces the same bytes twice | +| [`ops`](https://github.com/go-pdfkit/ops) | the verbs, and the `pdfops` command: merge, split, rotate, crop, n-up, watermark, encrypt, and reading a page back | +| [`render`](https://github.com/go-pdfkit/render) | turns a page into pixels: paths, images, text in every font flavour, functions, shadings and patterns | +| [`pdffont`](https://github.com/go-pdfkit/pdffont) | what a document says about a font — encodings, widths, and what text a code stands for | +| [`extract`](https://github.com/go-pdfkit/extract) | reads a page back: the text with where it sits, and the pictures with the box each covers | +| [`coedit`](https://github.com/go-pdfkit/coedit) | a PDF several people edit at once — the plan is shared, not the file | +| [`app`](https://github.com/go-pdfkit/app) | a PDF workbench that runs in a browser tab and nowhere else | +| [`pdfkit`](https://github.com/go-pdfkit/pdfkit) | the document builder: pages, vector graphics, and text in embedded subsetted fonts | +| [`docs`](https://github.com/go-pdfkit/docs) | this documentation site (MkDocs Material, versioned with mike) | + +Two libraries outside the org do the work under all of it: +[go-opentype/opentype](https://github.com/go-opentype/opentype), the sfnt, +CFF and Type 1 parser and shaper, and [go-gfx/gfx](https://github.com/go-gfx/gfx), +the rasteriser. [go-ruby-prawn](https://github.com/go-ruby-prawn) is the +Ruby-idiomatic writer built on the same font stack. + +## What it is + +- A **PDF 1.7 writer**: catalog, page tree, cross-reference table and + trailer, written to any `io.Writer`, with optional FlateDecode stream + compression (`Options.Compress`). +- **Vector graphics**: paths (move/line/cubic/rect/close), fill and stroke + (nonzero and even-odd), DeviceGray/RGB/CMYK colour, line width/cap/join/ + miter/dash, CTM transforms (translate/scale/rotate/skew), clipping, `q`/`Q` + graphics-state save/restore, and constant-alpha transparency via + `ExtGState`. +- **Text as embedded, subsetted fonts**: every font used is written as a + **Type0 (Identity-H)** composite font with a per-glyph `/W` width array and + a `/ToUnicode` CMap for copy/paste. TrueType `glyf` outlines embed as a + subsetted `FontFile2` / `CIDFontType2` with a `/CIDToGIDMap` stream; CFF/ + OpenType outlines embed as a **charstring-subsetted** `FontFile3` / + `CIDFontType0`. +- **Images**: JPEG embedded directly (`DCTDecode`, no re-encoding); PNG and + any `image.Image` rasterised as XObjects (`FlateDecode`) with an `/SMask` + for alpha. +- **Widget bridge**: `Page.AddWidget` and `Page.AddWidgetVector` "print" a + [go-widgets/toolkit](https://github.com/go-widgets/toolkit) widget tree onto + a page — as a rasterised image, or as PDF vector operators with selectable + text. See [Widgets](/docs/writing-a-document/widgets/). +- **Deterministic output**: with the zero `Options`, a document has no + timestamps and a content-derived `/ID` (a SHA-256 of the body), so + identical inputs produce byte-identical PDFs. + +## What it is not (yet) + +- A **CID-keyed CFF** or a **CFF2 (variable)** font cannot be + charstring-subsetted by the preserve-numbering path, so it falls back to + embedding the whole `CFF`/`CFF2` table. A plain (non-CID-keyed) CFF/OpenType + font **is** charstring-subsetted, same as TrueType `glyf`. +- Encryption, tagged PDF / PDF-A, interactive forms and annotations are not + yet implemented. + +See [Scope and limitations](/docs/writing-a-document/text-and-fonts/#scope-and-limitations) for the +full picture, and the upstream +[README](https://github.com/go-pdfkit/pdfkit#scope-and-limitations) for the +canonical statement. + +## Install + +```sh +go get github.com/go-pdfkit/pdfkit +``` + +## Where to go next + +- [Getting started](/docs/writing-a-document/getting-started/) — the five calls that make a PDF: + `New` → `AddPage` → `SetFont` → `Text` → `Write`. +- [Graphics](/docs/writing-a-document/graphics/) — paths, colour, line state, transforms, clipping + and transparency. +- [Text & fonts](/docs/writing-a-document/text-and-fonts/) — loading fonts, drawing text, measuring + and wrapping, and the shaped-text API for complex scripts. +- [Images](/docs/writing-a-document/images/) — `DrawImage`, `DrawPNG` and `DrawJPEG`. +- [Widgets](/docs/writing-a-document/widgets/) — printing a go-widgets/toolkit widget tree to a page. +- [API reference](/docs/api/) — the full public surface with Go examples. + +Source lives at +[github.com/go-pdfkit/pdfkit](https://github.com/go-pdfkit/pdfkit). diff --git a/content/docs/api.md b/content/api.md similarity index 100% rename from content/docs/api.md rename to content/api.md diff --git a/content/docs/_index.md b/content/docs/_index.md deleted file mode 100644 index 5beff7c..0000000 --- a/content/docs/_index.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: "Overview" -weight: 10 ---- - -# go-pdfkit documentation - -**The whole of PDF in Go, with `CGO_ENABLED=0` and no C anywhere**: read a -file, write one, rearrange it, draw it, read it back as words and pictures, -and edit it with other people. Every library here builds for `GOOS=js/wasm`, -so all of it runs in a browser tab. - -Fonts are parsed and shaped with -[go-opentype](https://github.com/go-opentype/opentype) and pages are -rasterised by [go-gfx](https://github.com/go-gfx/gfx); nothing outside the Go -standard library and our own pure-Go libraries is required. - -{{< callout type="info" >}} -**Measured against 118 863 real files** - -Every claim on this site is measured against a corpus of real PDFs — -arXiv's figures, from Matplotlib and Mathematica and pdfTeX and Ghostscript -and Adobe — rather than against files written to pass a test. Each wave of -work is checked by **hashing the pixels of every page before and after** and -putting the biggest changes beside what the operating system's own renderer -draws. That is what found a stroke that came out at half its colour, a font -that took the dots off every *i*, and a colour transform that turned every -plot's paper yellow. -{{< /callout >}} -{{< callout type="info" >}} -**Zero C dependencies** - -No cgo, no bundled `libpoppler`/`libharfbuzz`/`libfreetype`. Glyph -subsetting, font-descriptor metrics and per-glyph advances all come from -[go-opentype](https://github.com/go-opentype/opentype)'s -`Font.SubsetTrueType` / `Font.SubsetCFF` — `pdfkit` keeps no private sfnt -re-parse or subsetter of its own. See -[Text & fonts](/docs/writing-a-document/text-and-fonts/#font-embedding-architecture). -{{< /callout >}} -## The family - -| Repo | What it is | -| --- | --- | -| [`reader`](https://github.com/go-pdfkit/reader) | reads and writes the format itself: objects, cross-reference tables and streams, filters, encryption, the page tree, content streams — and a writer that produces the same bytes twice | -| [`ops`](https://github.com/go-pdfkit/ops) | the verbs, and the `pdfops` command: merge, split, rotate, crop, n-up, watermark, encrypt, and reading a page back | -| [`render`](https://github.com/go-pdfkit/render) | turns a page into pixels: paths, images, text in every font flavour, functions, shadings and patterns | -| [`pdffont`](https://github.com/go-pdfkit/pdffont) | what a document says about a font — encodings, widths, and what text a code stands for | -| [`extract`](https://github.com/go-pdfkit/extract) | reads a page back: the text with where it sits, and the pictures with the box each covers | -| [`coedit`](https://github.com/go-pdfkit/coedit) | a PDF several people edit at once — the plan is shared, not the file | -| [`app`](https://github.com/go-pdfkit/app) | a PDF workbench that runs in a browser tab and nowhere else | -| [`pdfkit`](https://github.com/go-pdfkit/pdfkit) | the document builder: pages, vector graphics, and text in embedded subsetted fonts | -| [`docs`](https://github.com/go-pdfkit/docs) | this documentation site (MkDocs Material, versioned with mike) | - -Two libraries outside the org do the work under all of it: -[go-opentype/opentype](https://github.com/go-opentype/opentype), the sfnt, -CFF and Type 1 parser and shaper, and [go-gfx/gfx](https://github.com/go-gfx/gfx), -the rasteriser. [go-ruby-prawn](https://github.com/go-ruby-prawn) is the -Ruby-idiomatic writer built on the same font stack. - -## What it is - -- A **PDF 1.7 writer**: catalog, page tree, cross-reference table and - trailer, written to any `io.Writer`, with optional FlateDecode stream - compression (`Options.Compress`). -- **Vector graphics**: paths (move/line/cubic/rect/close), fill and stroke - (nonzero and even-odd), DeviceGray/RGB/CMYK colour, line width/cap/join/ - miter/dash, CTM transforms (translate/scale/rotate/skew), clipping, `q`/`Q` - graphics-state save/restore, and constant-alpha transparency via - `ExtGState`. -- **Text as embedded, subsetted fonts**: every font used is written as a - **Type0 (Identity-H)** composite font with a per-glyph `/W` width array and - a `/ToUnicode` CMap for copy/paste. TrueType `glyf` outlines embed as a - subsetted `FontFile2` / `CIDFontType2` with a `/CIDToGIDMap` stream; CFF/ - OpenType outlines embed as a **charstring-subsetted** `FontFile3` / - `CIDFontType0`. -- **Images**: JPEG embedded directly (`DCTDecode`, no re-encoding); PNG and - any `image.Image` rasterised as XObjects (`FlateDecode`) with an `/SMask` - for alpha. -- **Widget bridge**: `Page.AddWidget` and `Page.AddWidgetVector` "print" a - [go-widgets/toolkit](https://github.com/go-widgets/toolkit) widget tree onto - a page — as a rasterised image, or as PDF vector operators with selectable - text. See [Widgets](/docs/writing-a-document/widgets/). -- **Deterministic output**: with the zero `Options`, a document has no - timestamps and a content-derived `/ID` (a SHA-256 of the body), so - identical inputs produce byte-identical PDFs. - -## What it is not (yet) - -- A **CID-keyed CFF** or a **CFF2 (variable)** font cannot be - charstring-subsetted by the preserve-numbering path, so it falls back to - embedding the whole `CFF`/`CFF2` table. A plain (non-CID-keyed) CFF/OpenType - font **is** charstring-subsetted, same as TrueType `glyf`. -- Encryption, tagged PDF / PDF-A, interactive forms and annotations are not - yet implemented. - -See [Scope and limitations](/docs/writing-a-document/text-and-fonts/#scope-and-limitations) for the -full picture, and the upstream -[README](https://github.com/go-pdfkit/pdfkit#scope-and-limitations) for the -canonical statement. - -## Install - -```sh -go get github.com/go-pdfkit/pdfkit -``` - -## Where to go next - -- [Getting started](/docs/writing-a-document/getting-started/) — the five calls that make a PDF: - `New` → `AddPage` → `SetFont` → `Text` → `Write`. -- [Graphics](/docs/writing-a-document/graphics/) — paths, colour, line state, transforms, clipping - and transparency. -- [Text & fonts](/docs/writing-a-document/text-and-fonts/) — loading fonts, drawing text, measuring - and wrapping, and the shaped-text API for complex scripts. -- [Images](/docs/writing-a-document/images/) — `DrawImage`, `DrawPNG` and `DrawJPEG`. -- [Widgets](/docs/writing-a-document/widgets/) — printing a go-widgets/toolkit widget tree to a page. -- [API reference](/docs/api/) — the full public surface with Go examples. - -Source lives at -[github.com/go-pdfkit/pdfkit](https://github.com/go-pdfkit/pdfkit). diff --git a/content/docs/working-with-a-file/_index.md b/content/working-with-a-file/_index.md similarity index 100% rename from content/docs/working-with-a-file/_index.md rename to content/working-with-a-file/_index.md diff --git a/content/docs/working-with-a-file/coediting.md b/content/working-with-a-file/coediting.md similarity index 100% rename from content/docs/working-with-a-file/coediting.md rename to content/working-with-a-file/coediting.md diff --git a/content/docs/working-with-a-file/extraction.md b/content/working-with-a-file/extraction.md similarity index 100% rename from content/docs/working-with-a-file/extraction.md rename to content/working-with-a-file/extraction.md diff --git a/content/docs/working-with-a-file/operations.md b/content/working-with-a-file/operations.md similarity index 100% rename from content/docs/working-with-a-file/operations.md rename to content/working-with-a-file/operations.md diff --git a/content/docs/working-with-a-file/reading.md b/content/working-with-a-file/reading.md similarity index 100% rename from content/docs/working-with-a-file/reading.md rename to content/working-with-a-file/reading.md diff --git a/content/docs/working-with-a-file/rendering.md b/content/working-with-a-file/rendering.md similarity index 100% rename from content/docs/working-with-a-file/rendering.md rename to content/working-with-a-file/rendering.md diff --git a/content/docs/working-with-a-file/workbench.md b/content/working-with-a-file/workbench.md similarity index 100% rename from content/docs/working-with-a-file/workbench.md rename to content/working-with-a-file/workbench.md diff --git a/content/docs/writing-a-document/_index.md b/content/writing-a-document/_index.md similarity index 100% rename from content/docs/writing-a-document/_index.md rename to content/writing-a-document/_index.md diff --git a/content/docs/writing-a-document/getting-started.md b/content/writing-a-document/getting-started.md similarity index 100% rename from content/docs/writing-a-document/getting-started.md rename to content/writing-a-document/getting-started.md diff --git a/content/docs/writing-a-document/graphics.md b/content/writing-a-document/graphics.md similarity index 100% rename from content/docs/writing-a-document/graphics.md rename to content/writing-a-document/graphics.md diff --git a/content/docs/writing-a-document/images.md b/content/writing-a-document/images.md similarity index 100% rename from content/docs/writing-a-document/images.md rename to content/writing-a-document/images.md diff --git a/content/docs/writing-a-document/text-and-fonts.md b/content/writing-a-document/text-and-fonts.md similarity index 100% rename from content/docs/writing-a-document/text-and-fonts.md rename to content/writing-a-document/text-and-fonts.md diff --git a/content/docs/writing-a-document/widgets.md b/content/writing-a-document/widgets.md similarity index 100% rename from content/docs/writing-a-document/widgets.md rename to content/writing-a-document/widgets.md diff --git a/hugo.toml b/hugo.toml index 5c08ce4..b485cd7 100644 --- a/hugo.toml +++ b/hugo.toml @@ -11,6 +11,19 @@ languageCode = "en" enableGitInfo = true +# ⛔ The content is NOT under content/docs. This site is served AT /docs/, so a +# `docs` section would make every page /docs/docs/… -- which it did, and which +# broke all nineteen internal links at once, because they are written /docs/… +# and that is the site root here. +# +# Hextra picks its documentation layout from the section being called `docs`, +# so with the content at the root the type is cascaded instead. +[[cascade]] + [cascade.params] + type = "docs" + [cascade._target] + path = "/**" + [markup.highlight] noClasses = false @@ -37,11 +50,6 @@ enableGitInfo = true enable = true base = "https://github.com/go-pdfkit/docs/edit/main/content" -[[menu.main]] - name = "Documentation" - pageRef = "/docs" - weight = 1 - [[menu.main]] name = "Workbench" url = "https://go-pdfkit.github.io/app/"