summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.hgignore3
-rw-r--r--def.go175
-rw-r--r--doc.go106
-rw-r--r--font.go451
-rw-r--r--font/calligra.json1
-rw-r--r--font/calligra.ttfbin0 -> 40120 bytes
-rw-r--r--font/calligra.zbin0 -> 25609 bytes
-rw-r--r--font/courier.json1
-rw-r--r--font/courierb.json1
-rw-r--r--font/courierbi.json1
-rw-r--r--font/courieri.json1
-rw-r--r--font/cp1250.map251
-rw-r--r--font/cp1251.map255
-rw-r--r--font/cp1252.map251
-rw-r--r--font/cp1253.map239
-rw-r--r--font/cp1254.map249
-rw-r--r--font/cp1255.map233
-rw-r--r--font/cp1257.map244
-rw-r--r--font/cp1258.map247
-rw-r--r--font/cp874.map225
-rw-r--r--font/helvetica.json1
-rw-r--r--font/helveticab.json1
-rw-r--r--font/helveticabi.json1
-rw-r--r--font/helveticai.json1
-rw-r--r--font/iso-8859-1.map256
-rw-r--r--font/iso-8859-11.map248
-rw-r--r--font/iso-8859-15.map256
-rw-r--r--font/iso-8859-16.map256
-rw-r--r--font/iso-8859-2.map256
-rw-r--r--font/iso-8859-4.map256
-rw-r--r--font/iso-8859-5.map256
-rw-r--r--font/iso-8859-7.map250
-rw-r--r--font/iso-8859-9.map256
-rw-r--r--font/koi8-r.map256
-rw-r--r--font/koi8-u.map256
-rw-r--r--font/times.json1
-rw-r--r--font/timesb.json1
-rw-r--r--font/timesbi.json1
-rw-r--r--font/timesi.json1
-rw-r--r--font/zapfdingbats.json1
-rw-r--r--fpdf.go2299
-rw-r--r--fpdf_test.go627
-rw-r--r--image/fpdf.pngbin0 -> 7551 bytes
-rw-r--r--image/golang-gopher.pngbin0 -> 89846 bytes
-rw-r--r--image/logo-gray.pngbin0 -> 3438 bytes
-rw-r--r--image/logo-progressive.jpgbin0 -> 7610 bytes
-rw-r--r--image/logo-rgb.pngbin0 -> 7083 bytes
-rw-r--r--image/logo.gifbin0 -> 2767 bytes
-rw-r--r--image/logo.jpgbin0 -> 4298 bytes
-rw-r--r--image/logo.pngbin0 -> 2373 bytes
-rw-r--r--makefont/doc.go23
-rw-r--r--makefont/makefont.go63
-rw-r--r--makefont/makefont_test.go19
-rw-r--r--text/20k_c1.txt10
-rw-r--r--text/20k_c2.txt23
-rw-r--r--text/countries.txt15
-rw-r--r--ttfparser.go367
-rw-r--r--ttfparser_test.go67
-rw-r--r--util.go204
59 files changed, 9462 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..26851ce
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,3 @@
+syntax: glob
+makefont/makefont
+pdf/*.pdf
diff --git a/def.go b/def.go
new file mode 100644
index 0000000..7136dee
--- /dev/null
+++ b/def.go
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "bytes"
+)
+
+const FPDF_VERSION = "1.7"
+
+type sizeType struct {
+ wd, ht float64
+}
+
+type imageInfoType struct {
+ data []byte
+ smask []byte
+ i int
+ n int
+ w float64
+ h float64
+ cs string
+ pal []byte
+ bpc int
+ f string
+ dp string
+ trns []int
+}
+
+type fontFileType struct {
+ length1, length2 int64
+ n int
+}
+
+type linkType struct {
+ x, y, wd, ht float64
+ link int // Auto-generated link ID or...
+ linkStr string // ...application-provided link string
+}
+
+type intLinkType struct {
+ page int
+ y float64
+}
+
+type Fpdf struct {
+ page int // current page number
+ n int // current object number
+ offsets []int // array of object offsets
+ buffer fmtBuffer // buffer holding in-memory PDF
+ pages []*bytes.Buffer // slice[page] of page content; 1-based
+ state int // current document state
+ compress bool // compression flag
+ k float64 // scale factor (number of points in user unit)
+ defOrientation string // default orientation
+ curOrientation string // current orientation
+ stdpageSizes map[string]sizeType // standard page sizes
+ defPageSize sizeType // default page size
+ curPageSize sizeType // current page size
+ pageSizes map[int]sizeType // used for pages with non default sizes or orientations
+ wPt, hPt float64 // dimensions of current page in points
+ w, h float64 // dimensions of current page in user unit
+ lMargin float64 // left margin
+ tMargin float64 // top margin
+ rMargin float64 // right margin
+ bMargin float64 // page break margin
+ cMargin float64 // cell margin
+ x, y float64 // current position in user unit
+ lasth float64 // height of last printed cell
+ lineWidth float64 // line width in user unit
+ fontpath string // path containing fonts
+ coreFonts map[string]bool // array of core font names
+ fonts map[string]fontDefType // array of used fonts
+ fontFiles map[string]fontFileType // array of font files
+ diffs []string // array of encoding differences
+ fontFamily string // current font family
+ fontStyle string // current font style
+ underline bool // underlining flag
+ currentFont fontDefType // current font info
+ fontSizePt float64 // current font size in points
+ fontSize float64 // current font size in user unit
+ drawColor string // commands for drawing color
+ fillColor string // commands for filling color
+ textColor string // commands for text color
+ colorFlag bool // indicates whether fill and text colors are different
+ ws float64 // word spacing
+ images map[string]imageInfoType // array of used images
+ pageLinks [][]linkType // pageLinks[page][link], both 1-based
+ links []intLinkType // array of internal links
+ autoPageBreak bool // automatic page breaking
+ acceptPageBreak func() bool // returns true to accept page break
+ pageBreakTrigger float64 // threshold used to trigger page breaks
+ inHeader bool // flag set when processing header
+ headerFnc func() // function provided by app and called to write header
+ inFooter bool // flag set when processing footer
+ footerFnc func() // function provided by app and called to write footer
+ zoomMode string // zoom display mode
+ layoutMode string // layout display mode
+ title string // title
+ subject string // subject
+ author string // author
+ keywords string // keywords
+ creator string // creator
+ aliasNbPagesStr string // alias for total number of pages
+ pdfVersion string // PDF version number
+ fontDirStr string // location of font definition files
+ err error // Set if error occurs during life cycle of instance
+}
+
+type encType struct {
+ uv int
+ name string
+}
+
+type encListType [256]encType
+
+type fontBoxType struct {
+ Xmin, Ymin, Xmax, Ymax int
+}
+
+type fontDescType struct {
+ Ascent int
+ Descent int
+ CapHeight int
+ Flags int
+ FontBBox fontBoxType
+ ItalicAngle int
+ StemV int
+ MissingWidth int
+}
+
+type fontDefType struct {
+ Tp string // "Core", "TrueType", ...
+ Name string // "Courier-Bold", ...
+ Desc fontDescType // Font descriptor
+ Up int // Underline position
+ Ut int // Underline thickness
+ Cw [256]int // Character width by ordinal
+ Enc string // "cp1252", ...
+ Diff string // Differences from reference encoding
+ File string // "Redressed.z"
+ Size1, Size2 int // Type1 values
+ OriginalSize int // Size of uncompressed font file
+ I int // 1-based position in font list, set by font loader, not this program
+ N int // Set by font loader
+ DiffN int // Position of diff in app array, set by font loader
+}
+
+type fontInfoType struct {
+ Data []byte
+ File string
+ OriginalSize int
+ FontName string
+ Bold bool
+ IsFixedPitch bool
+ UnderlineThickness int
+ UnderlinePosition int
+ Widths [256]int
+ Size1, Size2 uint32
+ Desc fontDescType
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..a95a7e0
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Package gofpdf implements a PDF document generator.
+
+This package's code and documentation are closely derived from the FPDF library
+created by Olivier Plathey, and a number of font and image resources are copied
+directly from it. The FPDF website is http://www.fpdf.org/.
+
+Features
+
+• Choice of measurement unit, page format and margins
+
+• Page header and footer management
+
+• Automatic page break
+
+• Automatic line break and text justification
+
+• Image support (JPEG, PNG and GIF)
+
+• Colors
+
+• Links
+
+• TrueType, Type1 and encoding support
+
+• Page compression
+
+gofpdf has no dependencies other than the Go standard library. All tests pass
+on Linux, Mac and Windows platforms. Like FPDF version 1.7, from which gofpdf
+is derived, this package does not yet support UTF-8 source text.
+
+Quick Start
+
+The following Go code generates a simple PDF.
+
+ pdf := fpdf.New("P", "mm", "A4", "../font")
+ pdf.AddPage()
+ pdf.SetFont("Arial", "B", 16)
+ pdf.Cell(40, 10, "Hello, world")
+ pdf.Output(os.Stdout)
+
+See the tutorials in the fpdf_test.go file (shown as examples in this
+documentation) for more advanced PDF examples.
+
+Errors
+
+If an error occurs in an Fpdf method, an internal error field is set. After
+this occurs, Fpdf method calls typically return without performing any
+operations and the error state is retained. This error management scheme
+facilitates PDF generation since individual method calls do not need to be
+examined for failure; it is generally sufficient to wait until after Output()
+is called. For the same reason, if an error occurs in the calling application
+during PDF generation, it may be desirable for the application to transfer the
+error to the Fpdf instance by calling the SetError() method or the SetErrorf()
+method. At any time during the life cycle of the Fpdf instance, the error state
+can be determined with a call to Ok() or Err(). The error itself can be
+retrieved with a call to Error().
+
+Conversion Notes
+
+This package is a relatively straightforward translation from the original FPDF
+library written in PHP (despite the caveat in the introduction to Effective
+Go). The API names have been retained even though the Go idiom would suggest
+otherwise (for example, pdf.GetX() is used rather than simply pdf.X()). The
+similarity of the two libraries makes the original FPDF website a good source
+of information. It includes a forum and FAQ.
+
+However, some internal changes have been made. Page content is built up using
+buffers (of type bytes.Buffer) rather than repeated string concatenation.
+Errors are handled as explained above rather than panicking. Output is
+generated through an interface of type io.WriteCloser. A number of the original
+PHP methods behave differently based on the type of the arguments that are
+passed to them; in these cases additional methods have been exported to provide
+similar functionality. Font definition files are produced in JSON rather than
+PHP.
+
+Tutorials
+
+A side effect of running "go test" in the fpdf directory is the production of
+the tutorial PDFs. These can be found in the gofpdf/pdf directory after the
+tests complete.
+
+Roadmap
+
+• Handle UTF-8 source text
+
+• Improve test coverage as reported by gocov (https://github.com/axw/gocov‎)
+
+*/
+package gofpdf
diff --git a/font.go b/font.go
new file mode 100644
index 0000000..f5cf8c1
--- /dev/null
+++ b/font.go
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+// Utility to generate font definition files
+
+// Version: 1.2
+// Date: 2011-06-18
+// Author: Olivier PLATHEY
+// Port to Go: Kurt Jung, 2013-07-15
+
+import (
+ "bufio"
+ "compress/zlib"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+func baseNoExt(fileStr string) string {
+ str := filepath.Base(fileStr)
+ extLen := len(filepath.Ext(str))
+ if extLen > 0 {
+ str = str[:len(str)-extLen]
+ }
+ return str
+}
+
+func loadMap(encodingFileStr string) (encList encListType, err error) {
+ // printf("Encoding file string [%s]\n", encodingFileStr)
+ var f *os.File
+ // f, err = os.Open(encodingFilepath(encodingFileStr))
+ f, err = os.Open(encodingFileStr)
+ if err == nil {
+ defer f.Close()
+ for j, _ := range encList {
+ encList[j].uv = -1
+ encList[j].name = ".notdef"
+ }
+ scanner := bufio.NewScanner(f)
+ var enc encType
+ var pos int
+ for scanner.Scan() {
+ // "!3F U+003F question"
+ _, err = fmt.Sscanf(scanner.Text(), "!%x U+%x %s", &pos, &enc.uv, &enc.name)
+ if err == nil {
+ if pos < 256 {
+ encList[pos] = enc
+ } else {
+ err = fmt.Errorf("Map position 0x%2X exceeds 0xFF", pos)
+ return
+ }
+ } else {
+ return
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ return
+ }
+ }
+ return
+}
+
+// Return informations from a TrueType font
+func getInfoFromTrueType(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
+ var ttf TtfType
+ ttf, err = TtfParse(fileStr)
+ if err != nil {
+ return
+ }
+ if embed {
+ if !ttf.Embeddable {
+ err = fmt.Errorf("Font license does not allow embedding")
+ return
+ }
+ info.Data, err = ioutil.ReadFile(fileStr)
+ if err != nil {
+ return
+ }
+ info.OriginalSize = len(info.Data)
+ }
+ k := 1000.0 / float64(ttf.UnitsPerEm)
+ info.FontName = ttf.PostScriptName
+ info.Bold = ttf.Bold
+ info.Desc.ItalicAngle = int(ttf.ItalicAngle)
+ info.IsFixedPitch = ttf.IsFixedPitch
+ info.Desc.Ascent = round(k * float64(ttf.TypoAscender))
+ info.Desc.Descent = round(k * float64(ttf.TypoDescender))
+ info.UnderlineThickness = round(k * float64(ttf.UnderlineThickness))
+ info.UnderlinePosition = round(k * float64(ttf.UnderlinePosition))
+ info.Desc.FontBBox = fontBoxType{
+ round(k * float64(ttf.Xmin)),
+ round(k * float64(ttf.Ymin)),
+ round(k * float64(ttf.Xmax)),
+ round(k * float64(ttf.Ymax)),
+ }
+ // printf("FontBBox\n")
+ // dump(info.Desc.FontBBox)
+ info.Desc.CapHeight = round(k * float64(ttf.CapHeight))
+ info.Desc.MissingWidth = round(k * float64(ttf.Widths[0]))
+ var wd int
+ for j := 0; j < len(info.Widths); j++ {
+ wd = info.Desc.MissingWidth
+ if encList[j].name != ".notdef" {
+ uv := encList[j].uv
+ pos, ok := ttf.Chars[uint16(uv)]
+ if ok {
+ wd = round(k * float64(ttf.Widths[pos]))
+ } else {
+ fmt.Fprintf(msgWriter, "Character %s is missing\n", encList[j].name)
+ }
+ }
+ info.Widths[j] = wd
+ }
+ // printf("getInfoFromTrueType/FontBBox\n")
+ // dump(info.Desc.FontBBox)
+ return
+}
+
+type segmentType struct {
+ marker uint8
+ tp uint8
+ size uint32
+ data []byte
+}
+
+func segmentRead(f *os.File) (s segmentType, err error) {
+ if err = binary.Read(f, binary.LittleEndian, &s.marker); err != nil {
+ return
+ }
+ if s.marker != 128 {
+ err = fmt.Errorf("Font file is not a valid binary Type1")
+ return
+ }
+ if err = binary.Read(f, binary.LittleEndian, &s.tp); err != nil {
+ return
+ }
+ if err = binary.Read(f, binary.LittleEndian, &s.size); err != nil {
+ return
+ }
+ s.data = make([]byte, s.size)
+ _, err = f.Read(s.data)
+ return
+}
+
+// -rw-r--r-- 1 root root 9532 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.afm
+// -rw-r--r-- 1 root root 37744 2010-04-22 11:27 /usr/share/fonts/type1/mathml/Symbol.pfb
+
+// Return informations from a Type1 font
+func getInfoFromType1(fileStr string, msgWriter io.Writer, embed bool, encList encListType) (info fontInfoType, err error) {
+ if embed {
+ var f *os.File
+ f, err = os.Open(fileStr)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ // Read first segment
+ var s1, s2 segmentType
+ s1, err = segmentRead(f)
+ if err != nil {
+ return
+ }
+ s2, err = segmentRead(f)
+ if err != nil {
+ return
+ }
+ info.Data = s1.data
+ info.Data = append(info.Data, s2.data...)
+ info.Size1 = s1.size
+ info.Size2 = s2.size
+ }
+ afmFileStr := fileStr[0:len(fileStr)-3] + "afm"
+ size, ok := fileSize(afmFileStr)
+ if !ok {
+ err = fmt.Errorf("AFM font file %s not found", afmFileStr)
+ return
+ } else if size == 0 {
+ err = fmt.Errorf("AFM font file %s empty or not readable", afmFileStr)
+ return
+ }
+ var f *os.File
+ f, err = os.Open(afmFileStr)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ scanner := bufio.NewScanner(f)
+ var fields []string
+ var wd int
+ var wt, name string
+ wdMap := make(map[string]int)
+ for scanner.Scan() {
+ fields = strings.Fields(strings.TrimSpace(scanner.Text()))
+ // Comment Generated by FontForge 20080203
+ // FontName Symbol
+ // C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+ if len(fields) >= 2 {
+ switch fields[0] {
+ case "C":
+ if wd, err = strconv.Atoi(fields[4]); err == nil {
+ name = fields[7]
+ wdMap[name] = wd
+ }
+ case "FontName":
+ info.FontName = fields[1]
+ case "Weight":
+ wt = strings.ToLower(fields[1])
+ case "ItalicAngle":
+ info.Desc.ItalicAngle, err = strconv.Atoi(fields[1])
+ case "Ascender":
+ info.Desc.Ascent, err = strconv.Atoi(fields[1])
+ case "Descender":
+ info.Desc.Descent, err = strconv.Atoi(fields[1])
+ case "UnderlineThickness":
+ info.UnderlineThickness, err = strconv.Atoi(fields[1])
+ case "UnderlinePosition":
+ info.UnderlinePosition, err = strconv.Atoi(fields[1])
+ case "IsFixedPitch":
+ info.IsFixedPitch = fields[1] == "true"
+ case "FontBBox":
+ if info.Desc.FontBBox.Xmin, err = strconv.Atoi(fields[1]); err == nil {
+ if info.Desc.FontBBox.Ymin, err = strconv.Atoi(fields[2]); err == nil {
+ if info.Desc.FontBBox.Xmax, err = strconv.Atoi(fields[3]); err == nil {
+ info.Desc.FontBBox.Ymax, err = strconv.Atoi(fields[4])
+ }
+ }
+ }
+ case "CapHeight":
+ info.Desc.CapHeight, err = strconv.Atoi(fields[1])
+ case "StdVW":
+ info.Desc.StemV, err = strconv.Atoi(fields[1])
+ }
+ }
+ if err != nil {
+ return
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ return
+ }
+ if info.FontName == "" {
+ err = fmt.Errorf("FontName missing in AFM file %s", afmFileStr)
+ return
+ }
+ info.Bold = wt == "bold" || wt == "black"
+ var missingWd int
+ missingWd, ok = wdMap[".notdef"]
+ if ok {
+ info.Desc.MissingWidth = missingWd
+ }
+ for j := 0; j < len(info.Widths); j++ {
+ info.Widths[j] = info.Desc.MissingWidth
+ }
+ for j := 0; j < len(info.Widths); j++ {
+ name = encList[j].name
+ if name != ".notdef" {
+ wd, ok = wdMap[name]
+ if ok {
+ info.Widths[j] = wd
+ } else {
+ fmt.Fprintf(msgWriter, "Character %s is missing\n", name)
+ }
+ }
+ }
+ // printf("getInfoFromType1/FontBBox\n")
+ // dump(info.Desc.FontBBox)
+ return
+}
+
+func makeFontDescriptor(info *fontInfoType) {
+ if info.Desc.CapHeight == 0 {
+ info.Desc.CapHeight = info.Desc.Ascent
+ }
+ info.Desc.Flags = 1 << 5
+ if info.IsFixedPitch {
+ info.Desc.Flags |= 1
+ }
+ if info.Desc.ItalicAngle != 0 {
+ info.Desc.Flags |= 1 << 6
+ }
+ if info.Desc.StemV == 0 {
+ if info.Bold {
+ info.Desc.StemV = 120
+ } else {
+ info.Desc.StemV = 70
+ }
+ }
+ // printf("makeFontDescriptor/FontBBox\n")
+ // dump(info.Desc.FontBBox)
+}
+
+// Build differences from reference encoding
+func makeFontEncoding(encList encListType, refEncFileStr string) (diffStr string, err error) {
+ var refList encListType
+ if refList, err = loadMap(refEncFileStr); err != nil {
+ return
+ }
+ var buf fmtBuffer
+ last := 0
+ for j := 32; j < 256; j++ {
+ if encList[j].name != refList[j].name {
+ if j != last+1 {
+ buf.printf("%d ", j)
+ }
+ last = j
+ buf.printf("/%s ", encList[j].name)
+ }
+ }
+ diffStr = strings.TrimSpace(buf.String())
+ return
+}
+
+func makeDefinitionFile(fileStr, tpStr, encodingFileStr string, embed bool, encList encListType, info fontInfoType) (err error) {
+ var def fontDefType
+ def.Tp = tpStr
+ def.Name = info.FontName
+ makeFontDescriptor(&info)
+ def.Desc = info.Desc
+ // printf("makeDefinitionFile/FontBBox\n")
+ // dump(def.Desc.FontBBox)
+ def.Up = info.UnderlinePosition
+ def.Ut = info.UnderlineThickness
+ def.Cw = info.Widths
+ def.Enc = baseNoExt(encodingFileStr)
+ // fmt.Printf("encodingFileStr [%s], def.Enc [%s]\n", encodingFileStr, def.Enc)
+ // fmt.Printf("reference [%s]\n", filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
+ def.Diff, err = makeFontEncoding(encList, filepath.Join(filepath.Dir(encodingFileStr), "cp1252.map"))
+ if err != nil {
+ return
+ }
+ def.File = info.File
+ def.Size1 = int(info.Size1)
+ def.Size2 = int(info.Size2)
+ def.OriginalSize = info.OriginalSize
+ // printf("Font definition file [%s]\n", fileStr)
+ var buf []byte
+ buf, err = json.Marshal(def)
+ if err != nil {
+ return
+ }
+ var f *os.File
+ f, err = os.Create(fileStr)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ f.Write(buf)
+ return
+}
+
+// Generate a font definition file in JSON format. A definition file of this
+// type is required to use non-core fonts in the PDF documents that gofpdf
+// generates. See the makefont utility in the gofpdf package for a command line
+// interface to this function.
+//
+// fontFileStr is the name of the TrueType (or OpenType based on TrueType) or
+// Type1 file from which to generate a definition file.
+//
+// encodingFileStr is the name of the encoding file that corresponds to the
+// font.
+//
+// dstDirStr is the name of the directory in which to save the definition file
+// and, if embed is true, the compressed font file.
+//
+// msgWriter is the writer that is called to display messages throughout the
+// process. Use nil to turn off messages.
+//
+// embed is true if the font is to be embedded in the PDF files.
+func MakeFont(fontFileStr, encodingFileStr, dstDirStr string, msgWriter io.Writer, embed bool) (err error) {
+ if msgWriter == nil {
+ msgWriter = ioutil.Discard
+ }
+ if !fileExist(fontFileStr) {
+ err = fmt.Errorf("Font file not found: %s", fontFileStr)
+ return
+ }
+ extStr := strings.ToLower(fontFileStr[len(fontFileStr)-3:])
+ // printf("Font file extension [%s]\n", extStr)
+ var tpStr string
+ if extStr == "ttf" || extStr == "otf" {
+ tpStr = "TrueType"
+ } else if extStr == "pfb" {
+ tpStr = "Type1"
+ } else {
+ err = fmt.Errorf("Unrecognized font file extension: %s", extStr)
+ return
+ }
+ var encList encListType
+ var info fontInfoType
+ encList, err = loadMap(encodingFileStr)
+ if err != nil {
+ return
+ }
+ // printf("Encoding table\n")
+ // dump(encList)
+ if tpStr == "TrueType" {
+ info, err = getInfoFromTrueType(fontFileStr, msgWriter, embed, encList)
+ if err != nil {
+ return
+ }
+ } else {
+ info, err = getInfoFromType1(fontFileStr, msgWriter, embed, encList)
+ if err != nil {
+ return
+ }
+ }
+ baseStr := baseNoExt(fontFileStr)
+ // fmt.Printf("Base [%s]\n", baseStr)
+ if embed {
+ var f *os.File
+ info.File = baseStr + ".z"
+ zFileStr := filepath.Join(dstDirStr, info.File)
+ f, err = os.Create(zFileStr)
+ if err != nil {
+ return
+ }
+ defer f.Close()
+ cmp := zlib.NewWriter(f)
+ cmp.Write(info.Data)
+ cmp.Close()
+ fmt.Fprintf(msgWriter, "Font file compressed: %s\n", zFileStr)
+ }
+ defFileStr := filepath.Join(dstDirStr, baseStr+".json")
+ err = makeDefinitionFile(defFileStr, tpStr, encodingFileStr, embed, encList, info)
+ if err != nil {
+ return
+ }
+ fmt.Fprintf(msgWriter, "Font definition file successfully generated: %s\n", defFileStr)
+ return
+}
diff --git a/font/calligra.json b/font/calligra.json
new file mode 100644
index 0000000..b968074
--- /dev/null
+++ b/font/calligra.json
@@ -0,0 +1 @@
+{"Tp":"TrueType","Name":"CalligrapherRegular","Desc":{"Ascent":899,"Descent":-234,"CapHeight":899,"Flags":32,"FontBBox":{"Xmin":-173,"Ymin":-234,"Xmax":1328,"Ymax":899},"ItalicAngle":0,"StemV":70,"MissingWidth":800},"Up":-200,"Ut":20,"Cw":[800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,800,282,324,405,584,632,980,776,259,299,299,377,600,259,432,254,597,529,298,451,359,525,423,464,417,457,479,275,282,600,600,600,501,800,743,636,598,712,608,562,680,756,308,314,676,552,1041,817,729,569,698,674,618,673,805,753,1238,716,754,599,315,463,315,600,547,278,581,564,440,571,450,347,628,611,283,283,560,252,976,595,508,549,540,395,441,307,614,556,915,559,597,452,315,222,315,600,800,800,800,0,0,0,780,0,0,278,0,0,0,1064,800,0,800,800,259,259,470,470,500,300,600,278,990,0,0,790,800,800,754,282,324,450,640,518,603,0,519,254,800,349,0,0,432,800,278,0,0,0,0,278,614,0,254,278,0,305,0,0,0,0,501,743,743,743,743,743,743,1060,598,608,608,608,608,308,308,308,308,0,817,729,729,729,729,729,0,729,805,805,805,805,0,0,688,581,581,581,581,581,581,792,440,450,450,450,450,283,283,283,283,0,595,508,508,508,508,508,0,508,614,614,614,614,0,0,597],"Enc":"cp1252","Diff":"","File":"calligra.z","Size1":0,"Size2":0,"OriginalSize":40120,"I":0,"N":0,"DiffN":0} \ No newline at end of file
diff --git a/font/calligra.ttf b/font/calligra.ttf
new file mode 100644
index 0000000..9713c46
--- /dev/null
+++ b/font/calligra.ttf
Binary files differ
diff --git a/font/calligra.z b/font/calligra.z
new file mode 100644
index 0000000..c64353f
--- /dev/null
+++ b/font/calligra.z
Binary files differ
diff --git a/font/courier.json b/font/courier.json
new file mode 100644
index 0000000..d979da5
--- /dev/null
+++ b/font/courier.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Courier","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} \ No newline at end of file
diff --git a/font/courierb.json b/font/courierb.json
new file mode 100644
index 0000000..ab15c59
--- /dev/null
+++ b/font/courierb.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Courier-Bold","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} \ No newline at end of file
diff --git a/font/courierbi.json b/font/courierbi.json
new file mode 100644
index 0000000..535b2a2
--- /dev/null
+++ b/font/courierbi.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Courier-BoldOblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} \ No newline at end of file
diff --git a/font/courieri.json b/font/courieri.json
new file mode 100644
index 0000000..9624d6e
--- /dev/null
+++ b/font/courieri.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Courier-Oblique","Up":-100,"Ut":50,"I":256,"Cw":[600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600]} \ No newline at end of file
diff --git a/font/cp1250.map b/font/cp1250.map
new file mode 100644
index 0000000..ec110af
--- /dev/null
+++ b/font/cp1250.map
@@ -0,0 +1,251 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+015A Sacute
+!8D U+0164 Tcaron
+!8E U+017D Zcaron
+!8F U+0179 Zacute
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+015B sacute
+!9D U+0165 tcaron
+!9E U+017E zcaron
+!9F U+017A zacute
+!A0 U+00A0 space
+!A1 U+02C7 caron
+!A2 U+02D8 breve
+!A3 U+0141 Lslash
+!A4 U+00A4 currency
+!A5 U+0104 Aogonek
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+015E Scedilla
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+02DB ogonek
+!B3 U+0142 lslash
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+0105 aogonek
+!BA U+015F scedilla
+!BB U+00BB guillemotright
+!BC U+013D Lcaron
+!BD U+02DD hungarumlaut
+!BE U+013E lcaron
+!BF U+017C zdotaccent
+!C0 U+0154 Racute
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0139 Lacute
+!C6 U+0106 Cacute
+!C7 U+00C7 Ccedilla
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+011A Ecaron
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+010E Dcaron
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+0147 Ncaron
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0158 Rcaron
+!D9 U+016E Uring
+!DA U+00DA Uacute
+!DB U+0170 Uhungarumlaut
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+0162 Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+0155 racute
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+013A lacute
+!E6 U+0107 cacute
+!E7 U+00E7 ccedilla
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+011B ecaron
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+010F dcaron
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+0148 ncaron
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0159 rcaron
+!F9 U+016F uring
+!FA U+00FA uacute
+!FB U+0171 uhungarumlaut
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+0163 tcommaaccent
+!FF U+02D9 dotaccent
diff --git a/font/cp1251.map b/font/cp1251.map
new file mode 100644
index 0000000..de6a198
--- /dev/null
+++ b/font/cp1251.map
@@ -0,0 +1,255 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0402 afii10051
+!81 U+0403 afii10052
+!82 U+201A quotesinglbase
+!83 U+0453 afii10100
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+20AC Euro
+!89 U+2030 perthousand
+!8A U+0409 afii10058
+!8B U+2039 guilsinglleft
+!8C U+040A afii10059
+!8D U+040C afii10061
+!8E U+040B afii10060
+!8F U+040F afii10145
+!90 U+0452 afii10099
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9A U+0459 afii10106
+!9B U+203A guilsinglright
+!9C U+045A afii10107
+!9D U+045C afii10109
+!9E U+045B afii10108
+!9F U+045F afii10193
+!A0 U+00A0 space
+!A1 U+040E afii10062
+!A2 U+045E afii10110
+!A3 U+0408 afii10057
+!A4 U+00A4 currency
+!A5 U+0490 afii10050
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+0401 afii10023
+!A9 U+00A9 copyright
+!AA U+0404 afii10053
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+0407 afii10056
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+0406 afii10055
+!B3 U+0456 afii10103
+!B4 U+0491 afii10098
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+0451 afii10071
+!B9 U+2116 afii61352
+!BA U+0454 afii10101
+!BB U+00BB guillemotright
+!BC U+0458 afii10105
+!BD U+0405 afii10054
+!BE U+0455 afii10102
+!BF U+0457 afii10104
+!C0 U+0410 afii10017
+!C1 U+0411 afii10018
+!C2 U+0412 afii10019
+!C3 U+0413 afii10020
+!C4 U+0414 afii10021
+!C5 U+0415 afii10022
+!C6 U+0416 afii10024
+!C7 U+0417 afii10025
+!C8 U+0418 afii10026
+!C9 U+0419 afii10027
+!CA U+041A afii10028
+!CB U+041B afii10029
+!CC U+041C afii10030
+!CD U+041D afii10031
+!CE U+041E afii10032
+!CF U+041F afii10033
+!D0 U+0420 afii10034
+!D1 U+0421 afii10035
+!D2 U+0422 afii10036
+!D3 U+0423 afii10037
+!D4 U+0424 afii10038
+!D5 U+0425 afii10039
+!D6 U+0426 afii10040
+!D7 U+0427 afii10041
+!D8 U+0428 afii10042
+!D9 U+0429 afii10043
+!DA U+042A afii10044
+!DB U+042B afii10045
+!DC U+042C afii10046
+!DD U+042D afii10047
+!DE U+042E afii10048
+!DF U+042F afii10049
+!E0 U+0430 afii10065
+!E1 U+0431 afii10066
+!E2 U+0432 afii10067
+!E3 U+0433 afii10068
+!E4 U+0434 afii10069
+!E5 U+0435 afii10070
+!E6 U+0436 afii10072
+!E7 U+0437 afii10073
+!E8 U+0438 afii10074
+!E9 U+0439 afii10075
+!EA U+043A afii10076
+!EB U+043B afii10077
+!EC U+043C afii10078
+!ED U+043D afii10079
+!EE U+043E afii10080
+!EF U+043F afii10081
+!F0 U+0440 afii10082
+!F1 U+0441 afii10083
+!F2 U+0442 afii10084
+!F3 U+0443 afii10085
+!F4 U+0444 afii10086
+!F5 U+0445 afii10087
+!F6 U+0446 afii10088
+!F7 U+0447 afii10089
+!F8 U+0448 afii10090
+!F9 U+0449 afii10091
+!FA U+044A afii10092
+!FB U+044B afii10093
+!FC U+044C afii10094
+!FD U+044D afii10095
+!FE U+044E afii10096
+!FF U+044F afii10097
diff --git a/font/cp1252.map b/font/cp1252.map
new file mode 100644
index 0000000..dd490e5
--- /dev/null
+++ b/font/cp1252.map
@@ -0,0 +1,251 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!8E U+017D Zcaron
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9E U+017E zcaron
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/font/cp1253.map b/font/cp1253.map
new file mode 100644
index 0000000..4bd826f
--- /dev/null
+++ b/font/cp1253.map
@@ -0,0 +1,239 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!A0 U+00A0 space
+!A1 U+0385 dieresistonos
+!A2 U+0386 Alphatonos
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+2015 afii00208
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+0384 tonos
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+0388 Epsilontonos
+!B9 U+0389 Etatonos
+!BA U+038A Iotatonos
+!BB U+00BB guillemotright
+!BC U+038C Omicrontonos
+!BD U+00BD onehalf
+!BE U+038E Upsilontonos
+!BF U+038F Omegatonos
+!C0 U+0390 iotadieresistonos
+!C1 U+0391 Alpha
+!C2 U+0392 Beta
+!C3 U+0393 Gamma
+!C4 U+0394 Delta
+!C5 U+0395 Epsilon
+!C6 U+0396 Zeta
+!C7 U+0397 Eta
+!C8 U+0398 Theta
+!C9 U+0399 Iota
+!CA U+039A Kappa
+!CB U+039B Lambda
+!CC U+039C Mu
+!CD U+039D Nu
+!CE U+039E Xi
+!CF U+039F Omicron
+!D0 U+03A0 Pi
+!D1 U+03A1 Rho
+!D3 U+03A3 Sigma
+!D4 U+03A4 Tau
+!D5 U+03A5 Upsilon
+!D6 U+03A6 Phi
+!D7 U+03A7 Chi
+!D8 U+03A8 Psi
+!D9 U+03A9 Omega
+!DA U+03AA Iotadieresis
+!DB U+03AB Upsilondieresis
+!DC U+03AC alphatonos
+!DD U+03AD epsilontonos
+!DE U+03AE etatonos
+!DF U+03AF iotatonos
+!E0 U+03B0 upsilondieresistonos
+!E1 U+03B1 alpha
+!E2 U+03B2 beta
+!E3 U+03B3 gamma
+!E4 U+03B4 delta
+!E5 U+03B5 epsilon
+!E6 U+03B6 zeta
+!E7 U+03B7 eta
+!E8 U+03B8 theta
+!E9 U+03B9 iota
+!EA U+03BA kappa
+!EB U+03BB lambda
+!EC U+03BC mu
+!ED U+03BD nu
+!EE U+03BE xi
+!EF U+03BF omicron
+!F0 U+03C0 pi
+!F1 U+03C1 rho
+!F2 U+03C2 sigma1
+!F3 U+03C3 sigma
+!F4 U+03C4 tau
+!F5 U+03C5 upsilon
+!F6 U+03C6 phi
+!F7 U+03C7 chi
+!F8 U+03C8 psi
+!F9 U+03C9 omega
+!FA U+03CA iotadieresis
+!FB U+03CB upsilondieresis
+!FC U+03CC omicrontonos
+!FD U+03CD upsilontonos
+!FE U+03CE omegatonos
diff --git a/font/cp1254.map b/font/cp1254.map
new file mode 100644
index 0000000..829473b
--- /dev/null
+++ b/font/cp1254.map
@@ -0,0 +1,249 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8A U+0160 Scaron
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9A U+0161 scaron
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+011E Gbreve
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0130 Idotaccent
+!DE U+015E Scedilla
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+011F gbreve
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0131 dotlessi
+!FE U+015F scedilla
+!FF U+00FF ydieresis
diff --git a/font/cp1255.map b/font/cp1255.map
new file mode 100644
index 0000000..079e10c
--- /dev/null
+++ b/font/cp1255.map
@@ -0,0 +1,233 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+20AA afii57636
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00D7 multiply
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD sfthyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 middot
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00F7 divide
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+05B0 afii57799
+!C1 U+05B1 afii57801
+!C2 U+05B2 afii57800
+!C3 U+05B3 afii57802
+!C4 U+05B4 afii57793
+!C5 U+05B5 afii57794
+!C6 U+05B6 afii57795
+!C7 U+05B7 afii57798
+!C8 U+05B8 afii57797
+!C9 U+05B9 afii57806
+!CB U+05BB afii57796
+!CC U+05BC afii57807
+!CD U+05BD afii57839
+!CE U+05BE afii57645
+!CF U+05BF afii57841
+!D0 U+05C0 afii57842
+!D1 U+05C1 afii57804
+!D2 U+05C2 afii57803
+!D3 U+05C3 afii57658
+!D4 U+05F0 afii57716
+!D5 U+05F1 afii57717
+!D6 U+05F2 afii57718
+!D7 U+05F3 gereshhebrew
+!D8 U+05F4 gershayimhebrew
+!E0 U+05D0 afii57664
+!E1 U+05D1 afii57665
+!E2 U+05D2 afii57666
+!E3 U+05D3 afii57667
+!E4 U+05D4 afii57668
+!E5 U+05D5 afii57669
+!E6 U+05D6 afii57670
+!E7 U+05D7 afii57671
+!E8 U+05D8 afii57672
+!E9 U+05D9 afii57673
+!EA U+05DA afii57674
+!EB U+05DB afii57675
+!EC U+05DC afii57676
+!ED U+05DD afii57677
+!EE U+05DE afii57678
+!EF U+05DF afii57679
+!F0 U+05E0 afii57680
+!F1 U+05E1 afii57681
+!F2 U+05E2 afii57682
+!F3 U+05E3 afii57683
+!F4 U+05E4 afii57684
+!F5 U+05E5 afii57685
+!F6 U+05E6 afii57686
+!F7 U+05E7 afii57687
+!F8 U+05E8 afii57688
+!F9 U+05E9 afii57689
+!FA U+05EA afii57690
+!FD U+200E afii299
+!FE U+200F afii300
diff --git a/font/cp1257.map b/font/cp1257.map
new file mode 100644
index 0000000..2f2ecfa
--- /dev/null
+++ b/font/cp1257.map
@@ -0,0 +1,244 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!8D U+00A8 dieresis
+!8E U+02C7 caron
+!8F U+00B8 cedilla
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!9D U+00AF macron
+!9E U+02DB ogonek
+!A0 U+00A0 space
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00D8 Oslash
+!A9 U+00A9 copyright
+!AA U+0156 Rcommaaccent
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00C6 AE
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00F8 oslash
+!B9 U+00B9 onesuperior
+!BA U+0157 rcommaaccent
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00E6 ae
+!C0 U+0104 Aogonek
+!C1 U+012E Iogonek
+!C2 U+0100 Amacron
+!C3 U+0106 Cacute
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+0118 Eogonek
+!C7 U+0112 Emacron
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0179 Zacute
+!CB U+0116 Edotaccent
+!CC U+0122 Gcommaaccent
+!CD U+0136 Kcommaaccent
+!CE U+012A Imacron
+!CF U+013B Lcommaaccent
+!D0 U+0160 Scaron
+!D1 U+0143 Nacute
+!D2 U+0145 Ncommaaccent
+!D3 U+00D3 Oacute
+!D4 U+014C Omacron
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0172 Uogonek
+!D9 U+0141 Lslash
+!DA U+015A Sacute
+!DB U+016A Umacron
+!DC U+00DC Udieresis
+!DD U+017B Zdotaccent
+!DE U+017D Zcaron
+!DF U+00DF germandbls
+!E0 U+0105 aogonek
+!E1 U+012F iogonek
+!E2 U+0101 amacron
+!E3 U+0107 cacute
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+0119 eogonek
+!E7 U+0113 emacron
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+017A zacute
+!EB U+0117 edotaccent
+!EC U+0123 gcommaaccent
+!ED U+0137 kcommaaccent
+!EE U+012B imacron
+!EF U+013C lcommaaccent
+!F0 U+0161 scaron
+!F1 U+0144 nacute
+!F2 U+0146 ncommaaccent
+!F3 U+00F3 oacute
+!F4 U+014D omacron
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0173 uogonek
+!F9 U+0142 lslash
+!FA U+015B sacute
+!FB U+016B umacron
+!FC U+00FC udieresis
+!FD U+017C zdotaccent
+!FE U+017E zcaron
+!FF U+02D9 dotaccent
diff --git a/font/cp1258.map b/font/cp1258.map
new file mode 100644
index 0000000..fed915f
--- /dev/null
+++ b/font/cp1258.map
@@ -0,0 +1,247 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!82 U+201A quotesinglbase
+!83 U+0192 florin
+!84 U+201E quotedblbase
+!85 U+2026 ellipsis
+!86 U+2020 dagger
+!87 U+2021 daggerdbl
+!88 U+02C6 circumflex
+!89 U+2030 perthousand
+!8B U+2039 guilsinglleft
+!8C U+0152 OE
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!98 U+02DC tilde
+!99 U+2122 trademark
+!9B U+203A guilsinglright
+!9C U+0153 oe
+!9F U+0178 Ydieresis
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+0300 gravecomb
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+0110 Dcroat
+!D1 U+00D1 Ntilde
+!D2 U+0309 hookabovecomb
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+01A0 Ohorn
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+01AF Uhorn
+!DE U+0303 tildecomb
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+0301 acutecomb
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+0111 dcroat
+!F1 U+00F1 ntilde
+!F2 U+0323 dotbelowcomb
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+01A1 ohorn
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+01B0 uhorn
+!FE U+20AB dong
+!FF U+00FF ydieresis
diff --git a/font/cp874.map b/font/cp874.map
new file mode 100644
index 0000000..1006e6b
--- /dev/null
+++ b/font/cp874.map
@@ -0,0 +1,225 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+20AC Euro
+!85 U+2026 ellipsis
+!91 U+2018 quoteleft
+!92 U+2019 quoteright
+!93 U+201C quotedblleft
+!94 U+201D quotedblright
+!95 U+2022 bullet
+!96 U+2013 endash
+!97 U+2014 emdash
+!A0 U+00A0 space
+!A1 U+0E01 kokaithai
+!A2 U+0E02 khokhaithai
+!A3 U+0E03 khokhuatthai
+!A4 U+0E04 khokhwaithai
+!A5 U+0E05 khokhonthai
+!A6 U+0E06 khorakhangthai
+!A7 U+0E07 ngonguthai
+!A8 U+0E08 chochanthai
+!A9 U+0E09 chochingthai
+!AA U+0E0A chochangthai
+!AB U+0E0B sosothai
+!AC U+0E0C chochoethai
+!AD U+0E0D yoyingthai
+!AE U+0E0E dochadathai
+!AF U+0E0F topatakthai
+!B0 U+0E10 thothanthai
+!B1 U+0E11 thonangmonthothai
+!B2 U+0E12 thophuthaothai
+!B3 U+0E13 nonenthai
+!B4 U+0E14 dodekthai
+!B5 U+0E15 totaothai
+!B6 U+0E16 thothungthai
+!B7 U+0E17 thothahanthai
+!B8 U+0E18 thothongthai
+!B9 U+0E19 nonuthai
+!BA U+0E1A bobaimaithai
+!BB U+0E1B poplathai
+!BC U+0E1C phophungthai
+!BD U+0E1D fofathai
+!BE U+0E1E phophanthai
+!BF U+0E1F fofanthai
+!C0 U+0E20 phosamphaothai
+!C1 U+0E21 momathai
+!C2 U+0E22 yoyakthai
+!C3 U+0E23 roruathai
+!C4 U+0E24 ruthai
+!C5 U+0E25 lolingthai
+!C6 U+0E26 luthai
+!C7 U+0E27 wowaenthai
+!C8 U+0E28 sosalathai
+!C9 U+0E29 sorusithai
+!CA U+0E2A sosuathai
+!CB U+0E2B hohipthai
+!CC U+0E2C lochulathai
+!CD U+0E2D oangthai
+!CE U+0E2E honokhukthai
+!CF U+0E2F paiyannoithai
+!D0 U+0E30 saraathai
+!D1 U+0E31 maihanakatthai
+!D2 U+0E32 saraaathai
+!D3 U+0E33 saraamthai
+!D4 U+0E34 saraithai
+!D5 U+0E35 saraiithai
+!D6 U+0E36 sarauethai
+!D7 U+0E37 saraueethai
+!D8 U+0E38 sarauthai
+!D9 U+0E39 sarauuthai
+!DA U+0E3A phinthuthai
+!DF U+0E3F bahtthai
+!E0 U+0E40 saraethai
+!E1 U+0E41 saraaethai
+!E2 U+0E42 saraothai
+!E3 U+0E43 saraaimaimuanthai
+!E4 U+0E44 saraaimaimalaithai
+!E5 U+0E45 lakkhangyaothai
+!E6 U+0E46 maiyamokthai
+!E7 U+0E47 maitaikhuthai
+!E8 U+0E48 maiekthai
+!E9 U+0E49 maithothai
+!EA U+0E4A maitrithai
+!EB U+0E4B maichattawathai
+!EC U+0E4C thanthakhatthai
+!ED U+0E4D nikhahitthai
+!EE U+0E4E yamakkanthai
+!EF U+0E4F fongmanthai
+!F0 U+0E50 zerothai
+!F1 U+0E51 onethai
+!F2 U+0E52 twothai
+!F3 U+0E53 threethai
+!F4 U+0E54 fourthai
+!F5 U+0E55 fivethai
+!F6 U+0E56 sixthai
+!F7 U+0E57 seventhai
+!F8 U+0E58 eightthai
+!F9 U+0E59 ninethai
+!FA U+0E5A angkhankhuthai
+!FB U+0E5B khomutthai
diff --git a/font/helvetica.json b/font/helvetica.json
new file mode 100644
index 0000000..2a8a93f
--- /dev/null
+++ b/font/helvetica.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Helvetica","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]} \ No newline at end of file
diff --git a/font/helveticab.json b/font/helveticab.json
new file mode 100644
index 0000000..198c2a2
--- /dev/null
+++ b/font/helveticab.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Helvetica-Bold","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]} \ No newline at end of file
diff --git a/font/helveticabi.json b/font/helveticabi.json
new file mode 100644
index 0000000..afc3024
--- /dev/null
+++ b/font/helveticabi.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Helvetica-BoldOblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556]} \ No newline at end of file
diff --git a/font/helveticai.json b/font/helveticai.json
new file mode 100644
index 0000000..502f938
--- /dev/null
+++ b/font/helveticai.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Helvetica-Oblique","Up":-100,"Ut":50,"Cw":[278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500]} \ No newline at end of file
diff --git a/font/iso-8859-1.map b/font/iso-8859-1.map
new file mode 100644
index 0000000..61740a3
--- /dev/null
+++ b/font/iso-8859-1.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/font/iso-8859-11.map b/font/iso-8859-11.map
new file mode 100644
index 0000000..9168812
--- /dev/null
+++ b/font/iso-8859-11.map
@@ -0,0 +1,248 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0E01 kokaithai
+!A2 U+0E02 khokhaithai
+!A3 U+0E03 khokhuatthai
+!A4 U+0E04 khokhwaithai
+!A5 U+0E05 khokhonthai
+!A6 U+0E06 khorakhangthai
+!A7 U+0E07 ngonguthai
+!A8 U+0E08 chochanthai
+!A9 U+0E09 chochingthai
+!AA U+0E0A chochangthai
+!AB U+0E0B sosothai
+!AC U+0E0C chochoethai
+!AD U+0E0D yoyingthai
+!AE U+0E0E dochadathai
+!AF U+0E0F topatakthai
+!B0 U+0E10 thothanthai
+!B1 U+0E11 thonangmonthothai
+!B2 U+0E12 thophuthaothai
+!B3 U+0E13 nonenthai
+!B4 U+0E14 dodekthai
+!B5 U+0E15 totaothai
+!B6 U+0E16 thothungthai
+!B7 U+0E17 thothahanthai
+!B8 U+0E18 thothongthai
+!B9 U+0E19 nonuthai
+!BA U+0E1A bobaimaithai
+!BB U+0E1B poplathai
+!BC U+0E1C phophungthai
+!BD U+0E1D fofathai
+!BE U+0E1E phophanthai
+!BF U+0E1F fofanthai
+!C0 U+0E20 phosamphaothai
+!C1 U+0E21 momathai
+!C2 U+0E22 yoyakthai
+!C3 U+0E23 roruathai
+!C4 U+0E24 ruthai
+!C5 U+0E25 lolingthai
+!C6 U+0E26 luthai
+!C7 U+0E27 wowaenthai
+!C8 U+0E28 sosalathai
+!C9 U+0E29 sorusithai
+!CA U+0E2A sosuathai
+!CB U+0E2B hohipthai
+!CC U+0E2C lochulathai
+!CD U+0E2D oangthai
+!CE U+0E2E honokhukthai
+!CF U+0E2F paiyannoithai
+!D0 U+0E30 saraathai
+!D1 U+0E31 maihanakatthai
+!D2 U+0E32 saraaathai
+!D3 U+0E33 saraamthai
+!D4 U+0E34 saraithai
+!D5 U+0E35 saraiithai
+!D6 U+0E36 sarauethai
+!D7 U+0E37 saraueethai
+!D8 U+0E38 sarauthai
+!D9 U+0E39 sarauuthai
+!DA U+0E3A phinthuthai
+!DF U+0E3F bahtthai
+!E0 U+0E40 saraethai
+!E1 U+0E41 saraaethai
+!E2 U+0E42 saraothai
+!E3 U+0E43 saraaimaimuanthai
+!E4 U+0E44 saraaimaimalaithai
+!E5 U+0E45 lakkhangyaothai
+!E6 U+0E46 maiyamokthai
+!E7 U+0E47 maitaikhuthai
+!E8 U+0E48 maiekthai
+!E9 U+0E49 maithothai
+!EA U+0E4A maitrithai
+!EB U+0E4B maichattawathai
+!EC U+0E4C thanthakhatthai
+!ED U+0E4D nikhahitthai
+!EE U+0E4E yamakkanthai
+!EF U+0E4F fongmanthai
+!F0 U+0E50 zerothai
+!F1 U+0E51 onethai
+!F2 U+0E52 twothai
+!F3 U+0E53 threethai
+!F4 U+0E54 fourthai
+!F5 U+0E55 fivethai
+!F6 U+0E56 sixthai
+!F7 U+0E57 seventhai
+!F8 U+0E58 eightthai
+!F9 U+0E59 ninethai
+!FA U+0E5A angkhankhuthai
+!FB U+0E5B khomutthai
diff --git a/font/iso-8859-15.map b/font/iso-8859-15.map
new file mode 100644
index 0000000..6c2b571
--- /dev/null
+++ b/font/iso-8859-15.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+20AC Euro
+!A5 U+00A5 yen
+!A6 U+0160 Scaron
+!A7 U+00A7 section
+!A8 U+0161 scaron
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+017D Zcaron
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+017E zcaron
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+0152 OE
+!BD U+0153 oe
+!BE U+0178 Ydieresis
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+00D0 Eth
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+00DE Thorn
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+00F0 eth
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+00FE thorn
+!FF U+00FF ydieresis
diff --git a/font/iso-8859-16.map b/font/iso-8859-16.map
new file mode 100644
index 0000000..202c8fe
--- /dev/null
+++ b/font/iso-8859-16.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+0105 aogonek
+!A3 U+0141 Lslash
+!A4 U+20AC Euro
+!A5 U+201E quotedblbase
+!A6 U+0160 Scaron
+!A7 U+00A7 section
+!A8 U+0161 scaron
+!A9 U+00A9 copyright
+!AA U+0218 Scommaaccent
+!AB U+00AB guillemotleft
+!AC U+0179 Zacute
+!AD U+00AD hyphen
+!AE U+017A zacute
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+010C Ccaron
+!B3 U+0142 lslash
+!B4 U+017D Zcaron
+!B5 U+201D quotedblright
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+017E zcaron
+!B9 U+010D ccaron
+!BA U+0219 scommaaccent
+!BB U+00BB guillemotright
+!BC U+0152 OE
+!BD U+0153 oe
+!BE U+0178 Ydieresis
+!BF U+017C zdotaccent
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0106 Cacute
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+015A Sacute
+!D8 U+0170 Uhungarumlaut
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0118 Eogonek
+!DE U+021A Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+0107 cacute
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+015B sacute
+!F8 U+0171 uhungarumlaut
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0119 eogonek
+!FE U+021B tcommaaccent
+!FF U+00FF ydieresis
diff --git a/font/iso-8859-2.map b/font/iso-8859-2.map
new file mode 100644
index 0000000..65ae09f
--- /dev/null
+++ b/font/iso-8859-2.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+02D8 breve
+!A3 U+0141 Lslash
+!A4 U+00A4 currency
+!A5 U+013D Lcaron
+!A6 U+015A Sacute
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+0160 Scaron
+!AA U+015E Scedilla
+!AB U+0164 Tcaron
+!AC U+0179 Zacute
+!AD U+00AD hyphen
+!AE U+017D Zcaron
+!AF U+017B Zdotaccent
+!B0 U+00B0 degree
+!B1 U+0105 aogonek
+!B2 U+02DB ogonek
+!B3 U+0142 lslash
+!B4 U+00B4 acute
+!B5 U+013E lcaron
+!B6 U+015B sacute
+!B7 U+02C7 caron
+!B8 U+00B8 cedilla
+!B9 U+0161 scaron
+!BA U+015F scedilla
+!BB U+0165 tcaron
+!BC U+017A zacute
+!BD U+02DD hungarumlaut
+!BE U+017E zcaron
+!BF U+017C zdotaccent
+!C0 U+0154 Racute
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+0102 Abreve
+!C4 U+00C4 Adieresis
+!C5 U+0139 Lacute
+!C6 U+0106 Cacute
+!C7 U+00C7 Ccedilla
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+011A Ecaron
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+010E Dcaron
+!D0 U+0110 Dcroat
+!D1 U+0143 Nacute
+!D2 U+0147 Ncaron
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+0150 Ohungarumlaut
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+0158 Rcaron
+!D9 U+016E Uring
+!DA U+00DA Uacute
+!DB U+0170 Uhungarumlaut
+!DC U+00DC Udieresis
+!DD U+00DD Yacute
+!DE U+0162 Tcommaaccent
+!DF U+00DF germandbls
+!E0 U+0155 racute
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+0103 abreve
+!E4 U+00E4 adieresis
+!E5 U+013A lacute
+!E6 U+0107 cacute
+!E7 U+00E7 ccedilla
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+011B ecaron
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+010F dcaron
+!F0 U+0111 dcroat
+!F1 U+0144 nacute
+!F2 U+0148 ncaron
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+0151 ohungarumlaut
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+0159 rcaron
+!F9 U+016F uring
+!FA U+00FA uacute
+!FB U+0171 uhungarumlaut
+!FC U+00FC udieresis
+!FD U+00FD yacute
+!FE U+0163 tcommaaccent
+!FF U+02D9 dotaccent
diff --git a/font/iso-8859-4.map b/font/iso-8859-4.map
new file mode 100644
index 0000000..a7d87bf
--- /dev/null
+++ b/font/iso-8859-4.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0104 Aogonek
+!A2 U+0138 kgreenlandic
+!A3 U+0156 Rcommaaccent
+!A4 U+00A4 currency
+!A5 U+0128 Itilde
+!A6 U+013B Lcommaaccent
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+0160 Scaron
+!AA U+0112 Emacron
+!AB U+0122 Gcommaaccent
+!AC U+0166 Tbar
+!AD U+00AD hyphen
+!AE U+017D Zcaron
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+0105 aogonek
+!B2 U+02DB ogonek
+!B3 U+0157 rcommaaccent
+!B4 U+00B4 acute
+!B5 U+0129 itilde
+!B6 U+013C lcommaaccent
+!B7 U+02C7 caron
+!B8 U+00B8 cedilla
+!B9 U+0161 scaron
+!BA U+0113 emacron
+!BB U+0123 gcommaaccent
+!BC U+0167 tbar
+!BD U+014A Eng
+!BE U+017E zcaron
+!BF U+014B eng
+!C0 U+0100 Amacron
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+012E Iogonek
+!C8 U+010C Ccaron
+!C9 U+00C9 Eacute
+!CA U+0118 Eogonek
+!CB U+00CB Edieresis
+!CC U+0116 Edotaccent
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+012A Imacron
+!D0 U+0110 Dcroat
+!D1 U+0145 Ncommaaccent
+!D2 U+014C Omacron
+!D3 U+0136 Kcommaaccent
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+0172 Uogonek
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0168 Utilde
+!DE U+016A Umacron
+!DF U+00DF germandbls
+!E0 U+0101 amacron
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+012F iogonek
+!E8 U+010D ccaron
+!E9 U+00E9 eacute
+!EA U+0119 eogonek
+!EB U+00EB edieresis
+!EC U+0117 edotaccent
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+012B imacron
+!F0 U+0111 dcroat
+!F1 U+0146 ncommaaccent
+!F2 U+014D omacron
+!F3 U+0137 kcommaaccent
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+0173 uogonek
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0169 utilde
+!FE U+016B umacron
+!FF U+02D9 dotaccent
diff --git a/font/iso-8859-5.map b/font/iso-8859-5.map
new file mode 100644
index 0000000..f9cd4ed
--- /dev/null
+++ b/font/iso-8859-5.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+0401 afii10023
+!A2 U+0402 afii10051
+!A3 U+0403 afii10052
+!A4 U+0404 afii10053
+!A5 U+0405 afii10054
+!A6 U+0406 afii10055
+!A7 U+0407 afii10056
+!A8 U+0408 afii10057
+!A9 U+0409 afii10058
+!AA U+040A afii10059
+!AB U+040B afii10060
+!AC U+040C afii10061
+!AD U+00AD hyphen
+!AE U+040E afii10062
+!AF U+040F afii10145
+!B0 U+0410 afii10017
+!B1 U+0411 afii10018
+!B2 U+0412 afii10019
+!B3 U+0413 afii10020
+!B4 U+0414 afii10021
+!B5 U+0415 afii10022
+!B6 U+0416 afii10024
+!B7 U+0417 afii10025
+!B8 U+0418 afii10026
+!B9 U+0419 afii10027
+!BA U+041A afii10028
+!BB U+041B afii10029
+!BC U+041C afii10030
+!BD U+041D afii10031
+!BE U+041E afii10032
+!BF U+041F afii10033
+!C0 U+0420 afii10034
+!C1 U+0421 afii10035
+!C2 U+0422 afii10036
+!C3 U+0423 afii10037
+!C4 U+0424 afii10038
+!C5 U+0425 afii10039
+!C6 U+0426 afii10040
+!C7 U+0427 afii10041
+!C8 U+0428 afii10042
+!C9 U+0429 afii10043
+!CA U+042A afii10044
+!CB U+042B afii10045
+!CC U+042C afii10046
+!CD U+042D afii10047
+!CE U+042E afii10048
+!CF U+042F afii10049
+!D0 U+0430 afii10065
+!D1 U+0431 afii10066
+!D2 U+0432 afii10067
+!D3 U+0433 afii10068
+!D4 U+0434 afii10069
+!D5 U+0435 afii10070
+!D6 U+0436 afii10072
+!D7 U+0437 afii10073
+!D8 U+0438 afii10074
+!D9 U+0439 afii10075
+!DA U+043A afii10076
+!DB U+043B afii10077
+!DC U+043C afii10078
+!DD U+043D afii10079
+!DE U+043E afii10080
+!DF U+043F afii10081
+!E0 U+0440 afii10082
+!E1 U+0441 afii10083
+!E2 U+0442 afii10084
+!E3 U+0443 afii10085
+!E4 U+0444 afii10086
+!E5 U+0445 afii10087
+!E6 U+0446 afii10088
+!E7 U+0447 afii10089
+!E8 U+0448 afii10090
+!E9 U+0449 afii10091
+!EA U+044A afii10092
+!EB U+044B afii10093
+!EC U+044C afii10094
+!ED U+044D afii10095
+!EE U+044E afii10096
+!EF U+044F afii10097
+!F0 U+2116 afii61352
+!F1 U+0451 afii10071
+!F2 U+0452 afii10099
+!F3 U+0453 afii10100
+!F4 U+0454 afii10101
+!F5 U+0455 afii10102
+!F6 U+0456 afii10103
+!F7 U+0457 afii10104
+!F8 U+0458 afii10105
+!F9 U+0459 afii10106
+!FA U+045A afii10107
+!FB U+045B afii10108
+!FC U+045C afii10109
+!FD U+00A7 section
+!FE U+045E afii10110
+!FF U+045F afii10193
diff --git a/font/iso-8859-7.map b/font/iso-8859-7.map
new file mode 100644
index 0000000..e163796
--- /dev/null
+++ b/font/iso-8859-7.map
@@ -0,0 +1,250 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+2018 quoteleft
+!A2 U+2019 quoteright
+!A3 U+00A3 sterling
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AF U+2015 afii00208
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+0384 tonos
+!B5 U+0385 dieresistonos
+!B6 U+0386 Alphatonos
+!B7 U+00B7 periodcentered
+!B8 U+0388 Epsilontonos
+!B9 U+0389 Etatonos
+!BA U+038A Iotatonos
+!BB U+00BB guillemotright
+!BC U+038C Omicrontonos
+!BD U+00BD onehalf
+!BE U+038E Upsilontonos
+!BF U+038F Omegatonos
+!C0 U+0390 iotadieresistonos
+!C1 U+0391 Alpha
+!C2 U+0392 Beta
+!C3 U+0393 Gamma
+!C4 U+0394 Delta
+!C5 U+0395 Epsilon
+!C6 U+0396 Zeta
+!C7 U+0397 Eta
+!C8 U+0398 Theta
+!C9 U+0399 Iota
+!CA U+039A Kappa
+!CB U+039B Lambda
+!CC U+039C Mu
+!CD U+039D Nu
+!CE U+039E Xi
+!CF U+039F Omicron
+!D0 U+03A0 Pi
+!D1 U+03A1 Rho
+!D3 U+03A3 Sigma
+!D4 U+03A4 Tau
+!D5 U+03A5 Upsilon
+!D6 U+03A6 Phi
+!D7 U+03A7 Chi
+!D8 U+03A8 Psi
+!D9 U+03A9 Omega
+!DA U+03AA Iotadieresis
+!DB U+03AB Upsilondieresis
+!DC U+03AC alphatonos
+!DD U+03AD epsilontonos
+!DE U+03AE etatonos
+!DF U+03AF iotatonos
+!E0 U+03B0 upsilondieresistonos
+!E1 U+03B1 alpha
+!E2 U+03B2 beta
+!E3 U+03B3 gamma
+!E4 U+03B4 delta
+!E5 U+03B5 epsilon
+!E6 U+03B6 zeta
+!E7 U+03B7 eta
+!E8 U+03B8 theta
+!E9 U+03B9 iota
+!EA U+03BA kappa
+!EB U+03BB lambda
+!EC U+03BC mu
+!ED U+03BD nu
+!EE U+03BE xi
+!EF U+03BF omicron
+!F0 U+03C0 pi
+!F1 U+03C1 rho
+!F2 U+03C2 sigma1
+!F3 U+03C3 sigma
+!F4 U+03C4 tau
+!F5 U+03C5 upsilon
+!F6 U+03C6 phi
+!F7 U+03C7 chi
+!F8 U+03C8 psi
+!F9 U+03C9 omega
+!FA U+03CA iotadieresis
+!FB U+03CB upsilondieresis
+!FC U+03CC omicrontonos
+!FD U+03CD upsilontonos
+!FE U+03CE omegatonos
diff --git a/font/iso-8859-9.map b/font/iso-8859-9.map
new file mode 100644
index 0000000..48c123a
--- /dev/null
+++ b/font/iso-8859-9.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+0080 .notdef
+!81 U+0081 .notdef
+!82 U+0082 .notdef
+!83 U+0083 .notdef
+!84 U+0084 .notdef
+!85 U+0085 .notdef
+!86 U+0086 .notdef
+!87 U+0087 .notdef
+!88 U+0088 .notdef
+!89 U+0089 .notdef
+!8A U+008A .notdef
+!8B U+008B .notdef
+!8C U+008C .notdef
+!8D U+008D .notdef
+!8E U+008E .notdef
+!8F U+008F .notdef
+!90 U+0090 .notdef
+!91 U+0091 .notdef
+!92 U+0092 .notdef
+!93 U+0093 .notdef
+!94 U+0094 .notdef
+!95 U+0095 .notdef
+!96 U+0096 .notdef
+!97 U+0097 .notdef
+!98 U+0098 .notdef
+!99 U+0099 .notdef
+!9A U+009A .notdef
+!9B U+009B .notdef
+!9C U+009C .notdef
+!9D U+009D .notdef
+!9E U+009E .notdef
+!9F U+009F .notdef
+!A0 U+00A0 space
+!A1 U+00A1 exclamdown
+!A2 U+00A2 cent
+!A3 U+00A3 sterling
+!A4 U+00A4 currency
+!A5 U+00A5 yen
+!A6 U+00A6 brokenbar
+!A7 U+00A7 section
+!A8 U+00A8 dieresis
+!A9 U+00A9 copyright
+!AA U+00AA ordfeminine
+!AB U+00AB guillemotleft
+!AC U+00AC logicalnot
+!AD U+00AD hyphen
+!AE U+00AE registered
+!AF U+00AF macron
+!B0 U+00B0 degree
+!B1 U+00B1 plusminus
+!B2 U+00B2 twosuperior
+!B3 U+00B3 threesuperior
+!B4 U+00B4 acute
+!B5 U+00B5 mu
+!B6 U+00B6 paragraph
+!B7 U+00B7 periodcentered
+!B8 U+00B8 cedilla
+!B9 U+00B9 onesuperior
+!BA U+00BA ordmasculine
+!BB U+00BB guillemotright
+!BC U+00BC onequarter
+!BD U+00BD onehalf
+!BE U+00BE threequarters
+!BF U+00BF questiondown
+!C0 U+00C0 Agrave
+!C1 U+00C1 Aacute
+!C2 U+00C2 Acircumflex
+!C3 U+00C3 Atilde
+!C4 U+00C4 Adieresis
+!C5 U+00C5 Aring
+!C6 U+00C6 AE
+!C7 U+00C7 Ccedilla
+!C8 U+00C8 Egrave
+!C9 U+00C9 Eacute
+!CA U+00CA Ecircumflex
+!CB U+00CB Edieresis
+!CC U+00CC Igrave
+!CD U+00CD Iacute
+!CE U+00CE Icircumflex
+!CF U+00CF Idieresis
+!D0 U+011E Gbreve
+!D1 U+00D1 Ntilde
+!D2 U+00D2 Ograve
+!D3 U+00D3 Oacute
+!D4 U+00D4 Ocircumflex
+!D5 U+00D5 Otilde
+!D6 U+00D6 Odieresis
+!D7 U+00D7 multiply
+!D8 U+00D8 Oslash
+!D9 U+00D9 Ugrave
+!DA U+00DA Uacute
+!DB U+00DB Ucircumflex
+!DC U+00DC Udieresis
+!DD U+0130 Idotaccent
+!DE U+015E Scedilla
+!DF U+00DF germandbls
+!E0 U+00E0 agrave
+!E1 U+00E1 aacute
+!E2 U+00E2 acircumflex
+!E3 U+00E3 atilde
+!E4 U+00E4 adieresis
+!E5 U+00E5 aring
+!E6 U+00E6 ae
+!E7 U+00E7 ccedilla
+!E8 U+00E8 egrave
+!E9 U+00E9 eacute
+!EA U+00EA ecircumflex
+!EB U+00EB edieresis
+!EC U+00EC igrave
+!ED U+00ED iacute
+!EE U+00EE icircumflex
+!EF U+00EF idieresis
+!F0 U+011F gbreve
+!F1 U+00F1 ntilde
+!F2 U+00F2 ograve
+!F3 U+00F3 oacute
+!F4 U+00F4 ocircumflex
+!F5 U+00F5 otilde
+!F6 U+00F6 odieresis
+!F7 U+00F7 divide
+!F8 U+00F8 oslash
+!F9 U+00F9 ugrave
+!FA U+00FA uacute
+!FB U+00FB ucircumflex
+!FC U+00FC udieresis
+!FD U+0131 dotlessi
+!FE U+015F scedilla
+!FF U+00FF ydieresis
diff --git a/font/koi8-r.map b/font/koi8-r.map
new file mode 100644
index 0000000..6ad5d05
--- /dev/null
+++ b/font/koi8-r.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+2500 SF100000
+!81 U+2502 SF110000
+!82 U+250C SF010000
+!83 U+2510 SF030000
+!84 U+2514 SF020000
+!85 U+2518 SF040000
+!86 U+251C SF080000
+!87 U+2524 SF090000
+!88 U+252C SF060000
+!89 U+2534 SF070000
+!8A U+253C SF050000
+!8B U+2580 upblock
+!8C U+2584 dnblock
+!8D U+2588 block
+!8E U+258C lfblock
+!8F U+2590 rtblock
+!90 U+2591 ltshade
+!91 U+2592 shade
+!92 U+2593 dkshade
+!93 U+2320 integraltp
+!94 U+25A0 filledbox
+!95 U+2219 periodcentered
+!96 U+221A radical
+!97 U+2248 approxequal
+!98 U+2264 lessequal
+!99 U+2265 greaterequal
+!9A U+00A0 space
+!9B U+2321 integralbt
+!9C U+00B0 degree
+!9D U+00B2 twosuperior
+!9E U+00B7 periodcentered
+!9F U+00F7 divide
+!A0 U+2550 SF430000
+!A1 U+2551 SF240000
+!A2 U+2552 SF510000
+!A3 U+0451 afii10071
+!A4 U+2553 SF520000
+!A5 U+2554 SF390000
+!A6 U+2555 SF220000
+!A7 U+2556 SF210000
+!A8 U+2557 SF250000
+!A9 U+2558 SF500000
+!AA U+2559 SF490000
+!AB U+255A SF380000
+!AC U+255B SF280000
+!AD U+255C SF270000
+!AE U+255D SF260000
+!AF U+255E SF360000
+!B0 U+255F SF370000
+!B1 U+2560 SF420000
+!B2 U+2561 SF190000
+!B3 U+0401 afii10023
+!B4 U+2562 SF200000
+!B5 U+2563 SF230000
+!B6 U+2564 SF470000
+!B7 U+2565 SF480000
+!B8 U+2566 SF410000
+!B9 U+2567 SF450000
+!BA U+2568 SF460000
+!BB U+2569 SF400000
+!BC U+256A SF540000
+!BD U+256B SF530000
+!BE U+256C SF440000
+!BF U+00A9 copyright
+!C0 U+044E afii10096
+!C1 U+0430 afii10065
+!C2 U+0431 afii10066
+!C3 U+0446 afii10088
+!C4 U+0434 afii10069
+!C5 U+0435 afii10070
+!C6 U+0444 afii10086
+!C7 U+0433 afii10068
+!C8 U+0445 afii10087
+!C9 U+0438 afii10074
+!CA U+0439 afii10075
+!CB U+043A afii10076
+!CC U+043B afii10077
+!CD U+043C afii10078
+!CE U+043D afii10079
+!CF U+043E afii10080
+!D0 U+043F afii10081
+!D1 U+044F afii10097
+!D2 U+0440 afii10082
+!D3 U+0441 afii10083
+!D4 U+0442 afii10084
+!D5 U+0443 afii10085
+!D6 U+0436 afii10072
+!D7 U+0432 afii10067
+!D8 U+044C afii10094
+!D9 U+044B afii10093
+!DA U+0437 afii10073
+!DB U+0448 afii10090
+!DC U+044D afii10095
+!DD U+0449 afii10091
+!DE U+0447 afii10089
+!DF U+044A afii10092
+!E0 U+042E afii10048
+!E1 U+0410 afii10017
+!E2 U+0411 afii10018
+!E3 U+0426 afii10040
+!E4 U+0414 afii10021
+!E5 U+0415 afii10022
+!E6 U+0424 afii10038
+!E7 U+0413 afii10020
+!E8 U+0425 afii10039
+!E9 U+0418 afii10026
+!EA U+0419 afii10027
+!EB U+041A afii10028
+!EC U+041B afii10029
+!ED U+041C afii10030
+!EE U+041D afii10031
+!EF U+041E afii10032
+!F0 U+041F afii10033
+!F1 U+042F afii10049
+!F2 U+0420 afii10034
+!F3 U+0421 afii10035
+!F4 U+0422 afii10036
+!F5 U+0423 afii10037
+!F6 U+0416 afii10024
+!F7 U+0412 afii10019
+!F8 U+042C afii10046
+!F9 U+042B afii10045
+!FA U+0417 afii10025
+!FB U+0428 afii10042
+!FC U+042D afii10047
+!FD U+0429 afii10043
+!FE U+0427 afii10041
+!FF U+042A afii10044
diff --git a/font/koi8-u.map b/font/koi8-u.map
new file mode 100644
index 0000000..40a7e4f
--- /dev/null
+++ b/font/koi8-u.map
@@ -0,0 +1,256 @@
+!00 U+0000 .notdef
+!01 U+0001 .notdef
+!02 U+0002 .notdef
+!03 U+0003 .notdef
+!04 U+0004 .notdef
+!05 U+0005 .notdef
+!06 U+0006 .notdef
+!07 U+0007 .notdef
+!08 U+0008 .notdef
+!09 U+0009 .notdef
+!0A U+000A .notdef
+!0B U+000B .notdef
+!0C U+000C .notdef
+!0D U+000D .notdef
+!0E U+000E .notdef
+!0F U+000F .notdef
+!10 U+0010 .notdef
+!11 U+0011 .notdef
+!12 U+0012 .notdef
+!13 U+0013 .notdef
+!14 U+0014 .notdef
+!15 U+0015 .notdef
+!16 U+0016 .notdef
+!17 U+0017 .notdef
+!18 U+0018 .notdef
+!19 U+0019 .notdef
+!1A U+001A .notdef
+!1B U+001B .notdef
+!1C U+001C .notdef
+!1D U+001D .notdef
+!1E U+001E .notdef
+!1F U+001F .notdef
+!20 U+0020 space
+!21 U+0021 exclam
+!22 U+0022 quotedbl
+!23 U+0023 numbersign
+!24 U+0024 dollar
+!25 U+0025 percent
+!26 U+0026 ampersand
+!27 U+0027 quotesingle
+!28 U+0028 parenleft
+!29 U+0029 parenright
+!2A U+002A asterisk
+!2B U+002B plus
+!2C U+002C comma
+!2D U+002D hyphen
+!2E U+002E period
+!2F U+002F slash
+!30 U+0030 zero
+!31 U+0031 one
+!32 U+0032 two
+!33 U+0033 three
+!34 U+0034 four
+!35 U+0035 five
+!36 U+0036 six
+!37 U+0037 seven
+!38 U+0038 eight
+!39 U+0039 nine
+!3A U+003A colon
+!3B U+003B semicolon
+!3C U+003C less
+!3D U+003D equal
+!3E U+003E greater
+!3F U+003F question
+!40 U+0040 at
+!41 U+0041 A
+!42 U+0042 B
+!43 U+0043 C
+!44 U+0044 D
+!45 U+0045 E
+!46 U+0046 F
+!47 U+0047 G
+!48 U+0048 H
+!49 U+0049 I
+!4A U+004A J
+!4B U+004B K
+!4C U+004C L
+!4D U+004D M
+!4E U+004E N
+!4F U+004F O
+!50 U+0050 P
+!51 U+0051 Q
+!52 U+0052 R
+!53 U+0053 S
+!54 U+0054 T
+!55 U+0055 U
+!56 U+0056 V
+!57 U+0057 W
+!58 U+0058 X
+!59 U+0059 Y
+!5A U+005A Z
+!5B U+005B bracketleft
+!5C U+005C backslash
+!5D U+005D bracketright
+!5E U+005E asciicircum
+!5F U+005F underscore
+!60 U+0060 grave
+!61 U+0061 a
+!62 U+0062 b
+!63 U+0063 c
+!64 U+0064 d
+!65 U+0065 e
+!66 U+0066 f
+!67 U+0067 g
+!68 U+0068 h
+!69 U+0069 i
+!6A U+006A j
+!6B U+006B k
+!6C U+006C l
+!6D U+006D m
+!6E U+006E n
+!6F U+006F o
+!70 U+0070 p
+!71 U+0071 q
+!72 U+0072 r
+!73 U+0073 s
+!74 U+0074 t
+!75 U+0075 u
+!76 U+0076 v
+!77 U+0077 w
+!78 U+0078 x
+!79 U+0079 y
+!7A U+007A z
+!7B U+007B braceleft
+!7C U+007C bar
+!7D U+007D braceright
+!7E U+007E asciitilde
+!7F U+007F .notdef
+!80 U+2500 SF100000
+!81 U+2502 SF110000
+!82 U+250C SF010000
+!83 U+2510 SF030000
+!84 U+2514 SF020000
+!85 U+2518 SF040000
+!86 U+251C SF080000
+!87 U+2524 SF090000
+!88 U+252C SF060000
+!89 U+2534 SF070000
+!8A U+253C SF050000
+!8B U+2580 upblock
+!8C U+2584 dnblock
+!8D U+2588 block
+!8E U+258C lfblock
+!8F U+2590 rtblock
+!90 U+2591 ltshade
+!91 U+2592 shade
+!92 U+2593 dkshade
+!93 U+2320 integraltp
+!94 U+25A0 filledbox
+!95 U+2022 bullet
+!96 U+221A radical
+!97 U+2248 approxequal
+!98 U+2264 lessequal
+!99 U+2265 greaterequal
+!9A U+00A0 space
+!9B U+2321 integralbt
+!9C U+00B0 degree
+!9D U+00B2 twosuperior
+!9E U+00B7 periodcentered
+!9F U+00F7 divide
+!A0 U+2550 SF430000
+!A1 U+2551 SF240000
+!A2 U+2552 SF510000
+!A3 U+0451 afii10071
+!A4 U+0454 afii10101
+!A5 U+2554 SF390000
+!A6 U+0456 afii10103
+!A7 U+0457 afii10104
+!A8 U+2557 SF250000
+!A9 U+2558 SF500000
+!AA U+2559 SF490000
+!AB U+255A SF380000
+!AC U+255B SF280000
+!AD U+0491 afii10098
+!AE U+255D SF260000
+!AF U+255E SF360000
+!B0 U+255F SF370000
+!B1 U+2560 SF420000
+!B2 U+2561 SF190000
+!B3 U+0401 afii10023
+!B4 U+0404 afii10053
+!B5 U+2563 SF230000
+!B6 U+0406 afii10055
+!B7 U+0407 afii10056
+!B8 U+2566 SF410000
+!B9 U+2567 SF450000
+!BA U+2568 SF460000
+!BB U+2569 SF400000
+!BC U+256A SF540000
+!BD U+0490 afii10050
+!BE U+256C SF440000
+!BF U+00A9 copyright
+!C0 U+044E afii10096
+!C1 U+0430 afii10065
+!C2 U+0431 afii10066
+!C3 U+0446 afii10088
+!C4 U+0434 afii10069
+!C5 U+0435 afii10070
+!C6 U+0444 afii10086
+!C7 U+0433 afii10068
+!C8 U+0445 afii10087
+!C9 U+0438 afii10074
+!CA U+0439 afii10075
+!CB U+043A afii10076
+!CC U+043B afii10077
+!CD U+043C afii10078
+!CE U+043D afii10079
+!CF U+043E afii10080
+!D0 U+043F afii10081
+!D1 U+044F afii10097
+!D2 U+0440 afii10082
+!D3 U+0441 afii10083
+!D4 U+0442 afii10084
+!D5 U+0443 afii10085
+!D6 U+0436 afii10072
+!D7 U+0432 afii10067
+!D8 U+044C afii10094
+!D9 U+044B afii10093
+!DA U+0437 afii10073
+!DB U+0448 afii10090
+!DC U+044D afii10095
+!DD U+0449 afii10091
+!DE U+0447 afii10089
+!DF U+044A afii10092
+!E0 U+042E afii10048
+!E1 U+0410 afii10017
+!E2 U+0411 afii10018
+!E3 U+0426 afii10040
+!E4 U+0414 afii10021
+!E5 U+0415 afii10022
+!E6 U+0424 afii10038
+!E7 U+0413 afii10020
+!E8 U+0425 afii10039
+!E9 U+0418 afii10026
+!EA U+0419 afii10027
+!EB U+041A afii10028
+!EC U+041B afii10029
+!ED U+041C afii10030
+!EE U+041D afii10031
+!EF U+041E afii10032
+!F0 U+041F afii10033
+!F1 U+042F afii10049
+!F2 U+0420 afii10034
+!F3 U+0421 afii10035
+!F4 U+0422 afii10036
+!F5 U+0423 afii10037
+!F6 U+0416 afii10024
+!F7 U+0412 afii10019
+!F8 U+042C afii10046
+!F9 U+042B afii10045
+!FA U+0417 afii10025
+!FB U+0428 afii10042
+!FC U+042D afii10047
+!FD U+0429 afii10043
+!FE U+0427 afii10041
+!FF U+042A afii10044
diff --git a/font/times.json b/font/times.json
new file mode 100644
index 0000000..6b99ae0
--- /dev/null
+++ b/font/times.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Times-Roman","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564,250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722,667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944,722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778,500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500,444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980,389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333,400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722,889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722,722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,564,500,500,500,500,500,500,500,500]} \ No newline at end of file
diff --git a/font/timesb.json b/font/timesb.json
new file mode 100644
index 0000000..727970f
--- /dev/null
+++ b/font/timesb.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Times-Bold","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722,667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000,722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833,556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500,500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333,400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722,1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722,722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,500,556,500]} \ No newline at end of file
diff --git a/font/timesbi.json b/font/timesbi.json
new file mode 100644
index 0000000..e49b917
--- /dev/null
+++ b/font/timesbi.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Times-BoldItalic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667,667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889,667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778,556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500,500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333,400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667,944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722,722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,444,500,444]} \ No newline at end of file
diff --git a/font/timesi.json b/font/timesi.json
new file mode 100644
index 0000000..19cae41
--- /dev/null
+++ b/font/timesi.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"Times-Italic","Up":-100,"Ut":50,"Cw":[250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611,611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833,611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722,500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500,556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980,389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333,400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611,889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722,722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,675,500,500,500,500,500,444,500,444]} \ No newline at end of file
diff --git a/font/zapfdingbats.json b/font/zapfdingbats.json
new file mode 100644
index 0000000..f65f7b2
--- /dev/null
+++ b/font/zapfdingbats.json
@@ -0,0 +1 @@
+{"Tp":"Core","Name":"ZapfDingbats","Up":-100,"Ut":50,"Cw":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939,549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692,786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776,768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873,761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317,276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918,927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874,760,946,771,865,771,888,967,888,831,873,927,970,918,0]} \ No newline at end of file
diff --git a/fpdf.go b/fpdf.go
new file mode 100644
index 0000000..e192c51
--- /dev/null
+++ b/fpdf.go
@@ -0,0 +1,2299 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+// Version: 1.7
+// Date: 2011-06-18
+// Author: Olivier PLATHEY
+// Port to Go: Kurt Jung, 2013-07-15
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "image"
+ "image/gif"
+ "image/png"
+ "io"
+ "io/ioutil"
+ "path"
+ "strings"
+ "time"
+)
+
+type fmtBuffer struct {
+ bytes.Buffer
+}
+
+func (b *fmtBuffer) printf(fmtStr string, args ...interface{}) {
+ b.Buffer.WriteString(fmt.Sprintf(fmtStr, args...))
+}
+
+// New returns a pointer to a new Fpdf instance. Its methods are subsequently
+// called to produce a single PDF document.
+//
+// orientationStr specifies the default page orientation. For portrait mode,
+// specify "P" or "Portrait". For landscape mode, specify "L" or "Landscape".
+// An empty string will be replaced with "P".
+//
+// unitStr specifies the unit of length used in size parameters for elements
+// other than fonts, which are always measured in points. Specify "pt" for
+// point, "mm" for millimeter, "cm" for centimeter, or "in" for inch. An empty
+// string will be replaced with "mm".
+//
+// sizeStr specifies the page size. Acceptable values are "A3", "A4", "A5",
+// "Letter", or "Legal". An empty string will be replaced with "A4".
+//
+// fontDirStr specifies the file system location in which font resources will
+// be found. An empty string is replaced with ".".
+func New(orientationStr, unitStr, sizeStr, fontDirStr string) (f *Fpdf) {
+ f = new(Fpdf)
+ if orientationStr == "" {
+ orientationStr = "P"
+ }
+ if unitStr == "" {
+ unitStr = "mm"
+ }
+ if sizeStr == "" {
+ sizeStr = "A4"
+ }
+ if fontDirStr == "" {
+ fontDirStr = "."
+ }
+ f.page = 0
+ f.n = 2
+ f.pages = make([]*bytes.Buffer, 0, 8)
+ f.pages = append(f.pages, bytes.NewBufferString("")) // pages[0] is unused (1-based)
+ f.pageSizes = make(map[int]sizeType)
+ f.state = 0
+ f.fonts = make(map[string]fontDefType)
+ f.fontFiles = make(map[string]fontFileType)
+ f.diffs = make([]string, 0, 8)
+ f.images = make(map[string]imageInfoType)
+ f.pageLinks = make([][]linkType, 0, 8)
+ f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0)) //pageLinks[0] is unused (1-based)
+ f.links = make([]intLinkType, 0, 8)
+ f.links = append(f.links, intLinkType{}) // links[0] is unused (1-based)
+ f.inHeader = false
+ f.inFooter = false
+ f.lasth = 0
+ f.fontFamily = ""
+ f.fontStyle = ""
+ f.fontSizePt = 12
+ f.underline = false
+ f.drawColor = "0 G"
+ f.fillColor = "0 g"
+ f.textColor = "0 g"
+ f.colorFlag = false
+ f.ws = 0
+ f.fontpath = fontDirStr
+ // Core fonts
+ f.coreFonts = map[string]bool{
+ "courier": true,
+ "helvetica": true,
+ "times": true,
+ "symbol": true,
+ "zapfdingbats": true,
+ }
+ // Scale factor
+ switch unitStr {
+ case "pt", "point":
+ f.k = 1.0
+ case "mm":
+ f.k = 72.0 / 25.4
+ case "cm":
+ f.k = 72.0 / 2.54
+ case "in", "inch":
+ f.k = 72.0
+ default:
+ f.err = fmt.Errorf("Incorrect unit %s", unitStr)
+ return
+ }
+ // Page sizes
+ f.stdpageSizes = make(map[string]sizeType)
+ f.stdpageSizes["a3"] = sizeType{841.89, 1190.55}
+ f.stdpageSizes["a4"] = sizeType{595.28, 841.89}
+ f.stdpageSizes["a5"] = sizeType{420.94, 595.28}
+ f.stdpageSizes["letter"] = sizeType{612, 792}
+ f.stdpageSizes["legal"] = sizeType{612, 1008}
+ f.defPageSize = f.getpagesizestr(sizeStr)
+ if f.err != nil {
+ return
+ }
+ f.curPageSize = f.defPageSize
+ // Page orientation
+ orientationStr = strings.ToLower(orientationStr)
+ switch orientationStr {
+ case "p", "portrait":
+ f.defOrientation = "P"
+ f.w = f.defPageSize.wd
+ f.h = f.defPageSize.ht
+ // dbg("Assign h: %8.2f", f.h)
+ case "l", "landscape":
+ f.defOrientation = "L"
+ f.w = f.defPageSize.ht
+ f.h = f.defPageSize.wd
+ default:
+ f.err = fmt.Errorf("Incorrect orientation: %s", orientationStr)
+ return
+ }
+ f.curOrientation = f.defOrientation
+ f.wPt = f.w * f.k
+ f.hPt = f.h * f.k
+ // Page margins (1 cm)
+ margin := 28.35 / f.k
+ f.SetMargins(margin, margin, margin)
+ // Interior cell margin (1 mm)
+ f.cMargin = margin / 10
+ // Line width (0.2 mm)
+ f.lineWidth = 0.567 / f.k
+ // Automatic page break
+ f.SetAutoPageBreak(true, 2*margin)
+ // Default display mode
+ f.SetDisplayMode("default", "default")
+ if f.err != nil {
+ return
+ }
+ f.acceptPageBreak = func() bool {
+ return f.autoPageBreak
+ }
+ // Enable compression
+ f.SetCompression(true)
+ // Set default PDF version number
+ f.pdfVersion = "1.3"
+ return
+}
+
+// Returns true if no processing errors have occurred.
+func (f *Fpdf) Ok() bool {
+ return f.err == nil
+}
+
+// Returns true if a processing error has occurred.
+func (f *Fpdf) Err() bool {
+ return f.err != nil
+}
+
+// Set the internal Fpdf error with formatted text to halt PDF generation; this
+// may facilitate error handling by application.
+//
+// See the documentation for printing in the standard fmt package for details
+// about fmtStr and args.
+func (f *Fpdf) SetErrorf(fmtStr string, args ...interface{}) {
+ if f.err == nil {
+ f.err = fmt.Errorf(fmtStr, args...)
+ }
+}
+
+// Summary of Fpdf instance that satisfies the fmt.Stringer interface.
+func (f *Fpdf) String() string {
+ return "Fpdf " + FPDF_VERSION
+}
+
+// Set error to halt PDF generation. This may facilitate error handling by
+// application. See also Ok(), Err() and Error().
+func (f *Fpdf) SetError(err error) {
+ if f.err == nil && err != nil {
+ f.err = err
+ }
+}
+
+// Returns the internal Fpdf error; this will be nil if no error has occurred.
+func (f *Fpdf) Error() error {
+ return f.err
+}
+
+// Defines the left, top and right margins. By default, they equal 1 cm. Call
+// this method to change them. If the value of the right margin is less than
+// zero, it is set to the same as the left margin.
+func (f *Fpdf) SetMargins(left, top, right float64) {
+ f.lMargin = left
+ f.tMargin = top
+ if right < 0 {
+ right = left
+ }
+ f.rMargin = right
+}
+
+// Defines the left margin. The method can be called before creating the first
+// page. If the current abscissa gets out of page, it is brought back to the
+// margin.
+func (f *Fpdf) SetLeftMargin(margin float64) {
+ f.lMargin = margin
+ if f.page > 0 && f.x < margin {
+ f.x = margin
+ }
+}
+
+// Set the location in the file system of the font and font definition files.
+func (f *Fpdf) SetFontLocation(fontDirStr string) {
+ f.fontpath = fontDirStr
+}
+
+// Sets the function that lets the application render the page header. The
+// specified function is automatically called by AddPage() and should not be
+// called directly by the application. The implementation in Fpdf is empty, so
+// you have to provide an appropriate function if you want page headers. fnc
+// will typically be a closure that has access to the Fpdf instance and other
+// document generation variables.
+func (f *Fpdf) SetHeaderFunc(fnc func()) {
+ f.headerFnc = fnc
+}
+
+// Sets the function that lets the application render the page footer. The
+// specified function is automatically called by AddPage() and Close() and
+// should not be called directly by the application. The implementation in Fpdf
+// is empty, so you have to provide an appropriate function if you want page
+// footers. fnc will typically be a closure that has access to the Fpdf
+// instance and other document generation variables.
+func (f *Fpdf) SetFooterFunc(fnc func()) {
+ f.footerFnc = fnc
+}
+
+// Defines the top margin. The method can be called before creating the first
+// page.
+func (f *Fpdf) SetTopMargin(margin float64) {
+ f.tMargin = margin
+}
+
+// Defines the right margin. The method can be called before creating the first
+// page.
+func (f *Fpdf) SetRightMargin(margin float64) {
+ f.rMargin = margin
+}
+
+// Enables or disables the automatic page breaking mode. When enabling, the
+// second parameter is the distance from the bottom of the page that defines
+// the triggering limit. By default, the mode is on and the margin is 2 cm.
+func (f *Fpdf) SetAutoPageBreak(auto bool, margin float64) {
+ f.autoPageBreak = auto
+ f.bMargin = margin
+ f.pageBreakTrigger = f.h - margin
+}
+
+// Defines the way the document is to be displayed by the viewer. The zoom
+// level can be set: pages can be displayed entirely on screen, occupy the full
+// width of the window, use real size, be scaled by a specific zooming factor
+// or use viewer default (configured in the Preferences menu of Adobe Reader).
+// The page layout can be specified too: single at once, continuous display,
+// two columns or viewer default.
+//
+// zoomStr can be "fullpage" to display the entire page on screen, "fullwidth"
+// to use maximum width of window, "real" to use real size (equivalent to 100%
+// zoom) or "default" to use viewer default mode.
+//
+// layoutStr can be "single" to display one page at once, "continuous" to
+// display pages continuously, "two" to display two pages on two columns, or
+// "default" to use viewer default mode.
+func (f *Fpdf) SetDisplayMode(zoomStr, layoutStr string) {
+ if f.err != nil {
+ return
+ }
+ if layoutStr == "" {
+ layoutStr = "default"
+ }
+ switch zoomStr {
+ case "fullpage", "fullwidth", "real", "default":
+ // || !is_string($zoom))
+ f.zoomMode = zoomStr
+ default:
+ f.err = fmt.Errorf("Incorrect zoom display mode: %s", zoomStr)
+ return
+ }
+ switch layoutStr {
+ case "single", "continuous", "two", "default":
+ f.layoutMode = layoutStr
+ default:
+ f.err = fmt.Errorf("Incorrect layout display mode: %s", layoutStr)
+ return
+ }
+}
+
+// Activates or deactivates page compression with zlib. When activated, the
+// internal representation of each page is compressed, which leads to a
+// compression ratio of about 2 for the resulting document. Compression is on
+// by default.
+func (f *Fpdf) SetCompression(compress bool) {
+ // if(function_exists('gzcompress'))
+ f.compress = compress
+ // else
+ // $this->compress = false;
+}
+
+// Defines the title of the document. isUTF8 indicates if the string is encoded
+// in ISO-8859-1 (false) or UTF-8 (true).
+func (f *Fpdf) SetTitle(titleStr string, isUTF8 bool) {
+ if isUTF8 {
+ titleStr = utf8toutf16(titleStr)
+ }
+ f.title = titleStr
+}
+
+// Defines the subject of the document. isUTF8 indicates if the string is encoded
+// in ISO-8859-1 (false) or UTF-8 (true).
+func (f *Fpdf) SetSubject(subjectStr string, isUTF8 bool) {
+ if isUTF8 {
+ subjectStr = utf8toutf16(subjectStr)
+ }
+ f.subject = subjectStr
+}
+
+// Defines the author of the document. isUTF8 indicates if the string is encoded
+// in ISO-8859-1 (false) or UTF-8 (true).
+func (f *Fpdf) SetAuthor(authorStr string, isUTF8 bool) {
+ if isUTF8 {
+ authorStr = utf8toutf16(authorStr)
+ }
+ f.author = authorStr
+}
+
+// Defines the keywords of the document. keywordStr is a space-delimited
+// string, for example "invoice August". isUTF8 indicates if the string is
+// encoded
+func (f *Fpdf) SetKeywords(keywordsStr string, isUTF8 bool) {
+ if isUTF8 {
+ keywordsStr = utf8toutf16(keywordsStr)
+ }
+ f.keywords = keywordsStr
+}
+
+// Defines the creator of the document. isUTF8 indicates if the string is encoded
+// in ISO-8859-1 (false) or UTF-8 (true).
+func (f *Fpdf) SetCreator(creatorStr string, isUTF8 bool) {
+ if isUTF8 {
+ creatorStr = utf8toutf16(creatorStr)
+ }
+ f.creator = creatorStr
+}
+
+// Defines an alias for the total number of pages. It will be substituted as
+// the document is closed.
+func (f *Fpdf) AliasNbPages(aliasStr string) {
+ if aliasStr == "" {
+ aliasStr = "{nb}"
+ }
+ f.aliasNbPagesStr = aliasStr
+}
+
+// Begin document
+func (f *Fpdf) open() {
+ f.state = 1
+}
+
+// Terminates the PDF document. It is not necessary to call this method
+// explicitly because Output() does it automatically. If the document contains
+// no page, AddPage() is called to prevent the generation of an invalid
+// document.
+func (f *Fpdf) Close() {
+ if f.err != nil {
+ return
+ }
+ if f.state == 3 {
+ return
+ }
+ if f.page == 0 {
+ f.AddPage()
+ if f.err != nil {
+ return
+ }
+ }
+ // Page footer
+ if f.footerFnc != nil {
+ f.inFooter = true
+ f.footerFnc()
+ f.inFooter = false
+ }
+ // Close page
+ f.endpage()
+ // Close document
+ f.enddoc()
+ return
+}
+
+// Adds a new page with non-default orientation or size. See AddPage() for more
+// details.
+//
+// See New() for a description of orientationStr.
+//
+// size specifies the size of the new page in the units established in New().
+func (f *Fpdf) AddPageFormat(orientationStr string, size sizeType) {
+ if f.err != nil {
+ return
+ }
+ if f.state == 0 {
+ f.open()
+ }
+ familyStr := f.fontFamily
+ style := f.fontStyle
+ if f.underline {
+ style += "U"
+ }
+ fontsize := f.fontSizePt
+ lw := f.lineWidth
+ dc := f.drawColor
+ fc := f.fillColor
+ tc := f.textColor
+ cf := f.colorFlag
+ if f.page > 0 {
+ // Page footer
+ if f.footerFnc != nil {
+ f.inFooter = true
+ f.footerFnc()
+ f.inFooter = false
+ }
+ // Close page
+ f.endpage()
+ }
+ // Start new page
+ f.beginpage(orientationStr, size)
+ // Set line cap style to square
+ f.out("2 J")
+ // Set line width
+ f.lineWidth = lw
+ f.outf("%.2f w", lw*f.k)
+ // Set font
+ if familyStr != "" {
+ f.SetFont(familyStr, style, fontsize)
+ if f.err != nil {
+ return
+ }
+ }
+ // Set colors
+ f.drawColor = dc
+ if dc != "0 G" {
+ f.out(dc)
+ }
+ f.fillColor = fc
+ if fc != "0 g" {
+ f.out(fc)
+ }
+ f.textColor = tc
+ f.colorFlag = cf
+ // Page header
+ if f.headerFnc != nil {
+ f.inHeader = true
+ f.headerFnc()
+ f.inHeader = false
+ }
+ // Restore line width
+ if f.lineWidth != lw {
+ f.lineWidth = lw
+ f.outf("%.2f w", lw*f.k)
+ }
+ // Restore font
+ if familyStr != "" {
+ f.SetFont(familyStr, style, fontsize)
+ if f.err != nil {
+ return
+ }
+ }
+ // Restore colors
+ if f.drawColor != dc {
+ f.drawColor = dc
+ f.out(dc)
+ }
+ if f.fillColor != fc {
+ f.fillColor = fc
+ f.out(fc)
+ }
+ f.textColor = tc
+ f.colorFlag = cf
+ return
+}
+
+// Adds a new page to the document. If a page is already present, the Footer()
+// method is called first to output the footer. Then the page is added, the
+// current position set to the top-left corner according to the left and top
+// margins, and Header() is called to display the header.
+//
+// The font which was set before calling is automatically restored. There is no
+// need to call SetFont() again if you want to continue with the same font. The
+// same is true for colors and line width.
+//
+// The origin of the coordinate system is at the top-left corner and increasing
+// ordinates go downwards.
+//
+// See AddPageFormat() for a version of this method that allows the page size
+// and orientation to be different than the default.
+func (f *Fpdf) AddPage() {
+ if f.err != nil {
+ return
+ }
+ // dbg("AddPage")
+ f.AddPageFormat(f.defOrientation, f.defPageSize)
+ return
+}
+
+// Returns the current page number.
+func (f *Fpdf) PageNo() int {
+ return f.page
+}
+
+type clrType struct {
+ r, g, b float64
+}
+
+func colorValue(r, g, b int) (clr clrType) {
+ clr.r = float64(r) / 255.0
+ clr.g = float64(g) / 255.0
+ clr.b = float64(b) / 255.0
+ return
+}
+
+func colorString(r, g, b int, grayStr, fullStr string) (str string) {
+ clr := colorValue(r, g, b)
+ if r == g && r == b {
+ str = sprintf("%.3f %s", clr.r, grayStr)
+ } else {
+ str = sprintf("%.3f %.3f %.3f %s", clr.r, clr.g, clr.b, fullStr)
+ }
+ return
+}
+
+// Defines the color used for all drawing operations (lines, rectangles and
+// cell borders). It is expressed in RGB components (0 - 255). The method can
+// be called before the first page is created and the value is retained from
+// page to page.
+func (f *Fpdf) SetDrawColor(r, g, b int) {
+ f.drawColor = colorString(r, g, b, "G", "RG")
+ if f.page > 0 {
+ f.out(f.drawColor)
+ }
+}
+
+// Defines the color used for all filling operations (filled rectangles and
+// cell backgrounds). It is expressed in RGB components (0 -255). The method
+// can be called before the first page is created and the value is retained
+// from page to page.
+func (f *Fpdf) SetFillColor(r, g, b int) {
+ f.fillColor = colorString(r, g, b, "g", "rg")
+ f.colorFlag = f.fillColor != f.textColor
+ if f.page > 0 {
+ f.out(f.fillColor)
+ }
+}
+
+// Defines the color used for text. It is expressed in RGB components (0 -
+// 255). The method can be called before the first page is created and the
+// value is retained from page to page.
+func (f *Fpdf) SetTextColor(r, g, b int) {
+ f.textColor = colorString(r, g, b, "g", "rg")
+ f.colorFlag = f.fillColor != f.textColor
+}
+
+// Returns the length of a string in user units. A font must be selected.
+func (f *Fpdf) GetStringWidth(s string) float64 {
+ w := 0
+ for _, ch := range s {
+ w += f.currentFont.Cw[ch]
+ }
+ return float64(w) * f.fontSize / 1000
+}
+
+// Defines the line width. By default, the value equals 0.2 mm. The method can
+// be called before the first page is created and the value is retained from
+// page to page.
+func (f *Fpdf) SetLineWidth(width float64) {
+ f.lineWidth = width
+ if f.page > 0 {
+ f.outf("%.2f w", width*f.k)
+ }
+}
+
+// Draws a line between (x1, y1) and (x2, y2).
+func (f *Fpdf) Line(x1, y1, x2, y2 float64) {
+ f.outf("%.2f %.2f m %.2f %.2f l S", x1*f.k, (f.h-y1)*f.k, x2*f.k, (f.h-y2)*f.k)
+}
+
+// Outputs a rectangle. It can be drawn (border only), filled (with no border)
+// or both. x and y specify the upper left corner of the rectangle. style can
+// be "F" for filled, "D" for outlined only, or "DF" or "FD" for outlined and
+// filled.
+func (f *Fpdf) Rect(x, y, w, h float64, style string) {
+ var op string
+ if style == "F" {
+ op = "f"
+ } else if style == "FD" || style == "DF" {
+ op = "B"
+ } else {
+ op = "S"
+ }
+ // dbg("(Rect) x %.2f f.k %.2f", x, f.k)
+ f.outf("%.2f %.2f %.2f %.2f re %s", x*f.k, (f.h-y)*f.k, w*f.k, -h*f.k, op)
+}
+
+// Imports a TrueType, OpenType or Type1 font and makes it available. It is
+// necessary to generate a font definition file first with the makefont
+// utility. It is not necessary to call this function for the core PDF fonts
+// (courier, helvetica, times, zapfdingbats).
+//
+// The JSON definition file (and the font file itself when embedding) must be
+// present in the font directory. If it is not found, the error "Could not
+// include font definition file" is set.
+//
+// family specifies the font family. The name can be chosen arbitrarily. If it
+// is a standard family name, it will override the corresponding font. This
+// string is used to subsequently set the font with the SetFont method.
+//
+// style specifies the font style. Acceptable values are (case insensitive) the
+// empty string for regular style, "B" for bold, "I" for italic, or "BI" or
+// "IB" for bold and italic combined.
+//
+// fileStr specifies the base name with ".json" extension of the font
+// definition file to be added. The file will be loaded from the font directory
+// specified in the call to New() or SetFontLocation().
+func (f *Fpdf) AddFont(familyStr, styleStr, fileStr string) {
+ if f.err != nil {
+ return
+ }
+ // dbg("Adding family [%s], style [%s]", familyStr, styleStr)
+ var ok bool
+ familyStr = strings.ToLower(familyStr)
+ if fileStr == "" {
+ fileStr = strings.Replace(familyStr, " ", "", -1) + strings.ToLower(styleStr) + ".json"
+ }
+ fileStr = path.Join(f.fontpath, fileStr)
+ styleStr = strings.ToUpper(styleStr)
+ if styleStr == "IB" {
+ styleStr = "BI"
+ }
+ fontkey := familyStr + styleStr
+ // dbg("fontkey [%s]", fontkey)
+ _, ok = f.fonts[fontkey]
+ if ok {
+ // dbg("fontkey found; returning")
+ return
+ }
+ var info fontDefType
+ info = f.loadfont(fileStr)
+ if f.err != nil {
+ return
+ }
+ info.I = len(f.fonts) + 1
+ // dbg("font [%s], I [%d]", fileStr, info.I)
+ if len(info.Diff) > 0 {
+ // Search existing encodings
+ n := -1
+ for j, str := range f.diffs {
+ if str == info.Diff {
+ n = j
+ break
+ }
+ }
+ if n < 0 {
+ n = len(f.diffs) + 1
+ f.diffs[n] = info.Diff
+ }
+ info.DiffN = n
+ }
+ // dbg("font [%s], type [%s]", info.File, info.Tp)
+ if len(info.File) > 0 {
+ // Embedded font
+ if info.Tp == "TrueType" {
+ f.fontFiles[info.File] = fontFileType{length1: int64(info.OriginalSize)}
+ } else {
+ f.fontFiles[info.File] = fontFileType{length1: int64(info.Size1), length2: int64(info.Size2)}
+ }
+ }
+ f.fonts[fontkey] = info
+ return
+}
+
+// Sets the font used to print character strings. It is mandatory to call this
+// method at least once before printing text or the resulting document will not
+// be valid.
+//
+// The font can be either a standard one or a font added via the AddFont()
+// method. Standard fonts use the Windows encoding cp1252 (Western Europe).
+//
+// The method can be called before the first page is created and the font is
+// kept from page to page. If you just wish to change the current font size, it
+// is simpler to call SetFontSize().
+//
+// Note: the font definition file must be accessible. An error is set if the
+// file cannot be read.
+//
+// familyStr specifies the font fammily. It can be either a name defined by
+// AddFont() or one of the standard families (case insensitive): "Courier" for
+// fixed-width, "Helvetica" or "Arial" for sans serif, "Times" for serif,
+// "Symbol" or "ZapfDingbats" for symbolic.
+//
+// styleStr can be "B" (bold), "I" (italic), "U" (underscore) or any
+// combination. The default value (specified with an empty string) is regular.
+// Bold and italic styles do not apply to Symbol and ZapfDingbats.
+//
+// size is the font size measured in points. The default value is the current
+// size. If no size has been specified since the beginning of the document, the
+// value taken is 12.
+func (f *Fpdf) SetFont(familyStr, styleStr string, size float64) {
+ // dbg("SetFont x %.2f, lMargin %.2f", f.x, f.lMargin)
+
+ if f.err != nil {
+ return
+ }
+ // dbg("SetFont")
+ var ok bool
+ if familyStr == "" {
+ familyStr = f.fontFamily
+ } else {
+ familyStr = strings.ToLower(familyStr)
+ }
+ styleStr = strings.ToUpper(styleStr)
+ f.underline = strings.Contains(styleStr, "U")
+ if f.underline {
+ styleStr = strings.Replace(styleStr, "U", "", -1)
+ }
+ if styleStr == "IB" {
+ styleStr = "BI"
+ }
+ if size == 0.0 {
+ size = f.fontSizePt
+ }
+ // Test if font is already selected
+ if f.fontFamily == familyStr && f.fontStyle == styleStr && f.fontSizePt == size {
+ return
+ }
+ // Test if font is already loaded
+ fontkey := familyStr + styleStr
+ _, ok = f.fonts[fontkey]
+ if !ok {
+ // Test if one of the core fonts
+ if familyStr == "arial" {
+ familyStr = "helvetica"
+ }
+ _, ok = f.coreFonts[familyStr]
+ if ok {
+ if familyStr == "symbol" || familyStr == "zapfdingbats" {
+ styleStr = ""
+ }
+ fontkey = familyStr + styleStr
+ _, ok = f.fonts[fontkey]
+ if !ok {
+ f.AddFont(familyStr, styleStr, "")
+ if f.err != nil {
+ return
+ }
+ }
+ } else {
+ f.err = fmt.Errorf("Undefined font: %s %s", familyStr, styleStr)
+ return
+ }
+ }
+ // Select it
+ f.fontFamily = familyStr
+ f.fontStyle = styleStr
+ f.fontSizePt = size
+ f.fontSize = size / f.k
+ f.currentFont = f.fonts[fontkey]
+ if f.page > 0 {
+ f.outf("BT /F%d %.2f Tf ET", f.currentFont.I, f.fontSizePt)
+ }
+ return
+}
+
+// Defines the size of the current font in points.
+func (f *Fpdf) SetFontSize(size float64) {
+ if f.fontSizePt == size {
+ return
+ }
+ f.fontSizePt = size
+ f.fontSize = size / f.k
+ if f.page > 0 {
+ f.outf("BT /F%d %.2f Tf ET", f.currentFont.I, f.fontSizePt)
+ }
+}
+
+// Creates a new internal link and returns its identifier. An internal link is
+// a clickable area which directs to another place within the document. The
+// identifier can then be passed to Cell(), Write(), Image() or Link(). The
+// destination is defined with SetLink().
+func (f *Fpdf) AddLink() int {
+ f.links = append(f.links, intLinkType{})
+ return len(f.links) - 1
+}
+
+// Defines the page and position a link points to. See AddLink().
+func (f *Fpdf) SetLink(link int, y float64, page int) {
+ if y == -1 {
+ y = f.y
+ }
+ if page == -1 {
+ page = f.page
+ }
+ f.links[link] = intLinkType{page, y}
+}
+
+// Add a new clickable link on current page
+func (f *Fpdf) newLink(x, y, w, h float64, link int, linkStr string) {
+ // linkList, ok := f.pageLinks[f.page]
+ // if !ok {
+ // linkList = make([]linkType, 0, 8)
+ // f.pageLinks[f.page] = linkList
+ // }
+ f.pageLinks[f.page] = append(f.pageLinks[f.page],
+ linkType{x * f.k, f.hPt - y*f.k, w * f.k, h * f.k, link, linkStr})
+}
+
+// Puts a link on a rectangular area of the page. Text or image links are
+// generally put via Cell(), Write() or Image(), but this method can be useful
+// for instance to define a clickable area inside an image. link is the value
+// returned by AddLink().
+func (f *Fpdf) Link(x, y, w, h float64, link int) {
+ f.newLink(x, y, w, h, link, "")
+}
+
+// Puts a link on a rectangular area of the page. Text or image links are
+// generally put via Cell(), Write() or Image(), but this method can be useful
+// for instance to define a clickable area inside an image. linkStr is the
+// target URL.
+func (f *Fpdf) LinkString(x, y, w, h float64, linkStr string) {
+ f.newLink(x, y, w, h, 0, linkStr)
+}
+
+// Prints a character string. The origin is on the left of the first character,
+// on the baseline. This method allows to place a string precisely on the page,
+// but it is usually easier to use Cell(), MultiCell() or Write() which are the
+// standard methods to print text.
+func (f *Fpdf) Text(x, y float64, txtStr string) {
+ s := sprintf("BT %.2f %.2f Td (%s) Tj ET", x*f.k, (f.h-y)*f.k, f.escape(txtStr))
+ if f.underline && txtStr != "" {
+ s += " " + f.dounderline(x, y, txtStr)
+ }
+ if f.colorFlag {
+ s = sprintf("q %s %s Q", f.textColor, s)
+ }
+ f.out(s)
+}
+
+// SetAcceptPageBreakFunc allows the application to control where page breaks
+// occur.
+//
+// fnc is an application function (typically a closure) that is called by the
+// library whenever a page break condition is met. The break is issued if true
+// is returned. The default implementation returns a value according to the
+// mode selected by SetAutoPageBreak. The function provided should not be
+// called by the application.
+//
+// See tutorial 4 for an example of this function.
+func (f *Fpdf) SetAcceptPageBreakFunc(fnc func() bool) {
+ f.acceptPageBreak = fnc
+}
+
+// Prints a cell (rectangular area) with optional borders, background color and
+// character string. The upper-left corner of the cell corresponds to the
+// current position. The text can be aligned or centered. After the call, the
+// current position moves to the right or to the next line. It is possible to
+// put a link on the text.
+//
+// If automatic page breaking is enabled and the cell goes beyond the limit, a
+// page break is done before outputting.
+//
+// w and h specify the width and height of the cell.
+//
+// txtStr specifies the text to display.
+//
+// borderStr specifies how the cell border will be drawn. An empty string
+// indicates no border, "1" indicates a full border, and one or more of "L",
+// "T", "R" and "B" indicate the left, top, right and bottom sides of the
+// border.
+//
+// ln indicates where the current position should go after the call. Possible
+// values are 0 (to the right), 1 (to the beginning of the next line), and 2
+// (below). Putting 1 is equivalent to putting 0 and calling Ln() just after.
+//
+// alignStr allows the text to be aligned to the right ("R"), center ("C") or
+// left ("L" or "").
+//
+// fill is true to paint the cell background or false to leave it transparent.
+//
+// link is the identifier returned by AddLink() or 0 for no internal link.
+//
+// linkStr is a target URL or empty for no external link. A non--zero value for
+// link takes precedence over linkStr.
+func (f *Fpdf) CellFormat(w, h float64, txtStr string, borderStr string, ln int, alignStr string, fill bool, link int, linkStr string) {
+ // dbg("CellFormat. h = %.2f, borderStr = %s", h, borderStr)
+ if f.err != nil {
+ return
+ }
+ borderStr = strings.ToUpper(borderStr)
+ k := f.k
+ if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
+ // Automatic page break
+ x := f.x
+ ws := f.ws
+ // dbg("auto page break, x %.2f, ws %.2f", x, ws)
+ if ws > 0 {
+ f.ws = 0
+ f.out("0 Tw")
+ }
+ f.AddPageFormat(f.curOrientation, f.curPageSize)
+ if f.err != nil {
+ return
+ }
+ f.x = x
+ if ws > 0 {
+ f.ws = ws
+ f.outf("%.3f Tw", ws*k)
+ }
+ }
+ if w == 0 {
+ w = f.w - f.rMargin - f.x
+ }
+ var s fmtBuffer
+ if fill || borderStr == "1" {
+ var op string
+ if fill {
+ if borderStr == "1" {
+ op = "B"
+ // dbg("border is '1', fill")
+ } else {
+ op = "f"
+ // dbg("border is empty, fill")
+ }
+ } else {
+ // dbg("border is '1', no fill")
+ op = "S"
+ }
+ /// dbg("(CellFormat) f.x %.2f f.k %.2f", f.x, f.k)
+ s.printf("%.2f %.2f %.2f %.2f re %s ", f.x*k, (f.h-f.y)*k, w*k, -h*k, op)
+ }
+ if len(borderStr) > 0 && borderStr != "1" {
+ // dbg("border is '%s', no fill", borderStr)
+ x := f.x
+ y := f.y
+ if strings.Index(borderStr, "L") >= 0 {
+ s.printf("%.2f %.2f m %.2f %.2f l S ", x*k, (f.h-y)*k, x*k, (f.h-(y+h))*k)
+ }
+ if strings.Index(borderStr, "T") >= 0 {
+ s.printf("%.2f %.2f m %.2f %.2f l S ", x*k, (f.h-y)*k, (x+w)*k, (f.h-y)*k)
+ }
+ if strings.Index(borderStr, "R") >= 0 {
+ s.printf("%.2f %.2f m %.2f %.2f l S ", (x+w)*k, (f.h-y)*k, (x+w)*k, (f.h-(y+h))*k)
+ }
+ if strings.Index(borderStr, "B") >= 0 {
+ s.printf("%.2f %.2f m %.2f %.2f l S ", x*k, (f.h-(y+h))*k, (x+w)*k, (f.h-(y+h))*k)
+ }
+ }
+ if len(txtStr) > 0 {
+ var dx float64
+ if alignStr == "R" {
+ dx = w - f.cMargin - f.GetStringWidth(txtStr)
+ } else if alignStr == "C" {
+ dx = (w - f.GetStringWidth(txtStr)) / 2
+ // dbg("center cell, dx %.2f\n", dx)
+ } else {
+ dx = f.cMargin
+ }
+ if f.colorFlag {
+ s.printf("q %s ", f.textColor)
+ }
+ txt2 := strings.Replace(txtStr, "\\", "\\\\", -1)
+ txt2 = strings.Replace(txt2, "(", "\\(", -1)
+ txt2 = strings.Replace(txt2, ")", "\\)", -1)
+ // if strings.Contains(txt2, "end of excerpt") {
+ // dbg("f.h %.2f, f.y %.2f, h %.2f, f.fontSize %.2f, k %.2f", f.h, f.y, h, f.fontSize, k)
+ // }
+ s.printf("BT %.2f %.2f Td (%s) Tj ET", (f.x+dx)*k, (f.h-(f.y+.5*h+.3*f.fontSize))*k, txt2)
+ //BT %.2F %.2F Td (%s) Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
+ if f.underline {
+ s.printf(" %s", f.dounderline(f.x+dx, f.y+.5*h+.3*f.fontSize, txtStr))
+ }
+ if f.colorFlag {
+ s.printf(" Q")
+ }
+ if link > 0 || len(linkStr) > 0 {
+ f.newLink(f.x+dx, f.y+.5*h-.5*f.fontSize, f.GetStringWidth(txtStr), f.fontSize, link, linkStr)
+ }
+ }
+ str := s.String()
+ if len(str) > 0 {
+ f.out(str)
+ }
+ f.lasth = h
+ if ln > 0 {
+ // Go to next line
+ f.y += h
+ if ln == 1 {
+ f.x = f.lMargin
+ }
+ } else {
+ f.x += w
+ }
+ return
+}
+
+// A simpler version of CellFormat with no fill, border, links or special
+// alignment.
+func (f *Fpdf) Cell(w, h float64, txtStr string) {
+ // dbg("Cell. h is %.2f", h)
+ f.CellFormat(w, h, txtStr, "", 0, "L", false, 0, "")
+}
+
+// This method allows printing text with line breaks. They can be automatic (as
+// soon as the text reaches the right border of the cell) or explicit (via the
+// \n character). As many cells as necessary are output, one below the other.
+//
+// Text can be aligned, centered or justified. The cell block can be framed and
+// the background painted. See CellFormat() for more details.
+//
+// w is the width of the cells. A value of zero indicates cells that reach to
+// the right margin.
+//
+// h indicates the line height of each cell in the unit of measure specified in New().
+func (f *Fpdf) MultiCell(w, h float64, txtStr, borderStr, alignStr string, fill bool) {
+ // dbg("MultiCell")
+ if alignStr == "" {
+ alignStr = "J"
+ }
+ cw := &f.currentFont.Cw
+ if w == 0 {
+ w = f.w - f.rMargin - f.x
+ }
+ wmax := (w - 2*f.cMargin) * 1000 / f.fontSize
+ s := strings.Replace(txtStr, "\r", "", -1)
+ nb := len(s)
+ // if nb > 0 && s[nb-1:nb] == "\n" {
+ if nb > 0 && []byte(s)[nb-1] == '\n' {
+ nb--
+ s = s[0:nb]
+ }
+ // dbg("[%s]\n", s)
+ var b, b2 string
+ b = "0"
+ if len(borderStr) > 0 {
+ if borderStr == "1" {
+ borderStr = "LTRB"
+ b = "LRT"
+ b2 = "LR"
+ } else {
+ b2 = ""
+ if strings.Contains(borderStr, "L") {
+ b += "L"
+ }
+ if strings.Contains(borderStr, "R") {
+ b2 += "R"
+ }
+ if strings.Contains(borderStr, "T") {
+ b = b2 + "T"
+ } else {
+ b = b2
+ }
+ }
+ }
+ sep := -1
+ i := 0
+ j := 0
+ l := 0.0
+ ls := 0.0
+ ns := 0
+ nl := 1
+ for i < nb {
+ // Get next character
+ c := []byte(s)[i]
+ if c == '\n' {
+ // Explicit line break
+ if f.ws > 0 {
+ f.ws = 0
+ f.out("0 Tw")
+ }
+ f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
+ i++
+ sep = -1
+ j = i
+ l = 0
+ ns = 0
+ nl++
+ if len(borderStr) > 0 && nl == 2 {
+ b = b2
+ }
+ continue
+ }
+ if c == ' ' {
+ sep = i
+ ls = l
+ ns++
+ }
+ l += float64(cw[c])
+ if l > wmax {
+ // Automatic line break
+ if sep == -1 {
+ if i == j {
+ i++
+ }
+ if f.ws > 0 {
+ f.ws = 0
+ f.out("0 Tw")
+ }
+ f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
+ } else {
+ if alignStr == "J" {
+ if ns > 1 {
+ f.ws = (wmax - ls) / 1000 * f.fontSize / float64(ns-1)
+ } else {
+ f.ws = 0
+ }
+ f.outf("%.3f Tw", f.ws*f.k)
+ }
+ f.CellFormat(w, h, s[j:sep], b, 2, alignStr, fill, 0, "")
+ i = sep + 1
+ }
+ sep = -1
+ j = i
+ l = 0
+ ns = 0
+ nl++
+ if len(borderStr) > 0 && nl == 2 {
+ b = b2
+ }
+ } else {
+ i++
+ }
+ }
+ // Last chunk
+ if f.ws > 0 {
+ f.ws = 0
+ f.out("0 Tw")
+ }
+ if len(borderStr) > 0 && !strings.Contains(borderStr, "B") {
+ b += "B"
+ }
+ f.CellFormat(w, h, s[j:i], b, 2, alignStr, fill, 0, "")
+ f.x = f.lMargin
+}
+
+// Output text in flowing mode
+func (f *Fpdf) write(h float64, txtStr string, link int, linkStr string) {
+ // dbg("Write")
+ cw := &f.currentFont.Cw
+ w := f.w - f.rMargin - f.x
+ wmax := (w - 2*f.cMargin) * 1000 / f.fontSize
+ s := strings.Replace(txtStr, "\r", "", -1)
+ nb := len(s)
+ sep := -1
+ i := 0
+ j := 0
+ l := 0.0
+ nl := 1
+ for i < nb {
+ // Get next character
+ c := []byte(s)[i]
+ if c == '\n' {
+ // Explicit line break
+ f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
+ i++
+ sep = -1
+ j = i
+ l = 0.0
+ if nl == 1 {
+ f.x = f.lMargin
+ w = f.w - f.rMargin - f.x
+ wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
+ }
+ nl++
+ continue
+ }
+ if c == ' ' {
+ sep = i
+ }
+ l += float64(cw[c])
+ if l > wmax {
+ // Automatic line break
+ if sep == -1 {
+ if f.x > f.lMargin {
+ // Move to next line
+ f.x = f.lMargin
+ f.y += h
+ w = f.w - f.rMargin - f.x
+ wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
+ i++
+ nl++
+ continue
+ }
+ if i == j {
+ i++
+ }
+ f.CellFormat(w, h, s[j:i], "", 2, "", false, link, linkStr)
+ } else {
+ f.CellFormat(w, h, s[j:sep], "", 2, "", false, link, linkStr)
+ i = sep + 1
+ }
+ sep = -1
+ j = i
+ l = 0.0
+ if nl == 1 {
+ f.x = f.lMargin
+ w = f.w - f.rMargin - f.x
+ wmax = (w - 2*f.cMargin) * 1000 / f.fontSize
+ }
+ nl++
+ } else {
+ i++
+ }
+ }
+ // Last chunk
+ if i != j {
+ f.CellFormat(l/1000*f.fontSize, h, s[j:], "", 0, "", false, link, linkStr)
+ }
+}
+
+// This method prints text from the current position. When the right margin is
+// reached (or the \n character is met) a line break occurs and text continues
+// from the left margin. Upon method exit, the current position is left just at
+// the end of the text.
+//
+// It is possible to put a link on the text.
+//
+// h indicates the line height in the unit of measure specified in New().
+func (f *Fpdf) Write(h float64, txtStr string) {
+ f.write(h, txtStr, 0, "")
+}
+
+// Write text that when clicked launches an external URL. See Write() for
+// argument details.
+func (f *Fpdf) WriteLinkString(h float64, displayStr, targetStr string) {
+ f.write(h, displayStr, 0, targetStr)
+}
+
+// Write text that when clicked jumps to another location in the PDF. linkId is
+// an identifier returned by AddLink(). See Write() for argument details.
+func (f *Fpdf) WriteLinkId(h float64, displayStr string, linkId int) {
+ f.write(h, displayStr, linkId, "")
+}
+
+// Performs a line break. The current abscissa goes back to the left margin and
+// the ordinate increases by the amount passed in parameter. A negative value
+// of h indicates the height of the last printed cell.
+func (f *Fpdf) Ln(h float64) {
+ f.x = f.lMargin
+ if h < 0 {
+ f.y += f.lasth
+ } else {
+ f.y += h
+ }
+}
+
+// Puts a JPEG, PNG or GIF image. The size it will take on the page can be
+// specified in different ways. If both w and h are 0, the image is rendered at
+// 96 dpi. If either w or h is zero, it will be calculated from the other
+// dimension so that the aspect ratio is maintained. If w and h are negative,
+// their absolute values indicate their dpi extents.
+//
+// Supported JPEG formats are 24 bit, 32 bit and gray scale. Supported PNG
+// formats are 24 bit, indexed color, and 8 bit indexed gray scale. If a GIF
+// image is animated, only the first frame is rendered. Transparency is
+// supported. It is possible to put a link on the image. Remark: if an image is
+// used several times, only one copy is embedded in the file.
+//
+// If x is negative, the current abscissa is used.
+//
+// tp specifies the image format. Possible values are (case insensitive):
+// "JPG", "JPEG", "PNG" and "GIF". If not specified, the type is inferred from
+// the file extension.
+func (f *Fpdf) Image(fileStr string, x, y, w, h float64, flow bool, tp string, link int, linkStr string) {
+ if f.err != nil {
+ return
+ }
+ var info imageInfoType
+ var ok bool
+ info, ok = f.images[fileStr]
+ if !ok {
+ // First use of this image, get info
+ if tp == "" {
+ pos := strings.LastIndex(fileStr, ".")
+ if pos < 0 {
+ f.err = fmt.Errorf("Image file has no extension and no type was specified: %s", fileStr)
+ return
+ }
+ tp = fileStr[pos+1:]
+ }
+ tp = strings.ToLower(tp)
+ if tp == "jpeg" {
+ tp = "jpg"
+ }
+ switch tp {
+ case "jpg":
+ info = f.parsejpg(fileStr)
+ case "png":
+ info = f.parsepng(fileStr)
+ case "gif":
+ info = f.parsegif(fileStr)
+ default:
+ f.err = fmt.Errorf("Unsupported image type: %s", tp)
+ }
+ if f.err != nil {
+ return
+ }
+ info.i = len(f.images) + 1
+ f.images[fileStr] = info
+ }
+ // Automatic width and height calculation if needed
+ if w == 0 && h == 0 {
+ // Put image at 96 dpi
+ w = -96
+ h = -96
+ }
+ if w < 0 {
+ w = -info.w * 72.0 / w / f.k
+ }
+ if h < 0 {
+ h = -info.h * 72.0 / h / f.k
+ }
+ if w == 0 {
+ w = h * info.w / info.h
+ }
+ if h == 0 {
+ h = w * info.h / info.w
+ }
+ // Flowing mode
+ if flow {
+ if f.y+h > f.pageBreakTrigger && !f.inHeader && !f.inFooter && f.acceptPageBreak() {
+ // Automatic page break
+ x2 := f.x
+ f.AddPageFormat(f.curOrientation, f.curPageSize)
+ if f.err != nil {
+ return
+ }
+ f.x = x2
+ }
+ y = f.y
+ f.y += h
+ }
+ if x < 0 {
+ x = f.x
+ }
+ // dbg("h %.2f", h)
+ // q 85.04 0 0 NaN 28.35 NaN cm /I2 Do Q
+ f.outf("q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q", w*f.k, h*f.k, x*f.k, (f.h-(y+h))*f.k, info.i)
+ if link > 0 || len(linkStr) > 0 {
+ f.newLink(x, y, w, h, link, linkStr)
+ }
+ return
+}
+
+// Returns the abscissa of the current position.
+func (f *Fpdf) GetX() float64 {
+ return f.x
+}
+
+// Defines the abscissa of the current position. If the passed value is
+// negative, it is relative to the right of the page.
+func (f *Fpdf) SetX(x float64) {
+ if x >= 0 {
+ f.x = x
+ } else {
+ f.x = f.w + x
+ }
+}
+
+// Returns the ordinate of the current position.
+func (f *Fpdf) GetY() float64 {
+ return f.y
+}
+
+// Moves the current abscissa back to the left margin and sets the ordinate. If
+// the passed value is negative, it is relative to the bottom of the page.
+func (f *Fpdf) SetY(y float64) {
+ // dbg("SetY x %.2f, lMargin %.2f", f.x, f.lMargin)
+ f.x = f.lMargin
+ if y >= 0 {
+ f.y = y
+ } else {
+ f.y = f.h + y
+ }
+}
+
+// Defines the abscissa and ordinate of the current position. If the passed
+// values are negative, they are relative respectively to the right and bottom
+// of the page.
+func (f *Fpdf) SetXY(x, y float64) {
+ f.SetY(y)
+ f.SetX(x)
+}
+
+// Send the PDF document to the writer specified by w. This method will close
+// w, even if an error is detected and no document is produced.
+func (f *Fpdf) Output(w io.WriteCloser) error {
+ defer w.Close()
+ if f.err != nil {
+ return f.err
+ }
+ // dbg("Output")
+ if f.state < 3 {
+ f.Close()
+ }
+ _, err := f.buffer.WriteTo(w)
+ if err != nil {
+ f.err = err
+ }
+ return f.err
+}
+
+func (f *Fpdf) getpagesizestr(sizeStr string) (size sizeType) {
+ if f.err != nil {
+ return
+ }
+ sizeStr = strings.ToLower(sizeStr)
+ // dbg("Size [%s]", sizeStr)
+ if sizeStr == "" {
+ // dbg("not found %s", sizeStr)
+ size = f.defPageSize
+ } else {
+ var ok bool
+ size, ok = f.stdpageSizes[sizeStr]
+ if ok {
+ // dbg("found %s", sizeStr)
+ size.wd /= f.k
+ size.ht /= f.k
+
+ } else {
+ f.err = fmt.Errorf("Unknown page size %s", sizeStr)
+ }
+ }
+ return
+}
+
+func (f *Fpdf) _getpagesize(size sizeType) sizeType {
+ if size.wd > size.ht {
+ size.wd, size.ht = size.ht, size.wd
+ }
+ return size
+}
+
+func (f *Fpdf) beginpage(orientationStr string, size sizeType) {
+ if f.err != nil {
+ return
+ }
+ f.page++
+ f.pages = append(f.pages, bytes.NewBufferString(""))
+ f.pageLinks = append(f.pageLinks, make([]linkType, 0, 0))
+ f.state = 2
+ f.x = f.lMargin
+ f.y = f.tMargin
+ f.fontFamily = ""
+ // Check page size and orientation
+ if orientationStr == "" {
+ orientationStr = f.defOrientation
+ } else {
+ orientationStr = strings.ToUpper(orientationStr[0:1])
+ }
+ if orientationStr != f.curOrientation || size.wd != f.curPageSize.wd || size.ht != f.curPageSize.ht {
+ // New size or orientation
+ if orientationStr == "P" {
+ f.w = size.wd
+ f.h = size.ht
+ } else {
+ f.w = size.ht
+ f.h = size.wd
+ }
+ f.wPt = f.w * f.k
+ f.hPt = f.h * f.k
+ f.pageBreakTrigger = f.h - f.bMargin
+ f.curOrientation = orientationStr
+ f.curPageSize = size
+ }
+ if orientationStr != f.defOrientation || size.wd != f.defPageSize.wd || size.ht != f.defPageSize.ht {
+ f.pageSizes[f.page] = sizeType{f.wPt, f.hPt}
+ }
+ return
+}
+
+func (f *Fpdf) endpage() {
+ f.state = 1
+}
+
+// Load a font definition file from the font directory
+func (f *Fpdf) loadfont(fontStr string) (def fontDefType) {
+ if f.err != nil {
+ return
+ }
+ // dbg("Loading font [%s]", fontStr)
+ buf, err := ioutil.ReadFile(fontStr)
+ if err != nil {
+ f.err = err
+ return
+ }
+ err = json.Unmarshal(buf, &def)
+ if err != nil {
+ f.err = err
+ }
+ // dump(def)
+ return
+}
+
+// Escape special characters in strings
+func (f *Fpdf) escape(s string) string {
+ s = strings.Replace(s, "\\", "\\\\", -1)
+ s = strings.Replace(s, "(", "\\(", -1)
+ s = strings.Replace(s, ")", "\\)", -1)
+ s = strings.Replace(s, "\r", "\\r", -1)
+ return s
+}
+
+// Format a text string
+func (f *Fpdf) textstring(s string) string {
+ return "(" + f.escape(s) + ")"
+}
+
+func blankCount(str string) (count int) {
+ l := len(str)
+ for j := 0; j < l; j++ {
+ if byte(' ') == str[j] {
+ count++
+ }
+ }
+ return
+}
+
+// Underline text
+func (f *Fpdf) dounderline(x, y float64, txt string) string {
+ up := float64(f.currentFont.Up)
+ ut := float64(f.currentFont.Ut)
+ w := f.GetStringWidth(txt) + f.ws*float64(blankCount(txt))
+ return sprintf("%.2f %.2f %.2f %.2f re f", x*f.k,
+ (f.h-(y-up/1000*f.fontSize))*f.k, w*f.k, -ut/1000*f.fontSizePt)
+}
+
+func bufEqual(buf []byte, str string) bool {
+ return string(buf[0:len(str)]) == str
+}
+
+func be16(buf []byte) int {
+ return 256*int(buf[0]) + int(buf[1])
+}
+
+// Extract info from a JPEG file
+// Thank you, Michael Petrov: http://www.64lines.com/jpeg-width-height
+func (f *Fpdf) parsejpg(fileStr string) (info imageInfoType) {
+ var err error
+ info.data, err = ioutil.ReadFile(fileStr)
+ if err != nil {
+ f.err = err
+ return
+ }
+ if bufEqual(info.data[0:], "\xff\xd8\xff\xe0") && bufEqual(info.data[6:], "JFIF\x00") {
+ dataLen := len(info.data)
+ pos := 4
+ blockLen := be16(info.data[4:])
+ loop := true
+ for pos+blockLen < dataLen && loop {
+ pos += blockLen
+ if info.data[pos] != 0xff {
+ f.err = fmt.Errorf("Unexpected JPEG segment header: %s\n", fileStr)
+ return
+ }
+ if info.data[pos+1] == 0xc0 {
+ // Start-of-frame segment
+ info.h = float64(be16(info.data[pos+5:]))
+ info.w = float64(be16(info.data[pos+7:]))
+ info.bpc = int(info.data[pos+4])
+ compNum := info.data[pos+9]
+ switch compNum {
+ case 3:
+ info.cs = "DeviceRGB"
+ case 4:
+ info.cs = "DeviceCMYK"
+ case 1:
+ info.cs = "DeviceGray"
+ default:
+ f.err = fmt.Errorf("JPEG buffer has unsupported color space (%d)", compNum)
+ return
+ }
+ loop = false
+ } else {
+ pos += 2
+ blockLen = be16(info.data[pos:])
+ }
+ }
+ } else {
+ f.err = fmt.Errorf("Improper JPEG header: %s\n", fileStr)
+ }
+ info.f = "DCTDecode"
+ return
+}
+
+// Extract info from a PNG file
+func (f *Fpdf) parsepng(fileStr string) (info imageInfoType) {
+ buf, err := bufferFromFile(fileStr)
+ if err != nil {
+ f.err = err
+ return
+ }
+ return f.parsepngstream(buf)
+}
+
+func (f *Fpdf) readBeInt32(buf *bytes.Buffer) (val int32) {
+ err := binary.Read(buf, binary.BigEndian, &val)
+ if err != nil {
+ f.err = err
+ }
+ return
+}
+
+func (f *Fpdf) readByte(buf *bytes.Buffer) (val byte) {
+ err := binary.Read(buf, binary.BigEndian, &val)
+ if err != nil {
+ f.err = err
+ }
+ return
+}
+
+func (f *Fpdf) parsepngstream(buf *bytes.Buffer) (info imageInfoType) {
+ // Check signature
+ if string(buf.Next(8)) != "\x89PNG\x0d\x0a\x1a\x0a" {
+ f.err = fmt.Errorf("Not a PNG buffer")
+ return
+ }
+ // Read header chunk
+ _ = buf.Next(4)
+ if string(buf.Next(4)) != "IHDR" {
+ f.err = fmt.Errorf("Incorrect PNG buffer")
+ return
+ }
+ w := f.readBeInt32(buf)
+ h := f.readBeInt32(buf)
+ bpc := f.readByte(buf)
+ if bpc > 8 {
+ f.err = fmt.Errorf("16-bit depth not supported in PNG file")
+ }
+ ct := f.readByte(buf)
+ var colspace string
+ colorVal := 1
+ switch ct {
+ case 0, 4:
+ colspace = "DeviceGray"
+ case 2, 6:
+ colspace = "DeviceRGB"
+ colorVal = 3
+ case 3:
+ colspace = "Indexed"
+ default:
+ f.err = fmt.Errorf("Unknown color type in PNG buffer: %d", ct)
+ }
+ if f.err != nil {
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("'Unknown compression method in PNG buffer")
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("'Unknown filter method in PNG buffer")
+ return
+ }
+ if f.readByte(buf) != 0 {
+ f.err = fmt.Errorf("Interlacing not supported in PNG buffer")
+ return
+ }
+ _ = buf.Next(4)
+ dp := sprintf("/Predictor 15 /Colors %d /BitsPerComponent %d /Columns %d", colorVal, bpc, w)
+ // Scan chunks looking for palette, transparency and image data
+ pal := make([]byte, 0, 32)
+ var trns []int
+ data := make([]byte, 0, 32)
+ loop := true
+ for loop {
+ n := int(f.readBeInt32(buf))
+ // dbg("Loop [%d]", n)
+ switch string(buf.Next(4)) {
+ case "PLTE":
+ // dbg("PLTE")
+ // Read palette
+ pal = buf.Next(n)
+ _ = buf.Next(4)
+ case "tRNS":
+ // dbg("tRNS")
+ // Read transparency info
+ t := buf.Next(n)
+ if ct == 0 {
+ trns = []int{int(t[1])} // ord(substr($t,1,1)));
+ } else if ct == 2 {
+ trns = []int{int(t[1]), int(t[3]), int(t[5])} // array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
+ } else {
+ pos := strings.Index(string(t), "\x00")
+ if pos >= 0 {
+ trns = []int{pos} // array($pos);
+ }
+ }
+ _ = buf.Next(4)
+ case "IDAT":
+ // dbg("IDAT")
+ // Read image data block
+ data = append(data, buf.Next(n)...)
+ _ = buf.Next(4)
+ case "IEND":
+ // dbg("IEND")
+ loop = false
+ default:
+ // dbg("default")
+ _ = buf.Next(n + 4)
+ }
+ if loop {
+ loop = n > 0
+ }
+ }
+ if colspace == "Indexed" && len(pal) == 0 {
+ f.err = fmt.Errorf("Missing palette in PNG buffer")
+ }
+ info.w = float64(w)
+ info.h = float64(h)
+ info.cs = colspace
+ info.bpc = int(bpc)
+ info.f = "FlateDecode"
+ info.dp = dp
+ info.pal = pal
+ info.trns = trns
+ // dbg("ct [%d]", ct)
+ if ct >= 4 {
+ // Separate alpha and color channels
+ var err error
+ data, err = sliceUncompress(data)
+ if err != nil {
+ f.err = err
+ return
+ }
+ var color, alpha bytes.Buffer
+ if ct == 4 {
+ // Gray image
+ width := int(w)
+ height := int(h)
+ length := 2 * width
+ var pos, elPos int
+ for i := 0; i < height; i++ {
+ pos = (1 + length) * i
+ color.WriteByte(data[pos])
+ alpha.WriteByte(data[pos])
+ elPos = pos + 1
+ for k := 0; k < width; k++ {
+ color.WriteByte(data[elPos])
+ alpha.WriteByte(data[elPos+1])
+ elPos += 2
+ }
+ }
+ } else {
+ // RGB image
+ width := int(w)
+ height := int(h)
+ length := 4 * width
+ var pos, elPos int
+ for i := 0; i < height; i++ {
+ pos = (1 + length) * i
+ color.WriteByte(data[pos])
+ alpha.WriteByte(data[pos])
+ elPos = pos + 1
+ for k := 0; k < width; k++ {
+ color.Write(data[elPos : elPos+3])
+ alpha.WriteByte(data[elPos+3])
+ elPos += 4
+ }
+ }
+ }
+ data = sliceCompress(color.Bytes())
+ info.smask = sliceCompress(alpha.Bytes())
+ if f.pdfVersion < "1.4" {
+ f.pdfVersion = "1.4"
+ }
+ }
+ info.data = data
+ return
+}
+
+// Extract info from a GIF file (via PNG conversion)
+func (f *Fpdf) parsegif(fileStr string) (info imageInfoType) {
+ data, err := ioutil.ReadFile(fileStr)
+ if err != nil {
+ f.err = err
+ return
+ }
+ gifBuf := bytes.NewBuffer(data)
+ var img image.Image
+ img, err = gif.Decode(gifBuf)
+ if err != nil {
+ f.err = err
+ return
+ }
+ pngBuf := new(bytes.Buffer)
+ err = png.Encode(pngBuf, img)
+ if err != nil {
+ f.err = err
+ return
+ }
+ return f.parsepngstream(pngBuf)
+}
+
+// Begin a new object
+func (f *Fpdf) newobj() {
+ // dbg("newobj")
+ f.n++
+ for j := len(f.offsets); j <= f.n; j++ {
+ f.offsets = append(f.offsets, 0)
+ }
+ f.offsets[f.n] = f.buffer.Len()
+ f.outf("%d 0 obj", f.n)
+}
+
+func (f *Fpdf) putstream(b []byte) {
+ // dbg("putstream")
+ f.out("stream")
+ f.out(string(b))
+ f.out("endstream")
+}
+
+// Add a line to the document
+func (f *Fpdf) out(s string) {
+ if f.state == 2 {
+ f.pages[f.page].WriteString(s)
+ f.pages[f.page].WriteString("\n")
+ } else {
+ f.buffer.WriteString(s)
+ f.buffer.WriteString("\n")
+ }
+}
+
+// Add a buffered line to the document
+func (f *Fpdf) outbuf(b *bytes.Buffer) {
+ if f.state == 2 {
+ f.pages[f.page].ReadFrom(b)
+ f.pages[f.page].WriteString("\n")
+ } else {
+ f.buffer.ReadFrom(b)
+ f.buffer.WriteString("\n")
+ }
+}
+
+// Add a formatted line to the document
+func (f *Fpdf) outf(fmtStr string, args ...interface{}) {
+ f.out(sprintf(fmtStr, args...))
+}
+
+func (f *Fpdf) putpages() {
+ var wPt, hPt float64
+ var pageSize sizeType
+ // var linkList []linkType
+ var ok bool
+ nb := f.page
+ if len(f.aliasNbPagesStr) > 0 {
+ // Replace number of pages
+ nbStr := sprintf("%d", nb)
+ for n := 1; n <= nb; n++ {
+ s := f.pages[n].String()
+ if strings.Contains(s, f.aliasNbPagesStr) {
+ s = strings.Replace(s, f.aliasNbPagesStr, nbStr, -1)
+ f.pages[n].Truncate(0)
+ f.pages[n].WriteString(s)
+ }
+ }
+ }
+ if f.defOrientation == "P" {
+ wPt = f.defPageSize.wd * f.k
+ hPt = f.defPageSize.ht * f.k
+ } else {
+ wPt = f.defPageSize.ht * f.k
+ hPt = f.defPageSize.wd * f.k
+ }
+ for n := 1; n <= nb; n++ {
+ // Page
+ f.newobj()
+ f.out("<</Type /Page")
+ f.out("/Parent 1 0 R")
+ pageSize, ok = f.pageSizes[n]
+ if ok {
+ f.outf("/MediaBox [0 0 %.2f %.2f]", pageSize.wd, pageSize.ht)
+ }
+ f.out("/Resources 2 0 R")
+ // Links
+ if len(f.pageLinks[n]) > 0 {
+ var annots fmtBuffer
+ annots.printf("/Annots [")
+ for _, pl := range f.pageLinks[n] {
+ annots.printf("<</Type /Annot /Subtype /Link /Rect [%.2f %.2f %.2f %.2f] /Border [0 0 0] ",
+ pl.x, pl.y, pl.x+pl.wd, pl.y-pl.ht)
+ if pl.link == 0 {
+ annots.printf("/A <</S /URI /URI %s>>>>", f.textstring(pl.linkStr))
+ } else {
+ l := f.links[pl.link]
+ var sz sizeType
+ var h float64
+ sz, ok = f.pageSizes[l.page]
+ if ok {
+ h = sz.ht
+ } else {
+ h = hPt
+ }
+ // dbg("h [%.2f], l.y [%.2f] f.k [%.2f]\n", h, l.y, f.k)
+ annots.printf("/Dest [%d 0 R /XYZ 0 %.2f null]>>", 1+2*l.page, h-l.y*f.k)
+ }
+ }
+ annots.printf("]")
+ f.out(annots.String())
+ }
+ if f.pdfVersion > "1.3" {
+ f.out("/Group <</Type /Group /S /Transparency /CS /DeviceRGB>>")
+ }
+ f.outf("/Contents %d 0 R>>", f.n+1)
+ f.out("endobj")
+ // Page content
+ f.newobj()
+ if f.compress {
+ data := sliceCompress(f.pages[n].Bytes())
+ f.outf("<</Filter /FlateDecode /Length %d>>", len(data))
+ f.putstream(data)
+ } else {
+ f.outf("<</Length %d>>", f.pages[n].Len())
+ f.putstream(f.pages[n].Bytes())
+ }
+ f.out("endobj")
+ }
+ // Pages root
+ f.offsets[1] = f.buffer.Len()
+ f.out("1 0 obj")
+ f.out("<</Type /Pages")
+ var kids fmtBuffer
+ kids.printf("/Kids [")
+ for i := 0; i < nb; i++ {
+ kids.printf("%d 0 R ", 3+2*i)
+ }
+ kids.printf("]")
+ f.out(kids.String())
+ f.outf("/Count %d", nb)
+ f.outf("/MediaBox [0 0 %.2f %.2f]", wPt, hPt)
+ f.out(">>")
+ f.out("endobj")
+}
+
+func (f *Fpdf) putfonts() {
+ if f.err != nil {
+ return
+ }
+ nf := f.n
+ for _, diff := range f.diffs {
+ // Encodings
+ f.newobj()
+ f.outf("<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [%s]>>", diff)
+ f.out("endobj")
+ }
+ for file, info := range f.fontFiles {
+ // foreach($this->fontFiles as $file=>$info)
+ // Font file embedding
+ f.newobj()
+ info.n = f.n
+ f.fontFiles[file] = info
+ font, err := ioutil.ReadFile(path.Join(f.fontpath, file))
+ if err != nil {
+ f.err = err
+ return
+ }
+ // dbg("font file [%s], ext [%s]", file, file[len(file)-2:])
+ compressed := file[len(file)-2:] == ".z"
+ if !compressed && info.length2 > 0 {
+ buf := font[6:info.length1]
+ buf = append(buf, font[6+info.length1+6:info.length2]...)
+ font = buf
+ }
+ f.outf("<</Length %d", len(font))
+ if compressed {
+ f.out("/Filter /FlateDecode")
+ }
+ f.outf("/Length1 %d", info.length1)
+ if info.length2 > 0 {
+ f.outf("/Length2 %d /Length3 0", info.length2)
+ }
+ f.out(">>")
+ f.putstream(font)
+ f.out("endobj")
+ }
+ for k, font := range f.fonts {
+ // Font objects
+ font.N = f.n + 1
+ f.fonts[k] = font
+ tp := font.Tp
+ name := font.Name
+ if tp == "Core" {
+ // Core font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.out("/Subtype /Type1")
+ if name != "Symbol" && name != "ZapfDingbats" {
+ f.out("/Encoding /WinAnsiEncoding")
+ }
+ f.out(">>")
+ f.out("endobj")
+ } else if tp == "Type1" || tp == "TrueType" {
+ // Additional Type1 or TrueType/OpenType font
+ f.newobj()
+ f.out("<</Type /Font")
+ f.outf("/BaseFont /%s", name)
+ f.outf("/Subtype /%s", tp)
+ f.out("/FirstChar 32 /LastChar 255")
+ f.outf("/Widths %d 0 R", f.n+1)
+ f.outf("/FontDescriptor %d 0 R", f.n+2)
+ if font.DiffN > 0 {
+ f.outf("/Encoding %d 0 R", nf+font.DiffN)
+ } else {
+ f.out("/Encoding /WinAnsiEncoding")
+ }
+ f.out(">>")
+ f.out("endobj")
+ // Widths
+ f.newobj()
+ var s fmtBuffer
+ s.WriteString("[")
+ for j := 32; j < 256; j++ {
+ s.printf("%d ", font.Cw[j])
+ }
+ s.WriteString("]")
+ f.out(s.String())
+ f.out("endobj")
+ // Descriptor
+ f.newobj()
+ s.Truncate(0)
+ s.printf("<</Type /FontDescriptor /FontName /%s ", name)
+ s.printf("/Ascent %d ", font.Desc.Ascent)
+ s.printf("/Descent %d ", font.Desc.Descent)
+ s.printf("/CapHeight %d ", font.Desc.CapHeight)
+ s.printf("/Flags %d ", font.Desc.Flags)
+ s.printf("/FontBBox [%d %d %d %d] ", font.Desc.FontBBox.Xmin, font.Desc.FontBBox.Ymin,
+ font.Desc.FontBBox.Xmax, font.Desc.FontBBox.Ymax)
+ s.printf("/ItalicAngle %d ", font.Desc.ItalicAngle)
+ s.printf("/StemV %d ", font.Desc.StemV)
+ s.printf("/MissingWidth %d ", font.Desc.MissingWidth)
+ var suffix string
+ if tp != "Type1" {
+ suffix = "2"
+ }
+ s.printf("/FontFile%s %d 0 R>>", suffix, f.fontFiles[font.File].n)
+ f.out(s.String())
+ f.out("endobj")
+ } else {
+ f.err = fmt.Errorf("Unsupported font type: %s", tp)
+ return
+ // Allow for additional types
+ // $mtd = 'put'.strtolower($type);
+ // if(!method_exists($this,$mtd))
+ // $this->Error('Unsupported font type: '.$type);
+ // $this->$mtd($font);
+ }
+ }
+ return
+}
+
+func (f *Fpdf) putimages() {
+ for fileStr, img := range f.images {
+ f.putimage(&img)
+ img.data = img.data[0:0]
+ img.smask = img.smask[0:0]
+ f.images[fileStr] = img
+ }
+}
+
+func (f *Fpdf) putimage(info *imageInfoType) {
+ f.newobj()
+ info.n = f.n
+ f.out("<</Type /XObject")
+ f.out("/Subtype /Image")
+ f.outf("/Width %d", int(info.w))
+ f.outf("/Height %d", int(info.h))
+ if info.cs == "Indexed" {
+ f.outf("/ColorSpace [/Indexed /DeviceRGB %d %d 0 R]", len(info.pal)/3-1, f.n+1)
+ } else {
+ f.outf("/ColorSpace /%s", info.cs)
+ if info.cs == "DeviceCMYK" {
+ f.out("/Decode [1 0 1 0 1 0 1 0]")
+ }
+ }
+ f.outf("/BitsPerComponent %d", info.bpc)
+ if len(info.f) > 0 {
+ f.outf("/Filter /%s", info.f)
+ }
+ if len(info.dp) > 0 {
+ f.outf("/DecodeParms <<%s>>", info.dp)
+ }
+ if len(info.trns) > 0 {
+ var trns fmtBuffer
+ for _, v := range info.trns {
+ trns.printf("%d %d ", v, v)
+ }
+ f.outf("/Mask [%s]", trns.String())
+ }
+ if info.smask != nil {
+ f.outf("/SMask %d 0 R", f.n+1)
+ }
+ f.outf("/Length %d>>", len(info.data))
+ f.putstream(info.data)
+ f.out("endobj")
+ // Soft mask
+ if len(info.smask) > 0 {
+ smask := imageInfoType{
+ w: info.w,
+ h: info.h,
+ cs: "DeviceGray",
+ bpc: 8,
+ f: info.f,
+ dp: sprintf("/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns %d", int(info.w)),
+ data: info.smask,
+ }
+ f.putimage(&smask)
+ }
+ // Palette
+ if info.cs == "Indexed" {
+ f.newobj()
+ if f.compress {
+ pal := sliceCompress(info.pal)
+ f.outf("<</Filter /FlateDecode /Length %d>>", len(pal))
+ f.putstream(pal)
+ } else {
+ f.outf("<</Length %d>>", len(info.pal))
+ f.putstream(info.pal)
+ }
+ f.out("endobj")
+ }
+}
+
+func (f *Fpdf) putxobjectdict() {
+ for _, image := range f.images {
+ // foreach($this->images as $image)
+ f.outf("/I%d %d 0 R", image.i, image.n)
+ }
+}
+
+func (f *Fpdf) putresourcedict() {
+ f.out("/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]")
+ f.out("/Font <<")
+ for _, font := range f.fonts {
+ // foreach($this->fonts as $font)
+ f.outf("/F%d %d 0 R", font.I, font.N)
+ }
+ f.out(">>")
+ f.out("/XObject <<")
+ f.putxobjectdict()
+ f.out(">>")
+}
+
+func (f *Fpdf) putresources() {
+ if f.err != nil {
+ return
+ }
+ f.putfonts()
+ if f.err != nil {
+ return
+ }
+ f.putimages()
+ // Resource dictionary
+ f.offsets[2] = f.buffer.Len()
+ f.out("2 0 obj")
+ f.out("<<")
+ f.putresourcedict()
+ f.out(">>")
+ f.out("endobj")
+ return
+}
+
+func (f *Fpdf) putinfo() {
+ f.outf("/Producer %s", f.textstring("FPDF "+FPDF_VERSION))
+ if len(f.title) > 0 {
+ f.outf("/Title %s", f.textstring(f.title))
+ }
+ if len(f.subject) > 0 {
+ f.outf("/Subject %s", f.textstring(f.subject))
+ }
+ if len(f.author) > 0 {
+ f.outf("/Author %s", f.textstring(f.author))
+ }
+ if len(f.keywords) > 0 {
+ f.outf("/Keywords %s", f.textstring(f.keywords))
+ }
+ if len(f.creator) > 0 {
+ f.outf("/Creator %s", f.textstring(f.creator))
+ }
+ f.outf("/CreationDate %s", f.textstring("D:"+time.Now().Format("20060102150405")))
+}
+
+func (f *Fpdf) putcatalog() {
+ f.out("/Type /Catalog")
+ f.out("/Pages 1 0 R")
+ switch f.zoomMode {
+ case "fullpage":
+ f.out("/OpenAction [3 0 R /Fit]")
+ case "fullwidth":
+ f.out("/OpenAction [3 0 R /FitH null]")
+ case "real":
+ f.out("/OpenAction [3 0 R /XYZ null null 1]")
+ }
+ // } else if !is_string($this->zoomMode))
+ // $this->out('/OpenAction [3 0 R /XYZ null null '.sprintf('%.2f',$this->zoomMode/100).']');
+ switch f.layoutMode {
+ case "single":
+ f.out("/PageLayout /SinglePage")
+ case "continuous":
+ f.out("/PageLayout /OneColumn")
+ case "two":
+ f.out("/PageLayout /TwoColumnLeft")
+ }
+}
+
+func (f *Fpdf) putheader() {
+ f.outf("%%PDF-%s", f.pdfVersion)
+}
+
+func (f *Fpdf) puttrailer() {
+ f.outf("/Size %d", f.n+1)
+ f.outf("/Root %d 0 R", f.n)
+ f.outf("/Info %d 0 R", f.n-1)
+}
+
+func (f *Fpdf) enddoc() {
+ if f.err != nil {
+ return
+ }
+ f.putheader()
+ f.putpages()
+ f.putresources()
+ if f.err != nil {
+ return
+ }
+ // Info
+ f.newobj()
+ f.out("<<")
+ f.putinfo()
+ f.out(">>")
+ f.out("endobj")
+ // Catalog
+ f.newobj()
+ f.out("<<")
+ f.putcatalog()
+ f.out(">>")
+ f.out("endobj")
+ // Cross-ref
+ o := f.buffer.Len()
+ f.out("xref")
+ f.outf("0 %d", f.n+1)
+ f.out("0000000000 65535 f ")
+ for j := 1; j <= f.n; j++ {
+ f.outf("%010d 00000 n ", f.offsets[j])
+ }
+ // Trailer
+ f.out("trailer")
+ f.out("<<")
+ f.puttrailer()
+ f.out(">>")
+ f.out("startxref")
+ f.outf("%d", o)
+ f.out("%%EOF")
+ f.state = 3
+ return
+}
diff --git a/fpdf_test.go b/fpdf_test.go
new file mode 100644
index 0000000..7440a3b
--- /dev/null
+++ b/fpdf_test.go
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+// Absolute path needed for gocov tool; relative OK for test
+const (
+ GOFPDF_DIR = "."
+ FONT_DIR = GOFPDF_DIR + "/font"
+ IMG_DIR = GOFPDF_DIR + "/image"
+ TEXT_DIR = GOFPDF_DIR + "/text"
+)
+
+type nullWriter struct {
+}
+
+func (nw *nullWriter) Write(p []byte) (n int, err error) {
+ n = len(p)
+ return
+}
+
+func (nw *nullWriter) Close() (err error) {
+ return
+}
+
+type pdfWriter struct {
+ pdf *Fpdf
+ fl *os.File
+ idx int
+}
+
+func (pw *pdfWriter) Write(p []byte) (n int, err error) {
+ if pw.pdf.Ok() {
+ return pw.fl.Write(p)
+ }
+ return
+}
+
+func (pw *pdfWriter) Close() (err error) {
+ if pw.fl != nil {
+ pw.fl.Close()
+ pw.fl = nil
+ }
+ if pw.pdf.Ok() {
+ fmt.Printf("Successfully generated pdf/tutorial%02d.pdf\n", pw.idx)
+ } else {
+ fmt.Printf("%s\n", pw.pdf.Error())
+ }
+ return
+}
+
+func docWriter(pdf *Fpdf, idx int) *pdfWriter {
+ pw := new(pdfWriter)
+ pw.pdf = pdf
+ pw.idx = idx
+ if pdf.Ok() {
+ var err error
+ fileStr := fmt.Sprintf("%s/pdf/tutorial%02d.pdf", GOFPDF_DIR, idx)
+ pw.fl, err = os.Create(fileStr)
+ if err != nil {
+ pdf.SetErrorf("Error opening output file %s", fileStr)
+ }
+ }
+ return pw
+}
+
+func ExampleFpdf_tutorial01() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ pdf.AddPage()
+ pdf.SetFont("Arial", "B", 16)
+ pdf.Cell(40, 10, "Hello World!")
+ pdf.Output(docWriter(pdf, 1))
+ // Output:
+ // Successfully generated pdf/tutorial01.pdf
+}
+
+func ExampleFpdf_tutorial02() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ pdf.SetHeaderFunc(func() {
+ pdf.Image(IMG_DIR+"/logo.png", 10, 6, 30, 0, false, "", 0, "")
+ pdf.SetY(5)
+ pdf.SetFont("Arial", "B", 15)
+ pdf.Cell(80, 0, "")
+ pdf.CellFormat(30, 10, "Title", "1", 0, "C", false, 0, "")
+ pdf.Ln(20)
+ })
+ pdf.SetFooterFunc(func() {
+ pdf.SetY(-15)
+ pdf.SetFont("Arial", "I", 8)
+ pdf.CellFormat(0, 10, fmt.Sprintf("Page %d/{nb}", pdf.PageNo()), "", 0, "C", false, 0, "")
+ })
+ pdf.AliasNbPages("")
+ pdf.AddPage()
+ pdf.SetFont("Times", "", 12)
+ for j := 1; j <= 40; j++ {
+ pdf.CellFormat(0, 10, fmt.Sprintf("Printing line number %d", j), "", 1, "", false, 0, "")
+ }
+ pdf.Output(docWriter(pdf, 2))
+ // Output:
+ // Successfully generated pdf/tutorial02.pdf
+}
+
+func ExampleFpdf_tutorial03() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ titleStr := "20000 Leagues Under the Seas"
+ pdf.SetTitle(titleStr, false)
+ pdf.SetAuthor("Jules Verne", false)
+ pdf.SetHeaderFunc(func() {
+ // Arial bold 15
+ pdf.SetFont("Arial", "B", 15)
+ // Calculate width of title and position
+ wd := pdf.GetStringWidth(titleStr) + 6
+ pdf.SetX((210 - wd) / 2)
+ // Colors of frame, background and text
+ pdf.SetDrawColor(0, 80, 180)
+ pdf.SetFillColor(230, 230, 0)
+ pdf.SetTextColor(220, 50, 50)
+ // Thickness of frame (1 mm)
+ pdf.SetLineWidth(1)
+ // Title
+ pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
+ // Line break
+ pdf.Ln(10)
+ })
+ pdf.SetFooterFunc(func() {
+ // Position at 1.5 cm from bottom
+ pdf.SetY(-15)
+ // Arial italic 8
+ pdf.SetFont("Arial", "I", 8)
+ // Text color in gray
+ pdf.SetTextColor(128, 128, 128)
+ // Page number
+ pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()), "", 0, "C", false, 0, "")
+ })
+ chapterTitle := func(chapNum int, titleStr string) {
+ // // Arial 12
+ pdf.SetFont("Arial", "", 12)
+ // Background color
+ pdf.SetFillColor(200, 220, 255)
+ // Title
+ pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr), "", 1, "L", true, 0, "")
+ // Line break
+ pdf.Ln(4)
+ }
+ chapterBody := func(fileStr string) {
+ // Read text file
+ txtStr, err := ioutil.ReadFile(fileStr)
+ if err != nil {
+ pdf.SetError(err)
+ }
+ // Times 12
+ pdf.SetFont("Times", "", 12)
+ // Output justified text
+ pdf.MultiCell(0, 5, string(txtStr), "", "", false)
+ // Line break
+ pdf.Ln(-1)
+ // Mention in italics
+ pdf.SetFont("", "I", 0)
+ pdf.Cell(0, 5, "(end of excerpt)")
+ }
+ printChapter := func(chapNum int, titleStr, fileStr string) {
+ pdf.AddPage()
+ chapterTitle(chapNum, titleStr)
+ chapterBody(fileStr)
+ }
+ printChapter(1, "A RUNAWAY REEF", TEXT_DIR+"/20k_c1.txt")
+ printChapter(2, "THE PROS AND CONS", TEXT_DIR+"/20k_c2.txt")
+ pdf.Output(docWriter(pdf, 3))
+ // Output:
+ // Successfully generated pdf/tutorial03.pdf
+}
+
+func ExampleFpdf_tutorial04() {
+ var y0 float64
+ var crrntCol int
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ titleStr := "20000 Leagues Under the Seas"
+ pdf.SetTitle(titleStr, false)
+ pdf.SetAuthor("Jules Verne", false)
+ setCol := func(col int) {
+ // Set position at a given column
+ crrntCol = col
+ x := 10.0 + float64(col)*65.0
+ pdf.SetLeftMargin(x)
+ pdf.SetX(x)
+ }
+ chapterTitle := func(chapNum int, titleStr string) {
+ // Arial 12
+ pdf.SetFont("Arial", "", 12)
+ // Background color
+ pdf.SetFillColor(200, 220, 255)
+ // Title
+ pdf.CellFormat(0, 6, fmt.Sprintf("Chapter %d : %s", chapNum, titleStr), "", 1, "L", true, 0, "")
+ // Line break
+ pdf.Ln(4)
+ y0 = pdf.GetY()
+ }
+ chapterBody := func(fileStr string) {
+ // Read text file
+ txtStr, err := ioutil.ReadFile(fileStr)
+ if err != nil {
+ pdf.SetError(err)
+ }
+ // Font
+ pdf.SetFont("Times", "", 12)
+ // Output text in a 6 cm width column
+ pdf.MultiCell(60, 5, string(txtStr), "", "", false)
+ pdf.Ln(-1)
+ // Mention
+ pdf.SetFont("", "I", 0)
+ pdf.Cell(0, 5, "(end of excerpt)")
+ // Go back to first column
+ setCol(0)
+ }
+ printChapter := func(num int, titleStr, fileStr string) {
+ // Add chapter
+ pdf.AddPage()
+ chapterTitle(num, titleStr)
+ chapterBody(fileStr)
+ }
+ pdf.SetAcceptPageBreakFunc(func() bool {
+ // Method accepting or not automatic page break
+ if crrntCol < 2 {
+ // Go to next column
+ setCol(crrntCol + 1)
+ // Set ordinate to top
+ pdf.SetY(y0)
+ // Keep on page
+ return false
+ } else {
+ // Go back to first column
+ setCol(0)
+ // Page break
+ return true
+ }
+ })
+ pdf.SetHeaderFunc(func() {
+ // Arial bold 15
+ pdf.SetFont("Arial", "B", 15)
+ // Calculate width of title and position
+ wd := pdf.GetStringWidth(titleStr) + 6
+ pdf.SetX((210 - wd) / 2)
+ // Colors of frame, background and text
+ pdf.SetDrawColor(0, 80, 180)
+ pdf.SetFillColor(230, 230, 0)
+ pdf.SetTextColor(220, 50, 50)
+ // Thickness of frame (1 mm)
+ pdf.SetLineWidth(1)
+ // Title
+ pdf.CellFormat(wd, 9, titleStr, "1", 1, "C", true, 0, "")
+ // Line break
+ pdf.Ln(10)
+ // Save ordinate
+ y0 = pdf.GetY()
+ })
+ pdf.SetFooterFunc(func() {
+ // Position at 1.5 cm from bottom
+ pdf.SetY(-15)
+ // Arial italic 8
+ pdf.SetFont("Arial", "I", 8)
+ // Text color in gray
+ pdf.SetTextColor(128, 128, 128)
+ // Page number
+ pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()), "", 0, "C", false, 0, "")
+ })
+ printChapter(1, "A RUNAWAY REEF", TEXT_DIR+"/20k_c1.txt")
+ printChapter(2, "THE PROS AND CONS", TEXT_DIR+"/20k_c2.txt")
+ pdf.Output(docWriter(pdf, 4))
+ // Output:
+ // Successfully generated pdf/tutorial04.pdf
+}
+
+func ExampleFpdf_tutorial05() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ type countryType struct {
+ nameStr, capitalStr, areaStr, popStr string
+ }
+ countryList := make([]countryType, 0, 8)
+ header := []string{"Country", "Capital", "Area (sq km)", "Pop. (thousands)"}
+ loadData := func(fileStr string) {
+ fl, err := os.Open(fileStr)
+ if err == nil {
+ scanner := bufio.NewScanner(fl)
+ var c countryType
+ for scanner.Scan() {
+ // Austria;Vienna;83859;8075
+ lineStr := scanner.Text()
+ list := strings.Split(lineStr, ";")
+ if len(list) == 4 {
+ c.nameStr = list[0]
+ c.capitalStr = list[1]
+ c.areaStr = list[2]
+ c.popStr = list[3]
+ countryList = append(countryList, c)
+ } else {
+ err = fmt.Errorf("Error tokenizing %s", lineStr)
+ }
+ }
+ fl.Close()
+ if len(countryList) == 0 {
+ err = fmt.Errorf("Error loading data from %s", fileStr)
+ }
+ }
+ if err != nil {
+ pdf.SetError(err)
+ }
+ }
+ // Simple table
+ basicTable := func() {
+ for _, str := range header {
+ pdf.CellFormat(40, 7, str, "1", 0, "", false, 0, "")
+ }
+ pdf.Ln(-1)
+ for _, c := range countryList {
+ pdf.CellFormat(40, 6, c.nameStr, "1", 0, "", false, 0, "")
+ pdf.CellFormat(40, 6, c.capitalStr, "1", 0, "", false, 0, "")
+ pdf.CellFormat(40, 6, c.areaStr, "1", 0, "", false, 0, "")
+ pdf.CellFormat(40, 6, c.popStr, "1", 0, "", false, 0, "")
+ pdf.Ln(-1)
+ }
+ }
+ // Better table
+ improvedTable := func() {
+ // Column widths
+ w := []float64{40.0, 35.0, 40.0, 45.0}
+ wSum := 0.0
+ for _, v := range w {
+ wSum += v
+ }
+ // Header
+ for j, str := range header {
+ pdf.CellFormat(w[j], 7, str, "1", 0, "C", false, 0, "")
+ }
+ pdf.Ln(-1)
+ // Data
+ for _, c := range countryList {
+ pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", false, 0, "")
+ pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", false, 0, "")
+ pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3), "LR", 0, "R", false, 0, "")
+ pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3), "LR", 0, "R", false, 0, "")
+ pdf.Ln(-1)
+ }
+ pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
+ }
+ // Colored table
+ fancyTable := func() {
+ // Colors, line width and bold font
+ pdf.SetFillColor(255, 0, 0)
+ pdf.SetTextColor(255, 255, 255)
+ pdf.SetDrawColor(128, 0, 0)
+ pdf.SetLineWidth(.3)
+ pdf.SetFont("", "B", 0)
+ // Header
+ w := []float64{40, 35, 40, 45}
+ wSum := 0.0
+ for _, v := range w {
+ wSum += v
+ }
+ for j, str := range header {
+ pdf.CellFormat(w[j], 7, str, "1", 0, "C", true, 0, "")
+ }
+ pdf.Ln(-1)
+ // Color and font restoration
+ pdf.SetFillColor(224, 235, 255)
+ pdf.SetTextColor(0, 0, 0)
+ pdf.SetFont("", "", 0)
+ // Data
+ fill := false
+ for _, c := range countryList {
+ pdf.CellFormat(w[0], 6, c.nameStr, "LR", 0, "", fill, 0, "")
+ pdf.CellFormat(w[1], 6, c.capitalStr, "LR", 0, "", fill, 0, "")
+ pdf.CellFormat(w[2], 6, strDelimit(c.areaStr, ",", 3), "LR", 0, "R", fill, 0, "")
+ pdf.CellFormat(w[3], 6, strDelimit(c.popStr, ",", 3), "LR", 0, "R", fill, 0, "")
+ pdf.Ln(-1)
+ fill = !fill
+ }
+ pdf.CellFormat(wSum, 0, "", "T", 0, "", false, 0, "")
+ }
+ loadData(TEXT_DIR + "/countries.txt")
+ pdf.SetFont("Arial", "", 14)
+ pdf.AddPage()
+ basicTable()
+ pdf.AddPage()
+ improvedTable()
+ pdf.AddPage()
+ fancyTable()
+ pdf.Output(docWriter(pdf, 5))
+ // Output:
+ // Successfully generated pdf/tutorial05.pdf
+}
+
+func ExampleFpdf_tutorial06() {
+ var boldLvl, italicLvl, underscoreLvl int
+ var hrefStr string
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ setStyle := func(boldAdj, italicAdj, underscoreAdj int) {
+ styleStr := ""
+ boldLvl += boldAdj
+ if boldLvl > 0 {
+ styleStr += "B"
+ }
+ italicLvl += italicAdj
+ if italicLvl > 0 {
+ styleStr += "I"
+ }
+ underscoreLvl += underscoreAdj
+ if underscoreLvl > 0 {
+ styleStr += "U"
+ }
+ pdf.SetFont("", styleStr, 0)
+ }
+ putLink := func(urlStr, txtStr string) {
+ // Put a hyperlink
+ pdf.SetTextColor(0, 0, 255)
+ setStyle(0, 0, 1)
+ pdf.WriteLinkString(5, txtStr, urlStr)
+ setStyle(0, 0, -1)
+ pdf.SetTextColor(0, 0, 0)
+ }
+
+ writeHtml := func(htmlStr string) {
+ list := htmlTokenize(htmlStr)
+ var ok bool
+ for _, el := range list {
+ switch el.cat {
+ case 'T':
+ if len(hrefStr) > 0 {
+ putLink(hrefStr, el.str)
+ hrefStr = ""
+ } else {
+ pdf.Write(5, el.str)
+ }
+ case 'O':
+ switch el.str {
+ case "b":
+ setStyle(1, 0, 0)
+ case "i":
+ setStyle(0, 1, 0)
+ case "u":
+ setStyle(0, 0, 1)
+ case "br":
+ pdf.Ln(5)
+ case "a":
+ hrefStr, ok = el.attr["href"]
+ if !ok {
+ hrefStr = ""
+ }
+ }
+ case 'C':
+ switch el.str {
+ case "b":
+ setStyle(-1, 0, 0)
+ case "i":
+ setStyle(0, -1, 0)
+ case "u":
+ setStyle(0, 0, -1)
+
+ }
+ }
+ }
+ }
+ // First page
+ pdf.AddPage()
+ pdf.SetFont("Arial", "", 20)
+ pdf.Write(5, "To find out what's new in this tutorial, click ")
+ pdf.SetFont("", "U", 0)
+ link := pdf.AddLink()
+ pdf.WriteLinkId(5, "here", link)
+ pdf.SetFont("", "", 0)
+ // Second page
+ pdf.AddPage()
+ pdf.SetLink(link, 0, -1)
+ pdf.Image(IMG_DIR+"/logo.png", 10, 12, 30, 0, false, "", 0, "http://www.fpdf.org")
+ pdf.SetLeftMargin(45)
+ pdf.SetFontSize(14)
+ htmlStr := `You can now easily print text mixing different styles: <b>bold</b>, ` +
+ `<i>italic</i>, <u>underlined</u>, or <b><i><u>all at once</u></i></b>!<br><br>` +
+ `You can also insert links on text, such as ` +
+ `<a href="http://www.fpdf.org">www.fpdf.org</a>, or on an image: click on the logo.`
+ writeHtml(htmlStr)
+ pdf.Output(docWriter(pdf, 6))
+ // Output:
+ // Successfully generated pdf/tutorial06.pdf
+}
+
+func ExampleFpdf_tutorial07() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ pdf.AddFont("Calligrapher", "", "calligra.json")
+ // pdf.AddFont("Marcellus-Regular", "", "Marcellus-Regular.json")
+ // pdf.AddFont("URWPalladioL-Roma", "", "p052003l.json")
+ pdf.AddPage()
+ pdf.SetFont("Calligrapher", "", 35)
+ // pdf.SetFont("Marcellus-Regular", "", 35)
+ // pdf.SetFont("URWPalladioL-Roma", "", 35)
+ pdf.Cell(0, 10, "Enjoy new fonts with FPDF!")
+ pdf.Output(docWriter(pdf, 7))
+ // Output:
+ // Successfully generated pdf/tutorial07.pdf
+}
+
+func ExampleFpdf_tutorial08() {
+ pdf := New("P", "mm", "A4", FONT_DIR)
+ pdf.AddPage()
+ pdf.SetFont("Arial", "", 11)
+ pdf.Image(IMG_DIR+"/logo.png", 10, 10, 30, 0, false, "", 0, "")
+ pdf.Text(50, 20, "logo.png")
+ pdf.Image(IMG_DIR+"/logo.gif", 10, 40, 30, 0, false, "", 0, "")
+ pdf.Text(50, 50, "logo.gif")
+ pdf.Image(IMG_DIR+"/logo-gray.png", 10, 70, 30, 0, false, "", 0, "")
+ pdf.Text(50, 80, "logo-gray.png")
+ pdf.Image(IMG_DIR+"/logo-rgb.png", 10, 100, 30, 0, false, "", 0, "")
+ pdf.Text(50, 110, "logo-rgb.png")
+ pdf.Image(IMG_DIR+"/logo.jpg", 10, 130, 30, 0, false, "", 0, "")
+ pdf.Text(50, 140, "logo.jpg")
+ pdf.Output(docWriter(pdf, 8))
+ // Output:
+ // Successfully generated pdf/tutorial08.pdf
+}
+
+func ExampleFpdf_tutorial09() {
+ var y0 float64
+ var crrntCol int
+ loremStr := "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod " +
+ "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis " +
+ "nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis " +
+ "aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat " +
+ "nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui " +
+ "officia deserunt mollit anim id est laborum."
+ pdf := New("L", "mm", "A4", FONT_DIR)
+ const (
+ pageWd = 297.0 // A4 210.0 x 297.0
+ margin = 10.0
+ gutter = 4
+ colNum = 3
+ colWd = (pageWd - 2*margin - (colNum-1)*gutter) / colNum
+ )
+ setCol := func(col int) {
+ crrntCol = col
+ x := margin + float64(col)*(colWd+gutter)
+ pdf.SetLeftMargin(x)
+ pdf.SetX(x)
+ }
+ pdf.SetHeaderFunc(func() {
+ titleStr := "gofpdf"
+ pdf.SetFont("Helvetica", "B", 48)
+ wd := pdf.GetStringWidth(titleStr) + 6
+ pdf.SetX((pageWd - wd) / 2)
+ pdf.SetTextColor(128, 128, 160)
+ pdf.Write(12, titleStr[:2])
+ pdf.SetTextColor(128, 128, 128)
+ pdf.Write(12, titleStr[2:])
+ pdf.Ln(20)
+ y0 = pdf.GetY()
+ })
+ pdf.SetAcceptPageBreakFunc(func() bool {
+ if crrntCol < colNum-1 {
+ setCol(crrntCol + 1)
+ pdf.SetY(y0)
+ // Start new column, not new page
+ return false
+ } else {
+ setCol(0)
+ return true
+ }
+ })
+ pdf.AddPage()
+ pdf.SetFont("Times", "", 12)
+ for j := 0; j < 20; j++ {
+ if j == 1 {
+ pdf.Image(IMG_DIR+"/fpdf.png", -1, 0, colWd, 0, true, "", 0, "")
+ } else if j == 5 {
+ pdf.Image(IMG_DIR+"/golang-gopher.png", -1, 0, colWd, 0, true, "", 0, "")
+ }
+ pdf.MultiCell(colWd, 5, loremStr, "", "", false)
+ pdf.Ln(-1)
+ }
+ pdf.Output(docWriter(pdf, 9))
+ // Output:
+ // Successfully generated pdf/tutorial09.pdf
+}
+
+// Test the corner cases as reported by the gocov tool
+func TestFpdf(t *testing.T) {
+ var nw nullWriter
+ tmpDirStr := os.TempDir()
+ MakeFont(FONT_DIR+"/calliga.ttf", FONT_DIR+"/cp1252.map", tmpDirStr, nil, true)
+ pdf := New("", "", "", "")
+ pdf.SetFontLocation(FONT_DIR)
+ pdf.SetTitle("世界", true)
+ pdf.SetAuthor("世界", true)
+ pdf.SetSubject("世界", true)
+ pdf.SetCreator("世界", true)
+ pdf.SetKeywords("世界", true)
+ pdf.Output(&nw)
+ if len(pdf.String()) < 1 {
+ t.Fatalf("Trouble with pdf.String()")
+ }
+ err := pdf.Error()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pdf.Err() {
+ t.Fatal(pdf.Error())
+ }
+}
diff --git a/image/fpdf.png b/image/fpdf.png
new file mode 100644
index 0000000..b8a570f
--- /dev/null
+++ b/image/fpdf.png
Binary files differ
diff --git a/image/golang-gopher.png b/image/golang-gopher.png
new file mode 100644
index 0000000..3626d01
--- /dev/null
+++ b/image/golang-gopher.png
Binary files differ
diff --git a/image/logo-gray.png b/image/logo-gray.png
new file mode 100644
index 0000000..addc3a3
--- /dev/null
+++ b/image/logo-gray.png
Binary files differ
diff --git a/image/logo-progressive.jpg b/image/logo-progressive.jpg
new file mode 100644
index 0000000..add0a8f
--- /dev/null
+++ b/image/logo-progressive.jpg
Binary files differ
diff --git a/image/logo-rgb.png b/image/logo-rgb.png
new file mode 100644
index 0000000..81140e2
--- /dev/null
+++ b/image/logo-rgb.png
Binary files differ
diff --git a/image/logo.gif b/image/logo.gif
new file mode 100644
index 0000000..85d6cf0
--- /dev/null
+++ b/image/logo.gif
Binary files differ
diff --git a/image/logo.jpg b/image/logo.jpg
new file mode 100644
index 0000000..60a144e
--- /dev/null
+++ b/image/logo.jpg
Binary files differ
diff --git a/image/logo.png b/image/logo.png
new file mode 100644
index 0000000..284a007
--- /dev/null
+++ b/image/logo.png
Binary files differ
diff --git a/makefont/doc.go b/makefont/doc.go
new file mode 100644
index 0000000..306ead2
--- /dev/null
+++ b/makefont/doc.go
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013 Kurt Jung
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Command makefont generates a font definition file.
+
+This utility is used to generate a font definition file that allows TrueType
+and Type1 fonts to be used in PDFs produced with the fpdf package.
+*/
+package main
diff --git a/makefont/makefont.go b/makefont/makefont.go
new file mode 100644
index 0000000..159db5b
--- /dev/null
+++ b/makefont/makefont.go
@@ -0,0 +1,63 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "gofpdf"
+ "os"
+)
+
+func errPrintf(fmtStr string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, fmtStr, args...)
+}
+
+func showHelp() {
+ errPrintf("Usage: %s [options] font_file [font_file...]\n", os.Args[0])
+ flag.PrintDefaults()
+ errPrintf("Example: %s --embed --enc=../font/cp1252.map --dst=../font calligra.ttf /opt/font/symbol.pfb\n", os.Args[0])
+}
+
+func tutorialSummary(f *gofpdf.Fpdf, fileStr string) {
+ if f.Ok() {
+ fl, err := os.Create(fileStr)
+ defer fl.Close()
+ if err == nil {
+ f.Output(fl)
+ } else {
+ f.SetError(err)
+ }
+ }
+ if f.Ok() {
+ fmt.Printf("Successfully generated %s\n", fileStr)
+ } else {
+ errPrintf("%s\n", f.Error())
+ }
+}
+
+func main() {
+ var dstDirStr, encodingFileStr string
+ var err error
+ var help, embed bool
+ flag.StringVar(&dstDirStr, "dst", ".", "directory for output files (*.z, *.json)")
+ flag.StringVar(&encodingFileStr, "enc", "cp1252.map", "code page file")
+ flag.BoolVar(&embed, "embed", false, "embed font into PDF")
+ flag.BoolVar(&help, "help", false, "command line usage")
+ flag.Parse()
+ if help {
+ showHelp()
+ } else {
+ args := flag.Args()
+ if len(args) > 0 {
+ for _, fileStr := range args {
+ err = gofpdf.MakeFont(fileStr, encodingFileStr, dstDirStr, os.Stderr, embed)
+ if err != nil {
+ errPrintf("%s\n", err)
+ }
+ // errPrintf("Font file [%s], Encoding file [%s], Embed [%v]\n", fileStr, encodingFileStr, embed)
+ }
+ } else {
+ errPrintf("At least one Type1 or TrueType font must be specified\n")
+ showHelp()
+ }
+ }
+}
diff --git a/makefont/makefont_test.go b/makefont/makefont_test.go
new file mode 100644
index 0000000..8193a5c
--- /dev/null
+++ b/makefont/makefont_test.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "os/exec"
+ "strings"
+ "testing"
+)
+
+func TestMakefont(t *testing.T) {
+ const expect = "Font definition file successfully generated"
+ out, err := exec.Command("./makefont", "--dst=../font", "--embed",
+ "--enc=../font/cp1252.map", "../font/calligra.ttf").CombinedOutput()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(out), expect) {
+ t.Fatalf("Unexpected output from makefont")
+ }
+}
diff --git a/text/20k_c1.txt b/text/20k_c1.txt
new file mode 100644
index 0000000..6d5b295
--- /dev/null
+++ b/text/20k_c1.txt
@@ -0,0 +1,10 @@
+The year 1866 was marked by a bizarre development, an unexplained and downright inexplicable phenomenon that surely no one has forgotten. Without getting into those rumors that upset civilians in the seaports and deranged the public mind even far inland, it must be said that professional seamen were especially alarmed. Traders, shipowners, captains of vessels, skippers, and master mariners from Europe and America, naval officers from every country, and at their heels the various national governments on these two continents, were all extremely disturbed by the business.
+In essence, over a period of time several ships had encountered "an enormous thing" at sea, a long spindle-shaped object, sometimes giving off a phosphorescent glow, infinitely bigger and faster than any whale.
+The relevant data on this apparition, as recorded in various logbooks, agreed pretty closely as to the structure of the object or creature in question, its unprecedented speed of movement, its startling locomotive power, and the unique vitality with which it seemed to be gifted. If it was a cetacean, it exceeded in bulk any whale previously classified by science. No naturalist, neither Cuvier nor Lacépède, neither Professor Dumeril nor Professor de Quatrefages, would have accepted the existence of such a monster sight unseen -- specifically, unseen by their own scientific eyes.
+Striking an average of observations taken at different times -- rejecting those timid estimates that gave the object a length of 200 feet, and ignoring those exaggerated views that saw it as a mile wide and three long--you could still assert that this phenomenal creature greatly exceeded the dimensions of anything then known to ichthyologists, if it existed at all.
+Now then, it did exist, this was an undeniable fact; and since the human mind dotes on objects of wonder, you can understand the worldwide excitement caused by this unearthly apparition. As for relegating it to the realm of fiction, that charge had to be dropped.
+In essence, on July 20, 1866, the steamer Governor Higginson, from the Calcutta & Burnach Steam Navigation Co., encountered this moving mass five miles off the eastern shores of Australia. Captain Baker at first thought he was in the presence of an unknown reef; he was even about to fix its exact position when two waterspouts shot out of this inexplicable object and sprang hissing into the air some 150 feet. So, unless this reef was subject to the intermittent eruptions of a geyser, the Governor Higginson had fair and honest dealings with some aquatic mammal, until then unknown, that could spurt from its blowholes waterspouts mixed with air and steam.
+Similar events were likewise observed in Pacific seas, on July 23 of the same year, by the Christopher Columbus from the West India & Pacific Steam Navigation Co. Consequently, this extraordinary cetacean could transfer itself from one locality to another with startling swiftness, since within an interval of just three days, the Governor Higginson and the Christopher Columbus had observed it at two positions on the charts separated by a distance of more than 700 nautical leagues.
+Fifteen days later and 2,000 leagues farther, the Helvetia from the Compagnie Nationale and the Shannon from the Royal Mail line, running on opposite tacks in that part of the Atlantic lying between the United States and Europe, respectively signaled each other that the monster had been sighted in latitude 42 degrees 15' north and longitude 60 degrees 35' west of the meridian of Greenwich. From their simultaneous observations, they were able to estimate the mammal's minimum length at more than 350 English feet; this was because both the Shannon and the Helvetia were of smaller dimensions, although each measured 100 meters stem to stern. Now then, the biggest whales, those rorqual whales that frequent the waterways of the Aleutian Islands, have never exceeded a length of 56 meters--if they reach even that.
+One after another, reports arrived that would profoundly affect public opinion: new observations taken by the transatlantic liner Pereire, the Inman line's Etna running afoul of the monster, an official report drawn up by officers on the French frigate Normandy, dead-earnest reckonings obtained by the general staff of Commodore Fitz-James aboard the Lord Clyde. In lighthearted countries, people joked about this phenomenon, but such serious, practical countries as England, America, and Germany were deeply concerned.
+In every big city the monster was the latest rage; they sang about it in the coffee houses, they ridiculed it in the newspapers, they dramatized it in the theaters. The tabloids found it a fine opportunity for hatching all sorts of hoaxes. In those newspapers short of copy, you saw the reappearance of every gigantic imaginary creature, from "Moby Dick," that dreadful white whale from the High Arctic regions, to the stupendous kraken whose tentacles could entwine a 500-ton craft and drag it into the ocean depths. They even reprinted reports from ancient times: the views of Aristotle and Pliny accepting the existence of such monsters, then the Norwegian stories of Bishop Pontoppidan, the narratives of Paul Egede, and finally the reports of Captain Harrington -- whose good faith is above suspicion--in which he claims he saw, while aboard the Castilian in 1857, one of those enormous serpents that, until then, had frequented only the seas of France's old extremist newspaper, The Constitutionalist.
diff --git a/text/20k_c2.txt b/text/20k_c2.txt
new file mode 100644
index 0000000..7b5c565
--- /dev/null
+++ b/text/20k_c2.txt
@@ -0,0 +1,23 @@
+During the period in which these developments were occurring, I had returned from a scientific undertaking organized to explore the Nebraska badlands in the United States. In my capacity as Assistant Professor at the Paris Museum of Natural History, I had been attached to this expedition by the French government. After spending six months in Nebraska, I arrived in New York laden with valuable collections near the end of March. My departure for France was set for early May. In the meantime, then, I was busy classifying my mineralogical, botanical, and zoological treasures when that incident took place with the Scotia.
+I was perfectly abreast of this question, which was the big news of the day, and how could I not have been? I had read and reread every American and European newspaper without being any farther along. This mystery puzzled me. Finding it impossible to form any views, I drifted from one extreme to the other. Something was out there, that much was certain, and any doubting Thomas was invited to place his finger on the Scotia's wound.
+When I arrived in New York, the question was at the boiling point. The hypothesis of a drifting islet or an elusive reef, put forward by people not quite in their right minds, was completely eliminated. And indeed, unless this reef had an engine in its belly, how could it move about with such prodigious speed?
+Also discredited was the idea of a floating hull or some other enormous wreckage, and again because of this speed of movement.
+So only two possible solutions to the question were left, creating two very distinct groups of supporters: on one side, those favoring a monster of colossal strength; on the other, those favoring an "underwater boat" of tremendous motor power.
+Now then, although the latter hypothesis was completely admissible, it couldn't stand up to inquiries conducted in both the New World and the Old. That a private individual had such a mechanism at his disposal was less than probable. Where and when had he built it, and how could he have built it in secret?
+Only some government could own such an engine of destruction, and in these disaster-filled times, when men tax their ingenuity to build increasingly powerful aggressive weapons, it was possible that, unknown to the rest of the world, some nation could have been testing such a fearsome machine. The Chassepot rifle led to the torpedo, and the torpedo has led to this underwater battering ram, which in turn will lead to the world putting its foot down. At least I hope it will.
+But this hypothesis of a war machine collapsed in the face of formal denials from the various governments. Since the public interest was at stake and transoceanic travel was suffering, the sincerity of these governments could not be doubted. Besides, how could the assembly of this underwater boat have escaped public notice? Keeping a secret under such circumstances would be difficult enough for an individual, and certainly impossible for a nation whose every move is under constant surveillance by rival powers.
+So, after inquiries conducted in England, France, Russia, Prussia, Spain, Italy, America, and even Turkey, the hypothesis of an underwater Monitor was ultimately rejected.
+After I arrived in New York, several people did me the honor of consulting me on the phenomenon in question. In France I had published a two-volume work, in quarto, entitled The Mysteries of the Great Ocean Depths. Well received in scholarly circles, this book had established me as a specialist in this pretty obscure field of natural history. My views were in demand. As long as I could deny the reality of the business, I confined myself to a flat "no comment." But soon, pinned to the wall, I had to explain myself straight out. And in this vein, "the honorable Pierre Aronnax, Professor at the Paris Museum," was summoned by The New York Herald to formulate his views no matter what.
+I complied. Since I could no longer hold my tongue, I let it wag. I discussed the question in its every aspect, both political and scientific, and this is an excerpt from the well-padded article I published in the issue of April 30.
+
+"Therefore," I wrote, "after examining these different hypotheses one by one, we are forced, every other supposition having been refuted, to accept the existence of an extremely powerful marine animal.
+"The deepest parts of the ocean are totally unknown to us. No soundings have been able to reach them. What goes on in those distant depths? What creatures inhabit, or could inhabit, those regions twelve or fifteen miles beneath the surface of the water? What is the constitution of these animals? It's almost beyond conjecture.
+"However, the solution to this problem submitted to me can take the form of a choice between two alternatives.
+"Either we know every variety of creature populating our planet, or we do not.
+"If we do not know every one of them, if nature still keeps ichthyological secrets from us, nothing is more admissible than to accept the existence of fish or cetaceans of new species or even new genera, animals with a basically 'cast-iron' constitution that inhabit strata beyond the reach of our soundings, and which some development or other, an urge or a whim if you prefer, can bring to the upper level of the ocean for long intervals.
+"If, on the other hand, we do know every living species, we must look for the animal in question among those marine creatures already cataloged, and in this event I would be inclined to accept the existence of a giant narwhale.
+"The common narwhale, or sea unicorn, often reaches a length of sixty feet. Increase its dimensions fivefold or even tenfold, then give this cetacean a strength in proportion to its size while enlarging its offensive weapons, and you have the animal we're looking for. It would have the proportions determined by the officers of the Shannon, the instrument needed to perforate the Scotia, and the power to pierce a steamer's hull.
+"In essence, the narwhale is armed with a sort of ivory sword, or lance, as certain naturalists have expressed it. It's a king-sized tooth as hard as steel. Some of these teeth have been found buried in the bodies of baleen whales, which the narwhale attacks with invariable success. Others have been wrenched, not without difficulty, from the undersides of vessels that narwhales have pierced clean through, as a gimlet pierces a wine barrel. The museum at the Faculty of Medicine in Paris owns one of these tusks with a length of 2.25 meters and a width at its base of forty-eight centimeters!
+"All right then! Imagine this weapon to be ten times stronger and the animal ten times more powerful, launch it at a speed of twenty miles per hour, multiply its mass times its velocity, and you get just the collision we need to cause the specified catastrophe.
+"So, until information becomes more abundant, I plump for a sea unicorn of colossal dimensions, no longer armed with a mere lance but with an actual spur, like ironclad frigates or those warships called 'rams,' whose mass and motor power it would possess simultaneously.
+"This inexplicable phenomenon is thus explained away--unless it's something else entirely, which, despite everything that has been sighted, studied, explored and experienced, is still possible!"
diff --git a/text/countries.txt b/text/countries.txt
new file mode 100644
index 0000000..5a48a42
--- /dev/null
+++ b/text/countries.txt
@@ -0,0 +1,15 @@
+Austria;Vienna;83859;8075
+Belgium;Brussels;30518;10192
+Denmark;Copenhagen;43094;5295
+Finland;Helsinki;304529;5147
+France;Paris;543965;58728
+Germany;Berlin;357022;82057
+Greece;Athens;131625;10511
+Ireland;Dublin;70723;3694
+Italy;Roma;301316;57563
+Luxembourg;Luxembourg;2586;424
+Netherlands;Amsterdam;41526;15654
+Portugal;Lisbon;91906;9957
+Spain;Madrid;504790;39348
+Sweden;Stockholm;410934;8839
+United Kingdom;London;243820;58862
diff --git a/ttfparser.go b/ttfparser.go
new file mode 100644
index 0000000..323f144
--- /dev/null
+++ b/ttfparser.go
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+// Utility to parse TTF font files
+// Version: 1.0
+// Date: 2011-06-18
+// Author: Olivier PLATHEY
+// Port to Go: Kurt Jung, 2013-07-15
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+ "regexp"
+ "strings"
+)
+
+type TtfType struct {
+ Embeddable bool
+ UnitsPerEm uint16
+ PostScriptName string
+ Bold bool
+ ItalicAngle int16
+ IsFixedPitch bool
+ TypoAscender int16
+ TypoDescender int16
+ UnderlinePosition int16
+ UnderlineThickness int16
+ Xmin, Ymin, Xmax, Ymax int16
+ CapHeight int16
+ Widths []uint16
+ Chars map[uint16]uint16
+}
+
+type ttfParser struct {
+ rec TtfType
+ f *os.File
+ tables map[string]uint32
+ numberOfHMetrics uint16
+ numGlyphs uint16
+}
+
+// Extract various metrics from a TrueType font file.
+func TtfParse(fileStr string) (TtfRec TtfType, err error) {
+ var t ttfParser
+ t.f, err = os.Open(fileStr)
+ if err != nil {
+ return
+ }
+ version, err := t.ReadStr(4)
+ if err != nil {
+ return
+ }
+ if version == "OTTO" {
+ err = fmt.Errorf("OpenType fonts based on PostScript outlines are not supported")
+ return
+ }
+ if version != "\x00\x01\x00\x00" {
+ err = fmt.Errorf("Unrecognized file format")
+ return
+ }
+ numTables := int(t.ReadUShort())
+ t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
+ t.tables = make(map[string]uint32)
+ var tag string
+ for j := 0; j < numTables; j++ {
+ tag, err = t.ReadStr(4)
+ if err != nil {
+ return
+ }
+ t.Skip(4) // checkSum
+ offset := t.ReadULong()
+ t.Skip(4) // length
+ t.tables[tag] = offset
+ }
+ if err = t.ParseHead(); err != nil {
+ return
+ }
+ if err = t.ParseHhea(); err != nil {
+ return
+ }
+ if err = t.ParseMaxp(); err != nil {
+ return
+ }
+ if err = t.ParseHmtx(); err != nil {
+ return
+ }
+ if err = t.ParseCmap(); err != nil {
+ return
+ }
+ if err = t.ParseName(); err != nil {
+ return
+ }
+ if err = t.ParseOS2(); err != nil {
+ return
+ }
+ if err = t.ParsePost(); err != nil {
+ return
+ }
+ t.f.Close()
+ TtfRec = t.rec
+ return
+}
+
+func (t *ttfParser) ParseHead() (err error) {
+ err = t.Seek("head")
+ t.Skip(3 * 4) // version, fontRevision, checkSumAdjustment
+ magicNumber := t.ReadULong()
+ if magicNumber != 0x5F0F3CF5 {
+ err = fmt.Errorf("Incorrect magic number")
+ return
+ }
+ t.Skip(2) // flags
+ t.rec.UnitsPerEm = t.ReadUShort()
+ t.Skip(2 * 8) // created, modified
+ t.rec.Xmin = t.ReadShort()
+ t.rec.Ymin = t.ReadShort()
+ t.rec.Xmax = t.ReadShort()
+ t.rec.Ymax = t.ReadShort()
+ return
+}
+
+func (t *ttfParser) ParseHhea() (err error) {
+ err = t.Seek("hhea")
+ if err == nil {
+ t.Skip(4 + 15*2)
+ t.numberOfHMetrics = t.ReadUShort()
+ }
+ return
+}
+
+func (t *ttfParser) ParseMaxp() (err error) {
+ err = t.Seek("maxp")
+ if err == nil {
+ t.Skip(4)
+ t.numGlyphs = t.ReadUShort()
+ }
+ return
+}
+
+func (t *ttfParser) ParseHmtx() (err error) {
+ err = t.Seek("hmtx")
+ if err == nil {
+ t.rec.Widths = make([]uint16, 0, 8)
+ for j := uint16(0); j < t.numberOfHMetrics; j++ {
+ t.rec.Widths = append(t.rec.Widths, t.ReadUShort())
+ t.Skip(2) // lsb
+ }
+ if t.numberOfHMetrics < t.numGlyphs {
+ lastWidth := t.rec.Widths[t.numberOfHMetrics-1]
+ for j := t.numberOfHMetrics; j < t.numGlyphs; j++ {
+ t.rec.Widths = append(t.rec.Widths, lastWidth)
+ }
+ }
+ }
+ return
+}
+
+func (t *ttfParser) ParseCmap() (err error) {
+ var offset int64
+ if err = t.Seek("cmap"); err != nil {
+ return
+ }
+ t.Skip(2) // version
+ numTables := int(t.ReadUShort())
+ offset31 := int64(0)
+ for j := 0; j < numTables; j++ {
+ platformID := t.ReadUShort()
+ encodingID := t.ReadUShort()
+ offset = int64(t.ReadULong())
+ if platformID == 3 && encodingID == 1 {
+ offset31 = offset
+ }
+ }
+ if offset31 == 0 {
+ err = fmt.Errorf("No Unicode encoding found")
+ return
+ }
+ startCount := make([]uint16, 0, 8)
+ endCount := make([]uint16, 0, 8)
+ idDelta := make([]int16, 0, 8)
+ idRangeOffset := make([]uint16, 0, 8)
+ t.rec.Chars = make(map[uint16]uint16)
+ t.f.Seek(int64(t.tables["cmap"])+offset31, os.SEEK_SET)
+ format := t.ReadUShort()
+ if format != 4 {
+ err = fmt.Errorf("Unexpected subtable format: %d", format)
+ return
+ }
+ t.Skip(2 * 2) // length, language
+ segCount := int(t.ReadUShort() / 2)
+ t.Skip(3 * 2) // searchRange, entrySelector, rangeShift
+ for j := 0; j < segCount; j++ {
+ endCount = append(endCount, t.ReadUShort())
+ }
+ t.Skip(2) // reservedPad
+ for j := 0; j < segCount; j++ {
+ startCount = append(startCount, t.ReadUShort())
+ }
+ for j := 0; j < segCount; j++ {
+ idDelta = append(idDelta, t.ReadShort())
+ }
+ offset, _ = t.f.Seek(int64(0), os.SEEK_CUR)
+ for j := 0; j < segCount; j++ {
+ idRangeOffset = append(idRangeOffset, t.ReadUShort())
+ }
+ for j := 0; j < segCount; j++ {
+ c1 := startCount[j]
+ c2 := endCount[j]
+ d := idDelta[j]
+ ro := idRangeOffset[j]
+ if ro > 0 {
+ t.f.Seek(offset+2*int64(j)+int64(ro), os.SEEK_SET)
+ }
+ for c := c1; c <= c2; c++ {
+ if c == 0xFFFF {
+ break
+ }
+ var gid int32
+ if ro > 0 {
+ gid = int32(t.ReadUShort())
+ if gid > 0 {
+ gid += int32(d)
+ }
+ } else {
+ gid = int32(c) + int32(d)
+ }
+ if gid >= 65536 {
+ gid -= 65536
+ }
+ if gid > 0 {
+ t.rec.Chars[c] = uint16(gid)
+ }
+ }
+ }
+ return
+}
+
+func (t *ttfParser) ParseName() (err error) {
+ err = t.Seek("name")
+ if err == nil {
+ tableOffset, _ := t.f.Seek(0, os.SEEK_CUR)
+ t.rec.PostScriptName = ""
+ t.Skip(2) // format
+ count := t.ReadUShort()
+ stringOffset := t.ReadUShort()
+ for j := uint16(0); j < count && t.rec.PostScriptName == ""; j++ {
+ t.Skip(3 * 2) // platformID, encodingID, languageID
+ nameID := t.ReadUShort()
+ length := t.ReadUShort()
+ offset := t.ReadUShort()
+ if nameID == 6 {
+ // PostScript name
+ t.f.Seek(int64(tableOffset)+int64(stringOffset)+int64(offset), os.SEEK_SET)
+ var s string
+ s, err = t.ReadStr(int(length))
+ if err != nil {
+ return
+ }
+ s = strings.Replace(s, "\x00", "", -1)
+ var re *regexp.Regexp
+ if re, err = regexp.Compile("[(){}<> /%[\\]]"); err != nil {
+ return
+ }
+ t.rec.PostScriptName = re.ReplaceAllString(s, "")
+ }
+ }
+ if t.rec.PostScriptName == "" {
+ err = fmt.Errorf("PostScript name not found")
+ }
+ }
+ return
+}
+
+func (t *ttfParser) ParseOS2() (err error) {
+ err = t.Seek("OS/2")
+ if err == nil {
+ version := t.ReadUShort()
+ t.Skip(3 * 2) // xAvgCharWidth, usWeightClass, usWidthClass
+ fsType := t.ReadUShort()
+ t.rec.Embeddable = (fsType != 2) && (fsType&0x200) == 0
+ t.Skip(11*2 + 10 + 4*4 + 4)
+ fsSelection := t.ReadUShort()
+ t.rec.Bold = (fsSelection & 32) != 0
+ t.Skip(2 * 2) // usFirstCharIndex, usLastCharIndex
+ t.rec.TypoAscender = t.ReadShort()
+ t.rec.TypoDescender = t.ReadShort()
+ if version >= 2 {
+ t.Skip(3*2 + 2*4 + 2)
+ t.rec.CapHeight = t.ReadShort()
+ } else {
+ t.rec.CapHeight = 0
+ }
+ }
+ return
+}
+
+func (t *ttfParser) ParsePost() (err error) {
+ err = t.Seek("post")
+ if err == nil {
+ t.Skip(4) // version
+ t.rec.ItalicAngle = t.ReadShort()
+ t.Skip(2) // Skip decimal part
+ t.rec.UnderlinePosition = t.ReadShort()
+ t.rec.UnderlineThickness = t.ReadShort()
+ t.rec.IsFixedPitch = t.ReadULong() != 0
+ }
+ return
+}
+
+func (t *ttfParser) Seek(tag string) (err error) {
+ ofs, ok := t.tables[tag]
+ if ok {
+ t.f.Seek(int64(ofs), os.SEEK_SET)
+ } else {
+ err = fmt.Errorf("Table not found: %s", tag)
+ }
+ return
+}
+
+func (t *ttfParser) Skip(n int) {
+ t.f.Seek(int64(n), os.SEEK_CUR)
+}
+
+func (t *ttfParser) ReadStr(length int) (str string, err error) {
+ var n int
+ buf := make([]byte, length)
+ n, err = t.f.Read(buf)
+ if err == nil {
+ if n == length {
+ str = string(buf)
+ } else {
+ err = fmt.Errorf("Unable to read %d bytes", length)
+ }
+ }
+ return
+}
+
+func (t *ttfParser) ReadUShort() (val uint16) {
+ binary.Read(t.f, binary.BigEndian, &val)
+ return
+}
+
+func (t *ttfParser) ReadShort() (val int16) {
+ binary.Read(t.f, binary.BigEndian, &val)
+ return
+}
+
+func (t *ttfParser) ReadULong() (val uint32) {
+ binary.Read(t.f, binary.BigEndian, &val)
+ return
+}
diff --git a/ttfparser_test.go b/ttfparser_test.go
new file mode 100644
index 0000000..dcdab4e
--- /dev/null
+++ b/ttfparser_test.go
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "fmt"
+ "testing"
+)
+
+func ExampleTtfParse() {
+ ttf, err := TtfParse(GOFPDF_DIR + "/font/calligra.ttf")
+ if err == nil {
+ fmt.Printf("Postscript name: %s\n", ttf.PostScriptName)
+ fmt.Printf("unitsPerEm: %8d\n", ttf.UnitsPerEm)
+ fmt.Printf("Xmin: %8d\n", ttf.Xmin)
+ fmt.Printf("Ymin: %8d\n", ttf.Ymin)
+ fmt.Printf("Xmax: %8d\n", ttf.Xmax)
+ fmt.Printf("Ymax: %8d\n", ttf.Ymax)
+ } else {
+ fmt.Printf("%s\n", err)
+ }
+ // Output:
+ // Postscript name: CalligrapherRegular
+ // unitsPerEm: 1000
+ // Xmin: -173
+ // Ymin: -234
+ // Xmax: 1328
+ // Ymax: 899
+}
+
+func TestLoadMap(t *testing.T) {
+ expectList := []string{
+ "164: 0x0E04 khokhwaithai",
+ "165: 0x0E05 khokhonthai",
+ "166: 0x0E06 khorakhangthai",
+ "167: 0x0E07 ngonguthai",
+ "168: 0x0E08 chochanthai",
+ "169: 0x0E09 chochingthai",
+ }
+ list, err := loadMap(GOFPDF_DIR + "/font/iso-8859-11.map")
+ if err == nil {
+ pos := 0
+ for j := 164; j < 170; j++ {
+ enc := list[j]
+ str := fmt.Sprintf("%3d: 0x%04X %s", j, enc.uv, enc.name)
+ // fmt.Printf("Expect [%s], Got [%s]\n", expectList[pos], str)
+ if expectList[pos] != str {
+ t.Fatalf("Unexpected output from loadMap")
+ }
+ pos++
+ }
+ }
+}
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..a1cf6f6
--- /dev/null
+++ b/util.go
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2013 Kurt Jung (Gmail: kurt.w.jung)
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gofpdf
+
+import (
+ "bytes"
+ "compress/zlib"
+ "fmt"
+ "math"
+ "os"
+ "regexp"
+ "strings"
+)
+
+func round(f float64) int {
+ if f < 0 {
+ return -int(math.Floor(-f + 0.5))
+ } else {
+ return int(math.Floor(f + 0.5))
+ }
+}
+
+func sprintf(fmtStr string, args ...interface{}) string {
+ return fmt.Sprintf(fmtStr, args...)
+}
+
+// Returns true if the specified normal file exists
+func fileExist(filename string) (ok bool) {
+ info, err := os.Stat(filename)
+ if err == nil {
+ if ^os.ModePerm&info.Mode() == 0 {
+ ok = true
+ }
+ }
+ return ok
+}
+
+// Returns the size of the specified file; ok will be false
+// if the file does not exist or is not an ordinary file
+func fileSize(filename string) (size int64, ok bool) {
+ info, err := os.Stat(filename)
+ ok = err == nil
+ if ok {
+ size = info.Size()
+ }
+ return
+}
+
+// Returns a new buffer populated with the contents of the specified file
+func bufferFromFile(fileStr string) (b *bytes.Buffer, err error) {
+ var fl *os.File
+ fl, err = os.Open(fileStr)
+ if err != nil {
+ return
+ }
+ defer fl.Close()
+ b = new(bytes.Buffer)
+ _, err = b.ReadFrom(fl)
+ return
+}
+
+// Returns a zlib-compressed copy of the specified byte array
+func sliceCompress(data []byte) []byte {
+ var buf bytes.Buffer
+ cmp := zlib.NewWriter(&buf)
+ cmp.Write(data)
+ cmp.Close()
+ return buf.Bytes()
+}
+
+// Returns an uncompressed copy of the specified zlib-compressed byte array
+func sliceUncompress(data []byte) (outData []byte, err error) {
+ inBuf := bytes.NewBuffer(data)
+ r, err := zlib.NewReader(inBuf)
+ defer r.Close()
+ if err == nil {
+ var outBuf bytes.Buffer
+ _, err = outBuf.ReadFrom(r)
+ if err == nil {
+ outData = outBuf.Bytes()
+ }
+ }
+ return
+}
+
+// Convert 'ABCDEFG' to, for example, 'A,BCD,EFG'
+func strDelimit(str string, sepstr string, sepcount int) string {
+ pos := len(str) - sepcount
+ for pos > 0 {
+ str = str[:pos] + sepstr + str[pos:]
+ pos = pos - sepcount
+ }
+ return str
+}
+
+type htmlSegmentType struct {
+ cat byte // 'O' open tag, 'C' close tag, 'T' text
+ str string // Literal text unchanged, tags are lower case
+ attr map[string]string // Attribute keys are lower case
+}
+
+// Returns a list of HTML tags and literal elements. This is done with regular
+// expressions, so the result is only marginally better than useless.
+// Adapted from http://www.fpdf.org/
+func htmlTokenize(htmlStr string) (list []htmlSegmentType) {
+ list = make([]htmlSegmentType, 0, 16)
+ htmlStr = strings.Replace(htmlStr, "\n", " ", -1)
+ htmlStr = strings.Replace(htmlStr, "\r", "", -1)
+ tagRe, _ := regexp.Compile(`(?U)<.*>`)
+ attrRe, _ := regexp.Compile(`([^=]+)=["']?([^"']+)`)
+ capList := tagRe.FindAllStringIndex(htmlStr, -1)
+ if list != nil {
+ var seg htmlSegmentType
+ var parts []string
+ pos := 0
+ for _, cap := range capList {
+ if pos < cap[0] {
+ seg.cat = 'T'
+ seg.str = htmlStr[pos:cap[0]]
+ seg.attr = nil
+ list = append(list, seg)
+ }
+ if htmlStr[cap[0]+1] == '/' {
+ seg.cat = 'C'
+ seg.str = strings.ToLower(htmlStr[cap[0]+2 : cap[1]-1])
+ seg.attr = nil
+ list = append(list, seg)
+ } else {
+ // Extract attributes
+ parts = strings.Split(htmlStr[cap[0]+1:cap[1]-1], " ")
+ if len(parts) > 0 {
+ for j, part := range parts {
+ if j == 0 {
+ seg.cat = 'O'
+ seg.str = strings.ToLower(parts[0])
+ seg.attr = make(map[string]string)
+ } else {
+ attrList := attrRe.FindAllStringSubmatch(part, -1)
+ if attrList != nil {
+ for _, attr := range attrList {
+ seg.attr[strings.ToLower(attr[1])] = attr[2]
+ }
+ }
+ }
+ }
+ list = append(list, seg)
+ }
+ }
+ pos = cap[1]
+ }
+ if len(htmlStr) > pos {
+ seg.cat = 'T'
+ seg.str = htmlStr[pos:]
+ seg.attr = nil
+ list = append(list, seg)
+ }
+ }
+ return
+}
+
+// Convert UTF-8 to UTF-16BE with BOM; from http://www.fpdf.org/
+func utf8toutf16(s string) string {
+ res := make([]byte, 0, 8)
+ res = append(res, 0xFE, 0xFF)
+ nb := len(s)
+ i := 0
+ for i < nb {
+ c1 := byte(s[i])
+ i++
+ if c1 >= 224 {
+ // 3-byte character
+ c2 := byte(s[i])
+ i++
+ c3 := byte(s[i])
+ i++
+ res = append(res, ((c1&0x0F)<<4)+((c2&0x3C)>>2),
+ ((c2&0x03)<<6)+(c3&0x3F))
+ } else if c1 >= 192 {
+ // 2-byte character
+ c2 := byte(s[i])
+ i++
+ res = append(res, ((c1 & 0x1C) >> 2),
+ ((c1&0x03)<<6)+(c2&0x3F))
+ } else {
+ // Single-byte character
+ res = append(res, 0, c1)
+ }
+ }
+ return string(res)
+}