Skip to content

Commit

Permalink
Fix #773
Browse files Browse the repository at this point in the history
  • Loading branch information
hhrutter committed Feb 10, 2024
1 parent 055e03f commit 032b32d
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 93 deletions.
3 changes: 1 addition & 2 deletions pkg/api/test/createFromJSON_test.go
Expand Up @@ -163,7 +163,6 @@ func TestCreateSinglePageDemoFormsViaJson(t *testing.T) {
inFileJSON string
outFile string
}{

{"TestFormDemoEN", "english.json", "english.pdf"}, // Core font (Helvetica)
{"TestFormDemoUK", "ukrainian.json", "ukrainian.pdf"}, // User font (Roboto-Regular)
{"TestFormDemoAR", "arabic.json", "arabic.pdf"}, // User font RTL (Roboto-Regular)
Expand Down Expand Up @@ -203,7 +202,6 @@ func TestCreateDemoFormsViaJson(t *testing.T) {
{"TestFormDemoIC", "icelandic.json", "icelandic.pdf"},
{"TestFormDemoIT", "italian.json", "italian.pdf"},
{"TestFormDemoNO", "norwegian.json", "norwegian.pdf"},
{"TestFormDemoPO", "polish.json", "polish.pdf"},
{"TestFormDemoPT", "portuguese.json", "portuguese.pdf"},
{"TestFormDemoSK", "slovak.json", "slovak.pdf"},
{"TestFormDemoSL", "slovenian.json", "slovenian.pdf"},
Expand All @@ -218,6 +216,7 @@ func TestCreateDemoFormsViaJson(t *testing.T) {
{"TestFormDemoCZ", "czech.json", "czech.pdf"},
{"TestFormDemoGR", "greek.json", "greek.pdf"},
{"TestFormDemoKU", "kurdish.json", "kurdish.pdf"},
{"TestFormDemoPO", "polish.json", "polish.pdf"},
{"TestFormDemoRO", "romanian.json", "romanian.pdf"},
{"TestFormDemoRU", "russian.json", "russian.pdf"},
{"TestFormDemoTK", "turkish.json", "turkish.pdf"},
Expand Down
15 changes: 14 additions & 1 deletion pkg/font/install.go
Expand Up @@ -663,7 +663,10 @@ func installTrueTypeRep(fontDir, fontName string, header []byte, tables map[stri
}
fd.FontFile = bb

log.CLI.Println(fd.PostscriptName)
if log.CLIEnabled() {
log.CLI.Println(fd.PostscriptName)
}

gobName := filepath.Join(fontDir, fd.PostscriptName+".gob")

// Write the populated ttf struct as gob.
Expand Down Expand Up @@ -747,6 +750,16 @@ func InstallTrueTypeFont(fontDir, fontName string) error {
return installTrueTypeRep(fontDir, fontName, header, tables)
}

// InstallFontFromBytes saves an internal representation of TrueType font fontName to the pdfcpu config dir.
func InstallFontFromBytes(fontDir, fontName string, bb []byte) error {
rd := bytes.NewReader(bb)
header, tables, err := headerAndTables(fontName, rd, 0)
if err != nil {
return err
}
return installTrueTypeRep(fontDir, fontName, header, tables)
}

func ttfTables(tableCount int, bb []byte) (map[string]*table, error) {
tables := map[string]*table{}
b := bb[12:]
Expand Down
7 changes: 7 additions & 0 deletions pkg/font/metrics.go
Expand Up @@ -347,6 +347,13 @@ func Size(text, fontName string, width float64) int {
return fontScalingFactor(float64(w), width)
}

// SizeForLineHeight returns the needed font size in points
// for rendering using a given font name fitting into given line height lh.
func SizeForLineHeight(fontName string, lh float64) int {
fbb := BoundingBox(fontName)
return int(math.Round(lh / (fbb.Height() / 1000)))
}

// UserSpaceFontBBox returns the font box for given font name and font size in user space coordinates.
func UserSpaceFontBBox(fontName string, fontSize int) *types.Rectangle {
fontBBox := BoundingBox(fontName)
Expand Down
87 changes: 75 additions & 12 deletions pkg/pdfcpu/font/fontDict.go
Expand Up @@ -51,8 +51,10 @@ var cjkParms = map[string]cjk{
"KANA": {"UniJIS-UTF16-H", "Japan1", 7},
"JPAN": {"UniJIS-UTF16-H", "Japan1", 7},
// K
"HANG": {"UniKS-UTF16-H", "KR", 9},
"KORE": {"UniKS-UTF16-H", "KR", 9},
"HANG": {"UniKS-UTF16-H", "Korea1", 1},
"KORE": {"UniKS-UTF16-H", "Korea1", 1},
//"HANG": {"UniKS-UTF16-H", "KR", 9},
//"KORE": {"UniKS-UTF16-H", "KR", 9},
}

func SupportedScript(s string) bool {
Expand Down Expand Up @@ -172,6 +174,56 @@ func ttfSubFontFile(xRefTable *model.XRefTable, ttf font.TTFLight, fontName stri
return indRef, nil
}

func PDFDocEncoding(xRefTable *model.XRefTable) (*types.IndirectRef, error) {
arr := types.Array{
types.Integer(24),
types.Name("breve"), types.Name("caron"), types.Name("circumflex"), types.Name("dotaccent"),
types.Name("hungarumlaut"), types.Name("ogonek"), types.Name("ring"), types.Name("tilde"),
types.Integer(39),
types.Name("quotesingle"),
types.Integer(96),
types.Name("grave"),
types.Integer(128),
types.Name("bullet"), types.Name("dagger"), types.Name("daggerdbl"), types.Name("ellipsis"), types.Name("emdash"), types.Name("endash"),
types.Name("florin"), types.Name("fraction"), types.Name("guilsinglleft"), types.Name("guilsinglright"), types.Name("minus"), types.Name("perthousand"),
types.Name("quotedblbase"), types.Name("quotedblleft"), types.Name("quotedblright"), types.Name("quoteleft"), types.Name("quoteright"), types.Name("quotesinglbase"),
types.Name("trademark"), types.Name("fi"), types.Name("fl"), types.Name("Lslash"), types.Name("OE"), types.Name("Scaron"), types.Name("Ydieresis"),
types.Name("Zcaron"), types.Name("dotlessi"), types.Name("lslash"), types.Name("oe"), types.Name("scaron"), types.Name("zcaron"),
types.Integer(160),
types.Name("Euro"),
types.Integer(164),
types.Name("currency"),
types.Integer(166),
types.Name("brokenbar"), types.Integer(168), types.Name("dieresis"), types.Name("copyright"), types.Name("ordfeminine"),
types.Integer(172),
types.Name("logicalnot"), types.Name(".notdef"), types.Name("registered"), types.Name("macron"), types.Name("degree"),
types.Name("plusminus"), types.Name("twosuperior"), types.Name("threesuperior"), types.Name("acute"), types.Name("mu"),
types.Integer(183),
types.Name("periodcentered"), types.Name("cedilla"), types.Name("onesuperior"), types.Name("ordmasculine"),
types.Integer(188),
types.Name("onequarter"), types.Name("onehalf"), types.Name("threequarters"),
types.Integer(192),
types.Name("Agrave"), types.Name("Aacute"), types.Name("Acircumflex"), types.Name("Atilde"), types.Name("Adieresis"), types.Name("Aring"), types.Name("AE"),
types.Name("Ccedilla"), types.Name("Egrave"), types.Name("Eacute"), types.Name("Ecircumflex"), types.Name("Edieresis"), types.Name("Igrave"), types.Name("Iacute"),
types.Name("Icircumflex"), types.Name("Idieresis"), types.Name("Eth"), types.Name("Ntilde"), types.Name("Ograve"), types.Name("Oacute"), types.Name("Ocircumflex"),
types.Name("Otilde"), types.Name("Odieresis"), types.Name("multiply"), types.Name("Oslash"), types.Name("Ugrave"), types.Name("Uacute"), types.Name("Ucircumflex"),
types.Name("Udieresis"), types.Name("Yacute"), types.Name("Thorn"), types.Name("germandbls"), types.Name("agrave"), types.Name("aacute"), types.Name("acircumflex"),
types.Name("atilde"), types.Name("adieresis"), types.Name("aring"), types.Name("ae"), types.Name("ccedilla"), types.Name("egrave"), types.Name("eacute"), types.Name("ecircumflex"),
types.Name("edieresis"), types.Name("igrave"), types.Name("iacute"), types.Name("icircumflex"), types.Name("idieresis"), types.Name("eth"), types.Name("ntilde"),
types.Name("ograve"), types.Name("oacute"), types.Name("ocircumflex"), types.Name("otilde"), types.Name("odieresis"), types.Name("divide"), types.Name("oslash"),
types.Name("ugrave"), types.Name("uacute"), types.Name("ucircumflex"), types.Name("udieresis"), types.Name("yacute"), types.Name("thorn"), types.Name("ydieresis"),
}

d := types.Dict(
map[string]types.Object{
"Type": types.Name("Encoding"),
"Differences": arr,
},
)

return xRefTable.IndRefForNewObject(d)
}

func coreFontDict(xRefTable *model.XRefTable, coreFontName string) (*types.IndirectRef, error) {
d := types.NewDict()
d.InsertName("Type", "Font")
Expand All @@ -180,6 +232,15 @@ func coreFontDict(xRefTable *model.XRefTable, coreFontName string) (*types.Indir
if coreFontName != "Symbol" && coreFontName != "ZapfDingbats" {
d.InsertName("Encoding", "WinAnsiEncoding")
}
// if coreFontName == "Helvetica" {
// indRef, err := PDFDocEncoding(xRefTable)
// if err != nil {
// return nil, err
// }
// d.Insert("Encoding", *indRef)
// } else if coreFontName != "Symbol" && coreFontName != "ZapfDingbats" {
// d.InsertName("Encoding", "WinAnsiEncoding")
// }
return xRefTable.IndRefForNewObject(d)
}

Expand Down Expand Up @@ -734,8 +795,8 @@ func subFontPrefix() string {
return string(bb)
}

// CIDFontSpecialEncDict returns the descendant font dict with special encoding for Type0 fonts.
func CIDFontSpecialEncDict(xRefTable *model.XRefTable, ttf font.TTFLight, fontName, baseFontName, lang string, parms *cjk) (*types.IndirectRef, error) {
// CIDFontDict returns the descendant font dict with special encoding for Type0 fonts.
func CIDFontDict(xRefTable *model.XRefTable, ttf font.TTFLight, fontName, baseFontName, lang string, parms *cjk) (*types.IndirectRef, error) {
fdIndRef, err := CIDFontDescriptor(xRefTable, ttf, fontName, baseFontName, lang, parms == nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -798,12 +859,14 @@ func CIDFontSpecialEncDict(xRefTable *model.XRefTable, ttf font.TTFLight, fontNa
d["CIDToGIDMap"] = types.Name("Identity")
}

wIndRef, err := CIDWidths(xRefTable, ttf, fontName, parms == nil, nil)
if err != nil {
return nil, err
}
if wIndRef != nil {
d["W"] = *wIndRef
if parms == nil {
wIndRef, err := CIDWidths(xRefTable, ttf, fontName, parms == nil, nil)
if err != nil {
return nil, err
}
if wIndRef != nil {
d["W"] = *wIndRef
}
}

return xRefTable.IndRefForNewObject(d)
Expand Down Expand Up @@ -841,7 +904,7 @@ func type0FontDict(xRefTable *model.XRefTable, fontName, lang, script string, in
encoding = parms.encoding
}

descendentFontIndRef, err := CIDFontSpecialEncDict(xRefTable, ttf, fontName, baseFontName, lang, parms)
descendentFontIndRef, err := CIDFontDict(xRefTable, ttf, fontName, baseFontName, lang, parms)
if err != nil {
return nil, err
}
Expand All @@ -850,7 +913,7 @@ func type0FontDict(xRefTable *model.XRefTable, fontName, lang, script string, in
d.InsertName("Type", "Font")
d.InsertName("Subtype", "Type0")
d.InsertName("BaseFont", baseFontName)
//d.InsertName("Name", fontName)
d.InsertName("Name", fontName)
d.InsertName("Encoding", encoding)
d.Insert("DescendantFonts", types.Array{*descendentFontIndRef})

Expand Down
3 changes: 2 additions & 1 deletion pkg/pdfcpu/form/fill.go
Expand Up @@ -1096,6 +1096,7 @@ func FillForm(
if len(ctx.UsedGIDs[fName]) == 0 {
continue
}
// Update user font.
fDict, err := xRefTable.DereferenceDict(indRef)
if err != nil {
return false, nil, err
Expand All @@ -1119,7 +1120,7 @@ func FillForm(

// pdfcpu provides all appearance streams for form fields.
// Yet for some files and viewers form fields don't get rendered.
// In these cases you can order the viewer to provide form field appearance streams.
// In these cases you can force the viewer to provide form field appearance streams.
if ctx.NeedAppearances {
xRefTable.Form["NeedAppearances"] = types.Boolean(true)
}
Expand Down
25 changes: 24 additions & 1 deletion pkg/pdfcpu/model/configuration.go
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"github.com/pdfcpu/pdfcpu/pkg/font"
"github.com/pdfcpu/pdfcpu/pkg/log"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
)

Expand Down Expand Up @@ -248,9 +249,12 @@ var ConfigPath string = "default"

var loadedDefaultConfig *Configuration

//go:embed config.yml
//go:embed resources/config.yml
var configFileBytes []byte

//go:embed resources/Roboto-Regular.ttf
var robotoFontFileBytes []byte

func ensureConfigFileAt(path string) error {
f, err := os.Open(path)
if err != nil {
Expand Down Expand Up @@ -282,6 +286,25 @@ func EnsureDefaultConfigAt(path string) error {
return err
}
//fmt.Println(loadedDefaultConfig)

files, err := os.ReadDir(font.UserFontDir)
if err != nil {
return err
}

if len(files) == 0 {
// Ensure Roboto font for form filling.
fn := "Roboto-Regular"
if log.CLIEnabled() {
log.CLI.Printf("installing user font:")
}
if err := font.InstallFontFromBytes(font.UserFontDir, fn, robotoFontFileBytes); err != nil {
if log.CLIEnabled() {
log.CLI.Printf("%v", err)
}
}
}

return font.LoadUserFonts()
}

Expand Down
Binary file added pkg/pdfcpu/model/resources/Roboto-Regular.ttf
Binary file not shown.
File renamed without changes.
2 changes: 1 addition & 1 deletion pkg/pdfcpu/primitives/comboBox.go
Expand Up @@ -792,7 +792,7 @@ func refreshComboBoxAP(ctx *model.Context, d types.Dict, v string, fonts map[str
return err
}

return UpdateForm(ctx.XRefTable, bb, irN)
return updateForm(ctx.XRefTable, bb, irN)
}

func EnsureComboBoxAP(ctx *model.Context, d types.Dict, v string, fonts map[string]types.IndirectRef) error {
Expand Down
7 changes: 5 additions & 2 deletions pkg/pdfcpu/primitives/dateField.go
Expand Up @@ -449,7 +449,10 @@ func (df *DateField) renderN(xRefTable *model.XRefTable) ([]byte, error) {
}

f := df.Font
//cjk := fo.CJK(f.Script, f.Lang)
if float64(f.Size) > h {
f.Size = font.SizeForLineHeight(f.Name, h)
}

lineBB := model.CalcBoundingBox(v, 0, 0, f.Name, f.Size)
s := model.PrepBytes(xRefTable, v, f.Name, true, false)
x := 2 * boWidth
Expand Down Expand Up @@ -929,7 +932,7 @@ func refreshDateFieldAP(ctx *model.Context, d types.Dict, v string, fonts map[st
return err
}

return UpdateForm(ctx.XRefTable, bb, irN)
return updateForm(ctx.XRefTable, bb, irN)
}

func EnsureDateFieldAP(ctx *model.Context, d types.Dict, v string, fonts map[string]types.IndirectRef) error {
Expand Down
61 changes: 26 additions & 35 deletions pkg/pdfcpu/primitives/font.go
Expand Up @@ -201,15 +201,6 @@ func formFontIndRef(xRefTable *model.XRefTable, fontID string) (*types.IndirectR
}
}

if font.IsCoreFont(fontID) {
indRef, err := pdffont.EnsureFontDict(xRefTable, fontID, "", "", false, nil)
if err != nil {
return nil, err
}
d[fontID] = *indRef
return indRef, nil
}

return nil, nil
}

Expand Down Expand Up @@ -321,21 +312,34 @@ func fontFromAcroDict(xRefTable *model.XRefTable, fontIndRef *types.IndirectRef,
return err
}

// if fN != nil {
// println("FN: " + *fN)
// }
return nil
}

func ensureUTF8FormFont(ctx *model.Context, fonts map[string]types.IndirectRef) (string, string, string, *types.IndirectRef, error) {

// if fL != nil {
// println("FL: " + *fL)
// }
// TODO Make name of UTF-8 userfont part of pdfcpu configs.

// *fName = fN
fontID, fontName := "F0", "Roboto-Regular"

// if fL != nil {
// *fLang = *fL
// }
if indRef, ok := fonts[fontName]; ok {
return fontID, fontName, "", &indRef, nil
}

return nil
for objNr, fo := range ctx.Optimize.FontObjects {
if fo.FontName == fontName && fo.Prefix != "" {
indRef := types.NewIndirectRef(objNr, 0)
fonts[fontName] = *indRef
return fontID, fontName, "", indRef, nil
}
}

indRef, err := pdffont.EnsureFontDict(ctx.XRefTable, fontName, "", "", false, nil)
if err != nil {
return "", "", "", nil, err
}
fonts[fontName] = *indRef

return fontID, fontName, "", indRef, nil
}

func extractFormFontDetails(
Expand Down Expand Up @@ -371,21 +375,8 @@ func extractFormFontDetails(

}

if fontIndRef == nil || !font.SupportedFont(fName) {
var indRef types.IndirectRef
if err = fontFromAcroDict(xRefTable, &indRef, &fName, &fLang, fontID); err != nil {
return "", "", "", nil, err
}
fontIndRef = &indRef
}

// var lang string
// if fLang != nil {
// lang = *fLang
// }

if font.IsUserFont(fName) {
err = ensureCorrectFontIndRef(ctx, &fontIndRef, fName, fonts)
if fontIndRef == nil {
return ensureUTF8FormFont(ctx, fonts)
}

return fontID, fName, fLang, fontIndRef, err
Expand Down

0 comments on commit 032b32d

Please sign in to comment.