github.com/andybalholm/giopdf@v0.0.0-20220317170119-aad9a095ad48/font.go (about)

     1  package giopdf
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"gioui.org/f32"
    10  	"github.com/andybalholm/giopdf/cff"
    11  	"github.com/andybalholm/giopdf/pdf"
    12  	"github.com/benoitkugler/textlayout/fonts"
    13  	"github.com/benoitkugler/textlayout/fonts/simpleencodings"
    14  	"github.com/benoitkugler/textlayout/fonts/truetype"
    15  	"github.com/benoitkugler/textlayout/fonts/type1"
    16  	"golang.org/x/image/math/fixed"
    17  )
    18  
    19  // A Glyph represents a character from a font. It uses a coordinate system
    20  // where the origin is at the left end of the baseline of the glyph, y
    21  // increases vertically, and the font size is one unit.
    22  type Glyph struct {
    23  	Outlines []PathElement
    24  	Width    float32
    25  }
    26  
    27  // A Font converts text strings to slices of Glyphs, so that they can be
    28  // displayed.
    29  type Font interface {
    30  	ToGlyphs(s string) []Glyph
    31  }
    32  
    33  // A SimpleFont is a font with a simple 8-bit encoding.
    34  type SimpleFont struct {
    35  	Glyphs [256]Glyph
    36  }
    37  
    38  func (f *SimpleFont) ToGlyphs(s string) []Glyph {
    39  	result := make([]Glyph, len(s))
    40  	for i := 0; i < len(s); i++ {
    41  		result[i] = f.Glyphs[s[i]]
    42  	}
    43  	return result
    44  }
    45  
    46  func scalePoint(p fixed.Point26_6, ppem fixed.Int26_6) f32.Point {
    47  	return f32.Pt(float32(p.X)/float32(ppem), -float32(p.Y)/float32(ppem))
    48  }
    49  
    50  // SimpleFontFromSFNT converts an SFNT (TrueType or OpenType) font to a
    51  // SimpleFont with the specified encoding.
    52  func SimpleFontFromSFNT(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) {
    53  	f, err := truetype.Parse(bytes.NewReader(data))
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	ppem := f.Upem()
    59  	scale := 1 / float32(ppem)
    60  	fm := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(scale, scale))
    61  
    62  	var GIDEncoding [256]fonts.GID
    63  
    64  	if enc != (simpleencodings.Encoding{}) {
    65  		for b, r := range enc.ByteToRune() {
    66  			gi, ok := f.NominalGlyph(r)
    67  			if ok {
    68  				GIDEncoding[b] = gi
    69  			}
    70  		}
    71  	} else {
    72  		// The encoding in the font dictionary was missing, so use the font's
    73  		// builtin encoding.
    74  		fp, err := truetype.NewFontParser(bytes.NewReader(data))
    75  		if err != nil {
    76  			return nil, err
    77  		}
    78  		ct, err := fp.CmapTable()
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		cmap3_0 := ct.FindSubtable(truetype.CmapID{3, 0})
    83  		if cmap3_0 != nil {
    84  			// If the font contains a (3, 0) subtable, the range of character codes must be one
    85  			// of the following: 0x0000 - 0x00FF, 0xF000 - 0xF0FF, 0xF100 - 0xF1FF, or
    86  			// 0xF200 - 0xF2FF. Depending on the range of codes, each byte from the string is
    87  			// prepended with the high byte of the range, to form a two-byte character, which
    88  			// is used to select the associated glyph description from the subtable.
    89  			iter := cmap3_0.Iter()
    90  			for iter.Next() {
    91  				code, gid := iter.Char()
    92  				switch code & 0xFF00 {
    93  				case 0x0000, 0xF000, 0xF100, 0xF200:
    94  					b := code & 0xFF
    95  					if GIDEncoding[b] == 0 {
    96  						GIDEncoding[b] = gid
    97  					}
    98  				}
    99  			}
   100  		}
   101  		cmap1_0 := ct.FindSubtable(truetype.CmapID{1, 0})
   102  		if cmap1_0 != nil {
   103  			// Otherwise, if the font contains a (1, 0) subtable, single bytes from the string are
   104  			// used to look up the associated glyph descriptions from the subtable.
   105  			iter := cmap1_0.Iter()
   106  			for iter.Next() {
   107  				code, gid := iter.Char()
   108  				if code > 255 {
   109  					continue
   110  				}
   111  				if GIDEncoding[code] == 0 {
   112  					GIDEncoding[code] = gid
   113  				}
   114  			}
   115  		}
   116  	}
   117  
   118  	simple := new(SimpleFont)
   119  
   120  	for i, gi := range GIDEncoding {
   121  		var g Glyph
   122  		g.Width = f.HorizontalAdvance(gi) * scale
   123  
   124  		gd := f.GlyphData(gi, ppem, ppem)
   125  		switch gd := gd.(type) {
   126  		case fonts.GlyphOutline:
   127  			g.Outlines = glyphOutline(gd, fm)
   128  		case fonts.GlyphSVG:
   129  			g.Outlines = glyphOutline(gd.Outline, fm)
   130  		case fonts.GlyphBitmap:
   131  			return nil, errors.New("bitmap fonts not supported")
   132  		}
   133  		simple.Glyphs[i] = g
   134  	}
   135  
   136  	return simple, nil
   137  }
   138  
   139  func getEncoding(e pdf.Value) (simpleencodings.Encoding, error) {
   140  	switch e.Kind() {
   141  	case pdf.Null:
   142  		return simpleencodings.Encoding{}, nil
   143  
   144  	case pdf.Name:
   145  		switch e.Name() {
   146  		case "MacRomanEncoding":
   147  			return simpleencodings.MacRoman, nil
   148  		case "MaxExpertEncoding":
   149  			return simpleencodings.MacExpert, nil
   150  		case "WinAnsiEncoding":
   151  			return simpleencodings.WinAnsi, nil
   152  		default:
   153  			return simpleencodings.Encoding{}, fmt.Errorf("unknown encoding: %v", e)
   154  		}
   155  
   156  	case pdf.Dict:
   157  		enc, err := getEncoding(e.Key("BaseEncoding"))
   158  		if err != nil {
   159  			return enc, err
   160  		}
   161  		diff := e.Key("Differences")
   162  		code := 0
   163  		for i := 0; i < diff.Len(); i++ {
   164  			item := diff.Index(i)
   165  			switch item.Kind() {
   166  			case pdf.Integer:
   167  				code = item.Int() - 1
   168  			case pdf.Name:
   169  				code++
   170  				enc[code] = item.Name()
   171  			}
   172  		}
   173  		return enc, nil
   174  
   175  	default:
   176  		return simpleencodings.Encoding{}, fmt.Errorf("invalid encoding: %v", e)
   177  	}
   178  }
   179  
   180  // SimpleFontFromType1 converts a Type 1 font to a SimpleFont, using the
   181  // encoding provided.
   182  func SimpleFontFromType1(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) {
   183  	f, err := type1.Parse(bytes.NewReader(data))
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	nameToGID := map[string]fonts.GID{}
   189  	for i := fonts.GID(0); ; i++ {
   190  		name := f.GlyphName(i)
   191  		if name == "" {
   192  			break
   193  		}
   194  		nameToGID[name] = i
   195  	}
   196  
   197  	for i, name := range enc {
   198  		// Fill in the blanks with the font's builtin encoding.
   199  		if name == "" {
   200  			enc[i] = f.Encoding[i]
   201  		}
   202  	}
   203  
   204  	fm := f32.NewAffine2D(f.FontMatrix[0], f.FontMatrix[2], f.FontMatrix[4], f.FontMatrix[1], f.FontMatrix[3], f.FontMatrix[5])
   205  
   206  	simple := new(SimpleFont)
   207  	for i, name := range enc {
   208  		var g Glyph
   209  		gi, ok := nameToGID[name]
   210  		if !ok {
   211  			continue
   212  		}
   213  
   214  		width := f.HorizontalAdvance(gi)
   215  		g.Width = fm.Transform(f32.Pt(width, 0)).X
   216  
   217  		gd := f.GlyphData(gi, 0, 0)
   218  		if gd == nil {
   219  			continue
   220  		}
   221  		g.Outlines = glyphOutline(gd.(fonts.GlyphOutline), fm)
   222  		simple.Glyphs[i] = g
   223  	}
   224  
   225  	return simple, nil
   226  }
   227  
   228  // SimpleFontFromCFF converts a CFF font to a SimpleFont, using the
   229  // encoding provided.
   230  func SimpleFontFromCFF(data []byte, enc simpleencodings.Encoding) (*SimpleFont, error) {
   231  	f, err := cff.Parse(bytes.NewReader(data))
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	nameToGID := map[string]fonts.GID{}
   237  	for i := fonts.GID(0); ; i++ {
   238  		name := f.GlyphName(i)
   239  		if name == "" {
   240  			break
   241  		}
   242  		nameToGID[name] = i
   243  	}
   244  
   245  	for i, name := range enc {
   246  		// Fill in the blanks with the font's builtin encoding.
   247  		if name == "" {
   248  			enc[i] = f.Encoding[i]
   249  		}
   250  	}
   251  
   252  	fm := f32.NewAffine2D(f.FontMatrix[0], f.FontMatrix[2], f.FontMatrix[4], f.FontMatrix[1], f.FontMatrix[3], f.FontMatrix[5])
   253  
   254  	simple := new(SimpleFont)
   255  	for i, name := range enc {
   256  		var g Glyph
   257  		gi, ok := nameToGID[name]
   258  		if !ok {
   259  			continue
   260  		}
   261  
   262  		gd, err := f.LoadGlyph(gi)
   263  		if err != nil {
   264  			return nil, err
   265  		}
   266  
   267  		g.Width = fm.Transform(f32.Pt(float32(gd.Width), 0)).X
   268  
   269  		g.Outlines = glyphOutline(fonts.GlyphOutline{Segments: gd.Outlines}, fm)
   270  		simple.Glyphs[i] = g
   271  	}
   272  
   273  	return simple, nil
   274  }
   275  
   276  // glyphOutline converts outline to our path format, transforming the points
   277  // with fm.
   278  func glyphOutline(outline fonts.GlyphOutline, fm f32.Affine2D) []PathElement {
   279  	var p PathBuilder
   280  	for _, seg := range outline.Segments {
   281  		p0 := fm.Transform(f32.Point(seg.Args[0]))
   282  		p1 := fm.Transform(f32.Point(seg.Args[1]))
   283  		p2 := fm.Transform(f32.Point(seg.Args[2]))
   284  		switch seg.Op {
   285  		case fonts.SegmentOpMoveTo:
   286  			p.ClosePath()
   287  			p.MoveTo(p0.X, p0.Y)
   288  		case fonts.SegmentOpLineTo:
   289  			p.LineTo(p0.X, p0.Y)
   290  		case fonts.SegmentOpQuadTo:
   291  			p.QuadraticCurveTo(p0.X, p0.Y, p1.X, p1.Y)
   292  		case fonts.SegmentOpCubeTo:
   293  			p.CurveTo(p0.X, p0.Y, p1.X, p1.Y, p2.X, p2.Y)
   294  		}
   295  	}
   296  	p.ClosePath()
   297  	return p.Path
   298  }
   299  
   300  func importPDFFont(f pdf.Font) (font Font, err error) {
   301  	enc, err := getEncoding(f.V.Key("Encoding"))
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	switch f.V.Key("Subtype").Name() {
   307  	case "TrueType":
   308  		file := f.V.Key("FontDescriptor").Key("FontFile2")
   309  		if file.IsNull() {
   310  			return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont"))
   311  		}
   312  		data, err := io.ReadAll(file.Reader())
   313  		if err != nil {
   314  			return nil, err
   315  		}
   316  		font, err = SimpleFontFromSFNT(data, enc)
   317  
   318  	case "Type1":
   319  		if f.V.Key("FontDescriptor").Key("FontFile3").Key("Subtype").Name() == "Type1C" {
   320  			file := f.V.Key("FontDescriptor").Key("FontFile3")
   321  			if file.IsNull() {
   322  				return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont"))
   323  			}
   324  			data, err := io.ReadAll(file.Reader())
   325  			if err != nil {
   326  				return nil, err
   327  			}
   328  			font, err = SimpleFontFromCFF(data, enc)
   329  		} else {
   330  			file := f.V.Key("FontDescriptor").Key("FontFile")
   331  			if file.IsNull() {
   332  				return nil, fmt.Errorf("%v does not have embedded font data", f.V.Key("BaseFont"))
   333  			}
   334  			data, err := io.ReadAll(file.Reader())
   335  			if err != nil {
   336  				return nil, err
   337  			}
   338  			font, err = SimpleFontFromType1(data, enc)
   339  		}
   340  
   341  	default:
   342  		return nil, fmt.Errorf("%v is an unsupported font type (%v)", f.V.Key("BaseFont"), f.V.Key("Subtype"))
   343  	}
   344  
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	// Glyph widths from the font dictionary override widths from the font itself.
   350  	if simple, ok := font.(*SimpleFont); ok {
   351  		firstChar := f.V.Key("FirstChar").Int()
   352  		lastChar := f.V.Key("LastChar").Int()
   353  		widths := f.V.Key("Widths")
   354  
   355  		for i := firstChar; i <= lastChar; i++ {
   356  			simple.Glyphs[i].Width = widths.Index(i-firstChar).Float32() / 1000
   357  		}
   358  	}
   359  
   360  	return font, nil
   361  }