github.com/pdfcpu/pdfcpu@v0.11.1/pkg/api/font.go (about) 1 /* 2 Copyright 2020 The pdfcpu Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package api 18 19 import ( 20 "bytes" 21 "fmt" 22 23 "path/filepath" 24 "sort" 25 "unicode/utf8" 26 27 "github.com/pdfcpu/pdfcpu/pkg/font" 28 "github.com/pdfcpu/pdfcpu/pkg/log" 29 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" 30 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/color" 31 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/draw" 32 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model" 33 "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types" 34 "github.com/pkg/errors" 35 ) 36 37 // ListFonts returns a list of supported fonts. 38 func ListFonts() ([]string, error) { 39 // Get list of PDF core fonts. 40 coreFonts := font.CoreFontNames() 41 for i, s := range coreFonts { 42 coreFonts[i] = " " + s 43 } 44 sort.Strings(coreFonts) 45 46 sscf := []string{"Corefonts:"} 47 sscf = append(sscf, coreFonts...) 48 49 // Get installed fonts from pdfcpu config dir in users home dir 50 userFonts := font.UserFontNamesVerbose() 51 for i, s := range userFonts { 52 userFonts[i] = " " + s 53 } 54 sort.Strings(userFonts) 55 56 ssuf := []string{fmt.Sprintf("Userfonts(%s):", font.UserFontDir)} 57 ssuf = append(ssuf, userFonts...) 58 59 sscf = append(sscf, "") 60 return append(sscf, ssuf...), nil 61 } 62 63 // InstallFonts installs true type fonts for embedding. 64 func InstallFonts(fileNames []string) error { 65 if log.CLIEnabled() { 66 log.CLI.Printf("installing to %s...", font.UserFontDir) 67 } 68 69 for _, fn := range fileNames { 70 switch filepath.Ext(fn) { 71 case ".ttf": 72 //log.CLI.Println(filepath.Base(fn)) 73 if err := font.InstallTrueTypeFont(font.UserFontDir, fn); err != nil { 74 if log.CLIEnabled() { 75 log.CLI.Printf("%v", err) 76 } 77 } 78 case ".ttc": 79 //log.CLI.Println(filepath.Base(fn)) 80 if err := font.InstallTrueTypeCollection(font.UserFontDir, fn); err != nil { 81 if log.CLIEnabled() { 82 log.CLI.Printf("%v", err) 83 } 84 } 85 } 86 } 87 88 return font.LoadUserFonts() 89 } 90 91 func rowLabel(xRefTable *model.XRefTable, i int, td model.TextDescriptor, baseFontName, baseFontKey string, buf *bytes.Buffer, mb *types.Rectangle, left bool) { 92 x := 39. 93 if !left { 94 x = 7750 95 } 96 s := fmt.Sprintf("#%02X", i) 97 td.X, td.Y, td.Text = x, float64(7677-i*30), s 98 td.StrokeCol, td.FillCol = color.Black, color.SimpleColor{B: .8} 99 td.FontName, td.FontKey, td.FontSize = baseFontName, baseFontKey, 14 100 101 model.WriteMultiLine(xRefTable, buf, mb, nil, td) 102 } 103 104 func columnsLabel(xRefTable *model.XRefTable, td model.TextDescriptor, baseFontName, baseFontKey string, buf *bytes.Buffer, mb *types.Rectangle, top bool) { 105 y := 7700. 106 if !top { 107 y = 0 108 } 109 110 td.FontName, td.FontKey = baseFontName, baseFontKey 111 112 for i := 0; i < 256; i++ { 113 s := fmt.Sprintf("#%02X", i) 114 td.X, td.Y, td.Text, td.FontSize = float64(70+i*30), y, s, 14 115 td.StrokeCol, td.FillCol = color.Black, color.SimpleColor{B: .8} 116 model.WriteMultiLine(xRefTable, buf, mb, nil, td) 117 } 118 } 119 120 func surrogate(r rune) bool { 121 return r >= 0xD800 && r <= 0xDFFF 122 } 123 124 func writeUserFontDemoContent(xRefTable *model.XRefTable, p model.Page, fontName string, plane int) { 125 baseFontName := "Helvetica" 126 baseFontSize := 24 127 baseFontKey := p.Fm.EnsureKey(baseFontName) 128 129 fontKey := p.Fm.EnsureKey(fontName) 130 fontSize := 24 131 132 fillCol := color.NewSimpleColor(0xf7e6c7) 133 draw.DrawGrid(p.Buf, 16*16, 16*16, types.RectForWidthAndHeight(55, 16, 16*480, 16*480), color.Black, &fillCol) 134 135 td := model.TextDescriptor{ 136 FontName: fontName, 137 Embed: true, 138 FontKey: fontKey, 139 FontSize: baseFontSize, 140 HAlign: types.AlignCenter, 141 VAlign: types.AlignBaseline, 142 Scale: 1.0, 143 ScaleAbs: true, 144 RMode: draw.RMFill, 145 StrokeCol: color.Black, 146 FillCol: color.NewSimpleColor(0xab6f30), 147 ShowBackground: true, 148 BackgroundCol: color.SimpleColor{R: 1., G: .98, B: .77}, 149 } 150 151 from := plane * 0x10000 152 to := (plane+1)*0x10000 - 1 153 s := fmt.Sprintf("%s %d points (%04X - %04X)", fontName, fontSize, from, to) 154 155 td.X, td.Y, td.Text = p.MediaBox.Width()/2, 7750, s 156 td.FontName, td.FontKey = baseFontName, baseFontKey 157 td.StrokeCol, td.FillCol = color.NewSimpleColor(0x77bdbd), color.NewSimpleColor(0xab6f30) 158 model.WriteMultiLine(xRefTable, p.Buf, p.MediaBox, nil, td) 159 160 columnsLabel(xRefTable, td, baseFontName, baseFontKey, p.Buf, p.MediaBox, true) 161 base := rune(plane * 0x10000) 162 for j := 0; j < 256; j++ { 163 rowLabel(xRefTable, j, td, baseFontName, baseFontKey, p.Buf, p.MediaBox, true) 164 buf := make([]byte, 4) 165 td.StrokeCol, td.FillCol = color.Black, color.Black 166 td.FontName, td.FontKey, td.FontSize = fontName, fontKey, fontSize-2 167 for i := 0; i < 256; i++ { 168 r := base + rune(j*256+i) 169 s = " " 170 if !surrogate(r) { 171 n := utf8.EncodeRune(buf, r) 172 s = string(buf[:n]) 173 } 174 td.X, td.Y, td.Text = float64(70+i*30), float64(7672-j*30), s 175 model.WriteMultiLine(xRefTable, p.Buf, p.MediaBox, nil, td) 176 } 177 rowLabel(xRefTable, j, td, baseFontName, baseFontKey, p.Buf, p.MediaBox, false) 178 } 179 columnsLabel(xRefTable, td, baseFontName, baseFontKey, p.Buf, p.MediaBox, false) 180 } 181 182 func createUserFontDemoPage(xRefTable *model.XRefTable, w, h, plane int, fontName string) model.Page { 183 mediaBox := types.RectForDim(float64(w), float64(h)) 184 p := model.NewPageWithBg(mediaBox, color.NewSimpleColor(0xbeded9)) 185 writeUserFontDemoContent(xRefTable, p, fontName, plane) 186 return p 187 } 188 189 func planeString(i int) string { 190 switch i { 191 case 0: 192 return "BMP" // Basic Multilingual Plane 193 case 1: 194 return "SMP" // Supplementary Multilingual Plane 195 case 2: 196 return "SIP" // Supplementary Ideographic Plane 197 case 3: 198 return "TIP" // Tertiary Ideographic Plane 199 case 14: 200 return "SSP" // Supplementary Special-purpose Plane 201 case 15: 202 return "SPUA" // Supplementary Private Use Area Plane 203 } 204 return "" 205 } 206 207 // CreateUserFontDemoFiles creates single page PDF for each Unicode plane covered. 208 func CreateUserFontDemoFiles(dir, fn string) error { 209 w, h := 7800, 7800 210 font.UserFontMetricsLock.RLock() 211 ttf, ok := font.UserFontMetrics[fn] 212 font.UserFontMetricsLock.RUnlock() 213 if !ok { 214 return errors.Errorf("pdfcpu: font %s not available\n", fn) 215 } 216 // Create a single page PDF for each Unicode plane with existing glyphs. 217 for i := range ttf.Planes { 218 xRefTable, err := pdfcpu.CreateDemoXRef() 219 if err != nil { 220 return err 221 } 222 p := createUserFontDemoPage(xRefTable, w, h, i, fn) 223 224 rootDict, err := xRefTable.Catalog() 225 if err != nil { 226 return err 227 } 228 if err = pdfcpu.AddPageTreeWithSamplePage(xRefTable, rootDict, p); err != nil { 229 return err 230 } 231 fileName := filepath.Join(dir, fn+"_"+planeString(i)+".pdf") 232 if err := CreatePDFFile(xRefTable, fileName, nil); err != nil { 233 return err 234 } 235 } 236 return nil 237 } 238 239 // CreateCheatSheetsUserFonts creates single page PDF cheat sheets for installed user fonts. 240 func CreateCheatSheetsUserFonts(fontNames []string) error { 241 if len(fontNames) == 0 { 242 fontNames = font.UserFontNames() 243 } 244 sort.Strings(fontNames) 245 for _, fn := range fontNames { 246 if !font.IsUserFont(fn) { 247 if log.CLIEnabled() { 248 log.CLI.Printf("unknown user font: %s\n", fn) 249 } 250 continue 251 } 252 if log.CLIEnabled() { 253 log.CLI.Println("creating cheatsheets for: " + fn) 254 } 255 if err := CreateUserFontDemoFiles(".", fn); err != nil { 256 return err 257 } 258 } 259 return nil 260 }