Skip to content

Commit

Permalink
PDF/SVG: fix splitting font/ into new repository, PDF unicode table n…
Browse files Browse the repository at this point in the history
…ow contains all entries
  • Loading branch information
tdewolff committed Mar 5, 2024
1 parent b1bc4a4 commit 332f406
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 36 deletions.
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/tdewolff/canvas

go 1.17
go 1.22.0

replace github.com/tdewolff/font => ../font

require (
fyne.io/fyne/v2 v2.4.1
Expand All @@ -16,13 +18,13 @@ require (
github.com/paulmach/osm v0.7.1
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/tdewolff/argp v0.0.0-20240303230445-a8dec846536d
github.com/tdewolff/font v0.0.0-20240304124129-69daaf2ab9ab
github.com/tdewolff/font v0.0.0-20240305151633-f69a3adc9d92
github.com/tdewolff/minify/v2 v2.20.5
github.com/tdewolff/parse/v2 v2.7.3
github.com/tdewolff/test v1.0.11-0.20231121141655-2d5236e10ae4
github.com/wcharczuk/go-chart/v2 v2.1.1
golang.org/x/image v0.13.0
golang.org/x/text v0.13.0
golang.org/x/image v0.15.0
golang.org/x/text v0.14.0
gonum.org/v1/plot v0.14.0
)

Expand All @@ -31,6 +33,7 @@ require (
gioui.org/shader v1.0.8 // indirect
git.sr.ht/~sbinet/gg v0.5.0 // indirect
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/benoitkugler/textlayout v0.3.0 // indirect
github.com/blend/go-sdk v1.20220411.3 // indirect
github.com/campoy/embedmd v1.0.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
Expand Down Expand Up @@ -339,6 +341,8 @@ github.com/tdewolff/argp v0.0.0-20240303230445-a8dec846536d h1:pzTSmw3ARDn5Y50di
github.com/tdewolff/argp v0.0.0-20240303230445-a8dec846536d/go.mod h1:e1dkYfBKpwfFhwXWrQpEU2ClFgxYOT4SrHd6fKD7nIE=
github.com/tdewolff/font v0.0.0-20240304124129-69daaf2ab9ab h1:Fao0QejqxrVSpgyCFQzSnUwjnFgcMEakEUsF1o9gANY=
github.com/tdewolff/font v0.0.0-20240304124129-69daaf2ab9ab/go.mod h1:t7g3ZEPalm2wkgagF2nss6DCfvBliekfnZYFiu4WqEU=
github.com/tdewolff/font v0.0.0-20240305151633-f69a3adc9d92 h1:QfqzZ5hJ0eH6kfsViAqL0vrXX3p/CUewuzbqrefzDho=
github.com/tdewolff/font v0.0.0-20240305151633-f69a3adc9d92/go.mod h1:NlGnfhGrPSwSRo7Q/SsJ/RI3/03lBBmqTRtT4/DuC0g=
github.com/tdewolff/minify/v2 v2.20.5 h1:IbJpmpAFESnuJPdsvFBJWsDcXE5qHsmaVQrRqhOI9sI=
github.com/tdewolff/minify/v2 v2.20.5/go.mod h1:N78HtaitkDYAWXFbqhWX/LzgwylwudK0JvybGDVQ+Mw=
github.com/tdewolff/parse/v2 v2.7.3 h1:SHj/ry85FdqniccvzJTG+Gt/mi/HNa1cJcTzYZnvc5U=
Expand Down Expand Up @@ -415,6 +419,8 @@ golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
Expand Down Expand Up @@ -579,6 +585,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
8 changes: 6 additions & 2 deletions renderers/pdf/pdf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ func TestPDFPath(t *testing.T) {
const fontDir = "../../resources/"

func TestPDFText(t *testing.T) {
doTestPDFText(t, false, 506000, "TestPDFText_no_subset.pdf")
doTestPDFText(t, true, 325000, "TestPDFText_subset_fonts.pdf")
t.Run("without_subset", func(t *testing.T) {
doTestPDFText(t, false, 670000, "TestPDFText_no_subset.pdf")
})
t.Run("with_subset", func(t *testing.T) {
doTestPDFText(t, true, 368000, "TestPDFText_subset_fonts.pdf")
})
}

func doTestPDFText(t *testing.T, subsetFonts bool, expectedSize int, filename string) {
Expand Down
44 changes: 25 additions & 19 deletions renderers/pdf/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,25 @@ func (w *pdfWriter) getFont(font *canvas.Font, vertical bool) pdfRef {

func (w *pdfWriter) writeFont(ref pdfRef, font *canvas.Font, vertical bool) {
// subset the font
fontProgram := font.SFNT.Data
glyphIDs := w.fontSubset[font].List()
if w.subset {
sfnt, sfntOld := font.SFNT, font.SFNT
var glyphIDs []uint16
if w.subset && sfnt.IsTrueType {
// TODO: CFF font subsetting doesn't work
// TODO: remove all optional tables such as kern, GPOS, GSUB, ...
fontProgram, glyphIDs = font.SFNT.Subset(glyphIDs, canvasFont.WritePDFTables)
glyphIDs = w.fontSubset[font].List()
sfnt = sfnt.Subset(glyphIDs, canvasFont.SubsetOptions{Tables: canvasFont.KeepPDFTables})
} else {
glyphIDs = make([]uint16, sfnt.NumGlyphs())
for glyphID := uint16(0); glyphID < sfnt.NumGlyphs(); glyphID++ {
glyphIDs = append(glyphIDs, glyphID)
}
}
fontProgram := sfnt.Write()

// calculate the character widths for the W array and shorten it
f := 1000.0 / float64(font.SFNT.Head.UnitsPerEm)
f := 1000.0 / float64(sfnt.Head.UnitsPerEm)
widths := make([]int, len(glyphIDs)+1)
for subsetGlyphID, glyphID := range glyphIDs {
widths[subsetGlyphID] = int(f*float64(font.SFNT.GlyphAdvance(glyphID)) + 0.5)
widths[subsetGlyphID] = int(f*float64(sfnt.GlyphAdvance(glyphID)) + 0.5)
}
DW := widths[0]
W := pdfArray{}
Expand Down Expand Up @@ -302,7 +308,7 @@ func (w *pdfWriter) writeFont(ref pdfRef, font *canvas.Font, vertical bool) {
startUnicode := uint32('\uFFFD')
length := uint16(1)
for subsetGlyphID, glyphID := range glyphIDs[1:] {
unicode := uint32(font.SFNT.Cmap.ToUnicode(glyphID))
unicode := uint32(sfntOld.Cmap.ToUnicode(glyphID))
if 0x010000 <= unicode && unicode <= 0x10FFFF {
// UTF-16 surrogates
unicode -= 0x10000
Expand Down Expand Up @@ -368,7 +374,7 @@ end`, bfRangeCount, bfRange.String(), bfCharCount, bfChar.String())

// get name and CID subtype
name := font.Name()
if records := font.SFNT.Name.Get(canvasFont.NamePostScript); 0 < len(records) {
if records := sfntOld.Name.Get(canvasFont.NamePostScript); 0 < len(records) {
name = records[0].String()
}
baseFont := strings.ReplaceAll(name, " ", "")
Expand All @@ -382,9 +388,9 @@ end`, bfRangeCount, bfRange.String(), bfCharCount, bfChar.String())
}

cidSubtype := ""
if font.SFNT.IsTrueType {
if sfnt.IsTrueType {
cidSubtype = "CIDFontType2"
} else if font.SFNT.IsCFF {
} else if sfnt.IsCFF {
cidSubtype = "CIDFontType0"
}

Expand Down Expand Up @@ -412,15 +418,15 @@ end`, bfRangeCount, bfRange.String(), bfCharCount, bfChar.String())
"FontName": pdfName(baseFont),
"Flags": 4, // Symbolic
"FontBBox": pdfArray{
int(f * float64(font.SFNT.Head.XMin)),
int(f * float64(font.SFNT.Head.YMin)),
int(f * float64(font.SFNT.Head.XMax)),
int(f * float64(font.SFNT.Head.YMax)),
int(f * float64(sfnt.Head.XMin)),
int(f * float64(sfnt.Head.YMin)),
int(f * float64(sfnt.Head.XMax)),
int(f * float64(sfnt.Head.YMax)),
},
"ItalicAngle": float64(font.SFNT.Post.ItalicAngle),
"Ascent": int(f * float64(font.SFNT.Hhea.Ascender)),
"Descent": -int(f * float64(font.SFNT.Hhea.Descender)),
"CapHeight": int(f * float64(font.SFNT.OS2.SCapHeight)),
"ItalicAngle": float64(sfntOld.Post.ItalicAngle),
"Ascent": int(f * float64(sfnt.Hhea.Ascender)),
"Descent": -int(f * float64(sfnt.Hhea.Descender)),
"CapHeight": int(f * float64(sfntOld.OS2.SCapHeight)),
"StemV": 80, // taken from Inkscape, should be calculated somehow, maybe use: 10+220*(usWeightClass-50)/900
"FontFile3": fontfileRef,
},
Expand Down
22 changes: 12 additions & 10 deletions renderers/svg/svg.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

"github.com/tdewolff/canvas"
canvasText "github.com/tdewolff/canvas/text"
canvasFont "github.com/tdewolff/font"
"github.com/tdewolff/font"
)

type Options struct {
Expand Down Expand Up @@ -85,22 +85,24 @@ func (r *SVG) Close() error {
func (r *SVG) writeFonts() {
if 0 < len(r.fonts) {
fmt.Fprintf(r.w, "<style>")
for font := range r.fonts {
b := font.SFNT.Data
for f := range r.fonts {
sfnt := f.SFNT
if r.opts.SubsetFonts {
glyphIDs := r.fontSubset[font].List()
b, _ = font.SFNT.Subset(glyphIDs, canvasFont.WriteMinTables)
glyphIDs := r.fontSubset[f].List()
sfnt = sfnt.Subset(glyphIDs, font.SubsetOptions{Tables: font.KeepMinTables})
}
fmt.Fprintf(r.w, "\n@font-face{font-family:'%s'", font.Name())
if font.Style().Weight() != canvas.FontRegular {
fmt.Fprintf(r.w, ";font-weight:%d", font.Style().CSS())
fontProgram := sfnt.Write()

fmt.Fprintf(r.w, "\n@font-face{font-family:'%s'", f.Name())
if f.Style().Weight() != canvas.FontRegular {
fmt.Fprintf(r.w, ";font-weight:%d", f.Style().CSS())
}
if font.Style().Italic() {
if f.Style().Italic() {
fmt.Fprintf(r.w, ";font-style:italic")
}
fmt.Fprintf(r.w, ";src:url('data:type/opentype;base64,")
encoder := base64.NewEncoder(base64.StdEncoding, r.w)
encoder.Write(b)
encoder.Write(fontProgram)
encoder.Close()
fmt.Fprintf(r.w, "');}")
}
Expand Down
2 changes: 1 addition & 1 deletion text/harfbuzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewShaper(b []byte, _ int) (Shaper, error) {
// NewShaperSFNT returns a new text shaper using a SFNT structure.
func NewShaperSFNT(sfnt *font.SFNT) (Shaper, error) {
// TODO: add interface to SFNT for use in this harfbuzz implementation
return NewShaper(sfnt.Data, 0)
return NewShaper(sfnt.Write(), 0)
}

// Destroy destroys the allocated C memory.
Expand Down

0 comments on commit 332f406

Please sign in to comment.