2017-07-05 23:10:57 +00:00
|
|
|
// Parse character metrics from an AFM file to convert into a static go code declaration.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2017-07-09 22:37:05 +00:00
|
|
|
"flag"
|
|
|
|
|
2017-07-05 23:10:57 +00:00
|
|
|
pdfcommon "github.com/unidoc/unidoc/common"
|
|
|
|
"github.com/unidoc/unidoc/pdf/model/fonts"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2017-07-09 22:37:05 +00:00
|
|
|
filepath := flag.String("file", "", "AFM input file")
|
|
|
|
method := flag.String("method", "charmetrics", "charmetrics/charcodes/glyph-to-charcode")
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if len(*filepath) == 0 {
|
|
|
|
fmt.Println("Please specify an input file. Run with -h to get options.")
|
2017-07-05 23:10:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-09 22:37:05 +00:00
|
|
|
var err error
|
|
|
|
switch *method {
|
|
|
|
case "charmetrics":
|
|
|
|
err = runCharmetricsOnFile(*filepath)
|
|
|
|
case "charcodes":
|
|
|
|
err = runCharcodeToGlyphRetrievalOnFile(*filepath)
|
|
|
|
case "glyph-to-charcode":
|
|
|
|
err = runGlyphToCharcodeRetrievalOnFile(*filepath)
|
|
|
|
}
|
|
|
|
|
2017-07-05 23:10:57 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Error: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2017-07-09 22:37:05 +00:00
|
|
|
// --charmetrics to get char metric data.
|
|
|
|
// --charcodes to get charcode to glyph data
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate a glyph to charmetrics (width and height) map.
|
|
|
|
func runCharmetricsOnFile(path string) error {
|
|
|
|
metrics, err := GetCharmetricsFromAfmFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-05 23:10:57 +00:00
|
|
|
keys := []string{}
|
|
|
|
for key, _ := range metrics {
|
|
|
|
keys = append(keys, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
fmt.Printf("var xxfontCharMetrics map[string]CharMetrics = map[string]CharMetrics{\n")
|
|
|
|
for _, key := range keys {
|
|
|
|
metric := metrics[key]
|
|
|
|
fmt.Printf("\t\"%s\":\t{GlyphName:\"%s\", Wx:%f, Wy:%f},\n", key, metric.GlyphName, metric.Wx, metric.Wy)
|
|
|
|
}
|
|
|
|
fmt.Printf("}\n")
|
2017-07-09 22:37:05 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func runCharcodeToGlyphRetrievalOnFile(afmpath string) error {
|
|
|
|
charcodeToGlyphMap, err := GetCharcodeToGlyphEncodingFromAfmFile(afmpath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
keys := []int{}
|
|
|
|
for key, _ := range charcodeToGlyphMap {
|
|
|
|
keys = append(keys, int(key))
|
|
|
|
}
|
|
|
|
sort.Ints(keys)
|
|
|
|
|
|
|
|
fmt.Printf("var xxfontCharcodeToGlyphMap map[byte]string = map[byte]string{\n")
|
|
|
|
for _, key := range keys {
|
|
|
|
fmt.Printf("\t%d: \"%s\",\n", key, charcodeToGlyphMap[byte(key)])
|
|
|
|
}
|
|
|
|
fmt.Printf("}\n")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func runGlyphToCharcodeRetrievalOnFile(afmpath string) error {
|
|
|
|
charcodeToGlyphMap, err := GetCharcodeToGlyphEncodingFromAfmFile(afmpath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
keys := []int{}
|
|
|
|
for key, _ := range charcodeToGlyphMap {
|
|
|
|
keys = append(keys, int(key))
|
|
|
|
}
|
|
|
|
sort.Ints(keys)
|
|
|
|
|
|
|
|
fmt.Printf("var xxfontGlyphToCharcodeMap map[string]byte = map[string]byte ={\n")
|
|
|
|
for _, key := range keys {
|
|
|
|
fmt.Printf("\t\"%s\":\t%d,\n", charcodeToGlyphMap[byte(key)], key)
|
|
|
|
}
|
|
|
|
fmt.Printf("}\n")
|
|
|
|
|
|
|
|
return nil
|
2017-07-05 23:10:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func GetCharmetricsFromAfmFile(filename string) (map[string]fonts.CharMetrics, error) {
|
|
|
|
glyphMetricsMap := map[string]fonts.CharMetrics{}
|
|
|
|
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
readingCharMetrics := false
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
|
|
|
|
parts := strings.Split(line, " ")
|
|
|
|
if len(parts) < 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !readingCharMetrics && parts[0] == "StartCharMetrics" {
|
|
|
|
readingCharMetrics = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if readingCharMetrics && parts[0] == "EndCharMetrics" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !readingCharMetrics {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if parts[0] != "C" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
parts = strings.Split(line, ";")
|
|
|
|
metrics := fonts.CharMetrics{}
|
|
|
|
metrics.GlyphName = ""
|
|
|
|
for _, part := range parts {
|
|
|
|
cmd := strings.TrimSpace(part)
|
|
|
|
if len(cmd) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
args := strings.Split(cmd, " ")
|
|
|
|
if len(args) < 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch args[0] {
|
|
|
|
case "N":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("Failed C line: ", line)
|
|
|
|
return nil, errors.New("Invalid C line")
|
|
|
|
}
|
|
|
|
metrics.GlyphName = strings.TrimSpace(args[1])
|
|
|
|
case "WX":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("WX: Invalid number of args != 1 (%s)\n", line)
|
|
|
|
return nil, errors.New("Invalid range")
|
|
|
|
}
|
|
|
|
wx, err := strconv.ParseFloat(args[1], 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.Wx = wx
|
|
|
|
case "WY":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("WY: Invalid number of args != 1 (%s)\n", line)
|
|
|
|
return nil, errors.New("Invalid range")
|
|
|
|
}
|
|
|
|
wy, err := strconv.ParseFloat(args[1], 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.Wy = wy
|
|
|
|
case "W":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("W: Invalid number of args != 1 (%s)\n", line)
|
|
|
|
return nil, errors.New("Invalid range")
|
|
|
|
}
|
|
|
|
w, err := strconv.ParseFloat(args[1], 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
metrics.Wy = w
|
|
|
|
metrics.Wx = w
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(metrics.GlyphName) > 0 {
|
|
|
|
glyphMetricsMap[metrics.GlyphName] = metrics
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return glyphMetricsMap, nil
|
|
|
|
}
|
2017-07-09 22:37:05 +00:00
|
|
|
|
|
|
|
func GetCharcodeToGlyphEncodingFromAfmFile(filename string) (map[byte]string, error) {
|
|
|
|
charcodeToGlypMap := map[byte]string{}
|
|
|
|
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
readingCharMetrics := false
|
|
|
|
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
|
|
for scanner.Scan() {
|
|
|
|
line := scanner.Text()
|
|
|
|
|
|
|
|
parts := strings.Split(line, " ")
|
|
|
|
if len(parts) < 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !readingCharMetrics && parts[0] == "StartCharMetrics" {
|
|
|
|
readingCharMetrics = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if readingCharMetrics && parts[0] == "EndCharMetrics" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if !readingCharMetrics {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if parts[0] != "C" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
parts = strings.Split(line, ";")
|
|
|
|
var charcode int64
|
|
|
|
var glyph string
|
|
|
|
|
|
|
|
for _, part := range parts {
|
|
|
|
cmd := strings.TrimSpace(part)
|
|
|
|
if len(cmd) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
args := strings.Split(cmd, " ")
|
|
|
|
if len(args) < 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch args[0] {
|
|
|
|
case "C":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("Failed C line: %s", line)
|
|
|
|
return nil, errors.New("Invalid C line")
|
|
|
|
}
|
|
|
|
charcode, err = strconv.ParseInt(strings.TrimSpace(args[1]), 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case "N":
|
|
|
|
if len(args) != 2 {
|
|
|
|
pdfcommon.Log.Debug("Failed C line: %s", line)
|
|
|
|
return nil, errors.New("Invalid C line")
|
|
|
|
}
|
|
|
|
|
|
|
|
glyph = strings.TrimSpace(args[1])
|
|
|
|
if charcode >= 0 && charcode <= 255 {
|
|
|
|
charcodeToGlypMap[byte(charcode)] = glyph
|
|
|
|
} else {
|
|
|
|
fmt.Printf("NOT included: %d -> %s\n", charcode, glyph)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return charcodeToGlypMap, nil
|
|
|
|
}
|