From 68c4efdc6b5ff18883c943d80feb60d474671be3 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Tue, 25 Aug 2026 23:27:32 +0200 Subject: [PATCH] Say whether the document gave a width, or whether it did not MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One of the fourteen standard faces is written with no widths at all — every reader is expected to know Times and Helvetica and Courier by heart. A reader that draws a stand-in for such a font has to take the advances from the stand-in too, and it cannot do that without knowing which widths were the document's own. Half an em for every letter reads as a typewriter, which is not what the page says. --- font.go | 13 +++++++++++++ font_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/font.go b/font.go index 1d8bd98..0c201f4 100644 --- a/font.go +++ b/font.go @@ -129,6 +129,19 @@ func (f *Font) Width(code int) float64 { return f.defaultW } +// HasWidth reports whether the document said how wide this code is, rather +// than the width being the fallback [Font.Width] gives for one it did not. +// +// It matters for a font the document does not carry. One of the fourteen +// standard faces is written with no widths at all — every reader is expected +// to know Times and Helvetica and Courier by heart — so a reader drawing a +// stand-in has to take the advances from the stand-in too. Half an em for +// every letter reads as a typewriter, which is not what the page says. +func (f *Font) HasWidth(code int) bool { + _, ok := f.widths[code] + return ok +} + // GlyphName is what the document's encoding calls a code. ok is false for a // composite font, which names nothing, and for a code the encoding passes over. func (f *Font) GlyphName(code int) (string, bool) { diff --git a/font_test.go b/font_test.go index 440ce2b..7abbbde 100644 --- a/font_test.go +++ b/font_test.go @@ -362,3 +362,34 @@ func TestTheProgramAFontCarries(t *testing.T) { t.Error("the descriptor is not there") } } + +func TestWhetherTheDocumentSaidHowWideACodeIs(t *testing.T) { + // One of the fourteen standard faces is written with no widths at all, + // so a reader drawing a stand-in has to know the difference between a + // width the document gave and the one it fell back on. + f := fontDoc(t, func(w *reader.Writer) reader.Dict { + return reader.Dict{ + "Type": reader.Name("Font"), "Subtype": reader.Name("Type1"), + "BaseFont": reader.Name("Helvetica"), "FirstChar": reader.Integer(65), + "LastChar": reader.Integer(66), "Widths": widthArray(600, 700), + } + }) + for code, want := range map[int]bool{65: true, 66: true, 67: false, 0: false} { + if got := f.HasWidth(code); got != want { + t.Errorf("HasWidth(%d) = %v, want %v", code, got, want) + } + } + // A standard face written with none at all says so for every code. + bare := fontDoc(t, func(w *reader.Writer) reader.Dict { + return reader.Dict{"Type": reader.Name("Font"), "Subtype": reader.Name("Type1"), + "BaseFont": reader.Name("Times-Bold")} + }) + for _, code := range []int{'A', 'z', ' '} { + if bare.HasWidth(code) { + t.Errorf("a font with no widths says it has one for %q", rune(code)) + } + if got := bare.Width(code); got != 0.5 { + t.Errorf("its fallback width is %v", got) + } + } +}