From e0405799cb805309cf8757e8e18b97b0fdf12096 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Thu, 3 Sep 2026 23:33:30 +0200 Subject: [PATCH] engine: hyperref pdftitle/pdfauthor set the PDF document metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The engine built every PDF with an empty information dictionary, so a produced document had no title or author in its properties — a viewer showed the filename and reference managers read nothing. \hypersetup{pdftitle=…,pdfauthor=…} (and the package-option form) now populate go-pdfkit's /Info /Title and /Author. Because go-pdfkit v0.10.0 encodes a non-ASCII text string as UTF-16BE, an accented title — pdftitle={Résumé des travaux} — is correct in the PDF, which is the direct payoff of that encoding work. Scope: pdftitle and pdfauthor map to go-pdfkit's existing Title/Author. pdfsubject and pdfkeywords need new go-pdfkit Info fields and are left for a follow-up. Verified: a document with pdftitle/pdfauthor renders /Title and /Author in the (uncompressed) info dictionary from both the \hypersetup and package-option forms; an accented title is UTF-16BE (), not raw UTF-8; a document with no pdftitle emits no /Title as before. Full suite, conformance ratchet, go vet and gofmt green. Co-Authored-By: Claude Opus 4.8 --- engine.go | 2 ++ hyperstyle.go | 6 ++++ pdfdriver.go | 7 +++- pdfinfo_test.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 pdfinfo_test.go diff --git a/engine.go b/engine.go index b324619..18bfaa8 100644 --- a/engine.go +++ b/engine.go @@ -98,6 +98,8 @@ type Engine struct { hyperColorlinks bool // colorlinks option: colour link text (else leave it black) hyperURLColor uint32 // \url/\href colour when colorlinks is on (hyperref urlcolor) hyperLinkColor uint32 // \hyperlink colour when colorlinks is on (hyperref linkcolor) + pdfTitle string // \hypersetup{pdftitle=…} → PDF /Info /Title + pdfAuthor string // \hypersetup{pdfauthor=…} → PDF /Info /Author mathR mathRendererT // lazily-built go-tex/math renderer (see math.go) // paragraph-builder state (horizontal mode at top level) diff --git a/hyperstyle.go b/hyperstyle.go index f46ab8f..7c0676e 100644 --- a/hyperstyle.go +++ b/hyperstyle.go @@ -28,6 +28,8 @@ import "strings" // - linkcolor: colour of internal links (\hyperlink). // - urlcolor: colour of \url and \href. // - allcolors: sets both link and url colours at once. +// - pdftitle / pdfauthor: the PDF /Info document title and author (see +// pdfdriver.go); a non-ASCII value reaches the PDF as UTF-16BE. func (e *Engine) applyHypersetup(opts string) { for _, seg := range splitKVSegments(opts, ',') { key, val, has := cutKeyVal(seg) @@ -41,6 +43,10 @@ func (e *Engine) applyHypersetup(opts string) { case "allcolors", "allcolours": c := e.resolveColor(val) e.hyperLinkColor, e.hyperURLColor = c, c + case "pdftitle": + e.pdfTitle = val + case "pdfauthor": + e.pdfAuthor = val } } } diff --git a/pdfdriver.go b/pdfdriver.go index e170d9f..e7bcb03 100644 --- a/pdfdriver.go +++ b/pdfdriver.go @@ -33,7 +33,12 @@ func (e *Engine) RenderPDF(w io.Writer, margin float64) error { return fmt.Errorf("texengine: PDF font load: %w", err) } size := float64(ef.sizePt()) - doc := pdfkit.New(pdfkit.Options{}) + // \hypersetup{pdftitle=…,pdfauthor=…} populates the PDF information dictionary, + // so a viewer's title bar and document properties — and reference managers — + // read the paper's real title and author rather than nothing (see hyperstyle.go). + // go-pdfkit encodes a non-ASCII value as a UTF-16BE text string, so an accented + // title is correct. + doc := pdfkit.New(pdfkit.Options{Title: e.pdfTitle, Author: e.pdfAuthor}) vmargin := e.renderVMargin(margin) paperW, paperH, _ := e.paperSizePt() for _, page := range e.Pages() { diff --git a/pdfinfo_test.go b/pdfinfo_test.go new file mode 100644 index 0000000..00c5558 --- /dev/null +++ b/pdfinfo_test.go @@ -0,0 +1,89 @@ +// Copyright (c) the go-tex/engine authors. +// SPDX-License-Identifier: BSD-3-Clause + +package engine + +import ( + "bytes" + "strings" + "testing" +) + +// renderPDFDoc runs a small document to PDF and returns the raw bytes as a string. +// The information dictionary (/Title, /Author) is not stream-compressed, so a test +// can grep the raw PDF for it. +func renderPDFDoc(t *testing.T, body string) string { + t.Helper() + fp := pdfFontOrSkip(t) + e := New() + e.LoadLaTeX() + e.Run(`\font\rm={` + fp + `} at 12pt \rm`) + if _, err := e.Run(`\documentclass{article}\begin{document}` + body + `\end{document}`); err != nil { + t.Fatal(err) + } + var buf bytes.Buffer + if err := e.RenderPDF(&buf, 24); err != nil { + t.Fatalf("RenderPDF: %v", err) + } + return buf.String() +} + +// \hypersetup{pdftitle=…,pdfauthor=…} reaches the PDF information dictionary. +func TestPDFInfoTitleAndAuthor(t *testing.T) { + out := renderPDFDoc(t, `\hypersetup{pdftitle={A Study of Widgets},pdfauthor={Ada Lovelace}}Body.`) + if !strings.Contains(out, `/Title (A Study of Widgets)`) { + t.Errorf("pdftitle did not reach /Info /Title") + } + if !strings.Contains(out, `/Author (Ada Lovelace)`) { + t.Errorf("pdfauthor did not reach /Info /Author") + } +} + +// An accented pdftitle is written UTF-16BE, not as raw UTF-8 (the payoff of the +// go-pdfkit v0.10.0 text-string work). +func TestPDFInfoAccentedTitleIsUTF16(t *testing.T) { + out := renderPDFDoc(t, `\hypersetup{pdftitle={Résumé des travaux}}Body.`) + if !strings.Contains(out, "