github.com/signintech/pdft@v0.5.0/minigopdf/fontmaker/core/fontmaker.go (about) 1 package core 2 3 import ( 4 "bufio" 5 "bytes" 6 "compress/zlib" 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strconv" 13 "strings" 14 ) 15 16 // ErrFontLicenseDoesNotAllowEmbedding Font license does not allow embedding 17 var ErrFontLicenseDoesNotAllowEmbedding = errors.New("Font license does not allow embedding") 18 19 // FontMaker font maker 20 type FontMaker struct { 21 results []string 22 } 23 24 // GetResults get result 25 func (f *FontMaker) GetResults() []string { 26 return f.results 27 } 28 29 // NewFontMaker new FontMaker 30 func NewFontMaker() *FontMaker { 31 return new(FontMaker) 32 } 33 34 func (f *FontMaker) MakeFont(fontpath string, mappath string, encode string, outfolderpath string) error { 35 36 encodingpath := mappath + "/" + encode + ".map" 37 38 //read font file 39 if _, err := os.Stat(fontpath); os.IsNotExist(err) { 40 return err 41 } 42 43 fileext := filepath.Ext(fontpath) 44 if strings.ToLower(fileext) != ".ttf" { 45 //now support only ttf :-P 46 return errors.New("support only ttf ") 47 } 48 49 fontmaps, err := f.LoadMap(encodingpath) 50 if err != nil { 51 return err 52 } 53 54 info, err := f.GetInfoFromTrueType(fontpath, fontmaps) 55 if err != nil { 56 return err 57 } 58 59 //zip 60 basename := filepath.Base(fontpath) 61 tmp := strings.Split(basename, ".") 62 basename = strings.Replace(tmp[0], " ", "_", -1) 63 gzfilename := basename + ".z" 64 65 var buff bytes.Buffer 66 gzipwriter := zlib.NewWriter(&buff) 67 68 fontbytes, err := ioutil.ReadFile(fontpath) 69 if err != nil { 70 return err 71 } 72 73 _, err = gzipwriter.Write(fontbytes) 74 if err != nil { 75 return err 76 } 77 gzipwriter.Close() 78 err = ioutil.WriteFile(outfolderpath+"/"+gzfilename, buff.Bytes(), 0644) 79 if err != nil { 80 return err 81 } 82 info.PushString("File", gzfilename) 83 f.results = append(f.results, fmt.Sprintf("Save Z file at %s.", outfolderpath+"/"+gzfilename)) 84 85 //Definition File 86 _, err = f.MakeDefinitionFile(f.GoStructName(basename), mappath, outfolderpath+"/"+basename+".font.go", encode, fontmaps, info) 87 if err != nil { 88 return err 89 } 90 91 return nil 92 } 93 94 func (f *FontMaker) GoStructName(name string) string { 95 goname := strings.ToUpper(name[0:1]) + name[1:] 96 return goname 97 } 98 99 func (f *FontMaker) MakeDefinitionFile(gofontname string, mappath string, exportfile string, encode string, fontmaps []FontMap, info TtfInfo) (string, error) { 100 101 fonttype := "TrueType" 102 str := "" 103 str += "package fonts //change this\n" 104 str += "import (\n" 105 str += " \"github.com/signintech/gopdf\"\n" 106 str += ")\n" 107 str += "type " + gofontname + " struct {\n" 108 str += "\tfamily string\n" 109 str += "\tfonttype string\n" 110 str += "\tname string\n" 111 str += "\tdesc []gopdf.FontDescItem\n" 112 str += "\tup int\n" 113 str += "\tut int\n" 114 str += "\tcw gopdf.FontCw\n" 115 str += "\tenc string\n" 116 str += "\tdiff string\n" 117 str += "}\n" 118 119 str += "func (me * " + gofontname + ") Init(){\n" 120 widths, err := info.GetMapIntInt64("Widths") 121 if err != nil { 122 return "", err 123 } 124 125 tmpStr, err := f.MakeWidthArray(widths) 126 if err != nil { 127 return "", err 128 } 129 str += tmpStr 130 131 tmpInt64, err := info.GetInt64("UnderlinePosition") 132 if err != nil { 133 return "", err 134 } 135 str += fmt.Sprintf("\tme.up = %d\n", tmpInt64) 136 137 tmpInt64, err = info.GetInt64("UnderlineThickness") 138 if err != nil { 139 return "", err 140 } 141 str += fmt.Sprintf("\tme.ut = %d\n", tmpInt64) 142 143 str += "\tme.fonttype = \"" + fonttype + "\"\n" 144 145 tmpStr, err = info.GetString("FontName") 146 if err != nil { 147 return "", err 148 } 149 str += fmt.Sprintf("\tme.name = \"%s\"\n", tmpStr) 150 151 str += "\tme.enc = \"" + encode + "\"\n" 152 diff, err := f.MakeFontEncoding(mappath, fontmaps) 153 if err != nil { 154 return "", err 155 } 156 if diff != "" { 157 str += "\tme.diff = \"" + diff + "\"\n" 158 } 159 160 fd, err := f.MakeFontDescriptor(info) 161 if err != nil { 162 return "", err 163 } 164 str += fd 165 166 str += "}\n" 167 168 str += "func (me * " + gofontname + ")GetType() string{\n" 169 str += "\treturn me.fonttype\n" 170 str += "}\n" 171 str += "func (me * " + gofontname + ")GetName() string{\n" 172 str += "\treturn me.name\n" 173 str += "} \n" 174 str += "func (me * " + gofontname + ")GetDesc() []gopdf.FontDescItem{\n" 175 str += "\treturn me.desc\n" 176 str += "}\n" 177 str += "func (me * " + gofontname + ")GetUp() int{\n" 178 str += "\treturn me.up\n" 179 str += "}\n" 180 str += "func (me * " + gofontname + ")GetUt() int{\n" 181 str += "\treturn me.ut\n" 182 str += "}\n" 183 str += "func (me * " + gofontname + ")GetCw() gopdf.FontCw{\n" 184 str += "\treturn me.cw\n" 185 str += "}\n" 186 str += "func (me * " + gofontname + ")GetEnc() string{\n" 187 str += "\treturn me.enc\n" 188 str += "}\n" 189 str += "func (me * " + gofontname + ")GetDiff() string {\n" 190 str += "\treturn me.diff\n" 191 str += "}\n" 192 193 str += "func (me * " + gofontname + ") GetOriginalsize() int{\n" 194 str += "\treturn 98764\n" 195 str += "}\n" 196 197 str += "func (me * " + gofontname + ") SetFamily(family string){\n" 198 str += "\tme.family = family\n" 199 str += "}\n" 200 201 str += "func (me * " + gofontname + ") GetFamily() string{\n" 202 str += "\treturn me.family\n" 203 str += "}\n" 204 205 err = ioutil.WriteFile(exportfile, []byte(str), 0666) 206 if err != nil { 207 return "", err 208 } 209 f.results = append(f.results, fmt.Sprintf("Save GO file at %s.", exportfile)) 210 return str, nil 211 } 212 213 func (f *FontMaker) MakeFontDescriptor(info TtfInfo) (string, error) { 214 215 fd := "" 216 fd = "\tme.desc = make([]gopdf.FontDescItem,8)\n" 217 218 // Ascent 219 ascender, err := info.GetInt64("Ascender") 220 if err != nil { 221 return "", err 222 } 223 fd += fmt.Sprintf("\tme.desc[0] = gopdf.FontDescItem{ Key:\"Ascent\",Val : \"%d\" }\n", ascender) 224 225 // Descent 226 descender, err := info.GetInt64("Descender") 227 if err != nil { 228 return "", err 229 } 230 fd += fmt.Sprintf("\tme.desc[1] = gopdf.FontDescItem{ Key: \"Descent\", Val : \"%d\" }\n", descender) 231 232 // CapHeight 233 capHeight, err := info.GetInt64("CapHeight") 234 if err == nil { 235 fd += fmt.Sprintf("\tme.desc[2] = gopdf.FontDescItem{ Key:\"CapHeight\", Val : \"%d\" }\n", capHeight) 236 } else if err == ERROR_NO_KEY_FOUND { 237 fd += fmt.Sprintf("\tme.desc[2] = gopdf.FontDescItem{ Key:\"CapHeight\", Val : \"%d\" }\n", ascender) 238 } else { 239 return "", err 240 } 241 242 // Flags 243 flags := 0 244 isFixedPitch, err := info.GetBool("IsFixedPitch") 245 if err != nil { 246 return "", err 247 } 248 249 if isFixedPitch { 250 flags += 1 << 0 251 } 252 flags += 1 << 5 253 italicAngle, err := info.GetInt64("ItalicAngle") 254 if italicAngle != 0 { 255 flags += 1 << 6 256 } 257 fd += fmt.Sprintf("\tme.desc[3] = gopdf.FontDescItem{ Key: \"Flags\", Val : \"%d\" }\n", flags) 258 //fmt.Printf("\n----\n") 259 // FontBBox 260 fbb, err := info.GetInt64s("FontBBox") 261 if err != nil { 262 return "", err 263 } 264 fd += fmt.Sprintf("\tme.desc[4] = gopdf.FontDescItem{ Key:\"FontBBox\", Val : \"[%d %d %d %d]\" }\n", fbb[0], fbb[1], fbb[2], fbb[3]) 265 266 // ItalicAngle 267 fd += fmt.Sprintf("\tme.desc[5] = gopdf.FontDescItem{ Key:\"ItalicAngle\", Val : \"%d\" }\n", italicAngle) 268 269 // StemV 270 stdVW, err := info.GetInt64("StdVW") 271 issetStdVW := false 272 if err == nil { 273 issetStdVW = true 274 } else if err == ERROR_NO_KEY_FOUND { 275 issetStdVW = false 276 } else { 277 return "", err 278 } 279 280 bold, err := info.GetBool("Bold") 281 if err != nil { 282 return "", err 283 } 284 285 stemv := int(0) 286 if issetStdVW { 287 stemv = stdVW 288 } else if bold { 289 stemv = 120 290 } else { 291 stemv = 70 292 } 293 fd += fmt.Sprintf("\tme.desc[6] = gopdf.FontDescItem{ Key:\"StemV\", Val : \"%d\" }\n ", stemv) 294 295 // MissingWidth 296 missingWidth, err := info.GetInt64("MissingWidth") 297 if err != nil { 298 return "", err 299 } 300 fd += fmt.Sprintf("\tme.desc[7] = gopdf.FontDescItem{ Key:\"MissingWidth\", Val : \"%d\" } \n ", missingWidth) 301 return fd, nil 302 } 303 304 func (f *FontMaker) MakeFontEncoding(mappath string, fontmaps []FontMap) (string, error) { 305 306 refpath := mappath + "/cp1252.map" 307 ref, err := f.LoadMap(refpath) 308 if err != nil { 309 return "", err 310 } 311 s := "" 312 last := 0 313 for c := 0; c <= 255; c++ { 314 if fontmaps[c].Name != ref[c].Name { 315 if c != last+1 { 316 s += fmt.Sprintf("%d ", c) 317 } 318 last = c 319 s += "/" + fontmaps[c].Name + " " 320 } 321 } 322 return strings.TrimSpace(s), nil 323 } 324 325 func (f *FontMaker) MakeWidthArray(widths map[int]int) (string, error) { 326 327 str := "\tme.cw = make(gopdf.FontCw)\n" 328 for c := 0; c <= 255; c++ { 329 str += "\tme.cw[" 330 chr := string(c) 331 if chr == "\"" { 332 str += "gopdf.ToByte(\"\\\"\")" 333 } else if chr == "\\" { 334 str += "gopdf.ToByte(\"\\\\\")" 335 } else if c >= 32 && c <= 126 { 336 str += "gopdf.ToByte(\"" + chr + "\")" 337 } else { 338 str += fmt.Sprintf("gopdf.Chr(%d)", c) 339 } 340 str += fmt.Sprintf("]=%d\n", widths[c]) 341 } 342 return str, nil 343 } 344 345 func (f *FontMaker) FileSize(path string) (int64, error) { 346 347 file, err := os.Open(path) 348 if err != nil { 349 return -1, err 350 } 351 defer file.Close() 352 353 // get the file size 354 stat, err := file.Stat() 355 if err != nil { 356 return -1, err 357 } 358 return stat.Size(), nil 359 } 360 361 func (f *FontMaker) GetInfoFromTrueType(fontpath string, fontmaps []FontMap) (TtfInfo, error) { 362 363 var parser TTFParser 364 err := parser.Parse(fontpath) 365 if err != nil { 366 return nil, err 367 } 368 369 if !parser.Embeddable { 370 return nil, ErrFontLicenseDoesNotAllowEmbedding 371 } 372 373 info := NewTtfInfo() 374 375 fileContent, err := ioutil.ReadFile(fontpath) 376 if err != nil { 377 return nil, err 378 } 379 info.PushBytes("Data", fileContent) 380 381 size, err := f.FileSize(fontpath) 382 if err != nil { 383 return nil, err 384 } 385 info.PushInt64("OriginalSize", size) 386 387 k := float64(1000.0 / float64(parser.unitsPerEm)) 388 info.PushString("FontName", parser.postScriptName) 389 info.PushBool("Bold", parser.Bold) 390 info.PushInt("ItalicAngle", parser.italicAngle) 391 info.PushBool("IsFixedPitch", parser.isFixedPitch) 392 info.PushInt("Ascender", f.MultiplyAndRound(k, parser.typoAscender)) 393 info.PushInt("Descender", f.MultiplyAndRound(k, parser.typoDescender)) 394 info.PushInt("UnderlineThickness", f.MultiplyAndRound(k, parser.underlineThickness)) 395 info.PushInt("UnderlinePosition", f.MultiplyAndRound(k, parser.underlinePosition)) 396 fontBBoxs := []int{ 397 f.MultiplyAndRound(k, parser.xMin), 398 f.MultiplyAndRound(k, parser.yMin), 399 f.MultiplyAndRound(k, parser.xMax), 400 f.MultiplyAndRound(k, parser.yMax), 401 } 402 info.PushInt64s("FontBBox", fontBBoxs) 403 info.PushInt("CapHeight", f.MultiplyAndRound(k, parser.capHeight)) 404 missingWidth := f.MultiplyAndRoundWithUInt64(k, parser.widths[0]) 405 info.PushInt("MissingWidth", missingWidth) 406 407 widths := make(map[int]int) 408 max := 256 409 c := 0 410 for c < max { 411 widths[c] = missingWidth 412 c++ 413 } 414 415 c = 0 //reset 416 for c < max { 417 if fontmaps[c].Name != ".notdef" { 418 uv := fontmaps[c].Uv 419 if val, ok := parser.chars[int(uv)]; ok { 420 w := parser.widths[val] 421 widths[c] = f.MultiplyAndRoundWithUInt64(k, w) 422 } else { 423 f.results = append(f.results, fmt.Sprintf("Warning: Character %s (%d) is missing", fontmaps[c].Name, fontmaps[c].Uv)) 424 } 425 } 426 c++ 427 } 428 info.PushMapIntInt64("Widths", widths) 429 430 return info, nil 431 } 432 433 func (f *FontMaker) MultiplyAndRoundWithUInt64(k float64, v uint) int { 434 r := k * float64(v) 435 return f.Round(r) 436 } 437 438 func (f *FontMaker) MultiplyAndRound(k float64, v int) int { 439 r := k * float64(v) 440 return f.Round(r) 441 } 442 443 func (f *FontMaker) Round(value float64) int { 444 return Round(value) 445 } 446 447 func (f *FontMaker) LoadMap(encodingpath string) ([]FontMap, error) { 448 449 if _, err := os.Stat(encodingpath); os.IsNotExist(err) { 450 return nil, err 451 } 452 453 var fontmaps []FontMap 454 i := 0 455 max := 256 456 for i < max { 457 fontmaps = append(fontmaps, FontMap{Uv: -1, Name: ".notdef"}) 458 i++ 459 } 460 461 file, err := os.Open(encodingpath) 462 if err != nil { 463 return nil, err 464 } 465 defer file.Close() 466 467 scanner := bufio.NewScanner(file) 468 scanner.Split(bufio.ScanLines) 469 for scanner.Scan() { 470 line := strings.TrimSpace(scanner.Text()) 471 e := strings.Split(line, " ") 472 strC := (e[0])[1:] 473 strUv := (e[1])[2:] 474 c, err := strconv.ParseInt(strC, 16, 0) 475 if err != nil { 476 return nil, err 477 } 478 uv, err := strconv.ParseInt(strUv, 16, 0) 479 if err != nil { 480 return nil, err 481 } 482 name := e[2] 483 //fmt.Println("strC = "+strC+"strUv = "+strUv+" c=%d , uv= %d", c, uv) 484 fontmaps[c].Name = name 485 fontmaps[c].Uv = int(uv) 486 } 487 488 return fontmaps, nil 489 }