use new SyncMap with FontFaces/FontEncoding

This commit is contained in:
Gavin Nishizawa 2023-10-30 12:13:18 -07:00
parent 94669f9b04
commit 1cfa2b9f81
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
5 changed files with 52 additions and 71 deletions

View file

@ -13,6 +13,7 @@ import (
"oss.terrastruct.com/d2/lib/font" "oss.terrastruct.com/d2/lib/font"
fontlib "oss.terrastruct.com/d2/lib/font" fontlib "oss.terrastruct.com/d2/lib/font"
"oss.terrastruct.com/d2/lib/syncmap"
) )
type FontFamily string type FontFamily string
@ -44,11 +45,7 @@ func (f Font) GetEncodedSubset(corpus string) string {
FontFamiliesMu.Lock() FontFamiliesMu.Lock()
defer FontFamiliesMu.Unlock() defer FontFamiliesMu.Unlock()
var face []byte face := FontFaces.Get(f)
{
ff, _ := FontFaces.Load(f)
face = ff.([]byte)
}
fontBuf := make([]byte, len(face)) fontBuf := make([]byte, len(face))
copy(fontBuf, face) copy(fontBuf, face)
fontBuf = font.UTF8CutFont(fontBuf, uniqueChars) fontBuf = font.UTF8CutFont(fontBuf, uniqueChars)
@ -56,8 +53,7 @@ func (f Font) GetEncodedSubset(corpus string) string {
fontBuf, err := fontlib.Sfnt2Woff(fontBuf) fontBuf, err := fontlib.Sfnt2Woff(fontBuf)
if err != nil { if err != nil {
// If subset fails, return full encoding // If subset fails, return full encoding
fe, _ := FontEncodings.Load(f) return FontEncodings.Get(f)
return fe.(string)
} }
return fmt.Sprintf("data:application/font-woff;base64,%v", base64.StdEncoding.EncodeToString(fontBuf)) return fmt.Sprintf("data:application/font-woff;base64,%v", base64.StdEncoding.EncodeToString(fontBuf))
@ -140,104 +136,105 @@ var fuzzyBubblesBoldBase64 string
//go:embed ttf/* //go:embed ttf/*
var fontFacesFS embed.FS var fontFacesFS embed.FS
// FontEncodings map[Font]string var FontEncodings syncmap.SyncMap[Font, string]
var FontEncodings sync.Map var FontFaces syncmap.SyncMap[Font, []byte]
// FontFaces map[Font][]byte
var FontFaces sync.Map
func init() { func init() {
FontEncodings.Store( FontEncodings = syncmap.New[Font, string]()
FontEncodings.Set(
Font{ Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, },
sourceSansProRegularBase64) sourceSansProRegularBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, },
sourceSansProBoldBase64) sourceSansProBoldBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
}, },
sourceSansProSemiboldBase64) sourceSansProSemiboldBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
}, },
sourceSansProItalicBase64) sourceSansProItalicBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, },
sourceCodeProRegularBase64) sourceCodeProRegularBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, },
sourceCodeProBoldBase64) sourceCodeProBoldBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
}, },
sourceCodeProSemiboldBase64) sourceCodeProSemiboldBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
}, },
sourceCodeProItalicBase64) sourceCodeProItalicBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, },
fuzzyBubblesRegularBase64) fuzzyBubblesRegularBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
// This font has no italic, so just reuse regular // This font has no italic, so just reuse regular
}, fuzzyBubblesRegularBase64) }, fuzzyBubblesRegularBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, fuzzyBubblesBoldBase64) }, fuzzyBubblesBoldBase64)
FontEncodings.Store( FontEncodings.Set(
Font{ Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
// This font has no semibold, so just reuse bold // This font has no semibold, so just reuse bold
}, fuzzyBubblesBoldBase64) }, fuzzyBubblesBoldBase64)
FontEncodings.Range(func(k, v any) bool { FontEncodings.Range(func(k Font, v string) bool {
FontEncodings.Swap(k, strings.TrimSuffix(v.(string), "\n")) FontEncodings.Set(k, strings.TrimSuffix(v, "\n"))
return true return true
}) })
FontFaces = syncmap.New[Font, []byte]()
b, err := fontFacesFS.ReadFile("ttf/SourceSansPro-Regular.ttf") b, err := fontFacesFS.ReadFile("ttf/SourceSansPro-Regular.ttf")
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, b) }, b)
@ -246,7 +243,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, b) }, b)
@ -255,7 +252,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, b) }, b)
@ -264,7 +261,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
}, b) }, b)
@ -273,7 +270,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
}, b) }, b)
@ -282,7 +279,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, b) }, b)
@ -291,7 +288,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
}, b) }, b)
@ -300,7 +297,7 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
}, b) }, b)
@ -309,11 +306,11 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
}, b) }, b)
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
}, b) }, b)
@ -322,11 +319,11 @@ func init() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
}, b) }, b)
FontFaces.Store(Font{ FontFaces.Set(Font{
Family: HandDrawn, Family: HandDrawn,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
}, b) }, b)
@ -338,14 +335,14 @@ var D2_FONT_TO_FAMILY = map[string]FontFamily{
} }
func AddFontStyle(font Font, style FontStyle, ttf []byte) error { func AddFontStyle(font Font, style FontStyle, ttf []byte) error {
FontFaces.Store(font, ttf) FontFaces.Set(font, ttf)
woff, err := fontlib.Sfnt2Woff(ttf) woff, err := fontlib.Sfnt2Woff(ttf)
if err != nil { if err != nil {
return fmt.Errorf("failed to encode ttf to woff: %v", err) return fmt.Errorf("failed to encode ttf to woff: %v", err)
} }
encodedWoff := fmt.Sprintf("data:application/font-woff;base64,%v", base64.StdEncoding.EncodeToString(woff)) encodedWoff := fmt.Sprintf("data:application/font-woff;base64,%v", base64.StdEncoding.EncodeToString(woff))
FontEncodings.Store(font, encodedWoff) FontEncodings.Set(font, encodedWoff)
return nil return nil
} }
@ -369,10 +366,8 @@ func AddFontFamily(name string, regularTTF, italicTTF, boldTTF, semiboldTTF []by
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_REGULAR, Style: FONT_STYLE_REGULAR,
} }
f, _ := FontFaces.Load(fallbackFont) FontFaces.Set(regularFont, FontFaces.Get(fallbackFont))
FontFaces.Store(regularFont, f) FontEncodings.Set(regularFont, FontEncodings.Get(fallbackFont))
e, _ := FontEncodings.Load(fallbackFont)
FontEncodings.Store(regularFont, e)
} }
italicFont := Font{ italicFont := Font{
@ -389,10 +384,8 @@ func AddFontFamily(name string, regularTTF, italicTTF, boldTTF, semiboldTTF []by
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_ITALIC, Style: FONT_STYLE_ITALIC,
} }
f, _ := FontFaces.Load(fallbackFont) FontFaces.Set(italicFont, FontFaces.Get(fallbackFont))
FontFaces.Store(italicFont, f) FontEncodings.Set(italicFont, FontEncodings.Get(fallbackFont))
fb, _ := FontEncodings.Load(fallbackFont)
FontEncodings.Store(italicFont, fb)
} }
boldFont := Font{ boldFont := Font{
@ -409,10 +402,8 @@ func AddFontFamily(name string, regularTTF, italicTTF, boldTTF, semiboldTTF []by
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
} }
f, _ := FontFaces.Load(fallbackFont) FontFaces.Set(boldFont, FontFaces.Get(fallbackFont))
FontFaces.Store(boldFont, f) FontEncodings.Set(boldFont, FontEncodings.Get(fallbackFont))
fb, _ := FontEncodings.Load(fallbackFont)
FontEncodings.Store(boldFont, fb)
} }
semiboldFont := Font{ semiboldFont := Font{
@ -429,10 +420,8 @@ func AddFontFamily(name string, regularTTF, italicTTF, boldTTF, semiboldTTF []by
Family: SourceSansPro, Family: SourceSansPro,
Style: FONT_STYLE_SEMIBOLD, Style: FONT_STYLE_SEMIBOLD,
} }
f, _ := FontFaces.Load(fallbackFont) FontFaces.Set(semiboldFont, FontFaces.Get(fallbackFont))
FontFaces.Store(semiboldFont, f) FontEncodings.Set(semiboldFont, FontEncodings.Get(fallbackFont))
fb, _ := FontEncodings.Load(fallbackFont)
FontEncodings.Store(semiboldFont, fb)
} }
FontFamilies = append(FontFamilies, customFontFamily) FontFamilies = append(FontFamilies, customFontFamily)

View file

@ -14,11 +14,7 @@ func TestCutFont(t *testing.T) {
Family: SourceCodePro, Family: SourceCodePro,
Style: FONT_STYLE_BOLD, Style: FONT_STYLE_BOLD,
} }
var face []byte face := FontFaces.Get(f)
{
ff, _ := FontFaces.Load(f)
face = ff.([]byte)
}
fontBuf := make([]byte, len(face)) fontBuf := make([]byte, len(face))
copy(fontBuf, face) copy(fontBuf, face)
fontBuf = font.UTF8CutFont(fontBuf, " 1") fontBuf = font.UTF8CutFont(fontBuf, " 1")

View file

@ -121,7 +121,6 @@ func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []by
} }
if !strings.Contains(svg, `font-family: "font-regular"`) { if !strings.Contains(svg, `font-family: "font-regular"`) {
font, _ := d2fonts.FontEncodings.Load(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR))
appendix += fmt.Sprintf(`<style type="text/css"><![CDATA[ appendix += fmt.Sprintf(`<style type="text/css"><![CDATA[
.text { .text {
font-family: "font-regular"; font-family: "font-regular";
@ -130,10 +129,9 @@ func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []by
font-family: font-regular; font-family: font-regular;
src: url("%s"); src: url("%s");
} }
]]></style>`, font.(string)) ]]></style>`, d2fonts.FontEncodings.Get(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)))
} }
if !strings.Contains(svg, `font-family: "font-bold"`) { if !strings.Contains(svg, `font-family: "font-bold"`) {
font, _ := d2fonts.FontEncodings.Load(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD))
appendix += fmt.Sprintf(`<style type="text/css"><![CDATA[ appendix += fmt.Sprintf(`<style type="text/css"><![CDATA[
.text-bold { .text-bold {
font-family: "font-bold"; font-family: "font-bold";
@ -142,7 +140,7 @@ func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []by
font-family: font-bold; font-family: font-bold;
src: url("%s"); src: url("%s");
} }
]]></style>`, font.(string)) ]]></style>`, d2fonts.FontEncodings.Get(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD)))
} }
closingIndex := strings.LastIndex(svg, "</svg></svg>") closingIndex := strings.LastIndex(svg, "</svg></svg>")

View file

@ -31,10 +31,8 @@ func Init() *GoFPDF {
UnitStr: "pt", UnitStr: "pt",
}) })
reg, _ := d2fonts.FontFaces.Load(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)) newGofPDF.AddUTF8FontFromBytes("source", "", d2fonts.FontFaces.Get(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)))
bold, _ := d2fonts.FontFaces.Load(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD)) newGofPDF.AddUTF8FontFromBytes("source", "B", d2fonts.FontFaces.Get(d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD)))
newGofPDF.AddUTF8FontFromBytes("source", "", reg.([]byte))
newGofPDF.AddUTF8FontFromBytes("source", "B", bold.([]byte))
newGofPDF.SetAutoPageBreak(false, 0) newGofPDF.SetAutoPageBreak(false, 0)
newGofPDF.SetLineWidth(2) newGofPDF.SetLineWidth(2)
newGofPDF.SetMargins(0, 0, 0) newGofPDF.SetMargins(0, 0, 0)

View file

@ -126,12 +126,12 @@ func NewRuler() (*Ruler, error) {
Style: fontStyle, Style: fontStyle,
} }
// Note: FontFaces lookup is size-agnostic // Note: FontFaces lookup is size-agnostic
face, has := d2fonts.FontFaces.Load(font) face, has := d2fonts.FontFaces.GetWithStatus(font)
if !has { if !has {
continue continue
} }
if _, loaded := r.ttfs[font]; !loaded { if _, loaded := r.ttfs[font]; !loaded {
ttf, err := truetype.Parse(face.([]byte)) ttf, err := truetype.Parse(face)
if err != nil { if err != nil {
return nil, err return nil, err
} }