summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Jamfile25
-rw-r--r--src/autofit/Jamfile39
-rw-r--r--src/autofit/afangles.c292
-rw-r--r--src/autofit/afangles.h7
-rw-r--r--src/autofit/afcjk.c1506
-rw-r--r--src/autofit/afcjk.h41
-rw-r--r--src/autofit/afdummy.c62
-rw-r--r--src/autofit/afdummy.h43
-rw-r--r--src/autofit/aferrors.h40
-rw-r--r--src/autofit/afglobal.c289
-rw-r--r--src/autofit/afglobal.h67
-rw-r--r--src/autofit/afhints.c1264
-rw-r--r--src/autofit/afhints.h333
-rw-r--r--src/autofit/afindic.c134
-rw-r--r--src/autofit/afindic.h41
-rw-r--r--src/autofit/aflatin.c2166
-rw-r--r--src/autofit/aflatin.h209
-rw-r--r--src/autofit/aflatin2.c2286
-rw-r--r--src/autofit/aflatin2.h40
-rw-r--r--src/autofit/afloader.c530
-rw-r--r--src/autofit/afloader.h73
-rw-r--r--src/autofit/afmodule.c97
-rw-r--r--src/autofit/afmodule.h37
-rw-r--r--src/autofit/aftypes.h349
-rw-r--r--src/autofit/afwarp.c338
-rw-r--r--src/autofit/afwarp.h64
-rw-r--r--src/autofit/autofit.c40
-rw-r--r--src/autofit/module.mk23
-rw-r--r--src/autofit/rules.mk78
-rw-r--r--src/base/Jamfile50
-rw-r--r--src/base/ftapi.c121
-rw-r--r--src/base/ftbase.c38
-rw-r--r--src/base/ftbbox.c659
-rw-r--r--src/base/ftbdf.c88
-rw-r--r--src/base/ftbitmap.c630
-rw-r--r--src/base/ftcalc.c822
-rw-r--r--src/base/ftdbgmem.c998
-rw-r--r--src/base/ftdebug.c246
-rw-r--r--src/base/ftgasp.c61
-rw-r--r--src/base/ftgloadr.c394
-rw-r--r--src/base/ftglyph.c682
-rw-r--r--src/base/ftgxval.c129
-rw-r--r--src/base/ftinit.c163
-rw-r--r--src/base/ftlcdfil.c351
-rw-r--r--src/base/ftmac.c1096
-rw-r--r--src/base/ftmm.c202
-rw-r--r--src/base/ftnames.c94
-rw-r--r--src/base/ftobjs.c3993
-rw-r--r--src/base/ftotval.c83
-rw-r--r--src/base/ftoutln.c1088
-rw-r--r--src/base/ftpatent.c281
-rw-r--r--src/base/ftpfr.c132
-rw-r--r--src/base/ftrfork.c728
-rw-r--r--src/base/ftstream.c842
-rw-r--r--src/base/ftstroke.c2010
-rw-r--r--src/base/ftsynth.c159
-rw-r--r--src/base/ftsystem.c301
-rw-r--r--src/base/fttrigon.c546
-rw-r--r--src/base/fttype1.c94
-rw-r--r--src/base/ftutil.c501
-rw-r--r--src/base/ftwinfnt.c51
-rw-r--r--src/base/ftxf86.c40
-rw-r--r--src/base/rules.mk90
-rw-r--r--src/bdf/Jamfile29
-rw-r--r--src/bdf/README148
-rw-r--r--src/bdf/bdf.c34
-rw-r--r--src/bdf/bdf.h295
-rw-r--r--src/bdf/bdfdrivr.c848
-rw-r--r--src/bdf/bdfdrivr.h76
-rw-r--r--src/bdf/bdferror.h44
-rw-r--r--src/bdf/bdflib.c2472
-rw-r--r--src/bdf/module.mk34
-rw-r--r--src/bdf/rules.mk80
-rw-r--r--src/cache/Jamfile43
-rw-r--r--src/cache/ftcache.c31
-rw-r--r--src/cache/ftcbasic.c811
-rw-r--r--src/cache/ftccache.c592
-rw-r--r--src/cache/ftccache.h317
-rw-r--r--src/cache/ftccback.h90
-rw-r--r--src/cache/ftccmap.c413
-rw-r--r--src/cache/ftcerror.h40
-rw-r--r--src/cache/ftcglyph.c211
-rw-r--r--src/cache/ftcglyph.h322
-rw-r--r--src/cache/ftcimage.c163
-rw-r--r--src/cache/ftcimage.h107
-rw-r--r--src/cache/ftcmanag.c732
-rw-r--r--src/cache/ftcmanag.h175
-rw-r--r--src/cache/ftcmru.c357
-rw-r--r--src/cache/ftcmru.h247
-rw-r--r--src/cache/ftcsbits.c401
-rw-r--r--src/cache/ftcsbits.h98
-rw-r--r--src/cache/rules.mk78
-rw-r--r--src/cff/Jamfile29
-rw-r--r--src/cff/cff.c29
-rw-r--r--src/cff/cffcmap.c220
-rw-r--r--src/cff/cffcmap.h69
-rw-r--r--src/cff/cffdrivr.c499
-rw-r--r--src/cff/cffdrivr.h39
-rw-r--r--src/cff/cfferrs.h41
-rw-r--r--src/cff/cffgload.c2634
-rw-r--r--src/cff/cffgload.h208
-rw-r--r--src/cff/cffload.c1598
-rw-r--r--src/cff/cffload.h79
-rw-r--r--src/cff/cffobjs.c782
-rw-r--r--src/cff/cffobjs.h165
-rw-r--r--src/cff/cffparse.c688
-rw-r--r--src/cff/cffparse.h69
-rw-r--r--src/cff/cfftoken.h97
-rw-r--r--src/cff/cfftypes.h270
-rw-r--r--src/cff/module.mk23
-rw-r--r--src/cff/rules.mk72
-rw-r--r--src/cid/Jamfile29
-rw-r--r--src/cid/ciderrs.h40
-rw-r--r--src/cid/cidgload.c433
-rw-r--r--src/cid/cidgload.h51
-rw-r--r--src/cid/cidload.c644
-rw-r--r--src/cid/cidload.h53
-rw-r--r--src/cid/cidobjs.c480
-rw-r--r--src/cid/cidobjs.h154
-rw-r--r--src/cid/cidparse.c226
-rw-r--r--src/cid/cidparse.h123
-rw-r--r--src/cid/cidriver.c163
-rw-r--r--src/cid/cidriver.h39
-rw-r--r--src/cid/cidtoken.h103
-rw-r--r--src/cid/module.mk23
-rw-r--r--src/cid/rules.mk70
-rw-r--r--src/cid/type1cid.c29
-rw-r--r--src/gxvalid/Jamfile33
-rw-r--r--src/gxvalid/README532
-rw-r--r--src/gxvalid/gxvalid.c46
-rw-r--r--src/gxvalid/gxvalid.h107
-rw-r--r--src/gxvalid/gxvbsln.c333
-rw-r--r--src/gxvalid/gxvcommn.c1758
-rw-r--r--src/gxvalid/gxvcommn.h560
-rw-r--r--src/gxvalid/gxverror.h51
-rw-r--r--src/gxvalid/gxvfeat.c343
-rw-r--r--src/gxvalid/gxvfeat.h172
-rw-r--r--src/gxvalid/gxvfgen.c482
-rw-r--r--src/gxvalid/gxvjust.c630
-rw-r--r--src/gxvalid/gxvkern.c876
-rw-r--r--src/gxvalid/gxvlcar.c223
-rw-r--r--src/gxvalid/gxvmod.c285
-rw-r--r--src/gxvalid/gxvmod.h46
-rw-r--r--src/gxvalid/gxvmort.c285
-rw-r--r--src/gxvalid/gxvmort.h93
-rw-r--r--src/gxvalid/gxvmort0.c137
-rw-r--r--src/gxvalid/gxvmort1.c258
-rw-r--r--src/gxvalid/gxvmort2.c282
-rw-r--r--src/gxvalid/gxvmort4.c125
-rw-r--r--src/gxvalid/gxvmort5.c226
-rw-r--r--src/gxvalid/gxvmorx.c183
-rw-r--r--src/gxvalid/gxvmorx.h67
-rw-r--r--src/gxvalid/gxvmorx0.c103
-rw-r--r--src/gxvalid/gxvmorx1.c274
-rw-r--r--src/gxvalid/gxvmorx2.c285
-rw-r--r--src/gxvalid/gxvmorx4.c55
-rw-r--r--src/gxvalid/gxvmorx5.c217
-rw-r--r--src/gxvalid/gxvopbd.c217
-rw-r--r--src/gxvalid/gxvprop.c301
-rw-r--r--src/gxvalid/gxvtrak.c277
-rw-r--r--src/gxvalid/module.mk23
-rw-r--r--src/gxvalid/rules.mk94
-rw-r--r--src/gzip/Jamfile16
-rw-r--r--src/gzip/adler32.c48
-rw-r--r--src/gzip/ftgzip.c682
-rw-r--r--src/gzip/infblock.c387
-rw-r--r--src/gzip/infblock.h36
-rw-r--r--src/gzip/infcodes.c250
-rw-r--r--src/gzip/infcodes.h31
-rw-r--r--src/gzip/inffixed.h151
-rw-r--r--src/gzip/inflate.c273
-rw-r--r--src/gzip/inftrees.c465
-rw-r--r--src/gzip/inftrees.h63
-rw-r--r--src/gzip/infutil.c86
-rw-r--r--src/gzip/infutil.h98
-rw-r--r--src/gzip/rules.mk75
-rw-r--r--src/gzip/zconf.h278
-rw-r--r--src/gzip/zlib.h830
-rw-r--r--src/gzip/zutil.c181
-rw-r--r--src/gzip/zutil.h215
-rw-r--r--src/lzw/Jamfile16
-rw-r--r--src/lzw/ftlzw.c413
-rw-r--r--src/lzw/ftzopen.c398
-rw-r--r--src/lzw/ftzopen.h171
-rw-r--r--src/lzw/rules.mk70
-rw-r--r--src/otvalid/Jamfile29
-rw-r--r--src/otvalid/module.mk23
-rw-r--r--src/otvalid/otvalid.c30
-rw-r--r--src/otvalid/otvalid.h72
-rw-r--r--src/otvalid/otvbase.c318
-rw-r--r--src/otvalid/otvcommn.c1055
-rw-r--r--src/otvalid/otvcommn.h436
-rw-r--r--src/otvalid/otverror.h43
-rw-r--r--src/otvalid/otvgdef.c219
-rw-r--r--src/otvalid/otvgpos.c1013
-rw-r--r--src/otvalid/otvgpos.h36
-rw-r--r--src/otvalid/otvgsub.c584
-rw-r--r--src/otvalid/otvjstf.c258
-rw-r--r--src/otvalid/otvmod.c242
-rw-r--r--src/otvalid/otvmod.h39
-rw-r--r--src/otvalid/rules.mk77
-rw-r--r--src/pcf/Jamfile29
-rw-r--r--src/pcf/README114
-rw-r--r--src/pcf/module.mk34
-rw-r--r--src/pcf/pcf.c36
-rw-r--r--src/pcf/pcf.h237
-rw-r--r--src/pcf/pcfdrivr.c668
-rw-r--r--src/pcf/pcfdrivr.h44
-rw-r--r--src/pcf/pcferror.h40
-rw-r--r--src/pcf/pcfread.c1267
-rw-r--r--src/pcf/pcfread.h45
-rw-r--r--src/pcf/pcfutil.c104
-rw-r--r--src/pcf/pcfutil.h55
-rw-r--r--src/pcf/rules.mk80
-rw-r--r--src/pfr/Jamfile29
-rw-r--r--src/pfr/module.mk23
-rw-r--r--src/pfr/pfr.c29
-rw-r--r--src/pfr/pfrcmap.c163
-rw-r--r--src/pfr/pfrcmap.h46
-rw-r--r--src/pfr/pfrdrivr.c207
-rw-r--r--src/pfr/pfrdrivr.h39
-rw-r--r--src/pfr/pfrerror.h40
-rw-r--r--src/pfr/pfrgload.c828
-rw-r--r--src/pfr/pfrgload.h49
-rw-r--r--src/pfr/pfrload.c938
-rw-r--r--src/pfr/pfrload.h118
-rw-r--r--src/pfr/pfrobjs.c576
-rw-r--r--src/pfr/pfrobjs.h96
-rw-r--r--src/pfr/pfrsbit.c680
-rw-r--r--src/pfr/pfrsbit.h36
-rw-r--r--src/pfr/pfrtypes.h362
-rw-r--r--src/pfr/rules.mk73
-rw-r--r--src/psaux/Jamfile31
-rw-r--r--src/psaux/afmparse.c960
-rw-r--r--src/psaux/afmparse.h87
-rw-r--r--src/psaux/module.mk23
-rw-r--r--src/psaux/psaux.c34
-rw-r--r--src/psaux/psauxerr.h41
-rw-r--r--src/psaux/psauxmod.c139
-rw-r--r--src/psaux/psauxmod.h38
-rw-r--r--src/psaux/psconv.c466
-rw-r--r--src/psaux/psconv.h71
-rw-r--r--src/psaux/psobjs.c1698
-rw-r--r--src/psaux/psobjs.h212
-rw-r--r--src/psaux/rules.mk73
-rw-r--r--src/psaux/t1cmap.c333
-rw-r--r--src/psaux/t1cmap.h105
-rw-r--r--src/psaux/t1decode.c1474
-rw-r--r--src/psaux/t1decode.h64
-rw-r--r--src/pshinter/Jamfile29
-rw-r--r--src/pshinter/module.mk23
-rw-r--r--src/pshinter/pshalgo.c2291
-rw-r--r--src/pshinter/pshalgo.h255
-rw-r--r--src/pshinter/pshglob.c750
-rw-r--r--src/pshinter/pshglob.h196
-rw-r--r--src/pshinter/pshinter.c28
-rw-r--r--src/pshinter/pshmod.c121
-rw-r--r--src/pshinter/pshmod.h39
-rw-r--r--src/pshinter/pshnterr.h40
-rw-r--r--src/pshinter/pshrec.c1215
-rw-r--r--src/pshinter/pshrec.h176
-rw-r--r--src/pshinter/rules.mk72
-rw-r--r--src/psnames/Jamfile29
-rw-r--r--src/psnames/module.mk23
-rw-r--r--src/psnames/psmodule.c458
-rw-r--r--src/psnames/psmodule.h38
-rw-r--r--src/psnames/psnamerr.h41
-rw-r--r--src/psnames/psnames.c25
-rw-r--r--src/psnames/pstables.h4090
-rw-r--r--src/psnames/rules.mk70
-rw-r--r--src/raster/Jamfile29
-rw-r--r--src/raster/ftmisc.h83
-rw-r--r--src/raster/ftraster.c3369
-rw-r--r--src/raster/ftraster.h46
-rw-r--r--src/raster/ftrend1.c273
-rw-r--r--src/raster/ftrend1.h44
-rw-r--r--src/raster/module.mk23
-rw-r--r--src/raster/raster.c26
-rw-r--r--src/raster/rasterrs.h41
-rw-r--r--src/raster/rules.mk69
-rw-r--r--src/sfnt/Jamfile29
-rw-r--r--src/sfnt/module.mk23
-rw-r--r--src/sfnt/rules.mk76
-rw-r--r--src/sfnt/sfdriver.c618
-rw-r--r--src/sfnt/sfdriver.h38
-rw-r--r--src/sfnt/sferrors.h41
-rw-r--r--src/sfnt/sfnt.c41
-rw-r--r--src/sfnt/sfobjs.c1070
-rw-r--r--src/sfnt/sfobjs.h54
-rw-r--r--src/sfnt/ttbdf.c250
-rw-r--r--src/sfnt/ttbdf.h46
-rw-r--r--src/sfnt/ttcmap.c2356
-rw-r--r--src/sfnt/ttcmap.h85
-rw-r--r--src/sfnt/ttkern.c292
-rw-r--r--src/sfnt/ttkern.h52
-rw-r--r--src/sfnt/ttload.c1176
-rw-r--r--src/sfnt/ttload.h112
-rw-r--r--src/sfnt/ttmtx.c465
-rw-r--r--src/sfnt/ttmtx.h55
-rw-r--r--src/sfnt/ttpost.c521
-rw-r--r--src/sfnt/ttpost.h46
-rw-r--r--src/sfnt/ttsbit.c1501
-rw-r--r--src/sfnt/ttsbit.h79
-rw-r--r--src/sfnt/ttsbit0.c996
-rw-r--r--src/smooth/Jamfile29
-rw-r--r--src/smooth/ftgrays.c1983
-rw-r--r--src/smooth/ftgrays.h57
-rw-r--r--src/smooth/ftsmerrs.h41
-rw-r--r--src/smooth/ftsmooth.c467
-rw-r--r--src/smooth/ftsmooth.h49
-rw-r--r--src/smooth/module.mk27
-rw-r--r--src/smooth/rules.mk69
-rw-r--r--src/smooth/smooth.c26
-rw-r--r--src/tools/Jamfile5
-rw-r--r--src/tools/apinames.c443
-rw-r--r--src/tools/cordic.py79
-rw-r--r--src/tools/docmaker/content.py588
-rw-r--r--src/tools/docmaker/docbeauty.py109
-rw-r--r--src/tools/docmaker/docmaker.py107
-rw-r--r--src/tools/docmaker/formatter.py204
-rw-r--r--src/tools/docmaker/sources.py358
-rw-r--r--src/tools/docmaker/tohtml.py538
-rw-r--r--src/tools/docmaker/utils.py132
-rw-r--r--src/tools/ftrandom/Makefile35
-rw-r--r--src/tools/ftrandom/README48
-rw-r--r--src/tools/ftrandom/ftrandom.c659
-rw-r--r--src/tools/glnames.py5282
-rw-r--r--src/tools/test_afm.c157
-rw-r--r--src/tools/test_bbox.c160
-rw-r--r--src/tools/test_trig.c236
-rw-r--r--src/truetype/Jamfile29
-rw-r--r--src/truetype/module.mk23
-rw-r--r--src/truetype/rules.mk72
-rw-r--r--src/truetype/truetype.c36
-rw-r--r--src/truetype/ttdriver.c418
-rw-r--r--src/truetype/ttdriver.h38
-rw-r--r--src/truetype/tterrors.h40
-rw-r--r--src/truetype/ttgload.c1957
-rw-r--r--src/truetype/ttgload.h49
-rw-r--r--src/truetype/ttgxvar.c1536
-rw-r--r--src/truetype/ttgxvar.h182
-rw-r--r--src/truetype/ttinterp.c7809
-rw-r--r--src/truetype/ttinterp.h311
-rw-r--r--src/truetype/ttobjs.c937
-rw-r--r--src/truetype/ttobjs.h459
-rw-r--r--src/truetype/ttpload.c523
-rw-r--r--src/truetype/ttpload.h75
-rw-r--r--src/type1/Jamfile29
-rw-r--r--src/type1/module.mk23
-rw-r--r--src/type1/rules.mk73
-rw-r--r--src/type1/t1afm.c385
-rw-r--r--src/type1/t1afm.h54
-rw-r--r--src/type1/t1driver.c313
-rw-r--r--src/type1/t1driver.h38
-rw-r--r--src/type1/t1errors.h40
-rw-r--r--src/type1/t1gload.c422
-rw-r--r--src/type1/t1gload.h46
-rw-r--r--src/type1/t1load.c2221
-rw-r--r--src/type1/t1load.h102
-rw-r--r--src/type1/t1objs.c566
-rw-r--r--src/type1/t1objs.h171
-rw-r--r--src/type1/t1parse.c479
-rw-r--r--src/type1/t1parse.h135
-rw-r--r--src/type1/t1tokens.h134
-rw-r--r--src/type1/type1.c33
-rw-r--r--src/type42/Jamfile29
-rw-r--r--src/type42/module.mk23
-rw-r--r--src/type42/rules.mk69
-rw-r--r--src/type42/t42drivr.c232
-rw-r--r--src/type42/t42drivr.h38
-rw-r--r--src/type42/t42error.h40
-rw-r--r--src/type42/t42objs.c647
-rw-r--r--src/type42/t42objs.h124
-rw-r--r--src/type42/t42parse.c1167
-rw-r--r--src/type42/t42parse.h90
-rw-r--r--src/type42/t42types.h54
-rw-r--r--src/type42/type42.c25
-rw-r--r--src/winfonts/Jamfile16
-rw-r--r--src/winfonts/fnterrs.h41
-rw-r--r--src/winfonts/module.mk23
-rw-r--r--src/winfonts/rules.mk65
-rw-r--r--src/winfonts/winfnt.c1122
-rw-r--r--src/winfonts/winfnt.h167
383 files changed, 140107 insertions, 0 deletions
diff --git a/src/Jamfile b/src/Jamfile
new file mode 100644
index 0000000..76ee0f4
--- /dev/null
+++ b/src/Jamfile
@@ -0,0 +1,25 @@
+# FreeType 2 src Jamfile
+#
+# Copyright 2001, 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) ;
+
+# The file <freetype/internal/internal.h> is used to define macros that are
+# later used in #include statements. It needs to be parsed in order to
+# record these definitions.
+#
+HDRMACRO [ FT2_SubDir $(FT2_INCLUDE_DIR) internal internal.h ] ;
+
+for xx in $(FT2_COMPONENTS)
+{
+ SubInclude FT2_TOP $(FT2_SRC_DIR) $(xx) ;
+}
+
+# end of src Jamfile
diff --git a/src/autofit/Jamfile b/src/autofit/Jamfile
new file mode 100644
index 0000000..acee8bf
--- /dev/null
+++ b/src/autofit/Jamfile
@@ -0,0 +1,39 @@
+# FreeType 2 src/autofit Jamfile
+#
+# Copyright 2003, 2004, 2005, 2006, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP src autofit ;
+
+{
+ local _sources ;
+
+ # define FT2_AUTOFIT2 do enable to experimental latin hinter replacement
+ if $(FT2_AUTOFIT2)
+ {
+ DEFINES += FT_OPTION_AUTOFIT2 ;
+ }
+ if $(FT2_MULTI)
+ {
+ _sources = afangles afglobal afhints aflatin afcjk afindic afloader afmodule afdummy afwarp ;
+
+ if $(FT2_AUTOFIT2)
+ {
+ _sources += aflatin2 ;
+ }
+ }
+ else
+ {
+ _sources = autofit ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/autofit Jamfile
diff --git a/src/autofit/afangles.c b/src/autofit/afangles.c
new file mode 100644
index 0000000..e2360d1
--- /dev/null
+++ b/src/autofit/afangles.c
@@ -0,0 +1,292 @@
+/***************************************************************************/
+/* */
+/* afangles.c */
+/* */
+/* Routines used to compute vector angles with limited accuracy */
+/* and very high speed. It also contains sorting routines (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "aftypes.h"
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_Int )
+ af_corner_is_flat( FT_Pos x_in,
+ FT_Pos y_in,
+ FT_Pos x_out,
+ FT_Pos y_out )
+ {
+ FT_Pos ax = x_in;
+ FT_Pos ay = y_in;
+
+ FT_Pos d_in, d_out, d_corner;
+
+
+ if ( ax < 0 )
+ ax = -ax;
+ if ( ay < 0 )
+ ay = -ay;
+ d_in = ax + ay;
+
+ ax = x_out;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = y_out;
+ if ( ay < 0 )
+ ay = -ay;
+ d_out = ax + ay;
+
+ ax = x_out + x_in;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = y_out + y_in;
+ if ( ay < 0 )
+ ay = -ay;
+ d_corner = ax + ay;
+
+ return ( d_in + d_out - d_corner ) < ( d_corner >> 4 );
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ af_corner_orientation( FT_Pos x_in,
+ FT_Pos y_in,
+ FT_Pos x_out,
+ FT_Pos y_out )
+ {
+ FT_Pos delta;
+
+
+ delta = x_in * y_out - y_in * x_out;
+
+ if ( delta == 0 )
+ return 0;
+ else
+ return 1 - 2 * ( delta < 0 );
+ }
+
+#endif
+
+
+ /*
+ * We are not using `af_angle_atan' anymore, but we keep the source
+ * code below just in case...
+ */
+
+
+#if 0
+
+
+ /*
+ * The trick here is to realize that we don't need a very accurate angle
+ * approximation. We are going to use the result of `af_angle_atan' to
+ * only compare the sign of angle differences, or check whether its
+ * magnitude is very small.
+ *
+ * The approximation
+ *
+ * dy * PI / (|dx|+|dy|)
+ *
+ * should be enough, and much faster to compute.
+ */
+ FT_LOCAL_DEF( AF_Angle )
+ af_angle_atan( FT_Fixed dx,
+ FT_Fixed dy )
+ {
+ AF_Angle angle;
+ FT_Fixed ax = dx;
+ FT_Fixed ay = dy;
+
+
+ if ( ax < 0 )
+ ax = -ax;
+ if ( ay < 0 )
+ ay = -ay;
+
+ ax += ay;
+
+ if ( ax == 0 )
+ angle = 0;
+ else
+ {
+ angle = ( AF_ANGLE_PI2 * dy ) / ( ax + ay );
+ if ( dx < 0 )
+ {
+ if ( angle >= 0 )
+ angle = AF_ANGLE_PI - angle;
+ else
+ angle = -AF_ANGLE_PI - angle;
+ }
+ }
+
+ return angle;
+ }
+
+
+#elif 0
+
+
+ /* the following table has been automatically generated with */
+ /* the `mather.py' Python script */
+
+#define AF_ATAN_BITS 8
+
+ static const FT_Byte af_arctan[1L << AF_ATAN_BITS] =
+ {
+ 0, 0, 1, 1, 1, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 5,
+ 5, 5, 6, 6, 6, 7, 7, 7,
+ 8, 8, 8, 9, 9, 9, 10, 10,
+ 10, 10, 11, 11, 11, 12, 12, 12,
+ 13, 13, 13, 14, 14, 14, 14, 15,
+ 15, 15, 16, 16, 16, 17, 17, 17,
+ 18, 18, 18, 18, 19, 19, 19, 20,
+ 20, 20, 21, 21, 21, 21, 22, 22,
+ 22, 23, 23, 23, 24, 24, 24, 24,
+ 25, 25, 25, 26, 26, 26, 26, 27,
+ 27, 27, 28, 28, 28, 28, 29, 29,
+ 29, 30, 30, 30, 30, 31, 31, 31,
+ 31, 32, 32, 32, 33, 33, 33, 33,
+ 34, 34, 34, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 37, 37, 37, 38,
+ 38, 38, 38, 39, 39, 39, 39, 40,
+ 40, 40, 40, 41, 41, 41, 41, 42,
+ 42, 42, 42, 42, 43, 43, 43, 43,
+ 44, 44, 44, 44, 45, 45, 45, 45,
+ 46, 46, 46, 46, 46, 47, 47, 47,
+ 47, 48, 48, 48, 48, 48, 49, 49,
+ 49, 49, 50, 50, 50, 50, 50, 51,
+ 51, 51, 51, 51, 52, 52, 52, 52,
+ 52, 53, 53, 53, 53, 53, 54, 54,
+ 54, 54, 54, 55, 55, 55, 55, 55,
+ 56, 56, 56, 56, 56, 57, 57, 57,
+ 57, 57, 57, 58, 58, 58, 58, 58,
+ 59, 59, 59, 59, 59, 59, 60, 60,
+ 60, 60, 60, 61, 61, 61, 61, 61,
+ 61, 62, 62, 62, 62, 62, 62, 63,
+ 63, 63, 63, 63, 63, 64, 64, 64
+ };
+
+
+ FT_LOCAL_DEF( AF_Angle )
+ af_angle_atan( FT_Fixed dx,
+ FT_Fixed dy )
+ {
+ AF_Angle angle;
+
+
+ /* check trivial cases */
+ if ( dy == 0 )
+ {
+ angle = 0;
+ if ( dx < 0 )
+ angle = AF_ANGLE_PI;
+ return angle;
+ }
+ else if ( dx == 0 )
+ {
+ angle = AF_ANGLE_PI2;
+ if ( dy < 0 )
+ angle = -AF_ANGLE_PI2;
+ return angle;
+ }
+
+ angle = 0;
+ if ( dx < 0 )
+ {
+ dx = -dx;
+ dy = -dy;
+ angle = AF_ANGLE_PI;
+ }
+
+ if ( dy < 0 )
+ {
+ FT_Pos tmp;
+
+
+ tmp = dx;
+ dx = -dy;
+ dy = tmp;
+ angle -= AF_ANGLE_PI2;
+ }
+
+ if ( dx == 0 && dy == 0 )
+ return 0;
+
+ if ( dx == dy )
+ angle += AF_ANGLE_PI4;
+ else if ( dx > dy )
+ angle += af_arctan[FT_DivFix( dy, dx ) >> ( 16 - AF_ATAN_BITS )];
+ else
+ angle += AF_ANGLE_PI2 -
+ af_arctan[FT_DivFix( dx, dy ) >> ( 16 - AF_ATAN_BITS )];
+
+ if ( angle > AF_ANGLE_PI )
+ angle -= AF_ANGLE_2PI;
+
+ return angle;
+ }
+
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( void )
+ af_sort_pos( FT_UInt count,
+ FT_Pos* table )
+ {
+ FT_UInt i, j;
+ FT_Pos swap;
+
+
+ for ( i = 1; i < count; i++ )
+ {
+ for ( j = i; j > 0; j-- )
+ {
+ if ( table[j] > table[j - 1] )
+ break;
+
+ swap = table[j];
+ table[j] = table[j - 1];
+ table[j - 1] = swap;
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_sort_widths( FT_UInt count,
+ AF_Width table )
+ {
+ FT_UInt i, j;
+ AF_WidthRec swap;
+
+
+ for ( i = 1; i < count; i++ )
+ {
+ for ( j = i; j > 0; j-- )
+ {
+ if ( table[j].org > table[j - 1].org )
+ break;
+
+ swap = table[j];
+ table[j] = table[j - 1];
+ table[j - 1] = swap;
+ }
+ }
+ }
+
+
+/* END */
diff --git a/src/autofit/afangles.h b/src/autofit/afangles.h
new file mode 100644
index 0000000..f33f9e1
--- /dev/null
+++ b/src/autofit/afangles.h
@@ -0,0 +1,7 @@
+/*
+ * afangles.h
+ *
+ * This is a dummy file, used to please the build system. It is never
+ * included by the auto-fitter sources.
+ *
+ */
diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c
new file mode 100644
index 0000000..c7ca266
--- /dev/null
+++ b/src/autofit/afcjk.c
@@ -0,0 +1,1506 @@
+/***************************************************************************/
+/* */
+/* afcjk.c */
+/* */
+/* Auto-fitter hinting routines for CJK script (body). */
+/* */
+/* Copyright 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*
+ * The algorithm is based on akito's autohint patch, available here:
+ *
+ * http://www.kde.gr.jp/~akito/patch/freetype2/
+ *
+ */
+
+#include "aftypes.h"
+#include "aflatin.h"
+
+
+#ifdef AF_CONFIG_OPTION_CJK
+
+#include "afcjk.h"
+#include "aferrors.h"
+
+
+#ifdef AF_USE_WARPER
+#include "afwarp.h"
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** C J K G L O B A L M E T R I C S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ af_cjk_metrics_init( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ FT_CharMap oldmap = face->charmap;
+
+
+ metrics->units_per_em = face->units_per_EM;
+
+ /* TODO are there blues? */
+
+ if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
+ face->charmap = NULL;
+
+ /* latin's version would suffice */
+ af_latin_metrics_init_widths( metrics, face, 0x7530 );
+
+ FT_Set_Charmap( face, oldmap );
+
+ return AF_Err_Ok;
+ }
+
+
+ static void
+ af_cjk_metrics_scale_dim( AF_LatinMetrics metrics,
+ AF_Scaler scaler,
+ AF_Dimension dim )
+ {
+ AF_LatinAxis axis;
+
+
+ axis = &metrics->axis[dim];
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ axis->scale = scaler->x_scale;
+ axis->delta = scaler->x_delta;
+ }
+ else
+ {
+ axis->scale = scaler->y_scale;
+ axis->delta = scaler->y_delta;
+ }
+ }
+
+
+ static void
+ af_cjk_metrics_scale( AF_LatinMetrics metrics,
+ AF_Scaler scaler )
+ {
+ metrics->root.scaler = *scaler;
+
+ af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
+ af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** C J K G L Y P H A N A L Y S I S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ af_cjk_hints_compute_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ FT_Error error;
+ AF_Segment seg;
+
+
+ error = af_latin_hints_compute_segments( hints, dim );
+ if ( error )
+ return error;
+
+ /* a segment is round if it doesn't have successive */
+ /* on-curve points. */
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Point pt = seg->first;
+ AF_Point last = seg->last;
+ AF_Flags f0 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
+ AF_Flags f1;
+
+
+ seg->flags &= ~AF_EDGE_ROUND;
+
+ for ( ; pt != last; f0 = f1 )
+ {
+ pt = pt->next;
+ f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
+
+ if ( !f0 && !f1 )
+ break;
+
+ if ( pt == last )
+ seg->flags |= AF_EDGE_ROUND;
+ }
+ }
+
+ return AF_Err_Ok;
+ }
+
+
+ static void
+ af_cjk_hints_link_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ AF_Direction major_dir = axis->major_dir;
+ AF_Segment seg1, seg2;
+ FT_Pos len_threshold;
+ FT_Pos dist_threshold;
+
+
+ len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
+
+ dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
+ : hints->y_scale;
+ dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
+
+ /* now compare each segment to the others */
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ /* the fake segments are for metrics hinting only */
+ if ( seg1->first == seg1->last )
+ continue;
+
+ if ( seg1->dir != major_dir )
+ continue;
+
+ for ( seg2 = segments; seg2 < segment_limit; seg2++ )
+ if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
+ {
+ FT_Pos dist = seg2->pos - seg1->pos;
+
+
+ if ( dist < 0 )
+ continue;
+
+ {
+ FT_Pos min = seg1->min_coord;
+ FT_Pos max = seg1->max_coord;
+ FT_Pos len;
+
+
+ if ( min < seg2->min_coord )
+ min = seg2->min_coord;
+
+ if ( max > seg2->max_coord )
+ max = seg2->max_coord;
+
+ len = max - min;
+ if ( len >= len_threshold )
+ {
+ if ( dist * 8 < seg1->score * 9 &&
+ ( dist * 8 < seg1->score * 7 || seg1->len < len ) )
+ {
+ seg1->score = dist;
+ seg1->len = len;
+ seg1->link = seg2;
+ }
+
+ if ( dist * 8 < seg2->score * 9 &&
+ ( dist * 8 < seg2->score * 7 || seg2->len < len ) )
+ {
+ seg2->score = dist;
+ seg2->len = len;
+ seg2->link = seg1;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * now compute the `serif' segments
+ *
+ * In Hanzi, some strokes are wider on one or both of the ends.
+ * We either identify the stems on the ends as serifs or remove
+ * the linkage, depending on the length of the stems.
+ *
+ */
+
+ {
+ AF_Segment link1, link2;
+
+
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ link1 = seg1->link;
+ if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
+ continue;
+
+ if ( seg1->score >= dist_threshold )
+ continue;
+
+ for ( seg2 = segments; seg2 < segment_limit; seg2++ )
+ {
+ if ( seg2->pos > seg1->pos || seg1 == seg2 )
+ continue;
+
+ link2 = seg2->link;
+ if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
+ continue;
+
+ if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
+ continue;
+
+ if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
+ continue;
+
+ /* seg2 < seg1 < link1 < link2 */
+
+ if ( seg1->len >= seg2->len * 3 )
+ {
+ AF_Segment seg;
+
+
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Segment link = seg->link;
+
+
+ if ( link == seg2 )
+ {
+ seg->link = 0;
+ seg->serif = link1;
+ }
+ else if ( link == link2 )
+ {
+ seg->link = 0;
+ seg->serif = seg1;
+ }
+ }
+ }
+ else
+ {
+ seg1->link = link1->link = 0;
+
+ break;
+ }
+ }
+ }
+ }
+
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ seg2 = seg1->link;
+
+ if ( seg2 )
+ {
+ seg2->num_linked++;
+ if ( seg2->link != seg1 )
+ {
+ seg1->link = 0;
+
+ if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
+ seg1->serif = seg2->link;
+ else
+ seg2->num_linked--;
+ }
+ }
+ }
+ }
+
+
+ static FT_Error
+ af_cjk_hints_compute_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ FT_Error error = AF_Err_Ok;
+ FT_Memory memory = hints->memory;
+ AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
+
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ AF_Segment seg;
+
+ FT_Fixed scale;
+ FT_Pos edge_distance_threshold;
+
+
+ axis->num_edges = 0;
+
+ scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
+ : hints->y_scale;
+
+ /*********************************************************************/
+ /* */
+ /* We begin by generating a sorted table of edges for the current */
+ /* direction. To do so, we simply scan each segment and try to find */
+ /* an edge in our table that corresponds to its position. */
+ /* */
+ /* If no edge is found, we create and insert a new edge in the */
+ /* sorted table. Otherwise, we simply add the segment to the edge's */
+ /* list which is then processed in the second step to compute the */
+ /* edge's properties. */
+ /* */
+ /* Note that the edges table is sorted along the segment/edge */
+ /* position. */
+ /* */
+ /*********************************************************************/
+
+ edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
+ scale );
+ if ( edge_distance_threshold > 64 / 4 )
+ edge_distance_threshold = FT_DivFix( 64 / 4, scale );
+ else
+ edge_distance_threshold = laxis->edge_distance_threshold;
+
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Edge found = 0;
+ FT_Pos best = 0xFFFFU;
+ FT_Int ee;
+
+
+ /* look for an edge corresponding to the segment */
+ for ( ee = 0; ee < axis->num_edges; ee++ )
+ {
+ AF_Edge edge = axis->edges + ee;
+ FT_Pos dist;
+
+
+ if ( edge->dir != seg->dir )
+ continue;
+
+ dist = seg->pos - edge->fpos;
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( dist < edge_distance_threshold && dist < best )
+ {
+ AF_Segment link = seg->link;
+
+
+ /* check whether all linked segments of the candidate edge */
+ /* can make a single edge. */
+ if ( link )
+ {
+ AF_Segment seg1 = edge->first;
+ AF_Segment link1;
+ FT_Pos dist2 = 0;
+
+
+ do
+ {
+ link1 = seg1->link;
+ if ( link1 )
+ {
+ dist2 = AF_SEGMENT_DIST( link, link1 );
+ if ( dist2 >= edge_distance_threshold )
+ break;
+ }
+
+ } while ( ( seg1 = seg1->edge_next ) != edge->first );
+
+ if ( dist2 >= edge_distance_threshold )
+ continue;
+ }
+
+ best = dist;
+ found = edge;
+ }
+ }
+
+ if ( !found )
+ {
+ AF_Edge edge;
+
+
+ /* insert a new edge in the list and */
+ /* sort according to the position */
+ error = af_axis_hints_new_edge( axis, seg->pos, seg->dir, memory, &edge );
+ if ( error )
+ goto Exit;
+
+ /* add the segment to the new edge's list */
+ FT_ZERO( edge );
+
+ edge->first = seg;
+ edge->last = seg;
+ edge->fpos = seg->pos;
+ edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
+ seg->edge_next = seg;
+ edge->dir = seg->dir;
+ }
+ else
+ {
+ /* if an edge was found, simply add the segment to the edge's */
+ /* list */
+ seg->edge_next = found->first;
+ found->last->edge_next = seg;
+ found->last = seg;
+ }
+ }
+
+ /*********************************************************************/
+ /* */
+ /* Good, we now compute each edge's properties according to segments */
+ /* found on its position. Basically, these are as follows. */
+ /* */
+ /* - edge's main direction */
+ /* - stem edge, serif edge or both (which defaults to stem then) */
+ /* - rounded edge, straight or both (which defaults to straight) */
+ /* - link for edge */
+ /* */
+ /*********************************************************************/
+
+ /* first of all, set the `edge' field in each segment -- this is */
+ /* required in order to compute edge links */
+ /* */
+ /* Note that removing this loop and setting the `edge' field of each */
+ /* segment directly in the code above slows down execution speed for */
+ /* some reasons on platforms like the Sun. */
+
+ {
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ AF_Edge edge;
+
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ seg = edge->first;
+ if ( seg )
+ do
+ {
+ seg->edge = edge;
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+ }
+
+ /* now compute each edge properties */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Int is_round = 0; /* does it contain round segments? */
+ FT_Int is_straight = 0; /* does it contain straight segments? */
+
+
+ seg = edge->first;
+
+ do
+ {
+ FT_Bool is_serif;
+
+
+ /* check for roundness of segment */
+ if ( seg->flags & AF_EDGE_ROUND )
+ is_round++;
+ else
+ is_straight++;
+
+ /* check for links -- if seg->serif is set, then seg->link must */
+ /* be ignored */
+ is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge );
+
+ if ( seg->link || is_serif )
+ {
+ AF_Edge edge2;
+ AF_Segment seg2;
+
+
+ edge2 = edge->link;
+ seg2 = seg->link;
+
+ if ( is_serif )
+ {
+ seg2 = seg->serif;
+ edge2 = edge->serif;
+ }
+
+ if ( edge2 )
+ {
+ FT_Pos edge_delta;
+ FT_Pos seg_delta;
+
+
+ edge_delta = edge->fpos - edge2->fpos;
+ if ( edge_delta < 0 )
+ edge_delta = -edge_delta;
+
+ seg_delta = AF_SEGMENT_DIST( seg, seg2 );
+
+ if ( seg_delta < edge_delta )
+ edge2 = seg2->edge;
+ }
+ else
+ edge2 = seg2->edge;
+
+ if ( is_serif )
+ {
+ edge->serif = edge2;
+ edge2->flags |= AF_EDGE_SERIF;
+ }
+ else
+ edge->link = edge2;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+
+ /* set the round/straight flags */
+ edge->flags = AF_EDGE_NORMAL;
+
+ if ( is_round > 0 && is_round >= is_straight )
+ edge->flags |= AF_EDGE_ROUND;
+
+ /* get rid of serifs if link is set */
+ /* XXX: This gets rid of many unpleasant artefacts! */
+ /* Example: the `c' in cour.pfa at size 13 */
+
+ if ( edge->serif && edge->link )
+ edge->serif = 0;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ af_cjk_hints_detect_features( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ FT_Error error;
+
+
+ error = af_cjk_hints_compute_segments( hints, dim );
+ if ( !error )
+ {
+ af_cjk_hints_link_segments( hints, dim );
+
+ error = af_cjk_hints_compute_edges( hints, dim );
+ }
+ return error;
+ }
+
+
+ static FT_Error
+ af_cjk_hints_init( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ FT_Render_Mode mode;
+ FT_UInt32 scaler_flags, other_flags;
+
+
+ af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
+
+ /*
+ * correct x_scale and y_scale when needed, since they may have
+ * been modified af_cjk_scale_dim above
+ */
+ hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
+ hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
+ hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
+ hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
+
+ /* compute flags depending on render mode, etc. */
+ mode = metrics->root.scaler.render_mode;
+
+#ifdef AF_USE_WARPER
+ if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
+ metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
+#endif
+
+ scaler_flags = hints->scaler_flags;
+ other_flags = 0;
+
+ /*
+ * We snap the width of vertical stems for the monochrome and
+ * horizontal LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
+ other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
+
+ /*
+ * We snap the width of horizontal stems for the monochrome and
+ * vertical LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
+ other_flags |= AF_LATIN_HINTS_VERT_SNAP;
+
+ /*
+ * We adjust stems to full pixels only if we don't use the `light' mode.
+ */
+ if ( mode != FT_RENDER_MODE_LIGHT )
+ other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
+
+ if ( mode == FT_RENDER_MODE_MONO )
+ other_flags |= AF_LATIN_HINTS_MONO;
+
+ scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE;
+
+ hints->scaler_flags = scaler_flags;
+ hints->other_flags = other_flags;
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** C J K G L Y P H G R I D - F I T T I N G *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* snap a given width in scaled coordinates to one of the */
+ /* current standard widths */
+
+ static FT_Pos
+ af_cjk_snap_width( AF_Width widths,
+ FT_Int count,
+ FT_Pos width )
+ {
+ int n;
+ FT_Pos best = 64 + 32 + 2;
+ FT_Pos reference = width;
+ FT_Pos scaled;
+
+
+ for ( n = 0; n < count; n++ )
+ {
+ FT_Pos w;
+ FT_Pos dist;
+
+
+ w = widths[n].cur;
+ dist = width - w;
+ if ( dist < 0 )
+ dist = -dist;
+ if ( dist < best )
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+
+ scaled = FT_PIX_ROUND( reference );
+
+ if ( width >= reference )
+ {
+ if ( width < scaled + 48 )
+ width = reference;
+ }
+ else
+ {
+ if ( width > scaled - 48 )
+ width = reference;
+ }
+
+ return width;
+ }
+
+
+ /* compute the snapped width of a given stem */
+
+ static FT_Pos
+ af_cjk_compute_stem_width( AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Pos width,
+ AF_Edge_Flags base_flags,
+ AF_Edge_Flags stem_flags )
+ {
+ AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics;
+ AF_LatinAxis axis = & metrics->axis[dim];
+ FT_Pos dist = width;
+ FT_Int sign = 0;
+ FT_Int vertical = ( dim == AF_DIMENSION_VERT );
+
+ FT_UNUSED( base_flags );
+ FT_UNUSED( stem_flags );
+
+
+ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
+ return width;
+
+ if ( dist < 0 )
+ {
+ dist = -width;
+ sign = 1;
+ }
+
+ if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
+ ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
+ {
+ /* smooth hinting process: very lightly quantize the stem width */
+
+ if ( axis->width_count > 0 )
+ {
+ if ( FT_ABS( dist - axis->widths[0].cur ) < 40 )
+ {
+ dist = axis->widths[0].cur;
+ if ( dist < 48 )
+ dist = 48;
+
+ goto Done_Width;
+ }
+ }
+
+ if ( dist < 54 )
+ dist += ( 54 - dist ) / 2 ;
+ else if ( dist < 3 * 64 )
+ {
+ FT_Pos delta;
+
+
+ delta = dist & 63;
+ dist &= -64;
+
+ if ( delta < 10 )
+ dist += delta;
+ else if ( delta < 22 )
+ dist += 10;
+ else if ( delta < 42 )
+ dist += delta;
+ else if ( delta < 54 )
+ dist += 54;
+ else
+ dist += delta;
+ }
+ }
+ else
+ {
+ /* strong hinting process: snap the stem width to integer pixels */
+
+ dist = af_cjk_snap_width( axis->widths, axis->width_count, dist );
+
+ if ( vertical )
+ {
+ /* in the case of vertical hinting, always round */
+ /* the stem heights to integer pixels */
+
+ if ( dist >= 64 )
+ dist = ( dist + 16 ) & ~63;
+ else
+ dist = 64;
+ }
+ else
+ {
+ if ( AF_LATIN_HINTS_DO_MONO( hints ) )
+ {
+ /* monochrome horizontal hinting: snap widths to integer pixels */
+ /* with a different threshold */
+
+ if ( dist < 64 )
+ dist = 64;
+ else
+ dist = ( dist + 32 ) & ~63;
+ }
+ else
+ {
+ /* for horizontal anti-aliased hinting, we adopt a more subtle */
+ /* approach: we strengthen small stems, round stems whose size */
+ /* is between 1 and 2 pixels to an integer, otherwise nothing */
+
+ if ( dist < 48 )
+ dist = ( dist + 64 ) >> 1;
+
+ else if ( dist < 128 )
+ dist = ( dist + 22 ) & ~63;
+ else
+ /* round otherwise to prevent color fringes in LCD mode */
+ dist = ( dist + 32 ) & ~63;
+ }
+ }
+ }
+
+ Done_Width:
+ if ( sign )
+ dist = -dist;
+
+ return dist;
+ }
+
+
+ /* align one stem edge relative to the previous stem edge */
+
+ static void
+ af_cjk_align_linked_edge( AF_GlyphHints hints,
+ AF_Dimension dim,
+ AF_Edge base_edge,
+ AF_Edge stem_edge )
+ {
+ FT_Pos dist = stem_edge->opos - base_edge->opos;
+
+ FT_Pos fitted_width = af_cjk_compute_stem_width(
+ hints, dim, dist,
+ (AF_Edge_Flags)base_edge->flags,
+ (AF_Edge_Flags)stem_edge->flags );
+
+
+ stem_edge->pos = base_edge->pos + fitted_width;
+ }
+
+
+ static void
+ af_cjk_align_serif_edge( AF_GlyphHints hints,
+ AF_Edge base,
+ AF_Edge serif )
+ {
+ FT_UNUSED( hints );
+
+ serif->pos = base->pos + ( serif->opos - base->opos );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** E D G E H I N T I N G ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#define AF_LIGHT_MODE_MAX_HORZ_GAP 9
+#define AF_LIGHT_MODE_MAX_VERT_GAP 15
+#define AF_LIGHT_MODE_MAX_DELTA_ABS 14
+
+
+ static FT_Pos
+ af_hint_normal_stem( AF_GlyphHints hints,
+ AF_Edge edge,
+ AF_Edge edge2,
+ FT_Pos anchor,
+ AF_Dimension dim )
+ {
+ FT_Pos org_len, cur_len, org_center;
+ FT_Pos cur_pos1, cur_pos2;
+ FT_Pos d_off1, u_off1, d_off2, u_off2, delta;
+ FT_Pos offset;
+ FT_Pos threshold = 64;
+
+
+ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
+ {
+ if ( ( edge->flags & AF_EDGE_ROUND ) &&
+ ( edge2->flags & AF_EDGE_ROUND ) )
+ {
+ if ( dim == AF_DIMENSION_VERT )
+ threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP;
+ else
+ threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP;
+ }
+ else
+ {
+ if ( dim == AF_DIMENSION_VERT )
+ threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3;
+ else
+ threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3;
+ }
+ }
+
+ org_len = edge2->opos - edge->opos;
+ cur_len = af_cjk_compute_stem_width( hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+
+ org_center = ( edge->opos + edge2->opos ) / 2 + anchor;
+ cur_pos1 = org_center - cur_len / 2;
+ cur_pos2 = cur_pos1 + cur_len;
+ d_off1 = cur_pos1 - FT_PIX_FLOOR( cur_pos1 );
+ d_off2 = cur_pos2 - FT_PIX_FLOOR( cur_pos2 );
+ u_off1 = 64 - d_off1;
+ u_off2 = 64 - d_off2;
+ delta = 0;
+
+
+ if ( d_off1 == 0 || d_off2 == 0 )
+ goto Exit;
+
+ if ( cur_len <= threshold )
+ {
+ if ( d_off2 < cur_len )
+ {
+ if ( u_off1 <= d_off2 )
+ delta = u_off1;
+ else
+ delta = -d_off2;
+ }
+
+ goto Exit;
+ }
+
+ if ( threshold < 64 )
+ {
+ if ( d_off1 >= threshold || u_off1 >= threshold ||
+ d_off2 >= threshold || u_off2 >= threshold )
+ goto Exit;
+ }
+
+ offset = cur_len % 64;
+
+ if ( offset < 32 )
+ {
+ if ( u_off1 <= offset || d_off2 <= offset )
+ goto Exit;
+ }
+ else
+ offset = 64 - threshold;
+
+ d_off1 = threshold - u_off1;
+ u_off1 = u_off1 - offset;
+ u_off2 = threshold - d_off2;
+ d_off2 = d_off2 - offset;
+
+ if ( d_off1 <= u_off1 )
+ u_off1 = -d_off1;
+
+ if ( d_off2 <= u_off2 )
+ u_off2 = -d_off2;
+
+ if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) )
+ delta = u_off1;
+ else
+ delta = u_off2;
+
+ Exit:
+
+#if 1
+ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
+ {
+ if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS )
+ delta = AF_LIGHT_MODE_MAX_DELTA_ABS;
+ else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS )
+ delta = -AF_LIGHT_MODE_MAX_DELTA_ABS;
+ }
+#endif
+
+ cur_pos1 += delta;
+
+ if ( edge->opos < edge2->opos )
+ {
+ edge->pos = cur_pos1;
+ edge2->pos = cur_pos1 + cur_len;
+ }
+ else
+ {
+ edge->pos = cur_pos1 + cur_len;
+ edge2->pos = cur_pos1;
+ }
+
+ return delta;
+ }
+
+
+ static void
+ af_cjk_hint_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ FT_Int n_edges;
+ AF_Edge edge;
+ AF_Edge anchor = 0;
+ FT_Pos delta = 0;
+ FT_Int skipped = 0;
+
+
+ /* now we align all stem edges. */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Edge edge2;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ /* skip all non-stem edges */
+ edge2 = edge->link;
+ if ( !edge2 )
+ {
+ skipped++;
+ continue;
+ }
+
+ /* now align the stem */
+
+ if ( edge2 < edge )
+ {
+ af_cjk_align_linked_edge( hints, dim, edge2, edge );
+ edge->flags |= AF_EDGE_DONE;
+ continue;
+ }
+
+ if ( dim != AF_DIMENSION_VERT && !anchor )
+ {
+
+#if 0
+ if ( fixedpitch )
+ {
+ AF_Edge left = edge;
+ AF_Edge right = edge_limit - 1;
+ AF_EdgeRec left1, left2, right1, right2;
+ FT_Pos target, center1, center2;
+ FT_Pos delta1, delta2, d1, d2;
+
+
+ while ( right > left && !right->link )
+ right--;
+
+ left1 = *left;
+ left2 = *left->link;
+ right1 = *right->link;
+ right2 = *right;
+
+ delta = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2;
+ target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16;
+
+ delta1 = delta;
+ delta1 += af_hint_normal_stem( hints, left, left->link,
+ delta1, 0 );
+
+ if ( left->link != right )
+ af_hint_normal_stem( hints, right->link, right, delta1, 0 );
+
+ center1 = left->pos + ( right->pos - left->pos ) / 2;
+
+ if ( center1 >= target )
+ delta2 = delta - 32;
+ else
+ delta2 = delta + 32;
+
+ delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 );
+
+ if ( delta1 != delta2 )
+ {
+ if ( left->link != right )
+ af_hint_normal_stem( hints, &right1, &right2, delta2, 0 );
+
+ center2 = left1.pos + ( right2.pos - left1.pos ) / 2;
+
+ d1 = center1 - target;
+ d2 = center2 - target;
+
+ if ( FT_ABS( d2 ) < FT_ABS( d1 ) )
+ {
+ left->pos = left1.pos;
+ left->link->pos = left2.pos;
+
+ if ( left->link != right )
+ {
+ right->link->pos = right1.pos;
+ right->pos = right2.pos;
+ }
+
+ delta1 = delta2;
+ }
+ }
+
+ delta = delta1;
+ right->link->flags |= AF_EDGE_DONE;
+ right->flags |= AF_EDGE_DONE;
+ }
+ else
+
+#endif /* 0 */
+
+ delta = af_hint_normal_stem( hints, edge, edge2, 0,
+ AF_DIMENSION_HORZ );
+ }
+ else
+ af_hint_normal_stem( hints, edge, edge2, delta, dim );
+
+#if 0
+ printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
+ edge - edges, edge2 - edges,
+ ( edge->pos - edge->opos ) / 64.0,
+ ( edge2->pos - edge2->opos ) / 64.0 );
+#endif
+
+ anchor = edge;
+ edge->flags |= AF_EDGE_DONE;
+ edge2->flags |= AF_EDGE_DONE;
+ }
+
+ /* make sure that lowercase m's maintain their symmetry */
+
+ /* In general, lowercase m's have six vertical edges if they are sans */
+ /* serif, or twelve if they are with serifs. This implementation is */
+ /* based on that assumption, and seems to work very well with most */
+ /* faces. However, if for a certain face this assumption is not */
+ /* true, the m is just rendered like before. In addition, any stem */
+ /* correction will only be applied to symmetrical glyphs (even if the */
+ /* glyph is not an m), so the potential for unwanted distortion is */
+ /* relatively low. */
+
+ /* We don't handle horizontal edges since we can't easily assure that */
+ /* the third (lowest) stem aligns with the base line; it might end up */
+ /* one pixel higher or lower. */
+
+ n_edges = edge_limit - edges;
+ if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
+ {
+ AF_Edge edge1, edge2, edge3;
+ FT_Pos dist1, dist2, span;
+
+
+ if ( n_edges == 6 )
+ {
+ edge1 = edges;
+ edge2 = edges + 2;
+ edge3 = edges + 4;
+ }
+ else
+ {
+ edge1 = edges + 1;
+ edge2 = edges + 5;
+ edge3 = edges + 9;
+ }
+
+ dist1 = edge2->opos - edge1->opos;
+ dist2 = edge3->opos - edge2->opos;
+
+ span = dist1 - dist2;
+ if ( span < 0 )
+ span = -span;
+
+ if ( edge1->link == edge1 + 1 &&
+ edge2->link == edge2 + 1 &&
+ edge3->link == edge3 + 1 && span < 8 )
+ {
+ delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
+ edge3->pos -= delta;
+ if ( edge3->link )
+ edge3->link->pos -= delta;
+
+ /* move the serifs along with the stem */
+ if ( n_edges == 12 )
+ {
+ ( edges + 8 )->pos -= delta;
+ ( edges + 11 )->pos -= delta;
+ }
+
+ edge3->flags |= AF_EDGE_DONE;
+ if ( edge3->link )
+ edge3->link->flags |= AF_EDGE_DONE;
+ }
+ }
+
+ if ( !skipped )
+ return;
+
+ /*
+ * now hint the remaining edges (serifs and single) in order
+ * to complete our processing
+ */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ if ( edge->serif )
+ {
+ af_cjk_align_serif_edge( hints, edge->serif, edge );
+ edge->flags |= AF_EDGE_DONE;
+ skipped--;
+ }
+ }
+
+ if ( !skipped )
+ return;
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Edge before, after;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ before = after = edge;
+
+ while ( --before >= edges )
+ if ( before->flags & AF_EDGE_DONE )
+ break;
+
+ while ( ++after < edge_limit )
+ if ( after->flags & AF_EDGE_DONE )
+ break;
+
+ if ( before >= edges || after < edge_limit )
+ {
+ if ( before < edges )
+ af_cjk_align_serif_edge( hints, after, edge );
+ else if ( after >= edge_limit )
+ af_cjk_align_serif_edge( hints, before, edge );
+ else
+ edge->pos = before->pos +
+ FT_MulDiv( edge->fpos - before->fpos,
+ after->pos - before->pos,
+ after->fpos - before->fpos );
+ }
+ }
+ }
+
+
+ static void
+ af_cjk_align_edge_points( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = & hints->axis[dim];
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ AF_Edge edge;
+ FT_Bool snapping;
+
+
+ snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ &&
+ AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) ||
+ ( dim == AF_DIMENSION_VERT &&
+ AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) );
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ /* move the points of each segment */
+ /* in each edge to the edge's position */
+ AF_Segment seg = edge->first;
+
+
+ if ( snapping )
+ {
+ do
+ {
+ AF_Point point = seg->first;
+
+
+ for (;;)
+ {
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ point->x = edge->pos;
+ point->flags |= AF_FLAG_TOUCH_X;
+ }
+ else
+ {
+ point->y = edge->pos;
+ point->flags |= AF_FLAG_TOUCH_Y;
+ }
+
+ if ( point == seg->last )
+ break;
+
+ point = point->next;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+ }
+ else
+ {
+ FT_Pos delta = edge->pos - edge->opos;
+
+
+ do
+ {
+ AF_Point point = seg->first;
+
+
+ for (;;)
+ {
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ point->x += delta;
+ point->flags |= AF_FLAG_TOUCH_X;
+ }
+ else
+ {
+ point->y += delta;
+ point->flags |= AF_FLAG_TOUCH_Y;
+ }
+
+ if ( point == seg->last )
+ break;
+
+ point = point->next;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+ }
+ }
+ }
+
+
+ static FT_Error
+ af_cjk_hints_apply( AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_LatinMetrics metrics )
+ {
+ FT_Error error;
+ int dim;
+
+ FT_UNUSED( metrics );
+
+
+ error = af_glyph_hints_reload( hints, outline, 0 );
+ if ( error )
+ goto Exit;
+
+ /* analyze glyph outline */
+ if ( AF_HINTS_DO_HORIZONTAL( hints ) )
+ {
+ error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( AF_HINTS_DO_VERTICAL( hints ) )
+ {
+ error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
+ if ( error )
+ goto Exit;
+ }
+
+ /* grid-fit the outline */
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
+ ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
+ {
+
+#ifdef AF_USE_WARPER
+ if ( dim == AF_DIMENSION_HORZ &&
+ metrics->root.scaler.render_mode == FT_RENDER_MODE_NORMAL )
+ {
+ AF_WarperRec warper;
+ FT_Fixed scale;
+ FT_Pos delta;
+
+
+ af_warper_compute( &warper, hints, dim, &scale, &delta );
+ af_glyph_hints_scale_dim( hints, dim, scale, delta );
+ continue;
+ }
+#endif /* AF_USE_WARPER */
+
+ af_cjk_hint_edges( hints, (AF_Dimension)dim );
+ af_cjk_align_edge_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
+ }
+ }
+
+#if 0
+ af_glyph_hints_dump_points( hints );
+ af_glyph_hints_dump_segments( hints );
+ af_glyph_hints_dump_edges( hints );
+#endif
+
+ af_glyph_hints_save( hints, outline );
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** C J K S C R I P T C L A S S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static const AF_Script_UniRangeRec af_cjk_uniranges[] =
+ {
+#if 0
+ { 0x0100, 0xFFFF }, /* why this? */
+#endif
+ { 0x2E80, 0x2EFF }, /* CJK Radicals Supplement */
+ { 0x2F00, 0x2FDF }, /* Kangxi Radicals */
+ { 0x3000, 0x303F }, /* CJK Symbols and Punctuation */
+ { 0x3040, 0x309F }, /* Hiragana */
+ { 0x30A0, 0x30FF }, /* Katakana */
+ { 0x3100, 0x312F }, /* Bopomofo */
+ { 0x3130, 0x318F }, /* Hangul Compatibility Jamo */
+ { 0x31A0, 0x31BF }, /* Bopomofo Extended */
+ { 0x31C0, 0x31EF }, /* CJK Strokes */
+ { 0x31F0, 0x31FF }, /* Katakana Phonetic Extensions */
+ { 0x3200, 0x32FF }, /* Enclosed CJK Letters and Months */
+ { 0x3300, 0x33FF }, /* CJK Compatibility */
+ { 0x3400, 0x4DBF }, /* CJK Unified Ideographs Extension A */
+ { 0x4DC0, 0x4DFF }, /* Yijing Hexagram Symbols */
+ { 0x4E00, 0x9FFF }, /* CJK Unified Ideographs */
+ { 0xF900, 0xFAFF }, /* CJK Compatibility Ideographs */
+ { 0xFE30, 0xFE4F }, /* CJK Compatibility Forms */
+ { 0xFF00, 0xFFEF }, /* Halfwidth and Fullwidth Forms */
+ { 0x20000, 0x2A6DF }, /* CJK Unified Ideographs Extension B */
+ { 0x2F800, 0x2FA1F }, /* CJK Compatibility Ideographs Supplement */
+ { 0, 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_cjk_script_class =
+ {
+ AF_SCRIPT_CJK,
+ af_cjk_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) af_cjk_metrics_init,
+ (AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) af_cjk_hints_init,
+ (AF_Script_ApplyHintsFunc) af_cjk_hints_apply
+ };
+
+#else /* !AF_CONFIG_OPTION_CJK */
+
+ static const AF_Script_UniRangeRec af_cjk_uniranges[] =
+ {
+ { 0, 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_cjk_script_class =
+ {
+ AF_SCRIPT_CJK,
+ af_cjk_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) NULL,
+ (AF_Script_ScaleMetricsFunc)NULL,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) NULL,
+ (AF_Script_ApplyHintsFunc) NULL
+ };
+
+#endif /* !AF_CONFIG_OPTION_CJK */
+
+
+/* END */
diff --git a/src/autofit/afcjk.h b/src/autofit/afcjk.h
new file mode 100644
index 0000000..0de4a5a
--- /dev/null
+++ b/src/autofit/afcjk.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* afcjk.h */
+/* */
+/* Auto-fitter hinting routines for CJK script (specification). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFCJK_H__
+#define __AFCJK_H__
+
+#include "afhints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* the CJK-specific script class */
+
+ FT_CALLBACK_TABLE const AF_ScriptClassRec
+ af_cjk_script_class;
+
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFCJK_H__ */
+
+
+/* END */
diff --git a/src/autofit/afdummy.c b/src/autofit/afdummy.c
new file mode 100644
index 0000000..ed96e96
--- /dev/null
+++ b/src/autofit/afdummy.c
@@ -0,0 +1,62 @@
+/***************************************************************************/
+/* */
+/* afdummy.c */
+/* */
+/* Auto-fitter dummy routines to be used if no hinting should be */
+/* performed (body). */
+/* */
+/* Copyright 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afdummy.h"
+#include "afhints.h"
+
+
+ static FT_Error
+ af_dummy_hints_init( AF_GlyphHints hints,
+ AF_ScriptMetrics metrics )
+ {
+ af_glyph_hints_rescale( hints,
+ metrics );
+ return 0;
+ }
+
+
+ static FT_Error
+ af_dummy_hints_apply( AF_GlyphHints hints,
+ FT_Outline* outline )
+ {
+ FT_UNUSED( hints );
+ FT_UNUSED( outline );
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_dummy_script_class =
+ {
+ AF_SCRIPT_NONE,
+ NULL,
+
+ sizeof( AF_ScriptMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) NULL,
+ (AF_Script_ScaleMetricsFunc)NULL,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) af_dummy_hints_init,
+ (AF_Script_ApplyHintsFunc) af_dummy_hints_apply
+ };
+
+
+/* END */
diff --git a/src/autofit/afdummy.h b/src/autofit/afdummy.h
new file mode 100644
index 0000000..2a5faf8
--- /dev/null
+++ b/src/autofit/afdummy.h
@@ -0,0 +1,43 @@
+/***************************************************************************/
+/* */
+/* afdummy.h */
+/* */
+/* Auto-fitter dummy routines to be used if no hinting should be */
+/* performed (specification). */
+/* */
+/* Copyright 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFDUMMY_H__
+#define __AFDUMMY_H__
+
+#include "aftypes.h"
+
+
+FT_BEGIN_HEADER
+
+ /* A dummy script metrics class used when no hinting should
+ * be performed. This is the default for non-latin glyphs!
+ */
+
+ FT_CALLBACK_TABLE const AF_ScriptClassRec
+ af_dummy_script_class;
+
+/* */
+
+FT_END_HEADER
+
+
+#endif /* __AFDUMMY_H__ */
+
+
+/* END */
diff --git a/src/autofit/aferrors.h b/src/autofit/aferrors.h
new file mode 100644
index 0000000..c2ed5fe
--- /dev/null
+++ b/src/autofit/aferrors.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* aferrors.h */
+/* */
+/* Autofitter error codes (specification only). */
+/* */
+/* Copyright 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the Autofitter error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __AFERRORS_H__
+#define __AFERRORS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX AF_Err_
+#define FT_ERR_BASE FT_Mod_Err_Autofit
+
+#include FT_ERRORS_H
+
+#endif /* __AFERRORS_H__ */
+
+/* END */
diff --git a/src/autofit/afglobal.c b/src/autofit/afglobal.c
new file mode 100644
index 0000000..ad3baa1
--- /dev/null
+++ b/src/autofit/afglobal.c
@@ -0,0 +1,289 @@
+/***************************************************************************/
+/* */
+/* afglobal.c */
+/* */
+/* Auto-fitter routines to compute global hinting values (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afglobal.h"
+#include "afdummy.h"
+#include "aflatin.h"
+#include "afcjk.h"
+#include "afindic.h"
+
+#include "aferrors.h"
+
+#ifdef FT_OPTION_AUTOFIT2
+#include "aflatin2.h"
+#endif
+
+ /* populate this list when you add new scripts */
+ static AF_ScriptClass const af_script_classes[] =
+ {
+ &af_dummy_script_class,
+#ifdef FT_OPTION_AUTOFIT2
+ &af_latin2_script_class,
+#endif
+ &af_latin_script_class,
+ &af_cjk_script_class,
+ &af_indic_script_class,
+ NULL /* do not remove */
+ };
+
+ /* index of default script in `af_script_classes' */
+#define AF_SCRIPT_LIST_DEFAULT 2
+ /* indicates an uncovered glyph */
+#define AF_SCRIPT_LIST_NONE 255
+
+
+ /*
+ * Note that glyph_scripts[] is used to map each glyph into
+ * an index into the `af_script_classes' array.
+ *
+ */
+ typedef struct AF_FaceGlobalsRec_
+ {
+ FT_Face face;
+ FT_UInt glyph_count; /* same as face->num_glyphs */
+ FT_Byte* glyph_scripts;
+
+ AF_ScriptMetrics metrics[AF_SCRIPT_MAX];
+
+ } AF_FaceGlobalsRec;
+
+
+ /* Compute the script index of each glyph within a given face. */
+
+ static FT_Error
+ af_face_globals_compute_script_coverage( AF_FaceGlobals globals )
+ {
+ FT_Error error = AF_Err_Ok;
+ FT_Face face = globals->face;
+ FT_CharMap old_charmap = face->charmap;
+ FT_Byte* gscripts = globals->glyph_scripts;
+ FT_UInt ss;
+
+
+ /* the value 255 means `uncovered glyph' */
+ FT_MEM_SET( globals->glyph_scripts,
+ AF_SCRIPT_LIST_NONE,
+ globals->glyph_count );
+
+ error = FT_Select_Charmap( face, FT_ENCODING_UNICODE );
+ if ( error )
+ {
+ /*
+ * Ignore this error; we simply use Latin as the standard
+ * script. XXX: Shouldn't we rather disable hinting?
+ */
+ error = AF_Err_Ok;
+ goto Exit;
+ }
+
+ /* scan each script in a Unicode charmap */
+ for ( ss = 0; af_script_classes[ss]; ss++ )
+ {
+ AF_ScriptClass clazz = af_script_classes[ss];
+ AF_Script_UniRange range;
+
+
+ if ( clazz->script_uni_ranges == NULL )
+ continue;
+
+ /*
+ * Scan all unicode points in the range and set the corresponding
+ * glyph script index.
+ */
+ for ( range = clazz->script_uni_ranges; range->first != 0; range++ )
+ {
+ FT_ULong charcode = range->first;
+ FT_UInt gindex;
+
+
+ gindex = FT_Get_Char_Index( face, charcode );
+
+ if ( gindex != 0 &&
+ gindex < globals->glyph_count &&
+ gscripts[gindex] == AF_SCRIPT_LIST_NONE )
+ {
+ gscripts[gindex] = (FT_Byte)ss;
+ }
+
+ for (;;)
+ {
+ charcode = FT_Get_Next_Char( face, charcode, &gindex );
+
+ if ( gindex == 0 || charcode > range->last )
+ break;
+
+ if ( gindex < globals->glyph_count &&
+ gscripts[gindex] == AF_SCRIPT_LIST_NONE )
+ {
+ gscripts[gindex] = (FT_Byte)ss;
+ }
+ }
+ }
+ }
+
+ Exit:
+ /*
+ * By default, all uncovered glyphs are set to the latin script.
+ * XXX: Shouldn't we disable hinting or do something similar?
+ */
+ {
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < globals->glyph_count; nn++ )
+ {
+ if ( gscripts[nn] == AF_SCRIPT_LIST_NONE )
+ gscripts[nn] = AF_SCRIPT_LIST_DEFAULT;
+ }
+ }
+
+ FT_Set_Charmap( face, old_charmap );
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_face_globals_new( FT_Face face,
+ AF_FaceGlobals *aglobals )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ AF_FaceGlobals globals;
+
+
+ memory = face->memory;
+
+ if ( !FT_ALLOC( globals, sizeof ( *globals ) +
+ face->num_glyphs * sizeof ( FT_Byte ) ) )
+ {
+ globals->face = face;
+ globals->glyph_count = face->num_glyphs;
+ globals->glyph_scripts = (FT_Byte*)( globals + 1 );
+
+ error = af_face_globals_compute_script_coverage( globals );
+ if ( error )
+ {
+ af_face_globals_free( globals );
+ globals = NULL;
+ }
+ }
+
+ *aglobals = globals;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_face_globals_free( AF_FaceGlobals globals )
+ {
+ if ( globals )
+ {
+ FT_Memory memory = globals->face->memory;
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < AF_SCRIPT_MAX; nn++ )
+ {
+ if ( globals->metrics[nn] )
+ {
+ AF_ScriptClass clazz = af_script_classes[nn];
+
+
+ FT_ASSERT( globals->metrics[nn]->clazz == clazz );
+
+ if ( clazz->script_metrics_done )
+ clazz->script_metrics_done( globals->metrics[nn] );
+
+ FT_FREE( globals->metrics[nn] );
+ }
+ }
+
+ globals->glyph_count = 0;
+ globals->glyph_scripts = NULL; /* no need to free this one! */
+ globals->face = NULL;
+
+ FT_FREE( globals );
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_face_globals_get_metrics( AF_FaceGlobals globals,
+ FT_UInt gindex,
+ FT_UInt options,
+ AF_ScriptMetrics *ametrics )
+ {
+ AF_ScriptMetrics metrics = NULL;
+ FT_UInt gidx;
+ AF_ScriptClass clazz;
+ FT_UInt script = options & 15;
+ const FT_UInt script_max = sizeof ( af_script_classes ) /
+ sizeof ( af_script_classes[0] );
+ FT_Error error = AF_Err_Ok;
+
+
+ if ( gindex >= globals->glyph_count )
+ {
+ error = AF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ gidx = script;
+ if ( gidx == 0 || gidx + 1 >= script_max )
+ gidx = globals->glyph_scripts[gindex];
+
+ clazz = af_script_classes[gidx];
+ if ( script == 0 )
+ script = clazz->script;
+
+ metrics = globals->metrics[clazz->script];
+ if ( metrics == NULL )
+ {
+ /* create the global metrics object when needed */
+ FT_Memory memory = globals->face->memory;
+
+
+ if ( FT_ALLOC( metrics, clazz->script_metrics_size ) )
+ goto Exit;
+
+ metrics->clazz = clazz;
+
+ if ( clazz->script_metrics_init )
+ {
+ error = clazz->script_metrics_init( metrics, globals->face );
+ if ( error )
+ {
+ if ( clazz->script_metrics_done )
+ clazz->script_metrics_done( metrics );
+
+ FT_FREE( metrics );
+ goto Exit;
+ }
+ }
+
+ globals->metrics[clazz->script] = metrics;
+ }
+
+ Exit:
+ *ametrics = metrics;
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/autofit/afglobal.h b/src/autofit/afglobal.h
new file mode 100644
index 0000000..cf52c08
--- /dev/null
+++ b/src/autofit/afglobal.h
@@ -0,0 +1,67 @@
+/***************************************************************************/
+/* */
+/* afglobal.h */
+/* */
+/* Auto-fitter routines to compute global hinting values */
+/* (specification). */
+/* */
+/* Copyright 2003, 2004, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AF_GLOBAL_H__
+#define __AF_GLOBAL_H__
+
+
+#include "aftypes.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /************************************************************************/
+ /************************************************************************/
+ /***** *****/
+ /***** F A C E G L O B A L S *****/
+ /***** *****/
+ /************************************************************************/
+ /************************************************************************/
+
+
+ /*
+ * model the global hints data for a given face, decomposed into
+ * script-specific items
+ */
+ typedef struct AF_FaceGlobalsRec_* AF_FaceGlobals;
+
+
+ FT_LOCAL( FT_Error )
+ af_face_globals_new( FT_Face face,
+ AF_FaceGlobals *aglobals );
+
+ FT_LOCAL( FT_Error )
+ af_face_globals_get_metrics( AF_FaceGlobals globals,
+ FT_UInt gindex,
+ FT_UInt options,
+ AF_ScriptMetrics *ametrics );
+
+ FT_LOCAL( void )
+ af_face_globals_free( AF_FaceGlobals globals );
+
+ /* */
+
+
+FT_END_HEADER
+
+#endif /* __AF_GLOBALS_H__ */
+
+
+/* END */
diff --git a/src/autofit/afhints.c b/src/autofit/afhints.c
new file mode 100644
index 0000000..4828706
--- /dev/null
+++ b/src/autofit/afhints.c
@@ -0,0 +1,1264 @@
+/***************************************************************************/
+/* */
+/* afhints.c */
+/* */
+/* Auto-fitter hinting routines (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afhints.h"
+#include "aferrors.h"
+#include FT_INTERNAL_CALC_H
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_axis_hints_new_segment( AF_AxisHints axis,
+ FT_Memory memory,
+ AF_Segment *asegment )
+ {
+ FT_Error error = AF_Err_Ok;
+ AF_Segment segment = NULL;
+
+
+ if ( axis->num_segments >= axis->max_segments )
+ {
+ FT_Int old_max = axis->max_segments;
+ FT_Int new_max = old_max;
+ FT_Int big_max = FT_INT_MAX / sizeof ( *segment );
+
+
+ if ( old_max >= big_max )
+ {
+ error = AF_Err_Out_Of_Memory;
+ goto Exit;
+ }
+
+ new_max += ( new_max >> 2 ) + 4;
+ if ( new_max < old_max || new_max > big_max )
+ new_max = big_max;
+
+ if ( FT_RENEW_ARRAY( axis->segments, old_max, new_max ) )
+ goto Exit;
+
+ axis->max_segments = new_max;
+ }
+
+ segment = axis->segments + axis->num_segments++;
+
+ Exit:
+ *asegment = segment;
+ return error;
+ }
+
+
+ FT_LOCAL( FT_Error )
+ af_axis_hints_new_edge( AF_AxisHints axis,
+ FT_Int fpos,
+ AF_Direction dir,
+ FT_Memory memory,
+ AF_Edge *aedge )
+ {
+ FT_Error error = AF_Err_Ok;
+ AF_Edge edge = NULL;
+ AF_Edge edges;
+
+
+ if ( axis->num_edges >= axis->max_edges )
+ {
+ FT_Int old_max = axis->max_edges;
+ FT_Int new_max = old_max;
+ FT_Int big_max = FT_INT_MAX / sizeof ( *edge );
+
+
+ if ( old_max >= big_max )
+ {
+ error = AF_Err_Out_Of_Memory;
+ goto Exit;
+ }
+
+ new_max += ( new_max >> 2 ) + 4;
+ if ( new_max < old_max || new_max > big_max )
+ new_max = big_max;
+
+ if ( FT_RENEW_ARRAY( axis->edges, old_max, new_max ) )
+ goto Exit;
+
+ axis->max_edges = new_max;
+ }
+
+ edges = axis->edges;
+ edge = edges + axis->num_edges;
+
+ while ( edge > edges )
+ {
+ if ( edge[-1].fpos < fpos )
+ break;
+
+ /* we want the edge with same position and minor direction */
+ /* to appear before those in the major one in the list */
+ if ( edge[-1].fpos == fpos && dir == axis->major_dir )
+ break;
+
+ edge[0] = edge[-1];
+ edge--;
+ }
+
+ axis->num_edges++;
+
+ FT_ZERO( edge );
+ edge->fpos = (FT_Short)fpos;
+ edge->dir = (FT_Char)dir;
+
+ Exit:
+ *aedge = edge;
+ return error;
+ }
+
+
+#ifdef AF_DEBUG
+
+#include <stdio.h>
+
+ static const char*
+ af_dir_str( AF_Direction dir )
+ {
+ const char* result;
+
+
+ switch ( dir )
+ {
+ case AF_DIR_UP:
+ result = "up";
+ break;
+ case AF_DIR_DOWN:
+ result = "down";
+ break;
+ case AF_DIR_LEFT:
+ result = "left";
+ break;
+ case AF_DIR_RIGHT:
+ result = "right";
+ break;
+ default:
+ result = "none";
+ }
+
+ return result;
+ }
+
+
+#define AF_INDEX_NUM( ptr, base ) ( (ptr) ? ( (ptr) - (base) ) : -1 )
+
+
+ void
+ af_glyph_hints_dump_points( AF_GlyphHints hints )
+ {
+ AF_Point points = hints->points;
+ AF_Point limit = points + hints->num_points;
+ AF_Point point;
+
+
+ printf( "Table of points:\n" );
+ printf( " [ index | xorg | yorg | xscale | yscale "
+ "| xfit | yfit | flags ]\n" );
+
+ for ( point = points; point < limit; point++ )
+ {
+ printf( " [ %5d | %5d | %5d | %-5.2f | %-5.2f "
+ "| %-5.2f | %-5.2f | %c%c%c%c%c%c ]\n",
+ point - points,
+ point->fx,
+ point->fy,
+ point->ox/64.0,
+ point->oy/64.0,
+ point->x/64.0,
+ point->y/64.0,
+ ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) ? 'w' : ' ',
+ ( point->flags & AF_FLAG_INFLECTION ) ? 'i' : ' ',
+ ( point->flags & AF_FLAG_EXTREMA_X ) ? '<' : ' ',
+ ( point->flags & AF_FLAG_EXTREMA_Y ) ? 'v' : ' ',
+ ( point->flags & AF_FLAG_ROUND_X ) ? '(' : ' ',
+ ( point->flags & AF_FLAG_ROUND_Y ) ? 'u' : ' ');
+ }
+ printf( "\n" );
+ }
+
+
+ static const char*
+ af_edge_flags_to_string( AF_Edge_Flags flags )
+ {
+ static char temp[32];
+ int pos = 0;
+
+
+ if ( flags & AF_EDGE_ROUND )
+ {
+ memcpy( temp + pos, "round", 5 );
+ pos += 5;
+ }
+ if ( flags & AF_EDGE_SERIF )
+ {
+ if ( pos > 0 )
+ temp[pos++] = ' ';
+ memcpy( temp + pos, "serif", 5 );
+ pos += 5;
+ }
+ if ( pos == 0 )
+ return "normal";
+
+ temp[pos] = 0;
+
+ return temp;
+ }
+
+
+ /* A function to dump the array of linked segments. */
+ void
+ af_glyph_hints_dump_segments( AF_GlyphHints hints )
+ {
+ FT_Int dimension;
+
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AF_AxisHints axis = &hints->axis[dimension];
+ AF_Segment segments = axis->segments;
+ AF_Segment limit = segments + axis->num_segments;
+ AF_Segment seg;
+
+
+ printf ( "Table of %s segments:\n",
+ dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
+ printf ( " [ index | pos | dir | link | serif |"
+ " height | extra | flags ]\n" );
+
+ for ( seg = segments; seg < limit; seg++ )
+ {
+ printf ( " [ %5d | %5.2g | %5s | %4d | %5d | %5d | %5d | %s ]\n",
+ seg - segments,
+ dimension == AF_DIMENSION_HORZ ? (int)seg->first->ox / 64.0
+ : (int)seg->first->oy / 64.0,
+ af_dir_str( (AF_Direction)seg->dir ),
+ AF_INDEX_NUM( seg->link, segments ),
+ AF_INDEX_NUM( seg->serif, segments ),
+ seg->height,
+ seg->height - ( seg->max_coord - seg->min_coord ),
+ af_edge_flags_to_string( seg->flags ) );
+ }
+ printf( "\n" );
+ }
+ }
+
+
+ void
+ af_glyph_hints_dump_edges( AF_GlyphHints hints )
+ {
+ FT_Int dimension;
+
+
+ for ( dimension = 1; dimension >= 0; dimension-- )
+ {
+ AF_AxisHints axis = &hints->axis[dimension];
+ AF_Edge edges = axis->edges;
+ AF_Edge limit = edges + axis->num_edges;
+ AF_Edge edge;
+
+
+ /*
+ * note: AF_DIMENSION_HORZ corresponds to _vertical_ edges
+ * since they have constant a X coordinate.
+ */
+ printf ( "Table of %s edges:\n",
+ dimension == AF_DIMENSION_HORZ ? "vertical" : "horizontal" );
+ printf ( " [ index | pos | dir | link |"
+ " serif | blue | opos | pos | flags ]\n" );
+
+ for ( edge = edges; edge < limit; edge++ )
+ {
+ printf ( " [ %5d | %5.2g | %5s | %4d |"
+ " %5d | %c | %5.2f | %5.2f | %s ]\n",
+ edge - edges,
+ (int)edge->opos / 64.0,
+ af_dir_str( (AF_Direction)edge->dir ),
+ AF_INDEX_NUM( edge->link, edges ),
+ AF_INDEX_NUM( edge->serif, edges ),
+ edge->blue_edge ? 'y' : 'n',
+ edge->opos / 64.0,
+ edge->pos / 64.0,
+ af_edge_flags_to_string( edge->flags ) );
+ }
+ printf( "\n" );
+ }
+ }
+
+#else /* !AF_DEBUG */
+
+ /* these empty stubs are only used to link the `ftgrid' test program */
+ /* when debugging is disabled */
+
+ void
+ af_glyph_hints_dump_points( AF_GlyphHints hints )
+ {
+ FT_UNUSED( hints );
+ }
+
+
+ void
+ af_glyph_hints_dump_segments( AF_GlyphHints hints )
+ {
+ FT_UNUSED( hints );
+ }
+
+
+ void
+ af_glyph_hints_dump_edges( AF_GlyphHints hints )
+ {
+ FT_UNUSED( hints );
+ }
+
+#endif /* !AF_DEBUG */
+
+
+ /* compute the direction value of a given vector */
+ FT_LOCAL_DEF( AF_Direction )
+ af_direction_compute( FT_Pos dx,
+ FT_Pos dy )
+ {
+ FT_Pos ll, ss; /* long and short arm lengths */
+ AF_Direction dir; /* candidate direction */
+
+
+ if ( dy >= dx )
+ {
+ if ( dy >= -dx )
+ {
+ dir = AF_DIR_UP;
+ ll = dy;
+ ss = dx;
+ }
+ else
+ {
+ dir = AF_DIR_LEFT;
+ ll = -dx;
+ ss = dy;
+ }
+ }
+ else /* dy < dx */
+ {
+ if ( dy >= -dx )
+ {
+ dir = AF_DIR_RIGHT;
+ ll = dx;
+ ss = dy;
+ }
+ else
+ {
+ dir = AF_DIR_DOWN;
+ ll = dy;
+ ss = dx;
+ }
+ }
+
+ ss *= 14;
+ if ( FT_ABS( ll ) <= FT_ABS( ss ) )
+ dir = AF_DIR_NONE;
+
+ return dir;
+ }
+
+
+ /* compute all inflex points in a given glyph */
+
+ static void
+ af_glyph_hints_compute_inflections( AF_GlyphHints hints )
+ {
+ AF_Point* contour = hints->contours;
+ AF_Point* contour_limit = contour + hints->num_contours;
+
+
+ /* do each contour separately */
+ for ( ; contour < contour_limit; contour++ )
+ {
+ AF_Point point = contour[0];
+ AF_Point first = point;
+ AF_Point start = point;
+ AF_Point end = point;
+ AF_Point before;
+ AF_Point after;
+ FT_Pos in_x, in_y, out_x, out_y;
+ AF_Angle orient_prev, orient_cur;
+ FT_Int finished = 0;
+
+
+ /* compute first segment in contour */
+ first = point;
+
+ start = end = first;
+ do
+ {
+ end = end->next;
+ if ( end == first )
+ goto Skip;
+
+ in_x = end->fx - start->fx;
+ in_y = end->fy - start->fy;
+
+ } while ( in_x == 0 && in_y == 0 );
+
+ /* extend the segment start whenever possible */
+ before = start;
+ do
+ {
+ do
+ {
+ start = before;
+ before = before->prev;
+ if ( before == first )
+ goto Skip;
+
+ out_x = start->fx - before->fx;
+ out_y = start->fy - before->fy;
+
+ } while ( out_x == 0 && out_y == 0 );
+
+ orient_prev = ft_corner_orientation( in_x, in_y, out_x, out_y );
+
+ } while ( orient_prev == 0 );
+
+ first = start;
+
+ in_x = out_x;
+ in_y = out_y;
+
+ /* now process all segments in the contour */
+ do
+ {
+ /* first, extend current segment's end whenever possible */
+ after = end;
+ do
+ {
+ do
+ {
+ end = after;
+ after = after->next;
+ if ( after == first )
+ finished = 1;
+
+ out_x = after->fx - end->fx;
+ out_y = after->fy - end->fy;
+
+ } while ( out_x == 0 && out_y == 0 );
+
+ orient_cur = ft_corner_orientation( in_x, in_y, out_x, out_y );
+
+ } while ( orient_cur == 0 );
+
+ if ( ( orient_prev + orient_cur ) == 0 )
+ {
+ /* we have an inflection point here */
+ do
+ {
+ start->flags |= AF_FLAG_INFLECTION;
+ start = start->next;
+
+ } while ( start != end );
+
+ start->flags |= AF_FLAG_INFLECTION;
+ }
+
+ start = end;
+ end = after;
+
+ orient_prev = orient_cur;
+ in_x = out_x;
+ in_y = out_y;
+
+ } while ( !finished );
+
+ Skip:
+ ;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_init( AF_GlyphHints hints,
+ FT_Memory memory )
+ {
+ FT_ZERO( hints );
+ hints->memory = memory;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_done( AF_GlyphHints hints )
+ {
+ if ( hints && hints->memory )
+ {
+ FT_Memory memory = hints->memory;
+ int dim;
+
+
+ /*
+ * note that we don't need to free the segment and edge
+ * buffers, since they are really within the hints->points array
+ */
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+
+
+ axis->num_segments = 0;
+ axis->max_segments = 0;
+ FT_FREE( axis->segments );
+
+ axis->num_edges = 0;
+ axis->max_edges = 0;
+ FT_FREE( axis->edges );
+ }
+
+ FT_FREE( hints->contours );
+ hints->max_contours = 0;
+ hints->num_contours = 0;
+
+ FT_FREE( hints->points );
+ hints->num_points = 0;
+ hints->max_points = 0;
+
+ hints->memory = NULL;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_rescale( AF_GlyphHints hints,
+ AF_ScriptMetrics metrics )
+ {
+ hints->metrics = metrics;
+ hints->scaler_flags = metrics->scaler.flags;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_glyph_hints_reload( AF_GlyphHints hints,
+ FT_Outline* outline,
+ FT_Bool get_inflections )
+ {
+ FT_Error error = AF_Err_Ok;
+ AF_Point points;
+ FT_UInt old_max, new_max;
+ FT_Fixed x_scale = hints->x_scale;
+ FT_Fixed y_scale = hints->y_scale;
+ FT_Pos x_delta = hints->x_delta;
+ FT_Pos y_delta = hints->y_delta;
+ FT_Memory memory = hints->memory;
+
+
+ hints->num_points = 0;
+ hints->num_contours = 0;
+
+ hints->axis[0].num_segments = 0;
+ hints->axis[0].num_edges = 0;
+ hints->axis[1].num_segments = 0;
+ hints->axis[1].num_edges = 0;
+
+ /* first of all, reallocate the contours array when necessary */
+ new_max = (FT_UInt)outline->n_contours;
+ old_max = hints->max_contours;
+ if ( new_max > old_max )
+ {
+ new_max = ( new_max + 3 ) & ~3;
+
+ if ( FT_RENEW_ARRAY( hints->contours, old_max, new_max ) )
+ goto Exit;
+
+ hints->max_contours = new_max;
+ }
+
+ /*
+ * then reallocate the points arrays if necessary --
+ * note that we reserve two additional point positions, used to
+ * hint metrics appropriately
+ */
+ new_max = (FT_UInt)( outline->n_points + 2 );
+ old_max = hints->max_points;
+ if ( new_max > old_max )
+ {
+ new_max = ( new_max + 2 + 7 ) & ~7;
+
+ if ( FT_RENEW_ARRAY( hints->points, old_max, new_max ) )
+ goto Exit;
+
+ hints->max_points = new_max;
+ }
+
+ hints->num_points = outline->n_points;
+ hints->num_contours = outline->n_contours;
+
+ /* We can't rely on the value of `FT_Outline.flags' to know the fill */
+ /* direction used for a glyph, given that some fonts are broken (e.g., */
+ /* the Arphic ones). We thus recompute it each time we need to. */
+ /* */
+ hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_UP;
+ hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_LEFT;
+
+ if ( FT_Outline_Get_Orientation( outline ) == FT_ORIENTATION_POSTSCRIPT )
+ {
+ hints->axis[AF_DIMENSION_HORZ].major_dir = AF_DIR_DOWN;
+ hints->axis[AF_DIMENSION_VERT].major_dir = AF_DIR_RIGHT;
+ }
+
+ hints->x_scale = x_scale;
+ hints->y_scale = y_scale;
+ hints->x_delta = x_delta;
+ hints->y_delta = y_delta;
+
+ hints->xmin_delta = 0;
+ hints->xmax_delta = 0;
+
+ points = hints->points;
+ if ( hints->num_points == 0 )
+ goto Exit;
+
+ {
+ AF_Point point;
+ AF_Point point_limit = points + hints->num_points;
+
+
+ /* compute coordinates & Bezier flags, next and prev */
+ {
+ FT_Vector* vec = outline->points;
+ char* tag = outline->tags;
+ AF_Point first = points;
+ AF_Point end = points + outline->contours[0];
+ AF_Point prev = end;
+ FT_Int contour_index = 0;
+
+
+ for ( point = points; point < point_limit; point++, vec++, tag++ )
+ {
+ point->fx = (FT_Short)vec->x;
+ point->fy = (FT_Short)vec->y;
+ point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
+ point->oy = point->y = FT_MulFix( vec->y, y_scale ) + y_delta;
+
+ switch ( FT_CURVE_TAG( *tag ) )
+ {
+ case FT_CURVE_TAG_CONIC:
+ point->flags = AF_FLAG_CONIC;
+ break;
+ case FT_CURVE_TAG_CUBIC:
+ point->flags = AF_FLAG_CUBIC;
+ break;
+ default:
+ point->flags = 0;
+ }
+
+ point->prev = prev;
+ prev->next = point;
+ prev = point;
+
+ if ( point == end )
+ {
+ if ( ++contour_index < outline->n_contours )
+ {
+ first = point + 1;
+ end = points + outline->contours[contour_index];
+ prev = end;
+ }
+ }
+ }
+ }
+
+ /* set-up the contours array */
+ {
+ AF_Point* contour = hints->contours;
+ AF_Point* contour_limit = contour + hints->num_contours;
+ short* end = outline->contours;
+ short idx = 0;
+
+
+ for ( ; contour < contour_limit; contour++, end++ )
+ {
+ contour[0] = points + idx;
+ idx = (short)( end[0] + 1 );
+ }
+ }
+
+ /* compute directions of in & out vectors */
+ {
+ AF_Point first = points;
+ AF_Point prev = NULL;
+ FT_Pos in_x = 0;
+ FT_Pos in_y = 0;
+ AF_Direction in_dir = AF_DIR_NONE;
+
+
+ for ( point = points; point < point_limit; point++ )
+ {
+ AF_Point next;
+ FT_Pos out_x, out_y;
+
+
+ if ( point == first )
+ {
+ prev = first->prev;
+ in_x = first->fx - prev->fx;
+ in_y = first->fy - prev->fy;
+ in_dir = af_direction_compute( in_x, in_y );
+ first = prev + 1;
+ }
+
+ point->in_dir = (FT_Char)in_dir;
+
+ next = point->next;
+ out_x = next->fx - point->fx;
+ out_y = next->fy - point->fy;
+
+ in_dir = af_direction_compute( out_x, out_y );
+ point->out_dir = (FT_Char)in_dir;
+
+ if ( point->flags & ( AF_FLAG_CONIC | AF_FLAG_CUBIC ) )
+ {
+ Is_Weak_Point:
+ point->flags |= AF_FLAG_WEAK_INTERPOLATION;
+ }
+ else if ( point->out_dir == point->in_dir )
+ {
+ if ( point->out_dir != AF_DIR_NONE )
+ goto Is_Weak_Point;
+
+ if ( ft_corner_is_flat( in_x, in_y, out_x, out_y ) )
+ goto Is_Weak_Point;
+ }
+ else if ( point->in_dir == -point->out_dir )
+ goto Is_Weak_Point;
+
+ in_x = out_x;
+ in_y = out_y;
+ prev = point;
+ }
+ }
+ }
+
+ /* compute inflection points -- */
+ /* disabled due to no longer perceived benefits */
+ if ( 0 && get_inflections )
+ af_glyph_hints_compute_inflections( hints );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_save( AF_GlyphHints hints,
+ FT_Outline* outline )
+ {
+ AF_Point point = hints->points;
+ AF_Point limit = point + hints->num_points;
+ FT_Vector* vec = outline->points;
+ char* tag = outline->tags;
+
+
+ for ( ; point < limit; point++, vec++, tag++ )
+ {
+ vec->x = point->x;
+ vec->y = point->y;
+
+ if ( point->flags & AF_FLAG_CONIC )
+ tag[0] = FT_CURVE_TAG_CONIC;
+ else if ( point->flags & AF_FLAG_CUBIC )
+ tag[0] = FT_CURVE_TAG_CUBIC;
+ else
+ tag[0] = FT_CURVE_TAG_ON;
+ }
+ }
+
+
+ /****************************************************************
+ *
+ * EDGE POINT GRID-FITTING
+ *
+ ****************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_align_edge_points( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = & hints->axis[dim];
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ AF_Segment seg;
+
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Edge edge = seg->edge;
+ AF_Point point, first, last;
+
+
+ if ( edge == NULL )
+ continue;
+
+ first = seg->first;
+ last = seg->last;
+ point = first;
+ for (;;)
+ {
+ point->x = edge->pos;
+ point->flags |= AF_FLAG_TOUCH_X;
+
+ if ( point == last )
+ break;
+
+ point = point->next;
+
+ }
+ }
+ }
+ else
+ {
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Edge edge = seg->edge;
+ AF_Point point, first, last;
+
+
+ if ( edge == NULL )
+ continue;
+
+ first = seg->first;
+ last = seg->last;
+ point = first;
+ for (;;)
+ {
+ point->y = edge->pos;
+ point->flags |= AF_FLAG_TOUCH_Y;
+
+ if ( point == last )
+ break;
+
+ point = point->next;
+ }
+ }
+ }
+ }
+
+
+ /****************************************************************
+ *
+ * STRONG POINT INTERPOLATION
+ *
+ ****************************************************************/
+
+
+ /* hint the strong points -- this is equivalent to the TrueType `IP' */
+ /* hinting instruction */
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_align_strong_points( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_Point points = hints->points;
+ AF_Point point_limit = points + hints->num_points;
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ AF_Flags touch_flag;
+
+
+ if ( dim == AF_DIMENSION_HORZ )
+ touch_flag = AF_FLAG_TOUCH_X;
+ else
+ touch_flag = AF_FLAG_TOUCH_Y;
+
+ if ( edges < edge_limit )
+ {
+ AF_Point point;
+ AF_Edge edge;
+
+
+ for ( point = points; point < point_limit; point++ )
+ {
+ FT_Pos u, ou, fu; /* point position */
+ FT_Pos delta;
+
+
+ if ( point->flags & touch_flag )
+ continue;
+
+ /* if this point is candidate to weak interpolation, we */
+ /* interpolate it after all strong points have been processed */
+
+ if ( ( point->flags & AF_FLAG_WEAK_INTERPOLATION ) &&
+ !( point->flags & AF_FLAG_INFLECTION ) )
+ continue;
+
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ u = point->fy;
+ ou = point->oy;
+ }
+ else
+ {
+ u = point->fx;
+ ou = point->ox;
+ }
+
+ fu = u;
+
+ /* is the point before the first edge? */
+ edge = edges;
+ delta = edge->fpos - u;
+ if ( delta >= 0 )
+ {
+ u = edge->pos - ( edge->opos - ou );
+ goto Store_Point;
+ }
+
+ /* is the point after the last edge? */
+ edge = edge_limit - 1;
+ delta = u - edge->fpos;
+ if ( delta >= 0 )
+ {
+ u = edge->pos + ( ou - edge->opos );
+ goto Store_Point;
+ }
+
+ {
+ FT_UInt min, max, mid;
+ FT_Pos fpos;
+
+
+ /* find enclosing edges */
+ min = 0;
+ max = edge_limit - edges;
+
+#if 1
+ /* for small edge counts, a linear search is better */
+ if ( max <= 8 )
+ {
+ FT_UInt nn;
+
+ for ( nn = 0; nn < max; nn++ )
+ if ( edges[nn].fpos >= u )
+ break;
+
+ if ( edges[nn].fpos == u )
+ {
+ u = edges[nn].pos;
+ goto Store_Point;
+ }
+ min = nn;
+ }
+ else
+#endif
+ while ( min < max )
+ {
+ mid = ( max + min ) >> 1;
+ edge = edges + mid;
+ fpos = edge->fpos;
+
+ if ( u < fpos )
+ max = mid;
+ else if ( u > fpos )
+ min = mid + 1;
+ else
+ {
+ /* we are on the edge */
+ u = edge->pos;
+ goto Store_Point;
+ }
+ }
+
+ {
+ AF_Edge before = edges + min - 1;
+ AF_Edge after = edges + min + 0;
+
+
+ /* assert( before && after && before != after ) */
+ if ( before->scale == 0 )
+ before->scale = FT_DivFix( after->pos - before->pos,
+ after->fpos - before->fpos );
+
+ u = before->pos + FT_MulFix( fu - before->fpos,
+ before->scale );
+ }
+ }
+
+ Store_Point:
+ /* save the point position */
+ if ( dim == AF_DIMENSION_HORZ )
+ point->x = u;
+ else
+ point->y = u;
+
+ point->flags |= touch_flag;
+ }
+ }
+ }
+
+
+ /****************************************************************
+ *
+ * WEAK POINT INTERPOLATION
+ *
+ ****************************************************************/
+
+
+ static void
+ af_iup_shift( AF_Point p1,
+ AF_Point p2,
+ AF_Point ref )
+ {
+ AF_Point p;
+ FT_Pos delta = ref->u - ref->v;
+
+ if ( delta == 0 )
+ return;
+
+ for ( p = p1; p < ref; p++ )
+ p->u = p->v + delta;
+
+ for ( p = ref + 1; p <= p2; p++ )
+ p->u = p->v + delta;
+ }
+
+
+ static void
+ af_iup_interp( AF_Point p1,
+ AF_Point p2,
+ AF_Point ref1,
+ AF_Point ref2 )
+ {
+ AF_Point p;
+ FT_Pos u;
+ FT_Pos v1 = ref1->v;
+ FT_Pos v2 = ref2->v;
+ FT_Pos d1 = ref1->u - v1;
+ FT_Pos d2 = ref2->u - v2;
+
+
+ if ( p1 > p2 )
+ return;
+
+ if ( v1 == v2 )
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ u = p->v;
+
+ if ( u <= v1 )
+ u += d1;
+ else
+ u += d2;
+
+ p->u = u;
+ }
+ return;
+ }
+
+ if ( v1 < v2 )
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ u = p->v;
+
+ if ( u <= v1 )
+ u += d1;
+ else if ( u >= v2 )
+ u += d2;
+ else
+ u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
+
+ p->u = u;
+ }
+ }
+ else
+ {
+ for ( p = p1; p <= p2; p++ )
+ {
+ u = p->v;
+
+ if ( u <= v2 )
+ u += d2;
+ else if ( u >= v1 )
+ u += d1;
+ else
+ u = ref1->u + FT_MulDiv( u - v1, ref2->u - ref1->u, v2 - v1 );
+
+ p->u = u;
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_align_weak_points( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_Point points = hints->points;
+ AF_Point point_limit = points + hints->num_points;
+ AF_Point* contour = hints->contours;
+ AF_Point* contour_limit = contour + hints->num_contours;
+ AF_Flags touch_flag;
+ AF_Point point;
+ AF_Point end_point;
+ AF_Point first_point;
+
+
+ /* PASS 1: Move segment points to edge positions */
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ touch_flag = AF_FLAG_TOUCH_X;
+
+ for ( point = points; point < point_limit; point++ )
+ {
+ point->u = point->x;
+ point->v = point->ox;
+ }
+ }
+ else
+ {
+ touch_flag = AF_FLAG_TOUCH_Y;
+
+ for ( point = points; point < point_limit; point++ )
+ {
+ point->u = point->y;
+ point->v = point->oy;
+ }
+ }
+
+ point = points;
+
+ for ( ; contour < contour_limit; contour++ )
+ {
+ AF_Point first_touched, last_touched;
+
+
+ point = *contour;
+ end_point = point->prev;
+ first_point = point;
+
+ /* find first touched point */
+ for (;;)
+ {
+ if ( point > end_point ) /* no touched point in contour */
+ goto NextContour;
+
+ if ( point->flags & touch_flag )
+ break;
+
+ point++;
+ }
+
+ first_touched = point;
+ last_touched = point;
+
+ for (;;)
+ {
+ FT_ASSERT( point <= end_point &&
+ ( point->flags & touch_flag ) != 0 );
+
+ /* skip any touched neighbhours */
+ while ( point < end_point && ( point[1].flags & touch_flag ) != 0 )
+ point++;
+
+ last_touched = point;
+
+ /* find the next touched point, if any */
+ point ++;
+ for (;;)
+ {
+ if ( point > end_point )
+ goto EndContour;
+
+ if ( ( point->flags & touch_flag ) != 0 )
+ break;
+
+ point++;
+ }
+
+ /* interpolate between last_touched and point */
+ af_iup_interp( last_touched + 1, point - 1,
+ last_touched, point );
+ }
+
+ EndContour:
+ /* special case: only one point was touched */
+ if ( last_touched == first_touched )
+ {
+ af_iup_shift( first_point, end_point, first_touched );
+ }
+ else /* interpolate the last part */
+ {
+ if ( last_touched < end_point )
+ af_iup_interp( last_touched + 1, end_point,
+ last_touched, first_touched );
+
+ if ( first_touched > points )
+ af_iup_interp( first_point, first_touched - 1,
+ last_touched, first_touched );
+ }
+
+ NextContour:
+ ;
+ }
+
+ /* now save the interpolated values back to x/y */
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ for ( point = points; point < point_limit; point++ )
+ point->x = point->u;
+ }
+ else
+ {
+ for ( point = points; point < point_limit; point++ )
+ point->y = point->u;
+ }
+ }
+
+
+#ifdef AF_USE_WARPER
+
+ FT_LOCAL_DEF( void )
+ af_glyph_hints_scale_dim( AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Fixed scale,
+ FT_Pos delta )
+ {
+ AF_Point points = hints->points;
+ AF_Point points_limit = points + hints->num_points;
+ AF_Point point;
+
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ for ( point = points; point < points_limit; point++ )
+ point->x = FT_MulFix( point->fx, scale ) + delta;
+ }
+ else
+ {
+ for ( point = points; point < points_limit; point++ )
+ point->y = FT_MulFix( point->fy, scale ) + delta;
+ }
+ }
+
+#endif /* AF_USE_WARPER */
+
+/* END */
diff --git a/src/autofit/afhints.h b/src/autofit/afhints.h
new file mode 100644
index 0000000..1308d5c
--- /dev/null
+++ b/src/autofit/afhints.h
@@ -0,0 +1,333 @@
+/***************************************************************************/
+/* */
+/* afhints.h */
+/* */
+/* Auto-fitter hinting routines (specification). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFHINTS_H__
+#define __AFHINTS_H__
+
+#include "aftypes.h"
+
+#define xxAF_SORT_SEGMENTS
+
+FT_BEGIN_HEADER
+
+ /*
+ * The definition of outline glyph hints. These are shared by all
+ * script analysis routines (until now).
+ */
+
+ typedef enum
+ {
+ AF_DIMENSION_HORZ = 0, /* x coordinates, */
+ /* i.e., vertical segments & edges */
+ AF_DIMENSION_VERT = 1, /* y coordinates, */
+ /* i.e., horizontal segments & edges */
+
+ AF_DIMENSION_MAX /* do not remove */
+
+ } AF_Dimension;
+
+
+ /* hint directions -- the values are computed so that two vectors are */
+ /* in opposite directions iff `dir1 + dir2 == 0' */
+ typedef enum
+ {
+ AF_DIR_NONE = 4,
+ AF_DIR_RIGHT = 1,
+ AF_DIR_LEFT = -1,
+ AF_DIR_UP = 2,
+ AF_DIR_DOWN = -2
+
+ } AF_Direction;
+
+
+ /* point hint flags */
+ typedef enum
+ {
+ AF_FLAG_NONE = 0,
+
+ /* point type flags */
+ AF_FLAG_CONIC = 1 << 0,
+ AF_FLAG_CUBIC = 1 << 1,
+ AF_FLAG_CONTROL = AF_FLAG_CONIC | AF_FLAG_CUBIC,
+
+ /* point extremum flags */
+ AF_FLAG_EXTREMA_X = 1 << 2,
+ AF_FLAG_EXTREMA_Y = 1 << 3,
+
+ /* point roundness flags */
+ AF_FLAG_ROUND_X = 1 << 4,
+ AF_FLAG_ROUND_Y = 1 << 5,
+
+ /* point touch flags */
+ AF_FLAG_TOUCH_X = 1 << 6,
+ AF_FLAG_TOUCH_Y = 1 << 7,
+
+ /* candidates for weak interpolation have this flag set */
+ AF_FLAG_WEAK_INTERPOLATION = 1 << 8,
+
+ /* all inflection points in the outline have this flag set */
+ AF_FLAG_INFLECTION = 1 << 9
+
+ } AF_Flags;
+
+
+ /* edge hint flags */
+ typedef enum
+ {
+ AF_EDGE_NORMAL = 0,
+ AF_EDGE_ROUND = 1 << 0,
+ AF_EDGE_SERIF = 1 << 1,
+ AF_EDGE_DONE = 1 << 2
+
+ } AF_Edge_Flags;
+
+
+ typedef struct AF_PointRec_* AF_Point;
+ typedef struct AF_SegmentRec_* AF_Segment;
+ typedef struct AF_EdgeRec_* AF_Edge;
+
+
+ typedef struct AF_PointRec_
+ {
+ FT_UShort flags; /* point flags used by hinter */
+ FT_Char in_dir; /* direction of inwards vector */
+ FT_Char out_dir; /* direction of outwards vector */
+
+ FT_Pos ox, oy; /* original, scaled position */
+ FT_Short fx, fy; /* original, unscaled position (font units) */
+ FT_Pos x, y; /* current position */
+ FT_Pos u, v; /* current (x,y) or (y,x) depending on context */
+
+ AF_Point next; /* next point in contour */
+ AF_Point prev; /* previous point in contour */
+
+ } AF_PointRec;
+
+
+ typedef struct AF_SegmentRec_
+ {
+ FT_Byte flags; /* edge/segment flags for this segment */
+ FT_Char dir; /* segment direction */
+ FT_Short pos; /* position of segment */
+ FT_Short min_coord; /* minimum coordinate of segment */
+ FT_Short max_coord; /* maximum coordinate of segment */
+ FT_Short height; /* the hinted segment height */
+
+ AF_Edge edge; /* the segment's parent edge */
+ AF_Segment edge_next; /* link to next segment in parent edge */
+
+ AF_Segment link; /* (stem) link segment */
+ AF_Segment serif; /* primary segment for serifs */
+ FT_Pos num_linked; /* number of linked segments */
+ FT_Pos score; /* used during stem matching */
+ FT_Pos len; /* used during stem matching */
+
+ AF_Point first; /* first point in edge segment */
+ AF_Point last; /* last point in edge segment */
+ AF_Point* contour; /* ptr to first point of segment's contour */
+
+ } AF_SegmentRec;
+
+
+ typedef struct AF_EdgeRec_
+ {
+ FT_Short fpos; /* original, unscaled position (font units) */
+ FT_Pos opos; /* original, scaled position */
+ FT_Pos pos; /* current position */
+
+ FT_Byte flags; /* edge flags */
+ FT_Char dir; /* edge direction */
+ FT_Fixed scale; /* used to speed up interpolation between edges */
+ AF_Width blue_edge; /* non-NULL if this is a blue edge */
+
+ AF_Edge link;
+ AF_Edge serif;
+ FT_Short num_linked;
+
+ FT_Int score;
+
+ AF_Segment first;
+ AF_Segment last;
+
+ } AF_EdgeRec;
+
+
+ typedef struct AF_AxisHintsRec_
+ {
+ FT_Int num_segments;
+ FT_Int max_segments;
+ AF_Segment segments;
+#ifdef AF_SORT_SEGMENTS
+ FT_Int mid_segments;
+#endif
+
+ FT_Int num_edges;
+ FT_Int max_edges;
+ AF_Edge edges;
+
+ AF_Direction major_dir;
+
+ } AF_AxisHintsRec, *AF_AxisHints;
+
+
+ typedef struct AF_GlyphHintsRec_
+ {
+ FT_Memory memory;
+
+ FT_Fixed x_scale;
+ FT_Pos x_delta;
+
+ FT_Fixed y_scale;
+ FT_Pos y_delta;
+
+ FT_Pos edge_distance_threshold;
+
+ FT_Int max_points;
+ FT_Int num_points;
+ AF_Point points;
+
+ FT_Int max_contours;
+ FT_Int num_contours;
+ AF_Point* contours;
+
+ AF_AxisHintsRec axis[AF_DIMENSION_MAX];
+
+ FT_UInt32 scaler_flags; /* copy of scaler flags */
+ FT_UInt32 other_flags; /* free for script-specific */
+ /* implementations */
+ AF_ScriptMetrics metrics;
+
+ FT_Pos xmin_delta; /* used for warping */
+ FT_Pos xmax_delta;
+
+ } AF_GlyphHintsRec;
+
+
+#define AF_HINTS_TEST_SCALER( h, f ) ( (h)->scaler_flags & (f) )
+#define AF_HINTS_TEST_OTHER( h, f ) ( (h)->other_flags & (f) )
+
+
+#ifdef AF_DEBUG
+
+#define AF_HINTS_DO_HORIZONTAL( h ) \
+ ( !_af_debug_disable_horz_hints && \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL ) )
+
+#define AF_HINTS_DO_VERTICAL( h ) \
+ ( !_af_debug_disable_vert_hints && \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL ) )
+
+#define AF_HINTS_DO_ADVANCE( h ) \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_ADVANCE )
+
+#define AF_HINTS_DO_BLUES( h ) ( !_af_debug_disable_blue_hints )
+
+#else /* !AF_DEBUG */
+
+#define AF_HINTS_DO_HORIZONTAL( h ) \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_HORIZONTAL )
+
+#define AF_HINTS_DO_VERTICAL( h ) \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_VERTICAL )
+
+#define AF_HINTS_DO_ADVANCE( h ) \
+ !AF_HINTS_TEST_SCALER( h, AF_SCALER_FLAG_NO_ADVANCE )
+
+#define AF_HINTS_DO_BLUES( h ) 1
+
+#endif /* !AF_DEBUG */
+
+
+ FT_LOCAL( AF_Direction )
+ af_direction_compute( FT_Pos dx,
+ FT_Pos dy );
+
+
+ FT_LOCAL( FT_Error )
+ af_axis_hints_new_segment( AF_AxisHints axis,
+ FT_Memory memory,
+ AF_Segment *asegment );
+
+ FT_LOCAL( FT_Error)
+ af_axis_hints_new_edge( AF_AxisHints axis,
+ FT_Int fpos,
+ AF_Direction dir,
+ FT_Memory memory,
+ AF_Edge *edge );
+
+ FT_LOCAL( void )
+ af_glyph_hints_init( AF_GlyphHints hints,
+ FT_Memory memory );
+
+
+
+ /*
+ * recompute all AF_Point in a AF_GlyphHints from the definitions
+ * in a source outline
+ */
+ FT_LOCAL( void )
+ af_glyph_hints_rescale( AF_GlyphHints hints,
+ AF_ScriptMetrics metrics );
+
+ FT_LOCAL( FT_Error )
+ af_glyph_hints_reload( AF_GlyphHints hints,
+ FT_Outline* outline,
+ FT_Bool get_inflections );
+
+ FT_LOCAL( void )
+ af_glyph_hints_save( AF_GlyphHints hints,
+ FT_Outline* outline );
+
+ FT_LOCAL( void )
+ af_glyph_hints_align_edge_points( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ FT_LOCAL( void )
+ af_glyph_hints_align_strong_points( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ FT_LOCAL( void )
+ af_glyph_hints_align_weak_points( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+#ifdef AF_USE_WARPER
+ FT_LOCAL( void )
+ af_glyph_hints_scale_dim( AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Fixed scale,
+ FT_Pos delta );
+#endif
+
+ FT_LOCAL( void )
+ af_glyph_hints_done( AF_GlyphHints hints );
+
+/* */
+
+#define AF_SEGMENT_LEN( seg ) ( (seg)->max_coord - (seg)->min_coord )
+
+#define AF_SEGMENT_DIST( seg1, seg2 ) ( ( (seg1)->pos > (seg2)->pos ) \
+ ? (seg1)->pos - (seg2)->pos \
+ : (seg2)->pos - (seg1)->pos )
+
+
+FT_END_HEADER
+
+#endif /* __AFHINTS_H__ */
+
+
+/* END */
diff --git a/src/autofit/afindic.c b/src/autofit/afindic.c
new file mode 100644
index 0000000..c6e7522
--- /dev/null
+++ b/src/autofit/afindic.c
@@ -0,0 +1,134 @@
+/***************************************************************************/
+/* */
+/* afindic.c */
+/* */
+/* Auto-fitter hinting routines for Indic scripts (body). */
+/* */
+/* Copyright 2007 by */
+/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "aftypes.h"
+#include "aflatin.h"
+
+
+#ifdef AF_CONFIG_OPTION_INDIC
+
+#include "afindic.h"
+#include "aferrors.h"
+#include "afcjk.h"
+
+
+#ifdef AF_USE_WARPER
+#include "afwarp.h"
+#endif
+
+
+ static FT_Error
+ af_indic_metrics_init( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ /* use CJK routines */
+ return af_cjk_metrics_init( metrics, face );
+ }
+
+
+ static void
+ af_indic_metrics_scale( AF_LatinMetrics metrics,
+ AF_Scaler scaler )
+ {
+ /* use CJK routines */
+ af_cjk_metrics_scale( metrics, scaler );
+ }
+
+
+ static FT_Error
+ af_indic_hints_init( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ /* use CJK routines */
+ return af_cjk_hints_init( hints, metrics );
+ }
+
+
+ static FT_Error
+ af_indic_hints_apply( AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_LatinMetrics metrics)
+ {
+ /* use CJK routines */
+ return af_cjk_hints_apply( hints, outline, metrics );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** I N D I C S C R I P T C L A S S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static const AF_Script_UniRangeRec af_indic_uniranges[] =
+ {
+#if 0
+ { 0x0100, 0xFFFF }, /* why this? */
+#endif
+ { 0x0900, 0x0DFF}, /* Indic Range */
+ { 0, 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_indic_script_class =
+ {
+ AF_SCRIPT_INDIC,
+ af_indic_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) af_indic_metrics_init,
+ (AF_Script_ScaleMetricsFunc)af_indic_metrics_scale,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) af_indic_hints_init,
+ (AF_Script_ApplyHintsFunc) af_indic_hints_apply
+ };
+
+#else /* !AF_CONFIG_OPTION_INDIC */
+
+ static const AF_Script_UniRangeRec af_indic_uniranges[] =
+ {
+ { 0, 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_indic_script_class =
+ {
+ AF_SCRIPT_INDIC,
+ af_indic_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) NULL,
+ (AF_Script_ScaleMetricsFunc)NULL,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) NULL,
+ (AF_Script_ApplyHintsFunc) NULL
+ };
+
+#endif /* !AF_CONFIG_OPTION_INDIC */
+
+
+/* END */
diff --git a/src/autofit/afindic.h b/src/autofit/afindic.h
new file mode 100644
index 0000000..b242b26
--- /dev/null
+++ b/src/autofit/afindic.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* afindic.h */
+/* */
+/* Auto-fitter hinting routines for Indic scripts (specification). */
+/* */
+/* Copyright 2007 by */
+/* Rahul Bhalerao <rahul.bhalerao@redhat.com>, <b.rahul.pm@gmail.com>. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFINDIC_H__
+#define __AFINDIC_H__
+
+#include "afhints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* the Indic-specific script class */
+
+ FT_CALLBACK_TABLE const AF_ScriptClassRec
+ af_indic_script_class;
+
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFINDIC_H__ */
+
+
+/* END */
diff --git a/src/autofit/aflatin.c b/src/autofit/aflatin.c
new file mode 100644
index 0000000..2ae9ec5
--- /dev/null
+++ b/src/autofit/aflatin.c
@@ -0,0 +1,2166 @@
+/***************************************************************************/
+/* */
+/* aflatin.c */
+/* */
+/* Auto-fitter hinting routines for latin script (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "aflatin.h"
+#include "aferrors.h"
+
+
+#ifdef AF_USE_WARPER
+#include "afwarp.h"
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L O B A L M E T R I C S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ af_latin_metrics_init_widths( AF_LatinMetrics metrics,
+ FT_Face face,
+ FT_ULong charcode )
+ {
+ /* scan the array of segments in each direction */
+ AF_GlyphHintsRec hints[1];
+
+
+ af_glyph_hints_init( hints, face->memory );
+
+ metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
+ metrics->axis[AF_DIMENSION_VERT].width_count = 0;
+
+ {
+ FT_Error error;
+ FT_UInt glyph_index;
+ int dim;
+ AF_LatinMetricsRec dummy[1];
+ AF_Scaler scaler = &dummy->root.scaler;
+
+
+ glyph_index = FT_Get_Char_Index( face, charcode );
+ if ( glyph_index == 0 )
+ goto Exit;
+
+ error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+ if ( error || face->glyph->outline.n_points <= 0 )
+ goto Exit;
+
+ FT_ZERO( dummy );
+
+ dummy->units_per_em = metrics->units_per_em;
+ scaler->x_scale = scaler->y_scale = 0x10000L;
+ scaler->x_delta = scaler->y_delta = 0;
+ scaler->face = face;
+ scaler->render_mode = FT_RENDER_MODE_NORMAL;
+ scaler->flags = 0;
+
+ af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy );
+
+ error = af_glyph_hints_reload( hints, &face->glyph->outline, 0 );
+ if ( error )
+ goto Exit;
+
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ AF_LatinAxis axis = &metrics->axis[dim];
+ AF_AxisHints axhints = &hints->axis[dim];
+ AF_Segment seg, limit, link;
+ FT_UInt num_widths = 0;
+
+
+ error = af_latin_hints_compute_segments( hints,
+ (AF_Dimension)dim );
+ if ( error )
+ goto Exit;
+
+ af_latin_hints_link_segments( hints,
+ (AF_Dimension)dim );
+
+ seg = axhints->segments;
+ limit = seg + axhints->num_segments;
+
+ for ( ; seg < limit; seg++ )
+ {
+ link = seg->link;
+
+ /* we only consider stem segments there! */
+ if ( link && link->link == seg && link > seg )
+ {
+ FT_Pos dist;
+
+
+ dist = seg->pos - link->pos;
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( num_widths < AF_LATIN_MAX_WIDTHS )
+ axis->widths[ num_widths++ ].org = dist;
+ }
+ }
+
+ af_sort_widths( num_widths, axis->widths );
+ axis->width_count = num_widths;
+ }
+
+ Exit:
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ AF_LatinAxis axis = &metrics->axis[dim];
+ FT_Pos stdw;
+
+
+ stdw = ( axis->width_count > 0 )
+ ? axis->widths[0].org
+ : AF_LATIN_CONSTANT( metrics, 50 );
+
+ /* let's try 20% of the smallest width */
+ axis->edge_distance_threshold = stdw / 5;
+ axis->standard_width = stdw;
+ axis->extra_light = 0;
+ }
+ }
+
+ af_glyph_hints_done( hints );
+ }
+
+
+
+#define AF_LATIN_MAX_TEST_CHARACTERS 12
+
+
+ static const char* const af_latin_blue_chars[AF_LATIN_MAX_BLUES] =
+ {
+ "THEZOCQS",
+ "HEZLOCUS",
+ "fijkdbh",
+ "xzroesc",
+ "xzroesc",
+ "pqgjy"
+ };
+
+
+ static void
+ af_latin_metrics_init_blues( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS];
+ FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS];
+ FT_Int num_flats;
+ FT_Int num_rounds;
+ FT_Int bb;
+ AF_LatinBlue blue;
+ FT_Error error;
+ AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
+ FT_GlyphSlot glyph = face->glyph;
+
+
+ /* we compute the blues simply by loading each character from the */
+ /* 'af_latin_blue_chars[blues]' string, then compute its top-most or */
+ /* bottom-most points (depending on `AF_IS_TOP_BLUE') */
+
+ AF_LOG(( "blue zones computation\n" ));
+ AF_LOG(( "------------------------------------------------\n" ));
+
+ for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
+ {
+ const char* p = af_latin_blue_chars[bb];
+ const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS;
+ FT_Pos* blue_ref;
+ FT_Pos* blue_shoot;
+
+
+ AF_LOG(( "blue %3d: ", bb ));
+
+ num_flats = 0;
+ num_rounds = 0;
+
+ for ( ; p < limit && *p; p++ )
+ {
+ FT_UInt glyph_index;
+ FT_Int best_point, best_y, best_first, best_last;
+ FT_Vector* points;
+ FT_Bool round;
+
+
+ AF_LOG(( "'%c'", *p ));
+
+ /* load the character in the face -- skip unknown or empty ones */
+ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
+ if ( glyph_index == 0 )
+ continue;
+
+ error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+ if ( error || glyph->outline.n_points <= 0 )
+ continue;
+
+ /* now compute min or max point indices and coordinates */
+ points = glyph->outline.points;
+ best_point = -1;
+ best_y = 0; /* make compiler happy */
+ best_first = 0; /* ditto */
+ best_last = 0; /* ditto */
+
+ {
+ FT_Int nn;
+ FT_Int first = 0;
+ FT_Int last = -1;
+
+
+ for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ )
+ {
+ FT_Int old_best_point = best_point;
+ FT_Int pp;
+
+
+ last = glyph->outline.contours[nn];
+
+ /* Avoid single-point contours since they are never rasterized. */
+ /* In some fonts, they correspond to mark attachment points */
+ /* which are way outside of the glyph's real outline. */
+ if ( last <= first )
+ continue;
+
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) )
+ {
+ for ( pp = first; pp <= last; pp++ )
+ if ( best_point < 0 || points[pp].y > best_y )
+ {
+ best_point = pp;
+ best_y = points[pp].y;
+ }
+ }
+ else
+ {
+ for ( pp = first; pp <= last; pp++ )
+ if ( best_point < 0 || points[pp].y < best_y )
+ {
+ best_point = pp;
+ best_y = points[pp].y;
+ }
+ }
+
+ if ( best_point != old_best_point )
+ {
+ best_first = first;
+ best_last = last;
+ }
+ }
+ AF_LOG(( "%5d", best_y ));
+ }
+
+ /* now check whether the point belongs to a straight or round */
+ /* segment; we first need to find in which contour the extremum */
+ /* lies, then inspect its previous and next points */
+ if ( best_point >= 0 )
+ {
+ FT_Int prev, next;
+ FT_Pos dist;
+
+
+ /* now look for the previous and next points that are not on the */
+ /* same Y coordinate. Threshold the `closeness'... */
+ prev = best_point;
+ next = prev;
+
+ do
+ {
+ if ( prev > best_first )
+ prev--;
+ else
+ prev = best_last;
+
+ dist = points[prev].y - best_y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ } while ( prev != best_point );
+
+ do
+ {
+ if ( next < best_last )
+ next++;
+ else
+ next = best_first;
+
+ dist = points[next].y - best_y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ } while ( next != best_point );
+
+ /* now, set the `round' flag depending on the segment's kind */
+ round = FT_BOOL(
+ FT_CURVE_TAG( glyph->outline.tags[prev] ) != FT_CURVE_TAG_ON ||
+ FT_CURVE_TAG( glyph->outline.tags[next] ) != FT_CURVE_TAG_ON );
+
+ AF_LOG(( "%c ", round ? 'r' : 'f' ));
+ }
+
+ if ( round )
+ rounds[num_rounds++] = best_y;
+ else
+ flats[num_flats++] = best_y;
+ }
+
+ AF_LOG(( "\n" ));
+
+ if ( num_flats == 0 && num_rounds == 0 )
+ {
+ /*
+ * we couldn't find a single glyph to compute this blue zone,
+ * we will simply ignore it then
+ */
+ AF_LOG(( "empty!\n" ));
+ continue;
+ }
+
+ /* we have computed the contents of the `rounds' and `flats' tables, */
+ /* now determine the reference and overshoot position of the blue -- */
+ /* we simply take the median value after a simple sort */
+ af_sort_pos( num_rounds, rounds );
+ af_sort_pos( num_flats, flats );
+
+ blue = & axis->blues[axis->blue_count];
+ blue_ref = & blue->ref.org;
+ blue_shoot = & blue->shoot.org;
+
+ axis->blue_count++;
+
+ if ( num_flats == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = rounds[num_rounds / 2];
+ }
+ else if ( num_rounds == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = flats[num_flats / 2];
+ }
+ else
+ {
+ *blue_ref = flats[num_flats / 2];
+ *blue_shoot = rounds[num_rounds / 2];
+ }
+
+ /* there are sometimes problems: if the overshoot position of top */
+ /* zones is under its reference position, or the opposite for bottom */
+ /* zones. We must thus check everything there and correct the errors */
+ if ( *blue_shoot != *blue_ref )
+ {
+ FT_Pos ref = *blue_ref;
+ FT_Pos shoot = *blue_shoot;
+ FT_Bool over_ref = FT_BOOL( shoot > ref );
+
+
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref )
+ *blue_shoot = *blue_ref = ( shoot + ref ) / 2;
+ }
+
+ blue->flags = 0;
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) )
+ blue->flags |= AF_LATIN_BLUE_TOP;
+
+ /*
+ * The following flags is used later to adjust the y and x scales
+ * in order to optimize the pixel grid alignment of the top of small
+ * letters.
+ */
+ if ( bb == AF_LATIN_BLUE_SMALL_TOP )
+ blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
+
+ AF_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
+ }
+
+ return;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin_metrics_init( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ FT_Error error = AF_Err_Ok;
+ FT_CharMap oldmap = face->charmap;
+ FT_UInt ee;
+
+ static const FT_Encoding latin_encodings[] =
+ {
+ FT_ENCODING_UNICODE,
+ FT_ENCODING_APPLE_ROMAN,
+ FT_ENCODING_ADOBE_STANDARD,
+ FT_ENCODING_ADOBE_LATIN_1,
+ FT_ENCODING_NONE /* end of list */
+ };
+
+
+ metrics->units_per_em = face->units_per_EM;
+
+ /* do we have a latin charmap in there? */
+ for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ )
+ {
+ error = FT_Select_Charmap( face, latin_encodings[ee] );
+ if ( !error )
+ break;
+ }
+
+ if ( !error )
+ {
+ /* For now, compute the standard width and height from the `o'. */
+ af_latin_metrics_init_widths( metrics, face, 'o' );
+ af_latin_metrics_init_blues( metrics, face );
+ }
+
+ FT_Set_Charmap( face, oldmap );
+ return AF_Err_Ok;
+ }
+
+
+ static void
+ af_latin_metrics_scale_dim( AF_LatinMetrics metrics,
+ AF_Scaler scaler,
+ AF_Dimension dim )
+ {
+ FT_Fixed scale;
+ FT_Pos delta;
+ AF_LatinAxis axis;
+ FT_UInt nn;
+
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ scale = scaler->x_scale;
+ delta = scaler->x_delta;
+ }
+ else
+ {
+ scale = scaler->y_scale;
+ delta = scaler->y_delta;
+ }
+
+ axis = &metrics->axis[dim];
+
+ if ( axis->org_scale == scale && axis->org_delta == delta )
+ return;
+
+ axis->org_scale = scale;
+ axis->org_delta = delta;
+
+ /*
+ * correct X and Y scale to optimize the alignment of the top of small
+ * letters to the pixel grid
+ */
+ {
+ AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT];
+ AF_LatinBlue blue = NULL;
+
+
+ for ( nn = 0; nn < Axis->blue_count; nn++ )
+ {
+ if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
+ {
+ blue = &Axis->blues[nn];
+ break;
+ }
+ }
+
+ if ( blue )
+ {
+ FT_Pos scaled = FT_MulFix( blue->shoot.org, scaler->y_scale );
+ FT_Pos fitted = ( scaled + 40 ) & ~63;
+
+
+ if ( scaled != fitted )
+ {
+#if 0
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ if ( fitted < scaled )
+ scale -= scale / 50; /* scale *= 0.98 */
+ }
+ else
+#endif
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ scale = FT_MulDiv( scale, fitted, scaled );
+ }
+ }
+ }
+ }
+
+ axis->scale = scale;
+ axis->delta = delta;
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ metrics->root.scaler.x_scale = scale;
+ metrics->root.scaler.x_delta = delta;
+ }
+ else
+ {
+ metrics->root.scaler.y_scale = scale;
+ metrics->root.scaler.y_delta = delta;
+ }
+
+ /* scale the standard widths */
+ for ( nn = 0; nn < axis->width_count; nn++ )
+ {
+ AF_Width width = axis->widths + nn;
+
+
+ width->cur = FT_MulFix( width->org, scale );
+ width->fit = width->cur;
+ }
+
+ /* an extra-light axis corresponds to a standard width that is */
+ /* smaller than 0.75 pixels */
+ axis->extra_light =
+ (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
+
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ /* scale the blue zones */
+ for ( nn = 0; nn < axis->blue_count; nn++ )
+ {
+ AF_LatinBlue blue = &axis->blues[nn];
+ FT_Pos dist;
+
+
+ blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta;
+ blue->ref.fit = blue->ref.cur;
+ blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
+ blue->shoot.fit = blue->shoot.cur;
+ blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
+
+ /* a blue zone is only active if it is less than 3/4 pixels tall */
+ dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
+ if ( dist <= 48 && dist >= -48 )
+ {
+ FT_Pos delta1, delta2;
+
+
+ delta1 = blue->shoot.org - blue->ref.org;
+ delta2 = delta1;
+ if ( delta1 < 0 )
+ delta2 = -delta2;
+
+ delta2 = FT_MulFix( delta2, scale );
+
+ if ( delta2 < 32 )
+ delta2 = 0;
+ else if ( delta2 < 64 )
+ delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
+ else
+ delta2 = FT_PIX_ROUND( delta2 );
+
+ if ( delta1 < 0 )
+ delta2 = -delta2;
+
+ blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
+ blue->shoot.fit = blue->ref.fit + delta2;
+
+ blue->flags |= AF_LATIN_BLUE_ACTIVE;
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin_metrics_scale( AF_LatinMetrics metrics,
+ AF_Scaler scaler )
+ {
+ metrics->root.scaler.render_mode = scaler->render_mode;
+ metrics->root.scaler.face = scaler->face;
+
+ af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
+ af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L Y P H A N A L Y S I S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin_hints_compute_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ FT_Memory memory = hints->memory;
+ FT_Error error = AF_Err_Ok;
+ AF_Segment segment = NULL;
+ AF_SegmentRec seg0;
+ AF_Point* contour = hints->contours;
+ AF_Point* contour_limit = contour + hints->num_contours;
+ AF_Direction major_dir, segment_dir;
+
+
+ FT_ZERO( &seg0 );
+ seg0.score = 32000;
+ seg0.flags = AF_EDGE_NORMAL;
+
+ major_dir = (AF_Direction)FT_ABS( axis->major_dir );
+ segment_dir = major_dir;
+
+ axis->num_segments = 0;
+
+ /* set up (u,v) in each point */
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ AF_Point point = hints->points;
+ AF_Point limit = point + hints->num_points;
+
+
+ for ( ; point < limit; point++ )
+ {
+ point->u = point->fx;
+ point->v = point->fy;
+ }
+ }
+ else
+ {
+ AF_Point point = hints->points;
+ AF_Point limit = point + hints->num_points;
+
+
+ for ( ; point < limit; point++ )
+ {
+ point->u = point->fy;
+ point->v = point->fx;
+ }
+ }
+
+ /* do each contour separately */
+ for ( ; contour < contour_limit; contour++ )
+ {
+ AF_Point point = contour[0];
+ AF_Point last = point->prev;
+ int on_edge = 0;
+ FT_Pos min_pos = 32000; /* minimum segment pos != min_coord */
+ FT_Pos max_pos = -32000; /* maximum segment pos != max_coord */
+ FT_Bool passed;
+
+
+ if ( point == last ) /* skip singletons -- just in case */
+ continue;
+
+ if ( FT_ABS( last->out_dir ) == major_dir &&
+ FT_ABS( point->out_dir ) == major_dir )
+ {
+ /* we are already on an edge, try to locate its start */
+ last = point;
+
+ for (;;)
+ {
+ point = point->prev;
+ if ( FT_ABS( point->out_dir ) != major_dir )
+ {
+ point = point->next;
+ break;
+ }
+ if ( point == last )
+ break;
+ }
+ }
+
+ last = point;
+ passed = 0;
+
+ for (;;)
+ {
+ FT_Pos u, v;
+
+
+ if ( on_edge )
+ {
+ u = point->u;
+ if ( u < min_pos )
+ min_pos = u;
+ if ( u > max_pos )
+ max_pos = u;
+
+ if ( point->out_dir != segment_dir || point == last )
+ {
+ /* we are just leaving an edge; record a new segment! */
+ segment->last = point;
+ segment->pos = (FT_Short)( ( min_pos + max_pos ) >> 1 );
+
+ /* a segment is round if either its first or last point */
+ /* is a control point */
+ if ( ( segment->first->flags | point->flags ) &
+ AF_FLAG_CONTROL )
+ segment->flags |= AF_EDGE_ROUND;
+
+ /* compute segment size */
+ min_pos = max_pos = point->v;
+
+ v = segment->first->v;
+ if ( v < min_pos )
+ min_pos = v;
+ if ( v > max_pos )
+ max_pos = v;
+
+ segment->min_coord = (FT_Short)min_pos;
+ segment->max_coord = (FT_Short)max_pos;
+ segment->height = (FT_Short)( segment->max_coord -
+ segment->min_coord );
+
+ on_edge = 0;
+ segment = NULL;
+ /* fallthrough */
+ }
+ }
+
+ /* now exit if we are at the start/end point */
+ if ( point == last )
+ {
+ if ( passed )
+ break;
+ passed = 1;
+ }
+
+ if ( !on_edge && FT_ABS( point->out_dir ) == major_dir )
+ {
+ /* this is the start of a new segment! */
+ segment_dir = (AF_Direction)point->out_dir;
+
+ /* clear all segment fields */
+ error = af_axis_hints_new_segment( axis, memory, &segment );
+ if ( error )
+ goto Exit;
+
+ segment[0] = seg0;
+ segment->dir = (FT_Char)segment_dir;
+ min_pos = max_pos = point->u;
+ segment->first = point;
+ segment->last = point;
+ segment->contour = contour;
+ on_edge = 1;
+ }
+
+ point = point->next;
+ }
+
+ } /* contours */
+
+
+ /* now slightly increase the height of segments when this makes */
+ /* sense -- this is used to better detect and ignore serifs */
+ {
+ AF_Segment segments = axis->segments;
+ AF_Segment segments_end = segments + axis->num_segments;
+
+
+ for ( segment = segments; segment < segments_end; segment++ )
+ {
+ AF_Point first = segment->first;
+ AF_Point last = segment->last;
+ FT_Pos first_v = first->v;
+ FT_Pos last_v = last->v;
+
+
+ if ( first == last )
+ continue;
+
+ if ( first_v < last_v )
+ {
+ AF_Point p;
+
+
+ p = first->prev;
+ if ( p->v < first_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( first_v - p->v ) >> 1 ) );
+
+ p = last->next;
+ if ( p->v > last_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( p->v - last_v ) >> 1 ) );
+ }
+ else
+ {
+ AF_Point p;
+
+
+ p = first->prev;
+ if ( p->v > first_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( p->v - first_v ) >> 1 ) );
+
+ p = last->next;
+ if ( p->v < last_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( last_v - p->v ) >> 1 ) );
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin_hints_link_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ FT_Pos len_threshold, len_score;
+ AF_Segment seg1, seg2;
+
+
+ len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
+ if ( len_threshold == 0 )
+ len_threshold = 1;
+
+ len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
+
+ /* now compare each segment to the others */
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ /* the fake segments are introduced to hint the metrics -- */
+ /* we must never link them to anything */
+ if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
+ continue;
+
+ for ( seg2 = segments; seg2 < segment_limit; seg2++ )
+ if ( seg1->dir + seg2->dir == 0 && seg2->pos > seg1->pos )
+ {
+ FT_Pos pos1 = seg1->pos;
+ FT_Pos pos2 = seg2->pos;
+ FT_Pos dist = pos2 - pos1;
+
+
+ if ( dist < 0 )
+ dist = -dist;
+
+ {
+ FT_Pos min = seg1->min_coord;
+ FT_Pos max = seg1->max_coord;
+ FT_Pos len, score;
+
+
+ if ( min < seg2->min_coord )
+ min = seg2->min_coord;
+
+ if ( max > seg2->max_coord )
+ max = seg2->max_coord;
+
+ len = max - min;
+ if ( len >= len_threshold )
+ {
+ score = dist + len_score / len;
+
+ if ( score < seg1->score )
+ {
+ seg1->score = score;
+ seg1->link = seg2;
+ }
+
+ if ( score < seg2->score )
+ {
+ seg2->score = score;
+ seg2->link = seg1;
+ }
+ }
+ }
+ }
+ }
+
+ /* now, compute the `serif' segments */
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ seg2 = seg1->link;
+
+ if ( seg2 )
+ {
+ if ( seg2->link != seg1 )
+ {
+ seg1->link = 0;
+ seg1->serif = seg2->link;
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin_hints_compute_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ FT_Error error = AF_Err_Ok;
+ FT_Memory memory = hints->memory;
+ AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
+
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ AF_Segment seg;
+
+ AF_Direction up_dir;
+ FT_Fixed scale;
+ FT_Pos edge_distance_threshold;
+ FT_Pos segment_length_threshold;
+
+
+ axis->num_edges = 0;
+
+ scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
+ : hints->y_scale;
+
+ up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
+ : AF_DIR_RIGHT;
+
+ /*
+ * We ignore all segments that are less than 1 pixels in length,
+ * to avoid many problems with serif fonts. We compute the
+ * corresponding threshold in font units.
+ */
+ if ( dim == AF_DIMENSION_HORZ )
+ segment_length_threshold = FT_DivFix( 64, hints->y_scale );
+ else
+ segment_length_threshold = 0;
+
+ /*********************************************************************/
+ /* */
+ /* We will begin by generating a sorted table of edges for the */
+ /* current direction. To do so, we simply scan each segment and try */
+ /* to find an edge in our table that corresponds to its position. */
+ /* */
+ /* If no edge is found, we create and insert a new edge in the */
+ /* sorted table. Otherwise, we simply add the segment to the edge's */
+ /* list which will be processed in the second step to compute the */
+ /* edge's properties. */
+ /* */
+ /* Note that the edges table is sorted along the segment/edge */
+ /* position. */
+ /* */
+ /*********************************************************************/
+
+ edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
+ scale );
+ if ( edge_distance_threshold > 64 / 4 )
+ edge_distance_threshold = 64 / 4;
+
+ edge_distance_threshold = FT_DivFix( edge_distance_threshold,
+ scale );
+
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Edge found = 0;
+ FT_Int ee;
+
+
+ if ( seg->height < segment_length_threshold )
+ continue;
+
+ /* A special case for serif edges: If they are smaller than */
+ /* 1.5 pixels we ignore them. */
+ if ( seg->serif &&
+ 2 * seg->height < 3 * segment_length_threshold )
+ continue;
+
+ /* look for an edge corresponding to the segment */
+ for ( ee = 0; ee < axis->num_edges; ee++ )
+ {
+ AF_Edge edge = axis->edges + ee;
+ FT_Pos dist;
+
+
+ dist = seg->pos - edge->fpos;
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( dist < edge_distance_threshold && edge->dir == seg->dir )
+ {
+ found = edge;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ AF_Edge edge;
+
+
+ /* insert a new edge in the list and */
+ /* sort according to the position */
+ error = af_axis_hints_new_edge( axis, seg->pos, seg->dir, memory, &edge );
+ if ( error )
+ goto Exit;
+
+ /* add the segment to the new edge's list */
+ FT_ZERO( edge );
+
+ edge->first = seg;
+ edge->last = seg;
+ edge->fpos = seg->pos;
+ edge->dir = seg->dir;
+ edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
+ seg->edge_next = seg;
+ }
+ else
+ {
+ /* if an edge was found, simply add the segment to the edge's */
+ /* list */
+ seg->edge_next = found->first;
+ found->last->edge_next = seg;
+ found->last = seg;
+ }
+ }
+
+
+ /*********************************************************************/
+ /* */
+ /* Good, we will now compute each edge's properties according to */
+ /* segments found on its position. Basically, these are: */
+ /* */
+ /* - edge's main direction */
+ /* - stem edge, serif edge or both (which defaults to stem then) */
+ /* - rounded edge, straight or both (which defaults to straight) */
+ /* - link for edge */
+ /* */
+ /*********************************************************************/
+
+ /* first of all, set the `edge' field in each segment -- this is */
+ /* required in order to compute edge links */
+
+ /*
+ * Note that removing this loop and setting the `edge' field of each
+ * segment directly in the code above slows down execution speed for
+ * some reasons on platforms like the Sun.
+ */
+ {
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ AF_Edge edge;
+
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ seg = edge->first;
+ if ( seg )
+ do
+ {
+ seg->edge = edge;
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+ }
+
+ /* now, compute each edge properties */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Int is_round = 0; /* does it contain round segments? */
+ FT_Int is_straight = 0; /* does it contain straight segments? */
+ FT_Pos ups = 0; /* number of upwards segments */
+ FT_Pos downs = 0; /* number of downwards segments */
+
+
+ seg = edge->first;
+
+ do
+ {
+ FT_Bool is_serif;
+
+
+ /* check for roundness of segment */
+ if ( seg->flags & AF_EDGE_ROUND )
+ is_round++;
+ else
+ is_straight++;
+
+ /* check for segment direction */
+ if ( seg->dir == up_dir )
+ ups += seg->max_coord-seg->min_coord;
+ else
+ downs += seg->max_coord-seg->min_coord;
+
+ /* check for links -- if seg->serif is set, then seg->link must */
+ /* be ignored */
+ is_serif = (FT_Bool)( seg->serif &&
+ seg->serif->edge &&
+ seg->serif->edge != edge );
+
+ if ( ( seg->link && seg->link->edge != NULL ) || is_serif )
+ {
+ AF_Edge edge2;
+ AF_Segment seg2;
+
+
+ edge2 = edge->link;
+ seg2 = seg->link;
+
+ if ( is_serif )
+ {
+ seg2 = seg->serif;
+ edge2 = edge->serif;
+ }
+
+ if ( edge2 )
+ {
+ FT_Pos edge_delta;
+ FT_Pos seg_delta;
+
+
+ edge_delta = edge->fpos - edge2->fpos;
+ if ( edge_delta < 0 )
+ edge_delta = -edge_delta;
+
+ seg_delta = seg->pos - seg2->pos;
+ if ( seg_delta < 0 )
+ seg_delta = -seg_delta;
+
+ if ( seg_delta < edge_delta )
+ edge2 = seg2->edge;
+ }
+ else
+ edge2 = seg2->edge;
+
+ if ( is_serif )
+ {
+ edge->serif = edge2;
+ edge2->flags |= AF_EDGE_SERIF;
+ }
+ else
+ edge->link = edge2;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+
+ /* set the round/straight flags */
+ edge->flags = AF_EDGE_NORMAL;
+
+ if ( is_round > 0 && is_round >= is_straight )
+ edge->flags |= AF_EDGE_ROUND;
+
+#if 0
+ /* set the edge's main direction */
+ edge->dir = AF_DIR_NONE;
+
+ if ( ups > downs )
+ edge->dir = (FT_Char)up_dir;
+
+ else if ( ups < downs )
+ edge->dir = (FT_Char)-up_dir;
+
+ else if ( ups == downs )
+ edge->dir = 0; /* both up and down! */
+#endif
+
+ /* gets rid of serifs if link is set */
+ /* XXX: This gets rid of many unpleasant artefacts! */
+ /* Example: the `c' in cour.pfa at size 13 */
+
+ if ( edge->serif && edge->link )
+ edge->serif = 0;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin_hints_detect_features( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ FT_Error error;
+
+
+ error = af_latin_hints_compute_segments( hints, dim );
+ if ( !error )
+ {
+ af_latin_hints_link_segments( hints, dim );
+
+ error = af_latin_hints_compute_edges( hints, dim );
+ }
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin_hints_compute_blue_edges( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ AF_AxisHints axis = &hints->axis[ AF_DIMENSION_VERT ];
+ AF_Edge edge = axis->edges;
+ AF_Edge edge_limit = edge + axis->num_edges;
+ AF_LatinAxis latin = &metrics->axis[ AF_DIMENSION_VERT ];
+ FT_Fixed scale = latin->scale;
+
+
+ /* compute which blue zones are active, i.e. have their scaled */
+ /* size < 3/4 pixels */
+
+ /* for each horizontal edge search the blue zone which is closest */
+ for ( ; edge < edge_limit; edge++ )
+ {
+ FT_Int bb;
+ AF_Width best_blue = NULL;
+ FT_Pos best_dist; /* initial threshold */
+
+
+ /* compute the initial threshold as a fraction of the EM size */
+ best_dist = FT_MulFix( metrics->units_per_em / 40, scale );
+
+ if ( best_dist > 64 / 2 )
+ best_dist = 64 / 2;
+
+ for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
+ {
+ AF_LatinBlue blue = latin->blues + bb;
+ FT_Bool is_top_blue, is_major_dir;
+
+
+ /* skip inactive blue zones (i.e., those that are too small) */
+ if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
+ continue;
+
+ /* if it is a top zone, check for right edges -- if it is a bottom */
+ /* zone, check for left edges */
+ /* */
+ /* of course, that's for TrueType */
+ is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
+ is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
+
+ /* if it is a top zone, the edge must be against the major */
+ /* direction; if it is a bottom zone, it must be in the major */
+ /* direction */
+ if ( is_top_blue ^ is_major_dir )
+ {
+ FT_Pos dist;
+
+
+ /* first of all, compare it to the reference position */
+ dist = edge->fpos - blue->ref.org;
+ if ( dist < 0 )
+ dist = -dist;
+
+ dist = FT_MulFix( dist, scale );
+ if ( dist < best_dist )
+ {
+ best_dist = dist;
+ best_blue = & blue->ref;
+ }
+
+ /* now, compare it to the overshoot position if the edge is */
+ /* rounded, and if the edge is over the reference position of a */
+ /* top zone, or under the reference position of a bottom zone */
+ if ( edge->flags & AF_EDGE_ROUND && dist != 0 )
+ {
+ FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
+
+
+ if ( is_top_blue ^ is_under_ref )
+ {
+ blue = latin->blues + bb;
+ dist = edge->fpos - blue->shoot.org;
+ if ( dist < 0 )
+ dist = -dist;
+
+ dist = FT_MulFix( dist, scale );
+ if ( dist < best_dist )
+ {
+ best_dist = dist;
+ best_blue = & blue->shoot;
+ }
+ }
+ }
+ }
+ }
+
+ if ( best_blue )
+ edge->blue_edge = best_blue;
+ }
+ }
+
+
+ static FT_Error
+ af_latin_hints_init( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ FT_Render_Mode mode;
+ FT_UInt32 scaler_flags, other_flags;
+ FT_Face face = metrics->root.scaler.face;
+
+
+ af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
+
+ /*
+ * correct x_scale and y_scale if needed, since they may have
+ * been modified `af_latin_metrics_scale_dim' above
+ */
+ hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
+ hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
+ hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
+ hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
+
+ /* compute flags depending on render mode, etc. */
+ mode = metrics->root.scaler.render_mode;
+
+#if 0 /* #ifdef AF_USE_WARPER */
+ if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
+ {
+ metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
+ }
+#endif
+
+ scaler_flags = hints->scaler_flags;
+ other_flags = 0;
+
+ /*
+ * We snap the width of vertical stems for the monochrome and
+ * horizontal LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
+ other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
+
+ /*
+ * We snap the width of horizontal stems for the monochrome and
+ * vertical LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
+ other_flags |= AF_LATIN_HINTS_VERT_SNAP;
+
+ /*
+ * We adjust stems to full pixels only if we don't use the `light' mode.
+ */
+ if ( mode != FT_RENDER_MODE_LIGHT )
+ other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
+
+ if ( mode == FT_RENDER_MODE_MONO )
+ other_flags |= AF_LATIN_HINTS_MONO;
+
+ /*
+ * In `light' hinting mode we disable horizontal hinting completely.
+ * We also do it if the face is italic.
+ */
+ if ( mode == FT_RENDER_MODE_LIGHT ||
+ (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0 )
+ scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
+
+ hints->scaler_flags = scaler_flags;
+ hints->other_flags = other_flags;
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L Y P H G R I D - F I T T I N G *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* snap a given width in scaled coordinates to one of the */
+ /* current standard widths */
+
+ static FT_Pos
+ af_latin_snap_width( AF_Width widths,
+ FT_Int count,
+ FT_Pos width )
+ {
+ int n;
+ FT_Pos best = 64 + 32 + 2;
+ FT_Pos reference = width;
+ FT_Pos scaled;
+
+
+ for ( n = 0; n < count; n++ )
+ {
+ FT_Pos w;
+ FT_Pos dist;
+
+
+ w = widths[n].cur;
+ dist = width - w;
+ if ( dist < 0 )
+ dist = -dist;
+ if ( dist < best )
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+
+ scaled = FT_PIX_ROUND( reference );
+
+ if ( width >= reference )
+ {
+ if ( width < scaled + 48 )
+ width = reference;
+ }
+ else
+ {
+ if ( width > scaled - 48 )
+ width = reference;
+ }
+
+ return width;
+ }
+
+
+ /* compute the snapped width of a given stem */
+
+ static FT_Pos
+ af_latin_compute_stem_width( AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Pos width,
+ AF_Edge_Flags base_flags,
+ AF_Edge_Flags stem_flags )
+ {
+ AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics;
+ AF_LatinAxis axis = & metrics->axis[dim];
+ FT_Pos dist = width;
+ FT_Int sign = 0;
+ FT_Int vertical = ( dim == AF_DIMENSION_VERT );
+
+
+ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
+ axis->extra_light )
+ return width;
+
+ if ( dist < 0 )
+ {
+ dist = -width;
+ sign = 1;
+ }
+
+ if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
+ ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
+ {
+ /* smooth hinting process: very lightly quantize the stem width */
+
+ /* leave the widths of serifs alone */
+
+ if ( ( stem_flags & AF_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
+ goto Done_Width;
+
+ else if ( ( base_flags & AF_EDGE_ROUND ) )
+ {
+ if ( dist < 80 )
+ dist = 64;
+ }
+ else if ( dist < 56 )
+ dist = 56;
+
+ if ( axis->width_count > 0 )
+ {
+ FT_Pos delta;
+
+
+ /* compare to standard width */
+ if ( axis->width_count > 0 )
+ {
+ delta = dist - axis->widths[0].cur;
+
+ if ( delta < 0 )
+ delta = -delta;
+
+ if ( delta < 40 )
+ {
+ dist = axis->widths[0].cur;
+ if ( dist < 48 )
+ dist = 48;
+
+ goto Done_Width;
+ }
+ }
+
+ if ( dist < 3 * 64 )
+ {
+ delta = dist & 63;
+ dist &= -64;
+
+ if ( delta < 10 )
+ dist += delta;
+
+ else if ( delta < 32 )
+ dist += 10;
+
+ else if ( delta < 54 )
+ dist += 54;
+
+ else
+ dist += delta;
+ }
+ else
+ dist = ( dist + 32 ) & ~63;
+ }
+ }
+ else
+ {
+ /* strong hinting process: snap the stem width to integer pixels */
+ FT_Pos org_dist = dist;
+
+
+ dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
+
+ if ( vertical )
+ {
+ /* in the case of vertical hinting, always round */
+ /* the stem heights to integer pixels */
+
+ if ( dist >= 64 )
+ dist = ( dist + 16 ) & ~63;
+ else
+ dist = 64;
+ }
+ else
+ {
+ if ( AF_LATIN_HINTS_DO_MONO( hints ) )
+ {
+ /* monochrome horizontal hinting: snap widths to integer pixels */
+ /* with a different threshold */
+
+ if ( dist < 64 )
+ dist = 64;
+ else
+ dist = ( dist + 32 ) & ~63;
+ }
+ else
+ {
+ /* for horizontal anti-aliased hinting, we adopt a more subtle */
+ /* approach: we strengthen small stems, round stems whose size */
+ /* is between 1 and 2 pixels to an integer, otherwise nothing */
+
+ if ( dist < 48 )
+ dist = ( dist + 64 ) >> 1;
+
+ else if ( dist < 128 )
+ {
+ /* We only round to an integer width if the corresponding */
+ /* distortion is less than 1/4 pixel. Otherwise this */
+ /* makes everything worse since the diagonals, which are */
+ /* not hinted, appear a lot bolder or thinner than the */
+ /* vertical stems. */
+
+ FT_Int delta;
+
+
+ dist = ( dist + 22 ) & ~63;
+ delta = dist - org_dist;
+ if ( delta < 0 )
+ delta = -delta;
+
+ if (delta >= 16)
+ {
+ dist = org_dist;
+ if ( dist < 48 )
+ dist = ( dist + 64 ) >> 1;
+ }
+ }
+ else
+ /* round otherwise to prevent color fringes in LCD mode */
+ dist = ( dist + 32 ) & ~63;
+ }
+ }
+ }
+
+ Done_Width:
+ if ( sign )
+ dist = -dist;
+
+ return dist;
+ }
+
+
+ /* align one stem edge relative to the previous stem edge */
+
+ static void
+ af_latin_align_linked_edge( AF_GlyphHints hints,
+ AF_Dimension dim,
+ AF_Edge base_edge,
+ AF_Edge stem_edge )
+ {
+ FT_Pos dist = stem_edge->opos - base_edge->opos;
+
+ FT_Pos fitted_width = af_latin_compute_stem_width(
+ hints, dim, dist,
+ (AF_Edge_Flags)base_edge->flags,
+ (AF_Edge_Flags)stem_edge->flags );
+
+
+ stem_edge->pos = base_edge->pos + fitted_width;
+
+ AF_LOG(( "LINK: edge %d (opos=%.2f) linked to (%.2f), "
+ "dist was %.2f, now %.2f\n",
+ stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0,
+ stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
+ }
+
+
+ static void
+ af_latin_align_serif_edge( AF_GlyphHints hints,
+ AF_Edge base,
+ AF_Edge serif )
+ {
+ FT_UNUSED( hints );
+
+ serif->pos = base->pos + (serif->opos - base->opos);
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** E D G E H I N T I N G ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ af_latin_hint_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ FT_Int n_edges;
+ AF_Edge edge;
+ AF_Edge anchor = 0;
+ FT_Int has_serifs = 0;
+
+
+ /* we begin by aligning all stems relative to the blue zone */
+ /* if needed -- that's only for horizontal edges */
+
+ if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
+ {
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Width blue;
+ AF_Edge edge1, edge2;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ blue = edge->blue_edge;
+ edge1 = NULL;
+ edge2 = edge->link;
+
+ if ( blue )
+ {
+ edge1 = edge;
+ }
+ else if ( edge2 && edge2->blue_edge )
+ {
+ blue = edge2->blue_edge;
+ edge1 = edge2;
+ edge2 = edge;
+ }
+
+ if ( !edge1 )
+ continue;
+
+ AF_LOG(( "BLUE: edge %d (opos=%.2f) snapped to (%.2f), "
+ "was (%.2f)\n",
+ edge1-edges, edge1->opos / 64.0, blue->fit / 64.0,
+ edge1->pos / 64.0 ));
+
+ edge1->pos = blue->fit;
+ edge1->flags |= AF_EDGE_DONE;
+
+ if ( edge2 && !edge2->blue_edge )
+ {
+ af_latin_align_linked_edge( hints, dim, edge1, edge2 );
+ edge2->flags |= AF_EDGE_DONE;
+ }
+
+ if ( !anchor )
+ anchor = edge;
+ }
+ }
+
+ /* now we will align all stem edges, trying to maintain the */
+ /* relative order of stems in the glyph */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Edge edge2;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ /* skip all non-stem edges */
+ edge2 = edge->link;
+ if ( !edge2 )
+ {
+ has_serifs++;
+ continue;
+ }
+
+ /* now align the stem */
+
+ /* this should not happen, but it's better to be safe */
+ if ( edge2->blue_edge )
+ {
+ AF_LOG(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
+
+ af_latin_align_linked_edge( hints, dim, edge2, edge );
+ edge->flags |= AF_EDGE_DONE;
+ continue;
+ }
+
+ if ( !anchor )
+ {
+ FT_Pos org_len, org_center, cur_len;
+ FT_Pos cur_pos1, error1, error2, u_off, d_off;
+
+
+ org_len = edge2->opos - edge->opos;
+ cur_len = af_latin_compute_stem_width(
+ hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+ if ( cur_len <= 64 )
+ u_off = d_off = 32;
+ else
+ {
+ u_off = 38;
+ d_off = 26;
+ }
+
+ if ( cur_len < 96 )
+ {
+ org_center = edge->opos + ( org_len >> 1 );
+
+ cur_pos1 = FT_PIX_ROUND( org_center );
+
+ error1 = org_center - ( cur_pos1 - u_off );
+ if ( error1 < 0 )
+ error1 = -error1;
+
+ error2 = org_center - ( cur_pos1 + d_off );
+ if ( error2 < 0 )
+ error2 = -error2;
+
+ if ( error1 < error2 )
+ cur_pos1 -= u_off;
+ else
+ cur_pos1 += d_off;
+
+ edge->pos = cur_pos1 - cur_len / 2;
+ edge2->pos = edge->pos + cur_len;
+ }
+ else
+ edge->pos = FT_PIX_ROUND( edge->opos );
+
+ AF_LOG(( "ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f) "
+ "snapped to (%.2f) (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge2-edges, edge2->opos / 64.0,
+ edge->pos / 64.0, edge2->pos / 64.0 ));
+ anchor = edge;
+
+ edge->flags |= AF_EDGE_DONE;
+
+ af_latin_align_linked_edge( hints, dim, edge, edge2 );
+ }
+ else
+ {
+ FT_Pos org_pos, org_len, org_center, cur_len;
+ FT_Pos cur_pos1, cur_pos2, delta1, delta2;
+
+
+ org_pos = anchor->pos + ( edge->opos - anchor->opos );
+ org_len = edge2->opos - edge->opos;
+ org_center = org_pos + ( org_len >> 1 );
+
+ cur_len = af_latin_compute_stem_width(
+ hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+
+ if ( edge2->flags & AF_EDGE_DONE )
+ edge->pos = edge2->pos - cur_len;
+
+ else if ( cur_len < 96 )
+ {
+ FT_Pos u_off, d_off;
+
+
+ cur_pos1 = FT_PIX_ROUND( org_center );
+
+ if (cur_len <= 64 )
+ u_off = d_off = 32;
+ else
+ {
+ u_off = 38;
+ d_off = 26;
+ }
+
+ delta1 = org_center - ( cur_pos1 - u_off );
+ if ( delta1 < 0 )
+ delta1 = -delta1;
+
+ delta2 = org_center - ( cur_pos1 + d_off );
+ if ( delta2 < 0 )
+ delta2 = -delta2;
+
+ if ( delta1 < delta2 )
+ cur_pos1 -= u_off;
+ else
+ cur_pos1 += d_off;
+
+ edge->pos = cur_pos1 - cur_len / 2;
+ edge2->pos = cur_pos1 + cur_len / 2;
+
+ AF_LOG(( "STEM: %d (opos=%.2f) to %d (opos=%.2f) "
+ "snapped to (%.2f) and (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge2-edges, edge2->opos / 64.0,
+ edge->pos / 64.0, edge2->pos / 64.0 ));
+ }
+ else
+ {
+ org_pos = anchor->pos + ( edge->opos - anchor->opos );
+ org_len = edge2->opos - edge->opos;
+ org_center = org_pos + ( org_len >> 1 );
+
+ cur_len = af_latin_compute_stem_width(
+ hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+
+ cur_pos1 = FT_PIX_ROUND( org_pos );
+ delta1 = cur_pos1 + ( cur_len >> 1 ) - org_center;
+ if ( delta1 < 0 )
+ delta1 = -delta1;
+
+ cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
+ delta2 = cur_pos2 + ( cur_len >> 1 ) - org_center;
+ if ( delta2 < 0 )
+ delta2 = -delta2;
+
+ edge->pos = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
+ edge2->pos = edge->pos + cur_len;
+
+ AF_LOG(( "STEM: %d (opos=%.2f) to %d (opos=%.2f) "
+ "snapped to (%.2f) and (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge2-edges, edge2->opos / 64.0,
+ edge->pos / 64.0, edge2->pos / 64.0 ));
+ }
+
+ edge->flags |= AF_EDGE_DONE;
+ edge2->flags |= AF_EDGE_DONE;
+
+ if ( edge > edges && edge->pos < edge[-1].pos )
+ {
+ AF_LOG(( "BOUND: %d (pos=%.2f) to (%.2f)\n",
+ edge-edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
+ edge->pos = edge[-1].pos;
+ }
+ }
+ }
+
+ /* make sure that lowercase m's maintain their symmetry */
+
+ /* In general, lowercase m's have six vertical edges if they are sans */
+ /* serif, or twelve if they are with serifs. This implementation is */
+ /* based on that assumption, and seems to work very well with most */
+ /* faces. However, if for a certain face this assumption is not */
+ /* true, the m is just rendered like before. In addition, any stem */
+ /* correction will only be applied to symmetrical glyphs (even if the */
+ /* glyph is not an m), so the potential for unwanted distortion is */
+ /* relatively low. */
+
+ /* We don't handle horizontal edges since we can't easily assure that */
+ /* the third (lowest) stem aligns with the base line; it might end up */
+ /* one pixel higher or lower. */
+
+ n_edges = edge_limit - edges;
+ if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
+ {
+ AF_Edge edge1, edge2, edge3;
+ FT_Pos dist1, dist2, span, delta;
+
+
+ if ( n_edges == 6 )
+ {
+ edge1 = edges;
+ edge2 = edges + 2;
+ edge3 = edges + 4;
+ }
+ else
+ {
+ edge1 = edges + 1;
+ edge2 = edges + 5;
+ edge3 = edges + 9;
+ }
+
+ dist1 = edge2->opos - edge1->opos;
+ dist2 = edge3->opos - edge2->opos;
+
+ span = dist1 - dist2;
+ if ( span < 0 )
+ span = -span;
+
+ if ( span < 8 )
+ {
+ delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
+ edge3->pos -= delta;
+ if ( edge3->link )
+ edge3->link->pos -= delta;
+
+ /* move the serifs along with the stem */
+ if ( n_edges == 12 )
+ {
+ ( edges + 8 )->pos -= delta;
+ ( edges + 11 )->pos -= delta;
+ }
+
+ edge3->flags |= AF_EDGE_DONE;
+ if ( edge3->link )
+ edge3->link->flags |= AF_EDGE_DONE;
+ }
+ }
+
+ if ( has_serifs || !anchor )
+ {
+ /*
+ * now hint the remaining edges (serifs and single) in order
+ * to complete our processing
+ */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Pos delta;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ delta = 1000;
+
+ if ( edge->serif )
+ {
+ delta = edge->serif->opos - edge->opos;
+ if ( delta < 0 )
+ delta = -delta;
+ }
+
+ if ( delta < 64 + 16 )
+ {
+ af_latin_align_serif_edge( hints, edge->serif, edge );
+ AF_LOG(( "SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f) "
+ "aligned to (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge->serif - edges, edge->serif->opos / 64.0,
+ edge->pos / 64.0 ));
+ }
+ else if ( !anchor )
+ {
+ AF_LOG(( "SERIF_ANCHOR: edge %d (opos=%.2f) snapped to (%.2f)\n",
+ edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
+ edge->pos = FT_PIX_ROUND( edge->opos );
+ anchor = edge;
+ }
+ else
+ {
+ AF_Edge before, after;
+
+
+ for ( before = edge - 1; before >= edges; before-- )
+ if ( before->flags & AF_EDGE_DONE )
+ break;
+
+ for ( after = edge + 1; after < edge_limit; after++ )
+ if ( after->flags & AF_EDGE_DONE )
+ break;
+
+ if ( before >= edges && before < edge &&
+ after < edge_limit && after > edge )
+ {
+ edge->pos = before->pos +
+ FT_MulDiv( edge->opos - before->opos,
+ after->pos - before->pos,
+ after->opos - before->opos );
+ AF_LOG(( "SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f) "
+ "from %d (opos=%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge->pos / 64.0, before - edges,
+ before->opos / 64.0 ));
+ }
+ else
+ {
+ edge->pos = anchor->pos +
+ ( ( edge->opos - anchor->opos + 16 ) & ~31 );
+ AF_LOG(( "SERIF_LINK2: edge %d (opos=%.2f) snapped to (%.2f)\n",
+ edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
+ }
+ }
+
+ edge->flags |= AF_EDGE_DONE;
+
+ if ( edge > edges && edge->pos < edge[-1].pos )
+ edge->pos = edge[-1].pos;
+
+ if ( edge + 1 < edge_limit &&
+ edge[1].flags & AF_EDGE_DONE &&
+ edge->pos > edge[1].pos )
+ edge->pos = edge[1].pos;
+ }
+ }
+ }
+
+
+ static FT_Error
+ af_latin_hints_apply( AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_LatinMetrics metrics )
+ {
+ FT_Error error;
+ int dim;
+
+
+ error = af_glyph_hints_reload( hints, outline, 1 );
+ if ( error )
+ goto Exit;
+
+ /* analyze glyph outline */
+#ifdef AF_USE_WARPER
+ if ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ||
+ AF_HINTS_DO_HORIZONTAL( hints ) )
+#else
+ if ( AF_HINTS_DO_HORIZONTAL( hints ) )
+#endif
+ {
+ error = af_latin_hints_detect_features( hints, AF_DIMENSION_HORZ );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( AF_HINTS_DO_VERTICAL( hints ) )
+ {
+ error = af_latin_hints_detect_features( hints, AF_DIMENSION_VERT );
+ if ( error )
+ goto Exit;
+
+ af_latin_hints_compute_blue_edges( hints, metrics );
+ }
+
+ /* grid-fit the outline */
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+#ifdef AF_USE_WARPER
+ if ( ( dim == AF_DIMENSION_HORZ &&
+ metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ) )
+ {
+ AF_WarperRec warper;
+ FT_Fixed scale;
+ FT_Pos delta;
+
+
+ af_warper_compute( &warper, hints, dim, &scale, &delta );
+ af_glyph_hints_scale_dim( hints, dim, scale, delta );
+ continue;
+ }
+#endif
+
+ if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
+ ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
+ {
+ af_latin_hint_edges( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
+ }
+ }
+ af_glyph_hints_save( hints, outline );
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N S C R I P T C L A S S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* XXX: this should probably fine tuned to differentiate better between */
+ /* scripts... */
+
+ static const AF_Script_UniRangeRec af_latin_uniranges[] =
+ {
+ { 0x0020, 0x007F }, /* Basic Latin (no control characters) */
+ { 0x00A0, 0x00FF }, /* Latin-1 Supplement (no control characters) */
+ { 0x0100, 0x017F }, /* Latin Extended-A */
+ { 0x0180, 0x024F }, /* Latin Extended-B */
+ { 0x0250, 0x02AF }, /* IPA Extensions */
+ { 0x02B0, 0x02FF }, /* Spacing Modifier Letters */
+ { 0x0300, 0x036F }, /* Combining Diacritical Marks */
+ { 0x0370, 0x03FF }, /* Greek and Coptic */
+ { 0x0400, 0x04FF }, /* Cyrillic */
+ { 0x0500, 0x052F }, /* Cyrillic Supplement */
+ { 0x1D00, 0x1D7F }, /* Phonetic Extensions */
+ { 0x1D80, 0x1DBF }, /* Phonetic Extensions Supplement */
+ { 0x1DC0, 0x1DFF }, /* Combining Diacritical Marks Supplement */
+ { 0x1E00, 0x1EFF }, /* Latin Extended Additional */
+ { 0x1F00, 0x1FFF }, /* Greek Extended */
+ { 0x2000, 0x206F }, /* General Punctuation */
+ { 0x2070, 0x209F }, /* Superscripts and Subscripts */
+ { 0x20A0, 0x20CF }, /* Currency Symbols */
+ { 0x2150, 0x218F }, /* Number Forms */
+ { 0x2460, 0x24FF }, /* Enclosed Alphanumerics */
+ { 0 , 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_latin_script_class =
+ {
+ AF_SCRIPT_LATIN,
+ af_latin_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) af_latin_metrics_init,
+ (AF_Script_ScaleMetricsFunc)af_latin_metrics_scale,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) af_latin_hints_init,
+ (AF_Script_ApplyHintsFunc) af_latin_hints_apply
+ };
+
+
+/* END */
diff --git a/src/autofit/aflatin.h b/src/autofit/aflatin.h
new file mode 100644
index 0000000..3251d37
--- /dev/null
+++ b/src/autofit/aflatin.h
@@ -0,0 +1,209 @@
+/***************************************************************************/
+/* */
+/* aflatin.h */
+/* */
+/* Auto-fitter hinting routines for latin script (specification). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFLATIN_H__
+#define __AFLATIN_H__
+
+#include "afhints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* the latin-specific script class */
+
+ FT_CALLBACK_TABLE const AF_ScriptClassRec
+ af_latin_script_class;
+
+
+/* constants are given with units_per_em == 2048 in mind */
+#define AF_LATIN_CONSTANT( metrics, c ) \
+ ( ( (c) * (FT_Long)( (AF_LatinMetrics)(metrics) )->units_per_em ) / 2048 )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L O B A L M E T R I C S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*
+ * The following declarations could be embedded in the file `aflatin.c';
+ * they have been made semi-public to allow alternate script hinters to
+ * re-use some of them.
+ */
+
+
+ /* Latin (global) metrics management */
+
+ enum
+ {
+ AF_LATIN_BLUE_CAPITAL_TOP,
+ AF_LATIN_BLUE_CAPITAL_BOTTOM,
+ AF_LATIN_BLUE_SMALL_F_TOP,
+ AF_LATIN_BLUE_SMALL_TOP,
+ AF_LATIN_BLUE_SMALL_BOTTOM,
+ AF_LATIN_BLUE_SMALL_MINOR,
+
+ AF_LATIN_BLUE_MAX
+ };
+
+
+#define AF_LATIN_IS_TOP_BLUE( b ) ( (b) == AF_LATIN_BLUE_CAPITAL_TOP || \
+ (b) == AF_LATIN_BLUE_SMALL_F_TOP || \
+ (b) == AF_LATIN_BLUE_SMALL_TOP )
+
+#define AF_LATIN_MAX_WIDTHS 16
+#define AF_LATIN_MAX_BLUES AF_LATIN_BLUE_MAX
+
+
+ enum
+ {
+ AF_LATIN_BLUE_ACTIVE = 1 << 0,
+ AF_LATIN_BLUE_TOP = 1 << 1,
+ AF_LATIN_BLUE_ADJUSTMENT = 1 << 2, /* used for scale adjustment */
+ /* optimization */
+ AF_LATIN_BLUE_FLAG_MAX
+ };
+
+
+ typedef struct AF_LatinBlueRec_
+ {
+ AF_WidthRec ref;
+ AF_WidthRec shoot;
+ FT_UInt flags;
+
+ } AF_LatinBlueRec, *AF_LatinBlue;
+
+
+ typedef struct AF_LatinAxisRec_
+ {
+ FT_Fixed scale;
+ FT_Pos delta;
+
+ FT_UInt width_count;
+ AF_WidthRec widths[AF_LATIN_MAX_WIDTHS];
+ FT_Pos edge_distance_threshold;
+ FT_Pos standard_width;
+ FT_Bool extra_light;
+
+ /* ignored for horizontal metrics */
+ FT_Bool control_overshoot;
+ FT_UInt blue_count;
+ AF_LatinBlueRec blues[AF_LATIN_BLUE_MAX];
+
+ FT_Fixed org_scale;
+ FT_Pos org_delta;
+
+ } AF_LatinAxisRec, *AF_LatinAxis;
+
+
+ typedef struct AF_LatinMetricsRec_
+ {
+ AF_ScriptMetricsRec root;
+ FT_UInt units_per_em;
+ AF_LatinAxisRec axis[AF_DIMENSION_MAX];
+
+ } AF_LatinMetricsRec, *AF_LatinMetrics;
+
+
+ FT_LOCAL( FT_Error )
+ af_latin_metrics_init( AF_LatinMetrics metrics,
+ FT_Face face );
+
+ FT_LOCAL( void )
+ af_latin_metrics_scale( AF_LatinMetrics metrics,
+ AF_Scaler scaler );
+
+ FT_LOCAL( void )
+ af_latin_metrics_init_widths( AF_LatinMetrics metrics,
+ FT_Face face,
+ FT_ULong charcode );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L Y P H A N A L Y S I S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ enum
+ {
+ AF_LATIN_HINTS_HORZ_SNAP = 1 << 0, /* enable stem width snapping */
+ AF_LATIN_HINTS_VERT_SNAP = 1 << 1, /* enable stem height snapping */
+ AF_LATIN_HINTS_STEM_ADJUST = 1 << 2, /* enable stem width/height */
+ /* adjustment */
+ AF_LATIN_HINTS_MONO = 1 << 3 /* indicate monochrome */
+ /* rendering */
+ };
+
+
+#define AF_LATIN_HINTS_DO_HORZ_SNAP( h ) \
+ AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_HORZ_SNAP )
+
+#define AF_LATIN_HINTS_DO_VERT_SNAP( h ) \
+ AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_VERT_SNAP )
+
+#define AF_LATIN_HINTS_DO_STEM_ADJUST( h ) \
+ AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_STEM_ADJUST )
+
+#define AF_LATIN_HINTS_DO_MONO( h ) \
+ AF_HINTS_TEST_OTHER( h, AF_LATIN_HINTS_MONO )
+
+
+ /*
+ * This shouldn't normally be exported. However, other scripts might
+ * like to use this function as-is.
+ */
+ FT_LOCAL( FT_Error )
+ af_latin_hints_compute_segments( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ /*
+ * This shouldn't normally be exported. However, other scripts might
+ * want to use this function as-is.
+ */
+ FT_LOCAL( void )
+ af_latin_hints_link_segments( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ /*
+ * This shouldn't normally be exported. However, other scripts might
+ * want to use this function as-is.
+ */
+ FT_LOCAL( FT_Error )
+ af_latin_hints_compute_edges( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ FT_LOCAL( FT_Error )
+ af_latin_hints_detect_features( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFLATIN_H__ */
+
+
+/* END */
diff --git a/src/autofit/aflatin2.c b/src/autofit/aflatin2.c
new file mode 100644
index 0000000..0b41774
--- /dev/null
+++ b/src/autofit/aflatin2.c
@@ -0,0 +1,2286 @@
+/***************************************************************************/
+/* */
+/* aflatin.c */
+/* */
+/* Auto-fitter hinting routines for latin script (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "aflatin.h"
+#include "aflatin2.h"
+#include "aferrors.h"
+
+
+#ifdef AF_USE_WARPER
+#include "afwarp.h"
+#endif
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin2_hints_compute_segments( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ FT_LOCAL_DEF( void )
+ af_latin2_hints_link_segments( AF_GlyphHints hints,
+ AF_Dimension dim );
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L O B A L M E T R I C S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ af_latin2_metrics_init_widths( AF_LatinMetrics metrics,
+ FT_Face face,
+ FT_ULong charcode )
+ {
+ /* scan the array of segments in each direction */
+ AF_GlyphHintsRec hints[1];
+
+
+ af_glyph_hints_init( hints, face->memory );
+
+ metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
+ metrics->axis[AF_DIMENSION_VERT].width_count = 0;
+
+ {
+ FT_Error error;
+ FT_UInt glyph_index;
+ int dim;
+ AF_LatinMetricsRec dummy[1];
+ AF_Scaler scaler = &dummy->root.scaler;
+
+
+ glyph_index = FT_Get_Char_Index( face, charcode );
+ if ( glyph_index == 0 )
+ goto Exit;
+
+ error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+ if ( error || face->glyph->outline.n_points <= 0 )
+ goto Exit;
+
+ FT_ZERO( dummy );
+
+ dummy->units_per_em = metrics->units_per_em;
+ scaler->x_scale = scaler->y_scale = 0x10000L;
+ scaler->x_delta = scaler->y_delta = 0;
+ scaler->face = face;
+ scaler->render_mode = FT_RENDER_MODE_NORMAL;
+ scaler->flags = 0;
+
+ af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy );
+
+ error = af_glyph_hints_reload( hints, &face->glyph->outline, 0 );
+ if ( error )
+ goto Exit;
+
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ AF_LatinAxis axis = &metrics->axis[dim];
+ AF_AxisHints axhints = &hints->axis[dim];
+ AF_Segment seg, limit, link;
+ FT_UInt num_widths = 0;
+
+
+ error = af_latin2_hints_compute_segments( hints,
+ (AF_Dimension)dim );
+ if ( error )
+ goto Exit;
+
+ af_latin2_hints_link_segments( hints,
+ (AF_Dimension)dim );
+
+ seg = axhints->segments;
+ limit = seg + axhints->num_segments;
+
+ for ( ; seg < limit; seg++ )
+ {
+ link = seg->link;
+
+ /* we only consider stem segments there! */
+ if ( link && link->link == seg && link > seg )
+ {
+ FT_Pos dist;
+
+
+ dist = seg->pos - link->pos;
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( num_widths < AF_LATIN_MAX_WIDTHS )
+ axis->widths[ num_widths++ ].org = dist;
+ }
+ }
+
+ af_sort_widths( num_widths, axis->widths );
+ axis->width_count = num_widths;
+ }
+
+ Exit:
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+ AF_LatinAxis axis = &metrics->axis[dim];
+ FT_Pos stdw;
+
+
+ stdw = ( axis->width_count > 0 )
+ ? axis->widths[0].org
+ : AF_LATIN_CONSTANT( metrics, 50 );
+
+ /* let's try 20% of the smallest width */
+ axis->edge_distance_threshold = stdw / 5;
+ axis->standard_width = stdw;
+ axis->extra_light = 0;
+ }
+ }
+
+ af_glyph_hints_done( hints );
+ }
+
+
+
+#define AF_LATIN_MAX_TEST_CHARACTERS 12
+
+
+ static const char* const af_latin2_blue_chars[AF_LATIN_MAX_BLUES] =
+ {
+ "THEZOCQS",
+ "HEZLOCUS",
+ "fijkdbh",
+ "xzroesc",
+ "xzroesc",
+ "pqgjy"
+ };
+
+
+ static void
+ af_latin2_metrics_init_blues( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ FT_Pos flats [AF_LATIN_MAX_TEST_CHARACTERS];
+ FT_Pos rounds[AF_LATIN_MAX_TEST_CHARACTERS];
+ FT_Int num_flats;
+ FT_Int num_rounds;
+ FT_Int bb;
+ AF_LatinBlue blue;
+ FT_Error error;
+ AF_LatinAxis axis = &metrics->axis[AF_DIMENSION_VERT];
+ FT_GlyphSlot glyph = face->glyph;
+
+
+ /* we compute the blues simply by loading each character from the */
+ /* 'af_latin2_blue_chars[blues]' string, then compute its top-most or */
+ /* bottom-most points (depending on `AF_IS_TOP_BLUE') */
+
+ AF_LOG(( "blue zones computation\n" ));
+ AF_LOG(( "------------------------------------------------\n" ));
+
+ for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
+ {
+ const char* p = af_latin2_blue_chars[bb];
+ const char* limit = p + AF_LATIN_MAX_TEST_CHARACTERS;
+ FT_Pos* blue_ref;
+ FT_Pos* blue_shoot;
+
+
+ AF_LOG(( "blue %3d: ", bb ));
+
+ num_flats = 0;
+ num_rounds = 0;
+
+ for ( ; p < limit && *p; p++ )
+ {
+ FT_UInt glyph_index;
+ FT_Int best_point, best_y, best_first, best_last;
+ FT_Vector* points;
+ FT_Bool round;
+
+
+ AF_LOG(( "'%c'", *p ));
+
+ /* load the character in the face -- skip unknown or empty ones */
+ glyph_index = FT_Get_Char_Index( face, (FT_UInt)*p );
+ if ( glyph_index == 0 )
+ continue;
+
+ error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+ if ( error || glyph->outline.n_points <= 0 )
+ continue;
+
+ /* now compute min or max point indices and coordinates */
+ points = glyph->outline.points;
+ best_point = -1;
+ best_y = 0; /* make compiler happy */
+ best_first = 0; /* ditto */
+ best_last = 0; /* ditto */
+
+ {
+ FT_Int nn;
+ FT_Int first = 0;
+ FT_Int last = -1;
+
+
+ for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ )
+ {
+ FT_Int old_best_point = best_point;
+ FT_Int pp;
+
+
+ last = glyph->outline.contours[nn];
+
+ /* Avoid single-point contours since they are never rasterized. */
+ /* In some fonts, they correspond to mark attachment points */
+ /* which are way outside of the glyph's real outline. */
+ if ( last == first )
+ continue;
+
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) )
+ {
+ for ( pp = first; pp <= last; pp++ )
+ if ( best_point < 0 || points[pp].y > best_y )
+ {
+ best_point = pp;
+ best_y = points[pp].y;
+ }
+ }
+ else
+ {
+ for ( pp = first; pp <= last; pp++ )
+ if ( best_point < 0 || points[pp].y < best_y )
+ {
+ best_point = pp;
+ best_y = points[pp].y;
+ }
+ }
+
+ if ( best_point != old_best_point )
+ {
+ best_first = first;
+ best_last = last;
+ }
+ }
+ AF_LOG(( "%5d", best_y ));
+ }
+
+ /* now check whether the point belongs to a straight or round */
+ /* segment; we first need to find in which contour the extremum */
+ /* lies, then inspect its previous and next points */
+ {
+ FT_Int start, end, prev, next;
+ FT_Pos dist;
+
+
+ /* now look for the previous and next points that are not on the */
+ /* same Y coordinate. Threshold the `closeness'... */
+ start = end = best_point;
+
+ do
+ {
+ prev = start-1;
+ if ( prev < best_first )
+ prev = best_last;
+
+ dist = points[prev].y - best_y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ start = prev;
+
+ } while ( start != best_point );
+
+ do
+ {
+ next = end+1;
+ if ( next > best_last )
+ next = best_first;
+
+ dist = points[next].y - best_y;
+ if ( dist < -5 || dist > 5 )
+ break;
+
+ end = next;
+
+ } while ( end != best_point );
+
+ /* now, set the `round' flag depending on the segment's kind */
+ round = FT_BOOL(
+ FT_CURVE_TAG( glyph->outline.tags[start] ) != FT_CURVE_TAG_ON ||
+ FT_CURVE_TAG( glyph->outline.tags[ end ] ) != FT_CURVE_TAG_ON );
+
+ AF_LOG(( "%c ", round ? 'r' : 'f' ));
+ }
+
+ if ( round )
+ rounds[num_rounds++] = best_y;
+ else
+ flats[num_flats++] = best_y;
+ }
+
+ AF_LOG(( "\n" ));
+
+ if ( num_flats == 0 && num_rounds == 0 )
+ {
+ /*
+ * we couldn't find a single glyph to compute this blue zone,
+ * we will simply ignore it then
+ */
+ AF_LOG(( "empty!\n" ));
+ continue;
+ }
+
+ /* we have computed the contents of the `rounds' and `flats' tables, */
+ /* now determine the reference and overshoot position of the blue -- */
+ /* we simply take the median value after a simple sort */
+ af_sort_pos( num_rounds, rounds );
+ af_sort_pos( num_flats, flats );
+
+ blue = & axis->blues[axis->blue_count];
+ blue_ref = & blue->ref.org;
+ blue_shoot = & blue->shoot.org;
+
+ axis->blue_count++;
+
+ if ( num_flats == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = rounds[num_rounds / 2];
+ }
+ else if ( num_rounds == 0 )
+ {
+ *blue_ref =
+ *blue_shoot = flats[num_flats / 2];
+ }
+ else
+ {
+ *blue_ref = flats[num_flats / 2];
+ *blue_shoot = rounds[num_rounds / 2];
+ }
+
+ /* there are sometimes problems: if the overshoot position of top */
+ /* zones is under its reference position, or the opposite for bottom */
+ /* zones. We must thus check everything there and correct the errors */
+ if ( *blue_shoot != *blue_ref )
+ {
+ FT_Pos ref = *blue_ref;
+ FT_Pos shoot = *blue_shoot;
+ FT_Bool over_ref = FT_BOOL( shoot > ref );
+
+
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) ^ over_ref )
+ *blue_shoot = *blue_ref = ( shoot + ref ) / 2;
+ }
+
+ blue->flags = 0;
+ if ( AF_LATIN_IS_TOP_BLUE( bb ) )
+ blue->flags |= AF_LATIN_BLUE_TOP;
+
+ /*
+ * The following flags is used later to adjust the y and x scales
+ * in order to optimize the pixel grid alignment of the top of small
+ * letters.
+ */
+ if ( bb == AF_LATIN_BLUE_SMALL_TOP )
+ blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
+
+ AF_LOG(( "-- ref = %ld, shoot = %ld\n", *blue_ref, *blue_shoot ));
+ }
+
+ return;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin2_metrics_init( AF_LatinMetrics metrics,
+ FT_Face face )
+ {
+ FT_Error error = AF_Err_Ok;
+ FT_CharMap oldmap = face->charmap;
+ FT_UInt ee;
+
+ static const FT_Encoding latin_encodings[] =
+ {
+ FT_ENCODING_UNICODE,
+ FT_ENCODING_APPLE_ROMAN,
+ FT_ENCODING_ADOBE_STANDARD,
+ FT_ENCODING_ADOBE_LATIN_1,
+ FT_ENCODING_NONE /* end of list */
+ };
+
+
+ metrics->units_per_em = face->units_per_EM;
+
+ /* do we have a latin charmap in there? */
+ for ( ee = 0; latin_encodings[ee] != FT_ENCODING_NONE; ee++ )
+ {
+ error = FT_Select_Charmap( face, latin_encodings[ee] );
+ if ( !error )
+ break;
+ }
+
+ if ( !error )
+ {
+ /* For now, compute the standard width and height from the `o'. */
+ af_latin2_metrics_init_widths( metrics, face, 'o' );
+ af_latin2_metrics_init_blues( metrics, face );
+ }
+
+ FT_Set_Charmap( face, oldmap );
+ return AF_Err_Ok;
+ }
+
+
+ static void
+ af_latin2_metrics_scale_dim( AF_LatinMetrics metrics,
+ AF_Scaler scaler,
+ AF_Dimension dim )
+ {
+ FT_Fixed scale;
+ FT_Pos delta;
+ AF_LatinAxis axis;
+ FT_UInt nn;
+
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ scale = scaler->x_scale;
+ delta = scaler->x_delta;
+ }
+ else
+ {
+ scale = scaler->y_scale;
+ delta = scaler->y_delta;
+ }
+
+ axis = &metrics->axis[dim];
+
+ if ( axis->org_scale == scale && axis->org_delta == delta )
+ return;
+
+ axis->org_scale = scale;
+ axis->org_delta = delta;
+
+ /*
+ * correct Y scale to optimize the alignment of the top of small
+ * letters to the pixel grid
+ */
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ AF_LatinAxis vaxis = &metrics->axis[AF_DIMENSION_VERT];
+ AF_LatinBlue blue = NULL;
+
+
+ for ( nn = 0; nn < vaxis->blue_count; nn++ )
+ {
+ if ( vaxis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
+ {
+ blue = &vaxis->blues[nn];
+ break;
+ }
+ }
+
+ if ( blue )
+ {
+ FT_Pos scaled = FT_MulFix( blue->shoot.org, scaler->y_scale );
+ FT_Pos fitted = ( scaled + 40 ) & ~63;
+
+#if 1
+ if ( scaled != fitted ) {
+ scale = FT_MulDiv( scale, fitted, scaled );
+ AF_LOG(( "== scaled x-top = %.2g fitted = %.2g, scaling = %.4g\n", scaled/64.0, fitted/64.0, (fitted*1.0)/scaled ));
+ }
+#endif
+ }
+ }
+
+ axis->scale = scale;
+ axis->delta = delta;
+
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ metrics->root.scaler.x_scale = scale;
+ metrics->root.scaler.x_delta = delta;
+ }
+ else
+ {
+ metrics->root.scaler.y_scale = scale;
+ metrics->root.scaler.y_delta = delta;
+ }
+
+ /* scale the standard widths */
+ for ( nn = 0; nn < axis->width_count; nn++ )
+ {
+ AF_Width width = axis->widths + nn;
+
+
+ width->cur = FT_MulFix( width->org, scale );
+ width->fit = width->cur;
+ }
+
+ /* an extra-light axis corresponds to a standard width that is */
+ /* smaller than 0.75 pixels */
+ axis->extra_light =
+ (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
+
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ /* scale the blue zones */
+ for ( nn = 0; nn < axis->blue_count; nn++ )
+ {
+ AF_LatinBlue blue = &axis->blues[nn];
+ FT_Pos dist;
+
+
+ blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta;
+ blue->ref.fit = blue->ref.cur;
+ blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
+ blue->shoot.fit = blue->shoot.cur;
+ blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
+
+ /* a blue zone is only active if it is less than 3/4 pixels tall */
+ dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
+ if ( dist <= 48 && dist >= -48 )
+ {
+ FT_Pos delta1, delta2;
+
+ delta1 = blue->shoot.org - blue->ref.org;
+ delta2 = delta1;
+ if ( delta1 < 0 )
+ delta2 = -delta2;
+
+ delta2 = FT_MulFix( delta2, scale );
+
+ if ( delta2 < 32 )
+ delta2 = 0;
+ else if ( delta2 < 64 )
+ delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
+ else
+ delta2 = FT_PIX_ROUND( delta2 );
+
+ if ( delta1 < 0 )
+ delta2 = -delta2;
+
+ blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
+ blue->shoot.fit = blue->ref.fit + delta2;
+
+ AF_LOG(( ">> activating blue zone %d: ref.cur=%.2g ref.fit=%.2g shoot.cur=%.2g shoot.fit=%.2g\n",
+ nn, blue->ref.cur/64.0, blue->ref.fit/64.0,
+ blue->shoot.cur/64.0, blue->shoot.fit/64.0 ));
+
+ blue->flags |= AF_LATIN_BLUE_ACTIVE;
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin2_metrics_scale( AF_LatinMetrics metrics,
+ AF_Scaler scaler )
+ {
+ metrics->root.scaler.render_mode = scaler->render_mode;
+ metrics->root.scaler.face = scaler->face;
+
+ af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
+ af_latin2_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L Y P H A N A L Y S I S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define SORT_SEGMENTS
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin2_hints_compute_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ FT_Memory memory = hints->memory;
+ FT_Error error = AF_Err_Ok;
+ AF_Segment segment = NULL;
+ AF_SegmentRec seg0;
+ AF_Point* contour = hints->contours;
+ AF_Point* contour_limit = contour + hints->num_contours;
+ AF_Direction major_dir, segment_dir;
+
+
+ FT_ZERO( &seg0 );
+ seg0.score = 32000;
+ seg0.flags = AF_EDGE_NORMAL;
+
+ major_dir = (AF_Direction)FT_ABS( axis->major_dir );
+ segment_dir = major_dir;
+
+ axis->num_segments = 0;
+
+ /* set up (u,v) in each point */
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ AF_Point point = hints->points;
+ AF_Point limit = point + hints->num_points;
+
+
+ for ( ; point < limit; point++ )
+ {
+ point->u = point->fx;
+ point->v = point->fy;
+ }
+ }
+ else
+ {
+ AF_Point point = hints->points;
+ AF_Point limit = point + hints->num_points;
+
+
+ for ( ; point < limit; point++ )
+ {
+ point->u = point->fy;
+ point->v = point->fx;
+ }
+ }
+
+ /* do each contour separately */
+ for ( ; contour < contour_limit; contour++ )
+ {
+ AF_Point point = contour[0];
+ AF_Point start = point;
+ AF_Point last = point->prev;
+
+
+ if ( point == last ) /* skip singletons -- just in case */
+ continue;
+
+ /* already on an edge ?, backtrack to find its start */
+ if ( FT_ABS( point->in_dir ) == major_dir )
+ {
+ point = point->prev;
+
+ while ( point->in_dir == start->in_dir )
+ point = point->prev;
+ }
+ else /* otherwise, find first segment start, if any */
+ {
+ while ( FT_ABS( point->out_dir ) != major_dir )
+ {
+ point = point->next;
+
+ if ( point == start )
+ goto NextContour;
+ }
+ }
+
+ start = point;
+
+ for (;;)
+ {
+ AF_Point first;
+ FT_Pos min_u, min_v, max_u, max_v;
+
+ /* we're at the start of a new segment */
+ FT_ASSERT( FT_ABS( point->out_dir ) == major_dir &&
+ point->in_dir != point->out_dir );
+ first = point;
+
+ min_u = max_u = point->u;
+ min_v = max_v = point->v;
+
+ point = point->next;
+
+ while ( point->out_dir == first->out_dir )
+ {
+ point = point->next;
+
+ if ( point->u < min_u )
+ min_u = point->u;
+
+ if ( point->u > max_u )
+ max_u = point->u;
+ }
+
+ if ( point->v < min_v )
+ min_v = point->v;
+
+ if ( point->v > max_v )
+ max_v = point->v;
+
+ /* record new segment */
+ error = af_axis_hints_new_segment( axis, memory, &segment );
+ if ( error )
+ goto Exit;
+
+ segment[0] = seg0;
+ segment->dir = first->out_dir;
+ segment->first = first;
+ segment->last = point;
+ segment->contour = contour;
+ segment->pos = (FT_Short)(( min_u + max_u ) >> 1);
+ segment->min_coord = (FT_Short) min_v;
+ segment->max_coord = (FT_Short) max_v;
+ segment->height = (FT_Short)(max_v - min_v);
+
+ /* a segment is round if it doesn't have successive */
+ /* on-curve points. */
+ {
+ AF_Point pt = first;
+ AF_Point last = point;
+ AF_Flags f0 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
+ AF_Flags f1;
+
+
+ segment->flags &= ~AF_EDGE_ROUND;
+
+ for ( ; pt != last; f0 = f1 )
+ {
+ pt = pt->next;
+ f1 = (AF_Flags)(pt->flags & AF_FLAG_CONTROL);
+
+ if ( !f0 && !f1 )
+ break;
+
+ if ( pt == last )
+ segment->flags |= AF_EDGE_ROUND;
+ }
+ }
+
+ /* this can happen in the case of a degenerate contour
+ * e.g. a 2-point vertical contour
+ */
+ if ( point == start )
+ break;
+
+ /* jump to the start of the next segment, if any */
+ while ( FT_ABS(point->out_dir) != major_dir )
+ {
+ point = point->next;
+
+ if ( point == start )
+ goto NextContour;
+ }
+ }
+
+ NextContour:
+ ;
+ } /* contours */
+
+ /* now slightly increase the height of segments when this makes */
+ /* sense -- this is used to better detect and ignore serifs */
+ {
+ AF_Segment segments = axis->segments;
+ AF_Segment segments_end = segments + axis->num_segments;
+
+
+ for ( segment = segments; segment < segments_end; segment++ )
+ {
+ AF_Point first = segment->first;
+ AF_Point last = segment->last;
+ AF_Point p;
+ FT_Pos first_v = first->v;
+ FT_Pos last_v = last->v;
+
+
+ if ( first == last )
+ continue;
+
+ if ( first_v < last_v )
+ {
+ p = first->prev;
+ if ( p->v < first_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( first_v - p->v ) >> 1 ) );
+
+ p = last->next;
+ if ( p->v > last_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( p->v - last_v ) >> 1 ) );
+ }
+ else
+ {
+ p = first->prev;
+ if ( p->v > first_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( p->v - first_v ) >> 1 ) );
+
+ p = last->next;
+ if ( p->v < last_v )
+ segment->height = (FT_Short)( segment->height +
+ ( ( last_v - p->v ) >> 1 ) );
+ }
+ }
+ }
+
+#ifdef AF_SORT_SEGMENTS
+ /* place all segments with a negative direction to the start
+ * of the array, used to speed up segment linking later...
+ */
+ {
+ AF_Segment segments = axis->segments;
+ FT_UInt count = axis->num_segments;
+ FT_UInt ii, jj;
+
+ for (ii = 0; ii < count; ii++)
+ {
+ if ( segments[ii].dir > 0 )
+ {
+ for (jj = ii+1; jj < count; jj++)
+ {
+ if ( segments[jj].dir < 0 )
+ {
+ AF_SegmentRec tmp;
+
+ tmp = segments[ii];
+ segments[ii] = segments[jj];
+ segments[jj] = tmp;
+
+ break;
+ }
+ }
+
+ if ( jj == count )
+ break;
+ }
+ }
+ axis->mid_segments = ii;
+ }
+#endif
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin2_hints_link_segments( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+#ifdef AF_SORT_SEGMENTS
+ AF_Segment segment_mid = segments + axis->mid_segments;
+#endif
+ FT_Pos len_threshold, len_score;
+ AF_Segment seg1, seg2;
+
+
+ len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
+ if ( len_threshold == 0 )
+ len_threshold = 1;
+
+ len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
+
+#ifdef AF_SORT_SEGMENTS
+ for ( seg1 = segments; seg1 < segment_mid; seg1++ )
+ {
+ if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
+ continue;
+
+ for ( seg2 = segment_mid; seg2 < segment_limit; seg2++ )
+#else
+ /* now compare each segment to the others */
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ /* the fake segments are introduced to hint the metrics -- */
+ /* we must never link them to anything */
+ if ( seg1->dir != axis->major_dir || seg1->first == seg1->last )
+ continue;
+
+ for ( seg2 = segments; seg2 < segment_limit; seg2++ )
+ if ( seg1->dir + seg2->dir == 0 && seg2->pos > seg1->pos )
+#endif
+ {
+ FT_Pos pos1 = seg1->pos;
+ FT_Pos pos2 = seg2->pos;
+ FT_Pos dist = pos2 - pos1;
+
+
+ if ( dist < 0 )
+ continue;
+
+ {
+ FT_Pos min = seg1->min_coord;
+ FT_Pos max = seg1->max_coord;
+ FT_Pos len, score;
+
+
+ if ( min < seg2->min_coord )
+ min = seg2->min_coord;
+
+ if ( max > seg2->max_coord )
+ max = seg2->max_coord;
+
+ len = max - min;
+ if ( len >= len_threshold )
+ {
+ score = dist + len_score / len;
+ if ( score < seg1->score )
+ {
+ seg1->score = score;
+ seg1->link = seg2;
+ }
+
+ if ( score < seg2->score )
+ {
+ seg2->score = score;
+ seg2->link = seg1;
+ }
+ }
+ }
+ }
+ }
+
+ /* now, compute the `serif' segments */
+ for ( seg1 = segments; seg1 < segment_limit; seg1++ )
+ {
+ seg2 = seg1->link;
+
+ if ( seg2 )
+ {
+ if ( seg2->link != seg1 )
+ {
+ seg1->link = 0;
+ seg1->serif = seg2->link;
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin2_hints_compute_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ FT_Error error = AF_Err_Ok;
+ FT_Memory memory = hints->memory;
+ AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim];
+
+ AF_Segment segments = axis->segments;
+ AF_Segment segment_limit = segments + axis->num_segments;
+ AF_Segment seg;
+
+ AF_Direction up_dir;
+ FT_Fixed scale;
+ FT_Pos edge_distance_threshold;
+ FT_Pos segment_length_threshold;
+
+
+ axis->num_edges = 0;
+
+ scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
+ : hints->y_scale;
+
+ up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
+ : AF_DIR_RIGHT;
+
+ /*
+ * We want to ignore very small (mostly serif) segments, we do that
+ * by ignoring those that whose length is less than a given fraction
+ * of the standard width. If there is no standard width, we ignore
+ * those that are less than a given size in pixels
+ *
+ * also, unlink serif segments that are linked to segments farther
+ * than 50% of the standard width
+ */
+ if ( dim == AF_DIMENSION_HORZ )
+ {
+ if ( laxis->width_count > 0 )
+ segment_length_threshold = (laxis->standard_width * 10 ) >> 4;
+ else
+ segment_length_threshold = FT_DivFix( 64, hints->y_scale );
+ }
+ else
+ segment_length_threshold = 0;
+
+ /*********************************************************************/
+ /* */
+ /* We will begin by generating a sorted table of edges for the */
+ /* current direction. To do so, we simply scan each segment and try */
+ /* to find an edge in our table that corresponds to its position. */
+ /* */
+ /* If no edge is found, we create and insert a new edge in the */
+ /* sorted table. Otherwise, we simply add the segment to the edge's */
+ /* list which will be processed in the second step to compute the */
+ /* edge's properties. */
+ /* */
+ /* Note that the edges table is sorted along the segment/edge */
+ /* position. */
+ /* */
+ /*********************************************************************/
+
+ edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
+ scale );
+ if ( edge_distance_threshold > 64 / 4 )
+ edge_distance_threshold = 64 / 4;
+
+ edge_distance_threshold = FT_DivFix( edge_distance_threshold,
+ scale );
+
+ for ( seg = segments; seg < segment_limit; seg++ )
+ {
+ AF_Edge found = 0;
+ FT_Int ee;
+
+
+ if ( seg->height < segment_length_threshold )
+ continue;
+
+ /* A special case for serif edges: If they are smaller than */
+ /* 1.5 pixels we ignore them. */
+ if ( seg->serif )
+ {
+ FT_Pos dist = seg->serif->pos - seg->pos;
+
+ if (dist < 0)
+ dist = -dist;
+
+ if (dist >= laxis->standard_width >> 1)
+ {
+ /* unlink this serif, it is too distant from its reference stem */
+ seg->serif = NULL;
+ }
+ else if ( 2*seg->height < 3 * segment_length_threshold )
+ continue;
+ }
+
+ /* look for an edge corresponding to the segment */
+ for ( ee = 0; ee < axis->num_edges; ee++ )
+ {
+ AF_Edge edge = axis->edges + ee;
+ FT_Pos dist;
+
+
+ dist = seg->pos - edge->fpos;
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( dist < edge_distance_threshold && edge->dir == seg->dir )
+ {
+ found = edge;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ AF_Edge edge;
+
+
+ /* insert a new edge in the list and */
+ /* sort according to the position */
+ error = af_axis_hints_new_edge( axis, seg->pos, seg->dir, memory, &edge );
+ if ( error )
+ goto Exit;
+
+ /* add the segment to the new edge's list */
+ FT_ZERO( edge );
+
+ edge->first = seg;
+ edge->last = seg;
+ edge->fpos = seg->pos;
+ edge->dir = seg->dir;
+ edge->opos = edge->pos = FT_MulFix( seg->pos, scale );
+ seg->edge_next = seg;
+ }
+ else
+ {
+ /* if an edge was found, simply add the segment to the edge's */
+ /* list */
+ seg->edge_next = found->first;
+ found->last->edge_next = seg;
+ found->last = seg;
+ }
+ }
+
+
+ /*********************************************************************/
+ /* */
+ /* Good, we will now compute each edge's properties according to */
+ /* segments found on its position. Basically, these are: */
+ /* */
+ /* - edge's main direction */
+ /* - stem edge, serif edge or both (which defaults to stem then) */
+ /* - rounded edge, straight or both (which defaults to straight) */
+ /* - link for edge */
+ /* */
+ /*********************************************************************/
+
+ /* first of all, set the `edge' field in each segment -- this is */
+ /* required in order to compute edge links */
+
+ /*
+ * Note that removing this loop and setting the `edge' field of each
+ * segment directly in the code above slows down execution speed for
+ * some reasons on platforms like the Sun.
+ */
+ {
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ AF_Edge edge;
+
+
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ seg = edge->first;
+ if ( seg )
+ do
+ {
+ seg->edge = edge;
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+ }
+
+ /* now, compute each edge properties */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Int is_round = 0; /* does it contain round segments? */
+ FT_Int is_straight = 0; /* does it contain straight segments? */
+ FT_Pos ups = 0; /* number of upwards segments */
+ FT_Pos downs = 0; /* number of downwards segments */
+
+
+ seg = edge->first;
+
+ do
+ {
+ FT_Bool is_serif;
+
+
+ /* check for roundness of segment */
+ if ( seg->flags & AF_EDGE_ROUND )
+ is_round++;
+ else
+ is_straight++;
+
+ /* check for segment direction */
+ if ( seg->dir == up_dir )
+ ups += seg->max_coord-seg->min_coord;
+ else
+ downs += seg->max_coord-seg->min_coord;
+
+ /* check for links -- if seg->serif is set, then seg->link must */
+ /* be ignored */
+ is_serif = (FT_Bool)( seg->serif &&
+ seg->serif->edge &&
+ seg->serif->edge != edge );
+
+ if ( ( seg->link && seg->link->edge != NULL ) || is_serif )
+ {
+ AF_Edge edge2;
+ AF_Segment seg2;
+
+
+ edge2 = edge->link;
+ seg2 = seg->link;
+
+ if ( is_serif )
+ {
+ seg2 = seg->serif;
+ edge2 = edge->serif;
+ }
+
+ if ( edge2 )
+ {
+ FT_Pos edge_delta;
+ FT_Pos seg_delta;
+
+
+ edge_delta = edge->fpos - edge2->fpos;
+ if ( edge_delta < 0 )
+ edge_delta = -edge_delta;
+
+ seg_delta = seg->pos - seg2->pos;
+ if ( seg_delta < 0 )
+ seg_delta = -seg_delta;
+
+ if ( seg_delta < edge_delta )
+ edge2 = seg2->edge;
+ }
+ else
+ edge2 = seg2->edge;
+
+ if ( is_serif )
+ {
+ edge->serif = edge2;
+ edge2->flags |= AF_EDGE_SERIF;
+ }
+ else
+ edge->link = edge2;
+ }
+
+ seg = seg->edge_next;
+
+ } while ( seg != edge->first );
+
+ /* set the round/straight flags */
+ edge->flags = AF_EDGE_NORMAL;
+
+ if ( is_round > 0 && is_round >= is_straight )
+ edge->flags |= AF_EDGE_ROUND;
+
+#if 0
+ /* set the edge's main direction */
+ edge->dir = AF_DIR_NONE;
+
+ if ( ups > downs )
+ edge->dir = (FT_Char)up_dir;
+
+ else if ( ups < downs )
+ edge->dir = (FT_Char)-up_dir;
+
+ else if ( ups == downs )
+ edge->dir = 0; /* both up and down! */
+#endif
+
+ /* gets rid of serifs if link is set */
+ /* XXX: This gets rid of many unpleasant artefacts! */
+ /* Example: the `c' in cour.pfa at size 13 */
+
+ if ( edge->serif && edge->link )
+ edge->serif = 0;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_latin2_hints_detect_features( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ FT_Error error;
+
+
+ error = af_latin2_hints_compute_segments( hints, dim );
+ if ( !error )
+ {
+ af_latin2_hints_link_segments( hints, dim );
+
+ error = af_latin2_hints_compute_edges( hints, dim );
+ }
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_latin2_hints_compute_blue_edges( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ AF_AxisHints axis = &hints->axis[ AF_DIMENSION_VERT ];
+ AF_Edge edge = axis->edges;
+ AF_Edge edge_limit = edge + axis->num_edges;
+ AF_LatinAxis latin = &metrics->axis[ AF_DIMENSION_VERT ];
+ FT_Fixed scale = latin->scale;
+ FT_Pos best_dist0; /* initial threshold */
+
+
+ /* compute the initial threshold as a fraction of the EM size */
+ best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale );
+
+ if ( best_dist0 > 64 / 2 )
+ best_dist0 = 64 / 2;
+
+ /* compute which blue zones are active, i.e. have their scaled */
+ /* size < 3/4 pixels */
+
+ /* for each horizontal edge search the blue zone which is closest */
+ for ( ; edge < edge_limit; edge++ )
+ {
+ FT_Int bb;
+ AF_Width best_blue = NULL;
+ FT_Pos best_dist = best_dist0;
+
+ for ( bb = 0; bb < AF_LATIN_BLUE_MAX; bb++ )
+ {
+ AF_LatinBlue blue = latin->blues + bb;
+ FT_Bool is_top_blue, is_major_dir;
+
+
+ /* skip inactive blue zones (i.e., those that are too small) */
+ if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
+ continue;
+
+ /* if it is a top zone, check for right edges -- if it is a bottom */
+ /* zone, check for left edges */
+ /* */
+ /* of course, that's for TrueType */
+ is_top_blue = (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_TOP ) != 0 );
+ is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
+
+ /* if it is a top zone, the edge must be against the major */
+ /* direction; if it is a bottom zone, it must be in the major */
+ /* direction */
+ if ( is_top_blue ^ is_major_dir )
+ {
+ FT_Pos dist;
+ AF_Width compare;
+
+
+ /* if it's a rounded edge, compare it to the overshoot position */
+ /* if it's a flat edge, compare it to the reference position */
+ if ( edge->flags & AF_EDGE_ROUND )
+ compare = &blue->shoot;
+ else
+ compare = &blue->ref;
+
+ dist = edge->fpos - compare->org;
+ if (dist < 0)
+ dist = -dist;
+
+ dist = FT_MulFix( dist, scale );
+ if ( dist < best_dist )
+ {
+ best_dist = dist;
+ best_blue = compare;
+ }
+
+#if 0
+ /* now, compare it to the overshoot position if the edge is */
+ /* rounded, and if the edge is over the reference position of a */
+ /* top zone, or under the reference position of a bottom zone */
+ if ( edge->flags & AF_EDGE_ROUND && dist != 0 )
+ {
+ FT_Bool is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
+
+
+ if ( is_top_blue ^ is_under_ref )
+ {
+ blue = latin->blues + bb;
+ dist = edge->fpos - blue->shoot.org;
+ if ( dist < 0 )
+ dist = -dist;
+
+ dist = FT_MulFix( dist, scale );
+ if ( dist < best_dist )
+ {
+ best_dist = dist;
+ best_blue = & blue->shoot;
+ }
+ }
+ }
+#endif
+ }
+ }
+
+ if ( best_blue )
+ edge->blue_edge = best_blue;
+ }
+ }
+
+
+ static FT_Error
+ af_latin2_hints_init( AF_GlyphHints hints,
+ AF_LatinMetrics metrics )
+ {
+ FT_Render_Mode mode;
+ FT_UInt32 scaler_flags, other_flags;
+ FT_Face face = metrics->root.scaler.face;
+
+
+ af_glyph_hints_rescale( hints, (AF_ScriptMetrics)metrics );
+
+ /*
+ * correct x_scale and y_scale if needed, since they may have
+ * been modified `af_latin2_metrics_scale_dim' above
+ */
+ hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
+ hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
+ hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
+ hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
+
+ /* compute flags depending on render mode, etc. */
+ mode = metrics->root.scaler.render_mode;
+
+#if 0 /* #ifdef AF_USE_WARPER */
+ if ( mode == FT_RENDER_MODE_LCD || mode == FT_RENDER_MODE_LCD_V )
+ {
+ metrics->root.scaler.render_mode = mode = FT_RENDER_MODE_NORMAL;
+ }
+#endif
+
+ scaler_flags = hints->scaler_flags;
+ other_flags = 0;
+
+ /*
+ * We snap the width of vertical stems for the monochrome and
+ * horizontal LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
+ other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
+
+ /*
+ * We snap the width of horizontal stems for the monochrome and
+ * vertical LCD rendering targets only.
+ */
+ if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
+ other_flags |= AF_LATIN_HINTS_VERT_SNAP;
+
+ /*
+ * We adjust stems to full pixels only if we don't use the `light' mode.
+ */
+ if ( mode != FT_RENDER_MODE_LIGHT )
+ other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
+
+ if ( mode == FT_RENDER_MODE_MONO )
+ other_flags |= AF_LATIN_HINTS_MONO;
+
+ /*
+ * In `light' hinting mode we disable horizontal hinting completely.
+ * We also do it if the face is italic.
+ */
+ if ( mode == FT_RENDER_MODE_LIGHT ||
+ (face->style_flags & FT_STYLE_FLAG_ITALIC) != 0 )
+ scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
+
+ hints->scaler_flags = scaler_flags;
+ hints->other_flags = other_flags;
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N G L Y P H G R I D - F I T T I N G *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* snap a given width in scaled coordinates to one of the */
+ /* current standard widths */
+
+ static FT_Pos
+ af_latin2_snap_width( AF_Width widths,
+ FT_Int count,
+ FT_Pos width )
+ {
+ int n;
+ FT_Pos best = 64 + 32 + 2;
+ FT_Pos reference = width;
+ FT_Pos scaled;
+
+
+ for ( n = 0; n < count; n++ )
+ {
+ FT_Pos w;
+ FT_Pos dist;
+
+
+ w = widths[n].cur;
+ dist = width - w;
+ if ( dist < 0 )
+ dist = -dist;
+ if ( dist < best )
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+
+ scaled = FT_PIX_ROUND( reference );
+
+ if ( width >= reference )
+ {
+ if ( width < scaled + 48 )
+ width = reference;
+ }
+ else
+ {
+ if ( width > scaled - 48 )
+ width = reference;
+ }
+
+ return width;
+ }
+
+
+ /* compute the snapped width of a given stem */
+
+ static FT_Pos
+ af_latin2_compute_stem_width( AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Pos width,
+ AF_Edge_Flags base_flags,
+ AF_Edge_Flags stem_flags )
+ {
+ AF_LatinMetrics metrics = (AF_LatinMetrics) hints->metrics;
+ AF_LatinAxis axis = & metrics->axis[dim];
+ FT_Pos dist = width;
+ FT_Int sign = 0;
+ FT_Int vertical = ( dim == AF_DIMENSION_VERT );
+
+
+ FT_UNUSED(base_flags);
+
+ if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
+ axis->extra_light )
+ return width;
+
+ if ( dist < 0 )
+ {
+ dist = -width;
+ sign = 1;
+ }
+
+ if ( ( vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
+ ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
+ {
+ /* smooth hinting process: very lightly quantize the stem width */
+
+ /* leave the widths of serifs alone */
+
+ if ( ( stem_flags & AF_EDGE_SERIF ) && vertical && ( dist < 3 * 64 ) )
+ goto Done_Width;
+
+#if 0
+ else if ( ( base_flags & AF_EDGE_ROUND ) )
+ {
+ if ( dist < 80 )
+ dist = 64;
+ }
+ else if ( dist < 56 )
+ dist = 56;
+#endif
+ if ( axis->width_count > 0 )
+ {
+ FT_Pos delta;
+
+
+ /* compare to standard width */
+ if ( axis->width_count > 0 )
+ {
+ delta = dist - axis->widths[0].cur;
+
+ if ( delta < 0 )
+ delta = -delta;
+
+ if ( delta < 40 )
+ {
+ dist = axis->widths[0].cur;
+ if ( dist < 48 )
+ dist = 48;
+
+ goto Done_Width;
+ }
+ }
+
+ if ( dist < 3 * 64 )
+ {
+ delta = dist & 63;
+ dist &= -64;
+
+ if ( delta < 10 )
+ dist += delta;
+
+ else if ( delta < 32 )
+ dist += 10;
+
+ else if ( delta < 54 )
+ dist += 54;
+
+ else
+ dist += delta;
+ }
+ else
+ dist = ( dist + 32 ) & ~63;
+ }
+ }
+ else
+ {
+ /* strong hinting process: snap the stem width to integer pixels */
+ FT_Pos org_dist = dist;
+
+
+ dist = af_latin2_snap_width( axis->widths, axis->width_count, dist );
+
+ if ( vertical )
+ {
+ /* in the case of vertical hinting, always round */
+ /* the stem heights to integer pixels */
+
+ if ( dist >= 64 )
+ dist = ( dist + 16 ) & ~63;
+ else
+ dist = 64;
+ }
+ else
+ {
+ if ( AF_LATIN_HINTS_DO_MONO( hints ) )
+ {
+ /* monochrome horizontal hinting: snap widths to integer pixels */
+ /* with a different threshold */
+
+ if ( dist < 64 )
+ dist = 64;
+ else
+ dist = ( dist + 32 ) & ~63;
+ }
+ else
+ {
+ /* for horizontal anti-aliased hinting, we adopt a more subtle */
+ /* approach: we strengthen small stems, round stems whose size */
+ /* is between 1 and 2 pixels to an integer, otherwise nothing */
+
+ if ( dist < 48 )
+ dist = ( dist + 64 ) >> 1;
+
+ else if ( dist < 128 )
+ {
+ /* We only round to an integer width if the corresponding */
+ /* distortion is less than 1/4 pixel. Otherwise this */
+ /* makes everything worse since the diagonals, which are */
+ /* not hinted, appear a lot bolder or thinner than the */
+ /* vertical stems. */
+
+ FT_Int delta;
+
+
+ dist = ( dist + 22 ) & ~63;
+ delta = dist - org_dist;
+ if ( delta < 0 )
+ delta = -delta;
+
+ if (delta >= 16)
+ {
+ dist = org_dist;
+ if ( dist < 48 )
+ dist = ( dist + 64 ) >> 1;
+ }
+ }
+ else
+ /* round otherwise to prevent color fringes in LCD mode */
+ dist = ( dist + 32 ) & ~63;
+ }
+ }
+ }
+
+ Done_Width:
+ if ( sign )
+ dist = -dist;
+
+ return dist;
+ }
+
+
+ /* align one stem edge relative to the previous stem edge */
+
+ static void
+ af_latin2_align_linked_edge( AF_GlyphHints hints,
+ AF_Dimension dim,
+ AF_Edge base_edge,
+ AF_Edge stem_edge )
+ {
+ FT_Pos dist = stem_edge->opos - base_edge->opos;
+
+ FT_Pos fitted_width = af_latin2_compute_stem_width(
+ hints, dim, dist,
+ (AF_Edge_Flags)base_edge->flags,
+ (AF_Edge_Flags)stem_edge->flags );
+
+
+ stem_edge->pos = base_edge->pos + fitted_width;
+
+ AF_LOG(( "LINK: edge %d (opos=%.2f) linked to (%.2f), "
+ "dist was %.2f, now %.2f\n",
+ stem_edge-hints->axis[dim].edges, stem_edge->opos / 64.0,
+ stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
+ }
+
+
+ static void
+ af_latin2_align_serif_edge( AF_GlyphHints hints,
+ AF_Edge base,
+ AF_Edge serif )
+ {
+ FT_UNUSED( hints );
+
+ serif->pos = base->pos + (serif->opos - base->opos);
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** E D G E H I N T I N G ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ af_latin2_hint_edges( AF_GlyphHints hints,
+ AF_Dimension dim )
+ {
+ AF_AxisHints axis = &hints->axis[dim];
+ AF_Edge edges = axis->edges;
+ AF_Edge edge_limit = edges + axis->num_edges;
+ FT_Int n_edges;
+ AF_Edge edge;
+ AF_Edge anchor = 0;
+ FT_Int has_serifs = 0;
+ FT_Pos anchor_drift = 0;
+
+
+
+ AF_LOG(( "==== hinting %s edges =====\n", dim == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ));
+
+ /* we begin by aligning all stems relative to the blue zone */
+ /* if needed -- that's only for horizontal edges */
+
+ if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
+ {
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Width blue;
+ AF_Edge edge1, edge2;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ blue = edge->blue_edge;
+ edge1 = NULL;
+ edge2 = edge->link;
+
+ if ( blue )
+ {
+ edge1 = edge;
+ }
+ else if ( edge2 && edge2->blue_edge )
+ {
+ blue = edge2->blue_edge;
+ edge1 = edge2;
+ edge2 = edge;
+ }
+
+ if ( !edge1 )
+ continue;
+
+ AF_LOG(( "BLUE: edge %d (opos=%.2f) snapped to (%.2f), "
+ "was (%.2f)\n",
+ edge1-edges, edge1->opos / 64.0, blue->fit / 64.0,
+ edge1->pos / 64.0 ));
+
+ edge1->pos = blue->fit;
+ edge1->flags |= AF_EDGE_DONE;
+
+ if ( edge2 && !edge2->blue_edge )
+ {
+ af_latin2_align_linked_edge( hints, dim, edge1, edge2 );
+ edge2->flags |= AF_EDGE_DONE;
+ }
+
+ if ( !anchor )
+ {
+ anchor = edge;
+
+ anchor_drift = (anchor->pos - anchor->opos);
+ if (edge2)
+ anchor_drift = (anchor_drift + (edge2->pos - edge2->opos)) >> 1;
+ }
+ }
+ }
+
+ /* now we will align all stem edges, trying to maintain the */
+ /* relative order of stems in the glyph */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ AF_Edge edge2;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ /* skip all non-stem edges */
+ edge2 = edge->link;
+ if ( !edge2 )
+ {
+ has_serifs++;
+ continue;
+ }
+
+ /* now align the stem */
+
+ /* this should not happen, but it's better to be safe */
+ if ( edge2->blue_edge )
+ {
+ AF_LOG(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
+
+ af_latin2_align_linked_edge( hints, dim, edge2, edge );
+ edge->flags |= AF_EDGE_DONE;
+ continue;
+ }
+
+ if ( !anchor )
+ {
+ FT_Pos org_len, org_center, cur_len;
+ FT_Pos cur_pos1, error1, error2, u_off, d_off;
+
+
+ org_len = edge2->opos - edge->opos;
+ cur_len = af_latin2_compute_stem_width(
+ hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+ if ( cur_len <= 64 )
+ u_off = d_off = 32;
+ else
+ {
+ u_off = 38;
+ d_off = 26;
+ }
+
+ if ( cur_len < 96 )
+ {
+ org_center = edge->opos + ( org_len >> 1 );
+
+ cur_pos1 = FT_PIX_ROUND( org_center );
+
+ error1 = org_center - ( cur_pos1 - u_off );
+ if ( error1 < 0 )
+ error1 = -error1;
+
+ error2 = org_center - ( cur_pos1 + d_off );
+ if ( error2 < 0 )
+ error2 = -error2;
+
+ if ( error1 < error2 )
+ cur_pos1 -= u_off;
+ else
+ cur_pos1 += d_off;
+
+ edge->pos = cur_pos1 - cur_len / 2;
+ edge2->pos = edge->pos + cur_len;
+ }
+ else
+ edge->pos = FT_PIX_ROUND( edge->opos );
+
+ AF_LOG(( "ANCHOR: edge %d (opos=%.2f) and %d (opos=%.2f) "
+ "snapped to (%.2f) (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge2-edges, edge2->opos / 64.0,
+ edge->pos / 64.0, edge2->pos / 64.0 ));
+ anchor = edge;
+
+ edge->flags |= AF_EDGE_DONE;
+
+ af_latin2_align_linked_edge( hints, dim, edge, edge2 );
+
+ edge2->flags |= AF_EDGE_DONE;
+
+ anchor_drift = ( (anchor->pos - anchor->opos) +
+ (edge2->pos - edge2->opos)) >> 1;
+
+ AF_LOG(( "DRIFT: %.2f\n", anchor_drift/64.0 ));
+ }
+ else
+ {
+ FT_Pos org_pos, org_len, org_center, cur_center, cur_len;
+ FT_Pos org_left, org_right;
+
+
+ org_pos = edge->opos + anchor_drift;
+ org_len = edge2->opos - edge->opos;
+ org_center = org_pos + ( org_len >> 1 );
+
+ cur_len = af_latin2_compute_stem_width(
+ hints, dim, org_len,
+ (AF_Edge_Flags)edge->flags,
+ (AF_Edge_Flags)edge2->flags );
+
+ org_left = org_pos + ((org_len - cur_len) >> 1);
+ org_right = org_pos + ((org_len + cur_len) >> 1);
+
+ AF_LOG(( "ALIGN: left=%.2f right=%.2f ", org_left/64.0, org_right/64.0 ));
+ cur_center = org_center;
+
+ if ( edge2->flags & AF_EDGE_DONE )
+ {
+ AF_LOG(( "\n" ));
+ edge->pos = edge2->pos - cur_len;
+ }
+ else
+ {
+ /* we want to compare several displacement, and choose
+ * the one that increases fitness while minimizing
+ * distortion as well
+ */
+ FT_Pos displacements[6], scores[6], org, fit, delta;
+ FT_UInt count = 0;
+
+ /* note: don't even try to fit tiny stems */
+ if ( cur_len < 32 )
+ {
+ AF_LOG(( "tiny stem\n" ));
+ goto AlignStem;
+ }
+
+ /* if the span is within a single pixel, don't touch it */
+ if ( FT_PIX_FLOOR(org_left) == FT_PIX_CEIL(org_right) )
+ {
+ AF_LOG(( "single pixel stem\n" ));
+ goto AlignStem;
+ }
+
+ if (cur_len <= 96)
+ {
+ /* we want to avoid the absolute worst case which is
+ * when the left and right edges of the span each represent
+ * about 50% of the gray. we'd better want to change this
+ * to 25/75%, since this is much more pleasant to the eye with
+ * very acceptable distortion
+ */
+ FT_Pos frac_left = (org_left) & 63;
+ FT_Pos frac_right = (org_right) & 63;
+
+ if ( frac_left >= 22 && frac_left <= 42 &&
+ frac_right >= 22 && frac_right <= 42 )
+ {
+ org = frac_left;
+ fit = (org <= 32) ? 16 : 48;
+ delta = FT_ABS(fit - org);
+ displacements[count] = fit - org;
+ scores[count++] = delta;
+ AF_LOG(( "dispA=%.2f (%d) ", (fit - org)/64.0, delta ));
+
+ org = frac_right;
+ fit = (org <= 32) ? 16 : 48;
+ delta = FT_ABS(fit - org);
+ displacements[count] = fit - org;
+ scores[count++] = delta;
+ AF_LOG(( "dispB=%.2f (%d) ", (fit - org)/64.0, delta ));
+ }
+ }
+
+ /* snapping the left edge to the grid */
+ org = org_left;
+ fit = FT_PIX_ROUND(org);
+ delta = FT_ABS(fit - org);
+ displacements[count] = fit - org;
+ scores[count++] = delta;
+ AF_LOG(( "dispC=%.2f (%d) ", (fit - org)/64.0, delta ));
+
+ /* snapping the right edge to the grid */
+ org = org_right;
+ fit = FT_PIX_ROUND(org);
+ delta = FT_ABS(fit - org);
+ displacements[count] = fit - org;
+ scores[count++] = delta;
+ AF_LOG(( "dispD=%.2f (%d) ", (fit - org)/64.0, delta ));
+
+ /* now find the best displacement */
+ {
+ FT_Pos best_score = scores[0];
+ FT_Pos best_disp = displacements[0];
+ FT_UInt nn;
+
+ for (nn = 1; nn < count; nn++)
+ {
+ if (scores[nn] < best_score)
+ {
+ best_score = scores[nn];
+ best_disp = displacements[nn];
+ }
+ }
+
+ cur_center = org_center + best_disp;
+ }
+ AF_LOG(( "\n" ));
+ }
+
+ AlignStem:
+ edge->pos = cur_center - (cur_len >> 1);
+ edge2->pos = edge->pos + cur_len;
+
+ AF_LOG(( "STEM1: %d (opos=%.2f) to %d (opos=%.2f) "
+ "snapped to (%.2f) and (%.2f), org_len = %.2f cur_len=%.2f\n",
+ edge-edges, edge->opos / 64.0,
+ edge2-edges, edge2->opos / 64.0,
+ edge->pos / 64.0, edge2->pos / 64.0,
+ org_len / 64.0, cur_len / 64.0 ));
+
+ edge->flags |= AF_EDGE_DONE;
+ edge2->flags |= AF_EDGE_DONE;
+
+ if ( edge > edges && edge->pos < edge[-1].pos )
+ {
+ AF_LOG(( "BOUND: %d (pos=%.2f) to (%.2f)\n",
+ edge-edges, edge->pos / 64.0, edge[-1].pos / 64.0 ));
+ edge->pos = edge[-1].pos;
+ }
+ }
+ }
+
+ /* make sure that lowercase m's maintain their symmetry */
+
+ /* In general, lowercase m's have six vertical edges if they are sans */
+ /* serif, or twelve if they are with serifs. This implementation is */
+ /* based on that assumption, and seems to work very well with most */
+ /* faces. However, if for a certain face this assumption is not */
+ /* true, the m is just rendered like before. In addition, any stem */
+ /* correction will only be applied to symmetrical glyphs (even if the */
+ /* glyph is not an m), so the potential for unwanted distortion is */
+ /* relatively low. */
+
+ /* We don't handle horizontal edges since we can't easily assure that */
+ /* the third (lowest) stem aligns with the base line; it might end up */
+ /* one pixel higher or lower. */
+#if 0
+ n_edges = edge_limit - edges;
+ if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
+ {
+ AF_Edge edge1, edge2, edge3;
+ FT_Pos dist1, dist2, span, delta;
+
+
+ if ( n_edges == 6 )
+ {
+ edge1 = edges;
+ edge2 = edges + 2;
+ edge3 = edges + 4;
+ }
+ else
+ {
+ edge1 = edges + 1;
+ edge2 = edges + 5;
+ edge3 = edges + 9;
+ }
+
+ dist1 = edge2->opos - edge1->opos;
+ dist2 = edge3->opos - edge2->opos;
+
+ span = dist1 - dist2;
+ if ( span < 0 )
+ span = -span;
+
+ if ( span < 8 )
+ {
+ delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
+ edge3->pos -= delta;
+ if ( edge3->link )
+ edge3->link->pos -= delta;
+
+ /* move the serifs along with the stem */
+ if ( n_edges == 12 )
+ {
+ ( edges + 8 )->pos -= delta;
+ ( edges + 11 )->pos -= delta;
+ }
+
+ edge3->flags |= AF_EDGE_DONE;
+ if ( edge3->link )
+ edge3->link->flags |= AF_EDGE_DONE;
+ }
+ }
+#endif
+ if ( has_serifs || !anchor )
+ {
+ /*
+ * now hint the remaining edges (serifs and single) in order
+ * to complete our processing
+ */
+ for ( edge = edges; edge < edge_limit; edge++ )
+ {
+ FT_Pos delta;
+
+
+ if ( edge->flags & AF_EDGE_DONE )
+ continue;
+
+ delta = 1000;
+
+ if ( edge->serif )
+ {
+ delta = edge->serif->opos - edge->opos;
+ if ( delta < 0 )
+ delta = -delta;
+ }
+
+ if ( delta < 64 + 16 )
+ {
+ af_latin2_align_serif_edge( hints, edge->serif, edge );
+ AF_LOG(( "SERIF: edge %d (opos=%.2f) serif to %d (opos=%.2f) "
+ "aligned to (%.2f)\n",
+ edge-edges, edge->opos / 64.0,
+ edge->serif - edges, edge->serif->opos / 64.0,
+ edge->pos / 64.0 ));
+ }
+ else if ( !anchor )
+ {
+ AF_LOG(( "SERIF_ANCHOR: edge %d (opos=%.2f) snapped to (%.2f)\n",
+ edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
+ edge->pos = FT_PIX_ROUND( edge->opos );
+ anchor = edge;
+ }
+ else
+ {
+ AF_Edge before, after;
+
+
+ for ( before = edge - 1; before >= edges; before-- )
+ if ( before->flags & AF_EDGE_DONE )
+ break;
+
+ for ( after = edge + 1; after < edge_limit; after++ )
+ if ( after->flags & AF_EDGE_DONE )
+ break;
+
+ if ( before >= edges && before < edge &&
+ after < edge_limit && after > edge )
+ {
+ edge->pos = before->pos +
+ FT_MulDiv( edge->opos - before->opos,
+ after->pos - before->pos,
+ after->opos - before->opos );
+ AF_LOG(( "SERIF_LINK1: edge %d (opos=%.2f) snapped to (%.2f) from %d (opos=%.2f)\n",
+ edge-edges, edge->opos / 64.0, edge->pos / 64.0, before - edges, before->opos / 64.0 ));
+ }
+ else
+ {
+ edge->pos = anchor->pos + (( edge->opos - anchor->opos + 16) & ~31);
+
+ AF_LOG(( "SERIF_LINK2: edge %d (opos=%.2f) snapped to (%.2f)\n",
+ edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
+ }
+ }
+
+ edge->flags |= AF_EDGE_DONE;
+
+ if ( edge > edges && edge->pos < edge[-1].pos )
+ edge->pos = edge[-1].pos;
+
+ if ( edge + 1 < edge_limit &&
+ edge[1].flags & AF_EDGE_DONE &&
+ edge->pos > edge[1].pos )
+ edge->pos = edge[1].pos;
+ }
+ }
+ }
+
+
+ static FT_Error
+ af_latin2_hints_apply( AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_LatinMetrics metrics )
+ {
+ FT_Error error;
+ int dim;
+
+
+ error = af_glyph_hints_reload( hints, outline, 1 );
+ if ( error )
+ goto Exit;
+
+ /* analyze glyph outline */
+#ifdef AF_USE_WARPER
+ if ( metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ||
+ AF_HINTS_DO_HORIZONTAL( hints ) )
+#else
+ if ( AF_HINTS_DO_HORIZONTAL( hints ) )
+#endif
+ {
+ error = af_latin2_hints_detect_features( hints, AF_DIMENSION_HORZ );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( AF_HINTS_DO_VERTICAL( hints ) )
+ {
+ error = af_latin2_hints_detect_features( hints, AF_DIMENSION_VERT );
+ if ( error )
+ goto Exit;
+
+ af_latin2_hints_compute_blue_edges( hints, metrics );
+ }
+
+ /* grid-fit the outline */
+ for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+ {
+#ifdef AF_USE_WARPER
+ if ( ( dim == AF_DIMENSION_HORZ &&
+ metrics->root.scaler.render_mode == FT_RENDER_MODE_LIGHT ) )
+ {
+ AF_WarperRec warper;
+ FT_Fixed scale;
+ FT_Pos delta;
+
+
+ af_warper_compute( &warper, hints, dim, &scale, &delta );
+ af_glyph_hints_scale_dim( hints, dim, scale, delta );
+ continue;
+ }
+#endif
+
+ if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
+ ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) ) )
+ {
+ af_latin2_hint_edges( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
+ af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
+ }
+ }
+ af_glyph_hints_save( hints, outline );
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** L A T I N S C R I P T C L A S S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static const AF_Script_UniRangeRec af_latin2_uniranges[] =
+ {
+ { 32, 127 }, /* XXX: TODO: Add new Unicode ranges here! */
+ { 160, 255 },
+ { 0, 0 }
+ };
+
+
+ FT_CALLBACK_TABLE_DEF const AF_ScriptClassRec
+ af_latin2_script_class =
+ {
+ AF_SCRIPT_LATIN2,
+ af_latin2_uniranges,
+
+ sizeof( AF_LatinMetricsRec ),
+
+ (AF_Script_InitMetricsFunc) af_latin2_metrics_init,
+ (AF_Script_ScaleMetricsFunc)af_latin2_metrics_scale,
+ (AF_Script_DoneMetricsFunc) NULL,
+
+ (AF_Script_InitHintsFunc) af_latin2_hints_init,
+ (AF_Script_ApplyHintsFunc) af_latin2_hints_apply
+ };
+
+
+/* END */
diff --git a/src/autofit/aflatin2.h b/src/autofit/aflatin2.h
new file mode 100644
index 0000000..34eda05
--- /dev/null
+++ b/src/autofit/aflatin2.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* aflatin2.h */
+/* */
+/* Auto-fitter hinting routines for latin script (specification). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFLATIN2_H__
+#define __AFLATIN2_H__
+
+#include "afhints.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /* the latin-specific script class */
+
+ FT_CALLBACK_TABLE const AF_ScriptClassRec
+ af_latin2_script_class;
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFLATIN_H__ */
+
+
+/* END */
diff --git a/src/autofit/afloader.c b/src/autofit/afloader.c
new file mode 100644
index 0000000..4e4373a
--- /dev/null
+++ b/src/autofit/afloader.c
@@ -0,0 +1,530 @@
+/***************************************************************************/
+/* */
+/* afloader.c */
+/* */
+/* Auto-fitter glyph loading routines (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afloader.h"
+#include "afhints.h"
+#include "afglobal.h"
+#include "aflatin.h"
+#include "aferrors.h"
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_loader_init( AF_Loader loader,
+ FT_Memory memory )
+ {
+ FT_ZERO( loader );
+
+ af_glyph_hints_init( &loader->hints, memory );
+#ifdef AF_DEBUG
+ _af_debug_hints = &loader->hints;
+#endif
+ return FT_GlyphLoader_New( memory, &loader->gloader );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_loader_reset( AF_Loader loader,
+ FT_Face face )
+ {
+ FT_Error error = AF_Err_Ok;
+
+
+ loader->face = face;
+ loader->globals = (AF_FaceGlobals)face->autohint.data;
+
+ FT_GlyphLoader_Rewind( loader->gloader );
+
+ if ( loader->globals == NULL )
+ {
+ error = af_face_globals_new( face, &loader->globals );
+ if ( !error )
+ {
+ face->autohint.data =
+ (FT_Pointer)loader->globals;
+ face->autohint.finalizer =
+ (FT_Generic_Finalizer)af_face_globals_free;
+ }
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_loader_done( AF_Loader loader )
+ {
+ af_glyph_hints_done( &loader->hints );
+
+ loader->face = NULL;
+ loader->globals = NULL;
+
+#ifdef AF_DEBUG
+ _af_debug_hints = NULL;
+#endif
+ FT_GlyphLoader_Done( loader->gloader );
+ loader->gloader = NULL;
+ }
+
+
+ static FT_Error
+ af_loader_load_g( AF_Loader loader,
+ AF_Scaler scaler,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags,
+ FT_UInt depth )
+ {
+ FT_Error error;
+ FT_Face face = loader->face;
+ FT_GlyphLoader gloader = loader->gloader;
+ AF_ScriptMetrics metrics = loader->metrics;
+ AF_GlyphHints hints = &loader->hints;
+ FT_GlyphSlot slot = face->glyph;
+ FT_Slot_Internal internal = slot->internal;
+
+
+ error = FT_Load_Glyph( face, glyph_index, load_flags );
+ if ( error )
+ goto Exit;
+
+ loader->transformed = internal->glyph_transformed;
+ if ( loader->transformed )
+ {
+ FT_Matrix inverse;
+
+
+ loader->trans_matrix = internal->glyph_matrix;
+ loader->trans_delta = internal->glyph_delta;
+
+ inverse = loader->trans_matrix;
+ FT_Matrix_Invert( &inverse );
+ FT_Vector_Transform( &loader->trans_delta, &inverse );
+ }
+
+ /* set linear metrics */
+ slot->linearHoriAdvance = slot->metrics.horiAdvance;
+ slot->linearVertAdvance = slot->metrics.vertAdvance;
+
+ switch ( slot->format )
+ {
+ case FT_GLYPH_FORMAT_OUTLINE:
+ /* translate the loaded glyph when an internal transform is needed */
+ if ( loader->transformed )
+ FT_Outline_Translate( &slot->outline,
+ loader->trans_delta.x,
+ loader->trans_delta.y );
+
+ /* copy the outline points in the loader's current */
+ /* extra points which is used to keep original glyph coordinates */
+ error = FT_GLYPHLOADER_CHECK_POINTS( gloader,
+ slot->outline.n_points + 4,
+ slot->outline.n_contours );
+ if ( error )
+ goto Exit;
+
+ FT_ARRAY_COPY( gloader->current.outline.points,
+ slot->outline.points,
+ slot->outline.n_points );
+
+ FT_ARRAY_COPY( gloader->current.outline.contours,
+ slot->outline.contours,
+ slot->outline.n_contours );
+
+ FT_ARRAY_COPY( gloader->current.outline.tags,
+ slot->outline.tags,
+ slot->outline.n_points );
+
+ gloader->current.outline.n_points = slot->outline.n_points;
+ gloader->current.outline.n_contours = slot->outline.n_contours;
+
+ /* compute original horizontal phantom points (and ignore */
+ /* vertical ones) */
+ loader->pp1.x = hints->x_delta;
+ loader->pp1.y = hints->y_delta;
+ loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
+ hints->x_scale ) + hints->x_delta;
+ loader->pp2.y = hints->y_delta;
+
+ /* be sure to check for spacing glyphs */
+ if ( slot->outline.n_points == 0 )
+ goto Hint_Metrics;
+
+ /* now load the slot image into the auto-outline and run the */
+ /* automatic hinting process */
+ metrics->clazz->script_hints_apply( hints,
+ &gloader->current.outline,
+ metrics );
+
+ /* we now need to hint the metrics according to the change in */
+ /* width/positioning that occurred during the hinting process */
+ if ( scaler->render_mode != FT_RENDER_MODE_LIGHT )
+ {
+ FT_Pos old_rsb, old_lsb, new_lsb;
+ FT_Pos pp1x_uh, pp2x_uh;
+ AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ];
+ AF_Edge edge1 = axis->edges; /* leftmost edge */
+ AF_Edge edge2 = edge1 +
+ axis->num_edges - 1; /* rightmost edge */
+
+
+ if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
+ {
+ old_rsb = loader->pp2.x - edge2->opos;
+ old_lsb = edge1->opos;
+ new_lsb = edge1->pos;
+
+ /* remember unhinted values to later account */
+ /* for rounding errors */
+
+ pp1x_uh = new_lsb - old_lsb;
+ pp2x_uh = edge2->pos + old_rsb;
+
+ /* prefer too much space over too little space */
+ /* for very small sizes */
+
+ if ( old_lsb < 24 )
+ pp1x_uh -= 8;
+
+ if ( old_rsb < 24 )
+ pp2x_uh += 8;
+
+ loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
+ loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
+
+ if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
+ loader->pp1.x -= 64;
+
+ if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
+ loader->pp2.x += 64;
+
+ slot->lsb_delta = loader->pp1.x - pp1x_uh;
+ slot->rsb_delta = loader->pp2.x - pp2x_uh;
+ }
+ else
+ {
+ FT_Pos pp1x = loader->pp1.x;
+ FT_Pos pp2x = loader->pp2.x;
+
+ loader->pp1.x = FT_PIX_ROUND( pp1x );
+ loader->pp2.x = FT_PIX_ROUND( pp2x );
+
+ slot->lsb_delta = loader->pp1.x - pp1x;
+ slot->rsb_delta = loader->pp2.x - pp2x;
+ }
+ }
+ else
+ {
+ FT_Pos pp1x = loader->pp1.x;
+ FT_Pos pp2x = loader->pp2.x;
+
+ loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
+ loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
+
+ slot->lsb_delta = loader->pp1.x - pp1x;
+ slot->rsb_delta = loader->pp2.x - pp2x;
+ }
+
+ /* good, we simply add the glyph to our loader's base */
+ FT_GlyphLoader_Add( gloader );
+ break;
+
+ case FT_GLYPH_FORMAT_COMPOSITE:
+ {
+ FT_UInt nn, num_subglyphs = slot->num_subglyphs;
+ FT_UInt num_base_subgs, start_point;
+ FT_SubGlyph subglyph;
+
+
+ start_point = gloader->base.outline.n_points;
+
+ /* first of all, copy the subglyph descriptors in the glyph loader */
+ error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs );
+ if ( error )
+ goto Exit;
+
+ FT_ARRAY_COPY( gloader->current.subglyphs,
+ slot->subglyphs,
+ num_subglyphs );
+
+ gloader->current.num_subglyphs = num_subglyphs;
+ num_base_subgs = gloader->base.num_subglyphs;
+
+ /* now, read each subglyph independently */
+ for ( nn = 0; nn < num_subglyphs; nn++ )
+ {
+ FT_Vector pp1, pp2;
+ FT_Pos x, y;
+ FT_UInt num_points, num_new_points, num_base_points;
+
+
+ /* gloader.current.subglyphs can change during glyph loading due */
+ /* to re-allocation -- we must recompute the current subglyph on */
+ /* each iteration */
+ subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+ pp1 = loader->pp1;
+ pp2 = loader->pp2;
+
+ num_base_points = gloader->base.outline.n_points;
+
+ error = af_loader_load_g( loader, scaler, subglyph->index,
+ load_flags, depth + 1 );
+ if ( error )
+ goto Exit;
+
+ /* recompute subglyph pointer */
+ subglyph = gloader->base.subglyphs + num_base_subgs + nn;
+
+ if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
+ {
+ pp1 = loader->pp1;
+ pp2 = loader->pp2;
+ }
+ else
+ {
+ loader->pp1 = pp1;
+ loader->pp2 = pp2;
+ }
+
+ num_points = gloader->base.outline.n_points;
+ num_new_points = num_points - num_base_points;
+
+ /* now perform the transform required for this subglyph */
+
+ if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
+ FT_SUBGLYPH_FLAG_XY_SCALE |
+ FT_SUBGLYPH_FLAG_2X2 ) )
+ {
+ FT_Vector* cur = gloader->base.outline.points +
+ num_base_points;
+ FT_Vector* limit = cur + num_new_points;
+
+
+ for ( ; cur < limit; cur++ )
+ FT_Vector_Transform( cur, &subglyph->transform );
+ }
+
+ /* apply offset */
+
+ if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
+ {
+ FT_Int k = subglyph->arg1;
+ FT_UInt l = subglyph->arg2;
+ FT_Vector* p1;
+ FT_Vector* p2;
+
+
+ if ( start_point + k >= num_base_points ||
+ l >= (FT_UInt)num_new_points )
+ {
+ error = AF_Err_Invalid_Composite;
+ goto Exit;
+ }
+
+ l += num_base_points;
+
+ /* for now, only use the current point coordinates; */
+ /* we may consider another approach in the near future */
+ p1 = gloader->base.outline.points + start_point + k;
+ p2 = gloader->base.outline.points + start_point + l;
+
+ x = p1->x - p2->x;
+ y = p1->y - p2->y;
+ }
+ else
+ {
+ x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta;
+ y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta;
+
+ x = FT_PIX_ROUND( x );
+ y = FT_PIX_ROUND( y );
+ }
+
+ {
+ FT_Outline dummy = gloader->base.outline;
+
+
+ dummy.points += num_base_points;
+ dummy.n_points = (short)num_new_points;
+
+ FT_Outline_Translate( &dummy, x, y );
+ }
+ }
+ }
+ break;
+
+ default:
+ /* we don't support other formats (yet?) */
+ error = AF_Err_Unimplemented_Feature;
+ }
+
+ Hint_Metrics:
+ if ( depth == 0 )
+ {
+ FT_BBox bbox;
+ FT_Vector vvector;
+
+
+ vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
+ vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
+ vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale );
+ vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale );
+
+ /* transform the hinted outline if needed */
+ if ( loader->transformed )
+ {
+ FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
+ FT_Vector_Transform( &vvector, &loader->trans_matrix );
+ }
+#if 1
+ /* we must translate our final outline by -pp1.x and compute */
+ /* the new metrics */
+ if ( loader->pp1.x )
+ FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
+#endif
+ FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
+
+ bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
+ bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
+ bbox.xMax = FT_PIX_CEIL( bbox.xMax );
+ bbox.yMax = FT_PIX_CEIL( bbox.yMax );
+
+ slot->metrics.width = bbox.xMax - bbox.xMin;
+ slot->metrics.height = bbox.yMax - bbox.yMin;
+ slot->metrics.horiBearingX = bbox.xMin;
+ slot->metrics.horiBearingY = bbox.yMax;
+
+ slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
+ slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
+
+ /* for mono-width fonts (like Andale, Courier, etc.) we need */
+ /* to keep the original rounded advance width */
+#if 0
+ if ( !FT_IS_FIXED_WIDTH( slot->face ) )
+ slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
+ else
+ slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
+ x_scale );
+#else
+ if ( !FT_IS_FIXED_WIDTH( slot->face ) )
+ {
+ /* non-spacing glyphs must stay as-is */
+ if ( slot->metrics.horiAdvance )
+ slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
+ }
+ else
+ {
+ slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
+ metrics->scaler.x_scale );
+
+ /* Set delta values to 0. Otherwise code that uses them is */
+ /* going to ruin the fixed advance width. */
+ slot->lsb_delta = 0;
+ slot->rsb_delta = 0;
+ }
+#endif
+
+ slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
+ metrics->scaler.y_scale );
+
+ slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
+ slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
+
+ /* now copy outline into glyph slot */
+ FT_GlyphLoader_Rewind( internal->loader );
+ error = FT_GlyphLoader_CopyPoints( internal->loader, gloader );
+ if ( error )
+ goto Exit;
+
+ slot->outline = internal->loader->base.outline;
+ slot->format = FT_GLYPH_FORMAT_OUTLINE;
+ }
+
+#ifdef DEBUG_HINTER
+ af_debug_hinter = hinter;
+#endif
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ af_loader_load_glyph( AF_Loader loader,
+ FT_Face face,
+ FT_UInt gindex,
+ FT_UInt32 load_flags )
+ {
+ FT_Error error;
+ FT_Size size = face->size;
+ AF_ScalerRec scaler;
+
+
+ if ( !size )
+ return AF_Err_Invalid_Argument;
+
+ FT_ZERO( &scaler );
+
+ scaler.face = face;
+ scaler.x_scale = size->metrics.x_scale;
+ scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
+ scaler.y_scale = size->metrics.y_scale;
+ scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */
+
+ scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
+ scaler.flags = 0; /* XXX: fix this */
+
+ error = af_loader_reset( loader, face );
+ if ( !error )
+ {
+ AF_ScriptMetrics metrics;
+ FT_UInt options = 0;
+
+
+#ifdef FT_OPTION_AUTOFIT2
+ /* XXX: undocumented hook to activate the latin2 hinter */
+ if ( load_flags & ( 1UL << 20 ) )
+ options = 2;
+#endif
+
+ error = af_face_globals_get_metrics( loader->globals, gindex,
+ options, &metrics );
+ if ( !error )
+ {
+ loader->metrics = metrics;
+
+ if ( metrics->clazz->script_metrics_scale )
+ metrics->clazz->script_metrics_scale( metrics, &scaler );
+ else
+ metrics->scaler = scaler;
+
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
+ load_flags &= ~FT_LOAD_RENDER;
+
+ error = metrics->clazz->script_hints_init( &loader->hints, metrics );
+ if ( error )
+ goto Exit;
+
+ error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 );
+ }
+ }
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/src/autofit/afloader.h b/src/autofit/afloader.h
new file mode 100644
index 0000000..fa67c10
--- /dev/null
+++ b/src/autofit/afloader.h
@@ -0,0 +1,73 @@
+/***************************************************************************/
+/* */
+/* afloader.h */
+/* */
+/* Auto-fitter glyph loading routines (specification). */
+/* */
+/* Copyright 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AF_LOADER_H__
+#define __AF_LOADER_H__
+
+#include "afhints.h"
+#include "afglobal.h"
+
+
+FT_BEGIN_HEADER
+
+ typedef struct AF_LoaderRec_
+ {
+ FT_Face face; /* current face */
+ AF_FaceGlobals globals; /* current face globals */
+ FT_GlyphLoader gloader; /* glyph loader */
+ AF_GlyphHintsRec hints;
+ AF_ScriptMetrics metrics;
+ FT_Bool transformed;
+ FT_Matrix trans_matrix;
+ FT_Vector trans_delta;
+ FT_Vector pp1;
+ FT_Vector pp2;
+ /* we don't handle vertical phantom points */
+
+ } AF_LoaderRec, *AF_Loader;
+
+
+ FT_LOCAL( FT_Error )
+ af_loader_init( AF_Loader loader,
+ FT_Memory memory );
+
+
+ FT_LOCAL( FT_Error )
+ af_loader_reset( AF_Loader loader,
+ FT_Face face );
+
+
+ FT_LOCAL( void )
+ af_loader_done( AF_Loader loader );
+
+
+ FT_LOCAL( FT_Error )
+ af_loader_load_glyph( AF_Loader loader,
+ FT_Face face,
+ FT_UInt gindex,
+ FT_UInt32 load_flags );
+
+/* */
+
+
+FT_END_HEADER
+
+#endif /* __AF_LOADER_H__ */
+
+
+/* END */
diff --git a/src/autofit/afmodule.c b/src/autofit/afmodule.c
new file mode 100644
index 0000000..cd5e1cc
--- /dev/null
+++ b/src/autofit/afmodule.c
@@ -0,0 +1,97 @@
+/***************************************************************************/
+/* */
+/* afmodule.c */
+/* */
+/* Auto-fitter module implementation (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afmodule.h"
+#include "afloader.h"
+
+#ifdef AF_DEBUG
+ int _af_debug;
+ int _af_debug_disable_horz_hints;
+ int _af_debug_disable_vert_hints;
+ int _af_debug_disable_blue_hints;
+ void* _af_debug_hints;
+#endif
+
+#include FT_INTERNAL_OBJECTS_H
+
+
+ typedef struct FT_AutofitterRec_
+ {
+ FT_ModuleRec root;
+ AF_LoaderRec loader[1];
+
+ } FT_AutofitterRec, *FT_Autofitter;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ af_autofitter_init( FT_Autofitter module )
+ {
+ return af_loader_init( module->loader, module->root.library->memory );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ af_autofitter_done( FT_Autofitter module )
+ {
+ af_loader_done( module->loader );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ af_autofitter_load_glyph( FT_Autofitter module,
+ FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_UNUSED( size );
+
+ return af_loader_load_glyph( module->loader, slot->face,
+ glyph_index, load_flags );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_AutoHinter_ServiceRec af_autofitter_service =
+ {
+ NULL,
+ NULL,
+ NULL,
+ (FT_AutoHinter_GlyphLoadFunc)af_autofitter_load_glyph
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class autofit_module_class =
+ {
+ FT_MODULE_HINTER,
+ sizeof ( FT_AutofitterRec ),
+
+ "autofitter",
+ 0x10000L, /* version 1.0 of the autofitter */
+ 0x20000L, /* requires FreeType 2.0 or above */
+
+ (const void*)&af_autofitter_service,
+
+ (FT_Module_Constructor)af_autofitter_init,
+ (FT_Module_Destructor) af_autofitter_done,
+ (FT_Module_Requester) NULL
+ };
+
+
+/* END */
diff --git a/src/autofit/afmodule.h b/src/autofit/afmodule.h
new file mode 100644
index 0000000..36268a0
--- /dev/null
+++ b/src/autofit/afmodule.h
@@ -0,0 +1,37 @@
+/***************************************************************************/
+/* */
+/* afmodule.h */
+/* */
+/* Auto-fitter module implementation (specification). */
+/* */
+/* Copyright 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFMODULE_H__
+#define __AFMODULE_H__
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+ FT_CALLBACK_TABLE
+ const FT_Module_Class autofit_module_class;
+
+
+FT_END_HEADER
+
+#endif /* __AFMODULE_H__ */
+
+
+/* END */
diff --git a/src/autofit/aftypes.h b/src/autofit/aftypes.h
new file mode 100644
index 0000000..bd3fa88
--- /dev/null
+++ b/src/autofit/aftypes.h
@@ -0,0 +1,349 @@
+/***************************************************************************/
+/* */
+/* aftypes.h */
+/* */
+/* Auto-fitter types (specification only). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************
+ *
+ * The auto-fitter is a complete rewrite of the old auto-hinter.
+ * Its main feature is the ability to differentiate between different
+ * scripts in order to apply language-specific rules.
+ *
+ * The code has also been compartmentized into several entities that
+ * should make algorithmic experimentation easier than with the old
+ * code.
+ *
+ * Finally, we get rid of the Catharon license, since this code is
+ * released under the FreeType one.
+ *
+ *************************************************************************/
+
+
+#ifndef __AFTYPES_H__
+#define __AFTYPES_H__
+
+#include <ft2build.h>
+
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+
+
+FT_BEGIN_HEADER
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** D E B U G G I N G *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define xxAF_USE_WARPER /* only define to use warp hinting */
+#define xxAF_DEBUG
+
+#ifdef AF_DEBUG
+
+#include <stdio.h>
+#define AF_LOG( x ) do { if ( _af_debug ) printf x; } while ( 0 )
+
+extern int _af_debug;
+extern int _af_debug_disable_horz_hints;
+extern int _af_debug_disable_vert_hints;
+extern int _af_debug_disable_blue_hints;
+extern void* _af_debug_hints;
+
+#else /* !AF_DEBUG */
+
+#define AF_LOG( x ) do ; while ( 0 ) /* nothing */
+
+#endif /* !AF_DEBUG */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** U T I L I T Y S T U F F *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct AF_WidthRec_
+ {
+ FT_Pos org; /* original position/width in font units */
+ FT_Pos cur; /* current/scaled position/width in device sub-pixels */
+ FT_Pos fit; /* current/fitted position/width in device sub-pixels */
+
+ } AF_WidthRec, *AF_Width;
+
+
+ FT_LOCAL( void )
+ af_sort_pos( FT_UInt count,
+ FT_Pos* table );
+
+ FT_LOCAL( void )
+ af_sort_widths( FT_UInt count,
+ AF_Width widths );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** A N G L E T Y P E S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*
+ * The auto-fitter doesn't need a very high angular accuracy;
+ * this allows us to speed up some computations considerably with a
+ * light Cordic algorithm (see afangles.c).
+ */
+
+ typedef FT_Int AF_Angle;
+
+
+#define AF_ANGLE_PI 256
+#define AF_ANGLE_2PI ( AF_ANGLE_PI * 2 )
+#define AF_ANGLE_PI2 ( AF_ANGLE_PI / 2 )
+#define AF_ANGLE_PI4 ( AF_ANGLE_PI / 4 )
+
+
+#if 0
+ /*
+ * compute the angle of a given 2-D vector
+ */
+ FT_LOCAL( AF_Angle )
+ af_angle_atan( FT_Pos dx,
+ FT_Pos dy );
+
+
+ /*
+ * compute `angle2 - angle1'; the result is always within
+ * the range [-AF_ANGLE_PI .. AF_ANGLE_PI - 1]
+ */
+ FT_LOCAL( AF_Angle )
+ af_angle_diff( AF_Angle angle1,
+ AF_Angle angle2 );
+#endif /* 0 */
+
+
+#define AF_ANGLE_DIFF( result, angle1, angle2 ) \
+ FT_BEGIN_STMNT \
+ AF_Angle _delta = (angle2) - (angle1); \
+ \
+ \
+ _delta %= AF_ANGLE_2PI; \
+ if ( _delta < 0 ) \
+ _delta += AF_ANGLE_2PI; \
+ \
+ if ( _delta > AF_ANGLE_PI ) \
+ _delta -= AF_ANGLE_2PI; \
+ \
+ result = _delta; \
+ FT_END_STMNT
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** O U T L I N E S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* opaque handle to glyph-specific hints -- see `afhints.h' for more
+ * details
+ */
+ typedef struct AF_GlyphHintsRec_* AF_GlyphHints;
+
+ /* This structure is used to model an input glyph outline to
+ * the auto-hinter. The latter will set the `hints' field
+ * depending on the glyph's script.
+ */
+ typedef struct AF_OutlineRec_
+ {
+ FT_Face face;
+ FT_Outline outline;
+ FT_UInt outline_resolution;
+
+ FT_Int advance;
+ FT_UInt metrics_resolution;
+
+ AF_GlyphHints hints;
+
+ } AF_OutlineRec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** S C A L E R S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*
+ * A scaler models the target pixel device that will receive the
+ * auto-hinted glyph image.
+ */
+
+ typedef enum
+ {
+ AF_SCALER_FLAG_NO_HORIZONTAL = 1, /* disable horizontal hinting */
+ AF_SCALER_FLAG_NO_VERTICAL = 2, /* disable vertical hinting */
+ AF_SCALER_FLAG_NO_ADVANCE = 4 /* disable advance hinting */
+
+ } AF_ScalerFlags;
+
+
+ typedef struct AF_ScalerRec_
+ {
+ FT_Face face; /* source font face */
+ FT_Fixed x_scale; /* from font units to 1/64th device pixels */
+ FT_Fixed y_scale; /* from font units to 1/64th device pixels */
+ FT_Pos x_delta; /* in 1/64th device pixels */
+ FT_Pos y_delta; /* in 1/64th device pixels */
+ FT_Render_Mode render_mode; /* monochrome, anti-aliased, LCD, etc. */
+ FT_UInt32 flags; /* additional control flags, see above */
+
+ } AF_ScalerRec, *AF_Scaler;
+
+
+#define AF_SCALER_EQUAL_SCALES( a, b ) \
+ ( (a)->x_scale == (b)->x_scale && \
+ (a)->y_scale == (b)->y_scale && \
+ (a)->x_delta == (b)->x_delta && \
+ (a)->y_delta == (b)->y_delta )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** S C R I P T S *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*
+ * The list of know scripts. Each different script corresponds to the
+ * following information:
+ *
+ * - A set of Unicode ranges to test whether the face supports the
+ * script.
+ *
+ * - A specific global analyzer that will compute global metrics
+ * specific to the script.
+ *
+ * - A specific glyph analyzer that will compute segments and
+ * edges for each glyph covered by the script.
+ *
+ * - A specific grid-fitting algorithm that will distort the
+ * scaled glyph outline according to the results of the glyph
+ * analyzer.
+ *
+ * Note that a given analyzer and/or grid-fitting algorithm can be
+ * used by more than one script.
+ */
+
+ typedef enum
+ {
+ AF_SCRIPT_NONE = 0,
+ AF_SCRIPT_LATIN = 1,
+ AF_SCRIPT_CJK = 2,
+ AF_SCRIPT_INDIC = 3,
+#ifdef FT_OPTION_AUTOFIT2
+ AF_SCRIPT_LATIN2,
+#endif
+
+ /* add new scripts here. Don't forget to update the list in */
+ /* `afglobal.c'. */
+
+ AF_SCRIPT_MAX /* do not remove */
+
+ } AF_Script;
+
+
+ typedef struct AF_ScriptClassRec_ const* AF_ScriptClass;
+
+ typedef struct AF_ScriptMetricsRec_
+ {
+ AF_ScriptClass clazz;
+ AF_ScalerRec scaler;
+
+ } AF_ScriptMetricsRec, *AF_ScriptMetrics;
+
+
+ /* This function parses an FT_Face to compute global metrics for
+ * a specific script.
+ */
+ typedef FT_Error
+ (*AF_Script_InitMetricsFunc)( AF_ScriptMetrics metrics,
+ FT_Face face );
+
+ typedef void
+ (*AF_Script_ScaleMetricsFunc)( AF_ScriptMetrics metrics,
+ AF_Scaler scaler );
+
+ typedef void
+ (*AF_Script_DoneMetricsFunc)( AF_ScriptMetrics metrics );
+
+
+ typedef FT_Error
+ (*AF_Script_InitHintsFunc)( AF_GlyphHints hints,
+ AF_ScriptMetrics metrics );
+
+ typedef void
+ (*AF_Script_ApplyHintsFunc)( AF_GlyphHints hints,
+ FT_Outline* outline,
+ AF_ScriptMetrics metrics );
+
+
+ typedef struct AF_Script_UniRangeRec_
+ {
+ FT_UInt32 first;
+ FT_UInt32 last;
+
+ } AF_Script_UniRangeRec;
+
+ typedef const AF_Script_UniRangeRec *AF_Script_UniRange;
+
+
+ typedef struct AF_ScriptClassRec_
+ {
+ AF_Script script;
+ AF_Script_UniRange script_uni_ranges; /* last must be { 0, 0 } */
+
+ FT_UInt script_metrics_size;
+ AF_Script_InitMetricsFunc script_metrics_init;
+ AF_Script_ScaleMetricsFunc script_metrics_scale;
+ AF_Script_DoneMetricsFunc script_metrics_done;
+
+ AF_Script_InitHintsFunc script_hints_init;
+ AF_Script_ApplyHintsFunc script_hints_apply;
+
+ } AF_ScriptClassRec;
+
+
+/* */
+
+FT_END_HEADER
+
+#endif /* __AFTYPES_H__ */
+
+
+/* END */
diff --git a/src/autofit/afwarp.c b/src/autofit/afwarp.c
new file mode 100644
index 0000000..f5bb9b1
--- /dev/null
+++ b/src/autofit/afwarp.c
@@ -0,0 +1,338 @@
+/***************************************************************************/
+/* */
+/* afwarp.c */
+/* */
+/* Auto-fitter warping algorithm (body). */
+/* */
+/* Copyright 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "afwarp.h"
+
+#ifdef AF_USE_WARPER
+
+#if 1
+ static const AF_WarpScore
+ af_warper_weights[64] =
+ {
+ 35, 32, 30, 25, 20, 15, 12, 10, 5, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -1, -2, -5, -8,-10,-10,-20,-20,-30,-30,
+
+ -30,-30,-20,-20,-10,-10, -8, -5, -2, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1, 5, 10, 12, 15, 20, 25, 30, 32,
+ };
+#else
+ static const AF_WarpScore
+ af_warper_weights[64] =
+ {
+ 30, 20, 10, 5, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -1, -2, -2, -5, -5,-10,-10,-15,-20,
+
+ -20,-15,-15,-10,-10, -5, -5, -2, -2, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 5, 10, 20,
+ };
+#endif
+
+
+ static void
+ af_warper_compute_line_best( AF_Warper warper,
+ FT_Fixed scale,
+ FT_Pos delta,
+ FT_Pos xx1,
+ FT_Pos xx2,
+ AF_WarpScore base_distort,
+ AF_Segment segments,
+ FT_UInt num_segments )
+ {
+ FT_Int idx_min, idx_max, idx0;
+ FT_UInt nn;
+ AF_WarpScore scores[65];
+
+
+ for ( nn = 0; nn < 65; nn++ )
+ scores[nn] = 0;
+
+ idx0 = xx1 - warper->t1;
+
+ /* compute minimum and maximum indices */
+ {
+ FT_Pos xx1min = warper->x1min;
+ FT_Pos xx1max = warper->x1max;
+ FT_Pos w = xx2 - xx1;
+
+
+ if ( xx1min + w < warper->x2min )
+ xx1min = warper->x2min - w;
+
+ xx1max = warper->x1max;
+ if ( xx1max + w > warper->x2max )
+ xx1max = warper->x2max - w;
+
+ idx_min = xx1min - warper->t1;
+ idx_max = xx1max - warper->t1;
+
+ if ( idx_min < 0 || idx_min > idx_max || idx_max > 64 )
+ {
+ AF_LOG(( "invalid indices:\n"
+ " min=%d max=%d, xx1=%ld xx2=%ld,\n"
+ " x1min=%ld x1max=%ld, x2min=%ld x2max=%ld\n",
+ idx_min, idx_max, xx1, xx2,
+ warper->x1min, warper->x1max,
+ warper->x2min, warper->x2max ));
+ return;
+ }
+ }
+
+ for ( nn = 0; nn < num_segments; nn++ )
+ {
+ FT_Pos len = segments[nn].max_coord - segments[nn].min_coord;
+ FT_Pos y0 = FT_MulFix( segments[nn].pos, scale ) + delta;
+ FT_Pos y = y0 + ( idx_min - idx0 );
+ FT_Int idx;
+
+
+ for ( idx = idx_min; idx <= idx_max; idx++, y++ )
+ scores[idx] += af_warper_weights[y & 63] * len;
+ }
+
+ /* find best score */
+ {
+ FT_Int idx;
+
+
+ for ( idx = idx_min; idx <= idx_max; idx++ )
+ {
+ AF_WarpScore score = scores[idx];
+ AF_WarpScore distort = base_distort + ( idx - idx0 );
+
+
+ if ( score > warper->best_score ||
+ ( score == warper->best_score &&
+ distort < warper->best_distort ) )
+ {
+ warper->best_score = score;
+ warper->best_distort = distort;
+ warper->best_scale = scale;
+ warper->best_delta = delta + ( idx - idx0 );
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ af_warper_compute( AF_Warper warper,
+ AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Fixed *a_scale,
+ FT_Pos *a_delta )
+ {
+ AF_AxisHints axis;
+ AF_Point points;
+
+ FT_Fixed org_scale;
+ FT_Pos org_delta;
+
+ FT_UInt nn, num_points, num_segments;
+ FT_Int X1, X2;
+ FT_Int w;
+
+ AF_WarpScore base_distort;
+ AF_Segment segments;
+
+
+ /* get original scaling transformation */
+ if ( dim == AF_DIMENSION_VERT )
+ {
+ org_scale = hints->y_scale;
+ org_delta = hints->y_delta;
+ }
+ else
+ {
+ org_scale = hints->x_scale;
+ org_delta = hints->x_delta;
+ }
+
+ warper->best_scale = org_scale;
+ warper->best_delta = org_delta;
+ warper->best_score = INT_MIN;
+ warper->best_distort = 0;
+
+ axis = &hints->axis[dim];
+ segments = axis->segments;
+ num_segments = axis->num_segments;
+ points = hints->points;
+ num_points = hints->num_points;
+
+ *a_scale = org_scale;
+ *a_delta = org_delta;
+
+ /* get X1 and X2, minimum and maximum in original coordinates */
+ if ( num_segments < 1 )
+ return;
+
+#if 1
+ X1 = X2 = points[0].fx;
+ for ( nn = 1; nn < num_points; nn++ )
+ {
+ FT_Int X = points[nn].fx;
+
+
+ if ( X < X1 )
+ X1 = X;
+ if ( X > X2 )
+ X2 = X;
+ }
+#else
+ X1 = X2 = segments[0].pos;
+ for ( nn = 1; nn < num_segments; nn++ )
+ {
+ FT_Int X = segments[nn].pos;
+
+
+ if ( X < X1 )
+ X1 = X;
+ if ( X > X2 )
+ X2 = X;
+ }
+#endif
+
+ if ( X1 >= X2 )
+ return;
+
+ warper->x1 = FT_MulFix( X1, org_scale ) + org_delta;
+ warper->x2 = FT_MulFix( X2, org_scale ) + org_delta;
+
+ warper->t1 = AF_WARPER_FLOOR( warper->x1 );
+ warper->t2 = AF_WARPER_CEIL( warper->x2 );
+
+ warper->x1min = warper->x1 & ~31;
+ warper->x1max = warper->x1min + 32;
+ warper->x2min = warper->x2 & ~31;
+ warper->x2max = warper->x2min + 32;
+
+ if ( warper->x1max > warper->x2 )
+ warper->x1max = warper->x2;
+
+ if ( warper->x2min < warper->x1 )
+ warper->x2min = warper->x1;
+
+ warper->w0 = warper->x2 - warper->x1;
+
+ if ( warper->w0 <= 64 )
+ {
+ warper->x1max = warper->x1;
+ warper->x2min = warper->x2;
+ }
+
+ warper->wmin = warper->x2min - warper->x1max;
+ warper->wmax = warper->x2max - warper->x1min;
+
+#if 1
+ {
+ int margin = 16;
+
+
+ if ( warper->w0 <= 128 )
+ {
+ margin = 8;
+ if ( warper->w0 <= 96 )
+ margin = 4;
+ }
+
+ if ( warper->wmin < warper->w0 - margin )
+ warper->wmin = warper->w0 - margin;
+
+ if ( warper->wmax > warper->w0 + margin )
+ warper->wmax = warper->w0 + margin;
+ }
+
+ if ( warper->wmin < warper->w0 * 3 / 4 )
+ warper->wmin = warper->w0 * 3 / 4;
+
+ if ( warper->wmax > warper->w0 * 5 / 4 )
+ warper->wmax = warper->w0 * 5 / 4;
+#else
+ /* no scaling, just translation */
+ warper->wmin = warper->wmax = warper->w0;
+#endif
+
+ for ( w = warper->wmin; w <= warper->wmax; w++ )
+ {
+ FT_Fixed new_scale;
+ FT_Pos new_delta;
+ FT_Pos xx1, xx2;
+
+
+ xx1 = warper->x1;
+ xx2 = warper->x2;
+ if ( w >= warper->w0 )
+ {
+ xx1 -= w - warper->w0;
+ if ( xx1 < warper->x1min )
+ {
+ xx2 += warper->x1min - xx1;
+ xx1 = warper->x1min;
+ }
+ }
+ else
+ {
+ xx1 -= w - warper->w0;
+ if ( xx1 > warper->x1max )
+ {
+ xx2 -= xx1 - warper->x1max;
+ xx1 = warper->x1max;
+ }
+ }
+
+ if ( xx1 < warper->x1 )
+ base_distort = warper->x1 - xx1;
+ else
+ base_distort = xx1 - warper->x1;
+
+ if ( xx2 < warper->x2 )
+ base_distort += warper->x2 - xx2;
+ else
+ base_distort += xx2 - warper->x2;
+
+ base_distort *= 10;
+
+ new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 );
+ new_delta = xx1 - FT_MulFix( X1, new_scale );
+
+ af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2,
+ base_distort,
+ segments, num_segments );
+ }
+
+ {
+ FT_Fixed best_scale = warper->best_scale;
+ FT_Pos best_delta = warper->best_delta;
+
+
+ hints->xmin_delta = FT_MulFix( X1, best_scale - org_scale )
+ + best_delta;
+ hints->xmax_delta = FT_MulFix( X2, best_scale - org_scale )
+ + best_delta;
+
+ *a_scale = best_scale;
+ *a_delta = best_delta;
+ }
+ }
+
+#else /* !AF_USE_WARPER */
+
+char af_warper_dummy = 0; /* make compiler happy */
+
+#endif /* !AF_USE_WARPER */
+
+/* END */
diff --git a/src/autofit/afwarp.h b/src/autofit/afwarp.h
new file mode 100644
index 0000000..7343fdd
--- /dev/null
+++ b/src/autofit/afwarp.h
@@ -0,0 +1,64 @@
+/***************************************************************************/
+/* */
+/* afwarp.h */
+/* */
+/* Auto-fitter warping algorithm (specification). */
+/* */
+/* Copyright 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFWARP_H__
+#define __AFWARP_H__
+
+#include "afhints.h"
+
+FT_BEGIN_HEADER
+
+#define AF_WARPER_SCALE
+
+#define AF_WARPER_FLOOR( x ) ( (x) & ~63 )
+#define AF_WARPER_CEIL( x ) AF_WARPER_FLOOR( (x) + 63 )
+
+
+ typedef FT_Int32 AF_WarpScore;
+
+ typedef struct AF_WarperRec_
+ {
+ FT_Pos x1, x2;
+ FT_Pos t1, t2;
+ FT_Pos x1min, x1max;
+ FT_Pos x2min, x2max;
+ FT_Pos w0, wmin, wmax;
+
+ FT_Fixed best_scale;
+ FT_Pos best_delta;
+ AF_WarpScore best_score;
+ AF_WarpScore best_distort;
+
+ } AF_WarperRec, *AF_Warper;
+
+
+ FT_LOCAL( void )
+ af_warper_compute( AF_Warper warper,
+ AF_GlyphHints hints,
+ AF_Dimension dim,
+ FT_Fixed *a_scale,
+ FT_Fixed *a_delta );
+
+
+FT_END_HEADER
+
+
+#endif /* __AFWARP_H__ */
+
+
+/* END */
diff --git a/src/autofit/autofit.c b/src/autofit/autofit.c
new file mode 100644
index 0000000..2fe66a9
--- /dev/null
+++ b/src/autofit/autofit.c
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* autofit.c */
+/* */
+/* Auto-fitter module (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+#include <ft2build.h>
+#include "afangles.c"
+#include "afglobal.c"
+#include "afhints.c"
+
+#include "afdummy.c"
+#include "aflatin.c"
+#ifdef FT_OPTION_AUTOFIT2
+#include "aflatin2.c"
+#endif
+#include "afcjk.c"
+#include "afindic.c"
+
+#include "afloader.c"
+#include "afmodule.c"
+
+#ifdef AF_USE_WARPER
+#include "afwarp.c"
+#endif
+
+/* END */
diff --git a/src/autofit/module.mk b/src/autofit/module.mk
new file mode 100644
index 0000000..4a386ce
--- /dev/null
+++ b/src/autofit/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 auto-fitter module definition
+#
+
+
+# Copyright 2003, 2004, 2005, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += AUTOFIT_MODULE
+
+define AUTOFIT_MODULE
+$(OPEN_DRIVER)autofit_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)autofit $(ECHO_DRIVER_DESC)automatic hinting module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/autofit/rules.mk b/src/autofit/rules.mk
new file mode 100644
index 0000000..017489d
--- /dev/null
+++ b/src/autofit/rules.mk
@@ -0,0 +1,78 @@
+#
+# FreeType 2 auto-fitter module configuration rules
+#
+
+
+# Copyright 2003, 2004, 2005, 2006, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# AUTOF driver directory
+#
+AUTOF_DIR := $(SRC_DIR)/autofit
+
+
+# compilation flags for the driver
+#
+AUTOF_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(AUTOF_DIR))
+
+
+# AUTOF driver sources (i.e., C files)
+#
+AUTOF_DRV_SRC := $(AUTOF_DIR)/afangles.c \
+ $(AUTOF_DIR)/afcjk.c \
+ $(AUTOF_DIR)/afdummy.c \
+ $(AUTOF_DIR)/afglobal.c \
+ $(AUTOF_DIR)/afhints.c \
+ $(AUTOF_DIR)/afindic.c \
+ $(AUTOF_DIR)/aflatin.c \
+ $(AUTOF_DIR)/afloader.c \
+ $(AUTOF_DIR)/afmodule.c \
+ $(AUTOF_DIR)/afwarp.c
+
+# AUTOF driver headers
+#
+AUTOF_DRV_H := $(AUTOF_DRV_SRC:%c=%h) \
+ $(AUTOF_DIR)/aftypes.h \
+ $(AUTOF_DIR)/aferrors.h
+
+
+# AUTOF driver object(s)
+#
+# AUTOF_DRV_OBJ_M is used during `multi' builds.
+# AUTOF_DRV_OBJ_S is used during `single' builds.
+#
+AUTOF_DRV_OBJ_M := $(AUTOF_DRV_SRC:$(AUTOF_DIR)/%.c=$(OBJ_DIR)/%.$O)
+AUTOF_DRV_OBJ_S := $(OBJ_DIR)/autofit.$O
+
+# AUTOF driver source file for single build
+#
+AUTOF_DRV_SRC_S := $(AUTOF_DIR)/autofit.c
+
+
+# AUTOF driver - single object
+#
+$(AUTOF_DRV_OBJ_S): $(AUTOF_DRV_SRC_S) $(AUTOF_DRV_SRC) \
+ $(FREETYPE_H) $(AUTOF_DRV_H)
+ $(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(AUTOF_DRV_SRC_S))
+
+
+# AUTOF driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(AUTOF_DIR)/%.c $(FREETYPE_H) $(AUTOF_DRV_H)
+ $(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(AUTOF_DRV_OBJ_S)
+DRV_OBJS_M += $(AUTOF_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/base/Jamfile b/src/base/Jamfile
new file mode 100644
index 0000000..aeffe38
--- /dev/null
+++ b/src/base/Jamfile
@@ -0,0 +1,50 @@
+# FreeType 2 src/base Jamfile
+#
+# Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) base ;
+
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = ftutil ftdbgmem ftstream ftcalc fttrigon ftgloadr ftoutln
+ ftobjs ftnames ftrfork ;
+ }
+ else
+ {
+ _sources = ftbase ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# Add the optional/replaceable files.
+#
+{
+ local _sources = system init glyph mm bdf
+ bbox debug xf86 type1 pfr
+ stroke winfnt otval bitmap synth
+ gxval lcdfil gasp patent
+ ;
+
+ Library $(FT2_LIB) : ft$(_sources).c ;
+}
+
+# Add Macintosh-specific file to the library when necessary.
+#
+if $(MAC)
+{
+ Library $(FT2_LIB) : ftmac.c ;
+}
+
+# end of src/base Jamfile
diff --git a/src/base/ftapi.c b/src/base/ftapi.c
new file mode 100644
index 0000000..8914d1f
--- /dev/null
+++ b/src/base/ftapi.c
@@ -0,0 +1,121 @@
+/***************************************************************************/
+/* */
+/* ftapi.c */
+/* */
+/* The FreeType compatibility functions (body). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_LIST_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_OUTLINE_H
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** C O M P A T I B I L I T Y ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* backwards compatibility API */
+
+ FT_BASE_DEF( void )
+ FT_New_Memory_Stream( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Stream stream )
+ {
+ FT_UNUSED( library );
+
+ FT_Stream_OpenMemory( stream, base, size );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Seek_Stream( FT_Stream stream,
+ FT_ULong pos )
+ {
+ return FT_Stream_Seek( stream, pos );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Skip_Stream( FT_Stream stream,
+ FT_Long distance )
+ {
+ return FT_Stream_Skip( stream, distance );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Read_Stream( FT_Stream stream,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ return FT_Stream_Read( stream, buffer, count );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Read_Stream_At( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ return FT_Stream_ReadAt( stream, pos, buffer, count );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Extract_Frame( FT_Stream stream,
+ FT_ULong count,
+ FT_Byte** pbytes )
+ {
+ return FT_Stream_ExtractFrame( stream, count, pbytes );
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Release_Frame( FT_Stream stream,
+ FT_Byte** pbytes )
+ {
+ FT_Stream_ReleaseFrame( stream, pbytes );
+ }
+
+ FT_BASE_DEF( FT_Error )
+ FT_Access_Frame( FT_Stream stream,
+ FT_ULong count )
+ {
+ return FT_Stream_EnterFrame( stream, count );
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Forget_Frame( FT_Stream stream )
+ {
+ FT_Stream_ExitFrame( stream );
+ }
+
+
+/* END */
diff --git a/src/base/ftbase.c b/src/base/ftbase.c
new file mode 100644
index 0000000..d176b81
--- /dev/null
+++ b/src/base/ftbase.c
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* ftbase.c */
+/* */
+/* Single object library component (body only). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include "ftcalc.c"
+#include "ftdbgmem.c"
+#include "ftgloadr.c"
+#include "ftnames.c"
+#include "ftobjs.c"
+#include "ftoutln.c"
+#include "ftrfork.c"
+#include "ftstream.c"
+#include "fttrigon.c"
+#include "ftutil.c"
+
+#if defined( __APPLE__ ) && !defined ( DARWIN_NO_CARBON )
+#include <ftmac.c>
+#endif
+
+/* END */
diff --git a/src/base/ftbbox.c b/src/base/ftbbox.c
new file mode 100644
index 0000000..532ab13
--- /dev/null
+++ b/src/base/ftbbox.c
@@ -0,0 +1,659 @@
+/***************************************************************************/
+/* */
+/* ftbbox.c */
+/* */
+/* FreeType bbox computation (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used */
+/* modified and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This component has a _single_ role: to compute exact outline bounding */
+ /* boxes. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_BBOX_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_CALC_H
+
+
+ typedef struct TBBox_Rec_
+ {
+ FT_Vector last;
+ FT_BBox bbox;
+
+ } TBBox_Rec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* BBox_Move_To */
+ /* */
+ /* <Description> */
+ /* This function is used as a `move_to' and `line_to' emitter during */
+ /* FT_Outline_Decompose(). It simply records the destination point */
+ /* in `user->last'; no further computations are necessary since we */
+ /* use the cbox as the starting bbox which must be refined. */
+ /* */
+ /* <Input> */
+ /* to :: A pointer to the destination vector. */
+ /* */
+ /* <InOut> */
+ /* user :: A pointer to the current walk context. */
+ /* */
+ /* <Return> */
+ /* Always 0. Needed for the interface only. */
+ /* */
+ static int
+ BBox_Move_To( FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ user->last = *to;
+
+ return 0;
+ }
+
+
+#define CHECK_X( p, bbox ) \
+ ( p->x < bbox.xMin || p->x > bbox.xMax )
+
+#define CHECK_Y( p, bbox ) \
+ ( p->y < bbox.yMin || p->y > bbox.yMax )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* BBox_Conic_Check */
+ /* */
+ /* <Description> */
+ /* Finds the extrema of a 1-dimensional conic Bezier curve and update */
+ /* a bounding range. This version uses direct computation, as it */
+ /* doesn't need square roots. */
+ /* */
+ /* <Input> */
+ /* y1 :: The start coordinate. */
+ /* */
+ /* y2 :: The coordinate of the control point. */
+ /* */
+ /* y3 :: The end coordinate. */
+ /* */
+ /* <InOut> */
+ /* min :: The address of the current minimum. */
+ /* */
+ /* max :: The address of the current maximum. */
+ /* */
+ static void
+ BBox_Conic_Check( FT_Pos y1,
+ FT_Pos y2,
+ FT_Pos y3,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ if ( y1 <= y3 && y2 == y1 ) /* flat arc */
+ goto Suite;
+
+ if ( y1 < y3 )
+ {
+ if ( y2 >= y1 && y2 <= y3 ) /* ascending arc */
+ goto Suite;
+ }
+ else
+ {
+ if ( y2 >= y3 && y2 <= y1 ) /* descending arc */
+ {
+ y2 = y1;
+ y1 = y3;
+ y3 = y2;
+ goto Suite;
+ }
+ }
+
+ y1 = y3 = y1 - FT_MulDiv( y2 - y1, y2 - y1, y1 - 2*y2 + y3 );
+
+ Suite:
+ if ( y1 < *min ) *min = y1;
+ if ( y3 > *max ) *max = y3;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* BBox_Conic_To */
+ /* */
+ /* <Description> */
+ /* This function is used as a `conic_to' emitter during */
+ /* FT_Raster_Decompose(). It checks a conic Bezier curve with the */
+ /* current bounding box, and computes its extrema if necessary to */
+ /* update it. */
+ /* */
+ /* <Input> */
+ /* control :: A pointer to a control point. */
+ /* */
+ /* to :: A pointer to the destination vector. */
+ /* */
+ /* <InOut> */
+ /* user :: The address of the current walk context. */
+ /* */
+ /* <Return> */
+ /* Always 0. Needed for the interface only. */
+ /* */
+ /* <Note> */
+ /* In the case of a non-monotonous arc, we compute directly the */
+ /* extremum coordinates, as it is sufficiently fast. */
+ /* */
+ static int
+ BBox_Conic_To( FT_Vector* control,
+ FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ /* we don't need to check `to' since it is always an `on' point, thus */
+ /* within the bbox */
+
+ if ( CHECK_X( control, user->bbox ) )
+ BBox_Conic_Check( user->last.x,
+ control->x,
+ to->x,
+ &user->bbox.xMin,
+ &user->bbox.xMax );
+
+ if ( CHECK_Y( control, user->bbox ) )
+ BBox_Conic_Check( user->last.y,
+ control->y,
+ to->y,
+ &user->bbox.yMin,
+ &user->bbox.yMax );
+
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* BBox_Cubic_Check */
+ /* */
+ /* <Description> */
+ /* Finds the extrema of a 1-dimensional cubic Bezier curve and */
+ /* updates a bounding range. This version uses splitting because we */
+ /* don't want to use square roots and extra accuracy. */
+ /* */
+ /* <Input> */
+ /* p1 :: The start coordinate. */
+ /* */
+ /* p2 :: The coordinate of the first control point. */
+ /* */
+ /* p3 :: The coordinate of the second control point. */
+ /* */
+ /* p4 :: The end coordinate. */
+ /* */
+ /* <InOut> */
+ /* min :: The address of the current minimum. */
+ /* */
+ /* max :: The address of the current maximum. */
+ /* */
+
+#if 0
+
+ static void
+ BBox_Cubic_Check( FT_Pos p1,
+ FT_Pos p2,
+ FT_Pos p3,
+ FT_Pos p4,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ FT_Pos stack[32*3 + 1], *arc;
+
+
+ arc = stack;
+
+ arc[0] = p1;
+ arc[1] = p2;
+ arc[2] = p3;
+ arc[3] = p4;
+
+ do
+ {
+ FT_Pos y1 = arc[0];
+ FT_Pos y2 = arc[1];
+ FT_Pos y3 = arc[2];
+ FT_Pos y4 = arc[3];
+
+
+ if ( y1 == y4 )
+ {
+ if ( y1 == y2 && y1 == y3 ) /* flat */
+ goto Test;
+ }
+ else if ( y1 < y4 )
+ {
+ if ( y2 >= y1 && y2 <= y4 && y3 >= y1 && y3 <= y4 ) /* ascending */
+ goto Test;
+ }
+ else
+ {
+ if ( y2 >= y4 && y2 <= y1 && y3 >= y4 && y3 <= y1 ) /* descending */
+ {
+ y2 = y1;
+ y1 = y4;
+ y4 = y2;
+ goto Test;
+ }
+ }
+
+ /* unknown direction -- split the arc in two */
+ arc[6] = y4;
+ arc[1] = y1 = ( y1 + y2 ) / 2;
+ arc[5] = y4 = ( y4 + y3 ) / 2;
+ y2 = ( y2 + y3 ) / 2;
+ arc[2] = y1 = ( y1 + y2 ) / 2;
+ arc[4] = y4 = ( y4 + y2 ) / 2;
+ arc[3] = ( y1 + y4 ) / 2;
+
+ arc += 3;
+ goto Suite;
+
+ Test:
+ if ( y1 < *min ) *min = y1;
+ if ( y4 > *max ) *max = y4;
+ arc -= 3;
+
+ Suite:
+ ;
+ } while ( arc >= stack );
+ }
+
+#else
+
+ static void
+ test_cubic_extrema( FT_Pos y1,
+ FT_Pos y2,
+ FT_Pos y3,
+ FT_Pos y4,
+ FT_Fixed u,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ /* FT_Pos a = y4 - 3*y3 + 3*y2 - y1; */
+ FT_Pos b = y3 - 2*y2 + y1;
+ FT_Pos c = y2 - y1;
+ FT_Pos d = y1;
+ FT_Pos y;
+ FT_Fixed uu;
+
+ FT_UNUSED ( y4 );
+
+
+ /* The polynomial is */
+ /* */
+ /* P(x) = a*x^3 + 3b*x^2 + 3c*x + d , */
+ /* */
+ /* dP/dx = 3a*x^2 + 6b*x + 3c . */
+ /* */
+ /* However, we also have */
+ /* */
+ /* dP/dx(u) = 0 , */
+ /* */
+ /* which implies by subtraction that */
+ /* */
+ /* P(u) = b*u^2 + 2c*u + d . */
+
+ if ( u > 0 && u < 0x10000L )
+ {
+ uu = FT_MulFix( u, u );
+ y = d + FT_MulFix( c, 2*u ) + FT_MulFix( b, uu );
+
+ if ( y < *min ) *min = y;
+ if ( y > *max ) *max = y;
+ }
+ }
+
+
+ static void
+ BBox_Cubic_Check( FT_Pos y1,
+ FT_Pos y2,
+ FT_Pos y3,
+ FT_Pos y4,
+ FT_Pos* min,
+ FT_Pos* max )
+ {
+ /* always compare first and last points */
+ if ( y1 < *min ) *min = y1;
+ else if ( y1 > *max ) *max = y1;
+
+ if ( y4 < *min ) *min = y4;
+ else if ( y4 > *max ) *max = y4;
+
+ /* now, try to see if there are split points here */
+ if ( y1 <= y4 )
+ {
+ /* flat or ascending arc test */
+ if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 )
+ return;
+ }
+ else /* y1 > y4 */
+ {
+ /* descending arc test */
+ if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 )
+ return;
+ }
+
+ /* There are some split points. Find them. */
+ {
+ FT_Pos a = y4 - 3*y3 + 3*y2 - y1;
+ FT_Pos b = y3 - 2*y2 + y1;
+ FT_Pos c = y2 - y1;
+ FT_Pos d;
+ FT_Fixed t;
+
+
+ /* We need to solve `ax^2+2bx+c' here, without floating points! */
+ /* The trick is to normalize to a different representation in order */
+ /* to use our 16.16 fixed point routines. */
+ /* */
+ /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */
+ /* These values must fit into a single 16.16 value. */
+ /* */
+ /* We normalize a, b, and c to `8.16' fixed float values to ensure */
+ /* that its product is held in a `16.16' value. */
+
+ {
+ FT_ULong t1, t2;
+ int shift = 0;
+
+
+ /* The following computation is based on the fact that for */
+ /* any value `y', if `n' is the position of the most */
+ /* significant bit of `abs(y)' (starting from 0 for the */
+ /* least significant bit), then `y' is in the range */
+ /* */
+ /* -2^n..2^n-1 */
+ /* */
+ /* We want to shift `a', `b', and `c' concurrently in order */
+ /* to ensure that they all fit in 8.16 values, which maps */
+ /* to the integer range `-2^23..2^23-1'. */
+ /* */
+ /* Necessarily, we need to shift `a', `b', and `c' so that */
+ /* the most significant bit of its absolute values is at */
+ /* _most_ at position 23. */
+ /* */
+ /* We begin by computing `t1' as the bitwise `OR' of the */
+ /* absolute values of `a', `b', `c'. */
+
+ t1 = (FT_ULong)( ( a >= 0 ) ? a : -a );
+ t2 = (FT_ULong)( ( b >= 0 ) ? b : -b );
+ t1 |= t2;
+ t2 = (FT_ULong)( ( c >= 0 ) ? c : -c );
+ t1 |= t2;
+
+ /* Now we can be sure that the most significant bit of `t1' */
+ /* is the most significant bit of either `a', `b', or `c', */
+ /* depending on the greatest integer range of the particular */
+ /* variable. */
+ /* */
+ /* Next, we compute the `shift', by shifting `t1' as many */
+ /* times as necessary to move its MSB to position 23. This */
+ /* corresponds to a value of `t1' that is in the range */
+ /* 0x40_0000..0x7F_FFFF. */
+ /* */
+ /* Finally, we shift `a', `b', and `c' by the same amount. */
+ /* This ensures that all values are now in the range */
+ /* -2^23..2^23, i.e., they are now expressed as 8.16 */
+ /* fixed-float numbers. This also means that we are using */
+ /* 24 bits of precision to compute the zeros, independently */
+ /* of the range of the original polynomial coefficients. */
+ /* */
+ /* This algorithm should ensure reasonably accurate values */
+ /* for the zeros. Note that they are only expressed with */
+ /* 16 bits when computing the extrema (the zeros need to */
+ /* be in 0..1 exclusive to be considered part of the arc). */
+
+ if ( t1 == 0 ) /* all coefficients are 0! */
+ return;
+
+ if ( t1 > 0x7FFFFFUL )
+ {
+ do
+ {
+ shift++;
+ t1 >>= 1;
+
+ } while ( t1 > 0x7FFFFFUL );
+
+ /* this loses some bits of precision, but we use 24 of them */
+ /* for the computation anyway */
+ a >>= shift;
+ b >>= shift;
+ c >>= shift;
+ }
+ else if ( t1 < 0x400000UL )
+ {
+ do
+ {
+ shift++;
+ t1 <<= 1;
+
+ } while ( t1 < 0x400000UL );
+
+ a <<= shift;
+ b <<= shift;
+ c <<= shift;
+ }
+ }
+
+ /* handle a == 0 */
+ if ( a == 0 )
+ {
+ if ( b != 0 )
+ {
+ t = - FT_DivFix( c, b ) / 2;
+ test_cubic_extrema( y1, y2, y3, y4, t, min, max );
+ }
+ }
+ else
+ {
+ /* solve the equation now */
+ d = FT_MulFix( b, b ) - FT_MulFix( a, c );
+ if ( d < 0 )
+ return;
+
+ if ( d == 0 )
+ {
+ /* there is a single split point at -b/a */
+ t = - FT_DivFix( b, a );
+ test_cubic_extrema( y1, y2, y3, y4, t, min, max );
+ }
+ else
+ {
+ /* there are two solutions; we need to filter them */
+ d = FT_SqrtFixed( (FT_Int32)d );
+ t = - FT_DivFix( b - d, a );
+ test_cubic_extrema( y1, y2, y3, y4, t, min, max );
+
+ t = - FT_DivFix( b + d, a );
+ test_cubic_extrema( y1, y2, y3, y4, t, min, max );
+ }
+ }
+ }
+ }
+
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* BBox_Cubic_To */
+ /* */
+ /* <Description> */
+ /* This function is used as a `cubic_to' emitter during */
+ /* FT_Raster_Decompose(). It checks a cubic Bezier curve with the */
+ /* current bounding box, and computes its extrema if necessary to */
+ /* update it. */
+ /* */
+ /* <Input> */
+ /* control1 :: A pointer to the first control point. */
+ /* */
+ /* control2 :: A pointer to the second control point. */
+ /* */
+ /* to :: A pointer to the destination vector. */
+ /* */
+ /* <InOut> */
+ /* user :: The address of the current walk context. */
+ /* */
+ /* <Return> */
+ /* Always 0. Needed for the interface only. */
+ /* */
+ /* <Note> */
+ /* In the case of a non-monotonous arc, we don't compute directly */
+ /* extremum coordinates, we subdivide instead. */
+ /* */
+ static int
+ BBox_Cubic_To( FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to,
+ TBBox_Rec* user )
+ {
+ /* we don't need to check `to' since it is always an `on' point, thus */
+ /* within the bbox */
+
+ if ( CHECK_X( control1, user->bbox ) ||
+ CHECK_X( control2, user->bbox ) )
+ BBox_Cubic_Check( user->last.x,
+ control1->x,
+ control2->x,
+ to->x,
+ &user->bbox.xMin,
+ &user->bbox.xMax );
+
+ if ( CHECK_Y( control1, user->bbox ) ||
+ CHECK_Y( control2, user->bbox ) )
+ BBox_Cubic_Check( user->last.y,
+ control1->y,
+ control2->y,
+ to->y,
+ &user->bbox.yMin,
+ &user->bbox.yMax );
+
+ user->last = *to;
+
+ return 0;
+ }
+
+
+ /* documentation is in ftbbox.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Get_BBox( FT_Outline* outline,
+ FT_BBox *abbox )
+ {
+ FT_BBox cbox;
+ FT_BBox bbox;
+ FT_Vector* vec;
+ FT_UShort n;
+
+
+ if ( !abbox )
+ return FT_Err_Invalid_Argument;
+
+ if ( !outline )
+ return FT_Err_Invalid_Outline;
+
+ /* if outline is empty, return (0,0,0,0) */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ {
+ abbox->xMin = abbox->xMax = 0;
+ abbox->yMin = abbox->yMax = 0;
+ return 0;
+ }
+
+ /* We compute the control box as well as the bounding box of */
+ /* all `on' points in the outline. Then, if the two boxes */
+ /* coincide, we exit immediately. */
+
+ vec = outline->points;
+ bbox.xMin = bbox.xMax = cbox.xMin = cbox.xMax = vec->x;
+ bbox.yMin = bbox.yMax = cbox.yMin = cbox.yMax = vec->y;
+ vec++;
+
+ for ( n = 1; n < outline->n_points; n++ )
+ {
+ FT_Pos x = vec->x;
+ FT_Pos y = vec->y;
+
+
+ /* update control box */
+ if ( x < cbox.xMin ) cbox.xMin = x;
+ if ( x > cbox.xMax ) cbox.xMax = x;
+
+ if ( y < cbox.yMin ) cbox.yMin = y;
+ if ( y > cbox.yMax ) cbox.yMax = y;
+
+ if ( FT_CURVE_TAG( outline->tags[n] ) == FT_CURVE_TAG_ON )
+ {
+ /* update bbox for `on' points only */
+ if ( x < bbox.xMin ) bbox.xMin = x;
+ if ( x > bbox.xMax ) bbox.xMax = x;
+
+ if ( y < bbox.yMin ) bbox.yMin = y;
+ if ( y > bbox.yMax ) bbox.yMax = y;
+ }
+
+ vec++;
+ }
+
+ /* test two boxes for equality */
+ if ( cbox.xMin < bbox.xMin || cbox.xMax > bbox.xMax ||
+ cbox.yMin < bbox.yMin || cbox.yMax > bbox.yMax )
+ {
+ /* the two boxes are different, now walk over the outline to */
+ /* get the Bezier arc extrema. */
+
+ static const FT_Outline_Funcs bbox_interface =
+ {
+ (FT_Outline_MoveTo_Func) BBox_Move_To,
+ (FT_Outline_LineTo_Func) BBox_Move_To,
+ (FT_Outline_ConicTo_Func)BBox_Conic_To,
+ (FT_Outline_CubicTo_Func)BBox_Cubic_To,
+ 0, 0
+ };
+
+ FT_Error error;
+ TBBox_Rec user;
+
+
+ user.bbox = bbox;
+
+ error = FT_Outline_Decompose( outline, &bbox_interface, &user );
+ if ( error )
+ return error;
+
+ *abbox = user.bbox;
+ }
+ else
+ *abbox = bbox;
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/base/ftbdf.c b/src/base/ftbdf.c
new file mode 100644
index 0000000..d29adf0
--- /dev/null
+++ b/src/base/ftbdf.c
@@ -0,0 +1,88 @@
+/***************************************************************************/
+/* */
+/* ftbdf.c */
+/* */
+/* FreeType API for accessing BDF-specific strings (body). */
+/* */
+/* Copyright 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_BDF_H
+
+
+ /* documentation is in ftbdf.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_BDF_Charset_ID( FT_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ FT_Error error;
+ const char* encoding = NULL;
+ const char* registry = NULL;
+
+
+ error = FT_Err_Invalid_Argument;
+
+ if ( face )
+ {
+ FT_Service_BDF service;
+
+
+ FT_FACE_FIND_SERVICE( face, service, BDF );
+
+ if ( service && service->get_charset_id )
+ error = service->get_charset_id( face, &encoding, &registry );
+ }
+
+ if ( acharset_encoding )
+ *acharset_encoding = encoding;
+
+ if ( acharset_registry )
+ *acharset_registry = registry;
+
+ return error;
+ }
+
+
+ /* documentation is in ftbdf.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_BDF_Property( FT_Face face,
+ const char* prop_name,
+ BDF_PropertyRec *aproperty )
+ {
+ FT_Error error;
+
+
+ error = FT_Err_Invalid_Argument;
+
+ aproperty->type = BDF_PROPERTY_TYPE_NONE;
+
+ if ( face )
+ {
+ FT_Service_BDF service;
+
+
+ FT_FACE_FIND_SERVICE( face, service, BDF );
+
+ if ( service && service->get_property )
+ error = service->get_property( face, prop_name, aproperty );
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftbitmap.c b/src/base/ftbitmap.c
new file mode 100644
index 0000000..4c1cdf2
--- /dev/null
+++ b/src/base/ftbitmap.c
@@ -0,0 +1,630 @@
+/***************************************************************************/
+/* */
+/* ftbitmap.c */
+/* */
+/* FreeType utility functions for converting 1bpp, 2bpp, 4bpp, and 8bpp */
+/* bitmaps into 8bpp format (body). */
+/* */
+/* Copyright 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_BITMAP_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+ static
+ const FT_Bitmap null_bitmap = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Bitmap_New( FT_Bitmap *abitmap )
+ {
+ *abitmap = null_bitmap;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Copy( FT_Library library,
+ const FT_Bitmap *source,
+ FT_Bitmap *target)
+ {
+ FT_Memory memory = library->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Int pitch = source->pitch;
+ FT_ULong size;
+
+
+ if ( source == target )
+ return FT_Err_Ok;
+
+ if ( source->buffer == NULL )
+ {
+ *target = *source;
+
+ return FT_Err_Ok;
+ }
+
+ if ( pitch < 0 )
+ pitch = -pitch;
+ size = (FT_ULong)( pitch * source->rows );
+
+ if ( target->buffer )
+ {
+ FT_Int target_pitch = target->pitch;
+ FT_ULong target_size;
+
+
+ if ( target_pitch < 0 )
+ target_pitch = -target_pitch;
+ target_size = (FT_ULong)( target_pitch * target->rows );
+
+ if ( target_size != size )
+ (void)FT_QREALLOC( target->buffer, target_size, size );
+ }
+ else
+ (void)FT_QALLOC( target->buffer, size );
+
+ if ( !error )
+ {
+ unsigned char *p;
+
+
+ p = target->buffer;
+ *target = *source;
+ target->buffer = p;
+
+ FT_MEM_COPY( target->buffer, source->buffer, size );
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_bitmap_assure_buffer( FT_Memory memory,
+ FT_Bitmap* bitmap,
+ FT_UInt xpixels,
+ FT_UInt ypixels )
+ {
+ FT_Error error;
+ int pitch;
+ int new_pitch;
+ FT_UInt bpp;
+ FT_Int i, width, height;
+ unsigned char* buffer;
+
+
+ width = bitmap->width;
+ height = bitmap->rows;
+ pitch = bitmap->pitch;
+ if ( pitch < 0 )
+ pitch = -pitch;
+
+ switch ( bitmap->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ bpp = 1;
+ new_pitch = ( width + xpixels + 7 ) >> 3;
+ break;
+ case FT_PIXEL_MODE_GRAY2:
+ bpp = 2;
+ new_pitch = ( width + xpixels + 3 ) >> 2;
+ break;
+ case FT_PIXEL_MODE_GRAY4:
+ bpp = 4;
+ new_pitch = ( width + xpixels + 1 ) >> 1;
+ break;
+ case FT_PIXEL_MODE_GRAY:
+ case FT_PIXEL_MODE_LCD:
+ case FT_PIXEL_MODE_LCD_V:
+ bpp = 8;
+ new_pitch = ( width + xpixels );
+ break;
+ default:
+ return FT_Err_Invalid_Glyph_Format;
+ }
+
+ /* if no need to allocate memory */
+ if ( ypixels == 0 && new_pitch <= pitch )
+ {
+ /* zero the padding */
+ FT_Int bit_width = pitch * 8;
+ FT_Int bit_last = ( width + xpixels ) * bpp;
+
+
+ if ( bit_last < bit_width )
+ {
+ FT_Byte* line = bitmap->buffer + ( bit_last >> 3 );
+ FT_Byte* end = bitmap->buffer + pitch;
+ FT_Int shift = bit_last & 7;
+ FT_UInt mask = 0xFF00U >> shift;
+ FT_Int count = height;
+
+
+ for ( ; count > 0; count--, line += pitch, end += pitch )
+ {
+ FT_Byte* write = line;
+
+
+ if ( shift > 0 )
+ {
+ write[0] = (FT_Byte)( write[0] & mask );
+ write++;
+ }
+ if ( write < end )
+ FT_MEM_ZERO( write, end-write );
+ }
+ }
+
+ return FT_Err_Ok;
+ }
+
+ if ( FT_QALLOC_MULT( buffer, new_pitch, bitmap->rows + ypixels ) )
+ return error;
+
+ if ( bitmap->pitch > 0 )
+ {
+ FT_Int len = ( width * bpp + 7 ) >> 3;
+
+
+ for ( i = 0; i < bitmap->rows; i++ )
+ FT_MEM_COPY( buffer + new_pitch * ( ypixels + i ),
+ bitmap->buffer + pitch * i, len );
+ }
+ else
+ {
+ FT_Int len = ( width * bpp + 7 ) >> 3;
+
+
+ for ( i = 0; i < bitmap->rows; i++ )
+ FT_MEM_COPY( buffer + new_pitch * i,
+ bitmap->buffer + pitch * i, len );
+ }
+
+ FT_FREE( bitmap->buffer );
+ bitmap->buffer = buffer;
+
+ if ( bitmap->pitch < 0 )
+ new_pitch = -new_pitch;
+
+ /* set pitch only, width and height are left untouched */
+ bitmap->pitch = new_pitch;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Embolden( FT_Library library,
+ FT_Bitmap* bitmap,
+ FT_Pos xStrength,
+ FT_Pos yStrength )
+ {
+ FT_Error error;
+ unsigned char* p;
+ FT_Int i, x, y, pitch;
+ FT_Int xstr, ystr;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !bitmap || !bitmap->buffer )
+ return FT_Err_Invalid_Argument;
+
+ xstr = FT_PIX_ROUND( xStrength ) >> 6;
+ ystr = FT_PIX_ROUND( yStrength ) >> 6;
+
+ if ( xstr == 0 && ystr == 0 )
+ return FT_Err_Ok;
+ else if ( xstr < 0 || ystr < 0 )
+ return FT_Err_Invalid_Argument;
+
+ switch ( bitmap->pixel_mode )
+ {
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ {
+ FT_Bitmap tmp;
+ FT_Int align;
+
+
+ if ( bitmap->pixel_mode == FT_PIXEL_MODE_GRAY2 )
+ align = ( bitmap->width + xstr + 3 ) / 4;
+ else
+ align = ( bitmap->width + xstr + 1 ) / 2;
+
+ FT_Bitmap_New( &tmp );
+
+ error = FT_Bitmap_Convert( library, bitmap, &tmp, align );
+ if ( error )
+ return error;
+
+ FT_Bitmap_Done( library, bitmap );
+ *bitmap = tmp;
+ }
+ break;
+
+ case FT_PIXEL_MODE_MONO:
+ if ( xstr > 8 )
+ xstr = 8;
+ break;
+
+ case FT_PIXEL_MODE_LCD:
+ xstr *= 3;
+ break;
+
+ case FT_PIXEL_MODE_LCD_V:
+ ystr *= 3;
+ break;
+ }
+
+ error = ft_bitmap_assure_buffer( library->memory, bitmap, xstr, ystr );
+ if ( error )
+ return error;
+
+ pitch = bitmap->pitch;
+ if ( pitch > 0 )
+ p = bitmap->buffer + pitch * ystr;
+ else
+ {
+ pitch = -pitch;
+ p = bitmap->buffer + pitch * ( bitmap->rows - 1 );
+ }
+
+ /* for each row */
+ for ( y = 0; y < bitmap->rows ; y++ )
+ {
+ /*
+ * Horizontally:
+ *
+ * From the last pixel on, make each pixel or'ed with the
+ * `xstr' pixels before it.
+ */
+ for ( x = pitch - 1; x >= 0; x-- )
+ {
+ unsigned char tmp;
+
+
+ tmp = p[x];
+ for ( i = 1; i <= xstr; i++ )
+ {
+ if ( bitmap->pixel_mode == FT_PIXEL_MODE_MONO )
+ {
+ p[x] |= tmp >> i;
+
+ /* the maximum value of 8 for `xstr' comes from here */
+ if ( x > 0 )
+ p[x] |= p[x - 1] << ( 8 - i );
+
+#if 0
+ if ( p[x] == 0xff )
+ break;
+#endif
+ }
+ else
+ {
+ if ( x - i >= 0 )
+ {
+ if ( p[x] + p[x - i] > bitmap->num_grays - 1 )
+ {
+ p[x] = (unsigned char)(bitmap->num_grays - 1);
+ break;
+ }
+ else
+ {
+ p[x] = (unsigned char)(p[x] + p[x-i]);
+ if ( p[x] == bitmap->num_grays - 1 )
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ /*
+ * Vertically:
+ *
+ * Make the above `ystr' rows or'ed with it.
+ */
+ for ( x = 1; x <= ystr; x++ )
+ {
+ unsigned char* q;
+
+
+ q = p - bitmap->pitch * x;
+ for ( i = 0; i < pitch; i++ )
+ q[i] |= p[i];
+ }
+
+ p += bitmap->pitch;
+ }
+
+ bitmap->width += xstr;
+ bitmap->rows += ystr;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Convert( FT_Library library,
+ const FT_Bitmap *source,
+ FT_Bitmap *target,
+ FT_Int alignment )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ memory = library->memory;
+
+ switch ( source->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ case FT_PIXEL_MODE_GRAY:
+ case FT_PIXEL_MODE_GRAY2:
+ case FT_PIXEL_MODE_GRAY4:
+ {
+ FT_Int pad;
+ FT_Long old_size;
+
+
+ old_size = target->rows * target->pitch;
+ if ( old_size < 0 )
+ old_size = -old_size;
+
+ target->pixel_mode = FT_PIXEL_MODE_GRAY;
+ target->rows = source->rows;
+ target->width = source->width;
+
+ pad = 0;
+ if ( alignment > 0 )
+ {
+ pad = source->width % alignment;
+ if ( pad != 0 )
+ pad = alignment - pad;
+ }
+
+ target->pitch = source->width + pad;
+
+ if ( target->rows * target->pitch > old_size &&
+ FT_QREALLOC( target->buffer,
+ old_size, target->rows * target->pitch ) )
+ return error;
+ }
+ break;
+
+ default:
+ error = FT_Err_Invalid_Argument;
+ }
+
+ switch ( source->pixel_mode )
+ {
+ case FT_PIXEL_MODE_MONO:
+ {
+ FT_Byte* s = source->buffer;
+ FT_Byte* t = target->buffer;
+ FT_Int i;
+
+
+ target->num_grays = 2;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_Int j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 3; j > 0; j-- )
+ {
+ FT_Int val = ss[0]; /* avoid a byte->int cast on each line */
+
+
+ tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7 );
+ tt[1] = (FT_Byte)( ( val & 0x40 ) >> 6 );
+ tt[2] = (FT_Byte)( ( val & 0x20 ) >> 5 );
+ tt[3] = (FT_Byte)( ( val & 0x10 ) >> 4 );
+ tt[4] = (FT_Byte)( ( val & 0x08 ) >> 3 );
+ tt[5] = (FT_Byte)( ( val & 0x04 ) >> 2 );
+ tt[6] = (FT_Byte)( ( val & 0x02 ) >> 1 );
+ tt[7] = (FT_Byte)( val & 0x01 );
+
+ tt += 8;
+ ss += 1;
+ }
+
+ /* get remaining pixels (if any) */
+ j = source->width & 7;
+ if ( j > 0 )
+ {
+ FT_Int val = *ss;
+
+
+ for ( ; j > 0; j-- )
+ {
+ tt[0] = (FT_Byte)( ( val & 0x80 ) >> 7);
+ val <<= 1;
+ tt += 1;
+ }
+ }
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY:
+ {
+ FT_Int width = source->width;
+ FT_Byte* s = source->buffer;
+ FT_Byte* t = target->buffer;
+ FT_Int s_pitch = source->pitch;
+ FT_Int t_pitch = target->pitch;
+ FT_Int i;
+
+
+ target->num_grays = 256;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_ARRAY_COPY( t, s, width );
+
+ s += s_pitch;
+ t += t_pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY2:
+ {
+ FT_Byte* s = source->buffer;
+ FT_Byte* t = target->buffer;
+ FT_Int i;
+
+
+ target->num_grays = 4;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_Int j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 2; j > 0; j-- )
+ {
+ FT_Int val = ss[0];
+
+
+ tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 );
+ tt[1] = (FT_Byte)( ( val & 0x30 ) >> 4 );
+ tt[2] = (FT_Byte)( ( val & 0x0C ) >> 2 );
+ tt[3] = (FT_Byte)( ( val & 0x03 ) );
+
+ ss += 1;
+ tt += 4;
+ }
+
+ j = source->width & 3;
+ if ( j > 0 )
+ {
+ FT_Int val = ss[0];
+
+
+ for ( ; j > 0; j-- )
+ {
+ tt[0] = (FT_Byte)( ( val & 0xC0 ) >> 6 );
+ val <<= 2;
+ tt += 1;
+ }
+ }
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ case FT_PIXEL_MODE_GRAY4:
+ {
+ FT_Byte* s = source->buffer;
+ FT_Byte* t = target->buffer;
+ FT_Int i;
+
+
+ target->num_grays = 16;
+
+ for ( i = source->rows; i > 0; i-- )
+ {
+ FT_Byte* ss = s;
+ FT_Byte* tt = t;
+ FT_Int j;
+
+
+ /* get the full bytes */
+ for ( j = source->width >> 1; j > 0; j-- )
+ {
+ FT_Int val = ss[0];
+
+
+ tt[0] = (FT_Byte)( ( val & 0xF0 ) >> 4 );
+ tt[1] = (FT_Byte)( ( val & 0x0F ) );
+
+ ss += 1;
+ tt += 2;
+ }
+
+ if ( source->width & 1 )
+ tt[0] = (FT_Byte)( ( ss[0] & 0xF0 ) >> 4 );
+
+ s += source->pitch;
+ t += target->pitch;
+ }
+ }
+ break;
+
+
+ default:
+ ;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftbitmap.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Bitmap_Done( FT_Library library,
+ FT_Bitmap *bitmap )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !bitmap )
+ return FT_Err_Invalid_Argument;
+
+ memory = library->memory;
+
+ FT_FREE( bitmap->buffer );
+ *bitmap = null_bitmap;
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/base/ftcalc.c b/src/base/ftcalc.c
new file mode 100644
index 0000000..63aed95
--- /dev/null
+++ b/src/base/ftcalc.c
@@ -0,0 +1,822 @@
+/***************************************************************************/
+/* */
+/* ftcalc.c */
+/* */
+/* Arithmetic computations (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Support for 1-complement arithmetic has been totally dropped in this */
+ /* release. You can still write your own code if you need it. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Implementing basic computation routines. */
+ /* */
+ /* FT_MulDiv(), FT_MulFix(), FT_DivFix(), FT_RoundFix(), FT_CeilFix(), */
+ /* and FT_FloorFix() are declared in freetype.h. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+/* we need to define a 64-bits data type here */
+
+#ifdef FT_LONG64
+
+ typedef FT_INT64 FT_Int64;
+
+#else
+
+ typedef struct FT_Int64_
+ {
+ FT_UInt32 lo;
+ FT_UInt32 hi;
+
+ } FT_Int64;
+
+#endif /* FT_LONG64 */
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_calc
+
+
+ /* The following three functions are available regardless of whether */
+ /* FT_LONG64 is defined. */
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_RoundFix( FT_Fixed a )
+ {
+ return ( a >= 0 ) ? ( a + 0x8000L ) & ~0xFFFFL
+ : -((-a + 0x8000L ) & ~0xFFFFL );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_CeilFix( FT_Fixed a )
+ {
+ return ( a >= 0 ) ? ( a + 0xFFFFL ) & ~0xFFFFL
+ : -((-a + 0xFFFFL ) & ~0xFFFFL );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_FloorFix( FT_Fixed a )
+ {
+ return ( a >= 0 ) ? a & ~0xFFFFL
+ : -((-a) & ~0xFFFFL );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /* documentation is in ftcalc.h */
+
+ FT_EXPORT_DEF( FT_Int32 )
+ FT_Sqrt32( FT_Int32 x )
+ {
+ FT_ULong val, root, newroot, mask;
+
+
+ root = 0;
+ mask = 0x40000000L;
+ val = (FT_ULong)x;
+
+ do
+ {
+ newroot = root + mask;
+ if ( newroot <= val )
+ {
+ val -= newroot;
+ root = newroot + mask;
+ }
+
+ root >>= 1;
+ mask >>= 2;
+
+ } while ( mask != 0 );
+
+ return root;
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+#ifdef FT_LONG64
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulDiv( FT_Long a,
+ FT_Long b,
+ FT_Long c )
+ {
+ FT_Int s;
+ FT_Long d;
+
+
+ s = 1;
+ if ( a < 0 ) { a = -a; s = -1; }
+ if ( b < 0 ) { b = -b; s = -s; }
+ if ( c < 0 ) { c = -c; s = -s; }
+
+ d = (FT_Long)( c > 0 ? ( (FT_Int64)a * b + ( c >> 1 ) ) / c
+ : 0x7FFFFFFFL );
+
+ return ( s > 0 ) ? d : -d;
+ }
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Long )
+ FT_MulDiv_No_Round( FT_Long a,
+ FT_Long b,
+ FT_Long c )
+ {
+ FT_Int s;
+ FT_Long d;
+
+
+ s = 1;
+ if ( a < 0 ) { a = -a; s = -1; }
+ if ( b < 0 ) { b = -b; s = -s; }
+ if ( c < 0 ) { c = -c; s = -s; }
+
+ d = (FT_Long)( c > 0 ? (FT_Int64)a * b / c
+ : 0x7FFFFFFFL );
+
+ return ( s > 0 ) ? d : -d;
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulFix( FT_Long a,
+ FT_Long b )
+ {
+ FT_Int s = 1;
+ FT_Long c;
+
+
+ if ( a < 0 ) { a = -a; s = -1; }
+ if ( b < 0 ) { b = -b; s = -s; }
+
+ c = (FT_Long)( ( (FT_Int64)a * b + 0x8000L ) >> 16 );
+ return ( s > 0 ) ? c : -c ;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_DivFix( FT_Long a,
+ FT_Long b )
+ {
+ FT_Int32 s;
+ FT_UInt32 q;
+
+ s = 1;
+ if ( a < 0 ) { a = -a; s = -1; }
+ if ( b < 0 ) { b = -b; s = -s; }
+
+ if ( b == 0 )
+ /* check for division by 0 */
+ q = 0x7FFFFFFFL;
+ else
+ /* compute result directly */
+ q = (FT_UInt32)( ( ( (FT_Int64)a << 16 ) + ( b >> 1 ) ) / b );
+
+ return ( s < 0 ? -(FT_Long)q : (FT_Long)q );
+ }
+
+
+#else /* !FT_LONG64 */
+
+
+ static void
+ ft_multo64( FT_UInt32 x,
+ FT_UInt32 y,
+ FT_Int64 *z )
+ {
+ FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+
+ lo1 = x & 0x0000FFFFU; hi1 = x >> 16;
+ lo2 = y & 0x0000FFFFU; hi2 = y >> 16;
+
+ lo = lo1 * lo2;
+ i1 = lo1 * hi2;
+ i2 = lo2 * hi1;
+ hi = hi1 * hi2;
+
+ /* Check carry overflow of i1 + i2 */
+ i1 += i2;
+ hi += (FT_UInt32)( i1 < i2 ) << 16;
+
+ hi += i1 >> 16;
+ i1 = i1 << 16;
+
+ /* Check carry overflow of i1 + lo */
+ lo += i1;
+ hi += ( lo < i1 );
+
+ z->lo = lo;
+ z->hi = hi;
+ }
+
+
+ static FT_UInt32
+ ft_div64by32( FT_UInt32 hi,
+ FT_UInt32 lo,
+ FT_UInt32 y )
+ {
+ FT_UInt32 r, q;
+ FT_Int i;
+
+
+ q = 0;
+ r = hi;
+
+ if ( r >= y )
+ return (FT_UInt32)0x7FFFFFFFL;
+
+ i = 32;
+ do
+ {
+ r <<= 1;
+ q <<= 1;
+ r |= lo >> 31;
+
+ if ( r >= (FT_UInt32)y )
+ {
+ r -= y;
+ q |= 1;
+ }
+ lo <<= 1;
+ } while ( --i );
+
+ return q;
+ }
+
+
+ static void
+ FT_Add64( FT_Int64* x,
+ FT_Int64* y,
+ FT_Int64 *z )
+ {
+ register FT_UInt32 lo, hi;
+
+
+ lo = x->lo + y->lo;
+ hi = x->hi + y->hi + ( lo < x->lo );
+
+ z->lo = lo;
+ z->hi = hi;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ /* The FT_MulDiv function has been optimized thanks to ideas from */
+ /* Graham Asher. The trick is to optimize computation when everything */
+ /* fits within 32-bits (a rather common case). */
+ /* */
+ /* we compute 'a*b+c/2', then divide it by 'c'. (positive values) */
+ /* */
+ /* 46340 is FLOOR(SQRT(2^31-1)). */
+ /* */
+ /* if ( a <= 46340 && b <= 46340 ) then ( a*b <= 0x7FFEA810 ) */
+ /* */
+ /* 0x7FFFFFFF - 0x7FFEA810 = 0x157F0 */
+ /* */
+ /* if ( c < 0x157F0*2 ) then ( a*b+c/2 <= 0x7FFFFFFF ) */
+ /* */
+ /* and 2*0x157F0 = 176096 */
+ /* */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulDiv( FT_Long a,
+ FT_Long b,
+ FT_Long c )
+ {
+ long s;
+
+
+ if ( a == 0 || b == c )
+ return a;
+
+ s = a; a = FT_ABS( a );
+ s ^= b; b = FT_ABS( b );
+ s ^= c; c = FT_ABS( c );
+
+ if ( a <= 46340L && b <= 46340L && c <= 176095L && c > 0 )
+ a = ( a * b + ( c >> 1 ) ) / c;
+
+ else if ( c > 0 )
+ {
+ FT_Int64 temp, temp2;
+
+
+ ft_multo64( a, b, &temp );
+
+ temp2.hi = 0;
+ temp2.lo = (FT_UInt32)(c >> 1);
+ FT_Add64( &temp, &temp2, &temp );
+ a = ft_div64by32( temp.hi, temp.lo, c );
+ }
+ else
+ a = 0x7FFFFFFFL;
+
+ return ( s < 0 ? -a : a );
+ }
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_BASE_DEF( FT_Long )
+ FT_MulDiv_No_Round( FT_Long a,
+ FT_Long b,
+ FT_Long c )
+ {
+ long s;
+
+
+ if ( a == 0 || b == c )
+ return a;
+
+ s = a; a = FT_ABS( a );
+ s ^= b; b = FT_ABS( b );
+ s ^= c; c = FT_ABS( c );
+
+ if ( a <= 46340L && b <= 46340L && c > 0 )
+ a = a * b / c;
+
+ else if ( c > 0 )
+ {
+ FT_Int64 temp;
+
+
+ ft_multo64( a, b, &temp );
+ a = ft_div64by32( temp.hi, temp.lo, c );
+ }
+ else
+ a = 0x7FFFFFFFL;
+
+ return ( s < 0 ? -a : a );
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_MulFix( FT_Long a,
+ FT_Long b )
+ {
+ /* use inline assembly to speed up things a bit */
+
+#if defined( __GNUC__ ) && defined( i386 )
+
+ FT_Long result;
+
+
+ __asm__ __volatile__ (
+ "imul %%edx\n"
+ "movl %%edx, %%ecx\n"
+ "sarl $31, %%ecx\n"
+ "addl $0x8000, %%ecx\n"
+ "addl %%ecx, %%eax\n"
+ "adcl $0, %%edx\n"
+ "shrl $16, %%eax\n"
+ "shll $16, %%edx\n"
+ "addl %%edx, %%eax\n"
+ "mov %%eax, %0\n"
+ : "=r"(result)
+ : "a"(a), "d"(b)
+ : "%ecx"
+ );
+ return result;
+
+#elif 1
+
+ FT_Long sa, sb;
+ FT_ULong ua, ub;
+
+
+ if ( a == 0 || b == 0x10000L )
+ return a;
+
+ sa = ( a >> ( sizeof ( a ) * 8 - 1 ) );
+ a = ( a ^ sa ) - sa;
+ sb = ( b >> ( sizeof ( b ) * 8 - 1 ) );
+ b = ( b ^ sb ) - sb;
+
+ ua = (FT_ULong)a;
+ ub = (FT_ULong)b;
+
+ if ( ua <= 2048 && ub <= 1048576L )
+ ua = ( ua * ub + 0x8000U ) >> 16;
+ else
+ {
+ FT_ULong al = ua & 0xFFFFU;
+
+
+ ua = ( ua >> 16 ) * ub + al * ( ub >> 16 ) +
+ ( ( al * ( ub & 0xFFFFU ) + 0x8000U ) >> 16 );
+ }
+
+ sa ^= sb,
+ ua = (FT_ULong)(( ua ^ sa ) - sa);
+
+ return (FT_Long)ua;
+
+#else /* 0 */
+
+ FT_Long s;
+ FT_ULong ua, ub;
+
+
+ if ( a == 0 || b == 0x10000L )
+ return a;
+
+ s = a; a = FT_ABS( a );
+ s ^= b; b = FT_ABS( b );
+
+ ua = (FT_ULong)a;
+ ub = (FT_ULong)b;
+
+ if ( ua <= 2048 && ub <= 1048576L )
+ ua = ( ua * ub + 0x8000UL ) >> 16;
+ else
+ {
+ FT_ULong al = ua & 0xFFFFUL;
+
+
+ ua = ( ua >> 16 ) * ub + al * ( ub >> 16 ) +
+ ( ( al * ( ub & 0xFFFFUL ) + 0x8000UL ) >> 16 );
+ }
+
+ return ( s < 0 ? -(FT_Long)ua : (FT_Long)ua );
+
+#endif /* 0 */
+
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_DivFix( FT_Long a,
+ FT_Long b )
+ {
+ FT_Int32 s;
+ FT_UInt32 q;
+
+
+ s = a; a = FT_ABS(a);
+ s ^= b; b = FT_ABS(b);
+
+ if ( b == 0 )
+ {
+ /* check for division by 0 */
+ q = 0x7FFFFFFFL;
+ }
+ else if ( ( a >> 16 ) == 0 )
+ {
+ /* compute result directly */
+ q = (FT_UInt32)( (a << 16) + (b >> 1) ) / (FT_UInt32)b;
+ }
+ else
+ {
+ /* we need more bits; we have to do it by hand */
+ FT_Int64 temp, temp2;
+
+ temp.hi = (FT_Int32) (a >> 16);
+ temp.lo = (FT_UInt32)(a << 16);
+ temp2.hi = 0;
+ temp2.lo = (FT_UInt32)( b >> 1 );
+ FT_Add64( &temp, &temp2, &temp );
+ q = ft_div64by32( temp.hi, temp.lo, b );
+ }
+
+ return ( s < 0 ? -(FT_Int32)q : (FT_Int32)q );
+ }
+
+
+#if 0
+
+ /* documentation is in ftcalc.h */
+
+ FT_EXPORT_DEF( void )
+ FT_MulTo64( FT_Int32 x,
+ FT_Int32 y,
+ FT_Int64 *z )
+ {
+ FT_Int32 s;
+
+
+ s = x; x = FT_ABS( x );
+ s ^= y; y = FT_ABS( y );
+
+ ft_multo64( x, y, z );
+
+ if ( s < 0 )
+ {
+ z->lo = (FT_UInt32)-(FT_Int32)z->lo;
+ z->hi = ~z->hi + !( z->lo );
+ }
+ }
+
+
+ /* apparently, the second version of this code is not compiled correctly */
+ /* on Mac machines with the MPW C compiler.. tsk, tsk, tsk... */
+
+#if 1
+
+ FT_EXPORT_DEF( FT_Int32 )
+ FT_Div64by32( FT_Int64* x,
+ FT_Int32 y )
+ {
+ FT_Int32 s;
+ FT_UInt32 q, r, i, lo;
+
+
+ s = x->hi;
+ if ( s < 0 )
+ {
+ x->lo = (FT_UInt32)-(FT_Int32)x->lo;
+ x->hi = ~x->hi + !x->lo;
+ }
+ s ^= y; y = FT_ABS( y );
+
+ /* Shortcut */
+ if ( x->hi == 0 )
+ {
+ if ( y > 0 )
+ q = x->lo / y;
+ else
+ q = 0x7FFFFFFFL;
+
+ return ( s < 0 ? -(FT_Int32)q : (FT_Int32)q );
+ }
+
+ r = x->hi;
+ lo = x->lo;
+
+ if ( r >= (FT_UInt32)y ) /* we know y is to be treated as unsigned here */
+ return ( s < 0 ? 0x80000001UL : 0x7FFFFFFFUL );
+ /* Return Max/Min Int32 if division overflow. */
+ /* This includes division by zero! */
+ q = 0;
+ for ( i = 0; i < 32; i++ )
+ {
+ r <<= 1;
+ q <<= 1;
+ r |= lo >> 31;
+
+ if ( r >= (FT_UInt32)y )
+ {
+ r -= y;
+ q |= 1;
+ }
+ lo <<= 1;
+ }
+
+ return ( s < 0 ? -(FT_Int32)q : (FT_Int32)q );
+ }
+
+#else /* 0 */
+
+ FT_EXPORT_DEF( FT_Int32 )
+ FT_Div64by32( FT_Int64* x,
+ FT_Int32 y )
+ {
+ FT_Int32 s;
+ FT_UInt32 q;
+
+
+ s = x->hi;
+ if ( s < 0 )
+ {
+ x->lo = (FT_UInt32)-(FT_Int32)x->lo;
+ x->hi = ~x->hi + !x->lo;
+ }
+ s ^= y; y = FT_ABS( y );
+
+ /* Shortcut */
+ if ( x->hi == 0 )
+ {
+ if ( y > 0 )
+ q = ( x->lo + ( y >> 1 ) ) / y;
+ else
+ q = 0x7FFFFFFFL;
+
+ return ( s < 0 ? -(FT_Int32)q : (FT_Int32)q );
+ }
+
+ q = ft_div64by32( x->hi, x->lo, y );
+
+ return ( s < 0 ? -(FT_Int32)q : (FT_Int32)q );
+ }
+
+#endif /* 0 */
+
+#endif /* 0 */
+
+
+#endif /* FT_LONG64 */
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int32 )
+ FT_SqrtFixed( FT_Int32 x )
+ {
+ FT_UInt32 root, rem_hi, rem_lo, test_div;
+ FT_Int count;
+
+
+ root = 0;
+
+ if ( x > 0 )
+ {
+ rem_hi = 0;
+ rem_lo = x;
+ count = 24;
+ do
+ {
+ rem_hi = ( rem_hi << 2 ) | ( rem_lo >> 30 );
+ rem_lo <<= 2;
+ root <<= 1;
+ test_div = ( root << 1 ) + 1;
+
+ if ( rem_hi >= test_div )
+ {
+ rem_hi -= test_div;
+ root += 1;
+ }
+ } while ( --count );
+ }
+
+ return (FT_Int32)root;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int )
+ ft_corner_orientation( FT_Pos in_x,
+ FT_Pos in_y,
+ FT_Pos out_x,
+ FT_Pos out_y )
+ {
+ FT_Int result;
+
+
+ /* deal with the trivial cases quickly */
+ if ( in_y == 0 )
+ {
+ if ( in_x >= 0 )
+ result = out_y;
+ else
+ result = -out_y;
+ }
+ else if ( in_x == 0 )
+ {
+ if ( in_y >= 0 )
+ result = -out_x;
+ else
+ result = out_x;
+ }
+ else if ( out_y == 0 )
+ {
+ if ( out_x >= 0 )
+ result = in_y;
+ else
+ result = -in_y;
+ }
+ else if ( out_x == 0 )
+ {
+ if ( out_y >= 0 )
+ result = -in_x;
+ else
+ result = in_x;
+ }
+ else /* general case */
+ {
+#ifdef FT_LONG64
+
+ FT_Int64 delta = (FT_Int64)in_x * out_y - (FT_Int64)in_y * out_x;
+
+
+ if ( delta == 0 )
+ result = 0;
+ else
+ result = 1 - 2 * ( delta < 0 );
+
+#else
+
+ FT_Int64 z1, z2;
+
+
+ ft_multo64( in_x, out_y, &z1 );
+ ft_multo64( in_y, out_x, &z2 );
+
+ if ( z1.hi > z2.hi )
+ result = +1;
+ else if ( z1.hi < z2.hi )
+ result = -1;
+ else if ( z1.lo > z2.lo )
+ result = +1;
+ else if ( z1.lo < z2.lo )
+ result = -1;
+ else
+ result = 0;
+
+#endif
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in ftcalc.h */
+
+ FT_BASE_DEF( FT_Int )
+ ft_corner_is_flat( FT_Pos in_x,
+ FT_Pos in_y,
+ FT_Pos out_x,
+ FT_Pos out_y )
+ {
+ FT_Pos ax = in_x;
+ FT_Pos ay = in_y;
+
+ FT_Pos d_in, d_out, d_corner;
+
+
+ if ( ax < 0 )
+ ax = -ax;
+ if ( ay < 0 )
+ ay = -ay;
+ d_in = ax + ay;
+
+ ax = out_x;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = out_y;
+ if ( ay < 0 )
+ ay = -ay;
+ d_out = ax + ay;
+
+ ax = out_x + in_x;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = out_y + in_y;
+ if ( ay < 0 )
+ ay = -ay;
+ d_corner = ax + ay;
+
+ return ( d_in + d_out - d_corner ) < ( d_corner >> 4 );
+ }
+
+
+/* END */
diff --git a/src/base/ftdbgmem.c b/src/base/ftdbgmem.c
new file mode 100644
index 0000000..52a5c20
--- /dev/null
+++ b/src/base/ftdbgmem.c
@@ -0,0 +1,998 @@
+/***************************************************************************/
+/* */
+/* ftdbgmem.c */
+/* */
+/* Memory debugger (body). */
+/* */
+/* Copyright 2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_MEMORY_H
+#include FT_SYSTEM_H
+#include FT_ERRORS_H
+#include FT_TYPES_H
+
+
+#ifdef FT_DEBUG_MEMORY
+
+#define KEEPALIVE /* `Keep alive' means that freed blocks aren't released
+ * to the heap. This is useful to detect double-frees
+ * or weird heap corruption, but it uses large amounts of
+ * memory, however.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+ FT_BASE_DEF( const char* ) _ft_debug_file = 0;
+ FT_BASE_DEF( long ) _ft_debug_lineno = 0;
+
+ extern void
+ FT_DumpMemory( FT_Memory memory );
+
+
+ typedef struct FT_MemSourceRec_* FT_MemSource;
+ typedef struct FT_MemNodeRec_* FT_MemNode;
+ typedef struct FT_MemTableRec_* FT_MemTable;
+
+
+#define FT_MEM_VAL( addr ) ((FT_ULong)(FT_Pointer)( addr ))
+
+ /*
+ * This structure holds statistics for a single allocation/release
+ * site. This is useful to know where memory operations happen the
+ * most.
+ */
+ typedef struct FT_MemSourceRec_
+ {
+ const char* file_name;
+ long line_no;
+
+ FT_Long cur_blocks; /* current number of allocated blocks */
+ FT_Long max_blocks; /* max. number of allocated blocks */
+ FT_Long all_blocks; /* total number of blocks allocated */
+
+ FT_Long cur_size; /* current cumulative allocated size */
+ FT_Long max_size; /* maximum cumulative allocated size */
+ FT_Long all_size; /* total cumulative allocated size */
+
+ FT_Long cur_max; /* current maximum allocated size */
+
+ FT_UInt32 hash;
+ FT_MemSource link;
+
+ } FT_MemSourceRec;
+
+
+ /*
+ * We don't need a resizable array for the memory sources, because
+ * their number is pretty limited within FreeType.
+ */
+#define FT_MEM_SOURCE_BUCKETS 128
+
+ /*
+ * This structure holds information related to a single allocated
+ * memory block. If KEEPALIVE is defined, blocks that are freed by
+ * FreeType are never released to the system. Instead, their `size'
+ * field is set to -size. This is mainly useful to detect double frees,
+ * at the price of large memory footprint during execution.
+ */
+ typedef struct FT_MemNodeRec_
+ {
+ FT_Byte* address;
+ FT_Long size; /* < 0 if the block was freed */
+
+ FT_MemSource source;
+
+#ifdef KEEPALIVE
+ const char* free_file_name;
+ FT_Long free_line_no;
+#endif
+
+ FT_MemNode link;
+
+ } FT_MemNodeRec;
+
+
+ /*
+ * The global structure, containing compound statistics and all hash
+ * tables.
+ */
+ typedef struct FT_MemTableRec_
+ {
+ FT_ULong size;
+ FT_ULong nodes;
+ FT_MemNode* buckets;
+
+ FT_ULong alloc_total;
+ FT_ULong alloc_current;
+ FT_ULong alloc_max;
+ FT_ULong alloc_count;
+
+ FT_Bool bound_total;
+ FT_ULong alloc_total_max;
+
+ FT_Bool bound_count;
+ FT_ULong alloc_count_max;
+
+ FT_MemSource sources[FT_MEM_SOURCE_BUCKETS];
+
+ FT_Bool keep_alive;
+
+ FT_Memory memory;
+ FT_Pointer memory_user;
+ FT_Alloc_Func alloc;
+ FT_Free_Func free;
+ FT_Realloc_Func realloc;
+
+ } FT_MemTableRec;
+
+
+#define FT_MEM_SIZE_MIN 7
+#define FT_MEM_SIZE_MAX 13845163
+
+#define FT_FILENAME( x ) ((x) ? (x) : "unknown file")
+
+
+ /*
+ * Prime numbers are ugly to handle. It would be better to implement
+ * L-Hashing, which is 10% faster and doesn't require divisions.
+ */
+ static const FT_UInt ft_mem_primes[] =
+ {
+ 7,
+ 11,
+ 19,
+ 37,
+ 73,
+ 109,
+ 163,
+ 251,
+ 367,
+ 557,
+ 823,
+ 1237,
+ 1861,
+ 2777,
+ 4177,
+ 6247,
+ 9371,
+ 14057,
+ 21089,
+ 31627,
+ 47431,
+ 71143,
+ 106721,
+ 160073,
+ 240101,
+ 360163,
+ 540217,
+ 810343,
+ 1215497,
+ 1823231,
+ 2734867,
+ 4102283,
+ 6153409,
+ 9230113,
+ 13845163,
+ };
+
+
+ static FT_ULong
+ ft_mem_closest_prime( FT_ULong num )
+ {
+ FT_UInt i;
+
+
+ for ( i = 0;
+ i < sizeof ( ft_mem_primes ) / sizeof ( ft_mem_primes[0] ); i++ )
+ if ( ft_mem_primes[i] > num )
+ return ft_mem_primes[i];
+
+ return FT_MEM_SIZE_MAX;
+ }
+
+
+ extern void
+ ft_mem_debug_panic( const char* fmt,
+ ... )
+ {
+ va_list ap;
+
+
+ printf( "FreeType.Debug: " );
+
+ va_start( ap, fmt );
+ vprintf( fmt, ap );
+ va_end( ap );
+
+ printf( "\n" );
+ exit( EXIT_FAILURE );
+ }
+
+
+ static FT_Pointer
+ ft_mem_table_alloc( FT_MemTable table,
+ FT_Long size )
+ {
+ FT_Memory memory = table->memory;
+ FT_Pointer block;
+
+
+ memory->user = table->memory_user;
+ block = table->alloc( memory, size );
+ memory->user = table;
+
+ return block;
+ }
+
+
+ static void
+ ft_mem_table_free( FT_MemTable table,
+ FT_Pointer block )
+ {
+ FT_Memory memory = table->memory;
+
+
+ memory->user = table->memory_user;
+ table->free( memory, block );
+ memory->user = table;
+ }
+
+
+ static void
+ ft_mem_table_resize( FT_MemTable table )
+ {
+ FT_ULong new_size;
+
+
+ new_size = ft_mem_closest_prime( table->nodes );
+ if ( new_size != table->size )
+ {
+ FT_MemNode* new_buckets;
+ FT_ULong i;
+
+
+ new_buckets = (FT_MemNode *)
+ ft_mem_table_alloc( table,
+ new_size * sizeof ( FT_MemNode ) );
+ if ( new_buckets == NULL )
+ return;
+
+ FT_ARRAY_ZERO( new_buckets, new_size );
+
+ for ( i = 0; i < table->size; i++ )
+ {
+ FT_MemNode node, next, *pnode;
+ FT_ULong hash;
+
+
+ node = table->buckets[i];
+ while ( node )
+ {
+ next = node->link;
+ hash = FT_MEM_VAL( node->address ) % new_size;
+ pnode = new_buckets + hash;
+
+ node->link = pnode[0];
+ pnode[0] = node;
+
+ node = next;
+ }
+ }
+
+ if ( table->buckets )
+ ft_mem_table_free( table, table->buckets );
+
+ table->buckets = new_buckets;
+ table->size = new_size;
+ }
+ }
+
+
+ static FT_MemTable
+ ft_mem_table_new( FT_Memory memory )
+ {
+ FT_MemTable table;
+
+
+ table = (FT_MemTable)memory->alloc( memory, sizeof ( *table ) );
+ if ( table == NULL )
+ goto Exit;
+
+ FT_ZERO( table );
+
+ table->size = FT_MEM_SIZE_MIN;
+ table->nodes = 0;
+
+ table->memory = memory;
+
+ table->memory_user = memory->user;
+
+ table->alloc = memory->alloc;
+ table->realloc = memory->realloc;
+ table->free = memory->free;
+
+ table->buckets = (FT_MemNode *)
+ memory->alloc( memory,
+ table->size * sizeof ( FT_MemNode ) );
+ if ( table->buckets )
+ FT_ARRAY_ZERO( table->buckets, table->size );
+ else
+ {
+ memory->free( memory, table );
+ table = NULL;
+ }
+
+ Exit:
+ return table;
+ }
+
+
+ static void
+ ft_mem_table_destroy( FT_MemTable table )
+ {
+ FT_ULong i;
+
+
+ FT_DumpMemory( table->memory );
+
+ if ( table )
+ {
+ FT_Long leak_count = 0;
+ FT_ULong leaks = 0;
+
+
+ /* remove all blocks from the table, revealing leaked ones */
+ for ( i = 0; i < table->size; i++ )
+ {
+ FT_MemNode *pnode = table->buckets + i, next, node = *pnode;
+
+
+ while ( node )
+ {
+ next = node->link;
+ node->link = 0;
+
+ if ( node->size > 0 )
+ {
+ printf(
+ "leaked memory block at address %p, size %8ld in (%s:%ld)\n",
+ node->address, node->size,
+ FT_FILENAME( node->source->file_name ),
+ node->source->line_no );
+
+ leak_count++;
+ leaks += node->size;
+
+ ft_mem_table_free( table, node->address );
+ }
+
+ node->address = NULL;
+ node->size = 0;
+
+ ft_mem_table_free( table, node );
+ node = next;
+ }
+ table->buckets[i] = 0;
+ }
+
+ ft_mem_table_free( table, table->buckets );
+ table->buckets = NULL;
+
+ table->size = 0;
+ table->nodes = 0;
+
+ /* remove all sources */
+ for ( i = 0; i < FT_MEM_SOURCE_BUCKETS; i++ )
+ {
+ FT_MemSource source, next;
+
+
+ for ( source = table->sources[i]; source != NULL; source = next )
+ {
+ next = source->link;
+ ft_mem_table_free( table, source );
+ }
+
+ table->sources[i] = NULL;
+ }
+
+ printf(
+ "FreeType: total memory allocations = %ld\n", table->alloc_total );
+ printf(
+ "FreeType: maximum memory footprint = %ld\n", table->alloc_max );
+
+ ft_mem_table_free( table, table );
+
+ if ( leak_count > 0 )
+ ft_mem_debug_panic(
+ "FreeType: %ld bytes of memory leaked in %ld blocks\n",
+ leaks, leak_count );
+
+ printf( "FreeType: No memory leaks detected!\n" );
+ }
+ }
+
+
+ static FT_MemNode*
+ ft_mem_table_get_nodep( FT_MemTable table,
+ FT_Byte* address )
+ {
+ FT_ULong hash;
+ FT_MemNode *pnode, node;
+
+
+ hash = FT_MEM_VAL( address );
+ pnode = table->buckets + ( hash % table->size );
+
+ for (;;)
+ {
+ node = pnode[0];
+ if ( !node )
+ break;
+
+ if ( node->address == address )
+ break;
+
+ pnode = &node->link;
+ }
+ return pnode;
+ }
+
+
+ static FT_MemSource
+ ft_mem_table_get_source( FT_MemTable table )
+ {
+ FT_UInt32 hash;
+ FT_MemSource node, *pnode;
+
+
+ /* cast to FT_PtrDist first since void* can be larger */
+ /* than FT_UInt32 and GCC 4.1.1 emits a warning */
+ hash = (FT_UInt32)(FT_PtrDist)(void*)_ft_debug_file +
+ (FT_UInt32)( 5 * _ft_debug_lineno );
+ pnode = &table->sources[hash % FT_MEM_SOURCE_BUCKETS];
+
+ for ( ;; )
+ {
+ node = *pnode;
+ if ( node == NULL )
+ break;
+
+ if ( node->file_name == _ft_debug_file &&
+ node->line_no == _ft_debug_lineno )
+ goto Exit;
+
+ pnode = &node->link;
+ }
+
+ node = (FT_MemSource)ft_mem_table_alloc( table, sizeof ( *node ) );
+ if ( node == NULL )
+ ft_mem_debug_panic(
+ "not enough memory to perform memory debugging\n" );
+
+ node->file_name = _ft_debug_file;
+ node->line_no = _ft_debug_lineno;
+
+ node->cur_blocks = 0;
+ node->max_blocks = 0;
+ node->all_blocks = 0;
+
+ node->cur_size = 0;
+ node->max_size = 0;
+ node->all_size = 0;
+
+ node->cur_max = 0;
+
+ node->link = NULL;
+ node->hash = hash;
+ *pnode = node;
+
+ Exit:
+ return node;
+ }
+
+
+ static void
+ ft_mem_table_set( FT_MemTable table,
+ FT_Byte* address,
+ FT_ULong size,
+ FT_Long delta )
+ {
+ FT_MemNode *pnode, node;
+
+
+ if ( table )
+ {
+ FT_MemSource source;
+
+
+ pnode = ft_mem_table_get_nodep( table, address );
+ node = *pnode;
+ if ( node )
+ {
+ if ( node->size < 0 )
+ {
+ /* This block was already freed. Our memory is now completely */
+ /* corrupted! */
+ /* This can only happen in keep-alive mode. */
+ ft_mem_debug_panic(
+ "memory heap corrupted (allocating freed block)" );
+ }
+ else
+ {
+ /* This block was already allocated. This means that our memory */
+ /* is also corrupted! */
+ ft_mem_debug_panic(
+ "memory heap corrupted (re-allocating allocated block at"
+ " %p, of size %ld)\n"
+ "org=%s:%d new=%s:%d\n",
+ node->address, node->size,
+ FT_FILENAME( node->source->file_name ), node->source->line_no,
+ FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
+ }
+ }
+
+ /* we need to create a new node in this table */
+ node = (FT_MemNode)ft_mem_table_alloc( table, sizeof ( *node ) );
+ if ( node == NULL )
+ ft_mem_debug_panic( "not enough memory to run memory tests" );
+
+ node->address = address;
+ node->size = size;
+ node->source = source = ft_mem_table_get_source( table );
+
+ if ( delta == 0 )
+ {
+ /* this is an allocation */
+ source->all_blocks++;
+ source->cur_blocks++;
+ if ( source->cur_blocks > source->max_blocks )
+ source->max_blocks = source->cur_blocks;
+ }
+
+ if ( size > (FT_ULong)source->cur_max )
+ source->cur_max = size;
+
+ if ( delta != 0 )
+ {
+ /* we are growing or shrinking a reallocated block */
+ source->cur_size += delta;
+ table->alloc_current += delta;
+ }
+ else
+ {
+ /* we are allocating a new block */
+ source->cur_size += size;
+ table->alloc_current += size;
+ }
+
+ source->all_size += size;
+
+ if ( source->cur_size > source->max_size )
+ source->max_size = source->cur_size;
+
+ node->free_file_name = NULL;
+ node->free_line_no = 0;
+
+ node->link = pnode[0];
+
+ pnode[0] = node;
+ table->nodes++;
+
+ table->alloc_total += size;
+
+ if ( table->alloc_current > table->alloc_max )
+ table->alloc_max = table->alloc_current;
+
+ if ( table->nodes * 3 < table->size ||
+ table->size * 3 < table->nodes )
+ ft_mem_table_resize( table );
+ }
+ }
+
+
+ static void
+ ft_mem_table_remove( FT_MemTable table,
+ FT_Byte* address,
+ FT_Long delta )
+ {
+ if ( table )
+ {
+ FT_MemNode *pnode, node;
+
+
+ pnode = ft_mem_table_get_nodep( table, address );
+ node = *pnode;
+ if ( node )
+ {
+ FT_MemSource source;
+
+
+ if ( node->size < 0 )
+ ft_mem_debug_panic(
+ "freeing memory block at %p more than once at (%s:%ld)\n"
+ "block allocated at (%s:%ld) and released at (%s:%ld)",
+ address,
+ FT_FILENAME( _ft_debug_file ), _ft_debug_lineno,
+ FT_FILENAME( node->source->file_name ), node->source->line_no,
+ FT_FILENAME( node->free_file_name ), node->free_line_no );
+
+ /* scramble the node's content for additional safety */
+ FT_MEM_SET( address, 0xF3, node->size );
+
+ if ( delta == 0 )
+ {
+ source = node->source;
+
+ source->cur_blocks--;
+ source->cur_size -= node->size;
+
+ table->alloc_current -= node->size;
+ }
+
+ if ( table->keep_alive )
+ {
+ /* we simply invert the node's size to indicate that the node */
+ /* was freed. */
+ node->size = -node->size;
+ node->free_file_name = _ft_debug_file;
+ node->free_line_no = _ft_debug_lineno;
+ }
+ else
+ {
+ table->nodes--;
+
+ *pnode = node->link;
+
+ node->size = 0;
+ node->source = NULL;
+
+ ft_mem_table_free( table, node );
+
+ if ( table->nodes * 3 < table->size ||
+ table->size * 3 < table->nodes )
+ ft_mem_table_resize( table );
+ }
+ }
+ else
+ ft_mem_debug_panic(
+ "trying to free unknown block at %p in (%s:%ld)\n",
+ address,
+ FT_FILENAME( _ft_debug_file ), _ft_debug_lineno );
+ }
+ }
+
+
+ extern FT_Pointer
+ ft_mem_debug_alloc( FT_Memory memory,
+ FT_Long size )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+ FT_Byte* block;
+
+
+ if ( size <= 0 )
+ ft_mem_debug_panic( "negative block size allocation (%ld)", size );
+
+ /* return NULL if the maximum number of allocations was reached */
+ if ( table->bound_count &&
+ table->alloc_count >= table->alloc_count_max )
+ return NULL;
+
+ /* return NULL if this allocation would overflow the maximum heap size */
+ if ( table->bound_total &&
+ table->alloc_total_max - table->alloc_current > (FT_ULong)size )
+ return NULL;
+
+ block = (FT_Byte *)ft_mem_table_alloc( table, size );
+ if ( block )
+ {
+ ft_mem_table_set( table, block, (FT_ULong)size, 0 );
+
+ table->alloc_count++;
+ }
+
+ _ft_debug_file = "<unknown>";
+ _ft_debug_lineno = 0;
+
+ return (FT_Pointer)block;
+ }
+
+
+ extern void
+ ft_mem_debug_free( FT_Memory memory,
+ FT_Pointer block )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+
+
+ if ( block == NULL )
+ ft_mem_debug_panic( "trying to free NULL in (%s:%ld)",
+ FT_FILENAME( _ft_debug_file ),
+ _ft_debug_lineno );
+
+ ft_mem_table_remove( table, (FT_Byte*)block, 0 );
+
+ if ( !table->keep_alive )
+ ft_mem_table_free( table, block );
+
+ table->alloc_count--;
+
+ _ft_debug_file = "<unknown>";
+ _ft_debug_lineno = 0;
+ }
+
+
+ extern FT_Pointer
+ ft_mem_debug_realloc( FT_Memory memory,
+ FT_Long cur_size,
+ FT_Long new_size,
+ FT_Pointer block )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+ FT_MemNode node, *pnode;
+ FT_Pointer new_block;
+ FT_Long delta;
+
+ const char* file_name = FT_FILENAME( _ft_debug_file );
+ FT_Long line_no = _ft_debug_lineno;
+
+
+ /* unlikely, but possible */
+ if ( new_size == cur_size )
+ return block;
+
+ /* the following is valid according to ANSI C */
+#if 0
+ if ( block == NULL || cur_size == 0 )
+ ft_mem_debug_panic( "trying to reallocate NULL in (%s:%ld)",
+ file_name, line_no );
+#endif
+
+ /* while the following is allowed in ANSI C also, we abort since */
+ /* such case should be handled by FreeType. */
+ if ( new_size <= 0 )
+ ft_mem_debug_panic(
+ "trying to reallocate %p to size 0 (current is %ld) in (%s:%ld)",
+ block, cur_size, file_name, line_no );
+
+ /* check `cur_size' value */
+ pnode = ft_mem_table_get_nodep( table, (FT_Byte*)block );
+ node = *pnode;
+ if ( !node )
+ ft_mem_debug_panic(
+ "trying to reallocate unknown block at %p in (%s:%ld)",
+ block, file_name, line_no );
+
+ if ( node->size <= 0 )
+ ft_mem_debug_panic(
+ "trying to reallocate freed block at %p in (%s:%ld)",
+ block, file_name, line_no );
+
+ if ( node->size != cur_size )
+ ft_mem_debug_panic( "invalid ft_realloc request for %p. cur_size is "
+ "%ld instead of %ld in (%s:%ld)",
+ block, cur_size, node->size, file_name, line_no );
+
+ /* return NULL if the maximum number of allocations was reached */
+ if ( table->bound_count &&
+ table->alloc_count >= table->alloc_count_max )
+ return NULL;
+
+ delta = (FT_Long)( new_size - cur_size );
+
+ /* return NULL if this allocation would overflow the maximum heap size */
+ if ( delta > 0 &&
+ table->bound_total &&
+ table->alloc_current + (FT_ULong)delta > table->alloc_total_max )
+ return NULL;
+
+ new_block = (FT_Byte *)ft_mem_table_alloc( table, new_size );
+ if ( new_block == NULL )
+ return NULL;
+
+ ft_mem_table_set( table, (FT_Byte*)new_block, new_size, delta );
+
+ ft_memcpy( new_block, block, cur_size < new_size ? cur_size : new_size );
+
+ ft_mem_table_remove( table, (FT_Byte*)block, delta );
+
+ _ft_debug_file = "<unknown>";
+ _ft_debug_lineno = 0;
+
+ if ( !table->keep_alive )
+ ft_mem_table_free( table, block );
+
+ return new_block;
+ }
+
+
+ extern FT_Int
+ ft_mem_debug_init( FT_Memory memory )
+ {
+ FT_MemTable table;
+ FT_Int result = 0;
+
+
+ if ( getenv( "FT2_DEBUG_MEMORY" ) )
+ {
+ table = ft_mem_table_new( memory );
+ if ( table )
+ {
+ const char* p;
+
+
+ memory->user = table;
+ memory->alloc = ft_mem_debug_alloc;
+ memory->realloc = ft_mem_debug_realloc;
+ memory->free = ft_mem_debug_free;
+
+ p = getenv( "FT2_ALLOC_TOTAL_MAX" );
+ if ( p != NULL )
+ {
+ FT_Long total_max = ft_atol( p );
+
+
+ if ( total_max > 0 )
+ {
+ table->bound_total = 1;
+ table->alloc_total_max = (FT_ULong)total_max;
+ }
+ }
+
+ p = getenv( "FT2_ALLOC_COUNT_MAX" );
+ if ( p != NULL )
+ {
+ FT_Long total_count = ft_atol( p );
+
+
+ if ( total_count > 0 )
+ {
+ table->bound_count = 1;
+ table->alloc_count_max = (FT_ULong)total_count;
+ }
+ }
+
+ p = getenv( "FT2_KEEP_ALIVE" );
+ if ( p != NULL )
+ {
+ FT_Long keep_alive = ft_atol( p );
+
+
+ if ( keep_alive > 0 )
+ table->keep_alive = 1;
+ }
+
+ result = 1;
+ }
+ }
+ return result;
+ }
+
+
+ extern void
+ ft_mem_debug_done( FT_Memory memory )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+
+
+ if ( table )
+ {
+ memory->free = table->free;
+ memory->realloc = table->realloc;
+ memory->alloc = table->alloc;
+
+ ft_mem_table_destroy( table );
+ memory->user = NULL;
+ }
+ }
+
+
+
+ static int
+ ft_mem_source_compare( const void* p1,
+ const void* p2 )
+ {
+ FT_MemSource s1 = *(FT_MemSource*)p1;
+ FT_MemSource s2 = *(FT_MemSource*)p2;
+
+
+ if ( s2->max_size > s1->max_size )
+ return 1;
+ else if ( s2->max_size < s1->max_size )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ extern void
+ FT_DumpMemory( FT_Memory memory )
+ {
+ FT_MemTable table = (FT_MemTable)memory->user;
+
+
+ if ( table )
+ {
+ FT_MemSource* bucket = table->sources;
+ FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS;
+ FT_MemSource* sources;
+ FT_UInt nn, count;
+ const char* fmt;
+
+
+ count = 0;
+ for ( ; bucket < limit; bucket++ )
+ {
+ FT_MemSource source = *bucket;
+
+
+ for ( ; source; source = source->link )
+ count++;
+ }
+
+ sources = (FT_MemSource*)ft_mem_table_alloc(
+ table, sizeof ( *sources ) * count );
+
+ count = 0;
+ for ( bucket = table->sources; bucket < limit; bucket++ )
+ {
+ FT_MemSource source = *bucket;
+
+
+ for ( ; source; source = source->link )
+ sources[count++] = source;
+ }
+
+ ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare );
+
+ printf( "FreeType Memory Dump: "
+ "current=%ld max=%ld total=%ld count=%ld\n",
+ table->alloc_current, table->alloc_max,
+ table->alloc_total, table->alloc_count );
+ printf( " block block sizes sizes sizes source\n" );
+ printf( " count high sum highsum max location\n" );
+ printf( "-------------------------------------------------\n" );
+
+ fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
+
+ for ( nn = 0; nn < count; nn++ )
+ {
+ FT_MemSource source = sources[nn];
+
+
+ printf( fmt,
+ source->cur_blocks, source->max_blocks,
+ source->cur_size, source->max_size, source->cur_max,
+ FT_FILENAME( source->file_name ),
+ source->line_no );
+ }
+ printf( "------------------------------------------------\n" );
+
+ ft_mem_table_free( table, sources );
+ }
+ }
+
+#else /* !FT_DEBUG_MEMORY */
+
+ /* ANSI C doesn't like empty source files */
+ const FT_Byte _debug_mem_dummy = 0;
+
+#endif /* !FT_DEBUG_MEMORY */
+
+
+/* END */
diff --git a/src/base/ftdebug.c b/src/base/ftdebug.c
new file mode 100644
index 0000000..c55d3c8
--- /dev/null
+++ b/src/base/ftdebug.c
@@ -0,0 +1,246 @@
+/***************************************************************************/
+/* */
+/* ftdebug.c */
+/* */
+/* Debugging and logging component (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This component contains various macros and functions used to ease the */
+ /* debugging of the FreeType engine. Its main purpose is in assertion */
+ /* checking, tracing, and error detection. */
+ /* */
+ /* There are now three debugging modes: */
+ /* */
+ /* - trace mode */
+ /* */
+ /* Error and trace messages are sent to the log file (which can be the */
+ /* standard error output). */
+ /* */
+ /* - error mode */
+ /* */
+ /* Only error messages are generated. */
+ /* */
+ /* - release mode: */
+ /* */
+ /* No error message is sent or generated. The code is free from any */
+ /* debugging parts. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_DEBUG_H
+
+
+#if defined( FT_DEBUG_LEVEL_ERROR )
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Message( const char* fmt, ... )
+ {
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ vprintf( fmt, ap );
+ va_end( ap );
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( void )
+ FT_Panic( const char* fmt, ... )
+ {
+ va_list ap;
+
+
+ va_start( ap, fmt );
+ vprintf( fmt, ap );
+ va_end( ap );
+
+ exit( EXIT_FAILURE );
+ }
+
+#endif /* FT_DEBUG_LEVEL_ERROR */
+
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ /* array of trace levels, initialized to 0 */
+ int ft_trace_levels[trace_count];
+
+
+ /* define array of trace toggle names */
+#define FT_TRACE_DEF( x ) #x ,
+
+ static const char* ft_trace_toggles[trace_count + 1] =
+ {
+#include FT_INTERNAL_TRACE_H
+ NULL
+ };
+
+#undef FT_TRACE_DEF
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( FT_Int )
+ FT_Trace_Get_Count( void )
+ {
+ return trace_count;
+ }
+
+
+ /* documentation is in ftdebug.h */
+
+ FT_BASE_DEF( const char * )
+ FT_Trace_Get_Name( FT_Int idx )
+ {
+ int max = FT_Trace_Get_Count();
+
+
+ if ( idx < max )
+ return ft_trace_toggles[idx];
+ else
+ return NULL;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Initialize the tracing sub-system. This is done by retrieving the */
+ /* value of the `FT2_DEBUG' environment variable. It must be a list of */
+ /* toggles, separated by spaces, `;', or `,'. Example: */
+ /* */
+ /* export FT2_DEBUG="any:3 memory:7 stream:5" */
+ /* */
+ /* This requests that all levels be set to 3, except the trace level for */
+ /* the memory and stream components which are set to 7 and 5, */
+ /* respectively. */
+ /* */
+ /* See the file <include/freetype/internal/fttrace.h> for details of the */
+ /* available toggle names. */
+ /* */
+ /* The level must be between 0 and 7; 0 means quiet (except for serious */
+ /* runtime errors), and 7 means _very_ verbose. */
+ /* */
+ FT_BASE_DEF( void )
+ ft_debug_init( void )
+ {
+ const char* ft2_debug = getenv( "FT2_DEBUG" );
+
+
+ if ( ft2_debug )
+ {
+ const char* p = ft2_debug;
+ const char* q;
+
+
+ for ( ; *p; p++ )
+ {
+ /* skip leading whitespace and separators */
+ if ( *p == ' ' || *p == '\t' || *p == ',' || *p == ';' || *p == '=' )
+ continue;
+
+ /* read toggle name, followed by ':' */
+ q = p;
+ while ( *p && *p != ':' )
+ p++;
+
+ if ( *p == ':' && p > q )
+ {
+ FT_Int n, i, len = (FT_Int)( p - q );
+ FT_Int level = -1, found = -1;
+
+
+ for ( n = 0; n < trace_count; n++ )
+ {
+ const char* toggle = ft_trace_toggles[n];
+
+
+ for ( i = 0; i < len; i++ )
+ {
+ if ( toggle[i] != q[i] )
+ break;
+ }
+
+ if ( i == len && toggle[i] == 0 )
+ {
+ found = n;
+ break;
+ }
+ }
+
+ /* read level */
+ p++;
+ if ( *p )
+ {
+ level = *p++ - '0';
+ if ( level < 0 || level > 7 )
+ level = -1;
+ }
+
+ if ( found >= 0 && level >= 0 )
+ {
+ if ( found == trace_any )
+ {
+ /* special case for `any' */
+ for ( n = 0; n < trace_count; n++ )
+ ft_trace_levels[n] = level;
+ }
+ else
+ ft_trace_levels[found] = level;
+ }
+ }
+ }
+ }
+ }
+
+
+#else /* !FT_DEBUG_LEVEL_TRACE */
+
+
+ FT_BASE_DEF( void )
+ ft_debug_init( void )
+ {
+ /* nothing */
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ FT_Trace_Get_Count( void )
+ {
+ return 0;
+ }
+
+
+ FT_BASE_DEF( const char * )
+ FT_Trace_Get_Name( FT_Int idx )
+ {
+ FT_UNUSED( idx );
+
+ return NULL;
+ }
+
+
+#endif /* !FT_DEBUG_LEVEL_TRACE */
+
+
+/* END */
diff --git a/src/base/ftgasp.c b/src/base/ftgasp.c
new file mode 100644
index 0000000..8485d29
--- /dev/null
+++ b/src/base/ftgasp.c
@@ -0,0 +1,61 @@
+/***************************************************************************/
+/* */
+/* ftgasp.c */
+/* */
+/* Access of TrueType's `gasp' table (body). */
+/* */
+/* Copyright 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_GASP_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Get_Gasp( FT_Face face,
+ FT_UInt ppem )
+ {
+ FT_Int result = FT_GASP_NO_TABLE;
+
+
+ if ( face && FT_IS_SFNT( face ) )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( ttface->gasp.numRanges > 0 )
+ {
+ TT_GaspRange range = ttface->gasp.gaspRanges;
+ TT_GaspRange range_end = range + ttface->gasp.numRanges;
+
+
+ while ( ppem > range->maxPPEM )
+ {
+ range++;
+ if ( range >= range_end )
+ goto Exit;
+ }
+
+ result = range->gaspFlag;
+
+ /* ensure that we don't have spurious bits */
+ if ( ttface->gasp.version == 0 )
+ result &= 3;
+ }
+ }
+ Exit:
+ return result;
+ }
+
+
+/* END */
diff --git a/src/base/ftgloadr.c b/src/base/ftgloadr.c
new file mode 100644
index 0000000..ab52621
--- /dev/null
+++ b/src/base/ftgloadr.c
@@ -0,0 +1,394 @@
+/***************************************************************************/
+/* */
+/* ftgloadr.c */
+/* */
+/* The FreeType glyph loader (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_GLYPH_LOADER_H
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_OBJECTS_H
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gloader
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** G L Y P H L O A D E R *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* The glyph loader is a simple object which is used to load a set of */
+ /* glyphs easily. It is critical for the correct loading of composites. */
+ /* */
+ /* Ideally, one can see it as a stack of abstract `glyph' objects. */
+ /* */
+ /* loader.base Is really the bottom of the stack. It describes a */
+ /* single glyph image made of the juxtaposition of */
+ /* several glyphs (those `in the stack'). */
+ /* */
+ /* loader.current Describes the top of the stack, on which a new */
+ /* glyph can be loaded. */
+ /* */
+ /* Rewind Clears the stack. */
+ /* Prepare Set up `loader.current' for addition of a new glyph */
+ /* image. */
+ /* Add Add the `current' glyph image to the `base' one, */
+ /* and prepare for another one. */
+ /* */
+ /* The glyph loader is now a base object. Each driver used to */
+ /* re-implement it in one way or the other, which wasted code and */
+ /* energy. */
+ /* */
+ /*************************************************************************/
+
+
+ /* create a new glyph loader */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_New( FT_Memory memory,
+ FT_GlyphLoader *aloader )
+ {
+ FT_GlyphLoader loader;
+ FT_Error error;
+
+
+ if ( !FT_NEW( loader ) )
+ {
+ loader->memory = memory;
+ *aloader = loader;
+ }
+ return error;
+ }
+
+
+ /* rewind the glyph loader - reset counters to 0 */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Rewind( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ base->outline.n_points = 0;
+ base->outline.n_contours = 0;
+ base->num_subglyphs = 0;
+
+ *current = *base;
+ }
+
+
+ /* reset the glyph loader, frees all allocated tables */
+ /* and starts from zero */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Reset( FT_GlyphLoader loader )
+ {
+ FT_Memory memory = loader->memory;
+
+
+ FT_FREE( loader->base.outline.points );
+ FT_FREE( loader->base.outline.tags );
+ FT_FREE( loader->base.outline.contours );
+ FT_FREE( loader->base.extra_points );
+ FT_FREE( loader->base.subglyphs );
+
+ loader->base.extra_points2 = NULL;
+
+ loader->max_points = 0;
+ loader->max_contours = 0;
+ loader->max_subglyphs = 0;
+
+ FT_GlyphLoader_Rewind( loader );
+ }
+
+
+ /* delete a glyph loader */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Done( FT_GlyphLoader loader )
+ {
+ if ( loader )
+ {
+ FT_Memory memory = loader->memory;
+
+
+ FT_GlyphLoader_Reset( loader );
+ FT_FREE( loader );
+ }
+ }
+
+
+ /* re-adjust the `current' outline fields */
+ static void
+ FT_GlyphLoader_Adjust_Points( FT_GlyphLoader loader )
+ {
+ FT_Outline* base = &loader->base.outline;
+ FT_Outline* current = &loader->current.outline;
+
+
+ current->points = base->points + base->n_points;
+ current->tags = base->tags + base->n_points;
+ current->contours = base->contours + base->n_contours;
+
+ /* handle extra points table - if any */
+ if ( loader->use_extra )
+ {
+ loader->current.extra_points = loader->base.extra_points +
+ base->n_points;
+
+ loader->current.extra_points2 = loader->base.extra_points2 +
+ base->n_points;
+ }
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CreateExtra( FT_GlyphLoader loader )
+ {
+ FT_Error error;
+ FT_Memory memory = loader->memory;
+
+
+ if ( !FT_NEW_ARRAY( loader->base.extra_points, 2 * loader->max_points ) )
+ {
+ loader->use_extra = 1;
+ loader->base.extra_points2 = loader->base.extra_points +
+ loader->max_points;
+
+ FT_GlyphLoader_Adjust_Points( loader );
+ }
+ return error;
+ }
+
+
+ /* re-adjust the `current' subglyphs field */
+ static void
+ FT_GlyphLoader_Adjust_Subglyphs( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ current->subglyphs = base->subglyphs + base->num_subglyphs;
+ }
+
+
+ /* Ensure that we can add `n_points' and `n_contours' to our glyph. */
+ /* This function reallocates its outline tables if necessary. Note that */
+ /* it DOESN'T change the number of points within the loader! */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CheckPoints( FT_GlyphLoader loader,
+ FT_UInt n_points,
+ FT_UInt n_contours )
+ {
+ FT_Memory memory = loader->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Outline* base = &loader->base.outline;
+ FT_Outline* current = &loader->current.outline;
+ FT_Bool adjust = 0;
+
+ FT_UInt new_max, old_max;
+
+
+ /* check points & tags */
+ new_max = base->n_points + current->n_points + n_points;
+ old_max = loader->max_points;
+
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 8 );
+
+ if ( FT_RENEW_ARRAY( base->points, old_max, new_max ) ||
+ FT_RENEW_ARRAY( base->tags, old_max, new_max ) )
+ goto Exit;
+
+ if ( loader->use_extra )
+ {
+ if ( FT_RENEW_ARRAY( loader->base.extra_points,
+ old_max * 2, new_max * 2 ) )
+ goto Exit;
+
+ FT_ARRAY_MOVE( loader->base.extra_points + new_max,
+ loader->base.extra_points + old_max,
+ old_max );
+
+ loader->base.extra_points2 = loader->base.extra_points + new_max;
+ }
+
+ adjust = 1;
+ loader->max_points = new_max;
+ }
+
+ /* check contours */
+ old_max = loader->max_contours;
+ new_max = base->n_contours + current->n_contours +
+ n_contours;
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 4 );
+ if ( FT_RENEW_ARRAY( base->contours, old_max, new_max ) )
+ goto Exit;
+
+ adjust = 1;
+ loader->max_contours = new_max;
+ }
+
+ if ( adjust )
+ FT_GlyphLoader_Adjust_Points( loader );
+
+ Exit:
+ return error;
+ }
+
+
+ /* Ensure that we can add `n_subglyphs' to our glyph. this function */
+ /* reallocates its subglyphs table if necessary. Note that it DOES */
+ /* NOT change the number of subglyphs within the loader! */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CheckSubGlyphs( FT_GlyphLoader loader,
+ FT_UInt n_subs )
+ {
+ FT_Memory memory = loader->memory;
+ FT_Error error = FT_Err_Ok;
+ FT_UInt new_max, old_max;
+
+ FT_GlyphLoad base = &loader->base;
+ FT_GlyphLoad current = &loader->current;
+
+
+ new_max = base->num_subglyphs + current->num_subglyphs + n_subs;
+ old_max = loader->max_subglyphs;
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 2 );
+ if ( FT_RENEW_ARRAY( base->subglyphs, old_max, new_max ) )
+ goto Exit;
+
+ loader->max_subglyphs = new_max;
+
+ FT_GlyphLoader_Adjust_Subglyphs( loader );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* prepare loader for the addition of a new glyph on top of the base one */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Prepare( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad current = &loader->current;
+
+
+ current->outline.n_points = 0;
+ current->outline.n_contours = 0;
+ current->num_subglyphs = 0;
+
+ FT_GlyphLoader_Adjust_Points ( loader );
+ FT_GlyphLoader_Adjust_Subglyphs( loader );
+ }
+
+
+ /* add current glyph to the base image - and prepare for another */
+ FT_BASE_DEF( void )
+ FT_GlyphLoader_Add( FT_GlyphLoader loader )
+ {
+ FT_GlyphLoad base;
+ FT_GlyphLoad current;
+
+ FT_UInt n_curr_contours;
+ FT_UInt n_base_points;
+ FT_UInt n;
+
+
+ if ( !loader )
+ return;
+
+ base = &loader->base;
+ current = &loader->current;
+
+ n_curr_contours = current->outline.n_contours;
+ n_base_points = base->outline.n_points;
+
+ base->outline.n_points =
+ (short)( base->outline.n_points + current->outline.n_points );
+ base->outline.n_contours =
+ (short)( base->outline.n_contours + current->outline.n_contours );
+
+ base->num_subglyphs += current->num_subglyphs;
+
+ /* adjust contours count in newest outline */
+ for ( n = 0; n < n_curr_contours; n++ )
+ current->outline.contours[n] =
+ (short)( current->outline.contours[n] + n_base_points );
+
+ /* prepare for another new glyph image */
+ FT_GlyphLoader_Prepare( loader );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_GlyphLoader_CopyPoints( FT_GlyphLoader target,
+ FT_GlyphLoader source )
+ {
+ FT_Error error;
+ FT_UInt num_points = source->base.outline.n_points;
+ FT_UInt num_contours = source->base.outline.n_contours;
+
+
+ error = FT_GlyphLoader_CheckPoints( target, num_points, num_contours );
+ if ( !error )
+ {
+ FT_Outline* out = &target->base.outline;
+ FT_Outline* in = &source->base.outline;
+
+
+ FT_ARRAY_COPY( out->points, in->points,
+ num_points );
+ FT_ARRAY_COPY( out->tags, in->tags,
+ num_points );
+ FT_ARRAY_COPY( out->contours, in->contours,
+ num_contours );
+
+ /* do we need to copy the extra points? */
+ if ( target->use_extra && source->use_extra )
+ {
+ FT_ARRAY_COPY( target->base.extra_points, source->base.extra_points,
+ num_points );
+ FT_ARRAY_COPY( target->base.extra_points2, source->base.extra_points2,
+ num_points );
+ }
+
+ out->n_points = (short)num_points;
+ out->n_contours = (short)num_contours;
+
+ FT_GlyphLoader_Adjust_Points( target );
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftglyph.c b/src/base/ftglyph.c
new file mode 100644
index 0000000..969c5db
--- /dev/null
+++ b/src/base/ftglyph.c
@@ -0,0 +1,682 @@
+/***************************************************************************/
+/* */
+/* ftglyph.c */
+/* */
+/* FreeType convenience functions to handle glyphs (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file contains the definition of several convenience functions */
+ /* that can be used by client applications to easily retrieve glyph */
+ /* bitmaps and outlines from a given face. */
+ /* */
+ /* These functions should be optional if you are writing a font server */
+ /* or text layout engine on top of FreeType. However, they are pretty */
+ /* handy for many other simple uses of the library. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+#include FT_BITMAP_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_glyph
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** Convenience functions ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Matrix_Multiply( const FT_Matrix* a,
+ FT_Matrix *b )
+ {
+ FT_Fixed xx, xy, yx, yy;
+
+
+ if ( !a || !b )
+ return;
+
+ xx = FT_MulFix( a->xx, b->xx ) + FT_MulFix( a->xy, b->yx );
+ xy = FT_MulFix( a->xx, b->xy ) + FT_MulFix( a->xy, b->yy );
+ yx = FT_MulFix( a->yx, b->xx ) + FT_MulFix( a->yy, b->yx );
+ yy = FT_MulFix( a->yx, b->xy ) + FT_MulFix( a->yy, b->yy );
+
+ b->xx = xx; b->xy = xy;
+ b->yx = yx; b->yy = yy;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Matrix_Invert( FT_Matrix* matrix )
+ {
+ FT_Pos delta, xx, yy;
+
+
+ if ( !matrix )
+ return FT_Err_Invalid_Argument;
+
+ /* compute discriminant */
+ delta = FT_MulFix( matrix->xx, matrix->yy ) -
+ FT_MulFix( matrix->xy, matrix->yx );
+
+ if ( !delta )
+ return FT_Err_Invalid_Argument; /* matrix can't be inverted */
+
+ matrix->xy = - FT_DivFix( matrix->xy, delta );
+ matrix->yx = - FT_DivFix( matrix->yx, delta );
+
+ xx = matrix->xx;
+ yy = matrix->yy;
+
+ matrix->xx = FT_DivFix( yy, delta );
+ matrix->yy = FT_DivFix( xx, delta );
+
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_BitmapGlyph support ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_bitmap_glyph_init( FT_Glyph bitmap_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+ FT_Error error = FT_Err_Ok;
+ FT_Library library = FT_GLYPH( glyph )->library;
+
+
+ if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ error = FT_Err_Invalid_Glyph_Format;
+ goto Exit;
+ }
+
+ glyph->left = slot->bitmap_left;
+ glyph->top = slot->bitmap_top;
+
+ /* do lazy copying whenever possible */
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ glyph->bitmap = slot->bitmap;
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+ else
+ {
+ FT_Bitmap_New( &glyph->bitmap );
+ error = FT_Bitmap_Copy( library, &slot->bitmap, &glyph->bitmap );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_bitmap_glyph_copy( FT_Glyph bitmap_source,
+ FT_Glyph bitmap_target )
+ {
+ FT_Library library = bitmap_source->library;
+ FT_BitmapGlyph source = (FT_BitmapGlyph)bitmap_source;
+ FT_BitmapGlyph target = (FT_BitmapGlyph)bitmap_target;
+
+
+ target->left = source->left;
+ target->top = source->top;
+
+ return FT_Bitmap_Copy( library, &source->bitmap, &target->bitmap );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_bitmap_glyph_done( FT_Glyph bitmap_glyph )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+ FT_Library library = FT_GLYPH( glyph )->library;
+
+
+ FT_Bitmap_Done( library, &glyph->bitmap );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_bitmap_glyph_bbox( FT_Glyph bitmap_glyph,
+ FT_BBox* cbox )
+ {
+ FT_BitmapGlyph glyph = (FT_BitmapGlyph)bitmap_glyph;
+
+
+ cbox->xMin = glyph->left << 6;
+ cbox->xMax = cbox->xMin + ( glyph->bitmap.width << 6 );
+ cbox->yMax = glyph->top << 6;
+ cbox->yMin = cbox->yMax - ( glyph->bitmap.rows << 6 );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Glyph_Class ft_bitmap_glyph_class =
+ {
+ sizeof ( FT_BitmapGlyphRec ),
+ FT_GLYPH_FORMAT_BITMAP,
+
+ ft_bitmap_glyph_init,
+ ft_bitmap_glyph_done,
+ ft_bitmap_glyph_copy,
+ 0, /* FT_Glyph_TransformFunc */
+ ft_bitmap_glyph_bbox,
+ 0 /* FT_Glyph_PrepareFunc */
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_OutlineGlyph support ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_init( FT_Glyph outline_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+ FT_Error error = FT_Err_Ok;
+ FT_Library library = FT_GLYPH( glyph )->library;
+ FT_Outline* source = &slot->outline;
+ FT_Outline* target = &glyph->outline;
+
+
+ /* check format in glyph slot */
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ {
+ error = FT_Err_Invalid_Glyph_Format;
+ goto Exit;
+ }
+
+ /* allocate new outline */
+ error = FT_Outline_New( library, source->n_points, source->n_contours,
+ &glyph->outline );
+ if ( error )
+ goto Exit;
+
+ FT_Outline_Copy( source, target );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_done( FT_Glyph outline_glyph )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ FT_Outline_Done( FT_GLYPH( glyph )->library, &glyph->outline );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_copy( FT_Glyph outline_source,
+ FT_Glyph outline_target )
+ {
+ FT_OutlineGlyph source = (FT_OutlineGlyph)outline_source;
+ FT_OutlineGlyph target = (FT_OutlineGlyph)outline_target;
+ FT_Error error;
+ FT_Library library = FT_GLYPH( source )->library;
+
+
+ error = FT_Outline_New( library, source->outline.n_points,
+ source->outline.n_contours, &target->outline );
+ if ( !error )
+ FT_Outline_Copy( &source->outline, &target->outline );
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_transform( FT_Glyph outline_glyph,
+ const FT_Matrix* matrix,
+ const FT_Vector* delta )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ if ( matrix )
+ FT_Outline_Transform( &glyph->outline, matrix );
+
+ if ( delta )
+ FT_Outline_Translate( &glyph->outline, delta->x, delta->y );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ft_outline_glyph_bbox( FT_Glyph outline_glyph,
+ FT_BBox* bbox )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ FT_Outline_Get_CBox( &glyph->outline, bbox );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ft_outline_glyph_prepare( FT_Glyph outline_glyph,
+ FT_GlyphSlot slot )
+ {
+ FT_OutlineGlyph glyph = (FT_OutlineGlyph)outline_glyph;
+
+
+ slot->format = FT_GLYPH_FORMAT_OUTLINE;
+ slot->outline = glyph->outline;
+ slot->outline.flags &= ~FT_OUTLINE_OWNER;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Glyph_Class ft_outline_glyph_class =
+ {
+ sizeof ( FT_OutlineGlyphRec ),
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ ft_outline_glyph_init,
+ ft_outline_glyph_done,
+ ft_outline_glyph_copy,
+ ft_outline_glyph_transform,
+ ft_outline_glyph_bbox,
+ ft_outline_glyph_prepare
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** FT_Glyph class and API ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ ft_new_glyph( FT_Library library,
+ const FT_Glyph_Class* clazz,
+ FT_Glyph* aglyph )
+ {
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_Glyph glyph;
+
+
+ *aglyph = 0;
+
+ if ( !FT_ALLOC( glyph, clazz->glyph_size ) )
+ {
+ glyph->library = library;
+ glyph->clazz = clazz;
+ glyph->format = clazz->glyph_format;
+
+ *aglyph = glyph;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Copy( FT_Glyph source,
+ FT_Glyph *target )
+ {
+ FT_Glyph copy;
+ FT_Error error;
+ const FT_Glyph_Class* clazz;
+
+
+ *target = 0;
+
+ /* check arguments */
+ if ( !target || !source || !source->clazz )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ clazz = source->clazz;
+ error = ft_new_glyph( source->library, clazz, &copy );
+ if ( error )
+ goto Exit;
+
+ copy->advance = source->advance;
+ copy->format = source->format;
+
+ if ( clazz->glyph_copy )
+ error = clazz->glyph_copy( source, copy );
+
+ if ( error )
+ FT_Done_Glyph( copy );
+ else
+ *target = copy;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Glyph( FT_GlyphSlot slot,
+ FT_Glyph *aglyph )
+ {
+ FT_Library library;
+ FT_Error error;
+ FT_Glyph glyph;
+
+ const FT_Glyph_Class* clazz = 0;
+
+
+ if ( !slot )
+ return FT_Err_Invalid_Slot_Handle;
+
+ library = slot->library;
+
+ if ( !aglyph )
+ return FT_Err_Invalid_Argument;
+
+ /* if it is a bitmap, that's easy :-) */
+ if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
+ clazz = &ft_bitmap_glyph_class;
+
+ /* it it is an outline too */
+ else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ clazz = &ft_outline_glyph_class;
+
+ else
+ {
+ /* try to find a renderer that supports the glyph image format */
+ FT_Renderer render = FT_Lookup_Renderer( library, slot->format, 0 );
+
+
+ if ( render )
+ clazz = &render->glyph_class;
+ }
+
+ if ( !clazz )
+ {
+ error = FT_Err_Invalid_Glyph_Format;
+ goto Exit;
+ }
+
+ /* create FT_Glyph object */
+ error = ft_new_glyph( library, clazz, &glyph );
+ if ( error )
+ goto Exit;
+
+ /* copy advance while converting it to 16.16 format */
+ glyph->advance.x = slot->advance.x << 10;
+ glyph->advance.y = slot->advance.y << 10;
+
+ /* now import the image from the glyph slot */
+ error = clazz->glyph_init( glyph, slot );
+
+ /* if an error occurred, destroy the glyph */
+ if ( error )
+ FT_Done_Glyph( glyph );
+ else
+ *aglyph = glyph;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Transform( FT_Glyph glyph,
+ FT_Matrix* matrix,
+ FT_Vector* delta )
+ {
+ const FT_Glyph_Class* clazz;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !glyph || !glyph->clazz )
+ error = FT_Err_Invalid_Argument;
+ else
+ {
+ clazz = glyph->clazz;
+ if ( clazz->glyph_transform )
+ {
+ /* transform glyph image */
+ clazz->glyph_transform( glyph, matrix, delta );
+
+ /* transform advance vector */
+ if ( matrix )
+ FT_Vector_Transform( &glyph->advance, matrix );
+ }
+ else
+ error = FT_Err_Invalid_Glyph_Format;
+ }
+ return error;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Glyph_Get_CBox( FT_Glyph glyph,
+ FT_UInt bbox_mode,
+ FT_BBox *acbox )
+ {
+ const FT_Glyph_Class* clazz;
+
+
+ if ( !acbox )
+ return;
+
+ acbox->xMin = acbox->yMin = acbox->xMax = acbox->yMax = 0;
+
+ if ( !glyph || !glyph->clazz )
+ return;
+ else
+ {
+ clazz = glyph->clazz;
+ if ( !clazz->glyph_bbox )
+ return;
+ else
+ {
+ /* retrieve bbox in 26.6 coordinates */
+ clazz->glyph_bbox( glyph, acbox );
+
+ /* perform grid fitting if needed */
+ if ( bbox_mode == FT_GLYPH_BBOX_GRIDFIT ||
+ bbox_mode == FT_GLYPH_BBOX_PIXELS )
+ {
+ acbox->xMin = FT_PIX_FLOOR( acbox->xMin );
+ acbox->yMin = FT_PIX_FLOOR( acbox->yMin );
+ acbox->xMax = FT_PIX_CEIL( acbox->xMax );
+ acbox->yMax = FT_PIX_CEIL( acbox->yMax );
+ }
+
+ /* convert to integer pixels if needed */
+ if ( bbox_mode == FT_GLYPH_BBOX_TRUNCATE ||
+ bbox_mode == FT_GLYPH_BBOX_PIXELS )
+ {
+ acbox->xMin >>= 6;
+ acbox->yMin >>= 6;
+ acbox->xMax >>= 6;
+ acbox->yMax >>= 6;
+ }
+ }
+ }
+ return;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_To_Bitmap( FT_Glyph* the_glyph,
+ FT_Render_Mode render_mode,
+ FT_Vector* origin,
+ FT_Bool destroy )
+ {
+ FT_GlyphSlotRec dummy;
+ FT_GlyphSlot_InternalRec dummy_internal;
+ FT_Error error = FT_Err_Ok;
+ FT_Glyph glyph;
+ FT_BitmapGlyph bitmap = NULL;
+
+ const FT_Glyph_Class* clazz;
+
+
+ /* check argument */
+ if ( !the_glyph )
+ goto Bad;
+
+ /* we render the glyph into a glyph bitmap using a `dummy' glyph slot */
+ /* then calling FT_Render_Glyph_Internal() */
+
+ glyph = *the_glyph;
+ if ( !glyph )
+ goto Bad;
+
+ clazz = glyph->clazz;
+
+ /* when called with a bitmap glyph, do nothing and return successfully */
+ if ( clazz == &ft_bitmap_glyph_class )
+ goto Exit;
+
+ if ( !clazz || !clazz->glyph_prepare )
+ goto Bad;
+
+ FT_MEM_ZERO( &dummy, sizeof ( dummy ) );
+ FT_MEM_ZERO( &dummy_internal, sizeof ( dummy_internal ) );
+ dummy.internal = &dummy_internal;
+ dummy.library = glyph->library;
+ dummy.format = clazz->glyph_format;
+
+ /* create result bitmap glyph */
+ error = ft_new_glyph( glyph->library, &ft_bitmap_glyph_class,
+ (FT_Glyph*)(void*)&bitmap );
+ if ( error )
+ goto Exit;
+
+#if 1
+ /* if `origin' is set, translate the glyph image */
+ if ( origin )
+ FT_Glyph_Transform( glyph, 0, origin );
+#else
+ FT_UNUSED( origin );
+#endif
+
+ /* prepare dummy slot for rendering */
+ error = clazz->glyph_prepare( glyph, &dummy );
+ if ( !error )
+ error = FT_Render_Glyph_Internal( glyph->library, &dummy, render_mode );
+
+#if 1
+ if ( !destroy && origin )
+ {
+ FT_Vector v;
+
+
+ v.x = -origin->x;
+ v.y = -origin->y;
+ FT_Glyph_Transform( glyph, 0, &v );
+ }
+#endif
+
+ if ( error )
+ goto Exit;
+
+ /* in case of success, copy the bitmap to the glyph bitmap */
+ error = ft_bitmap_glyph_init( (FT_Glyph)bitmap, &dummy );
+ if ( error )
+ goto Exit;
+
+ /* copy advance */
+ bitmap->root.advance = glyph->advance;
+
+ if ( destroy )
+ FT_Done_Glyph( glyph );
+
+ *the_glyph = FT_GLYPH( bitmap );
+
+ Exit:
+ if ( error && bitmap )
+ FT_Done_Glyph( FT_GLYPH( bitmap ) );
+
+ return error;
+
+ Bad:
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+
+ /* documentation is in ftglyph.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Done_Glyph( FT_Glyph glyph )
+ {
+ if ( glyph )
+ {
+ FT_Memory memory = glyph->library->memory;
+ const FT_Glyph_Class* clazz = glyph->clazz;
+
+
+ if ( clazz->glyph_done )
+ clazz->glyph_done( glyph );
+
+ FT_FREE( glyph );
+ }
+ }
+
+
+/* END */
diff --git a/src/base/ftgxval.c b/src/base/ftgxval.c
new file mode 100644
index 0000000..32662be
--- /dev/null
+++ b/src/base/ftgxval.c
@@ -0,0 +1,129 @@
+/***************************************************************************/
+/* */
+/* ftgxval.c */
+/* */
+/* FreeType API for validating TrueTyepGX/AAT tables (body). */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* Masatake YAMATO, Redhat K.K, */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_GX_VALIDATE_H
+
+
+ /* documentation is in ftgxval.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_TrueTypeGX_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes tables[FT_VALIDATE_GX_LENGTH],
+ FT_UInt table_length )
+ {
+ FT_Service_GXvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_Err_Invalid_Face_Handle;
+ goto Exit;
+ }
+
+ if ( tables == NULL )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, GX_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ tables,
+ table_length );
+ else
+ error = FT_Err_Unimplemented_Feature;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_TrueTypeGX_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( table );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_ClassicKern_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes *ckern_table )
+ {
+ FT_Service_CKERNvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_Err_Invalid_Face_Handle;
+ goto Exit;
+ }
+
+ if ( ckern_table == NULL )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, CLASSICKERN_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ ckern_table );
+ else
+ error = FT_Err_Unimplemented_Feature;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_ClassicKern_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( table );
+ }
+
+
+/* END */
diff --git a/src/base/ftinit.c b/src/base/ftinit.c
new file mode 100644
index 0000000..7af19c3
--- /dev/null
+++ b/src/base/ftinit.c
@@ -0,0 +1,163 @@
+/***************************************************************************/
+/* */
+/* ftinit.c */
+/* */
+/* FreeType initialization layer (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* The purpose of this file is to implement the following two */
+ /* functions: */
+ /* */
+ /* FT_Add_Default_Modules(): */
+ /* This function is used to add the set of default modules to a */
+ /* fresh new library object. The set is taken from the header file */
+ /* `freetype/config/ftmodule.h'. See the document `FreeType 2.0 */
+ /* Build System' for more information. */
+ /* */
+ /* FT_Init_FreeType(): */
+ /* This function creates a system object for the current platform, */
+ /* builds a library out of it, then calls FT_Default_Drivers(). */
+ /* */
+ /* Note that even if FT_Init_FreeType() uses the implementation of the */
+ /* system object defined at build time, client applications are still */
+ /* able to provide their own `ftsystem.c'. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_MODULE_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_init
+
+#undef FT_USE_MODULE
+#ifdef __cplusplus
+#define FT_USE_MODULE( x ) extern "C" const FT_Module_Class x;
+#else
+#define FT_USE_MODULE( x ) extern const FT_Module_Class x;
+#endif
+
+
+#include FT_CONFIG_MODULES_H
+
+
+#undef FT_USE_MODULE
+#define FT_USE_MODULE( x ) (const FT_Module_Class*)&(x),
+
+ static
+ const FT_Module_Class* const ft_default_modules[] =
+ {
+#include FT_CONFIG_MODULES_H
+ 0
+ };
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Add_Default_Modules( FT_Library library )
+ {
+ FT_Error error;
+ const FT_Module_Class* const* cur;
+
+
+ /* test for valid `library' delayed to FT_Add_Module() */
+
+ cur = ft_default_modules;
+ while ( *cur )
+ {
+ error = FT_Add_Module( library, *cur );
+ /* notify errors, but don't stop */
+ if ( error )
+ {
+ FT_ERROR(( "FT_Add_Default_Module: Cannot install `%s', error = 0x%x\n",
+ (*cur)->module_name, error ));
+ }
+ cur++;
+ }
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Init_FreeType( FT_Library *alibrary )
+ {
+ FT_Error error;
+ FT_Memory memory;
+
+
+ /* First of all, allocate a new system object -- this function is part */
+ /* of the system-specific component, i.e. `ftsystem.c'. */
+
+ memory = FT_New_Memory();
+ if ( !memory )
+ {
+ FT_ERROR(( "FT_Init_FreeType: cannot find memory manager\n" ));
+ return FT_Err_Unimplemented_Feature;
+ }
+
+ /* build a library out of it, then fill it with the set of */
+ /* default drivers. */
+
+ error = FT_New_Library( memory, alibrary );
+ if ( error )
+ FT_Done_Memory( memory );
+ else
+ {
+ (*alibrary)->version_major = FREETYPE_MAJOR;
+ (*alibrary)->version_minor = FREETYPE_MINOR;
+ (*alibrary)->version_patch = FREETYPE_PATCH;
+
+ FT_Add_Default_Modules( *alibrary );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_FreeType( FT_Library library )
+ {
+ if ( library )
+ {
+ FT_Memory memory = library->memory;
+
+
+ /* Discard the library object */
+ FT_Done_Library( library );
+
+ /* discard memory manager */
+ FT_Done_Memory( memory );
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/base/ftlcdfil.c b/src/base/ftlcdfil.c
new file mode 100644
index 0000000..f40bbea
--- /dev/null
+++ b/src/base/ftlcdfil.c
@@ -0,0 +1,351 @@
+/***************************************************************************/
+/* */
+/* ftlcdfil.c */
+/* */
+/* FreeType API for color filtering of subpixel bitmap glyphs (body). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_LCD_FILTER_H
+#include FT_IMAGE_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+/* define USE_LEGACY to implement the legacy filter */
+#define USE_LEGACY
+
+ /* FIR filter used by the default and light filters */
+ static void
+ _ft_lcd_filter_fir( FT_Bitmap* bitmap,
+ FT_Render_Mode mode,
+ FT_Library library )
+ {
+ FT_Byte* weights = library->lcd_weights;
+ FT_UInt width = (FT_UInt)bitmap->width;
+ FT_UInt height = (FT_UInt)bitmap->rows;
+
+
+ /* horizontal in-place FIR filter */
+ if ( mode == FT_RENDER_MODE_LCD && width >= 4 )
+ {
+ FT_Byte* line = bitmap->buffer;
+
+
+ for ( ; height > 0; height--, line += bitmap->pitch )
+ {
+ FT_UInt fir[5];
+ FT_UInt val1, xx;
+
+
+ val1 = line[0];
+ fir[0] = weights[2] * val1;
+ fir[1] = weights[3] * val1;
+ fir[2] = weights[4] * val1;
+ fir[3] = 0;
+ fir[4] = 0;
+
+ val1 = line[1];
+ fir[0] += weights[1] * val1;
+ fir[1] += weights[2] * val1;
+ fir[2] += weights[3] * val1;
+ fir[3] += weights[4] * val1;
+
+ for ( xx = 2; xx < width; xx++ )
+ {
+ FT_UInt val, pix;
+
+
+ val = line[xx];
+ pix = fir[0] + weights[0] * val;
+ fir[0] = fir[1] + weights[1] * val;
+ fir[1] = fir[2] + weights[2] * val;
+ fir[2] = fir[3] + weights[3] * val;
+ fir[3] = weights[4] * val;
+
+ pix >>= 8;
+ pix |= -( pix >> 8 );
+ line[xx - 2] = (FT_Byte)pix;
+ }
+
+ {
+ FT_UInt pix;
+
+
+ pix = fir[0] >> 8;
+ pix |= -( pix >> 8 );
+ line[xx - 2] = (FT_Byte)pix;
+
+ pix = fir[1] >> 8;
+ pix |= -( pix >> 8 );
+ line[xx - 1] = (FT_Byte)pix;
+ }
+ }
+ }
+
+ /* vertical in-place FIR filter */
+ else if ( mode == FT_RENDER_MODE_LCD_V && height >= 4 )
+ {
+ FT_Byte* column = bitmap->buffer;
+ FT_Int pitch = bitmap->pitch;
+
+
+ for ( ; width > 0; width--, column++ )
+ {
+ FT_Byte* col = column;
+ FT_UInt fir[5];
+ FT_UInt val1, yy;
+
+
+ val1 = col[0];
+ fir[0] = weights[2] * val1;
+ fir[1] = weights[3] * val1;
+ fir[2] = weights[4] * val1;
+ fir[3] = 0;
+ fir[4] = 0;
+ col += pitch;
+
+ val1 = col[0];
+ fir[0] += weights[1] * val1;
+ fir[1] += weights[2] * val1;
+ fir[2] += weights[3] * val1;
+ fir[3] += weights[4] * val1;
+ col += pitch;
+
+ for ( yy = 2; yy < height; yy++ )
+ {
+ FT_UInt val, pix;
+
+
+ val = col[0];
+ pix = fir[0] + weights[0] * val;
+ fir[0] = fir[1] + weights[1] * val;
+ fir[1] = fir[2] + weights[2] * val;
+ fir[2] = fir[3] + weights[3] * val;
+ fir[3] = weights[4] * val;
+
+ pix >>= 8;
+ pix |= -( pix >> 8 );
+ col[-2 * pitch] = (FT_Byte)pix;
+ col += pitch;
+ }
+
+ {
+ FT_UInt pix;
+
+
+ pix = fir[0] >> 8;
+ pix |= -( pix >> 8 );
+ col[-2 * pitch] = (FT_Byte)pix;
+
+ pix = fir[1] >> 8;
+ pix |= -( pix >> 8 );
+ col[-pitch] = (FT_Byte)pix;
+ }
+ }
+ }
+ }
+
+
+#ifdef USE_LEGACY
+
+ /* FIR filter used by the default and light filters */
+ static void
+ _ft_lcd_filter_legacy( FT_Bitmap* bitmap,
+ FT_Render_Mode mode,
+ FT_Library library )
+ {
+ FT_UInt width = (FT_UInt)bitmap->width;
+ FT_UInt height = (FT_UInt)bitmap->rows;
+ FT_Int pitch = bitmap->pitch;
+
+ static const int filters[3][3] =
+ {
+ { 65538 * 9/13, 65538 * 1/6, 65538 * 1/13 },
+ { 65538 * 3/13, 65538 * 4/6, 65538 * 3/13 },
+ { 65538 * 1/13, 65538 * 1/6, 65538 * 9/13 }
+ };
+
+ FT_UNUSED( library );
+
+
+ /* horizontal in-place FIR filter */
+ if ( mode == FT_RENDER_MODE_LCD && width >= 3 )
+ {
+ FT_Byte* line = bitmap->buffer;
+
+
+ for ( ; height > 0; height--, line += pitch )
+ {
+ FT_UInt xx;
+
+
+ for ( xx = 0; xx < width; xx += 3 )
+ {
+ FT_UInt r = 0;
+ FT_UInt g = 0;
+ FT_UInt b = 0;
+ FT_UInt p;
+
+
+ p = line[xx];
+ r += filters[0][0] * p;
+ g += filters[0][1] * p;
+ b += filters[0][2] * p;
+
+ p = line[xx + 1];
+ r += filters[1][0] * p;
+ g += filters[1][1] * p;
+ b += filters[1][2] * p;
+
+ p = line[xx + 2];
+ r += filters[2][0] * p;
+ g += filters[2][1] * p;
+ b += filters[2][2] * p;
+
+ line[xx] = (FT_Byte)( r / 65536 );
+ line[xx + 1] = (FT_Byte)( g / 65536 );
+ line[xx + 2] = (FT_Byte)( b / 65536 );
+ }
+ }
+ }
+ else if ( mode == FT_RENDER_MODE_LCD_V && height >= 3 )
+ {
+ FT_Byte* column = bitmap->buffer;
+
+
+ for ( ; width > 0; width--, column++ )
+ {
+ FT_Byte* col = column;
+ FT_Byte* col_end = col + height * pitch;
+
+
+ for ( ; col < col_end; col += 3 * pitch )
+ {
+ FT_UInt r = 0;
+ FT_UInt g = 0;
+ FT_UInt b = 0;
+ FT_UInt p;
+
+
+ p = col[0];
+ r += filters[0][0] * p;
+ g += filters[0][1] * p;
+ b += filters[0][2] * p;
+
+ p = col[pitch];
+ r += filters[1][0] * p;
+ g += filters[1][1] * p;
+ b += filters[1][2] * p;
+
+ p = col[pitch * 2];
+ r += filters[2][0] * p;
+ g += filters[2][1] * p;
+ b += filters[2][2] * p;
+
+ col[0] = (FT_Byte)( r / 65536 );
+ col[pitch] = (FT_Byte)( g / 65536 );
+ col[2 * pitch] = (FT_Byte)( b / 65536 );
+ }
+ }
+ }
+ }
+
+#endif /* USE_LEGACY */
+
+
+ FT_EXPORT( FT_Error )
+ FT_Library_SetLcdFilter( FT_Library library,
+ FT_LcdFilter filter )
+ {
+ static const FT_Byte light_filter[5] =
+ { 0, 85, 86, 85, 0 };
+ /* the values here sum up to a value larger than 256, */
+ /* providing a cheap gamma correction */
+ static const FT_Byte default_filter[5] =
+ { 0x10, 0x40, 0x70, 0x40, 0x10 };
+
+
+ if ( library == NULL )
+ return FT_Err_Invalid_Argument;
+
+ switch ( filter )
+ {
+ case FT_LCD_FILTER_NONE:
+ library->lcd_filter_func = NULL;
+ library->lcd_extra = 0;
+ break;
+
+ case FT_LCD_FILTER_DEFAULT:
+#if defined( FT_FORCE_LEGACY_LCD_FILTER )
+
+ library->lcd_filter_func = _ft_lcd_filter_legacy;
+ library->lcd_extra = 0;
+
+#elif defined( FT_FORCE_LIGHT_LCD_FILTER )
+
+ memcpy( library->lcd_weights, light_filter, 5 );
+ library->lcd_filter_func = _ft_lcd_filter_fir;
+ library->lcd_extra = 2;
+
+#else
+
+ memcpy( library->lcd_weights, default_filter, 5 );
+ library->lcd_filter_func = _ft_lcd_filter_fir;
+ library->lcd_extra = 2;
+
+#endif
+
+ break;
+
+ case FT_LCD_FILTER_LIGHT:
+ memcpy( library->lcd_weights, light_filter, 5 );
+ library->lcd_filter_func = _ft_lcd_filter_fir;
+ library->lcd_extra = 2;
+ break;
+
+#ifdef USE_LEGACY
+
+ case FT_LCD_FILTER_LEGACY:
+ library->lcd_filter_func = _ft_lcd_filter_legacy;
+ library->lcd_extra = 0;
+ break;
+
+#endif
+
+ default:
+ return FT_Err_Invalid_Argument;
+ }
+
+ library->lcd_filter = filter;
+ return 0;
+ }
+
+#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+ FT_EXPORT( FT_Error )
+ FT_Library_SetLcdFilter( FT_Library library,
+ FT_LcdFilter filter )
+ {
+ FT_UNUSED( library );
+ FT_UNUSED( filter );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+
+/* END */
diff --git a/src/base/ftmac.c b/src/base/ftmac.c
new file mode 100644
index 0000000..fd6201a
--- /dev/null
+++ b/src/base/ftmac.c
@@ -0,0 +1,1096 @@
+/***************************************************************************/
+/* */
+/* ftmac.c */
+/* */
+/* Mac FOND support. Written by just@letterror.com. */
+/* Heavily modified by mpsuzuki, George Williams, and Sean McBride. */
+/* */
+/* This file is for Mac OS X only; see builds/mac/ftoldmac.c for */
+/* classic platforms built by MPW. */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*
+ Notes
+
+ Mac suitcase files can (and often do!) contain multiple fonts. To
+ support this I use the face_index argument of FT_(Open|New)_Face()
+ functions, and pretend the suitcase file is a collection.
+
+ Warning: fbit and NFNT bitmap resources are not supported yet. In old
+ sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
+ resources instead of the `bdat' table in the sfnt resource. Therefore,
+ face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
+ resource is unavailable at present.
+
+ The Mac FOND support works roughly like this:
+
+ - Check whether the offered stream points to a Mac suitcase file. This
+ is done by checking the file type: it has to be 'FFIL' or 'tfil'. The
+ stream that gets passed to our init_face() routine is a stdio stream,
+ which isn't usable for us, since the FOND resources live in the
+ resource fork. So we just grab the stream->pathname field.
+
+ - Read the FOND resource into memory, then check whether there is a
+ TrueType font and/or(!) a Type 1 font available.
+
+ - If there is a Type 1 font available (as a separate `LWFN' file), read
+ its data into memory, massage it slightly so it becomes PFB data, wrap
+ it into a memory stream, load the Type 1 driver and delegate the rest
+ of the work to it by calling FT_Open_Face(). (XXX TODO: after this
+ has been done, the kerning data from the FOND resource should be
+ appended to the face: On the Mac there are usually no AFM files
+ available. However, this is tricky since we need to map Mac char
+ codes to ps glyph names to glyph ID's...)
+
+ - If there is a TrueType font (an `sfnt' resource), read it into memory,
+ wrap it into a memory stream, load the TrueType driver and delegate
+ the rest of the work to it, by calling FT_Open_Face().
+
+ - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
+ itself, even though it doesn't contains `POST' resources. To handle
+ this special case without opening the file an extra time, we just
+ ignore errors from the `LWFN' and fallback to the `sfnt' if both are
+ available.
+ */
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_STREAM_H
+
+ /* This is for Mac OS X. Without redefinition, OS_INLINE */
+ /* expands to `static inline' which doesn't survive the */
+ /* -ansi compilation flag of GCC. */
+#if !HAVE_ANSI_OS_INLINE
+#undef OS_INLINE
+#define OS_INLINE static __inline__
+#endif
+#include <Carbon/Carbon.h>
+
+#ifndef HFS_MAXPATHLEN
+#define HFS_MAXPATHLEN 1024
+#endif
+
+#define FT_DEPRECATED_ATTRIBUTE
+
+#include FT_MAC_H
+
+ /* undefine blocking-macros in ftmac.h */
+#undef FT_GetFile_From_Mac_Name( a, b, c )
+#undef FT_GetFile_From_Mac_ATS_Name( a, b, c )
+#undef FT_New_Face_From_FSSpec( a, b, c, d )
+
+
+ /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
+ TrueType in case *both* are available (this is not common,
+ but it *is* possible). */
+#ifndef PREFER_LWFN
+#define PREFER_LWFN 1
+#endif
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFile_From_Mac_Name( const char* fontName,
+ FSSpec* pathSpec,
+ FT_Long* face_index )
+ {
+ FT_UNUSED( fontName );
+ FT_UNUSED( pathSpec );
+ FT_UNUSED( face_index );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ /* Private function. */
+ /* The FSSpec type has been discouraged for a long time, */
+ /* but for some reason, there is no FSRef version of */
+ /* ATSFontGetFileSpecification(), so we made our own. */
+ /* Apple will provide one eventually. */
+ static OSStatus
+ FT_ATSFontGetFileReference( ATSFontRef ats_font_id,
+ FSRef* ats_font_ref )
+ {
+#if __LP64__
+ FT_UNUSED( ats_font_id );
+ FT_UNUSED( ats_font_ref );
+
+ return fnfErr;
+#else
+ OSStatus err;
+ FSSpec spec;
+
+
+ err = ATSFontGetFileSpecification( ats_font_id, &spec );
+ if ( noErr == err )
+ err = FSpMakeFSRef( &spec, ats_font_ref );
+
+ return err;
+#endif
+ }
+
+
+ static FT_Error
+ FT_GetFileRef_From_Mac_ATS_Name( const char* fontName,
+ FSRef* ats_font_ref,
+ FT_Long* face_index )
+ {
+ CFStringRef cf_fontName;
+ ATSFontRef ats_font_id;
+
+
+ *face_index = 0;
+
+ cf_fontName = CFStringCreateWithCString( NULL, fontName,
+ kCFStringEncodingMacRoman );
+ ats_font_id = ATSFontFindFromName( cf_fontName,
+ kATSOptionFlagsUnRestrictedScope );
+ CFRelease( cf_fontName );
+
+ if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
+ return FT_Err_Unknown_File_Format;
+
+ if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) )
+ return FT_Err_Unknown_File_Format;
+
+ /* face_index calculation by searching preceding fontIDs */
+ /* with same FSRef */
+ {
+ ATSFontRef id2 = ats_font_id - 1;
+ FSRef ref2;
+
+
+ while ( id2 > 0 )
+ {
+ if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) )
+ break;
+ if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) )
+ break;
+
+ id2 --;
+ }
+ *face_index = ats_font_id - ( id2 + 1 );
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFilePath_From_Mac_ATS_Name( const char* fontName,
+ UInt8* path,
+ UInt32 maxPathSize,
+ FT_Long* face_index )
+ {
+ FSRef ref;
+ FT_Error err;
+
+
+ err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
+ if ( FT_Err_Ok != err )
+ return err;
+
+ if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
+ return FT_Err_Unknown_File_Format;
+
+ return FT_Err_Ok;
+ }
+
+
+ /* This function is deprecated because FSSpec is deprecated in Mac OS X */
+ FT_EXPORT_DEF( FT_Error )
+ FT_GetFile_From_Mac_ATS_Name( const char* fontName,
+ FSSpec* pathSpec,
+ FT_Long* face_index )
+ {
+#if __LP64__
+ FT_UNUSED( fontName );
+ FT_UNUSED( pathSpec );
+ FT_UNUSED( face_index );
+
+ return FT_Err_Unimplemented_Feature;
+#else
+ FSRef ref;
+ FT_Error err;
+
+
+ err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
+ if ( FT_Err_Ok != err )
+ return err;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
+ pathSpec, NULL ) )
+ return FT_Err_Unknown_File_Format;
+
+ return FT_Err_Ok;
+#endif
+ }
+
+
+ static OSErr
+ FT_FSPathMakeRes( const UInt8* pathname,
+ short* res )
+ {
+ OSErr err;
+ FSRef ref;
+
+
+ if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
+ return FT_Err_Cannot_Open_Resource;
+
+ /* at present, no support for dfont format */
+ err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
+ if ( noErr == err )
+ return err;
+
+ /* fallback to original resource-fork font */
+ *res = FSOpenResFile( &ref, fsRdPerm );
+ err = ResError();
+
+ return err;
+ }
+
+
+ /* Return the file type for given pathname */
+ static OSType
+ get_file_type_from_path( const UInt8* pathname )
+ {
+ FSRef ref;
+ FSCatalogInfo info;
+
+
+ if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
+ return ( OSType ) 0;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
+ NULL, NULL, NULL ) )
+ return ( OSType ) 0;
+
+ return ((FInfo *)(info.finderInfo))->fdType;
+ }
+
+
+ /* Given a PostScript font name, create the Macintosh LWFN file name. */
+ static void
+ create_lwfn_name( char* ps_name,
+ Str255 lwfn_file_name )
+ {
+ int max = 5, count = 0;
+ FT_Byte* p = lwfn_file_name;
+ FT_Byte* q = (FT_Byte*)ps_name;
+
+
+ lwfn_file_name[0] = 0;
+
+ while ( *q )
+ {
+ if ( ft_isupper( *q ) )
+ {
+ if ( count )
+ max = 3;
+ count = 0;
+ }
+ if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
+ {
+ *++p = *q;
+ lwfn_file_name[0]++;
+ count++;
+ }
+ q++;
+ }
+ }
+
+
+ static short
+ count_faces_sfnt( char* fond_data )
+ {
+ /* The count is 1 greater than the value in the FOND. */
+ /* Isn't that cute? :-) */
+
+ return EndianS16_BtoN( *( (short*)( fond_data +
+ sizeof ( FamRec ) ) ) ) + 1;
+ }
+
+
+ static short
+ count_faces_scalable( char* fond_data )
+ {
+ AsscEntry* assoc;
+ FamRec* fond;
+ short i, face, face_all;
+
+
+ fond = (FamRec*)fond_data;
+ face_all = EndianS16_BtoN( *( (short *)( fond_data +
+ sizeof ( FamRec ) ) ) ) + 1;
+ assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+ face = 0;
+
+ for ( i = 0; i < face_all; i++ )
+ {
+ if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
+ face++;
+ }
+ return face;
+ }
+
+
+ /* Look inside the FOND data, answer whether there should be an SFNT
+ resource, and answer the name of a possible LWFN Type 1 file.
+
+ Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
+ to load a face OTHER than the first one in the FOND!
+ */
+
+
+ static void
+ parse_fond( char* fond_data,
+ short* have_sfnt,
+ short* sfnt_id,
+ Str255 lwfn_file_name,
+ short face_index )
+ {
+ AsscEntry* assoc;
+ AsscEntry* base_assoc;
+ FamRec* fond;
+
+
+ *sfnt_id = 0;
+ *have_sfnt = 0;
+ lwfn_file_name[0] = 0;
+
+ fond = (FamRec*)fond_data;
+ assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
+ base_assoc = assoc;
+
+ /* Let's do a little range checking before we get too excited here */
+ if ( face_index < count_faces_sfnt( fond_data ) )
+ {
+ assoc += face_index; /* add on the face_index! */
+
+ /* if the face at this index is not scalable,
+ fall back to the first one (old behavior) */
+ if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
+ {
+ *have_sfnt = 1;
+ *sfnt_id = EndianS16_BtoN( assoc->fontID );
+ }
+ else if ( base_assoc->fontSize == 0 )
+ {
+ *have_sfnt = 1;
+ *sfnt_id = EndianS16_BtoN( base_assoc->fontID );
+ }
+ }
+
+ if ( EndianS32_BtoN( fond->ffStylOff ) )
+ {
+ unsigned char* p = (unsigned char*)fond_data;
+ StyleTable* style;
+ unsigned short string_count;
+ char ps_name[256];
+ unsigned char* names[64];
+ int i;
+
+
+ p += EndianS32_BtoN( fond->ffStylOff );
+ style = (StyleTable*)p;
+ p += sizeof ( StyleTable );
+ string_count = EndianS16_BtoN( *(short*)(p) );
+ p += sizeof ( short );
+
+ for ( i = 0; i < string_count && i < 64; i++ )
+ {
+ names[i] = p;
+ p += names[i][0];
+ p++;
+ }
+
+ {
+ size_t ps_name_len = (size_t)names[0][0];
+
+
+ if ( ps_name_len != 0 )
+ {
+ ft_memcpy(ps_name, names[0] + 1, ps_name_len);
+ ps_name[ps_name_len] = 0;
+ }
+ if ( style->indexes[0] > 1 )
+ {
+ unsigned char* suffixes = names[style->indexes[0] - 1];
+
+
+ for ( i = 1; i <= suffixes[0]; i++ )
+ {
+ unsigned char* s;
+ size_t j = suffixes[i] - 1;
+
+
+ if ( j < string_count && ( s = names[j] ) != NULL )
+ {
+ size_t s_len = (size_t)s[0];
+
+
+ if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
+ {
+ ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
+ ps_name_len += s_len;
+ ps_name[ps_name_len] = 0;
+ }
+ }
+ }
+ }
+ }
+
+ create_lwfn_name( ps_name, lwfn_file_name );
+ }
+ }
+
+
+ static FT_Error
+ lookup_lwfn_by_fond( const UInt8* path_fond,
+ ConstStr255Param base_lwfn,
+ UInt8* path_lwfn,
+ size_t path_size )
+ {
+ FSRef ref, par_ref;
+ int dirname_len;
+
+
+ /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
+ /* We should not extract parent directory by string manipulation. */
+
+ if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
+ return FT_Err_Invalid_Argument;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
+ NULL, NULL, NULL, &par_ref ) )
+ return FT_Err_Invalid_Argument;
+
+ if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
+ return FT_Err_Invalid_Argument;
+
+ if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
+ return FT_Err_Invalid_Argument;
+
+ /* now we have absolute dirname in path_lwfn */
+ ft_strcat( (char *)path_lwfn, "/" );
+ dirname_len = ft_strlen( (char *)path_lwfn );
+ ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
+ path_lwfn[dirname_len + base_lwfn[0]] = '\0';
+
+ if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
+ return FT_Err_Cannot_Open_Resource;
+
+ if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
+ NULL, NULL, NULL, NULL ) )
+ return FT_Err_Cannot_Open_Resource;
+
+ return FT_Err_Ok;
+ }
+
+
+ static short
+ count_faces( Handle fond,
+ const UInt8* pathname )
+ {
+ short sfnt_id;
+ short have_sfnt, have_lwfn;
+ Str255 lwfn_file_name;
+ UInt8 buff[HFS_MAXPATHLEN];
+ FT_Error err;
+ short num_faces;
+
+
+ have_sfnt = have_lwfn = 0;
+
+ parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
+
+ if ( lwfn_file_name[0] )
+ {
+ err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
+ buff, sizeof ( buff ) );
+ if ( FT_Err_Ok == err )
+ have_lwfn = 1;
+ }
+
+ if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
+ num_faces = 1;
+ else
+ num_faces = count_faces_scalable( *fond );
+
+ return num_faces;
+ }
+
+
+ /* Read Type 1 data from the POST resources inside the LWFN file,
+ return a PFB buffer. This is somewhat convoluted because the FT2
+ PFB parser wants the ASCII header as one chunk, and the LWFN
+ chunks are often not organized that way, so we glue chunks
+ of the same type together. */
+ static FT_Error
+ read_lwfn( FT_Memory memory,
+ short res,
+ FT_Byte** pfb_data,
+ FT_ULong* size )
+ {
+ FT_Error error = FT_Err_Ok;
+ short res_id;
+ unsigned char *buffer, *p, *size_p = NULL;
+ FT_ULong total_size = 0;
+ FT_ULong old_total_size = 0;
+ FT_ULong post_size, pfb_chunk_size;
+ Handle post_data;
+ char code, last_code;
+
+
+ UseResFile( res );
+
+ /* First pass: load all POST resources, and determine the size of */
+ /* the output buffer. */
+ res_id = 501;
+ last_code = -1;
+
+ for (;;)
+ {
+ post_data = Get1Resource( 'POST', res_id++ );
+ if ( post_data == NULL )
+ break; /* we are done */
+
+ code = (*post_data)[0];
+
+ if ( code != last_code )
+ {
+ if ( code == 5 )
+ total_size += 2; /* just the end code */
+ else
+ total_size += 6; /* code + 4 bytes chunk length */
+ }
+
+ total_size += GetHandleSize( post_data ) - 2;
+ last_code = code;
+
+ /* detect integer overflows */
+ if ( total_size < old_total_size )
+ {
+ error = FT_Err_Array_Too_Large;
+ goto Error;
+ }
+
+ old_total_size = total_size;
+ }
+
+ if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
+ goto Error;
+
+ /* Second pass: append all POST data to the buffer, add PFB fields. */
+ /* Glue all consecutive chunks of the same type together. */
+ p = buffer;
+ res_id = 501;
+ last_code = -1;
+ pfb_chunk_size = 0;
+
+ for (;;)
+ {
+ post_data = Get1Resource( 'POST', res_id++ );
+ if ( post_data == NULL )
+ break; /* we are done */
+
+ post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
+ code = (*post_data)[0];
+
+ if ( code != last_code )
+ {
+ if ( last_code != -1 )
+ {
+ /* we are done adding a chunk, fill in the size field */
+ if ( size_p != NULL )
+ {
+ *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
+ *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
+ }
+ pfb_chunk_size = 0;
+ }
+
+ *p++ = 0x80;
+ if ( code == 5 )
+ *p++ = 0x03; /* the end */
+ else if ( code == 2 )
+ *p++ = 0x02; /* binary segment */
+ else
+ *p++ = 0x01; /* ASCII segment */
+
+ if ( code != 5 )
+ {
+ size_p = p; /* save for later */
+ p += 4; /* make space for size field */
+ }
+ }
+
+ ft_memcpy( p, *post_data + 2, post_size );
+ pfb_chunk_size += post_size;
+ p += post_size;
+ last_code = code;
+ }
+
+ *pfb_data = buffer;
+ *size = total_size;
+
+ Error:
+ CloseResFile( res );
+ return error;
+ }
+
+
+ /* Finalizer for a memory stream; gets called by FT_Done_Face().
+ It frees the memory it uses. */
+ static void
+ memory_stream_close( FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_FREE( stream->base );
+
+ stream->size = 0;
+ stream->base = 0;
+ stream->close = 0;
+ }
+
+
+ /* Create a new memory stream from a buffer and a size. */
+ static FT_Error
+ new_memory_stream( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Stream_CloseFunc close,
+ FT_Stream* astream )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stream stream;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !base )
+ return FT_Err_Invalid_Argument;
+
+ *astream = 0;
+ memory = library->memory;
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ FT_Stream_OpenMemory( stream, base, size );
+
+ stream->close = close;
+
+ *astream = stream;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Create a new FT_Face given a buffer and a driver name. */
+ static FT_Error
+ open_face_from_buffer( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Long face_index,
+ char* driver_name,
+ FT_Face* aface )
+ {
+ FT_Open_Args args;
+ FT_Error error;
+ FT_Stream stream;
+ FT_Memory memory = library->memory;
+
+
+ error = new_memory_stream( library,
+ base,
+ size,
+ memory_stream_close,
+ &stream );
+ if ( error )
+ {
+ FT_FREE( base );
+ return error;
+ }
+
+ args.flags = FT_OPEN_STREAM;
+ args.stream = stream;
+ if ( driver_name )
+ {
+ args.flags = args.flags | FT_OPEN_DRIVER;
+ args.driver = FT_Get_Module( library, driver_name );
+ }
+
+ /* At this point, face_index has served its purpose; */
+ /* whoever calls this function has already used it to */
+ /* locate the correct font data. We should not propagate */
+ /* this index to FT_Open_Face() (unless it is negative). */
+
+ if ( face_index > 0 )
+ face_index = 0;
+
+ error = FT_Open_Face( library, &args, face_index, aface );
+ if ( error == FT_Err_Ok )
+ (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+ else
+ FT_Stream_Free( stream, 0 );
+
+ return error;
+ }
+
+
+ /* Create a new FT_Face from a file spec to an LWFN file. */
+ static FT_Error
+ FT_New_Face_From_LWFN( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Byte* pfb_data;
+ FT_ULong pfb_size;
+ FT_Error error;
+ short res;
+
+
+ if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
+ return FT_Err_Cannot_Open_Resource;
+
+ pfb_data = NULL;
+ pfb_size = 0;
+ error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
+ CloseResFile( res ); /* PFB is already loaded, useless anymore */
+ if ( error )
+ return error;
+
+ return open_face_from_buffer( library,
+ pfb_data,
+ pfb_size,
+ face_index,
+ "type1",
+ aface );
+ }
+
+
+ /* Create a new FT_Face from an SFNT resource, specified by res ID. */
+ static FT_Error
+ FT_New_Face_From_SFNT( FT_Library library,
+ short sfnt_id,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ Handle sfnt = NULL;
+ FT_Byte* sfnt_data;
+ size_t sfnt_size;
+ FT_Error error = FT_Err_Ok;
+ FT_Memory memory = library->memory;
+ int is_cff;
+
+
+ sfnt = GetResource( 'sfnt', sfnt_id );
+ if ( ResError() )
+ return FT_Err_Invalid_Handle;
+
+ sfnt_size = (FT_ULong)GetHandleSize( sfnt );
+ if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
+ {
+ ReleaseResource( sfnt );
+ return error;
+ }
+
+ ft_memcpy( sfnt_data, *sfnt, sfnt_size );
+ ReleaseResource( sfnt );
+
+ is_cff = sfnt_size > 4 && sfnt_data[0] == 'O' &&
+ sfnt_data[1] == 'T' &&
+ sfnt_data[2] == 'T' &&
+ sfnt_data[3] == 'O';
+
+ return open_face_from_buffer( library,
+ sfnt_data,
+ sfnt_size,
+ face_index,
+ is_cff ? "cff" : "truetype",
+ aface );
+ }
+
+
+ /* Create a new FT_Face from a file spec to a suitcase file. */
+ static FT_Error
+ FT_New_Face_From_Suitcase( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Error error = FT_Err_Cannot_Open_Resource;
+ short res_ref, res_index;
+ Handle fond;
+ short num_faces_in_res, num_faces_in_fond;
+
+
+ if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
+ return FT_Err_Cannot_Open_Resource;
+
+ UseResFile( res_ref );
+ if ( ResError() )
+ return FT_Err_Cannot_Open_Resource;
+
+ num_faces_in_res = 0;
+ for ( res_index = 1; ; ++res_index )
+ {
+ fond = Get1IndResource( 'FOND', res_index );
+ if ( ResError() )
+ break;
+
+ num_faces_in_fond = count_faces( fond, pathname );
+ num_faces_in_res += num_faces_in_fond;
+
+ if ( 0 <= face_index && face_index < num_faces_in_fond && error )
+ error = FT_New_Face_From_FOND( library, fond, face_index, aface );
+
+ face_index -= num_faces_in_fond;
+ }
+
+ CloseResFile( res_ref );
+ if ( FT_Err_Ok == error && NULL != aface )
+ (*aface)->num_faces = num_faces_in_res;
+ return error;
+ }
+
+
+ /* documentation is in ftmac.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FOND( FT_Library library,
+ Handle fond,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ short sfnt_id, have_sfnt, have_lwfn = 0;
+ short fond_id;
+ OSType fond_type;
+ Str255 fond_name;
+ Str255 lwfn_file_name;
+ UInt8 path_lwfn[HFS_MAXPATHLEN];
+ OSErr err;
+ FT_Error error = FT_Err_Ok;
+
+
+ GetResInfo( fond, &fond_id, &fond_type, fond_name );
+ if ( ResError() != noErr || fond_type != 'FOND' )
+ return FT_Err_Invalid_File_Format;
+
+ parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
+
+ if ( lwfn_file_name[0] )
+ {
+ short res;
+
+
+ res = HomeResFile( fond );
+ if ( noErr != ResError() )
+ goto found_no_lwfn_file;
+
+ {
+ UInt8 path_fond[HFS_MAXPATHLEN];
+ FSRef ref;
+
+
+ err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
+ NULL, NULL, NULL, &ref, NULL );
+ if ( noErr != err )
+ goto found_no_lwfn_file;
+
+ err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
+ if ( noErr != err )
+ goto found_no_lwfn_file;
+
+ error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
+ path_lwfn, sizeof ( path_lwfn ) );
+ if ( FT_Err_Ok == error )
+ have_lwfn = 1;
+ }
+ }
+
+ if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
+ error = FT_New_Face_From_LWFN( library,
+ path_lwfn,
+ face_index,
+ aface );
+ else
+ error = FT_Err_Unknown_File_Format;
+
+ found_no_lwfn_file:
+ if ( have_sfnt && FT_Err_Ok != error )
+ error = FT_New_Face_From_SFNT( library,
+ sfnt_id,
+ face_index,
+ aface );
+
+ return error;
+ }
+
+
+ /* Common function to load a new FT_Face from a resource file. */
+ static FT_Error
+ FT_New_Face_From_Resource( FT_Library library,
+ const UInt8* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ OSType file_type;
+ FT_Error error;
+
+
+ /* LWFN is a (very) specific file format, check for it explicitly */
+ file_type = get_file_type_from_path( pathname );
+ if ( file_type == 'LWFN' )
+ return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
+
+ /* Otherwise the file type doesn't matter (there are more than */
+ /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */
+ /* if it works, fine. */
+
+ error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
+ if ( error == 0 )
+ return error;
+
+ /* let it fall through to normal loader (.ttf, .otf, etc.); */
+ /* we signal this by returning no error and no FT_Face */
+ *aface = NULL;
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FT_New_Face */
+ /* */
+ /* <Description> */
+ /* This is the Mac-specific implementation of FT_New_Face. In */
+ /* addition to the standard FT_New_Face() functionality, it also */
+ /* accepts pathnames to Mac suitcase files. For further */
+ /* documentation see the original FT_New_Face() in freetype.h. */
+ /* */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face( FT_Library library,
+ const char* pathname,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Open_Args args;
+ FT_Error error;
+
+
+ /* test for valid `library' and `aface' delayed to FT_Open_Face() */
+ if ( !pathname )
+ return FT_Err_Invalid_Argument;
+
+ error = FT_Err_Ok;
+ *aface = NULL;
+
+ /* try resourcefork based font: LWFN, FFIL */
+ error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
+ face_index, aface );
+ if ( error != 0 || *aface != NULL )
+ return error;
+
+ /* let it fall through to normal loader (.ttf, .otf, etc.) */
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FT_New_Face_From_FSRef */
+ /* */
+ /* <Description> */
+ /* FT_New_Face_From_FSRef is identical to FT_New_Face except it */
+ /* accepts an FSRef instead of a path. */
+ /* */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FSRef( FT_Library library,
+ const FSRef* ref,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+ FT_Error error;
+ FT_Open_Args args;
+ OSErr err;
+ UInt8 pathname[HFS_MAXPATHLEN];
+
+
+ if ( !ref )
+ return FT_Err_Invalid_Argument;
+
+ err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
+ if ( err )
+ error = FT_Err_Cannot_Open_Resource;
+
+ error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
+ if ( error != 0 || *aface != NULL )
+ return error;
+
+ /* fallback to datafork font */
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FT_New_Face_From_FSSpec */
+ /* */
+ /* <Description> */
+ /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */
+ /* accepts an FSSpec instead of a path. */
+ /* */
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face_From_FSSpec( FT_Library library,
+ const FSSpec* spec,
+ FT_Long face_index,
+ FT_Face* aface )
+ {
+#if __LP64__
+ FT_UNUSED( library );
+ FT_UNUSED( spec );
+ FT_UNUSED( face_index );
+ FT_UNUSED( aface );
+
+ return FT_Err_Unimplemented_Feature;
+#else
+ FSRef ref;
+
+
+ if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
+ return FT_Err_Invalid_Argument;
+ else
+ return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
+#endif
+ }
+
+
+/* END */
diff --git a/src/base/ftmm.c b/src/base/ftmm.c
new file mode 100644
index 0000000..586d5e8
--- /dev/null
+++ b/src/base/ftmm.c
@@ -0,0 +1,202 @@
+/***************************************************************************/
+/* */
+/* ftmm.c */
+/* */
+/* Multiple Master font support (body). */
+/* */
+/* Copyright 1996-2001, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_MULTIPLE_MASTERS_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_MULTIPLE_MASTERS_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_mm
+
+
+ static FT_Error
+ ft_face_get_mm_service( FT_Face face,
+ FT_Service_MultiMasters *aservice )
+ {
+ FT_Error error;
+
+
+ *aservice = NULL;
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ error = FT_Err_Invalid_Argument;
+
+ if ( FT_HAS_MULTIPLE_MASTERS( face ) )
+ {
+ FT_FACE_LOOKUP_SERVICE( face,
+ *aservice,
+ MULTI_MASTERS );
+
+ if ( aservice )
+ error = FT_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Multi_Master( FT_Face face,
+ FT_Multi_Master *amaster )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->get_mm )
+ error = service->get_mm( face, amaster );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_MM_Var( FT_Face face,
+ FT_MM_Var* *amaster )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->get_mm_var )
+ error = service->get_mm_var( face, amaster );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_MM_Design_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Long* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->set_mm_design )
+ error = service->set_mm_design( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Var_Design_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->set_var_design )
+ error = service->set_var_design( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_MM_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->set_mm_blend )
+ error = service->set_mm_blend( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftmm.h */
+
+ /* This is exactly the same as the previous function. It exists for */
+ /* orthogonality. */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Var_Blend_Coordinates( FT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error;
+ FT_Service_MultiMasters service;
+
+
+ error = ft_face_get_mm_service( face, &service );
+ if ( !error )
+ {
+ error = FT_Err_Invalid_Argument;
+ if ( service->set_mm_blend )
+ error = service->set_mm_blend( face, num_coords, coords );
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftnames.c b/src/base/ftnames.c
new file mode 100644
index 0000000..7fde5c4
--- /dev/null
+++ b/src/base/ftnames.c
@@ -0,0 +1,94 @@
+/***************************************************************************/
+/* */
+/* ftnames.c */
+/* */
+/* Simple interface to access SFNT name tables (which are used */
+/* to hold font names, copyright info, notices, etc.) (body). */
+/* */
+/* This is _not_ used to retrieve glyph names! */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_SFNT_NAMES_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+#include FT_INTERNAL_STREAM_H
+
+
+#ifdef TT_CONFIG_OPTION_SFNT_NAMES
+
+
+ /* documentation is in ftnames.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Sfnt_Name_Count( FT_Face face )
+ {
+ return (face && FT_IS_SFNT( face )) ? ((TT_Face)face)->num_names : 0;
+ }
+
+
+ /* documentation is in ftnames.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Sfnt_Name( FT_Face face,
+ FT_UInt idx,
+ FT_SfntName *aname )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+
+
+ if ( aname && face && FT_IS_SFNT( face ) )
+ {
+ TT_Face ttface = (TT_Face)face;
+
+
+ if ( idx < (FT_UInt)ttface->num_names )
+ {
+ TT_NameEntryRec* entry = ttface->name_table.names + idx;
+
+
+ /* load name on demand */
+ if ( entry->stringLength > 0 && entry->string == NULL )
+ {
+ FT_Memory memory = face->memory;
+ FT_Stream stream = face->stream;
+
+
+ if ( FT_NEW_ARRAY ( entry->string, entry->stringLength ) ||
+ FT_STREAM_SEEK( entry->stringOffset ) ||
+ FT_STREAM_READ( entry->string, entry->stringLength ) )
+ {
+ FT_FREE( entry->string );
+ entry->stringLength = 0;
+ }
+ }
+
+ aname->platform_id = entry->platformID;
+ aname->encoding_id = entry->encodingID;
+ aname->language_id = entry->languageID;
+ aname->name_id = entry->nameID;
+ aname->string = (FT_Byte*)entry->string;
+ aname->string_len = entry->stringLength;
+
+ error = FT_Err_Ok;
+ }
+ }
+
+ return error;
+ }
+
+
+#endif /* TT_CONFIG_OPTION_SFNT_NAMES */
+
+
+/* END */
diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c
new file mode 100644
index 0000000..fa08094
--- /dev/null
+++ b/src/base/ftobjs.c
@@ -0,0 +1,3993 @@
+/***************************************************************************/
+/* */
+/* ftobjs.c */
+/* */
+/* The FreeType private base classes (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_LIST_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_VALIDATE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_RFORK_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H /* for SFNT_Load_Table_Func */
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_IDS_H
+#include FT_OUTLINE_H
+
+#include FT_SERVICE_SFNT_H
+#include FT_SERVICE_POSTSCRIPT_NAME_H
+#include FT_SERVICE_GLYPH_DICT_H
+#include FT_SERVICE_TT_CMAP_H
+#include FT_SERVICE_KERNING_H
+#include FT_SERVICE_TRUETYPE_ENGINE_H
+
+#define GRID_FIT_METRICS
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_service_list_lookup( FT_ServiceDesc service_descriptors,
+ const char* service_id )
+ {
+ FT_Pointer result = NULL;
+ FT_ServiceDesc desc = service_descriptors;
+
+
+ if ( desc && service_id )
+ {
+ for ( ; desc->serv_id != NULL; desc++ )
+ {
+ if ( ft_strcmp( desc->serv_id, service_id ) == 0 )
+ {
+ result = (FT_Pointer)desc->serv_data;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_validator_init( FT_Validator valid,
+ const FT_Byte* base,
+ const FT_Byte* limit,
+ FT_ValidationLevel level )
+ {
+ valid->base = base;
+ valid->limit = limit;
+ valid->level = level;
+ valid->error = FT_Err_Ok;
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ ft_validator_run( FT_Validator valid )
+ {
+ /* This function doesn't work! None should call it. */
+ FT_UNUSED( valid );
+
+ return -1;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_validator_error( FT_Validator valid,
+ FT_Error error )
+ {
+ /* since the cast below also disables the compiler's */
+ /* type check, we introduce a dummy variable, which */
+ /* will be optimized away */
+ volatile ft_jmp_buf* jump_buffer = &valid->jump_buffer;
+
+
+ valid->error = error;
+
+ /* throw away volatileness; use `jump_buffer' or the */
+ /* compiler may warn about an unused local variable */
+ ft_longjmp( *(ft_jmp_buf*) jump_buffer, 1 );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** S T R E A M ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* create a new input stream from an FT_Open_Args structure */
+ /* */
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_New( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Stream *astream )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stream stream;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !args )
+ return FT_Err_Invalid_Argument;
+
+ *astream = 0;
+ memory = library->memory;
+
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ stream->memory = memory;
+
+ if ( args->flags & FT_OPEN_MEMORY )
+ {
+ /* create a memory-based stream */
+ FT_Stream_OpenMemory( stream,
+ (const FT_Byte*)args->memory_base,
+ args->memory_size );
+ }
+ else if ( args->flags & FT_OPEN_PATHNAME )
+ {
+ /* create a normal system stream */
+ error = FT_Stream_Open( stream, args->pathname );
+ stream->pathname.pointer = args->pathname;
+ }
+ else if ( ( args->flags & FT_OPEN_STREAM ) && args->stream )
+ {
+ /* use an existing, user-provided stream */
+
+ /* in this case, we do not need to allocate a new stream object */
+ /* since the caller is responsible for closing it himself */
+ FT_FREE( stream );
+ stream = args->stream;
+ }
+ else
+ error = FT_Err_Invalid_Argument;
+
+ if ( error )
+ FT_FREE( stream );
+ else
+ stream->memory = memory; /* just to be certain */
+
+ *astream = stream;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_Free( FT_Stream stream,
+ FT_Int external )
+ {
+ if ( stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_Stream_Close( stream );
+
+ if ( !external )
+ FT_FREE( stream );
+ }
+ }
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_objs
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** FACE, SIZE & GLYPH SLOT OBJECTS ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static FT_Error
+ ft_glyphslot_init( FT_GlyphSlot slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Driver_Class clazz = driver->clazz;
+ FT_Memory memory = driver->root.memory;
+ FT_Error error = FT_Err_Ok;
+ FT_Slot_Internal internal;
+
+
+ slot->library = driver->root.library;
+
+ if ( FT_NEW( internal ) )
+ goto Exit;
+
+ slot->internal = internal;
+
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ error = FT_GlyphLoader_New( memory, &internal->loader );
+
+ if ( !error && clazz->init_slot )
+ error = clazz->init_slot( slot );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_glyphslot_free_bitmap( FT_GlyphSlot slot )
+ {
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( slot->face );
+
+
+ FT_FREE( slot->bitmap.buffer );
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+ else
+ {
+ /* assume that the bitmap buffer was stolen or not */
+ /* allocated from the heap */
+ slot->bitmap.buffer = NULL;
+ }
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_glyphslot_set_bitmap( FT_GlyphSlot slot,
+ FT_Byte* buffer )
+ {
+ ft_glyphslot_free_bitmap( slot );
+
+ slot->bitmap.buffer = buffer;
+
+ FT_ASSERT( (slot->internal->flags & FT_GLYPH_OWN_BITMAP) == 0 );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot,
+ FT_ULong size )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( slot->face );
+ FT_Error error;
+
+
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ FT_FREE( slot->bitmap.buffer );
+ else
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+
+ (void)FT_ALLOC( slot->bitmap.buffer, size );
+ return error;
+ }
+
+
+ static void
+ ft_glyphslot_clear( FT_GlyphSlot slot )
+ {
+ /* free bitmap if needed */
+ ft_glyphslot_free_bitmap( slot );
+
+ /* clear all public fields in the glyph slot */
+ FT_ZERO( &slot->metrics );
+ FT_ZERO( &slot->outline );
+
+ slot->bitmap.width = 0;
+ slot->bitmap.rows = 0;
+ slot->bitmap.pitch = 0;
+ slot->bitmap.pixel_mode = 0;
+ /* `slot->bitmap.buffer' has been handled by ft_glyphslot_free_bitmap */
+
+ slot->bitmap_left = 0;
+ slot->bitmap_top = 0;
+ slot->num_subglyphs = 0;
+ slot->subglyphs = 0;
+ slot->control_data = 0;
+ slot->control_len = 0;
+ slot->other = 0;
+ slot->format = FT_GLYPH_FORMAT_NONE;
+
+ slot->linearHoriAdvance = 0;
+ slot->linearVertAdvance = 0;
+ slot->lsb_delta = 0;
+ slot->rsb_delta = 0;
+ }
+
+
+ static void
+ ft_glyphslot_done( FT_GlyphSlot slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Driver_Class clazz = driver->clazz;
+ FT_Memory memory = driver->root.memory;
+
+
+ if ( clazz->done_slot )
+ clazz->done_slot( slot );
+
+ /* free bitmap buffer if needed */
+ ft_glyphslot_free_bitmap( slot );
+
+ /* free glyph loader */
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ {
+ FT_GlyphLoader_Done( slot->internal->loader );
+ slot->internal->loader = 0;
+ }
+
+ FT_FREE( slot->internal );
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_New_GlyphSlot( FT_Face face,
+ FT_GlyphSlot *aslot )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Driver_Class clazz;
+ FT_Memory memory;
+ FT_GlyphSlot slot;
+
+
+ if ( !face || !face->driver )
+ return FT_Err_Invalid_Argument;
+
+ driver = face->driver;
+ clazz = driver->clazz;
+ memory = driver->root.memory;
+
+ FT_TRACE4(( "FT_New_GlyphSlot: Creating new slot object\n" ));
+ if ( !FT_ALLOC( slot, clazz->slot_object_size ) )
+ {
+ slot->face = face;
+
+ error = ft_glyphslot_init( slot );
+ if ( error )
+ {
+ ft_glyphslot_done( slot );
+ FT_FREE( slot );
+ goto Exit;
+ }
+
+ slot->next = face->glyph;
+ face->glyph = slot;
+
+ if ( aslot )
+ *aslot = slot;
+ }
+ else if ( aslot )
+ *aslot = 0;
+
+
+ Exit:
+ FT_TRACE4(( "FT_New_GlyphSlot: Return %d\n", error ));
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ FT_Done_GlyphSlot( FT_GlyphSlot slot )
+ {
+ if ( slot )
+ {
+ FT_Driver driver = slot->face->driver;
+ FT_Memory memory = driver->root.memory;
+ FT_GlyphSlot prev;
+ FT_GlyphSlot cur;
+
+
+ /* Remove slot from its parent face's list */
+ prev = NULL;
+ cur = slot->face->glyph;
+
+ while ( cur )
+ {
+ if ( cur == slot )
+ {
+ if ( !prev )
+ slot->face->glyph = cur->next;
+ else
+ prev->next = cur->next;
+
+ ft_glyphslot_done( slot );
+ FT_FREE( slot );
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+ }
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Transform( FT_Face face,
+ FT_Matrix* matrix,
+ FT_Vector* delta )
+ {
+ FT_Face_Internal internal;
+
+
+ if ( !face )
+ return;
+
+ internal = face->internal;
+
+ internal->transform_flags = 0;
+
+ if ( !matrix )
+ {
+ internal->transform_matrix.xx = 0x10000L;
+ internal->transform_matrix.xy = 0;
+ internal->transform_matrix.yx = 0;
+ internal->transform_matrix.yy = 0x10000L;
+ matrix = &internal->transform_matrix;
+ }
+ else
+ internal->transform_matrix = *matrix;
+
+ /* set transform_flags bit flag 0 if `matrix' isn't the identity */
+ if ( ( matrix->xy | matrix->yx ) ||
+ matrix->xx != 0x10000L ||
+ matrix->yy != 0x10000L )
+ internal->transform_flags |= 1;
+
+ if ( !delta )
+ {
+ internal->transform_delta.x = 0;
+ internal->transform_delta.y = 0;
+ delta = &internal->transform_delta;
+ }
+ else
+ internal->transform_delta = *delta;
+
+ /* set transform_flags bit flag 1 if `delta' isn't the null vector */
+ if ( delta->x | delta->y )
+ internal->transform_flags |= 2;
+ }
+
+
+ static FT_Renderer
+ ft_lookup_glyph_renderer( FT_GlyphSlot slot );
+
+
+#ifdef GRID_FIT_METRICS
+ static void
+ ft_glyphslot_grid_fit_metrics( FT_GlyphSlot slot,
+ FT_Bool vertical )
+ {
+ FT_Glyph_Metrics* metrics = &slot->metrics;
+ FT_Pos right, bottom;
+
+
+ if ( vertical )
+ {
+ metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
+ metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY );
+
+ right = FT_PIX_CEIL( metrics->vertBearingX + metrics->width );
+ bottom = FT_PIX_CEIL( metrics->vertBearingY + metrics->height );
+
+ metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
+ metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
+
+ metrics->width = right - metrics->vertBearingX;
+ metrics->height = bottom - metrics->vertBearingY;
+ }
+ else
+ {
+ metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
+ metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
+
+ right = FT_PIX_CEIL ( metrics->horiBearingX + metrics->width );
+ bottom = FT_PIX_FLOOR( metrics->horiBearingY - metrics->height );
+
+ metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
+ metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY );
+
+ metrics->width = right - metrics->horiBearingX;
+ metrics->height = metrics->horiBearingY - bottom;
+ }
+
+ metrics->horiAdvance = FT_PIX_ROUND( metrics->horiAdvance );
+ metrics->vertAdvance = FT_PIX_ROUND( metrics->vertAdvance );
+ }
+#endif /* GRID_FIT_METRICS */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Glyph( FT_Face face,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_GlyphSlot slot;
+ FT_Library library;
+ FT_Bool autohint = 0;
+ FT_Module hinter;
+
+
+ if ( !face || !face->size || !face->glyph )
+ return FT_Err_Invalid_Face_Handle;
+
+ /* The validity test for `glyph_index' is performed by the */
+ /* font drivers. */
+
+ slot = face->glyph;
+ ft_glyphslot_clear( slot );
+
+ driver = face->driver;
+ library = driver->root.library;
+ hinter = library->auto_hinter;
+
+ /* resolve load flags dependencies */
+
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ load_flags |= FT_LOAD_NO_SCALE |
+ FT_LOAD_IGNORE_TRANSFORM;
+
+ if ( load_flags & FT_LOAD_NO_SCALE )
+ {
+ load_flags |= FT_LOAD_NO_HINTING |
+ FT_LOAD_NO_BITMAP;
+
+ load_flags &= ~FT_LOAD_RENDER;
+ }
+
+ /*
+ * Determine whether we need to auto-hint or not.
+ * The general rules are:
+ *
+ * - Do only auto-hinting if we have a hinter module,
+ * a scalable font format dealing with outlines,
+ * and no transforms except simple slants.
+ *
+ * - Then, autohint if FT_LOAD_FORCE_AUTOHINT is set
+ * or if we don't have a native font hinter.
+ *
+ * - Otherwise, auto-hint for LIGHT hinting mode.
+ *
+ * - Exception: The font requires the unpatented
+ * bytecode interpreter to load properly.
+ */
+
+ autohint = 0;
+ if ( hinter &&
+ ( load_flags & FT_LOAD_NO_HINTING ) == 0 &&
+ ( load_flags & FT_LOAD_NO_AUTOHINT ) == 0 &&
+ FT_DRIVER_IS_SCALABLE( driver ) &&
+ FT_DRIVER_USES_OUTLINES( driver ) &&
+ face->internal->transform_matrix.yy > 0 &&
+ face->internal->transform_matrix.yx == 0 )
+ {
+ if ( ( load_flags & FT_LOAD_FORCE_AUTOHINT ) != 0 ||
+ !FT_DRIVER_HAS_HINTER( driver ) )
+ autohint = 1;
+ else
+ {
+ FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
+
+
+ if ( mode == FT_RENDER_MODE_LIGHT ||
+ face->internal->ignore_unpatented_hinter )
+ autohint = 1;
+ }
+ }
+
+ if ( autohint )
+ {
+ FT_AutoHinter_Service hinting;
+
+
+ /* try to load embedded bitmaps first if available */
+ /* */
+ /* XXX: This is really a temporary hack that should disappear */
+ /* promptly with FreeType 2.1! */
+ /* */
+ if ( FT_HAS_FIXED_SIZES( face ) &&
+ ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
+ {
+ error = driver->clazz->load_glyph( slot, face->size,
+ glyph_index,
+ load_flags | FT_LOAD_SBITS_ONLY );
+
+ if ( !error && slot->format == FT_GLYPH_FORMAT_BITMAP )
+ goto Load_Ok;
+ }
+
+ /* load auto-hinted outline */
+ hinting = (FT_AutoHinter_Service)hinter->clazz->module_interface;
+
+ error = hinting->load_glyph( (FT_AutoHinter)hinter,
+ slot, face->size,
+ glyph_index, load_flags );
+ }
+ else
+ {
+ error = driver->clazz->load_glyph( slot,
+ face->size,
+ glyph_index,
+ load_flags );
+ if ( error )
+ goto Exit;
+
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ /* check that the loaded outline is correct */
+ error = FT_Outline_Check( &slot->outline );
+ if ( error )
+ goto Exit;
+
+#ifdef GRID_FIT_METRICS
+ if ( !( load_flags & FT_LOAD_NO_HINTING ) )
+ ft_glyphslot_grid_fit_metrics( slot,
+ FT_BOOL( load_flags & FT_LOAD_VERTICAL_LAYOUT ) );
+#endif
+ }
+ }
+
+ Load_Ok:
+ /* compute the advance */
+ if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+ {
+ slot->advance.x = 0;
+ slot->advance.y = slot->metrics.vertAdvance;
+ }
+ else
+ {
+ slot->advance.x = slot->metrics.horiAdvance;
+ slot->advance.y = 0;
+ }
+
+ /* compute the linear advance in 16.16 pixels */
+ if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 &&
+ ( face->face_flags & FT_FACE_FLAG_SCALABLE ) )
+ {
+ FT_Size_Metrics* metrics = &face->size->metrics;
+
+
+ /* it's tricky! */
+ slot->linearHoriAdvance = FT_MulDiv( slot->linearHoriAdvance,
+ metrics->x_scale, 64 );
+
+ slot->linearVertAdvance = FT_MulDiv( slot->linearVertAdvance,
+ metrics->y_scale, 64 );
+ }
+
+ if ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) == 0 )
+ {
+ FT_Face_Internal internal = face->internal;
+
+
+ /* now, transform the glyph image if needed */
+ if ( internal->transform_flags )
+ {
+ /* get renderer */
+ FT_Renderer renderer = ft_lookup_glyph_renderer( slot );
+
+
+ if ( renderer )
+ error = renderer->clazz->transform_glyph(
+ renderer, slot,
+ &internal->transform_matrix,
+ &internal->transform_delta );
+ /* transform advance */
+ FT_Vector_Transform( &slot->advance, &internal->transform_matrix );
+ }
+ }
+
+ /* do we need to render the image now? */
+ if ( !error &&
+ slot->format != FT_GLYPH_FORMAT_BITMAP &&
+ slot->format != FT_GLYPH_FORMAT_COMPOSITE &&
+ load_flags & FT_LOAD_RENDER )
+ {
+ FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
+
+
+ if ( mode == FT_RENDER_MODE_NORMAL &&
+ (load_flags & FT_LOAD_MONOCHROME ) )
+ mode = FT_RENDER_MODE_MONO;
+
+ error = FT_Render_Glyph( slot, mode );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Char( FT_Face face,
+ FT_ULong char_code,
+ FT_Int32 load_flags )
+ {
+ FT_UInt glyph_index;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ glyph_index = (FT_UInt)char_code;
+ if ( face->charmap )
+ glyph_index = FT_Get_Char_Index( face, char_code );
+
+ return FT_Load_Glyph( face, glyph_index, load_flags );
+ }
+
+
+ /* destructor for sizes list */
+ static void
+ destroy_size( FT_Memory memory,
+ FT_Size size,
+ FT_Driver driver )
+ {
+ /* finalize client-specific data */
+ if ( size->generic.finalizer )
+ size->generic.finalizer( size );
+
+ /* finalize format-specific stuff */
+ if ( driver->clazz->done_size )
+ driver->clazz->done_size( size );
+
+ FT_FREE( size->internal );
+ FT_FREE( size );
+ }
+
+
+ static void
+ ft_cmap_done_internal( FT_CMap cmap );
+
+
+ static void
+ destroy_charmaps( FT_Face face,
+ FT_Memory memory )
+ {
+ FT_Int n;
+
+
+ if ( !face )
+ return;
+
+ for ( n = 0; n < face->num_charmaps; n++ )
+ {
+ FT_CMap cmap = FT_CMAP( face->charmaps[n] );
+
+
+ ft_cmap_done_internal( cmap );
+
+ face->charmaps[n] = NULL;
+ }
+
+ FT_FREE( face->charmaps );
+ face->num_charmaps = 0;
+ }
+
+
+ /* destructor for faces list */
+ static void
+ destroy_face( FT_Memory memory,
+ FT_Face face,
+ FT_Driver driver )
+ {
+ FT_Driver_Class clazz = driver->clazz;
+
+
+ /* discard auto-hinting data */
+ if ( face->autohint.finalizer )
+ face->autohint.finalizer( face->autohint.data );
+
+ /* Discard glyph slots for this face. */
+ /* Beware! FT_Done_GlyphSlot() changes the field `face->glyph' */
+ while ( face->glyph )
+ FT_Done_GlyphSlot( face->glyph );
+
+ /* discard all sizes for this face */
+ FT_List_Finalize( &face->sizes_list,
+ (FT_List_Destructor)destroy_size,
+ memory,
+ driver );
+ face->size = 0;
+
+ /* now discard client data */
+ if ( face->generic.finalizer )
+ face->generic.finalizer( face );
+
+ /* discard charmaps */
+ destroy_charmaps( face, memory );
+
+ /* finalize format-specific stuff */
+ if ( clazz->done_face )
+ clazz->done_face( face );
+
+ /* close the stream for this face if needed */
+ FT_Stream_Free(
+ face->stream,
+ ( face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
+
+ face->stream = 0;
+
+ /* get rid of it */
+ if ( face->internal )
+ {
+ FT_FREE( face->internal );
+ }
+ FT_FREE( face );
+ }
+
+
+ static void
+ Destroy_Driver( FT_Driver driver )
+ {
+ FT_List_Finalize( &driver->faces_list,
+ (FT_List_Destructor)destroy_face,
+ driver->root.memory,
+ driver );
+
+ /* check whether we need to drop the driver's glyph loader */
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ FT_GlyphLoader_Done( driver->glyph_loader );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* find_unicode_charmap */
+ /* */
+ /* <Description> */
+ /* This function finds a Unicode charmap, if there is one. */
+ /* And if there is more than one, it tries to favour the more */
+ /* extensive one, i.e., one that supports UCS-4 against those which */
+ /* are limited to the BMP (said UCS-2 encoding.) */
+ /* */
+ /* This function is called from open_face() (just below), and also */
+ /* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE). */
+ /* */
+ static FT_Error
+ find_unicode_charmap( FT_Face face )
+ {
+ FT_CharMap* first;
+ FT_CharMap* cur;
+ FT_CharMap* unicmap = NULL; /* some UCS-2 map, if we found it */
+
+
+ /* caller should have already checked that `face' is valid */
+ FT_ASSERT( face );
+
+ first = face->charmaps;
+
+ if ( !first )
+ return FT_Err_Invalid_CharMap_Handle;
+
+ /*
+ * The original TrueType specification(s) only specified charmap
+ * formats that are capable of mapping 8 or 16 bit character codes to
+ * glyph indices.
+ *
+ * However, recent updates to the Apple and OpenType specifications
+ * introduced new formats that are capable of mapping 32-bit character
+ * codes as well. And these are already used on some fonts, mainly to
+ * map non-BMP Asian ideographs as defined in Unicode.
+ *
+ * For compatibility purposes, these fonts generally come with
+ * *several* Unicode charmaps:
+ *
+ * - One of them in the "old" 16-bit format, that cannot access
+ * all glyphs in the font.
+ *
+ * - Another one in the "new" 32-bit format, that can access all
+ * the glyphs.
+ *
+ * This function has been written to always favor a 32-bit charmap
+ * when found. Otherwise, a 16-bit one is returned when found.
+ */
+
+ /* Since the `interesting' table, with IDs (3,10), is normally the */
+ /* last one, we loop backwards. This loses with type1 fonts with */
+ /* non-BMP characters (<.0001%), this wins with .ttf with non-BMP */
+ /* chars (.01% ?), and this is the same about 99.99% of the time! */
+
+ cur = first + face->num_charmaps; /* points after the last one */
+
+ for ( ; --cur >= first; )
+ {
+ if ( cur[0]->encoding == FT_ENCODING_UNICODE )
+ {
+ unicmap = cur; /* record we found a Unicode charmap */
+
+ /* XXX If some new encodings to represent UCS-4 are added, */
+ /* they should be added here. */
+ if ( ( cur[0]->platform_id == TT_PLATFORM_MICROSOFT &&
+ cur[0]->encoding_id == TT_MS_ID_UCS_4 ) ||
+ ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
+ cur[0]->encoding_id == TT_APPLE_ID_UNICODE_32 ) )
+
+ /* Hurray! We found a UCS-4 charmap. We can stop the scan! */
+ {
+ face->charmap = cur[0];
+ return 0;
+ }
+ }
+ }
+
+ /* We do not have any UCS-4 charmap. Sigh. */
+ /* Let's see if we have some other kind of Unicode charmap, though. */
+ if ( unicmap != NULL )
+ {
+ face->charmap = unicmap[0];
+ return 0;
+ }
+
+ /* Chou blanc! */
+ return FT_Err_Invalid_CharMap_Handle;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* open_face */
+ /* */
+ /* <Description> */
+ /* This function does some work for FT_Open_Face(). */
+ /* */
+ static FT_Error
+ open_face( FT_Driver driver,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Int num_params,
+ FT_Parameter* params,
+ FT_Face *aface )
+ {
+ FT_Memory memory;
+ FT_Driver_Class clazz;
+ FT_Face face = 0;
+ FT_Error error, error2;
+ FT_Face_Internal internal = NULL;
+
+
+ clazz = driver->clazz;
+ memory = driver->root.memory;
+
+ /* allocate the face object and perform basic initialization */
+ if ( FT_ALLOC( face, clazz->face_object_size ) )
+ goto Fail;
+
+ if ( FT_NEW( internal ) )
+ goto Fail;
+
+ face->internal = internal;
+
+ face->driver = driver;
+ face->memory = memory;
+ face->stream = stream;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ {
+ int i;
+
+
+ face->internal->incremental_interface = 0;
+ for ( i = 0; i < num_params && !face->internal->incremental_interface;
+ i++ )
+ if ( params[i].tag == FT_PARAM_TAG_INCREMENTAL )
+ face->internal->incremental_interface = params[i].data;
+ }
+#endif
+
+ error = clazz->init_face( stream,
+ face,
+ (FT_Int)face_index,
+ num_params,
+ params );
+ if ( error )
+ goto Fail;
+
+ /* select Unicode charmap by default */
+ error2 = find_unicode_charmap( face );
+
+ /* if no Unicode charmap can be found, FT_Err_Invalid_CharMap_Handle */
+ /* is returned. */
+
+ /* no error should happen, but we want to play safe */
+ if ( error2 && error2 != FT_Err_Invalid_CharMap_Handle )
+ {
+ error = error2;
+ goto Fail;
+ }
+
+ *aface = face;
+
+ Fail:
+ if ( error )
+ {
+ destroy_charmaps( face, memory );
+ clazz->done_face( face );
+ FT_FREE( internal );
+ FT_FREE( face );
+ *aface = 0;
+ }
+
+ return error;
+ }
+
+
+ /* there's a Mac-specific extended implementation of FT_New_Face() */
+ /* in src/base/ftmac.c */
+
+#ifndef FT_MACINTOSH
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Face( FT_Library library,
+ const char* pathname,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+
+
+ /* test for valid `library' and `aface' delayed to FT_Open_Face() */
+ if ( !pathname )
+ return FT_Err_Invalid_Argument;
+
+ args.flags = FT_OPEN_PATHNAME;
+ args.pathname = (char*)pathname;
+
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+#endif /* !FT_MACINTOSH */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Memory_Face( FT_Library library,
+ const FT_Byte* file_base,
+ FT_Long file_size,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+
+
+ /* test for valid `library' and `face' delayed to FT_Open_Face() */
+ if ( !file_base )
+ return FT_Err_Invalid_Argument;
+
+ args.flags = FT_OPEN_MEMORY;
+ args.memory_base = file_base;
+ args.memory_size = file_size;
+
+ return FT_Open_Face( library, &args, face_index, aface );
+ }
+
+
+#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS )
+
+ /* The behavior here is very similar to that in base/ftmac.c, but it */
+ /* is designed to work on non-mac systems, so no mac specific calls. */
+ /* */
+ /* We look at the file and determine if it is a mac dfont file or a mac */
+ /* resource file, or a macbinary file containing a mac resource file. */
+ /* */
+ /* Unlike ftmac I'm not going to look at a `FOND'. I don't really see */
+ /* the point, especially since there may be multiple `FOND' resources. */
+ /* Instead I'll just look for `sfnt' and `POST' resources, ordered as */
+ /* they occur in the file. */
+ /* */
+ /* Note that multiple `POST' resources do not mean multiple postscript */
+ /* fonts; they all get jammed together to make what is essentially a */
+ /* pfb file. */
+ /* */
+ /* We aren't interested in `NFNT' or `FONT' bitmap resources. */
+ /* */
+ /* As soon as we get an `sfnt' load it into memory and pass it off to */
+ /* FT_Open_Face. */
+ /* */
+ /* If we have a (set of) `POST' resources, massage them into a (memory) */
+ /* pfb file and pass that to FT_Open_Face. (As with ftmac.c I'm not */
+ /* going to try to save the kerning info. After all that lives in the */
+ /* `FOND' which isn't in the file containing the `POST' resources so */
+ /* we don't really have access to it. */
+
+
+ /* Finalizer for a memory stream; gets called by FT_Done_Face().
+ It frees the memory it uses. */
+ /* from ftmac.c */
+ static void
+ memory_stream_close( FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ FT_FREE( stream->base );
+
+ stream->size = 0;
+ stream->base = 0;
+ stream->close = 0;
+ }
+
+
+ /* Create a new memory stream from a buffer and a size. */
+ /* from ftmac.c */
+ static FT_Error
+ new_memory_stream( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Stream_CloseFunc close,
+ FT_Stream *astream )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stream stream;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !base )
+ return FT_Err_Invalid_Argument;
+
+ *astream = 0;
+ memory = library->memory;
+ if ( FT_NEW( stream ) )
+ goto Exit;
+
+ FT_Stream_OpenMemory( stream, base, size );
+
+ stream->close = close;
+
+ *astream = stream;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Create a new FT_Face given a buffer and a driver name. */
+ /* from ftmac.c */
+ static FT_Error
+ open_face_from_buffer( FT_Library library,
+ FT_Byte* base,
+ FT_ULong size,
+ FT_Long face_index,
+ const char* driver_name,
+ FT_Face *aface )
+ {
+ FT_Open_Args args;
+ FT_Error error;
+ FT_Stream stream = NULL;
+ FT_Memory memory = library->memory;
+
+
+ error = new_memory_stream( library,
+ base,
+ size,
+ memory_stream_close,
+ &stream );
+ if ( error )
+ {
+ FT_FREE( base );
+ return error;
+ }
+
+ args.flags = FT_OPEN_STREAM;
+ args.stream = stream;
+ if ( driver_name )
+ {
+ args.flags = args.flags | FT_OPEN_DRIVER;
+ args.driver = FT_Get_Module( library, driver_name );
+ }
+
+ error = FT_Open_Face( library, &args, face_index, aface );
+
+ if ( error == FT_Err_Ok )
+ (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
+ else
+ {
+ FT_Stream_Close( stream );
+ FT_FREE( stream );
+ }
+
+ return error;
+ }
+
+
+ /* The resource header says we've got resource_cnt `POST' (type1) */
+ /* resources in this file. They all need to be coalesced into */
+ /* one lump which gets passed on to the type1 driver. */
+ /* Here can be only one PostScript font in a file so face_index */
+ /* must be 0 (or -1). */
+ /* */
+ static FT_Error
+ Mac_Read_POST_Resource( FT_Library library,
+ FT_Stream stream,
+ FT_Long *offsets,
+ FT_Long resource_cnt,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Error error = FT_Err_Cannot_Open_Resource;
+ FT_Memory memory = library->memory;
+ FT_Byte* pfb_data;
+ int i, type, flags;
+ FT_Long len;
+ FT_Long pfb_len, pfb_pos, pfb_lenpos;
+ FT_Long rlen, temp;
+
+
+ if ( face_index == -1 )
+ face_index = 0;
+ if ( face_index != 0 )
+ return error;
+
+ /* Find the length of all the POST resources, concatenated. Assume */
+ /* worst case (each resource in its own section). */
+ pfb_len = 0;
+ for ( i = 0; i < resource_cnt; ++i )
+ {
+ error = FT_Stream_Seek( stream, offsets[i] );
+ if ( error )
+ goto Exit;
+ if ( FT_READ_LONG( temp ) )
+ goto Exit;
+ pfb_len += temp + 6;
+ }
+
+ if ( FT_ALLOC( pfb_data, (FT_Long)pfb_len + 2 ) )
+ goto Exit;
+
+ pfb_data[0] = 0x80;
+ pfb_data[1] = 1; /* Ascii section */
+ pfb_data[2] = 0; /* 4-byte length, fill in later */
+ pfb_data[3] = 0;
+ pfb_data[4] = 0;
+ pfb_data[5] = 0;
+ pfb_pos = 6;
+ pfb_lenpos = 2;
+
+ len = 0;
+ type = 1;
+ for ( i = 0; i < resource_cnt; ++i )
+ {
+ error = FT_Stream_Seek( stream, offsets[i] );
+ if ( error )
+ goto Exit2;
+ if ( FT_READ_LONG( rlen ) )
+ goto Exit;
+ if ( FT_READ_USHORT( flags ) )
+ goto Exit;
+ rlen -= 2; /* the flags are part of the resource */
+ if ( ( flags >> 8 ) == type )
+ len += rlen;
+ else
+ {
+ pfb_data[pfb_lenpos ] = (FT_Byte)( len );
+ pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
+ pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
+ pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
+
+ if ( ( flags >> 8 ) == 5 ) /* End of font mark */
+ break;
+
+ pfb_data[pfb_pos++] = 0x80;
+
+ type = flags >> 8;
+ len = rlen;
+
+ pfb_data[pfb_pos++] = (FT_Byte)type;
+ pfb_lenpos = pfb_pos;
+ pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */
+ pfb_data[pfb_pos++] = 0;
+ pfb_data[pfb_pos++] = 0;
+ pfb_data[pfb_pos++] = 0;
+ }
+
+ error = FT_Stream_Read( stream, (FT_Byte *)pfb_data + pfb_pos, rlen );
+ pfb_pos += rlen;
+ }
+
+ pfb_data[pfb_pos++] = 0x80;
+ pfb_data[pfb_pos++] = 3;
+
+ pfb_data[pfb_lenpos ] = (FT_Byte)( len );
+ pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
+ pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
+ pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
+
+ return open_face_from_buffer( library,
+ pfb_data,
+ pfb_pos,
+ face_index,
+ "type1",
+ aface );
+
+ Exit2:
+ FT_FREE( pfb_data );
+
+ Exit:
+ return error;
+ }
+
+
+ /* The resource header says we've got resource_cnt `sfnt' */
+ /* (TrueType/OpenType) resources in this file. Look through */
+ /* them for the one indicated by face_index, load it into mem, */
+ /* pass it on the the truetype driver and return it. */
+ /* */
+ static FT_Error
+ Mac_Read_sfnt_Resource( FT_Library library,
+ FT_Stream stream,
+ FT_Long *offsets,
+ FT_Long resource_cnt,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Memory memory = library->memory;
+ FT_Byte* sfnt_data;
+ FT_Error error;
+ FT_Long flag_offset;
+ FT_Long rlen;
+ int is_cff;
+ FT_Long face_index_in_resource = 0;
+
+
+ if ( face_index == -1 )
+ face_index = 0;
+ if ( face_index >= resource_cnt )
+ return FT_Err_Cannot_Open_Resource;
+
+ flag_offset = offsets[face_index];
+ error = FT_Stream_Seek( stream, flag_offset );
+ if ( error )
+ goto Exit;
+
+ if ( FT_READ_LONG( rlen ) )
+ goto Exit;
+ if ( rlen == -1 )
+ return FT_Err_Cannot_Open_Resource;
+
+ if ( FT_ALLOC( sfnt_data, (FT_Long)rlen ) )
+ return error;
+ error = FT_Stream_Read( stream, (FT_Byte *)sfnt_data, rlen );
+ if ( error )
+ goto Exit;
+
+ is_cff = rlen > 4 && sfnt_data[0] == 'O' &&
+ sfnt_data[1] == 'T' &&
+ sfnt_data[2] == 'T' &&
+ sfnt_data[3] == 'O';
+
+ error = open_face_from_buffer( library,
+ sfnt_data,
+ rlen,
+ face_index_in_resource,
+ is_cff ? "cff" : "truetype",
+ aface );
+
+ Exit:
+ return error;
+ }
+
+
+ /* Check for a valid resource fork header, or a valid dfont */
+ /* header. In a resource fork the first 16 bytes are repeated */
+ /* at the location specified by bytes 4-7. In a dfont bytes */
+ /* 4-7 point to 16 bytes of zeroes instead. */
+ /* */
+ static FT_Error
+ IsMacResource( FT_Library library,
+ FT_Stream stream,
+ FT_Long resource_offset,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_Long map_offset, rdara_pos;
+ FT_Long *data_offsets;
+ FT_Long count;
+
+
+ error = FT_Raccess_Get_HeaderInfo( library, stream, resource_offset,
+ &map_offset, &rdara_pos );
+ if ( error )
+ return error;
+
+ error = FT_Raccess_Get_DataOffsets( library, stream,
+ map_offset, rdara_pos,
+ FT_MAKE_TAG( 'P', 'O', 'S', 'T' ),
+ &data_offsets, &count );
+ if ( !error )
+ {
+ error = Mac_Read_POST_Resource( library, stream, data_offsets, count,
+ face_index, aface );
+ FT_FREE( data_offsets );
+ /* POST exists in an LWFN providing a single face */
+ if ( !error )
+ (*aface)->num_faces = 1;
+ return error;
+ }
+
+ error = FT_Raccess_Get_DataOffsets( library, stream,
+ map_offset, rdara_pos,
+ FT_MAKE_TAG( 's', 'f', 'n', 't' ),
+ &data_offsets, &count );
+ if ( !error )
+ {
+ FT_Long face_index_internal = face_index % count;
+
+
+ error = Mac_Read_sfnt_Resource( library, stream, data_offsets, count,
+ face_index_internal, aface );
+ FT_FREE( data_offsets );
+ if ( !error )
+ (*aface)->num_faces = count;
+ }
+
+ return error;
+ }
+
+
+ /* Check for a valid macbinary header, and if we find one */
+ /* check that the (flattened) resource fork in it is valid. */
+ /* */
+ static FT_Error
+ IsMacBinary( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ unsigned char header[128];
+ FT_Error error;
+ FT_Long dlen, offset;
+
+
+ error = FT_Stream_Seek( stream, 0 );
+ if ( error )
+ goto Exit;
+
+ error = FT_Stream_Read( stream, (FT_Byte*)header, 128 );
+ if ( error )
+ goto Exit;
+
+ if ( header[ 0] != 0 ||
+ header[74] != 0 ||
+ header[82] != 0 ||
+ header[ 1] == 0 ||
+ header[ 1] > 33 ||
+ header[63] != 0 ||
+ header[2 + header[1]] != 0 )
+ return FT_Err_Unknown_File_Format;
+
+ dlen = ( header[0x53] << 24 ) |
+ ( header[0x54] << 16 ) |
+ ( header[0x55] << 8 ) |
+ header[0x56];
+#if 0
+ rlen = ( header[0x57] << 24 ) |
+ ( header[0x58] << 16 ) |
+ ( header[0x59] << 8 ) |
+ header[0x5a];
+#endif /* 0 */
+ offset = 128 + ( ( dlen + 127 ) & ~127 );
+
+ return IsMacResource( library, stream, offset, face_index, aface );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_face_in_embedded_rfork( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface,
+ const FT_Open_Args *args )
+ {
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raccess
+
+ FT_Memory memory = library->memory;
+ FT_Error error = FT_Err_Unknown_File_Format;
+ int i;
+
+ char * file_names[FT_RACCESS_N_RULES];
+ FT_Long offsets[FT_RACCESS_N_RULES];
+ FT_Error errors[FT_RACCESS_N_RULES];
+
+ FT_Open_Args args2;
+ FT_Stream stream2;
+
+
+ FT_Raccess_Guess( library, stream,
+ args->pathname, file_names, offsets, errors );
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ if ( errors[i] )
+ {
+ FT_TRACE3(( "Error[%d] has occurred in rule %d\n", errors[i], i ));
+ continue;
+ }
+
+ args2.flags = FT_OPEN_PATHNAME;
+ args2.pathname = file_names[i] ? file_names[i] : args->pathname;
+
+ FT_TRACE3(( "Try rule %d: %s (offset=%d) ...",
+ i, args2.pathname, offsets[i] ));
+
+ error = FT_Stream_New( library, &args2, &stream2 );
+ if ( error )
+ {
+ FT_TRACE3(( "failed\n" ));
+ continue;
+ }
+
+ error = IsMacResource( library, stream2, offsets[i],
+ face_index, aface );
+ FT_Stream_Free( stream2, 0 );
+
+ FT_TRACE3(( "%s\n", error ? "failed": "successful" ));
+
+ if ( !error )
+ break;
+ }
+
+ for (i = 0; i < FT_RACCESS_N_RULES; i++)
+ {
+ if ( file_names[i] )
+ FT_FREE( file_names[i] );
+ }
+
+ /* Caller (load_mac_face) requires FT_Err_Unknown_File_Format. */
+ if ( error )
+ error = FT_Err_Unknown_File_Format;
+
+ return error;
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_objs
+
+ }
+
+
+ /* Check for some macintosh formats. */
+ /* Is this a macbinary file? If so look at the resource fork. */
+ /* Is this a mac dfont file? */
+ /* Is this an old style resource fork? (in data) */
+ /* Else call load_face_in_embedded_rfork to try extra rules */
+ /* (defined in `ftrfork.c'). */
+ /* */
+ static FT_Error
+ load_mac_face( FT_Library library,
+ FT_Stream stream,
+ FT_Long face_index,
+ FT_Face *aface,
+ const FT_Open_Args *args )
+ {
+ FT_Error error;
+ FT_UNUSED( args );
+
+
+ error = IsMacBinary( library, stream, face_index, aface );
+ if ( FT_ERROR_BASE( error ) == FT_Err_Unknown_File_Format )
+ {
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raccess
+
+ FT_TRACE3(( "Try as dfont: %s ...", args->pathname ));
+
+ error = IsMacResource( library, stream, 0, face_index, aface );
+
+ FT_TRACE3(( "%s\n", error ? "failed" : "successful" ));
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_objs
+
+ }
+
+ if ( ( FT_ERROR_BASE( error ) == FT_Err_Unknown_File_Format ||
+ FT_ERROR_BASE( error ) == FT_Err_Invalid_Stream_Operation ) &&
+ ( args->flags & FT_OPEN_PATHNAME ) )
+ error = load_face_in_embedded_rfork( library, stream,
+ face_index, aface, args );
+ return error;
+ }
+
+#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Open_Face( FT_Library library,
+ const FT_Open_Args* args,
+ FT_Long face_index,
+ FT_Face *aface )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_Stream stream;
+ FT_Face face = 0;
+ FT_ListNode node = 0;
+ FT_Bool external_stream;
+
+
+ /* test for valid `library' delayed to */
+ /* FT_Stream_New() */
+
+ if ( ( !aface && face_index >= 0 ) || !args )
+ return FT_Err_Invalid_Argument;
+
+ external_stream = FT_BOOL( ( args->flags & FT_OPEN_STREAM ) &&
+ args->stream );
+
+ /* create input stream */
+ error = FT_Stream_New( library, args, &stream );
+ if ( error )
+ goto Exit;
+
+ memory = library->memory;
+
+ /* If the font driver is specified in the `args' structure, use */
+ /* it. Otherwise, we scan the list of registered drivers. */
+ if ( ( args->flags & FT_OPEN_DRIVER ) && args->driver )
+ {
+ driver = FT_DRIVER( args->driver );
+
+ /* not all modules are drivers, so check... */
+ if ( FT_MODULE_IS_DRIVER( driver ) )
+ {
+ FT_Int num_params = 0;
+ FT_Parameter* params = 0;
+
+
+ if ( args->flags & FT_OPEN_PARAMS )
+ {
+ num_params = args->num_params;
+ params = args->params;
+ }
+
+ error = open_face( driver, stream, face_index,
+ num_params, params, &face );
+ if ( !error )
+ goto Success;
+ }
+ else
+ error = FT_Err_Invalid_Handle;
+
+ FT_Stream_Free( stream, external_stream );
+ goto Fail;
+ }
+ else
+ {
+ /* check each font driver for an appropriate format */
+ FT_Module* cur = library->modules;
+ FT_Module* limit = cur + library->num_modules;
+
+
+ for ( ; cur < limit; cur++ )
+ {
+ /* not all modules are font drivers, so check... */
+ if ( FT_MODULE_IS_DRIVER( cur[0] ) )
+ {
+ FT_Int num_params = 0;
+ FT_Parameter* params = 0;
+
+
+ driver = FT_DRIVER( cur[0] );
+
+ if ( args->flags & FT_OPEN_PARAMS )
+ {
+ num_params = args->num_params;
+ params = args->params;
+ }
+
+ error = open_face( driver, stream, face_index,
+ num_params, params, &face );
+ if ( !error )
+ goto Success;
+
+ if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format )
+ goto Fail3;
+ }
+ }
+
+ Fail3:
+ /* If we are on the mac, and we get an FT_Err_Invalid_Stream_Operation */
+ /* it may be because we have an empty data fork, so we need to check */
+ /* the resource fork. */
+ if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format &&
+ FT_ERROR_BASE( error ) != FT_Err_Invalid_Stream_Operation )
+ goto Fail2;
+
+#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS )
+ error = load_mac_face( library, stream, face_index, aface, args );
+ if ( !error )
+ {
+ /* We don't want to go to Success here. We've already done that. */
+ /* On the other hand, if we succeeded we still need to close this */
+ /* stream (we opened a different stream which extracted the */
+ /* interesting information out of this stream here. That stream */
+ /* will still be open and the face will point to it). */
+ FT_Stream_Free( stream, external_stream );
+ return error;
+ }
+
+ if ( FT_ERROR_BASE( error ) != FT_Err_Unknown_File_Format )
+ goto Fail2;
+#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
+
+ /* no driver is able to handle this format */
+ error = FT_Err_Unknown_File_Format;
+
+ Fail2:
+ FT_Stream_Free( stream, external_stream );
+ goto Fail;
+ }
+
+ Success:
+ FT_TRACE4(( "FT_Open_Face: New face object, adding to list\n" ));
+
+ /* set the FT_FACE_FLAG_EXTERNAL_STREAM bit for FT_Done_Face */
+ if ( external_stream )
+ face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
+
+ /* add the face object to its driver's list */
+ if ( FT_NEW( node ) )
+ goto Fail;
+
+ node->data = face;
+ /* don't assume driver is the same as face->driver, so use */
+ /* face->driver instead. */
+ FT_List_Add( &face->driver->faces_list, node );
+
+ /* now allocate a glyph slot object for the face */
+ FT_TRACE4(( "FT_Open_Face: Creating glyph slot\n" ));
+
+ if ( face_index >= 0 )
+ {
+ error = FT_New_GlyphSlot( face, NULL );
+ if ( error )
+ goto Fail;
+
+ /* finally, allocate a size object for the face */
+ {
+ FT_Size size;
+
+
+ FT_TRACE4(( "FT_Open_Face: Creating size object\n" ));
+
+ error = FT_New_Size( face, &size );
+ if ( error )
+ goto Fail;
+
+ face->size = size;
+ }
+ }
+
+ /* some checks */
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ if ( face->height < 0 )
+ face->height = (FT_Short)-face->height;
+
+ if ( !FT_HAS_VERTICAL( face ) )
+ face->max_advance_height = (FT_Short)face->height;
+ }
+
+ if ( FT_HAS_FIXED_SIZES( face ) )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < face->num_fixed_sizes; i++ )
+ {
+ FT_Bitmap_Size* bsize = face->available_sizes + i;
+
+
+ if ( bsize->height < 0 )
+ bsize->height = (FT_Short)-bsize->height;
+ if ( bsize->x_ppem < 0 )
+ bsize->x_ppem = (FT_Short)-bsize->x_ppem;
+ if ( bsize->y_ppem < 0 )
+ bsize->y_ppem = -bsize->y_ppem;
+ }
+ }
+
+ /* initialize internal face data */
+ {
+ FT_Face_Internal internal = face->internal;
+
+
+ internal->transform_matrix.xx = 0x10000L;
+ internal->transform_matrix.xy = 0;
+ internal->transform_matrix.yx = 0;
+ internal->transform_matrix.yy = 0x10000L;
+
+ internal->transform_delta.x = 0;
+ internal->transform_delta.y = 0;
+ }
+
+ if ( aface )
+ *aface = face;
+ else
+ FT_Done_Face( face );
+
+ goto Exit;
+
+ Fail:
+ FT_Done_Face( face );
+
+ Exit:
+ FT_TRACE4(( "FT_Open_Face: Return %d\n", error ));
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Attach_File( FT_Face face,
+ const char* filepathname )
+ {
+ FT_Open_Args open;
+
+
+ /* test for valid `face' delayed to FT_Attach_Stream() */
+
+ if ( !filepathname )
+ return FT_Err_Invalid_Argument;
+
+ open.stream = NULL;
+ open.flags = FT_OPEN_PATHNAME;
+ open.pathname = (char*)filepathname;
+
+ return FT_Attach_Stream( face, &open );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Attach_Stream( FT_Face face,
+ FT_Open_Args* parameters )
+ {
+ FT_Stream stream;
+ FT_Error error;
+ FT_Driver driver;
+
+ FT_Driver_Class clazz;
+
+
+ /* test for valid `parameters' delayed to FT_Stream_New() */
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ driver = face->driver;
+ if ( !driver )
+ return FT_Err_Invalid_Driver_Handle;
+
+ error = FT_Stream_New( driver->root.library, parameters, &stream );
+ if ( error )
+ goto Exit;
+
+ /* we implement FT_Attach_Stream in each driver through the */
+ /* `attach_file' interface */
+
+ error = FT_Err_Unimplemented_Feature;
+ clazz = driver->clazz;
+ if ( clazz->attach_file )
+ error = clazz->attach_file( face, stream );
+
+ /* close the attached stream */
+ FT_Stream_Free( stream,
+ (FT_Bool)( parameters->stream &&
+ ( parameters->flags & FT_OPEN_STREAM ) ) );
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Face( FT_Face face )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_ListNode node;
+
+
+ error = FT_Err_Invalid_Face_Handle;
+ if ( face && face->driver )
+ {
+ driver = face->driver;
+ memory = driver->root.memory;
+
+ /* find face in driver's list */
+ node = FT_List_Find( &driver->faces_list, face );
+ if ( node )
+ {
+ /* remove face object from the driver's list */
+ FT_List_Remove( &driver->faces_list, node );
+ FT_FREE( node );
+
+ /* now destroy the object proper */
+ destroy_face( memory, face, driver );
+ error = FT_Err_Ok;
+ }
+ }
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Size( FT_Face face,
+ FT_Size *asize )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Driver driver;
+ FT_Driver_Class clazz;
+
+ FT_Size size = 0;
+ FT_ListNode node = 0;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( !asize )
+ return FT_Err_Invalid_Size_Handle;
+
+ if ( !face->driver )
+ return FT_Err_Invalid_Driver_Handle;
+
+ *asize = 0;
+
+ driver = face->driver;
+ clazz = driver->clazz;
+ memory = face->memory;
+
+ /* Allocate new size object and perform basic initialisation */
+ if ( FT_ALLOC( size, clazz->size_object_size ) || FT_NEW( node ) )
+ goto Exit;
+
+ size->face = face;
+
+ /* for now, do not use any internal fields in size objects */
+ size->internal = 0;
+
+ if ( clazz->init_size )
+ error = clazz->init_size( size );
+
+ /* in case of success, add to the face's list */
+ if ( !error )
+ {
+ *asize = size;
+ node->data = size;
+ FT_List_Add( &face->sizes_list, node );
+ }
+
+ Exit:
+ if ( error )
+ {
+ FT_FREE( node );
+ FT_FREE( size );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Size( FT_Size size )
+ {
+ FT_Error error;
+ FT_Driver driver;
+ FT_Memory memory;
+ FT_Face face;
+ FT_ListNode node;
+
+
+ if ( !size )
+ return FT_Err_Invalid_Size_Handle;
+
+ face = size->face;
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ driver = face->driver;
+ if ( !driver )
+ return FT_Err_Invalid_Driver_Handle;
+
+ memory = driver->root.memory;
+
+ error = FT_Err_Ok;
+ node = FT_List_Find( &face->sizes_list, size );
+ if ( node )
+ {
+ FT_List_Remove( &face->sizes_list, node );
+ FT_FREE( node );
+
+ if ( face->size == size )
+ {
+ face->size = 0;
+ if ( face->sizes_list.head )
+ face->size = (FT_Size)(face->sizes_list.head->data);
+ }
+
+ destroy_size( memory, size, driver );
+ }
+ else
+ error = FT_Err_Invalid_Size_Handle;
+
+ return error;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_Match_Size( FT_Face face,
+ FT_Size_Request req,
+ FT_Bool ignore_width,
+ FT_ULong* size_index )
+ {
+ FT_Int i;
+ FT_Long w, h;
+
+
+ if ( !FT_HAS_FIXED_SIZES( face ) )
+ return FT_Err_Invalid_Face_Handle;
+
+ /* FT_Bitmap_Size doesn't provide enough info... */
+ if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
+ return FT_Err_Unimplemented_Feature;
+
+ w = FT_REQUEST_WIDTH ( req );
+ h = FT_REQUEST_HEIGHT( req );
+
+ if ( req->width && !req->height )
+ h = w;
+ else if ( !req->width && req->height )
+ w = h;
+
+ w = FT_PIX_ROUND( w );
+ h = FT_PIX_ROUND( h );
+
+ for ( i = 0; i < face->num_fixed_sizes; i++ )
+ {
+ FT_Bitmap_Size* bsize = face->available_sizes + i;
+
+
+ if ( h != FT_PIX_ROUND( bsize->y_ppem ) )
+ continue;
+
+ if ( w == FT_PIX_ROUND( bsize->x_ppem ) || ignore_width )
+ {
+ if ( size_index )
+ *size_index = (FT_ULong)i;
+
+ return FT_Err_Ok;
+ }
+ }
+
+ return FT_Err_Invalid_Pixel_Size;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ ft_synthesize_vertical_metrics( FT_Glyph_Metrics* metrics,
+ FT_Pos advance )
+ {
+ /* the factor 1.2 is a heuristical value */
+ if ( !advance )
+ advance = metrics->height * 12 / 10;
+
+ metrics->vertBearingX = -( metrics->width / 2 );
+ metrics->vertBearingY = ( advance - metrics->height ) / 2;
+ metrics->vertAdvance = advance;
+ }
+
+
+ static void
+ ft_recompute_scaled_metrics( FT_Face face,
+ FT_Size_Metrics* metrics )
+ {
+ /* Compute root ascender, descender, test height, and max_advance */
+
+#ifdef GRID_FIT_METRICS
+ metrics->ascender = FT_PIX_CEIL( FT_MulFix( face->ascender,
+ metrics->y_scale ) );
+
+ metrics->descender = FT_PIX_FLOOR( FT_MulFix( face->descender,
+ metrics->y_scale ) );
+
+ metrics->height = FT_PIX_ROUND( FT_MulFix( face->height,
+ metrics->y_scale ) );
+
+ metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->max_advance_width,
+ metrics->x_scale ) );
+#else /* !GRID_FIT_METRICS */
+ metrics->ascender = FT_MulFix( face->ascender,
+ metrics->y_scale );
+
+ metrics->descender = FT_MulFix( face->descender,
+ metrics->y_scale );
+
+ metrics->height = FT_MulFix( face->height,
+ metrics->y_scale );
+
+ metrics->max_advance = FT_MulFix( face->max_advance_width,
+ metrics->x_scale );
+#endif /* !GRID_FIT_METRICS */
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Select_Metrics( FT_Face face,
+ FT_ULong strike_index )
+ {
+ FT_Size_Metrics* metrics;
+ FT_Bitmap_Size* bsize;
+
+
+ metrics = &face->size->metrics;
+ bsize = face->available_sizes + strike_index;
+
+ metrics->x_ppem = (FT_UShort)( ( bsize->x_ppem + 32 ) >> 6 );
+ metrics->y_ppem = (FT_UShort)( ( bsize->y_ppem + 32 ) >> 6 );
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ metrics->x_scale = FT_DivFix( bsize->x_ppem,
+ face->units_per_EM );
+ metrics->y_scale = FT_DivFix( bsize->y_ppem,
+ face->units_per_EM );
+
+ ft_recompute_scaled_metrics( face, metrics );
+ }
+ else
+ {
+ metrics->x_scale = 1L << 22;
+ metrics->y_scale = 1L << 22;
+ metrics->ascender = bsize->y_ppem;
+ metrics->descender = 0;
+ metrics->height = bsize->height << 6;
+ metrics->max_advance = bsize->x_ppem;
+ }
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Request_Metrics( FT_Face face,
+ FT_Size_Request req )
+ {
+ FT_Size_Metrics* metrics;
+
+
+ metrics = &face->size->metrics;
+
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ FT_Long w = 0, h = 0, scaled_w = 0, scaled_h = 0;
+
+
+ switch ( req->type )
+ {
+ case FT_SIZE_REQUEST_TYPE_NOMINAL:
+ w = h = face->units_per_EM;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_REAL_DIM:
+ w = h = face->ascender - face->descender;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_BBOX:
+ w = face->bbox.xMax - face->bbox.xMin;
+ h = face->bbox.yMax - face->bbox.yMin;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_CELL:
+ w = face->max_advance_width;
+ h = face->ascender - face->descender;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_SCALES:
+ metrics->x_scale = (FT_Fixed)req->width;
+ metrics->y_scale = (FT_Fixed)req->height;
+ if ( !metrics->x_scale )
+ metrics->x_scale = metrics->y_scale;
+ else if ( !metrics->y_scale )
+ metrics->y_scale = metrics->x_scale;
+ goto Calculate_Ppem;
+
+ case FT_SIZE_REQUEST_TYPE_MAX:
+ break;
+ }
+
+ /* to be on the safe side */
+ if ( w < 0 )
+ w = -w;
+
+ if ( h < 0 )
+ h = -h;
+
+ scaled_w = FT_REQUEST_WIDTH ( req );
+ scaled_h = FT_REQUEST_HEIGHT( req );
+
+ /* determine scales */
+ if ( req->width )
+ {
+ metrics->x_scale = FT_DivFix( scaled_w, w );
+
+ if ( req->height )
+ {
+ metrics->y_scale = FT_DivFix( scaled_h, h );
+
+ if ( req->type == FT_SIZE_REQUEST_TYPE_CELL )
+ {
+ if ( metrics->y_scale > metrics->x_scale )
+ metrics->y_scale = metrics->x_scale;
+ else
+ metrics->x_scale = metrics->y_scale;
+ }
+ }
+ else
+ {
+ metrics->y_scale = metrics->x_scale;
+ scaled_h = FT_MulDiv( scaled_w, h, w );
+ }
+ }
+ else
+ {
+ metrics->x_scale = metrics->y_scale = FT_DivFix( scaled_h, h );
+ scaled_w = FT_MulDiv( scaled_h, w, h );
+ }
+
+ Calculate_Ppem:
+ /* calculate the ppems */
+ if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
+ {
+ scaled_w = FT_MulFix( face->units_per_EM, metrics->x_scale );
+ scaled_h = FT_MulFix( face->units_per_EM, metrics->y_scale );
+ }
+
+ metrics->x_ppem = (FT_UShort)( ( scaled_w + 32 ) >> 6 );
+ metrics->y_ppem = (FT_UShort)( ( scaled_h + 32 ) >> 6 );
+
+ ft_recompute_scaled_metrics( face, metrics );
+ }
+ else
+ {
+ FT_ZERO( metrics );
+ metrics->x_scale = 1L << 22;
+ metrics->y_scale = 1L << 22;
+ }
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Select_Size( FT_Face face,
+ FT_Int strike_index )
+ {
+ FT_Driver_Class clazz;
+
+
+ if ( !face || !FT_HAS_FIXED_SIZES( face ) )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( strike_index < 0 || strike_index >= face->num_fixed_sizes )
+ return FT_Err_Invalid_Argument;
+
+ clazz = face->driver->clazz;
+
+ if ( clazz->select_size )
+ return clazz->select_size( face->size, (FT_ULong)strike_index );
+
+ FT_Select_Metrics( face, (FT_ULong)strike_index );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Request_Size( FT_Face face,
+ FT_Size_Request req )
+ {
+ FT_Driver_Class clazz;
+ FT_ULong strike_index;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( !req || req->width < 0 || req->height < 0 ||
+ req->type >= FT_SIZE_REQUEST_TYPE_MAX )
+ return FT_Err_Invalid_Argument;
+
+ clazz = face->driver->clazz;
+
+ if ( clazz->request_size )
+ return clazz->request_size( face->size, req );
+
+ /*
+ * The reason that a driver doesn't have `request_size' defined is
+ * either that the scaling here suffices or that the supported formats
+ * are bitmap-only and size matching is not implemented.
+ *
+ * In the latter case, a simple size matching is done.
+ */
+ if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) )
+ {
+ FT_Error error;
+
+
+ error = FT_Match_Size( face, req, 0, &strike_index );
+ if ( error )
+ return error;
+
+ FT_TRACE3(( "FT_Request_Size: bitmap strike %lu matched\n",
+ strike_index ));
+
+ return FT_Select_Size( face, (FT_Int)strike_index );
+ }
+
+ FT_Request_Metrics( face, req );
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Char_Size( FT_Face face,
+ FT_F26Dot6 char_width,
+ FT_F26Dot6 char_height,
+ FT_UInt horz_resolution,
+ FT_UInt vert_resolution )
+ {
+ FT_Size_RequestRec req;
+
+
+ if ( !char_width )
+ char_width = char_height;
+ else if ( !char_height )
+ char_height = char_width;
+
+ if ( !horz_resolution )
+ horz_resolution = vert_resolution;
+ else if ( !vert_resolution )
+ vert_resolution = horz_resolution;
+
+ if ( char_width < 1 * 64 )
+ char_width = 1 * 64;
+ if ( char_height < 1 * 64 )
+ char_height = 1 * 64;
+
+ if ( !horz_resolution )
+ horz_resolution = vert_resolution = 72;
+
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = char_width;
+ req.height = char_height;
+ req.horiResolution = horz_resolution;
+ req.vertResolution = vert_resolution;
+
+ return FT_Request_Size( face, &req );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Pixel_Sizes( FT_Face face,
+ FT_UInt pixel_width,
+ FT_UInt pixel_height )
+ {
+ FT_Size_RequestRec req;
+
+
+ if ( pixel_width == 0 )
+ pixel_width = pixel_height;
+ else if ( pixel_height == 0 )
+ pixel_height = pixel_width;
+
+ if ( pixel_width < 1 )
+ pixel_width = 1;
+ if ( pixel_height < 1 )
+ pixel_height = 1;
+
+ /* use `>=' to avoid potential compiler warning on 16bit platforms */
+ if ( pixel_width >= 0xFFFFU )
+ pixel_width = 0xFFFFU;
+ if ( pixel_height >= 0xFFFFU )
+ pixel_height = 0xFFFFU;
+
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = pixel_width << 6;
+ req.height = pixel_height << 6;
+ req.horiResolution = 0;
+ req.vertResolution = 0;
+
+ return FT_Request_Size( face, &req );
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Kerning( FT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_UInt kern_mode,
+ FT_Vector *akerning )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Driver driver;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( !akerning )
+ return FT_Err_Invalid_Argument;
+
+ driver = face->driver;
+
+ akerning->x = 0;
+ akerning->y = 0;
+
+ if ( driver->clazz->get_kerning )
+ {
+ error = driver->clazz->get_kerning( face,
+ left_glyph,
+ right_glyph,
+ akerning );
+ if ( !error )
+ {
+ if ( kern_mode != FT_KERNING_UNSCALED )
+ {
+ akerning->x = FT_MulFix( akerning->x, face->size->metrics.x_scale );
+ akerning->y = FT_MulFix( akerning->y, face->size->metrics.y_scale );
+
+ if ( kern_mode != FT_KERNING_UNFITTED )
+ {
+ /* we scale down kerning values for small ppem values */
+ /* to avoid that rounding makes them too big. */
+ /* `25' has been determined heuristically. */
+ if ( face->size->metrics.x_ppem < 25 )
+ akerning->x = FT_MulDiv( akerning->x,
+ face->size->metrics.x_ppem, 25 );
+ if ( face->size->metrics.y_ppem < 25 )
+ akerning->y = FT_MulDiv( akerning->y,
+ face->size->metrics.y_ppem, 25 );
+
+ akerning->x = FT_PIX_ROUND( akerning->x );
+ akerning->y = FT_PIX_ROUND( akerning->y );
+ }
+ }
+ }
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Track_Kerning( FT_Face face,
+ FT_Fixed point_size,
+ FT_Int degree,
+ FT_Fixed* akerning )
+ {
+ FT_Service_Kerning service;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( !akerning )
+ return FT_Err_Invalid_Argument;
+
+ FT_FACE_FIND_SERVICE( face, service, KERNING );
+ if ( !service )
+ return FT_Err_Unimplemented_Feature;
+
+ error = service->get_track( face,
+ point_size,
+ degree,
+ akerning );
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Select_Charmap( FT_Face face,
+ FT_Encoding encoding )
+ {
+ FT_CharMap* cur;
+ FT_CharMap* limit;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ if ( encoding == FT_ENCODING_NONE )
+ return FT_Err_Invalid_Argument;
+
+ /* FT_ENCODING_UNICODE is special. We try to find the `best' Unicode */
+ /* charmap available, i.e., one with UCS-4 characters, if possible. */
+ /* */
+ /* This is done by find_unicode_charmap() above, to share code. */
+ if ( encoding == FT_ENCODING_UNICODE )
+ return find_unicode_charmap( face );
+
+ cur = face->charmaps;
+ if ( !cur )
+ return FT_Err_Invalid_CharMap_Handle;
+
+ limit = cur + face->num_charmaps;
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0]->encoding == encoding )
+ {
+ face->charmap = cur[0];
+ return 0;
+ }
+ }
+
+ return FT_Err_Invalid_Argument;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Charmap( FT_Face face,
+ FT_CharMap charmap )
+ {
+ FT_CharMap* cur;
+ FT_CharMap* limit;
+
+
+ if ( !face )
+ return FT_Err_Invalid_Face_Handle;
+
+ cur = face->charmaps;
+ if ( !cur )
+ return FT_Err_Invalid_CharMap_Handle;
+
+ limit = cur + face->num_charmaps;
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] == charmap )
+ {
+ face->charmap = cur[0];
+ return 0;
+ }
+ }
+ return FT_Err_Invalid_Argument;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Get_Charmap_Index( FT_CharMap charmap )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < charmap->face->num_charmaps; i++ )
+ if ( charmap->face->charmaps[i] == charmap )
+ break;
+
+ FT_ASSERT( i < charmap->face->num_charmaps );
+
+ return i;
+ }
+
+
+ static void
+ ft_cmap_done_internal( FT_CMap cmap )
+ {
+ FT_CMap_Class clazz = cmap->clazz;
+ FT_Face face = cmap->charmap.face;
+ FT_Memory memory = FT_FACE_MEMORY(face);
+
+
+ if ( clazz->done )
+ clazz->done( cmap );
+
+ FT_FREE( cmap );
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_CMap_Done( FT_CMap cmap )
+ {
+ if ( cmap )
+ {
+ FT_Face face = cmap->charmap.face;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Error error;
+ FT_Int i, j;
+
+
+ for ( i = 0; i < face->num_charmaps; i++ )
+ {
+ if ( (FT_CMap)face->charmaps[i] == cmap )
+ {
+ FT_CharMap last_charmap = face->charmaps[face->num_charmaps - 1];
+
+
+ if ( FT_RENEW_ARRAY( face->charmaps,
+ face->num_charmaps,
+ face->num_charmaps - 1 ) )
+ return;
+
+ /* remove it from our list of charmaps */
+ for ( j = i + 1; j < face->num_charmaps; j++ )
+ {
+ if ( j == face->num_charmaps - 1 )
+ face->charmaps[j - 1] = last_charmap;
+ else
+ face->charmaps[j - 1] = face->charmaps[j];
+ }
+
+ face->num_charmaps--;
+
+ if ( (FT_CMap)face->charmap == cmap )
+ face->charmap = NULL;
+
+ ft_cmap_done_internal( cmap );
+
+ break;
+ }
+ }
+ }
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_CMap_New( FT_CMap_Class clazz,
+ FT_Pointer init_data,
+ FT_CharMap charmap,
+ FT_CMap *acmap )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Face face;
+ FT_Memory memory;
+ FT_CMap cmap;
+
+
+ if ( clazz == NULL || charmap == NULL || charmap->face == NULL )
+ return FT_Err_Invalid_Argument;
+
+ face = charmap->face;
+ memory = FT_FACE_MEMORY( face );
+
+ if ( !FT_ALLOC( cmap, clazz->size ) )
+ {
+ cmap->charmap = *charmap;
+ cmap->clazz = clazz;
+
+ if ( clazz->init )
+ {
+ error = clazz->init( cmap, init_data );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add it to our list of charmaps */
+ if ( FT_RENEW_ARRAY( face->charmaps,
+ face->num_charmaps,
+ face->num_charmaps + 1 ) )
+ goto Fail;
+
+ face->charmaps[face->num_charmaps++] = (FT_CharMap)cmap;
+ }
+
+ Exit:
+ if ( acmap )
+ *acmap = cmap;
+
+ return error;
+
+ Fail:
+ ft_cmap_done_internal( cmap );
+ cmap = NULL;
+ goto Exit;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Char_Index( FT_Face face,
+ FT_ULong charcode )
+ {
+ FT_UInt result = 0;
+
+
+ if ( face && face->charmap )
+ {
+ FT_CMap cmap = FT_CMAP( face->charmap );
+
+
+ result = cmap->clazz->char_index( cmap, charcode );
+ }
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_First_Char( FT_Face face,
+ FT_UInt *agindex )
+ {
+ FT_ULong result = 0;
+ FT_UInt gindex = 0;
+
+
+ if ( face && face->charmap )
+ {
+ gindex = FT_Get_Char_Index( face, 0 );
+ if ( gindex == 0 )
+ result = FT_Get_Next_Char( face, 0, &gindex );
+ }
+
+ if ( agindex )
+ *agindex = gindex;
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_Next_Char( FT_Face face,
+ FT_ULong charcode,
+ FT_UInt *agindex )
+ {
+ FT_ULong result = 0;
+ FT_UInt gindex = 0;
+
+
+ if ( face && face->charmap )
+ {
+ FT_UInt32 code = (FT_UInt32)charcode;
+ FT_CMap cmap = FT_CMAP( face->charmap );
+
+
+ gindex = cmap->clazz->char_next( cmap, &code );
+ result = ( gindex == 0 ) ? 0 : code;
+ }
+
+ if ( agindex )
+ *agindex = gindex;
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FT_Get_Name_Index( FT_Face face,
+ FT_String* glyph_name )
+ {
+ FT_UInt result = 0;
+
+
+ if ( face && FT_HAS_GLYPH_NAMES( face ) )
+ {
+ FT_Service_GlyphDict service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face,
+ service,
+ GLYPH_DICT );
+
+ if ( service && service->name_index )
+ result = service->name_index( face, glyph_name );
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_Glyph_Name( FT_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+
+
+ /* clean up buffer */
+ if ( buffer && buffer_max > 0 )
+ ((FT_Byte*)buffer)[0] = 0;
+
+ if ( face &&
+ glyph_index <= (FT_UInt)face->num_glyphs &&
+ FT_HAS_GLYPH_NAMES( face ) )
+ {
+ FT_Service_GlyphDict service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face,
+ service,
+ GLYPH_DICT );
+
+ if ( service && service->get_name )
+ error = service->get_name( face, glyph_index, buffer, buffer_max );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Get_Postscript_Name( FT_Face face )
+ {
+ const char* result = NULL;
+
+
+ if ( !face )
+ goto Exit;
+
+ if ( !result )
+ {
+ FT_Service_PsFontName service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face,
+ service,
+ POSTSCRIPT_FONT_NAME );
+
+ if ( service && service->get_ps_font_name )
+ result = service->get_ps_font_name( face );
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( void* )
+ FT_Get_Sfnt_Table( FT_Face face,
+ FT_Sfnt_Tag tag )
+ {
+ void* table = 0;
+ FT_Service_SFNT_Table service;
+
+
+ if ( face && FT_IS_SFNT( face ) )
+ {
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( service != NULL )
+ table = service->get_table( face, tag );
+ }
+
+ return table;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Load_Sfnt_Table( FT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length )
+ {
+ FT_Service_SFNT_Table service;
+
+
+ if ( !face || !FT_IS_SFNT( face ) )
+ return FT_Err_Invalid_Face_Handle;
+
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( service == NULL )
+ return FT_Err_Unimplemented_Feature;
+
+ return service->load_table( face, tag, offset, buffer, length );
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Sfnt_Table_Info( FT_Face face,
+ FT_UInt table_index,
+ FT_ULong *tag,
+ FT_ULong *length )
+ {
+ FT_Service_SFNT_Table service;
+
+
+ if ( !face || !FT_IS_SFNT( face ) )
+ return FT_Err_Invalid_Face_Handle;
+
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+ if ( service == NULL )
+ return FT_Err_Unimplemented_Feature;
+
+ return service->table_info( face, table_index, tag, length );
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_ULong )
+ FT_Get_CMap_Language_ID( FT_CharMap charmap )
+ {
+ FT_Service_TTCMaps service;
+ FT_Face face;
+ TT_CMapInfo cmap_info;
+
+
+ if ( !charmap || !charmap->face )
+ return 0;
+
+ face = charmap->face;
+ FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
+ if ( service == NULL )
+ return 0;
+ if ( service->get_cmap_info( charmap, &cmap_info ))
+ return 0;
+
+ return cmap_info.language;
+ }
+
+
+ /* documentation is in tttables.h */
+
+ FT_EXPORT_DEF( FT_Long )
+ FT_Get_CMap_Format( FT_CharMap charmap )
+ {
+ FT_Service_TTCMaps service;
+ FT_Face face;
+ TT_CMapInfo cmap_info;
+
+
+ if ( !charmap || !charmap->face )
+ return -1;
+
+ face = charmap->face;
+ FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
+ if ( service == NULL )
+ return -1;
+ if ( service->get_cmap_info( charmap, &cmap_info ))
+ return -1;
+
+ return cmap_info.format;
+ }
+
+
+ /* documentation is in ftsizes.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Activate_Size( FT_Size size )
+ {
+ FT_Face face;
+
+
+ if ( size == NULL )
+ return FT_Err_Bad_Argument;
+
+ face = size->face;
+ if ( face == NULL || face->driver == NULL )
+ return FT_Err_Bad_Argument;
+
+ /* we don't need anything more complex than that; all size objects */
+ /* are already listed by the face */
+ face->size = size;
+
+ return FT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** R E N D E R E R S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* lookup a renderer by glyph format in the library's list */
+ FT_BASE_DEF( FT_Renderer )
+ FT_Lookup_Renderer( FT_Library library,
+ FT_Glyph_Format format,
+ FT_ListNode* node )
+ {
+ FT_ListNode cur;
+ FT_Renderer result = 0;
+
+
+ if ( !library )
+ goto Exit;
+
+ cur = library->renderers.head;
+
+ if ( node )
+ {
+ if ( *node )
+ cur = (*node)->next;
+ *node = 0;
+ }
+
+ while ( cur )
+ {
+ FT_Renderer renderer = FT_RENDERER( cur->data );
+
+
+ if ( renderer->glyph_format == format )
+ {
+ if ( node )
+ *node = cur;
+
+ result = renderer;
+ break;
+ }
+ cur = cur->next;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ static FT_Renderer
+ ft_lookup_glyph_renderer( FT_GlyphSlot slot )
+ {
+ FT_Face face = slot->face;
+ FT_Library library = FT_FACE_LIBRARY( face );
+ FT_Renderer result = library->cur_renderer;
+
+
+ if ( !result || result->glyph_format != slot->format )
+ result = FT_Lookup_Renderer( library, slot->format, 0 );
+
+ return result;
+ }
+
+
+ static void
+ ft_set_current_renderer( FT_Library library )
+ {
+ FT_Renderer renderer;
+
+
+ renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 0 );
+ library->cur_renderer = renderer;
+ }
+
+
+ static FT_Error
+ ft_add_renderer( FT_Module module )
+ {
+ FT_Library library = module->library;
+ FT_Memory memory = library->memory;
+ FT_Error error;
+ FT_ListNode node;
+
+
+ if ( FT_NEW( node ) )
+ goto Exit;
+
+ {
+ FT_Renderer render = FT_RENDERER( module );
+ FT_Renderer_Class* clazz = (FT_Renderer_Class*)module->clazz;
+
+
+ render->clazz = clazz;
+ render->glyph_format = clazz->glyph_format;
+
+ /* allocate raster object if needed */
+ if ( clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE &&
+ clazz->raster_class->raster_new )
+ {
+ error = clazz->raster_class->raster_new( memory, &render->raster );
+ if ( error )
+ goto Fail;
+
+ render->raster_render = clazz->raster_class->raster_render;
+ render->render = clazz->render_glyph;
+ }
+
+ /* add to list */
+ node->data = module;
+ FT_List_Add( &library->renderers, node );
+
+ ft_set_current_renderer( library );
+ }
+
+ Fail:
+ if ( error )
+ FT_FREE( node );
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_remove_renderer( FT_Module module )
+ {
+ FT_Library library = module->library;
+ FT_Memory memory = library->memory;
+ FT_ListNode node;
+
+
+ node = FT_List_Find( &library->renderers, module );
+ if ( node )
+ {
+ FT_Renderer render = FT_RENDERER( module );
+
+
+ /* release raster object, if any */
+ if ( render->raster )
+ render->clazz->raster_class->raster_done( render->raster );
+
+ /* remove from list */
+ FT_List_Remove( &library->renderers, node );
+ FT_FREE( node );
+
+ ft_set_current_renderer( library );
+ }
+ }
+
+
+ /* documentation is in ftrender.h */
+
+ FT_EXPORT_DEF( FT_Renderer )
+ FT_Get_Renderer( FT_Library library,
+ FT_Glyph_Format format )
+ {
+ /* test for valid `library' delayed to FT_Lookup_Renderer() */
+
+ return FT_Lookup_Renderer( library, format, 0 );
+ }
+
+
+ /* documentation is in ftrender.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Set_Renderer( FT_Library library,
+ FT_Renderer renderer,
+ FT_UInt num_params,
+ FT_Parameter* parameters )
+ {
+ FT_ListNode node;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !renderer )
+ return FT_Err_Invalid_Argument;
+
+ node = FT_List_Find( &library->renderers, renderer );
+ if ( !node )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_List_Up( &library->renderers, node );
+
+ if ( renderer->glyph_format == FT_GLYPH_FORMAT_OUTLINE )
+ library->cur_renderer = renderer;
+
+ if ( num_params > 0 )
+ {
+ FT_Renderer_SetModeFunc set_mode = renderer->clazz->set_mode;
+
+
+ for ( ; num_params > 0; num_params-- )
+ {
+ error = set_mode( renderer, parameters->tag, parameters->data );
+ if ( error )
+ break;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Render_Glyph_Internal( FT_Library library,
+ FT_GlyphSlot slot,
+ FT_Render_Mode render_mode )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Renderer renderer;
+
+
+ /* if it is already a bitmap, no need to do anything */
+ switch ( slot->format )
+ {
+ case FT_GLYPH_FORMAT_BITMAP: /* already a bitmap, don't do anything */
+ break;
+
+ default:
+ {
+ FT_ListNode node = 0;
+ FT_Bool update = 0;
+
+
+ /* small shortcut for the very common case */
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ renderer = library->cur_renderer;
+ node = library->renderers.head;
+ }
+ else
+ renderer = FT_Lookup_Renderer( library, slot->format, &node );
+
+ error = FT_Err_Unimplemented_Feature;
+ while ( renderer )
+ {
+ error = renderer->render( renderer, slot, render_mode, NULL );
+ if ( !error ||
+ FT_ERROR_BASE( error ) != FT_Err_Cannot_Render_Glyph )
+ break;
+
+ /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
+ /* is unsupported by the current renderer for this glyph image */
+ /* format. */
+
+ /* now, look for another renderer that supports the same */
+ /* format. */
+ renderer = FT_Lookup_Renderer( library, slot->format, &node );
+ update = 1;
+ }
+
+ /* if we changed the current renderer for the glyph image format */
+ /* we need to select it as the next current one */
+ if ( !error && update && renderer )
+ FT_Set_Renderer( library, renderer, 0, 0 );
+ }
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Render_Glyph( FT_GlyphSlot slot,
+ FT_Render_Mode render_mode )
+ {
+ FT_Library library;
+
+
+ if ( !slot )
+ return FT_Err_Invalid_Argument;
+
+ library = FT_FACE_LIBRARY( slot->face );
+
+ return FT_Render_Glyph_Internal( library, slot, render_mode );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** M O D U L E S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Destroy_Module */
+ /* */
+ /* <Description> */
+ /* Destroys a given module object. For drivers, this also destroys */
+ /* all child faces. */
+ /* */
+ /* <InOut> */
+ /* module :: A handle to the target driver object. */
+ /* */
+ /* <Note> */
+ /* The driver _must_ be LOCKED! */
+ /* */
+ static void
+ Destroy_Module( FT_Module module )
+ {
+ FT_Memory memory = module->memory;
+ FT_Module_Class* clazz = module->clazz;
+ FT_Library library = module->library;
+
+
+ /* finalize client-data - before anything else */
+ if ( module->generic.finalizer )
+ module->generic.finalizer( module );
+
+ if ( library && library->auto_hinter == module )
+ library->auto_hinter = 0;
+
+ /* if the module is a renderer */
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ ft_remove_renderer( module );
+
+ /* if the module is a font driver, add some steps */
+ if ( FT_MODULE_IS_DRIVER( module ) )
+ Destroy_Driver( FT_DRIVER( module ) );
+
+ /* finalize the module object */
+ if ( clazz->module_done )
+ clazz->module_done( module );
+
+ /* discard it */
+ FT_FREE( module );
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Add_Module( FT_Library library,
+ const FT_Module_Class* clazz )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Module module;
+ FT_UInt nn;
+
+
+#define FREETYPE_VER_FIXED ( ( (FT_Long)FREETYPE_MAJOR << 16 ) | \
+ FREETYPE_MINOR )
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !clazz )
+ return FT_Err_Invalid_Argument;
+
+ /* check freetype version */
+ if ( clazz->module_requires > FREETYPE_VER_FIXED )
+ return FT_Err_Invalid_Version;
+
+ /* look for a module with the same name in the library's table */
+ for ( nn = 0; nn < library->num_modules; nn++ )
+ {
+ module = library->modules[nn];
+ if ( ft_strcmp( module->clazz->module_name, clazz->module_name ) == 0 )
+ {
+ /* this installed module has the same name, compare their versions */
+ if ( clazz->module_version <= module->clazz->module_version )
+ return FT_Err_Lower_Module_Version;
+
+ /* remove the module from our list, then exit the loop to replace */
+ /* it by our new version.. */
+ FT_Remove_Module( library, module );
+ break;
+ }
+ }
+
+ memory = library->memory;
+ error = FT_Err_Ok;
+
+ if ( library->num_modules >= FT_MAX_MODULES )
+ {
+ error = FT_Err_Too_Many_Drivers;
+ goto Exit;
+ }
+
+ /* allocate module object */
+ if ( FT_ALLOC( module, clazz->module_size ) )
+ goto Exit;
+
+ /* base initialization */
+ module->library = library;
+ module->memory = memory;
+ module->clazz = (FT_Module_Class*)clazz;
+
+ /* check whether the module is a renderer - this must be performed */
+ /* before the normal module initialization */
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ {
+ /* add to the renderers list */
+ error = ft_add_renderer( module );
+ if ( error )
+ goto Fail;
+ }
+
+ /* is the module a auto-hinter? */
+ if ( FT_MODULE_IS_HINTER( module ) )
+ library->auto_hinter = module;
+
+ /* if the module is a font driver */
+ if ( FT_MODULE_IS_DRIVER( module ) )
+ {
+ /* allocate glyph loader if needed */
+ FT_Driver driver = FT_DRIVER( module );
+
+
+ driver->clazz = (FT_Driver_Class)module->clazz;
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ {
+ error = FT_GlyphLoader_New( memory, &driver->glyph_loader );
+ if ( error )
+ goto Fail;
+ }
+ }
+
+ if ( clazz->module_init )
+ {
+ error = clazz->module_init( module );
+ if ( error )
+ goto Fail;
+ }
+
+ /* add module to the library's table */
+ library->modules[library->num_modules++] = module;
+
+ Exit:
+ return error;
+
+ Fail:
+ if ( FT_MODULE_IS_DRIVER( module ) )
+ {
+ FT_Driver driver = FT_DRIVER( module );
+
+
+ if ( FT_DRIVER_USES_OUTLINES( driver ) )
+ FT_GlyphLoader_Done( driver->glyph_loader );
+ }
+
+ if ( FT_MODULE_IS_RENDERER( module ) )
+ {
+ FT_Renderer renderer = FT_RENDERER( module );
+
+
+ if ( renderer->raster )
+ renderer->clazz->raster_class->raster_done( renderer->raster );
+ }
+
+ FT_FREE( module );
+ goto Exit;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Module )
+ FT_Get_Module( FT_Library library,
+ const char* module_name )
+ {
+ FT_Module result = 0;
+ FT_Module* cur;
+ FT_Module* limit;
+
+
+ if ( !library || !module_name )
+ return result;
+
+ cur = library->modules;
+ limit = cur + library->num_modules;
+
+ for ( ; cur < limit; cur++ )
+ if ( ft_strcmp( cur[0]->clazz->module_name, module_name ) == 0 )
+ {
+ result = cur[0];
+ break;
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( const void* )
+ FT_Get_Module_Interface( FT_Library library,
+ const char* mod_name )
+ {
+ FT_Module module;
+
+
+ /* test for valid `library' delayed to FT_Get_Module() */
+
+ module = FT_Get_Module( library, mod_name );
+
+ return module ? module->clazz->module_interface : 0;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_module_get_service( FT_Module module,
+ const char* service_id )
+ {
+ FT_Pointer result = NULL;
+
+ if ( module )
+ {
+ FT_ASSERT( module->clazz && module->clazz->get_interface );
+
+ /* first, look for the service in the module
+ */
+ if ( module->clazz->get_interface )
+ result = module->clazz->get_interface( module, service_id );
+
+ if ( result == NULL )
+ {
+ /* we didn't find it, look in all other modules then
+ */
+ FT_Library library = module->library;
+ FT_Module* cur = library->modules;
+ FT_Module* limit = cur + library->num_modules;
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] != module )
+ {
+ FT_ASSERT( cur[0]->clazz );
+
+ if ( cur[0]->clazz->get_interface )
+ {
+ result = cur[0]->clazz->get_interface( cur[0], service_id );
+ if ( result != NULL )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Remove_Module( FT_Library library,
+ FT_Module module )
+ {
+ /* try to find the module from the table, then remove it from there */
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( module )
+ {
+ FT_Module* cur = library->modules;
+ FT_Module* limit = cur + library->num_modules;
+
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur[0] == module )
+ {
+ /* remove it from the table */
+ library->num_modules--;
+ limit--;
+ while ( cur < limit )
+ {
+ cur[0] = cur[1];
+ cur++;
+ }
+ limit[0] = 0;
+
+ /* destroy the module */
+ Destroy_Module( module );
+
+ return FT_Err_Ok;
+ }
+ }
+ }
+ return FT_Err_Invalid_Driver_Handle;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** L I B R A R Y ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_New_Library( FT_Memory memory,
+ FT_Library *alibrary )
+ {
+ FT_Library library = 0;
+ FT_Error error;
+
+
+ if ( !memory )
+ return FT_Err_Invalid_Argument;
+
+#ifdef FT_DEBUG_LEVEL_ERROR
+ /* init debugging support */
+ ft_debug_init();
+#endif
+
+ /* first of all, allocate the library object */
+ if ( FT_NEW( library ) )
+ return error;
+
+ library->memory = memory;
+
+ /* allocate the render pool */
+ library->raster_pool_size = FT_RENDER_POOL_SIZE;
+ if ( FT_RENDER_POOL_SIZE > 0 )
+ if ( FT_ALLOC( library->raster_pool, FT_RENDER_POOL_SIZE ) )
+ goto Fail;
+
+ /* That's ok now */
+ *alibrary = library;
+
+ return FT_Err_Ok;
+
+ Fail:
+ FT_FREE( library );
+ return error;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Library_Version( FT_Library library,
+ FT_Int *amajor,
+ FT_Int *aminor,
+ FT_Int *apatch )
+ {
+ FT_Int major = 0;
+ FT_Int minor = 0;
+ FT_Int patch = 0;
+
+
+ if ( library )
+ {
+ major = library->version_major;
+ minor = library->version_minor;
+ patch = library->version_patch;
+ }
+
+ if ( amajor )
+ *amajor = major;
+
+ if ( aminor )
+ *aminor = minor;
+
+ if ( apatch )
+ *apatch = patch;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Done_Library( FT_Library library )
+ {
+ FT_Memory memory;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ memory = library->memory;
+
+ /* Discard client-data */
+ if ( library->generic.finalizer )
+ library->generic.finalizer( library );
+
+ /* Close all faces in the library. If we don't do
+ * this, we can have some subtle memory leaks.
+ * Example:
+ *
+ * - the cff font driver uses the pshinter module in cff_size_done
+ * - if the pshinter module is destroyed before the cff font driver,
+ * opened FT_Face objects managed by the driver are not properly
+ * destroyed, resulting in a memory leak
+ */
+ {
+ FT_UInt n;
+
+
+ for ( n = 0; n < library->num_modules; n++ )
+ {
+ FT_Module module = library->modules[n];
+ FT_List faces;
+
+
+ if ( ( module->clazz->module_flags & FT_MODULE_FONT_DRIVER ) == 0 )
+ continue;
+
+ faces = &FT_DRIVER(module)->faces_list;
+ while ( faces->head )
+ FT_Done_Face( FT_FACE( faces->head->data ) );
+ }
+ }
+
+ /* Close all other modules in the library */
+#if 1
+ /* XXX Modules are removed in the reversed order so that */
+ /* type42 module is removed before truetype module. This */
+ /* avoids double free in some occasions. It is a hack. */
+ while ( library->num_modules > 0 )
+ FT_Remove_Module( library,
+ library->modules[library->num_modules - 1] );
+#else
+ {
+ FT_UInt n;
+
+
+ for ( n = 0; n < library->num_modules; n++ )
+ {
+ FT_Module module = library->modules[n];
+
+
+ if ( module )
+ {
+ Destroy_Module( module );
+ library->modules[n] = 0;
+ }
+ }
+ }
+#endif
+
+ /* Destroy raster objects */
+ FT_FREE( library->raster_pool );
+ library->raster_pool_size = 0;
+
+ FT_FREE( library );
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Set_Debug_Hook( FT_Library library,
+ FT_UInt hook_index,
+ FT_DebugHook_Func debug_hook )
+ {
+ if ( library && debug_hook &&
+ hook_index <
+ ( sizeof ( library->debug_hooks ) / sizeof ( void* ) ) )
+ library->debug_hooks[hook_index] = debug_hook;
+ }
+
+
+ /* documentation is in ftmodapi.h */
+
+ FT_EXPORT_DEF( FT_TrueTypeEngineType )
+ FT_Get_TrueType_Engine_Type( FT_Library library )
+ {
+ FT_TrueTypeEngineType result = FT_TRUETYPE_ENGINE_TYPE_NONE;
+
+
+ if ( library )
+ {
+ FT_Module module = FT_Get_Module( library, "truetype" );
+
+
+ if ( module )
+ {
+ FT_Service_TrueTypeEngine service;
+
+
+ service = (FT_Service_TrueTypeEngine)
+ ft_module_get_service( module,
+ FT_SERVICE_ID_TRUETYPE_ENGINE );
+ if ( service )
+ result = service->engine_type;
+ }
+ }
+
+ return result;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_BASE_DEF( FT_Error )
+ ft_stub_set_char_sizes( FT_Size size,
+ FT_F26Dot6 width,
+ FT_F26Dot6 height,
+ FT_UInt horz_res,
+ FT_UInt vert_res )
+ {
+ FT_Size_RequestRec req;
+ FT_Driver driver = size->face->driver;
+
+
+ if ( driver->clazz->request_size )
+ {
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = width;
+ req.height = height;
+
+ if ( horz_res == 0 )
+ horz_res = vert_res;
+
+ if ( vert_res == 0 )
+ vert_res = horz_res;
+
+ if ( horz_res == 0 )
+ horz_res = vert_res = 72;
+
+ req.horiResolution = horz_res;
+ req.vertResolution = vert_res;
+
+ return driver->clazz->request_size( size, &req );
+ }
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ ft_stub_set_pixel_sizes( FT_Size size,
+ FT_UInt width,
+ FT_UInt height )
+ {
+ FT_Size_RequestRec req;
+ FT_Driver driver = size->face->driver;
+
+
+ if ( driver->clazz->request_size )
+ {
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = width << 6;
+ req.height = height << 6;
+ req.horiResolution = 0;
+ req.vertResolution = 0;
+
+ return driver->clazz->request_size( size, &req );
+ }
+
+ return 0;
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_SubGlyph_Info( FT_GlyphSlot glyph,
+ FT_UInt sub_index,
+ FT_Int *p_index,
+ FT_UInt *p_flags,
+ FT_Int *p_arg1,
+ FT_Int *p_arg2,
+ FT_Matrix *p_transform )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+
+
+ if ( glyph != NULL &&
+ glyph->format == FT_GLYPH_FORMAT_COMPOSITE &&
+ sub_index < glyph->num_subglyphs )
+ {
+ FT_SubGlyph subg = glyph->subglyphs + sub_index;
+
+
+ *p_index = subg->index;
+ *p_flags = subg->flags;
+ *p_arg1 = subg->arg1;
+ *p_arg2 = subg->arg2;
+ *p_transform = subg->transform;
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftotval.c b/src/base/ftotval.c
new file mode 100644
index 0000000..b6de6db
--- /dev/null
+++ b/src/base/ftotval.c
@@ -0,0 +1,83 @@
+/***************************************************************************/
+/* */
+/* ftotval.c */
+/* */
+/* FreeType API for validating OpenType tables (body). */
+/* */
+/* Copyright 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_OPENTYPE_VALIDATE_H
+
+
+ /* documentation is in ftotval.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_OpenType_Validate( FT_Face face,
+ FT_UInt validation_flags,
+ FT_Bytes *BASE_table,
+ FT_Bytes *GDEF_table,
+ FT_Bytes *GPOS_table,
+ FT_Bytes *GSUB_table,
+ FT_Bytes *JSTF_table )
+ {
+ FT_Service_OTvalidate service;
+ FT_Error error;
+
+
+ if ( !face )
+ {
+ error = FT_Err_Invalid_Face_Handle;
+ goto Exit;
+ }
+
+ if ( !( BASE_table &&
+ GDEF_table &&
+ GPOS_table &&
+ GSUB_table &&
+ JSTF_table ) )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, service, OPENTYPE_VALIDATE );
+
+ if ( service )
+ error = service->validate( face,
+ validation_flags,
+ BASE_table,
+ GDEF_table,
+ GPOS_table,
+ GSUB_table,
+ JSTF_table );
+ else
+ error = FT_Err_Unimplemented_Feature;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_EXPORT_DEF( void )
+ FT_OpenType_Free( FT_Face face,
+ FT_Bytes table )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( table );
+ }
+
+
+/* END */
diff --git a/src/base/ftoutln.c b/src/base/ftoutln.c
new file mode 100644
index 0000000..6926f3a
--- /dev/null
+++ b/src/base/ftoutln.c
@@ -0,0 +1,1088 @@
+/***************************************************************************/
+/* */
+/* ftoutln.c */
+/* */
+/* FreeType outline management (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* All functions are declared in freetype.h. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_OUTLINE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_TRIGONOMETRY_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_outline
+
+
+ static
+ const FT_Outline null_outline = { 0, 0, 0, 0, 0, 0 };
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Decompose( FT_Outline* outline,
+ const FT_Outline_Funcs* func_interface,
+ void* user )
+ {
+#undef SCALED
+#define SCALED( x ) ( ( (x) << shift ) - delta )
+
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ FT_Error error;
+
+ FT_Int n; /* index of contour in outline */
+ FT_UInt first; /* index of first point in contour */
+ FT_Int tag; /* current point's state */
+
+ FT_Int shift;
+ FT_Pos delta;
+
+
+ if ( !outline || !func_interface )
+ return FT_Err_Invalid_Argument;
+
+ shift = func_interface->shift;
+ delta = func_interface->delta;
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ FT_Int last; /* index of last point in contour */
+
+
+ last = outline->contours[n];
+ if ( last < 0 )
+ goto Invalid_Outline;
+ limit = outline->points + last;
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y );
+ v_last.x = SCALED( v_last.x ); v_last.y = SCALED( v_last.y );
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ error = func_interface->move_to( &v_start, user );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->line_to( &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = SCALED( point->x );
+ v_control.y = SCALED( point->y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector vec;
+ FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ error = func_interface->conic_to( &v_control, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = func_interface->conic_to( &v_control, &v_middle, user );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = func_interface->conic_to( &v_control, &v_start, user );
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1.x = SCALED( point[-2].x ); vec1.y = SCALED( point[-2].y );
+ vec2.x = SCALED( point[-1].x ); vec2.y = SCALED( point[-1].y );
+
+ if ( point <= limit )
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ error = func_interface->line_to( &v_start, user );
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ first = last + 1;
+ }
+
+ return 0;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return FT_Err_Invalid_Outline;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_New_Internal( FT_Memory memory,
+ FT_UInt numPoints,
+ FT_Int numContours,
+ FT_Outline *anoutline )
+ {
+ FT_Error error;
+
+
+ if ( !anoutline || !memory )
+ return FT_Err_Invalid_Argument;
+
+ *anoutline = null_outline;
+
+ if ( FT_NEW_ARRAY( anoutline->points, numPoints * 2L ) ||
+ FT_NEW_ARRAY( anoutline->tags, numPoints ) ||
+ FT_NEW_ARRAY( anoutline->contours, numContours ) )
+ goto Fail;
+
+ anoutline->n_points = (FT_UShort)numPoints;
+ anoutline->n_contours = (FT_Short)numContours;
+ anoutline->flags |= FT_OUTLINE_OWNER;
+
+ return FT_Err_Ok;
+
+ Fail:
+ anoutline->flags |= FT_OUTLINE_OWNER;
+ FT_Outline_Done_Internal( memory, anoutline );
+
+ return error;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_New( FT_Library library,
+ FT_UInt numPoints,
+ FT_Int numContours,
+ FT_Outline *anoutline )
+ {
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ return FT_Outline_New_Internal( library->memory, numPoints,
+ numContours, anoutline );
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Check( FT_Outline* outline )
+ {
+ if ( outline )
+ {
+ FT_Int n_points = outline->n_points;
+ FT_Int n_contours = outline->n_contours;
+ FT_Int end0, end;
+ FT_Int n;
+
+
+ /* empty glyph? */
+ if ( n_points == 0 && n_contours == 0 )
+ return 0;
+
+ /* check point and contour counts */
+ if ( n_points <= 0 || n_contours <= 0 )
+ goto Bad;
+
+ end0 = end = -1;
+ for ( n = 0; n < n_contours; n++ )
+ {
+ end = outline->contours[n];
+
+ /* note that we don't accept empty contours */
+ if ( end <= end0 || end >= n_points )
+ goto Bad;
+
+ end0 = end;
+ }
+
+ if ( end != n_points - 1 )
+ goto Bad;
+
+ /* XXX: check the tags array */
+ return 0;
+ }
+
+ Bad:
+ return FT_Err_Invalid_Argument;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Copy( const FT_Outline* source,
+ FT_Outline *target )
+ {
+ FT_Int is_owner;
+
+
+ if ( !source || !target ||
+ source->n_points != target->n_points ||
+ source->n_contours != target->n_contours )
+ return FT_Err_Invalid_Argument;
+
+ if ( source == target )
+ return FT_Err_Ok;
+
+ FT_ARRAY_COPY( target->points, source->points, source->n_points );
+
+ FT_ARRAY_COPY( target->tags, source->tags, source->n_points );
+
+ FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
+
+ /* copy all flags, except the `FT_OUTLINE_OWNER' one */
+ is_owner = target->flags & FT_OUTLINE_OWNER;
+ target->flags = source->flags;
+
+ target->flags &= ~FT_OUTLINE_OWNER;
+ target->flags |= is_owner;
+
+ return FT_Err_Ok;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Done_Internal( FT_Memory memory,
+ FT_Outline* outline )
+ {
+ if ( memory && outline )
+ {
+ if ( outline->flags & FT_OUTLINE_OWNER )
+ {
+ FT_FREE( outline->points );
+ FT_FREE( outline->tags );
+ FT_FREE( outline->contours );
+ }
+ *outline = null_outline;
+
+ return FT_Err_Ok;
+ }
+ else
+ return FT_Err_Invalid_Argument;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Done( FT_Library library,
+ FT_Outline* outline )
+ {
+ /* check for valid `outline' in FT_Outline_Done_Internal() */
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ return FT_Outline_Done_Internal( library->memory, outline );
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Get_CBox( const FT_Outline* outline,
+ FT_BBox *acbox )
+ {
+ FT_Pos xMin, yMin, xMax, yMax;
+
+
+ if ( outline && acbox )
+ {
+ if ( outline->n_points == 0 )
+ {
+ xMin = 0;
+ yMin = 0;
+ xMax = 0;
+ yMax = 0;
+ }
+ else
+ {
+ FT_Vector* vec = outline->points;
+ FT_Vector* limit = vec + outline->n_points;
+
+
+ xMin = xMax = vec->x;
+ yMin = yMax = vec->y;
+ vec++;
+
+ for ( ; vec < limit; vec++ )
+ {
+ FT_Pos x, y;
+
+
+ x = vec->x;
+ if ( x < xMin ) xMin = x;
+ if ( x > xMax ) xMax = x;
+
+ y = vec->y;
+ if ( y < yMin ) yMin = y;
+ if ( y > yMax ) yMax = y;
+ }
+ }
+ acbox->xMin = xMin;
+ acbox->xMax = xMax;
+ acbox->yMin = yMin;
+ acbox->yMax = yMax;
+ }
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Translate( const FT_Outline* outline,
+ FT_Pos xOffset,
+ FT_Pos yOffset )
+ {
+ FT_UShort n;
+ FT_Vector* vec = outline->points;
+
+
+ if ( !outline )
+ return;
+
+ for ( n = 0; n < outline->n_points; n++ )
+ {
+ vec->x += xOffset;
+ vec->y += yOffset;
+ vec++;
+ }
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Reverse( FT_Outline* outline )
+ {
+ FT_UShort n;
+ FT_Int first, last;
+
+
+ if ( !outline )
+ return;
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ last = outline->contours[n];
+
+ /* reverse point table */
+ {
+ FT_Vector* p = outline->points + first;
+ FT_Vector* q = outline->points + last;
+ FT_Vector swap;
+
+
+ while ( p < q )
+ {
+ swap = *p;
+ *p = *q;
+ *q = swap;
+ p++;
+ q--;
+ }
+ }
+
+ /* reverse tags table */
+ {
+ char* p = outline->tags + first;
+ char* q = outline->tags + last;
+ char swap;
+
+
+ while ( p < q )
+ {
+ swap = *p;
+ *p = *q;
+ *q = swap;
+ p++;
+ q--;
+ }
+ }
+
+ first = last + 1;
+ }
+
+ outline->flags ^= FT_OUTLINE_REVERSE_FILL;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Render( FT_Library library,
+ FT_Outline* outline,
+ FT_Raster_Params* params )
+ {
+ FT_Error error;
+ FT_Bool update = 0;
+ FT_Renderer renderer;
+ FT_ListNode node;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Library_Handle;
+
+ if ( !outline || !params )
+ return FT_Err_Invalid_Argument;
+
+ renderer = library->cur_renderer;
+ node = library->renderers.head;
+
+ params->source = (void*)outline;
+
+ error = FT_Err_Cannot_Render_Glyph;
+ while ( renderer )
+ {
+ error = renderer->raster_render( renderer->raster, params );
+ if ( !error || FT_ERROR_BASE( error ) != FT_Err_Cannot_Render_Glyph )
+ break;
+
+ /* FT_Err_Cannot_Render_Glyph is returned if the render mode */
+ /* is unsupported by the current renderer for this glyph image */
+ /* format */
+
+ /* now, look for another renderer that supports the same */
+ /* format */
+ renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
+ &node );
+ update = 1;
+ }
+
+ /* if we changed the current renderer for the glyph image format */
+ /* we need to select it as the next current one */
+ if ( !error && update && renderer )
+ FT_Set_Renderer( library, renderer, 0, 0 );
+
+ return error;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Get_Bitmap( FT_Library library,
+ FT_Outline* outline,
+ const FT_Bitmap *abitmap )
+ {
+ FT_Raster_Params params;
+
+
+ if ( !abitmap )
+ return FT_Err_Invalid_Argument;
+
+ /* other checks are delayed to FT_Outline_Render() */
+
+ params.target = abitmap;
+ params.flags = 0;
+
+ if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY ||
+ abitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
+ abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
+ params.flags |= FT_RASTER_FLAG_AA;
+
+ return FT_Outline_Render( library, outline, &params );
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Transform( FT_Vector* vector,
+ const FT_Matrix* matrix )
+ {
+ FT_Pos xz, yz;
+
+
+ if ( !vector || !matrix )
+ return;
+
+ xz = FT_MulFix( vector->x, matrix->xx ) +
+ FT_MulFix( vector->y, matrix->xy );
+
+ yz = FT_MulFix( vector->x, matrix->yx ) +
+ FT_MulFix( vector->y, matrix->yy );
+
+ vector->x = xz;
+ vector->y = yz;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Outline_Transform( const FT_Outline* outline,
+ const FT_Matrix* matrix )
+ {
+ FT_Vector* vec;
+ FT_Vector* limit;
+
+
+ if ( !outline || !matrix )
+ return;
+
+ vec = outline->points;
+ limit = vec + outline->n_points;
+
+ for ( ; vec < limit; vec++ )
+ FT_Vector_Transform( vec, matrix );
+ }
+
+
+#if 0
+
+#define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
+ do { \
+ (first) = ( c > 0 ) ? (outline)->points + \
+ (outline)->contours[c - 1] + 1 \
+ : (outline)->points; \
+ (last) = (outline)->points + (outline)->contours[c]; \
+ } while ( 0 )
+
+
+ /* Is a point in some contour? */
+ /* */
+ /* We treat every point of the contour as if it */
+ /* it were ON. That is, we allow false positives, */
+ /* but disallow false negatives. (XXX really?) */
+ static FT_Bool
+ ft_contour_has( FT_Outline* outline,
+ FT_Short c,
+ FT_Vector* point )
+ {
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Vector* a;
+ FT_Vector* b;
+ FT_UInt n = 0;
+
+
+ FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
+
+ for ( a = first; a <= last; a++ )
+ {
+ FT_Pos x;
+ FT_Int intersect;
+
+
+ b = ( a == last ) ? first : a + 1;
+
+ intersect = ( a->y - point->y ) ^ ( b->y - point->y );
+
+ /* a and b are on the same side */
+ if ( intersect >= 0 )
+ {
+ if ( intersect == 0 && a->y == point->y )
+ {
+ if ( ( a->x <= point->x && b->x >= point->x ) ||
+ ( a->x >= point->x && b->x <= point->x ) )
+ return 1;
+ }
+
+ continue;
+ }
+
+ x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
+
+ if ( x < point->x )
+ n++;
+ else if ( x == point->x )
+ return 1;
+ }
+
+ return ( n % 2 );
+ }
+
+
+ static FT_Bool
+ ft_contour_enclosed( FT_Outline* outline,
+ FT_UShort c )
+ {
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Short i;
+
+
+ FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
+
+ for ( i = 0; i < outline->n_contours; i++ )
+ {
+ if ( i != c && ft_contour_has( outline, i, first ) )
+ {
+ FT_Vector* pt;
+
+
+ for ( pt = first + 1; pt <= last; pt++ )
+ if ( !ft_contour_has( outline, i, pt ) )
+ return 0;
+
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+ /* This version differs from the public one in that each */
+ /* part (contour not enclosed in another contour) of the */
+ /* outline is checked for orientation. This is */
+ /* necessary for some buggy CJK fonts. */
+ static FT_Orientation
+ ft_outline_get_orientation( FT_Outline* outline )
+ {
+ FT_Short i;
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Orientation orient = FT_ORIENTATION_NONE;
+
+
+ first = outline->points;
+ for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
+ {
+ FT_Vector* point;
+ FT_Vector* xmin_point;
+ FT_Pos xmin;
+
+
+ last = outline->points + outline->contours[i];
+
+ /* skip degenerate contours */
+ if ( last < first + 2 )
+ continue;
+
+ if ( ft_contour_enclosed( outline, i ) )
+ continue;
+
+ xmin = first->x;
+ xmin_point = first;
+
+ for ( point = first + 1; point <= last; point++ )
+ {
+ if ( point->x < xmin )
+ {
+ xmin = point->x;
+ xmin_point = point;
+ }
+ }
+
+ /* check the orientation of the contour */
+ {
+ FT_Vector* prev;
+ FT_Vector* next;
+ FT_Orientation o;
+
+
+ prev = ( xmin_point == first ) ? last : xmin_point - 1;
+ next = ( xmin_point == last ) ? first : xmin_point + 1;
+
+ if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
+ FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
+ o = FT_ORIENTATION_POSTSCRIPT;
+ else
+ o = FT_ORIENTATION_TRUETYPE;
+
+ if ( orient == FT_ORIENTATION_NONE )
+ orient = o;
+ else if ( orient != o )
+ return FT_ORIENTATION_NONE;
+ }
+ }
+
+ return orient;
+ }
+
+#endif /* 0 */
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Outline_Embolden( FT_Outline* outline,
+ FT_Pos strength )
+ {
+ FT_Vector* points;
+ FT_Vector v_prev, v_first, v_next, v_cur;
+ FT_Angle rotate, angle_in, angle_out;
+ FT_Int c, n, first;
+ FT_Int orientation;
+
+
+ if ( !outline )
+ return FT_Err_Invalid_Argument;
+
+ strength /= 2;
+ if ( strength == 0 )
+ return FT_Err_Ok;
+
+ orientation = FT_Outline_Get_Orientation( outline );
+ if ( orientation == FT_ORIENTATION_NONE )
+ {
+ if ( outline->n_contours )
+ return FT_Err_Invalid_Argument;
+ else
+ return FT_Err_Ok;
+ }
+
+ if ( orientation == FT_ORIENTATION_TRUETYPE )
+ rotate = -FT_ANGLE_PI2;
+ else
+ rotate = FT_ANGLE_PI2;
+
+ points = outline->points;
+
+ first = 0;
+ for ( c = 0; c < outline->n_contours; c++ )
+ {
+ int last = outline->contours[c];
+
+
+ v_first = points[first];
+ v_prev = points[last];
+ v_cur = v_first;
+
+ for ( n = first; n <= last; n++ )
+ {
+ FT_Vector in, out;
+ FT_Angle angle_diff;
+ FT_Pos d;
+ FT_Fixed scale;
+
+
+ if ( n < last )
+ v_next = points[n + 1];
+ else
+ v_next = v_first;
+
+ /* compute the in and out vectors */
+ in.x = v_cur.x - v_prev.x;
+ in.y = v_cur.y - v_prev.y;
+
+ out.x = v_next.x - v_cur.x;
+ out.y = v_next.y - v_cur.y;
+
+ angle_in = FT_Atan2( in.x, in.y );
+ angle_out = FT_Atan2( out.x, out.y );
+ angle_diff = FT_Angle_Diff( angle_in, angle_out );
+ scale = FT_Cos( angle_diff / 2 );
+
+ if ( scale < 0x4000L && scale > -0x4000L )
+ in.x = in.y = 0;
+ else
+ {
+ d = FT_DivFix( strength, scale );
+
+ FT_Vector_From_Polar( &in, d, angle_in + angle_diff / 2 - rotate );
+ }
+
+ outline->points[n].x = v_cur.x + strength + in.x;
+ outline->points[n].y = v_cur.y + strength + in.y;
+
+ v_prev = v_cur;
+ v_cur = v_next;
+ }
+
+ first = last + 1;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftoutln.h */
+
+ FT_EXPORT_DEF( FT_Orientation )
+ FT_Outline_Get_Orientation( FT_Outline* outline )
+ {
+ FT_Pos xmin = 32768L;
+ FT_Pos xmin_ymin = 32768L;
+ FT_Pos xmin_ymax = -32768L;
+ FT_Vector* xmin_first = NULL;
+ FT_Vector* xmin_last = NULL;
+
+ short* contour;
+
+ FT_Vector* first;
+ FT_Vector* last;
+ FT_Vector* prev;
+ FT_Vector* point;
+
+ int i;
+ FT_Pos ray_y[3];
+ FT_Orientation result[3];
+
+
+ if ( !outline || outline->n_points <= 0 )
+ return FT_ORIENTATION_TRUETYPE;
+
+ /* We use the nonzero winding rule to find the orientation. */
+ /* Since glyph outlines behave much more `regular' than arbitrary */
+ /* cubic or quadratic curves, this test deals with the polygon */
+ /* only which is spanned up by the control points. */
+
+ first = outline->points;
+ for ( contour = outline->contours;
+ contour < outline->contours + outline->n_contours;
+ contour++, first = last + 1 )
+ {
+ FT_Pos contour_xmin = 32768L;
+ FT_Pos contour_xmax = -32768L;
+ FT_Pos contour_ymin = 32768L;
+ FT_Pos contour_ymax = -32768L;
+
+
+ last = outline->points + *contour;
+
+ /* skip degenerate contours */
+ if ( last < first + 2 )
+ continue;
+
+ for ( point = first; point <= last; ++point )
+ {
+ if ( point->x < contour_xmin )
+ contour_xmin = point->x;
+
+ if ( point->x > contour_xmax )
+ contour_xmax = point->x;
+
+ if ( point->y < contour_ymin )
+ contour_ymin = point->y;
+
+ if ( point->y > contour_ymax )
+ contour_ymax = point->y;
+ }
+
+ if ( contour_xmin < xmin &&
+ contour_xmin != contour_xmax &&
+ contour_ymin != contour_ymax )
+ {
+ xmin = contour_xmin;
+ xmin_ymin = contour_ymin;
+ xmin_ymax = contour_ymax;
+ xmin_first = first;
+ xmin_last = last;
+ }
+ }
+
+ if ( xmin == 32768 )
+ return FT_ORIENTATION_TRUETYPE;
+
+ ray_y[0] = ( xmin_ymin * 3 + xmin_ymax ) >> 2;
+ ray_y[1] = ( xmin_ymin + xmin_ymax ) >> 1;
+ ray_y[2] = ( xmin_ymin + xmin_ymax * 3 ) >> 2;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ FT_Pos left_x;
+ FT_Pos right_x;
+ FT_Vector* left1;
+ FT_Vector* left2;
+ FT_Vector* right1;
+ FT_Vector* right2;
+
+
+ RedoRay:
+ left_x = 32768L;
+ right_x = -32768L;
+
+ left1 = left2 = right1 = right2 = NULL;
+
+ prev = xmin_last;
+ for ( point = xmin_first; point <= xmin_last; prev = point, ++point )
+ {
+ FT_Pos tmp_x;
+
+
+ if ( point->y == ray_y[i] || prev->y == ray_y[i] )
+ {
+ ray_y[i]++;
+ goto RedoRay;
+ }
+
+ if ( ( point->y < ray_y[i] && prev->y < ray_y[i] ) ||
+ ( point->y > ray_y[i] && prev->y > ray_y[i] ) )
+ continue;
+
+ tmp_x = FT_MulDiv( point->x - prev->x,
+ ray_y[i] - prev->y,
+ point->y - prev->y ) + prev->x;
+
+ if ( tmp_x < left_x )
+ {
+ left_x = tmp_x;
+ left1 = prev;
+ left2 = point;
+ }
+
+ if ( tmp_x > right_x )
+ {
+ right_x = tmp_x;
+ right1 = prev;
+ right2 = point;
+ }
+ }
+
+ if ( left1 && right1 )
+ {
+ if ( left1->y < left2->y && right1->y > right2->y )
+ result[i] = FT_ORIENTATION_TRUETYPE;
+ else if ( left1->y > left2->y && right1->y < right2->y )
+ result[i] = FT_ORIENTATION_POSTSCRIPT;
+ else
+ result[i] = FT_ORIENTATION_NONE;
+ }
+ }
+
+ if ( result[0] != FT_ORIENTATION_NONE &&
+ ( result[0] == result[1] || result[0] == result[2] ) )
+ return result[0];
+
+ if ( result[1] != FT_ORIENTATION_NONE && result[1] == result[2] )
+ return result[1];
+
+ return FT_ORIENTATION_TRUETYPE;
+ }
+
+
+/* END */
diff --git a/src/base/ftpatent.c b/src/base/ftpatent.c
new file mode 100644
index 0000000..d63f191
--- /dev/null
+++ b/src/base/ftpatent.c
@@ -0,0 +1,281 @@
+/***************************************************************************/
+/* */
+/* ftpatent.c */
+/* */
+/* FreeType API for checking patented TrueType bytecode instructions */
+/* (body). */
+/* */
+/* Copyright 2007 by David Turner. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_STREAM_H
+#include FT_SERVICE_SFNT_H
+#include FT_SERVICE_TRUETYPE_GLYF_H
+
+
+ static FT_Bool
+ _tt_check_patents_in_range( FT_Stream stream,
+ FT_ULong size )
+ {
+ FT_Bool result = FALSE;
+ FT_Error error;
+ FT_Bytes p, end;
+
+
+ if ( FT_FRAME_ENTER( size ) )
+ return 0;
+
+ p = stream->cursor;
+ end = p + size;
+
+ while ( p < end )
+ {
+ switch (p[0])
+ {
+ case 0x06: /* SPvTL // */
+ case 0x07: /* SPvTL + */
+ case 0x08: /* SFvTL // */
+ case 0x09: /* SFvTL + */
+ case 0x0A: /* SPvFS */
+ case 0x0B: /* SFvFS */
+ result = TRUE;
+ goto Exit;
+
+ case 0x40:
+ if ( p + 1 >= end )
+ goto Exit;
+
+ p += p[1] + 2;
+ break;
+
+ case 0x41:
+ if ( p + 1 >= end )
+ goto Exit;
+
+ p += p[1] * 2 + 2;
+ break;
+
+ case 0x71: /* DELTAP2 */
+ case 0x72: /* DELTAP3 */
+ case 0x73: /* DELTAC0 */
+ case 0x74: /* DELTAC1 */
+ case 0x75: /* DELTAC2 */
+ result = TRUE;
+ goto Exit;
+
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ p += ( p[0] - 0xB0 ) + 2;
+ break;
+
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ p += ( p[0] - 0xB8 ) * 2 + 3;
+ break;
+
+ default:
+ p += 1;
+ break;
+ }
+ }
+
+ Exit:
+ FT_FRAME_EXIT();
+ return result;
+ }
+
+
+ static FT_Bool
+ _tt_check_patents_in_table( FT_Face face,
+ FT_ULong tag )
+ {
+ FT_Stream stream = face->stream;
+ FT_Error error;
+ FT_Service_SFNT_Table service;
+ FT_Bool result = FALSE;
+
+
+ FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
+
+ if ( service )
+ {
+ FT_ULong offset, size;
+
+
+ error = service->table_info( face, tag, &offset, &size );
+ if ( error ||
+ FT_STREAM_SEEK( offset ) )
+ goto Exit;
+
+ result = _tt_check_patents_in_range( stream, size );
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ static FT_Bool
+ _tt_face_check_patents( FT_Face face )
+ {
+ FT_Stream stream = face->stream;
+ FT_UInt gindex;
+ FT_Error error;
+ FT_Bool result;
+
+ FT_Service_TTGlyf service;
+
+
+ result = _tt_check_patents_in_table( face, TTAG_fpgm );
+ if ( result )
+ goto Exit;
+
+ result = _tt_check_patents_in_table( face, TTAG_prep );
+ if ( result )
+ goto Exit;
+
+ FT_FACE_FIND_SERVICE( face, service, TT_GLYF );
+ if ( service == NULL )
+ goto Exit;
+
+ for ( gindex = 0; gindex < (FT_UInt)face->num_glyphs; gindex++ )
+ {
+ FT_ULong offset, num_ins, size;
+ FT_Int num_contours;
+
+
+ offset = service->get_location( face, gindex, &size );
+ if ( size == 0 )
+ continue;
+
+ if ( FT_STREAM_SEEK( offset ) ||
+ FT_READ_SHORT( num_contours ) )
+ continue;
+
+ if ( num_contours >= 0 ) /* simple glyph */
+ {
+ if ( FT_STREAM_SKIP( 8 + num_contours * 2 ) )
+ continue;
+ }
+ else /* compound glyph */
+ {
+ FT_Bool has_instr = 0;
+
+
+ if ( FT_STREAM_SKIP( 8 ) )
+ continue;
+
+ /* now read each component */
+ for (;;)
+ {
+ FT_UInt flags, toskip;
+
+
+ if( FT_READ_USHORT( flags ) )
+ break;
+
+ toskip = 2 + 1 + 1;
+
+ if ( ( flags & ( 1 << 0 ) ) != 0 ) /* ARGS_ARE_WORDS */
+ toskip += 2;
+
+ if ( ( flags & ( 1 << 3 ) ) != 0 ) /* WE_HAVE_A_SCALE */
+ toskip += 2;
+ else if ( ( flags & ( 1 << 6 ) ) != 0 ) /* WE_HAVE_X_Y_SCALE */
+ toskip += 4;
+ else if ( ( flags & ( 1 << 7 ) ) != 0 ) /* WE_HAVE_A_2x2 */
+ toskip += 8;
+
+ if ( ( flags & ( 1 << 8 ) ) != 0 ) /* WE_HAVE_INSTRUCTIONS */
+ has_instr = 1;
+
+ if ( FT_STREAM_SKIP( toskip ) )
+ goto NextGlyph;
+
+ if ( ( flags & ( 1 << 5 ) ) == 0 ) /* MORE_COMPONENTS */
+ break;
+ }
+
+ if ( !has_instr )
+ goto NextGlyph;
+ }
+
+ if ( FT_READ_USHORT( num_ins ) )
+ continue;
+
+ result = _tt_check_patents_in_range( stream, num_ins );
+ if ( result )
+ goto Exit;
+
+ NextGlyph:
+ ;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Face_CheckTrueTypePatents( FT_Face face )
+ {
+ FT_Bool result = FALSE;
+
+
+ if ( face && FT_IS_SFNT( face ) )
+ result = _tt_face_check_patents( face );
+
+ return result;
+ }
+
+
+ /* documentation is in freetype.h */
+
+ FT_EXPORT_DEF( FT_Bool )
+ FT_Face_SetUnpatentedHinting( FT_Face face,
+ FT_Bool value )
+ {
+ FT_Bool result = 0;
+
+
+#if defined( TT_CONFIG_OPTION_UNPATENTED_HINTING ) && \
+ !defined( TT_CONFIG_OPTION_BYTECODE_INTEPRETER )
+ if ( face && FT_IS_SFNT( face ) )
+ {
+ result = !face->internal->ignore_unpatented_hinter;
+ face->internal->ignore_unpatented_hinter = !value;
+ }
+#else
+ FT_UNUSED( face );
+ FT_UNUSED( value );
+#endif
+
+ return result;
+ }
+
+/* END */
diff --git a/src/base/ftpfr.c b/src/base/ftpfr.c
new file mode 100644
index 0000000..9e930dd
--- /dev/null
+++ b/src/base/ftpfr.c
@@ -0,0 +1,132 @@
+/***************************************************************************/
+/* */
+/* ftpfr.c */
+/* */
+/* FreeType API for accessing PFR-specific data (body). */
+/* */
+/* Copyright 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_PFR_H
+
+
+ /* check the format */
+ static FT_Service_PfrMetrics
+ ft_pfr_check( FT_Face face )
+ {
+ FT_Service_PfrMetrics service;
+
+
+ FT_FACE_LOOKUP_SERVICE( face, service, PFR_METRICS );
+
+ return service;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Metrics( FT_Face face,
+ FT_UInt *aoutline_resolution,
+ FT_UInt *ametrics_resolution,
+ FT_Fixed *ametrics_x_scale,
+ FT_Fixed *ametrics_y_scale )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Service_PfrMetrics service;
+
+
+ service = ft_pfr_check( face );
+ if ( service )
+ {
+ error = service->get_metrics( face,
+ aoutline_resolution,
+ ametrics_resolution,
+ ametrics_x_scale,
+ ametrics_y_scale );
+ }
+ else if ( face )
+ {
+ FT_Fixed x_scale, y_scale;
+
+
+ /* this is not a PFR font */
+ *aoutline_resolution = face->units_per_EM;
+ *ametrics_resolution = face->units_per_EM;
+
+ x_scale = y_scale = 0x10000L;
+ if ( face->size )
+ {
+ x_scale = face->size->metrics.x_scale;
+ y_scale = face->size->metrics.y_scale;
+ }
+ *ametrics_x_scale = x_scale;
+ *ametrics_y_scale = y_scale;
+ }
+ else
+ error = FT_Err_Invalid_Argument;
+
+ return error;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Kerning( FT_Face face,
+ FT_UInt left,
+ FT_UInt right,
+ FT_Vector *avector )
+ {
+ FT_Error error;
+ FT_Service_PfrMetrics service;
+
+
+ service = ft_pfr_check( face );
+ if ( service )
+ error = service->get_kerning( face, left, right, avector );
+ else if ( face )
+ error = FT_Get_Kerning( face, left, right,
+ FT_KERNING_UNSCALED, avector );
+ else
+ error = FT_Err_Invalid_Argument;
+
+ return error;
+ }
+
+
+ /* documentation is in ftpfr.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PFR_Advance( FT_Face face,
+ FT_UInt gindex,
+ FT_Pos *aadvance )
+ {
+ FT_Error error;
+ FT_Service_PfrMetrics service;
+
+
+ service = ft_pfr_check( face );
+ if ( service )
+ {
+ error = service->get_advance( face, gindex, aadvance );
+ }
+ else
+ /* XXX: TODO: PROVIDE ADVANCE-LOADING METHOD TO ALL FONT DRIVERS */
+ error = FT_Err_Invalid_Argument;
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftrfork.c b/src/base/ftrfork.c
new file mode 100644
index 0000000..a4f726d
--- /dev/null
+++ b/src/base/ftrfork.c
@@ -0,0 +1,728 @@
+/***************************************************************************/
+/* */
+/* ftrfork.c */
+/* */
+/* Embedded resource forks accessor (body). */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* Masatake YAMATO and Redhat K.K. */
+/* */
+/* FT_Raccess_Get_HeaderInfo() and raccess_guess_darwin_hfsplus() are */
+/* derived from ftobjs.c. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* Development of the code in this file is support of */
+/* Information-technology Promotion Agency, Japan. */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_RFORK_H
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raccess
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** Resource fork directory access ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_BASE_DEF( FT_Error )
+ FT_Raccess_Get_HeaderInfo( FT_Library library,
+ FT_Stream stream,
+ FT_Long rfork_offset,
+ FT_Long *map_offset,
+ FT_Long *rdata_pos )
+ {
+ FT_Error error;
+ unsigned char head[16], head2[16];
+ FT_Long map_pos, rdata_len;
+ int allzeros, allmatch, i;
+ FT_Long type_list;
+
+ FT_UNUSED( library );
+
+
+ error = FT_Stream_Seek( stream, rfork_offset );
+ if ( error )
+ return error;
+
+ error = FT_Stream_Read( stream, (FT_Byte *)head, 16 );
+ if ( error )
+ return error;
+
+ *rdata_pos = rfork_offset + ( ( head[0] << 24 ) |
+ ( head[1] << 16 ) |
+ ( head[2] << 8 ) |
+ head[3] );
+ map_pos = rfork_offset + ( ( head[4] << 24 ) |
+ ( head[5] << 16 ) |
+ ( head[6] << 8 ) |
+ head[7] );
+ rdata_len = ( head[ 8] << 24 ) |
+ ( head[ 9] << 16 ) |
+ ( head[10] << 8 ) |
+ head[11];
+
+ /* map_len = head[12] .. head[15] */
+
+ if ( *rdata_pos + rdata_len != map_pos || map_pos == rfork_offset )
+ return FT_Err_Unknown_File_Format;
+
+ error = FT_Stream_Seek( stream, map_pos );
+ if ( error )
+ return error;
+
+ head2[15] = (FT_Byte)( head[15] + 1 ); /* make it be different */
+
+ error = FT_Stream_Read( stream, (FT_Byte*)head2, 16 );
+ if ( error )
+ return error;
+
+ allzeros = 1;
+ allmatch = 1;
+ for ( i = 0; i < 16; ++i )
+ {
+ if ( head2[i] != 0 )
+ allzeros = 0;
+ if ( head2[i] != head[i] )
+ allmatch = 0;
+ }
+ if ( !allzeros && !allmatch )
+ return FT_Err_Unknown_File_Format;
+
+ /* If we have reached this point then it is probably a mac resource */
+ /* file. Now, does it contain any interesting resources? */
+ /* Skip handle to next resource map, the file resource number, and */
+ /* attributes. */
+ (void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */
+ + 2 /* skip file resource number */
+ + 2 ); /* skip attributes */
+
+ if ( FT_READ_USHORT( type_list ) )
+ return error;
+ if ( type_list == -1 )
+ return FT_Err_Unknown_File_Format;
+
+ error = FT_Stream_Seek( stream, map_pos + type_list );
+ if ( error )
+ return error;
+
+ *map_offset = map_pos + type_list;
+ return FT_Err_Ok;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Raccess_Get_DataOffsets( FT_Library library,
+ FT_Stream stream,
+ FT_Long map_offset,
+ FT_Long rdata_pos,
+ FT_Long tag,
+ FT_Long **offsets,
+ FT_Long *count )
+ {
+ FT_Error error;
+ int i, j, cnt, subcnt;
+ FT_Long tag_internal, rpos;
+ FT_Memory memory = library->memory;
+ FT_Long temp;
+ FT_Long *offsets_internal;
+
+
+ error = FT_Stream_Seek( stream, map_offset );
+ if ( error )
+ return error;
+
+ if ( FT_READ_USHORT( cnt ) )
+ return error;
+ cnt++;
+
+ for ( i = 0; i < cnt; ++i )
+ {
+ if ( FT_READ_LONG( tag_internal ) ||
+ FT_READ_USHORT( subcnt ) ||
+ FT_READ_USHORT( rpos ) )
+ return error;
+
+ FT_TRACE2(( "Resource tags: %c%c%c%c\n",
+ (char)( 0xff & ( tag_internal >> 24 ) ),
+ (char)( 0xff & ( tag_internal >> 16 ) ),
+ (char)( 0xff & ( tag_internal >> 8 ) ),
+ (char)( 0xff & ( tag_internal >> 0 ) ) ));
+
+ if ( tag_internal == tag )
+ {
+ *count = subcnt + 1;
+ rpos += map_offset;
+
+ error = FT_Stream_Seek( stream, rpos );
+ if ( error )
+ return error;
+
+ if ( FT_NEW_ARRAY( offsets_internal, *count ) )
+ return error;
+
+ for ( j = 0; j < *count; ++j )
+ {
+ (void)FT_STREAM_SKIP( 2 ); /* resource id */
+ (void)FT_STREAM_SKIP( 2 ); /* rsource name */
+
+ if ( FT_READ_LONG( temp ) )
+ {
+ FT_FREE( offsets_internal );
+ return error;
+ }
+
+ offsets_internal[j] = rdata_pos + ( temp & 0xFFFFFFL );
+
+ (void)FT_STREAM_SKIP( 4 ); /* mbz */
+ }
+
+ *offsets = offsets_internal;
+
+ return FT_Err_Ok;
+ }
+ }
+
+ return FT_Err_Cannot_Open_Resource;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** Guessing functions ****/
+ /**** ****/
+ /**** When you add a new guessing function, ****/
+ /**** update FT_RACCESS_N_RULES in ftrfork.h. ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef FT_Error
+ (*raccess_guess_func)( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+
+ static FT_Error
+ raccess_guess_apple_double( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_apple_single( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_darwin_ufs_export( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_darwin_hfsplus( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_vfat( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_cap( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_double( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_netatalk( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset );
+
+
+ /*************************************************************************/
+ /**** ****/
+ /**** Helper functions ****/
+ /**** ****/
+ /*************************************************************************/
+
+ static FT_Error
+ raccess_guess_apple_generic( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ FT_Int32 magic,
+ FT_Long *result_offset );
+
+ static FT_Error
+ raccess_guess_linux_double_from_file_name( FT_Library library,
+ char * file_name,
+ FT_Long *result_offset );
+
+ static char *
+ raccess_make_file_name( FT_Memory memory,
+ const char *original_name,
+ const char *insertion );
+
+
+ FT_BASE_DEF( void )
+ FT_Raccess_Guess( FT_Library library,
+ FT_Stream stream,
+ char* base_name,
+ char **new_names,
+ FT_Long *offsets,
+ FT_Error *errors )
+ {
+ FT_Long i;
+
+
+ raccess_guess_func funcs[FT_RACCESS_N_RULES] =
+ {
+ raccess_guess_apple_double,
+ raccess_guess_apple_single,
+ raccess_guess_darwin_ufs_export,
+ raccess_guess_darwin_hfsplus,
+ raccess_guess_vfat,
+ raccess_guess_linux_cap,
+ raccess_guess_linux_double,
+ raccess_guess_linux_netatalk,
+ };
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ new_names[i] = NULL;
+ errors[i] = FT_Stream_Seek( stream, 0 );
+ if ( errors[i] )
+ continue ;
+
+ errors[i] = (funcs[i])( library, stream, base_name,
+ &(new_names[i]), &(offsets[i]) );
+ }
+
+ return;
+ }
+
+
+ static FT_Error
+ raccess_guess_apple_double( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic = ( 0x00 << 24 | 0x05 << 16 | 0x16 << 8 | 0x07 );
+
+
+ *result_file_name = NULL;
+ return raccess_guess_apple_generic( library, stream, base_file_name,
+ magic, result_offset );
+ }
+
+
+ static FT_Error
+ raccess_guess_apple_single( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic = (0x00 << 24 | 0x05 << 16 | 0x16 << 8 | 0x00);
+
+
+ *result_file_name = NULL;
+ return raccess_guess_apple_generic( library, stream, base_file_name,
+ magic, result_offset );
+ }
+
+
+ static FT_Error
+ raccess_guess_darwin_ufs_export( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+ newpath = raccess_make_file_name( memory, base_file_name, "._" );
+ if ( !newpath )
+ return FT_Err_Out_Of_Memory;
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_darwin_hfsplus( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ /*
+ Only meaningful on systems with hfs+ drivers (or Macs).
+ */
+ FT_Error error;
+ char* newpath;
+ FT_Memory memory;
+ FT_Long base_file_len = ft_strlen( base_file_name );
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ if ( base_file_len > FT_INT_MAX )
+ return FT_Err_Array_Too_Large;
+
+ if ( FT_ALLOC( newpath, base_file_len + 6 ) )
+ return error;
+
+ FT_MEM_COPY( newpath, base_file_name, base_file_len );
+ FT_MEM_COPY( newpath + base_file_len, "/rsrc", 6 );
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_vfat( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name,
+ "resource.frk/" );
+ if ( !newpath )
+ return FT_Err_Out_Of_Memory;
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_cap( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name, ".resource/" );
+ if ( !newpath )
+ return FT_Err_Out_Of_Memory;
+
+ *result_file_name = newpath;
+ *result_offset = 0;
+
+ return FT_Err_Ok;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_double( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name, "%" );
+ if ( !newpath )
+ return FT_Err_Out_Of_Memory;
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_netatalk( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ char **result_file_name,
+ FT_Long *result_offset )
+ {
+ char* newpath;
+ FT_Error error;
+ FT_Memory memory;
+
+ FT_UNUSED( stream );
+
+
+ memory = library->memory;
+
+ newpath = raccess_make_file_name( memory, base_file_name,
+ ".AppleDouble/" );
+ if ( !newpath )
+ return FT_Err_Out_Of_Memory;
+
+ error = raccess_guess_linux_double_from_file_name( library, newpath,
+ result_offset );
+ if ( !error )
+ *result_file_name = newpath;
+ else
+ FT_FREE( newpath );
+
+ return error;
+ }
+
+
+ static FT_Error
+ raccess_guess_apple_generic( FT_Library library,
+ FT_Stream stream,
+ char * base_file_name,
+ FT_Int32 magic,
+ FT_Long *result_offset )
+ {
+ FT_Int32 magic_from_stream;
+ FT_Error error;
+ FT_Int32 version_number = 0;
+ FT_UShort n_of_entries;
+
+ int i;
+ FT_UInt32 entry_id, entry_offset, entry_length = 0;
+
+ const FT_UInt32 resource_fork_entry_id = 0x2;
+
+ FT_UNUSED( library );
+ FT_UNUSED( base_file_name );
+ FT_UNUSED( version_number );
+ FT_UNUSED( entry_length );
+
+
+ if ( FT_READ_LONG( magic_from_stream ) )
+ return error;
+ if ( magic_from_stream != magic )
+ return FT_Err_Unknown_File_Format;
+
+ if ( FT_READ_LONG( version_number ) )
+ return error;
+
+ /* filler */
+ error = FT_Stream_Skip( stream, 16 );
+ if ( error )
+ return error;
+
+ if ( FT_READ_USHORT( n_of_entries ) )
+ return error;
+ if ( n_of_entries == 0 )
+ return FT_Err_Unknown_File_Format;
+
+ for ( i = 0; i < n_of_entries; i++ )
+ {
+ if ( FT_READ_LONG( entry_id ) )
+ return error;
+ if ( entry_id == resource_fork_entry_id )
+ {
+ if ( FT_READ_LONG( entry_offset ) ||
+ FT_READ_LONG( entry_length ) )
+ continue;
+ *result_offset = entry_offset;
+
+ return FT_Err_Ok;
+ }
+ else
+ FT_Stream_Skip( stream, 4 + 4 ); /* offset + length */
+ }
+
+ return FT_Err_Unknown_File_Format;
+ }
+
+
+ static FT_Error
+ raccess_guess_linux_double_from_file_name( FT_Library library,
+ char * file_name,
+ FT_Long *result_offset )
+ {
+ FT_Open_Args args2;
+ FT_Stream stream2;
+ char * nouse = NULL;
+ FT_Error error;
+
+
+ args2.flags = FT_OPEN_PATHNAME;
+ args2.pathname = file_name;
+ error = FT_Stream_New( library, &args2, &stream2 );
+ if ( error )
+ return error;
+
+ error = raccess_guess_apple_double( library, stream2, file_name,
+ &nouse, result_offset );
+
+ FT_Stream_Free( stream2, 0 );
+
+ return error;
+ }
+
+
+ static char*
+ raccess_make_file_name( FT_Memory memory,
+ const char *original_name,
+ const char *insertion )
+ {
+ char* new_name;
+ char* tmp;
+ const char* slash;
+ unsigned new_length;
+ FT_Error error = FT_Err_Ok;
+
+ FT_UNUSED( error );
+
+
+ new_length = ft_strlen( original_name ) + ft_strlen( insertion );
+ if ( FT_ALLOC( new_name, new_length + 1 ) )
+ return NULL;
+
+ tmp = ft_strrchr( original_name, '/' );
+ if ( tmp )
+ {
+ ft_strncpy( new_name, original_name, tmp - original_name + 1 );
+ new_name[tmp - original_name + 1] = '\0';
+ slash = tmp + 1;
+ }
+ else
+ {
+ slash = original_name;
+ new_name[0] = '\0';
+ }
+
+ ft_strcat( new_name, insertion );
+ ft_strcat( new_name, slash );
+
+ return new_name;
+ }
+
+
+#else /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
+
+
+ /*************************************************************************/
+ /* Dummy function; just sets errors */
+ /*************************************************************************/
+
+ FT_BASE_DEF( void )
+ FT_Raccess_Guess( FT_Library library,
+ FT_Stream stream,
+ char* base_name,
+ char **new_names,
+ FT_Long *offsets,
+ FT_Error *errors )
+ {
+ int i;
+
+ FT_UNUSED( library );
+ FT_UNUSED( stream );
+ FT_UNUSED( base_name );
+
+
+ for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
+ {
+ new_names[i] = NULL;
+ offsets[i] = 0;
+ errors[i] = FT_Err_Unimplemented_Feature;
+ }
+ }
+
+
+#endif /* !FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK */
+
+
+/* END */
diff --git a/src/base/ftstream.c b/src/base/ftstream.c
new file mode 100644
index 0000000..a067a1f
--- /dev/null
+++ b/src/base/ftstream.c
@@ -0,0 +1,842 @@
+/***************************************************************************/
+/* */
+/* ftstream.c */
+/* */
+/* I/O stream support (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_stream
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_OpenMemory( FT_Stream stream,
+ const FT_Byte* base,
+ FT_ULong size )
+ {
+ stream->base = (FT_Byte*) base;
+ stream->size = size;
+ stream->pos = 0;
+ stream->cursor = 0;
+ stream->read = 0;
+ stream->close = 0;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_Close( FT_Stream stream )
+ {
+ if ( stream && stream->close )
+ stream->close( stream );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Seek( FT_Stream stream,
+ FT_ULong pos )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ stream->pos = pos;
+
+ if ( stream->read )
+ {
+ if ( stream->read( stream, pos, 0, 0 ) )
+ {
+ FT_ERROR(( "FT_Stream_Seek: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ error = FT_Err_Invalid_Stream_Operation;
+ }
+ }
+ /* note that seeking to the first position after the file is valid */
+ else if ( pos > stream->size )
+ {
+ FT_ERROR(( "FT_Stream_Seek: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ error = FT_Err_Invalid_Stream_Operation;
+ }
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Skip( FT_Stream stream,
+ FT_Long distance )
+ {
+ return FT_Stream_Seek( stream, (FT_ULong)( stream->pos + distance ) );
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_Pos( FT_Stream stream )
+ {
+ return stream->pos;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Read( FT_Stream stream,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ReadAt( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_ULong read_bytes;
+
+
+ if ( pos >= stream->size )
+ {
+ FT_ERROR(( "FT_Stream_ReadAt: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ pos, stream->size ));
+
+ return FT_Err_Invalid_Stream_Operation;
+ }
+
+ if ( stream->read )
+ read_bytes = stream->read( stream, pos, buffer, count );
+ else
+ {
+ read_bytes = stream->size - pos;
+ if ( read_bytes > count )
+ read_bytes = count;
+
+ FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
+ }
+
+ stream->pos = pos + read_bytes;
+
+ if ( read_bytes < count )
+ {
+ FT_ERROR(( "FT_Stream_ReadAt:" ));
+ FT_ERROR(( " invalid read; expected %lu bytes, got %lu\n",
+ count, read_bytes ));
+
+ error = FT_Err_Invalid_Stream_Operation;
+ }
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_ULong )
+ FT_Stream_TryRead( FT_Stream stream,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_ULong read_bytes = 0;
+
+
+ if ( stream->pos >= stream->size )
+ goto Exit;
+
+ if ( stream->read )
+ read_bytes = stream->read( stream, stream->pos, buffer, count );
+ else
+ {
+ read_bytes = stream->size - stream->pos;
+ if ( read_bytes > count )
+ read_bytes = count;
+
+ FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
+ }
+
+ stream->pos += read_bytes;
+
+ Exit:
+ return read_bytes;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ExtractFrame( FT_Stream stream,
+ FT_ULong count,
+ FT_Byte** pbytes )
+ {
+ FT_Error error;
+
+
+ error = FT_Stream_EnterFrame( stream, count );
+ if ( !error )
+ {
+ *pbytes = (FT_Byte*)stream->cursor;
+
+ /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
+ stream->cursor = 0;
+ stream->limit = 0;
+ }
+
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_ReleaseFrame( FT_Stream stream,
+ FT_Byte** pbytes )
+ {
+ if ( stream->read )
+ {
+ FT_Memory memory = stream->memory;
+
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_free( memory, *pbytes );
+ *pbytes = NULL;
+#else
+ FT_FREE( *pbytes );
+#endif
+ }
+ *pbytes = 0;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_EnterFrame( FT_Stream stream,
+ FT_ULong count )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_ULong read_bytes;
+
+
+ /* check for nested frame access */
+ FT_ASSERT( stream && stream->cursor == 0 );
+
+ if ( stream->read )
+ {
+ /* allocate the frame in memory */
+ FT_Memory memory = stream->memory;
+
+#ifdef FT_DEBUG_MEMORY
+ /* assume _ft_debug_file and _ft_debug_lineno are already set */
+ stream->base = (unsigned char*)ft_mem_qalloc( memory, count, &error );
+ if ( error )
+ goto Exit;
+#else
+ if ( FT_QALLOC( stream->base, count ) )
+ goto Exit;
+#endif
+ /* read it */
+ read_bytes = stream->read( stream, stream->pos,
+ stream->base, count );
+ if ( read_bytes < count )
+ {
+ FT_ERROR(( "FT_Stream_EnterFrame:" ));
+ FT_ERROR(( " invalid read; expected %lu bytes, got %lu\n",
+ count, read_bytes ));
+
+ FT_FREE( stream->base );
+ error = FT_Err_Invalid_Stream_Operation;
+ }
+ stream->cursor = stream->base;
+ stream->limit = stream->cursor + count;
+ stream->pos += read_bytes;
+ }
+ else
+ {
+ /* check current and new position */
+ if ( stream->pos >= stream->size ||
+ stream->pos + count > stream->size )
+ {
+ FT_ERROR(( "FT_Stream_EnterFrame:" ));
+ FT_ERROR(( " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
+ stream->pos, count, stream->size ));
+
+ error = FT_Err_Invalid_Stream_Operation;
+ goto Exit;
+ }
+
+ /* set cursor */
+ stream->cursor = stream->base + stream->pos;
+ stream->limit = stream->cursor + count;
+ stream->pos += count;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Stream_ExitFrame( FT_Stream stream )
+ {
+ /* IMPORTANT: The assertion stream->cursor != 0 was removed, given */
+ /* that it is possible to access a frame of length 0 in */
+ /* some weird fonts (usually, when accessing an array of */
+ /* 0 records, like in some strange kern tables). */
+ /* */
+ /* In this case, the loader code handles the 0-length table */
+ /* gracefully; however, stream.cursor is really set to 0 by the */
+ /* FT_Stream_EnterFrame() call, and this is not an error. */
+ /* */
+ FT_ASSERT( stream );
+
+ if ( stream->read )
+ {
+ FT_Memory memory = stream->memory;
+
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_free( memory, stream->base );
+ stream->base = NULL;
+#else
+ FT_FREE( stream->base );
+#endif
+ }
+ stream->cursor = 0;
+ stream->limit = 0;
+ }
+
+
+ FT_BASE_DEF( FT_Char )
+ FT_Stream_GetChar( FT_Stream stream )
+ {
+ FT_Char result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ if ( stream->cursor < stream->limit )
+ result = *stream->cursor++;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Short )
+ FT_Stream_GetShort( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_Short result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 1 < stream->limit )
+ result = FT_NEXT_SHORT( p );
+ stream->cursor = p;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Short )
+ FT_Stream_GetShortLE( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_Short result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 1 < stream->limit )
+ result = FT_NEXT_SHORT_LE( p );
+ stream->cursor = p;
+
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_GetOffset( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_Long result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 2 < stream->limit )
+ result = FT_NEXT_OFF3( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_GetLong( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_Long result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 3 < stream->limit )
+ result = FT_NEXT_LONG( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_GetLongLE( FT_Stream stream )
+ {
+ FT_Byte* p;
+ FT_Long result;
+
+
+ FT_ASSERT( stream && stream->cursor );
+
+ result = 0;
+ p = stream->cursor;
+ if ( p + 3 < stream->limit )
+ result = FT_NEXT_LONG_LE( p );
+ stream->cursor = p;
+ return result;
+ }
+
+
+ FT_BASE_DEF( FT_Char )
+ FT_Stream_ReadChar( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
+ goto Fail;
+ }
+ else
+ {
+ if ( stream->pos < stream->size )
+ result = stream->base[stream->pos];
+ else
+ goto Fail;
+ }
+ stream->pos++;
+
+ return result;
+
+ Fail:
+ *error = FT_Err_Invalid_Stream_Operation;
+ FT_ERROR(( "FT_Stream_ReadChar: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Short )
+ FT_Stream_ReadShort( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[2];
+ FT_Byte* p = 0;
+ FT_Short result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->pos + 1 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ {
+ p = stream->base + stream->pos;
+ }
+
+ if ( p )
+ result = FT_NEXT_SHORT( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 2;
+
+ return result;
+
+ Fail:
+ *error = FT_Err_Invalid_Stream_Operation;
+ FT_ERROR(( "FT_Stream_ReadShort:" ));
+ FT_ERROR(( " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Short )
+ FT_Stream_ReadShortLE( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[2];
+ FT_Byte* p = 0;
+ FT_Short result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->pos + 1 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ {
+ p = stream->base + stream->pos;
+ }
+
+ if ( p )
+ result = FT_NEXT_SHORT_LE( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 2;
+
+ return result;
+
+ Fail:
+ *error = FT_Err_Invalid_Stream_Operation;
+ FT_ERROR(( "FT_Stream_ReadShortLE:" ));
+ FT_ERROR(( " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_ReadOffset( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[3];
+ FT_Byte* p = 0;
+ FT_Long result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->pos + 2 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ {
+ p = stream->base + stream->pos;
+ }
+
+ if ( p )
+ result = FT_NEXT_OFF3( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 3;
+
+ return result;
+
+ Fail:
+ *error = FT_Err_Invalid_Stream_Operation;
+ FT_ERROR(( "FT_Stream_ReadOffset:" ));
+ FT_ERROR(( " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_ReadLong( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[4];
+ FT_Byte* p = 0;
+ FT_Long result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->pos + 3 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ {
+ p = stream->base + stream->pos;
+ }
+
+ if ( p )
+ result = FT_NEXT_LONG( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 4;
+
+ return result;
+
+ Fail:
+ FT_ERROR(( "FT_Stream_ReadLong: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+ *error = FT_Err_Invalid_Stream_Operation;
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Long )
+ FT_Stream_ReadLongLE( FT_Stream stream,
+ FT_Error* error )
+ {
+ FT_Byte reads[4];
+ FT_Byte* p = 0;
+ FT_Long result = 0;
+
+
+ FT_ASSERT( stream );
+
+ *error = FT_Err_Ok;
+
+ if ( stream->pos + 3 < stream->size )
+ {
+ if ( stream->read )
+ {
+ if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
+ goto Fail;
+
+ p = reads;
+ }
+ else
+ {
+ p = stream->base + stream->pos;
+ }
+
+ if ( p )
+ result = FT_NEXT_LONG_LE( p );
+ }
+ else
+ goto Fail;
+
+ stream->pos += 4;
+
+ return result;
+
+ Fail:
+ FT_ERROR(( "FT_Stream_ReadLongLE:" ));
+ FT_ERROR(( " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
+ stream->pos, stream->size ));
+ *error = FT_Err_Invalid_Stream_Operation;
+
+ return 0;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_ReadFields( FT_Stream stream,
+ const FT_Frame_Field* fields,
+ void* structure )
+ {
+ FT_Error error;
+ FT_Bool frame_accessed = 0;
+ FT_Byte* cursor = stream->cursor;
+
+
+ if ( !fields || !stream )
+ return FT_Err_Invalid_Argument;
+
+ error = FT_Err_Ok;
+ do
+ {
+ FT_ULong value;
+ FT_Int sign_shift;
+ FT_Byte* p;
+
+
+ switch ( fields->value )
+ {
+ case ft_frame_start: /* access a new frame */
+ error = FT_Stream_EnterFrame( stream, fields->offset );
+ if ( error )
+ goto Exit;
+
+ frame_accessed = 1;
+ cursor = stream->cursor;
+ fields++;
+ continue; /* loop! */
+
+ case ft_frame_bytes: /* read a byte sequence */
+ case ft_frame_skip: /* skip some bytes */
+ {
+ FT_UInt len = fields->size;
+
+
+ if ( cursor + len > stream->limit )
+ {
+ error = FT_Err_Invalid_Stream_Operation;
+ goto Exit;
+ }
+
+ if ( fields->value == ft_frame_bytes )
+ {
+ p = (FT_Byte*)structure + fields->offset;
+ FT_MEM_COPY( p, cursor, len );
+ }
+ cursor += len;
+ fields++;
+ continue;
+ }
+
+ case ft_frame_byte:
+ case ft_frame_schar: /* read a single byte */
+ value = FT_NEXT_BYTE(cursor);
+ sign_shift = 24;
+ break;
+
+ case ft_frame_short_be:
+ case ft_frame_ushort_be: /* read a 2-byte big-endian short */
+ value = FT_NEXT_USHORT(cursor);
+ sign_shift = 16;
+ break;
+
+ case ft_frame_short_le:
+ case ft_frame_ushort_le: /* read a 2-byte little-endian short */
+ value = FT_NEXT_USHORT_LE(cursor);
+ sign_shift = 16;
+ break;
+
+ case ft_frame_long_be:
+ case ft_frame_ulong_be: /* read a 4-byte big-endian long */
+ value = FT_NEXT_ULONG(cursor);
+ sign_shift = 0;
+ break;
+
+ case ft_frame_long_le:
+ case ft_frame_ulong_le: /* read a 4-byte little-endian long */
+ value = FT_NEXT_ULONG_LE(cursor);
+ sign_shift = 0;
+ break;
+
+ case ft_frame_off3_be:
+ case ft_frame_uoff3_be: /* read a 3-byte big-endian long */
+ value = FT_NEXT_UOFF3(cursor);
+ sign_shift = 8;
+ break;
+
+ case ft_frame_off3_le:
+ case ft_frame_uoff3_le: /* read a 3-byte little-endian long */
+ value = FT_NEXT_UOFF3_LE(cursor);
+ sign_shift = 8;
+ break;
+
+ default:
+ /* otherwise, exit the loop */
+ stream->cursor = cursor;
+ goto Exit;
+ }
+
+ /* now, compute the signed value is necessary */
+ if ( fields->value & FT_FRAME_OP_SIGNED )
+ value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
+
+ /* finally, store the value in the object */
+
+ p = (FT_Byte*)structure + fields->offset;
+ switch ( fields->size )
+ {
+ case (8 / FT_CHAR_BIT):
+ *(FT_Byte*)p = (FT_Byte)value;
+ break;
+
+ case (16 / FT_CHAR_BIT):
+ *(FT_UShort*)p = (FT_UShort)value;
+ break;
+
+ case (32 / FT_CHAR_BIT):
+ *(FT_UInt32*)p = (FT_UInt32)value;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_ULong*)p = (FT_ULong)value;
+ }
+
+ /* go to next field */
+ fields++;
+ }
+ while ( 1 );
+
+ Exit:
+ /* close the frame if it was opened by this read */
+ if ( frame_accessed )
+ FT_Stream_ExitFrame( stream );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftstroke.c b/src/base/ftstroke.c
new file mode 100644
index 0000000..8f7e045
--- /dev/null
+++ b/src/base/ftstroke.c
@@ -0,0 +1,2010 @@
+/***************************************************************************/
+/* */
+/* ftstroke.c */
+/* */
+/* FreeType path stroker (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_STROKER_H
+#include FT_TRIGONOMETRY_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_StrokerBorder )
+ FT_Outline_GetInsideBorder( FT_Outline* outline )
+ {
+ FT_Orientation o = FT_Outline_Get_Orientation( outline );
+
+
+ return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_RIGHT
+ : FT_STROKER_BORDER_LEFT ;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_StrokerBorder )
+ FT_Outline_GetOutsideBorder( FT_Outline* outline )
+ {
+ FT_Orientation o = FT_Outline_Get_Orientation( outline );
+
+
+ return o == FT_ORIENTATION_TRUETYPE ? FT_STROKER_BORDER_LEFT
+ : FT_STROKER_BORDER_RIGHT ;
+ }
+
+
+ /***************************************************************************/
+ /***************************************************************************/
+ /***** *****/
+ /***** BEZIER COMPUTATIONS *****/
+ /***** *****/
+ /***************************************************************************/
+ /***************************************************************************/
+
+#define FT_SMALL_CONIC_THRESHOLD ( FT_ANGLE_PI / 6 )
+#define FT_SMALL_CUBIC_THRESHOLD ( FT_ANGLE_PI / 6 )
+#define FT_EPSILON 2
+
+#define FT_IS_SMALL( x ) ( (x) > -FT_EPSILON && (x) < FT_EPSILON )
+
+
+ static FT_Pos
+ ft_pos_abs( FT_Pos x )
+ {
+ return x >= 0 ? x : -x ;
+ }
+
+
+ static void
+ ft_conic_split( FT_Vector* base )
+ {
+ FT_Pos a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+ }
+
+
+ static FT_Bool
+ ft_conic_is_small_enough( FT_Vector* base,
+ FT_Angle *angle_in,
+ FT_Angle *angle_out )
+ {
+ FT_Vector d1, d2;
+ FT_Angle theta;
+ FT_Int close1, close2;
+
+
+ d1.x = base[1].x - base[2].x;
+ d1.y = base[1].y - base[2].y;
+ d2.x = base[0].x - base[1].x;
+ d2.y = base[0].y - base[1].y;
+
+ close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
+ close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
+
+ if ( close1 )
+ {
+ if ( close2 )
+ *angle_in = *angle_out = 0;
+ else
+ *angle_in = *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ else if ( close2 )
+ {
+ *angle_in = *angle_out = FT_Atan2( d1.x, d1.y );
+ }
+ else
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+
+ theta = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_out ) );
+
+ return FT_BOOL( theta < FT_SMALL_CONIC_THRESHOLD );
+ }
+
+
+ static void
+ ft_cubic_split( FT_Vector* base )
+ {
+ FT_Pos a, b, c, d;
+
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c ) / 2;
+ base[5].x = b = ( base[3].x + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].x = a = ( a + c ) / 2;
+ base[4].x = b = ( b + c ) / 2;
+ base[3].x = ( a + b ) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c ) / 2;
+ base[5].y = b = ( base[3].y + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].y = a = ( a + c ) / 2;
+ base[4].y = b = ( b + c ) / 2;
+ base[3].y = ( a + b ) / 2;
+ }
+
+
+ static FT_Bool
+ ft_cubic_is_small_enough( FT_Vector* base,
+ FT_Angle *angle_in,
+ FT_Angle *angle_mid,
+ FT_Angle *angle_out )
+ {
+ FT_Vector d1, d2, d3;
+ FT_Angle theta1, theta2;
+ FT_Int close1, close2, close3;
+
+
+ d1.x = base[2].x - base[3].x;
+ d1.y = base[2].y - base[3].y;
+ d2.x = base[1].x - base[2].x;
+ d2.y = base[1].y - base[2].y;
+ d3.x = base[0].x - base[1].x;
+ d3.y = base[0].y - base[1].y;
+
+ close1 = FT_IS_SMALL( d1.x ) && FT_IS_SMALL( d1.y );
+ close2 = FT_IS_SMALL( d2.x ) && FT_IS_SMALL( d2.y );
+ close3 = FT_IS_SMALL( d3.x ) && FT_IS_SMALL( d3.y );
+
+ if ( close1 || close3 )
+ {
+ if ( close2 )
+ {
+ /* basically a point */
+ *angle_in = *angle_out = *angle_mid = 0;
+ }
+ else if ( close1 )
+ {
+ *angle_in = *angle_mid = FT_Atan2( d2.x, d2.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+ else /* close2 */
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_mid = *angle_out = FT_Atan2( d2.x, d2.y );
+ }
+ }
+ else if ( close2 )
+ {
+ *angle_in = *angle_mid = FT_Atan2( d1.x, d1.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+ else
+ {
+ *angle_in = FT_Atan2( d1.x, d1.y );
+ *angle_mid = FT_Atan2( d2.x, d2.y );
+ *angle_out = FT_Atan2( d3.x, d3.y );
+ }
+
+ theta1 = ft_pos_abs( FT_Angle_Diff( *angle_in, *angle_mid ) );
+ theta2 = ft_pos_abs( FT_Angle_Diff( *angle_mid, *angle_out ) );
+
+ return FT_BOOL( theta1 < FT_SMALL_CUBIC_THRESHOLD &&
+ theta2 < FT_SMALL_CUBIC_THRESHOLD );
+ }
+
+
+ /***************************************************************************/
+ /***************************************************************************/
+ /***** *****/
+ /***** STROKE BORDERS *****/
+ /***** *****/
+ /***************************************************************************/
+ /***************************************************************************/
+
+ typedef enum
+ {
+ FT_STROKE_TAG_ON = 1, /* on-curve point */
+ FT_STROKE_TAG_CUBIC = 2, /* cubic off-point */
+ FT_STROKE_TAG_BEGIN = 4, /* sub-path start */
+ FT_STROKE_TAG_END = 8 /* sub-path end */
+
+ } FT_StrokeTags;
+
+#define FT_STROKE_TAG_BEGIN_END (FT_STROKE_TAG_BEGIN|FT_STROKE_TAG_END)
+
+ typedef struct FT_StrokeBorderRec_
+ {
+ FT_UInt num_points;
+ FT_UInt max_points;
+ FT_Vector* points;
+ FT_Byte* tags;
+ FT_Bool movable;
+ FT_Int start; /* index of current sub-path start point */
+ FT_Memory memory;
+ FT_Bool valid;
+
+ } FT_StrokeBorderRec, *FT_StrokeBorder;
+
+
+ static FT_Error
+ ft_stroke_border_grow( FT_StrokeBorder border,
+ FT_UInt new_points )
+ {
+ FT_UInt old_max = border->max_points;
+ FT_UInt new_max = border->num_points + new_points;
+ FT_Error error = 0;
+
+
+ if ( new_max > old_max )
+ {
+ FT_UInt cur_max = old_max;
+ FT_Memory memory = border->memory;
+
+
+ while ( cur_max < new_max )
+ cur_max += ( cur_max >> 1 ) + 16;
+
+ if ( FT_RENEW_ARRAY( border->points, old_max, cur_max ) ||
+ FT_RENEW_ARRAY( border->tags, old_max, cur_max ) )
+ goto Exit;
+
+ border->max_points = cur_max;
+ }
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_stroke_border_close( FT_StrokeBorder border,
+ FT_Bool reverse )
+ {
+ FT_UInt start = border->start;
+ FT_UInt count = border->num_points;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ /* don't record empty paths! */
+ if ( count <= start + 1U )
+ border->num_points = start;
+ else
+ {
+ /* copy the last point to the start of this sub-path, since */
+ /* it contains the `adjusted' starting coordinates */
+ border->num_points = --count;
+ border->points[start] = border->points[count];
+
+ if ( reverse )
+ {
+ /* reverse the points */
+ {
+ FT_Vector* vec1 = border->points + start + 1;
+ FT_Vector* vec2 = border->points + count - 1;
+
+
+ for ( ; vec1 < vec2; vec1++, vec2-- )
+ {
+ FT_Vector tmp;
+
+
+ tmp = *vec1;
+ *vec1 = *vec2;
+ *vec2 = tmp;
+ }
+ }
+
+ /* then the tags */
+ {
+ FT_Byte* tag1 = border->tags + start + 1;
+ FT_Byte* tag2 = border->tags + count - 1;
+
+
+ for ( ; tag1 < tag2; tag1++, tag2-- )
+ {
+ FT_Byte tmp;
+
+
+ tmp = *tag1;
+ *tag1 = *tag2;
+ *tag2 = tmp;
+ }
+ }
+ }
+
+ border->tags[start ] |= FT_STROKE_TAG_BEGIN;
+ border->tags[count - 1] |= FT_STROKE_TAG_END;
+ }
+
+ border->start = -1;
+ border->movable = 0;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_lineto( FT_StrokeBorder border,
+ FT_Vector* to,
+ FT_Bool movable )
+ {
+ FT_Error error = 0;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ if ( border->movable )
+ {
+ /* move last point */
+ border->points[border->num_points - 1] = *to;
+ }
+ else
+ {
+ /* add one point */
+ error = ft_stroke_border_grow( border, 1 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+
+ vec[0] = *to;
+ tag[0] = FT_STROKE_TAG_ON;
+
+ border->num_points += 1;
+ }
+ }
+ border->movable = movable;
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_conicto( FT_StrokeBorder border,
+ FT_Vector* control,
+ FT_Vector* to )
+ {
+ FT_Error error;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ error = ft_stroke_border_grow( border, 2 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+ vec[0] = *control;
+ vec[1] = *to;
+
+ tag[0] = 0;
+ tag[1] = FT_STROKE_TAG_ON;
+
+ border->num_points += 2;
+ }
+ border->movable = 0;
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_cubicto( FT_StrokeBorder border,
+ FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to )
+ {
+ FT_Error error;
+
+
+ FT_ASSERT( border->start >= 0 );
+
+ error = ft_stroke_border_grow( border, 3 );
+ if ( !error )
+ {
+ FT_Vector* vec = border->points + border->num_points;
+ FT_Byte* tag = border->tags + border->num_points;
+
+
+ vec[0] = *control1;
+ vec[1] = *control2;
+ vec[2] = *to;
+
+ tag[0] = FT_STROKE_TAG_CUBIC;
+ tag[1] = FT_STROKE_TAG_CUBIC;
+ tag[2] = FT_STROKE_TAG_ON;
+
+ border->num_points += 3;
+ }
+ border->movable = 0;
+ return error;
+ }
+
+
+#define FT_ARC_CUBIC_ANGLE ( FT_ANGLE_PI / 2 )
+
+
+ static FT_Error
+ ft_stroke_border_arcto( FT_StrokeBorder border,
+ FT_Vector* center,
+ FT_Fixed radius,
+ FT_Angle angle_start,
+ FT_Angle angle_diff )
+ {
+ FT_Angle total, angle, step, rotate, next, theta;
+ FT_Vector a, b, a2, b2;
+ FT_Fixed length;
+ FT_Error error = 0;
+
+
+ /* compute start point */
+ FT_Vector_From_Polar( &a, radius, angle_start );
+ a.x += center->x;
+ a.y += center->y;
+
+ total = angle_diff;
+ angle = angle_start;
+ rotate = ( angle_diff >= 0 ) ? FT_ANGLE_PI2 : -FT_ANGLE_PI2;
+
+ while ( total != 0 )
+ {
+ step = total;
+ if ( step > FT_ARC_CUBIC_ANGLE )
+ step = FT_ARC_CUBIC_ANGLE;
+
+ else if ( step < -FT_ARC_CUBIC_ANGLE )
+ step = -FT_ARC_CUBIC_ANGLE;
+
+ next = angle + step;
+ theta = step;
+ if ( theta < 0 )
+ theta = -theta;
+
+ theta >>= 1;
+
+ /* compute end point */
+ FT_Vector_From_Polar( &b, radius, next );
+ b.x += center->x;
+ b.y += center->y;
+
+ /* compute first and second control points */
+ length = FT_MulDiv( radius, FT_Sin( theta ) * 4,
+ ( 0x10000L + FT_Cos( theta ) ) * 3 );
+
+ FT_Vector_From_Polar( &a2, length, angle + rotate );
+ a2.x += a.x;
+ a2.y += a.y;
+
+ FT_Vector_From_Polar( &b2, length, next - rotate );
+ b2.x += b.x;
+ b2.y += b.y;
+
+ /* add cubic arc */
+ error = ft_stroke_border_cubicto( border, &a2, &b2, &b );
+ if ( error )
+ break;
+
+ /* process the rest of the arc ?? */
+ a = b;
+ total -= step;
+ angle = next;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_moveto( FT_StrokeBorder border,
+ FT_Vector* to )
+ {
+ /* close current open path if any ? */
+ if ( border->start >= 0 )
+ ft_stroke_border_close( border, 0 );
+
+ border->start = border->num_points;
+ border->movable = 0;
+
+ return ft_stroke_border_lineto( border, to, 0 );
+ }
+
+
+ static void
+ ft_stroke_border_init( FT_StrokeBorder border,
+ FT_Memory memory )
+ {
+ border->memory = memory;
+ border->points = NULL;
+ border->tags = NULL;
+
+ border->num_points = 0;
+ border->max_points = 0;
+ border->start = -1;
+ border->valid = 0;
+ }
+
+
+ static void
+ ft_stroke_border_reset( FT_StrokeBorder border )
+ {
+ border->num_points = 0;
+ border->start = -1;
+ border->valid = 0;
+ }
+
+
+ static void
+ ft_stroke_border_done( FT_StrokeBorder border )
+ {
+ FT_Memory memory = border->memory;
+
+
+ FT_FREE( border->points );
+ FT_FREE( border->tags );
+
+ border->num_points = 0;
+ border->max_points = 0;
+ border->start = -1;
+ border->valid = 0;
+ }
+
+
+ static FT_Error
+ ft_stroke_border_get_counts( FT_StrokeBorder border,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_Error error = 0;
+ FT_UInt num_points = 0;
+ FT_UInt num_contours = 0;
+
+ FT_UInt count = border->num_points;
+ FT_Vector* point = border->points;
+ FT_Byte* tags = border->tags;
+ FT_Int in_contour = 0;
+
+
+ for ( ; count > 0; count--, num_points++, point++, tags++ )
+ {
+ if ( tags[0] & FT_STROKE_TAG_BEGIN )
+ {
+ if ( in_contour != 0 )
+ goto Fail;
+
+ in_contour = 1;
+ }
+ else if ( in_contour == 0 )
+ goto Fail;
+
+ if ( tags[0] & FT_STROKE_TAG_END )
+ {
+ if ( in_contour == 0 )
+ goto Fail;
+
+ in_contour = 0;
+ num_contours++;
+ }
+ }
+
+ if ( in_contour != 0 )
+ goto Fail;
+
+ border->valid = 1;
+
+ Exit:
+ *anum_points = num_points;
+ *anum_contours = num_contours;
+ return error;
+
+ Fail:
+ num_points = 0;
+ num_contours = 0;
+ goto Exit;
+ }
+
+
+ static void
+ ft_stroke_border_export( FT_StrokeBorder border,
+ FT_Outline* outline )
+ {
+ /* copy point locations */
+ FT_ARRAY_COPY( outline->points + outline->n_points,
+ border->points,
+ border->num_points );
+
+ /* copy tags */
+ {
+ FT_UInt count = border->num_points;
+ FT_Byte* read = border->tags;
+ FT_Byte* write = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ for ( ; count > 0; count--, read++, write++ )
+ {
+ if ( *read & FT_STROKE_TAG_ON )
+ *write = FT_CURVE_TAG_ON;
+ else if ( *read & FT_STROKE_TAG_CUBIC )
+ *write = FT_CURVE_TAG_CUBIC;
+ else
+ *write = FT_CURVE_TAG_CONIC;
+ }
+ }
+
+ /* copy contours */
+ {
+ FT_UInt count = border->num_points;
+ FT_Byte* tags = border->tags;
+ FT_Short* write = outline->contours + outline->n_contours;
+ FT_Short idx = (FT_Short)outline->n_points;
+
+
+ for ( ; count > 0; count--, tags++, idx++ )
+ {
+ if ( *tags & FT_STROKE_TAG_END )
+ {
+ *write++ = idx;
+ outline->n_contours++;
+ }
+ }
+ }
+
+ outline->n_points = (short)( outline->n_points + border->num_points );
+
+ FT_ASSERT( FT_Outline_Check( outline ) == 0 );
+ }
+
+
+ /***************************************************************************/
+ /***************************************************************************/
+ /***** *****/
+ /***** STROKER *****/
+ /***** *****/
+ /***************************************************************************/
+ /***************************************************************************/
+
+#define FT_SIDE_TO_ROTATE( s ) ( FT_ANGLE_PI2 - (s) * FT_ANGLE_PI )
+
+ typedef struct FT_StrokerRec_
+ {
+ FT_Angle angle_in;
+ FT_Angle angle_out;
+ FT_Vector center;
+ FT_Bool first_point;
+ FT_Bool subpath_open;
+ FT_Angle subpath_angle;
+ FT_Vector subpath_start;
+
+ FT_Stroker_LineCap line_cap;
+ FT_Stroker_LineJoin line_join;
+ FT_Fixed miter_limit;
+ FT_Fixed radius;
+
+ FT_Bool valid;
+ FT_StrokeBorderRec borders[2];
+ FT_Memory memory;
+
+ } FT_StrokerRec;
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_New( FT_Library library,
+ FT_Stroker *astroker )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FT_Stroker stroker;
+
+
+ if ( !library )
+ return FT_Err_Invalid_Argument;
+
+ memory = library->memory;
+
+ if ( !FT_NEW( stroker ) )
+ {
+ stroker->memory = memory;
+
+ ft_stroke_border_init( &stroker->borders[0], memory );
+ ft_stroke_border_init( &stroker->borders[1], memory );
+ }
+ *astroker = stroker;
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Set( FT_Stroker stroker,
+ FT_Fixed radius,
+ FT_Stroker_LineCap line_cap,
+ FT_Stroker_LineJoin line_join,
+ FT_Fixed miter_limit )
+ {
+ stroker->radius = radius;
+ stroker->line_cap = line_cap;
+ stroker->line_join = line_join;
+ stroker->miter_limit = miter_limit;
+
+ FT_Stroker_Rewind( stroker );
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Rewind( FT_Stroker stroker )
+ {
+ if ( stroker )
+ {
+ ft_stroke_border_reset( &stroker->borders[0] );
+ ft_stroke_border_reset( &stroker->borders[1] );
+ }
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Done( FT_Stroker stroker )
+ {
+ if ( stroker )
+ {
+ FT_Memory memory = stroker->memory;
+
+
+ ft_stroke_border_done( &stroker->borders[0] );
+ ft_stroke_border_done( &stroker->borders[1] );
+
+ stroker->memory = NULL;
+ FT_FREE( stroker );
+ }
+ }
+
+
+ /* creates a circular arc at a corner or cap */
+ static FT_Error
+ ft_stroker_arcto( FT_Stroker stroker,
+ FT_Int side )
+ {
+ FT_Angle total, rotate;
+ FT_Fixed radius = stroker->radius;
+ FT_Error error = 0;
+ FT_StrokeBorder border = stroker->borders + side;
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ total = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+ if ( total == FT_ANGLE_PI )
+ total = -rotate * 2;
+
+ error = ft_stroke_border_arcto( border,
+ &stroker->center,
+ radius,
+ stroker->angle_in + rotate,
+ total );
+ border->movable = 0;
+ return error;
+ }
+
+
+ /* adds a cap at the end of an opened path */
+ static FT_Error
+ ft_stroker_cap( FT_Stroker stroker,
+ FT_Angle angle,
+ FT_Int side )
+ {
+ FT_Error error = 0;
+
+
+ if ( stroker->line_cap == FT_STROKER_LINECAP_ROUND )
+ {
+ /* add a round cap */
+ stroker->angle_in = angle;
+ stroker->angle_out = angle + FT_ANGLE_PI;
+ error = ft_stroker_arcto( stroker, side );
+ }
+ else if ( stroker->line_cap == FT_STROKER_LINECAP_SQUARE )
+ {
+ /* add a square cap */
+ FT_Vector delta, delta2;
+ FT_Angle rotate = FT_SIDE_TO_ROTATE( side );
+ FT_Fixed radius = stroker->radius;
+ FT_StrokeBorder border = stroker->borders + side;
+
+
+ FT_Vector_From_Polar( &delta2, radius, angle + rotate );
+ FT_Vector_From_Polar( &delta, radius, angle );
+
+ delta.x += stroker->center.x + delta2.x;
+ delta.y += stroker->center.y + delta2.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+ if ( error )
+ goto Exit;
+
+ FT_Vector_From_Polar( &delta2, radius, angle - rotate );
+ FT_Vector_From_Polar( &delta, radius, angle );
+
+ delta.x += delta2.x + stroker->center.x;
+ delta.y += delta2.y + stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* process an inside corner, i.e. compute intersection */
+ static FT_Error
+ ft_stroker_inside( FT_Stroker stroker,
+ FT_Int side)
+ {
+ FT_StrokeBorder border = stroker->borders + side;
+ FT_Angle phi, theta, rotate;
+ FT_Fixed length, thcos, sigma;
+ FT_Vector delta;
+ FT_Error error = 0;
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ /* compute median angle */
+ theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+ if ( theta == FT_ANGLE_PI )
+ theta = rotate;
+ else
+ theta = theta / 2;
+
+ phi = stroker->angle_in + theta;
+
+ thcos = FT_Cos( theta );
+ sigma = FT_MulFix( stroker->miter_limit, thcos );
+
+ /* TODO: find better criterion to switch off the optimization */
+ if ( sigma < 0x10000L )
+ {
+ FT_Vector_From_Polar( &delta, stroker->radius,
+ stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+ border->movable = 0;
+ }
+ else
+ {
+ length = FT_DivFix( stroker->radius, thcos );
+
+ FT_Vector_From_Polar( &delta, length, phi + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+ }
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+
+ return error;
+ }
+
+
+ /* process an outside corner, i.e. compute bevel/miter/round */
+ static FT_Error
+ ft_stroker_outside( FT_Stroker stroker,
+ FT_Int side )
+ {
+ FT_StrokeBorder border = stroker->borders + side;
+ FT_Error error;
+ FT_Angle rotate;
+
+
+ if ( stroker->line_join == FT_STROKER_LINEJOIN_ROUND )
+ {
+ error = ft_stroker_arcto( stroker, side );
+ }
+ else
+ {
+ /* this is a mitered or beveled corner */
+ FT_Fixed sigma, radius = stroker->radius;
+ FT_Angle theta, phi;
+ FT_Fixed thcos;
+ FT_Bool miter;
+
+
+ rotate = FT_SIDE_TO_ROTATE( side );
+ miter = FT_BOOL( stroker->line_join == FT_STROKER_LINEJOIN_MITER );
+
+ theta = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+ if ( theta == FT_ANGLE_PI )
+ {
+ theta = rotate;
+ phi = stroker->angle_in;
+ }
+ else
+ {
+ theta = theta / 2;
+ phi = stroker->angle_in + theta + rotate;
+ }
+
+ thcos = FT_Cos( theta );
+ sigma = FT_MulFix( stroker->miter_limit, thcos );
+
+ if ( sigma >= 0x10000L )
+ miter = 0;
+
+ if ( miter ) /* this is a miter (broken angle) */
+ {
+ FT_Vector middle, delta;
+ FT_Fixed length;
+
+
+ /* compute middle point */
+ FT_Vector_From_Polar( &middle,
+ FT_MulFix( radius, stroker->miter_limit ),
+ phi );
+ middle.x += stroker->center.x;
+ middle.y += stroker->center.y;
+
+ /* compute first angle point */
+ length = FT_MulFix( radius,
+ FT_DivFix( 0x10000L - sigma,
+ ft_pos_abs( FT_Sin( theta ) ) ) );
+
+ FT_Vector_From_Polar( &delta, length, phi + rotate );
+ delta.x += middle.x;
+ delta.y += middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+ if ( error )
+ goto Exit;
+
+ /* compute second angle point */
+ FT_Vector_From_Polar( &delta, length, phi - rotate );
+ delta.x += middle.x;
+ delta.y += middle.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+ if ( error )
+ goto Exit;
+
+ /* finally, add a movable end point */
+ FT_Vector_From_Polar( &delta, radius, stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 1 );
+ }
+
+ else /* this is a bevel (intersection) */
+ {
+ FT_Fixed length;
+ FT_Vector delta;
+
+
+ length = FT_DivFix( stroker->radius, thcos );
+
+ FT_Vector_From_Polar( &delta, length, phi );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 0 );
+ if (error) goto Exit;
+
+ /* now add end point */
+ FT_Vector_From_Polar( &delta, stroker->radius,
+ stroker->angle_out + rotate );
+ delta.x += stroker->center.x;
+ delta.y += stroker->center.y;
+
+ error = ft_stroke_border_lineto( border, &delta, 1 );
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ ft_stroker_process_corner( FT_Stroker stroker )
+ {
+ FT_Error error = 0;
+ FT_Angle turn;
+ FT_Int inside_side;
+
+
+ turn = FT_Angle_Diff( stroker->angle_in, stroker->angle_out );
+
+ /* no specific corner processing is required if the turn is 0 */
+ if ( turn == 0 )
+ goto Exit;
+
+ /* when we turn to the right, the inside side is 0 */
+ inside_side = 0;
+
+ /* otherwise, the inside side is 1 */
+ if ( turn < 0 )
+ inside_side = 1;
+
+ /* process the inside side */
+ error = ft_stroker_inside( stroker, inside_side );
+ if ( error )
+ goto Exit;
+
+ /* process the outside side */
+ error = ft_stroker_outside( stroker, 1 - inside_side );
+
+ Exit:
+ return error;
+ }
+
+
+ /* add two points to the left and right borders corresponding to the */
+ /* start of the subpath.. */
+ static FT_Error
+ ft_stroker_subpath_start( FT_Stroker stroker,
+ FT_Angle start_angle )
+ {
+ FT_Vector delta;
+ FT_Vector point;
+ FT_Error error;
+ FT_StrokeBorder border;
+
+
+ FT_Vector_From_Polar( &delta, stroker->radius,
+ start_angle + FT_ANGLE_PI2 );
+
+ point.x = stroker->center.x + delta.x;
+ point.y = stroker->center.y + delta.y;
+
+ border = stroker->borders;
+ error = ft_stroke_border_moveto( border, &point );
+ if ( error )
+ goto Exit;
+
+ point.x = stroker->center.x - delta.x;
+ point.y = stroker->center.y - delta.y;
+
+ border++;
+ error = ft_stroke_border_moveto( border, &point );
+
+ /* save angle for last cap */
+ stroker->subpath_angle = start_angle;
+ stroker->first_point = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_LineTo( FT_Stroker stroker,
+ FT_Vector* to )
+ {
+ FT_Error error = 0;
+ FT_StrokeBorder border;
+ FT_Vector delta;
+ FT_Angle angle;
+ FT_Int side;
+
+ delta.x = to->x - stroker->center.x;
+ delta.y = to->y - stroker->center.y;
+
+ angle = FT_Atan2( delta.x, delta.y );
+ FT_Vector_From_Polar( &delta, stroker->radius, angle + FT_ANGLE_PI2 );
+
+ /* process corner if necessary */
+ if ( stroker->first_point )
+ {
+ /* This is the first segment of a subpath. We need to */
+ /* add a point to each border at their respective starting */
+ /* point locations. */
+ error = ft_stroker_subpath_start( stroker, angle );
+ if ( error )
+ goto Exit;
+ }
+ else
+ {
+ /* process the current corner */
+ stroker->angle_out = angle;
+ error = ft_stroker_process_corner( stroker );
+ if ( error )
+ goto Exit;
+ }
+
+ /* now add a line segment to both the "inside" and "outside" paths */
+
+ for ( border = stroker->borders, side = 1; side >= 0; side--, border++ )
+ {
+ FT_Vector point;
+
+
+ point.x = to->x + delta.x;
+ point.y = to->y + delta.y;
+
+ error = ft_stroke_border_lineto( border, &point, 1 );
+ if ( error )
+ goto Exit;
+
+ delta.x = -delta.x;
+ delta.y = -delta.y;
+ }
+
+ stroker->angle_in = angle;
+ stroker->center = *to;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_ConicTo( FT_Stroker stroker,
+ FT_Vector* control,
+ FT_Vector* to )
+ {
+ FT_Error error = 0;
+ FT_Vector bez_stack[34];
+ FT_Vector* arc;
+ FT_Vector* limit = bez_stack + 30;
+ FT_Angle start_angle;
+ FT_Bool first_arc = 1;
+
+
+ arc = bez_stack;
+ arc[0] = *to;
+ arc[1] = *control;
+ arc[2] = stroker->center;
+
+ while ( arc >= bez_stack )
+ {
+ FT_Angle angle_in, angle_out;
+
+
+ angle_in = angle_out = 0; /* remove compiler warnings */
+
+ if ( arc < limit &&
+ !ft_conic_is_small_enough( arc, &angle_in, &angle_out ) )
+ {
+ ft_conic_split( arc );
+ arc += 2;
+ continue;
+ }
+
+ if ( first_arc )
+ {
+ first_arc = 0;
+
+ start_angle = angle_in;
+
+ /* process corner if necessary */
+ if ( stroker->first_point )
+ error = ft_stroker_subpath_start( stroker, start_angle );
+ else
+ {
+ stroker->angle_out = start_angle;
+ error = ft_stroker_process_corner( stroker );
+ }
+ }
+
+ /* the arc's angle is small enough; we can add it directly to each */
+ /* border */
+ {
+ FT_Vector ctrl, end;
+ FT_Angle theta, phi, rotate;
+ FT_Fixed length;
+ FT_Int side;
+
+
+ theta = FT_Angle_Diff( angle_in, angle_out ) / 2;
+ phi = angle_in + theta;
+ length = FT_DivFix( stroker->radius, FT_Cos( theta ) );
+
+ for ( side = 0; side <= 1; side++ )
+ {
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ /* compute control point */
+ FT_Vector_From_Polar( &ctrl, length, phi + rotate );
+ ctrl.x += arc[1].x;
+ ctrl.y += arc[1].y;
+
+ /* compute end point */
+ FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+ end.x += arc[0].x;
+ end.y += arc[0].y;
+
+ error = ft_stroke_border_conicto( stroker->borders + side,
+ &ctrl, &end );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ arc -= 2;
+
+ if ( arc < bez_stack )
+ stroker->angle_in = angle_out;
+ }
+
+ stroker->center = *to;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_CubicTo( FT_Stroker stroker,
+ FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to )
+ {
+ FT_Error error = 0;
+ FT_Vector bez_stack[37];
+ FT_Vector* arc;
+ FT_Vector* limit = bez_stack + 32;
+ FT_Angle start_angle;
+ FT_Bool first_arc = 1;
+
+
+ arc = bez_stack;
+ arc[0] = *to;
+ arc[1] = *control2;
+ arc[2] = *control1;
+ arc[3] = stroker->center;
+
+ while ( arc >= bez_stack )
+ {
+ FT_Angle angle_in, angle_mid, angle_out;
+
+
+ /* remove compiler warnings */
+ angle_in = angle_out = angle_mid = 0;
+
+ if ( arc < limit &&
+ !ft_cubic_is_small_enough( arc, &angle_in,
+ &angle_mid, &angle_out ) )
+ {
+ ft_cubic_split( arc );
+ arc += 3;
+ continue;
+ }
+
+ if ( first_arc )
+ {
+ first_arc = 0;
+
+ /* process corner if necessary */
+ start_angle = angle_in;
+
+ if ( stroker->first_point )
+ error = ft_stroker_subpath_start( stroker, start_angle );
+ else
+ {
+ stroker->angle_out = start_angle;
+ error = ft_stroker_process_corner( stroker );
+ }
+ if ( error )
+ goto Exit;
+ }
+
+ /* the arc's angle is small enough; we can add it directly to each */
+ /* border */
+ {
+ FT_Vector ctrl1, ctrl2, end;
+ FT_Angle theta1, phi1, theta2, phi2, rotate;
+ FT_Fixed length1, length2;
+ FT_Int side;
+
+
+ theta1 = ft_pos_abs( angle_mid - angle_in ) / 2;
+ theta2 = ft_pos_abs( angle_out - angle_mid ) / 2;
+ phi1 = (angle_mid + angle_in ) / 2;
+ phi2 = (angle_mid + angle_out ) / 2;
+ length1 = FT_DivFix( stroker->radius, FT_Cos( theta1 ) );
+ length2 = FT_DivFix( stroker->radius, FT_Cos(theta2) );
+
+ for ( side = 0; side <= 1; side++ )
+ {
+ rotate = FT_SIDE_TO_ROTATE( side );
+
+ /* compute control points */
+ FT_Vector_From_Polar( &ctrl1, length1, phi1 + rotate );
+ ctrl1.x += arc[2].x;
+ ctrl1.y += arc[2].y;
+
+ FT_Vector_From_Polar( &ctrl2, length2, phi2 + rotate );
+ ctrl2.x += arc[1].x;
+ ctrl2.y += arc[1].y;
+
+ /* compute end point */
+ FT_Vector_From_Polar( &end, stroker->radius, angle_out + rotate );
+ end.x += arc[0].x;
+ end.y += arc[0].y;
+
+ error = ft_stroke_border_cubicto( stroker->borders + side,
+ &ctrl1, &ctrl2, &end );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ arc -= 3;
+ if ( arc < bez_stack )
+ stroker->angle_in = angle_out;
+ }
+
+ stroker->center = *to;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_BeginSubPath( FT_Stroker stroker,
+ FT_Vector* to,
+ FT_Bool open )
+ {
+ /* We cannot process the first point, because there is not enough */
+ /* information regarding its corner/cap. The latter will be processed */
+ /* in the "end_subpath" routine. */
+ /* */
+ stroker->first_point = 1;
+ stroker->center = *to;
+ stroker->subpath_open = open;
+
+ /* record the subpath start point index for each border */
+ stroker->subpath_start = *to;
+ return 0;
+ }
+
+
+ static FT_Error
+ ft_stroker_add_reverse_left( FT_Stroker stroker,
+ FT_Bool open )
+ {
+ FT_StrokeBorder right = stroker->borders + 0;
+ FT_StrokeBorder left = stroker->borders + 1;
+ FT_Int new_points;
+ FT_Error error = 0;
+
+
+ FT_ASSERT( left->start >= 0 );
+
+ new_points = left->num_points - left->start;
+ if ( new_points > 0 )
+ {
+ error = ft_stroke_border_grow( right, (FT_UInt)new_points );
+ if ( error )
+ goto Exit;
+
+ {
+ FT_Vector* dst_point = right->points + right->num_points;
+ FT_Byte* dst_tag = right->tags + right->num_points;
+ FT_Vector* src_point = left->points + left->num_points - 1;
+ FT_Byte* src_tag = left->tags + left->num_points - 1;
+
+ while ( src_point >= left->points + left->start )
+ {
+ *dst_point = *src_point;
+ *dst_tag = *src_tag;
+
+ if ( open )
+ dst_tag[0] &= ~FT_STROKE_TAG_BEGIN_END;
+ else
+ {
+ FT_Byte ttag = (FT_Byte)( dst_tag[0] & FT_STROKE_TAG_BEGIN_END );
+
+
+ /* switch begin/end tags if necessary */
+ if ( ttag == FT_STROKE_TAG_BEGIN ||
+ ttag == FT_STROKE_TAG_END )
+ dst_tag[0] ^= FT_STROKE_TAG_BEGIN_END;
+
+ }
+
+ src_point--;
+ src_tag--;
+ dst_point++;
+ dst_tag++;
+ }
+ }
+
+ left->num_points = left->start;
+ right->num_points += new_points;
+
+ right->movable = 0;
+ left->movable = 0;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ /* there's a lot of magic in this function! */
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_EndSubPath( FT_Stroker stroker )
+ {
+ FT_Error error = 0;
+
+ if ( stroker->subpath_open )
+ {
+ FT_StrokeBorder right = stroker->borders;
+
+ /* All right, this is an opened path, we need to add a cap between */
+ /* right & left, add the reverse of left, then add a final cap */
+ /* between left & right. */
+ error = ft_stroker_cap( stroker, stroker->angle_in, 0 );
+ if ( error )
+ goto Exit;
+
+ /* add reversed points from "left" to "right" */
+ error = ft_stroker_add_reverse_left( stroker, 1 );
+ if ( error )
+ goto Exit;
+
+ /* now add the final cap */
+ stroker->center = stroker->subpath_start;
+ error = ft_stroker_cap( stroker,
+ stroker->subpath_angle + FT_ANGLE_PI, 0 );
+ if ( error )
+ goto Exit;
+
+ /* Now end the right subpath accordingly. The left one is */
+ /* rewind and doesn't need further processing. */
+ ft_stroke_border_close( right, 0 );
+ }
+ else
+ {
+ FT_Angle turn;
+ FT_Int inside_side;
+
+ /* close the path if needed */
+ if ( stroker->center.x != stroker->subpath_start.x ||
+ stroker->center.y != stroker->subpath_start.y )
+ {
+ error = FT_Stroker_LineTo( stroker, &stroker->subpath_start );
+ if ( error )
+ goto Exit;
+ }
+
+ /* process the corner */
+ stroker->angle_out = stroker->subpath_angle;
+ turn = FT_Angle_Diff( stroker->angle_in,
+ stroker->angle_out );
+
+ /* no specific corner processing is required if the turn is 0 */
+ if ( turn != 0 )
+ {
+ /* when we turn to the right, the inside side is 0 */
+ inside_side = 0;
+
+ /* otherwise, the inside side is 1 */
+ if ( turn < 0 )
+ inside_side = 1;
+
+ error = ft_stroker_inside( stroker, inside_side );
+ if ( error )
+ goto Exit;
+
+ /* process the outside side */
+ error = ft_stroker_outside( stroker, 1 - inside_side );
+ if ( error )
+ goto Exit;
+ }
+
+ /* then end our two subpaths */
+ ft_stroke_border_close( stroker->borders + 0, 1 );
+ ft_stroke_border_close( stroker->borders + 1, 0 );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_GetBorderCounts( FT_Stroker stroker,
+ FT_StrokerBorder border,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_UInt num_points = 0, num_contours = 0;
+ FT_Error error;
+
+
+ if ( !stroker || border > 1 )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ error = ft_stroke_border_get_counts( stroker->borders + border,
+ &num_points, &num_contours );
+ Exit:
+ if ( anum_points )
+ *anum_points = num_points;
+
+ if ( anum_contours )
+ *anum_contours = num_contours;
+
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_GetCounts( FT_Stroker stroker,
+ FT_UInt *anum_points,
+ FT_UInt *anum_contours )
+ {
+ FT_UInt count1, count2, num_points = 0;
+ FT_UInt count3, count4, num_contours = 0;
+ FT_Error error;
+
+
+ error = ft_stroke_border_get_counts( stroker->borders + 0,
+ &count1, &count2 );
+ if ( error )
+ goto Exit;
+
+ error = ft_stroke_border_get_counts( stroker->borders + 1,
+ &count3, &count4 );
+ if ( error )
+ goto Exit;
+
+ num_points = count1 + count3;
+ num_contours = count2 + count4;
+
+ Exit:
+ *anum_points = num_points;
+ *anum_contours = num_contours;
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_ExportBorder( FT_Stroker stroker,
+ FT_StrokerBorder border,
+ FT_Outline* outline )
+ {
+ if ( border == FT_STROKER_BORDER_LEFT ||
+ border == FT_STROKER_BORDER_RIGHT )
+ {
+ FT_StrokeBorder sborder = & stroker->borders[border];
+
+
+ if ( sborder->valid )
+ ft_stroke_border_export( sborder, outline );
+ }
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Stroker_Export( FT_Stroker stroker,
+ FT_Outline* outline )
+ {
+ FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_LEFT, outline );
+ FT_Stroker_ExportBorder( stroker, FT_STROKER_BORDER_RIGHT, outline );
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ /*
+ * The following is very similar to FT_Outline_Decompose, except
+ * that we do support opened paths, and do not scale the outline.
+ */
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stroker_ParseOutline( FT_Stroker stroker,
+ FT_Outline* outline,
+ FT_Bool opened )
+ {
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ FT_Error error;
+
+ FT_Int n; /* index of contour in outline */
+ FT_UInt first; /* index of first point in contour */
+ FT_Int tag; /* current point's state */
+
+
+ if ( !outline || !stroker )
+ return FT_Err_Invalid_Argument;
+
+ FT_Stroker_Rewind( stroker );
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ FT_UInt last; /* index of last point in contour */
+
+
+ last = outline->contours[n];
+ limit = outline->points + last;
+
+ /* skip empty points; we don't stroke these */
+ if ( last <= first )
+ {
+ first = last + 1;
+ continue;
+ }
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* First point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ error = FT_Stroker_BeginSubPath( stroker, &v_start, opened );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ FT_Vector vec;
+
+
+ vec.x = point->x;
+ vec.y = point->y;
+
+ error = FT_Stroker_LineTo( stroker, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = point->x;
+ v_control.y = point->y;
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector vec;
+ FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ vec = point[0];
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ error = FT_Stroker_ConicTo( stroker, &v_control, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = FT_Stroker_ConicTo( stroker, &v_control, &v_middle );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = FT_Stroker_ConicTo( stroker, &v_control, &v_start );
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1 = point[-2];
+ vec2 = point[-1];
+
+ if ( point <= limit )
+ {
+ FT_Vector vec;
+
+
+ vec = point[0];
+
+ error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &vec );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = FT_Stroker_CubicTo( stroker, &vec1, &vec2, &v_start );
+ goto Close;
+ }
+ }
+ }
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ error = FT_Stroker_EndSubPath( stroker );
+ if ( error )
+ goto Exit;
+
+ first = last + 1;
+ }
+
+ return 0;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return FT_Err_Invalid_Outline;
+ }
+
+
+ extern const FT_Glyph_Class ft_outline_glyph_class;
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_Stroke( FT_Glyph *pglyph,
+ FT_Stroker stroker,
+ FT_Bool destroy )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+ FT_Glyph glyph = NULL;
+
+
+ if ( pglyph == NULL )
+ goto Exit;
+
+ glyph = *pglyph;
+ if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
+ goto Exit;
+
+ {
+ FT_Glyph copy;
+
+
+ error = FT_Glyph_Copy( glyph, &copy );
+ if ( error )
+ goto Exit;
+
+ glyph = copy;
+ }
+
+ {
+ FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
+ FT_Outline* outline = &oglyph->outline;
+ FT_UInt num_points, num_contours;
+
+
+ error = FT_Stroker_ParseOutline( stroker, outline, 0 );
+ if ( error )
+ goto Fail;
+
+ (void)FT_Stroker_GetCounts( stroker, &num_points, &num_contours );
+
+ FT_Outline_Done( glyph->library, outline );
+
+ error = FT_Outline_New( glyph->library,
+ num_points, num_contours, outline );
+ if ( error )
+ goto Fail;
+
+ outline->n_points = 0;
+ outline->n_contours = 0;
+
+ FT_Stroker_Export( stroker, outline );
+ }
+
+ if ( destroy )
+ FT_Done_Glyph( *pglyph );
+
+ *pglyph = glyph;
+ goto Exit;
+
+ Fail:
+ FT_Done_Glyph( glyph );
+ glyph = NULL;
+
+ if ( !destroy )
+ *pglyph = NULL;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftstroke.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Glyph_StrokeBorder( FT_Glyph *pglyph,
+ FT_Stroker stroker,
+ FT_Bool inside,
+ FT_Bool destroy )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+ FT_Glyph glyph = NULL;
+
+
+ if ( pglyph == NULL )
+ goto Exit;
+
+ glyph = *pglyph;
+ if ( glyph == NULL || glyph->clazz != &ft_outline_glyph_class )
+ goto Exit;
+
+ {
+ FT_Glyph copy;
+
+
+ error = FT_Glyph_Copy( glyph, &copy );
+ if ( error )
+ goto Exit;
+
+ glyph = copy;
+ }
+
+ {
+ FT_OutlineGlyph oglyph = (FT_OutlineGlyph) glyph;
+ FT_StrokerBorder border;
+ FT_Outline* outline = &oglyph->outline;
+ FT_UInt num_points, num_contours;
+
+
+ border = FT_Outline_GetOutsideBorder( outline );
+ if ( inside )
+ {
+ if ( border == FT_STROKER_BORDER_LEFT )
+ border = FT_STROKER_BORDER_RIGHT;
+ else
+ border = FT_STROKER_BORDER_LEFT;
+ }
+
+ error = FT_Stroker_ParseOutline( stroker, outline, 0 );
+ if ( error )
+ goto Fail;
+
+ (void)FT_Stroker_GetBorderCounts( stroker, border,
+ &num_points, &num_contours );
+
+ FT_Outline_Done( glyph->library, outline );
+
+ error = FT_Outline_New( glyph->library,
+ num_points,
+ num_contours,
+ outline );
+ if ( error )
+ goto Fail;
+
+ outline->n_points = 0;
+ outline->n_contours = 0;
+
+ FT_Stroker_ExportBorder( stroker, border, outline );
+ }
+
+ if ( destroy )
+ FT_Done_Glyph( *pglyph );
+
+ *pglyph = glyph;
+ goto Exit;
+
+ Fail:
+ FT_Done_Glyph( glyph );
+ glyph = NULL;
+
+ if ( !destroy )
+ *pglyph = NULL;
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftsynth.c b/src/base/ftsynth.c
new file mode 100644
index 0000000..ff88ce9
--- /dev/null
+++ b/src/base/ftsynth.c
@@ -0,0 +1,159 @@
+/***************************************************************************/
+/* */
+/* ftsynth.c */
+/* */
+/* FreeType synthesizing code for emboldening and slanting (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_SYNTHESIS_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_OUTLINE_H
+#include FT_BITMAP_H
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** EXPERIMENTAL OBLIQUING SUPPORT ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* documentation is in ftsynth.h */
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_Oblique( FT_GlyphSlot slot )
+ {
+ FT_Matrix transform;
+ FT_Outline* outline = &slot->outline;
+
+
+ /* only oblique outline glyphs */
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE )
+ return;
+
+ /* we don't touch the advance width */
+
+ /* For italic, simply apply a shear transform, with an angle */
+ /* of about 12 degrees. */
+
+ transform.xx = 0x10000L;
+ transform.yx = 0x00000L;
+
+ transform.xy = 0x06000L;
+ transform.yy = 0x10000L;
+
+ FT_Outline_Transform( outline, &transform );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** EXPERIMENTAL EMBOLDENING/OUTLINING SUPPORT ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_GlyphSlot_Own_Bitmap( FT_GlyphSlot slot )
+ {
+ if ( slot && slot->format == FT_GLYPH_FORMAT_BITMAP &&
+ !( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
+ {
+ FT_Bitmap bitmap;
+ FT_Error error;
+
+
+ FT_Bitmap_New( &bitmap );
+ error = FT_Bitmap_Copy( slot->library, &slot->bitmap, &bitmap );
+ if ( error )
+ return error;
+
+ slot->bitmap = bitmap;
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+ }
+
+ return FT_Err_Ok;
+ }
+
+
+ /* documentation is in ftsynth.h */
+
+ FT_EXPORT_DEF( void )
+ FT_GlyphSlot_Embolden( FT_GlyphSlot slot )
+ {
+ FT_Library library = slot->library;
+ FT_Face face = FT_SLOT_FACE( slot );
+ FT_Error error;
+ FT_Pos xstr, ystr;
+
+
+ if ( slot->format != FT_GLYPH_FORMAT_OUTLINE &&
+ slot->format != FT_GLYPH_FORMAT_BITMAP )
+ return;
+
+ /* some reasonable strength */
+ xstr = FT_MulFix( face->units_per_EM,
+ face->size->metrics.y_scale ) / 24;
+ ystr = xstr;
+
+ if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ error = FT_Outline_Embolden( &slot->outline, xstr );
+ /* ignore error */
+
+ /* this is more than enough for most glyphs; if you need accurate */
+ /* values, you have to call FT_Outline_Get_CBox */
+ xstr = xstr * 2;
+ ystr = xstr;
+ }
+ else if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
+ {
+ xstr = FT_PIX_FLOOR( xstr );
+ if ( xstr == 0 )
+ xstr = 1 << 6;
+ ystr = FT_PIX_FLOOR( ystr );
+
+ error = FT_GlyphSlot_Own_Bitmap( slot );
+ if ( error )
+ return;
+
+ error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr );
+ if ( error )
+ return;
+ }
+
+ if ( slot->advance.x )
+ slot->advance.x += xstr;
+
+ if ( slot->advance.y )
+ slot->advance.y += ystr;
+
+ slot->metrics.width += xstr;
+ slot->metrics.height += ystr;
+ slot->metrics.horiBearingY += ystr;
+ slot->metrics.horiAdvance += xstr;
+ slot->metrics.vertBearingX -= xstr / 2;
+ slot->metrics.vertBearingY += ystr;
+ slot->metrics.vertAdvance += ystr;
+
+ if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
+ slot->bitmap_top += ystr >> 6;
+ }
+
+
+/* END */
diff --git a/src/base/ftsystem.c b/src/base/ftsystem.c
new file mode 100644
index 0000000..f61a3ed
--- /dev/null
+++ b/src/base/ftsystem.c
@@ -0,0 +1,301 @@
+/***************************************************************************/
+/* */
+/* ftsystem.c */
+/* */
+/* ANSI-specific FreeType low-level system interface (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file contains the default interface used by FreeType to access */
+ /* low-level, i.e. memory management, i/o access as well as thread */
+ /* synchronisation. It can be replaced by user-specific routines if */
+ /* necessary. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_SYSTEM_H
+#include FT_ERRORS_H
+#include FT_TYPES_H
+
+
+ /*************************************************************************/
+ /* */
+ /* MEMORY MANAGEMENT INTERFACE */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* It is not necessary to do any error checking for the */
+ /* allocation-related functions. This will be done by the higher level */
+ /* routines like ft_mem_alloc() or ft_mem_realloc(). */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_alloc */
+ /* */
+ /* <Description> */
+ /* The memory allocation function. */
+ /* */
+ /* <Input> */
+ /* memory :: A pointer to the memory object. */
+ /* */
+ /* size :: The requested size in bytes. */
+ /* */
+ /* <Return> */
+ /* The address of newly allocated block. */
+ /* */
+ FT_CALLBACK_DEF( void* )
+ ft_alloc( FT_Memory memory,
+ long size )
+ {
+ FT_UNUSED( memory );
+
+ return ft_smalloc( size );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_realloc */
+ /* */
+ /* <Description> */
+ /* The memory reallocation function. */
+ /* */
+ /* <Input> */
+ /* memory :: A pointer to the memory object. */
+ /* */
+ /* cur_size :: The current size of the allocated memory block. */
+ /* */
+ /* new_size :: The newly requested size in bytes. */
+ /* */
+ /* block :: The current address of the block in memory. */
+ /* */
+ /* <Return> */
+ /* The address of the reallocated memory block. */
+ /* */
+ FT_CALLBACK_DEF( void* )
+ ft_realloc( FT_Memory memory,
+ long cur_size,
+ long new_size,
+ void* block )
+ {
+ FT_UNUSED( memory );
+ FT_UNUSED( cur_size );
+
+ return ft_srealloc( block, new_size );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_free */
+ /* */
+ /* <Description> */
+ /* The memory release function. */
+ /* */
+ /* <Input> */
+ /* memory :: A pointer to the memory object. */
+ /* */
+ /* block :: The address of block in memory to be freed. */
+ /* */
+ FT_CALLBACK_DEF( void )
+ ft_free( FT_Memory memory,
+ void* block )
+ {
+ FT_UNUSED( memory );
+
+ ft_sfree( block );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RESOURCE MANAGEMENT INTERFACE */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_io
+
+ /* We use the macro STREAM_FILE for convenience to extract the */
+ /* system-specific stream handle from a given FreeType stream object */
+#define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_ansi_stream_close */
+ /* */
+ /* <Description> */
+ /* The function to close a stream. */
+ /* */
+ /* <Input> */
+ /* stream :: A pointer to the stream object. */
+ /* */
+ FT_CALLBACK_DEF( void )
+ ft_ansi_stream_close( FT_Stream stream )
+ {
+ ft_fclose( STREAM_FILE( stream ) );
+
+ stream->descriptor.pointer = NULL;
+ stream->size = 0;
+ stream->base = 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_ansi_stream_io */
+ /* */
+ /* <Description> */
+ /* The function to open a stream. */
+ /* */
+ /* <Input> */
+ /* stream :: A pointer to the stream object. */
+ /* */
+ /* offset :: The position in the data stream to start reading. */
+ /* */
+ /* buffer :: The address of buffer to store the read data. */
+ /* */
+ /* count :: The number of bytes to read from the stream. */
+ /* */
+ /* <Return> */
+ /* The number of bytes actually read. */
+ /* */
+ FT_CALLBACK_DEF( unsigned long )
+ ft_ansi_stream_io( FT_Stream stream,
+ unsigned long offset,
+ unsigned char* buffer,
+ unsigned long count )
+ {
+ FT_FILE* file;
+
+
+ file = STREAM_FILE( stream );
+
+ ft_fseek( file, offset, SEEK_SET );
+
+ return (unsigned long)ft_fread( buffer, 1, count, file );
+ }
+
+
+ /* documentation is in ftstream.h */
+
+ FT_BASE_DEF( FT_Error )
+ FT_Stream_Open( FT_Stream stream,
+ const char* filepathname )
+ {
+ FT_FILE* file;
+
+
+ if ( !stream )
+ return FT_Err_Invalid_Stream_Handle;
+
+ file = ft_fopen( filepathname, "rb" );
+ if ( !file )
+ {
+ FT_ERROR(( "FT_Stream_Open:" ));
+ FT_ERROR(( " could not open `%s'\n", filepathname ));
+
+ return FT_Err_Cannot_Open_Resource;
+ }
+
+ ft_fseek( file, 0, SEEK_END );
+ stream->size = ft_ftell( file );
+ ft_fseek( file, 0, SEEK_SET );
+
+ stream->descriptor.pointer = file;
+ stream->pathname.pointer = (char*)filepathname;
+ stream->pos = 0;
+
+ stream->read = ft_ansi_stream_io;
+ stream->close = ft_ansi_stream_close;
+
+ FT_TRACE1(( "FT_Stream_Open:" ));
+ FT_TRACE1(( " opened `%s' (%d bytes) successfully\n",
+ filepathname, stream->size ));
+
+ return FT_Err_Ok;
+ }
+
+
+#ifdef FT_DEBUG_MEMORY
+
+ extern FT_Int
+ ft_mem_debug_init( FT_Memory memory );
+
+ extern void
+ ft_mem_debug_done( FT_Memory memory );
+
+#endif
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( FT_Memory )
+ FT_New_Memory( void )
+ {
+ FT_Memory memory;
+
+
+ memory = (FT_Memory)ft_smalloc( sizeof ( *memory ) );
+ if ( memory )
+ {
+ memory->user = 0;
+ memory->alloc = ft_alloc;
+ memory->realloc = ft_realloc;
+ memory->free = ft_free;
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_debug_init( memory );
+#endif
+ }
+
+ return memory;
+ }
+
+
+ /* documentation is in ftobjs.h */
+
+ FT_BASE_DEF( void )
+ FT_Done_Memory( FT_Memory memory )
+ {
+#ifdef FT_DEBUG_MEMORY
+ ft_mem_debug_done( memory );
+#endif
+ memory->free( memory, memory );
+ }
+
+
+/* END */
diff --git a/src/base/fttrigon.c b/src/base/fttrigon.c
new file mode 100644
index 0000000..9f51394
--- /dev/null
+++ b/src/base/fttrigon.c
@@ -0,0 +1,546 @@
+/***************************************************************************/
+/* */
+/* fttrigon.c */
+/* */
+/* FreeType trigonometric functions (body). */
+/* */
+/* Copyright 2001, 2002, 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_TRIGONOMETRY_H
+
+
+ /* the following is 0.2715717684432231 * 2^30 */
+#define FT_TRIG_COSCALE 0x11616E8EUL
+
+ /* this table was generated for FT_PI = 180L << 16, i.e. degrees */
+#define FT_TRIG_MAX_ITERS 23
+
+ static const FT_Fixed
+ ft_trig_arctan_table[24] =
+ {
+ 4157273L, 2949120L, 1740967L, 919879L, 466945L, 234379L, 117304L,
+ 58666L, 29335L, 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+ 57L, 29L, 14L, 7L, 4L, 2L, 1L
+ };
+
+ /* the Cordic shrink factor, multiplied by 2^32 */
+#define FT_TRIG_SCALE 1166391785UL /* 0x4585BA38UL */
+
+
+#ifdef FT_CONFIG_HAS_INT64
+
+ /* multiply a given value by the CORDIC shrink factor */
+ static FT_Fixed
+ ft_trig_downscale( FT_Fixed val )
+ {
+ FT_Fixed s;
+ FT_Int64 v;
+
+
+ s = val;
+ val = ( val >= 0 ) ? val : -val;
+
+ v = ( val * (FT_Int64)FT_TRIG_SCALE ) + 0x100000000UL;
+ val = (FT_Fixed)( v >> 32 );
+
+ return ( s >= 0 ) ? val : -val;
+ }
+
+#else /* !FT_CONFIG_HAS_INT64 */
+
+ /* multiply a given value by the CORDIC shrink factor */
+ static FT_Fixed
+ ft_trig_downscale( FT_Fixed val )
+ {
+ FT_Fixed s;
+ FT_UInt32 v1, v2, k1, k2, hi, lo1, lo2, lo3;
+
+
+ s = val;
+ val = ( val >= 0 ) ? val : -val;
+
+ v1 = (FT_UInt32)val >> 16;
+ v2 = (FT_UInt32)val & 0xFFFFL;
+
+ k1 = FT_TRIG_SCALE >> 16; /* constant */
+ k2 = FT_TRIG_SCALE & 0xFFFFL; /* constant */
+
+ hi = k1 * v1;
+ lo1 = k1 * v2 + k2 * v1; /* can't overflow */
+
+ lo2 = ( k2 * v2 ) >> 16;
+ lo3 = ( lo1 >= lo2 ) ? lo1 : lo2;
+ lo1 += lo2;
+
+ hi += lo1 >> 16;
+ if ( lo1 < lo3 )
+ hi += 0x10000UL;
+
+ val = (FT_Fixed)hi;
+
+ return ( s >= 0 ) ? val : -val;
+ }
+
+#endif /* !FT_CONFIG_HAS_INT64 */
+
+
+ static FT_Int
+ ft_trig_prenorm( FT_Vector* vec )
+ {
+ FT_Fixed x, y, z;
+ FT_Int shift;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ z = ( ( x >= 0 ) ? x : - x ) | ( (y >= 0) ? y : -y );
+ shift = 0;
+
+#if 1
+ /* determine msb bit index in `shift' */
+ if ( z >= ( 1L << 16 ) )
+ {
+ z >>= 16;
+ shift += 16;
+ }
+ if ( z >= ( 1L << 8 ) )
+ {
+ z >>= 8;
+ shift += 8;
+ }
+ if ( z >= ( 1L << 4 ) )
+ {
+ z >>= 4;
+ shift += 4;
+ }
+ if ( z >= ( 1L << 2 ) )
+ {
+ z >>= 2;
+ shift += 2;
+ }
+ if ( z >= ( 1L << 1 ) )
+ {
+ z >>= 1;
+ shift += 1;
+ }
+
+ if ( shift <= 27 )
+ {
+ shift = 27 - shift;
+ vec->x = x << shift;
+ vec->y = y << shift;
+ }
+ else
+ {
+ shift -= 27;
+ vec->x = x >> shift;
+ vec->y = y >> shift;
+ shift = -shift;
+ }
+
+#else /* 0 */
+
+ if ( z < ( 1L << 27 ) )
+ {
+ do
+ {
+ shift++;
+ z <<= 1;
+ } while ( z < ( 1L << 27 ) );
+ vec->x = x << shift;
+ vec->y = y << shift;
+ }
+ else if ( z > ( 1L << 28 ) )
+ {
+ do
+ {
+ shift++;
+ z >>= 1;
+ } while ( z > ( 1L << 28 ) );
+
+ vec->x = x >> shift;
+ vec->y = y >> shift;
+ shift = -shift;
+ }
+
+#endif /* 0 */
+
+ return shift;
+ }
+
+
+ static void
+ ft_trig_pseudo_rotate( FT_Vector* vec,
+ FT_Angle theta )
+ {
+ FT_Int i;
+ FT_Fixed x, y, xtemp;
+ const FT_Fixed *arctanptr;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ /* Get angle between -90 and 90 degrees */
+ while ( theta <= -FT_ANGLE_PI2 )
+ {
+ x = -x;
+ y = -y;
+ theta += FT_ANGLE_PI;
+ }
+
+ while ( theta > FT_ANGLE_PI2 )
+ {
+ x = -x;
+ y = -y;
+ theta -= FT_ANGLE_PI;
+ }
+
+ /* Initial pseudorotation, with left shift */
+ arctanptr = ft_trig_arctan_table;
+
+ if ( theta < 0 )
+ {
+ xtemp = x + ( y << 1 );
+ y = y - ( x << 1 );
+ x = xtemp;
+ theta += *arctanptr++;
+ }
+ else
+ {
+ xtemp = x - ( y << 1 );
+ y = y + ( x << 1 );
+ x = xtemp;
+ theta -= *arctanptr++;
+ }
+
+ /* Subsequent pseudorotations, with right shifts */
+ i = 0;
+ do
+ {
+ if ( theta < 0 )
+ {
+ xtemp = x + ( y >> i );
+ y = y - ( x >> i );
+ x = xtemp;
+ theta += *arctanptr++;
+ }
+ else
+ {
+ xtemp = x - ( y >> i );
+ y = y + ( x >> i );
+ x = xtemp;
+ theta -= *arctanptr++;
+ }
+ } while ( ++i < FT_TRIG_MAX_ITERS );
+
+ vec->x = x;
+ vec->y = y;
+ }
+
+
+ static void
+ ft_trig_pseudo_polarize( FT_Vector* vec )
+ {
+ FT_Fixed theta;
+ FT_Fixed yi, i;
+ FT_Fixed x, y;
+ const FT_Fixed *arctanptr;
+
+
+ x = vec->x;
+ y = vec->y;
+
+ /* Get the vector into the right half plane */
+ theta = 0;
+ if ( x < 0 )
+ {
+ x = -x;
+ y = -y;
+ theta = 2 * FT_ANGLE_PI2;
+ }
+
+ if ( y > 0 )
+ theta = - theta;
+
+ arctanptr = ft_trig_arctan_table;
+
+ if ( y < 0 )
+ {
+ /* Rotate positive */
+ yi = y + ( x << 1 );
+ x = x - ( y << 1 );
+ y = yi;
+ theta -= *arctanptr++; /* Subtract angle */
+ }
+ else
+ {
+ /* Rotate negative */
+ yi = y - ( x << 1 );
+ x = x + ( y << 1 );
+ y = yi;
+ theta += *arctanptr++; /* Add angle */
+ }
+
+ i = 0;
+ do
+ {
+ if ( y < 0 )
+ {
+ /* Rotate positive */
+ yi = y + ( x >> i );
+ x = x - ( y >> i );
+ y = yi;
+ theta -= *arctanptr++;
+ }
+ else
+ {
+ /* Rotate negative */
+ yi = y - ( x >> i );
+ x = x + ( y >> i );
+ y = yi;
+ theta += *arctanptr++;
+ }
+ } while ( ++i < FT_TRIG_MAX_ITERS );
+
+ /* round theta */
+ if ( theta >= 0 )
+ theta = FT_PAD_ROUND( theta, 32 );
+ else
+ theta = -FT_PAD_ROUND( -theta, 32 );
+
+ vec->x = x;
+ vec->y = theta;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Cos( FT_Angle angle )
+ {
+ FT_Vector v;
+
+
+ v.x = FT_TRIG_COSCALE >> 2;
+ v.y = 0;
+ ft_trig_pseudo_rotate( &v, angle );
+
+ return v.x / ( 1 << 12 );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Sin( FT_Angle angle )
+ {
+ return FT_Cos( FT_ANGLE_PI2 - angle );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Tan( FT_Angle angle )
+ {
+ FT_Vector v;
+
+
+ v.x = FT_TRIG_COSCALE >> 2;
+ v.y = 0;
+ ft_trig_pseudo_rotate( &v, angle );
+
+ return FT_DivFix( v.y, v.x );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Angle )
+ FT_Atan2( FT_Fixed dx,
+ FT_Fixed dy )
+ {
+ FT_Vector v;
+
+
+ if ( dx == 0 && dy == 0 )
+ return 0;
+
+ v.x = dx;
+ v.y = dy;
+ ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ return v.y;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Unit( FT_Vector* vec,
+ FT_Angle angle )
+ {
+ vec->x = FT_TRIG_COSCALE >> 2;
+ vec->y = 0;
+ ft_trig_pseudo_rotate( vec, angle );
+ vec->x >>= 12;
+ vec->y >>= 12;
+ }
+
+
+ /* these macros return 0 for positive numbers,
+ and -1 for negative ones */
+#define FT_SIGN_LONG( x ) ( (x) >> ( FT_SIZEOF_LONG * 8 - 1 ) )
+#define FT_SIGN_INT( x ) ( (x) >> ( FT_SIZEOF_INT * 8 - 1 ) )
+#define FT_SIGN_INT32( x ) ( (x) >> 31 )
+#define FT_SIGN_INT16( x ) ( (x) >> 15 )
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Rotate( FT_Vector* vec,
+ FT_Angle angle )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ v.x = vec->x;
+ v.y = vec->y;
+
+ if ( angle && ( v.x != 0 || v.y != 0 ) )
+ {
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_rotate( &v, angle );
+ v.x = ft_trig_downscale( v.x );
+ v.y = ft_trig_downscale( v.y );
+
+ if ( shift > 0 )
+ {
+ FT_Int32 half = 1L << ( shift - 1 );
+
+
+ vec->x = ( v.x + half + FT_SIGN_LONG( v.x ) ) >> shift;
+ vec->y = ( v.y + half + FT_SIGN_LONG( v.y ) ) >> shift;
+ }
+ else
+ {
+ shift = -shift;
+ vec->x = v.x << shift;
+ vec->y = v.y << shift;
+ }
+ }
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Fixed )
+ FT_Vector_Length( FT_Vector* vec )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ v = *vec;
+
+ /* handle trivial cases */
+ if ( v.x == 0 )
+ {
+ return ( v.y >= 0 ) ? v.y : -v.y;
+ }
+ else if ( v.y == 0 )
+ {
+ return ( v.x >= 0 ) ? v.x : -v.x;
+ }
+
+ /* general case */
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ v.x = ft_trig_downscale( v.x );
+
+ if ( shift > 0 )
+ return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift;
+
+ return v.x << -shift;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_Polarize( FT_Vector* vec,
+ FT_Fixed *length,
+ FT_Angle *angle )
+ {
+ FT_Int shift;
+ FT_Vector v;
+
+
+ v = *vec;
+
+ if ( v.x == 0 && v.y == 0 )
+ return;
+
+ shift = ft_trig_prenorm( &v );
+ ft_trig_pseudo_polarize( &v );
+
+ v.x = ft_trig_downscale( v.x );
+
+ *length = ( shift >= 0 ) ? ( v.x >> shift ) : ( v.x << -shift );
+ *angle = v.y;
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( void )
+ FT_Vector_From_Polar( FT_Vector* vec,
+ FT_Fixed length,
+ FT_Angle angle )
+ {
+ vec->x = length;
+ vec->y = 0;
+
+ FT_Vector_Rotate( vec, angle );
+ }
+
+
+ /* documentation is in fttrigon.h */
+
+ FT_EXPORT_DEF( FT_Angle )
+ FT_Angle_Diff( FT_Angle angle1,
+ FT_Angle angle2 )
+ {
+ FT_Angle delta = angle2 - angle1;
+
+
+ delta %= FT_ANGLE_2PI;
+ if ( delta < 0 )
+ delta += FT_ANGLE_2PI;
+
+ if ( delta > FT_ANGLE_PI )
+ delta -= FT_ANGLE_2PI;
+
+ return delta;
+ }
+
+
+/* END */
diff --git a/src/base/fttype1.c b/src/base/fttype1.c
new file mode 100644
index 0000000..3975584
--- /dev/null
+++ b/src/base/fttype1.c
@@ -0,0 +1,94 @@
+/***************************************************************************/
+/* */
+/* fttype1.c */
+/* */
+/* FreeType utility file for PS names support (body). */
+/* */
+/* Copyright 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_SERVICE_H
+#include FT_SERVICE_POSTSCRIPT_INFO_H
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PS_Font_Info( FT_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+
+
+ if ( face )
+ {
+ FT_Service_PsInfo service = NULL;
+
+
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_info )
+ error = service->ps_get_font_info( face, afont_info );
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Int )
+ FT_Has_PS_Glyph_Names( FT_Face face )
+ {
+ FT_Int result = 0;
+ FT_Service_PsInfo service = NULL;
+
+
+ if ( face )
+ {
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_has_glyph_names )
+ result = service->ps_has_glyph_names( face );
+ }
+
+ return result;
+ }
+
+
+ /* documentation is in t1tables.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_PS_Font_Private( FT_Face face,
+ PS_PrivateRec* afont_private )
+ {
+ FT_Error error = FT_Err_Invalid_Argument;
+
+
+ if ( face )
+ {
+ FT_Service_PsInfo service = NULL;
+
+
+ FT_FACE_FIND_SERVICE( face, service, POSTSCRIPT_INFO );
+
+ if ( service && service->ps_get_font_private )
+ error = service->ps_get_font_private( face, afont_private );
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftutil.c b/src/base/ftutil.c
new file mode 100644
index 0000000..5f77be5
--- /dev/null
+++ b/src/base/ftutil.c
@@ -0,0 +1,501 @@
+/***************************************************************************/
+/* */
+/* ftutil.c */
+/* */
+/* FreeType utility file for memory and list management (body). */
+/* */
+/* Copyright 2002, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_LIST_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_memory
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** M E M O R Y M A N A G E M E N T *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_alloc( FT_Memory memory,
+ FT_Long size,
+ FT_Error *p_error )
+ {
+ FT_Error error;
+ FT_Pointer block = ft_mem_qalloc( memory, size, &error );
+
+ if ( !error && size > 0 )
+ FT_MEM_ZERO( block, size );
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_qalloc( FT_Memory memory,
+ FT_Long size,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_Pointer block = NULL;
+
+
+ if ( size > 0 )
+ {
+ block = memory->alloc( memory, size );
+ if ( block == NULL )
+ error = FT_Err_Out_Of_Memory;
+ }
+ else if ( size < 0 )
+ {
+ /* may help catch/prevent security issues */
+ error = FT_Err_Invalid_Argument;
+ }
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_realloc( FT_Memory memory,
+ FT_Long item_size,
+ FT_Long cur_count,
+ FT_Long new_count,
+ void* block,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+
+ block = ft_mem_qrealloc( memory, item_size,
+ cur_count, new_count, block, &error );
+ if ( !error && new_count > cur_count )
+ FT_MEM_ZERO( (char*)block + cur_count * item_size,
+ ( new_count - cur_count ) * item_size );
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_qrealloc( FT_Memory memory,
+ FT_Long item_size,
+ FT_Long cur_count,
+ FT_Long new_count,
+ void* block,
+ FT_Error *p_error )
+ {
+ FT_Error error = FT_Err_Ok;
+
+
+ /* Note that we now accept `item_size == 0' as a valid parameter, in
+ * order to cover very weird cases where an ALLOC_MULT macro would be
+ * called.
+ */
+ if ( cur_count < 0 || new_count < 0 || item_size < 0 )
+ {
+ /* may help catch/prevent nasty security issues */
+ error = FT_Err_Invalid_Argument;
+ }
+ else if ( new_count == 0 || item_size == 0 )
+ {
+ ft_mem_free( memory, block );
+ block = NULL;
+ }
+ else if ( new_count > FT_INT_MAX/item_size )
+ {
+ error = FT_Err_Array_Too_Large;
+ }
+ else if ( cur_count == 0 )
+ {
+ FT_ASSERT( block == NULL );
+
+ block = ft_mem_alloc( memory, new_count*item_size, &error );
+ }
+ else
+ {
+ FT_Pointer block2;
+ FT_Long cur_size = cur_count*item_size;
+ FT_Long new_size = new_count*item_size;
+
+
+ block2 = memory->realloc( memory, cur_size, new_size, block );
+ if ( block2 == NULL )
+ error = FT_Err_Out_Of_Memory;
+ else
+ block = block2;
+ }
+
+ *p_error = error;
+ return block;
+ }
+
+
+ FT_BASE_DEF( void )
+ ft_mem_free( FT_Memory memory,
+ const void *P )
+ {
+ if ( P )
+ memory->free( memory, (void*)P );
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_dup( FT_Memory memory,
+ const void* address,
+ FT_ULong size,
+ FT_Error *p_error )
+ {
+ FT_Error error;
+ FT_Pointer p = ft_mem_qalloc( memory, size, &error );
+
+
+ if ( !error && address )
+ ft_memcpy( p, address, size );
+
+ *p_error = error;
+ return p;
+ }
+
+
+ FT_BASE_DEF( FT_Pointer )
+ ft_mem_strdup( FT_Memory memory,
+ const char* str,
+ FT_Error *p_error )
+ {
+ FT_ULong len = str ? (FT_ULong)ft_strlen( str ) + 1
+ : 0;
+
+
+ return ft_mem_dup( memory, str, len, p_error );
+ }
+
+
+ FT_BASE_DEF( FT_Int )
+ ft_mem_strcpyn( char* dst,
+ const char* src,
+ FT_ULong size )
+ {
+ while ( size > 1 && *src != 0 )
+ {
+ *dst++ = *src++;
+ size--;
+ }
+
+ *dst = 0; /* always zero-terminate */
+
+ return *src != 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** *****/
+ /***** D O U B L Y L I N K E D L I S T S *****/
+ /***** *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_list
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( FT_ListNode )
+ FT_List_Find( FT_List list,
+ void* data )
+ {
+ FT_ListNode cur;
+
+
+ cur = list->head;
+ while ( cur )
+ {
+ if ( cur->data == data )
+ return cur;
+
+ cur = cur->next;
+ }
+
+ return (FT_ListNode)0;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Add( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before = list->tail;
+
+
+ node->next = 0;
+ node->prev = before;
+
+ if ( before )
+ before->next = node;
+ else
+ list->head = node;
+
+ list->tail = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Insert( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode after = list->head;
+
+
+ node->next = after;
+ node->prev = 0;
+
+ if ( !after )
+ list->tail = node;
+ else
+ after->prev = node;
+
+ list->head = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Remove( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before, after;
+
+
+ before = node->prev;
+ after = node->next;
+
+ if ( before )
+ before->next = after;
+ else
+ list->head = after;
+
+ if ( after )
+ after->prev = before;
+ else
+ list->tail = before;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Up( FT_List list,
+ FT_ListNode node )
+ {
+ FT_ListNode before, after;
+
+
+ before = node->prev;
+ after = node->next;
+
+ /* check whether we are already on top of the list */
+ if ( !before )
+ return;
+
+ before->next = after;
+
+ if ( after )
+ after->prev = before;
+ else
+ list->tail = before;
+
+ node->prev = 0;
+ node->next = list->head;
+ list->head->prev = node;
+ list->head = node;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_List_Iterate( FT_List list,
+ FT_List_Iterator iterator,
+ void* user )
+ {
+ FT_ListNode cur = list->head;
+ FT_Error error = FT_Err_Ok;
+
+
+ while ( cur )
+ {
+ FT_ListNode next = cur->next;
+
+
+ error = iterator( cur, user );
+ if ( error )
+ break;
+
+ cur = next;
+ }
+
+ return error;
+ }
+
+
+ /* documentation is in ftlist.h */
+
+ FT_EXPORT_DEF( void )
+ FT_List_Finalize( FT_List list,
+ FT_List_Destructor destroy,
+ FT_Memory memory,
+ void* user )
+ {
+ FT_ListNode cur;
+
+
+ cur = list->head;
+ while ( cur )
+ {
+ FT_ListNode next = cur->next;
+ void* data = cur->data;
+
+
+ if ( destroy )
+ destroy( memory, data, user );
+
+ FT_FREE( cur );
+ cur = next;
+ }
+
+ list->head = 0;
+ list->tail = 0;
+ }
+
+
+ FT_BASE_DEF( FT_UInt32 )
+ ft_highpow2( FT_UInt32 value )
+ {
+ FT_UInt32 value2;
+
+
+ /*
+ * We simply clear the lowest bit in each iteration. When
+ * we reach 0, we know that the previous value was our result.
+ */
+ for ( ;; )
+ {
+ value2 = value & (value - 1); /* clear lowest bit */
+ if ( value2 == 0 )
+ break;
+
+ value = value2;
+ }
+ return value;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_BASE_DEF( FT_Error )
+ FT_Alloc( FT_Memory memory,
+ FT_Long size,
+ void* *P )
+ {
+ FT_Error error;
+
+
+ (void)FT_ALLOC( *P, size );
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_QAlloc( FT_Memory memory,
+ FT_Long size,
+ void* *p )
+ {
+ FT_Error error;
+
+
+ (void)FT_QALLOC( *p, size );
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_Realloc( FT_Memory memory,
+ FT_Long current,
+ FT_Long size,
+ void* *P )
+ {
+ FT_Error error;
+
+
+ (void)FT_REALLOC( *P, current, size );
+ return error;
+ }
+
+
+ FT_BASE_DEF( FT_Error )
+ FT_QRealloc( FT_Memory memory,
+ FT_Long current,
+ FT_Long size,
+ void* *p )
+ {
+ FT_Error error;
+
+
+ (void)FT_QREALLOC( *p, current, size );
+ return error;
+ }
+
+
+ FT_BASE_DEF( void )
+ FT_Free( FT_Memory memory,
+ void* *P )
+ {
+ if ( *P )
+ FT_MEM_FREE( *P );
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+/* END */
diff --git a/src/base/ftwinfnt.c b/src/base/ftwinfnt.c
new file mode 100644
index 0000000..bc2e90e
--- /dev/null
+++ b/src/base/ftwinfnt.c
@@ -0,0 +1,51 @@
+/***************************************************************************/
+/* */
+/* ftwinfnt.c */
+/* */
+/* FreeType API for accessing Windows FNT specific info (body). */
+/* */
+/* Copyright 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_WINFONTS_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_WINFNT_H
+
+
+ /* documentation is in ftwinfnt.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Get_WinFNT_Header( FT_Face face,
+ FT_WinFNT_HeaderRec *header )
+ {
+ FT_Service_WinFnt service;
+ FT_Error error;
+
+
+ error = FT_Err_Invalid_Argument;
+
+ if ( face != NULL )
+ {
+ FT_FACE_LOOKUP_SERVICE( face, service, WINFNT );
+
+ if ( service != NULL )
+ {
+ error = service->get_header( face, header );
+ }
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/base/ftxf86.c b/src/base/ftxf86.c
new file mode 100644
index 0000000..a4bf767
--- /dev/null
+++ b/src/base/ftxf86.c
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* ftxf86.c */
+/* */
+/* FreeType utility file for X11 support (body). */
+/* */
+/* Copyright 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_XFREE86_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_XFREE86_NAME_H
+
+
+ /* documentation is in ftxf86.h */
+
+ FT_EXPORT_DEF( const char* )
+ FT_Get_X11_Font_Format( FT_Face face )
+ {
+ const char* result = NULL;
+
+
+ if ( face )
+ FT_FACE_FIND_SERVICE( face, result, XF86_NAME );
+
+ return result;
+ }
+
+
+/* END */
diff --git a/src/base/rules.mk b/src/base/rules.mk
new file mode 100644
index 0000000..d6e4412
--- /dev/null
+++ b/src/base/rules.mk
@@ -0,0 +1,90 @@
+#
+# FreeType 2 base layer configuration rules
+#
+
+
+# Copyright 1996-2000, 2002, 2003, 2004, 2005, 2006, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# It sets the following variables which are used by the master Makefile
+# after the call:
+#
+# BASE_OBJ_S: The single-object base layer.
+# BASE_OBJ_M: A list of all objects for a multiple-objects build.
+# BASE_EXT_OBJ: A list of base layer extensions, i.e., components found
+# in `freetype/src/base' which are not compiled within the
+# base layer proper.
+#
+# BASE_H is defined in freetype.mk to simplify the dependency rules.
+
+
+BASE_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(SRC_DIR)/base)
+
+
+# Base layer sources
+#
+# ftsystem, ftinit, and ftdebug are handled by freetype.mk
+#
+# All files listed here should be included in `ftbase.c' (for a `single'
+# build).
+#
+BASE_SRC := $(BASE_DIR)/ftcalc.c \
+ $(BASE_DIR)/ftdbgmem.c \
+ $(BASE_DIR)/ftgloadr.c \
+ $(BASE_DIR)/ftnames.c \
+ $(BASE_DIR)/ftobjs.c \
+ $(BASE_DIR)/ftoutln.c \
+ $(BASE_DIR)/ftrfork.c \
+ $(BASE_DIR)/ftstream.c \
+ $(BASE_DIR)/fttrigon.c \
+ $(BASE_DIR)/ftutil.c
+
+# Base layer `extensions' sources
+#
+# An extension is added to the library file as a separate object. It is
+# then linked to the final executable only if one of its symbols is used by
+# the application.
+#
+BASE_EXT_SRC := $(patsubst %,$(BASE_DIR)/%,$(BASE_EXTENSIONS))
+
+# Default extensions objects
+#
+BASE_EXT_OBJ := $(BASE_EXT_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+
+
+# Base layer object(s)
+#
+# BASE_OBJ_M is used during `multi' builds (each base source file compiles
+# to a single object file).
+#
+# BASE_OBJ_S is used during `single' builds (the whole base layer is
+# compiled as a single object file using ftbase.c).
+#
+BASE_OBJ_M := $(BASE_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+BASE_OBJ_S := $(OBJ_DIR)/ftbase.$O
+
+# Base layer root source file for single build
+#
+BASE_SRC_S := $(BASE_DIR)/ftbase.c
+
+
+# Base layer - single object build
+#
+$(BASE_OBJ_S): $(BASE_SRC_S) $(BASE_SRC) $(FREETYPE_H)
+ $(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BASE_SRC_S))
+
+
+# Multiple objects build + extensions
+#
+$(OBJ_DIR)/%.$O: $(BASE_DIR)/%.c $(FREETYPE_H)
+ $(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# EOF
diff --git a/src/bdf/Jamfile b/src/bdf/Jamfile
new file mode 100644
index 0000000..da23ccd
--- /dev/null
+++ b/src/bdf/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/bdf Jamfile
+#
+# Copyright 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) bdf ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = bdfdrivr bdflib ;
+ }
+ else
+ {
+ _sources = bdf ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/bdf Jamfile
diff --git a/src/bdf/README b/src/bdf/README
new file mode 100644
index 0000000..e3f2ae3
--- /dev/null
+++ b/src/bdf/README
@@ -0,0 +1,148 @@
+ FreeType font driver for BDF fonts
+
+ Francesco Zappa Nardelli
+ <francesco.zappa.nardelli@ens.fr>
+
+
+Introduction
+************
+
+BDF (Bitmap Distribution Format) is a bitmap font format defined by Adobe,
+which is intended to be easily understood by both humans and computers.
+This code implements a BDF driver for the FreeType library, following the
+Adobe Specification V 2.2. The specification of the BDF font format is
+available from Adobe's web site:
+
+ http://partners.adobe.com/asn/developer/PDFS/TN/5005.BDF_Spec.pdf
+
+Many good bitmap fonts in bdf format come with XFree86 (www.XFree86.org).
+They do not define vertical metrics, because the X Consortium BDF
+specification has removed them.
+
+
+Encodings
+*********
+
+The variety of encodings that accompanies bdf fonts appears to encompass the
+small set defined in freetype.h. On the other hand, two properties that
+specify encoding and registry are usually defined in bdf fonts.
+
+I decided to make these two properties directly accessible, leaving to the
+client application the work of interpreting them. For instance:
+
+
+ #include FT_INTERNAL_BDF_TYPES_H
+
+ FT_Face face;
+ BDF_Public_Face bdfface;
+
+
+ FT_New_Face( library, ..., &face );
+
+ bdfface = (BDF_Public_Face)face;
+
+ if ( ( bdfface->charset_registry == "ISO10646" ) &&
+ ( bdfface->charset_encoding == "1" ) )
+ [..]
+
+
+Thus the driver always exports `ft_encoding_none' as face->charmap.encoding.
+FT_Get_Char_Index's behavior is unmodified, that is, it converts the ULong
+value given as argument into the corresponding glyph number.
+
+If the two properties are not available, Adobe Standard Encoding should be
+assumed.
+
+
+Anti-Aliased Bitmaps
+********************
+
+The driver supports an extension to the BDF format as used in Mark Leisher's
+xmbdfed bitmap font editor. Microsoft's SBIT tool expects bitmap fonts in
+that format for adding anti-aliased them to TrueType fonts. It introduces a
+fourth field to the `SIZE' keyword which gives the bpp value (bits per
+pixel) of the glyph data in the font. Possible values are 1 (the default),
+2 (four gray levels), 4 (16 gray levels), and 8 (256 gray levels). The
+driver returns either a bitmap with 1 bit per pixel or a pixmap with 8bits
+per pixel (using 4, 16, and 256 gray levels, respectively).
+
+
+Known problems
+**************
+
+- A font is entirely loaded into memory. Obviously, this is not the Right
+ Thing(TM). If you have big fonts I suggest you convert them into PCF
+ format (using the bdftopcf utility): the PCF font drive of FreeType can
+ perform incremental glyph loading.
+
+When I have some time, I will implement on-demand glyph parsing.
+
+- Except for encodings properties, client applications have no visibility of
+ the PCF_Face object. This means that applications cannot directly access
+ font tables and must trust FreeType.
+
+- Currently, glyph names are ignored.
+
+ I plan to give full visibility of the BDF_Face object in an upcoming
+ revision of the driver, thus implementing also glyph names.
+
+- As I have never seen a BDF font that defines vertical metrics, vertical
+ metrics are (parsed and) discarded. If you own a BDF font that defines
+ vertical metrics, please let me know (I will implement them in 5-10
+ minutes).
+
+
+License
+*******
+
+Copyright (C) 2001-2002 by Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*** Portions of the driver (that is, bdflib.c and bdf.h):
+
+Copyright 2000 Computing Research Labs, New Mexico State University
+Copyright 2001-2002 Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Credits
+*******
+
+This driver is based on excellent Mark Leisher's bdf library. If you
+find something good in this driver you should probably thank him, not
+me.
diff --git a/src/bdf/bdf.c b/src/bdf/bdf.c
new file mode 100644
index 0000000..f95fb76
--- /dev/null
+++ b/src/bdf/bdf.c
@@ -0,0 +1,34 @@
+/* bdf.c
+
+ FreeType font driver for bdf files
+
+ Copyright (C) 2001, 2002 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "bdflib.c"
+#include "bdfdrivr.c"
+
+
+/* END */
diff --git a/src/bdf/bdf.h b/src/bdf/bdf.h
new file mode 100644
index 0000000..1b64426
--- /dev/null
+++ b/src/bdf/bdf.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2000 Computing Research Labs, New Mexico State University
+ * Copyright 2001, 2002, 2003, 2004 Francesco Zappa Nardelli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#ifndef __BDF_H__
+#define __BDF_H__
+
+
+/*
+ * Based on bdf.h,v 1.16 2000/03/16 20:08:51 mleisher
+ */
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+
+/* Imported from bdfP.h */
+
+#define _bdf_glyph_modified( map, e ) \
+ ( (map)[(e) >> 5] & ( 1 << ( (e) & 31 ) ) )
+#define _bdf_set_glyph_modified( map, e ) \
+ ( (map)[(e) >> 5] |= ( 1 << ( (e) & 31 ) ) )
+#define _bdf_clear_glyph_modified( map, e ) \
+ ( (map)[(e) >> 5] &= ~( 1 << ( (e) & 31 ) ) )
+
+/* end of bdfP.h */
+
+
+ /*************************************************************************/
+ /* */
+ /* BDF font options macros and types. */
+ /* */
+ /*************************************************************************/
+
+
+#define BDF_CORRECT_METRICS 0x01 /* Correct invalid metrics when loading. */
+#define BDF_KEEP_COMMENTS 0x02 /* Preserve the font comments. */
+#define BDF_KEEP_UNENCODED 0x04 /* Keep the unencoded glyphs. */
+#define BDF_PROPORTIONAL 0x08 /* Font has proportional spacing. */
+#define BDF_MONOWIDTH 0x10 /* Font has mono width. */
+#define BDF_CHARCELL 0x20 /* Font has charcell spacing. */
+
+#define BDF_ALL_SPACING ( BDF_PROPORTIONAL | \
+ BDF_MONOWIDTH | \
+ BDF_CHARCELL )
+
+#define BDF_DEFAULT_LOAD_OPTIONS ( BDF_CORRECT_METRICS | \
+ BDF_KEEP_COMMENTS | \
+ BDF_KEEP_UNENCODED | \
+ BDF_PROPORTIONAL )
+
+
+ typedef struct bdf_options_t_
+ {
+ int correct_metrics;
+ int keep_unencoded;
+ int keep_comments;
+ int font_spacing;
+
+ } bdf_options_t;
+
+
+ /* Callback function type for unknown configuration options. */
+ typedef int
+ (*bdf_options_callback_t)( bdf_options_t* opts,
+ char** params,
+ unsigned long nparams,
+ void* client_data );
+
+
+ /*************************************************************************/
+ /* */
+ /* BDF font property macros and types. */
+ /* */
+ /*************************************************************************/
+
+
+#define BDF_ATOM 1
+#define BDF_INTEGER 2
+#define BDF_CARDINAL 3
+
+
+ /* This structure represents a particular property of a font. */
+ /* There are a set of defaults and each font has their own. */
+ typedef struct bdf_property_t_
+ {
+ char* name; /* Name of the property. */
+ int format; /* Format of the property. */
+ int builtin; /* A builtin property. */
+ union
+ {
+ char* atom;
+ long int32;
+ unsigned long card32;
+
+ } value; /* Value of the property. */
+
+ } bdf_property_t;
+
+
+ /*************************************************************************/
+ /* */
+ /* BDF font metric and glyph types. */
+ /* */
+ /*************************************************************************/
+
+
+ typedef struct bdf_bbx_t_
+ {
+ unsigned short width;
+ unsigned short height;
+
+ short x_offset;
+ short y_offset;
+
+ short ascent;
+ short descent;
+
+ } bdf_bbx_t;
+
+
+ typedef struct bdf_glyph_t_
+ {
+ char* name; /* Glyph name. */
+ long encoding; /* Glyph encoding. */
+ unsigned short swidth; /* Scalable width. */
+ unsigned short dwidth; /* Device width. */
+ bdf_bbx_t bbx; /* Glyph bounding box. */
+ unsigned char* bitmap; /* Glyph bitmap. */
+ unsigned long bpr; /* Number of bytes used per row. */
+ unsigned short bytes; /* Number of bytes used for the bitmap. */
+
+ } bdf_glyph_t;
+
+
+ typedef struct _hashnode_
+ {
+ const char* key;
+ void* data;
+
+ } _hashnode, *hashnode;
+
+
+ typedef struct hashtable_
+ {
+ int limit;
+ int size;
+ int used;
+ hashnode* table;
+
+ } hashtable;
+
+
+ typedef struct bdf_glyphlist_t_
+ {
+ unsigned short pad; /* Pad to 4-byte boundary. */
+ unsigned short bpp; /* Bits per pixel. */
+ long start; /* Beginning encoding value of glyphs. */
+ long end; /* Ending encoding value of glyphs. */
+ bdf_glyph_t* glyphs; /* Glyphs themselves. */
+ unsigned long glyphs_size; /* Glyph structures allocated. */
+ unsigned long glyphs_used; /* Glyph structures used. */
+ bdf_bbx_t bbx; /* Overall bounding box of glyphs. */
+
+ } bdf_glyphlist_t;
+
+
+ typedef struct bdf_font_t_
+ {
+ char* name; /* Name of the font. */
+ bdf_bbx_t bbx; /* Font bounding box. */
+
+ long point_size; /* Point size of the font. */
+ unsigned long resolution_x; /* Font horizontal resolution. */
+ unsigned long resolution_y; /* Font vertical resolution. */
+
+ int spacing; /* Font spacing value. */
+
+ unsigned short monowidth; /* Logical width for monowidth font. */
+
+ long default_char; /* Encoding of the default glyph. */
+
+ long font_ascent; /* Font ascent. */
+ long font_descent; /* Font descent. */
+
+ unsigned long glyphs_size; /* Glyph structures allocated. */
+ unsigned long glyphs_used; /* Glyph structures used. */
+ bdf_glyph_t* glyphs; /* Glyphs themselves. */
+
+ unsigned long unencoded_size; /* Unencoded glyph struct. allocated. */
+ unsigned long unencoded_used; /* Unencoded glyph struct. used. */
+ bdf_glyph_t* unencoded; /* Unencoded glyphs themselves. */
+
+ unsigned long props_size; /* Font properties allocated. */
+ unsigned long props_used; /* Font properties used. */
+ bdf_property_t* props; /* Font properties themselves. */
+
+ char* comments; /* Font comments. */
+ unsigned long comments_len; /* Length of comment string. */
+
+ bdf_glyphlist_t overflow; /* Storage used for glyph insertion. */
+
+ void* internal; /* Internal data for the font. */
+
+ unsigned long nmod[2048]; /* Bitmap indicating modified glyphs. */
+ unsigned long umod[2048]; /* Bitmap indicating modified */
+ /* unencoded glyphs. */
+ unsigned short modified; /* Boolean indicating font modified. */
+ unsigned short bpp; /* Bits per pixel. */
+
+ FT_Memory memory;
+
+ bdf_property_t* user_props;
+ unsigned long nuser_props;
+ hashtable proptbl;
+
+ } bdf_font_t;
+
+
+ /*************************************************************************/
+ /* */
+ /* Types for load/save callbacks. */
+ /* */
+ /*************************************************************************/
+
+
+ /* Error codes. */
+#define BDF_MISSING_START -1
+#define BDF_MISSING_FONTNAME -2
+#define BDF_MISSING_SIZE -3
+#define BDF_MISSING_CHARS -4
+#define BDF_MISSING_STARTCHAR -5
+#define BDF_MISSING_ENCODING -6
+#define BDF_MISSING_BBX -7
+
+#define BDF_OUT_OF_MEMORY -20
+
+#define BDF_INVALID_LINE -100
+
+
+ /*************************************************************************/
+ /* */
+ /* BDF font API. */
+ /* */
+ /*************************************************************************/
+
+ FT_LOCAL( FT_Error )
+ bdf_load_font( FT_Stream stream,
+ FT_Memory memory,
+ bdf_options_t* opts,
+ bdf_font_t* *font );
+
+ FT_LOCAL( void )
+ bdf_free_font( bdf_font_t* font );
+
+ FT_LOCAL( bdf_property_t * )
+ bdf_get_property( char* name,
+ bdf_font_t* font );
+
+ FT_LOCAL( bdf_property_t * )
+ bdf_get_font_property( bdf_font_t* font,
+ const char* name );
+
+
+FT_END_HEADER
+
+
+#endif /* __BDF_H__ */
+
+
+/* END */
diff --git a/src/bdf/bdfdrivr.c b/src/bdf/bdfdrivr.c
new file mode 100644
index 0000000..74cc2f1
--- /dev/null
+++ b/src/bdf/bdfdrivr.c
@@ -0,0 +1,848 @@
+/* bdfdrivr.c
+
+ FreeType font driver for bdf files
+
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include <ft2build.h>
+
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_BDF_H
+
+#include FT_SERVICE_BDF_H
+#include FT_SERVICE_XFREE86_NAME_H
+
+#include "bdf.h"
+#include "bdfdrivr.h"
+
+#include "bdferror.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_bdfdriver
+
+
+ typedef struct BDF_CMapRec_
+ {
+ FT_CMapRec cmap;
+ FT_UInt num_encodings;
+ BDF_encoding_el* encodings;
+
+ } BDF_CMapRec, *BDF_CMap;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ bdf_cmap_init( FT_CMap bdfcmap,
+ FT_Pointer init_data )
+ {
+ BDF_CMap cmap = (BDF_CMap)bdfcmap;
+ BDF_Face face = (BDF_Face)FT_CMAP_FACE( cmap );
+ FT_UNUSED( init_data );
+
+
+ cmap->num_encodings = face->bdffont->glyphs_used;
+ cmap->encodings = face->en_table;
+
+ return BDF_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ bdf_cmap_done( FT_CMap bdfcmap )
+ {
+ BDF_CMap cmap = (BDF_CMap)bdfcmap;
+
+
+ cmap->encodings = NULL;
+ cmap->num_encodings = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ bdf_cmap_char_index( FT_CMap bdfcmap,
+ FT_UInt32 charcode )
+ {
+ BDF_CMap cmap = (BDF_CMap)bdfcmap;
+ BDF_encoding_el* encodings = cmap->encodings;
+ FT_UInt min, max, mid;
+ FT_UInt result = 0;
+
+
+ min = 0;
+ max = cmap->num_encodings;
+
+ while ( min < max )
+ {
+ FT_UInt32 code;
+
+
+ mid = ( min + max ) >> 1;
+ code = encodings[mid].enc;
+
+ if ( charcode == code )
+ {
+ /* increase glyph index by 1 -- */
+ /* we reserve slot 0 for the undefined glyph */
+ result = encodings[mid].glyph + 1;
+ break;
+ }
+
+ if ( charcode < code )
+ max = mid;
+ else
+ min = mid + 1;
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ bdf_cmap_char_next( FT_CMap bdfcmap,
+ FT_UInt32 *acharcode )
+ {
+ BDF_CMap cmap = (BDF_CMap)bdfcmap;
+ BDF_encoding_el* encodings = cmap->encodings;
+ FT_UInt min, max, mid;
+ FT_UInt32 charcode = *acharcode + 1;
+ FT_UInt result = 0;
+
+
+ min = 0;
+ max = cmap->num_encodings;
+
+ while ( min < max )
+ {
+ FT_UInt32 code;
+
+
+ mid = ( min + max ) >> 1;
+ code = encodings[mid].enc;
+
+ if ( charcode == code )
+ {
+ /* increase glyph index by 1 -- */
+ /* we reserve slot 0 for the undefined glyph */
+ result = encodings[mid].glyph + 1;
+ goto Exit;
+ }
+
+ if ( charcode < code )
+ max = mid;
+ else
+ min = mid + 1;
+ }
+
+ charcode = 0;
+ if ( min < cmap->num_encodings )
+ {
+ charcode = encodings[min].enc;
+ result = encodings[min].glyph + 1;
+ }
+
+ Exit:
+ *acharcode = charcode;
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_CMap_ClassRec bdf_cmap_class =
+ {
+ sizeof ( BDF_CMapRec ),
+ bdf_cmap_init,
+ bdf_cmap_done,
+ bdf_cmap_char_index,
+ bdf_cmap_char_next
+ };
+
+
+ static FT_Error
+ bdf_interpret_style( BDF_Face bdf )
+ {
+ FT_Error error = BDF_Err_Ok;
+ FT_Face face = FT_FACE( bdf );
+ FT_Memory memory = face->memory;
+ bdf_font_t* font = bdf->bdffont;
+ bdf_property_t* prop;
+
+ int nn, len;
+ char* strings[4] = { NULL, NULL, NULL, NULL };
+ int lengths[4];
+
+
+ face->style_flags = 0;
+
+ prop = bdf_get_font_property( font, (char *)"SLANT" );
+ if ( prop && prop->format == BDF_ATOM &&
+ prop->value.atom &&
+ ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ||
+ *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) )
+ {
+ face->style_flags |= FT_STYLE_FLAG_ITALIC;
+ strings[2] = ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' )
+ ? (char *)"Oblique"
+ : (char *)"Italic";
+ }
+
+ prop = bdf_get_font_property( font, (char *)"WEIGHT_NAME" );
+ if ( prop && prop->format == BDF_ATOM &&
+ prop->value.atom &&
+ ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) )
+ {
+ face->style_flags |= FT_STYLE_FLAG_BOLD;
+ strings[1] = (char *)"Bold";
+ }
+
+ prop = bdf_get_font_property( font, (char *)"SETWIDTH_NAME" );
+ if ( prop && prop->format == BDF_ATOM &&
+ prop->value.atom && *(prop->value.atom) &&
+ !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
+ strings[3] = (char *)(prop->value.atom);
+
+ prop = bdf_get_font_property( font, (char *)"ADD_STYLE_NAME" );
+ if ( prop && prop->format == BDF_ATOM &&
+ prop->value.atom && *(prop->value.atom) &&
+ !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
+ strings[0] = (char *)(prop->value.atom);
+
+ len = 0;
+
+ for ( len = 0, nn = 0; nn < 4; nn++ )
+ {
+ lengths[nn] = 0;
+ if ( strings[nn] )
+ {
+ lengths[nn] = ft_strlen( strings[nn] );
+ len += lengths[nn] + 1;
+ }
+ }
+
+ if ( len == 0 )
+ {
+ strings[0] = (char *)"Regular";
+ lengths[0] = ft_strlen( strings[0] );
+ len = lengths[0] + 1;
+ }
+
+ {
+ char* s;
+
+
+ if ( FT_ALLOC( face->style_name, len ) )
+ return error;
+
+ s = face->style_name;
+
+ for ( nn = 0; nn < 4; nn++ )
+ {
+ char* src = strings[nn];
+
+
+ len = lengths[nn];
+
+ if ( src == NULL )
+ continue;
+
+ /* separate elements with a space */
+ if ( s != face->style_name )
+ *s++ = ' ';
+
+ ft_memcpy( s, src, len );
+
+ /* need to convert spaces to dashes for */
+ /* add_style_name and setwidth_name */
+ if ( nn == 0 || nn == 3 )
+ {
+ int mm;
+
+
+ for ( mm = 0; mm < len; mm++ )
+ if ( s[mm] == ' ' )
+ s[mm] = '-';
+ }
+
+ s += len;
+ }
+ *s = 0;
+ }
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ BDF_Face_Done( FT_Face bdfface ) /* BDF_Face */
+ {
+ BDF_Face face = (BDF_Face)bdfface;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ bdf_free_font( face->bdffont );
+
+ FT_FREE( face->en_table );
+
+ FT_FREE( face->charset_encoding );
+ FT_FREE( face->charset_registry );
+ FT_FREE( bdfface->family_name );
+ FT_FREE( bdfface->style_name );
+
+ FT_FREE( bdfface->available_sizes );
+
+ FT_FREE( face->bdffont );
+
+ FT_TRACE4(( "BDF_Face_Done: done face\n" ));
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ BDF_Face_Init( FT_Stream stream,
+ FT_Face bdfface, /* BDF_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error = BDF_Err_Ok;
+ BDF_Face face = (BDF_Face)bdfface;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+ bdf_font_t* font = NULL;
+ bdf_options_t options;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+ FT_UNUSED( face_index );
+
+
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ options.correct_metrics = 1; /* FZ XXX: options semantics */
+ options.keep_unencoded = 1;
+ options.keep_comments = 0;
+ options.font_spacing = BDF_PROPORTIONAL;
+
+ error = bdf_load_font( stream, memory, &options, &font );
+ if ( error == BDF_Err_Missing_Startfont_Field )
+ {
+ FT_TRACE2(( "[not a valid BDF file]\n" ));
+ goto Fail;
+ }
+ else if ( error )
+ goto Exit;
+
+ /* we have a bdf font: let's construct the face object */
+ face->bdffont = font;
+ {
+ bdf_property_t* prop = NULL;
+
+
+ FT_TRACE4(( "number of glyphs: %d (%d)\n",
+ font->glyphs_size,
+ font->glyphs_used ));
+ FT_TRACE4(( "number of unencoded glyphs: %d (%d)\n",
+ font->unencoded_size,
+ font->unencoded_used ));
+
+ bdfface->num_faces = 1;
+ bdfface->face_index = 0;
+ bdfface->face_flags = FT_FACE_FLAG_FIXED_SIZES |
+ FT_FACE_FLAG_HORIZONTAL |
+ FT_FACE_FLAG_FAST_GLYPHS;
+
+ prop = bdf_get_font_property( font, "SPACING" );
+ if ( prop && prop->format == BDF_ATOM &&
+ prop->value.atom &&
+ ( *(prop->value.atom) == 'M' || *(prop->value.atom) == 'm' ||
+ *(prop->value.atom) == 'C' || *(prop->value.atom) == 'c' ) )
+ bdfface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* FZ XXX: TO DO: FT_FACE_FLAGS_VERTICAL */
+ /* FZ XXX: I need a font to implement this */
+
+ prop = bdf_get_font_property( font, "FAMILY_NAME" );
+ if ( prop && prop->value.atom )
+ {
+ if ( FT_STRDUP( bdfface->family_name, prop->value.atom ) )
+ goto Exit;
+ }
+ else
+ bdfface->family_name = 0;
+
+ if ( ( error = bdf_interpret_style( face ) ) != 0 )
+ goto Exit;
+
+ /* the number of glyphs (with one slot for the undefined glyph */
+ /* at position 0 and all unencoded glyphs) */
+ bdfface->num_glyphs = font->glyphs_size + 1;
+
+ bdfface->num_fixed_sizes = 1;
+ if ( FT_NEW_ARRAY( bdfface->available_sizes, 1 ) )
+ goto Exit;
+
+ {
+ FT_Bitmap_Size* bsize = bdfface->available_sizes;
+ FT_Short resolution_x = 0, resolution_y = 0;
+
+
+ FT_MEM_ZERO( bsize, sizeof ( FT_Bitmap_Size ) );
+
+ bsize->height = (FT_Short)( font->font_ascent + font->font_descent );
+
+ prop = bdf_get_font_property( font, "AVERAGE_WIDTH" );
+ if ( prop )
+ bsize->width = (FT_Short)( ( prop->value.int32 + 5 ) / 10 );
+ else
+ bsize->width = (FT_Short)( bsize->height * 2/3 );
+
+ prop = bdf_get_font_property( font, "POINT_SIZE" );
+ if ( prop )
+ /* convert from 722.7 decipoints to 72 points per inch */
+ bsize->size =
+ (FT_Pos)( ( prop->value.int32 * 64 * 7200 + 36135L ) / 72270L );
+ else
+ bsize->size = bsize->width << 6;
+
+ prop = bdf_get_font_property( font, "PIXEL_SIZE" );
+ if ( prop )
+ bsize->y_ppem = (FT_Short)prop->value.int32 << 6;
+
+ prop = bdf_get_font_property( font, "RESOLUTION_X" );
+ if ( prop )
+ resolution_x = (FT_Short)prop->value.int32;
+
+ prop = bdf_get_font_property( font, "RESOLUTION_Y" );
+ if ( prop )
+ resolution_y = (FT_Short)prop->value.int32;
+
+ if ( bsize->y_ppem == 0 )
+ {
+ bsize->y_ppem = bsize->size;
+ if ( resolution_y )
+ bsize->y_ppem = bsize->y_ppem * resolution_y / 72;
+ }
+ if ( resolution_x && resolution_y )
+ bsize->x_ppem = bsize->y_ppem * resolution_x / resolution_y;
+ else
+ bsize->x_ppem = bsize->y_ppem;
+ }
+
+ /* encoding table */
+ {
+ bdf_glyph_t* cur = font->glyphs;
+ unsigned long n;
+
+
+ if ( FT_NEW_ARRAY( face->en_table, font->glyphs_size ) )
+ goto Exit;
+
+ face->default_glyph = 0;
+ for ( n = 0; n < font->glyphs_size; n++ )
+ {
+ (face->en_table[n]).enc = cur[n].encoding;
+ FT_TRACE4(( "idx %d, val 0x%lX\n", n, cur[n].encoding ));
+ (face->en_table[n]).glyph = (FT_Short)n;
+
+ if ( cur[n].encoding == font->default_char )
+ face->default_glyph = n;
+ }
+ }
+
+ /* charmaps */
+ {
+ bdf_property_t *charset_registry = 0, *charset_encoding = 0;
+ FT_Bool unicode_charmap = 0;
+
+
+ charset_registry =
+ bdf_get_font_property( font, "CHARSET_REGISTRY" );
+ charset_encoding =
+ bdf_get_font_property( font, "CHARSET_ENCODING" );
+ if ( charset_registry && charset_encoding )
+ {
+ if ( charset_registry->format == BDF_ATOM &&
+ charset_encoding->format == BDF_ATOM &&
+ charset_registry->value.atom &&
+ charset_encoding->value.atom )
+ {
+ const char* s;
+
+
+ if ( FT_STRDUP( face->charset_encoding,
+ charset_encoding->value.atom ) ||
+ FT_STRDUP( face->charset_registry,
+ charset_registry->value.atom ) )
+ goto Exit;
+
+ /* Uh, oh, compare first letters manually to avoid dependency */
+ /* on locales. */
+ s = face->charset_registry;
+ if ( ( s[0] == 'i' || s[0] == 'I' ) &&
+ ( s[1] == 's' || s[1] == 'S' ) &&
+ ( s[2] == 'o' || s[2] == 'O' ) )
+ {
+ s += 3;
+ if ( !ft_strcmp( s, "10646" ) ||
+ ( !ft_strcmp( s, "8859" ) &&
+ !ft_strcmp( face->charset_encoding, "1" ) ) )
+ unicode_charmap = 1;
+ }
+
+ {
+ FT_CharMapRec charmap;
+
+
+ charmap.face = FT_FACE( face );
+ charmap.encoding = FT_ENCODING_NONE;
+ charmap.platform_id = 0;
+ charmap.encoding_id = 0;
+
+ if ( unicode_charmap )
+ {
+ charmap.encoding = FT_ENCODING_UNICODE;
+ charmap.platform_id = 3;
+ charmap.encoding_id = 1;
+ }
+
+ error = FT_CMap_New( &bdf_cmap_class, NULL, &charmap, NULL );
+
+#if 0
+ /* Select default charmap */
+ if ( bdfface->num_charmaps )
+ bdfface->charmap = bdfface->charmaps[0];
+#endif
+ }
+
+ goto Exit;
+ }
+ }
+
+ /* otherwise assume Adobe standard encoding */
+
+ {
+ FT_CharMapRec charmap;
+
+
+ charmap.face = FT_FACE( face );
+ charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
+ charmap.platform_id = 7;
+ charmap.encoding_id = 0;
+
+ error = FT_CMap_New( &bdf_cmap_class, NULL, &charmap, NULL );
+
+ /* Select default charmap */
+ if ( bdfface->num_charmaps )
+ bdfface->charmap = bdfface->charmaps[0];
+ }
+ }
+ }
+
+ Exit:
+ return error;
+
+ Fail:
+ BDF_Face_Done( bdfface );
+ return BDF_Err_Unknown_File_Format;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ BDF_Size_Select( FT_Size size,
+ FT_ULong strike_index )
+ {
+ bdf_font_t* bdffont = ( (BDF_Face)size->face )->bdffont;
+
+
+ FT_Select_Metrics( size->face, strike_index );
+
+ size->metrics.ascender = bdffont->font_ascent << 6;
+ size->metrics.descender = -bdffont->font_descent << 6;
+ size->metrics.max_advance = bdffont->bbx.width << 6;
+
+ return BDF_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ BDF_Size_Request( FT_Size size,
+ FT_Size_Request req )
+ {
+ FT_Face face = size->face;
+ FT_Bitmap_Size* bsize = face->available_sizes;
+ bdf_font_t* bdffont = ( (BDF_Face)face )->bdffont;
+ FT_Error error = BDF_Err_Invalid_Pixel_Size;
+ FT_Long height;
+
+
+ height = FT_REQUEST_HEIGHT( req );
+ height = ( height + 32 ) >> 6;
+
+ switch ( req->type )
+ {
+ case FT_SIZE_REQUEST_TYPE_NOMINAL:
+ if ( height == ( bsize->y_ppem + 32 ) >> 6 )
+ error = BDF_Err_Ok;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_REAL_DIM:
+ if ( height == ( bdffont->font_ascent +
+ bdffont->font_descent ) )
+ error = BDF_Err_Ok;
+ break;
+
+ default:
+ error = BDF_Err_Unimplemented_Feature;
+ break;
+ }
+
+ if ( error )
+ return error;
+ else
+ return BDF_Size_Select( size, 0 );
+ }
+
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ BDF_Glyph_Load( FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ BDF_Face bdf = (BDF_Face)FT_SIZE_FACE( size );
+ FT_Face face = FT_FACE( bdf );
+ FT_Error error = BDF_Err_Ok;
+ FT_Bitmap* bitmap = &slot->bitmap;
+ bdf_glyph_t glyph;
+ int bpp = bdf->bdffont->bpp;
+
+ FT_UNUSED( load_flags );
+
+
+ if ( !face || glyph_index >= (FT_UInt)face->num_glyphs )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* index 0 is the undefined glyph */
+ if ( glyph_index == 0 )
+ glyph_index = bdf->default_glyph;
+ else
+ glyph_index--;
+
+ /* slot, bitmap => freetype, glyph => bdflib */
+ glyph = bdf->bdffont->glyphs[glyph_index];
+
+ bitmap->rows = glyph.bbx.height;
+ bitmap->width = glyph.bbx.width;
+ bitmap->pitch = glyph.bpr;
+
+ /* note: we don't allocate a new array to hold the bitmap; */
+ /* we can simply point to it */
+ ft_glyphslot_set_bitmap( slot, glyph.bitmap );
+
+ switch ( bpp )
+ {
+ case 1:
+ bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
+ break;
+ case 2:
+ bitmap->pixel_mode = FT_PIXEL_MODE_GRAY2;
+ break;
+ case 4:
+ bitmap->pixel_mode = FT_PIXEL_MODE_GRAY4;
+ break;
+ case 8:
+ bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+ bitmap->num_grays = 256;
+ break;
+ }
+
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ slot->bitmap_left = glyph.bbx.x_offset;
+ slot->bitmap_top = glyph.bbx.ascent;
+
+ slot->metrics.horiAdvance = glyph.dwidth << 6;
+ slot->metrics.horiBearingX = glyph.bbx.x_offset << 6;
+ slot->metrics.horiBearingY = glyph.bbx.ascent << 6;
+ slot->metrics.width = bitmap->width << 6;
+ slot->metrics.height = bitmap->rows << 6;
+
+ /*
+ * XXX DWIDTH1 and VVECTOR should be parsed and
+ * used here, provided such fonts do exist.
+ */
+ ft_synthesize_vertical_metrics( &slot->metrics,
+ bdf->bdffont->bbx.height << 6 );
+
+ Exit:
+ return error;
+ }
+
+
+ /*
+ *
+ * BDF SERVICE
+ *
+ */
+
+ static FT_Error
+ bdf_get_bdf_property( BDF_Face face,
+ const char* prop_name,
+ BDF_PropertyRec *aproperty )
+ {
+ bdf_property_t* prop;
+
+
+ FT_ASSERT( face && face->bdffont );
+
+ prop = bdf_get_font_property( face->bdffont, prop_name );
+ if ( prop )
+ {
+ switch ( prop->format )
+ {
+ case BDF_ATOM:
+ aproperty->type = BDF_PROPERTY_TYPE_ATOM;
+ aproperty->u.atom = prop->value.atom;
+ break;
+
+ case BDF_INTEGER:
+ aproperty->type = BDF_PROPERTY_TYPE_INTEGER;
+ aproperty->u.integer = prop->value.int32;
+ break;
+
+ case BDF_CARDINAL:
+ aproperty->type = BDF_PROPERTY_TYPE_CARDINAL;
+ aproperty->u.cardinal = prop->value.card32;
+ break;
+
+ default:
+ goto Fail;
+ }
+ return 0;
+ }
+
+ Fail:
+ return BDF_Err_Invalid_Argument;
+ }
+
+
+ static FT_Error
+ bdf_get_charset_id( BDF_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ *acharset_encoding = face->charset_encoding;
+ *acharset_registry = face->charset_registry;
+
+ return 0;
+ }
+
+
+ static const FT_Service_BDFRec bdf_service_bdf =
+ {
+ (FT_BDF_GetCharsetIdFunc)bdf_get_charset_id,
+ (FT_BDF_GetPropertyFunc) bdf_get_bdf_property
+ };
+
+
+ /*
+ *
+ * SERVICES LIST
+ *
+ */
+
+ static const FT_ServiceDescRec bdf_services[] =
+ {
+ { FT_SERVICE_ID_BDF, &bdf_service_bdf },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_BDF },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ bdf_driver_requester( FT_Module module,
+ const char* name )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( bdf_services, name );
+ }
+
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec bdf_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_NO_OUTLINES,
+ sizeof ( FT_DriverRec ),
+
+ "bdf",
+ 0x10000L,
+ 0x20000L,
+
+ 0,
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) bdf_driver_requester
+ },
+
+ sizeof ( BDF_FaceRec ),
+ sizeof ( FT_SizeRec ),
+ sizeof ( FT_GlyphSlotRec ),
+
+ BDF_Face_Init,
+ BDF_Face_Done,
+ 0, /* FT_Size_InitFunc */
+ 0, /* FT_Size_DoneFunc */
+ 0, /* FT_Slot_InitFunc */
+ 0, /* FT_Slot_DoneFunc */
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ BDF_Glyph_Load,
+
+ 0, /* FT_Face_GetKerningFunc */
+ 0, /* FT_Face_AttachFunc */
+ 0, /* FT_Face_GetAdvancesFunc */
+
+ BDF_Size_Request,
+ BDF_Size_Select
+ };
+
+
+/* END */
diff --git a/src/bdf/bdfdrivr.h b/src/bdf/bdfdrivr.h
new file mode 100644
index 0000000..86f40ee
--- /dev/null
+++ b/src/bdf/bdfdrivr.h
@@ -0,0 +1,76 @@
+/* bdfdrivr.h
+
+ FreeType font driver for bdf fonts
+
+ Copyright (C) 2001, 2002, 2003, 2004 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __BDFDRIVR_H__
+#define __BDFDRIVR_H__
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+#include "bdf.h"
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct BDF_encoding_el_
+ {
+ FT_ULong enc;
+ FT_UShort glyph;
+
+ } BDF_encoding_el;
+
+
+ typedef struct BDF_FaceRec_
+ {
+ FT_FaceRec root;
+
+ char* charset_encoding;
+ char* charset_registry;
+
+ bdf_font_t* bdffont;
+
+ BDF_encoding_el* en_table;
+
+ FT_CharMap charmap_handle;
+ FT_CharMapRec charmap; /* a single charmap per face */
+
+ FT_UInt default_glyph;
+
+ } BDF_FaceRec, *BDF_Face;
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) bdf_driver_class;
+
+
+FT_END_HEADER
+
+
+#endif /* __BDFDRIVR_H__ */
+
+
+/* END */
diff --git a/src/bdf/bdferror.h b/src/bdf/bdferror.h
new file mode 100644
index 0000000..b27fa33
--- /dev/null
+++ b/src/bdf/bdferror.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2001, 2002 Francesco Zappa Nardelli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the BDF error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __BDFERROR_H__
+#define __BDFERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX BDF_Err_
+#define FT_ERR_BASE FT_Mod_Err_BDF
+
+#include FT_ERRORS_H
+
+#endif /* __BDFERROR_H__ */
+
+
+/* END */
diff --git a/src/bdf/bdflib.c b/src/bdf/bdflib.c
new file mode 100644
index 0000000..512cd62
--- /dev/null
+++ b/src/bdf/bdflib.c
@@ -0,0 +1,2472 @@
+/*
+ * Copyright 2000 Computing Research Labs, New Mexico State University
+ * Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ * Francesco Zappa Nardelli
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ /*************************************************************************/
+ /* */
+ /* This file is based on bdf.c,v 1.22 2000/03/16 20:08:50 */
+ /* */
+ /* taken from Mark Leisher's xmbdfed package */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+
+#include FT_FREETYPE_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_OBJECTS_H
+
+#include "bdf.h"
+#include "bdferror.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_bdflib
+
+
+ /*************************************************************************/
+ /* */
+ /* Default BDF font options. */
+ /* */
+ /*************************************************************************/
+
+
+ static const bdf_options_t _bdf_opts =
+ {
+ 1, /* Correct metrics. */
+ 1, /* Preserve unencoded glyphs. */
+ 0, /* Preserve comments. */
+ BDF_PROPORTIONAL /* Default spacing. */
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* Builtin BDF font properties. */
+ /* */
+ /*************************************************************************/
+
+ /* List of most properties that might appear in a font. Doesn't include */
+ /* the RAW_* and AXIS_* properties in X11R6 polymorphic fonts. */
+
+ static const bdf_property_t _bdf_properties[] =
+ {
+ { (char *)"ADD_STYLE_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"AVERAGE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"AVG_CAPITAL_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"CAP_HEIGHT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"CHARSET_COLLECTIONS", BDF_ATOM, 1, { 0 } },
+ { (char *)"CHARSET_ENCODING", BDF_ATOM, 1, { 0 } },
+ { (char *)"CHARSET_REGISTRY", BDF_ATOM, 1, { 0 } },
+ { (char *)"COMMENT", BDF_ATOM, 1, { 0 } },
+ { (char *)"COPYRIGHT", BDF_ATOM, 1, { 0 } },
+ { (char *)"DEFAULT_CHAR", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"DESTINATION", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"DEVICE_FONT_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"END_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"FACE_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"FAMILY_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"FIGURE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"FONT", BDF_ATOM, 1, { 0 } },
+ { (char *)"FONTNAME_REGISTRY", BDF_ATOM, 1, { 0 } },
+ { (char *)"FONT_ASCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"FONT_DESCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"FOUNDRY", BDF_ATOM, 1, { 0 } },
+ { (char *)"FULL_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"ITALIC_ANGLE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"MAX_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"MIN_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"NORM_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"NOTICE", BDF_ATOM, 1, { 0 } },
+ { (char *)"PIXEL_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"POINT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"QUAD_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_ASCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_AVERAGE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_AVG_CAPITAL_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_AVG_LOWERCASE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_CAP_HEIGHT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_DESCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_END_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_FIGURE_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_MAX_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_MIN_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_NORM_SPACE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_PIXEL_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_POINT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_PIXELSIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_POINTSIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_QUAD_WIDTH", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SMALL_CAP_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_STRIKEOUT_ASCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_STRIKEOUT_DESCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUBSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUBSCRIPT_X", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUBSCRIPT_Y", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUPERSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUPERSCRIPT_X", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_SUPERSCRIPT_Y", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_UNDERLINE_POSITION", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_UNDERLINE_THICKNESS", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RAW_X_HEIGHT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RELATIVE_SETWIDTH", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"RELATIVE_WEIGHT", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"RESOLUTION", BDF_INTEGER, 1, { 0 } },
+ { (char *)"RESOLUTION_X", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"RESOLUTION_Y", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"SETWIDTH_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"SLANT", BDF_ATOM, 1, { 0 } },
+ { (char *)"SMALL_CAP_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SPACING", BDF_ATOM, 1, { 0 } },
+ { (char *)"STRIKEOUT_ASCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"STRIKEOUT_DESCENT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUBSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUBSCRIPT_X", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUBSCRIPT_Y", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUPERSCRIPT_SIZE", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUPERSCRIPT_X", BDF_INTEGER, 1, { 0 } },
+ { (char *)"SUPERSCRIPT_Y", BDF_INTEGER, 1, { 0 } },
+ { (char *)"UNDERLINE_POSITION", BDF_INTEGER, 1, { 0 } },
+ { (char *)"UNDERLINE_THICKNESS", BDF_INTEGER, 1, { 0 } },
+ { (char *)"WEIGHT", BDF_CARDINAL, 1, { 0 } },
+ { (char *)"WEIGHT_NAME", BDF_ATOM, 1, { 0 } },
+ { (char *)"X_HEIGHT", BDF_INTEGER, 1, { 0 } },
+ { (char *)"_MULE_BASELINE_OFFSET", BDF_INTEGER, 1, { 0 } },
+ { (char *)"_MULE_RELATIVE_COMPOSE", BDF_INTEGER, 1, { 0 } },
+ };
+
+ static const unsigned long
+ _num_bdf_properties = sizeof ( _bdf_properties ) /
+ sizeof ( _bdf_properties[0] );
+
+
+ /*************************************************************************/
+ /* */
+ /* Hash table utilities for the properties. */
+ /* */
+ /*************************************************************************/
+
+ /* XXX: Replace this with FreeType's hash functions */
+
+
+#define INITIAL_HT_SIZE 241
+
+ typedef void
+ (*hash_free_func)( hashnode node );
+
+ static hashnode*
+ hash_bucket( const char* key,
+ hashtable* ht )
+ {
+ const char* kp = key;
+ unsigned long res = 0;
+ hashnode* bp = ht->table, *ndp;
+
+
+ /* Mocklisp hash function. */
+ while ( *kp )
+ res = ( res << 5 ) - res + *kp++;
+
+ ndp = bp + ( res % ht->size );
+ while ( *ndp )
+ {
+ kp = (*ndp)->key;
+ if ( kp[0] == key[0] && ft_strcmp( kp, key ) == 0 )
+ break;
+ ndp--;
+ if ( ndp < bp )
+ ndp = bp + ( ht->size - 1 );
+ }
+
+ return ndp;
+ }
+
+
+ static FT_Error
+ hash_rehash( hashtable* ht,
+ FT_Memory memory )
+ {
+ hashnode* obp = ht->table, *bp, *nbp;
+ int i, sz = ht->size;
+ FT_Error error = BDF_Err_Ok;
+
+
+ ht->size <<= 1;
+ ht->limit = ht->size / 3;
+
+ if ( FT_NEW_ARRAY( ht->table, ht->size ) )
+ goto Exit;
+
+ for ( i = 0, bp = obp; i < sz; i++, bp++ )
+ {
+ if ( *bp )
+ {
+ nbp = hash_bucket( (*bp)->key, ht );
+ *nbp = *bp;
+ }
+ }
+ FT_FREE( obp );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ hash_init( hashtable* ht,
+ FT_Memory memory )
+ {
+ int sz = INITIAL_HT_SIZE;
+ FT_Error error = BDF_Err_Ok;
+
+
+ ht->size = sz;
+ ht->limit = sz / 3;
+ ht->used = 0;
+
+ if ( FT_NEW_ARRAY( ht->table, sz ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ hash_free( hashtable* ht,
+ FT_Memory memory )
+ {
+ if ( ht != 0 )
+ {
+ int i, sz = ht->size;
+ hashnode* bp = ht->table;
+
+
+ for ( i = 0; i < sz; i++, bp++ )
+ FT_FREE( *bp );
+
+ FT_FREE( ht->table );
+ }
+ }
+
+
+ static FT_Error
+ hash_insert( char* key,
+ void* data,
+ hashtable* ht,
+ FT_Memory memory )
+ {
+ hashnode nn, *bp = hash_bucket( key, ht );
+ FT_Error error = BDF_Err_Ok;
+
+
+ nn = *bp;
+ if ( !nn )
+ {
+ if ( FT_NEW( nn ) )
+ goto Exit;
+ *bp = nn;
+
+ nn->key = key;
+ nn->data = data;
+
+ if ( ht->used >= ht->limit )
+ {
+ error = hash_rehash( ht, memory );
+ if ( error )
+ goto Exit;
+ }
+ ht->used++;
+ }
+ else
+ nn->data = data;
+
+ Exit:
+ return error;
+ }
+
+
+ static hashnode
+ hash_lookup( const char* key,
+ hashtable* ht )
+ {
+ hashnode *np = hash_bucket( key, ht );
+
+
+ return *np;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Utility types and functions. */
+ /* */
+ /*************************************************************************/
+
+
+ /* Function type for parsing lines of a BDF font. */
+
+ typedef FT_Error
+ (*_bdf_line_func_t)( char* line,
+ unsigned long linelen,
+ unsigned long lineno,
+ void* call_data,
+ void* client_data );
+
+
+ /* List structure for splitting lines into fields. */
+
+ typedef struct _bdf_list_t_
+ {
+ char** field;
+ unsigned long size;
+ unsigned long used;
+ FT_Memory memory;
+
+ } _bdf_list_t;
+
+
+ /* Structure used while loading BDF fonts. */
+
+ typedef struct _bdf_parse_t_
+ {
+ unsigned long flags;
+ unsigned long cnt;
+ unsigned long row;
+
+ short minlb;
+ short maxlb;
+ short maxrb;
+ short maxas;
+ short maxds;
+
+ short rbearing;
+
+ char* glyph_name;
+ long glyph_enc;
+
+ bdf_font_t* font;
+ bdf_options_t* opts;
+
+ unsigned long have[2048];
+ _bdf_list_t list;
+
+ FT_Memory memory;
+
+ } _bdf_parse_t;
+
+
+#define setsbit( m, cc ) \
+ ( m[(FT_Byte)(cc) >> 3] |= (FT_Byte)( 1 << ( (cc) & 7 ) ) )
+#define sbitset( m, cc ) \
+ ( m[(FT_Byte)(cc) >> 3] & ( 1 << ( (cc) & 7 ) ) )
+
+
+ static void
+ _bdf_list_init( _bdf_list_t* list,
+ FT_Memory memory )
+ {
+ FT_ZERO( list );
+ list->memory = memory;
+ }
+
+
+ static void
+ _bdf_list_done( _bdf_list_t* list )
+ {
+ FT_Memory memory = list->memory;
+
+
+ if ( memory )
+ {
+ FT_FREE( list->field );
+ FT_ZERO( list );
+ }
+ }
+
+
+ static FT_Error
+ _bdf_list_ensure( _bdf_list_t* list,
+ int num_items )
+ {
+ FT_Error error = BDF_Err_Ok;
+
+
+ if ( num_items > (int)list->size )
+ {
+ int oldsize = list->size;
+ int newsize = oldsize + ( oldsize >> 1 ) + 4;
+ int bigsize = FT_INT_MAX / sizeof ( char* );
+ FT_Memory memory = list->memory;
+
+
+ if ( oldsize == bigsize )
+ {
+ error = BDF_Err_Out_Of_Memory;
+ goto Exit;
+ }
+ else if ( newsize < oldsize || newsize > bigsize )
+ newsize = bigsize;
+
+ if ( FT_RENEW_ARRAY( list->field, oldsize, newsize ) )
+ goto Exit;
+
+ list->size = newsize;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ _bdf_list_shift( _bdf_list_t* list,
+ unsigned long n )
+ {
+ unsigned long i, u;
+
+
+ if ( list == 0 || list->used == 0 || n == 0 )
+ return;
+
+ if ( n >= list->used )
+ {
+ list->used = 0;
+ return;
+ }
+
+ for ( u = n, i = 0; u < list->used; i++, u++ )
+ list->field[i] = list->field[u];
+ list->used -= n;
+ }
+
+
+ static char *
+ _bdf_list_join( _bdf_list_t* list,
+ int c,
+ unsigned long *alen )
+ {
+ unsigned long i, j;
+ char *fp, *dp;
+
+
+ *alen = 0;
+
+ if ( list == 0 || list->used == 0 )
+ return 0;
+
+ dp = list->field[0];
+ for ( i = j = 0; i < list->used; i++ )
+ {
+ fp = list->field[i];
+ while ( *fp )
+ dp[j++] = *fp++;
+
+ if ( i + 1 < list->used )
+ dp[j++] = (char)c;
+ }
+ dp[j] = 0;
+
+ *alen = j;
+ return dp;
+ }
+
+
+ /* An empty string for empty fields. */
+
+ static const char empty[1] = { 0 }; /* XXX eliminate this */
+
+
+ static FT_Error
+ _bdf_list_split( _bdf_list_t* list,
+ char* separators,
+ char* line,
+ unsigned long linelen )
+ {
+ int mult, final_empty;
+ char *sp, *ep, *end;
+ char seps[32];
+ FT_Error error = BDF_Err_Ok;
+
+
+ /* Initialize the list. */
+ list->used = 0;
+
+ /* If the line is empty, then simply return. */
+ if ( linelen == 0 || line[0] == 0 )
+ goto Exit;
+
+ /* In the original code, if the `separators' parameter is NULL or */
+ /* empty, the list is split into individual bytes. We don't need */
+ /* this, so an error is signaled. */
+ if ( separators == 0 || *separators == 0 )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* Prepare the separator bitmap. */
+ FT_MEM_ZERO( seps, 32 );
+
+ /* If the very last character of the separator string is a plus, then */
+ /* set the `mult' flag to indicate that multiple separators should be */
+ /* collapsed into one. */
+ for ( mult = 0, sp = separators; sp && *sp; sp++ )
+ {
+ if ( *sp == '+' && *( sp + 1 ) == 0 )
+ mult = 1;
+ else
+ setsbit( seps, *sp );
+ }
+
+ /* Break the line up into fields. */
+ for ( final_empty = 0, sp = ep = line, end = sp + linelen;
+ sp < end && *sp; )
+ {
+ /* Collect everything that is not a separator. */
+ for ( ; *ep && !sbitset( seps, *ep ); ep++ )
+ ;
+
+ /* Resize the list if necessary. */
+ if ( list->used == list->size )
+ {
+ error = _bdf_list_ensure( list, list->used + 1 );
+ if ( error )
+ goto Exit;
+ }
+
+ /* Assign the field appropriately. */
+ list->field[list->used++] = ( ep > sp ) ? sp : (char*)empty;
+
+ sp = ep;
+
+ if ( mult )
+ {
+ /* If multiple separators should be collapsed, do it now by */
+ /* setting all the separator characters to 0. */
+ for ( ; *ep && sbitset( seps, *ep ); ep++ )
+ *ep = 0;
+ }
+ else if ( *ep != 0 )
+ /* Don't collapse multiple separators by making them 0, so just */
+ /* make the one encountered 0. */
+ *ep++ = 0;
+
+ final_empty = ( ep > sp && *ep == 0 );
+ sp = ep;
+ }
+
+ /* Finally, NULL-terminate the list. */
+ if ( list->used + final_empty >= list->size )
+ {
+ error = _bdf_list_ensure( list, list->used + final_empty + 1 );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( final_empty )
+ list->field[list->used++] = (char*)empty;
+
+ list->field[list->used] = 0;
+
+ Exit:
+ return error;
+ }
+
+
+#define NO_SKIP 256 /* this value cannot be stored in a 'char' */
+
+
+ static FT_Error
+ _bdf_readstream( FT_Stream stream,
+ _bdf_line_func_t callback,
+ void* client_data,
+ unsigned long *lno )
+ {
+ _bdf_line_func_t cb;
+ unsigned long lineno, buf_size;
+ int refill, bytes, hold, to_skip;
+ int start, end, cursor, avail;
+ char* buf = 0;
+ FT_Memory memory = stream->memory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ if ( callback == 0 )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* initial size and allocation of the input buffer */
+ buf_size = 1024;
+
+ if ( FT_NEW_ARRAY( buf, buf_size ) )
+ goto Exit;
+
+ cb = callback;
+ lineno = 1;
+ buf[0] = 0;
+ start = 0;
+ end = 0;
+ avail = 0;
+ cursor = 0;
+ refill = 1;
+ to_skip = NO_SKIP;
+ bytes = 0; /* make compiler happy */
+
+ for (;;)
+ {
+ if ( refill )
+ {
+ bytes = (int)FT_Stream_TryRead( stream, (FT_Byte*)buf + cursor,
+ (FT_ULong)(buf_size - cursor) );
+ avail = cursor + bytes;
+ cursor = 0;
+ refill = 0;
+ }
+
+ end = start;
+
+ /* should we skip an optional character like \n or \r? */
+ if ( start < avail && buf[start] == to_skip )
+ {
+ start += 1;
+ to_skip = NO_SKIP;
+ continue;
+ }
+
+ /* try to find the end of the line */
+ while ( end < avail && buf[end] != '\n' && buf[end] != '\r' )
+ end++;
+
+ /* if we hit the end of the buffer, try shifting its content */
+ /* or even resizing it */
+ if ( end >= avail )
+ {
+ if ( bytes == 0 ) /* last line in file doesn't end in \r or \n */
+ break; /* ignore it then exit */
+
+ if ( start == 0 )
+ {
+ /* this line is definitely too long; try resizing the input */
+ /* buffer a bit to handle it. */
+ FT_ULong new_size;
+
+
+ if ( buf_size >= 65536UL ) /* limit ourselves to 64KByte */
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ new_size = buf_size * 2;
+ if ( FT_RENEW_ARRAY( buf, buf_size, new_size ) )
+ goto Exit;
+
+ cursor = buf_size;
+ buf_size = new_size;
+ }
+ else
+ {
+ bytes = avail - start;
+
+ FT_MEM_COPY( buf, buf + start, bytes );
+
+ cursor = bytes;
+ avail -= bytes;
+ start = 0;
+ }
+ refill = 1;
+ continue;
+ }
+
+ /* Temporarily NUL-terminate the line. */
+ hold = buf[end];
+ buf[end] = 0;
+
+ /* XXX: Use encoding independent value for 0x1a */
+ if ( buf[start] != '#' && buf[start] != 0x1a && end > start )
+ {
+ error = (*cb)( buf + start, end - start, lineno,
+ (void*)&cb, client_data );
+ if ( error )
+ break;
+ }
+
+ lineno += 1;
+ buf[end] = (char)hold;
+ start = end + 1;
+
+ if ( hold == '\n' )
+ to_skip = '\r';
+ else if ( hold == '\r' )
+ to_skip = '\n';
+ else
+ to_skip = NO_SKIP;
+ }
+
+ *lno = lineno;
+
+ Exit:
+ FT_FREE( buf );
+ return error;
+ }
+
+
+ /* XXX: make this work with EBCDIC also */
+
+ static const unsigned char a2i[128] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ static const unsigned char odigits[32] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ static const unsigned char ddigits[32] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ static const unsigned char hdigits[32] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03,
+ 0x7e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+
+#define isdigok( m, d ) (m[(d) >> 3] & ( 1 << ( (d) & 7 ) ) )
+
+
+ /* Routine to convert an ASCII string into an unsigned long integer. */
+ static unsigned long
+ _bdf_atoul( char* s,
+ char** end,
+ int base )
+ {
+ unsigned long v;
+ const unsigned char* dmap;
+
+
+ if ( s == 0 || *s == 0 )
+ return 0;
+
+ /* Make sure the radix is something recognizable. Default to 10. */
+ switch ( base )
+ {
+ case 8:
+ dmap = odigits;
+ break;
+ case 16:
+ dmap = hdigits;
+ break;
+ default:
+ base = 10;
+ dmap = ddigits;
+ break;
+ }
+
+ /* Check for the special hex prefix. */
+ if ( *s == '0' &&
+ ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
+ {
+ base = 16;
+ dmap = hdigits;
+ s += 2;
+ }
+
+ for ( v = 0; isdigok( dmap, *s ); s++ )
+ v = v * base + a2i[(int)*s];
+
+ if ( end != 0 )
+ *end = s;
+
+ return v;
+ }
+
+
+ /* Routine to convert an ASCII string into an signed long integer. */
+ static long
+ _bdf_atol( char* s,
+ char** end,
+ int base )
+ {
+ long v, neg;
+ const unsigned char* dmap;
+
+
+ if ( s == 0 || *s == 0 )
+ return 0;
+
+ /* Make sure the radix is something recognizable. Default to 10. */
+ switch ( base )
+ {
+ case 8:
+ dmap = odigits;
+ break;
+ case 16:
+ dmap = hdigits;
+ break;
+ default:
+ base = 10;
+ dmap = ddigits;
+ break;
+ }
+
+ /* Check for a minus sign. */
+ neg = 0;
+ if ( *s == '-' )
+ {
+ s++;
+ neg = 1;
+ }
+
+ /* Check for the special hex prefix. */
+ if ( *s == '0' &&
+ ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
+ {
+ base = 16;
+ dmap = hdigits;
+ s += 2;
+ }
+
+ for ( v = 0; isdigok( dmap, *s ); s++ )
+ v = v * base + a2i[(int)*s];
+
+ if ( end != 0 )
+ *end = s;
+
+ return ( !neg ) ? v : -v;
+ }
+
+
+ /* Routine to convert an ASCII string into an signed short integer. */
+ static short
+ _bdf_atos( char* s,
+ char** end,
+ int base )
+ {
+ short v, neg;
+ const unsigned char* dmap;
+
+
+ if ( s == 0 || *s == 0 )
+ return 0;
+
+ /* Make sure the radix is something recognizable. Default to 10. */
+ switch ( base )
+ {
+ case 8:
+ dmap = odigits;
+ break;
+ case 16:
+ dmap = hdigits;
+ break;
+ default:
+ base = 10;
+ dmap = ddigits;
+ break;
+ }
+
+ /* Check for a minus. */
+ neg = 0;
+ if ( *s == '-' )
+ {
+ s++;
+ neg = 1;
+ }
+
+ /* Check for the special hex prefix. */
+ if ( *s == '0' &&
+ ( *( s + 1 ) == 'x' || *( s + 1 ) == 'X' ) )
+ {
+ base = 16;
+ dmap = hdigits;
+ s += 2;
+ }
+
+ for ( v = 0; isdigok( dmap, *s ); s++ )
+ v = (short)( v * base + a2i[(int)*s] );
+
+ if ( end != 0 )
+ *end = s;
+
+ return (short)( ( !neg ) ? v : -v );
+ }
+
+
+ /* Routine to compare two glyphs by encoding so they can be sorted. */
+ static int
+ by_encoding( const void* a,
+ const void* b )
+ {
+ bdf_glyph_t *c1, *c2;
+
+
+ c1 = (bdf_glyph_t *)a;
+ c2 = (bdf_glyph_t *)b;
+
+ if ( c1->encoding < c2->encoding )
+ return -1;
+
+ if ( c1->encoding > c2->encoding )
+ return 1;
+
+ return 0;
+ }
+
+
+ static FT_Error
+ bdf_create_property( char* name,
+ int format,
+ bdf_font_t* font )
+ {
+ unsigned long n;
+ bdf_property_t* p;
+ FT_Memory memory = font->memory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ /* First check to see if the property has */
+ /* already been added or not. If it has, then */
+ /* simply ignore it. */
+ if ( hash_lookup( name, &(font->proptbl) ) )
+ goto Exit;
+
+ if ( FT_RENEW_ARRAY( font->user_props,
+ font->nuser_props,
+ font->nuser_props + 1 ) )
+ goto Exit;
+
+ p = font->user_props + font->nuser_props;
+ FT_ZERO( p );
+
+ n = (unsigned long)( ft_strlen( name ) + 1 );
+
+ if ( FT_NEW_ARRAY( p->name, n ) )
+ goto Exit;
+
+ FT_MEM_COPY( (char *)p->name, name, n );
+
+ p->format = format;
+ p->builtin = 0;
+
+ n = _num_bdf_properties + font->nuser_props;
+
+ error = hash_insert( p->name, (void *)n, &(font->proptbl), memory );
+ if ( error )
+ goto Exit;
+
+ font->nuser_props++;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( bdf_property_t * )
+ bdf_get_property( char* name,
+ bdf_font_t* font )
+ {
+ hashnode hn;
+ unsigned long propid;
+
+
+ if ( name == 0 || *name == 0 )
+ return 0;
+
+ if ( ( hn = hash_lookup( name, &(font->proptbl) ) ) == 0 )
+ return 0;
+
+ propid = (unsigned long)hn->data;
+ if ( propid >= _num_bdf_properties )
+ return font->user_props + ( propid - _num_bdf_properties );
+
+ return (bdf_property_t*)_bdf_properties + propid;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* BDF font file parsing flags and functions. */
+ /* */
+ /*************************************************************************/
+
+
+ /* Parse flags. */
+
+#define _BDF_START 0x0001
+#define _BDF_FONT_NAME 0x0002
+#define _BDF_SIZE 0x0004
+#define _BDF_FONT_BBX 0x0008
+#define _BDF_PROPS 0x0010
+#define _BDF_GLYPHS 0x0020
+#define _BDF_GLYPH 0x0040
+#define _BDF_ENCODING 0x0080
+#define _BDF_SWIDTH 0x0100
+#define _BDF_DWIDTH 0x0200
+#define _BDF_BBX 0x0400
+#define _BDF_BITMAP 0x0800
+
+#define _BDF_SWIDTH_ADJ 0x1000
+
+#define _BDF_GLYPH_BITS ( _BDF_GLYPH | \
+ _BDF_ENCODING | \
+ _BDF_SWIDTH | \
+ _BDF_DWIDTH | \
+ _BDF_BBX | \
+ _BDF_BITMAP )
+
+#define _BDF_GLYPH_WIDTH_CHECK 0x40000000UL
+#define _BDF_GLYPH_HEIGHT_CHECK 0x80000000UL
+
+
+ /* Auto correction messages. */
+#define ACMSG1 "FONT_ASCENT property missing. " \
+ "Added \"FONT_ASCENT %hd\".\n"
+#define ACMSG2 "FONT_DESCENT property missing. " \
+ "Added \"FONT_DESCENT %hd\".\n"
+#define ACMSG3 "Font width != actual width. Old: %hd New: %hd.\n"
+#define ACMSG4 "Font left bearing != actual left bearing. " \
+ "Old: %hd New: %hd.\n"
+#define ACMSG5 "Font ascent != actual ascent. Old: %hd New: %hd.\n"
+#define ACMSG6 "Font descent != actual descent. Old: %hd New: %hd.\n"
+#define ACMSG7 "Font height != actual height. Old: %hd New: %hd.\n"
+#define ACMSG8 "Glyph scalable width (SWIDTH) adjustments made.\n"
+#define ACMSG9 "SWIDTH field missing at line %ld. Set automatically.\n"
+#define ACMSG10 "DWIDTH field missing at line %ld. Set to glyph width.\n"
+#define ACMSG11 "SIZE bits per pixel field adjusted to %hd.\n"
+#define ACMSG12 "Duplicate encoding %ld (%s) changed to unencoded.\n"
+#define ACMSG13 "Glyph %ld extra rows removed.\n"
+#define ACMSG14 "Glyph %ld extra columns removed.\n"
+#define ACMSG15 "Incorrect glyph count: %ld indicated but %ld found.\n"
+
+ /* Error messages. */
+#define ERRMSG1 "[line %ld] Missing \"%s\" line.\n"
+#define ERRMSG2 "[line %ld] Font header corrupted or missing fields.\n"
+#define ERRMSG3 "[line %ld] Font glyphs corrupted or missing fields.\n"
+#define ERRMSG4 "[line %ld] BBX too big.\n"
+
+
+ static FT_Error
+ _bdf_add_comment( bdf_font_t* font,
+ char* comment,
+ unsigned long len )
+ {
+ char* cp;
+ FT_Memory memory = font->memory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ if ( FT_RENEW_ARRAY( font->comments,
+ font->comments_len,
+ font->comments_len + len + 1 ) )
+ goto Exit;
+
+ cp = font->comments + font->comments_len;
+
+ FT_MEM_COPY( cp, comment, len );
+ cp[len] = '\n';
+
+ font->comments_len += len + 1;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Set the spacing from the font name if it exists, or set it to the */
+ /* default specified in the options. */
+ static FT_Error
+ _bdf_set_default_spacing( bdf_font_t* font,
+ bdf_options_t* opts )
+ {
+ unsigned long len;
+ char name[256];
+ _bdf_list_t list;
+ FT_Memory memory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ if ( font == 0 || font->name == 0 || font->name[0] == 0 )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ memory = font->memory;
+
+ _bdf_list_init( &list, memory );
+
+ font->spacing = opts->font_spacing;
+
+ len = (unsigned long)( ft_strlen( font->name ) + 1 );
+ /* Limit ourselves to 256 characters in the font name. */
+ if ( len >= 256 )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_MEM_COPY( name, font->name, len );
+
+ error = _bdf_list_split( &list, (char *)"-", name, len );
+ if ( error )
+ goto Fail;
+
+ if ( list.used == 15 )
+ {
+ switch ( list.field[11][0] )
+ {
+ case 'C':
+ case 'c':
+ font->spacing = BDF_CHARCELL;
+ break;
+ case 'M':
+ case 'm':
+ font->spacing = BDF_MONOWIDTH;
+ break;
+ case 'P':
+ case 'p':
+ font->spacing = BDF_PROPORTIONAL;
+ break;
+ }
+ }
+
+ Fail:
+ _bdf_list_done( &list );
+
+ Exit:
+ return error;
+ }
+
+
+ /* Determine whether the property is an atom or not. If it is, then */
+ /* clean it up so the double quotes are removed if they exist. */
+ static int
+ _bdf_is_atom( char* line,
+ unsigned long linelen,
+ char** name,
+ char** value,
+ bdf_font_t* font )
+ {
+ int hold;
+ char *sp, *ep;
+ bdf_property_t* p;
+
+
+ *name = sp = ep = line;
+
+ while ( *ep && *ep != ' ' && *ep != '\t' )
+ ep++;
+
+ hold = -1;
+ if ( *ep )
+ {
+ hold = *ep;
+ *ep = 0;
+ }
+
+ p = bdf_get_property( sp, font );
+
+ /* Restore the character that was saved before any return can happen. */
+ if ( hold != -1 )
+ *ep = (char)hold;
+
+ /* If the property exists and is not an atom, just return here. */
+ if ( p && p->format != BDF_ATOM )
+ return 0;
+
+ /* The property is an atom. Trim all leading and trailing whitespace */
+ /* and double quotes for the atom value. */
+ sp = ep;
+ ep = line + linelen;
+
+ /* Trim the leading whitespace if it exists. */
+ *sp++ = 0;
+ while ( *sp &&
+ ( *sp == ' ' || *sp == '\t' ) )
+ sp++;
+
+ /* Trim the leading double quote if it exists. */
+ if ( *sp == '"' )
+ sp++;
+ *value = sp;
+
+ /* Trim the trailing whitespace if it exists. */
+ while ( ep > sp &&
+ ( *( ep - 1 ) == ' ' || *( ep - 1 ) == '\t' ) )
+ *--ep = 0;
+
+ /* Trim the trailing double quote if it exists. */
+ if ( ep > sp && *( ep - 1 ) == '"' )
+ *--ep = 0;
+
+ return 1;
+ }
+
+
+ static FT_Error
+ _bdf_add_property( bdf_font_t* font,
+ char* name,
+ char* value )
+ {
+ unsigned long propid;
+ hashnode hn;
+ bdf_property_t *prop, *fp;
+ FT_Memory memory = font->memory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ /* First, check to see if the property already exists in the font. */
+ if ( ( hn = hash_lookup( name, (hashtable *)font->internal ) ) != 0 )
+ {
+ /* The property already exists in the font, so simply replace */
+ /* the value of the property with the current value. */
+ fp = font->props + (unsigned long)hn->data;
+
+ switch ( fp->format )
+ {
+ case BDF_ATOM:
+ /* Delete the current atom if it exists. */
+ FT_FREE( fp->value.atom );
+
+ if ( value && value[0] != 0 )
+ {
+ if ( FT_STRDUP( fp->value.atom, value ) )
+ goto Exit;
+ }
+ break;
+
+ case BDF_INTEGER:
+ fp->value.int32 = _bdf_atol( value, 0, 10 );
+ break;
+
+ case BDF_CARDINAL:
+ fp->value.card32 = _bdf_atoul( value, 0, 10 );
+ break;
+
+ default:
+ ;
+ }
+
+ goto Exit;
+ }
+
+ /* See whether this property type exists yet or not. */
+ /* If not, create it. */
+ hn = hash_lookup( name, &(font->proptbl) );
+ if ( hn == 0 )
+ {
+ error = bdf_create_property( name, BDF_ATOM, font );
+ if ( error )
+ goto Exit;
+ hn = hash_lookup( name, &(font->proptbl) );
+ }
+
+ /* Allocate another property if this is overflow. */
+ if ( font->props_used == font->props_size )
+ {
+ if ( font->props_size == 0 )
+ {
+ if ( FT_NEW_ARRAY( font->props, 1 ) )
+ goto Exit;
+ }
+ else
+ {
+ if ( FT_RENEW_ARRAY( font->props,
+ font->props_size,
+ font->props_size + 1 ) )
+ goto Exit;
+ }
+
+ fp = font->props + font->props_size;
+ FT_MEM_ZERO( fp, sizeof ( bdf_property_t ) );
+ font->props_size++;
+ }
+
+ propid = (unsigned long)hn->data;
+ if ( propid >= _num_bdf_properties )
+ prop = font->user_props + ( propid - _num_bdf_properties );
+ else
+ prop = (bdf_property_t*)_bdf_properties + propid;
+
+ fp = font->props + font->props_used;
+
+ fp->name = prop->name;
+ fp->format = prop->format;
+ fp->builtin = prop->builtin;
+
+ switch ( prop->format )
+ {
+ case BDF_ATOM:
+ fp->value.atom = 0;
+ if ( value != 0 && value[0] )
+ {
+ if ( FT_STRDUP( fp->value.atom, value ) )
+ goto Exit;
+ }
+ break;
+
+ case BDF_INTEGER:
+ fp->value.int32 = _bdf_atol( value, 0, 10 );
+ break;
+
+ case BDF_CARDINAL:
+ fp->value.card32 = _bdf_atoul( value, 0, 10 );
+ break;
+ }
+
+ /* If the property happens to be a comment, then it doesn't need */
+ /* to be added to the internal hash table. */
+ if ( ft_memcmp( name, "COMMENT", 7 ) != 0 ) {
+ /* Add the property to the font property table. */
+ error = hash_insert( fp->name,
+ (void *)font->props_used,
+ (hashtable *)font->internal,
+ memory );
+ if ( error )
+ goto Exit;
+ }
+
+ font->props_used++;
+
+ /* Some special cases need to be handled here. The DEFAULT_CHAR */
+ /* property needs to be located if it exists in the property list, the */
+ /* FONT_ASCENT and FONT_DESCENT need to be assigned if they are */
+ /* present, and the SPACING property should override the default */
+ /* spacing. */
+ if ( ft_memcmp( name, "DEFAULT_CHAR", 12 ) == 0 )
+ font->default_char = fp->value.int32;
+ else if ( ft_memcmp( name, "FONT_ASCENT", 11 ) == 0 )
+ font->font_ascent = fp->value.int32;
+ else if ( ft_memcmp( name, "FONT_DESCENT", 12 ) == 0 )
+ font->font_descent = fp->value.int32;
+ else if ( ft_memcmp( name, "SPACING", 7 ) == 0 )
+ {
+ if ( fp->value.atom[0] == 'p' || fp->value.atom[0] == 'P' )
+ font->spacing = BDF_PROPORTIONAL;
+ else if ( fp->value.atom[0] == 'm' || fp->value.atom[0] == 'M' )
+ font->spacing = BDF_MONOWIDTH;
+ else if ( fp->value.atom[0] == 'c' || fp->value.atom[0] == 'C' )
+ font->spacing = BDF_CHARCELL;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static const unsigned char nibble_mask[8] =
+ {
+ 0xFF, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE
+ };
+
+
+ /* Actually parse the glyph info and bitmaps. */
+ static FT_Error
+ _bdf_parse_glyphs( char* line,
+ unsigned long linelen,
+ unsigned long lineno,
+ void* call_data,
+ void* client_data )
+ {
+ int c, mask_index;
+ char* s;
+ unsigned char* bp;
+ unsigned long i, slen, nibbles;
+
+ _bdf_parse_t* p;
+ bdf_glyph_t* glyph;
+ bdf_font_t* font;
+
+ FT_Memory memory;
+ FT_Error error = BDF_Err_Ok;
+
+ FT_UNUSED( call_data );
+ FT_UNUSED( lineno ); /* only used in debug mode */
+
+
+ p = (_bdf_parse_t *)client_data;
+
+ font = p->font;
+ memory = font->memory;
+
+ /* Check for a comment. */
+ if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
+ {
+ linelen -= 7;
+
+ s = line + 7;
+ if ( *s != 0 )
+ {
+ s++;
+ linelen--;
+ }
+ error = _bdf_add_comment( p->font, s, linelen );
+ goto Exit;
+ }
+
+ /* The very first thing expected is the number of glyphs. */
+ if ( !( p->flags & _BDF_GLYPHS ) )
+ {
+ if ( ft_memcmp( line, "CHARS", 5 ) != 0 )
+ {
+ FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "CHARS" ));
+ error = BDF_Err_Missing_Chars_Field;
+ goto Exit;
+ }
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+ p->cnt = font->glyphs_size = _bdf_atoul( p->list.field[1], 0, 10 );
+
+ /* Make sure the number of glyphs is non-zero. */
+ if ( p->cnt == 0 )
+ font->glyphs_size = 64;
+
+ /* Limit ourselves to 1,114,112 glyphs in the font (this is the */
+ /* number of code points available in Unicode). */
+ if ( p->cnt >= 1114112UL )
+ {
+ error = BDF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( FT_NEW_ARRAY( font->glyphs, font->glyphs_size ) )
+ goto Exit;
+
+ p->flags |= _BDF_GLYPHS;
+
+ goto Exit;
+ }
+
+ /* Check for the ENDFONT field. */
+ if ( ft_memcmp( line, "ENDFONT", 7 ) == 0 )
+ {
+ /* Sort the glyphs by encoding. */
+ ft_qsort( (char *)font->glyphs,
+ font->glyphs_used,
+ sizeof ( bdf_glyph_t ),
+ by_encoding );
+
+ p->flags &= ~_BDF_START;
+
+ goto Exit;
+ }
+
+ /* Check for the ENDCHAR field. */
+ if ( ft_memcmp( line, "ENDCHAR", 7 ) == 0 )
+ {
+ p->glyph_enc = 0;
+ p->flags &= ~_BDF_GLYPH_BITS;
+
+ goto Exit;
+ }
+
+ /* Check to see whether a glyph is being scanned but should be */
+ /* ignored because it is an unencoded glyph. */
+ if ( ( p->flags & _BDF_GLYPH ) &&
+ p->glyph_enc == -1 &&
+ p->opts->keep_unencoded == 0 )
+ goto Exit;
+
+ /* Check for the STARTCHAR field. */
+ if ( ft_memcmp( line, "STARTCHAR", 9 ) == 0 )
+ {
+ /* Set the character name in the parse info first until the */
+ /* encoding can be checked for an unencoded character. */
+ FT_FREE( p->glyph_name );
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ _bdf_list_shift( &p->list, 1 );
+
+ s = _bdf_list_join( &p->list, ' ', &slen );
+
+ if ( !s )
+ {
+ error = BDF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( FT_NEW_ARRAY( p->glyph_name, slen + 1 ) )
+ goto Exit;
+
+ FT_MEM_COPY( p->glyph_name, s, slen + 1 );
+
+ p->flags |= _BDF_GLYPH;
+
+ goto Exit;
+ }
+
+ /* Check for the ENCODING field. */
+ if ( ft_memcmp( line, "ENCODING", 8 ) == 0 )
+ {
+ if ( !( p->flags & _BDF_GLYPH ) )
+ {
+ /* Missing STARTCHAR field. */
+ FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "STARTCHAR" ));
+ error = BDF_Err_Missing_Startchar_Field;
+ goto Exit;
+ }
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ p->glyph_enc = _bdf_atol( p->list.field[1], 0, 10 );
+
+ /* Check that the encoding is in the range [0,65536] because */
+ /* otherwise p->have (a bitmap with static size) overflows. */
+ if ( (size_t)p->glyph_enc >= sizeof ( p->have ) * 8 )
+ {
+ error = BDF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Check to see whether this encoding has already been encountered. */
+ /* If it has then change it to unencoded so it gets added if */
+ /* indicated. */
+ if ( p->glyph_enc >= 0 )
+ {
+ if ( _bdf_glyph_modified( p->have, p->glyph_enc ) )
+ {
+ /* Emit a message saying a glyph has been moved to the */
+ /* unencoded area. */
+ FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG12,
+ p->glyph_enc, p->glyph_name ));
+ p->glyph_enc = -1;
+ font->modified = 1;
+ }
+ else
+ _bdf_set_glyph_modified( p->have, p->glyph_enc );
+ }
+
+ if ( p->glyph_enc >= 0 )
+ {
+ /* Make sure there are enough glyphs allocated in case the */
+ /* number of characters happen to be wrong. */
+ if ( font->glyphs_used == font->glyphs_size )
+ {
+ if ( FT_RENEW_ARRAY( font->glyphs,
+ font->glyphs_size,
+ font->glyphs_size + 64 ) )
+ goto Exit;
+
+ font->glyphs_size += 64;
+ }
+
+ glyph = font->glyphs + font->glyphs_used++;
+ glyph->name = p->glyph_name;
+ glyph->encoding = p->glyph_enc;
+
+ /* Reset the initial glyph info. */
+ p->glyph_name = 0;
+ }
+ else
+ {
+ /* Unencoded glyph. Check to see whether it should */
+ /* be added or not. */
+ if ( p->opts->keep_unencoded != 0 )
+ {
+ /* Allocate the next unencoded glyph. */
+ if ( font->unencoded_used == font->unencoded_size )
+ {
+ if ( FT_RENEW_ARRAY( font->unencoded ,
+ font->unencoded_size,
+ font->unencoded_size + 4 ) )
+ goto Exit;
+
+ font->unencoded_size += 4;
+ }
+
+ glyph = font->unencoded + font->unencoded_used;
+ glyph->name = p->glyph_name;
+ glyph->encoding = font->unencoded_used++;
+ }
+ else
+ /* Free up the glyph name if the unencoded shouldn't be */
+ /* kept. */
+ FT_FREE( p->glyph_name );
+
+ p->glyph_name = 0;
+ }
+
+ /* Clear the flags that might be added when width and height are */
+ /* checked for consistency. */
+ p->flags &= ~( _BDF_GLYPH_WIDTH_CHECK | _BDF_GLYPH_HEIGHT_CHECK );
+
+ p->flags |= _BDF_ENCODING;
+
+ goto Exit;
+ }
+
+ /* Point at the glyph being constructed. */
+ if ( p->glyph_enc == -1 )
+ glyph = font->unencoded + ( font->unencoded_used - 1 );
+ else
+ glyph = font->glyphs + ( font->glyphs_used - 1 );
+
+ /* Check to see whether a bitmap is being constructed. */
+ if ( p->flags & _BDF_BITMAP )
+ {
+ /* If there are more rows than are specified in the glyph metrics, */
+ /* ignore the remaining lines. */
+ if ( p->row >= (unsigned long)glyph->bbx.height )
+ {
+ if ( !( p->flags & _BDF_GLYPH_HEIGHT_CHECK ) )
+ {
+ FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG13, glyph->encoding ));
+ p->flags |= _BDF_GLYPH_HEIGHT_CHECK;
+ font->modified = 1;
+ }
+
+ goto Exit;
+ }
+
+ /* Only collect the number of nibbles indicated by the glyph */
+ /* metrics. If there are more columns, they are simply ignored. */
+ nibbles = glyph->bpr << 1;
+ bp = glyph->bitmap + p->row * glyph->bpr;
+
+ for ( i = 0; i < nibbles; i++ )
+ {
+ c = line[i];
+ *bp = (FT_Byte)( ( *bp << 4 ) + a2i[c] );
+ if ( i + 1 < nibbles && ( i & 1 ) )
+ *++bp = 0;
+ }
+
+ /* Remove possible garbage at the right. */
+ mask_index = ( glyph->bbx.width * p->font->bpp ) & 7;
+ if ( glyph->bbx.width )
+ *bp &= nibble_mask[mask_index];
+
+ /* If any line has extra columns, indicate they have been removed. */
+ if ( ( line[nibbles] == '0' || a2i[(int)line[nibbles]] != 0 ) &&
+ !( p->flags & _BDF_GLYPH_WIDTH_CHECK ) )
+ {
+ FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG14, glyph->encoding ));
+ p->flags |= _BDF_GLYPH_WIDTH_CHECK;
+ font->modified = 1;
+ }
+
+ p->row++;
+ goto Exit;
+ }
+
+ /* Expect the SWIDTH (scalable width) field next. */
+ if ( ft_memcmp( line, "SWIDTH", 6 ) == 0 )
+ {
+ if ( !( p->flags & _BDF_ENCODING ) )
+ {
+ /* Missing ENCODING field. */
+ FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "ENCODING" ));
+ error = BDF_Err_Missing_Encoding_Field;
+ goto Exit;
+ }
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ glyph->swidth = (unsigned short)_bdf_atoul( p->list.field[1], 0, 10 );
+ p->flags |= _BDF_SWIDTH;
+
+ goto Exit;
+ }
+
+ /* Expect the DWIDTH (scalable width) field next. */
+ if ( ft_memcmp( line, "DWIDTH", 6 ) == 0 )
+ {
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ glyph->dwidth = (unsigned short)_bdf_atoul( p->list.field[1], 0, 10 );
+
+ if ( !( p->flags & _BDF_SWIDTH ) )
+ {
+ /* Missing SWIDTH field. Emit an auto correction message and set */
+ /* the scalable width from the device width. */
+ FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG9, lineno ));
+
+ glyph->swidth = (unsigned short)FT_MulDiv(
+ glyph->dwidth, 72000L,
+ (FT_Long)( font->point_size *
+ font->resolution_x ) );
+ }
+
+ p->flags |= _BDF_DWIDTH;
+ goto Exit;
+ }
+
+ /* Expect the BBX field next. */
+ if ( ft_memcmp( line, "BBX", 3 ) == 0 )
+ {
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ glyph->bbx.width = _bdf_atos( p->list.field[1], 0, 10 );
+ glyph->bbx.height = _bdf_atos( p->list.field[2], 0, 10 );
+ glyph->bbx.x_offset = _bdf_atos( p->list.field[3], 0, 10 );
+ glyph->bbx.y_offset = _bdf_atos( p->list.field[4], 0, 10 );
+
+ /* Generate the ascent and descent of the character. */
+ glyph->bbx.ascent = (short)( glyph->bbx.height + glyph->bbx.y_offset );
+ glyph->bbx.descent = (short)( -glyph->bbx.y_offset );
+
+ /* Determine the overall font bounding box as the characters are */
+ /* loaded so corrections can be done later if indicated. */
+ p->maxas = (short)FT_MAX( glyph->bbx.ascent, p->maxas );
+ p->maxds = (short)FT_MAX( glyph->bbx.descent, p->maxds );
+
+ p->rbearing = (short)( glyph->bbx.width + glyph->bbx.x_offset );
+
+ p->maxrb = (short)FT_MAX( p->rbearing, p->maxrb );
+ p->minlb = (short)FT_MIN( glyph->bbx.x_offset, p->minlb );
+ p->maxlb = (short)FT_MAX( glyph->bbx.x_offset, p->maxlb );
+
+ if ( !( p->flags & _BDF_DWIDTH ) )
+ {
+ /* Missing DWIDTH field. Emit an auto correction message and set */
+ /* the device width to the glyph width. */
+ FT_TRACE2(( "_bdf_parse_glyphs: " ACMSG10, lineno ));
+ glyph->dwidth = glyph->bbx.width;
+ }
+
+ /* If the BDF_CORRECT_METRICS flag is set, then adjust the SWIDTH */
+ /* value if necessary. */
+ if ( p->opts->correct_metrics != 0 )
+ {
+ /* Determine the point size of the glyph. */
+ unsigned short sw = (unsigned short)FT_MulDiv(
+ glyph->dwidth, 72000L,
+ (FT_Long)( font->point_size *
+ font->resolution_x ) );
+
+
+ if ( sw != glyph->swidth )
+ {
+ glyph->swidth = sw;
+
+ if ( p->glyph_enc == -1 )
+ _bdf_set_glyph_modified( font->umod,
+ font->unencoded_used - 1 );
+ else
+ _bdf_set_glyph_modified( font->nmod, glyph->encoding );
+
+ p->flags |= _BDF_SWIDTH_ADJ;
+ font->modified = 1;
+ }
+ }
+
+ p->flags |= _BDF_BBX;
+ goto Exit;
+ }
+
+ /* And finally, gather up the bitmap. */
+ if ( ft_memcmp( line, "BITMAP", 6 ) == 0 )
+ {
+ unsigned long bitmap_size;
+
+
+ if ( !( p->flags & _BDF_BBX ) )
+ {
+ /* Missing BBX field. */
+ FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG1, lineno, "BBX" ));
+ error = BDF_Err_Missing_Bbx_Field;
+ goto Exit;
+ }
+
+ /* Allocate enough space for the bitmap. */
+ glyph->bpr = ( glyph->bbx.width * p->font->bpp + 7 ) >> 3;
+
+ bitmap_size = glyph->bpr * glyph->bbx.height;
+ if ( bitmap_size > 0xFFFFU )
+ {
+ FT_ERROR(( "_bdf_parse_glyphs: " ERRMSG4, lineno ));
+ error = BDF_Err_Bbx_Too_Big;
+ goto Exit;
+ }
+ else
+ glyph->bytes = (unsigned short)bitmap_size;
+
+ if ( FT_NEW_ARRAY( glyph->bitmap, glyph->bytes ) )
+ goto Exit;
+
+ p->row = 0;
+ p->flags |= _BDF_BITMAP;
+
+ goto Exit;
+ }
+
+ error = BDF_Err_Invalid_File_Format;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Load the font properties. */
+ static FT_Error
+ _bdf_parse_properties( char* line,
+ unsigned long linelen,
+ unsigned long lineno,
+ void* call_data,
+ void* client_data )
+ {
+ unsigned long vlen;
+ _bdf_line_func_t* next;
+ _bdf_parse_t* p;
+ char* name;
+ char* value;
+ char nbuf[128];
+ FT_Error error = BDF_Err_Ok;
+
+ FT_UNUSED( lineno );
+
+
+ next = (_bdf_line_func_t *)call_data;
+ p = (_bdf_parse_t *) client_data;
+
+ /* Check for the end of the properties. */
+ if ( ft_memcmp( line, "ENDPROPERTIES", 13 ) == 0 )
+ {
+ /* If the FONT_ASCENT or FONT_DESCENT properties have not been */
+ /* encountered yet, then make sure they are added as properties and */
+ /* make sure they are set from the font bounding box info. */
+ /* */
+ /* This is *always* done regardless of the options, because X11 */
+ /* requires these two fields to compile fonts. */
+ if ( bdf_get_font_property( p->font, "FONT_ASCENT" ) == 0 )
+ {
+ p->font->font_ascent = p->font->bbx.ascent;
+ ft_sprintf( nbuf, "%hd", p->font->bbx.ascent );
+ error = _bdf_add_property( p->font, (char *)"FONT_ASCENT", nbuf );
+ if ( error )
+ goto Exit;
+
+ FT_TRACE2(( "_bdf_parse_properties: " ACMSG1, p->font->bbx.ascent ));
+ p->font->modified = 1;
+ }
+
+ if ( bdf_get_font_property( p->font, "FONT_DESCENT" ) == 0 )
+ {
+ p->font->font_descent = p->font->bbx.descent;
+ ft_sprintf( nbuf, "%hd", p->font->bbx.descent );
+ error = _bdf_add_property( p->font, (char *)"FONT_DESCENT", nbuf );
+ if ( error )
+ goto Exit;
+
+ FT_TRACE2(( "_bdf_parse_properties: " ACMSG2, p->font->bbx.descent ));
+ p->font->modified = 1;
+ }
+
+ p->flags &= ~_BDF_PROPS;
+ *next = _bdf_parse_glyphs;
+
+ goto Exit;
+ }
+
+ /* Ignore the _XFREE86_GLYPH_RANGES properties. */
+ if ( ft_memcmp( line, "_XFREE86_GLYPH_RANGES", 21 ) == 0 )
+ goto Exit;
+
+ /* Handle COMMENT fields and properties in a special way to preserve */
+ /* the spacing. */
+ if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
+ {
+ name = value = line;
+ value += 7;
+ if ( *value )
+ *value++ = 0;
+ error = _bdf_add_property( p->font, name, value );
+ if ( error )
+ goto Exit;
+ }
+ else if ( _bdf_is_atom( line, linelen, &name, &value, p->font ) )
+ {
+ error = _bdf_add_property( p->font, name, value );
+ if ( error )
+ goto Exit;
+ }
+ else
+ {
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+ name = p->list.field[0];
+
+ _bdf_list_shift( &p->list, 1 );
+ value = _bdf_list_join( &p->list, ' ', &vlen );
+
+ error = _bdf_add_property( p->font, name, value );
+ if ( error )
+ goto Exit;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* Load the font header. */
+ static FT_Error
+ _bdf_parse_start( char* line,
+ unsigned long linelen,
+ unsigned long lineno,
+ void* call_data,
+ void* client_data )
+ {
+ unsigned long slen;
+ _bdf_line_func_t* next;
+ _bdf_parse_t* p;
+ bdf_font_t* font;
+ char *s;
+
+ FT_Memory memory = NULL;
+ FT_Error error = BDF_Err_Ok;
+
+ FT_UNUSED( lineno ); /* only used in debug mode */
+
+
+ next = (_bdf_line_func_t *)call_data;
+ p = (_bdf_parse_t *) client_data;
+
+ if ( p->font )
+ memory = p->font->memory;
+
+ /* Check for a comment. This is done to handle those fonts that have */
+ /* comments before the STARTFONT line for some reason. */
+ if ( ft_memcmp( line, "COMMENT", 7 ) == 0 )
+ {
+ if ( p->opts->keep_comments != 0 && p->font != 0 )
+ {
+ linelen -= 7;
+
+ s = line + 7;
+ if ( *s != 0 )
+ {
+ s++;
+ linelen--;
+ }
+
+ error = _bdf_add_comment( p->font, s, linelen );
+ if ( error )
+ goto Exit;
+ /* here font is not defined! */
+ }
+
+ goto Exit;
+ }
+
+ if ( !( p->flags & _BDF_START ) )
+ {
+ memory = p->memory;
+
+ if ( ft_memcmp( line, "STARTFONT", 9 ) != 0 )
+ {
+ /* No STARTFONT field is a good indication of a problem. */
+ error = BDF_Err_Missing_Startfont_Field;
+ goto Exit;
+ }
+
+ p->flags = _BDF_START;
+ font = p->font = 0;
+
+ if ( FT_NEW( font ) )
+ goto Exit;
+ p->font = font;
+
+ font->memory = p->memory;
+ p->memory = 0;
+
+ { /* setup */
+ unsigned long i;
+ bdf_property_t* prop;
+
+
+ error = hash_init( &(font->proptbl), memory );
+ if ( error )
+ goto Exit;
+ for ( i = 0, prop = (bdf_property_t*)_bdf_properties;
+ i < _num_bdf_properties; i++, prop++ )
+ {
+ error = hash_insert( prop->name, (void *)i,
+ &(font->proptbl), memory );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ if ( FT_ALLOC( p->font->internal, sizeof ( hashtable ) ) )
+ goto Exit;
+ error = hash_init( (hashtable *)p->font->internal,memory );
+ if ( error )
+ goto Exit;
+ p->font->spacing = p->opts->font_spacing;
+ p->font->default_char = -1;
+
+ goto Exit;
+ }
+
+ /* Check for the start of the properties. */
+ if ( ft_memcmp( line, "STARTPROPERTIES", 15 ) == 0 )
+ {
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+ p->cnt = p->font->props_size = _bdf_atoul( p->list.field[1], 0, 10 );
+
+ if ( FT_NEW_ARRAY( p->font->props, p->cnt ) )
+ goto Exit;
+
+ p->flags |= _BDF_PROPS;
+ *next = _bdf_parse_properties;
+
+ goto Exit;
+ }
+
+ /* Check for the FONTBOUNDINGBOX field. */
+ if ( ft_memcmp( line, "FONTBOUNDINGBOX", 15 ) == 0 )
+ {
+ if ( !(p->flags & _BDF_SIZE ) )
+ {
+ /* Missing the SIZE field. */
+ FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "SIZE" ));
+ error = BDF_Err_Missing_Size_Field;
+ goto Exit;
+ }
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ p->font->bbx.width = _bdf_atos( p->list.field[1], 0, 10 );
+ p->font->bbx.height = _bdf_atos( p->list.field[2], 0, 10 );
+
+ p->font->bbx.x_offset = _bdf_atos( p->list.field[3], 0, 10 );
+ p->font->bbx.y_offset = _bdf_atos( p->list.field[4], 0, 10 );
+
+ p->font->bbx.ascent = (short)( p->font->bbx.height +
+ p->font->bbx.y_offset );
+
+ p->font->bbx.descent = (short)( -p->font->bbx.y_offset );
+
+ p->flags |= _BDF_FONT_BBX;
+
+ goto Exit;
+ }
+
+ /* The next thing to check for is the FONT field. */
+ if ( ft_memcmp( line, "FONT", 4 ) == 0 )
+ {
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+ _bdf_list_shift( &p->list, 1 );
+
+ s = _bdf_list_join( &p->list, ' ', &slen );
+
+ if ( !s )
+ {
+ error = BDF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( FT_NEW_ARRAY( p->font->name, slen + 1 ) )
+ goto Exit;
+ FT_MEM_COPY( p->font->name, s, slen + 1 );
+
+ /* If the font name is an XLFD name, set the spacing to the one in */
+ /* the font name. If there is no spacing fall back on the default. */
+ error = _bdf_set_default_spacing( p->font, p->opts );
+ if ( error )
+ goto Exit;
+
+ p->flags |= _BDF_FONT_NAME;
+
+ goto Exit;
+ }
+
+ /* Check for the SIZE field. */
+ if ( ft_memcmp( line, "SIZE", 4 ) == 0 )
+ {
+ if ( !( p->flags & _BDF_FONT_NAME ) )
+ {
+ /* Missing the FONT field. */
+ FT_ERROR(( "_bdf_parse_start: " ERRMSG1, lineno, "FONT" ));
+ error = BDF_Err_Missing_Font_Field;
+ goto Exit;
+ }
+
+ error = _bdf_list_split( &p->list, (char *)" +", line, linelen );
+ if ( error )
+ goto Exit;
+
+ p->font->point_size = _bdf_atoul( p->list.field[1], 0, 10 );
+ p->font->resolution_x = _bdf_atoul( p->list.field[2], 0, 10 );
+ p->font->resolution_y = _bdf_atoul( p->list.field[3], 0, 10 );
+
+ /* Check for the bits per pixel field. */
+ if ( p->list.used == 5 )
+ {
+ unsigned short bitcount, i, shift;
+
+
+ p->font->bpp = (unsigned short)_bdf_atos( p->list.field[4], 0, 10 );
+
+ /* Only values 1, 2, 4, 8 are allowed. */
+ shift = p->font->bpp;
+ bitcount = 0;
+ for ( i = 0; shift > 0; i++ )
+ {
+ if ( shift & 1 )
+ bitcount = i;
+ shift >>= 1;
+ }
+
+ shift = (short)( ( bitcount > 3 ) ? 8 : ( 1 << bitcount ) );
+
+ if ( p->font->bpp > shift || p->font->bpp != shift )
+ {
+ /* select next higher value */
+ p->font->bpp = (unsigned short)( shift << 1 );
+ FT_TRACE2(( "_bdf_parse_start: " ACMSG11, p->font->bpp ));
+ }
+ }
+ else
+ p->font->bpp = 1;
+
+ p->flags |= _BDF_SIZE;
+
+ goto Exit;
+ }
+
+ error = BDF_Err_Invalid_File_Format;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* API. */
+ /* */
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ bdf_load_font( FT_Stream stream,
+ FT_Memory extmemory,
+ bdf_options_t* opts,
+ bdf_font_t* *font )
+ {
+ unsigned long lineno = 0; /* make compiler happy */
+ _bdf_parse_t *p;
+
+ FT_Memory memory = extmemory;
+ FT_Error error = BDF_Err_Ok;
+
+
+ if ( FT_NEW( p ) )
+ goto Exit;
+
+ memory = NULL;
+ p->opts = (bdf_options_t*)( ( opts != 0 ) ? opts : &_bdf_opts );
+ p->minlb = 32767;
+ p->memory = extmemory; /* only during font creation */
+
+ _bdf_list_init( &p->list, extmemory );
+
+ error = _bdf_readstream( stream, _bdf_parse_start,
+ (void *)p, &lineno );
+ if ( error )
+ goto Fail;
+
+ if ( p->font != 0 )
+ {
+ /* If the font is not proportional, set the font's monowidth */
+ /* field to the width of the font bounding box. */
+ memory = p->font->memory;
+
+ if ( p->font->spacing != BDF_PROPORTIONAL )
+ p->font->monowidth = p->font->bbx.width;
+
+ /* If the number of glyphs loaded is not that of the original count, */
+ /* indicate the difference. */
+ if ( p->cnt != p->font->glyphs_used + p->font->unencoded_used )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG15, p->cnt,
+ p->font->glyphs_used + p->font->unencoded_used ));
+ p->font->modified = 1;
+ }
+
+ /* Once the font has been loaded, adjust the overall font metrics if */
+ /* necessary. */
+ if ( p->opts->correct_metrics != 0 &&
+ ( p->font->glyphs_used > 0 || p->font->unencoded_used > 0 ) )
+ {
+ if ( p->maxrb - p->minlb != p->font->bbx.width )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG3,
+ p->font->bbx.width, p->maxrb - p->minlb ));
+ p->font->bbx.width = (unsigned short)( p->maxrb - p->minlb );
+ p->font->modified = 1;
+ }
+
+ if ( p->font->bbx.x_offset != p->minlb )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG4,
+ p->font->bbx.x_offset, p->minlb ));
+ p->font->bbx.x_offset = p->minlb;
+ p->font->modified = 1;
+ }
+
+ if ( p->font->bbx.ascent != p->maxas )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG5,
+ p->font->bbx.ascent, p->maxas ));
+ p->font->bbx.ascent = p->maxas;
+ p->font->modified = 1;
+ }
+
+ if ( p->font->bbx.descent != p->maxds )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG6,
+ p->font->bbx.descent, p->maxds ));
+ p->font->bbx.descent = p->maxds;
+ p->font->bbx.y_offset = (short)( -p->maxds );
+ p->font->modified = 1;
+ }
+
+ if ( p->maxas + p->maxds != p->font->bbx.height )
+ {
+ FT_TRACE2(( "bdf_load_font: " ACMSG7,
+ p->font->bbx.height, p->maxas + p->maxds ));
+ p->font->bbx.height = (unsigned short)( p->maxas + p->maxds );
+ }
+
+ if ( p->flags & _BDF_SWIDTH_ADJ )
+ FT_TRACE2(( "bdf_load_font: " ACMSG8 ));
+ }
+ }
+
+ if ( p->flags & _BDF_START )
+ {
+ {
+ /* The ENDFONT field was never reached or did not exist. */
+ if ( !( p->flags & _BDF_GLYPHS ) )
+ {
+ /* Error happened while parsing header. */
+ FT_ERROR(( "bdf_load_font: " ERRMSG2, lineno ));
+ error = BDF_Err_Corrupted_Font_Header;
+ goto Exit;
+ }
+ else
+ {
+ /* Error happened when parsing glyphs. */
+ FT_ERROR(( "bdf_load_font: " ERRMSG3, lineno ));
+ error = BDF_Err_Corrupted_Font_Glyphs;
+ goto Exit;
+ }
+ }
+ }
+
+ if ( p->font != 0 )
+ {
+ /* Make sure the comments are NULL terminated if they exist. */
+ memory = p->font->memory;
+
+ if ( p->font->comments_len > 0 ) {
+ if ( FT_RENEW_ARRAY( p->font->comments,
+ p->font->comments_len,
+ p->font->comments_len + 1 ) )
+ goto Fail;
+
+ p->font->comments[p->font->comments_len] = 0;
+ }
+ }
+ else if ( error == BDF_Err_Ok )
+ error = BDF_Err_Invalid_File_Format;
+
+ *font = p->font;
+
+ Exit:
+ if ( p )
+ {
+ _bdf_list_done( &p->list );
+
+ memory = extmemory;
+
+ FT_FREE( p );
+ }
+
+ return error;
+
+ Fail:
+ bdf_free_font( p->font );
+
+ memory = extmemory;
+
+ FT_FREE( p->font );
+
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ bdf_free_font( bdf_font_t* font )
+ {
+ bdf_property_t* prop;
+ unsigned long i;
+ bdf_glyph_t* glyphs;
+ FT_Memory memory;
+
+
+ if ( font == 0 )
+ return;
+
+ memory = font->memory;
+
+ FT_FREE( font->name );
+
+ /* Free up the internal hash table of property names. */
+ if ( font->internal )
+ {
+ hash_free( (hashtable *)font->internal, memory );
+ FT_FREE( font->internal );
+ }
+
+ /* Free up the comment info. */
+ FT_FREE( font->comments );
+
+ /* Free up the properties. */
+ for ( i = 0; i < font->props_size; i++ )
+ {
+ if ( font->props[i].format == BDF_ATOM )
+ FT_FREE( font->props[i].value.atom );
+ }
+
+ FT_FREE( font->props );
+
+ /* Free up the character info. */
+ for ( i = 0, glyphs = font->glyphs;
+ i < font->glyphs_used; i++, glyphs++ )
+ {
+ FT_FREE( glyphs->name );
+ FT_FREE( glyphs->bitmap );
+ }
+
+ for ( i = 0, glyphs = font->unencoded; i < font->unencoded_used;
+ i++, glyphs++ )
+ {
+ FT_FREE( glyphs->name );
+ FT_FREE( glyphs->bitmap );
+ }
+
+ FT_FREE( font->glyphs );
+ FT_FREE( font->unencoded );
+
+ /* Free up the overflow storage if it was used. */
+ for ( i = 0, glyphs = font->overflow.glyphs;
+ i < font->overflow.glyphs_used; i++, glyphs++ )
+ {
+ FT_FREE( glyphs->name );
+ FT_FREE( glyphs->bitmap );
+ }
+
+ FT_FREE( font->overflow.glyphs );
+
+ /* bdf_cleanup */
+ hash_free( &(font->proptbl), memory );
+
+ /* Free up the user defined properties. */
+ for (prop = font->user_props, i = 0;
+ i < font->nuser_props; i++, prop++ )
+ {
+ FT_FREE( prop->name );
+ if ( prop->format == BDF_ATOM )
+ FT_FREE( prop->value.atom );
+ }
+
+ FT_FREE( font->user_props );
+
+ /* FREE( font ); */ /* XXX Fixme */
+ }
+
+
+ FT_LOCAL_DEF( bdf_property_t * )
+ bdf_get_font_property( bdf_font_t* font,
+ const char* name )
+ {
+ hashnode hn;
+
+
+ if ( font == 0 || font->props_size == 0 || name == 0 || *name == 0 )
+ return 0;
+
+ hn = hash_lookup( name, (hashtable *)font->internal );
+
+ return hn ? ( font->props + (unsigned long)hn->data ) : 0;
+ }
+
+
+/* END */
diff --git a/src/bdf/module.mk b/src/bdf/module.mk
new file mode 100644
index 0000000..dfaa274
--- /dev/null
+++ b/src/bdf/module.mk
@@ -0,0 +1,34 @@
+#
+# FreeType 2 BDF module definition
+#
+
+# Copyright 2001, 2002, 2006 by
+# Francesco Zappa Nardelli
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+FTMODULE_H_COMMANDS += BDF_DRIVER
+
+define BDF_DRIVER
+$(OPEN_DRIVER)bdf_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)bdf $(ECHO_DRIVER_DESC)bdf bitmap fonts$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/bdf/rules.mk b/src/bdf/rules.mk
new file mode 100644
index 0000000..25d98e5
--- /dev/null
+++ b/src/bdf/rules.mk
@@ -0,0 +1,80 @@
+#
+# FreeType 2 bdf driver configuration rules
+#
+
+
+# Copyright (C) 2001, 2002, 2003 by
+# Francesco Zappa Nardelli
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+
+
+# bdf driver directory
+#
+BDF_DIR := $(SRC_DIR)/bdf
+
+
+BDF_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(BDF_DIR))
+
+
+# bdf driver sources (i.e., C files)
+#
+BDF_DRV_SRC := $(BDF_DIR)/bdflib.c \
+ $(BDF_DIR)/bdfdrivr.c
+
+
+# bdf driver headers
+#
+BDF_DRV_H := $(BDF_DIR)/bdf.h \
+ $(BDF_DIR)/bdfdrivr.h
+
+# bdf driver object(s)
+#
+# BDF_DRV_OBJ_M is used during `multi' builds
+# BDF_DRV_OBJ_S is used during `single' builds
+#
+BDF_DRV_OBJ_M := $(BDF_DRV_SRC:$(BDF_DIR)/%.c=$(OBJ_DIR)/%.$O)
+BDF_DRV_OBJ_S := $(OBJ_DIR)/bdf.$O
+
+# bdf driver source file for single build
+#
+BDF_DRV_SRC_S := $(BDF_DIR)/bdf.c
+
+
+# bdf driver - single object
+#
+$(BDF_DRV_OBJ_S): $(BDF_DRV_SRC_S) $(BDF_DRV_SRC) $(FREETYPE_H) $(BDF_DRV_H)
+ $(BDF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BDF_DRV_SRC_S))
+
+
+# bdf driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(BDF_DIR)/%.c $(FREETYPE_H) $(BDF_DRV_H)
+ $(BDF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(BDF_DRV_OBJ_S)
+DRV_OBJS_M += $(BDF_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/cache/Jamfile b/src/cache/Jamfile
new file mode 100644
index 0000000..340cff7
--- /dev/null
+++ b/src/cache/Jamfile
@@ -0,0 +1,43 @@
+# FreeType 2 src/cache Jamfile
+#
+# Copyright 2001, 2003, 2004 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) cache ;
+
+# The file <freetype/ftcache.h> contains some macro definitions that are
+# later used in #include statements related to the cache sub-system. It
+# needs to be parsed through a HDRMACRO rule for macro definitions.
+#
+HDRMACRO [ FT2_SubDir include ftcache.h ] ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = ftcmru
+ ftcmanag
+ ftccache
+ ftcglyph
+ ftcsbits
+ ftcimage
+ ftcbasic
+ ftccmap
+ ;
+ }
+ else
+ {
+ _sources = ftcache ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/cache Jamfile
diff --git a/src/cache/ftcache.c b/src/cache/ftcache.c
new file mode 100644
index 0000000..d41e91e
--- /dev/null
+++ b/src/cache/ftcache.c
@@ -0,0 +1,31 @@
+/***************************************************************************/
+/* */
+/* ftcache.c */
+/* */
+/* The FreeType Caching sub-system (body only). */
+/* */
+/* Copyright 2000-2001, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "ftcmru.c"
+#include "ftcmanag.c"
+#include "ftccache.c"
+#include "ftccmap.c"
+#include "ftcglyph.c"
+#include "ftcimage.c"
+#include "ftcsbits.c"
+#include "ftcbasic.c"
+
+/* END */
diff --git a/src/cache/ftcbasic.c b/src/cache/ftcbasic.c
new file mode 100644
index 0000000..a568b97
--- /dev/null
+++ b/src/cache/ftcbasic.c
@@ -0,0 +1,811 @@
+/***************************************************************************/
+/* */
+/* ftcbasic.c */
+/* */
+/* The FreeType basic cache interface (body). */
+/* */
+/* Copyright 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcglyph.h"
+#include "ftcimage.h"
+#include "ftcsbits.h"
+#include FT_INTERNAL_MEMORY_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /*
+ * These structures correspond to the FTC_Font and FTC_ImageDesc types
+ * that were defined in version 2.1.7.
+ */
+ typedef struct FTC_OldFontRec_
+ {
+ FTC_FaceID face_id;
+ FT_UShort pix_width;
+ FT_UShort pix_height;
+
+ } FTC_OldFontRec, *FTC_OldFont;
+
+
+ typedef struct FTC_OldImageDescRec_
+ {
+ FTC_OldFontRec font;
+ FT_UInt32 flags;
+
+ } FTC_OldImageDescRec, *FTC_OldImageDesc;
+
+
+ /*
+ * Notice that FTC_OldImageDescRec and FTC_ImageTypeRec are nearly
+ * identical, bit-wise. The only difference is that the `width' and
+ * `height' fields are expressed as 16-bit integers in the old structure,
+ * and as normal `int' in the new one.
+ *
+ * We are going to perform a weird hack to detect which structure is
+ * being passed to the image and sbit caches. If the new structure's
+ * `width' is larger than 0x10000, we assume that we are really receiving
+ * an FTC_OldImageDesc.
+ */
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ /*
+ * Basic Families
+ *
+ */
+ typedef struct FTC_BasicAttrRec_
+ {
+ FTC_ScalerRec scaler;
+ FT_UInt load_flags;
+
+ } FTC_BasicAttrRec, *FTC_BasicAttrs;
+
+#define FTC_BASIC_ATTR_COMPARE( a, b ) \
+ FT_BOOL( FTC_SCALER_COMPARE( &(a)->scaler, &(b)->scaler ) && \
+ (a)->load_flags == (b)->load_flags )
+
+#define FTC_BASIC_ATTR_HASH( a ) \
+ ( FTC_SCALER_HASH( &(a)->scaler ) + 31*(a)->load_flags )
+
+
+ typedef struct FTC_BasicQueryRec_
+ {
+ FTC_GQueryRec gquery;
+ FTC_BasicAttrRec attrs;
+
+ } FTC_BasicQueryRec, *FTC_BasicQuery;
+
+
+ typedef struct FTC_BasicFamilyRec_
+ {
+ FTC_FamilyRec family;
+ FTC_BasicAttrRec attrs;
+
+ } FTC_BasicFamilyRec, *FTC_BasicFamily;
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_basic_family_compare( FTC_MruNode ftcfamily,
+ FT_Pointer ftcquery )
+ {
+ FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily;
+ FTC_BasicQuery query = (FTC_BasicQuery)ftcquery;
+
+
+ return FTC_BASIC_ATTR_COMPARE( &family->attrs, &query->attrs );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_basic_family_init( FTC_MruNode ftcfamily,
+ FT_Pointer ftcquery,
+ FT_Pointer ftccache )
+ {
+ FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily;
+ FTC_BasicQuery query = (FTC_BasicQuery)ftcquery;
+ FTC_Cache cache = (FTC_Cache)ftccache;
+
+
+ FTC_Family_Init( FTC_FAMILY( family ), cache );
+ family->attrs = query->attrs;
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ ftc_basic_family_get_count( FTC_Family ftcfamily,
+ FTC_Manager manager )
+ {
+ FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily;
+ FT_Error error;
+ FT_Face face;
+ FT_UInt result = 0;
+
+
+ error = FTC_Manager_LookupFace( manager, family->attrs.scaler.face_id,
+ &face );
+ if ( !error )
+ result = face->num_glyphs;
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_basic_family_load_bitmap( FTC_Family ftcfamily,
+ FT_UInt gindex,
+ FTC_Manager manager,
+ FT_Face *aface )
+ {
+ FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily;
+ FT_Error error;
+ FT_Size size;
+
+
+ error = FTC_Manager_LookupSize( manager, &family->attrs.scaler, &size );
+ if ( !error )
+ {
+ FT_Face face = size->face;
+
+
+ error = FT_Load_Glyph( face, gindex,
+ family->attrs.load_flags | FT_LOAD_RENDER );
+ if ( !error )
+ *aface = face;
+ }
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_basic_family_load_glyph( FTC_Family ftcfamily,
+ FT_UInt gindex,
+ FTC_Cache cache,
+ FT_Glyph *aglyph )
+ {
+ FTC_BasicFamily family = (FTC_BasicFamily)ftcfamily;
+ FT_Error error;
+ FTC_Scaler scaler = &family->attrs.scaler;
+ FT_Face face;
+ FT_Size size;
+
+
+ /* we will now load the glyph image */
+ error = FTC_Manager_LookupSize( cache->manager,
+ scaler,
+ &size );
+ if ( !error )
+ {
+ face = size->face;
+
+ error = FT_Load_Glyph( face, gindex, family->attrs.load_flags );
+ if ( !error )
+ {
+ if ( face->glyph->format == FT_GLYPH_FORMAT_BITMAP ||
+ face->glyph->format == FT_GLYPH_FORMAT_OUTLINE )
+ {
+ /* ok, copy it */
+ FT_Glyph glyph;
+
+
+ error = FT_Get_Glyph( face->glyph, &glyph );
+ if ( !error )
+ {
+ *aglyph = glyph;
+ goto Exit;
+ }
+ }
+ else
+ error = FTC_Err_Invalid_Argument;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_basic_gnode_compare_faceid( FTC_Node ftcgnode,
+ FT_Pointer ftcface_id,
+ FTC_Cache cache )
+ {
+ FTC_GNode gnode = (FTC_GNode)ftcgnode;
+ FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
+ FTC_BasicFamily family = (FTC_BasicFamily)gnode->family;
+ FT_Bool result;
+
+
+ result = FT_BOOL( family->attrs.scaler.face_id == face_id );
+ if ( result )
+ {
+ /* we must call this function to avoid this node from appearing
+ * in later lookups with the same face_id!
+ */
+ FTC_GNode_UnselectFamily( gnode, cache );
+ }
+ return result;
+ }
+
+
+ /*
+ *
+ * basic image cache
+ *
+ */
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_IFamilyClassRec ftc_basic_image_family_class =
+ {
+ {
+ sizeof ( FTC_BasicFamilyRec ),
+ ftc_basic_family_compare,
+ ftc_basic_family_init,
+ 0, /* FTC_MruNode_ResetFunc */
+ 0 /* FTC_MruNode_DoneFunc */
+ },
+ ftc_basic_family_load_glyph
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_GCacheClassRec ftc_basic_image_cache_class =
+ {
+ {
+ ftc_inode_new,
+ ftc_inode_weight,
+ ftc_gnode_compare,
+ ftc_basic_gnode_compare_faceid,
+ ftc_inode_free,
+
+ sizeof ( FTC_GCacheRec ),
+ ftc_gcache_init,
+ ftc_gcache_done
+ },
+ (FTC_MruListClass)&ftc_basic_image_family_class
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_ImageCache_New( FTC_Manager manager,
+ FTC_ImageCache *acache )
+ {
+ return FTC_GCache_New( manager, &ftc_basic_image_cache_class,
+ (FTC_GCache*)acache );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_ImageCache_Lookup( FTC_ImageCache cache,
+ FTC_ImageType type,
+ FT_UInt gindex,
+ FT_Glyph *aglyph,
+ FTC_Node *anode )
+ {
+ FTC_BasicQueryRec query;
+ FTC_INode node = 0; /* make compiler happy */
+ FT_Error error;
+ FT_UInt32 hash;
+
+
+ /* some argument checks are delayed to FTC_Cache_Lookup */
+ if ( !aglyph )
+ {
+ error = FTC_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ *aglyph = NULL;
+ if ( anode )
+ *anode = NULL;
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /*
+ * This one is a major hack used to detect whether we are passed a
+ * regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one.
+ */
+ if ( type->width >= 0x10000 )
+ {
+ FTC_OldImageDesc desc = (FTC_OldImageDesc)type;
+
+
+ query.attrs.scaler.face_id = desc->font.face_id;
+ query.attrs.scaler.width = desc->font.pix_width;
+ query.attrs.scaler.height = desc->font.pix_height;
+ query.attrs.load_flags = desc->flags;
+ }
+ else
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+ {
+ query.attrs.scaler.face_id = type->face_id;
+ query.attrs.scaler.width = type->width;
+ query.attrs.scaler.height = type->height;
+ query.attrs.load_flags = type->flags;
+ }
+
+ query.attrs.scaler.pixel = 1;
+ query.attrs.scaler.x_res = 0; /* make compilers happy */
+ query.attrs.scaler.y_res = 0;
+
+ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
+
+#if 1 /* inlining is about 50% faster! */
+ FTC_GCACHE_LOOKUP_CMP( cache,
+ ftc_basic_family_compare,
+ FTC_GNode_Compare,
+ hash, gindex,
+ &query,
+ node,
+ error );
+#else
+ error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
+ hash, gindex,
+ FTC_GQUERY( &query ),
+ (FTC_Node*) &node );
+#endif
+ if ( !error )
+ {
+ *aglyph = FTC_INODE( node )->glyph;
+
+ if ( anode )
+ {
+ *anode = FTC_NODE( node );
+ FTC_NODE( node )->ref_count++;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_ImageCache_LookupScaler( FTC_ImageCache cache,
+ FTC_Scaler scaler,
+ FT_ULong load_flags,
+ FT_UInt gindex,
+ FT_Glyph *aglyph,
+ FTC_Node *anode )
+ {
+ FTC_BasicQueryRec query;
+ FTC_INode node = 0; /* make compiler happy */
+ FT_Error error;
+ FT_UInt32 hash;
+
+
+ /* some argument checks are delayed to FTC_Cache_Lookup */
+ if ( !aglyph || !scaler )
+ {
+ error = FTC_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ *aglyph = NULL;
+ if ( anode )
+ *anode = NULL;
+
+ query.attrs.scaler = scaler[0];
+ query.attrs.load_flags = load_flags;
+
+ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) + gindex;
+
+ FTC_GCACHE_LOOKUP_CMP( cache,
+ ftc_basic_family_compare,
+ FTC_GNode_Compare,
+ hash, gindex,
+ &query,
+ node,
+ error );
+ if ( !error )
+ {
+ *aglyph = FTC_INODE( node )->glyph;
+
+ if ( anode )
+ {
+ *anode = FTC_NODE( node );
+ FTC_NODE( node )->ref_count++;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /* yet another backwards-legacy structure */
+ typedef struct FTC_OldImage_Desc_
+ {
+ FTC_FontRec font;
+ FT_UInt image_type;
+
+ } FTC_OldImage_Desc;
+
+
+#define FTC_OLD_IMAGE_FORMAT( x ) ( (x) & 7 )
+
+
+#define ftc_old_image_format_bitmap 0x0000
+#define ftc_old_image_format_outline 0x0001
+
+#define ftc_old_image_format_mask 0x000F
+
+#define ftc_old_image_flag_monochrome 0x0010
+#define ftc_old_image_flag_unhinted 0x0020
+#define ftc_old_image_flag_autohinted 0x0040
+#define ftc_old_image_flag_unscaled 0x0080
+#define ftc_old_image_flag_no_sbits 0x0100
+
+ /* monochrome bitmap */
+#define ftc_old_image_mono ftc_old_image_format_bitmap | \
+ ftc_old_image_flag_monochrome
+
+ /* anti-aliased bitmap */
+#define ftc_old_image_grays ftc_old_image_format_bitmap
+
+ /* scaled outline */
+#define ftc_old_image_outline ftc_old_image_format_outline
+
+
+ static void
+ ftc_image_type_from_old_desc( FTC_ImageType typ,
+ FTC_OldImage_Desc* desc )
+ {
+ typ->face_id = desc->font.face_id;
+ typ->width = desc->font.pix_width;
+ typ->height = desc->font.pix_height;
+
+ /* convert image type flags to load flags */
+ {
+ FT_UInt load_flags = FT_LOAD_DEFAULT;
+ FT_UInt type = desc->image_type;
+
+
+ /* determine load flags, depending on the font description's */
+ /* image type */
+
+ if ( FTC_OLD_IMAGE_FORMAT( type ) == ftc_old_image_format_bitmap )
+ {
+ if ( type & ftc_old_image_flag_monochrome )
+ load_flags |= FT_LOAD_MONOCHROME;
+
+ /* disable embedded bitmaps loading if necessary */
+ if ( type & ftc_old_image_flag_no_sbits )
+ load_flags |= FT_LOAD_NO_BITMAP;
+ }
+ else
+ {
+ /* we want an outline, don't load embedded bitmaps */
+ load_flags |= FT_LOAD_NO_BITMAP;
+
+ if ( type & ftc_old_image_flag_unscaled )
+ load_flags |= FT_LOAD_NO_SCALE;
+ }
+
+ /* always render glyphs to bitmaps */
+ load_flags |= FT_LOAD_RENDER;
+
+ if ( type & ftc_old_image_flag_unhinted )
+ load_flags |= FT_LOAD_NO_HINTING;
+
+ if ( type & ftc_old_image_flag_autohinted )
+ load_flags |= FT_LOAD_FORCE_AUTOHINT;
+
+ typ->flags = load_flags;
+ }
+ }
+
+
+ FT_EXPORT( FT_Error )
+ FTC_Image_Cache_New( FTC_Manager manager,
+ FTC_ImageCache *acache );
+
+ FT_EXPORT( FT_Error )
+ FTC_Image_Cache_Lookup( FTC_ImageCache icache,
+ FTC_OldImage_Desc* desc,
+ FT_UInt gindex,
+ FT_Glyph *aglyph );
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Image_Cache_New( FTC_Manager manager,
+ FTC_ImageCache *acache )
+ {
+ return FTC_ImageCache_New( manager, (FTC_ImageCache*)acache );
+ }
+
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Image_Cache_Lookup( FTC_ImageCache icache,
+ FTC_OldImage_Desc* desc,
+ FT_UInt gindex,
+ FT_Glyph *aglyph )
+ {
+ FTC_ImageTypeRec type0;
+
+
+ if ( !desc )
+ return FTC_Err_Invalid_Argument;
+
+ ftc_image_type_from_old_desc( &type0, desc );
+
+ return FTC_ImageCache_Lookup( (FTC_ImageCache)icache,
+ &type0,
+ gindex,
+ aglyph,
+ NULL );
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ /*
+ *
+ * basic small bitmap cache
+ *
+ */
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_SFamilyClassRec ftc_basic_sbit_family_class =
+ {
+ {
+ sizeof( FTC_BasicFamilyRec ),
+ ftc_basic_family_compare,
+ ftc_basic_family_init,
+ 0, /* FTC_MruNode_ResetFunc */
+ 0 /* FTC_MruNode_DoneFunc */
+ },
+ ftc_basic_family_get_count,
+ ftc_basic_family_load_bitmap
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_GCacheClassRec ftc_basic_sbit_cache_class =
+ {
+ {
+ ftc_snode_new,
+ ftc_snode_weight,
+ ftc_snode_compare,
+ ftc_basic_gnode_compare_faceid,
+ ftc_snode_free,
+
+ sizeof ( FTC_GCacheRec ),
+ ftc_gcache_init,
+ ftc_gcache_done
+ },
+ (FTC_MruListClass)&ftc_basic_sbit_family_class
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_SBitCache_New( FTC_Manager manager,
+ FTC_SBitCache *acache )
+ {
+ return FTC_GCache_New( manager, &ftc_basic_sbit_cache_class,
+ (FTC_GCache*)acache );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_SBitCache_Lookup( FTC_SBitCache cache,
+ FTC_ImageType type,
+ FT_UInt gindex,
+ FTC_SBit *ansbit,
+ FTC_Node *anode )
+ {
+ FT_Error error;
+ FTC_BasicQueryRec query;
+ FTC_SNode node = 0; /* make compiler happy */
+ FT_UInt32 hash;
+
+
+ if ( anode )
+ *anode = NULL;
+
+ /* other argument checks delayed to FTC_Cache_Lookup */
+ if ( !ansbit )
+ return FTC_Err_Invalid_Argument;
+
+ *ansbit = NULL;
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /* This one is a major hack used to detect whether we are passed a
+ * regular FTC_ImageType handle, or a legacy FTC_OldImageDesc one.
+ */
+ if ( type->width >= 0x10000 )
+ {
+ FTC_OldImageDesc desc = (FTC_OldImageDesc)type;
+
+
+ query.attrs.scaler.face_id = desc->font.face_id;
+ query.attrs.scaler.width = desc->font.pix_width;
+ query.attrs.scaler.height = desc->font.pix_height;
+ query.attrs.load_flags = desc->flags;
+ }
+ else
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+ {
+ query.attrs.scaler.face_id = type->face_id;
+ query.attrs.scaler.width = type->width;
+ query.attrs.scaler.height = type->height;
+ query.attrs.load_flags = type->flags;
+ }
+
+ query.attrs.scaler.pixel = 1;
+ query.attrs.scaler.x_res = 0; /* make compilers happy */
+ query.attrs.scaler.y_res = 0;
+
+ /* beware, the hash must be the same for all glyph ranges! */
+ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
+ gindex / FTC_SBIT_ITEMS_PER_NODE;
+
+#if 1 /* inlining is about 50% faster! */
+ FTC_GCACHE_LOOKUP_CMP( cache,
+ ftc_basic_family_compare,
+ FTC_SNode_Compare,
+ hash, gindex,
+ &query,
+ node,
+ error );
+#else
+ error = FTC_GCache_Lookup( FTC_GCACHE( cache ),
+ hash,
+ gindex,
+ FTC_GQUERY( &query ),
+ (FTC_Node*)&node );
+#endif
+ if ( error )
+ goto Exit;
+
+ *ansbit = node->sbits + ( gindex - FTC_GNODE( node )->gindex );
+
+ if ( anode )
+ {
+ *anode = FTC_NODE( node );
+ FTC_NODE( node )->ref_count++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_SBitCache_LookupScaler( FTC_SBitCache cache,
+ FTC_Scaler scaler,
+ FT_ULong load_flags,
+ FT_UInt gindex,
+ FTC_SBit *ansbit,
+ FTC_Node *anode )
+ {
+ FT_Error error;
+ FTC_BasicQueryRec query;
+ FTC_SNode node = 0; /* make compiler happy */
+ FT_UInt32 hash;
+
+
+ if ( anode )
+ *anode = NULL;
+
+ /* other argument checks delayed to FTC_Cache_Lookup */
+ if ( !ansbit || !scaler )
+ return FTC_Err_Invalid_Argument;
+
+ *ansbit = NULL;
+
+ query.attrs.scaler = scaler[0];
+ query.attrs.load_flags = load_flags;
+
+ /* beware, the hash must be the same for all glyph ranges! */
+ hash = FTC_BASIC_ATTR_HASH( &query.attrs ) +
+ gindex / FTC_SBIT_ITEMS_PER_NODE;
+
+ FTC_GCACHE_LOOKUP_CMP( cache,
+ ftc_basic_family_compare,
+ FTC_SNode_Compare,
+ hash, gindex,
+ &query,
+ node,
+ error );
+ if ( error )
+ goto Exit;
+
+ *ansbit = node->sbits + ( gindex - FTC_GNODE( node )->gindex );
+
+ if ( anode )
+ {
+ *anode = FTC_NODE( node );
+ FTC_NODE( node )->ref_count++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_EXPORT( FT_Error )
+ FTC_SBit_Cache_New( FTC_Manager manager,
+ FTC_SBitCache *acache );
+
+ FT_EXPORT( FT_Error )
+ FTC_SBit_Cache_Lookup( FTC_SBitCache cache,
+ FTC_OldImage_Desc* desc,
+ FT_UInt gindex,
+ FTC_SBit *ansbit );
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_SBit_Cache_New( FTC_Manager manager,
+ FTC_SBitCache *acache )
+ {
+ return FTC_SBitCache_New( manager, (FTC_SBitCache*)acache );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_SBit_Cache_Lookup( FTC_SBitCache cache,
+ FTC_OldImage_Desc* desc,
+ FT_UInt gindex,
+ FTC_SBit *ansbit )
+ {
+ FTC_ImageTypeRec type0;
+
+
+ if ( !desc )
+ return FT_Err_Invalid_Argument;
+
+ ftc_image_type_from_old_desc( &type0, desc );
+
+ return FTC_SBitCache_Lookup( (FTC_SBitCache)cache,
+ &type0,
+ gindex,
+ ansbit,
+ NULL );
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+/* END */
diff --git a/src/cache/ftccache.c b/src/cache/ftccache.c
new file mode 100644
index 0000000..f3e699c
--- /dev/null
+++ b/src/cache/ftccache.c
@@ -0,0 +1,592 @@
+/***************************************************************************/
+/* */
+/* ftccache.c */
+/* */
+/* The FreeType internal cache interface (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "ftcmanag.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+#define FTC_HASH_MAX_LOAD 2
+#define FTC_HASH_MIN_LOAD 1
+#define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
+
+/* this one _must_ be a power of 2! */
+#define FTC_HASH_INITIAL_SIZE 8
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE NODE DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* add a new node to the head of the manager's circular MRU list */
+ static void
+ ftc_node_mru_link( FTC_Node node,
+ FTC_Manager manager )
+ {
+ void *nl = &manager->nodes_list;
+
+
+ FTC_MruNode_Prepend( (FTC_MruNode*)nl,
+ (FTC_MruNode)node );
+ manager->num_nodes++;
+ }
+
+
+ /* remove a node from the manager's MRU list */
+ static void
+ ftc_node_mru_unlink( FTC_Node node,
+ FTC_Manager manager )
+ {
+ void *nl = &manager->nodes_list;
+
+
+ FTC_MruNode_Remove( (FTC_MruNode*)nl,
+ (FTC_MruNode)node );
+ manager->num_nodes--;
+ }
+
+
+#ifndef FTC_INLINE
+
+ /* move a node to the head of the manager's MRU list */
+ static void
+ ftc_node_mru_up( FTC_Node node,
+ FTC_Manager manager )
+ {
+ FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
+ (FTC_MruNode)node );
+ }
+
+#endif /* !FTC_INLINE */
+
+
+ /* Note that this function cannot fail. If we cannot re-size the
+ * buckets array appropriately, we simply degrade the hash table's
+ * performance!
+ */
+ static void
+ ftc_cache_resize( FTC_Cache cache )
+ {
+ for (;;)
+ {
+ FTC_Node node, *pnode;
+ FT_UInt p = cache->p;
+ FT_UInt mask = cache->mask;
+ FT_UInt count = mask + p + 1; /* number of buckets */
+
+
+ /* do we need to shrink the buckets array? */
+ if ( cache->slack < 0 )
+ {
+ FTC_Node new_list = NULL;
+
+
+ /* try to expand the buckets array _before_ splitting
+ * the bucket lists
+ */
+ if ( p >= mask )
+ {
+ FT_Memory memory = cache->memory;
+ FT_Error error;
+
+
+ /* if we can't expand the array, leave immediately */
+ if ( FT_RENEW_ARRAY( cache->buckets, (mask+1)*2, (mask+1)*4 ) )
+ break;
+ }
+
+ /* split a single bucket */
+ pnode = cache->buckets + p;
+
+ for (;;)
+ {
+ node = *pnode;
+ if ( node == NULL )
+ break;
+
+ if ( node->hash & ( mask + 1 ) )
+ {
+ *pnode = node->link;
+ node->link = new_list;
+ new_list = node;
+ }
+ else
+ pnode = &node->link;
+ }
+
+ cache->buckets[p + mask + 1] = new_list;
+
+ cache->slack += FTC_HASH_MAX_LOAD;
+
+ if ( p >= mask )
+ {
+ cache->mask = 2 * mask + 1;
+ cache->p = 0;
+ }
+ else
+ cache->p = p + 1;
+ }
+
+ /* do we need to expand the buckets array? */
+ else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
+ {
+ FT_UInt old_index = p + mask;
+ FTC_Node* pold;
+
+
+ if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
+ break;
+
+ if ( p == 0 )
+ {
+ FT_Memory memory = cache->memory;
+ FT_Error error;
+
+
+ /* if we can't shrink the array, leave immediately */
+ if ( FT_RENEW_ARRAY( cache->buckets,
+ ( mask + 1 ) * 2, mask + 1 ) )
+ break;
+
+ cache->mask >>= 1;
+ p = cache->mask;
+ }
+ else
+ p--;
+
+ pnode = cache->buckets + p;
+ while ( *pnode )
+ pnode = &(*pnode)->link;
+
+ pold = cache->buckets + old_index;
+ *pnode = *pold;
+ *pold = NULL;
+
+ cache->slack -= FTC_HASH_MAX_LOAD;
+ cache->p = p;
+ }
+ else /* the hash table is balanced */
+ break;
+ }
+ }
+
+
+ /* remove a node from its cache's hash table */
+ static void
+ ftc_node_hash_unlink( FTC_Node node0,
+ FTC_Cache cache )
+ {
+ FTC_Node *pnode;
+ FT_UInt idx;
+
+
+ idx = (FT_UInt)( node0->hash & cache->mask );
+ if ( idx < cache->p )
+ idx = (FT_UInt)( node0->hash & ( 2 * cache->mask + 1 ) );
+
+ pnode = cache->buckets + idx;
+
+ for (;;)
+ {
+ FTC_Node node = *pnode;
+
+
+ if ( node == NULL )
+ {
+ FT_ERROR(( "ftc_node_hash_unlink: unknown node!\n" ));
+ return;
+ }
+
+ if ( node == node0 )
+ break;
+
+ pnode = &(*pnode)->link;
+ }
+
+ *pnode = node0->link;
+ node0->link = NULL;
+
+ cache->slack++;
+ ftc_cache_resize( cache );
+ }
+
+
+ /* add a node to the `top' of its cache's hash table */
+ static void
+ ftc_node_hash_link( FTC_Node node,
+ FTC_Cache cache )
+ {
+ FTC_Node *pnode;
+ FT_UInt idx;
+
+
+ idx = (FT_UInt)( node->hash & cache->mask );
+ if ( idx < cache->p )
+ idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) );
+
+ pnode = cache->buckets + idx;
+
+ node->link = *pnode;
+ *pnode = node;
+
+ cache->slack--;
+ ftc_cache_resize( cache );
+ }
+
+
+ /* remove a node from the cache manager */
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ FT_BASE_DEF( void )
+#else
+ FT_LOCAL_DEF( void )
+#endif
+ ftc_node_destroy( FTC_Node node,
+ FTC_Manager manager )
+ {
+ FTC_Cache cache;
+
+
+#ifdef FT_DEBUG_ERROR
+ /* find node's cache */
+ if ( node->cache_index >= manager->num_caches )
+ {
+ FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
+ return;
+ }
+#endif
+
+ cache = manager->caches[node->cache_index];
+
+#ifdef FT_DEBUG_ERROR
+ if ( cache == NULL )
+ {
+ FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
+ return;
+ }
+#endif
+
+ manager->cur_weight -= cache->clazz.node_weight( node, cache );
+
+ /* remove node from mru list */
+ ftc_node_mru_unlink( node, manager );
+
+ /* remove node from cache's hash table */
+ ftc_node_hash_unlink( node, cache );
+
+ /* now finalize it */
+ cache->clazz.node_free( node, cache );
+
+#if 0
+ /* check, just in case of general corruption :-) */
+ if ( manager->num_nodes == 0 )
+ FT_ERROR(( "ftc_node_destroy: invalid cache node count! = %d\n",
+ manager->num_nodes ));
+#endif
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** ABSTRACT CACHE CLASS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_Cache_Init( FTC_Cache cache )
+ {
+ return ftc_cache_init( cache );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ftc_cache_init( FTC_Cache cache )
+ {
+ FT_Memory memory = cache->memory;
+ FT_Error error;
+
+
+ cache->p = 0;
+ cache->mask = FTC_HASH_INITIAL_SIZE - 1;
+ cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
+
+ (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
+ return error;
+ }
+
+
+ static void
+ FTC_Cache_Clear( FTC_Cache cache )
+ {
+ if ( cache )
+ {
+ FTC_Manager manager = cache->manager;
+ FT_UFast i;
+ FT_UInt count;
+
+
+ count = cache->p + cache->mask + 1;
+
+ for ( i = 0; i < count; i++ )
+ {
+ FTC_Node *pnode = cache->buckets + i, next, node = *pnode;
+
+
+ while ( node )
+ {
+ next = node->link;
+ node->link = NULL;
+
+ /* remove node from mru list */
+ ftc_node_mru_unlink( node, manager );
+
+ /* now finalize it */
+ manager->cur_weight -= cache->clazz.node_weight( node, cache );
+
+ cache->clazz.node_free( node, cache );
+ node = next;
+ }
+ cache->buckets[i] = NULL;
+ }
+ ftc_cache_resize( cache );
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ftc_cache_done( FTC_Cache cache )
+ {
+ if ( cache->memory )
+ {
+ FT_Memory memory = cache->memory;
+
+
+ FTC_Cache_Clear( cache );
+
+ FT_FREE( cache->buckets );
+ cache->mask = 0;
+ cache->p = 0;
+ cache->slack = 0;
+
+ cache->memory = NULL;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_Cache_Done( FTC_Cache cache )
+ {
+ ftc_cache_done( cache );
+ }
+
+
+ static void
+ ftc_cache_add( FTC_Cache cache,
+ FT_UInt32 hash,
+ FTC_Node node )
+ {
+ node->hash = hash;
+ node->cache_index = (FT_UInt16) cache->index;
+ node->ref_count = 0;
+
+ ftc_node_hash_link( node, cache );
+ ftc_node_mru_link( node, cache->manager );
+
+ {
+ FTC_Manager manager = cache->manager;
+
+
+ manager->cur_weight += cache->clazz.node_weight( node, cache );
+
+ if ( manager->cur_weight >= manager->max_weight )
+ {
+ node->ref_count++;
+ FTC_Manager_Compress( manager );
+ node->ref_count--;
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_Cache_NewNode( FTC_Cache cache,
+ FT_UInt32 hash,
+ FT_Pointer query,
+ FTC_Node *anode )
+ {
+ FT_Error error;
+ FTC_Node node;
+
+
+ /*
+ * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
+ * errors (OOM) correctly, i.e., by flushing the cache progressively
+ * in order to make more room.
+ */
+
+ FTC_CACHE_TRYLOOP( cache )
+ {
+ error = cache->clazz.node_new( &node, query, cache );
+ }
+ FTC_CACHE_TRYLOOP_END();
+
+ if ( error )
+ node = NULL;
+ else
+ {
+ /* don't assume that the cache has the same number of buckets, since
+ * our allocation request might have triggered global cache flushing
+ */
+ ftc_cache_add( cache, hash, node );
+ }
+
+ *anode = node;
+ return error;
+ }
+
+
+#ifndef FTC_INLINE
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_Cache_Lookup( FTC_Cache cache,
+ FT_UInt32 hash,
+ FT_Pointer query,
+ FTC_Node *anode )
+ {
+ FT_UFast idx;
+ FTC_Node* bucket;
+ FTC_Node* pnode;
+ FTC_Node node;
+ FT_Error error = 0;
+
+ FTC_Node_CompareFunc compare = cache->clazz.node_compare;
+
+
+ if ( cache == NULL || anode == NULL )
+ return FT_Err_Invalid_Argument;
+
+ idx = hash & cache->mask;
+ if ( idx < cache->p )
+ idx = hash & ( cache->mask * 2 + 1 );
+
+ bucket = cache->buckets + idx;
+ pnode = bucket;
+ for (;;)
+ {
+ node = *pnode;
+ if ( node == NULL )
+ goto NewNode;
+
+ if ( node->hash == hash && compare( node, query, cache ) )
+ break;
+
+ pnode = &node->link;
+ }
+
+ if ( node != *bucket )
+ {
+ *pnode = node->link;
+ node->link = *bucket;
+ *bucket = node;
+ }
+
+ /* move to head of MRU list */
+ {
+ FTC_Manager manager = cache->manager;
+
+
+ if ( node != manager->nodes_list )
+ ftc_node_mru_up( node, manager );
+ }
+ *anode = node;
+ return error;
+
+ NewNode:
+ return FTC_Cache_NewNode( cache, hash, query, anode );
+ }
+
+#endif /* !FTC_INLINE */
+
+
+ FT_LOCAL_DEF( void )
+ FTC_Cache_RemoveFaceID( FTC_Cache cache,
+ FTC_FaceID face_id )
+ {
+ FT_UFast i, count;
+ FTC_Manager manager = cache->manager;
+ FTC_Node frees = NULL;
+
+
+ count = cache->p + cache->mask;
+ for ( i = 0; i < count; i++ )
+ {
+ FTC_Node* bucket = cache->buckets + i;
+ FTC_Node* pnode = bucket;
+
+
+ for ( ;; )
+ {
+ FTC_Node node = *pnode;
+
+
+ if ( node == NULL )
+ break;
+
+ if ( cache->clazz.node_remove_faceid( node, face_id, cache ) )
+ {
+ *pnode = node->link;
+ node->link = frees;
+ frees = node;
+ }
+ else
+ pnode = &node->link;
+ }
+ }
+
+ /* remove all nodes in the free list */
+ while ( frees )
+ {
+ FTC_Node node;
+
+
+ node = frees;
+ frees = node->link;
+
+ manager->cur_weight -= cache->clazz.node_weight( node, cache );
+ ftc_node_mru_unlink( node, manager );
+
+ cache->clazz.node_free( node, cache );
+
+ cache->slack++;
+ }
+
+ ftc_cache_resize( cache );
+ }
+
+
+/* END */
diff --git a/src/cache/ftccache.h b/src/cache/ftccache.h
new file mode 100644
index 0000000..8c0a7c9
--- /dev/null
+++ b/src/cache/ftccache.h
@@ -0,0 +1,317 @@
+/***************************************************************************/
+/* */
+/* ftccache.h */
+/* */
+/* FreeType internal cache interface (specification). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTCCACHE_H__
+#define __FTCCACHE_H__
+
+
+#include "ftcmru.h"
+
+FT_BEGIN_HEADER
+
+ /* handle to cache object */
+ typedef struct FTC_CacheRec_* FTC_Cache;
+
+ /* handle to cache class */
+ typedef const struct FTC_CacheClassRec_* FTC_CacheClass;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE NODE DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* Each cache controls one or more cache nodes. Each node is part of */
+ /* the global_lru list of the manager. Its `data' field however is used */
+ /* as a reference count for now. */
+ /* */
+ /* A node can be anything, depending on the type of information held by */
+ /* the cache. It can be an individual glyph image, a set of bitmaps */
+ /* glyphs for a given size, some metrics, etc. */
+ /* */
+ /*************************************************************************/
+
+ /* structure size should be 20 bytes on 32-bits machines */
+ typedef struct FTC_NodeRec_
+ {
+ FTC_MruNodeRec mru; /* circular mru list pointer */
+ FTC_Node link; /* used for hashing */
+ FT_UInt32 hash; /* used for hashing too */
+ FT_UShort cache_index; /* index of cache the node belongs to */
+ FT_Short ref_count; /* reference count for this node */
+
+ } FTC_NodeRec;
+
+
+#define FTC_NODE( x ) ( (FTC_Node)(x) )
+#define FTC_NODE_P( x ) ( (FTC_Node*)(x) )
+
+#define FTC_NODE__NEXT( x ) FTC_NODE( (x)->mru.next )
+#define FTC_NODE__PREV( x ) FTC_NODE( (x)->mru.prev )
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ FT_BASE( void )
+ ftc_node_destroy( FTC_Node node,
+ FTC_Manager manager );
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE DEFINITIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* initialize a new cache node */
+ typedef FT_Error
+ (*FTC_Node_NewFunc)( FTC_Node *pnode,
+ FT_Pointer query,
+ FTC_Cache cache );
+
+ typedef FT_ULong
+ (*FTC_Node_WeightFunc)( FTC_Node node,
+ FTC_Cache cache );
+
+ /* compare a node to a given key pair */
+ typedef FT_Bool
+ (*FTC_Node_CompareFunc)( FTC_Node node,
+ FT_Pointer key,
+ FTC_Cache cache );
+
+
+ typedef void
+ (*FTC_Node_FreeFunc)( FTC_Node node,
+ FTC_Cache cache );
+
+ typedef FT_Error
+ (*FTC_Cache_InitFunc)( FTC_Cache cache );
+
+ typedef void
+ (*FTC_Cache_DoneFunc)( FTC_Cache cache );
+
+
+ typedef struct FTC_CacheClassRec_
+ {
+ FTC_Node_NewFunc node_new;
+ FTC_Node_WeightFunc node_weight;
+ FTC_Node_CompareFunc node_compare;
+ FTC_Node_CompareFunc node_remove_faceid;
+ FTC_Node_FreeFunc node_free;
+
+ FT_UInt cache_size;
+ FTC_Cache_InitFunc cache_init;
+ FTC_Cache_DoneFunc cache_done;
+
+ } FTC_CacheClassRec;
+
+
+ /* each cache really implements a dynamic hash table to manage its nodes */
+ typedef struct FTC_CacheRec_
+ {
+ FT_UFast p;
+ FT_UFast mask;
+ FT_Long slack;
+ FTC_Node* buckets;
+
+ FTC_CacheClassRec clazz; /* local copy, for speed */
+
+ FTC_Manager manager;
+ FT_Memory memory;
+ FT_UInt index; /* in manager's table */
+
+ FTC_CacheClass org_class; /* original class pointer */
+
+ } FTC_CacheRec;
+
+
+#define FTC_CACHE( x ) ( (FTC_Cache)(x) )
+#define FTC_CACHE_P( x ) ( (FTC_Cache*)(x) )
+
+
+ /* default cache initialize */
+ FT_LOCAL( FT_Error )
+ FTC_Cache_Init( FTC_Cache cache );
+
+ /* default cache finalizer */
+ FT_LOCAL( void )
+ FTC_Cache_Done( FTC_Cache cache );
+
+ /* Call this function to lookup the cache. If no corresponding
+ * node is found, a new one is automatically created. This function
+ * is capable of flushing the cache adequately to make room for the
+ * new cache object.
+ */
+
+#ifndef FTC_INLINE
+ FT_LOCAL( FT_Error )
+ FTC_Cache_Lookup( FTC_Cache cache,
+ FT_UInt32 hash,
+ FT_Pointer query,
+ FTC_Node *anode );
+#endif
+
+ FT_LOCAL( FT_Error )
+ FTC_Cache_NewNode( FTC_Cache cache,
+ FT_UInt32 hash,
+ FT_Pointer query,
+ FTC_Node *anode );
+
+ /* Remove all nodes that relate to a given face_id. This is useful
+ * when un-installing fonts. Note that if a cache node relates to
+ * the face_id, but is locked (i.e., has `ref_count > 0'), the node
+ * will _not_ be destroyed, but its internal face_id reference will
+ * be modified.
+ *
+ * The final result will be that the node will never come back
+ * in further lookup requests, and will be flushed on demand from
+ * the cache normally when its reference count reaches 0.
+ */
+ FT_LOCAL( void )
+ FTC_Cache_RemoveFaceID( FTC_Cache cache,
+ FTC_FaceID face_id );
+
+
+#ifdef FTC_INLINE
+
+#define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \
+ FT_BEGIN_STMNT \
+ FTC_Node *_bucket, *_pnode, _node; \
+ FTC_Cache _cache = FTC_CACHE(cache); \
+ FT_UInt32 _hash = (FT_UInt32)(hash); \
+ FTC_Node_CompareFunc _nodcomp = (FTC_Node_CompareFunc)(nodecmp); \
+ FT_UInt _idx; \
+ \
+ \
+ error = 0; \
+ node = NULL; \
+ _idx = _hash & _cache->mask; \
+ if ( _idx < _cache->p ) \
+ _idx = _hash & ( _cache->mask*2 + 1 ); \
+ \
+ _bucket = _pnode = _cache->buckets + _idx; \
+ for (;;) \
+ { \
+ _node = *_pnode; \
+ if ( _node == NULL ) \
+ goto _NewNode; \
+ \
+ if ( _node->hash == _hash && _nodcomp( _node, query, _cache ) ) \
+ break; \
+ \
+ _pnode = &_node->link; \
+ } \
+ \
+ if ( _node != *_bucket ) \
+ { \
+ *_pnode = _node->link; \
+ _node->link = *_bucket; \
+ *_bucket = _node; \
+ } \
+ \
+ { \
+ FTC_Manager _manager = _cache->manager; \
+ void* _nl = &_manager->nodes_list; \
+ \
+ \
+ if ( _node != _manager->nodes_list ) \
+ FTC_MruNode_Up( (FTC_MruNode*)_nl, \
+ (FTC_MruNode)_node ); \
+ } \
+ goto _Ok; \
+ \
+ _NewNode: \
+ error = FTC_Cache_NewNode( _cache, _hash, query, &_node ); \
+ \
+ _Ok: \
+ _pnode = (FTC_Node*)(void*)&(node); \
+ *_pnode = _node; \
+ FT_END_STMNT
+
+#else /* !FTC_INLINE */
+
+#define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \
+ FT_BEGIN_STMNT \
+ error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query, \
+ (FTC_Node*)&(node) ); \
+ FT_END_STMNT
+
+#endif /* !FTC_INLINE */
+
+
+ /*
+ * This macro, together with FTC_CACHE_TRYLOOP_END, defines a retry
+ * loop to flush the cache repeatedly in case of memory overflows.
+ *
+ * It is used when creating a new cache node, or within a lookup
+ * that needs to allocate data (e.g., the sbit cache lookup).
+ *
+ * Example:
+ *
+ * {
+ * FTC_CACHE_TRYLOOP( cache )
+ * error = load_data( ... );
+ * FTC_CACHE_TRYLOOP_END()
+ * }
+ *
+ */
+#define FTC_CACHE_TRYLOOP( cache ) \
+ { \
+ FTC_Manager _try_manager = FTC_CACHE( cache )->manager; \
+ FT_UInt _try_count = 4; \
+ \
+ \
+ for (;;) \
+ { \
+ FT_UInt _try_done;
+
+
+#define FTC_CACHE_TRYLOOP_END() \
+ if ( !error || error != FT_Err_Out_Of_Memory ) \
+ break; \
+ \
+ _try_done = FTC_Manager_FlushN( _try_manager, _try_count ); \
+ if ( _try_done == 0 ) \
+ break; \
+ \
+ if ( _try_done == _try_count ) \
+ { \
+ _try_count *= 2; \
+ if ( _try_count < _try_done || \
+ _try_count > _try_manager->num_nodes ) \
+ _try_count = _try_manager->num_nodes; \
+ } \
+ } \
+ }
+
+ /* */
+
+FT_END_HEADER
+
+
+#endif /* __FTCCACHE_H__ */
+
+
+/* END */
diff --git a/src/cache/ftccback.h b/src/cache/ftccback.h
new file mode 100644
index 0000000..86e72a7
--- /dev/null
+++ b/src/cache/ftccback.h
@@ -0,0 +1,90 @@
+/***************************************************************************/
+/* */
+/* ftccback.h */
+/* */
+/* Callback functions of the caching sub-system (specification only). */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#ifndef __FTCCBACK_H__
+#define __FTCCBACK_H__
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcmru.h"
+#include "ftcimage.h"
+#include "ftcmanag.h"
+#include "ftcglyph.h"
+#include "ftcsbits.h"
+
+
+ FT_LOCAL( void )
+ ftc_inode_free( FTC_Node inode,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_Error )
+ ftc_inode_new( FTC_Node *pinode,
+ FT_Pointer gquery,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_ULong )
+ ftc_inode_weight( FTC_Node inode,
+ FTC_Cache cache );
+
+
+ FT_LOCAL( void )
+ ftc_snode_free( FTC_Node snode,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_Error )
+ ftc_snode_new( FTC_Node *psnode,
+ FT_Pointer gquery,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_ULong )
+ ftc_snode_weight( FTC_Node snode,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_Bool )
+ ftc_snode_compare( FTC_Node snode,
+ FT_Pointer gquery,
+ FTC_Cache cache );
+
+
+ FT_LOCAL( FT_Bool )
+ ftc_gnode_compare( FTC_Node gnode,
+ FT_Pointer gquery,
+ FTC_Cache cache );
+
+
+ FT_LOCAL( FT_Error )
+ ftc_gcache_init( FTC_Cache cache );
+
+ FT_LOCAL( void )
+ ftc_gcache_done( FTC_Cache cache );
+
+
+ FT_LOCAL( FT_Error )
+ ftc_cache_init( FTC_Cache cache );
+
+ FT_LOCAL( void )
+ ftc_cache_done( FTC_Cache cache );
+
+#ifndef FT_CONFIG_OPTION_OLD_INTERNALS
+ FT_LOCAL( void )
+ ftc_node_destroy( FTC_Node node,
+ FTC_Manager manager );
+#endif
+
+#endif /* __FTCCBACK_H__ */
+
+/* END */
diff --git a/src/cache/ftccmap.c b/src/cache/ftccmap.c
new file mode 100644
index 0000000..aa59307
--- /dev/null
+++ b/src/cache/ftccmap.c
@@ -0,0 +1,413 @@
+/***************************************************************************/
+/* */
+/* ftccmap.c */
+/* */
+/* FreeType CharMap cache (body) */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_CACHE_H
+#include "ftcmanag.h"
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_TRUETYPE_IDS_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cache
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ typedef enum FTC_OldCMapType_
+ {
+ FTC_OLD_CMAP_BY_INDEX = 0,
+ FTC_OLD_CMAP_BY_ENCODING = 1,
+ FTC_OLD_CMAP_BY_ID = 2
+
+ } FTC_OldCMapType;
+
+
+ typedef struct FTC_OldCMapIdRec_
+ {
+ FT_UInt platform;
+ FT_UInt encoding;
+
+ } FTC_OldCMapIdRec, *FTC_OldCMapId;
+
+
+ typedef struct FTC_OldCMapDescRec_
+ {
+ FTC_FaceID face_id;
+ FTC_OldCMapType type;
+
+ union
+ {
+ FT_UInt index;
+ FT_Encoding encoding;
+ FTC_OldCMapIdRec id;
+
+ } u;
+
+ } FTC_OldCMapDescRec, *FTC_OldCMapDesc;
+
+#endif /* FT_CONFIG_OLD_INTERNALS */
+
+
+ /*************************************************************************/
+ /* */
+ /* Each FTC_CMapNode contains a simple array to map a range of character */
+ /* codes to equivalent glyph indices. */
+ /* */
+ /* For now, the implementation is very basic: Each node maps a range of */
+ /* 128 consecutive character codes to their corresponding glyph indices. */
+ /* */
+ /* We could do more complex things, but I don't think it is really very */
+ /* useful. */
+ /* */
+ /*************************************************************************/
+
+
+ /* number of glyph indices / character code per node */
+#define FTC_CMAP_INDICES_MAX 128
+
+ /* compute a query/node hash */
+#define FTC_CMAP_HASH( faceid, index, charcode ) \
+ ( FTC_FACE_ID_HASH( faceid ) + 211 * ( index ) + \
+ ( (char_code) / FTC_CMAP_INDICES_MAX ) )
+
+ /* the charmap query */
+ typedef struct FTC_CMapQueryRec_
+ {
+ FTC_FaceID face_id;
+ FT_UInt cmap_index;
+ FT_UInt32 char_code;
+
+ } FTC_CMapQueryRec, *FTC_CMapQuery;
+
+#define FTC_CMAP_QUERY( x ) ((FTC_CMapQuery)(x))
+#define FTC_CMAP_QUERY_HASH( x ) \
+ FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->char_code )
+
+ /* the cmap cache node */
+ typedef struct FTC_CMapNodeRec_
+ {
+ FTC_NodeRec node;
+ FTC_FaceID face_id;
+ FT_UInt cmap_index;
+ FT_UInt32 first; /* first character in node */
+ FT_UInt16 indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices */
+
+ } FTC_CMapNodeRec, *FTC_CMapNode;
+
+#define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) )
+#define FTC_CMAP_NODE_HASH( x ) \
+ FTC_CMAP_HASH( (x)->face_id, (x)->cmap_index, (x)->first )
+
+ /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */
+ /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet */
+#define FTC_CMAP_UNKNOWN ( (FT_UInt16)-1 )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CHARMAP NODES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( void )
+ ftc_cmap_node_free( FTC_Node ftcnode,
+ FTC_Cache cache )
+ {
+ FTC_CMapNode node = (FTC_CMapNode)ftcnode;
+ FT_Memory memory = cache->memory;
+
+
+ FT_FREE( node );
+ }
+
+
+ /* initialize a new cmap node */
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_cmap_node_new( FTC_Node *ftcanode,
+ FT_Pointer ftcquery,
+ FTC_Cache cache )
+ {
+ FTC_CMapNode *anode = (FTC_CMapNode*)ftcanode;
+ FTC_CMapQuery query = (FTC_CMapQuery)ftcquery;
+ FT_Error error;
+ FT_Memory memory = cache->memory;
+ FTC_CMapNode node;
+ FT_UInt nn;
+
+
+ if ( !FT_NEW( node ) )
+ {
+ node->face_id = query->face_id;
+ node->cmap_index = query->cmap_index;
+ node->first = (query->char_code / FTC_CMAP_INDICES_MAX) *
+ FTC_CMAP_INDICES_MAX;
+
+ for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ )
+ node->indices[nn] = FTC_CMAP_UNKNOWN;
+ }
+
+ *anode = node;
+ return error;
+ }
+
+
+ /* compute the weight of a given cmap node */
+ FT_CALLBACK_DEF( FT_ULong )
+ ftc_cmap_node_weight( FTC_Node cnode,
+ FTC_Cache cache )
+ {
+ FT_UNUSED( cnode );
+ FT_UNUSED( cache );
+
+ return sizeof ( *cnode );
+ }
+
+
+ /* compare a cmap node to a given query */
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_cmap_node_compare( FTC_Node ftcnode,
+ FT_Pointer ftcquery,
+ FTC_Cache cache )
+ {
+ FTC_CMapNode node = (FTC_CMapNode)ftcnode;
+ FTC_CMapQuery query = (FTC_CMapQuery)ftcquery;
+ FT_UNUSED( cache );
+
+
+ if ( node->face_id == query->face_id &&
+ node->cmap_index == query->cmap_index )
+ {
+ FT_UInt32 offset = (FT_UInt32)( query->char_code - node->first );
+
+
+ return FT_BOOL( offset < FTC_CMAP_INDICES_MAX );
+ }
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_cmap_node_remove_faceid( FTC_Node ftcnode,
+ FT_Pointer ftcface_id,
+ FTC_Cache cache )
+ {
+ FTC_CMapNode node = (FTC_CMapNode)ftcnode;
+ FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
+ FT_UNUSED( cache );
+
+ return FT_BOOL( node->face_id == face_id );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GLYPH IMAGE CACHE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_CacheClassRec ftc_cmap_cache_class =
+ {
+ ftc_cmap_node_new,
+ ftc_cmap_node_weight,
+ ftc_cmap_node_compare,
+ ftc_cmap_node_remove_faceid,
+ ftc_cmap_node_free,
+
+ sizeof ( FTC_CacheRec ),
+ ftc_cache_init,
+ ftc_cache_done,
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_CMapCache_New( FTC_Manager manager,
+ FTC_CMapCache *acache )
+ {
+ return FTC_Manager_RegisterCache( manager,
+ &ftc_cmap_cache_class,
+ FTC_CACHE_P( acache ) );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /*
+ * Unfortunately, it is not possible to support binary backwards
+ * compatibility in the cmap cache. The FTC_CMapCache_Lookup signature
+ * changes were too deep, and there is no clever hackish way to detect
+ * what kind of structure we are being passed.
+ *
+ * On the other hand it seems that no production code is using this
+ * function on Unix distributions.
+ */
+
+#endif
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_UInt )
+ FTC_CMapCache_Lookup( FTC_CMapCache cmap_cache,
+ FTC_FaceID face_id,
+ FT_Int cmap_index,
+ FT_UInt32 char_code )
+ {
+ FTC_Cache cache = FTC_CACHE( cmap_cache );
+ FTC_CMapQueryRec query;
+ FTC_CMapNode node;
+ FT_Error error;
+ FT_UInt gindex = 0;
+ FT_UInt32 hash;
+
+
+ if ( !cache )
+ {
+ FT_ERROR(( "FTC_CMapCache_Lookup: bad arguments, returning 0!\n" ));
+ return 0;
+ }
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ /*
+ * Detect a call from a rogue client that thinks it is linking
+ * to FreeType 2.1.7. This is possible because the third parameter
+ * is then a character code, and we have never seen any font with
+ * more than a few charmaps, so if the index is very large...
+ *
+ * It is also very unlikely that a rogue client is interested
+ * in Unicode values 0 to 15.
+ *
+ * NOTE: The original threshold was 4, but we found a font from the
+ * Adobe Acrobat Reader Pack, named `KozMinProVI-Regular.otf',
+ * which contains more than 5 charmaps.
+ */
+ if ( cmap_index >= 16 )
+ {
+ FTC_OldCMapDesc desc = (FTC_OldCMapDesc) face_id;
+
+
+ char_code = (FT_UInt32)cmap_index;
+ query.face_id = desc->face_id;
+
+
+ switch ( desc->type )
+ {
+ case FTC_OLD_CMAP_BY_INDEX:
+ query.cmap_index = desc->u.index;
+ query.char_code = (FT_UInt32)cmap_index;
+ break;
+
+ case FTC_OLD_CMAP_BY_ENCODING:
+ {
+ FT_Face face;
+
+
+ error = FTC_Manager_LookupFace( cache->manager, desc->face_id,
+ &face );
+ if ( error )
+ return 0;
+
+ FT_Select_Charmap( face, desc->u.encoding );
+
+ return FT_Get_Char_Index( face, char_code );
+ }
+
+ default:
+ return 0;
+ }
+ }
+ else
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+ {
+ query.face_id = face_id;
+ query.cmap_index = (FT_UInt)cmap_index;
+ query.char_code = char_code;
+ }
+
+ hash = FTC_CMAP_HASH( face_id, cmap_index, char_code );
+
+#if 1
+ FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query,
+ node, error );
+#else
+ error = FTC_Cache_Lookup( cache, hash, &query, (FTC_Node*) &node );
+#endif
+ if ( error )
+ goto Exit;
+
+ FT_ASSERT( (FT_UInt)( char_code - node->first ) < FTC_CMAP_INDICES_MAX );
+
+ /* something rotten can happen with rogue clients */
+ if ( (FT_UInt)( char_code - node->first >= FTC_CMAP_INDICES_MAX ) )
+ return 0;
+
+ gindex = node->indices[char_code - node->first];
+ if ( gindex == FTC_CMAP_UNKNOWN )
+ {
+ FT_Face face;
+
+
+ gindex = 0;
+
+ error = FTC_Manager_LookupFace( cache->manager, node->face_id, &face );
+ if ( error )
+ goto Exit;
+
+ if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps )
+ {
+ FT_CharMap old, cmap = NULL;
+
+
+ old = face->charmap;
+ cmap = face->charmaps[cmap_index];
+
+ if ( old != cmap )
+ FT_Set_Charmap( face, cmap );
+
+ gindex = FT_Get_Char_Index( face, char_code );
+
+ if ( old != cmap )
+ FT_Set_Charmap( face, old );
+ }
+
+ node->indices[char_code - node->first] = (FT_UShort)gindex;
+ }
+
+ Exit:
+ return gindex;
+ }
+
+
+/* END */
diff --git a/src/cache/ftcerror.h b/src/cache/ftcerror.h
new file mode 100644
index 0000000..5998d42
--- /dev/null
+++ b/src/cache/ftcerror.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* ftcerror.h */
+/* */
+/* Caching sub-system error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the caching sub-system error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __FTCERROR_H__
+#define __FTCERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX FTC_Err_
+#define FT_ERR_BASE FT_Mod_Err_Cache
+
+#include FT_ERRORS_H
+
+#endif /* __FTCERROR_H__ */
+
+/* END */
diff --git a/src/cache/ftcglyph.c b/src/cache/ftcglyph.c
new file mode 100644
index 0000000..5c03abe
--- /dev/null
+++ b/src/cache/ftcglyph.c
@@ -0,0 +1,211 @@
+/***************************************************************************/
+/* */
+/* ftcglyph.c */
+/* */
+/* FreeType Glyph Image (FT_Glyph) cache (body). */
+/* */
+/* Copyright 2000-2001, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcglyph.h"
+#include FT_ERRORS_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+ /* create a new chunk node, setting its cache index and ref count */
+ FT_LOCAL_DEF( void )
+ FTC_GNode_Init( FTC_GNode gnode,
+ FT_UInt gindex,
+ FTC_Family family )
+ {
+ gnode->family = family;
+ gnode->gindex = gindex;
+ family->num_nodes++;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_GNode_UnselectFamily( FTC_GNode gnode,
+ FTC_Cache cache )
+ {
+ FTC_Family family = gnode->family;
+
+
+ gnode->family = NULL;
+ if ( family && --family->num_nodes == 0 )
+ FTC_FAMILY_FREE( family, cache );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_GNode_Done( FTC_GNode gnode,
+ FTC_Cache cache )
+ {
+ /* finalize the node */
+ gnode->gindex = 0;
+
+ FTC_GNode_UnselectFamily( gnode, cache );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ ftc_gnode_compare( FTC_Node ftcgnode,
+ FT_Pointer ftcgquery,
+ FTC_Cache cache )
+ {
+ FTC_GNode gnode = (FTC_GNode)ftcgnode;
+ FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
+ FT_UNUSED( cache );
+
+
+ return FT_BOOL( gnode->family == gquery->family &&
+ gnode->gindex == gquery->gindex );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ FTC_GNode_Compare( FTC_GNode gnode,
+ FTC_GQuery gquery )
+ {
+ return ftc_gnode_compare( FTC_NODE( gnode ), gquery, NULL );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CHUNK SETS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ FTC_Family_Init( FTC_Family family,
+ FTC_Cache cache )
+ {
+ FTC_GCacheClass clazz = FTC_CACHE__GCACHE_CLASS( cache );
+
+
+ family->clazz = clazz->family_class;
+ family->num_nodes = 0;
+ family->cache = cache;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ftc_gcache_init( FTC_Cache ftccache )
+ {
+ FTC_GCache cache = (FTC_GCache)ftccache;
+ FT_Error error;
+
+
+ error = FTC_Cache_Init( FTC_CACHE( cache ) );
+ if ( !error )
+ {
+ FTC_GCacheClass clazz = (FTC_GCacheClass)FTC_CACHE( cache )->org_class;
+
+ FTC_MruList_Init( &cache->families,
+ clazz->family_class,
+ 0, /* no maximum here! */
+ cache,
+ FTC_CACHE( cache )->memory );
+ }
+
+ return error;
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_GCache_Init( FTC_GCache cache )
+ {
+ return ftc_gcache_init( FTC_CACHE( cache ) );
+ }
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( void )
+ ftc_gcache_done( FTC_Cache ftccache )
+ {
+ FTC_GCache cache = (FTC_GCache)ftccache;
+
+
+ FTC_Cache_Done( (FTC_Cache)cache );
+ FTC_MruList_Done( &cache->families );
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( void )
+ FTC_GCache_Done( FTC_GCache cache )
+ {
+ ftc_gcache_done( FTC_CACHE( cache ) );
+ }
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_GCache_New( FTC_Manager manager,
+ FTC_GCacheClass clazz,
+ FTC_GCache *acache )
+ {
+ return FTC_Manager_RegisterCache( manager, (FTC_CacheClass)clazz,
+ (FTC_Cache*)acache );
+ }
+
+
+#ifndef FTC_INLINE
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_GCache_Lookup( FTC_GCache cache,
+ FT_UInt32 hash,
+ FT_UInt gindex,
+ FTC_GQuery query,
+ FTC_Node *anode )
+ {
+ FT_Error error;
+
+
+ query->gindex = gindex;
+
+ FTC_MRULIST_LOOKUP( &cache->families, query, query->family, error );
+ if ( !error )
+ {
+ FTC_Family family = query->family;
+
+
+ /* prevent the family from being destroyed too early when an */
+ /* out-of-memory condition occurs during glyph node initialization. */
+ family->num_nodes++;
+
+ error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query, anode );
+
+ if ( --family->num_nodes == 0 )
+ FTC_FAMILY_FREE( family, cache );
+ }
+ return error;
+ }
+
+#endif /* !FTC_INLINE */
+
+
+/* END */
diff --git a/src/cache/ftcglyph.h b/src/cache/ftcglyph.h
new file mode 100644
index 0000000..87a4199
--- /dev/null
+++ b/src/cache/ftcglyph.h
@@ -0,0 +1,322 @@
+/***************************************************************************/
+/* */
+/* ftcglyph.h */
+/* */
+/* FreeType abstract glyph cache (specification). */
+/* */
+/* Copyright 2000-2001, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*
+ *
+ * FTC_GCache is an _abstract_ cache object optimized to store glyph
+ * data. It works as follows:
+ *
+ * - It manages FTC_GNode objects. Each one of them can hold one or more
+ * glyph `items'. Item types are not specified in the FTC_GCache but
+ * in classes that extend it.
+ *
+ * - Glyph attributes, like face ID, character size, render mode, etc.,
+ * can be grouped into abstract `glyph families'. This avoids storing
+ * the attributes within the FTC_GCache, since it is likely that many
+ * FTC_GNodes will belong to the same family in typical uses.
+ *
+ * - Each FTC_GNode is thus an FTC_Node with two additional fields:
+ *
+ * * gindex: A glyph index, or the first index in a glyph range.
+ * * family: A pointer to a glyph `family'.
+ *
+ * - Family types are not fully specific in the FTC_Family type, but
+ * by classes that extend it.
+ *
+ * Note that both FTC_ImageCache and FTC_SBitCache extend FTC_GCache.
+ * They share an FTC_Family sub-class called FTC_BasicFamily which is
+ * used to store the following data: face ID, pixel/point sizes, load
+ * flags. For more details see the file `src/cache/ftcbasic.c'.
+ *
+ * Client applications can extend FTC_GNode with their own FTC_GNode
+ * and FTC_Family sub-classes to implement more complex caches (e.g.,
+ * handling automatic synthesis, like obliquing & emboldening, colored
+ * glyphs, etc.).
+ *
+ * See also the FTC_ICache & FTC_SCache classes in `ftcimage.h' and
+ * `ftcsbits.h', which both extend FTC_GCache with additional
+ * optimizations.
+ *
+ * A typical FTC_GCache implementation must provide at least the
+ * following:
+ *
+ * - FTC_GNode sub-class, e.g. MyNode, with relevant methods:
+ * my_node_new (must call FTC_GNode_Init)
+ * my_node_free (must call FTC_GNode_Done)
+ * my_node_compare (must call FTC_GNode_Compare)
+ * my_node_remove_faceid (must call ftc_gnode_unselect in case
+ * of match)
+ *
+ * - FTC_Family sub-class, e.g. MyFamily, with relevant methods:
+ * my_family_compare
+ * my_family_init
+ * my_family_reset (optional)
+ * my_family_done
+ *
+ * - FTC_GQuery sub-class, e.g. MyQuery, to hold cache-specific query
+ * data.
+ *
+ * - Constant structures for a FTC_GNodeClass.
+ *
+ * - MyCacheNew() can be implemented easily as a call to the convenience
+ * function FTC_GCache_New.
+ *
+ * - MyCacheLookup with a call to FTC_GCache_Lookup. This function will
+ * automatically:
+ *
+ * - Search for the corresponding family in the cache, or create
+ * a new one if necessary. Put it in FTC_GQUERY(myquery).family
+ *
+ * - Call FTC_Cache_Lookup.
+ *
+ * If it returns NULL, you should create a new node, then call
+ * ftc_cache_add as usual.
+ */
+
+
+ /*************************************************************************/
+ /* */
+ /* Important: The functions defined in this file are only used to */
+ /* implement an abstract glyph cache class. You need to */
+ /* provide additional logic to implement a complete cache. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********* *********/
+ /********* WARNING, THIS IS BETA CODE. *********/
+ /********* *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#ifndef __FTCGLYPH_H__
+#define __FTCGLYPH_H__
+
+
+#include <ft2build.h>
+#include "ftcmanag.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*
+ * We can group glyphs into `families'. Each family correspond to a
+ * given face ID, character size, transform, etc.
+ *
+ * Families are implemented as MRU list nodes. They are
+ * reference-counted.
+ */
+
+ typedef struct FTC_FamilyRec_
+ {
+ FTC_MruNodeRec mrunode;
+ FT_UInt num_nodes; /* current number of nodes in this family */
+ FTC_Cache cache;
+ FTC_MruListClass clazz;
+
+ } FTC_FamilyRec, *FTC_Family;
+
+#define FTC_FAMILY(x) ( (FTC_Family)(x) )
+#define FTC_FAMILY_P(x) ( (FTC_Family*)(x) )
+
+
+ typedef struct FTC_GNodeRec_
+ {
+ FTC_NodeRec node;
+ FTC_Family family;
+ FT_UInt gindex;
+
+ } FTC_GNodeRec, *FTC_GNode;
+
+#define FTC_GNODE( x ) ( (FTC_GNode)(x) )
+#define FTC_GNODE_P( x ) ( (FTC_GNode*)(x) )
+
+
+ typedef struct FTC_GQueryRec_
+ {
+ FT_UInt gindex;
+ FTC_Family family;
+
+ } FTC_GQueryRec, *FTC_GQuery;
+
+#define FTC_GQUERY( x ) ( (FTC_GQuery)(x) )
+
+
+ /*************************************************************************/
+ /* */
+ /* These functions are exported so that they can be called from */
+ /* user-provided cache classes; otherwise, they are really part of the */
+ /* cache sub-system internals. */
+ /* */
+
+ /* must be called by derived FTC_Node_InitFunc routines */
+ FT_LOCAL( void )
+ FTC_GNode_Init( FTC_GNode node,
+ FT_UInt gindex, /* glyph index for node */
+ FTC_Family family );
+
+ /* returns TRUE iff the query's glyph index correspond to the node; */
+ /* this assumes that the `family' and `hash' fields of the query are */
+ /* already correctly set */
+ FT_LOCAL( FT_Bool )
+ FTC_GNode_Compare( FTC_GNode gnode,
+ FTC_GQuery gquery );
+
+ /* call this function to clear a node's family -- this is necessary */
+ /* to implement the `node_remove_faceid' cache method correctly */
+ FT_LOCAL( void )
+ FTC_GNode_UnselectFamily( FTC_GNode gnode,
+ FTC_Cache cache );
+
+ /* must be called by derived FTC_Node_DoneFunc routines */
+ FT_LOCAL( void )
+ FTC_GNode_Done( FTC_GNode node,
+ FTC_Cache cache );
+
+
+ FT_LOCAL( void )
+ FTC_Family_Init( FTC_Family family,
+ FTC_Cache cache );
+
+ typedef struct FTC_GCacheRec_
+ {
+ FTC_CacheRec cache;
+ FTC_MruListRec families;
+
+ } FTC_GCacheRec, *FTC_GCache;
+
+#define FTC_GCACHE( x ) ((FTC_GCache)(x))
+
+
+#if 0
+ /* can be used as @FTC_Cache_InitFunc */
+ FT_LOCAL( FT_Error )
+ FTC_GCache_Init( FTC_GCache cache );
+#endif
+
+
+#if 0
+ /* can be used as @FTC_Cache_DoneFunc */
+ FT_LOCAL( void )
+ FTC_GCache_Done( FTC_GCache cache );
+#endif
+
+
+ /* the glyph cache class adds fields for the family implementation */
+ typedef struct FTC_GCacheClassRec_
+ {
+ FTC_CacheClassRec clazz;
+ FTC_MruListClass family_class;
+
+ } FTC_GCacheClassRec;
+
+ typedef const FTC_GCacheClassRec* FTC_GCacheClass;
+
+#define FTC_GCACHE_CLASS( x ) ((FTC_GCacheClass)(x))
+
+#define FTC_CACHE__GCACHE_CLASS( x ) \
+ FTC_GCACHE_CLASS( FTC_CACHE(x)->org_class )
+#define FTC_CACHE__FAMILY_CLASS( x ) \
+ ( (FTC_MruListClass)FTC_CACHE__GCACHE_CLASS( x )->family_class )
+
+
+ /* convenience function; use it instead of FTC_Manager_Register_Cache */
+ FT_LOCAL( FT_Error )
+ FTC_GCache_New( FTC_Manager manager,
+ FTC_GCacheClass clazz,
+ FTC_GCache *acache );
+
+#ifndef FTC_INLINE
+ FT_LOCAL( FT_Error )
+ FTC_GCache_Lookup( FTC_GCache cache,
+ FT_UInt32 hash,
+ FT_UInt gindex,
+ FTC_GQuery query,
+ FTC_Node *anode );
+#endif
+
+
+ /* */
+
+
+#define FTC_FAMILY_FREE( family, cache ) \
+ FTC_MruList_Remove( &FTC_GCACHE((cache))->families, \
+ (FTC_MruNode)(family) )
+
+
+#ifdef FTC_INLINE
+
+#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash, \
+ gindex, query, node, error ) \
+ FT_BEGIN_STMNT \
+ FTC_GCache _gcache = FTC_GCACHE( cache ); \
+ FTC_GQuery _gquery = (FTC_GQuery)( query ); \
+ FTC_MruNode_CompareFunc _fcompare = (FTC_MruNode_CompareFunc)(famcmp); \
+ \
+ \
+ _gquery->gindex = (gindex); \
+ \
+ FTC_MRULIST_LOOKUP_CMP( &_gcache->families, _gquery, _fcompare, \
+ _gquery->family, error ); \
+ if ( !error ) \
+ { \
+ FTC_Family _gqfamily = _gquery->family; \
+ \
+ \
+ _gqfamily->num_nodes++; \
+ \
+ FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ); \
+ \
+ if ( --_gqfamily->num_nodes == 0 ) \
+ FTC_FAMILY_FREE( _gqfamily, _gcache ); \
+ } \
+ FT_END_STMNT
+ /* */
+
+#else /* !FTC_INLINE */
+
+#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash, \
+ gindex, query, node, error ) \
+ FT_BEGIN_STMNT \
+ void* _n = &(node); \
+ \
+ \
+ error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex, \
+ FTC_GQUERY( query ), (FTC_Node*)_n ); \
+ FT_END_STMNT
+
+#endif /* !FTC_INLINE */
+
+
+FT_END_HEADER
+
+
+#endif /* __FTCGLYPH_H__ */
+
+
+/* END */
diff --git a/src/cache/ftcimage.c b/src/cache/ftcimage.c
new file mode 100644
index 0000000..15d4e80
--- /dev/null
+++ b/src/cache/ftcimage.c
@@ -0,0 +1,163 @@
+/***************************************************************************/
+/* */
+/* ftcimage.c */
+/* */
+/* FreeType Image cache (body). */
+/* */
+/* Copyright 2000-2001, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcimage.h"
+#include FT_INTERNAL_MEMORY_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+ /* finalize a given glyph image node */
+ FT_LOCAL_DEF( void )
+ ftc_inode_free( FTC_Node ftcinode,
+ FTC_Cache cache )
+ {
+ FTC_INode inode = (FTC_INode)ftcinode;
+ FT_Memory memory = cache->memory;
+
+
+ if ( inode->glyph )
+ {
+ FT_Done_Glyph( inode->glyph );
+ inode->glyph = NULL;
+ }
+
+ FTC_GNode_Done( FTC_GNODE( inode ), cache );
+ FT_FREE( inode );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_INode_Free( FTC_INode inode,
+ FTC_Cache cache )
+ {
+ ftc_inode_free( FTC_NODE( inode ), cache );
+ }
+
+
+ /* initialize a new glyph image node */
+ FT_LOCAL_DEF( FT_Error )
+ FTC_INode_New( FTC_INode *pinode,
+ FTC_GQuery gquery,
+ FTC_Cache cache )
+ {
+ FT_Memory memory = cache->memory;
+ FT_Error error;
+ FTC_INode inode;
+
+
+ if ( !FT_NEW( inode ) )
+ {
+ FTC_GNode gnode = FTC_GNODE( inode );
+ FTC_Family family = gquery->family;
+ FT_UInt gindex = gquery->gindex;
+ FTC_IFamilyClass clazz = FTC_CACHE__IFAMILY_CLASS( cache );
+
+
+ /* initialize its inner fields */
+ FTC_GNode_Init( gnode, gindex, family );
+
+ /* we will now load the glyph image */
+ error = clazz->family_load_glyph( family, gindex, cache,
+ &inode->glyph );
+ if ( error )
+ {
+ FTC_INode_Free( inode, cache );
+ inode = NULL;
+ }
+ }
+
+ *pinode = inode;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ftc_inode_new( FTC_Node *ftcpinode,
+ FT_Pointer ftcgquery,
+ FTC_Cache cache )
+ {
+ FTC_INode *pinode = (FTC_INode*)ftcpinode;
+ FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
+
+
+ return FTC_INode_New( pinode, gquery, cache );
+ }
+
+
+ FT_LOCAL_DEF( FT_ULong )
+ ftc_inode_weight( FTC_Node ftcinode,
+ FTC_Cache ftccache )
+ {
+ FTC_INode inode = (FTC_INode)ftcinode;
+ FT_ULong size = 0;
+ FT_Glyph glyph = inode->glyph;
+
+ FT_UNUSED( ftccache );
+
+
+ switch ( glyph->format )
+ {
+ case FT_GLYPH_FORMAT_BITMAP:
+ {
+ FT_BitmapGlyph bitg;
+
+
+ bitg = (FT_BitmapGlyph)glyph;
+ size = bitg->bitmap.rows * ft_labs( bitg->bitmap.pitch ) +
+ sizeof ( *bitg );
+ }
+ break;
+
+ case FT_GLYPH_FORMAT_OUTLINE:
+ {
+ FT_OutlineGlyph outg;
+
+
+ outg = (FT_OutlineGlyph)glyph;
+ size = outg->outline.n_points *
+ ( sizeof ( FT_Vector ) + sizeof ( FT_Byte ) ) +
+ outg->outline.n_contours * sizeof ( FT_Short ) +
+ sizeof ( *outg );
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ size += sizeof ( *inode );
+ return size;
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_ULong )
+ FTC_INode_Weight( FTC_INode inode )
+ {
+ return ftc_inode_weight( FTC_NODE( inode ), NULL );
+ }
+
+#endif /* 0 */
+
+
+/* END */
diff --git a/src/cache/ftcimage.h b/src/cache/ftcimage.h
new file mode 100644
index 0000000..20d5d3e
--- /dev/null
+++ b/src/cache/ftcimage.h
@@ -0,0 +1,107 @@
+/***************************************************************************/
+/* */
+/* ftcimage.h */
+/* */
+/* FreeType Generic Image cache (specification) */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*
+ * FTC_ICache is an _abstract_ cache used to store a single FT_Glyph
+ * image per cache node.
+ *
+ * FTC_ICache extends FTC_GCache. For an implementation example,
+ * see FTC_ImageCache in `src/cache/ftbasic.c'.
+ */
+
+
+ /*************************************************************************/
+ /* */
+ /* Each image cache really manages FT_Glyph objects. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef __FTCIMAGE_H__
+#define __FTCIMAGE_H__
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcglyph.h"
+
+FT_BEGIN_HEADER
+
+
+ /* the FT_Glyph image node type - we store only 1 glyph per node */
+ typedef struct FTC_INodeRec_
+ {
+ FTC_GNodeRec gnode;
+ FT_Glyph glyph;
+
+ } FTC_INodeRec, *FTC_INode;
+
+#define FTC_INODE( x ) ( (FTC_INode)( x ) )
+#define FTC_INODE_GINDEX( x ) FTC_GNODE(x)->gindex
+#define FTC_INODE_FAMILY( x ) FTC_GNODE(x)->family
+
+ typedef FT_Error
+ (*FTC_IFamily_LoadGlyphFunc)( FTC_Family family,
+ FT_UInt gindex,
+ FTC_Cache cache,
+ FT_Glyph *aglyph );
+
+ typedef struct FTC_IFamilyClassRec_
+ {
+ FTC_MruListClassRec clazz;
+ FTC_IFamily_LoadGlyphFunc family_load_glyph;
+
+ } FTC_IFamilyClassRec;
+
+ typedef const FTC_IFamilyClassRec* FTC_IFamilyClass;
+
+#define FTC_IFAMILY_CLASS( x ) ((FTC_IFamilyClass)(x))
+
+#define FTC_CACHE__IFAMILY_CLASS( x ) \
+ FTC_IFAMILY_CLASS( FTC_CACHE__GCACHE_CLASS(x)->family_class )
+
+
+ /* can be used as a @FTC_Node_FreeFunc */
+ FT_LOCAL( void )
+ FTC_INode_Free( FTC_INode inode,
+ FTC_Cache cache );
+
+ /* Can be used as @FTC_Node_NewFunc. `gquery.index' and `gquery.family'
+ * must be set correctly. This function will call the `family_load_glyph'
+ * method to load the FT_Glyph into the cache node.
+ */
+ FT_LOCAL( FT_Error )
+ FTC_INode_New( FTC_INode *pinode,
+ FTC_GQuery gquery,
+ FTC_Cache cache );
+
+#if 0
+ /* can be used as @FTC_Node_WeightFunc */
+ FT_LOCAL( FT_ULong )
+ FTC_INode_Weight( FTC_INode inode );
+#endif
+
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __FTCIMAGE_H__ */
+
+
+/* END */
diff --git a/src/cache/ftcmanag.c b/src/cache/ftcmanag.c
new file mode 100644
index 0000000..9d7347c
--- /dev/null
+++ b/src/cache/ftcmanag.c
@@ -0,0 +1,732 @@
+/***************************************************************************/
+/* */
+/* ftcmanag.c */
+/* */
+/* FreeType Cache Manager (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcmanag.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_SIZES_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cache
+
+#define FTC_LRU_GET_MANAGER( lru ) ( (FTC_Manager)(lru)->user_data )
+
+
+ static FT_Error
+ ftc_scaler_lookup_size( FTC_Manager manager,
+ FTC_Scaler scaler,
+ FT_Size *asize )
+ {
+ FT_Face face;
+ FT_Size size = NULL;
+ FT_Error error;
+
+
+ error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
+ if ( error )
+ goto Exit;
+
+ error = FT_New_Size( face, &size );
+ if ( error )
+ goto Exit;
+
+ FT_Activate_Size( size );
+
+ if ( scaler->pixel )
+ error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
+ else
+ error = FT_Set_Char_Size( face, scaler->width, scaler->height,
+ scaler->x_res, scaler->y_res );
+ if ( error )
+ {
+ FT_Done_Size( size );
+ size = NULL;
+ }
+
+ Exit:
+ *asize = size;
+ return error;
+ }
+
+
+ typedef struct FTC_SizeNodeRec_
+ {
+ FTC_MruNodeRec node;
+ FT_Size size;
+ FTC_ScalerRec scaler;
+
+ } FTC_SizeNodeRec, *FTC_SizeNode;
+
+
+ FT_CALLBACK_DEF( void )
+ ftc_size_node_done( FTC_MruNode ftcnode,
+ FT_Pointer data )
+ {
+ FTC_SizeNode node = (FTC_SizeNode)ftcnode;
+ FT_Size size = node->size;
+ FT_UNUSED( data );
+
+
+ if ( size )
+ FT_Done_Size( size );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_size_node_compare( FTC_MruNode ftcnode,
+ FT_Pointer ftcscaler )
+ {
+ FTC_SizeNode node = (FTC_SizeNode)ftcnode;
+ FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
+ FTC_Scaler scaler0 = &node->scaler;
+
+
+ if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
+ {
+ FT_Activate_Size( node->size );
+ return 1;
+ }
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_size_node_init( FTC_MruNode ftcnode,
+ FT_Pointer ftcscaler,
+ FT_Pointer ftcmanager )
+ {
+ FTC_SizeNode node = (FTC_SizeNode)ftcnode;
+ FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
+ FTC_Manager manager = (FTC_Manager)ftcmanager;
+
+
+ node->scaler = scaler[0];
+
+ return ftc_scaler_lookup_size( manager, scaler, &node->size );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_size_node_reset( FTC_MruNode ftcnode,
+ FT_Pointer ftcscaler,
+ FT_Pointer ftcmanager )
+ {
+ FTC_SizeNode node = (FTC_SizeNode)ftcnode;
+ FTC_Scaler scaler = (FTC_Scaler)ftcscaler;
+ FTC_Manager manager = (FTC_Manager)ftcmanager;
+
+
+ FT_Done_Size( node->size );
+
+ node->scaler = scaler[0];
+
+ return ftc_scaler_lookup_size( manager, scaler, &node->size );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_MruListClassRec ftc_size_list_class =
+ {
+ sizeof ( FTC_SizeNodeRec ),
+ ftc_size_node_compare,
+ ftc_size_node_init,
+ ftc_size_node_reset,
+ ftc_size_node_done
+ };
+
+
+ /* helper function used by ftc_face_node_done */
+ static FT_Bool
+ ftc_size_node_compare_faceid( FTC_MruNode ftcnode,
+ FT_Pointer ftcface_id )
+ {
+ FTC_SizeNode node = (FTC_SizeNode)ftcnode;
+ FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
+
+
+ return FT_BOOL( node->scaler.face_id == face_id );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_LookupSize( FTC_Manager manager,
+ FTC_Scaler scaler,
+ FT_Size *asize )
+ {
+ FT_Error error;
+ FTC_SizeNode node;
+
+
+ if ( asize == NULL )
+ return FTC_Err_Bad_Argument;
+
+ *asize = NULL;
+
+ if ( !manager )
+ return FTC_Err_Invalid_Cache_Handle;
+
+#ifdef FTC_INLINE
+
+ FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
+ node, error );
+
+#else
+ error = FTC_MruList_Lookup( &manager->sizes, scaler, (FTC_MruNode*)&node );
+#endif
+
+ if ( !error )
+ *asize = node->size;
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FACE MRU IMPLEMENTATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct FTC_FaceNodeRec_
+ {
+ FTC_MruNodeRec node;
+ FTC_FaceID face_id;
+ FT_Face face;
+
+ } FTC_FaceNodeRec, *FTC_FaceNode;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ ftc_face_node_init( FTC_MruNode ftcnode,
+ FT_Pointer ftcface_id,
+ FT_Pointer ftcmanager )
+ {
+ FTC_FaceNode node = (FTC_FaceNode)ftcnode;
+ FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
+ FTC_Manager manager = (FTC_Manager)ftcmanager;
+ FT_Error error;
+
+
+ node->face_id = face_id;
+
+ error = manager->request_face( face_id,
+ manager->library,
+ manager->request_data,
+ &node->face );
+ if ( !error )
+ {
+ /* destroy initial size object; it will be re-created later */
+ if ( node->face->size )
+ FT_Done_Size( node->face->size );
+ }
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ ftc_face_node_done( FTC_MruNode ftcnode,
+ FT_Pointer ftcmanager )
+ {
+ FTC_FaceNode node = (FTC_FaceNode)ftcnode;
+ FTC_Manager manager = (FTC_Manager)ftcmanager;
+
+
+ /* we must begin by removing all scalers for the target face */
+ /* from the manager's list */
+ FTC_MruList_RemoveSelection( &manager->sizes,
+ ftc_size_node_compare_faceid,
+ node->face_id );
+
+ /* all right, we can discard the face now */
+ FT_Done_Face( node->face );
+ node->face = NULL;
+ node->face_id = NULL;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Bool )
+ ftc_face_node_compare( FTC_MruNode ftcnode,
+ FT_Pointer ftcface_id )
+ {
+ FTC_FaceNode node = (FTC_FaceNode)ftcnode;
+ FTC_FaceID face_id = (FTC_FaceID)ftcface_id;
+
+
+ return FT_BOOL( node->face_id == face_id );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FTC_MruListClassRec ftc_face_list_class =
+ {
+ sizeof ( FTC_FaceNodeRec),
+
+ ftc_face_node_compare,
+ ftc_face_node_init,
+ 0, /* FTC_MruNode_ResetFunc */
+ ftc_face_node_done
+ };
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_LookupFace( FTC_Manager manager,
+ FTC_FaceID face_id,
+ FT_Face *aface )
+ {
+ FT_Error error;
+ FTC_FaceNode node;
+
+
+ if ( aface == NULL )
+ return FTC_Err_Bad_Argument;
+
+ *aface = NULL;
+
+ if ( !manager )
+ return FTC_Err_Invalid_Cache_Handle;
+
+ /* we break encapsulation for the sake of speed */
+#ifdef FTC_INLINE
+
+ FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
+ node, error );
+
+#else
+ error = FTC_MruList_Lookup( &manager->faces, face_id, (FTC_MruNode*)&node );
+#endif
+
+ if ( !error )
+ *aface = node->face;
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CACHE MANAGER ROUTINES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_New( FT_Library library,
+ FT_UInt max_faces,
+ FT_UInt max_sizes,
+ FT_ULong max_bytes,
+ FTC_Face_Requester requester,
+ FT_Pointer req_data,
+ FTC_Manager *amanager )
+ {
+ FT_Error error;
+ FT_Memory memory;
+ FTC_Manager manager = 0;
+
+
+ if ( !library )
+ return FTC_Err_Invalid_Library_Handle;
+
+ memory = library->memory;
+
+ if ( FT_NEW( manager ) )
+ goto Exit;
+
+ if ( max_faces == 0 )
+ max_faces = FTC_MAX_FACES_DEFAULT;
+
+ if ( max_sizes == 0 )
+ max_sizes = FTC_MAX_SIZES_DEFAULT;
+
+ if ( max_bytes == 0 )
+ max_bytes = FTC_MAX_BYTES_DEFAULT;
+
+ manager->library = library;
+ manager->memory = memory;
+ manager->max_weight = max_bytes;
+
+ manager->request_face = requester;
+ manager->request_data = req_data;
+
+ FTC_MruList_Init( &manager->faces,
+ &ftc_face_list_class,
+ max_faces,
+ manager,
+ memory );
+
+ FTC_MruList_Init( &manager->sizes,
+ &ftc_size_list_class,
+ max_sizes,
+ manager,
+ memory );
+
+ *amanager = manager;
+
+ Exit:
+ return error;
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Done( FTC_Manager manager )
+ {
+ FT_Memory memory;
+ FT_UInt idx;
+
+
+ if ( !manager || !manager->library )
+ return;
+
+ memory = manager->memory;
+
+ /* now discard all caches */
+ for (idx = manager->num_caches; idx-- > 0; )
+ {
+ FTC_Cache cache = manager->caches[idx];
+
+
+ if ( cache )
+ {
+ cache->clazz.cache_done( cache );
+ FT_FREE( cache );
+ manager->caches[idx] = NULL;
+ }
+ }
+ manager->num_caches = 0;
+
+ /* discard faces and sizes */
+ FTC_MruList_Done( &manager->sizes );
+ FTC_MruList_Done( &manager->faces );
+
+ manager->library = NULL;
+ manager->memory = NULL;
+
+ FT_FREE( manager );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_Reset( FTC_Manager manager )
+ {
+ if ( manager )
+ {
+ FTC_MruList_Reset( &manager->sizes );
+ FTC_MruList_Reset( &manager->faces );
+ }
+ /* XXX: FIXME: flush the caches? */
+ }
+
+
+#ifdef FT_DEBUG_ERROR
+
+ static void
+ FTC_Manager_Check( FTC_Manager manager )
+ {
+ FTC_Node node, first;
+
+
+ first = manager->nodes_list;
+
+ /* check node weights */
+ if ( first )
+ {
+ FT_ULong weight = 0;
+
+
+ node = first;
+
+ do
+ {
+ FTC_Cache cache = manager->caches[node->cache_index];
+
+
+ if ( (FT_UInt)node->cache_index >= manager->num_caches )
+ FT_ERROR(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
+ node->cache_index ));
+ else
+ weight += cache->clazz.node_weight( node, cache );
+
+ node = FTC_NODE__NEXT( node );
+
+ } while ( node != first );
+
+ if ( weight != manager->cur_weight )
+ FT_ERROR(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
+ manager->cur_weight, weight ));
+ }
+
+ /* check circular list */
+ if ( first )
+ {
+ FT_UFast count = 0;
+
+
+ node = first;
+ do
+ {
+ count++;
+ node = FTC_NODE__NEXT( node );
+
+ } while ( node != first );
+
+ if ( count != manager->num_nodes )
+ FT_ERROR((
+ "FTC_Manager_Check: invalid cache node count %d instead of %d\n",
+ manager->num_nodes, count ));
+ }
+ }
+
+#endif /* FT_DEBUG_ERROR */
+
+
+ /* `Compress' the manager's data, i.e., get rid of old cache nodes */
+ /* that are not referenced anymore in order to limit the total */
+ /* memory used by the cache. */
+
+ /* documentation is in ftcmanag.h */
+
+ FT_LOCAL_DEF( void )
+ FTC_Manager_Compress( FTC_Manager manager )
+ {
+ FTC_Node node, first;
+
+
+ if ( !manager )
+ return;
+
+ first = manager->nodes_list;
+
+#ifdef FT_DEBUG_ERROR
+ FTC_Manager_Check( manager );
+
+ FT_ERROR(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
+ manager->cur_weight, manager->max_weight,
+ manager->num_nodes ));
+#endif
+
+ if ( manager->cur_weight < manager->max_weight || first == NULL )
+ return;
+
+ /* go to last node -- it's a circular list */
+ node = FTC_NODE__PREV( first );
+ do
+ {
+ FTC_Node prev;
+
+
+ prev = ( node == first ) ? NULL : FTC_NODE__PREV( node );
+
+ if ( node->ref_count <= 0 )
+ ftc_node_destroy( node, manager );
+
+ node = prev;
+
+ } while ( node && manager->cur_weight > manager->max_weight );
+ }
+
+
+ /* documentation is in ftcmanag.h */
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_Manager_RegisterCache( FTC_Manager manager,
+ FTC_CacheClass clazz,
+ FTC_Cache *acache )
+ {
+ FT_Error error = FTC_Err_Invalid_Argument;
+ FTC_Cache cache = NULL;
+
+
+ if ( manager && clazz && acache )
+ {
+ FT_Memory memory = manager->memory;
+
+
+ if ( manager->num_caches >= FTC_MAX_CACHES )
+ {
+ error = FTC_Err_Too_Many_Caches;
+ FT_ERROR(( "%s: too many registered caches\n",
+ "FTC_Manager_Register_Cache" ));
+ goto Exit;
+ }
+
+ if ( !FT_ALLOC( cache, clazz->cache_size ) )
+ {
+ cache->manager = manager;
+ cache->memory = memory;
+ cache->clazz = clazz[0];
+ cache->org_class = clazz;
+
+ /* THIS IS VERY IMPORTANT! IT WILL WRETCH THE MANAGER */
+ /* IF IT IS NOT SET CORRECTLY */
+ cache->index = manager->num_caches;
+
+ error = clazz->cache_init( cache );
+ if ( error )
+ {
+ clazz->cache_done( cache );
+ FT_FREE( cache );
+ goto Exit;
+ }
+
+ manager->caches[manager->num_caches++] = cache;
+ }
+ }
+
+ Exit:
+ *acache = cache;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ FTC_Manager_FlushN( FTC_Manager manager,
+ FT_UInt count )
+ {
+ FTC_Node first = manager->nodes_list;
+ FTC_Node node;
+ FT_UInt result;
+
+
+ /* try to remove `count' nodes from the list */
+ if ( first == NULL ) /* empty list! */
+ return 0;
+
+ /* go to last node - it's a circular list */
+ node = FTC_NODE__PREV(first);
+ for ( result = 0; result < count; )
+ {
+ FTC_Node prev = FTC_NODE__PREV( node );
+
+
+ /* don't touch locked nodes */
+ if ( node->ref_count <= 0 )
+ {
+ ftc_node_destroy( node, manager );
+ result++;
+ }
+
+ if ( node == first )
+ break;
+
+ node = prev;
+ }
+ return result;
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Manager_RemoveFaceID( FTC_Manager manager,
+ FTC_FaceID face_id )
+ {
+ FT_UInt nn;
+
+ /* this will remove all FTC_SizeNode that correspond to
+ * the face_id as well
+ */
+ FTC_MruList_RemoveSelection( &manager->faces, NULL, face_id );
+
+ for ( nn = 0; nn < manager->num_caches; nn++ )
+ FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
+ }
+
+
+ /* documentation is in ftcache.h */
+
+ FT_EXPORT_DEF( void )
+ FTC_Node_Unref( FTC_Node node,
+ FTC_Manager manager )
+ {
+ if ( node && (FT_UInt)node->cache_index < manager->num_caches )
+ node->ref_count--;
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_EXPORT_DEF( FT_Error )
+ FTC_Manager_Lookup_Face( FTC_Manager manager,
+ FTC_FaceID face_id,
+ FT_Face *aface )
+ {
+ return FTC_Manager_LookupFace( manager, face_id, aface );
+ }
+
+
+ FT_EXPORT( FT_Error )
+ FTC_Manager_Lookup_Size( FTC_Manager manager,
+ FTC_Font font,
+ FT_Face *aface,
+ FT_Size *asize )
+ {
+ FTC_ScalerRec scaler;
+ FT_Error error;
+ FT_Size size;
+ FT_Face face;
+
+
+ scaler.face_id = font->face_id;
+ scaler.width = font->pix_width;
+ scaler.height = font->pix_height;
+ scaler.pixel = TRUE;
+ scaler.x_res = 0;
+ scaler.y_res = 0;
+
+ error = FTC_Manager_LookupSize( manager, &scaler, &size );
+ if ( error )
+ {
+ face = NULL;
+ size = NULL;
+ }
+ else
+ face = size->face;
+
+ if ( aface )
+ *aface = face;
+
+ if ( asize )
+ *asize = size;
+
+ return error;
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+/* END */
diff --git a/src/cache/ftcmanag.h b/src/cache/ftcmanag.h
new file mode 100644
index 0000000..3fdc2c7
--- /dev/null
+++ b/src/cache/ftcmanag.h
@@ -0,0 +1,175 @@
+/***************************************************************************/
+/* */
+/* ftcmanag.h */
+/* */
+/* FreeType Cache Manager (specification). */
+/* */
+/* Copyright 2000-2001, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* A cache manager is in charge of the following: */
+ /* */
+ /* - Maintain a mapping between generic FTC_FaceIDs and live FT_Face */
+ /* objects. The mapping itself is performed through a user-provided */
+ /* callback. However, the manager maintains a small cache of FT_Face */
+ /* and FT_Size objects in order to speed up things considerably. */
+ /* */
+ /* - Manage one or more cache objects. Each cache is in charge of */
+ /* holding a varying number of `cache nodes'. Each cache node */
+ /* represents a minimal amount of individually accessible cached */
+ /* data. For example, a cache node can be an FT_Glyph image */
+ /* containing a vector outline, or some glyph metrics, or anything */
+ /* else. */
+ /* */
+ /* Each cache node has a certain size in bytes that is added to the */
+ /* total amount of `cache memory' within the manager. */
+ /* */
+ /* All cache nodes are located in a global LRU list, where the oldest */
+ /* node is at the tail of the list. */
+ /* */
+ /* Each node belongs to a single cache, and includes a reference */
+ /* count to avoid destroying it (due to caching). */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********* *********/
+ /********* WARNING, THIS IS BETA CODE. *********/
+ /********* *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#ifndef __FTCMANAG_H__
+#define __FTCMANAG_H__
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcmru.h"
+#include "ftccache.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Section> */
+ /* cache_subsystem */
+ /* */
+ /*************************************************************************/
+
+
+#define FTC_MAX_FACES_DEFAULT 2
+#define FTC_MAX_SIZES_DEFAULT 4
+#define FTC_MAX_BYTES_DEFAULT 200000L /* ~200kByte by default */
+
+ /* maximum number of caches registered in a single manager */
+#define FTC_MAX_CACHES 16
+
+
+ typedef struct FTC_ManagerRec_
+ {
+ FT_Library library;
+ FT_Memory memory;
+
+ FTC_Node nodes_list;
+ FT_ULong max_weight;
+ FT_ULong cur_weight;
+ FT_UInt num_nodes;
+
+ FTC_Cache caches[FTC_MAX_CACHES];
+ FT_UInt num_caches;
+
+ FTC_MruListRec faces;
+ FTC_MruListRec sizes;
+
+ FT_Pointer request_data;
+ FTC_Face_Requester request_face;
+
+ } FTC_ManagerRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FTC_Manager_Compress */
+ /* */
+ /* <Description> */
+ /* This function is used to check the state of the cache manager if */
+ /* its `num_bytes' field is greater than its `max_bytes' field. It */
+ /* will flush as many old cache nodes as possible (ignoring cache */
+ /* nodes with a non-zero reference count). */
+ /* */
+ /* <InOut> */
+ /* manager :: A handle to the cache manager. */
+ /* */
+ /* <Note> */
+ /* Client applications should not call this function directly. It is */
+ /* normally invoked by specific cache implementations. */
+ /* */
+ /* The reason this function is exported is to allow client-specific */
+ /* cache classes. */
+ /* */
+ FT_LOCAL( void )
+ FTC_Manager_Compress( FTC_Manager manager );
+
+
+ /* try to flush `count' old nodes from the cache; return the number
+ * of really flushed nodes
+ */
+ FT_LOCAL( FT_UInt )
+ FTC_Manager_FlushN( FTC_Manager manager,
+ FT_UInt count );
+
+
+ /* this must be used internally for the moment */
+ FT_LOCAL( FT_Error )
+ FTC_Manager_RegisterCache( FTC_Manager manager,
+ FTC_CacheClass clazz,
+ FTC_Cache *acache );
+
+ /* */
+
+#define FTC_SCALER_COMPARE( a, b ) \
+ ( (a)->face_id == (b)->face_id && \
+ (a)->width == (b)->width && \
+ (a)->height == (b)->height && \
+ ((a)->pixel != 0) == ((b)->pixel != 0) && \
+ ( (a)->pixel || \
+ ( (a)->x_res == (b)->x_res && \
+ (a)->y_res == (b)->y_res ) ) )
+
+#define FTC_SCALER_HASH( q ) \
+ ( FTC_FACE_ID_HASH( (q)->face_id ) + \
+ (q)->width + (q)->height*7 + \
+ ( (q)->pixel ? 0 : ( (q)->x_res*33 ^ (q)->y_res*61 ) ) )
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __FTCMANAG_H__ */
+
+
+/* END */
diff --git a/src/cache/ftcmru.c b/src/cache/ftcmru.c
new file mode 100644
index 0000000..3a6c625
--- /dev/null
+++ b/src/cache/ftcmru.c
@@ -0,0 +1,357 @@
+/***************************************************************************/
+/* */
+/* ftcmru.c */
+/* */
+/* FreeType MRU support (body). */
+/* */
+/* Copyright 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcmru.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "ftcerror.h"
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruNode_Prepend( FTC_MruNode *plist,
+ FTC_MruNode node )
+ {
+ FTC_MruNode first = *plist;
+
+
+ if ( first )
+ {
+ FTC_MruNode last = first->prev;
+
+
+#ifdef FT_DEBUG_ERROR
+ {
+ FTC_MruNode cnode = first;
+
+
+ do
+ {
+ if ( cnode == node )
+ {
+ fprintf( stderr, "FTC_MruNode_Prepend: invalid action!\n" );
+ exit( 2 );
+ }
+ cnode = cnode->next;
+
+ } while ( cnode != first );
+ }
+#endif
+
+ first->prev = node;
+ last->next = node;
+ node->next = first;
+ node->prev = last;
+ }
+ else
+ {
+ node->next = node;
+ node->prev = node;
+ }
+ *plist = node;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruNode_Up( FTC_MruNode *plist,
+ FTC_MruNode node )
+ {
+ FTC_MruNode first = *plist;
+
+
+ FT_ASSERT( first != NULL );
+
+ if ( first != node )
+ {
+ FTC_MruNode prev, next, last;
+
+
+#ifdef FT_DEBUG_ERROR
+ {
+ FTC_MruNode cnode = first;
+ do
+ {
+ if ( cnode == node )
+ goto Ok;
+ cnode = cnode->next;
+
+ } while ( cnode != first );
+
+ fprintf( stderr, "FTC_MruNode_Up: invalid action!\n" );
+ exit( 2 );
+ Ok:
+ }
+#endif
+ prev = node->prev;
+ next = node->next;
+
+ prev->next = next;
+ next->prev = prev;
+
+ last = first->prev;
+
+ last->next = node;
+ first->prev = node;
+
+ node->next = first;
+ node->prev = last;
+
+ *plist = node;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruNode_Remove( FTC_MruNode *plist,
+ FTC_MruNode node )
+ {
+ FTC_MruNode first = *plist;
+ FTC_MruNode prev, next;
+
+
+ FT_ASSERT( first != NULL );
+
+#ifdef FT_DEBUG_ERROR
+ {
+ FTC_MruNode cnode = first;
+
+
+ do
+ {
+ if ( cnode == node )
+ goto Ok;
+ cnode = cnode->next;
+
+ } while ( cnode != first );
+
+ fprintf( stderr, "FTC_MruNode_Remove: invalid action!\n" );
+ exit( 2 );
+ Ok:
+ }
+#endif
+
+ prev = node->prev;
+ next = node->next;
+
+ prev->next = next;
+ next->prev = prev;
+
+ if ( node == next )
+ {
+ FT_ASSERT( first == node );
+ FT_ASSERT( prev == node );
+
+ *plist = NULL;
+ }
+ else if ( node == first )
+ *plist = next;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruList_Init( FTC_MruList list,
+ FTC_MruListClass clazz,
+ FT_UInt max_nodes,
+ FT_Pointer data,
+ FT_Memory memory )
+ {
+ list->num_nodes = 0;
+ list->max_nodes = max_nodes;
+ list->nodes = NULL;
+ list->clazz = *clazz;
+ list->data = data;
+ list->memory = memory;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruList_Reset( FTC_MruList list )
+ {
+ while ( list->nodes )
+ FTC_MruList_Remove( list, list->nodes );
+
+ FT_ASSERT( list->num_nodes == 0 );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruList_Done( FTC_MruList list )
+ {
+ FTC_MruList_Reset( list );
+ }
+
+
+#ifndef FTC_INLINE
+ FT_LOCAL_DEF( FTC_MruNode )
+ FTC_MruList_Find( FTC_MruList list,
+ FT_Pointer key )
+ {
+ FTC_MruNode_CompareFunc compare = list->clazz.node_compare;
+ FTC_MruNode first, node;
+
+
+ first = list->nodes;
+ node = NULL;
+
+ if ( first )
+ {
+ node = first;
+ do
+ {
+ if ( compare( node, key ) )
+ {
+ if ( node != first )
+ FTC_MruNode_Up( &list->nodes, node );
+
+ return node;
+ }
+
+ node = node->next;
+
+ } while ( node != first);
+ }
+
+ return NULL;
+ }
+#endif
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_MruList_New( FTC_MruList list,
+ FT_Pointer key,
+ FTC_MruNode *anode )
+ {
+ FT_Error error;
+ FTC_MruNode node;
+ FT_Memory memory = list->memory;
+
+
+ if ( list->num_nodes >= list->max_nodes && list->max_nodes > 0 )
+ {
+ node = list->nodes->prev;
+
+ FT_ASSERT( node );
+
+ if ( list->clazz.node_reset )
+ {
+ FTC_MruNode_Up( &list->nodes, node );
+
+ error = list->clazz.node_reset( node, key, list->data );
+ if ( !error )
+ goto Exit;
+ }
+
+ FTC_MruNode_Remove( &list->nodes, node );
+ list->num_nodes--;
+
+ if ( list->clazz.node_done )
+ list->clazz.node_done( node, list->data );
+ }
+ else if ( FT_ALLOC( node, list->clazz.node_size ) )
+ goto Exit;
+
+ error = list->clazz.node_init( node, key, list->data );
+ if ( error )
+ goto Fail;
+
+ FTC_MruNode_Prepend( &list->nodes, node );
+ list->num_nodes++;
+
+ Exit:
+ *anode = node;
+ return error;
+
+ Fail:
+ if ( list->clazz.node_done )
+ list->clazz.node_done( node, list->data );
+
+ FT_FREE( node );
+ goto Exit;
+ }
+
+
+#ifndef FTC_INLINE
+ FT_LOCAL_DEF( FT_Error )
+ FTC_MruList_Lookup( FTC_MruList list,
+ FT_Pointer key,
+ FTC_MruNode *anode )
+ {
+ FTC_MruNode node;
+
+
+ node = FTC_MruList_Find( list, key );
+ if ( node == NULL )
+ return FTC_MruList_New( list, key, anode );
+
+ *anode = node;
+ return 0;
+ }
+#endif /* FTC_INLINE */
+
+ FT_LOCAL_DEF( void )
+ FTC_MruList_Remove( FTC_MruList list,
+ FTC_MruNode node )
+ {
+ FTC_MruNode_Remove( &list->nodes, node );
+ list->num_nodes--;
+
+ {
+ FT_Memory memory = list->memory;
+
+
+ if ( list->clazz.node_done )
+ list->clazz.node_done( node, list->data );
+
+ FT_FREE( node );
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_MruList_RemoveSelection( FTC_MruList list,
+ FTC_MruNode_CompareFunc selection,
+ FT_Pointer key )
+ {
+ FTC_MruNode first, node, next;
+
+
+ first = list->nodes;
+ while ( first && ( selection == NULL || selection( first, key ) ) )
+ {
+ FTC_MruList_Remove( list, first );
+ first = list->nodes;
+ }
+
+ if ( first )
+ {
+ node = first->next;
+ while ( node != first )
+ {
+ next = node->next;
+
+ if ( selection( node, key ) )
+ FTC_MruList_Remove( list, node );
+
+ node = next;
+ }
+ }
+ }
+
+
+/* END */
diff --git a/src/cache/ftcmru.h b/src/cache/ftcmru.h
new file mode 100644
index 0000000..c8f0c6e
--- /dev/null
+++ b/src/cache/ftcmru.h
@@ -0,0 +1,247 @@
+/***************************************************************************/
+/* */
+/* ftcmru.h */
+/* */
+/* Simple MRU list-cache (specification). */
+/* */
+/* Copyright 2000-2001, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* An MRU is a list that cannot hold more than a certain number of */
+ /* elements (`max_elements'). All elements in the list are sorted in */
+ /* least-recently-used order, i.e., the `oldest' element is at the tail */
+ /* of the list. */
+ /* */
+ /* When doing a lookup (either through `Lookup()' or `Lookup_Node()'), */
+ /* the list is searched for an element with the corresponding key. If */
+ /* it is found, the element is moved to the head of the list and is */
+ /* returned. */
+ /* */
+ /* If no corresponding element is found, the lookup routine will try to */
+ /* obtain a new element with the relevant key. If the list is already */
+ /* full, the oldest element from the list is discarded and replaced by a */
+ /* new one; a new element is added to the list otherwise. */
+ /* */
+ /* Note that it is possible to pre-allocate the element list nodes. */
+ /* This is handy if `max_elements' is sufficiently small, as it saves */
+ /* allocations/releases during the lookup process. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef __FTCMRU_H__
+#define __FTCMRU_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef FREETYPE_H
+#error "freetype.h of FreeType 1 has been loaded!"
+#error "Please fix the directory search order for header files"
+#error "so that freetype.h of FreeType 2 is found first."
+#endif
+
+#define xxFT_DEBUG_ERROR
+#define FTC_INLINE
+
+FT_BEGIN_HEADER
+
+ typedef struct FTC_MruNodeRec_* FTC_MruNode;
+
+ typedef struct FTC_MruNodeRec_
+ {
+ FTC_MruNode next;
+ FTC_MruNode prev;
+
+ } FTC_MruNodeRec;
+
+
+ FT_LOCAL( void )
+ FTC_MruNode_Prepend( FTC_MruNode *plist,
+ FTC_MruNode node );
+
+ FT_LOCAL( void )
+ FTC_MruNode_Up( FTC_MruNode *plist,
+ FTC_MruNode node );
+
+ FT_LOCAL( void )
+ FTC_MruNode_Remove( FTC_MruNode *plist,
+ FTC_MruNode node );
+
+
+ typedef struct FTC_MruListRec_* FTC_MruList;
+
+ typedef struct FTC_MruListClassRec_ const * FTC_MruListClass;
+
+
+ typedef FT_Bool
+ (*FTC_MruNode_CompareFunc)( FTC_MruNode node,
+ FT_Pointer key );
+
+ typedef FT_Error
+ (*FTC_MruNode_InitFunc)( FTC_MruNode node,
+ FT_Pointer key,
+ FT_Pointer data );
+
+ typedef FT_Error
+ (*FTC_MruNode_ResetFunc)( FTC_MruNode node,
+ FT_Pointer key,
+ FT_Pointer data );
+
+ typedef void
+ (*FTC_MruNode_DoneFunc)( FTC_MruNode node,
+ FT_Pointer data );
+
+
+ typedef struct FTC_MruListClassRec_
+ {
+ FT_UInt node_size;
+ FTC_MruNode_CompareFunc node_compare;
+ FTC_MruNode_InitFunc node_init;
+ FTC_MruNode_ResetFunc node_reset;
+ FTC_MruNode_DoneFunc node_done;
+
+ } FTC_MruListClassRec;
+
+ typedef struct FTC_MruListRec_
+ {
+ FT_UInt num_nodes;
+ FT_UInt max_nodes;
+ FTC_MruNode nodes;
+ FT_Pointer data;
+ FTC_MruListClassRec clazz;
+ FT_Memory memory;
+
+ } FTC_MruListRec;
+
+
+ FT_LOCAL( void )
+ FTC_MruList_Init( FTC_MruList list,
+ FTC_MruListClass clazz,
+ FT_UInt max_nodes,
+ FT_Pointer data,
+ FT_Memory memory );
+
+ FT_LOCAL( void )
+ FTC_MruList_Reset( FTC_MruList list );
+
+
+ FT_LOCAL( void )
+ FTC_MruList_Done( FTC_MruList list );
+
+
+ FT_LOCAL( FT_Error )
+ FTC_MruList_New( FTC_MruList list,
+ FT_Pointer key,
+ FTC_MruNode *anode );
+
+ FT_LOCAL( void )
+ FTC_MruList_Remove( FTC_MruList list,
+ FTC_MruNode node );
+
+ FT_LOCAL( void )
+ FTC_MruList_RemoveSelection( FTC_MruList list,
+ FTC_MruNode_CompareFunc selection,
+ FT_Pointer key );
+
+
+#ifdef FTC_INLINE
+
+#define FTC_MRULIST_LOOKUP_CMP( list, key, compare, node, error ) \
+ FT_BEGIN_STMNT \
+ FTC_MruNode* _pfirst = &(list)->nodes; \
+ FTC_MruNode_CompareFunc _compare = (FTC_MruNode_CompareFunc)(compare); \
+ FTC_MruNode _first, _node, *_pnode; \
+ \
+ \
+ error = 0; \
+ _first = *(_pfirst); \
+ _node = NULL; \
+ \
+ if ( _first ) \
+ { \
+ _node = _first; \
+ do \
+ { \
+ if ( _compare( _node, (key) ) ) \
+ { \
+ if ( _node != _first ) \
+ FTC_MruNode_Up( _pfirst, _node ); \
+ \
+ _pnode = (FTC_MruNode*)(void*)&(node); \
+ *_pnode = _node; \
+ goto _MruOk; \
+ } \
+ _node = _node->next; \
+ \
+ } while ( _node != _first) ; \
+ } \
+ \
+ error = FTC_MruList_New( (list), (key), (FTC_MruNode*)(void*)&(node) ); \
+ _MruOk: \
+ ; \
+ FT_END_STMNT
+
+#define FTC_MRULIST_LOOKUP( list, key, node, error ) \
+ FTC_MRULIST_LOOKUP_CMP( list, key, (list)->clazz.node_compare, node, error )
+
+#else /* !FTC_INLINE */
+
+ FT_LOCAL( FTC_MruNode )
+ FTC_MruList_Find( FTC_MruList list,
+ FT_Pointer key );
+
+ FT_LOCAL( FT_Error )
+ FTC_MruList_Lookup( FTC_MruList list,
+ FT_Pointer key,
+ FTC_MruNode *pnode );
+
+#define FTC_MRULIST_LOOKUP( list, key, node, error ) \
+ error = FTC_MruList_Lookup( (list), (key), (FTC_MruNode*)&(node) )
+
+#endif /* !FTC_INLINE */
+
+
+#define FTC_MRULIST_LOOP( list, node ) \
+ FT_BEGIN_STMNT \
+ FTC_MruNode _first = (list)->nodes; \
+ \
+ \
+ if ( _first ) \
+ { \
+ FTC_MruNode _node = _first; \
+ \
+ \
+ do \
+ { \
+ *(FTC_MruNode*)&(node) = _node;
+
+
+#define FTC_MRULIST_LOOP_END() \
+ _node = _node->next; \
+ \
+ } while ( _node != _first ); \
+ } \
+ FT_END_STMNT
+
+ /* */
+
+FT_END_HEADER
+
+
+#endif /* __FTCMRU_H__ */
+
+
+/* END */
diff --git a/src/cache/ftcsbits.c b/src/cache/ftcsbits.c
new file mode 100644
index 0000000..72f139d
--- /dev/null
+++ b/src/cache/ftcsbits.c
@@ -0,0 +1,401 @@
+/***************************************************************************/
+/* */
+/* ftcsbits.c */
+/* */
+/* FreeType sbits manager (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcsbits.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_ERRORS_H
+
+#include "ftccback.h"
+#include "ftcerror.h"
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SBIT CACHE NODES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static FT_Error
+ ftc_sbit_copy_bitmap( FTC_SBit sbit,
+ FT_Bitmap* bitmap,
+ FT_Memory memory )
+ {
+ FT_Error error;
+ FT_Int pitch = bitmap->pitch;
+ FT_ULong size;
+
+
+ if ( pitch < 0 )
+ pitch = -pitch;
+
+ size = (FT_ULong)( pitch * bitmap->rows );
+
+ if ( !FT_ALLOC( sbit->buffer, size ) )
+ FT_MEM_COPY( sbit->buffer, bitmap->buffer, size );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ftc_snode_free( FTC_Node ftcsnode,
+ FTC_Cache cache )
+ {
+ FTC_SNode snode = (FTC_SNode)ftcsnode;
+ FTC_SBit sbit = snode->sbits;
+ FT_UInt count = snode->count;
+ FT_Memory memory = cache->memory;
+
+
+ for ( ; count > 0; sbit++, count-- )
+ FT_FREE( sbit->buffer );
+
+ FTC_GNode_Done( FTC_GNODE( snode ), cache );
+
+ FT_FREE( snode );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ FTC_SNode_Free( FTC_SNode snode,
+ FTC_Cache cache )
+ {
+ ftc_snode_free( FTC_NODE( snode ), cache );
+ }
+
+
+ /*
+ * This function tries to load a small bitmap within a given FTC_SNode.
+ * Note that it returns a non-zero error code _only_ in the case of
+ * out-of-memory condition. For all other errors (e.g., corresponding
+ * to a bad font file), this function will mark the sbit as `unavailable'
+ * and return a value of 0.
+ *
+ * You should also read the comment within the @ftc_snode_compare
+ * function below to see how out-of-memory is handled during a lookup.
+ */
+ static FT_Error
+ ftc_snode_load( FTC_SNode snode,
+ FTC_Manager manager,
+ FT_UInt gindex,
+ FT_ULong *asize )
+ {
+ FT_Error error;
+ FTC_GNode gnode = FTC_GNODE( snode );
+ FTC_Family family = gnode->family;
+ FT_Memory memory = manager->memory;
+ FT_Face face;
+ FTC_SBit sbit;
+ FTC_SFamilyClass clazz;
+
+
+ if ( (FT_UInt)(gindex - gnode->gindex) >= snode->count )
+ {
+ FT_ERROR(( "ftc_snode_load: invalid glyph index" ));
+ return FTC_Err_Invalid_Argument;
+ }
+
+ sbit = snode->sbits + ( gindex - gnode->gindex );
+ clazz = (FTC_SFamilyClass)family->clazz;
+
+ sbit->buffer = 0;
+
+ error = clazz->family_load_glyph( family, gindex, manager, &face );
+ if ( error )
+ goto BadGlyph;
+
+ {
+ FT_Int temp;
+ FT_GlyphSlot slot = face->glyph;
+ FT_Bitmap* bitmap = &slot->bitmap;
+ FT_Int xadvance, yadvance;
+
+
+ if ( slot->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ FT_ERROR(( "%s: glyph loaded didn't return a bitmap!\n",
+ "ftc_snode_load" ));
+ goto BadGlyph;
+ }
+
+ /* Check that our values fit into 8-bit containers! */
+ /* If this is not the case, our bitmap is too large */
+ /* and we will leave it as `missing' with sbit.buffer = 0 */
+
+#define CHECK_CHAR( d ) ( temp = (FT_Char)d, temp == d )
+#define CHECK_BYTE( d ) ( temp = (FT_Byte)d, temp == d )
+
+ /* horizontal advance in pixels */
+ xadvance = ( slot->advance.x + 32 ) >> 6;
+ yadvance = ( slot->advance.y + 32 ) >> 6;
+
+ if ( !CHECK_BYTE( bitmap->rows ) ||
+ !CHECK_BYTE( bitmap->width ) ||
+ !CHECK_CHAR( bitmap->pitch ) ||
+ !CHECK_CHAR( slot->bitmap_left ) ||
+ !CHECK_CHAR( slot->bitmap_top ) ||
+ !CHECK_CHAR( xadvance ) ||
+ !CHECK_CHAR( yadvance ) )
+ goto BadGlyph;
+
+ sbit->width = (FT_Byte)bitmap->width;
+ sbit->height = (FT_Byte)bitmap->rows;
+ sbit->pitch = (FT_Char)bitmap->pitch;
+ sbit->left = (FT_Char)slot->bitmap_left;
+ sbit->top = (FT_Char)slot->bitmap_top;
+ sbit->xadvance = (FT_Char)xadvance;
+ sbit->yadvance = (FT_Char)yadvance;
+ sbit->format = (FT_Byte)bitmap->pixel_mode;
+ sbit->max_grays = (FT_Byte)(bitmap->num_grays - 1);
+
+ /* copy the bitmap into a new buffer -- ignore error */
+ error = ftc_sbit_copy_bitmap( sbit, bitmap, memory );
+
+ /* now, compute size */
+ if ( asize )
+ *asize = FT_ABS( sbit->pitch ) * sbit->height;
+
+ } /* glyph loading successful */
+
+ /* ignore the errors that might have occurred -- */
+ /* we mark unloaded glyphs with `sbit.buffer == 0' */
+ /* and `width == 255', `height == 0' */
+ /* */
+ if ( error && error != FTC_Err_Out_Of_Memory )
+ {
+ BadGlyph:
+ sbit->width = 255;
+ sbit->height = 0;
+ sbit->buffer = NULL;
+ error = 0;
+ if ( asize )
+ *asize = 0;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ FTC_SNode_New( FTC_SNode *psnode,
+ FTC_GQuery gquery,
+ FTC_Cache cache )
+ {
+ FT_Memory memory = cache->memory;
+ FT_Error error;
+ FTC_SNode snode = NULL;
+ FT_UInt gindex = gquery->gindex;
+ FTC_Family family = gquery->family;
+
+ FTC_SFamilyClass clazz = FTC_CACHE__SFAMILY_CLASS( cache );
+ FT_UInt total;
+
+
+ total = clazz->family_get_count( family, cache->manager );
+ if ( total == 0 || gindex >= total )
+ {
+ error = FT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( !FT_NEW( snode ) )
+ {
+ FT_UInt count, start;
+
+
+ start = gindex - ( gindex % FTC_SBIT_ITEMS_PER_NODE );
+ count = total - start;
+ if ( count > FTC_SBIT_ITEMS_PER_NODE )
+ count = FTC_SBIT_ITEMS_PER_NODE;
+
+ FTC_GNode_Init( FTC_GNODE( snode ), start, family );
+
+ snode->count = count;
+
+ error = ftc_snode_load( snode,
+ cache->manager,
+ gindex,
+ NULL );
+ if ( error )
+ {
+ FTC_SNode_Free( snode, cache );
+ snode = NULL;
+ }
+ }
+
+ Exit:
+ *psnode = snode;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ftc_snode_new( FTC_Node *ftcpsnode,
+ FT_Pointer ftcgquery,
+ FTC_Cache cache )
+ {
+ FTC_SNode *psnode = (FTC_SNode*)ftcpsnode;
+ FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
+
+
+ return FTC_SNode_New( psnode, gquery, cache );
+ }
+
+
+ FT_LOCAL_DEF( FT_ULong )
+ ftc_snode_weight( FTC_Node ftcsnode,
+ FTC_Cache cache )
+ {
+ FTC_SNode snode = (FTC_SNode)ftcsnode;
+ FT_UInt count = snode->count;
+ FTC_SBit sbit = snode->sbits;
+ FT_Int pitch;
+ FT_ULong size;
+
+ FT_UNUSED( cache );
+
+
+ FT_ASSERT( snode->count <= FTC_SBIT_ITEMS_PER_NODE );
+
+ /* the node itself */
+ size = sizeof ( *snode );
+
+ for ( ; count > 0; count--, sbit++ )
+ {
+ if ( sbit->buffer )
+ {
+ pitch = sbit->pitch;
+ if ( pitch < 0 )
+ pitch = -pitch;
+
+ /* add the size of a given glyph image */
+ size += pitch * sbit->height;
+ }
+ }
+
+ return size;
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_ULong )
+ FTC_SNode_Weight( FTC_SNode snode )
+ {
+ return ftc_snode_weight( FTC_NODE( snode ), NULL );
+ }
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ ftc_snode_compare( FTC_Node ftcsnode,
+ FT_Pointer ftcgquery,
+ FTC_Cache cache )
+ {
+ FTC_SNode snode = (FTC_SNode)ftcsnode;
+ FTC_GQuery gquery = (FTC_GQuery)ftcgquery;
+ FTC_GNode gnode = FTC_GNODE( snode );
+ FT_UInt gindex = gquery->gindex;
+ FT_Bool result;
+
+
+ result = FT_BOOL( gnode->family == gquery->family &&
+ (FT_UInt)( gindex - gnode->gindex ) < snode->count );
+ if ( result )
+ {
+ /* check if we need to load the glyph bitmap now */
+ FTC_SBit sbit = snode->sbits + ( gindex - gnode->gindex );
+
+
+ /*
+ * The following code illustrates what to do when you want to
+ * perform operations that may fail within a lookup function.
+ *
+ * Here, we want to load a small bitmap on-demand; we thus
+ * need to call the `ftc_snode_load' function which may return
+ * a non-zero error code only when we are out of memory (OOM).
+ *
+ * The correct thing to do is to use @FTC_CACHE_TRYLOOP and
+ * @FTC_CACHE_TRYLOOP_END in order to implement a retry loop
+ * that is capable of flushing the cache incrementally when
+ * an OOM errors occur.
+ *
+ * However, we need to `lock' the node before this operation to
+ * prevent it from being flushed within the loop.
+ *
+ * When we exit the loop, we unlock the node, then check the `error'
+ * variable. If it is non-zero, this means that the cache was
+ * completely flushed and that no usable memory was found to load
+ * the bitmap.
+ *
+ * We then prefer to return a value of 0 (i.e., NO MATCH). This
+ * ensures that the caller will try to allocate a new node.
+ * This operation consequently _fail_ and the lookup function
+ * returns the appropriate OOM error code.
+ *
+ * Note that `buffer == NULL && width == 255' is a hack used to
+ * tag `unavailable' bitmaps in the array. We should never try
+ * to load these.
+ *
+ */
+
+ if ( sbit->buffer == NULL && sbit->width != 255 )
+ {
+ FT_ULong size;
+ FT_Error error;
+
+
+ ftcsnode->ref_count++; /* lock node to prevent flushing */
+ /* in retry loop */
+
+ FTC_CACHE_TRYLOOP( cache )
+ {
+ error = ftc_snode_load( snode, cache->manager, gindex, &size );
+ }
+ FTC_CACHE_TRYLOOP_END();
+
+ ftcsnode->ref_count--; /* unlock the node */
+
+ if ( error )
+ result = 0;
+ else
+ cache->manager->cur_weight += size;
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ FTC_SNode_Compare( FTC_SNode snode,
+ FTC_GQuery gquery,
+ FTC_Cache cache )
+ {
+ return ftc_snode_compare( FTC_NODE( snode ), gquery, cache );
+ }
+
+
+/* END */
diff --git a/src/cache/ftcsbits.h b/src/cache/ftcsbits.h
new file mode 100644
index 0000000..6261745
--- /dev/null
+++ b/src/cache/ftcsbits.h
@@ -0,0 +1,98 @@
+/***************************************************************************/
+/* */
+/* ftcsbits.h */
+/* */
+/* A small-bitmap cache (specification). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTCSBITS_H__
+#define __FTCSBITS_H__
+
+
+#include <ft2build.h>
+#include FT_CACHE_H
+#include "ftcglyph.h"
+
+
+FT_BEGIN_HEADER
+
+#define FTC_SBIT_ITEMS_PER_NODE 16
+
+ typedef struct FTC_SNodeRec_
+ {
+ FTC_GNodeRec gnode;
+ FT_UInt count;
+ FTC_SBitRec sbits[FTC_SBIT_ITEMS_PER_NODE];
+
+ } FTC_SNodeRec, *FTC_SNode;
+
+
+#define FTC_SNODE( x ) ( (FTC_SNode)( x ) )
+#define FTC_SNODE_GINDEX( x ) FTC_GNODE( x )->gindex
+#define FTC_SNODE_FAMILY( x ) FTC_GNODE( x )->family
+
+ typedef FT_UInt
+ (*FTC_SFamily_GetCountFunc)( FTC_Family family,
+ FTC_Manager manager );
+
+ typedef FT_Error
+ (*FTC_SFamily_LoadGlyphFunc)( FTC_Family family,
+ FT_UInt gindex,
+ FTC_Manager manager,
+ FT_Face *aface );
+
+ typedef struct FTC_SFamilyClassRec_
+ {
+ FTC_MruListClassRec clazz;
+ FTC_SFamily_GetCountFunc family_get_count;
+ FTC_SFamily_LoadGlyphFunc family_load_glyph;
+
+ } FTC_SFamilyClassRec;
+
+ typedef const FTC_SFamilyClassRec* FTC_SFamilyClass;
+
+#define FTC_SFAMILY_CLASS( x ) ((FTC_SFamilyClass)(x))
+
+#define FTC_CACHE__SFAMILY_CLASS( x ) \
+ FTC_SFAMILY_CLASS( FTC_CACHE__GCACHE_CLASS( x )->family_class )
+
+
+ FT_LOCAL( void )
+ FTC_SNode_Free( FTC_SNode snode,
+ FTC_Cache cache );
+
+ FT_LOCAL( FT_Error )
+ FTC_SNode_New( FTC_SNode *psnode,
+ FTC_GQuery gquery,
+ FTC_Cache cache );
+
+#if 0
+ FT_LOCAL( FT_ULong )
+ FTC_SNode_Weight( FTC_SNode inode );
+#endif
+
+
+ FT_LOCAL( FT_Bool )
+ FTC_SNode_Compare( FTC_SNode snode,
+ FTC_GQuery gquery,
+ FTC_Cache cache );
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __FTCSBITS_H__ */
+
+
+/* END */
diff --git a/src/cache/rules.mk b/src/cache/rules.mk
new file mode 100644
index 0000000..457dec8
--- /dev/null
+++ b/src/cache/rules.mk
@@ -0,0 +1,78 @@
+#
+# FreeType 2 Cache configuration rules
+#
+
+
+# Copyright 2000, 2001, 2003, 2004, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# Cache driver directory
+#
+CACHE_DIR := $(SRC_DIR)/cache
+
+# compilation flags for the driver
+#
+CACHE_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(CACHE_DIR))
+
+
+# Cache driver sources (i.e., C files)
+#
+CACHE_DRV_SRC := $(CACHE_DIR)/ftcbasic.c \
+ $(CACHE_DIR)/ftccache.c \
+ $(CACHE_DIR)/ftccmap.c \
+ $(CACHE_DIR)/ftcglyph.c \
+ $(CACHE_DIR)/ftcimage.c \
+ $(CACHE_DIR)/ftcmanag.c \
+ $(CACHE_DIR)/ftcmru.c \
+ $(CACHE_DIR)/ftcsbits.c
+
+# Cache driver headers
+#
+CACHE_DRV_H := $(CACHE_DIR)/ftccback.h \
+ $(CACHE_DIR)/ftcerror.h \
+ $(CACHE_DIR)/ftcglyph.h \
+ $(CACHE_DIR)/ftcimage.h \
+ $(CACHE_DIR)/ftcmanag.h \
+ $(CACHE_DIR)/ftcmru.h
+
+
+# Cache driver object(s)
+#
+# CACHE_DRV_OBJ_M is used during `multi' builds.
+# CACHE_DRV_OBJ_S is used during `single' builds.
+#
+CACHE_DRV_OBJ_M := $(CACHE_DRV_SRC:$(CACHE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+CACHE_DRV_OBJ_S := $(OBJ_DIR)/ftcache.$O
+
+# Cache driver source file for single build
+#
+CACHE_DRV_SRC_S := $(CACHE_DIR)/ftcache.c
+
+
+# Cache driver - single object
+#
+$(CACHE_DRV_OBJ_S): $(CACHE_DRV_SRC_S) $(CACHE_DRV_SRC) \
+ $(FREETYPE_H) $(CACHE_DRV_H)
+ $(CACHE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CACHE_DRV_SRC_S))
+
+
+# Cache driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(CACHE_DIR)/%.c $(FREETYPE_H) $(CACHE_DRV_H)
+ $(CACHE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(CACHE_DRV_OBJ_S)
+DRV_OBJS_M += $(CACHE_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/cff/Jamfile b/src/cff/Jamfile
new file mode 100644
index 0000000..6d0bb1b
--- /dev/null
+++ b/src/cff/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/cff Jamfile
+#
+# Copyright 2001, 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) cff ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = cffdrivr cffgload cffload cffobjs cffparse cffcmap ;
+ }
+ else
+ {
+ _sources = cff ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/cff Jamfile
diff --git a/src/cff/cff.c b/src/cff/cff.c
new file mode 100644
index 0000000..e6d8954
--- /dev/null
+++ b/src/cff/cff.c
@@ -0,0 +1,29 @@
+/***************************************************************************/
+/* */
+/* cff.c */
+/* */
+/* FreeType OpenType driver component (body only). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "cffdrivr.c"
+#include "cffparse.c"
+#include "cffload.c"
+#include "cffobjs.c"
+#include "cffgload.c"
+#include "cffcmap.c"
+
+/* END */
diff --git a/src/cff/cffcmap.c b/src/cff/cffcmap.c
new file mode 100644
index 0000000..fffc5fc
--- /dev/null
+++ b/src/cff/cffcmap.c
@@ -0,0 +1,220 @@
+/***************************************************************************/
+/* */
+/* cffcmap.c */
+/* */
+/* CFF character mapping table (cmap) support (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "cffcmap.h"
+#include "cffload.h"
+
+#include "cfferrs.h"
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( FT_Error )
+ cff_cmap_encoding_init( CFF_CMapStd cmap )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( cmap );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ CFF_Encoding encoding = &cff->encoding;
+
+
+ cmap->gids = encoding->codes;
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ cff_cmap_encoding_done( CFF_CMapStd cmap )
+ {
+ cmap->gids = NULL;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_encoding_char_index( CFF_CMapStd cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt result = 0;
+
+
+ if ( char_code < 256 )
+ result = cmap->gids[char_code];
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_encoding_char_next( CFF_CMapStd cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code;
+
+
+ *pchar_code = 0;
+
+ if ( char_code < 255 )
+ {
+ FT_UInt code = (FT_UInt)(char_code + 1);
+
+
+ for (;;)
+ {
+ if ( code >= 256 )
+ break;
+
+ result = cmap->gids[code];
+ if ( result != 0 )
+ {
+ *pchar_code = code;
+ break;
+ }
+
+ code++;
+ }
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ cff_cmap_encoding_class_rec =
+ {
+ sizeof ( CFF_CMapStdRec ),
+
+ (FT_CMap_InitFunc) cff_cmap_encoding_init,
+ (FT_CMap_DoneFunc) cff_cmap_encoding_done,
+ (FT_CMap_CharIndexFunc)cff_cmap_encoding_char_index,
+ (FT_CMap_CharNextFunc) cff_cmap_encoding_char_next
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( const char* )
+ cff_sid_to_glyph_name( TT_Face face,
+ FT_UInt idx )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ CFF_Charset charset = &cff->charset;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+ FT_UInt sid = charset->sids[idx];
+
+
+ return cff_index_get_sid_string( &cff->string_index, sid, psnames );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ cff_sid_free_glyph_name( TT_Face face,
+ const char* gname )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( gname );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ cff_cmap_unicode_init( PS_Unicodes unicodes )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ CFF_Charset charset = &cff->charset;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+
+
+ /* can't build Unicode map for CID-keyed font */
+ if ( !charset->sids )
+ return CFF_Err_Invalid_Argument;
+
+ return psnames->unicodes_init( memory,
+ unicodes,
+ cff->num_glyphs,
+ (PS_GetGlyphNameFunc)&cff_sid_to_glyph_name,
+ (PS_FreeGlyphNameFunc)&cff_sid_free_glyph_name,
+ (FT_Pointer)face );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ cff_cmap_unicode_done( PS_Unicodes unicodes )
+ {
+ FT_Face face = FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( unicodes->maps );
+ unicodes->num_maps = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_unicode_char_index( PS_Unicodes unicodes,
+ FT_UInt32 char_code )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+
+
+ return psnames->unicodes_char_index( unicodes, char_code );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ cff_cmap_unicode_char_next( PS_Unicodes unicodes,
+ FT_UInt32 *pchar_code )
+ {
+ TT_Face face = (TT_Face)FT_CMAP_FACE( unicodes );
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)cff->psnames;
+
+
+ return psnames->unicodes_char_next( unicodes, pchar_code );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ cff_cmap_unicode_class_rec =
+ {
+ sizeof ( PS_UnicodesRec ),
+
+ (FT_CMap_InitFunc) cff_cmap_unicode_init,
+ (FT_CMap_DoneFunc) cff_cmap_unicode_done,
+ (FT_CMap_CharIndexFunc)cff_cmap_unicode_char_index,
+ (FT_CMap_CharNextFunc) cff_cmap_unicode_char_next
+ };
+
+
+/* END */
diff --git a/src/cff/cffcmap.h b/src/cff/cffcmap.h
new file mode 100644
index 0000000..3809b85
--- /dev/null
+++ b/src/cff/cffcmap.h
@@ -0,0 +1,69 @@
+/***************************************************************************/
+/* */
+/* cffcmap.h */
+/* */
+/* CFF character mapping table (cmap) support (specification). */
+/* */
+/* Copyright 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFCMAP_H__
+#define __CFFCMAP_H__
+
+#include "cffobjs.h"
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* standard (and expert) encoding cmaps */
+ typedef struct CFF_CMapStdRec_* CFF_CMapStd;
+
+ typedef struct CFF_CMapStdRec_
+ {
+ FT_CMapRec cmap;
+ FT_UShort* gids; /* up to 256 elements */
+
+ } CFF_CMapStdRec;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ cff_cmap_encoding_class_rec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CFF SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* unicode (synthetic) cmaps */
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ cff_cmap_unicode_class_rec;
+
+
+FT_END_HEADER
+
+#endif /* __CFFCMAP_H__ */
+
+
+/* END */
diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c
new file mode 100644
index 0000000..952e88e
--- /dev/null
+++ b/src/cff/cffdrivr.c
@@ -0,0 +1,499 @@
+/***************************************************************************/
+/* */
+/* cffdrivr.c */
+/* */
+/* OpenType font driver implementation (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H
+#include FT_TRUETYPE_IDS_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_SERVICE_POSTSCRIPT_INFO_H
+#include FT_SERVICE_TT_CMAP_H
+
+#include "cffdrivr.h"
+#include "cffgload.h"
+#include "cffload.h"
+#include "cffcmap.h"
+
+#include "cfferrs.h"
+
+#include FT_SERVICE_XFREE86_NAME_H
+#include FT_SERVICE_GLYPH_DICT_H
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffdriver
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** F A C E S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#undef PAIR_TAG
+#define PAIR_TAG( left, right ) ( ( (FT_ULong)left << 16 ) | \
+ (FT_ULong)right )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_get_kerning */
+ /* */
+ /* <Description> */
+ /* A driver method used to return the kerning vector between two */
+ /* glyphs of the same face. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* left_glyph :: The index of the left glyph in the kern pair. */
+ /* */
+ /* right_glyph :: The index of the right glyph in the kern pair. */
+ /* */
+ /* <Output> */
+ /* kerning :: The kerning vector. This is in font units for */
+ /* scalable formats, and in pixels for fixed-sizes */
+ /* formats. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only horizontal layouts (left-to-right & right-to-left) are */
+ /* supported by this function. Other layouts, or more sophisticated */
+ /* kernings, are out of scope of this method (the basic driver */
+ /* interface is meant to be simple). */
+ /* */
+ /* They can be implemented by format-specific interfaces. */
+ /* */
+ FT_CALLBACK_DEF( FT_Error )
+ cff_get_kerning( FT_Face ttface, /* TT_Face */
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_Vector* kerning )
+ {
+ TT_Face face = (TT_Face)ttface;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+
+ kerning->x = 0;
+ kerning->y = 0;
+
+ if ( sfnt )
+ kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
+
+ return CFF_Err_Ok;
+ }
+
+
+#undef PAIR_TAG
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Load_Glyph */
+ /* */
+ /* <Description> */
+ /* A driver method used to load a glyph within a given glyph slot. */
+ /* */
+ /* <Input> */
+ /* slot :: A handle to the target slot object where the glyph */
+ /* will be loaded. */
+ /* */
+ /* size :: A handle to the source face size at which the glyph */
+ /* must be scaled, loaded, etc. */
+ /* */
+ /* glyph_index :: The index of the glyph in the font file. */
+ /* */
+ /* load_flags :: A flag indicating what to load for this glyph. The */
+ /* FT_LOAD_??? constants can be used to control the */
+ /* glyph loading process (e.g., whether the outline */
+ /* should be scaled, whether to load bitmaps or not, */
+ /* whether to hint the outline, etc). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_CALLBACK_DEF( FT_Error )
+ Load_Glyph( FT_GlyphSlot cffslot, /* CFF_GlyphSlot */
+ FT_Size cffsize, /* CFF_Size */
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ CFF_GlyphSlot slot = (CFF_GlyphSlot)cffslot;
+ CFF_Size size = (CFF_Size)cffsize;
+
+
+ if ( !slot )
+ return CFF_Err_Invalid_Slot_Handle;
+
+ /* check whether we want a scaled outline or bitmap */
+ if ( !size )
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
+
+ if ( load_flags & FT_LOAD_NO_SCALE )
+ size = NULL;
+
+ /* reset the size object if necessary */
+ if ( size )
+ {
+ /* these two objects must have the same parent */
+ if ( cffsize->face != cffslot->face )
+ return CFF_Err_Invalid_Face_Handle;
+ }
+
+ /* now load the glyph outline if necessary */
+ error = cff_slot_load( slot, size, glyph_index, load_flags );
+
+ /* force drop-out mode to 2 - irrelevant now */
+ /* slot->outline.dropout_mode = 2; */
+
+ return error;
+ }
+
+
+ /*
+ * GLYPH DICT SERVICE
+ *
+ */
+
+ static FT_Error
+ cff_get_glyph_name( CFF_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ CFF_Font font = (CFF_Font)face->extra.data;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_String* gname;
+ FT_UShort sid;
+ FT_Service_PsCMaps psnames;
+ FT_Error error;
+
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ if ( !psnames )
+ {
+ FT_ERROR(( "cff_get_glyph_name:" ));
+ FT_ERROR(( " cannot get glyph name from CFF & CEF fonts\n" ));
+ FT_ERROR(( " " ));
+ FT_ERROR(( " without the `PSNames' module\n" ));
+ error = CFF_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* first, locate the sid in the charset table */
+ sid = font->charset.sids[glyph_index];
+
+ /* now, lookup the name itself */
+ gname = cff_index_get_sid_string( &font->string_index, sid, psnames );
+
+ if ( gname )
+ FT_STRCPYN( buffer, gname, buffer_max );
+
+ FT_FREE( gname );
+ error = CFF_Err_Ok;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_UInt
+ cff_get_name_index( CFF_Face face,
+ FT_String* glyph_name )
+ {
+ CFF_Font cff;
+ CFF_Charset charset;
+ FT_Service_PsCMaps psnames;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_String* name;
+ FT_UShort sid;
+ FT_UInt i;
+ FT_Int result;
+
+
+ cff = (CFF_FontRec *)face->extra.data;
+ charset = &cff->charset;
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ if ( !psnames )
+ return 0;
+
+ for ( i = 0; i < cff->num_glyphs; i++ )
+ {
+ sid = charset->sids[i];
+
+ if ( sid > 390 )
+ name = cff_index_get_name( &cff->string_index, sid - 391 );
+ else
+ name = (FT_String *)psnames->adobe_std_strings( sid );
+
+ if ( !name )
+ continue;
+
+ result = ft_strcmp( glyph_name, name );
+
+ if ( sid > 390 )
+ FT_FREE( name );
+
+ if ( !result )
+ return i;
+ }
+
+ return 0;
+ }
+
+
+ static const FT_Service_GlyphDictRec cff_service_glyph_dict =
+ {
+ (FT_GlyphDict_GetNameFunc) cff_get_glyph_name,
+ (FT_GlyphDict_NameIndexFunc)cff_get_name_index,
+ };
+
+
+ /*
+ * POSTSCRIPT INFO SERVICE
+ *
+ */
+
+ static FT_Int
+ cff_ps_has_glyph_names( FT_Face face )
+ {
+ return ( face->face_flags & FT_FACE_FLAG_GLYPH_NAMES ) > 0;
+ }
+
+
+ static FT_Error
+ cff_ps_get_font_info( CFF_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+ FT_Error error = FT_Err_Ok;
+
+
+ if ( cff && cff->font_info == NULL )
+ {
+ CFF_FontRecDict dict = &cff->top_font.font_dict;
+ PS_FontInfoRec *font_info;
+ FT_Memory memory = face->root.memory;
+
+
+ if ( FT_ALLOC( font_info, sizeof ( *font_info ) ) )
+ goto Fail;
+
+ font_info->version = cff_index_get_sid_string( &cff->string_index,
+ dict->version,
+ cff->psnames );
+ font_info->notice = cff_index_get_sid_string( &cff->string_index,
+ dict->notice,
+ cff->psnames );
+ font_info->full_name = cff_index_get_sid_string( &cff->string_index,
+ dict->full_name,
+ cff->psnames );
+ font_info->family_name = cff_index_get_sid_string( &cff->string_index,
+ dict->family_name,
+ cff->psnames );
+ font_info->weight = cff_index_get_sid_string( &cff->string_index,
+ dict->weight,
+ cff->psnames );
+ font_info->italic_angle = dict->italic_angle;
+ font_info->is_fixed_pitch = dict->is_fixed_pitch;
+ font_info->underline_position = (FT_Short)dict->underline_position;
+ font_info->underline_thickness = (FT_Short)dict->underline_thickness;
+
+ cff->font_info = font_info;
+ }
+
+ *afont_info = *cff->font_info;
+
+ Fail:
+ return error;
+ }
+
+
+ static const FT_Service_PsInfoRec cff_service_ps_info =
+ {
+ (PS_GetFontInfoFunc) cff_ps_get_font_info,
+ (PS_HasGlyphNamesFunc) cff_ps_has_glyph_names,
+ (PS_GetFontPrivateFunc)NULL /* unsupported with CFF fonts */
+ };
+
+
+ /*
+ * TT CMAP INFO
+ *
+ * If the charmap is a synthetic Unicode encoding cmap or
+ * a Type 1 standard (or expert) encoding cmap, hide TT CMAP INFO
+ * service defined in SFNT module.
+ *
+ * Otherwise call the service function in the sfnt module.
+ *
+ */
+ static FT_Error
+ cff_get_cmap_info( FT_CharMap charmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_CMap cmap = FT_CMAP( charmap );
+ FT_Error error = CFF_Err_Ok;
+
+
+ cmap_info->language = 0;
+
+ if ( cmap->clazz != &cff_cmap_encoding_class_rec &&
+ cmap->clazz != &cff_cmap_unicode_class_rec )
+ {
+ FT_Face face = FT_CMAP_FACE( cmap );
+ FT_Library library = FT_FACE_LIBRARY( face );
+ FT_Module sfnt = FT_Get_Module( library, "sfnt" );
+ FT_Service_TTCMaps service =
+ (FT_Service_TTCMaps)ft_module_get_service( sfnt,
+ FT_SERVICE_ID_TT_CMAP );
+
+
+ if ( service && service->get_cmap_info )
+ error = service->get_cmap_info( charmap, cmap_info );
+ }
+
+ return error;
+ }
+
+
+ static const FT_Service_TTCMapsRec cff_service_get_cmap_info =
+ {
+ (TT_CMap_Info_GetFunc)cff_get_cmap_info
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** D R I V E R I N T E R F A C E ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static const FT_ServiceDescRec cff_services[] =
+ {
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_CFF },
+ { FT_SERVICE_ID_POSTSCRIPT_INFO, &cff_service_ps_info },
+#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ { FT_SERVICE_ID_GLYPH_DICT, &cff_service_glyph_dict },
+#endif
+ { FT_SERVICE_ID_TT_CMAP, &cff_service_get_cmap_info },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ cff_get_interface( FT_Module driver, /* CFF_Driver */
+ const char* module_interface )
+ {
+ FT_Module sfnt;
+ FT_Module_Interface result;
+
+
+ result = ft_service_list_lookup( cff_services, module_interface );
+ if ( result != NULL )
+ return result;
+
+ /* we pass our request to the `sfnt' module */
+ sfnt = FT_Get_Module( driver->library, "sfnt" );
+
+ return sfnt ? sfnt->clazz->get_interface( sfnt, module_interface ) : 0;
+ }
+
+
+ /* The FT_DriverInterface structure is defined in ftdriver.h. */
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec cff_driver_class =
+ {
+ /* begin with the FT_Module_Class fields */
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE |
+ FT_MODULE_DRIVER_HAS_HINTER,
+
+ sizeof( CFF_DriverRec ),
+ "cff",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module-specific interface */
+
+ cff_driver_init,
+ cff_driver_done,
+ cff_get_interface,
+ },
+
+ /* now the specific driver fields */
+ sizeof( TT_FaceRec ),
+ sizeof( CFF_SizeRec ),
+ sizeof( CFF_GlyphSlotRec ),
+
+ cff_face_init,
+ cff_face_done,
+ cff_size_init,
+ cff_size_done,
+ cff_slot_init,
+ cff_slot_done,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+
+ Load_Glyph,
+
+ cff_get_kerning,
+ 0, /* FT_Face_AttachFunc */
+ 0, /* FT_Face_GetAdvancesFunc */
+
+ cff_size_request,
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ cff_size_select
+#else
+ 0 /* FT_Size_SelectFunc */
+#endif
+ };
+
+
+/* END */
diff --git a/src/cff/cffdrivr.h b/src/cff/cffdrivr.h
new file mode 100644
index 0000000..553848c
--- /dev/null
+++ b/src/cff/cffdrivr.h
@@ -0,0 +1,39 @@
+/***************************************************************************/
+/* */
+/* cffdrivr.h */
+/* */
+/* High-level OpenType driver interface (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFDRIVER_H__
+#define __CFFDRIVER_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_CALLBACK_TABLE
+ const FT_Driver_ClassRec cff_driver_class;
+
+
+FT_END_HEADER
+
+#endif /* __CFFDRIVER_H__ */
+
+
+/* END */
diff --git a/src/cff/cfferrs.h b/src/cff/cfferrs.h
new file mode 100644
index 0000000..1b2a5c9
--- /dev/null
+++ b/src/cff/cfferrs.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* cfferrs.h */
+/* */
+/* CFF error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the CFF error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __CFFERRS_H__
+#define __CFFERRS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX CFF_Err_
+#define FT_ERR_BASE FT_Mod_Err_CFF
+
+
+#include FT_ERRORS_H
+
+#endif /* __CFFERRS_H__ */
+
+
+/* END */
diff --git a/src/cff/cffgload.c b/src/cff/cffgload.c
new file mode 100644
index 0000000..0e2a179
--- /dev/null
+++ b/src/cff/cffgload.c
@@ -0,0 +1,2634 @@
+/***************************************************************************/
+/* */
+/* cffgload.c */
+/* */
+/* OpenType Glyph Loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H
+#include FT_OUTLINE_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+#include "cffobjs.h"
+#include "cffload.h"
+#include "cffgload.h"
+
+#include "cfferrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffgload
+
+
+ typedef enum CFF_Operator_
+ {
+ cff_op_unknown = 0,
+
+ cff_op_rmoveto,
+ cff_op_hmoveto,
+ cff_op_vmoveto,
+
+ cff_op_rlineto,
+ cff_op_hlineto,
+ cff_op_vlineto,
+
+ cff_op_rrcurveto,
+ cff_op_hhcurveto,
+ cff_op_hvcurveto,
+ cff_op_rcurveline,
+ cff_op_rlinecurve,
+ cff_op_vhcurveto,
+ cff_op_vvcurveto,
+
+ cff_op_flex,
+ cff_op_hflex,
+ cff_op_hflex1,
+ cff_op_flex1,
+
+ cff_op_endchar,
+
+ cff_op_hstem,
+ cff_op_vstem,
+ cff_op_hstemhm,
+ cff_op_vstemhm,
+
+ cff_op_hintmask,
+ cff_op_cntrmask,
+ cff_op_dotsection, /* deprecated, acts as no-op */
+
+ cff_op_abs,
+ cff_op_add,
+ cff_op_sub,
+ cff_op_div,
+ cff_op_neg,
+ cff_op_random,
+ cff_op_mul,
+ cff_op_sqrt,
+
+ cff_op_blend,
+
+ cff_op_drop,
+ cff_op_exch,
+ cff_op_index,
+ cff_op_roll,
+ cff_op_dup,
+
+ cff_op_put,
+ cff_op_get,
+ cff_op_store,
+ cff_op_load,
+
+ cff_op_and,
+ cff_op_or,
+ cff_op_not,
+ cff_op_eq,
+ cff_op_ifelse,
+
+ cff_op_callsubr,
+ cff_op_callgsubr,
+ cff_op_return,
+
+ /* do not remove */
+ cff_op_max
+
+ } CFF_Operator;
+
+
+#define CFF_COUNT_CHECK_WIDTH 0x80
+#define CFF_COUNT_EXACT 0x40
+#define CFF_COUNT_CLEAR_STACK 0x20
+
+
+ static const FT_Byte cff_argument_counts[] =
+ {
+ 0, /* unknown */
+
+ 2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+ 1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rlineto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+ 0 | CFF_COUNT_CLEAR_STACK,
+
+ 13, /* flex */
+ 7,
+ 9,
+ 11,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* endchar */
+
+ 2 | CFF_COUNT_CHECK_WIDTH, /* hstem */
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+ 2 | CFF_COUNT_CHECK_WIDTH,
+
+ 0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */
+ 0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */
+ 0, /* dotsection */
+
+ 1, /* abs */
+ 2,
+ 2,
+ 2,
+ 1,
+ 0,
+ 2,
+ 1,
+
+ 1, /* blend */
+
+ 1, /* drop */
+ 2,
+ 1,
+ 2,
+ 1,
+
+ 2, /* put */
+ 1,
+ 4,
+ 3,
+
+ 2, /* and */
+ 2,
+ 1,
+ 2,
+ 4,
+
+ 1, /* callsubr */
+ 1,
+ 0
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** *********/
+ /********** GENERIC CHARSTRING PARSING *********/
+ /********** *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_builder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph builder. */
+ /* */
+ /* <InOut> */
+ /* builder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* glyph :: The current glyph object. */
+ /* */
+ static void
+ cff_builder_init( CFF_Builder* builder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot glyph,
+ FT_Bool hinting )
+ {
+ builder->path_begun = 0;
+ builder->load_points = 1;
+
+ builder->face = face;
+ builder->glyph = glyph;
+ builder->memory = face->root.memory;
+
+ if ( glyph )
+ {
+ FT_GlyphLoader loader = glyph->root.internal->loader;
+
+
+ builder->loader = loader;
+ builder->base = &loader->base.outline;
+ builder->current = &loader->current.outline;
+ FT_GlyphLoader_Rewind( loader );
+
+ builder->hints_globals = 0;
+ builder->hints_funcs = 0;
+
+ if ( hinting && size )
+ {
+ builder->hints_globals = size->root.internal;
+ builder->hints_funcs = glyph->root.internal->glyph_hints;
+ }
+ }
+
+ if ( size )
+ {
+ builder->scale_x = size->root.metrics.x_scale;
+ builder->scale_y = size->root.metrics.y_scale;
+ }
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+ builder->advance.x = 0;
+ builder->advance.y = 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_builder_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given glyph builder. Its contents can still be used */
+ /* after the call, but the function saves important information */
+ /* within the corresponding glyph slot. */
+ /* */
+ /* <Input> */
+ /* builder :: A pointer to the glyph builder to finalize. */
+ /* */
+ static void
+ cff_builder_done( CFF_Builder* builder )
+ {
+ CFF_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->root.outline = *builder->base;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_compute_bias */
+ /* */
+ /* <Description> */
+ /* Computes the bias value in dependence of the number of glyph */
+ /* subroutines. */
+ /* */
+ /* <Input> */
+ /* num_subrs :: The number of glyph subroutines. */
+ /* */
+ /* <Return> */
+ /* The bias value. */
+ static FT_Int
+ cff_compute_bias( FT_UInt num_subrs )
+ {
+ FT_Int result;
+
+
+ if ( num_subrs < 1240 )
+ result = 107;
+ else if ( num_subrs < 33900U )
+ result = 1131;
+ else
+ result = 32768U;
+
+ return result;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_decoder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph decoder. */
+ /* */
+ /* <InOut> */
+ /* decoder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* slot :: The current glyph object. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode )
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+
+
+ /* clear everything */
+ FT_MEM_ZERO( decoder, sizeof ( *decoder ) );
+
+ /* initialize builder */
+ cff_builder_init( &decoder->builder, face, size, slot, hinting );
+
+ /* initialize Type2 decoder */
+ decoder->num_globals = cff->num_global_subrs;
+ decoder->globals = cff->global_subrs;
+ decoder->globals_bias = cff_compute_bias( decoder->num_globals );
+
+ decoder->hint_mode = hint_mode;
+ }
+
+
+ /* this function is used to select the locals subrs array */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ FT_UInt glyph_index )
+ {
+ CFF_Font cff = (CFF_Font)decoder->builder.face->extra.data;
+ CFF_SubFont sub = &cff->top_font;
+ FT_Error error = CFF_Err_Ok;
+
+
+ /* manage CID fonts */
+ if ( cff->num_subfonts >= 1 )
+ {
+ FT_Byte fd_index = cff_fd_select_get( &cff->fd_select, glyph_index );
+
+
+ if ( fd_index >= cff->num_subfonts )
+ {
+ FT_TRACE4(( "cff_decoder_prepare: invalid CID subfont index\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ sub = cff->subfonts[fd_index];
+ }
+
+ decoder->num_locals = sub->num_local_subrs;
+ decoder->locals = sub->local_subrs;
+ decoder->locals_bias = cff_compute_bias( decoder->num_locals );
+
+ decoder->glyph_width = sub->private_dict.default_width;
+ decoder->nominal_width = sub->private_dict.nominal_width;
+
+ Exit:
+ return error;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ static FT_Error
+ check_points( CFF_Builder* builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ static void
+ cff_builder_add_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ point->x = x >> 16;
+ point->y = y >> 16;
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+
+ builder->last = *point;
+ }
+
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ static FT_Error
+ cff_builder_add_point1( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = check_points( builder, 1 );
+ if ( !error )
+ cff_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ static FT_Error
+ cff_builder_add_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return CFF_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ static FT_Error
+ cff_builder_start_point( CFF_Builder* builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = CFF_Err_Ok;
+
+
+ /* test whether we are building a new contour */
+ if ( !builder->path_begun )
+ {
+ builder->path_begun = 1;
+ error = cff_builder_add_contour( builder );
+ if ( !error )
+ error = cff_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ static void
+ cff_builder_close_contour( CFF_Builder* builder )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( !outline )
+ return;
+
+ /* XXXX: We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Int first = 0;
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ if ( outline->n_contours > 1 )
+ {
+ first = outline->contours[outline->n_contours - 2] + 1;
+ p1 = outline->points + first;
+ }
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and if it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+
+
+ static FT_Int
+ cff_lookup_glyph_by_stdcharcode( CFF_Font cff,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ FT_UShort glyph_sid;
+
+
+ /* CID-keyed fonts don't have glyph names */
+ if ( !cff->charset.sids )
+ return -1;
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+ /* Get code to SID mapping from `cff_standard_encoding'. */
+ glyph_sid = cff_get_standard_encoding( (FT_UInt)charcode );
+
+ for ( n = 0; n < cff->num_glyphs; n++ )
+ {
+ if ( cff->charset.sids[n] == glyph_sid )
+ return n;
+ }
+
+ return -1;
+ }
+
+
+ static FT_Error
+ cff_get_glyph_data( TT_Face face,
+ FT_UInt glyph_index,
+ FT_Byte** pointer,
+ FT_ULong* length )
+ {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* For incremental fonts get the character data using the */
+ /* callback function. */
+ if ( face->root.internal->incremental_interface )
+ {
+ FT_Data data;
+ FT_Error error =
+ face->root.internal->incremental_interface->funcs->get_glyph_data(
+ face->root.internal->incremental_interface->object,
+ glyph_index, &data );
+
+
+ *pointer = (FT_Byte*)data.pointer;
+ *length = data.length;
+
+ return error;
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ {
+ CFF_Font cff = (CFF_Font)(face->extra.data);
+
+
+ return cff_index_access_element( &cff->charstrings_index, glyph_index,
+ pointer, length );
+ }
+ }
+
+
+ static void
+ cff_free_glyph_data( TT_Face face,
+ FT_Byte** pointer,
+ FT_ULong length )
+ {
+#ifndef FT_CONFIG_OPTION_INCREMENTAL
+ FT_UNUSED( length );
+#endif
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* For incremental fonts get the character data using the */
+ /* callback function. */
+ if ( face->root.internal->incremental_interface )
+ {
+ FT_Data data;
+
+
+ data.pointer = *pointer;
+ data.length = length;
+
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,&data );
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ {
+ CFF_Font cff = (CFF_Font)(face->extra.data);
+
+
+ cff_index_forget_element( &cff->charstrings_index, pointer );
+ }
+ }
+
+
+ static FT_Error
+ cff_operator_seac( CFF_Decoder* decoder,
+ FT_Pos adx,
+ FT_Pos ady,
+ FT_Int bchar,
+ FT_Int achar )
+ {
+ FT_Error error;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Int bchar_index, achar_index;
+ TT_Face face = decoder->builder.face;
+ FT_Vector left_bearing, advance;
+ FT_Byte* charstring;
+ FT_ULong charstring_len;
+
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* Incremental fonts don't necessarily have valid charsets. */
+ /* They use the character code, not the glyph index, in this case. */
+ if ( face->root.internal->incremental_interface )
+ {
+ bchar_index = bchar;
+ achar_index = achar;
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+ {
+ CFF_Font cff = (CFF_Font)(face->extra.data);
+
+
+ bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar );
+ achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar );
+ }
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "cff_operator_seac:" ));
+ FT_ERROR(( " invalid seac character code arguments\n" ));
+ return CFF_Err_Syntax_Error;
+ }
+
+ /* If we are trying to load a composite glyph, do not load the */
+ /* accent character and return the array of subglyphs. */
+ if ( builder->no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)builder->glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error )
+ goto Exit;
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)( adx >> 16 );
+ subg->arg2 = (FT_Int)( ady >> 16 );
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+ }
+
+ FT_GlyphLoader_Prepare( builder->loader );
+
+ /* First load `bchar' in builder */
+ error = cff_get_glyph_data( face, bchar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len );
+
+ if ( error )
+ goto Exit;
+
+ cff_free_glyph_data( face, &charstring, charstring_len );
+ }
+
+ /* Save the left bearing and width of the base character */
+ /* as they will be erased by the next load. */
+
+ left_bearing = builder->left_bearing;
+ advance = builder->advance;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+
+ builder->pos_x = adx;
+ builder->pos_y = ady;
+
+ /* Now load `achar' on top of the base outline. */
+ error = cff_get_glyph_data( face, achar_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ error = cff_decoder_parse_charstrings( decoder, charstring,
+ charstring_len );
+
+ if ( error )
+ goto Exit;
+
+ cff_free_glyph_data( face, &charstring, charstring_len );
+ }
+
+ /* Restore the left side bearing and advance width */
+ /* of the base character. */
+ builder->left_bearing = left_bearing;
+ builder->advance = advance;
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cff_decoder_parse_charstrings */
+ /* */
+ /* <Description> */
+ /* Parses a given Type 2 charstrings program. */
+ /* */
+ /* <InOut> */
+ /* decoder :: The current Type 1 decoder. */
+ /* */
+ /* <Input> */
+ /* charstring_base :: The base of the charstring stream. */
+ /* */
+ /* charstring_len :: The length in bytes of the charstring stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len )
+ {
+ FT_Error error;
+ CFF_Decoder_Zone* zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ CFF_Builder* builder = &decoder->builder;
+ FT_Pos x, y;
+ FT_Fixed seed;
+ FT_Fixed* stack;
+
+ T2_Hints_Funcs hinter;
+
+
+ /* set default width */
+ decoder->num_hints = 0;
+ decoder->read_width = 1;
+
+ /* compute random seed from stack address of parameter */
+ seed = (FT_Fixed)(char*)&seed ^
+ (FT_Fixed)(char*)&decoder ^
+ (FT_Fixed)(char*)&charstring_base;
+ seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL;
+ if ( seed == 0 )
+ seed = 0x7384;
+
+ /* initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+ stack = decoder->top;
+
+ hinter = (T2_Hints_Funcs)builder->hints_funcs;
+
+ builder->path_begun = 0;
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ error = CFF_Err_Ok;
+
+ x = builder->pos_x;
+ y = builder->pos_y;
+
+ /* begin hints recording session, if any */
+ if ( hinter )
+ hinter->open( hinter->hints );
+
+ /* now execute loop */
+ while ( ip < limit )
+ {
+ CFF_Operator op;
+ FT_Byte v;
+
+
+ /********************************************************************/
+ /* */
+ /* Decode operator or operand */
+ /* */
+ v = *ip++;
+ if ( v >= 32 || v == 28 )
+ {
+ FT_Int shift = 16;
+ FT_Int32 val;
+
+
+ /* this is an operand, push it on the stack */
+ if ( v == 28 )
+ {
+ if ( ip + 1 >= limit )
+ goto Syntax_Error;
+ val = (FT_Short)( ( (FT_Short)ip[0] << 8 ) | ip[1] );
+ ip += 2;
+ }
+ else if ( v < 247 )
+ val = (FT_Long)v - 139;
+ else if ( v < 251 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = ( (FT_Long)v - 247 ) * 256 + *ip++ + 108;
+ }
+ else if ( v < 255 )
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ val = -( (FT_Long)v - 251 ) * 256 - *ip++ - 108;
+ }
+ else
+ {
+ if ( ip + 3 >= limit )
+ goto Syntax_Error;
+ val = ( (FT_Int32)ip[0] << 24 ) |
+ ( (FT_Int32)ip[1] << 16 ) |
+ ( (FT_Int32)ip[2] << 8 ) |
+ ip[3];
+ ip += 4;
+ shift = 0;
+ }
+ if ( decoder->top - stack >= CFF_MAX_OPERANDS )
+ goto Stack_Overflow;
+
+ val <<= shift;
+ *decoder->top++ = val;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ if ( !( val & 0xFFFFL ) )
+ FT_TRACE4(( " %ld", (FT_Int32)( val >> 16 ) ));
+ else
+ FT_TRACE4(( " %.2f", val / 65536.0 ));
+#endif
+
+ }
+ else
+ {
+ FT_Fixed* args = decoder->top;
+ FT_Int num_args = (FT_Int)( args - decoder->stack );
+ FT_Int req_args;
+
+
+ /* find operator */
+ op = cff_op_unknown;
+
+ switch ( v )
+ {
+ case 1:
+ op = cff_op_hstem;
+ break;
+ case 3:
+ op = cff_op_vstem;
+ break;
+ case 4:
+ op = cff_op_vmoveto;
+ break;
+ case 5:
+ op = cff_op_rlineto;
+ break;
+ case 6:
+ op = cff_op_hlineto;
+ break;
+ case 7:
+ op = cff_op_vlineto;
+ break;
+ case 8:
+ op = cff_op_rrcurveto;
+ break;
+ case 10:
+ op = cff_op_callsubr;
+ break;
+ case 11:
+ op = cff_op_return;
+ break;
+ case 12:
+ {
+ if ( ip >= limit )
+ goto Syntax_Error;
+ v = *ip++;
+
+ switch ( v )
+ {
+ case 0:
+ op = cff_op_dotsection;
+ break;
+ case 3:
+ op = cff_op_and;
+ break;
+ case 4:
+ op = cff_op_or;
+ break;
+ case 5:
+ op = cff_op_not;
+ break;
+ case 8:
+ op = cff_op_store;
+ break;
+ case 9:
+ op = cff_op_abs;
+ break;
+ case 10:
+ op = cff_op_add;
+ break;
+ case 11:
+ op = cff_op_sub;
+ break;
+ case 12:
+ op = cff_op_div;
+ break;
+ case 13:
+ op = cff_op_load;
+ break;
+ case 14:
+ op = cff_op_neg;
+ break;
+ case 15:
+ op = cff_op_eq;
+ break;
+ case 18:
+ op = cff_op_drop;
+ break;
+ case 20:
+ op = cff_op_put;
+ break;
+ case 21:
+ op = cff_op_get;
+ break;
+ case 22:
+ op = cff_op_ifelse;
+ break;
+ case 23:
+ op = cff_op_random;
+ break;
+ case 24:
+ op = cff_op_mul;
+ break;
+ case 26:
+ op = cff_op_sqrt;
+ break;
+ case 27:
+ op = cff_op_dup;
+ break;
+ case 28:
+ op = cff_op_exch;
+ break;
+ case 29:
+ op = cff_op_index;
+ break;
+ case 30:
+ op = cff_op_roll;
+ break;
+ case 34:
+ op = cff_op_hflex;
+ break;
+ case 35:
+ op = cff_op_flex;
+ break;
+ case 36:
+ op = cff_op_hflex1;
+ break;
+ case 37:
+ op = cff_op_flex1;
+ break;
+ default:
+ /* decrement ip for syntax error message */
+ ip--;
+ }
+ }
+ break;
+ case 14:
+ op = cff_op_endchar;
+ break;
+ case 16:
+ op = cff_op_blend;
+ break;
+ case 18:
+ op = cff_op_hstemhm;
+ break;
+ case 19:
+ op = cff_op_hintmask;
+ break;
+ case 20:
+ op = cff_op_cntrmask;
+ break;
+ case 21:
+ op = cff_op_rmoveto;
+ break;
+ case 22:
+ op = cff_op_hmoveto;
+ break;
+ case 23:
+ op = cff_op_vstemhm;
+ break;
+ case 24:
+ op = cff_op_rcurveline;
+ break;
+ case 25:
+ op = cff_op_rlinecurve;
+ break;
+ case 26:
+ op = cff_op_vvcurveto;
+ break;
+ case 27:
+ op = cff_op_hhcurveto;
+ break;
+ case 29:
+ op = cff_op_callgsubr;
+ break;
+ case 30:
+ op = cff_op_vhcurveto;
+ break;
+ case 31:
+ op = cff_op_hvcurveto;
+ break;
+ default:
+ ;
+ }
+ if ( op == cff_op_unknown )
+ goto Syntax_Error;
+
+ /* check arguments */
+ req_args = cff_argument_counts[op];
+ if ( req_args & CFF_COUNT_CHECK_WIDTH )
+ {
+ args = stack;
+
+ if ( num_args > 0 && decoder->read_width )
+ {
+ /* If `nominal_width' is non-zero, the number is really a */
+ /* difference against `nominal_width'. Else, the number here */
+ /* is truly a width, not a difference against `nominal_width'. */
+ /* If the font does not set `nominal_width', then */
+ /* `nominal_width' defaults to zero, and so we can set */
+ /* `glyph_width' to `nominal_width' plus number on the stack */
+ /* -- for either case. */
+
+ FT_Int set_width_ok;
+
+
+ switch ( op )
+ {
+ case cff_op_hmoveto:
+ case cff_op_vmoveto:
+ set_width_ok = num_args & 2;
+ break;
+
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ case cff_op_rmoveto:
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ set_width_ok = num_args & 1;
+ break;
+
+ case cff_op_endchar:
+ /* If there is a width specified for endchar, we either have */
+ /* 1 argument or 5 arguments. We like to argue. */
+ set_width_ok = ( ( num_args == 5 ) || ( num_args == 1 ) );
+ break;
+
+ default:
+ set_width_ok = 0;
+ break;
+ }
+
+ if ( set_width_ok )
+ {
+ decoder->glyph_width = decoder->nominal_width +
+ ( stack[0] >> 16 );
+
+ /* Consumed an argument. */
+ num_args--;
+ args++;
+ }
+ }
+
+ decoder->read_width = 0;
+ req_args = 0;
+ }
+
+ req_args &= 15;
+ if ( num_args < req_args )
+ goto Stack_Underflow;
+ args -= req_args;
+ num_args -= req_args;
+
+ switch ( op )
+ {
+ case cff_op_hstem:
+ case cff_op_vstem:
+ case cff_op_hstemhm:
+ case cff_op_vstemhm:
+ /* the number of arguments is always even here */
+ FT_TRACE4(( op == cff_op_hstem ? " hstem" :
+ ( op == cff_op_vstem ? " vstem" :
+ ( op == cff_op_hstemhm ? " hstemhm" : " vstemhm" ) ) ));
+
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ ( op == cff_op_hstem || op == cff_op_hstemhm ),
+ num_args / 2,
+ args );
+
+ decoder->num_hints += num_args / 2;
+ args = stack;
+ break;
+
+ case cff_op_hintmask:
+ case cff_op_cntrmask:
+ FT_TRACE4(( op == cff_op_hintmask ? " hintmask" : " cntrmask" ));
+
+ /* implement vstem when needed -- */
+ /* the specification doesn't say it, but this also works */
+ /* with the 'cntrmask' operator */
+ /* */
+ if ( num_args > 0 )
+ {
+ if ( hinter )
+ hinter->stems( hinter->hints,
+ 0,
+ num_args / 2,
+ args );
+
+ decoder->num_hints += num_args / 2;
+ }
+
+ if ( hinter )
+ {
+ if ( op == cff_op_hintmask )
+ hinter->hintmask( hinter->hints,
+ builder->current->n_points,
+ decoder->num_hints,
+ ip );
+ else
+ hinter->counter( hinter->hints,
+ decoder->num_hints,
+ ip );
+ }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ {
+ FT_UInt maskbyte;
+
+
+ FT_TRACE4(( " " ));
+
+ for ( maskbyte = 0;
+ maskbyte < (FT_UInt)(( decoder->num_hints + 7 ) >> 3);
+ maskbyte++, ip++ )
+ FT_TRACE4(( "0x%02X", *ip ));
+ }
+#else
+ ip += ( decoder->num_hints + 7 ) >> 3;
+#endif
+ if ( ip >= limit )
+ goto Syntax_Error;
+ args = stack;
+ break;
+
+ case cff_op_rmoveto:
+ FT_TRACE4(( " rmoveto" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x += args[0];
+ y += args[1];
+ args = stack;
+ break;
+
+ case cff_op_vmoveto:
+ FT_TRACE4(( " vmoveto" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ y += args[0];
+ args = stack;
+ break;
+
+ case cff_op_hmoveto:
+ FT_TRACE4(( " hmoveto" ));
+
+ cff_builder_close_contour( builder );
+ builder->path_begun = 0;
+ x += args[0];
+ args = stack;
+ break;
+
+ case cff_op_rlineto:
+ FT_TRACE4(( " rlineto" ));
+
+ if ( cff_builder_start_point ( builder, x, y ) ||
+ check_points( builder, num_args / 2 ) )
+ goto Fail;
+
+ if ( num_args < 2 || num_args & 1 )
+ goto Stack_Underflow;
+
+ args = stack;
+ while ( args < decoder->top )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 2;
+ }
+ args = stack;
+ break;
+
+ case cff_op_hlineto:
+ case cff_op_vlineto:
+ {
+ FT_Int phase = ( op == cff_op_hlineto );
+
+
+ FT_TRACE4(( op == cff_op_hlineto ? " hlineto"
+ : " vlineto" ));
+
+ if ( cff_builder_start_point ( builder, x, y ) ||
+ check_points( builder, num_args ) )
+ goto Fail;
+
+ args = stack;
+ while ( args < decoder->top )
+ {
+ if ( phase )
+ x += args[0];
+ else
+ y += args[0];
+
+ if ( cff_builder_add_point1( builder, x, y ) )
+ goto Fail;
+
+ args++;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rrcurveto:
+ FT_TRACE4(( " rrcurveto" ));
+
+ /* check number of arguments; must be a multiple of 6 */
+ if ( num_args % 6 != 0 )
+ goto Stack_Underflow;
+
+ if ( cff_builder_start_point ( builder, x, y ) ||
+ check_points( builder, num_args / 2 ) )
+ goto Fail;
+
+ args = stack;
+ while ( args < decoder->top )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[2];
+ y += args[3];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[4];
+ y += args[5];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 6;
+ }
+ args = stack;
+ break;
+
+ case cff_op_vvcurveto:
+ FT_TRACE4(( " vvcurveto" ));
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args = stack;
+ if ( num_args & 1 )
+ {
+ x += args[0];
+ args++;
+ num_args--;
+ }
+
+ if ( num_args % 4 != 0 )
+ goto Stack_Underflow;
+
+ if ( check_points( builder, 3 * ( num_args / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ y += args[0];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[1];
+ y += args[2];
+ cff_builder_add_point( builder, x, y, 0 );
+ y += args[3];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 4;
+ }
+ args = stack;
+ break;
+
+ case cff_op_hhcurveto:
+ FT_TRACE4(( " hhcurveto" ));
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args = stack;
+ if ( num_args & 1 )
+ {
+ y += args[0];
+ args++;
+ num_args--;
+ }
+
+ if ( num_args % 4 != 0 )
+ goto Stack_Underflow;
+
+ if ( check_points( builder, 3 * ( num_args / 4 ) ) )
+ goto Fail;
+
+ while ( args < decoder->top )
+ {
+ x += args[0];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[1];
+ y += args[2];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[3];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 4;
+ }
+ args = stack;
+ break;
+
+ case cff_op_vhcurveto:
+ case cff_op_hvcurveto:
+ {
+ FT_Int phase;
+
+
+ FT_TRACE4(( op == cff_op_vhcurveto ? " vhcurveto"
+ : " hvcurveto" ));
+
+ if ( cff_builder_start_point( builder, x, y ) )
+ goto Fail;
+
+ args = stack;
+ if ( num_args < 4 || ( num_args % 4 ) > 1 )
+ goto Stack_Underflow;
+
+ if ( check_points( builder, ( num_args / 4 ) * 3 ) )
+ goto Stack_Underflow;
+
+ phase = ( op == cff_op_hvcurveto );
+
+ while ( num_args >= 4 )
+ {
+ num_args -= 4;
+ if ( phase )
+ {
+ x += args[0];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[1];
+ y += args[2];
+ cff_builder_add_point( builder, x, y, 0 );
+ y += args[3];
+ if ( num_args == 1 )
+ x += args[4];
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ else
+ {
+ y += args[0];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[1];
+ y += args[2];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[3];
+ if ( num_args == 1 )
+ y += args[4];
+ cff_builder_add_point( builder, x, y, 1 );
+ }
+ args += 4;
+ phase ^= 1;
+ }
+ args = stack;
+ }
+ break;
+
+ case cff_op_rlinecurve:
+ {
+ FT_Int num_lines = ( num_args - 6 ) / 2;
+
+
+ FT_TRACE4(( " rlinecurve" ));
+
+ if ( num_args < 8 || ( num_args - 6 ) & 1 )
+ goto Stack_Underflow;
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ check_points( builder, num_lines + 3 ) )
+ goto Fail;
+
+ args = stack;
+
+ /* first, add the line segments */
+ while ( num_lines > 0 )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 2;
+ num_lines--;
+ }
+
+ /* then the curve */
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[2];
+ y += args[3];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[4];
+ y += args[5];
+ cff_builder_add_point( builder, x, y, 1 );
+ args = stack;
+ }
+ break;
+
+ case cff_op_rcurveline:
+ {
+ FT_Int num_curves = ( num_args - 2 ) / 6;
+
+
+ FT_TRACE4(( " rcurveline" ));
+
+ if ( num_args < 8 || ( num_args - 2 ) % 6 )
+ goto Stack_Underflow;
+
+ if ( cff_builder_start_point ( builder, x, y ) ||
+ check_points( builder, num_curves*3 + 2 ) )
+ goto Fail;
+
+ args = stack;
+
+ /* first, add the curves */
+ while ( num_curves > 0 )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[2];
+ y += args[3];
+ cff_builder_add_point( builder, x, y, 0 );
+ x += args[4];
+ y += args[5];
+ cff_builder_add_point( builder, x, y, 1 );
+ args += 6;
+ num_curves--;
+ }
+
+ /* then the final line */
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 1 );
+ args = stack;
+ }
+ break;
+
+ case cff_op_hflex1:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex1" ));
+
+ args = stack;
+
+ /* adding five more points; 4 control points, 1 on-curve point */
+ /* make sure we have enough space for the start point if it */
+ /* needs to be added */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ check_points( builder, 6 ) )
+ goto Fail;
+
+ /* Record the starting point's y position for later use */
+ start_y = y;
+
+ /* first control point */
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x += args[2];
+ y += args[3];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x += args[4];
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x += args[5];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x += args[6];
+ y += args[7];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start */
+ x += args[8];
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_hflex:
+ {
+ FT_Pos start_y;
+
+
+ FT_TRACE4(( " hflex" ));
+
+ args = stack;
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's y-position for later use */
+ start_y = y;
+
+ /* first control point */
+ x += args[0];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* second control point */
+ x += args[1];
+ y += args[2];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* join point; on curve, with y-value the same as the last */
+ /* control point's y-value */
+ x += args[3];
+ cff_builder_add_point( builder, x, y, 1 );
+
+ /* third control point, with y-value the same as the join */
+ /* point's y-value */
+ x += args[4];
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* fourth control point */
+ x += args[5];
+ y = start_y;
+ cff_builder_add_point( builder, x, y, 0 );
+
+ /* ending point, with y-value the same as the start point's */
+ /* y-value -- we don't add this point, though */
+ x += args[6];
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex1:
+ {
+ FT_Pos start_x, start_y; /* record start x, y values for */
+ /* alter use */
+ FT_Fixed dx = 0, dy = 0; /* used in horizontal/vertical */
+ /* algorithm below */
+ FT_Int horizontal, count;
+
+
+ FT_TRACE4(( " flex1" ));
+
+ /* adding six more points; 4 control points, 2 on-curve points */
+ if ( cff_builder_start_point( builder, x, y ) ||
+ check_points( builder, 6 ) )
+ goto Fail;
+
+ /* record the starting point's x, y position for later use */
+ start_x = x;
+ start_y = y;
+
+ /* XXX: figure out whether this is supposed to be a horizontal */
+ /* or vertical flex; the Type 2 specification is vague... */
+
+ args = stack;
+
+ /* grab up to the last argument */
+ for ( count = 5; count > 0; count-- )
+ {
+ dx += args[0];
+ dy += args[1];
+ args += 2;
+ }
+
+ /* rewind */
+ args = stack;
+
+ if ( dx < 0 ) dx = -dx;
+ if ( dy < 0 ) dy = -dy;
+
+ /* strange test, but here it is... */
+ horizontal = ( dx > dy );
+
+ for ( count = 5; count > 0; count-- )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y, (FT_Bool)( count == 3 ) );
+ args += 2;
+ }
+
+ /* is last operand an x- or y-delta? */
+ if ( horizontal )
+ {
+ x += args[0];
+ y = start_y;
+ }
+ else
+ {
+ x = start_x;
+ y += args[0];
+ }
+
+ cff_builder_add_point( builder, x, y, 1 );
+
+ args = stack;
+ break;
+ }
+
+ case cff_op_flex:
+ {
+ FT_UInt count;
+
+
+ FT_TRACE4(( " flex" ));
+
+ if ( cff_builder_start_point( builder, x, y ) ||
+ check_points( builder, 6 ) )
+ goto Fail;
+
+ args = stack;
+ for ( count = 6; count > 0; count-- )
+ {
+ x += args[0];
+ y += args[1];
+ cff_builder_add_point( builder, x, y,
+ (FT_Bool)( count == 4 || count == 1 ) );
+ args += 2;
+ }
+
+ args = stack;
+ }
+ break;
+
+ case cff_op_endchar:
+ FT_TRACE4(( " endchar" ));
+
+ /* We are going to emulate the seac operator. */
+ if ( num_args == 4 )
+ {
+ /* Save glyph width so that the subglyphs don't overwrite it. */
+ FT_Pos glyph_width = decoder->glyph_width;
+
+
+ error = cff_operator_seac( decoder,
+ args[0],
+ args[1],
+ (FT_Int)( args[2] >> 16 ),
+ (FT_Int)( args[3] >> 16 ) );
+ args += 4;
+
+ decoder->glyph_width = glyph_width;
+ }
+ else
+ {
+ if ( !error )
+ error = CFF_Err_Ok;
+
+ cff_builder_close_contour( builder );
+
+ /* close hints recording session */
+ if ( hinter )
+ {
+ if ( hinter->close( hinter->hints,
+ builder->current->n_points ) )
+ goto Syntax_Error;
+
+ /* apply hints to the loaded glyph outline now */
+ hinter->apply( hinter->hints,
+ builder->current,
+ (PSH_Globals)builder->hints_globals,
+ decoder->hint_mode );
+ }
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+ }
+
+ /* return now! */
+ FT_TRACE4(( "\n\n" ));
+ return error;
+
+ case cff_op_abs:
+ FT_TRACE4(( " abs" ));
+
+ if ( args[0] < 0 )
+ args[0] = -args[0];
+ args++;
+ break;
+
+ case cff_op_add:
+ FT_TRACE4(( " add" ));
+
+ args[0] += args[1];
+ args++;
+ break;
+
+ case cff_op_sub:
+ FT_TRACE4(( " sub" ));
+
+ args[0] -= args[1];
+ args++;
+ break;
+
+ case cff_op_div:
+ FT_TRACE4(( " div" ));
+
+ args[0] = FT_DivFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_neg:
+ FT_TRACE4(( " neg" ));
+
+ args[0] = -args[0];
+ args++;
+ break;
+
+ case cff_op_random:
+ {
+ FT_Fixed Rand;
+
+
+ FT_TRACE4(( " rand" ));
+
+ Rand = seed;
+ if ( Rand >= 0x8000L )
+ Rand++;
+
+ args[0] = Rand;
+ seed = FT_MulFix( seed, 0x10000L - seed );
+ if ( seed == 0 )
+ seed += 0x2873;
+ args++;
+ }
+ break;
+
+ case cff_op_mul:
+ FT_TRACE4(( " mul" ));
+
+ args[0] = FT_MulFix( args[0], args[1] );
+ args++;
+ break;
+
+ case cff_op_sqrt:
+ FT_TRACE4(( " sqrt" ));
+
+ if ( args[0] > 0 )
+ {
+ FT_Int count = 9;
+ FT_Fixed root = args[0];
+ FT_Fixed new_root;
+
+
+ for (;;)
+ {
+ new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
+ if ( new_root == root || count <= 0 )
+ break;
+ root = new_root;
+ }
+ args[0] = new_root;
+ }
+ else
+ args[0] = 0;
+ args++;
+ break;
+
+ case cff_op_drop:
+ /* nothing */
+ FT_TRACE4(( " drop" ));
+
+ break;
+
+ case cff_op_exch:
+ {
+ FT_Fixed tmp;
+
+
+ FT_TRACE4(( " exch" ));
+
+ tmp = args[0];
+ args[0] = args[1];
+ args[1] = tmp;
+ args += 2;
+ }
+ break;
+
+ case cff_op_index:
+ {
+ FT_Int idx = (FT_Int)( args[0] >> 16 );
+
+
+ FT_TRACE4(( " index" ));
+
+ if ( idx < 0 )
+ idx = 0;
+ else if ( idx > num_args - 2 )
+ idx = num_args - 2;
+ args[0] = args[-( idx + 1 )];
+ args++;
+ }
+ break;
+
+ case cff_op_roll:
+ {
+ FT_Int count = (FT_Int)( args[0] >> 16 );
+ FT_Int idx = (FT_Int)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " roll" ));
+
+ if ( count <= 0 )
+ count = 1;
+
+ args -= count;
+ if ( args < stack )
+ goto Stack_Underflow;
+
+ if ( idx >= 0 )
+ {
+ while ( idx > 0 )
+ {
+ FT_Fixed tmp = args[count - 1];
+ FT_Int i;
+
+
+ for ( i = count - 2; i >= 0; i-- )
+ args[i + 1] = args[i];
+ args[0] = tmp;
+ idx--;
+ }
+ }
+ else
+ {
+ while ( idx < 0 )
+ {
+ FT_Fixed tmp = args[0];
+ FT_Int i;
+
+
+ for ( i = 0; i < count - 1; i++ )
+ args[i] = args[i + 1];
+ args[count - 1] = tmp;
+ idx++;
+ }
+ }
+ args += count;
+ }
+ break;
+
+ case cff_op_dup:
+ FT_TRACE4(( " dup" ));
+
+ args[1] = args[0];
+ args++;
+ break;
+
+ case cff_op_put:
+ {
+ FT_Fixed val = args[0];
+ FT_Int idx = (FT_Int)( args[1] >> 16 );
+
+
+ FT_TRACE4(( " put" ));
+
+ if ( idx >= 0 && idx < decoder->len_buildchar )
+ decoder->buildchar[idx] = val;
+ }
+ break;
+
+ case cff_op_get:
+ {
+ FT_Int idx = (FT_Int)( args[0] >> 16 );
+ FT_Fixed val = 0;
+
+
+ FT_TRACE4(( " get" ));
+
+ if ( idx >= 0 && idx < decoder->len_buildchar )
+ val = decoder->buildchar[idx];
+
+ args[0] = val;
+ args++;
+ }
+ break;
+
+ case cff_op_store:
+ FT_TRACE4(( " store "));
+
+ goto Unimplemented;
+
+ case cff_op_load:
+ FT_TRACE4(( " load" ));
+
+ goto Unimplemented;
+
+ case cff_op_dotsection:
+ /* this operator is deprecated and ignored by the parser */
+ FT_TRACE4(( " dotsection" ));
+ break;
+
+ case cff_op_and:
+ {
+ FT_Fixed cond = args[0] && args[1];
+
+
+ FT_TRACE4(( " and" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_or:
+ {
+ FT_Fixed cond = args[0] || args[1];
+
+
+ FT_TRACE4(( " or" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_eq:
+ {
+ FT_Fixed cond = !args[0];
+
+
+ FT_TRACE4(( " eq" ));
+
+ args[0] = cond ? 0x10000L : 0;
+ args++;
+ }
+ break;
+
+ case cff_op_ifelse:
+ {
+ FT_Fixed cond = ( args[2] <= args[3] );
+
+
+ FT_TRACE4(( " ifelse" ));
+
+ if ( !cond )
+ args[0] = args[1];
+ args++;
+ }
+ break;
+
+ case cff_op_callsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->locals_bias );
+
+
+ FT_TRACE4(( " callsubr(%d)", idx ));
+
+ if ( idx >= decoder->num_locals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:" ));
+ FT_ERROR(( " invalid local subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->locals[idx];
+ zone->limit = decoder->locals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs!\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_callgsubr:
+ {
+ FT_UInt idx = (FT_UInt)( ( args[0] >> 16 ) +
+ decoder->globals_bias );
+
+
+ FT_TRACE4(( " callgsubr(%d)", idx ));
+
+ if ( idx >= decoder->num_globals )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:" ));
+ FT_ERROR(( " invalid global subr index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+ zone->base = decoder->globals[idx];
+ zone->limit = decoder->globals[idx + 1];
+ zone->cursor = zone->base;
+
+ if ( !zone->base || zone->limit == zone->base )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " invoking empty subrs!\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ }
+ break;
+
+ case cff_op_return:
+ FT_TRACE4(( " return" ));
+
+ if ( decoder->zone <= decoder->zones )
+ {
+ FT_ERROR(( "cff_decoder_parse_charstrings:"
+ " unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone--;
+ zone = decoder->zone;
+ ip = zone->cursor;
+ limit = zone->limit;
+ break;
+
+ default:
+ Unimplemented:
+ FT_ERROR(( "Unimplemented opcode: %d", ip[-1] ));
+
+ if ( ip[-1] == 12 )
+ FT_ERROR(( " %d", ip[0] ));
+ FT_ERROR(( "\n" ));
+
+ return CFF_Err_Unimplemented_Feature;
+ }
+
+ decoder->top = args;
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n\n" ));
+
+ Fail:
+ return error;
+
+ Syntax_Error:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error!" ));
+ return CFF_Err_Invalid_File_Format;
+
+ Stack_Underflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow!" ));
+ return CFF_Err_Too_Few_Arguments;
+
+ Stack_Overflow:
+ FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow!" ));
+ return CFF_Err_Stack_Overflow;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** *********/
+ /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/
+ /********** *********/
+ /********** The following code is in charge of computing *********/
+ /********** the maximum advance width of the font. It *********/
+ /********** quickly processes each glyph charstring to *********/
+ /********** extract the value from either a `sbw' or `seac' *********/
+ /********** operator. *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#if 0 /* unused until we support pure CFF fonts */
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_compute_max_advance( TT_Face face,
+ FT_Int* max_advance )
+ {
+ FT_Error error = CFF_Err_Ok;
+ CFF_Decoder decoder;
+ FT_Int glyph_index;
+ CFF_Font cff = (CFF_Font)face->other;
+
+
+ *max_advance = 0;
+
+ /* Initialize load decoder */
+ cff_decoder_init( &decoder, face, 0, 0, 0, 0 );
+
+ decoder.builder.metrics_only = 1;
+ decoder.builder.load_points = 0;
+
+ /* For each glyph, parse the glyph charstring and extract */
+ /* the advance width. */
+ for ( glyph_index = 0; glyph_index < face->root.num_glyphs;
+ glyph_index++ )
+ {
+ FT_Byte* charstring;
+ FT_ULong charstring_len;
+
+
+ /* now get load the unscaled outline */
+ error = cff_get_glyph_data( face, glyph_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ error = cff_decoder_prepare( &decoder, glyph_index );
+ if ( !error )
+ error = cff_decoder_parse_charstrings( &decoder,
+ charstring,
+ charstring_len );
+
+ cff_free_glyph_data( face, &charstring, &charstring_len );
+ }
+
+ /* ignore the error if one has occurred -- skip to next glyph */
+ error = CFF_Err_Ok;
+ }
+
+ *max_advance = decoder.builder.advance.x;
+
+ return CFF_Err_Ok;
+ }
+
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_slot_load( CFF_GlyphSlot glyph,
+ CFF_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ CFF_Decoder decoder;
+ TT_Face face = (TT_Face)glyph->root.face;
+ FT_Bool hinting;
+ CFF_Font cff = (CFF_Font)face->extra.data;
+
+ FT_Matrix font_matrix;
+ FT_Vector font_offset;
+
+
+ /* in a CID-keyed font, consider `glyph_index' as a CID and map */
+ /* it immediately to the real glyph_index -- if it isn't a */
+ /* subsetted font, glyph_indices and CIDs are identical, though */
+ if ( cff->top_font.font_dict.cid_registry != 0xFFFFU &&
+ cff->charset.cids )
+ {
+ glyph_index = cff_charset_cid_to_gindex( &cff->charset, glyph_index );
+ if ( glyph_index == 0 )
+ return CFF_Err_Invalid_Argument;
+ }
+ else if ( glyph_index >= cff->num_glyphs )
+ return CFF_Err_Invalid_Argument;
+
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
+
+ glyph->x_scale = 0x10000L;
+ glyph->y_scale = 0x10000L;
+ if ( size )
+ {
+ glyph->x_scale = size->root.metrics.x_scale;
+ glyph->y_scale = size->root.metrics.y_scale;
+ }
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ /* try to load embedded bitmap if any */
+ /* */
+ /* XXX: The convention should be emphasized in */
+ /* the documents because it can be confusing. */
+ if ( size )
+ {
+ CFF_Face cff_face = (CFF_Face)size->root.face;
+ SFNT_Service sfnt = (SFNT_Service)cff_face->sfnt;
+ FT_Stream stream = cff_face->root.stream;
+
+
+ if ( size->strike_index != 0xFFFFFFFFUL &&
+ sfnt->load_eblc &&
+ ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
+ {
+ TT_SBit_MetricsRec metrics;
+
+
+ error = sfnt->load_sbit_image( face,
+ size->strike_index,
+ glyph_index,
+ (FT_Int)load_flags,
+ stream,
+ &glyph->root.bitmap,
+ &metrics );
+
+ if ( !error )
+ {
+ glyph->root.outline.n_points = 0;
+ glyph->root.outline.n_contours = 0;
+
+ glyph->root.metrics.width = (FT_Pos)metrics.width << 6;
+ glyph->root.metrics.height = (FT_Pos)metrics.height << 6;
+
+ glyph->root.metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6;
+ glyph->root.metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6;
+ glyph->root.metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6;
+
+ glyph->root.metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6;
+ glyph->root.metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6;
+ glyph->root.metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6;
+
+ glyph->root.format = FT_GLYPH_FORMAT_BITMAP;
+
+ if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+ {
+ glyph->root.bitmap_left = metrics.vertBearingX;
+ glyph->root.bitmap_top = metrics.vertBearingY;
+ }
+ else
+ {
+ glyph->root.bitmap_left = metrics.horiBearingX;
+ glyph->root.bitmap_top = metrics.horiBearingY;
+ }
+ return error;
+ }
+ }
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ /* return immediately if we only want the embedded bitmaps */
+ if ( load_flags & FT_LOAD_SBITS_ONLY )
+ return CFF_Err_Invalid_Argument;
+
+ glyph->root.outline.n_points = 0;
+ glyph->root.outline.n_contours = 0;
+
+ hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
+ ( load_flags & FT_LOAD_NO_HINTING ) == 0 );
+
+ glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; /* by default */
+
+ {
+ FT_Byte* charstring;
+ FT_ULong charstring_len;
+
+
+ cff_decoder_init( &decoder, face, size, glyph, hinting,
+ FT_LOAD_TARGET_MODE( load_flags ) );
+
+ decoder.builder.no_recurse =
+ (FT_Bool)( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 );
+
+ /* now load the unscaled outline */
+ error = cff_get_glyph_data( face, glyph_index,
+ &charstring, &charstring_len );
+ if ( !error )
+ {
+ error = cff_decoder_prepare( &decoder, glyph_index );
+ if ( !error )
+ {
+ error = cff_decoder_parse_charstrings( &decoder,
+ charstring,
+ charstring_len );
+
+ cff_free_glyph_data( face, &charstring, charstring_len );
+
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* Control data and length may not be available for incremental */
+ /* fonts. */
+ if ( face->root.internal->incremental_interface )
+ {
+ glyph->root.control_data = 0;
+ glyph->root.control_len = 0;
+ }
+ else
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ /* We set control_data and control_len if charstrings is loaded. */
+ /* See how charstring loads at cff_index_access_element() in */
+ /* cffload.c. */
+ {
+ CFF_Index csindex = &cff->charstrings_index;
+
+
+ if ( csindex->offsets )
+ {
+ glyph->root.control_data = csindex->bytes +
+ csindex->offsets[glyph_index] - 1;
+ glyph->root.control_len = charstring_len;
+ }
+ }
+ }
+ }
+
+ /* save new glyph tables */
+ cff_builder_done( &decoder.builder );
+ }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* Incremental fonts can optionally override the metrics. */
+ if ( !error &&
+ face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->get_glyph_metrics )
+ {
+ FT_Incremental_MetricsRec metrics;
+
+
+ metrics.bearing_x = decoder.builder.left_bearing.x;
+ metrics.bearing_y = decoder.builder.left_bearing.y;
+ metrics.advance = decoder.builder.advance.x;
+ error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+ face->root.internal->incremental_interface->object,
+ glyph_index, FALSE, &metrics );
+ decoder.builder.left_bearing.x = metrics.bearing_x;
+ decoder.builder.left_bearing.y = metrics.bearing_y;
+ decoder.builder.advance.x = metrics.advance;
+ decoder.builder.advance.y = 0;
+ }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ if ( !error )
+ {
+ if ( cff->num_subfonts >= 1 )
+ {
+ FT_Byte fd_index = cff_fd_select_get( &cff->fd_select,
+ glyph_index );
+
+
+ font_matrix = cff->subfonts[fd_index]->font_dict.font_matrix;
+ font_offset = cff->subfonts[fd_index]->font_dict.font_offset;
+ }
+ else
+ {
+ font_matrix = cff->top_font.font_dict.font_matrix;
+ font_offset = cff->top_font.font_dict.font_offset;
+ }
+
+ /* Now, set the metrics -- this is rather simple, as */
+ /* the left side bearing is the xMin, and the top side */
+ /* bearing the yMax. */
+
+ /* For composite glyphs, return only left side bearing and */
+ /* advance width. */
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ {
+ FT_Slot_Internal internal = glyph->root.internal;
+
+
+ glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x;
+ glyph->root.metrics.horiAdvance = decoder.glyph_width;
+ internal->glyph_matrix = font_matrix;
+ internal->glyph_delta = font_offset;
+ internal->glyph_transformed = 1;
+ }
+ else
+ {
+ FT_BBox cbox;
+ FT_Glyph_Metrics* metrics = &glyph->root.metrics;
+ FT_Vector advance;
+ FT_Bool has_vertical_info;
+
+
+ /* copy the _unscaled_ advance width */
+ metrics->horiAdvance = decoder.glyph_width;
+ glyph->root.linearHoriAdvance = decoder.glyph_width;
+ glyph->root.internal->glyph_transformed = 0;
+
+ has_vertical_info = FT_BOOL( face->vertical_info &&
+ face->vertical.number_Of_VMetrics > 0 &&
+ face->vertical.long_metrics != 0 );
+
+ /* get the vertical metrics from the vtmx table if we have one */
+ if ( has_vertical_info )
+ {
+ FT_Short vertBearingY = 0;
+ FT_UShort vertAdvance = 0;
+
+
+ ( (SFNT_Service)face->sfnt )->get_metrics( face, 1,
+ glyph_index,
+ &vertBearingY,
+ &vertAdvance );
+ metrics->vertBearingY = vertBearingY;
+ metrics->vertAdvance = vertAdvance;
+ }
+ else
+ {
+ /* make up vertical ones */
+ if ( face->os2.version != 0xFFFFU )
+ metrics->vertAdvance = (FT_Pos)( face->os2.sTypoAscender -
+ face->os2.sTypoDescender );
+ else
+ metrics->vertAdvance = (FT_Pos)( face->horizontal.Ascender -
+ face->horizontal.Descender );
+ }
+
+ glyph->root.linearVertAdvance = metrics->vertAdvance;
+
+ glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;
+
+ glyph->root.outline.flags = 0;
+ if ( size && size->root.metrics.y_ppem < 24 )
+ glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL;
+
+ /* apply the font matrix */
+ if ( !( font_matrix.xx == 0x10000L &&
+ font_matrix.yy == 0x10000L &&
+ font_matrix.xy == 0 &&
+ font_matrix.yx == 0 ) )
+ FT_Outline_Transform( &glyph->root.outline, &font_matrix );
+
+ if ( !( font_offset.x == 0 &&
+ font_offset.y == 0 ) )
+ FT_Outline_Translate( &glyph->root.outline,
+ font_offset.x, font_offset.y );
+
+ advance.x = metrics->horiAdvance;
+ advance.y = 0;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->horiAdvance = advance.x + font_offset.x;
+
+ advance.x = 0;
+ advance.y = metrics->vertAdvance;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->vertAdvance = advance.y + font_offset.y;
+
+ if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ /* scale the outline and the metrics */
+ FT_Int n;
+ FT_Outline* cur = &glyph->root.outline;
+ FT_Vector* vec = cur->points;
+ FT_Fixed x_scale = glyph->x_scale;
+ FT_Fixed y_scale = glyph->y_scale;
+
+
+ /* First of all, scale the points */
+ if ( !hinting || !decoder.builder.hints_funcs )
+ for ( n = cur->n_points; n > 0; n--, vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, x_scale );
+ vec->y = FT_MulFix( vec->y, y_scale );
+ }
+
+ /* Then scale the metrics */
+ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
+ metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
+ }
+
+ /* compute the other metrics */
+ FT_Outline_Get_CBox( &glyph->root.outline, &cbox );
+
+ metrics->width = cbox.xMax - cbox.xMin;
+ metrics->height = cbox.yMax - cbox.yMin;
+
+ metrics->horiBearingX = cbox.xMin;
+ metrics->horiBearingY = cbox.yMax;
+
+ if ( has_vertical_info )
+ metrics->vertBearingX = -metrics->width / 2;
+ else
+ ft_synthesize_vertical_metrics( metrics,
+ metrics->vertAdvance );
+ }
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/cff/cffgload.h b/src/cff/cffgload.h
new file mode 100644
index 0000000..f67864a
--- /dev/null
+++ b/src/cff/cffgload.h
@@ -0,0 +1,208 @@
+/***************************************************************************/
+/* */
+/* cffgload.h */
+/* */
+/* OpenType Glyph Loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFGLOAD_H__
+#define __CFFGLOAD_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "cffobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+#define CFF_MAX_OPERANDS 48
+#define CFF_MAX_SUBRS_CALLS 32
+
+
+ /*************************************************************************/
+ /* */
+ /* <Structure> */
+ /* CFF_Builder */
+ /* */
+ /* <Description> */
+ /* A structure used during glyph loading to store its outline. */
+ /* */
+ /* <Fields> */
+ /* memory :: The current memory object. */
+ /* */
+ /* face :: The current face object. */
+ /* */
+ /* glyph :: The current glyph slot. */
+ /* */
+ /* loader :: The current glyph loader. */
+ /* */
+ /* base :: The base glyph outline. */
+ /* */
+ /* current :: The current glyph outline. */
+ /* */
+ /* last :: The last point position. */
+ /* */
+ /* scale_x :: The horizontal scale (FUnits to sub-pixels). */
+ /* */
+ /* scale_y :: The vertical scale (FUnits to sub-pixels). */
+ /* */
+ /* pos_x :: The horizontal translation (if composite glyph). */
+ /* */
+ /* pos_y :: The vertical translation (if composite glyph). */
+ /* */
+ /* left_bearing :: The left side bearing point. */
+ /* */
+ /* advance :: The horizontal advance vector. */
+ /* */
+ /* bbox :: Unused. */
+ /* */
+ /* path_begun :: A flag which indicates that a new path has begun. */
+ /* */
+ /* load_points :: If this flag is not set, no points are loaded. */
+ /* */
+ /* no_recurse :: Set but not used. */
+ /* */
+ /* metrics_only :: A boolean indicating that we only want to compute */
+ /* the metrics of a given glyph, not load all of its */
+ /* points. */
+ /* */
+ /* hints_funcs :: Auxiliary pointer for hinting. */
+ /* */
+ /* hints_globals :: Auxiliary pointer for hinting. */
+ /* */
+ typedef struct CFF_Builder_
+ {
+ FT_Memory memory;
+ TT_Face face;
+ CFF_GlyphSlot glyph;
+ FT_GlyphLoader loader;
+ FT_Outline* base;
+ FT_Outline* current;
+
+ FT_Vector last;
+
+ FT_Fixed scale_x;
+ FT_Fixed scale_y;
+
+ FT_Pos pos_x;
+ FT_Pos pos_y;
+
+ FT_Vector left_bearing;
+ FT_Vector advance;
+
+ FT_BBox bbox; /* bounding box */
+ FT_Bool path_begun;
+ FT_Bool load_points;
+ FT_Bool no_recurse;
+
+ FT_Bool metrics_only;
+
+ void* hints_funcs; /* hinter-specific */
+ void* hints_globals; /* hinter-specific */
+
+ } CFF_Builder;
+
+
+ /* execution context charstring zone */
+
+ typedef struct CFF_Decoder_Zone_
+ {
+ FT_Byte* base;
+ FT_Byte* limit;
+ FT_Byte* cursor;
+
+ } CFF_Decoder_Zone;
+
+
+ typedef struct CFF_Decoder_
+ {
+ CFF_Builder builder;
+ CFF_Font cff;
+
+ FT_Fixed stack[CFF_MAX_OPERANDS + 1];
+ FT_Fixed* top;
+
+ CFF_Decoder_Zone zones[CFF_MAX_SUBRS_CALLS + 1];
+ CFF_Decoder_Zone* zone;
+
+ FT_Int flex_state;
+ FT_Int num_flex_vectors;
+ FT_Vector flex_vectors[7];
+
+ FT_Pos glyph_width;
+ FT_Pos nominal_width;
+
+ FT_Bool read_width;
+ FT_Int num_hints;
+ FT_Fixed* buildchar;
+ FT_Int len_buildchar;
+
+ FT_UInt num_locals;
+ FT_UInt num_globals;
+
+ FT_Int locals_bias;
+ FT_Int globals_bias;
+
+ FT_Byte** locals;
+ FT_Byte** globals;
+
+ FT_Byte** glyph_names; /* for pure CFF fonts only */
+ FT_UInt num_glyphs; /* number of glyphs in font */
+
+ FT_Render_Mode hint_mode;
+
+ } CFF_Decoder;
+
+
+ FT_LOCAL( void )
+ cff_decoder_init( CFF_Decoder* decoder,
+ TT_Face face,
+ CFF_Size size,
+ CFF_GlyphSlot slot,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode );
+
+ FT_LOCAL( FT_Error )
+ cff_decoder_prepare( CFF_Decoder* decoder,
+ FT_UInt glyph_index );
+
+#if 0 /* unused until we support pure CFF fonts */
+
+ /* Compute the maximum advance width of a font through quick parsing */
+ FT_LOCAL( FT_Error )
+ cff_compute_max_advance( TT_Face face,
+ FT_Int* max_advance );
+
+#endif /* 0 */
+
+ FT_LOCAL( FT_Error )
+ cff_decoder_parse_charstrings( CFF_Decoder* decoder,
+ FT_Byte* charstring_base,
+ FT_ULong charstring_len );
+
+ FT_LOCAL( FT_Error )
+ cff_slot_load( CFF_GlyphSlot glyph,
+ CFF_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+
+FT_END_HEADER
+
+#endif /* __CFFGLOAD_H__ */
+
+
+/* END */
diff --git a/src/cff/cffload.c b/src/cff/cffload.c
new file mode 100644
index 0000000..dd2f113
--- /dev/null
+++ b/src/cff/cffload.c
@@ -0,0 +1,1598 @@
+/***************************************************************************/
+/* */
+/* cffload.c */
+/* */
+/* OpenType and CFF data/program tables loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_STREAM_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_TYPE1_TABLES_H
+
+#include "cffload.h"
+#include "cffparse.h"
+
+#include "cfferrs.h"
+
+
+#if 1
+ static const FT_UShort cff_isoadobe_charset[229] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167,
+ 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183,
+ 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228
+ };
+
+ static const FT_UShort cff_expert_charset[166] =
+ {
+ 0, 1, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 13, 14, 15, 99,
+ 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 27, 28, 249, 250, 251, 252,
+ 253, 254, 255, 256, 257, 258, 259, 260,
+ 261, 262, 263, 264, 265, 266, 109, 110,
+ 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282,
+ 283, 284, 285, 286, 287, 288, 289, 290,
+ 291, 292, 293, 294, 295, 296, 297, 298,
+ 299, 300, 301, 302, 303, 304, 305, 306,
+ 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 158, 155, 163, 319,
+ 320, 321, 322, 323, 324, 325, 326, 150,
+ 164, 169, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340,
+ 341, 342, 343, 344, 345, 346, 347, 348,
+ 349, 350, 351, 352, 353, 354, 355, 356,
+ 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372,
+ 373, 374, 375, 376, 377, 378
+ };
+
+ static const FT_UShort cff_expertsubset_charset[87] =
+ {
+ 0, 1, 231, 232, 235, 236, 237, 238,
+ 13, 14, 15, 99, 239, 240, 241, 242,
+ 243, 244, 245, 246, 247, 248, 27, 28,
+ 249, 250, 251, 253, 254, 255, 256, 257,
+ 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 109, 110, 267, 268, 269, 270, 272,
+ 300, 301, 302, 305, 314, 315, 158, 155,
+ 163, 320, 321, 322, 323, 324, 325, 326,
+ 150, 164, 169, 327, 328, 329, 330, 331,
+ 332, 333, 334, 335, 336, 337, 338, 339,
+ 340, 341, 342, 343, 344, 345, 346
+ };
+
+ static const FT_UShort cff_standard_encoding[256] =
+ {
+ 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,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 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, 0,
+ 0, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 109, 110,
+ 0, 111, 112, 113, 114, 0, 115, 116,
+ 117, 118, 119, 120, 121, 122, 0, 123,
+ 0, 124, 125, 126, 127, 128, 129, 130,
+ 131, 0, 132, 133, 0, 134, 135, 136,
+ 137, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 138, 0, 139, 0, 0, 0, 0,
+ 140, 141, 142, 143, 0, 0, 0, 0,
+ 0, 144, 0, 0, 0, 145, 0, 0,
+ 146, 147, 148, 149, 0, 0, 0, 0
+ };
+
+ static const FT_UShort cff_expert_encoding[256] =
+ {
+ 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,
+ 1, 229, 230, 0, 231, 232, 233, 234,
+ 235, 236, 237, 238, 13, 14, 15, 99,
+ 239, 240, 241, 242, 243, 244, 245, 246,
+ 247, 248, 27, 28, 249, 250, 251, 252,
+ 0, 253, 254, 255, 256, 257, 0, 0,
+ 0, 258, 0, 0, 259, 260, 261, 262,
+ 0, 0, 263, 264, 265, 0, 266, 109,
+ 110, 267, 268, 269, 0, 270, 271, 272,
+ 273, 274, 275, 276, 277, 278, 279, 280,
+ 281, 282, 283, 284, 285, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 302, 303, 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, 0,
+ 0, 304, 305, 306, 0, 0, 307, 308,
+ 309, 310, 311, 0, 312, 0, 0, 312,
+ 0, 0, 314, 315, 0, 0, 316, 317,
+ 318, 0, 0, 0, 158, 155, 163, 319,
+ 320, 321, 322, 323, 324, 325, 0, 0,
+ 326, 150, 164, 169, 327, 328, 329, 330,
+ 331, 332, 333, 334, 335, 336, 337, 338,
+ 339, 340, 341, 342, 343, 344, 345, 346,
+ 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362,
+ 363, 364, 365, 366, 367, 368, 369, 370,
+ 371, 372, 373, 374, 375, 376, 377, 378
+ };
+#endif
+
+
+ FT_LOCAL_DEF( FT_UShort )
+ cff_get_standard_encoding( FT_UInt charcode )
+ {
+ return (FT_UShort)(charcode < 256 ? cff_standard_encoding[charcode] : 0);
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffload
+
+
+ /* read an offset from the index's stream current position */
+ static FT_ULong
+ cff_index_read_offset( CFF_Index idx,
+ FT_Error *errorp )
+ {
+ FT_Error error;
+ FT_Stream stream = idx->stream;
+ FT_Byte tmp[4];
+ FT_ULong result = 0;
+
+
+ if ( !FT_STREAM_READ( tmp, idx->off_size ) )
+ {
+ FT_Int nn;
+
+
+ for ( nn = 0; nn < idx->off_size; nn++ )
+ result = ( result << 8 ) | tmp[nn];
+ }
+
+ *errorp = error;
+ return result;
+ }
+
+
+ static FT_Error
+ cff_index_init( CFF_Index idx,
+ FT_Stream stream,
+ FT_Bool load )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UShort count;
+
+
+ FT_MEM_ZERO( idx, sizeof ( *idx ) );
+
+ idx->stream = stream;
+ idx->start = FT_STREAM_POS();
+ if ( !FT_READ_USHORT( count ) &&
+ count > 0 )
+ {
+ FT_Byte offsize;
+ FT_ULong size;
+
+
+ /* there is at least one element; read the offset size, */
+ /* then access the offset table to compute the index's total size */
+ if ( FT_READ_BYTE( offsize ) )
+ goto Exit;
+
+ if ( offsize < 1 || offsize > 4 )
+ {
+ error = FT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ idx->count = count;
+ idx->off_size = offsize;
+ size = (FT_ULong)( count + 1 ) * offsize;
+
+ idx->data_offset = idx->start + 3 + size;
+
+ if ( FT_STREAM_SKIP( size - offsize ) )
+ goto Exit;
+
+ size = cff_index_read_offset( idx, &error );
+ if ( error )
+ goto Exit;
+
+ if ( size == 0 )
+ {
+ error = CFF_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ idx->data_size = --size;
+
+ if ( load )
+ {
+ /* load the data */
+ if ( FT_FRAME_EXTRACT( size, idx->bytes ) )
+ goto Exit;
+ }
+ else
+ {
+ /* skip the data */
+ if ( FT_STREAM_SKIP( size ) )
+ goto Exit;
+ }
+ }
+
+ Exit:
+ if ( error )
+ FT_FREE( idx->offsets );
+
+ return error;
+ }
+
+
+ static void
+ cff_index_done( CFF_Index idx )
+ {
+ if ( idx->stream )
+ {
+ FT_Stream stream = idx->stream;
+ FT_Memory memory = stream->memory;
+
+
+ if ( idx->bytes )
+ FT_FRAME_RELEASE( idx->bytes );
+
+ FT_FREE( idx->offsets );
+ FT_MEM_ZERO( idx, sizeof ( *idx ) );
+ }
+ }
+
+
+ static FT_Error
+ cff_index_load_offsets( CFF_Index idx )
+ {
+ FT_Error error = 0;
+ FT_Stream stream = idx->stream;
+ FT_Memory memory = stream->memory;
+
+
+ if ( idx->count > 0 && idx->offsets == NULL )
+ {
+ FT_Byte offsize = idx->off_size;
+ FT_ULong data_size;
+ FT_Byte* p;
+ FT_Byte* p_end;
+ FT_ULong* poff;
+
+
+ data_size = (FT_ULong)( idx->count + 1 ) * offsize;
+
+ if ( FT_NEW_ARRAY( idx->offsets, idx->count + 1 ) ||
+ FT_STREAM_SEEK( idx->start + 3 ) ||
+ FT_FRAME_ENTER( data_size ) )
+ goto Exit;
+
+ poff = idx->offsets;
+ p = (FT_Byte*)stream->cursor;
+ p_end = p + data_size;
+
+ switch ( offsize )
+ {
+ case 1:
+ for ( ; p < p_end; p++, poff++ )
+ poff[0] = p[0];
+ break;
+
+ case 2:
+ for ( ; p < p_end; p += 2, poff++ )
+ poff[0] = FT_PEEK_USHORT( p );
+ break;
+
+ case 3:
+ for ( ; p < p_end; p += 3, poff++ )
+ poff[0] = FT_PEEK_OFF3( p );
+ break;
+
+ default:
+ for ( ; p < p_end; p += 4, poff++ )
+ poff[0] = FT_PEEK_ULONG( p );
+ }
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ if ( error )
+ FT_FREE( idx->offsets );
+
+ return error;
+ }
+
+
+ /* allocate a table containing pointers to an index's elements */
+ static FT_Error
+ cff_index_get_pointers( CFF_Index idx,
+ FT_Byte*** table )
+ {
+ FT_Error error = CFF_Err_Ok;
+ FT_Memory memory = idx->stream->memory;
+ FT_ULong n, offset, old_offset;
+ FT_Byte** t;
+
+
+ *table = 0;
+
+ if ( idx->offsets == NULL )
+ {
+ error = cff_index_load_offsets( idx );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( idx->count > 0 && !FT_NEW_ARRAY( t, idx->count + 1 ) )
+ {
+ old_offset = 1;
+ for ( n = 0; n <= idx->count; n++ )
+ {
+ offset = idx->offsets[n];
+ if ( !offset )
+ offset = old_offset;
+
+ /* two sanity checks for invalid offset tables */
+ else if ( offset < old_offset )
+ offset = old_offset;
+
+ else if ( offset - 1 >= idx->data_size && n < idx->count )
+ offset = old_offset;
+
+ t[n] = idx->bytes + offset - 1;
+
+ old_offset = offset;
+ }
+ *table = t;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_index_access_element( CFF_Index idx,
+ FT_UInt element,
+ FT_Byte** pbytes,
+ FT_ULong* pbyte_len )
+ {
+ FT_Error error = CFF_Err_Ok;
+
+
+ if ( idx && idx->count > element )
+ {
+ /* compute start and end offsets */
+ FT_Stream stream = idx->stream;
+ FT_ULong off1, off2 = 0;
+
+
+ /* load offsets from file or the offset table */
+ if ( !idx->offsets )
+ {
+ FT_ULong pos = element * idx->off_size;
+
+
+ if ( FT_STREAM_SEEK( idx->start + 3 + pos ) )
+ goto Exit;
+
+ off1 = cff_index_read_offset( idx, &error );
+ if ( error )
+ goto Exit;
+
+ if ( off1 != 0 )
+ {
+ do
+ {
+ element++;
+ off2 = cff_index_read_offset( idx, &error );
+ }
+ while ( off2 == 0 && element < idx->count );
+ }
+ }
+ else /* use offsets table */
+ {
+ off1 = idx->offsets[element];
+ if ( off1 )
+ {
+ do
+ {
+ element++;
+ off2 = idx->offsets[element];
+
+ } while ( off2 == 0 && element < idx->count );
+ }
+ }
+
+ /* access element */
+ if ( off1 && off2 > off1 )
+ {
+ *pbyte_len = off2 - off1;
+
+ if ( idx->bytes )
+ {
+ /* this index was completely loaded in memory, that's easy */
+ *pbytes = idx->bytes + off1 - 1;
+ }
+ else
+ {
+ /* this index is still on disk/file, access it through a frame */
+ if ( FT_STREAM_SEEK( idx->data_offset + off1 - 1 ) ||
+ FT_FRAME_EXTRACT( off2 - off1, *pbytes ) )
+ goto Exit;
+ }
+ }
+ else
+ {
+ /* empty index element */
+ *pbytes = 0;
+ *pbyte_len = 0;
+ }
+ }
+ else
+ error = CFF_Err_Invalid_Argument;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cff_index_forget_element( CFF_Index idx,
+ FT_Byte** pbytes )
+ {
+ if ( idx->bytes == 0 )
+ {
+ FT_Stream stream = idx->stream;
+
+
+ FT_FRAME_RELEASE( *pbytes );
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_String* )
+ cff_index_get_name( CFF_Index idx,
+ FT_UInt element )
+ {
+ FT_Memory memory = idx->stream->memory;
+ FT_Byte* bytes;
+ FT_ULong byte_len;
+ FT_Error error;
+ FT_String* name = 0;
+
+
+ error = cff_index_access_element( idx, element, &bytes, &byte_len );
+ if ( error )
+ goto Exit;
+
+ if ( !FT_ALLOC( name, byte_len + 1 ) )
+ {
+ FT_MEM_COPY( name, bytes, byte_len );
+ name[byte_len] = 0;
+ }
+ cff_index_forget_element( idx, &bytes );
+
+ Exit:
+ return name;
+ }
+
+
+ FT_LOCAL_DEF( FT_String* )
+ cff_index_get_sid_string( CFF_Index idx,
+ FT_UInt sid,
+ FT_Service_PsCMaps psnames )
+ {
+ /* value 0xFFFFU indicates a missing dictionary entry */
+ if ( sid == 0xFFFFU )
+ return 0;
+
+ /* if it is not a standard string, return it */
+ if ( sid > 390 )
+ return cff_index_get_name( idx, sid - 391 );
+
+ /* CID-keyed CFF fonts don't have glyph names */
+ if ( !psnames )
+ return 0;
+
+ /* that's a standard string, fetch a copy from the PSName module */
+ {
+ FT_String* name = 0;
+ const char* adobe_name = psnames->adobe_std_strings( sid );
+
+
+ if ( adobe_name )
+ {
+ FT_Memory memory = idx->stream->memory;
+ FT_Error error;
+
+
+ (void)FT_STRDUP( name, adobe_name );
+
+ FT_UNUSED( error );
+ }
+
+ return name;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*** ***/
+ /*** FD Select table support ***/
+ /*** ***/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static void
+ CFF_Done_FD_Select( CFF_FDSelect fdselect,
+ FT_Stream stream )
+ {
+ if ( fdselect->data )
+ FT_FRAME_RELEASE( fdselect->data );
+
+ fdselect->data_size = 0;
+ fdselect->format = 0;
+ fdselect->range_count = 0;
+ }
+
+
+ static FT_Error
+ CFF_Load_FD_Select( CFF_FDSelect fdselect,
+ FT_UInt num_glyphs,
+ FT_Stream stream,
+ FT_ULong offset )
+ {
+ FT_Error error;
+ FT_Byte format;
+ FT_UInt num_ranges;
+
+
+ /* read format */
+ if ( FT_STREAM_SEEK( offset ) || FT_READ_BYTE( format ) )
+ goto Exit;
+
+ fdselect->format = format;
+ fdselect->cache_count = 0; /* clear cache */
+
+ switch ( format )
+ {
+ case 0: /* format 0, that's simple */
+ fdselect->data_size = num_glyphs;
+ goto Load_Data;
+
+ case 3: /* format 3, a tad more complex */
+ if ( FT_READ_USHORT( num_ranges ) )
+ goto Exit;
+
+ fdselect->data_size = num_ranges * 3 + 2;
+
+ Load_Data:
+ if ( FT_FRAME_EXTRACT( fdselect->data_size, fdselect->data ) )
+ goto Exit;
+ break;
+
+ default: /* hmm... that's wrong */
+ error = CFF_Err_Invalid_File_Format;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Byte )
+ cff_fd_select_get( CFF_FDSelect fdselect,
+ FT_UInt glyph_index )
+ {
+ FT_Byte fd = 0;
+
+
+ switch ( fdselect->format )
+ {
+ case 0:
+ fd = fdselect->data[glyph_index];
+ break;
+
+ case 3:
+ /* first, compare to cache */
+ if ( (FT_UInt)( glyph_index - fdselect->cache_first ) <
+ fdselect->cache_count )
+ {
+ fd = fdselect->cache_fd;
+ break;
+ }
+
+ /* then, lookup the ranges array */
+ {
+ FT_Byte* p = fdselect->data;
+ FT_Byte* p_limit = p + fdselect->data_size;
+ FT_Byte fd2;
+ FT_UInt first, limit;
+
+
+ first = FT_NEXT_USHORT( p );
+ do
+ {
+ if ( glyph_index < first )
+ break;
+
+ fd2 = *p++;
+ limit = FT_NEXT_USHORT( p );
+
+ if ( glyph_index < limit )
+ {
+ fd = fd2;
+
+ /* update cache */
+ fdselect->cache_first = first;
+ fdselect->cache_count = limit-first;
+ fdselect->cache_fd = fd2;
+ break;
+ }
+ first = limit;
+
+ } while ( p < p_limit );
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return fd;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*** ***/
+ /*** CFF font support ***/
+ /*** ***/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ cff_charset_compute_cids( CFF_Charset charset,
+ FT_UInt num_glyphs,
+ FT_Memory memory )
+ {
+ FT_Error error = FT_Err_Ok;
+ FT_UInt i;
+ FT_UShort max_cid = 0;
+
+
+ if ( charset->max_cid > 0 )
+ goto Exit;
+
+ for ( i = 0; i < num_glyphs; i++ )
+ if ( charset->sids[i] > max_cid )
+ max_cid = charset->sids[i];
+ max_cid++;
+
+ if ( FT_NEW_ARRAY( charset->cids, max_cid ) )
+ goto Exit;
+
+ for ( i = 0; i < num_glyphs; i++ )
+ charset->cids[charset->sids[i]] = (FT_UShort)i;
+
+ charset->max_cid = max_cid;
+ charset->num_glyphs = num_glyphs;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ cff_charset_cid_to_gindex( CFF_Charset charset,
+ FT_UInt cid )
+ {
+ FT_UInt result = 0;
+
+
+ if ( cid < charset->max_cid )
+ result = charset->cids[cid];
+
+ return result;
+ }
+
+
+ static void
+ cff_charset_free_cids( CFF_Charset charset,
+ FT_Memory memory )
+ {
+ FT_FREE( charset->cids );
+ charset->max_cid = 0;
+ }
+
+
+ static void
+ cff_charset_done( CFF_Charset charset,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+
+
+ cff_charset_free_cids( charset, memory );
+
+ FT_FREE( charset->sids );
+ charset->format = 0;
+ charset->offset = 0;
+ }
+
+
+ static FT_Error
+ cff_charset_load( CFF_Charset charset,
+ FT_UInt num_glyphs,
+ FT_Stream stream,
+ FT_ULong base_offset,
+ FT_ULong offset,
+ FT_Bool invert )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error = CFF_Err_Ok;
+ FT_UShort glyph_sid;
+
+
+ /* If the the offset is greater than 2, we have to parse the */
+ /* charset table. */
+ if ( offset > 2 )
+ {
+ FT_UInt j;
+
+
+ charset->offset = base_offset + offset;
+
+ /* Get the format of the table. */
+ if ( FT_STREAM_SEEK( charset->offset ) ||
+ FT_READ_BYTE( charset->format ) )
+ goto Exit;
+
+ /* Allocate memory for sids. */
+ if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) )
+ goto Exit;
+
+ /* assign the .notdef glyph */
+ charset->sids[0] = 0;
+
+ switch ( charset->format )
+ {
+ case 0:
+ if ( num_glyphs > 0 )
+ {
+ if ( FT_FRAME_ENTER( ( num_glyphs - 1 ) * 2 ) )
+ goto Exit;
+
+ for ( j = 1; j < num_glyphs; j++ )
+ charset->sids[j] = FT_GET_USHORT();
+
+ FT_FRAME_EXIT();
+ }
+ break;
+
+ case 1:
+ case 2:
+ {
+ FT_UInt nleft;
+ FT_UInt i;
+
+
+ j = 1;
+
+ while ( j < num_glyphs )
+ {
+ /* Read the first glyph sid of the range. */
+ if ( FT_READ_USHORT( glyph_sid ) )
+ goto Exit;
+
+ /* Read the number of glyphs in the range. */
+ if ( charset->format == 2 )
+ {
+ if ( FT_READ_USHORT( nleft ) )
+ goto Exit;
+ }
+ else
+ {
+ if ( FT_READ_BYTE( nleft ) )
+ goto Exit;
+ }
+
+ /* Fill in the range of sids -- `nleft + 1' glyphs. */
+ for ( i = 0; j < num_glyphs && i <= nleft; i++, j++, glyph_sid++ )
+ charset->sids[j] = glyph_sid;
+ }
+ }
+ break;
+
+ default:
+ FT_ERROR(( "cff_charset_load: invalid table format!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+ }
+ else
+ {
+ /* Parse default tables corresponding to offset == 0, 1, or 2. */
+ /* CFF specification intimates the following: */
+ /* */
+ /* In order to use a predefined charset, the following must be */
+ /* true: The charset constructed for the glyphs in the font's */
+ /* charstrings dictionary must match the predefined charset in */
+ /* the first num_glyphs. */
+
+ charset->offset = offset; /* record charset type */
+
+ switch ( (FT_UInt)offset )
+ {
+ case 0:
+ if ( num_glyphs > 229 )
+ {
+ FT_ERROR(( "cff_charset_load: implicit charset larger than\n"
+ "predefined charset (Adobe ISO-Latin)!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Allocate memory for sids. */
+ if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) )
+ goto Exit;
+
+ /* Copy the predefined charset into the allocated memory. */
+ FT_ARRAY_COPY( charset->sids, cff_isoadobe_charset, num_glyphs );
+
+ break;
+
+ case 1:
+ if ( num_glyphs > 166 )
+ {
+ FT_ERROR(( "cff_charset_load: implicit charset larger than\n"
+ "predefined charset (Adobe Expert)!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Allocate memory for sids. */
+ if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) )
+ goto Exit;
+
+ /* Copy the predefined charset into the allocated memory. */
+ FT_ARRAY_COPY( charset->sids, cff_expert_charset, num_glyphs );
+
+ break;
+
+ case 2:
+ if ( num_glyphs > 87 )
+ {
+ FT_ERROR(( "cff_charset_load: implicit charset larger than\n"
+ "predefined charset (Adobe Expert Subset)!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Allocate memory for sids. */
+ if ( FT_NEW_ARRAY( charset->sids, num_glyphs ) )
+ goto Exit;
+
+ /* Copy the predefined charset into the allocated memory. */
+ FT_ARRAY_COPY( charset->sids, cff_expertsubset_charset, num_glyphs );
+
+ break;
+
+ default:
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+ }
+
+ /* we have to invert the `sids' array for subsetted CID-keyed fonts */
+ if ( invert )
+ error = cff_charset_compute_cids( charset, num_glyphs, memory );
+
+ Exit:
+ /* Clean up if there was an error. */
+ if ( error )
+ {
+ FT_FREE( charset->sids );
+ FT_FREE( charset->cids );
+ charset->format = 0;
+ charset->offset = 0;
+ charset->sids = 0;
+ }
+
+ return error;
+ }
+
+
+ static void
+ cff_encoding_done( CFF_Encoding encoding )
+ {
+ encoding->format = 0;
+ encoding->offset = 0;
+ encoding->count = 0;
+ }
+
+
+ static FT_Error
+ cff_encoding_load( CFF_Encoding encoding,
+ CFF_Charset charset,
+ FT_UInt num_glyphs,
+ FT_Stream stream,
+ FT_ULong base_offset,
+ FT_ULong offset )
+ {
+ FT_Error error = CFF_Err_Ok;
+ FT_UInt count;
+ FT_UInt j;
+ FT_UShort glyph_sid;
+ FT_UInt glyph_code;
+
+
+ /* Check for charset->sids. If we do not have this, we fail. */
+ if ( !charset->sids )
+ {
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Zero out the code to gid/sid mappings. */
+ for ( j = 0; j < 256; j++ )
+ {
+ encoding->sids [j] = 0;
+ encoding->codes[j] = 0;
+ }
+
+ /* Note: The encoding table in a CFF font is indexed by glyph index; */
+ /* the first encoded glyph index is 1. Hence, we read the character */
+ /* code (`glyph_code') at index j and make the assignment: */
+ /* */
+ /* encoding->codes[glyph_code] = j + 1 */
+ /* */
+ /* We also make the assignment: */
+ /* */
+ /* encoding->sids[glyph_code] = charset->sids[j + 1] */
+ /* */
+ /* This gives us both a code to GID and a code to SID mapping. */
+
+ if ( offset > 1 )
+ {
+ encoding->offset = base_offset + offset;
+
+ /* we need to parse the table to determine its size */
+ if ( FT_STREAM_SEEK( encoding->offset ) ||
+ FT_READ_BYTE( encoding->format ) ||
+ FT_READ_BYTE( count ) )
+ goto Exit;
+
+ switch ( encoding->format & 0x7F )
+ {
+ case 0:
+ {
+ FT_Byte* p;
+
+
+ /* By convention, GID 0 is always ".notdef" and is never */
+ /* coded in the font. Hence, the number of codes found */
+ /* in the table is `count+1'. */
+ /* */
+ encoding->count = count + 1;
+
+ if ( FT_FRAME_ENTER( count ) )
+ goto Exit;
+
+ p = (FT_Byte*)stream->cursor;
+
+ for ( j = 1; j <= count; j++ )
+ {
+ glyph_code = *p++;
+
+ /* Make sure j is not too big. */
+ if ( j < num_glyphs )
+ {
+ /* Assign code to GID mapping. */
+ encoding->codes[glyph_code] = (FT_UShort)j;
+
+ /* Assign code to SID mapping. */
+ encoding->sids[glyph_code] = charset->sids[j];
+ }
+ }
+
+ FT_FRAME_EXIT();
+ }
+ break;
+
+ case 1:
+ {
+ FT_UInt nleft;
+ FT_UInt i = 1;
+ FT_UInt k;
+
+
+ encoding->count = 0;
+
+ /* Parse the Format1 ranges. */
+ for ( j = 0; j < count; j++, i += nleft )
+ {
+ /* Read the first glyph code of the range. */
+ if ( FT_READ_BYTE( glyph_code ) )
+ goto Exit;
+
+ /* Read the number of codes in the range. */
+ if ( FT_READ_BYTE( nleft ) )
+ goto Exit;
+
+ /* Increment nleft, so we read `nleft + 1' codes/sids. */
+ nleft++;
+
+ /* compute max number of character codes */
+ if ( (FT_UInt)nleft > encoding->count )
+ encoding->count = nleft;
+
+ /* Fill in the range of codes/sids. */
+ for ( k = i; k < nleft + i; k++, glyph_code++ )
+ {
+ /* Make sure k is not too big. */
+ if ( k < num_glyphs && glyph_code < 256 )
+ {
+ /* Assign code to GID mapping. */
+ encoding->codes[glyph_code] = (FT_UShort)k;
+
+ /* Assign code to SID mapping. */
+ encoding->sids[glyph_code] = charset->sids[k];
+ }
+ }
+ }
+
+ /* simple check; one never knows what can be found in a font */
+ if ( encoding->count > 256 )
+ encoding->count = 256;
+ }
+ break;
+
+ default:
+ FT_ERROR(( "cff_encoding_load: invalid table format!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* Parse supplemental encodings, if any. */
+ if ( encoding->format & 0x80 )
+ {
+ FT_UInt gindex;
+
+
+ /* count supplements */
+ if ( FT_READ_BYTE( count ) )
+ goto Exit;
+
+ for ( j = 0; j < count; j++ )
+ {
+ /* Read supplemental glyph code. */
+ if ( FT_READ_BYTE( glyph_code ) )
+ goto Exit;
+
+ /* Read the SID associated with this glyph code. */
+ if ( FT_READ_USHORT( glyph_sid ) )
+ goto Exit;
+
+ /* Assign code to SID mapping. */
+ encoding->sids[glyph_code] = glyph_sid;
+
+ /* First, look up GID which has been assigned to */
+ /* SID glyph_sid. */
+ for ( gindex = 0; gindex < num_glyphs; gindex++ )
+ {
+ if ( charset->sids[gindex] == glyph_sid )
+ {
+ encoding->codes[glyph_code] = (FT_UShort)gindex;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* We take into account the fact a CFF font can use a predefined */
+ /* encoding without containing all of the glyphs encoded by this */
+ /* encoding (see the note at the end of section 12 in the CFF */
+ /* specification). */
+
+ switch ( (FT_UInt)offset )
+ {
+ case 0:
+ /* First, copy the code to SID mapping. */
+ FT_ARRAY_COPY( encoding->sids, cff_standard_encoding, 256 );
+ goto Populate;
+
+ case 1:
+ /* First, copy the code to SID mapping. */
+ FT_ARRAY_COPY( encoding->sids, cff_expert_encoding, 256 );
+
+ Populate:
+ /* Construct code to GID mapping from code to SID mapping */
+ /* and charset. */
+
+ encoding->count = 0;
+
+ error = cff_charset_compute_cids( charset, num_glyphs,
+ stream->memory );
+ if ( error )
+ goto Exit;
+
+ for ( j = 0; j < 256; j++ )
+ {
+ FT_UInt sid = encoding->sids[j];
+ FT_UInt gid = 0;
+
+
+ if ( sid )
+ gid = cff_charset_cid_to_gindex( charset, sid );
+
+ if ( gid != 0 )
+ {
+ encoding->codes[j] = (FT_UShort)gid;
+
+ if ( encoding->count < j + 1 )
+ encoding->count = j + 1;
+ }
+ else
+ {
+ encoding->codes[j] = 0;
+ encoding->sids [j] = 0;
+ }
+ }
+ break;
+
+ default:
+ FT_ERROR(( "cff_encoding_load: invalid table format!\n" ));
+ error = CFF_Err_Invalid_File_Format;
+ goto Exit;
+ }
+ }
+
+ Exit:
+
+ /* Clean up if there was an error. */
+ return error;
+ }
+
+
+ static FT_Error
+ cff_subfont_load( CFF_SubFont font,
+ CFF_Index idx,
+ FT_UInt font_index,
+ FT_Stream stream,
+ FT_ULong base_offset )
+ {
+ FT_Error error;
+ CFF_ParserRec parser;
+ FT_Byte* dict = NULL;
+ FT_ULong dict_len;
+ CFF_FontRecDict top = &font->font_dict;
+ CFF_Private priv = &font->private_dict;
+
+
+ cff_parser_init( &parser, CFF_CODE_TOPDICT, &font->font_dict );
+
+ /* set defaults */
+ FT_MEM_ZERO( top, sizeof ( *top ) );
+
+ top->underline_position = -100L << 16;
+ top->underline_thickness = 50L << 16;
+ top->charstring_type = 2;
+ top->font_matrix.xx = 0x10000L;
+ top->font_matrix.yy = 0x10000L;
+ top->cid_count = 8720;
+
+ /* we use the implementation specific SID value 0xFFFF to indicate */
+ /* missing entries */
+ top->version = 0xFFFFU;
+ top->notice = 0xFFFFU;
+ top->copyright = 0xFFFFU;
+ top->full_name = 0xFFFFU;
+ top->family_name = 0xFFFFU;
+ top->weight = 0xFFFFU;
+ top->embedded_postscript = 0xFFFFU;
+
+ top->cid_registry = 0xFFFFU;
+ top->cid_ordering = 0xFFFFU;
+ top->cid_font_name = 0xFFFFU;
+
+ error = cff_index_access_element( idx, font_index, &dict, &dict_len ) ||
+ cff_parser_run( &parser, dict, dict + dict_len );
+
+ cff_index_forget_element( idx, &dict );
+
+ if ( error )
+ goto Exit;
+
+ /* if it is a CID font, we stop there */
+ if ( top->cid_registry != 0xFFFFU )
+ goto Exit;
+
+ /* parse the private dictionary, if any */
+ if ( top->private_offset && top->private_size )
+ {
+ /* set defaults */
+ FT_MEM_ZERO( priv, sizeof ( *priv ) );
+
+ priv->blue_shift = 7;
+ priv->blue_fuzz = 1;
+ priv->lenIV = -1;
+ priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L );
+ priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 );
+
+ cff_parser_init( &parser, CFF_CODE_PRIVATE, priv );
+
+ if ( FT_STREAM_SEEK( base_offset + font->font_dict.private_offset ) ||
+ FT_FRAME_ENTER( font->font_dict.private_size ) )
+ goto Exit;
+
+ error = cff_parser_run( &parser,
+ (FT_Byte*)stream->cursor,
+ (FT_Byte*)stream->limit );
+ FT_FRAME_EXIT();
+ if ( error )
+ goto Exit;
+
+ /* ensure that `num_blue_values' is even */
+ priv->num_blue_values &= ~1;
+ }
+
+ /* read the local subrs, if any */
+ if ( priv->local_subrs_offset )
+ {
+ if ( FT_STREAM_SEEK( base_offset + top->private_offset +
+ priv->local_subrs_offset ) )
+ goto Exit;
+
+ error = cff_index_init( &font->local_subrs_index, stream, 1 );
+ if ( error )
+ goto Exit;
+
+ font->num_local_subrs = font->local_subrs_index.count;
+ error = cff_index_get_pointers( &font->local_subrs_index,
+ &font->local_subrs );
+ if ( error )
+ goto Exit;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ cff_subfont_done( FT_Memory memory,
+ CFF_SubFont subfont )
+ {
+ if ( subfont )
+ {
+ cff_index_done( &subfont->local_subrs_index );
+ FT_FREE( subfont->local_subrs );
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_font_load( FT_Stream stream,
+ FT_Int face_index,
+ CFF_Font font )
+ {
+ static const FT_Frame_Field cff_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE CFF_FontRec
+
+ FT_FRAME_START( 4 ),
+ FT_FRAME_BYTE( version_major ),
+ FT_FRAME_BYTE( version_minor ),
+ FT_FRAME_BYTE( header_size ),
+ FT_FRAME_BYTE( absolute_offsize ),
+ FT_FRAME_END
+ };
+
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_ULong base_offset;
+ CFF_FontRecDict dict;
+
+
+ FT_ZERO( font );
+
+ font->stream = stream;
+ font->memory = memory;
+ dict = &font->top_font.font_dict;
+ base_offset = FT_STREAM_POS();
+
+ /* read CFF font header */
+ if ( FT_STREAM_READ_FIELDS( cff_header_fields, font ) )
+ goto Exit;
+
+ /* check format */
+ if ( font->version_major != 1 ||
+ font->header_size < 4 ||
+ font->absolute_offsize > 4 )
+ {
+ FT_TRACE2(( "[not a CFF font header!]\n" ));
+ error = CFF_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* skip the rest of the header */
+ if ( FT_STREAM_SKIP( font->header_size - 4 ) )
+ goto Exit;
+
+ /* read the name, top dict, string and global subrs index */
+ if ( FT_SET_ERROR( cff_index_init( &font->name_index,
+ stream, 0 ) ) ||
+ FT_SET_ERROR( cff_index_init( &font->font_dict_index,
+ stream, 0 ) ) ||
+ FT_SET_ERROR( cff_index_init( &font->string_index,
+ stream, 0 ) ) ||
+ FT_SET_ERROR( cff_index_init( &font->global_subrs_index,
+ stream, 1 ) ) )
+ goto Exit;
+
+ /* well, we don't really forget the `disabled' fonts... */
+ font->num_faces = font->name_index.count;
+ if ( face_index >= (FT_Int)font->num_faces )
+ {
+ FT_ERROR(( "cff_font_load: incorrect face index = %d\n",
+ face_index ));
+ error = CFF_Err_Invalid_Argument;
+ }
+
+ /* in case of a font format check, simply exit now */
+ if ( face_index < 0 )
+ goto Exit;
+
+ /* now, parse the top-level font dictionary */
+ error = cff_subfont_load( &font->top_font,
+ &font->font_dict_index,
+ face_index,
+ stream,
+ base_offset );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_SEEK( base_offset + dict->charstrings_offset ) )
+ goto Exit;
+
+ error = cff_index_init( &font->charstrings_index, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ /* now, check for a CID font */
+ if ( dict->cid_registry != 0xFFFFU )
+ {
+ CFF_IndexRec fd_index;
+ CFF_SubFont sub;
+ FT_UInt idx;
+
+
+ /* this is a CID-keyed font, we must now allocate a table of */
+ /* sub-fonts, then load each of them separately */
+ if ( FT_STREAM_SEEK( base_offset + dict->cid_fd_array_offset ) )
+ goto Exit;
+
+ error = cff_index_init( &fd_index, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( fd_index.count > CFF_MAX_CID_FONTS )
+ {
+ FT_ERROR(( "cff_font_load: FD array too large in CID font\n" ));
+ goto Fail_CID;
+ }
+
+ /* allocate & read each font dict independently */
+ font->num_subfonts = fd_index.count;
+ if ( FT_NEW_ARRAY( sub, fd_index.count ) )
+ goto Fail_CID;
+
+ /* set up pointer table */
+ for ( idx = 0; idx < fd_index.count; idx++ )
+ font->subfonts[idx] = sub + idx;
+
+ /* now load each subfont independently */
+ for ( idx = 0; idx < fd_index.count; idx++ )
+ {
+ sub = font->subfonts[idx];
+ error = cff_subfont_load( sub, &fd_index, idx,
+ stream, base_offset );
+ if ( error )
+ goto Fail_CID;
+ }
+
+ /* now load the FD Select array */
+ error = CFF_Load_FD_Select( &font->fd_select,
+ font->charstrings_index.count,
+ stream,
+ base_offset + dict->cid_fd_select_offset );
+
+ Fail_CID:
+ cff_index_done( &fd_index );
+
+ if ( error )
+ goto Exit;
+ }
+ else
+ font->num_subfonts = 0;
+
+ /* read the charstrings index now */
+ if ( dict->charstrings_offset == 0 )
+ {
+ FT_ERROR(( "cff_font_load: no charstrings offset!\n" ));
+ error = CFF_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* explicit the global subrs */
+ font->num_global_subrs = font->global_subrs_index.count;
+ font->num_glyphs = font->charstrings_index.count;
+
+ error = cff_index_get_pointers( &font->global_subrs_index,
+ &font->global_subrs ) ;
+
+ if ( error )
+ goto Exit;
+
+ /* read the Charset and Encoding tables if available */
+ if ( font->num_glyphs > 0 )
+ {
+ FT_Bool invert = FT_BOOL( dict->cid_registry != 0xFFFFU );
+
+
+ error = cff_charset_load( &font->charset, font->num_glyphs, stream,
+ base_offset, dict->charset_offset, invert );
+ if ( error )
+ goto Exit;
+
+ /* CID-keyed CFFs don't have an encoding */
+ if ( dict->cid_registry == 0xFFFFU )
+ {
+ error = cff_encoding_load( &font->encoding,
+ &font->charset,
+ font->num_glyphs,
+ stream,
+ base_offset,
+ dict->encoding_offset );
+ if ( error )
+ goto Exit;
+ }
+ else
+ /* CID-keyed fonts only need CIDs */
+ FT_FREE( font->charset.sids );
+ }
+
+ /* get the font name (/CIDFontName for CID-keyed fonts, */
+ /* /FontName otherwise) */
+ font->font_name = cff_index_get_name( &font->name_index, face_index );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cff_font_done( CFF_Font font )
+ {
+ FT_Memory memory = font->memory;
+ FT_UInt idx;
+
+
+ cff_index_done( &font->global_subrs_index );
+ cff_index_done( &font->string_index );
+ cff_index_done( &font->font_dict_index );
+ cff_index_done( &font->name_index );
+ cff_index_done( &font->charstrings_index );
+
+ /* release font dictionaries, but only if working with */
+ /* a CID keyed CFF font */
+ if ( font->num_subfonts > 0 )
+ {
+ for ( idx = 0; idx < font->num_subfonts; idx++ )
+ cff_subfont_done( memory, font->subfonts[idx] );
+
+ /* the subfonts array has been allocated as a single block */
+ FT_FREE( font->subfonts[0] );
+ }
+
+ cff_encoding_done( &font->encoding );
+ cff_charset_done( &font->charset, font->stream );
+
+ cff_subfont_done( memory, &font->top_font );
+
+ CFF_Done_FD_Select( &font->fd_select, font->stream );
+
+ if (font->font_info != NULL)
+ {
+ FT_FREE( font->font_info->version );
+ FT_FREE( font->font_info->notice );
+ FT_FREE( font->font_info->full_name );
+ FT_FREE( font->font_info->family_name );
+ FT_FREE( font->font_info->weight );
+ FT_FREE( font->font_info );
+ }
+
+ FT_FREE( font->global_subrs );
+ FT_FREE( font->font_name );
+ }
+
+
+/* END */
diff --git a/src/cff/cffload.h b/src/cff/cffload.h
new file mode 100644
index 0000000..068cbb5
--- /dev/null
+++ b/src/cff/cffload.h
@@ -0,0 +1,79 @@
+/***************************************************************************/
+/* */
+/* cffload.h */
+/* */
+/* OpenType & CFF data/program tables loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFLOAD_H__
+#define __CFFLOAD_H__
+
+
+#include <ft2build.h>
+#include "cfftypes.h"
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( FT_UShort )
+ cff_get_standard_encoding( FT_UInt charcode );
+
+
+ FT_LOCAL( FT_String* )
+ cff_index_get_name( CFF_Index idx,
+ FT_UInt element );
+
+ FT_LOCAL( FT_String* )
+ cff_index_get_sid_string( CFF_Index idx,
+ FT_UInt sid,
+ FT_Service_PsCMaps psnames );
+
+
+ FT_LOCAL( FT_Error )
+ cff_index_access_element( CFF_Index idx,
+ FT_UInt element,
+ FT_Byte** pbytes,
+ FT_ULong* pbyte_len );
+
+ FT_LOCAL( void )
+ cff_index_forget_element( CFF_Index idx,
+ FT_Byte** pbytes );
+
+
+ FT_LOCAL( FT_UInt )
+ cff_charset_cid_to_gindex( CFF_Charset charset,
+ FT_UInt cid );
+
+
+ FT_LOCAL( FT_Error )
+ cff_font_load( FT_Stream stream,
+ FT_Int face_index,
+ CFF_Font font );
+
+ FT_LOCAL( void )
+ cff_font_done( CFF_Font font );
+
+
+ FT_LOCAL( FT_Byte )
+ cff_fd_select_get( CFF_FDSelect fdselect,
+ FT_UInt glyph_index );
+
+
+FT_END_HEADER
+
+#endif /* __CFFLOAD_H__ */
+
+
+/* END */
diff --git a/src/cff/cffobjs.c b/src/cff/cffobjs.c
new file mode 100644
index 0000000..c02cf33
--- /dev/null
+++ b/src/cff/cffobjs.c
@@ -0,0 +1,782 @@
+/***************************************************************************/
+/* */
+/* cffobjs.c */
+/* */
+/* OpenType objects manager (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_STREAM_H
+#include FT_ERRORS_H
+#include FT_TRUETYPE_IDS_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_INTERNAL_SFNT_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+#include "cffobjs.h"
+#include "cffload.h"
+#include "cffcmap.h"
+#include "cfferrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffobjs
+
+
+ /*************************************************************************/
+ /* */
+ /* SIZE FUNCTIONS */
+ /* */
+ /* Note that we store the global hints in the size's `internal' root */
+ /* field. */
+ /* */
+ /*************************************************************************/
+
+
+ static PSH_Globals_Funcs
+ cff_size_get_globals_funcs( CFF_Size size )
+ {
+ CFF_Face face = (CFF_Face)size->root.face;
+ CFF_Font font = (CFF_FontRec *)face->extra.data;
+ PSHinter_Service pshinter = (PSHinter_Service)font->pshinter;
+ FT_Module module;
+
+
+ module = FT_Get_Module( size->root.face->driver->root.library,
+ "pshinter" );
+ return ( module && pshinter && pshinter->get_globals_funcs )
+ ? pshinter->get_globals_funcs( module )
+ : 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cff_size_done( FT_Size cffsize ) /* CFF_Size */
+ {
+ CFF_Size size = (CFF_Size)cffsize;
+
+
+ if ( cffsize->internal )
+ {
+ PSH_Globals_Funcs funcs;
+
+
+ funcs = cff_size_get_globals_funcs( size );
+ if ( funcs )
+ funcs->destroy( (PSH_Globals)cffsize->internal );
+
+ cffsize->internal = 0;
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_size_init( FT_Size cffsize ) /* CFF_Size */
+ {
+ CFF_Size size = (CFF_Size)cffsize;
+ FT_Error error = CFF_Err_Ok;
+ PSH_Globals_Funcs funcs = cff_size_get_globals_funcs( size );
+
+
+ if ( funcs )
+ {
+ PSH_Globals globals;
+ CFF_Face face = (CFF_Face)cffsize->face;
+ CFF_Font font = (CFF_FontRec *)face->extra.data;
+ CFF_SubFont subfont = &font->top_font;
+
+ CFF_Private cpriv = &subfont->private_dict;
+ PS_PrivateRec priv;
+
+
+ /* IMPORTANT: The CFF and Type1 private dictionaries have */
+ /* slightly different structures; we need to */
+ /* synthetize a type1 dictionary on the fly here. */
+
+ {
+ FT_UInt n, count;
+
+
+ FT_MEM_ZERO( &priv, sizeof ( priv ) );
+
+ count = priv.num_blue_values = cpriv->num_blue_values;
+ for ( n = 0; n < count; n++ )
+ priv.blue_values[n] = (FT_Short)cpriv->blue_values[n];
+
+ count = priv.num_other_blues = cpriv->num_other_blues;
+ for ( n = 0; n < count; n++ )
+ priv.other_blues[n] = (FT_Short)cpriv->other_blues[n];
+
+ count = priv.num_family_blues = cpriv->num_family_blues;
+ for ( n = 0; n < count; n++ )
+ priv.family_blues[n] = (FT_Short)cpriv->family_blues[n];
+
+ count = priv.num_family_other_blues = cpriv->num_family_other_blues;
+ for ( n = 0; n < count; n++ )
+ priv.family_other_blues[n] = (FT_Short)cpriv->family_other_blues[n];
+
+ priv.blue_scale = cpriv->blue_scale;
+ priv.blue_shift = (FT_Int)cpriv->blue_shift;
+ priv.blue_fuzz = (FT_Int)cpriv->blue_fuzz;
+
+ priv.standard_width[0] = (FT_UShort)cpriv->standard_width;
+ priv.standard_height[0] = (FT_UShort)cpriv->standard_height;
+
+ count = priv.num_snap_widths = cpriv->num_snap_widths;
+ for ( n = 0; n < count; n++ )
+ priv.snap_widths[n] = (FT_Short)cpriv->snap_widths[n];
+
+ count = priv.num_snap_heights = cpriv->num_snap_heights;
+ for ( n = 0; n < count; n++ )
+ priv.snap_heights[n] = (FT_Short)cpriv->snap_heights[n];
+
+ priv.force_bold = cpriv->force_bold;
+ priv.language_group = cpriv->language_group;
+ priv.lenIV = cpriv->lenIV;
+ }
+
+ error = funcs->create( cffsize->face->memory, &priv, &globals );
+ if ( !error )
+ cffsize->internal = (FT_Size_Internal)(void*)globals;
+ }
+
+ size->strike_index = 0xFFFFFFFFUL;
+
+ return error;
+ }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_size_select( FT_Size size,
+ FT_ULong strike_index )
+ {
+ CFF_Size cffsize = (CFF_Size)size;
+ PSH_Globals_Funcs funcs;
+
+
+ cffsize->strike_index = strike_index;
+
+ FT_Select_Metrics( size->face, strike_index );
+
+ funcs = cff_size_get_globals_funcs( cffsize );
+
+ if ( funcs )
+ funcs->set_scale( (PSH_Globals)size->internal,
+ size->metrics.x_scale,
+ size->metrics.y_scale,
+ 0, 0 );
+
+ return CFF_Err_Ok;
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_size_request( FT_Size size,
+ FT_Size_Request req )
+ {
+ CFF_Size cffsize = (CFF_Size)size;
+ PSH_Globals_Funcs funcs;
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ if ( FT_HAS_FIXED_SIZES( size->face ) )
+ {
+ CFF_Face cffface = (CFF_Face)size->face;
+ SFNT_Service sfnt = (SFNT_Service)cffface->sfnt;
+ FT_ULong strike_index;
+
+
+ if ( sfnt->set_sbit_strike( cffface, req, &strike_index ) )
+ cffsize->strike_index = 0xFFFFFFFFUL;
+ else
+ return cff_size_select( size, strike_index );
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ FT_Request_Metrics( size->face, req );
+
+ funcs = cff_size_get_globals_funcs( cffsize );
+
+ if ( funcs )
+ funcs->set_scale( (PSH_Globals)size->internal,
+ size->metrics.x_scale,
+ size->metrics.y_scale,
+ 0, 0 );
+
+ return CFF_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SLOT FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ cff_slot_done( FT_GlyphSlot slot )
+ {
+ slot->internal->glyph_hints = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_slot_init( FT_GlyphSlot slot )
+ {
+ CFF_Face face = (CFF_Face)slot->face;
+ CFF_Font font = (CFF_FontRec *)face->extra.data;
+ PSHinter_Service pshinter = (PSHinter_Service)font->pshinter;
+
+
+ if ( pshinter )
+ {
+ FT_Module module;
+
+
+ module = FT_Get_Module( slot->face->driver->root.library,
+ "pshinter" );
+ if ( module )
+ {
+ T2_Hints_Funcs funcs;
+
+
+ funcs = pshinter->get_t2_funcs( module );
+ slot->internal->glyph_hints = (void*)funcs;
+ }
+ }
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FACE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+ static FT_String*
+ cff_strcpy( FT_Memory memory,
+ const FT_String* source )
+ {
+ FT_Error error;
+ FT_String* result;
+
+
+ result = ft_mem_strdup( memory, source, &error );
+
+ return result;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_face_init( FT_Stream stream,
+ FT_Face cffface, /* CFF_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ CFF_Face face = (CFF_Face)cffface;
+ FT_Error error;
+ SFNT_Service sfnt;
+ FT_Service_PsCMaps psnames;
+ PSHinter_Service pshinter;
+ FT_Bool pure_cff = 1;
+ FT_Bool sfnt_format = 0;
+
+
+#if 0
+ FT_FACE_FIND_GLOBAL_SERVICE( face, sfnt, SFNT );
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_NAMES );
+ FT_FACE_FIND_GLOBAL_SERVICE( face, pshinter, POSTSCRIPT_HINTER );
+
+ if ( !sfnt )
+ goto Bad_Format;
+#else
+ sfnt = (SFNT_Service)FT_Get_Module_Interface(
+ cffface->driver->root.library, "sfnt" );
+ if ( !sfnt )
+ goto Bad_Format;
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+
+ pshinter = (PSHinter_Service)FT_Get_Module_Interface(
+ cffface->driver->root.library, "pshinter" );
+#endif
+
+ /* create input stream from resource */
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ /* check whether we have a valid OpenType file */
+ error = sfnt->init_face( stream, face, face_index, num_params, params );
+ if ( !error )
+ {
+ if ( face->format_tag != 0x4F54544FL ) /* `OTTO'; OpenType/CFF font */
+ {
+ FT_TRACE2(( "[not a valid OpenType/CFF font]\n" ));
+ goto Bad_Format;
+ }
+
+ /* if we are performing a simple font format check, exit immediately */
+ if ( face_index < 0 )
+ return CFF_Err_Ok;
+
+ /* UNDOCUMENTED! A CFF in an SFNT can have only a single font. */
+ if ( face_index > 0 )
+ {
+ FT_ERROR(( "cff_face_init: invalid face index\n" ));
+ error = CFF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ sfnt_format = 1;
+
+ /* now, the font can be either an OpenType/CFF font, or an SVG CEF */
+ /* font; in the latter case it doesn't have a `head' table */
+ error = face->goto_table( face, TTAG_head, stream, 0 );
+ if ( !error )
+ {
+ pure_cff = 0;
+
+ /* load font directory */
+ error = sfnt->load_face( stream, face,
+ face_index, num_params, params );
+ if ( error )
+ goto Exit;
+ }
+ else
+ {
+ /* load the `cmap' table explicitly */
+ error = sfnt->load_cmap( face, stream );
+ if ( error )
+ goto Exit;
+
+ /* XXX: we don't load the GPOS table, as OpenType Layout */
+ /* support will be added later to a layout library on top of */
+ /* FreeType 2 */
+ }
+
+ /* now load the CFF part of the file */
+ error = face->goto_table( face, TTAG_CFF, stream, 0 );
+ if ( error )
+ goto Exit;
+ }
+ else
+ {
+ /* rewind to start of file; we are going to load a pure-CFF font */
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+ error = CFF_Err_Ok;
+ }
+
+ /* now load and parse the CFF table in the file */
+ {
+ CFF_Font cff;
+ CFF_FontRecDict dict;
+ FT_Memory memory = cffface->memory;
+ FT_Int32 flags;
+ FT_UInt i;
+
+
+ if ( FT_NEW( cff ) )
+ goto Exit;
+
+ face->extra.data = cff;
+ error = cff_font_load( stream, face_index, cff );
+ if ( error )
+ goto Exit;
+
+ cff->pshinter = pshinter;
+ cff->psnames = (void*)psnames;
+
+ /* Complement the root flags with some interesting information. */
+ /* Note that this is only necessary for pure CFF and CEF fonts; */
+ /* SFNT based fonts use the `name' table instead. */
+
+ cffface->num_glyphs = cff->num_glyphs;
+
+ dict = &cff->top_font.font_dict;
+
+ /* we need the `PSNames' module for CFF and CEF formats */
+ /* which aren't CID-keyed */
+ if ( dict->cid_registry == 0xFFFFU && !psnames )
+ {
+ FT_ERROR(( "cff_face_init:" ));
+ FT_ERROR(( " cannot open CFF & CEF fonts\n" ));
+ FT_ERROR(( " " ));
+ FT_ERROR(( " without the `PSNames' module\n" ));
+ goto Bad_Format;
+ }
+
+ if ( pure_cff )
+ {
+ char* style_name = NULL;
+
+
+ /* set up num_faces */
+ cffface->num_faces = cff->num_faces;
+
+ /* compute number of glyphs */
+ if ( dict->cid_registry != 0xFFFFU )
+ cffface->num_glyphs = dict->cid_count;
+ else
+ cffface->num_glyphs = cff->charstrings_index.count;
+
+ /* set global bbox, as well as EM size */
+ cffface->bbox.xMin = dict->font_bbox.xMin >> 16;
+ cffface->bbox.yMin = dict->font_bbox.yMin >> 16;
+ cffface->bbox.xMax = ( dict->font_bbox.xMax + 0xFFFFU ) >> 16;
+ cffface->bbox.yMax = ( dict->font_bbox.yMax + 0xFFFFU ) >> 16;
+
+ if ( !dict->units_per_em )
+ dict->units_per_em = 1000;
+
+ cffface->units_per_EM = dict->units_per_em;
+
+ cffface->ascender = (FT_Short)( cffface->bbox.yMax );
+ cffface->descender = (FT_Short)( cffface->bbox.yMin );
+
+ cffface->height = (FT_Short)( ( cffface->units_per_EM * 12 ) / 10 );
+ if ( cffface->height < cffface->ascender - cffface->descender )
+ cffface->height = (FT_Short)( cffface->ascender - cffface->descender );
+
+ cffface->underline_position =
+ (FT_Short)( dict->underline_position >> 16 );
+ cffface->underline_thickness =
+ (FT_Short)( dict->underline_thickness >> 16 );
+
+ /* retrieve font family & style name */
+ cffface->family_name = cff_index_get_name( &cff->name_index,
+ face_index );
+
+ if ( cffface->family_name )
+ {
+ char* full = cff_index_get_sid_string( &cff->string_index,
+ dict->full_name,
+ psnames );
+ char* fullp = full;
+ char* family = cffface->family_name;
+ char* family_name = 0;
+
+
+ if ( dict->family_name )
+ {
+ family_name = cff_index_get_sid_string( &cff->string_index,
+ dict->family_name,
+ psnames);
+ if ( family_name )
+ family = family_name;
+ }
+
+ /* We try to extract the style name from the full name. */
+ /* We need to ignore spaces and dashes during the search. */
+ if ( full && family )
+ {
+ while ( *fullp )
+ {
+ /* skip common characters at the start of both strings */
+ if ( *fullp == *family )
+ {
+ family++;
+ fullp++;
+ continue;
+ }
+
+ /* ignore spaces and dashes in full name during comparison */
+ if ( *fullp == ' ' || *fullp == '-' )
+ {
+ fullp++;
+ continue;
+ }
+
+ /* ignore spaces and dashes in family name during comparison */
+ if ( *family == ' ' || *family == '-' )
+ {
+ family++;
+ continue;
+ }
+
+ if ( !*family && *fullp )
+ {
+ /* The full name begins with the same characters as the */
+ /* family name, with spaces and dashes removed. In this */
+ /* case, the remaining string in `fullp' will be used as */
+ /* the style name. */
+ style_name = cff_strcpy( memory, fullp );
+ }
+ break;
+ }
+
+ if ( family_name )
+ FT_FREE( family_name );
+ FT_FREE( full );
+ }
+ }
+ else
+ {
+ char *cid_font_name =
+ cff_index_get_sid_string( &cff->string_index,
+ dict->cid_font_name,
+ psnames );
+
+
+ /* do we have a `/FontName' for a CID-keyed font? */
+ if ( cid_font_name )
+ cffface->family_name = cid_font_name;
+ }
+
+ if ( style_name )
+ cffface->style_name = style_name;
+ else
+ /* assume "Regular" style if we don't know better */
+ cffface->style_name = cff_strcpy( memory, (char *)"Regular" );
+
+ /*******************************************************************/
+ /* */
+ /* Compute face flags. */
+ /* */
+ flags = FT_FACE_FLAG_SCALABLE | /* scalable outlines */
+ FT_FACE_FLAG_HORIZONTAL | /* horizontal data */
+ FT_FACE_FLAG_HINTER; /* has native hinter */
+
+ if ( sfnt_format )
+ flags |= FT_FACE_FLAG_SFNT;
+
+ /* fixed width font? */
+ if ( dict->is_fixed_pitch )
+ flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* XXX: WE DO NOT SUPPORT KERNING METRICS IN THE GPOS TABLE FOR NOW */
+#if 0
+ /* kerning available? */
+ if ( face->kern_pairs )
+ flags |= FT_FACE_FLAG_KERNING;
+#endif
+
+ cffface->face_flags = flags;
+
+ /*******************************************************************/
+ /* */
+ /* Compute style flags. */
+ /* */
+ flags = 0;
+
+ if ( dict->italic_angle )
+ flags |= FT_STYLE_FLAG_ITALIC;
+
+ {
+ char *weight = cff_index_get_sid_string( &cff->string_index,
+ dict->weight,
+ psnames );
+
+
+ if ( weight )
+ if ( !ft_strcmp( weight, "Bold" ) ||
+ !ft_strcmp( weight, "Black" ) )
+ flags |= FT_STYLE_FLAG_BOLD;
+ FT_FREE( weight );
+ }
+
+ /* double check */
+ if ( !(flags & FT_STYLE_FLAG_BOLD) && cffface->style_name )
+ if ( !ft_strncmp( cffface->style_name, "Bold", 4 ) ||
+ !ft_strncmp( cffface->style_name, "Black", 5 ) )
+ flags |= FT_STYLE_FLAG_BOLD;
+
+ cffface->style_flags = flags;
+ }
+ else
+ {
+ if ( !dict->units_per_em )
+ dict->units_per_em = face->root.units_per_EM;
+ }
+
+ /* handle font matrix settings in subfonts (if any) */
+ for ( i = cff->num_subfonts; i > 0; i-- )
+ {
+ CFF_FontRecDict sub = &cff->subfonts[i - 1]->font_dict;
+ CFF_FontRecDict top = &cff->top_font.font_dict;
+
+
+ if ( sub->units_per_em )
+ {
+ FT_Matrix scale;
+
+
+ scale.xx = scale.yy = (FT_Fixed)FT_DivFix( top->units_per_em,
+ sub->units_per_em );
+ scale.xy = scale.yx = 0;
+
+ FT_Matrix_Multiply( &scale, &sub->font_matrix );
+ FT_Vector_Transform( &sub->font_offset, &scale );
+ }
+ else
+ {
+ sub->font_matrix = top->font_matrix;
+ sub->font_offset = top->font_offset;
+ }
+ }
+
+#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ /* CID-keyed CFF fonts don't have glyph names -- the SFNT loader */
+ /* has unset this flag because of the 3.0 `post' table */
+ if ( dict->cid_registry == 0xFFFFU )
+ cffface->face_flags |= FT_FACE_FLAG_GLYPH_NAMES;
+#endif
+
+ /*******************************************************************/
+ /* */
+ /* Compute char maps. */
+ /* */
+
+ /* Try to synthetize a Unicode charmap if there is none available */
+ /* already. If an OpenType font contains a Unicode "cmap", we */
+ /* will use it, whatever be in the CFF part of the file. */
+ {
+ FT_CharMapRec cmaprec;
+ FT_CharMap cmap;
+ FT_UInt nn;
+ CFF_Encoding encoding = &cff->encoding;
+
+
+ for ( nn = 0; nn < (FT_UInt)cffface->num_charmaps; nn++ )
+ {
+ cmap = cffface->charmaps[nn];
+
+ /* Windows Unicode (3,1)? */
+ if ( cmap->platform_id == 3 && cmap->encoding_id == 1 )
+ goto Skip_Unicode;
+
+ /* Deprecated Unicode platform id? */
+ if ( cmap->platform_id == 0 )
+ goto Skip_Unicode; /* Standard Unicode (deprecated) */
+ }
+
+ /* since CID-keyed fonts don't contain glyph names, we can't */
+ /* construct a cmap */
+ if ( pure_cff && cff->top_font.font_dict.cid_registry != 0xFFFFU )
+ goto Exit;
+
+ /* we didn't find a Unicode charmap -- synthetize one */
+ cmaprec.face = cffface;
+ cmaprec.platform_id = 3;
+ cmaprec.encoding_id = 1;
+ cmaprec.encoding = FT_ENCODING_UNICODE;
+
+ nn = (FT_UInt)cffface->num_charmaps;
+
+ FT_CMap_New( &cff_cmap_unicode_class_rec, NULL, &cmaprec, NULL );
+
+ /* if no Unicode charmap was previously selected, select this one */
+ if ( cffface->charmap == NULL && nn != (FT_UInt)cffface->num_charmaps )
+ cffface->charmap = cffface->charmaps[nn];
+
+ Skip_Unicode:
+ if ( encoding->count > 0 )
+ {
+ FT_CMap_Class clazz;
+
+
+ cmaprec.face = cffface;
+ cmaprec.platform_id = 7; /* Adobe platform id */
+
+ if ( encoding->offset == 0 )
+ {
+ cmaprec.encoding_id = TT_ADOBE_ID_STANDARD;
+ cmaprec.encoding = FT_ENCODING_ADOBE_STANDARD;
+ clazz = &cff_cmap_encoding_class_rec;
+ }
+ else if ( encoding->offset == 1 )
+ {
+ cmaprec.encoding_id = TT_ADOBE_ID_EXPERT;
+ cmaprec.encoding = FT_ENCODING_ADOBE_EXPERT;
+ clazz = &cff_cmap_encoding_class_rec;
+ }
+ else
+ {
+ cmaprec.encoding_id = TT_ADOBE_ID_CUSTOM;
+ cmaprec.encoding = FT_ENCODING_ADOBE_CUSTOM;
+ clazz = &cff_cmap_encoding_class_rec;
+ }
+
+ FT_CMap_New( clazz, NULL, &cmaprec, NULL );
+ }
+ }
+ }
+
+ Exit:
+ return error;
+
+ Bad_Format:
+ error = CFF_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cff_face_done( FT_Face cffface ) /* CFF_Face */
+ {
+ CFF_Face face = (CFF_Face)cffface;
+ FT_Memory memory = cffface->memory;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+
+ if ( sfnt )
+ sfnt->done_face( face );
+
+ {
+ CFF_Font cff = (CFF_Font)face->extra.data;
+
+
+ if ( cff )
+ {
+ cff_font_done( cff );
+ FT_FREE( face->extra.data );
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_driver_init( FT_Module module )
+ {
+ FT_UNUSED( module );
+
+ return CFF_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cff_driver_done( FT_Module module )
+ {
+ FT_UNUSED( module );
+ }
+
+
+/* END */
diff --git a/src/cff/cffobjs.h b/src/cff/cffobjs.h
new file mode 100644
index 0000000..f18b5d9
--- /dev/null
+++ b/src/cff/cffobjs.h
@@ -0,0 +1,165 @@
+/***************************************************************************/
+/* */
+/* cffobjs.h */
+/* */
+/* OpenType objects manager (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFOBJS_H__
+#define __CFFOBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include "cfftypes.h"
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_Driver */
+ /* */
+ /* <Description> */
+ /* A handle to an OpenType driver object. */
+ /* */
+ typedef struct CFF_DriverRec_* CFF_Driver;
+
+ typedef TT_Face CFF_Face;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_Size */
+ /* */
+ /* <Description> */
+ /* A handle to an OpenType size object. */
+ /* */
+ typedef struct CFF_SizeRec_
+ {
+ FT_SizeRec root;
+ FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */
+
+ } CFF_SizeRec, *CFF_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CFF_GlyphSlot */
+ /* */
+ /* <Description> */
+ /* A handle to an OpenType glyph slot object. */
+ /* */
+ typedef struct CFF_GlyphSlotRec_
+ {
+ FT_GlyphSlotRec root;
+
+ FT_Bool hint;
+ FT_Bool scaled;
+
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+
+ } CFF_GlyphSlotRec, *CFF_GlyphSlot;
+
+
+
+ /*************************************************************************/
+ /* */
+ /* Subglyph transformation record. */
+ /* */
+ typedef struct CFF_Transform_
+ {
+ FT_Fixed xx, xy; /* transformation matrix coefficients */
+ FT_Fixed yx, yy;
+ FT_F26Dot6 ox, oy; /* offsets */
+
+ } CFF_Transform;
+
+
+ /***********************************************************************/
+ /* */
+ /* TrueType driver class. */
+ /* */
+ typedef struct CFF_DriverRec_
+ {
+ FT_DriverRec root;
+ void* extension_component;
+
+ } CFF_DriverRec;
+
+
+ FT_LOCAL( FT_Error )
+ cff_size_init( FT_Size size ); /* CFF_Size */
+
+ FT_LOCAL( void )
+ cff_size_done( FT_Size size ); /* CFF_Size */
+
+ FT_LOCAL( FT_Error )
+ cff_size_request( FT_Size size,
+ FT_Size_Request req );
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL( FT_Error )
+ cff_size_select( FT_Size size,
+ FT_ULong strike_index );
+
+#endif
+
+ FT_LOCAL( void )
+ cff_slot_done( FT_GlyphSlot slot );
+
+ FT_LOCAL( FT_Error )
+ cff_slot_init( FT_GlyphSlot slot );
+
+
+ /*************************************************************************/
+ /* */
+ /* Face functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ cff_face_init( FT_Stream stream,
+ FT_Face face, /* CFF_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ cff_face_done( FT_Face face ); /* CFF_Face */
+
+
+ /*************************************************************************/
+ /* */
+ /* Driver functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ cff_driver_init( FT_Module module );
+
+ FT_LOCAL( void )
+ cff_driver_done( FT_Module module );
+
+
+FT_END_HEADER
+
+#endif /* __CFFOBJS_H__ */
+
+
+/* END */
diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c
new file mode 100644
index 0000000..41af6a3
--- /dev/null
+++ b/src/cff/cffparse.c
@@ -0,0 +1,688 @@
+/***************************************************************************/
+/* */
+/* cffparse.c */
+/* */
+/* CFF token stream parser (body) */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "cffparse.h"
+#include FT_INTERNAL_STREAM_H
+
+#include "cfferrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cffparse
+
+
+ enum
+ {
+ cff_kind_none = 0,
+ cff_kind_num,
+ cff_kind_fixed,
+ cff_kind_fixed_thousand,
+ cff_kind_string,
+ cff_kind_bool,
+ cff_kind_delta,
+ cff_kind_callback,
+
+ cff_kind_max /* do not remove */
+ };
+
+
+ /* now generate handlers for the most simple fields */
+ typedef FT_Error (*CFF_Field_Reader)( CFF_Parser parser );
+
+ typedef struct CFF_Field_Handler_
+ {
+ int kind;
+ int code;
+ FT_UInt offset;
+ FT_Byte size;
+ CFF_Field_Reader reader;
+ FT_UInt array_max;
+ FT_UInt count_offset;
+
+ } CFF_Field_Handler;
+
+
+ FT_LOCAL_DEF( void )
+ cff_parser_init( CFF_Parser parser,
+ FT_UInt code,
+ void* object )
+ {
+ FT_MEM_ZERO( parser, sizeof ( *parser ) );
+
+ parser->top = parser->stack;
+ parser->object_code = code;
+ parser->object = object;
+ }
+
+
+ /* read an integer */
+ static FT_Long
+ cff_parse_integer( FT_Byte* start,
+ FT_Byte* limit )
+ {
+ FT_Byte* p = start;
+ FT_Int v = *p++;
+ FT_Long val = 0;
+
+
+ if ( v == 28 )
+ {
+ if ( p + 2 > limit )
+ goto Bad;
+
+ val = (FT_Short)( ( (FT_Int)p[0] << 8 ) | p[1] );
+ p += 2;
+ }
+ else if ( v == 29 )
+ {
+ if ( p + 4 > limit )
+ goto Bad;
+
+ val = ( (FT_Long)p[0] << 24 ) |
+ ( (FT_Long)p[1] << 16 ) |
+ ( (FT_Long)p[2] << 8 ) |
+ p[3];
+ p += 4;
+ }
+ else if ( v < 247 )
+ {
+ val = v - 139;
+ }
+ else if ( v < 251 )
+ {
+ if ( p + 1 > limit )
+ goto Bad;
+
+ val = ( v - 247 ) * 256 + p[0] + 108;
+ p++;
+ }
+ else
+ {
+ if ( p + 1 > limit )
+ goto Bad;
+
+ val = -( v - 251 ) * 256 - p[0] - 108;
+ p++;
+ }
+
+ Exit:
+ return val;
+
+ Bad:
+ val = 0;
+ goto Exit;
+ }
+
+
+ /* read a real */
+ static FT_Fixed
+ cff_parse_real( FT_Byte* start,
+ FT_Byte* limit,
+ FT_Int power_ten )
+ {
+ FT_Byte* p = start;
+ FT_Long num, divider, result, exponent;
+ FT_Int sign = 0, exponent_sign = 0;
+ FT_UInt nib;
+ FT_UInt phase;
+
+
+ result = 0;
+ num = 0;
+ divider = 1;
+
+ /* first of all, read the integer part */
+ phase = 4;
+
+ for (;;)
+ {
+ /* If we entered this iteration with phase == 4, we need to */
+ /* read a new byte. This also skips past the initial 0x1E. */
+ if ( phase )
+ {
+ p++;
+
+ /* Make sure we don't read past the end. */
+ if ( p >= limit )
+ goto Bad;
+ }
+
+ /* Get the nibble. */
+ nib = ( p[0] >> phase ) & 0xF;
+ phase = 4 - phase;
+
+ if ( nib == 0xE )
+ sign = 1;
+ else if ( nib > 9 )
+ break;
+ else
+ result = result * 10 + nib;
+ }
+
+ /* read decimal part, if any */
+ if ( nib == 0xa )
+ for (;;)
+ {
+ /* If we entered this iteration with phase == 4, we need */
+ /* to read a new byte. */
+ if ( phase )
+ {
+ p++;
+
+ /* Make sure we don't read past the end. */
+ if ( p >= limit )
+ goto Bad;
+ }
+
+ /* Get the nibble. */
+ nib = ( p[0] >> phase ) & 0xF;
+ phase = 4 - phase;
+ if ( nib >= 10 )
+ break;
+
+ if ( divider < 10000000L )
+ {
+ num = num * 10 + nib;
+ divider *= 10;
+ }
+ }
+
+ /* read exponent, if any */
+ if ( nib == 12 )
+ {
+ exponent_sign = 1;
+ nib = 11;
+ }
+
+ if ( nib == 11 )
+ {
+ exponent = 0;
+
+ for (;;)
+ {
+ /* If we entered this iteration with phase == 4, we need */
+ /* to read a new byte. */
+ if ( phase )
+ {
+ p++;
+
+ /* Make sure we don't read past the end. */
+ if ( p >= limit )
+ goto Bad;
+ }
+
+ /* Get the nibble. */
+ nib = ( p[0] >> phase ) & 0xF;
+ phase = 4 - phase;
+ if ( nib >= 10 )
+ break;
+
+ exponent = exponent * 10 + nib;
+ }
+
+ if ( exponent_sign )
+ exponent = -exponent;
+
+ power_ten += (FT_Int)exponent;
+ }
+
+ /* raise to power of ten if needed */
+ while ( power_ten > 0 )
+ {
+ result = result * 10;
+ num = num * 10;
+
+ power_ten--;
+ }
+
+ while ( power_ten < 0 )
+ {
+ result = result / 10;
+ divider = divider * 10;
+
+ power_ten++;
+ }
+
+ /* Move the integer part into the high 16 bits. */
+ result <<= 16;
+
+ /* Place the decimal part into the low 16 bits. */
+ if ( num )
+ result |= FT_DivFix( num, divider );
+
+ if ( sign )
+ result = -result;
+
+ Exit:
+ return result;
+
+ Bad:
+ result = 0;
+ goto Exit;
+ }
+
+
+ /* read a number, either integer or real */
+ static FT_Long
+ cff_parse_num( FT_Byte** d )
+ {
+ return ( **d == 30 ? ( cff_parse_real ( d[0], d[1], 0 ) >> 16 )
+ : cff_parse_integer( d[0], d[1] ) );
+ }
+
+
+ /* read a floating point number, either integer or real */
+ static FT_Fixed
+ cff_parse_fixed( FT_Byte** d )
+ {
+ return ( **d == 30 ? cff_parse_real ( d[0], d[1], 0 )
+ : cff_parse_integer( d[0], d[1] ) << 16 );
+ }
+
+ /* read a floating point number, either integer or real, */
+ /* but return 1000 times the number read in. */
+ static FT_Fixed
+ cff_parse_fixed_thousand( FT_Byte** d )
+ {
+ return **d ==
+ 30 ? cff_parse_real ( d[0], d[1], 3 )
+ : (FT_Fixed)FT_MulFix( cff_parse_integer( d[0], d[1] ) << 16, 1000 );
+ }
+
+ static FT_Error
+ cff_parse_font_matrix( CFF_Parser parser )
+ {
+ CFF_FontRecDict dict = (CFF_FontRecDict)parser->object;
+ FT_Matrix* matrix = &dict->font_matrix;
+ FT_Vector* offset = &dict->font_offset;
+ FT_UShort* upm = &dict->units_per_em;
+ FT_Byte** data = parser->stack;
+ FT_Error error;
+ FT_Fixed temp;
+
+
+ error = CFF_Err_Stack_Underflow;
+
+ if ( parser->top >= parser->stack + 6 )
+ {
+ matrix->xx = cff_parse_fixed_thousand( data++ );
+ matrix->yx = cff_parse_fixed_thousand( data++ );
+ matrix->xy = cff_parse_fixed_thousand( data++ );
+ matrix->yy = cff_parse_fixed_thousand( data++ );
+ offset->x = cff_parse_fixed_thousand( data++ );
+ offset->y = cff_parse_fixed_thousand( data );
+
+ temp = FT_ABS( matrix->yy );
+
+ *upm = (FT_UShort)FT_DivFix( 0x10000L, FT_DivFix( temp, 1000 ) );
+
+ if ( temp != 0x10000L )
+ {
+ matrix->xx = FT_DivFix( matrix->xx, temp );
+ matrix->yx = FT_DivFix( matrix->yx, temp );
+ matrix->xy = FT_DivFix( matrix->xy, temp );
+ matrix->yy = FT_DivFix( matrix->yy, temp );
+ offset->x = FT_DivFix( offset->x, temp );
+ offset->y = FT_DivFix( offset->y, temp );
+ }
+
+ /* note that the offsets must be expressed in integer font units */
+ offset->x >>= 16;
+ offset->y >>= 16;
+
+ error = CFF_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ cff_parse_font_bbox( CFF_Parser parser )
+ {
+ CFF_FontRecDict dict = (CFF_FontRecDict)parser->object;
+ FT_BBox* bbox = &dict->font_bbox;
+ FT_Byte** data = parser->stack;
+ FT_Error error;
+
+
+ error = CFF_Err_Stack_Underflow;
+
+ if ( parser->top >= parser->stack + 4 )
+ {
+ bbox->xMin = FT_RoundFix( cff_parse_fixed( data++ ) );
+ bbox->yMin = FT_RoundFix( cff_parse_fixed( data++ ) );
+ bbox->xMax = FT_RoundFix( cff_parse_fixed( data++ ) );
+ bbox->yMax = FT_RoundFix( cff_parse_fixed( data ) );
+ error = CFF_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ cff_parse_private_dict( CFF_Parser parser )
+ {
+ CFF_FontRecDict dict = (CFF_FontRecDict)parser->object;
+ FT_Byte** data = parser->stack;
+ FT_Error error;
+
+
+ error = CFF_Err_Stack_Underflow;
+
+ if ( parser->top >= parser->stack + 2 )
+ {
+ dict->private_size = cff_parse_num( data++ );
+ dict->private_offset = cff_parse_num( data );
+ error = CFF_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ cff_parse_cid_ros( CFF_Parser parser )
+ {
+ CFF_FontRecDict dict = (CFF_FontRecDict)parser->object;
+ FT_Byte** data = parser->stack;
+ FT_Error error;
+
+
+ error = CFF_Err_Stack_Underflow;
+
+ if ( parser->top >= parser->stack + 3 )
+ {
+ dict->cid_registry = (FT_UInt)cff_parse_num ( data++ );
+ dict->cid_ordering = (FT_UInt)cff_parse_num ( data++ );
+ dict->cid_supplement = (FT_ULong)cff_parse_num( data );
+ error = CFF_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+#define CFF_FIELD_NUM( code, name ) \
+ CFF_FIELD( code, name, cff_kind_num )
+#define CFF_FIELD_FIXED( code, name ) \
+ CFF_FIELD( code, name, cff_kind_fixed )
+#define CFF_FIELD_FIXED_1000( code, name ) \
+ CFF_FIELD( code, name, cff_kind_fixed_thousand )
+#define CFF_FIELD_STRING( code, name ) \
+ CFF_FIELD( code, name, cff_kind_string )
+#define CFF_FIELD_BOOL( code, name ) \
+ CFF_FIELD( code, name, cff_kind_bool )
+#define CFF_FIELD_DELTA( code, name, max ) \
+ CFF_FIELD( code, name, cff_kind_delta )
+
+#define CFF_FIELD_CALLBACK( code, name ) \
+ { \
+ cff_kind_callback, \
+ code | CFFCODE, \
+ 0, 0, \
+ cff_parse_ ## name, \
+ 0, 0 \
+ },
+
+#undef CFF_FIELD
+#define CFF_FIELD( code, name, kind ) \
+ { \
+ kind, \
+ code | CFFCODE, \
+ FT_FIELD_OFFSET( name ), \
+ FT_FIELD_SIZE( name ), \
+ 0, 0, 0 \
+ },
+
+#undef CFF_FIELD_DELTA
+#define CFF_FIELD_DELTA( code, name, max ) \
+ { \
+ cff_kind_delta, \
+ code | CFFCODE, \
+ FT_FIELD_OFFSET( name ), \
+ FT_FIELD_SIZE_DELTA( name ), \
+ 0, \
+ max, \
+ FT_FIELD_OFFSET( num_ ## name ) \
+ },
+
+#define CFFCODE_TOPDICT 0x1000
+#define CFFCODE_PRIVATE 0x2000
+
+ static const CFF_Field_Handler cff_field_handlers[] =
+ {
+
+#include "cfftoken.h"
+
+ { 0, 0, 0, 0, 0, 0, 0 }
+ };
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cff_parser_run( CFF_Parser parser,
+ FT_Byte* start,
+ FT_Byte* limit )
+ {
+ FT_Byte* p = start;
+ FT_Error error = CFF_Err_Ok;
+
+
+ parser->top = parser->stack;
+ parser->start = start;
+ parser->limit = limit;
+ parser->cursor = start;
+
+ while ( p < limit )
+ {
+ FT_UInt v = *p;
+
+
+ if ( v >= 27 && v != 31 )
+ {
+ /* it's a number; we will push its position on the stack */
+ if ( parser->top - parser->stack >= CFF_MAX_STACK_DEPTH )
+ goto Stack_Overflow;
+
+ *parser->top ++ = p;
+
+ /* now, skip it */
+ if ( v == 30 )
+ {
+ /* skip real number */
+ p++;
+ for (;;)
+ {
+ if ( p >= limit )
+ goto Syntax_Error;
+ v = p[0] >> 4;
+ if ( v == 15 )
+ break;
+ v = p[0] & 0xF;
+ if ( v == 15 )
+ break;
+ p++;
+ }
+ }
+ else if ( v == 28 )
+ p += 2;
+ else if ( v == 29 )
+ p += 4;
+ else if ( v > 246 )
+ p += 1;
+ }
+ else
+ {
+ /* This is not a number, hence it's an operator. Compute its code */
+ /* and look for it in our current list. */
+
+ FT_UInt code;
+ FT_UInt num_args = (FT_UInt)
+ ( parser->top - parser->stack );
+ const CFF_Field_Handler* field;
+
+
+ *parser->top = p;
+ code = v;
+ if ( v == 12 )
+ {
+ /* two byte operator */
+ p++;
+ if ( p >= limit )
+ goto Syntax_Error;
+
+ code = 0x100 | p[0];
+ }
+ code = code | parser->object_code;
+
+ for ( field = cff_field_handlers; field->kind; field++ )
+ {
+ if ( field->code == (FT_Int)code )
+ {
+ /* we found our field's handler; read it */
+ FT_Long val;
+ FT_Byte* q = (FT_Byte*)parser->object + field->offset;
+
+
+ /* check that we have enough arguments -- except for */
+ /* delta encoded arrays, which can be empty */
+ if ( field->kind != cff_kind_delta && num_args < 1 )
+ goto Stack_Underflow;
+
+ switch ( field->kind )
+ {
+ case cff_kind_bool:
+ case cff_kind_string:
+ case cff_kind_num:
+ val = cff_parse_num( parser->stack );
+ goto Store_Number;
+
+ case cff_kind_fixed:
+ val = cff_parse_fixed( parser->stack );
+ goto Store_Number;
+
+ case cff_kind_fixed_thousand:
+ val = cff_parse_fixed_thousand( parser->stack );
+
+ Store_Number:
+ switch ( field->size )
+ {
+ case (8 / FT_CHAR_BIT):
+ *(FT_Byte*)q = (FT_Byte)val;
+ break;
+
+ case (16 / FT_CHAR_BIT):
+ *(FT_Short*)q = (FT_Short)val;
+ break;
+
+ case (32 / FT_CHAR_BIT):
+ *(FT_Int32*)q = (FT_Int)val;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_Long*)q = val;
+ }
+ break;
+
+ case cff_kind_delta:
+ {
+ FT_Byte* qcount = (FT_Byte*)parser->object +
+ field->count_offset;
+
+ FT_Byte** data = parser->stack;
+
+
+ if ( num_args > field->array_max )
+ num_args = field->array_max;
+
+ /* store count */
+ *qcount = (FT_Byte)num_args;
+
+ val = 0;
+ while ( num_args > 0 )
+ {
+ val += cff_parse_num( data++ );
+ switch ( field->size )
+ {
+ case (8 / FT_CHAR_BIT):
+ *(FT_Byte*)q = (FT_Byte)val;
+ break;
+
+ case (16 / FT_CHAR_BIT):
+ *(FT_Short*)q = (FT_Short)val;
+ break;
+
+ case (32 / FT_CHAR_BIT):
+ *(FT_Int32*)q = (FT_Int)val;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_Long*)q = val;
+ }
+
+ q += field->size;
+ num_args--;
+ }
+ }
+ break;
+
+ default: /* callback */
+ error = field->reader( parser );
+ if ( error )
+ goto Exit;
+ }
+ goto Found;
+ }
+ }
+
+ /* this is an unknown operator, or it is unsupported; */
+ /* we will ignore it for now. */
+
+ Found:
+ /* clear stack */
+ parser->top = parser->stack;
+ }
+ p++;
+ }
+
+ Exit:
+ return error;
+
+ Stack_Overflow:
+ error = CFF_Err_Invalid_Argument;
+ goto Exit;
+
+ Stack_Underflow:
+ error = CFF_Err_Invalid_Argument;
+ goto Exit;
+
+ Syntax_Error:
+ error = CFF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+
+/* END */
diff --git a/src/cff/cffparse.h b/src/cff/cffparse.h
new file mode 100644
index 0000000..8f3fa58
--- /dev/null
+++ b/src/cff/cffparse.h
@@ -0,0 +1,69 @@
+/***************************************************************************/
+/* */
+/* cffparse.h */
+/* */
+/* CFF token stream parser (specification) */
+/* */
+/* Copyright 1996-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFF_PARSE_H__
+#define __CFF_PARSE_H__
+
+
+#include <ft2build.h>
+#include "cfftypes.h"
+#include FT_INTERNAL_OBJECTS_H
+
+
+FT_BEGIN_HEADER
+
+
+#define CFF_MAX_STACK_DEPTH 96
+
+#define CFF_CODE_TOPDICT 0x1000
+#define CFF_CODE_PRIVATE 0x2000
+
+
+ typedef struct CFF_ParserRec_
+ {
+ FT_Byte* start;
+ FT_Byte* limit;
+ FT_Byte* cursor;
+
+ FT_Byte* stack[CFF_MAX_STACK_DEPTH + 1];
+ FT_Byte** top;
+
+ FT_UInt object_code;
+ void* object;
+
+ } CFF_ParserRec, *CFF_Parser;
+
+
+ FT_LOCAL( void )
+ cff_parser_init( CFF_Parser parser,
+ FT_UInt code,
+ void* object );
+
+ FT_LOCAL( FT_Error )
+ cff_parser_run( CFF_Parser parser,
+ FT_Byte* start,
+ FT_Byte* limit );
+
+
+FT_END_HEADER
+
+
+#endif /* __CFF_PARSE_H__ */
+
+
+/* END */
diff --git a/src/cff/cfftoken.h b/src/cff/cfftoken.h
new file mode 100644
index 0000000..6bb27d5
--- /dev/null
+++ b/src/cff/cfftoken.h
@@ -0,0 +1,97 @@
+/***************************************************************************/
+/* */
+/* cfftoken.h */
+/* */
+/* CFF token definitions (specification only). */
+/* */
+/* Copyright 1996-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE CFF_FontRecDictRec
+
+#undef CFFCODE
+#define CFFCODE CFFCODE_TOPDICT
+
+ CFF_FIELD_STRING ( 0, version )
+ CFF_FIELD_STRING ( 1, notice )
+ CFF_FIELD_STRING ( 0x100, copyright )
+ CFF_FIELD_STRING ( 2, full_name )
+ CFF_FIELD_STRING ( 3, family_name )
+ CFF_FIELD_STRING ( 4, weight )
+ CFF_FIELD_BOOL ( 0x101, is_fixed_pitch )
+ CFF_FIELD_FIXED ( 0x102, italic_angle )
+ CFF_FIELD_FIXED ( 0x103, underline_position )
+ CFF_FIELD_FIXED ( 0x104, underline_thickness )
+ CFF_FIELD_NUM ( 0x105, paint_type )
+ CFF_FIELD_NUM ( 0x106, charstring_type )
+ CFF_FIELD_CALLBACK( 0x107, font_matrix )
+ CFF_FIELD_NUM ( 13, unique_id )
+ CFF_FIELD_CALLBACK( 5, font_bbox )
+ CFF_FIELD_NUM ( 0x108, stroke_width )
+ CFF_FIELD_NUM ( 15, charset_offset )
+ CFF_FIELD_NUM ( 16, encoding_offset )
+ CFF_FIELD_NUM ( 17, charstrings_offset )
+ CFF_FIELD_CALLBACK( 18, private_dict )
+ CFF_FIELD_NUM ( 0x114, synthetic_base )
+ CFF_FIELD_STRING ( 0x115, embedded_postscript )
+
+#if 0
+ CFF_FIELD_STRING ( 0x116, base_font_name )
+ CFF_FIELD_DELTA ( 0x117, base_font_blend, 16 )
+ CFF_FIELD_CALLBACK( 0x118, multiple_master )
+ CFF_FIELD_CALLBACK( 0x119, blend_axis_types )
+#endif
+
+ CFF_FIELD_CALLBACK( 0x11E, cid_ros )
+ CFF_FIELD_NUM ( 0x11F, cid_font_version )
+ CFF_FIELD_NUM ( 0x120, cid_font_revision )
+ CFF_FIELD_NUM ( 0x121, cid_font_type )
+ CFF_FIELD_NUM ( 0x122, cid_count )
+ CFF_FIELD_NUM ( 0x123, cid_uid_base )
+ CFF_FIELD_NUM ( 0x124, cid_fd_array_offset )
+ CFF_FIELD_NUM ( 0x125, cid_fd_select_offset )
+ CFF_FIELD_STRING ( 0x126, cid_font_name )
+
+#if 0
+ CFF_FIELD_NUM ( 0x127, chameleon )
+#endif
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE CFF_PrivateRec
+#undef CFFCODE
+#define CFFCODE CFFCODE_PRIVATE
+
+ CFF_FIELD_DELTA ( 6, blue_values, 14 )
+ CFF_FIELD_DELTA ( 7, other_blues, 10 )
+ CFF_FIELD_DELTA ( 8, family_blues, 14 )
+ CFF_FIELD_DELTA ( 9, family_other_blues, 10 )
+ CFF_FIELD_FIXED_1000( 0x109, blue_scale )
+ CFF_FIELD_NUM ( 0x10A, blue_shift )
+ CFF_FIELD_NUM ( 0x10B, blue_fuzz )
+ CFF_FIELD_NUM ( 10, standard_width )
+ CFF_FIELD_NUM ( 11, standard_height )
+ CFF_FIELD_DELTA ( 0x10C, snap_widths, 13 )
+ CFF_FIELD_DELTA ( 0x10D, snap_heights, 13 )
+ CFF_FIELD_BOOL ( 0x10E, force_bold )
+ CFF_FIELD_FIXED ( 0x10F, force_bold_threshold )
+ CFF_FIELD_NUM ( 0x110, lenIV )
+ CFF_FIELD_NUM ( 0x111, language_group )
+ CFF_FIELD_FIXED ( 0x112, expansion_factor )
+ CFF_FIELD_NUM ( 0x113, initial_random_seed )
+ CFF_FIELD_NUM ( 19, local_subrs_offset )
+ CFF_FIELD_NUM ( 20, default_width )
+ CFF_FIELD_NUM ( 21, nominal_width )
+
+
+/* END */
diff --git a/src/cff/cfftypes.h b/src/cff/cfftypes.h
new file mode 100644
index 0000000..306e5aa
--- /dev/null
+++ b/src/cff/cfftypes.h
@@ -0,0 +1,270 @@
+/***************************************************************************/
+/* */
+/* cfftypes.h */
+/* */
+/* Basic OpenType/CFF type definitions and interface (specification */
+/* only). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CFFTYPES_H__
+#define __CFFTYPES_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* CFF_IndexRec */
+ /* */
+ /* <Description> */
+ /* A structure used to model a CFF Index table. */
+ /* */
+ /* <Fields> */
+ /* stream :: The source input stream. */
+ /* */
+ /* start :: The position of the first index byte in the */
+ /* input stream. */
+ /* */
+ /* count :: The number of elements in the index. */
+ /* */
+ /* off_size :: The size in bytes of object offsets in index. */
+ /* */
+ /* data_offset :: The position of first data byte in the index's */
+ /* bytes. */
+ /* */
+ /* data_size :: The size of the data table in this index. */
+ /* */
+ /* offsets :: A table of element offsets in the index. Must be */
+ /* loaded explicitly. */
+ /* */
+ /* bytes :: If the index is loaded in memory, its bytes. */
+ /* */
+ typedef struct CFF_IndexRec_
+ {
+ FT_Stream stream;
+ FT_ULong start;
+ FT_UInt count;
+ FT_Byte off_size;
+ FT_ULong data_offset;
+ FT_ULong data_size;
+
+ FT_ULong* offsets;
+ FT_Byte* bytes;
+
+ } CFF_IndexRec, *CFF_Index;
+
+
+ typedef struct CFF_EncodingRec_
+ {
+ FT_UInt format;
+ FT_ULong offset;
+
+ FT_UInt count;
+ FT_UShort sids [256]; /* avoid dynamic allocations */
+ FT_UShort codes[256];
+
+ } CFF_EncodingRec, *CFF_Encoding;
+
+
+ typedef struct CFF_CharsetRec_
+ {
+
+ FT_UInt format;
+ FT_ULong offset;
+
+ FT_UShort* sids;
+ FT_UShort* cids; /* the inverse mapping of `sids'; only needed */
+ /* for CID-keyed fonts */
+ FT_UInt max_cid;
+ FT_UInt num_glyphs;
+
+ } CFF_CharsetRec, *CFF_Charset;
+
+
+ typedef struct CFF_FontRecDictRec_
+ {
+ FT_UInt version;
+ FT_UInt notice;
+ FT_UInt copyright;
+ FT_UInt full_name;
+ FT_UInt family_name;
+ FT_UInt weight;
+ FT_Bool is_fixed_pitch;
+ FT_Fixed italic_angle;
+ FT_Fixed underline_position;
+ FT_Fixed underline_thickness;
+ FT_Int paint_type;
+ FT_Int charstring_type;
+ FT_Matrix font_matrix;
+ FT_UShort units_per_em;
+ FT_Vector font_offset;
+ FT_ULong unique_id;
+ FT_BBox font_bbox;
+ FT_Pos stroke_width;
+ FT_ULong charset_offset;
+ FT_ULong encoding_offset;
+ FT_ULong charstrings_offset;
+ FT_ULong private_offset;
+ FT_ULong private_size;
+ FT_Long synthetic_base;
+ FT_UInt embedded_postscript;
+
+ /* these should only be used for the top-level font dictionary */
+ FT_UInt cid_registry;
+ FT_UInt cid_ordering;
+ FT_ULong cid_supplement;
+
+ FT_Long cid_font_version;
+ FT_Long cid_font_revision;
+ FT_Long cid_font_type;
+ FT_ULong cid_count;
+ FT_ULong cid_uid_base;
+ FT_ULong cid_fd_array_offset;
+ FT_ULong cid_fd_select_offset;
+ FT_UInt cid_font_name;
+
+ } CFF_FontRecDictRec, *CFF_FontRecDict;
+
+
+ typedef struct CFF_PrivateRec_
+ {
+ FT_Byte num_blue_values;
+ FT_Byte num_other_blues;
+ FT_Byte num_family_blues;
+ FT_Byte num_family_other_blues;
+
+ FT_Pos blue_values[14];
+ FT_Pos other_blues[10];
+ FT_Pos family_blues[14];
+ FT_Pos family_other_blues[10];
+
+ FT_Fixed blue_scale;
+ FT_Pos blue_shift;
+ FT_Pos blue_fuzz;
+ FT_Pos standard_width;
+ FT_Pos standard_height;
+
+ FT_Byte num_snap_widths;
+ FT_Byte num_snap_heights;
+ FT_Pos snap_widths[13];
+ FT_Pos snap_heights[13];
+ FT_Bool force_bold;
+ FT_Fixed force_bold_threshold;
+ FT_Int lenIV;
+ FT_Int language_group;
+ FT_Fixed expansion_factor;
+ FT_Long initial_random_seed;
+ FT_ULong local_subrs_offset;
+ FT_Pos default_width;
+ FT_Pos nominal_width;
+
+ } CFF_PrivateRec, *CFF_Private;
+
+
+ typedef struct CFF_FDSelectRec_
+ {
+ FT_Byte format;
+ FT_UInt range_count;
+
+ /* that's the table, taken from the file `as is' */
+ FT_Byte* data;
+ FT_UInt data_size;
+
+ /* small cache for format 3 only */
+ FT_UInt cache_first;
+ FT_UInt cache_count;
+ FT_Byte cache_fd;
+
+ } CFF_FDSelectRec, *CFF_FDSelect;
+
+
+ /* A SubFont packs a font dict and a private dict together. They are */
+ /* needed to support CID-keyed CFF fonts. */
+ typedef struct CFF_SubFontRec_
+ {
+ CFF_FontRecDictRec font_dict;
+ CFF_PrivateRec private_dict;
+
+ CFF_IndexRec local_subrs_index;
+ FT_UInt num_local_subrs;
+ FT_Byte** local_subrs;
+
+ } CFF_SubFontRec, *CFF_SubFont;
+
+
+ /* maximum number of sub-fonts in a CID-keyed file */
+#define CFF_MAX_CID_FONTS 32
+
+
+ typedef struct CFF_FontRec_
+ {
+ FT_Stream stream;
+ FT_Memory memory;
+ FT_UInt num_faces;
+ FT_UInt num_glyphs;
+
+ FT_Byte version_major;
+ FT_Byte version_minor;
+ FT_Byte header_size;
+ FT_Byte absolute_offsize;
+
+
+ CFF_IndexRec name_index;
+ CFF_IndexRec top_dict_index;
+ CFF_IndexRec string_index;
+ CFF_IndexRec global_subrs_index;
+
+ CFF_EncodingRec encoding;
+ CFF_CharsetRec charset;
+
+ CFF_IndexRec charstrings_index;
+ CFF_IndexRec font_dict_index;
+ CFF_IndexRec private_index;
+ CFF_IndexRec local_subrs_index;
+
+ FT_String* font_name;
+ FT_UInt num_global_subrs;
+ FT_Byte** global_subrs;
+
+ CFF_SubFontRec top_font;
+ FT_UInt num_subfonts;
+ CFF_SubFont subfonts[CFF_MAX_CID_FONTS];
+
+ CFF_FDSelectRec fd_select;
+
+ /* interface to PostScript hinter */
+ void* pshinter;
+
+ /* interface to Postscript Names service */
+ void* psnames;
+
+ /* since version 2.3.0 */
+ PS_FontInfoRec* font_info; /* font info dictionary */
+
+ } CFF_FontRec, *CFF_Font;
+
+
+FT_END_HEADER
+
+#endif /* __CFFTYPES_H__ */
+
+
+/* END */
diff --git a/src/cff/module.mk b/src/cff/module.mk
new file mode 100644
index 0000000..0474e37
--- /dev/null
+++ b/src/cff/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 CFF module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += CFF_DRIVER
+
+define CFF_DRIVER
+$(OPEN_DRIVER)cff_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)cff $(ECHO_DRIVER_DESC)OpenType fonts with extension *.otf$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/cff/rules.mk b/src/cff/rules.mk
new file mode 100644
index 0000000..4100c80
--- /dev/null
+++ b/src/cff/rules.mk
@@ -0,0 +1,72 @@
+#
+# FreeType 2 OpenType/CFF driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# OpenType driver directory
+#
+CFF_DIR := $(SRC_DIR)/cff
+
+
+CFF_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(CFF_DIR))
+
+
+# CFF driver sources (i.e., C files)
+#
+CFF_DRV_SRC := $(CFF_DIR)/cffobjs.c \
+ $(CFF_DIR)/cffload.c \
+ $(CFF_DIR)/cffgload.c \
+ $(CFF_DIR)/cffparse.c \
+ $(CFF_DIR)/cffcmap.c \
+ $(CFF_DIR)/cffdrivr.c
+
+# CFF driver headers
+#
+CFF_DRV_H := $(CFF_DRV_SRC:%.c=%.h) \
+ $(CFF_DIR)/cfftoken.h \
+ $(CFF_DIR)/cfftypes.h \
+ $(CFF_DIR)/cfferrs.h
+
+
+# CFF driver object(s)
+#
+# CFF_DRV_OBJ_M is used during `multi' builds
+# CFF_DRV_OBJ_S is used during `single' builds
+#
+CFF_DRV_OBJ_M := $(CFF_DRV_SRC:$(CFF_DIR)/%.c=$(OBJ_DIR)/%.$O)
+CFF_DRV_OBJ_S := $(OBJ_DIR)/cff.$O
+
+# CFF driver source file for single build
+#
+CFF_DRV_SRC_S := $(CFF_DIR)/cff.c
+
+
+# CFF driver - single object
+#
+$(CFF_DRV_OBJ_S): $(CFF_DRV_SRC_S) $(CFF_DRV_SRC) $(FREETYPE_H) $(CFF_DRV_H)
+ $(CFF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CFF_DRV_SRC_S))
+
+
+# CFF driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(CFF_DIR)/%.c $(FREETYPE_H) $(CFF_DRV_H)
+ $(CFF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(CFF_DRV_OBJ_S)
+DRV_OBJS_M += $(CFF_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/cid/Jamfile b/src/cid/Jamfile
new file mode 100644
index 0000000..ebeaed5
--- /dev/null
+++ b/src/cid/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/cid Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) cid ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = cidobjs cidload cidgload cidriver cidparse ;
+ }
+ else
+ {
+ _sources = type1cid ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/cid Jamfile
diff --git a/src/cid/ciderrs.h b/src/cid/ciderrs.h
new file mode 100644
index 0000000..01813e1
--- /dev/null
+++ b/src/cid/ciderrs.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* ciderrs.h */
+/* */
+/* CID error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the CID error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __CIDERRS_H__
+#define __CIDERRS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX CID_Err_
+#define FT_ERR_BASE FT_Mod_Err_CID
+
+#include FT_ERRORS_H
+
+#endif /* __CIDERRS_H__ */
+
+
+/* END */
diff --git a/src/cid/cidgload.c b/src/cid/cidgload.c
new file mode 100644
index 0000000..8bec6e1
--- /dev/null
+++ b/src/cid/cidgload.c
@@ -0,0 +1,433 @@
+/***************************************************************************/
+/* */
+/* cidgload.c */
+/* */
+/* CID-keyed Type1 Glyph Loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "cidload.h"
+#include "cidgload.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_OUTLINE_H
+
+#include "ciderrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cidgload
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ cid_load_glyph( T1_Decoder decoder,
+ FT_UInt glyph_index )
+ {
+ CID_Face face = (CID_Face)decoder->builder.face;
+ CID_FaceInfo cid = &face->cid;
+ FT_Byte* p;
+ FT_UInt fd_select;
+ FT_Stream stream = face->cid_stream;
+ FT_Error error = CID_Err_Ok;
+ FT_Byte* charstring = 0;
+ FT_Memory memory = face->root.memory;
+ FT_ULong glyph_length = 0;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* For incremental fonts get the character data using */
+ /* the callback function. */
+ if ( face->root.internal->incremental_interface )
+ {
+ FT_Data glyph_data;
+
+
+ error = face->root.internal->incremental_interface->funcs->get_glyph_data(
+ face->root.internal->incremental_interface->object,
+ glyph_index,
+ &glyph_data );
+ if ( error )
+ goto Exit;
+
+ p = (FT_Byte*)glyph_data.pointer;
+ fd_select = (FT_UInt)cid_get_offset( &p, (FT_Byte)cid->fd_bytes );
+
+ if ( glyph_data.length != 0 )
+ {
+ glyph_length = glyph_data.length - cid->fd_bytes;
+ FT_ALLOC( charstring, glyph_length );
+ if ( !error )
+ ft_memcpy( charstring, glyph_data.pointer + cid->fd_bytes,
+ glyph_length );
+ }
+
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,
+ &glyph_data );
+
+ if ( error )
+ goto Exit;
+ }
+
+ else
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ /* For ordinary fonts read the CID font dictionary index */
+ /* and charstring offset from the CIDMap. */
+ {
+ FT_UInt entry_len = cid->fd_bytes + cid->gd_bytes;
+ FT_ULong off1;
+
+
+ if ( FT_STREAM_SEEK( cid->data_offset + cid->cidmap_offset +
+ glyph_index * entry_len ) ||
+ FT_FRAME_ENTER( 2 * entry_len ) )
+ goto Exit;
+
+ p = (FT_Byte*)stream->cursor;
+ fd_select = (FT_UInt) cid_get_offset( &p, (FT_Byte)cid->fd_bytes );
+ off1 = (FT_ULong)cid_get_offset( &p, (FT_Byte)cid->gd_bytes );
+ p += cid->fd_bytes;
+ glyph_length = cid_get_offset( &p, (FT_Byte)cid->gd_bytes ) - off1;
+ FT_FRAME_EXIT();
+
+ if ( fd_select >= (FT_UInt)cid->num_dicts )
+ {
+ error = CID_Err_Invalid_Offset;
+ goto Exit;
+ }
+ if ( glyph_length == 0 )
+ goto Exit;
+ if ( FT_ALLOC( charstring, glyph_length ) )
+ goto Exit;
+ if ( FT_STREAM_READ_AT( cid->data_offset + off1,
+ charstring, glyph_length ) )
+ goto Exit;
+ }
+
+ /* Now set up the subrs array and parse the charstrings. */
+ {
+ CID_FaceDict dict;
+ CID_Subrs cid_subrs = face->subrs + fd_select;
+ FT_Int cs_offset;
+
+
+ /* Set up subrs */
+ decoder->num_subrs = cid_subrs->num_subrs;
+ decoder->subrs = cid_subrs->code;
+ decoder->subrs_len = 0;
+
+ /* Set up font matrix */
+ dict = cid->font_dicts + fd_select;
+
+ decoder->font_matrix = dict->font_matrix;
+ decoder->font_offset = dict->font_offset;
+ decoder->lenIV = dict->private_dict.lenIV;
+
+ /* Decode the charstring. */
+
+ /* Adjustment for seed bytes. */
+ cs_offset = ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
+
+ /* Decrypt only if lenIV >= 0. */
+ if ( decoder->lenIV >= 0 )
+ psaux->t1_decrypt( charstring, glyph_length, 4330 );
+
+ error = decoder->funcs.parse_charstrings(
+ decoder, charstring + cs_offset,
+ (FT_Int)glyph_length - cs_offset );
+ }
+
+ FT_FREE( charstring );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* Incremental fonts can optionally override the metrics. */
+ if ( !error &&
+ face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->get_glyph_metrics )
+ {
+ FT_Incremental_MetricsRec metrics;
+
+
+ metrics.bearing_x = decoder->builder.left_bearing.x;
+ metrics.bearing_y = decoder->builder.left_bearing.y;
+ metrics.advance = decoder->builder.advance.x;
+ error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+ face->root.internal->incremental_interface->object,
+ glyph_index, FALSE, &metrics );
+ decoder->builder.left_bearing.x = metrics.bearing_x;
+ decoder->builder.left_bearing.y = metrics.bearing_y;
+ decoder->builder.advance.x = metrics.advance;
+ decoder->builder.advance.y = 0;
+ }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ Exit:
+ return error;
+ }
+
+
+#if 0
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** *********/
+ /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/
+ /********** *********/
+ /********** The following code is in charge of computing *********/
+ /********** the maximum advance width of the font. It *********/
+ /********** quickly processes each glyph charstring to *********/
+ /********** extract the value from either a `sbw' or `seac' *********/
+ /********** operator. *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_face_compute_max_advance( CID_Face face,
+ FT_Int* max_advance )
+ {
+ FT_Error error;
+ T1_DecoderRec decoder;
+ FT_Int glyph_index;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ *max_advance = 0;
+
+ /* Initialize load decoder */
+ error = psaux->t1_decoder_funcs->init( &decoder,
+ (FT_Face)face,
+ 0, /* size */
+ 0, /* glyph slot */
+ 0, /* glyph names! XXX */
+ 0, /* blend == 0 */
+ 0, /* hinting == 0 */
+ cid_load_glyph );
+ if ( error )
+ return error;
+
+ /* TODO: initialize decoder.len_buildchar and decoder.buildchar */
+ /* if we ever support CID-keyed multiple master fonts */
+
+ decoder.builder.metrics_only = 1;
+ decoder.builder.load_points = 0;
+
+ /* for each glyph, parse the glyph charstring and extract */
+ /* the advance width */
+ for ( glyph_index = 0; glyph_index < face->root.num_glyphs;
+ glyph_index++ )
+ {
+ /* now get load the unscaled outline */
+ error = cid_load_glyph( &decoder, glyph_index );
+ /* ignore the error if one occurred - skip to next glyph */
+ }
+
+ *max_advance = decoder.builder.advance.x;
+
+ psaux->t1_decoder_funcs->done( &decoder );
+
+ return CID_Err_Ok;
+ }
+
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_slot_load_glyph( FT_GlyphSlot cidglyph, /* CID_GlyphSlot */
+ FT_Size cidsize, /* CID_Size */
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ CID_GlyphSlot glyph = (CID_GlyphSlot)cidglyph;
+ CID_Size size = (CID_Size)cidsize;
+ FT_Error error;
+ T1_DecoderRec decoder;
+ CID_Face face = (CID_Face)cidglyph->face;
+ FT_Bool hinting;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+ FT_Matrix font_matrix;
+ FT_Vector font_offset;
+
+
+ if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
+ {
+ error = CID_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
+
+ glyph->x_scale = cidsize->metrics.x_scale;
+ glyph->y_scale = cidsize->metrics.y_scale;
+
+ cidglyph->outline.n_points = 0;
+ cidglyph->outline.n_contours = 0;
+
+ hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
+ ( load_flags & FT_LOAD_NO_HINTING ) == 0 );
+
+ cidglyph->format = FT_GLYPH_FORMAT_OUTLINE;
+
+ error = psaux->t1_decoder_funcs->init( &decoder,
+ cidglyph->face,
+ cidsize,
+ cidglyph,
+ 0, /* glyph names -- XXX */
+ 0, /* blend == 0 */
+ hinting,
+ FT_LOAD_TARGET_MODE( load_flags ),
+ cid_load_glyph );
+ if ( error )
+ goto Exit;
+
+ /* TODO: initialize decoder.len_buildchar and decoder.buildchar */
+ /* if we ever support CID-keyed multiple master fonts */
+
+ /* set up the decoder */
+ decoder.builder.no_recurse = FT_BOOL(
+ ( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ) );
+
+ error = cid_load_glyph( &decoder, glyph_index );
+ if ( error )
+ goto Exit;
+
+ font_matrix = decoder.font_matrix;
+ font_offset = decoder.font_offset;
+
+ /* save new glyph tables */
+ psaux->t1_decoder_funcs->done( &decoder );
+
+ /* now set the metrics -- this is rather simple, as */
+ /* the left side bearing is the xMin, and the top side */
+ /* bearing the yMax */
+ cidglyph->outline.flags &= FT_OUTLINE_OWNER;
+ cidglyph->outline.flags |= FT_OUTLINE_REVERSE_FILL;
+
+ /* for composite glyphs, return only left side bearing and */
+ /* advance width */
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ {
+ FT_Slot_Internal internal = cidglyph->internal;
+
+
+ cidglyph->metrics.horiBearingX = decoder.builder.left_bearing.x;
+ cidglyph->metrics.horiAdvance = decoder.builder.advance.x;
+
+ internal->glyph_matrix = font_matrix;
+ internal->glyph_delta = font_offset;
+ internal->glyph_transformed = 1;
+ }
+ else
+ {
+ FT_BBox cbox;
+ FT_Glyph_Metrics* metrics = &cidglyph->metrics;
+ FT_Vector advance;
+
+
+ /* copy the _unscaled_ advance width */
+ metrics->horiAdvance = decoder.builder.advance.x;
+ cidglyph->linearHoriAdvance = decoder.builder.advance.x;
+ cidglyph->internal->glyph_transformed = 0;
+
+ /* make up vertical ones */
+ metrics->vertAdvance = ( face->cid.font_bbox.yMax -
+ face->cid.font_bbox.yMin ) >> 16;
+ cidglyph->linearVertAdvance = metrics->vertAdvance;
+
+ cidglyph->format = FT_GLYPH_FORMAT_OUTLINE;
+
+ if ( size && cidsize->metrics.y_ppem < 24 )
+ cidglyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ /* apply the font matrix */
+ FT_Outline_Transform( &cidglyph->outline, &font_matrix );
+
+ FT_Outline_Translate( &cidglyph->outline,
+ font_offset.x,
+ font_offset.y );
+
+ advance.x = metrics->horiAdvance;
+ advance.y = 0;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->horiAdvance = advance.x + font_offset.x;
+
+ advance.x = 0;
+ advance.y = metrics->vertAdvance;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->vertAdvance = advance.y + font_offset.y;
+
+ if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ /* scale the outline and the metrics */
+ FT_Int n;
+ FT_Outline* cur = decoder.builder.base;
+ FT_Vector* vec = cur->points;
+ FT_Fixed x_scale = glyph->x_scale;
+ FT_Fixed y_scale = glyph->y_scale;
+
+
+ /* First of all, scale the points */
+ if ( !hinting || !decoder.builder.hints_funcs )
+ for ( n = cur->n_points; n > 0; n--, vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, x_scale );
+ vec->y = FT_MulFix( vec->y, y_scale );
+ }
+
+ /* Then scale the metrics */
+ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
+ metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
+ }
+
+ /* compute the other metrics */
+ FT_Outline_Get_CBox( &cidglyph->outline, &cbox );
+
+ metrics->width = cbox.xMax - cbox.xMin;
+ metrics->height = cbox.yMax - cbox.yMin;
+
+ metrics->horiBearingX = cbox.xMin;
+ metrics->horiBearingY = cbox.yMax;
+
+ /* make up vertical ones */
+ ft_synthesize_vertical_metrics( metrics,
+ metrics->vertAdvance );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/src/cid/cidgload.h b/src/cid/cidgload.h
new file mode 100644
index 0000000..a0a91bf
--- /dev/null
+++ b/src/cid/cidgload.h
@@ -0,0 +1,51 @@
+/***************************************************************************/
+/* */
+/* cidgload.h */
+/* */
+/* OpenType Glyph Loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CIDGLOAD_H__
+#define __CIDGLOAD_H__
+
+
+#include <ft2build.h>
+#include "cidobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+#if 0
+
+ /* Compute the maximum advance width of a font through quick parsing */
+ FT_LOCAL( FT_Error )
+ cid_face_compute_max_advance( CID_Face face,
+ FT_Int* max_advance );
+
+#endif /* 0 */
+
+ FT_LOCAL( FT_Error )
+ cid_slot_load_glyph( FT_GlyphSlot glyph, /* CID_Glyph_Slot */
+ FT_Size size, /* CID_Size */
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+
+FT_END_HEADER
+
+#endif /* __CIDGLOAD_H__ */
+
+
+/* END */
diff --git a/src/cid/cidload.c b/src/cid/cidload.c
new file mode 100644
index 0000000..9ed8cee
--- /dev/null
+++ b/src/cid/cidload.c
@@ -0,0 +1,644 @@
+/***************************************************************************/
+/* */
+/* cidload.c */
+/* */
+/* CID-keyed Type1 font loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_CONFIG_CONFIG_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+#include "cidload.h"
+
+#include "ciderrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cidload
+
+
+ /* read a single offset */
+ FT_LOCAL_DEF( FT_Long )
+ cid_get_offset( FT_Byte* *start,
+ FT_Byte offsize )
+ {
+ FT_Long result;
+ FT_Byte* p = *start;
+
+
+ for ( result = 0; offsize > 0; offsize-- )
+ {
+ result <<= 8;
+ result |= *p++;
+ }
+
+ *start = p;
+ return result;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE 1 SYMBOL PARSING *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static FT_Error
+ cid_load_keyword( CID_Face face,
+ CID_Loader* loader,
+ const T1_Field keyword )
+ {
+ FT_Error error;
+ CID_Parser* parser = &loader->parser;
+ FT_Byte* object;
+ void* dummy_object;
+ CID_FaceInfo cid = &face->cid;
+
+
+ /* if the keyword has a dedicated callback, call it */
+ if ( keyword->type == T1_FIELD_TYPE_CALLBACK )
+ {
+ keyword->reader( (FT_Face)face, parser );
+ error = parser->root.error;
+ goto Exit;
+ }
+
+ /* we must now compute the address of our target object */
+ switch ( keyword->location )
+ {
+ case T1_FIELD_LOCATION_CID_INFO:
+ object = (FT_Byte*)cid;
+ break;
+
+ case T1_FIELD_LOCATION_FONT_INFO:
+ object = (FT_Byte*)&cid->font_info;
+ break;
+
+ case T1_FIELD_LOCATION_BBOX:
+ object = (FT_Byte*)&cid->font_bbox;
+ break;
+
+ default:
+ {
+ CID_FaceDict dict;
+
+
+ if ( parser->num_dict < 0 )
+ {
+ FT_ERROR(( "cid_load_keyword: invalid use of `%s'!\n",
+ keyword->ident ));
+ error = CID_Err_Syntax_Error;
+ goto Exit;
+ }
+
+ dict = cid->font_dicts + parser->num_dict;
+ switch ( keyword->location )
+ {
+ case T1_FIELD_LOCATION_PRIVATE:
+ object = (FT_Byte*)&dict->private_dict;
+ break;
+
+ default:
+ object = (FT_Byte*)dict;
+ }
+ }
+ }
+
+ dummy_object = object;
+
+ /* now, load the keyword data in the object's field(s) */
+ if ( keyword->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
+ keyword->type == T1_FIELD_TYPE_FIXED_ARRAY )
+ error = cid_parser_load_field_table( &loader->parser, keyword,
+ &dummy_object );
+ else
+ error = cid_parser_load_field( &loader->parser,
+ keyword, &dummy_object );
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ parse_font_matrix( CID_Face face,
+ CID_Parser* parser )
+ {
+ FT_Matrix* matrix;
+ FT_Vector* offset;
+ CID_FaceDict dict;
+ FT_Face root = (FT_Face)&face->root;
+ FT_Fixed temp[6];
+ FT_Fixed temp_scale;
+
+
+ if ( parser->num_dict >= 0 )
+ {
+ dict = face->cid.font_dicts + parser->num_dict;
+ matrix = &dict->font_matrix;
+ offset = &dict->font_offset;
+
+ (void)cid_parser_to_fixed_array( parser, 6, temp, 3 );
+
+ temp_scale = FT_ABS( temp[3] );
+
+ /* Set units per EM based on FontMatrix values. We set the value to */
+ /* `1000/temp_scale', because temp_scale was already multiplied by */
+ /* 1000 (in `t1_tofixed', from psobjs.c). */
+ root->units_per_EM = (FT_UShort)( FT_DivFix( 0x10000L,
+ FT_DivFix( temp_scale, 1000 ) ) );
+
+ /* we need to scale the values by 1.0/temp[3] */
+ if ( temp_scale != 0x10000L )
+ {
+ temp[0] = FT_DivFix( temp[0], temp_scale );
+ temp[1] = FT_DivFix( temp[1], temp_scale );
+ temp[2] = FT_DivFix( temp[2], temp_scale );
+ temp[4] = FT_DivFix( temp[4], temp_scale );
+ temp[5] = FT_DivFix( temp[5], temp_scale );
+ temp[3] = 0x10000L;
+ }
+
+ matrix->xx = temp[0];
+ matrix->yx = temp[1];
+ matrix->xy = temp[2];
+ matrix->yy = temp[3];
+
+ /* note that the font offsets are expressed in integer font units */
+ offset->x = temp[4] >> 16;
+ offset->y = temp[5] >> 16;
+ }
+
+ return CID_Err_Ok; /* this is a callback function; */
+ /* we must return an error code */
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ parse_fd_array( CID_Face face,
+ CID_Parser* parser )
+ {
+ CID_FaceInfo cid = &face->cid;
+ FT_Memory memory = face->root.memory;
+ FT_Error error = CID_Err_Ok;
+ FT_Long num_dicts;
+
+
+ num_dicts = cid_parser_to_int( parser );
+
+ if ( !cid->font_dicts )
+ {
+ FT_Int n;
+
+
+ if ( FT_NEW_ARRAY( cid->font_dicts, num_dicts ) )
+ goto Exit;
+
+ cid->num_dicts = (FT_UInt)num_dicts;
+
+ /* don't forget to set a few defaults */
+ for ( n = 0; n < cid->num_dicts; n++ )
+ {
+ CID_FaceDict dict = cid->font_dicts + n;
+
+
+ /* default value for lenIV */
+ dict->private_dict.lenIV = 4;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static
+ const T1_FieldRec cid_field_records[] =
+ {
+
+#include "cidtoken.h"
+
+ T1_FIELD_CALLBACK( "FDArray", parse_fd_array, 0 )
+ T1_FIELD_CALLBACK( "FontMatrix", parse_font_matrix, 0 )
+
+ { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
+ };
+
+
+ static FT_Error
+ cid_parse_dict( CID_Face face,
+ CID_Loader* loader,
+ FT_Byte* base,
+ FT_Long size )
+ {
+ CID_Parser* parser = &loader->parser;
+
+
+ parser->root.cursor = base;
+ parser->root.limit = base + size;
+ parser->root.error = CID_Err_Ok;
+
+ {
+ FT_Byte* cur = base;
+ FT_Byte* limit = cur + size;
+
+
+ for (;;)
+ {
+ FT_Byte* newlimit;
+
+
+ parser->root.cursor = cur;
+ cid_parser_skip_spaces( parser );
+
+ if ( parser->root.cursor >= limit )
+ newlimit = limit - 1 - 17;
+ else
+ newlimit = parser->root.cursor - 17;
+
+ /* look for `%ADOBeginFontDict' */
+ for ( ; cur < newlimit; cur++ )
+ {
+ if ( *cur == '%' &&
+ ft_strncmp( (char*)cur, "%ADOBeginFontDict", 17 ) == 0 )
+ {
+ /* if /FDArray was found, then cid->num_dicts is > 0, and */
+ /* we can start increasing parser->num_dict */
+ if ( face->cid.num_dicts > 0 )
+ parser->num_dict++;
+ }
+ }
+
+ cur = parser->root.cursor;
+ /* no error can occur in cid_parser_skip_spaces */
+ if ( cur >= limit )
+ break;
+
+ cid_parser_skip_PS_token( parser );
+ if ( parser->root.cursor >= limit || parser->root.error )
+ break;
+
+ /* look for immediates */
+ if ( *cur == '/' && cur + 2 < limit )
+ {
+ FT_PtrDist len;
+
+
+ cur++;
+ len = parser->root.cursor - cur;
+
+ if ( len > 0 && len < 22 )
+ {
+ /* now compare the immediate name to the keyword table */
+ T1_Field keyword = (T1_Field)cid_field_records;
+
+
+ for (;;)
+ {
+ FT_Byte* name;
+
+
+ name = (FT_Byte*)keyword->ident;
+ if ( !name )
+ break;
+
+ if ( cur[0] == name[0] &&
+ len == (FT_PtrDist)ft_strlen( (const char*)name ) )
+ {
+ FT_PtrDist n;
+
+
+ for ( n = 1; n < len; n++ )
+ if ( cur[n] != name[n] )
+ break;
+
+ if ( n >= len )
+ {
+ /* we found it - run the parsing callback */
+ parser->root.error = cid_load_keyword( face,
+ loader,
+ keyword );
+ if ( parser->root.error )
+ return parser->root.error;
+ break;
+ }
+ }
+ keyword++;
+ }
+ }
+ }
+
+ cur = parser->root.cursor;
+ }
+ }
+ return parser->root.error;
+ }
+
+
+ /* read the subrmap and the subrs of each font dict */
+ static FT_Error
+ cid_read_subrs( CID_Face face )
+ {
+ CID_FaceInfo cid = &face->cid;
+ FT_Memory memory = face->root.memory;
+ FT_Stream stream = face->cid_stream;
+ FT_Error error;
+ FT_Int n;
+ CID_Subrs subr;
+ FT_UInt max_offsets = 0;
+ FT_ULong* offsets = 0;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ if ( FT_NEW_ARRAY( face->subrs, cid->num_dicts ) )
+ goto Exit;
+
+ subr = face->subrs;
+ for ( n = 0; n < cid->num_dicts; n++, subr++ )
+ {
+ CID_FaceDict dict = cid->font_dicts + n;
+ FT_Int lenIV = dict->private_dict.lenIV;
+ FT_UInt count, num_subrs = dict->num_subrs;
+ FT_ULong data_len;
+ FT_Byte* p;
+
+
+ /* reallocate offsets array if needed */
+ if ( num_subrs + 1 > max_offsets )
+ {
+ FT_UInt new_max = FT_PAD_CEIL( num_subrs + 1, 4 );
+
+
+ if ( FT_RENEW_ARRAY( offsets, max_offsets, new_max ) )
+ goto Fail;
+
+ max_offsets = new_max;
+ }
+
+ /* read the subrmap's offsets */
+ if ( FT_STREAM_SEEK( cid->data_offset + dict->subrmap_offset ) ||
+ FT_FRAME_ENTER( ( num_subrs + 1 ) * dict->sd_bytes ) )
+ goto Fail;
+
+ p = (FT_Byte*)stream->cursor;
+ for ( count = 0; count <= num_subrs; count++ )
+ offsets[count] = cid_get_offset( &p, (FT_Byte)dict->sd_bytes );
+
+ FT_FRAME_EXIT();
+
+ /* now, compute the size of subrs charstrings, */
+ /* allocate, and read them */
+ data_len = offsets[num_subrs] - offsets[0];
+
+ if ( FT_NEW_ARRAY( subr->code, num_subrs + 1 ) ||
+ FT_ALLOC( subr->code[0], data_len ) )
+ goto Fail;
+
+ if ( FT_STREAM_SEEK( cid->data_offset + offsets[0] ) ||
+ FT_STREAM_READ( subr->code[0], data_len ) )
+ goto Fail;
+
+ /* set up pointers */
+ for ( count = 1; count <= num_subrs; count++ )
+ {
+ FT_ULong len;
+
+
+ len = offsets[count] - offsets[count - 1];
+ subr->code[count] = subr->code[count - 1] + len;
+ }
+
+ /* decrypt subroutines, but only if lenIV >= 0 */
+ if ( lenIV >= 0 )
+ {
+ for ( count = 0; count < num_subrs; count++ )
+ {
+ FT_ULong len;
+
+
+ len = offsets[count + 1] - offsets[count];
+ psaux->t1_decrypt( subr->code[count], len, 4330 );
+ }
+ }
+
+ subr->num_subrs = num_subrs;
+ }
+
+ Exit:
+ FT_FREE( offsets );
+ return error;
+
+ Fail:
+ if ( face->subrs )
+ {
+ for ( n = 0; n < cid->num_dicts; n++ )
+ {
+ if ( face->subrs[n].code )
+ FT_FREE( face->subrs[n].code[0] );
+
+ FT_FREE( face->subrs[n].code );
+ }
+ FT_FREE( face->subrs );
+ }
+ goto Exit;
+ }
+
+
+ static void
+ t1_init_loader( CID_Loader* loader,
+ CID_Face face )
+ {
+ FT_UNUSED( face );
+
+ FT_MEM_ZERO( loader, sizeof ( *loader ) );
+ }
+
+
+ static void
+ t1_done_loader( CID_Loader* loader )
+ {
+ CID_Parser* parser = &loader->parser;
+
+
+ /* finalize parser */
+ cid_parser_done( parser );
+ }
+
+
+ static FT_Error
+ cid_hex_to_binary( FT_Byte* data,
+ FT_Long data_len,
+ FT_ULong offset,
+ CID_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Error error;
+
+ FT_Byte buffer[256];
+ FT_Byte *p, *plimit;
+ FT_Byte *d, *dlimit;
+ FT_Byte val;
+
+ FT_Bool upper_nibble, done;
+
+
+ if ( FT_STREAM_SEEK( offset ) )
+ goto Exit;
+
+ d = data;
+ dlimit = d + data_len;
+ p = buffer;
+ plimit = p;
+
+ upper_nibble = 1;
+ done = 0;
+
+ while ( d < dlimit )
+ {
+ if ( p >= plimit )
+ {
+ FT_ULong oldpos = FT_STREAM_POS();
+ FT_ULong size = stream->size - oldpos;
+
+
+ if ( size == 0 )
+ {
+ error = CID_Err_Syntax_Error;
+ goto Exit;
+ }
+
+ if ( FT_STREAM_READ( buffer, 256 > size ? size : 256 ) )
+ goto Exit;
+ p = buffer;
+ plimit = p + FT_STREAM_POS() - oldpos;
+ }
+
+ if ( ft_isdigit( *p ) )
+ val = (FT_Byte)( *p - '0' );
+ else if ( *p >= 'a' && *p <= 'f' )
+ val = (FT_Byte)( *p - 'a' );
+ else if ( *p >= 'A' && *p <= 'F' )
+ val = (FT_Byte)( *p - 'A' + 10 );
+ else if ( *p == ' ' ||
+ *p == '\t' ||
+ *p == '\r' ||
+ *p == '\n' ||
+ *p == '\f' ||
+ *p == '\0' )
+ {
+ p++;
+ continue;
+ }
+ else if ( *p == '>' )
+ {
+ val = 0;
+ done = 1;
+ }
+ else
+ {
+ error = CID_Err_Syntax_Error;
+ goto Exit;
+ }
+
+ if ( upper_nibble )
+ *d = (FT_Byte)( val << 4 );
+ else
+ {
+ *d = (FT_Byte)( *d + val );
+ d++;
+ }
+
+ upper_nibble = (FT_Byte)( 1 - upper_nibble );
+
+ if ( done )
+ break;
+
+ p++;
+ }
+
+ error = CID_Err_Ok;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_face_open( CID_Face face,
+ FT_Int face_index )
+ {
+ CID_Loader loader;
+ CID_Parser* parser;
+ FT_Memory memory = face->root.memory;
+ FT_Error error;
+
+
+ t1_init_loader( &loader, face );
+
+ parser = &loader.parser;
+ error = cid_parser_new( parser, face->root.stream, face->root.memory,
+ (PSAux_Service)face->psaux );
+ if ( error )
+ goto Exit;
+
+ error = cid_parse_dict( face, &loader,
+ parser->postscript,
+ parser->postscript_len );
+ if ( error )
+ goto Exit;
+
+ if ( face_index < 0 )
+ goto Exit;
+
+ if ( FT_NEW( face->cid_stream ) )
+ goto Exit;
+
+ if ( parser->binary_length )
+ {
+ /* we must convert the data section from hexadecimal to binary */
+ if ( FT_ALLOC( face->binary_data, parser->binary_length ) ||
+ cid_hex_to_binary( face->binary_data, parser->binary_length,
+ parser->data_offset, face ) )
+ goto Exit;
+
+ FT_Stream_OpenMemory( face->cid_stream,
+ face->binary_data, parser->binary_length );
+ face->cid.data_offset = 0;
+ }
+ else
+ {
+ *face->cid_stream = *face->root.stream;
+ face->cid.data_offset = loader.parser.data_offset;
+ }
+
+ error = cid_read_subrs( face );
+
+ Exit:
+ t1_done_loader( &loader );
+ return error;
+ }
+
+
+/* END */
diff --git a/src/cid/cidload.h b/src/cid/cidload.h
new file mode 100644
index 0000000..8c172ff
--- /dev/null
+++ b/src/cid/cidload.h
@@ -0,0 +1,53 @@
+/***************************************************************************/
+/* */
+/* cidload.h */
+/* */
+/* CID-keyed Type1 font loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CIDLOAD_H__
+#define __CIDLOAD_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include "cidparse.h"
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct CID_Loader_
+ {
+ CID_Parser parser; /* parser used to read the stream */
+ FT_Int num_chars; /* number of characters in encoding */
+
+ } CID_Loader;
+
+
+ FT_LOCAL( FT_Long )
+ cid_get_offset( FT_Byte** start,
+ FT_Byte offsize );
+
+ FT_LOCAL( FT_Error )
+ cid_face_open( CID_Face face,
+ FT_Int face_index );
+
+
+FT_END_HEADER
+
+#endif /* __CIDLOAD_H__ */
+
+
+/* END */
diff --git a/src/cid/cidobjs.c b/src/cid/cidobjs.c
new file mode 100644
index 0000000..1b3bfbf
--- /dev/null
+++ b/src/cid/cidobjs.c
@@ -0,0 +1,480 @@
+/***************************************************************************/
+/* */
+/* cidobjs.c */
+/* */
+/* CID objects manager (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+
+#include "cidgload.h"
+#include "cidload.h"
+
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+#include "ciderrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cidobjs
+
+
+ /*************************************************************************/
+ /* */
+ /* SLOT FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ cid_slot_done( FT_GlyphSlot slot )
+ {
+ slot->internal->glyph_hints = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_slot_init( FT_GlyphSlot slot )
+ {
+ CID_Face face;
+ PSHinter_Service pshinter;
+
+
+ face = (CID_Face)slot->face;
+ pshinter = (PSHinter_Service)face->pshinter;
+
+ if ( pshinter )
+ {
+ FT_Module module;
+
+
+ module = FT_Get_Module( slot->face->driver->root.library,
+ "pshinter" );
+ if ( module )
+ {
+ T1_Hints_Funcs funcs;
+
+
+ funcs = pshinter->get_t1_funcs( module );
+ slot->internal->glyph_hints = (void*)funcs;
+ }
+ }
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SIZE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+
+ static PSH_Globals_Funcs
+ cid_size_get_globals_funcs( CID_Size size )
+ {
+ CID_Face face = (CID_Face)size->root.face;
+ PSHinter_Service pshinter = (PSHinter_Service)face->pshinter;
+ FT_Module module;
+
+
+ module = FT_Get_Module( size->root.face->driver->root.library,
+ "pshinter" );
+ return ( module && pshinter && pshinter->get_globals_funcs )
+ ? pshinter->get_globals_funcs( module )
+ : 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cid_size_done( FT_Size cidsize ) /* CID_Size */
+ {
+ CID_Size size = (CID_Size)cidsize;
+
+
+ if ( cidsize->internal )
+ {
+ PSH_Globals_Funcs funcs;
+
+
+ funcs = cid_size_get_globals_funcs( size );
+ if ( funcs )
+ funcs->destroy( (PSH_Globals)cidsize->internal );
+
+ cidsize->internal = 0;
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_size_init( FT_Size cidsize ) /* CID_Size */
+ {
+ CID_Size size = (CID_Size)cidsize;
+ FT_Error error = 0;
+ PSH_Globals_Funcs funcs = cid_size_get_globals_funcs( size );
+
+
+ if ( funcs )
+ {
+ PSH_Globals globals;
+ CID_Face face = (CID_Face)cidsize->face;
+ CID_FaceDict dict = face->cid.font_dicts + face->root.face_index;
+ PS_Private priv = &dict->private_dict;
+
+
+ error = funcs->create( cidsize->face->memory, priv, &globals );
+ if ( !error )
+ cidsize->internal = (FT_Size_Internal)(void*)globals;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL( FT_Error )
+ cid_size_request( FT_Size size,
+ FT_Size_Request req )
+ {
+ PSH_Globals_Funcs funcs;
+
+
+ FT_Request_Metrics( size->face, req );
+
+ funcs = cid_size_get_globals_funcs( (CID_Size)size );
+
+ if ( funcs )
+ funcs->set_scale( (PSH_Globals)size->internal,
+ size->metrics.x_scale,
+ size->metrics.y_scale,
+ 0, 0 );
+
+ return CID_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FACE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cid_face_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given face object. */
+ /* */
+ /* <Input> */
+ /* face :: A pointer to the face object to destroy. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cid_face_done( FT_Face cidface ) /* CID_Face */
+ {
+ CID_Face face = (CID_Face)cidface;
+ FT_Memory memory;
+
+
+ if ( face )
+ {
+ CID_FaceInfo cid = &face->cid;
+ PS_FontInfo info = &cid->font_info;
+
+
+ memory = cidface->memory;
+
+ /* release subrs */
+ if ( face->subrs )
+ {
+ FT_Int n;
+
+
+ for ( n = 0; n < cid->num_dicts; n++ )
+ {
+ CID_Subrs subr = face->subrs + n;
+
+
+ if ( subr->code )
+ {
+ FT_FREE( subr->code[0] );
+ FT_FREE( subr->code );
+ }
+ }
+
+ FT_FREE( face->subrs );
+ }
+
+ /* release FontInfo strings */
+ FT_FREE( info->version );
+ FT_FREE( info->notice );
+ FT_FREE( info->full_name );
+ FT_FREE( info->family_name );
+ FT_FREE( info->weight );
+
+ /* release font dictionaries */
+ FT_FREE( cid->font_dicts );
+ cid->num_dicts = 0;
+
+ /* release other strings */
+ FT_FREE( cid->cid_font_name );
+ FT_FREE( cid->registry );
+ FT_FREE( cid->ordering );
+
+ cidface->family_name = 0;
+ cidface->style_name = 0;
+
+ FT_FREE( face->binary_data );
+ FT_FREE( face->cid_stream );
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cid_face_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given CID face object. */
+ /* */
+ /* <Input> */
+ /* stream :: The source font stream. */
+ /* */
+ /* face_index :: The index of the font face in the resource. */
+ /* */
+ /* num_params :: Number of additional generic parameters. Ignored. */
+ /* */
+ /* params :: Additional generic parameters. Ignored. */
+ /* */
+ /* <InOut> */
+ /* face :: The newly built face object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ cid_face_init( FT_Stream stream,
+ FT_Face cidface, /* CID_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ CID_Face face = (CID_Face)cidface;
+ FT_Error error;
+ PSAux_Service psaux;
+ PSHinter_Service pshinter;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+ FT_UNUSED( stream );
+
+
+ cidface->num_faces = 1;
+
+ psaux = (PSAux_Service)face->psaux;
+ if ( !psaux )
+ {
+ psaux = (PSAux_Service)FT_Get_Module_Interface(
+ FT_FACE_LIBRARY( face ), "psaux" );
+
+ face->psaux = psaux;
+ }
+
+ pshinter = (PSHinter_Service)face->pshinter;
+ if ( !pshinter )
+ {
+ pshinter = (PSHinter_Service)FT_Get_Module_Interface(
+ FT_FACE_LIBRARY( face ), "pshinter" );
+
+ face->pshinter = pshinter;
+ }
+
+ /* open the tokenizer; this will also check the font format */
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ error = cid_face_open( face, face_index );
+ if ( error )
+ goto Exit;
+
+ /* if we just wanted to check the format, leave successfully now */
+ if ( face_index < 0 )
+ goto Exit;
+
+ /* check the face index */
+ if ( face_index != 0 )
+ {
+ FT_ERROR(( "cid_face_init: invalid face index\n" ));
+ error = CID_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* now load the font program into the face object */
+
+ /* initialize the face object fields */
+
+ /* set up root face fields */
+ {
+ CID_FaceInfo cid = &face->cid;
+ PS_FontInfo info = &cid->font_info;
+
+
+ cidface->num_glyphs = cid->cid_count;
+ cidface->num_charmaps = 0;
+
+ cidface->face_index = face_index;
+ cidface->face_flags = FT_FACE_FLAG_SCALABLE | /* scalable outlines */
+ FT_FACE_FLAG_HORIZONTAL | /* horizontal data */
+ FT_FACE_FLAG_HINTER; /* has native hinter */
+
+ if ( info->is_fixed_pitch )
+ cidface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* XXX: TODO: add kerning with .afm support */
+
+ /* get style name -- be careful, some broken fonts only */
+ /* have a /FontName dictionary entry! */
+ cidface->family_name = info->family_name;
+ /* assume "Regular" style if we don't know better */
+ cidface->style_name = (char *)"Regular";
+ if ( cidface->family_name )
+ {
+ char* full = info->full_name;
+ char* family = cidface->family_name;
+
+
+ if ( full )
+ {
+ while ( *full )
+ {
+ if ( *full == *family )
+ {
+ family++;
+ full++;
+ }
+ else
+ {
+ if ( *full == ' ' || *full == '-' )
+ full++;
+ else if ( *family == ' ' || *family == '-' )
+ family++;
+ else
+ {
+ if ( !*family )
+ cidface->style_name = full;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* do we have a `/FontName'? */
+ if ( cid->cid_font_name )
+ cidface->family_name = cid->cid_font_name;
+ }
+
+ /* compute style flags */
+ cidface->style_flags = 0;
+ if ( info->italic_angle )
+ cidface->style_flags |= FT_STYLE_FLAG_ITALIC;
+ if ( info->weight )
+ {
+ if ( !ft_strcmp( info->weight, "Bold" ) ||
+ !ft_strcmp( info->weight, "Black" ) )
+ cidface->style_flags |= FT_STYLE_FLAG_BOLD;
+ }
+
+ /* no embedded bitmap support */
+ cidface->num_fixed_sizes = 0;
+ cidface->available_sizes = 0;
+
+ cidface->bbox.xMin = cid->font_bbox.xMin >> 16;
+ cidface->bbox.yMin = cid->font_bbox.yMin >> 16;
+ cidface->bbox.xMax = ( cid->font_bbox.xMax + 0xFFFFU ) >> 16;
+ cidface->bbox.yMax = ( cid->font_bbox.yMax + 0xFFFFU ) >> 16;
+
+ if ( !cidface->units_per_EM )
+ cidface->units_per_EM = 1000;
+
+ cidface->ascender = (FT_Short)( cidface->bbox.yMax );
+ cidface->descender = (FT_Short)( cidface->bbox.yMin );
+
+ cidface->height = (FT_Short)( ( cidface->units_per_EM * 12 ) / 10 );
+ if ( cidface->height < cidface->ascender - cidface->descender )
+ cidface->height = (FT_Short)( cidface->ascender - cidface->descender );
+
+ cidface->underline_position = (FT_Short)info->underline_position;
+ cidface->underline_thickness = (FT_Short)info->underline_thickness;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cid_driver_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given CID driver object. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target driver object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ cid_driver_init( FT_Module driver )
+ {
+ FT_UNUSED( driver );
+
+ return CID_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* cid_driver_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given CID driver. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target CID driver. */
+ /* */
+ FT_LOCAL_DEF( void )
+ cid_driver_done( FT_Module driver )
+ {
+ FT_UNUSED( driver );
+ }
+
+
+/* END */
diff --git a/src/cid/cidobjs.h b/src/cid/cidobjs.h
new file mode 100644
index 0000000..aee346d
--- /dev/null
+++ b/src/cid/cidobjs.h
@@ -0,0 +1,154 @@
+/***************************************************************************/
+/* */
+/* cidobjs.h */
+/* */
+/* CID objects manager (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CIDOBJS_H__
+#define __CIDOBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /* The following structures must be defined by the hinter */
+ typedef struct CID_Size_Hints_ CID_Size_Hints;
+ typedef struct CID_Glyph_Hints_ CID_Glyph_Hints;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CID_Driver */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 driver object. */
+ /* */
+ typedef struct CID_DriverRec_* CID_Driver;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CID_Size */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 size object. */
+ /* */
+ typedef struct CID_SizeRec_* CID_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CID_GlyphSlot */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 glyph slot object. */
+ /* */
+ typedef struct CID_GlyphSlotRec_* CID_GlyphSlot;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* CID_CharMap */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 character mapping object. */
+ /* */
+ /* <Note> */
+ /* The Type 1 format doesn't use a charmap but an encoding table. */
+ /* The driver is responsible for making up charmap objects */
+ /* corresponding to these tables. */
+ /* */
+ typedef struct CID_CharMapRec_* CID_CharMap;
+
+
+ /*************************************************************************/
+ /* */
+ /* HERE BEGINS THE TYPE 1 SPECIFIC STUFF */
+ /* */
+ /*************************************************************************/
+
+
+ typedef struct CID_SizeRec_
+ {
+ FT_SizeRec root;
+ FT_Bool valid;
+
+ } CID_SizeRec;
+
+
+ typedef struct CID_GlyphSlotRec_
+ {
+ FT_GlyphSlotRec root;
+
+ FT_Bool hint;
+ FT_Bool scaled;
+
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+
+ } CID_GlyphSlotRec;
+
+
+ FT_LOCAL( void )
+ cid_slot_done( FT_GlyphSlot slot );
+
+ FT_LOCAL( FT_Error )
+ cid_slot_init( FT_GlyphSlot slot );
+
+
+ FT_LOCAL( void )
+ cid_size_done( FT_Size size ); /* CID_Size */
+
+ FT_LOCAL( FT_Error )
+ cid_size_init( FT_Size size ); /* CID_Size */
+
+ FT_LOCAL( FT_Error )
+ cid_size_request( FT_Size size, /* CID_Size */
+ FT_Size_Request req );
+
+ FT_LOCAL( FT_Error )
+ cid_face_init( FT_Stream stream,
+ FT_Face face, /* CID_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ cid_face_done( FT_Face face ); /* CID_Face */
+
+
+ FT_LOCAL( FT_Error )
+ cid_driver_init( FT_Module driver );
+
+ FT_LOCAL( void )
+ cid_driver_done( FT_Module driver );
+
+
+FT_END_HEADER
+
+#endif /* __CIDOBJS_H__ */
+
+
+/* END */
diff --git a/src/cid/cidparse.c b/src/cid/cidparse.c
new file mode 100644
index 0000000..bb87afc
--- /dev/null
+++ b/src/cid/cidparse.c
@@ -0,0 +1,226 @@
+/***************************************************************************/
+/* */
+/* cidparse.c */
+/* */
+/* CID-keyed Type1 parser (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_STREAM_H
+
+#include "cidparse.h"
+
+#include "ciderrs.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_cidparse
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** INPUT STREAM PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ cid_parser_new( CID_Parser* parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux )
+ {
+ FT_Error error;
+ FT_ULong base_offset, offset, ps_len;
+ FT_Byte *cur, *limit;
+ FT_Byte *arg1, *arg2;
+
+
+ FT_MEM_ZERO( parser, sizeof ( *parser ) );
+ psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );
+
+ parser->stream = stream;
+
+ base_offset = FT_STREAM_POS();
+
+ /* first of all, check the font format in the header */
+ if ( FT_FRAME_ENTER( 31 ) )
+ goto Exit;
+
+ if ( ft_strncmp( (char *)stream->cursor,
+ "%!PS-Adobe-3.0 Resource-CIDFont", 31 ) )
+ {
+ FT_TRACE2(( "[not a valid CID-keyed font]\n" ));
+ error = CID_Err_Unknown_File_Format;
+ }
+
+ FT_FRAME_EXIT();
+ if ( error )
+ goto Exit;
+
+ Again:
+ /* now, read the rest of the file until we find */
+ /* `StartData' or `/sfnts' */
+ {
+ FT_Byte buffer[256 + 10];
+ FT_Int read_len = 256 + 10;
+ FT_Byte* p = buffer;
+
+
+ for ( offset = (FT_ULong)FT_STREAM_POS(); ; offset += 256 )
+ {
+ FT_Int stream_len;
+
+
+ stream_len = stream->size - FT_STREAM_POS();
+ if ( stream_len == 0 )
+ {
+ FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
+ error = CID_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ read_len = FT_MIN( read_len, stream_len );
+ if ( FT_STREAM_READ( p, read_len ) )
+ goto Exit;
+
+ if ( read_len < 256 )
+ p[read_len] = '\0';
+
+ limit = p + read_len - 10;
+
+ for ( p = buffer; p < limit; p++ )
+ {
+ if ( p[0] == 'S' && ft_strncmp( (char*)p, "StartData", 9 ) == 0 )
+ {
+ /* save offset of binary data after `StartData' */
+ offset += p - buffer + 10;
+ goto Found;
+ }
+ else if ( p[1] == 's' && ft_strncmp( (char*)p, "/sfnts", 6 ) == 0 )
+ {
+ offset += p - buffer + 7;
+ goto Found;
+ }
+ }
+
+ FT_MEM_MOVE( buffer, p, 10 );
+ read_len = 256;
+ p = buffer + 10;
+ }
+ }
+
+ Found:
+ /* We have found the start of the binary data or the `/sfnts' token. */
+ /* Now rewind and extract the frame corresponding to this PostScript */
+ /* section. */
+
+ ps_len = offset - base_offset;
+ if ( FT_STREAM_SEEK( base_offset ) ||
+ FT_FRAME_EXTRACT( ps_len, parser->postscript ) )
+ goto Exit;
+
+ parser->data_offset = offset;
+ parser->postscript_len = ps_len;
+ parser->root.base = parser->postscript;
+ parser->root.cursor = parser->postscript;
+ parser->root.limit = parser->root.cursor + ps_len;
+ parser->num_dict = -1;
+
+ /* Finally, we check whether `StartData' or `/sfnts' was real -- */
+ /* it could be in a comment or string. We also get the arguments */
+ /* of `StartData' to find out whether the data is represented in */
+ /* binary or hex format. */
+
+ arg1 = parser->root.cursor;
+ cid_parser_skip_PS_token( parser );
+ cid_parser_skip_spaces ( parser );
+ arg2 = parser->root.cursor;
+ cid_parser_skip_PS_token( parser );
+ cid_parser_skip_spaces ( parser );
+
+ limit = parser->root.limit;
+ cur = parser->root.cursor;
+
+ while ( cur < limit )
+ {
+ if ( parser->root.error )
+ {
+ error = parser->root.error;
+ goto Exit;
+ }
+
+ if ( cur[0] == 'S' && ft_strncmp( (char*)cur, "StartData", 9 ) == 0 )
+ {
+ if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
+ parser->binary_length = ft_atol( (const char *)arg2 );
+
+ limit = parser->root.limit;
+ cur = parser->root.cursor;
+ goto Exit;
+ }
+ else if ( cur[1] == 's' && ft_strncmp( (char*)cur, "/sfnts", 6 ) == 0 )
+ {
+ FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" ));
+ error = CID_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ cid_parser_skip_PS_token( parser );
+ cid_parser_skip_spaces ( parser );
+ arg1 = arg2;
+ arg2 = cur;
+ cur = parser->root.cursor;
+ }
+
+ /* we haven't found the correct `StartData'; go back and continue */
+ /* searching */
+ FT_FRAME_RELEASE( parser->postscript );
+ if ( !FT_STREAM_SEEK( offset ) )
+ goto Again;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ cid_parser_done( CID_Parser* parser )
+ {
+ /* always free the private dictionary */
+ if ( parser->postscript )
+ {
+ FT_Stream stream = parser->stream;
+
+
+ FT_FRAME_RELEASE( parser->postscript );
+ }
+ parser->root.funcs.done( &parser->root );
+ }
+
+
+/* END */
diff --git a/src/cid/cidparse.h b/src/cid/cidparse.h
new file mode 100644
index 0000000..ca37dea
--- /dev/null
+++ b/src/cid/cidparse.h
@@ -0,0 +1,123 @@
+/***************************************************************************/
+/* */
+/* cidparse.h */
+/* */
+/* CID-keyed Type1 parser (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CIDPARSE_H__
+#define __CIDPARSE_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_TYPE1_TYPES_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* CID_Parser */
+ /* */
+ /* <Description> */
+ /* A CID_Parser is an object used to parse a Type 1 fonts very */
+ /* quickly. */
+ /* */
+ /* <Fields> */
+ /* root :: The root PS_ParserRec fields. */
+ /* */
+ /* stream :: The current input stream. */
+ /* */
+ /* postscript :: A pointer to the data to be parsed. */
+ /* */
+ /* postscript_len :: The length of the data to be parsed. */
+ /* */
+ /* data_offset :: The start position of the binary data (i.e., the */
+ /* end of the data to be parsed. */
+ /* */
+ /* binary_length :: The length of the data after the `StartData' */
+ /* command if the data format is hexadecimal. */
+ /* */
+ /* cid :: A structure which holds the information about */
+ /* the current font. */
+ /* */
+ /* num_dict :: The number of font dictionaries. */
+ /* */
+ typedef struct CID_Parser_
+ {
+ PS_ParserRec root;
+ FT_Stream stream;
+
+ FT_Byte* postscript;
+ FT_Long postscript_len;
+
+ FT_ULong data_offset;
+
+ FT_Long binary_length;
+
+ CID_FaceInfo cid;
+ FT_Int num_dict;
+
+ } CID_Parser;
+
+
+ FT_LOCAL( FT_Error )
+ cid_parser_new( CID_Parser* parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux );
+
+ FT_LOCAL( void )
+ cid_parser_done( CID_Parser* parser );
+
+
+ /*************************************************************************/
+ /* */
+ /* PARSING ROUTINES */
+ /* */
+ /*************************************************************************/
+
+#define cid_parser_skip_spaces( p ) \
+ (p)->root.funcs.skip_spaces( &(p)->root )
+#define cid_parser_skip_PS_token( p ) \
+ (p)->root.funcs.skip_PS_token( &(p)->root )
+
+#define cid_parser_to_int( p ) (p)->root.funcs.to_int( &(p)->root )
+#define cid_parser_to_fixed( p, t ) (p)->root.funcs.to_fixed( &(p)->root, t )
+
+#define cid_parser_to_coord_array( p, m, c ) \
+ (p)->root.funcs.to_coord_array( &(p)->root, m, c )
+#define cid_parser_to_fixed_array( p, m, f, t ) \
+ (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t )
+#define cid_parser_to_token( p, t ) \
+ (p)->root.funcs.to_token( &(p)->root, t )
+#define cid_parser_to_token_array( p, t, m, c ) \
+ (p)->root.funcs.to_token_array( &(p)->root, t, m, c )
+
+#define cid_parser_load_field( p, f, o ) \
+ (p)->root.funcs.load_field( &(p)->root, f, o, 0, 0 )
+#define cid_parser_load_field_table( p, f, o ) \
+ (p)->root.funcs.load_field_table( &(p)->root, f, o, 0, 0 )
+
+
+FT_END_HEADER
+
+#endif /* __CIDPARSE_H__ */
+
+
+/* END */
diff --git a/src/cid/cidriver.c b/src/cid/cidriver.c
new file mode 100644
index 0000000..5c5a729
--- /dev/null
+++ b/src/cid/cidriver.c
@@ -0,0 +1,163 @@
+/***************************************************************************/
+/* */
+/* cidriver.c */
+/* */
+/* CID driver interface (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "cidriver.h"
+#include "cidgload.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+
+#include "ciderrs.h"
+
+#include FT_SERVICE_POSTSCRIPT_NAME_H
+#include FT_SERVICE_XFREE86_NAME_H
+#include FT_SERVICE_POSTSCRIPT_INFO_H
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ciddriver
+
+
+ /*
+ * POSTSCRIPT NAME SERVICE
+ *
+ */
+
+ static const char*
+ cid_get_postscript_name( CID_Face face )
+ {
+ const char* result = face->cid.cid_font_name;
+
+
+ if ( result && result[0] == '/' )
+ result++;
+
+ return result;
+ }
+
+
+ static const FT_Service_PsFontNameRec cid_service_ps_name =
+ {
+ (FT_PsName_GetFunc) cid_get_postscript_name
+ };
+
+
+ /*
+ * POSTSCRIPT INFO SERVICE
+ *
+ */
+
+ static FT_Error
+ cid_ps_get_font_info( FT_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ *afont_info = ((CID_Face)face)->cid.font_info;
+ return 0;
+ }
+
+
+ static const FT_Service_PsInfoRec cid_service_ps_info =
+ {
+ (PS_GetFontInfoFunc) cid_ps_get_font_info,
+ (PS_HasGlyphNamesFunc) NULL, /* unsupported with CID fonts */
+ (PS_GetFontPrivateFunc)NULL /* unsupported */
+ };
+
+
+ /*
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec cid_services[] =
+ {
+ { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &cid_service_ps_name },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_CID },
+ { FT_SERVICE_ID_POSTSCRIPT_INFO, &cid_service_ps_info },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ cid_get_interface( FT_Module module,
+ const char* cid_interface )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( cid_services, cid_interface );
+ }
+
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec t1cid_driver_class =
+ {
+ /* first of all, the FT_Module_Class fields */
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE |
+ FT_MODULE_DRIVER_HAS_HINTER,
+
+ sizeof( FT_DriverRec ),
+ "t1cid", /* module name */
+ 0x10000L, /* version 1.0 of driver */
+ 0x20000L, /* requires FreeType 2.0 */
+
+ 0,
+
+ cid_driver_init,
+ cid_driver_done,
+ cid_get_interface
+ },
+
+ /* then the other font drivers fields */
+ sizeof( CID_FaceRec ),
+ sizeof( CID_SizeRec ),
+ sizeof( CID_GlyphSlotRec ),
+
+ cid_face_init,
+ cid_face_done,
+
+ cid_size_init,
+ cid_size_done,
+ cid_slot_init,
+ cid_slot_done,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+
+ cid_slot_load_glyph,
+
+ 0, /* FT_Face_GetKerningFunc */
+ 0, /* FT_Face_AttachFunc */
+
+ 0, /* FT_Face_GetAdvancesFunc */
+
+ cid_size_request,
+ 0 /* FT_Size_SelectFunc */
+ };
+
+
+/* END */
diff --git a/src/cid/cidriver.h b/src/cid/cidriver.h
new file mode 100644
index 0000000..d5a80f6
--- /dev/null
+++ b/src/cid/cidriver.h
@@ -0,0 +1,39 @@
+/***************************************************************************/
+/* */
+/* cidriver.h */
+/* */
+/* High-level CID driver interface (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __CIDRIVER_H__
+#define __CIDRIVER_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_CALLBACK_TABLE
+ const FT_Driver_ClassRec t1cid_driver_class;
+
+
+FT_END_HEADER
+
+#endif /* __CIDRIVER_H__ */
+
+
+/* END */
diff --git a/src/cid/cidtoken.h b/src/cid/cidtoken.h
new file mode 100644
index 0000000..ad5bbb2
--- /dev/null
+++ b/src/cid/cidtoken.h
@@ -0,0 +1,103 @@
+/***************************************************************************/
+/* */
+/* cidtoken.h */
+/* */
+/* CID token definitions (specification only). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE CID_FaceInfoRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_CID_INFO
+
+ T1_FIELD_KEY ( "CIDFontName", cid_font_name, 0 )
+ T1_FIELD_NUM ( "CIDFontVersion", cid_version, 0 )
+ T1_FIELD_NUM ( "CIDFontType", cid_font_type, 0 )
+ T1_FIELD_STRING( "Registry", registry, 0 )
+ T1_FIELD_STRING( "Ordering", ordering, 0 )
+ T1_FIELD_NUM ( "Supplement", supplement, 0 )
+ T1_FIELD_NUM ( "UIDBase", uid_base, 0 )
+ T1_FIELD_NUM ( "CIDMapOffset", cidmap_offset, 0 )
+ T1_FIELD_NUM ( "FDBytes", fd_bytes, 0 )
+ T1_FIELD_NUM ( "GDBytes", gd_bytes, 0 )
+ T1_FIELD_NUM ( "CIDCount", cid_count, 0 )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PS_FontInfoRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_INFO
+
+ T1_FIELD_STRING( "version", version, 0 )
+ T1_FIELD_STRING( "Notice", notice, 0 )
+ T1_FIELD_STRING( "FullName", full_name, 0 )
+ T1_FIELD_STRING( "FamilyName", family_name, 0 )
+ T1_FIELD_STRING( "Weight", weight, 0 )
+ T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 )
+ T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 )
+ T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 )
+ T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE CID_FaceDictRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_DICT
+
+ T1_FIELD_NUM ( "PaintType", paint_type, 0 )
+ T1_FIELD_NUM ( "FontType", font_type, 0 )
+ T1_FIELD_NUM ( "SubrMapOffset", subrmap_offset, 0 )
+ T1_FIELD_NUM ( "SDBytes", sd_bytes, 0 )
+ T1_FIELD_NUM ( "SubrCount", num_subrs, 0 )
+ T1_FIELD_NUM ( "lenBuildCharArray", len_buildchar, 0 )
+ T1_FIELD_FIXED( "ForceBoldThreshold", forcebold_threshold, 0 )
+ T1_FIELD_FIXED( "ExpansionFactor", expansion_factor, 0 )
+ T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PS_PrivateRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_PRIVATE
+
+ T1_FIELD_NUM ( "UniqueID", unique_id, 0 )
+ T1_FIELD_NUM ( "lenIV", lenIV, 0 )
+ T1_FIELD_NUM ( "LanguageGroup", language_group, 0 )
+ T1_FIELD_NUM ( "password", password, 0 )
+
+ T1_FIELD_FIXED_1000( "BlueScale", blue_scale, 0 )
+ T1_FIELD_NUM ( "BlueShift", blue_shift, 0 )
+ T1_FIELD_NUM ( "BlueFuzz", blue_fuzz, 0 )
+
+ T1_FIELD_NUM_TABLE ( "BlueValues", blue_values, 14, 0 )
+ T1_FIELD_NUM_TABLE ( "OtherBlues", other_blues, 10, 0 )
+ T1_FIELD_NUM_TABLE ( "FamilyBlues", family_blues, 14, 0 )
+ T1_FIELD_NUM_TABLE ( "FamilyOtherBlues", family_other_blues, 10, 0 )
+
+ T1_FIELD_NUM_TABLE2( "StdHW", standard_width, 1, 0 )
+ T1_FIELD_NUM_TABLE2( "StdVW", standard_height, 1, 0 )
+ T1_FIELD_NUM_TABLE2( "MinFeature", min_feature, 2, 0 )
+
+ T1_FIELD_NUM_TABLE ( "StemSnapH", snap_widths, 12, 0 )
+ T1_FIELD_NUM_TABLE ( "StemSnapV", snap_heights, 12, 0 )
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE FT_BBox
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_BBOX
+
+ T1_FIELD_BBOX( "FontBBox", xMin, 0 )
+
+
+/* END */
diff --git a/src/cid/module.mk b/src/cid/module.mk
new file mode 100644
index 0000000..41e5a68
--- /dev/null
+++ b/src/cid/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 CID module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += TYPE1CID_DRIVER
+
+define TYPE1CID_DRIVER
+$(OPEN_DRIVER)t1cid_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)cid $(ECHO_DRIVER_DESC)Postscript CID-keyed fonts, no known extension$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/cid/rules.mk b/src/cid/rules.mk
new file mode 100644
index 0000000..f362744
--- /dev/null
+++ b/src/cid/rules.mk
@@ -0,0 +1,70 @@
+#
+# FreeType 2 CID driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# CID driver directory
+#
+CID_DIR := $(SRC_DIR)/cid
+
+
+CID_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(CID_DIR))
+
+
+# CID driver sources (i.e., C files)
+#
+CID_DRV_SRC := $(CID_DIR)/cidparse.c \
+ $(CID_DIR)/cidload.c \
+ $(CID_DIR)/cidriver.c \
+ $(CID_DIR)/cidgload.c \
+ $(CID_DIR)/cidobjs.c
+
+# CID driver headers
+#
+CID_DRV_H := $(CID_DRV_SRC:%.c=%.h) \
+ $(CID_DIR)/cidtoken.h \
+ $(CID_DIR)/ciderrs.h
+
+
+# CID driver object(s)
+#
+# CID_DRV_OBJ_M is used during `multi' builds
+# CID_DRV_OBJ_S is used during `single' builds
+#
+CID_DRV_OBJ_M := $(CID_DRV_SRC:$(CID_DIR)/%.c=$(OBJ_DIR)/%.$O)
+CID_DRV_OBJ_S := $(OBJ_DIR)/type1cid.$O
+
+# CID driver source file for single build
+#
+CID_DRV_SRC_S := $(CID_DIR)/type1cid.c
+
+
+# CID driver - single object
+#
+$(CID_DRV_OBJ_S): $(CID_DRV_SRC_S) $(CID_DRV_SRC) $(FREETYPE_H) $(CID_DRV_H)
+ $(CID_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CID_DRV_SRC_S))
+
+
+# CID driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(CID_DIR)/%.c $(FREETYPE_H) $(CID_DRV_H)
+ $(CID_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(CID_DRV_OBJ_S)
+DRV_OBJS_M += $(CID_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/cid/type1cid.c b/src/cid/type1cid.c
new file mode 100644
index 0000000..0b866e9
--- /dev/null
+++ b/src/cid/type1cid.c
@@ -0,0 +1,29 @@
+/***************************************************************************/
+/* */
+/* type1cid.c */
+/* */
+/* FreeType OpenType driver component (body only). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "cidparse.c"
+#include "cidload.c"
+#include "cidobjs.c"
+#include "cidriver.c"
+#include "cidgload.c"
+
+
+/* END */
diff --git a/src/gxvalid/Jamfile b/src/gxvalid/Jamfile
new file mode 100644
index 0000000..88049a6
--- /dev/null
+++ b/src/gxvalid/Jamfile
@@ -0,0 +1,33 @@
+# FreeType 2 src/gxvalid Jamfile
+#
+# Copyright 2005 by
+# suzuki toshiya, Masatake YAMATO and Red Hat K.K.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) gxvalid ;
+
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = gxvcommn gxvfeat gxvbsln gxvtrak gxvopbd gxvprop
+ gxvmort gxvmort0 gxvmort1 gxvmort2 gxvmort4 gxvmort5
+ gxvmorx gxvmorx0 gxvmorx1 gxvmorx2 gxvmorx4 gxvmorx5
+ gxvlcar gxvkern gxvmod gxvjust ;
+ }
+ else
+ {
+ _sources = gxvalid ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/gxvalid Jamfile
diff --git a/src/gxvalid/README b/src/gxvalid/README
new file mode 100644
index 0000000..28e535b
--- /dev/null
+++ b/src/gxvalid/README
@@ -0,0 +1,532 @@
+gxvalid: TrueType GX validator
+==============================
+
+
+1. What is this
+---------------
+
+ `gxvalid' is a module to validate TrueType GX tables: a collection of
+ additional tables in TrueType font which are used by `QuickDraw GX
+ Text', Apple Advanced Typography (AAT). In addition, gxvalid can
+ validates `kern' tables which have been extended for AAT. Like the
+ otvalid module, gxvalid uses Freetype 2's validator framework
+ (ftvalid).
+
+ You can link gxvalid with your program; before running your own layout
+ engine, gxvalid validates a font file. As the result, you can remove
+ error-checking code from the layout engine. It is also possible to
+ use gxvalid as a stand-alone font validator; the `ftvalid' test
+ program included in the ft2demo bundle calls gxvalid internally.
+ A stand-alone font validator may be useful for font developers.
+
+ This documents documents the following issues.
+
+ - supported TrueType GX tables
+ - fundamental validation limitations
+ - permissive error handling of broken GX tables
+ - `kern' table issue.
+
+
+2. Supported tables
+-------------------
+
+ The following GX tables are currently supported.
+
+ bsln
+ feat
+ just
+ kern(*)
+ lcar
+ mort
+ morx
+ opbd
+ prop
+ trak
+
+ The following GX tables are currently unsupported.
+
+ cvar
+ fdsc
+ fmtx
+ fvar
+ gvar
+ Zapf
+
+ The following GX tables won't be supported.
+
+ acnt(**)
+ hsty(***)
+
+ The following undocumented tables in TrueType fonts designed for Apple
+ platform aren't handled either.
+
+ addg
+ CVTM
+ TPNM
+ umif
+
+
+ *) The `kern' validator handles both the classic and the new kern
+ formats; the former is supported on both Microsoft and Apple
+ platforms, while the latter is supported on Apple platforms.
+
+ **) `acnt' tables are not supported by currently available Apple font
+ tools.
+
+ ***) There is one more Apple extension, `hsty', but it is for
+ Newton-OS, not GX (Newton-OS is a platform by Apple, but it can
+ use sfnt- housed bitmap fonts only). Therefore, it should be
+ excluded from `Apple platform' in the context of TrueType.
+ gxvalid ignores it as Apple font tools do so.
+
+
+ We have checked 183 fonts bundled with MacOS 9.1, MacOS 9.2, MacOS
+ 10.0, MacOS X 10.1, MSIE for MacOS, and AppleWorks 6.0. In addition,
+ we have checked 67 Dynalab fonts (designed for MacOS) and 189 Ricoh
+ fonts (designed for Windows and MacOS dual platforms). The number of
+ fonts including TrueType GX tables are as follows.
+
+ bsln: 76
+ feat: 191
+ just: 84
+ kern: 59
+ lcar: 4
+ mort: 326
+ morx: 19
+ opbd: 4
+ prop: 114
+ trak: 16
+
+ Dynalab and Ricoh fonts don't have GX tables except of `feat' and
+ `mort'.
+
+
+3. Fundamental validation limitations
+-------------------------------------
+
+ TrueType GX provides layout information to libraries for font
+ rasterizers and text layout. gxvalid can check whether the layout
+ data in a font is conformant to the TrueType GX format specified by
+ Apple. But gxvalid cannot check a how QuickDraw GX/AAT renderer uses
+ the stored information.
+
+ 3-1. Validation of State Machine activity
+ -----------------------------------------
+
+ QuickDraw GX/AAT uses a `State Machine' to provide `stateful' layout
+ features, and TrueType GX stores the state transition diagram of
+ this `State Machine' in a `StateTable' data structure. While the
+ State Machine receives a series of glyph IDs, the State Machine
+ starts with `start of text' state, walks around various states and
+ generates various layout information to the renderer, and finally
+ reaches the `end of text' state.
+
+ gxvalid can check essential errors like:
+
+ - possibility of state transitions to undefined states
+ - existence of glyph IDs that the State Machine doesn't know how
+ to handle
+ - the State Machine cannot compute the layout information from
+ given diagram
+
+ These errors can be checked within finite steps, and without the
+ State Machine itself, because these are `expression' errors of state
+ transition diagram.
+
+ There is no limitation about how long the State Machine walks
+ around, so validation of the algorithm in the state transition
+ diagram requires infinite steps, even if we had a State Machine in
+ gxvalid. Therefore, the following errors and problems cannot be
+ checked.
+
+ - existence of states which the State Machine never transits to
+ - the possibility that the State Machine never reaches `end of
+ text'
+ - the possibility of stack underflow/overflow in the State Machine
+ (in ligature and contextual glyph substitutions, the State
+ Machine can store 16 glyphs onto its stack)
+
+ In addition, gxvalid doesn't check `temporary glyph IDs' used in the
+ chained State Machines (in `mort' and `morx' tables). If a layout
+ feature is implemented by a single State Machine, a glyph ID
+ converted by the State Machine is passed to the glyph renderer, thus
+ it should not point to an undefined glyph ID. But if a layout
+ feature is implemented by chained State Machines, a component State
+ Machine (if it is not the final one) is permitted to generate
+ undefined glyph IDs for temporary use, because it is handled by next
+ component State Machine and not by the glyph renderer. To validate
+ such temporary glyph IDs, gxvalid must stack all undefined glyph IDs
+ which can occur in the output of the previous State Machine and
+ search them in the `ClassTable' structure of the current State
+ Machine. It is too complex to list all possible glyph IDs from the
+ StateTable, especially from a ligature substitution table.
+
+ 3-2. Validation of relationship between multiple layout features
+ ----------------------------------------------------------------
+
+ gxvalid does not validate the relationship between multiple layout
+ features at all.
+
+ If multiple layout features are defined in TrueType GX tables,
+ possible interactions, overrides, and conflicts between layout
+ features are implicitly given in the font too. For example, there
+ are several predefined spacing control features:
+
+ - Text Spacing (Proportional/Monospace/Half-width/Normal)
+ - Number Spacing (Monospaced-numbers/Proportional-numbers)
+ - Kana Spacing (Full-width/Proportional)
+ - Ideographic Spacing (Full-width/Proportional)
+ - CJK Roman Spacing (Half-width/Proportional/Default-roman
+ /Full-width-roman/Proportional)
+
+ If all layout features are independently managed, we can activate
+ inconsistent typographic rules like `Text Spacing=Monospace' and
+ `Ideographic Spacing=Proportional' at the same time.
+
+ The combinations of layout features is managed by a 32bit integer
+ (one bit each for selector setting), so we can define relationships
+ between up to 32 features, theoretically. But if one feature
+ setting affects another feature setting, we need typographic
+ priority rules to validate the relationship. Unfortunately, the
+ TrueType GX format specification does not give such information even
+ for predefined features.
+
+
+4. Permissive error handling of broken GX tables
+------------------------------------------------
+
+ When Apple's font rendering system finds an inconsistency, like a
+ specification violation or an unspecified value in a TrueType GX
+ table, it does not always return error. In most cases, the rendering
+ engine silently ignores such wrong values or even whole tables. In
+ fact, MacOS is shipped with fonts including broken GX/AAT tables, but
+ no harmful effects due to `officially broken' fonts are observed by
+ end-users.
+
+ gxvalid is designed to continue the validation process as long as
+ possible. When gxvalid find wrong values, gxvalid warns it at least,
+ and takes a fallback procedure if possible. The fallback procedure
+ depends on the debug level.
+
+ We used the following three tools to investigate Apple's error handling.
+
+ - FontValidator (for MacOS 8.5 - 9.2) resource fork font
+ - ftxvalidator (for MacOS X 10.1 -) dfont or naked-sfnt
+ - ftxdumperfuser (for MacOS X 10.1 -) dfont or naked-sfnt
+
+ However, all tests were done on a PowerPC based Macintosh; at present,
+ we have not checked those tools on a m68k-based Macintosh.
+
+ In total, we checked 183 fonts bundled to MacOS 9.1, MacOS 9.2, MacOS
+ 10.0, MacOS X 10.1, MSIE for MacOS, and AppleWorks 6.0. These fonts
+ are distributed officially, but many broken GX/AAT tables were found
+ by Apple's font tools. In the following, we list typical violation of
+ the GX specification, in fonts officially distributed with those Apple
+ systems.
+
+ 4-1. broken BinSrchHeader (19/183)
+ ----------------------------------
+
+ `BinSrchHeader' is a header of a data array for m68k platforms to
+ access memory efficiently. Although there are only two independent
+ parameters for real (`unitSize' and `nUnits'), BinSrchHeader has
+ three additional parameters which can be calculated from `unitSize'
+ and `nUnits', for fast setup. Apple font tools ignore them
+ silently, so gxvalid warns if it finds and inconsistency, and always
+ continues validation. The additional parameters are ignored
+ regardless of the consistency.
+
+ 19 fonts include such inconsistencies; all breaks are in the
+ BinSrchHeader structure of the `kern' table.
+
+ 4-2. too-short LookupTable (5/183)
+ ----------------------------------
+
+ LookupTable format 0 is a simple array to get a value from a given
+ GID (glyph ID); the index of this array is a GID too. Therefore,
+ the length of the array is expected to be same as the maximum GID
+ value defined in the `maxp' table, but there are some fonts whose
+ LookupTable format 0 is too short to cover all GIDs. FontValidator
+ ignores this error silently, ftxvalidator and ftxdumperfuser both
+ warn and continue. Similar problems are found in format 3 subtables
+ of `kern'. gxvalid warns always and abort if the validation level
+ is set to FT_VALIDATE_PARANOID.
+
+ 5 fonts include too-short kern format 0 subtables.
+ 1 font includes too-short kern format 3 subtable.
+
+ 4-3. broken LookupTable format 2 (1/183)
+ ----------------------------------------
+
+ LookupTable format 2, subformat 4 covers the GID space by a
+ collection of segments which are specified by `firstGlyph' and
+ `lastGlyph'. Some fonts store `firstGlyph' and `lastGlyph' in
+ reverse order, so the segment specification is broken. Apple font
+ tools ignore this error silently; a broken segment is ignored as if
+ it did not exist. gxvalid warns and normalize the segment at
+ FT_VALIDATE_DEFAULT, or ignore the segment at FT_VALIDATE_TIGHT, or
+ abort at FT_VALIDATE_PARANOID.
+
+ 1 font includes broken LookupTable format 2, in the `just' table.
+
+ *) It seems that all fonts manufactured by ITC for AppleWorks have
+ this error.
+
+ 4-4. bad bracketing in glyph property (14/183)
+ ----------------------------------------------
+
+ GX/AAT defines a `bracketing' property of the glyphs in the `prop'
+ table, to control layout features of strings enclosed inside and
+ outside of brackets. Some fonts give inappropriate bracket
+ properties to glyphs. Apple font tools warn about this error;
+ gxvalid warns too and aborts at FT_VALIDATE_PARANOID.
+
+ 14 fonts include wrong bracket properties.
+
+
+ 4-5. invalid feature number (117/183)
+ -------------------------------------
+
+ The GX/AAT extension can include 255 different layout features, but
+ popular layout features are predefined (see
+ http://developer.apple.com/fonts/Registry/index.html). Some fonts
+ include feature numbers which are incompatible with the predefined
+ feature registry.
+
+ In our survey, there are 140 fonts including `feat' table.
+
+ a) 67 fonts use a feature number which should not be used.
+ b) 117 fonts set the wrong feature range (nSetting). This is mostly
+ found in the `mort' and `morx' tables.
+
+ Apple font tools give no warning, although they cannot recognize
+ what the feature is. At FT_VALIDATE_DEFAULT, gxvalid warns but
+ continues in both cases (a, b). At FT_VALIDATE_TIGHT, gxvalid warns
+ and aborts for (a), but continues for (b). At FT_VALIDATE_PARANOID,
+ gxvalid warns and aborts in both cases (a, b).
+
+ 4-6. invalid prop version (10/183)
+ ----------------------------------
+
+ As most TrueType GX tables, the `prop' table must start with a 32bit
+ version identifier: 0x00010000, 0x00020000 or 0x00030000. But some
+ fonts store nonsense binary data instead. When Apple font tools
+ find them, they abort the processing immediately, and the data which
+ follows is unhandled. gxvalid does the same.
+
+ 10 fonts include broken `prop' version.
+
+ All of these fonts are classic TrueType fonts for the Japanese
+ script, manufactured by Apple.
+
+ 4-7. unknown resource name (2/183)
+ ------------------------------------
+
+ NOTE: THIS IS NOT A TRUETYPE GX ERROR.
+
+ If a TrueType font is stored in the resource fork or in dfont
+ format, the data must be tagged as `sfnt' in the resource fork index
+ to invoke TrueType font handler for the data. But the TrueType font
+ data in `Keyboard.dfont' is tagged as `kbd', and that in
+ `LastResort.dfont' is tagged as `lst'. Apple font tools can detect
+ that the data is in TrueType format and successfully validate them.
+ Maybe this is possible because they are known to be dfont. The
+ current implementation of the resource fork driver of FreeType
+ cannot do that, thus gxvalid cannot validate them.
+
+ 2 fonts use an unknown tag for the TrueType font resource.
+
+5. `kern' table issues
+----------------------
+
+ In common terminology of TrueType, `kern' is classified as a basic and
+ platform-independent table. But there are Apple extensions of `kern',
+ and there is an extension which requires a GX state machine for
+ contextual kerning. Therefore, gxvalid includes a special validator
+ for `kern' tables. Unfortunately, there is no exact algorithm to
+ check Apple's extension, so gxvalid includes a heuristic algorithm to
+ find the proper validation routines for all possible data formats,
+ including the data format for Microsoft. By calling
+ classic_kern_validate() instead of gxv_validate(), you can specify the
+ `kern' format explicitly. However, current FreeType2 uses Microsoft
+ `kern' format only, others are ignored (and should be handled in a
+ library one level higher than FreeType).
+
+ 5-1. History
+ ------------
+
+ The original 16bit version of `kern' was designed by Apple in the
+ pre-GX era, and it was also approved by Microsoft. Afterwards,
+ Apple designed a new 32bit version of the `kern' table. According
+ to the documentation, the difference between the 16bit and 32bit
+ version is only the size of variables in the `kern' header. In the
+ following, we call the original 16bit version as `classic', and
+ 32bit version as `new'.
+
+ 5-2. Versions and dialects which should be differentiated
+ ---------------------------------------------------------
+
+ The `kern' table consists of a table header and several subtables.
+ The version number which identifies a `classic' or a `new' version
+ is explicitly written in the table header, but there are
+ undocumented differences between Microsoft's and Apple's formats.
+ It is called a `dialect' in the following. There are three cases
+ which should be handled: the new Apple-dialect, the classic
+ Apple-dialect, and the classic Microsoft-dialect. An analysis of
+ the formats and the auto detection algorithm of gxvalid is described
+ in the following.
+
+ 5-2-1. Version detection: classic and new kern
+ ----------------------------------------------
+
+ According to Apple TrueType specification, there are only two
+ differences between the classic and the new:
+
+ - The `kern' table header starts with the version number.
+ The classic version starts with 0x0000 (16bit),
+ the new version starts with 0x00010000 (32bit).
+
+ - In the `kern' table header, the number of subtables follows
+ the version number.
+ In the classic version, it is stored as a 16bit value.
+ In the new version, it is stored as a 32bit value.
+
+ From Apple font tool's output (DumpKERN is also tested in addition
+ to the three Apple font tools in above), there is another
+ undocumented difference. In the new version, the subtable header
+ includes a 16bit variable named `tupleIndex' which does not exist
+ in the classic version.
+
+ The new version can store all subtable formats (0, 1, 2, and 3),
+ but the Apple TrueType specification does not mention the subtable
+ formats available in the classic version.
+
+ 5-2-2. Available subtable formats in classic version
+ ----------------------------------------------------
+
+ Although the Apple TrueType specification recommends to use the
+ classic version in the case if the font is designed for both the
+ Apple and Microsoft platforms, it does not document the available
+ subtable formats in the classic version.
+
+ According to the Microsoft TrueType specification, the subtable
+ format assured for Windows and OS/2 support is only subtable
+ format 0. The Microsoft TrueType specification also describes
+ subtable format 2, but does not mention which platforms support
+ it. Aubtable formats 1, 3, and higher are documented as reserved
+ for future use. Therefore, the classic version can store subtable
+ formats 0 and 2, at least. `ttfdump.exe', a font tool provided by
+ Microsoft, ignores the subtable format written in the subtable
+ header, and parses the table as if all subtables are in format 0.
+
+ `kern' subtable format 1 uses a StateTable, so it cannot be
+ utilized without a GX State Machine. Therefore, it is reasonable
+ to assume that format 1 (and 3) were introduced after Apple had
+ introduced GX and moved to the new 32bit version.
+
+ 5-2-3. Apple and Microsoft dialects
+ -----------------------------------
+
+ The `kern' subtable has a 16bit `coverage' field to describe
+ kerning attributes, but bit interpretations by Apple and Microsoft
+ are different: For example, Apple uses bits 0-7 to identify the
+ subtable, while Microsoft uses bits 8-15.
+
+ In addition, due to the output of DumpKERN and FontValidator,
+ Apple's bit interpretations of coverage in classic and new version
+ are incompatible also. In summary, there are three dialects:
+ classic Apple dialect, classic Microsoft dialect, and new Apple
+ dialect. The classic Microsoft dialect and the new Apple dialect
+ are documented by each vendors' TrueType font specification, but
+ the documentation for classic Apple dialect is not available.
+
+ For example, in the new Apple dialect, bit 15 is documented as
+ `set to 1 if the kerning is vertical'. On the other hand, in
+ classic Microsoft dialect, bit 1 is documented as `set to 1 if the
+ kerning is horizontal'. From the outputs of DumpKERN and
+ FontValidator, classic Apple dialect recognizes 15 as `set to 1
+ when the kerning is horizontal'. From the results of similar
+ experiments, classic Apple dialect seems to be the Endian reverse
+ of the classic Microsoft dialect.
+
+ As a conclusion it must be noted that no font tool can identify
+ classic Apple dialect or classic Microsoft dialect automatically.
+
+ 5-2-4. gxvalid auto dialect detection algorithm
+ -----------------------------------------------
+
+ The first 16 bits of the `kern' table are enough to identify the
+ version:
+
+ - if the first 16 bits are 0x0000, the `kern' table is in
+ classic Apple dialect or classic Microsoft dialect
+ - if the first 16 bits are 0x0001, and next 16 bits are 0x0000,
+ the kern table is in new Apple dialect.
+
+ If the `kern' table is a classic one, the 16bit `coverage' field
+ is checked next. Firstly, the coverage bits are decoded for the
+ classic Apple dialect using the following bit masks (this is based
+ on DumpKERN output):
+
+ 0x8000: 1=horizontal, 0=vertical
+ 0x4000: not used
+ 0x2000: 1=cross-stream, 0=normal
+ 0x1FF0: reserved
+ 0x000F: subtable format
+
+ If any of reserved bits are set or the subtable bits is
+ interpreted as format 1 or 3, we take it as `impossible in classic
+ Apple dialect' and retry, using the classic Microsoft dialect.
+
+ The most popular coverage in new Apple-dialect: 0x8000,
+ The most popular coverage in classic Apple-dialect: 0x0000,
+ The most popular coverage in classic Microsoft dialect: 0x0001.
+
+ 5-3. Tested fonts
+ -----------------
+
+ We checked 59 fonts bundled with MacOS and 38 fonts bundled with
+ Windows, where all font include a `kern' table.
+
+ - fonts bundled with MacOS
+ * new Apple dialect
+ format 0: 18
+ format 2: 1
+ format 3: 1
+ * classic Apple dialect
+ format 0: 14
+ * classic Microsoft dialect
+ format 0: 15
+
+ - fonts bundled with Windows
+ * classic Microsoft dialect
+ format 0: 38
+
+ It looks strange that classic Microsoft-dialect fonts are bundled to
+ MacOS: they come from MSIE for MacOS, except of MarkerFelt.dfont.
+
+
+ ACKNOWLEDGEMENT
+ ---------------
+
+ Some parts of gxvalid are derived from both the `gxlayout' module and
+ the `otvalid' module. Development of gxlayout was supported by the
+ Information-technology Promotion Agency(IPA), Japan.
+
+ The detailed analysis of undefined glyph ID utilization in `mort' and
+ `morx' tables is provided by George Williams.
+
+------------------------------------------------------------------------
+
+Copyright 2004, 2005, 2007 by
+suzuki toshiya, Masatake YAMATO, Red hat K.K.,
+David Turner, Robert Wilhelm, and Werner Lemberg.
+
+This file is part of the FreeType project, and may only be used,
+modified, and distributed under the terms of the FreeType project
+license, LICENSE.TXT. By continuing to use, modify, or distribute this
+file you indicate that you have read the license and understand and
+accept it fully.
+
+
+--- end of README ---
diff --git a/src/gxvalid/gxvalid.c b/src/gxvalid/gxvalid.c
new file mode 100644
index 0000000..bc36e67
--- /dev/null
+++ b/src/gxvalid/gxvalid.c
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* gxvalid.c */
+/* */
+/* FreeType validator for TrueTypeGX/AAT tables (body only). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+
+#include "gxvfeat.c"
+#include "gxvcommn.c"
+#include "gxvbsln.c"
+#include "gxvtrak.c"
+#include "gxvjust.c"
+#include "gxvmort.c"
+#include "gxvmort0.c"
+#include "gxvmort1.c"
+#include "gxvmort2.c"
+#include "gxvmort4.c"
+#include "gxvmort5.c"
+#include "gxvmorx.c"
+#include "gxvmorx0.c"
+#include "gxvmorx1.c"
+#include "gxvmorx2.c"
+#include "gxvmorx4.c"
+#include "gxvmorx5.c"
+#include "gxvkern.c"
+#include "gxvopbd.c"
+#include "gxvprop.c"
+#include "gxvlcar.c"
+#include "gxvmod.c"
+
+
+/* END */
diff --git a/src/gxvalid/gxvalid.h b/src/gxvalid/gxvalid.h
new file mode 100644
index 0000000..27be9ec
--- /dev/null
+++ b/src/gxvalid/gxvalid.h
@@ -0,0 +1,107 @@
+/***************************************************************************/
+/* */
+/* gxvalid.h */
+/* */
+/* TrueTyeeGX/AAT table validation (specification only). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __GXVALID_H__
+#define __GXVALID_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include "gxverror.h" /* must come before FT_INTERNAL_VALIDATE_H */
+
+#include FT_INTERNAL_VALIDATE_H
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ gxv_feat_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+
+ FT_LOCAL( void )
+ gxv_bsln_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+
+ FT_LOCAL( void )
+ gxv_trak_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_just_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_morx_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_kern_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_kern_validate_classic( FT_Bytes table,
+ FT_Face face,
+ FT_Int dialect_flags,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_opbd_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_prop_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_lcar_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator valid );
+
+
+FT_END_HEADER
+
+
+#endif /* __GXVALID_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvbsln.c b/src/gxvalid/gxvbsln.c
new file mode 100644
index 0000000..6cca658
--- /dev/null
+++ b/src/gxvalid/gxvbsln.c
@@ -0,0 +1,333 @@
+/***************************************************************************/
+/* */
+/* gxvbsln.c */
+/* */
+/* TrueTypeGX/AAT bsln table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvbsln
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define GXV_BSLN_VALUE_COUNT 32
+#define GXV_BSLN_VALUE_EMPTY 0xFFFFU
+
+
+ typedef struct GXV_bsln_DataRec_
+ {
+ FT_Bytes ctlPoints_p;
+ FT_UShort defaultBaseline;
+
+ } GXV_bsln_DataRec, *GXV_bsln_Data;
+
+
+#define GXV_BSLN_DATA( field ) GXV_TABLE_DATA( bsln, field )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_bsln_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UShort v = value.u;
+ FT_UShort* ctlPoints;
+
+ FT_UNUSED( glyph );
+
+
+ GXV_NAME_ENTER( "lookup value" );
+
+ if ( v >= GXV_BSLN_VALUE_COUNT )
+ FT_INVALID_DATA;
+
+ ctlPoints = (FT_UShort*)GXV_BSLN_DATA( ctlPoints_p );
+ if ( ctlPoints && ctlPoints[v] == GXV_BSLN_VALUE_EMPTY )
+ FT_INVALID_DATA;
+
+ GXV_EXIT;
+ }
+
+
+ /*
+ +===============+ --------+
+ | lookup header | |
+ +===============+ |
+ | BinSrchHeader | |
+ +===============+ |
+ | lastGlyph[0] | |
+ +---------------+ |
+ | firstGlyph[0] | | head of lookup table
+ +---------------+ | +
+ | offset[0] | -> | offset [byte]
+ +===============+ | +
+ | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte]
+ +---------------+ |
+ | firstGlyph[1] | |
+ +---------------+ |
+ | offset[1] | |
+ +===============+ |
+ |
+ ... |
+ |
+ 16bit value array |
+ +===============+ |
+ | value | <-------+
+ ...
+ */
+
+ static GXV_LookupValueDesc
+ gxv_bsln_LookupFmt4_transit( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ /* XXX: check range ? */
+ offset = (FT_UShort)( base_value.u +
+ ( relative_gindex * sizeof ( FT_UShort ) ) );
+
+ p = valid->lookuptbl_head + offset;
+ limit = lookuptbl_limit;
+ GXV_LIMIT_CHECK( 2 );
+
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ static void
+ gxv_bsln_parts_fmt0_validate( FT_Bytes tables,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = tables;
+
+
+ GXV_NAME_ENTER( "parts format 0" );
+
+ /* deltas */
+ GXV_LIMIT_CHECK( 2 * GXV_BSLN_VALUE_COUNT );
+
+ valid->table_data = NULL; /* No ctlPoints here. */
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_bsln_parts_fmt1_validate( FT_Bytes tables,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = tables;
+
+
+ GXV_NAME_ENTER( "parts format 1" );
+
+ /* deltas */
+ gxv_bsln_parts_fmt0_validate( p, limit, valid );
+
+ /* mappingData */
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_bsln_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_bsln_LookupFmt4_transit;
+ gxv_LookupTable_validate( p + 2 * GXV_BSLN_VALUE_COUNT,
+ limit,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_bsln_parts_fmt2_validate( FT_Bytes tables,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = tables;
+
+ FT_UShort stdGlyph;
+ FT_UShort ctlPoint;
+ FT_Int i;
+
+ FT_UShort defaultBaseline = GXV_BSLN_DATA( defaultBaseline );
+
+
+ GXV_NAME_ENTER( "parts format 2" );
+
+ GXV_LIMIT_CHECK( 2 + ( 2 * GXV_BSLN_VALUE_COUNT ) );
+
+ /* stdGlyph */
+ stdGlyph = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " (stdGlyph = %u)\n", stdGlyph ));
+
+ gxv_glyphid_validate( stdGlyph, valid );
+
+ /* Record the position of ctlPoints */
+ GXV_BSLN_DATA( ctlPoints_p ) = p;
+
+ /* ctlPoints */
+ for ( i = 0; i < GXV_BSLN_VALUE_COUNT; i++ )
+ {
+ ctlPoint = FT_NEXT_USHORT( p );
+ if ( ctlPoint == GXV_BSLN_VALUE_EMPTY )
+ {
+ if ( i == defaultBaseline )
+ FT_INVALID_DATA;
+ }
+ else
+ gxv_ctlPoint_validate( stdGlyph, (FT_Short)ctlPoint, valid );
+ }
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_bsln_parts_fmt3_validate( FT_Bytes tables,
+ FT_Bytes limit,
+ GXV_Validator valid)
+ {
+ FT_Bytes p = tables;
+
+
+ GXV_NAME_ENTER( "parts format 3" );
+
+ /* stdGlyph + ctlPoints */
+ gxv_bsln_parts_fmt2_validate( p, limit, valid );
+
+ /* mappingData */
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_bsln_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_bsln_LookupFmt4_transit;
+ gxv_LookupTable_validate( p + ( 2 + 2 * GXV_BSLN_VALUE_COUNT ),
+ limit,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** bsln TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_bsln_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+
+ GXV_bsln_DataRec bslnrec;
+ GXV_bsln_Data bsln = &bslnrec;
+
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+
+ FT_ULong version;
+ FT_UShort format;
+ FT_UShort defaultBaseline;
+
+ GXV_Validate_Func fmt_funcs_table [] =
+ {
+ gxv_bsln_parts_fmt0_validate,
+ gxv_bsln_parts_fmt1_validate,
+ gxv_bsln_parts_fmt2_validate,
+ gxv_bsln_parts_fmt3_validate,
+ };
+
+
+ valid->root = ftvalid;
+ valid->table_data = bsln;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `bsln' table\n" ));
+ GXV_INIT;
+
+
+ GXV_LIMIT_CHECK( 4 + 2 + 2 );
+ version = FT_NEXT_ULONG( p );
+ format = FT_NEXT_USHORT( p );
+ defaultBaseline = FT_NEXT_USHORT( p );
+
+ /* only version 1.0 is defined (1996) */
+ if ( version != 0x00010000UL )
+ FT_INVALID_FORMAT;
+
+ /* only format 1, 2, 3 are defined (1996) */
+ GXV_TRACE(( " (format = %d)\n", format ));
+ if ( format > 3 )
+ FT_INVALID_FORMAT;
+
+ if ( defaultBaseline > 31 )
+ FT_INVALID_FORMAT;
+
+ bsln->defaultBaseline = defaultBaseline;
+
+ fmt_funcs_table[format]( p, limit, valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* arch-tag: ebe81143-fdaa-4c68-a4d1-b57227daa3bc
+ (do not change this comment) */
+
+
+/* END */
diff --git a/src/gxvalid/gxvcommn.c b/src/gxvalid/gxvcommn.c
new file mode 100644
index 0000000..82fd6b3
--- /dev/null
+++ b/src/gxvalid/gxvcommn.c
@@ -0,0 +1,1758 @@
+/***************************************************************************/
+/* */
+/* gxvcommn.c */
+/* */
+/* TrueTypeGX/AAT common tables validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvcommon
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** 16bit offset sorter *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static int
+ gxv_compare_ushort_offset( FT_UShort* a,
+ FT_UShort* b )
+ {
+ if ( *a < *b )
+ return ( -1 );
+ else if ( *a > *b )
+ return ( 1 );
+ else
+ return ( 0 );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_set_length_by_ushort_offset( FT_UShort* offset,
+ FT_UShort** length,
+ FT_UShort* buff,
+ FT_UInt nmemb,
+ FT_UShort limit,
+ GXV_Validator valid )
+ {
+ FT_UInt i;
+
+
+ for ( i = 0; i < nmemb; i++ )
+ *(length[i]) = 0;
+
+ for ( i = 0; i < nmemb; i++ )
+ buff[i] = offset[i];
+ buff[nmemb] = limit;
+
+ ft_qsort( buff, ( nmemb + 1 ), sizeof ( FT_UShort ),
+ ( int(*)(const void*, const void*) )gxv_compare_ushort_offset );
+
+ if ( buff[nmemb] > limit )
+ FT_INVALID_OFFSET;
+
+ for ( i = 0; i < nmemb; i++ )
+ {
+ FT_UInt j;
+
+
+ for ( j = 0; j < nmemb; j++ )
+ if ( buff[j] == offset[i] )
+ break;
+
+ if ( j == nmemb )
+ FT_INVALID_OFFSET;
+
+ *(length[i]) = (FT_UShort)( buff[j + 1] - buff[j] );
+
+ if ( 0 != offset[i] && 0 == *(length[i]) )
+ FT_INVALID_OFFSET;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** 32bit offset sorter *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static int
+ gxv_compare_ulong_offset( FT_ULong* a,
+ FT_ULong* b )
+ {
+ if ( *a < *b )
+ return ( -1 );
+ else if ( *a > *b )
+ return ( 1 );
+ else
+ return ( 0 );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_set_length_by_ulong_offset( FT_ULong* offset,
+ FT_ULong** length,
+ FT_ULong* buff,
+ FT_UInt nmemb,
+ FT_ULong limit,
+ GXV_Validator valid)
+ {
+ FT_UInt i;
+
+
+ for ( i = 0; i < nmemb; i++ )
+ *(length[i]) = 0;
+
+ for ( i = 0; i < nmemb; i++ )
+ buff[i] = offset[i];
+ buff[nmemb] = limit;
+
+ ft_qsort( buff, ( nmemb + 1 ), sizeof ( FT_ULong ),
+ ( int(*)(const void*, const void*) )gxv_compare_ulong_offset );
+
+ if ( buff[nmemb] > limit )
+ FT_INVALID_OFFSET;
+
+ for ( i = 0; i < nmemb; i++ )
+ {
+ FT_UInt j;
+
+
+ for ( j = 0; j < nmemb; j++ )
+ if ( buff[j] == offset[i] )
+ break;
+
+ if ( j == nmemb )
+ FT_INVALID_OFFSET;
+
+ *(length[i]) = buff[j + 1] - buff[j];
+
+ if ( 0 != offset[i] && 0 == *(length[i]) )
+ FT_INVALID_OFFSET;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** scan value array and get min & max *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ gxv_array_getlimits_byte( FT_Bytes table,
+ FT_Bytes limit,
+ FT_Byte* min,
+ FT_Byte* max,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ *min = 0xFF;
+ *max = 0x00;
+
+ while ( p < limit )
+ {
+ FT_Byte val;
+
+
+ GXV_LIMIT_CHECK( 1 );
+ val = FT_NEXT_BYTE( p );
+
+ *min = (FT_Byte)FT_MIN( *min, val );
+ *max = (FT_Byte)FT_MAX( *max, val );
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_array_getlimits_ushort( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort* min,
+ FT_UShort* max,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ *min = 0xFFFFU;
+ *max = 0x0000;
+
+ while ( p < limit )
+ {
+ FT_UShort val;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ val = FT_NEXT_USHORT( p );
+
+ *min = (FT_Byte)FT_MIN( *min, val );
+ *max = (FT_Byte)FT_MAX( *max, val );
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BINSEARCHHEADER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_BinSrchHeader_
+ {
+ FT_UShort unitSize;
+ FT_UShort nUnits;
+ FT_UShort searchRange;
+ FT_UShort entrySelector;
+ FT_UShort rangeShift;
+
+ } GXV_BinSrchHeader;
+
+
+ static void
+ gxv_BinSrchHeader_check_consistency( GXV_BinSrchHeader* binSrchHeader,
+ GXV_Validator valid )
+ {
+ FT_UShort searchRange;
+ FT_UShort entrySelector;
+ FT_UShort rangeShift;
+
+
+ if ( binSrchHeader->unitSize == 0 )
+ FT_INVALID_DATA;
+
+ if ( binSrchHeader->nUnits == 0 )
+ {
+ if ( binSrchHeader->searchRange == 0 &&
+ binSrchHeader->entrySelector == 0 &&
+ binSrchHeader->rangeShift == 0 )
+ return;
+ else
+ FT_INVALID_DATA;
+ }
+
+ for ( searchRange = 1, entrySelector = 1;
+ ( searchRange * 2 ) <= binSrchHeader->nUnits &&
+ searchRange < 0x8000U;
+ searchRange *= 2, entrySelector++ )
+ ;
+
+ entrySelector--;
+ searchRange = (FT_UShort)( searchRange * binSrchHeader->unitSize );
+ rangeShift = (FT_UShort)( binSrchHeader->nUnits * binSrchHeader->unitSize
+ - searchRange );
+
+ if ( searchRange != binSrchHeader->searchRange ||
+ entrySelector != binSrchHeader->entrySelector ||
+ rangeShift != binSrchHeader->rangeShift )
+ {
+ GXV_TRACE(( "Inconsistency found in BinSrchHeader\n" ));
+ GXV_TRACE(( "originally: unitSize=%d, nUnits=%d, "
+ "searchRange=%d, entrySelector=%d, "
+ "rangeShift=%d\n",
+ binSrchHeader->unitSize, binSrchHeader->nUnits,
+ binSrchHeader->searchRange, binSrchHeader->entrySelector,
+ binSrchHeader->rangeShift ));
+ GXV_TRACE(( "calculated: unitSize=%d, nUnits=%d, "
+ "searchRange=%d, entrySelector=%d, "
+ "rangeShift=%d\n",
+ binSrchHeader->unitSize, binSrchHeader->nUnits,
+ searchRange, entrySelector, rangeShift ));
+
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+ }
+
+
+ /*
+ * parser & validator of BinSrchHeader
+ * which is used in LookupTable format 2, 4, 6.
+ *
+ * Essential parameters (unitSize, nUnits) are returned by
+ * given pointer, others (searchRange, entrySelector, rangeShift)
+ * can be calculated by essential parameters, so they are just
+ * validated and discarded.
+ *
+ * However, wrong values in searchRange, entrySelector, rangeShift
+ * won't cause fatal errors, because these parameters might be
+ * only used in old m68k font driver in MacOS.
+ * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ */
+
+ FT_LOCAL_DEF( void )
+ gxv_BinSrchHeader_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort* unitSize_p,
+ FT_UShort* nUnits_p,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ GXV_BinSrchHeader binSrchHeader;
+
+
+ GXV_NAME_ENTER( "BinSrchHeader validate" );
+
+ if ( *unitSize_p == 0 )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ binSrchHeader.unitSize = FT_NEXT_USHORT( p );
+ }
+ else
+ binSrchHeader.unitSize = *unitSize_p;
+
+ if ( *nUnits_p == 0 )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ binSrchHeader.nUnits = FT_NEXT_USHORT( p );
+ }
+ else
+ binSrchHeader.nUnits = *nUnits_p;
+
+ GXV_LIMIT_CHECK( 2 + 2 + 2 );
+ binSrchHeader.searchRange = FT_NEXT_USHORT( p );
+ binSrchHeader.entrySelector = FT_NEXT_USHORT( p );
+ binSrchHeader.rangeShift = FT_NEXT_USHORT( p );
+ GXV_TRACE(( "nUnits %d\n", binSrchHeader.nUnits ));
+
+ gxv_BinSrchHeader_check_consistency( &binSrchHeader, valid );
+
+ if ( *unitSize_p == 0 )
+ *unitSize_p = binSrchHeader.unitSize;
+
+ if ( *nUnits_p == 0 )
+ *nUnits_p = binSrchHeader.nUnits;
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LOOKUP TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define GXV_LOOKUP_VALUE_LOAD( P, SIGNSPEC ) \
+ ( P += 2, gxv_lookup_value_load( P - 2, SIGNSPEC ) )
+
+ static GXV_LookupValueDesc
+ gxv_lookup_value_load( FT_Bytes p,
+ int signspec )
+ {
+ GXV_LookupValueDesc v;
+
+
+ if ( signspec == GXV_LOOKUPVALUE_UNSIGNED )
+ v.u = FT_NEXT_USHORT( p );
+ else
+ v.s = FT_NEXT_SHORT( p );
+
+ return v;
+ }
+
+
+#define GXV_UNITSIZE_VALIDATE( FORMAT, UNITSIZE, NUNITS, CORRECTSIZE ) \
+ FT_BEGIN_STMNT \
+ if ( UNITSIZE != CORRECTSIZE ) \
+ { \
+ FT_ERROR(( "unitSize=%d differs from" \
+ "expected unitSize=%d" \
+ "in LookupTable %s", \
+ UNITSIZE, CORRECTSIZE, FORMAT )); \
+ if ( UNITSIZE != 0 && NUNITS != 0 ) \
+ { \
+ FT_ERROR(( " cannot validate anymore\n" )); \
+ FT_INVALID_FORMAT; \
+ } \
+ else \
+ FT_ERROR(( " forcibly continues\n" )); \
+ } \
+ FT_END_STMNT
+
+
+ /* ================= Simple Array Format 0 Lookup Table ================ */
+ static void
+ gxv_LookupTable_fmt0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort i;
+
+ GXV_LookupValueDesc value;
+
+
+ GXV_NAME_ENTER( "LookupTable format 0" );
+
+ GXV_LIMIT_CHECK( 2 * valid->face->num_glyphs );
+
+ for ( i = 0; i < valid->face->num_glyphs; i++ )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ if ( p + 2 >= limit ) /* some fonts have too-short fmt0 array */
+ {
+ GXV_TRACE(( "too short, glyphs %d - %d are missing\n",
+ i, valid->face->num_glyphs ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ break;
+ }
+
+ value = GXV_LOOKUP_VALUE_LOAD( p, valid->lookupval_sign );
+ valid->lookupval_func( i, value, valid );
+ }
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ /* ================= Segment Single Format 2 Loolup Table ============== */
+ /*
+ * Apple spec says:
+ *
+ * To guarantee that a binary search terminates, you must include one or
+ * more special `end of search table' values at the end of the data to
+ * be searched. The number of termination values that need to be
+ * included is table-specific. The value that indicates binary search
+ * termination is 0xFFFF.
+ *
+ * The problem is that nUnits does not include this end-marker. It's
+ * quite difficult to discriminate whether the following 0xFFFF comes from
+ * the end-marker or some next data.
+ *
+ * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ */
+ static void
+ gxv_LookupTable_fmt2_skip_endmarkers( FT_Bytes table,
+ FT_UShort unitSize,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ while ( ( p + 4 ) < valid->root->limit )
+ {
+ if ( p[0] != 0xFF || p[1] != 0xFF || /* lastGlyph */
+ p[2] != 0xFF || p[3] != 0xFF ) /* firstGlyph */
+ break;
+ p += unitSize;
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_LookupTable_fmt2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort gid;
+
+ FT_UShort unitSize;
+ FT_UShort nUnits;
+ FT_UShort unit;
+ FT_UShort lastGlyph;
+ FT_UShort firstGlyph;
+ GXV_LookupValueDesc value;
+
+
+ GXV_NAME_ENTER( "LookupTable format 2" );
+
+ unitSize = nUnits = 0;
+ gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, valid );
+ p += valid->subtable_length;
+
+ GXV_UNITSIZE_VALIDATE( "format2", unitSize, nUnits, 6 );
+
+ for ( unit = 0, gid = 0; unit < nUnits; unit++ )
+ {
+ GXV_LIMIT_CHECK( 2 + 2 + 2 );
+ lastGlyph = FT_NEXT_USHORT( p );
+ firstGlyph = FT_NEXT_USHORT( p );
+ value = GXV_LOOKUP_VALUE_LOAD( p, valid->lookupval_sign );
+
+ gxv_glyphid_validate( firstGlyph, valid );
+ gxv_glyphid_validate( lastGlyph, valid );
+
+ if ( lastGlyph < gid )
+ {
+ GXV_TRACE(( "reverse ordered segment specification:"
+ " lastGlyph[%d]=%d < lastGlyph[%d]=%d\n",
+ unit, lastGlyph, unit - 1 , gid ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ if ( lastGlyph < firstGlyph )
+ {
+ GXV_TRACE(( "reverse ordered range specification at unit %d:",
+ " lastGlyph %d < firstGlyph %d ",
+ unit, lastGlyph, firstGlyph ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+
+ if ( valid->root->level == FT_VALIDATE_TIGHT )
+ continue; /* ftxvalidator silently skips such an entry */
+
+ FT_TRACE4(( "continuing with exchanged values\n" ));
+ gid = firstGlyph;
+ firstGlyph = lastGlyph;
+ lastGlyph = gid;
+ }
+
+ for ( gid = firstGlyph; gid <= lastGlyph; gid++ )
+ valid->lookupval_func( gid, value, valid );
+ }
+
+ gxv_LookupTable_fmt2_skip_endmarkers( p, unitSize, valid );
+ p += valid->subtable_length;
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ /* ================= Segment Array Format 4 Lookup Table =============== */
+ static void
+ gxv_LookupTable_fmt4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort unit;
+ FT_UShort gid;
+
+ FT_UShort unitSize;
+ FT_UShort nUnits;
+ FT_UShort lastGlyph;
+ FT_UShort firstGlyph;
+ GXV_LookupValueDesc base_value;
+ GXV_LookupValueDesc value;
+
+
+ GXV_NAME_ENTER( "LookupTable format 4" );
+
+ unitSize = nUnits = 0;
+ gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, valid );
+ p += valid->subtable_length;
+
+ GXV_UNITSIZE_VALIDATE( "format4", unitSize, nUnits, 6 );
+
+ for ( unit = 0, gid = 0; unit < nUnits; unit++ )
+ {
+ GXV_LIMIT_CHECK( 2 + 2 );
+ lastGlyph = FT_NEXT_USHORT( p );
+ firstGlyph = FT_NEXT_USHORT( p );
+
+ gxv_glyphid_validate( firstGlyph, valid );
+ gxv_glyphid_validate( lastGlyph, valid );
+
+ if ( lastGlyph < gid )
+ {
+ GXV_TRACE(( "reverse ordered segment specification:"
+ " lastGlyph[%d]=%d < lastGlyph[%d]=%d\n",
+ unit, lastGlyph, unit - 1 , gid ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ if ( lastGlyph < firstGlyph )
+ {
+ GXV_TRACE(( "reverse ordered range specification at unit %d:",
+ " lastGlyph %d < firstGlyph %d ",
+ unit, lastGlyph, firstGlyph ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+
+ if ( valid->root->level == FT_VALIDATE_TIGHT )
+ continue; /* ftxvalidator silently skips such an entry */
+
+ FT_TRACE4(( "continuing with exchanged values\n" ));
+ gid = firstGlyph;
+ firstGlyph = lastGlyph;
+ lastGlyph = gid;
+ }
+
+ GXV_LIMIT_CHECK( 2 );
+ base_value = GXV_LOOKUP_VALUE_LOAD( p, GXV_LOOKUPVALUE_UNSIGNED );
+
+ for ( gid = firstGlyph; gid <= lastGlyph; gid++ )
+ {
+ value = valid->lookupfmt4_trans( (FT_UShort)( gid - firstGlyph ),
+ base_value,
+ limit,
+ valid );
+
+ valid->lookupval_func( gid, value, valid );
+ }
+ }
+
+ gxv_LookupTable_fmt2_skip_endmarkers( p, unitSize, valid );
+ p += valid->subtable_length;
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ /* ================= Segment Table Format 6 Lookup Table =============== */
+ static void
+ gxv_LookupTable_fmt6_skip_endmarkers( FT_Bytes table,
+ FT_UShort unitSize,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ while ( p < valid->root->limit )
+ {
+ if ( p[0] != 0xFF || p[1] != 0xFF )
+ break;
+ p += unitSize;
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_LookupTable_fmt6_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort unit;
+ FT_UShort prev_glyph;
+
+ FT_UShort unitSize;
+ FT_UShort nUnits;
+ FT_UShort glyph;
+ GXV_LookupValueDesc value;
+
+
+ GXV_NAME_ENTER( "LookupTable format 6" );
+
+ unitSize = nUnits = 0;
+ gxv_BinSrchHeader_validate( p, limit, &unitSize, &nUnits, valid );
+ p += valid->subtable_length;
+
+ GXV_UNITSIZE_VALIDATE( "format6", unitSize, nUnits, 4 );
+
+ for ( unit = 0, prev_glyph = 0; unit < nUnits; unit++ )
+ {
+ GXV_LIMIT_CHECK( 2 + 2 );
+ glyph = FT_NEXT_USHORT( p );
+ value = GXV_LOOKUP_VALUE_LOAD( p, valid->lookupval_sign );
+
+ if ( gxv_glyphid_validate( glyph, valid ) )
+ GXV_TRACE(( " endmarker found within defined range"
+ " (entry %d < nUnits=%d)\n",
+ unit, nUnits ));
+
+ if ( prev_glyph > glyph )
+ {
+ GXV_TRACE(( "current gid 0x%04x < previous gid 0x%04x\n",
+ glyph, prev_glyph ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+ prev_glyph = glyph;
+
+ valid->lookupval_func( glyph, value, valid );
+ }
+
+ gxv_LookupTable_fmt6_skip_endmarkers( p, unitSize, valid );
+ p += valid->subtable_length;
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ /* ================= Trimmed Array Format 8 Lookup Table =============== */
+ static void
+ gxv_LookupTable_fmt8_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort i;
+
+ GXV_LookupValueDesc value;
+ FT_UShort firstGlyph;
+ FT_UShort glyphCount;
+
+
+ GXV_NAME_ENTER( "LookupTable format 8" );
+
+ /* firstGlyph + glyphCount */
+ GXV_LIMIT_CHECK( 2 + 2 );
+ firstGlyph = FT_NEXT_USHORT( p );
+ glyphCount = FT_NEXT_USHORT( p );
+
+ gxv_glyphid_validate( firstGlyph, valid );
+ gxv_glyphid_validate( (FT_UShort)( firstGlyph + glyphCount ), valid );
+
+ /* valueArray */
+ for ( i = 0; i < glyphCount; i++ )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ value = GXV_LOOKUP_VALUE_LOAD( p, valid->lookupval_sign );
+ valid->lookupval_func( (FT_UShort)( firstGlyph + i ), value, valid );
+ }
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_LookupTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort format;
+
+ GXV_Validate_Func fmt_funcs_table[] =
+ {
+ gxv_LookupTable_fmt0_validate, /* 0 */
+ NULL, /* 1 */
+ gxv_LookupTable_fmt2_validate, /* 2 */
+ NULL, /* 3 */
+ gxv_LookupTable_fmt4_validate, /* 4 */
+ NULL, /* 5 */
+ gxv_LookupTable_fmt6_validate, /* 6 */
+ NULL, /* 7 */
+ gxv_LookupTable_fmt8_validate, /* 8 */
+ };
+
+ GXV_Validate_Func func;
+
+
+ GXV_NAME_ENTER( "LookupTable" );
+
+ /* lookuptbl_head may be used in fmt4 transit function. */
+ valid->lookuptbl_head = table;
+
+ /* format */
+ GXV_LIMIT_CHECK( 2 );
+ format = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " (format %d)\n", format ));
+
+ if ( format > 8 )
+ FT_INVALID_FORMAT;
+
+ func = fmt_funcs_table[format];
+ if ( func == NULL )
+ FT_INVALID_FORMAT;
+
+ func( p, limit, valid );
+ p += valid->subtable_length;
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Glyph ID *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( FT_Int )
+ gxv_glyphid_validate( FT_UShort gid,
+ GXV_Validator valid )
+ {
+ FT_Face face;
+
+
+ if ( gid == 0xFFFFU )
+ {
+ GXV_EXIT;
+ return 1;
+ }
+
+ face = valid->face;
+ if ( face->num_glyphs < gid )
+ {
+ GXV_TRACE(( " gxv_glyphid_check() gid overflow: num_glyphs %d < %d\n",
+ face->num_glyphs, gid ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CONTROL POINT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_ctlPoint_validate( FT_UShort gid,
+ FT_Short ctl_point,
+ GXV_Validator valid )
+ {
+ FT_Face face;
+ FT_Error error;
+
+ FT_GlyphSlot glyph;
+ FT_Outline outline;
+ short n_points;
+
+
+ face = valid->face;
+
+ error = FT_Load_Glyph( face,
+ gid,
+ FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM );
+ if ( error )
+ FT_INVALID_GLYPH_ID;
+
+ glyph = face->glyph;
+ outline = glyph->outline;
+ n_points = outline.n_points;
+
+
+ if ( !( ctl_point < n_points ) )
+ FT_INVALID_DATA;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SFNT NAME *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_sfntName_validate( FT_UShort name_index,
+ FT_UShort min_index,
+ FT_UShort max_index,
+ GXV_Validator valid )
+ {
+ FT_SfntName name;
+ FT_UInt i;
+ FT_UInt nnames;
+
+
+ GXV_NAME_ENTER( "sfntName" );
+
+ if ( name_index < min_index || max_index < name_index )
+ FT_INVALID_FORMAT;
+
+ nnames = FT_Get_Sfnt_Name_Count( valid->face );
+ for ( i = 0; i < nnames; i++ )
+ {
+ if ( FT_Get_Sfnt_Name( valid->face, i, &name ) != FT_Err_Ok )
+ continue ;
+
+ if ( name.name_id == name_index )
+ goto Out;
+ }
+
+ GXV_TRACE(( " nameIndex = %d (UNTITLED)\n", name_index ));
+ FT_INVALID_DATA;
+ goto Exit; /* make compiler happy */
+
+ Out:
+ FT_TRACE1(( " nameIndex = %d (", name_index ));
+ GXV_TRACE_HEXDUMP_SFNTNAME( name );
+ FT_TRACE1(( ")\n" ));
+
+ Exit:
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** STATE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* -------------------------- Class Table --------------------------- */
+
+ /*
+ * highestClass specifies how many classes are defined in this
+ * Class Subtable. Apple spec does not mention whether undefined
+ * holes in the class (e.g.: 0-3 are predefined, 4 is unused, 5 is used)
+ * are permitted. At present, holes in a defined class are not checked.
+ * -- suzuki toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ */
+
+ static void
+ gxv_ClassTable_validate( FT_Bytes table,
+ FT_UShort* length_p,
+ FT_UShort stateSize,
+ FT_Byte* maxClassID_p,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = table + *length_p;
+ FT_UShort firstGlyph;
+ FT_UShort nGlyphs;
+
+
+ GXV_NAME_ENTER( "ClassTable" );
+
+ *maxClassID_p = 3; /* Classes 0, 2, and 3 are predefined */
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ firstGlyph = FT_NEXT_USHORT( p );
+ nGlyphs = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( " (firstGlyph = %d, nGlyphs = %d)\n", firstGlyph, nGlyphs ));
+
+ if ( !nGlyphs )
+ goto Out;
+
+ gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs ), valid );
+
+ {
+ FT_Byte nGlyphInClass[256];
+ FT_Byte classID;
+ FT_UShort i;
+
+
+ ft_memset( nGlyphInClass, 0, 256 );
+
+
+ for ( i = 0; i < nGlyphs; i++ )
+ {
+ GXV_LIMIT_CHECK( 1 );
+ classID = FT_NEXT_BYTE( p );
+ switch ( classID )
+ {
+ /* following classes should not appear in class array */
+ case 0: /* end of text */
+ case 2: /* out of bounds */
+ case 3: /* end of line */
+ FT_INVALID_DATA;
+ break;
+
+ case 1: /* out of bounds */
+ default: /* user-defined: 4 - ( stateSize - 1 ) */
+ if ( classID >= stateSize )
+ FT_INVALID_DATA; /* assign glyph to undefined state */
+
+ nGlyphInClass[classID]++;
+ break;
+ }
+ }
+ *length_p = (FT_UShort)( p - table );
+
+ /* scan max ClassID in use */
+ for ( i = 0; i < stateSize; i++ )
+ if ( ( 3 < i ) && ( nGlyphInClass[i] > 0 ) )
+ *maxClassID_p = (FT_Byte)i; /* XXX: Check Range? */
+ }
+
+ Out:
+ GXV_TRACE(( "Declared stateSize=0x%02x, Used maxClassID=0x%02x\n",
+ stateSize, *maxClassID_p ));
+ GXV_EXIT;
+ }
+
+
+ /* --------------------------- State Array ----------------------------- */
+
+ static void
+ gxv_StateArray_validate( FT_Bytes table,
+ FT_UShort* length_p,
+ FT_Byte maxClassID,
+ FT_UShort stateSize,
+ FT_Byte* maxState_p,
+ FT_Byte* maxEntry_p,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = table + *length_p;
+ FT_Byte clazz;
+ FT_Byte entry;
+
+ FT_UNUSED( stateSize ); /* for the non-debugging case */
+
+
+ GXV_NAME_ENTER( "StateArray" );
+
+ GXV_TRACE(( "parse %d bytes by stateSize=%d maxClassID=%d\n",
+ (int)(*length_p), stateSize, (int)(maxClassID) ));
+
+ /*
+ * 2 states are predefined and must be described in StateArray:
+ * state 0 (start of text), 1 (start of line)
+ */
+ GXV_LIMIT_CHECK( ( 1 + maxClassID ) * 2 );
+
+ *maxState_p = 0;
+ *maxEntry_p = 0;
+
+ /* read if enough to read another state */
+ while ( p + ( 1 + maxClassID ) <= limit )
+ {
+ (*maxState_p)++;
+ for ( clazz = 0; clazz <= maxClassID; clazz++ )
+ {
+ entry = FT_NEXT_BYTE( p );
+ *maxEntry_p = (FT_Byte)FT_MAX( *maxEntry_p, entry );
+ }
+ }
+ GXV_TRACE(( "parsed: maxState=%d, maxEntry=%d\n",
+ *maxState_p, *maxEntry_p ));
+
+ *length_p = (FT_UShort)( p - table );
+
+ GXV_EXIT;
+ }
+
+
+ /* --------------------------- Entry Table ----------------------------- */
+
+ static void
+ gxv_EntryTable_validate( FT_Bytes table,
+ FT_UShort* length_p,
+ FT_Byte maxEntry,
+ FT_UShort stateArray,
+ FT_UShort stateArray_length,
+ FT_Byte maxClassID,
+ FT_Bytes statetable_table,
+ FT_Bytes statetable_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = table + *length_p;
+ FT_Byte entry;
+ FT_Byte state;
+ FT_Int entrySize = 2 + 2 + GXV_GLYPHOFFSET_SIZE( statetable );
+
+ GXV_XStateTable_GlyphOffsetDesc glyphOffset;
+
+
+ GXV_NAME_ENTER( "EntryTable" );
+
+ GXV_TRACE(( "maxEntry=%d entrySize=%d\n", maxEntry, entrySize ));
+
+ if ( ( maxEntry + 1 ) * entrySize > *length_p )
+ {
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_TOO_SHORT;
+
+ /* ftxvalidator and FontValidator both warn and continue */
+ maxEntry = (FT_Byte)( *length_p / entrySize - 1 );
+ GXV_TRACE(( "too large maxEntry, shrinking to %d fit EntryTable length\n",
+ maxEntry ));
+ }
+
+ for ( entry = 0; entry <= maxEntry; entry++ )
+ {
+ FT_UShort newState;
+ FT_UShort flags;
+
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ newState = FT_NEXT_USHORT( p );
+ flags = FT_NEXT_USHORT( p );
+
+
+ if ( newState < stateArray ||
+ stateArray + stateArray_length < newState )
+ {
+ GXV_TRACE(( " newState offset 0x%04x is out of stateArray\n",
+ newState ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ continue;
+ }
+
+ if ( 0 != ( ( newState - stateArray ) % ( 1 + maxClassID ) ) )
+ {
+ GXV_TRACE(( " newState offset 0x%04x is not aligned to %d-classes\n",
+ newState, 1 + maxClassID ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ continue;
+ }
+
+ state = (FT_Byte)( ( newState - stateArray ) / ( 1 + maxClassID ) );
+
+ switch ( GXV_GLYPHOFFSET_FMT( statetable ) )
+ {
+ case GXV_GLYPHOFFSET_NONE:
+ glyphOffset.uc = 0; /* make compiler happy */
+ break;
+
+ case GXV_GLYPHOFFSET_UCHAR:
+ glyphOffset.uc = FT_NEXT_BYTE( p );
+ break;
+
+ case GXV_GLYPHOFFSET_CHAR:
+ glyphOffset.c = FT_NEXT_CHAR( p );
+ break;
+
+ case GXV_GLYPHOFFSET_USHORT:
+ glyphOffset.u = FT_NEXT_USHORT( p );
+ break;
+
+ case GXV_GLYPHOFFSET_SHORT:
+ glyphOffset.s = FT_NEXT_SHORT( p );
+ break;
+
+ case GXV_GLYPHOFFSET_ULONG:
+ glyphOffset.ul = FT_NEXT_ULONG( p );
+ break;
+
+ case GXV_GLYPHOFFSET_LONG:
+ glyphOffset.l = FT_NEXT_LONG( p );
+ break;
+
+ default:
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_FORMAT;
+ goto Exit;
+ }
+
+ if ( NULL != valid->statetable.entry_validate_func )
+ valid->statetable.entry_validate_func( state,
+ flags,
+ glyphOffset,
+ statetable_table,
+ statetable_limit,
+ valid );
+ }
+
+ Exit:
+ *length_p = (FT_UShort)( p - table );
+
+ GXV_EXIT;
+ }
+
+
+ /* =========================== State Table ============================= */
+
+ FT_LOCAL_DEF( void )
+ gxv_StateTable_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_UShort o[3];
+ FT_UShort* l[3];
+ FT_UShort buff[4];
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+
+ gxv_set_length_by_ushort_offset( o, l, buff, 3, table_size, valid );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_StateTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort stateSize;
+ FT_UShort classTable; /* offset to Class(Sub)Table */
+ FT_UShort stateArray; /* offset to StateArray */
+ FT_UShort entryTable; /* offset to EntryTable */
+
+ FT_UShort classTable_length;
+ FT_UShort stateArray_length;
+ FT_UShort entryTable_length;
+ FT_Byte maxClassID;
+ FT_Byte maxState;
+ FT_Byte maxEntry;
+
+ GXV_StateTable_Subtable_Setup_Func setup_func;
+
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER( "StateTable" );
+
+ GXV_TRACE(( "StateTable header\n" ));
+
+ GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 );
+ stateSize = FT_NEXT_USHORT( p );
+ classTable = FT_NEXT_USHORT( p );
+ stateArray = FT_NEXT_USHORT( p );
+ entryTable = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( "stateSize=0x%04x\n", stateSize ));
+ GXV_TRACE(( "offset to classTable=0x%04x\n", classTable ));
+ GXV_TRACE(( "offset to stateArray=0x%04x\n", stateArray ));
+ GXV_TRACE(( "offset to entryTable=0x%04x\n", entryTable ));
+
+ if ( stateSize > 0xFF )
+ FT_INVALID_DATA;
+
+ if ( valid->statetable.optdata_load_func != NULL )
+ valid->statetable.optdata_load_func( p, limit, valid );
+
+ if ( valid->statetable.subtable_setup_func != NULL)
+ setup_func = valid->statetable.subtable_setup_func;
+ else
+ setup_func = gxv_StateTable_subtable_setup;
+
+ setup_func( (FT_UShort)( limit - table ),
+ classTable,
+ stateArray,
+ entryTable,
+ &classTable_length,
+ &stateArray_length,
+ &entryTable_length,
+ valid );
+
+ GXV_TRACE(( "StateTable Subtables\n" ));
+
+ if ( classTable != 0 )
+ gxv_ClassTable_validate( table + classTable,
+ &classTable_length,
+ stateSize,
+ &maxClassID,
+ valid );
+ else
+ maxClassID = (FT_Byte)( stateSize - 1 );
+
+ if ( stateArray != 0 )
+ gxv_StateArray_validate( table + stateArray,
+ &stateArray_length,
+ maxClassID,
+ stateSize,
+ &maxState,
+ &maxEntry,
+ valid );
+ else
+ {
+ maxState = 1; /* 0:start of text, 1:start of line are predefined */
+ maxEntry = 0;
+ }
+
+ if ( maxEntry > 0 && entryTable == 0 )
+ FT_INVALID_OFFSET;
+
+ if ( entryTable != 0 )
+ gxv_EntryTable_validate( table + entryTable,
+ &entryTable_length,
+ maxEntry,
+ stateArray,
+ stateArray_length,
+ maxClassID,
+ table,
+ limit,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+ /* ================= eXtended State Table (for morx) =================== */
+
+ FT_LOCAL_DEF( void )
+ gxv_XStateTable_subtable_setup( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_ULong o[3];
+ FT_ULong* l[3];
+ FT_ULong buff[4];
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+
+ gxv_set_length_by_ulong_offset( o, l, buff, 4, table_size, valid );
+ }
+
+
+ static void
+ gxv_XClassTable_lookupval_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( glyph );
+
+ if ( value.u >= valid->xstatetable.nClasses )
+ FT_INVALID_DATA;
+ if ( value.u > valid->xstatetable.maxClassID )
+ valid->xstatetable.maxClassID = value.u;
+ }
+
+
+ /*
+ +===============+ --------+
+ | lookup header | |
+ +===============+ |
+ | BinSrchHeader | |
+ +===============+ |
+ | lastGlyph[0] | |
+ +---------------+ |
+ | firstGlyph[0] | | head of lookup table
+ +---------------+ | +
+ | offset[0] | -> | offset [byte]
+ +===============+ | +
+ | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte]
+ +---------------+ |
+ | firstGlyph[1] | |
+ +---------------+ |
+ | offset[1] | |
+ +===============+ |
+ |
+ .... |
+ |
+ 16bit value array |
+ +===============+ |
+ | value | <-------+
+ ....
+ */
+ static GXV_LookupValueDesc
+ gxv_XClassTable_lookupfmt4_transit( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ /* XXX: check range? */
+ offset = (FT_UShort)( base_value.u +
+ relative_gindex * sizeof ( FT_UShort ) );
+
+ p = valid->lookuptbl_head + offset;
+ limit = lookuptbl_limit;
+
+ GXV_LIMIT_CHECK ( 2 );
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ static void
+ gxv_XStateArray_validate( FT_Bytes table,
+ FT_ULong* length_p,
+ FT_UShort maxClassID,
+ FT_ULong stateSize,
+ FT_UShort* maxState_p,
+ FT_UShort* maxEntry_p,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = table + *length_p;
+ FT_UShort clazz;
+ FT_UShort entry;
+
+ FT_UNUSED( stateSize ); /* for the non-debugging case */
+
+
+ GXV_NAME_ENTER( "XStateArray" );
+
+ GXV_TRACE(( "parse % 3d bytes by stateSize=% 3d maxClassID=% 3d\n",
+ (int)(*length_p), stateSize, (int)(maxClassID) ));
+
+ /*
+ * 2 states are predefined and must be described:
+ * state 0 (start of text), 1 (start of line)
+ */
+ GXV_LIMIT_CHECK( ( 1 + maxClassID ) * 2 * 2 );
+
+ *maxState_p = 0;
+ *maxEntry_p = 0;
+
+ /* read if enough to read another state */
+ while ( p + ( ( 1 + maxClassID ) * 2 ) <= limit )
+ {
+ (*maxState_p)++;
+ for ( clazz = 0; clazz <= maxClassID; clazz++ )
+ {
+ entry = FT_NEXT_USHORT( p );
+ *maxEntry_p = (FT_UShort)FT_MAX( *maxEntry_p, entry );
+ }
+ }
+ GXV_TRACE(( "parsed: maxState=%d, maxEntry=%d\n",
+ *maxState_p, *maxEntry_p ));
+
+ *length_p = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_XEntryTable_validate( FT_Bytes table,
+ FT_ULong* length_p,
+ FT_UShort maxEntry,
+ FT_ULong stateArray_length,
+ FT_UShort maxClassID,
+ FT_Bytes xstatetable_table,
+ FT_Bytes xstatetable_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = table + *length_p;
+ FT_UShort entry;
+ FT_UShort state;
+ FT_Int entrySize = 2 + 2 + GXV_GLYPHOFFSET_SIZE( xstatetable );
+
+
+ GXV_NAME_ENTER( "XEntryTable" );
+ GXV_TRACE(( "maxEntry=%d entrySize=%d\n", maxEntry, entrySize ));
+
+ if ( ( p + ( maxEntry + 1 ) * entrySize ) > limit )
+ FT_INVALID_TOO_SHORT;
+
+ for (entry = 0; entry <= maxEntry ; entry++ )
+ {
+ FT_UShort newState_idx;
+ FT_UShort flags;
+ GXV_XStateTable_GlyphOffsetDesc glyphOffset;
+
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ newState_idx = FT_NEXT_USHORT( p );
+ flags = FT_NEXT_USHORT( p );
+
+ if ( stateArray_length < (FT_ULong)( newState_idx * 2 ) )
+ {
+ GXV_TRACE(( " newState index 0x%04x points out of stateArray\n",
+ newState_idx ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ }
+
+ state = (FT_UShort)( newState_idx / ( 1 + maxClassID ) );
+ if ( 0 != ( newState_idx % ( 1 + maxClassID ) ) )
+ {
+ FT_TRACE4(( "-> new state = %d (supposed)\n"
+ "but newState index 0x%04x is not aligned to %d-classes\n",
+ state, newState_idx, 1 + maxClassID ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ }
+
+ switch ( GXV_GLYPHOFFSET_FMT( xstatetable ) )
+ {
+ case GXV_GLYPHOFFSET_NONE:
+ glyphOffset.uc = 0; /* make compiler happy */
+ break;
+
+ case GXV_GLYPHOFFSET_UCHAR:
+ glyphOffset.uc = FT_NEXT_BYTE( p );
+ break;
+
+ case GXV_GLYPHOFFSET_CHAR:
+ glyphOffset.c = FT_NEXT_CHAR( p );
+ break;
+
+ case GXV_GLYPHOFFSET_USHORT:
+ glyphOffset.u = FT_NEXT_USHORT( p );
+ break;
+
+ case GXV_GLYPHOFFSET_SHORT:
+ glyphOffset.s = FT_NEXT_SHORT( p );
+ break;
+
+ case GXV_GLYPHOFFSET_ULONG:
+ glyphOffset.ul = FT_NEXT_ULONG( p );
+ break;
+
+ case GXV_GLYPHOFFSET_LONG:
+ glyphOffset.l = FT_NEXT_LONG( p );
+ break;
+
+ default:
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_FORMAT;
+ goto Exit;
+ }
+
+ if ( NULL != valid->xstatetable.entry_validate_func )
+ valid->xstatetable.entry_validate_func( state,
+ flags,
+ glyphOffset,
+ xstatetable_table,
+ xstatetable_limit,
+ valid );
+ }
+
+ Exit:
+ *length_p = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_XStateTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ /* StateHeader members */
+ FT_ULong classTable; /* offset to Class(Sub)Table */
+ FT_ULong stateArray; /* offset to StateArray */
+ FT_ULong entryTable; /* offset to EntryTable */
+
+ FT_ULong classTable_length;
+ FT_ULong stateArray_length;
+ FT_ULong entryTable_length;
+ FT_UShort maxState;
+ FT_UShort maxEntry;
+
+ GXV_XStateTable_Subtable_Setup_Func setup_func;
+
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER( "XStateTable" );
+
+ GXV_TRACE(( "XStateTable header\n" ));
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 );
+ valid->xstatetable.nClasses = FT_NEXT_ULONG( p );
+ classTable = FT_NEXT_ULONG( p );
+ stateArray = FT_NEXT_ULONG( p );
+ entryTable = FT_NEXT_ULONG( p );
+
+ GXV_TRACE(( "nClasses =0x%08x\n", valid->xstatetable.nClasses ));
+ GXV_TRACE(( "offset to classTable=0x%08x\n", classTable ));
+ GXV_TRACE(( "offset to stateArray=0x%08x\n", stateArray ));
+ GXV_TRACE(( "offset to entryTable=0x%08x\n", entryTable ));
+
+ if ( valid->xstatetable.nClasses > 0xFFFFU )
+ FT_INVALID_DATA;
+
+ GXV_TRACE(( "StateTable Subtables\n" ));
+
+ if ( valid->xstatetable.optdata_load_func != NULL )
+ valid->xstatetable.optdata_load_func( p, limit, valid );
+
+ if ( valid->xstatetable.subtable_setup_func != NULL )
+ setup_func = valid->xstatetable.subtable_setup_func;
+ else
+ setup_func = gxv_XStateTable_subtable_setup;
+
+ setup_func( limit - table,
+ classTable,
+ stateArray,
+ entryTable,
+ &classTable_length,
+ &stateArray_length,
+ &entryTable_length,
+ valid );
+
+ if ( classTable != 0 )
+ {
+ valid->xstatetable.maxClassID = 0;
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_XClassTable_lookupval_validate;
+ valid->lookupfmt4_trans = gxv_XClassTable_lookupfmt4_transit;
+ gxv_LookupTable_validate( table + classTable,
+ table + classTable + classTable_length,
+ valid );
+ if ( valid->subtable_length < classTable_length )
+ classTable_length = valid->subtable_length;
+ }
+ else
+ {
+ /* XXX: check range? */
+ valid->xstatetable.maxClassID =
+ (FT_UShort)( valid->xstatetable.nClasses - 1 );
+ }
+
+ if ( stateArray != 0 )
+ gxv_XStateArray_validate( table + stateArray,
+ &stateArray_length,
+ valid->xstatetable.maxClassID,
+ valid->xstatetable.nClasses,
+ &maxState,
+ &maxEntry,
+ valid );
+ else
+ {
+ maxState = 1; /* 0:start of text, 1:start of line are predefined */
+ maxEntry = 0;
+ }
+
+ if ( maxEntry > 0 && entryTable == 0 )
+ FT_INVALID_OFFSET;
+
+ if ( entryTable != 0 )
+ gxv_XEntryTable_validate( table + entryTable,
+ &entryTable_length,
+ maxEntry,
+ stateArray_length,
+ valid->xstatetable.maxClassID,
+ table,
+ limit,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Table overlapping *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static int
+ gxv_compare_ranges( FT_Bytes table1_start,
+ FT_ULong table1_length,
+ FT_Bytes table2_start,
+ FT_ULong table2_length )
+ {
+ if ( table1_start == table2_start )
+ {
+ if ( ( table1_length == 0 || table2_length == 0 ) )
+ goto Out;
+ }
+ else if ( table1_start < table2_start )
+ {
+ if ( ( table1_start + table1_length ) <= table2_start )
+ goto Out;
+ }
+ else if ( table1_start > table2_start )
+ {
+ if ( ( table1_start >= table2_start + table2_length ) )
+ goto Out;
+ }
+ return 1;
+
+ Out:
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_odtect_add_range( FT_Bytes start,
+ FT_ULong length,
+ const FT_String* name,
+ GXV_odtect_Range odtect )
+ {
+ odtect->range[ odtect->nRanges ].start = start;
+ odtect->range[ odtect->nRanges ].length = length;
+ odtect->range[ odtect->nRanges ].name = (FT_String*)name;
+ odtect->nRanges++;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_odtect_validate( GXV_odtect_Range odtect,
+ GXV_Validator valid )
+ {
+ FT_UInt i, j;
+
+
+ GXV_NAME_ENTER( "check overlap among multi ranges" );
+
+ for ( i = 0; i < odtect->nRanges; i++ )
+ for ( j = 0; j < i; j++ )
+ if ( 0 != gxv_compare_ranges( odtect->range[i].start,
+ odtect->range[i].length,
+ odtect->range[j].start,
+ odtect->range[j].length ) )
+ {
+ if ( odtect->range[i].name || odtect->range[j].name )
+ GXV_TRACE(( "found overlap between range %d and range %d\n",
+ i, j ));
+ else
+ GXV_TRACE(( "found overlap between `%s' and `%s\'\n",
+ odtect->range[i].name,
+ odtect->range[j].name ));
+ FT_INVALID_OFFSET;
+ }
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvcommn.h b/src/gxvalid/gxvcommn.h
new file mode 100644
index 0000000..0128eca
--- /dev/null
+++ b/src/gxvalid/gxvcommn.h
@@ -0,0 +1,560 @@
+/***************************************************************************/
+/* */
+/* gxvcommn.h */
+/* */
+/* TrueTypeGX/AAT common tables validation (specification). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+ /*
+ * keywords in variable naming
+ * ---------------------------
+ * table: Of type FT_Bytes, pointing to the start of this table/subtable.
+ * limit: Of type FT_Bytes, pointing to the end of this table/subtable,
+ * including padding for alignment.
+ * offset: Of type FT_UInt, the number of octets from the start to target.
+ * length: Of type FT_UInt, the number of octets from the start to the
+ * end in this table/subtable, including padding for alignment.
+ *
+ * _MIN, _MAX: Should be added to the tail of macros, as INT_MIN, etc.
+ */
+
+
+#ifndef __GXVCOMMN_H__
+#define __GXVCOMMN_H__
+
+
+#include <ft2build.h>
+#include "gxvalid.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_SFNT_NAMES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** VALIDATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_ValidatorRec_* GXV_Validator;
+
+
+#define DUMMY_LIMIT 0
+
+ typedef void
+ (*GXV_Validate_Func)( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+
+ /* ====================== LookupTable Validator ======================== */
+
+ typedef union GXV_LookupValueDesc_
+ {
+ FT_UShort u;
+ FT_Short s;
+
+ } GXV_LookupValueDesc;
+
+ typedef enum GXV_LookupValue_SignSpec_
+ {
+ GXV_LOOKUPVALUE_UNSIGNED = 0,
+ GXV_LOOKUPVALUE_SIGNED
+
+ } GXV_LookupValue_SignSpec;
+
+
+ typedef void
+ (*GXV_Lookup_Value_Validate_Func)( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid );
+
+ typedef GXV_LookupValueDesc
+ (*GXV_Lookup_Fmt4_Transit_Func)( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid );
+
+
+ /* ====================== StateTable Validator ========================= */
+
+ typedef enum GXV_GlyphOffset_Format_
+ {
+ GXV_GLYPHOFFSET_NONE = -1,
+ GXV_GLYPHOFFSET_UCHAR = 2,
+ GXV_GLYPHOFFSET_CHAR,
+ GXV_GLYPHOFFSET_USHORT = 4,
+ GXV_GLYPHOFFSET_SHORT,
+ GXV_GLYPHOFFSET_ULONG = 8,
+ GXV_GLYPHOFFSET_LONG
+
+ } GXV_GlyphOffset_Format;
+
+
+#define GXV_GLYPHOFFSET_FMT( table ) \
+ ( valid->table.entry_glyphoffset_fmt )
+
+#define GXV_GLYPHOFFSET_SIZE( table ) \
+ ( valid->table.entry_glyphoffset_fmt / 2 )
+
+
+ /* ----------------------- 16bit StateTable ---------------------------- */
+
+ typedef union GXV_StateTable_GlyphOffsetDesc_
+ {
+ FT_Byte uc;
+ FT_UShort u; /* same as GXV_LookupValueDesc */
+ FT_ULong ul;
+ FT_Char c;
+ FT_Short s; /* same as GXV_LookupValueDesc */
+ FT_Long l;
+
+ } GXV_StateTable_GlyphOffsetDesc;
+
+
+ typedef void
+ (*GXV_StateTable_Subtable_Setup_Func)( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid );
+
+ typedef void
+ (*GXV_StateTable_Entry_Validate_Func)(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes statetable_table,
+ FT_Bytes statetable_limit,
+ GXV_Validator valid );
+
+ typedef void
+ (*GXV_StateTable_OptData_Load_Func)( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ typedef struct GXV_StateTable_ValidatorRec_
+ {
+ GXV_GlyphOffset_Format entry_glyphoffset_fmt;
+ void* optdata;
+
+ GXV_StateTable_Subtable_Setup_Func subtable_setup_func;
+ GXV_StateTable_Entry_Validate_Func entry_validate_func;
+ GXV_StateTable_OptData_Load_Func optdata_load_func;
+
+ } GXV_StateTable_ValidatorRec, *GXV_StateTable_ValidatorRecData;
+
+
+ /* ---------------------- 32bit XStateTable ---------------------------- */
+
+ typedef GXV_StateTable_GlyphOffsetDesc GXV_XStateTable_GlyphOffsetDesc;
+
+ typedef void
+ (*GXV_XStateTable_Subtable_Setup_Func)( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid );
+
+ typedef void
+ (*GXV_XStateTable_Entry_Validate_Func)(
+ FT_UShort state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes xstatetable_table,
+ FT_Bytes xstatetable_limit,
+ GXV_Validator valid );
+
+
+ typedef GXV_StateTable_OptData_Load_Func GXV_XStateTable_OptData_Load_Func;
+
+
+ typedef struct GXV_XStateTable_ValidatorRec_
+ {
+ int entry_glyphoffset_fmt;
+ void* optdata;
+
+ GXV_XStateTable_Subtable_Setup_Func subtable_setup_func;
+ GXV_XStateTable_Entry_Validate_Func entry_validate_func;
+ GXV_XStateTable_OptData_Load_Func optdata_load_func;
+
+ FT_ULong nClasses;
+ FT_UShort maxClassID;
+
+ } GXV_XStateTable_ValidatorRec, *GXV_XStateTable_ValidatorRecData;
+
+
+ /* ===================================================================== */
+
+ typedef struct GXV_ValidatorRec_
+ {
+ FT_Validator root;
+
+ FT_Face face;
+ void* table_data;
+
+ FT_ULong subtable_length;
+
+ GXV_LookupValue_SignSpec lookupval_sign;
+ GXV_Lookup_Value_Validate_Func lookupval_func;
+ GXV_Lookup_Fmt4_Transit_Func lookupfmt4_trans;
+ FT_Bytes lookuptbl_head;
+
+ GXV_StateTable_ValidatorRec statetable;
+ GXV_XStateTable_ValidatorRec xstatetable;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_UInt debug_indent;
+ const FT_String* debug_function_name[3];
+#endif
+
+ } GXV_ValidatorRec;
+
+
+#define GXV_TABLE_DATA( tag, field ) \
+ ( ( (GXV_ ## tag ## _Data)valid->table_data )->field )
+
+#undef FT_INVALID_
+#define FT_INVALID_( _prefix, _error ) \
+ ft_validator_error( valid->root, _prefix ## _error )
+
+#define GXV_LIMIT_CHECK( _count ) \
+ FT_BEGIN_STMNT \
+ if ( p + _count > ( limit? limit : valid->root->limit ) ) \
+ FT_INVALID_TOO_SHORT; \
+ FT_END_STMNT
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+#define GXV_INIT valid->debug_indent = 0
+
+#define GXV_NAME_ENTER( name ) \
+ FT_BEGIN_STMNT \
+ valid->debug_indent += 2; \
+ FT_TRACE4(( "%*.s", valid->debug_indent, 0 )); \
+ FT_TRACE4(( "%s table\n", name )); \
+ FT_END_STMNT
+
+#define GXV_EXIT valid->debug_indent -= 2
+
+#define GXV_TRACE( s ) \
+ FT_BEGIN_STMNT \
+ FT_TRACE4(( "%*.s", valid->debug_indent, 0 )); \
+ FT_TRACE4( s ); \
+ FT_END_STMNT
+
+#else /* !FT_DEBUG_LEVEL_TRACE */
+
+#define GXV_INIT do ; while ( 0 )
+#define GXV_NAME_ENTER( name ) do ; while ( 0 )
+#define GXV_EXIT do ; while ( 0 )
+
+#define GXV_TRACE( s ) do ; while ( 0 )
+
+#endif /* !FT_DEBUG_LEVEL_TRACE */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** 32bit alignment checking *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define GXV_32BIT_ALIGNMENT_VALIDATE( a ) \
+ FT_BEGIN_STMNT \
+ { \
+ if ( 0 != ( (a) % 4 ) ) \
+ FT_INVALID_OFFSET ; \
+ } \
+ FT_END_STMNT
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Dumping Binary Data *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define GXV_TRACE_HEXDUMP( p, len ) \
+ FT_BEGIN_STMNT \
+ { \
+ FT_Bytes b; \
+ \
+ \
+ for ( b = p; b < (FT_Bytes)p + len; b++ ) \
+ FT_TRACE1(("\\x%02x", *b)) ; \
+ } \
+ FT_END_STMNT
+
+#define GXV_TRACE_HEXDUMP_C( p, len ) \
+ FT_BEGIN_STMNT \
+ { \
+ FT_Bytes b; \
+ \
+ \
+ for ( b = p; b < (FT_Bytes)p + len; b++ ) \
+ if ( 0x40 < *b && *b < 0x7e ) \
+ FT_TRACE1(("%c", *b)) ; \
+ else \
+ FT_TRACE1(("\\x%02x", *b)) ; \
+ } \
+ FT_END_STMNT
+
+#define GXV_TRACE_HEXDUMP_SFNTNAME( n ) \
+ GXV_TRACE_HEXDUMP( n.string, n.string_len )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LOOKUP TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ gxv_BinSrchHeader_validate( FT_Bytes p,
+ FT_Bytes limit,
+ FT_UShort* unitSize_p,
+ FT_UShort* nUnits_p,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_LookupTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Glyph ID *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( FT_Int )
+ gxv_glyphid_validate( FT_UShort gid,
+ GXV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CONTROL POINT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ gxv_ctlPoint_validate( FT_UShort gid,
+ FT_Short ctl_point,
+ GXV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SFNT NAME *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ gxv_sfntName_validate( FT_UShort name_index,
+ FT_UShort min_index,
+ FT_UShort max_index,
+ GXV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** STATE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ gxv_StateTable_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_XStateTable_subtable_setup( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_StateTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_XStateTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY MACROS AND FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ gxv_array_getlimits_byte( FT_Bytes table,
+ FT_Bytes limit,
+ FT_Byte* min,
+ FT_Byte* max,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_array_getlimits_ushort( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort* min,
+ FT_UShort* max,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_set_length_by_ushort_offset( FT_UShort* offset,
+ FT_UShort** length,
+ FT_UShort* buff,
+ FT_UInt nmemb,
+ FT_UShort limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_set_length_by_ulong_offset( FT_ULong* offset,
+ FT_ULong** length,
+ FT_ULong* buff,
+ FT_UInt nmemb,
+ FT_ULong limit,
+ GXV_Validator valid);
+
+
+#define GXV_SUBTABLE_OFFSET_CHECK( _offset ) \
+ FT_BEGIN_STMNT \
+ if ( (_offset) > valid->subtable_length ) \
+ FT_INVALID_OFFSET; \
+ FT_END_STMNT
+
+#define GXV_SUBTABLE_LIMIT_CHECK( _count ) \
+ FT_BEGIN_STMNT \
+ if ( ( p + (_count) - valid->subtable_start ) > \
+ valid->subtable_length ) \
+ FT_INVALID_TOO_SHORT; \
+ FT_END_STMNT
+
+#define GXV_USHORT_TO_SHORT( _us ) \
+ ( ( 0x8000U < ( _us ) ) ? ( ( _us ) - 0x8000U ) : ( _us ) )
+
+#define GXV_STATETABLE_HEADER_SIZE ( 2 + 2 + 2 + 2 )
+#define GXV_STATEHEADER_SIZE GXV_STATETABLE_HEADER_SIZE
+
+#define GXV_XSTATETABLE_HEADER_SIZE ( 4 + 4 + 4 + 4 )
+#define GXV_XSTATEHEADER_SIZE GXV_XSTATETABLE_HEADER_SIZE
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Table overlapping *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_odtect_DataRec_
+ {
+ FT_Bytes start;
+ FT_ULong length;
+ FT_String* name;
+
+ } GXV_odtect_DataRec, *GXV_odtect_Data;
+
+ typedef struct GXV_odtect_RangeRec_
+ {
+ FT_UInt nRanges;
+ GXV_odtect_Data range;
+
+ } GXV_odtect_RangeRec, *GXV_odtect_Range;
+
+
+ FT_LOCAL( void )
+ gxv_odtect_add_range( FT_Bytes start,
+ FT_ULong length,
+ const FT_String* name,
+ GXV_odtect_Range odtect );
+
+ FT_LOCAL( void )
+ gxv_odtect_validate( GXV_odtect_Range odtect,
+ GXV_Validator valid );
+
+
+#define GXV_ODTECT( n, odtect ) \
+ GXV_odtect_DataRec odtect ## _range[n]; \
+ GXV_odtect_RangeRec odtect ## _rec = { 0, NULL }; \
+ GXV_odtect_Range odtect = NULL
+
+#define GXV_ODTECT_INIT( odtect ) \
+ FT_BEGIN_STMNT \
+ odtect ## _rec.nRanges = 0; \
+ odtect ## _rec.range = odtect ## _range; \
+ odtect = & odtect ## _rec; \
+ FT_END_STMNT
+
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __GXVCOMMN_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxverror.h b/src/gxvalid/gxverror.h
new file mode 100644
index 0000000..0196199
--- /dev/null
+++ b/src/gxvalid/gxverror.h
@@ -0,0 +1,51 @@
+/***************************************************************************/
+/* */
+/* gxverror.h */
+/* */
+/* TrueTypeGX/AAT validation module error codes (specification only). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the OpenType validation module error */
+ /* enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __GXVERROR_H__
+#define __GXVERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX GXV_Err_
+#define FT_ERR_BASE FT_Mod_Err_GXV
+
+#define FT_KEEP_ERR_PREFIX
+
+#include FT_ERRORS_H
+
+#endif /* __GXVERROR_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvfeat.c b/src/gxvalid/gxvfeat.c
new file mode 100644
index 0000000..d7c6ad1
--- /dev/null
+++ b/src/gxvalid/gxvfeat.c
@@ -0,0 +1,343 @@
+/***************************************************************************/
+/* */
+/* gxvfeat.c */
+/* */
+/* TrueTypeGX/AAT feat table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+#include "gxvfeat.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvfeat
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_feat_DataRec_
+ {
+ FT_UInt reserved_size;
+ FT_UShort feature;
+ FT_UShort setting;
+
+ } GXV_feat_DataRec, *GXV_feat_Data;
+
+
+#define GXV_FEAT_DATA( field ) GXV_TABLE_DATA( feat, field )
+
+
+ typedef enum
+ {
+ GXV_FEAT_MASK_EXCLUSIVE_SETTINGS = 0x8000U,
+ GXV_FEAT_MASK_DYNAMIC_DEFAULT = 0x4000,
+ GXV_FEAT_MASK_UNUSED = 0x3F00,
+ GXV_FEAT_MASK_DEFAULT_SETTING = 0x00FF
+
+ } GXV_FeatureFlagsMask;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_feat_registry_validate( FT_UShort feature,
+ FT_UShort nSettings,
+ FT_Bool exclusive,
+ GXV_Validator valid )
+ {
+ GXV_NAME_ENTER( "feature in registry" );
+
+ GXV_TRACE(( " (feature = %u)\n", feature ));
+
+ if ( feature >= gxv_feat_registry_length )
+ {
+ GXV_TRACE(( "feature number %d is out of range %d\n",
+ feature, gxv_feat_registry_length ));
+ if ( valid->root->level == FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ goto Exit;
+ }
+
+ if ( gxv_feat_registry[feature].existence == 0 )
+ {
+ GXV_TRACE(( "feature number %d is in defined range but doesn't exist\n",
+ feature ));
+ if ( valid->root->level == FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ goto Exit;
+ }
+
+ if ( gxv_feat_registry[feature].apple_reserved )
+ {
+ /* Don't use here. Apple is reserved. */
+ GXV_TRACE(( "feature number %d is reserved by Apple\n", feature ));
+ if ( valid->root->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_DATA;
+ }
+
+ if ( nSettings != gxv_feat_registry[feature].nSettings )
+ {
+ GXV_TRACE(( "feature %d: nSettings %d != defined nSettings %d\n",
+ feature, nSettings,
+ gxv_feat_registry[feature].nSettings ));
+ if ( valid->root->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_DATA;
+ }
+
+ if ( exclusive != gxv_feat_registry[feature].exclusive )
+ {
+ GXV_TRACE(( "exclusive flag %d differs from predefined value\n",
+ exclusive ));
+ if ( valid->root->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_DATA;
+ }
+
+ Exit:
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_feat_name_index_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ FT_Short nameIndex;
+
+
+ GXV_NAME_ENTER( "nameIndex" );
+
+ GXV_LIMIT_CHECK( 2 );
+ nameIndex = FT_NEXT_SHORT ( p );
+ GXV_TRACE(( " (nameIndex = %d)\n", nameIndex ));
+
+ gxv_sfntName_validate( (FT_UShort)nameIndex,
+ 255,
+ 32768U,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_feat_setting_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_Bool exclusive,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort setting;
+
+
+ GXV_NAME_ENTER( "setting" );
+
+ GXV_LIMIT_CHECK( 2 );
+
+ setting = FT_NEXT_USHORT( p );
+
+ /* If we have exclusive setting, the setting should be odd. */
+ if ( exclusive && ( setting % 2 ) == 0 )
+ FT_INVALID_DATA;
+
+ gxv_feat_name_index_validate( p, limit, valid );
+
+ GXV_FEAT_DATA( setting ) = setting;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_feat_name_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt reserved_size = GXV_FEAT_DATA( reserved_size );
+
+ FT_UShort feature;
+ FT_UShort nSettings;
+ FT_UInt settingTable;
+ FT_UShort featureFlags;
+
+ FT_Bool exclusive;
+ FT_Int last_setting;
+ FT_UInt i;
+
+
+ GXV_NAME_ENTER( "name" );
+
+ /* feature + nSettings + settingTable + featureFlags */
+ GXV_LIMIT_CHECK( 2 + 2 + 4 + 2 );
+
+ feature = FT_NEXT_USHORT( p );
+ GXV_FEAT_DATA( feature ) = feature;
+
+ nSettings = FT_NEXT_USHORT( p );
+ settingTable = FT_NEXT_ULONG ( p );
+ featureFlags = FT_NEXT_USHORT( p );
+
+ if ( settingTable < reserved_size )
+ FT_INVALID_OFFSET;
+
+ if ( valid->root->level == FT_VALIDATE_PARANOID &&
+ ( featureFlags & GXV_FEAT_MASK_UNUSED ) == 0 )
+ FT_INVALID_DATA;
+
+ exclusive = FT_BOOL( featureFlags & GXV_FEAT_MASK_EXCLUSIVE_SETTINGS );
+ if ( exclusive )
+ {
+ FT_Byte dynamic_default;
+
+
+ if ( featureFlags & GXV_FEAT_MASK_DYNAMIC_DEFAULT )
+ dynamic_default = (FT_Byte)( featureFlags &
+ GXV_FEAT_MASK_DEFAULT_SETTING );
+ else
+ dynamic_default = 0;
+
+ /* If exclusive, check whether default setting is in the range. */
+ if ( !( dynamic_default < nSettings ) )
+ FT_INVALID_FORMAT;
+ }
+
+ gxv_feat_registry_validate( feature, nSettings, exclusive, valid );
+
+ gxv_feat_name_index_validate( p, limit, valid );
+
+ p = valid->root->base + settingTable;
+ for ( last_setting = -1, i = 0; i < nSettings; i++ )
+ {
+ gxv_feat_setting_validate( p, limit, exclusive, valid );
+
+ if ( valid->root->level == FT_VALIDATE_PARANOID &&
+ (FT_Int)GXV_FEAT_DATA( setting ) <= last_setting )
+ FT_INVALID_FORMAT;
+
+ last_setting = (FT_Int)GXV_FEAT_DATA( setting );
+ /* setting + nameIndex */
+ p += ( 2 + 2 );
+ }
+
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** feat TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_feat_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+
+ GXV_feat_DataRec featrec;
+ GXV_feat_Data feat = &featrec;
+
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+
+ FT_UInt featureNameCount;
+
+ FT_UInt i;
+ FT_Int last_feature;
+
+
+ valid->root = ftvalid;
+ valid->table_data = feat;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `feat' table\n" ));
+ GXV_INIT;
+
+ feat->reserved_size = 0;
+
+ /* version + featureNameCount + none_0 + none_1 */
+ GXV_LIMIT_CHECK( 4 + 2 + 2 + 4 );
+ feat->reserved_size += 4 + 2 + 2 + 4;
+
+ if ( FT_NEXT_ULONG( p ) != 0x00010000UL ) /* Version */
+ FT_INVALID_FORMAT;
+
+ featureNameCount = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " (featureNameCount = %d)\n", featureNameCount ));
+
+ if ( valid->root->level != FT_VALIDATE_PARANOID )
+ p += 6; /* skip (none) and (none) */
+ else
+ {
+ if ( FT_NEXT_USHORT( p ) != 0 )
+ FT_INVALID_DATA;
+
+ if ( FT_NEXT_ULONG( p ) != 0 )
+ FT_INVALID_DATA;
+ }
+
+ feat->reserved_size += featureNameCount * ( 2 + 2 + 4 + 2 + 2 );
+
+ for ( last_feature = -1, i = 0; i < featureNameCount; i++ )
+ {
+ gxv_feat_name_validate( p, limit, valid );
+
+ if ( valid->root->level == FT_VALIDATE_PARANOID &&
+ (FT_Int)GXV_FEAT_DATA( feature ) <= last_feature )
+ FT_INVALID_FORMAT;
+
+ last_feature = GXV_FEAT_DATA( feature );
+ p += 2 + 2 + 4 + 2 + 2;
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvfeat.h b/src/gxvalid/gxvfeat.h
new file mode 100644
index 0000000..049d23a
--- /dev/null
+++ b/src/gxvalid/gxvfeat.h
@@ -0,0 +1,172 @@
+/***************************************************************************/
+/* */
+/* gxvfeat.h */
+/* */
+/* TrueTypeGX/AAT feat table validation (specification). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __GXVFEAT_H__
+#define __GXVFEAT_H__
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Registry predefined by Apple *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* TODO: More compact format */
+ typedef struct GXV_Feature_RegistryRec_
+ {
+ FT_Bool existence;
+ FT_Bool apple_reserved;
+ FT_Bool exclusive;
+ FT_Byte nSettings;
+
+ } GX_Feature_RegistryRec;
+
+
+#define gxv_feat_registry_length \
+ ( sizeof ( gxv_feat_registry ) / \
+ sizeof ( GX_Feature_RegistryRec ) )
+
+
+ static GX_Feature_RegistryRec gxv_feat_registry[] =
+ {
+ /* Generated from gxvfgen.c */
+ {1, 0, 0, 1}, /* All Typographic Features */
+ {1, 0, 0, 8}, /* Ligatures */
+ {1, 0, 1, 3}, /* Cursive Connection */
+ {1, 0, 1, 6}, /* Letter Case */
+ {1, 0, 0, 1}, /* Vertical Substitution */
+ {1, 0, 0, 1}, /* Linguistic Rearrangement */
+ {1, 0, 1, 2}, /* Number Spacing */
+ {1, 1, 0, 0}, /* Apple Reserved 1 */
+ {1, 0, 0, 5}, /* Smart Swashes */
+ {1, 0, 1, 3}, /* Diacritics */
+ {1, 0, 1, 4}, /* Vertical Position */
+ {1, 0, 1, 3}, /* Fractions */
+ {1, 1, 0, 0}, /* Apple Reserved 2 */
+ {1, 0, 0, 1}, /* Overlapping Characters */
+ {1, 0, 0, 6}, /* Typographic Extras */
+ {1, 0, 0, 5}, /* Mathematical Extras */
+ {1, 0, 1, 7}, /* Ornament Sets */
+ {1, 0, 1, 1}, /* Character Alternatives */
+ {1, 0, 1, 5}, /* Design Complexity */
+ {1, 0, 1, 6}, /* Style Options */
+ {1, 0, 1, 11}, /* Character Shape */
+ {1, 0, 1, 2}, /* Number Case */
+ {1, 0, 1, 4}, /* Text Spacing */
+ {1, 0, 1, 10}, /* Transliteration */
+ {1, 0, 1, 9}, /* Annotation */
+ {1, 0, 1, 2}, /* Kana Spacing */
+ {1, 0, 1, 2}, /* Ideographic Spacing */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {0, 0, 0, 0}, /* __EMPTY__ */
+ {1, 0, 1, 4}, /* Text Spacing */
+ {1, 0, 1, 2}, /* Kana Spacing */
+ {1, 0, 1, 2}, /* Ideographic Spacing */
+ {1, 0, 1, 4}, /* CJK Roman Spacing */
+ };
+
+
+#endif /* __GXVFEAT_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvfgen.c b/src/gxvalid/gxvfgen.c
new file mode 100644
index 0000000..e48778a
--- /dev/null
+++ b/src/gxvalid/gxvfgen.c
@@ -0,0 +1,482 @@
+/***************************************************************************/
+/* */
+/* gxfgen.c */
+/* */
+/* Generate feature registry data for gxv `feat' validator. */
+/* This program is derived from gxfeatreg.c in gxlayout. */
+/* */
+/* Copyright 2004, 2005, 2006 by Masatake YAMATO and Redhat K.K. */
+/* */
+/* This file may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxfeatreg.c */
+/* */
+/* Database of font features pre-defined by Apple Computer, Inc. */
+/* http://developer.apple.com/fonts/Registry/ */
+/* (body). */
+/* */
+/* Copyright 2003 by */
+/* Masatake YAMATO and Redhat K.K. */
+/* */
+/* This file may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* Development of gxfeatreg.c is supported by */
+/* Information-technology Promotion Agency, Japan. */
+/* */
+/***************************************************************************/
+
+
+/***************************************************************************/
+/* */
+/* This file is compiled as a stand-alone executable. */
+/* This file is never compiled into `libfreetype2'. */
+/* The output of this file is used in `gxvfeat.c'. */
+/* ----------------------------------------------------------------------- */
+/* Compile: gcc `pkg-config --cflags freetype2` gxvfgen.c -o gxvfgen */
+/* Run: ./gxvfgen > tmp.c */
+/* */
+/***************************************************************************/
+
+ /*******************************************************************/
+ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+ /*******************************************************************/
+
+ /*
+ * If you add a new setting to a feature, check the number of settings
+ * in the feature. If the number is greater than the value defined as
+ * FEATREG_MAX_SETTING, update the value.
+ */
+#define FEATREG_MAX_SETTING 12
+
+ /*******************************************************************/
+ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+ /*******************************************************************/
+
+
+#include <stdio.h>
+#include <string.h>
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define APPLE_RESERVED "Apple Reserved"
+#define APPLE_RESERVED_LENGTH 14
+
+ typedef struct GX_Feature_RegistryRec_
+ {
+ const char* feat_name;
+ char exclusive;
+ char* setting_name[FEATREG_MAX_SETTING];
+
+ } GX_Feature_RegistryRec;
+
+
+#define EMPTYFEAT {0, 0, {NULL}}
+
+
+ static GX_Feature_RegistryRec featreg_table[] = {
+ { /* 0 */
+ "All Typographic Features",
+ 0,
+ {
+ "All Type Features",
+ NULL
+ }
+ }, { /* 1 */
+ "Ligatures",
+ 0,
+ {
+ "Required Ligatures",
+ "Common Ligatures",
+ "Rare Ligatures",
+ "Logos",
+ "Rebus Pictures",
+ "Diphthong Ligatures",
+ "Squared Ligatures",
+ "Squared Ligatures, Abbreviated",
+ NULL
+ }
+ }, { /* 2 */
+ "Cursive Connection",
+ 1,
+ {
+ "Unconnected",
+ "Partially Connected",
+ "Cursive",
+ NULL
+ }
+ }, { /* 3 */
+ "Letter Case",
+ 1,
+ {
+ "Upper & Lower Case",
+ "All Caps",
+ "All Lower Case",
+ "Small Caps",
+ "Initial Caps",
+ "Initial Caps & Small Caps",
+ NULL
+ }
+ }, { /* 4 */
+ "Vertical Substitution",
+ 0,
+ {
+ /* "Substitute Vertical Forms", */
+ "Turns on the feature",
+ NULL
+ }
+ }, { /* 5 */
+ "Linguistic Rearrangement",
+ 0,
+ {
+ /* "Linguistic Rearrangement", */
+ "Turns on the feature",
+ NULL
+ }
+ }, { /* 6 */
+ "Number Spacing",
+ 1,
+ {
+ "Monospaced Numbers",
+ "Proportional Numbers",
+ NULL
+ }
+ }, { /* 7 */
+ APPLE_RESERVED " 1",
+ 0,
+ {NULL}
+ }, { /* 8 */
+ "Smart Swashes",
+ 0,
+ {
+ "Word Initial Swashes",
+ "Word Final Swashes",
+ "Line Initial Swashes",
+ "Line Final Swashes",
+ "Non-Final Swashes",
+ NULL
+ }
+ }, { /* 9 */
+ "Diacritics",
+ 1,
+ {
+ "Show Diacritics",
+ "Hide Diacritics",
+ "Decompose Diacritics",
+ NULL
+ }
+ }, { /* 10 */
+ "Vertical Position",
+ 1,
+ {
+ /* "Normal Position", */
+ "No Vertical Position",
+ "Superiors",
+ "Inferiors",
+ "Ordinals",
+ NULL
+ }
+ }, { /* 11 */
+ "Fractions",
+ 1,
+ {
+ "No Fractions",
+ "Vertical Fractions",
+ "Diagonal Fractions",
+ NULL
+ }
+ }, { /* 12 */
+ APPLE_RESERVED " 2",
+ 0,
+ {NULL}
+ }, { /* 13 */
+ "Overlapping Characters",
+ 0,
+ {
+ /* "Prevent Overlap", */
+ "Turns on the feature",
+ NULL
+ }
+ }, { /* 14 */
+ "Typographic Extras",
+ 0,
+ {
+ "Hyphens to Em Dash",
+ "Hyphens to En Dash",
+ "Unslashed Zero",
+ "Form Interrobang",
+ "Smart Quotes",
+ "Periods to Ellipsis",
+ NULL
+ }
+ }, { /* 15 */
+ "Mathematical Extras",
+ 0,
+ {
+ "Hyphens to Minus",
+ "Asterisk to Multiply",
+ "Slash to Divide",
+ "Inequality Ligatures",
+ "Exponents",
+ NULL
+ }
+ }, { /* 16 */
+ "Ornament Sets",
+ 1,
+ {
+ "No Ornaments",
+ "Dingbats",
+ "Pi Characters",
+ "Fleurons",
+ "Decorative Borders",
+ "International Symbols",
+ "Math Symbols",
+ NULL
+ }
+ }, { /* 17 */
+ "Character Alternatives",
+ 1,
+ {
+ "No Alternates",
+ /* TODO */
+ NULL
+ }
+ }, { /* 18 */
+ "Design Complexity",
+ 1,
+ {
+ "Design Level 1",
+ "Design Level 2",
+ "Design Level 3",
+ "Design Level 4",
+ "Design Level 5",
+ /* TODO */
+ NULL
+ }
+ }, { /* 19 */
+ "Style Options",
+ 1,
+ {
+ "No Style Options",
+ "Display Text",
+ "Engraved Text",
+ "Illuminated Caps",
+ "Tilling Caps",
+ "Tall Caps",
+ NULL
+ }
+ }, { /* 20 */
+ "Character Shape",
+ 1,
+ {
+ "Traditional Characters",
+ "Simplified Characters",
+ "JIS 1978 Characters",
+ "JIS 1983 Characters",
+ "JIS 1990 Characters",
+ "Traditional Characters, Alternative Set 1",
+ "Traditional Characters, Alternative Set 2",
+ "Traditional Characters, Alternative Set 3",
+ "Traditional Characters, Alternative Set 4",
+ "Traditional Characters, Alternative Set 5",
+ "Expert Characters",
+ NULL /* count => 12 */
+ }
+ }, { /* 21 */
+ "Number Case",
+ 1,
+ {
+ "Lower Case Numbers",
+ "Upper Case Numbers",
+ NULL
+ }
+ }, { /* 22 */
+ "Text Spacing",
+ 1,
+ {
+ "Proportional",
+ "Monospaced",
+ "Half-width",
+ "Normal",
+ NULL
+ }
+ }, /* Here after Newer */ { /* 23 */
+ "Transliteration",
+ 1,
+ {
+ "No Transliteration",
+ "Hanja To Hangul",
+ "Hiragana to Katakana",
+ "Katakana to Hiragana",
+ "Kana to Romanization",
+ "Romanization to Hiragana",
+ "Romanization to Katakana",
+ "Hanja to Hangul, Alternative Set 1",
+ "Hanja to Hangul, Alternative Set 2",
+ "Hanja to Hangul, Alternative Set 3",
+ NULL
+ }
+ }, { /* 24 */
+ "Annotation",
+ 1,
+ {
+ "No Annotation",
+ "Box Annotation",
+ "Rounded Box Annotation",
+ "Circle Annotation",
+ "Inverted Circle Annotation",
+ "Parenthesis Annotation",
+ "Period Annotation",
+ "Roman Numeral Annotation",
+ "Diamond Annotation",
+ NULL
+ }
+ }, { /* 25 */
+ "Kana Spacing",
+ 1,
+ {
+ "Full Width",
+ "Proportional",
+ NULL
+ }
+ }, { /* 26 */
+ "Ideographic Spacing",
+ 1,
+ {
+ "Full Width",
+ "Proportional",
+ NULL
+ }
+ }, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 27-30 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 31-35 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 36-40 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 40-45 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 46-50 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 51-55 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 56-60 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 61-65 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 66-70 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 71-75 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 76-80 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 81-85 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 86-90 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 91-95 */
+ EMPTYFEAT, EMPTYFEAT, EMPTYFEAT, /* 96-98 */
+ EMPTYFEAT, /* 99 */ { /* 100 => 22 */
+ "Text Spacing",
+ 1,
+ {
+ "Proportional",
+ "Monospaced",
+ "Half-width",
+ "Normal",
+ NULL
+ }
+ }, { /* 101 => 25 */
+ "Kana Spacing",
+ 1,
+ {
+ "Full Width",
+ "Proportional",
+ NULL
+ }
+ }, { /* 102 => 26 */
+ "Ideographic Spacing",
+ 1,
+ {
+ "Full Width",
+ "Proportional",
+ NULL
+ }
+ }, { /* 103 */
+ "CJK Roman Spacing",
+ 1,
+ {
+ "Half-width",
+ "Proportional",
+ "Default Roman",
+ "Full-width Roman",
+ NULL
+ }
+ }, { /* 104 => 1 */
+ "All Typographic Features",
+ 0,
+ {
+ "All Type Features",
+ NULL
+ }
+ }
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Generator *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ int
+ main( void )
+ {
+ int i;
+
+
+ printf( " {\n" );
+ printf( " /* Generated from %s */\n", __FILE__ );
+
+ for ( i = 0;
+ i < sizeof ( featreg_table ) / sizeof ( GX_Feature_RegistryRec );
+ i++ )
+ {
+ const char* feat_name;
+ int nSettings;
+
+
+ feat_name = featreg_table[i].feat_name;
+ for ( nSettings = 0;
+ featreg_table[i].setting_name[nSettings];
+ nSettings++)
+ ; /* Do nothing */
+
+ printf( " {%1d, %1d, %1d, %2d}, /* %s */\n",
+ feat_name ? 1 : 0,
+ ( feat_name &&
+ ( ft_strncmp( feat_name,
+ APPLE_RESERVED, APPLE_RESERVED_LENGTH ) == 0 )
+ ) ? 1 : 0,
+ featreg_table[i].exclusive ? 1 : 0,
+ nSettings,
+ feat_name ? feat_name : "__EMPTY__" );
+ }
+
+ printf( " };\n" );
+
+ return 0;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvjust.c b/src/gxvalid/gxvjust.c
new file mode 100644
index 0000000..29bf840
--- /dev/null
+++ b/src/gxvalid/gxvjust.c
@@ -0,0 +1,630 @@
+/***************************************************************************/
+/* */
+/* gxvjust.c */
+/* */
+/* TrueTypeGX/AAT just table validation (body). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+#include FT_SFNT_NAMES_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvjust
+
+ /*
+ * referred `just' table format specification:
+ * http://developer.apple.com/fonts/TTRefMan/RM06/Chap6just.html
+ * last updated 2000.
+ * ----------------------------------------------
+ * [JUST HEADER]: GXV_JUST_HEADER_SIZE
+ * version (fixed: 32bit) = 0x00010000
+ * format (uint16: 16bit) = 0 is only defined (2000)
+ * horizOffset (uint16: 16bit)
+ * vertOffset (uint16: 16bit)
+ * ----------------------------------------------
+ */
+
+ typedef struct GXV_just_DataRec_
+ {
+ FT_UShort wdc_offset_max;
+ FT_UShort wdc_offset_min;
+ FT_UShort pc_offset_max;
+ FT_UShort pc_offset_min;
+
+ } GXV_just_DataRec, *GXV_just_Data;
+
+
+#define GXV_JUST_DATA( a ) GXV_TABLE_DATA( just, a )
+
+
+ static void
+ gxv_just_wdp_entry_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong justClass;
+ FT_Fixed beforeGrowLimit;
+ FT_Fixed beforeShrinkGrowLimit;
+ FT_Fixed afterGrowLimit;
+ FT_Fixed afterShrinkGrowLimit;
+ FT_UShort growFlags;
+ FT_UShort shrinkFlags;
+
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 + 4 + 2 + 2 );
+ justClass = FT_NEXT_ULONG( p );
+ beforeGrowLimit = FT_NEXT_ULONG( p );
+ beforeShrinkGrowLimit = FT_NEXT_ULONG( p );
+ afterGrowLimit = FT_NEXT_ULONG( p );
+ afterShrinkGrowLimit = FT_NEXT_ULONG( p );
+ growFlags = FT_NEXT_USHORT( p );
+ shrinkFlags = FT_NEXT_USHORT( p );
+
+ /* TODO: decode flags for human readability */
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_wdc_entry_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong count, i;
+
+
+ GXV_LIMIT_CHECK( 4 );
+ count = FT_NEXT_ULONG( p );
+ for ( i = 0; i < count; i++ )
+ {
+ GXV_TRACE(( "validating wdc pair %d/%d\n", i + 1, count ));
+ gxv_just_wdp_entry_validate( p, limit, valid );
+ p += valid->subtable_length;
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_widthDeltaClusters_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table ;
+ FT_Bytes wdc_end = table + GXV_JUST_DATA( wdc_offset_max );
+ FT_UInt i;
+
+
+ GXV_NAME_ENTER( "just justDeltaClusters" );
+
+ if ( limit <= wdc_end )
+ FT_INVALID_OFFSET;
+
+ for ( i = 0; p <= wdc_end; i++ )
+ {
+ gxv_just_wdc_entry_validate( p, limit, valid );
+ p += valid->subtable_length;
+ }
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_actSubrecord_type0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ FT_Fixed lowerLimit;
+ FT_Fixed upperLimit;
+
+ FT_UShort order;
+ FT_UShort decomposedCount;
+
+ FT_UInt i;
+
+
+ GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 );
+ lowerLimit = FT_NEXT_ULONG( p );
+ upperLimit = FT_NEXT_ULONG( p );
+ order = FT_NEXT_USHORT( p );
+ decomposedCount = FT_NEXT_USHORT( p );
+
+ for ( i = 0; i < decomposedCount; i++ )
+ {
+ FT_UShort glyphs;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ glyphs = FT_NEXT_USHORT( p );
+ }
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_actSubrecord_type1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort addGlyph;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ addGlyph = FT_NEXT_USHORT( p );
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_actSubrecord_type2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Fixed substThreshhold; /* Apple misspelled "Threshhold" */
+ FT_UShort addGlyph;
+ FT_UShort substGlyph;
+
+
+ GXV_LIMIT_CHECK( 4 + 2 + 2 );
+ substThreshhold = FT_NEXT_ULONG( p );
+ addGlyph = FT_NEXT_USHORT( p );
+ substGlyph = FT_NEXT_USHORT( p );
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_actSubrecord_type4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong variantsAxis;
+ FT_Fixed minimumLimit;
+ FT_Fixed noStretchValue;
+ FT_Fixed maximumLimit;
+
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 );
+ variantsAxis = FT_NEXT_ULONG( p );
+ minimumLimit = FT_NEXT_ULONG( p );
+ noStretchValue = FT_NEXT_ULONG( p );
+ maximumLimit = FT_NEXT_ULONG( p );
+
+ valid->subtable_length = p - table;
+ }
+
+
+ static void
+ gxv_just_actSubrecord_type5_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort flags;
+ FT_UShort glyph;
+
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ flags = FT_NEXT_USHORT( p );
+ glyph = FT_NEXT_USHORT( p );
+
+ valid->subtable_length = p - table;
+ }
+
+
+ /* parse single actSubrecord */
+ static void
+ gxv_just_actSubrecord_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort actionClass;
+ FT_UShort actionType;
+ FT_ULong actionLength;
+
+
+ GXV_NAME_ENTER( "just actSubrecord" );
+
+ GXV_LIMIT_CHECK( 2 + 2 + 4 );
+ actionClass = FT_NEXT_USHORT( p );
+ actionType = FT_NEXT_USHORT( p );
+ actionLength = FT_NEXT_ULONG( p );
+
+ if ( actionType == 0 )
+ gxv_just_actSubrecord_type0_validate( p, limit, valid );
+ else if ( actionType == 1 )
+ gxv_just_actSubrecord_type1_validate( p, limit, valid );
+ else if ( actionType == 2 )
+ gxv_just_actSubrecord_type2_validate( p, limit, valid );
+ else if ( actionType == 3 )
+ ; /* Stretch glyph action: no actionData */
+ else if ( actionType == 4 )
+ gxv_just_actSubrecord_type4_validate( p, limit, valid );
+ else if ( actionType == 5 )
+ gxv_just_actSubrecord_type5_validate( p, limit, valid );
+ else
+ FT_INVALID_DATA;
+
+ valid->subtable_length = actionLength;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_pcActionRecord_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong actionCount;
+ FT_ULong i;
+
+
+ GXV_LIMIT_CHECK( 4 );
+ actionCount = FT_NEXT_ULONG( p );
+ GXV_TRACE(( "actionCount = %d\n", actionCount ));
+
+ for ( i = 0; i < actionCount; i++ )
+ {
+ gxv_just_actSubrecord_validate( p, limit, valid );
+ p += valid->subtable_length;
+ }
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_pcTable_LookupValue_entry_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( glyph );
+
+ if ( value.u > GXV_JUST_DATA( pc_offset_max ) )
+ GXV_JUST_DATA( pc_offset_max ) = value.u;
+ if ( value.u < GXV_JUST_DATA( pc_offset_max ) )
+ GXV_JUST_DATA( pc_offset_min ) = value.u;
+ }
+
+
+ static void
+ gxv_just_pcLookupTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER( "just pcLookupTable" );
+ GXV_JUST_DATA( pc_offset_max ) = 0x0000;
+ GXV_JUST_DATA( pc_offset_min ) = 0xFFFFU;
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_just_pcTable_LookupValue_entry_validate;
+
+ gxv_LookupTable_validate( p, limit, valid );
+
+ /* subtable_length is set by gxv_LookupTable_validate() */
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_postcompTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER( "just postcompTable" );
+
+ gxv_just_pcLookupTable_validate( p, limit, valid );
+ p += valid->subtable_length;
+
+ gxv_just_pcActionRecord_validate( p, limit, valid );
+ p += valid->subtable_length;
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_classTable_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort setMark;
+ FT_UShort dontAdvance;
+ FT_UShort markClass;
+ FT_UShort currentClass;
+
+ FT_UNUSED( state );
+ FT_UNUSED( glyphOffset );
+ FT_UNUSED( table );
+ FT_UNUSED( limit );
+ FT_UNUSED( valid );
+
+
+ setMark = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ markClass = (FT_UShort)( ( flags >> 7 ) & 0x7F );
+ currentClass = (FT_UShort)( flags & 0x7F );
+
+ /* TODO: validate markClass & currentClass */
+ }
+
+
+ static void
+ gxv_just_justClassTable_validate ( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort length;
+ FT_UShort coverage;
+ FT_ULong subFeatureFlags;
+
+
+ GXV_NAME_ENTER( "just justClassTable" );
+
+ GXV_LIMIT_CHECK( 2 + 2 + 4 );
+ length = FT_NEXT_USHORT( p );
+ coverage = FT_NEXT_USHORT( p );
+ subFeatureFlags = FT_NEXT_ULONG( p );
+
+ GXV_TRACE(( " justClassTable: coverage = 0x%04x (%s)",
+ coverage,
+ ( 0x4000 & coverage ) == 0 ? "ascending" : "descending" ));
+
+ valid->statetable.optdata = NULL;
+ valid->statetable.optdata_load_func = NULL;
+ valid->statetable.subtable_setup_func = NULL;
+ valid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE;
+ valid->statetable.entry_validate_func =
+ gxv_just_classTable_entry_validate;
+
+ gxv_StateTable_validate( p, table + length, valid );
+
+ /* subtable_length is set by gxv_LookupTable_validate() */
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_just_wdcTable_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( glyph );
+
+ if ( value.u > GXV_JUST_DATA( wdc_offset_max ) )
+ GXV_JUST_DATA( wdc_offset_max ) = value.u;
+ if ( value.u < GXV_JUST_DATA( wdc_offset_min ) )
+ GXV_JUST_DATA( wdc_offset_min ) = value.u;
+ }
+
+
+ static void
+ gxv_just_justData_lookuptable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_JUST_DATA( wdc_offset_max ) = 0x0000;
+ GXV_JUST_DATA( wdc_offset_min ) = 0xFFFFU;
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_just_wdcTable_LookupValue_validate;
+
+ gxv_LookupTable_validate( p, limit, valid );
+
+ /* subtable_length is set by gxv_LookupTable_validate() */
+
+ GXV_EXIT;
+ }
+
+
+ /*
+ * gxv_just_justData_validate() parses and validates horizData, vertData.
+ */
+ static void
+ gxv_just_justData_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ /*
+ * following 3 offsets are measured from the start of `just'
+ * (which table points to), not justData
+ */
+ FT_UShort justClassTableOffset;
+ FT_UShort wdcTableOffset;
+ FT_UShort pcTableOffset;
+ FT_Bytes p = table;
+
+ GXV_ODTECT( 4, odtect );
+
+
+ GXV_NAME_ENTER( "just justData" );
+
+ GXV_ODTECT_INIT( odtect );
+ GXV_LIMIT_CHECK( 2 + 2 + 2 );
+ justClassTableOffset = FT_NEXT_USHORT( p );
+ wdcTableOffset = FT_NEXT_USHORT( p );
+ pcTableOffset = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( " (justClassTableOffset = 0x%04x)\n", justClassTableOffset ));
+ GXV_TRACE(( " (wdcTableOffset = 0x%04x)\n", wdcTableOffset ));
+ GXV_TRACE(( " (pcTableOffset = 0x%04x)\n", pcTableOffset ));
+
+ gxv_just_justData_lookuptable_validate( p, limit, valid );
+ gxv_odtect_add_range( p, valid->subtable_length,
+ "just_LookupTable", odtect );
+
+ if ( wdcTableOffset )
+ {
+ gxv_just_widthDeltaClusters_validate(
+ valid->root->base + wdcTableOffset, limit, valid );
+ gxv_odtect_add_range( valid->root->base + wdcTableOffset,
+ valid->subtable_length, "just_wdcTable", odtect );
+ }
+
+ if ( pcTableOffset )
+ {
+ gxv_just_postcompTable_validate( valid->root->base + pcTableOffset,
+ limit, valid );
+ gxv_odtect_add_range( valid->root->base + pcTableOffset,
+ valid->subtable_length, "just_pcTable", odtect );
+ }
+
+ if ( justClassTableOffset )
+ {
+ gxv_just_justClassTable_validate(
+ valid->root->base + justClassTableOffset, limit, valid );
+ gxv_odtect_add_range( valid->root->base + justClassTableOffset,
+ valid->subtable_length, "just_justClassTable",
+ odtect );
+ }
+
+ gxv_odtect_validate( odtect, valid );
+
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_just_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ FT_UInt table_size;
+
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+ GXV_just_DataRec justrec;
+ GXV_just_Data just = &justrec;
+
+ FT_ULong version;
+ FT_UShort format;
+ FT_UShort horizOffset;
+ FT_UShort vertOffset;
+
+ GXV_ODTECT( 3, odtect );
+
+
+ GXV_ODTECT_INIT( odtect );
+
+ valid->root = ftvalid;
+ valid->table_data = just;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `just' table\n" ));
+ GXV_INIT;
+
+ limit = valid->root->limit;
+ table_size = limit - table;
+
+ GXV_LIMIT_CHECK( 4 + 2 + 2 + 2 );
+ version = FT_NEXT_ULONG( p );
+ format = FT_NEXT_USHORT( p );
+ horizOffset = FT_NEXT_USHORT( p );
+ vertOffset = FT_NEXT_USHORT( p );
+ gxv_odtect_add_range( table, p - table, "just header", odtect );
+
+
+ /* Version 1.0 (always:2000) */
+ GXV_TRACE(( " (version = 0x%08x)\n", version ));
+ if ( version != 0x00010000UL )
+ FT_INVALID_FORMAT;
+
+ /* format 0 (always:2000) */
+ GXV_TRACE(( " (format = 0x%04x)\n", format ));
+ if ( format != 0x0000 )
+ FT_INVALID_FORMAT;
+
+ GXV_TRACE(( " (horizOffset = %d)\n", horizOffset ));
+ GXV_TRACE(( " (vertOffset = %d)\n", vertOffset ));
+
+
+ /* validate justData */
+ if ( 0 < horizOffset )
+ {
+ gxv_just_justData_validate( table + horizOffset, limit, valid );
+ gxv_odtect_add_range( table + horizOffset, valid->subtable_length,
+ "horizJustData", odtect );
+ }
+
+ if ( 0 < vertOffset )
+ {
+ gxv_just_justData_validate( table + vertOffset, limit, valid );
+ gxv_odtect_add_range( table + vertOffset, valid->subtable_length,
+ "vertJustData", odtect );
+ }
+
+ gxv_odtect_validate( odtect, valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvkern.c b/src/gxvalid/gxvkern.c
new file mode 100644
index 0000000..bfb405f
--- /dev/null
+++ b/src/gxvalid/gxvkern.c
@@ -0,0 +1,876 @@
+/***************************************************************************/
+/* */
+/* gxvkern.c */
+/* */
+/* TrueTypeGX/AAT kern table validation (body). */
+/* */
+/* Copyright 2004, 2005, 2006, 2007 */
+/* by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+#include FT_SFNT_NAMES_H
+#include FT_SERVICE_GX_VALIDATE_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvkern
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef enum GXV_kern_Version_
+ {
+ KERN_VERSION_CLASSIC = 0x0000,
+ KERN_VERSION_NEW = 0x0001
+
+ } GXV_kern_Version;
+
+
+ typedef enum GXV_kern_Dialect_
+ {
+ KERN_DIALECT_UNKNOWN = 0,
+ KERN_DIALECT_MS = FT_VALIDATE_MS,
+ KERN_DIALECT_APPLE = FT_VALIDATE_APPLE,
+ KERN_DIALECT_ANY = FT_VALIDATE_CKERN
+
+ } GXV_kern_Dialect;
+
+
+ typedef struct GXV_kern_DataRec_
+ {
+ GXV_kern_Version version;
+ void *subtable_data;
+ GXV_kern_Dialect dialect_request;
+
+ } GXV_kern_DataRec, *GXV_kern_Data;
+
+
+#define GXV_KERN_DATA( field ) GXV_TABLE_DATA( kern, field )
+
+#define KERN_IS_CLASSIC( valid ) \
+ ( KERN_VERSION_CLASSIC == GXV_KERN_DATA( version ) )
+#define KERN_IS_NEW( valid ) \
+ ( KERN_VERSION_NEW == GXV_KERN_DATA( version ) )
+
+#define KERN_DIALECT( valid ) \
+ GXV_KERN_DATA( dialect_request )
+#define KERN_ALLOWS_MS( valid ) \
+ ( KERN_DIALECT( valid ) & KERN_DIALECT_MS )
+#define KERN_ALLOWS_APPLE( valid ) \
+ ( KERN_DIALECT( valid ) & KERN_DIALECT_APPLE )
+
+#define GXV_KERN_HEADER_SIZE ( KERN_IS_NEW( valid ) ? 8 : 4 )
+#define GXV_KERN_SUBTABLE_HEADER_SIZE ( KERN_IS_NEW( valid ) ? 8 : 6 )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SUBTABLE VALIDATORS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* ============================= format 0 ============================== */
+
+ static void
+ gxv_kern_subtable_fmt0_pairs_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort nPairs,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort i;
+
+ FT_UShort last_gid_left = 0;
+ FT_UShort last_gid_right = 0;
+
+ FT_UNUSED( limit );
+
+
+ GXV_NAME_ENTER( "kern format 0 pairs" );
+
+ for ( i = 0; i < nPairs; i++ )
+ {
+ FT_UShort gid_left;
+ FT_UShort gid_right;
+ FT_Short kernValue;
+
+
+ /* left */
+ gid_left = FT_NEXT_USHORT( p );
+ gxv_glyphid_validate( gid_left, valid );
+
+ /* right */
+ gid_right = FT_NEXT_USHORT( p );
+ gxv_glyphid_validate( gid_right, valid );
+
+ /* Pairs of left and right GIDs must be unique and sorted. */
+ GXV_TRACE(( "left gid = %u, right gid = %u\n", gid_left, gid_right ));
+ if ( gid_left == last_gid_left )
+ {
+ if ( last_gid_right < gid_right )
+ last_gid_right = gid_right;
+ else
+ FT_INVALID_DATA;
+ }
+ else if ( last_gid_left < gid_left )
+ {
+ last_gid_left = gid_left;
+ last_gid_right = gid_right;
+ }
+ else
+ FT_INVALID_DATA;
+
+ /* skip the kern value */
+ kernValue = FT_NEXT_SHORT( p );
+ }
+
+ GXV_EXIT;
+ }
+
+ static void
+ gxv_kern_subtable_fmt0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;
+
+ FT_UShort nPairs;
+ FT_UShort unitSize;
+
+
+ GXV_NAME_ENTER( "kern subtable format 0" );
+
+ unitSize = 2 + 2 + 2;
+ nPairs = 0;
+
+ /* nPairs, searchRange, entrySelector, rangeShift */
+ GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 );
+ gxv_BinSrchHeader_validate( p, limit, &unitSize, &nPairs, valid );
+ p += 2 + 2 + 2 + 2;
+
+ gxv_kern_subtable_fmt0_pairs_validate( p, limit, nPairs, valid );
+
+ GXV_EXIT;
+ }
+
+
+ /* ============================= format 1 ============================== */
+
+
+ typedef struct GXV_kern_fmt1_StateOptRec_
+ {
+ FT_UShort valueTable;
+ FT_UShort valueTable_length;
+
+ } GXV_kern_fmt1_StateOptRec, *GXV_kern_fmt1_StateOptRecData;
+
+
+ static void
+ gxv_kern_subtable_fmt1_valueTable_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ GXV_kern_fmt1_StateOptRecData optdata =
+ (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ optdata->valueTable = FT_NEXT_USHORT( p );
+ }
+
+
+ /*
+ * passed tables_size covers whole StateTable, including kern fmt1 header
+ */
+ static void
+ gxv_kern_subtable_fmt1_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_UShort o[4];
+ FT_UShort *l[4];
+ FT_UShort buff[5];
+
+ GXV_kern_fmt1_StateOptRecData optdata =
+ (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->valueTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &(optdata->valueTable_length);
+
+ gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, valid );
+ }
+
+
+ /*
+ * passed table & limit are of whole StateTable, not including subtables
+ */
+ static void
+ gxv_kern_subtable_fmt1_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort push;
+ FT_UShort dontAdvance;
+ FT_UShort valueOffset;
+ FT_UShort kernAction;
+ FT_UShort kernValue;
+
+ FT_UNUSED( state );
+ FT_UNUSED( glyphOffset );
+
+
+ push = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ valueOffset = (FT_UShort)( flags & 0x3FFF );
+
+ {
+ GXV_kern_fmt1_StateOptRecData vt_rec =
+ (GXV_kern_fmt1_StateOptRecData)valid->statetable.optdata;
+ FT_Bytes p;
+
+
+ if ( valueOffset < vt_rec->valueTable )
+ FT_INVALID_OFFSET;
+
+ p = table + valueOffset;
+ limit = table + vt_rec->valueTable + vt_rec->valueTable_length;
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ kernAction = FT_NEXT_USHORT( p );
+ kernValue = FT_NEXT_USHORT( p );
+ }
+ }
+
+
+ static void
+ gxv_kern_subtable_fmt1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ GXV_kern_fmt1_StateOptRec vt_rec;
+
+
+ GXV_NAME_ENTER( "kern subtable format 1" );
+
+ valid->statetable.optdata =
+ &vt_rec;
+ valid->statetable.optdata_load_func =
+ gxv_kern_subtable_fmt1_valueTable_load;
+ valid->statetable.subtable_setup_func =
+ gxv_kern_subtable_fmt1_subtable_setup;
+ valid->statetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_NONE;
+ valid->statetable.entry_validate_func =
+ gxv_kern_subtable_fmt1_entry_validate;
+
+ gxv_StateTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+ /* ================ Data for Class-Based Subtables 2, 3 ================ */
+
+ typedef enum GXV_kern_ClassSpec_
+ {
+ GXV_KERN_CLS_L = 0,
+ GXV_KERN_CLS_R
+
+ } GXV_kern_ClassSpec;
+
+
+ /* ============================= format 2 ============================== */
+
+ /* ---------------------- format 2 specific data ----------------------- */
+
+ typedef struct GXV_kern_subtable_fmt2_DataRec_
+ {
+ FT_UShort rowWidth;
+ FT_UShort array;
+ FT_UShort offset_min[2];
+ FT_UShort offset_max[2];
+ const FT_String* class_tag[2];
+ GXV_odtect_Range odtect;
+
+ } GXV_kern_subtable_fmt2_DataRec, *GXV_kern_subtable_fmt2_Data;
+
+
+#define GXV_KERN_FMT2_DATA( field ) \
+ ( ( (GXV_kern_subtable_fmt2_DataRec *) \
+ ( GXV_KERN_DATA( subtable_data ) ) )->field )
+
+
+ /* -------------------------- utility functions ----------------------- */
+
+ static void
+ gxv_kern_subtable_fmt2_clstbl_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_kern_ClassSpec spec,
+ GXV_Validator valid )
+ {
+ const FT_String* tag = GXV_KERN_FMT2_DATA( class_tag[spec] );
+ GXV_odtect_Range odtect = GXV_KERN_FMT2_DATA( odtect );
+
+ FT_Bytes p = table;
+ FT_UShort firstGlyph;
+ FT_UShort nGlyphs;
+
+
+ GXV_NAME_ENTER( "kern format 2 classTable" );
+
+ GXV_LIMIT_CHECK( 2 + 2 );
+ firstGlyph = FT_NEXT_USHORT( p );
+ nGlyphs = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " %s firstGlyph=%d, nGlyphs=%d\n",
+ tag, firstGlyph, nGlyphs ));
+
+ gxv_glyphid_validate( firstGlyph, valid );
+ gxv_glyphid_validate( (FT_UShort)( firstGlyph + nGlyphs - 1 ), valid );
+
+ gxv_array_getlimits_ushort( p, p + ( 2 * nGlyphs ),
+ &( GXV_KERN_FMT2_DATA( offset_min[spec] ) ),
+ &( GXV_KERN_FMT2_DATA( offset_max[spec] ) ),
+ valid );
+
+ gxv_odtect_add_range( table, 2 * nGlyphs, tag, odtect );
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_kern_subtable_fmt2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ GXV_ODTECT( 3, odtect );
+ GXV_kern_subtable_fmt2_DataRec fmt2_rec =
+ { 0, 0, { 0, 0 }, { 0, 0 }, { "leftClass", "rightClass" }, NULL };
+
+ FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;
+ FT_UShort leftOffsetTable;
+ FT_UShort rightOffsetTable;
+
+
+ GXV_NAME_ENTER( "kern subtable format 2" );
+
+ GXV_ODTECT_INIT( odtect );
+ fmt2_rec.odtect = odtect;
+ GXV_KERN_DATA( subtable_data ) = &fmt2_rec;
+
+ GXV_LIMIT_CHECK( 2 + 2 + 2 + 2 );
+ GXV_KERN_FMT2_DATA( rowWidth ) = FT_NEXT_USHORT( p );
+ leftOffsetTable = FT_NEXT_USHORT( p );
+ rightOffsetTable = FT_NEXT_USHORT( p );
+ GXV_KERN_FMT2_DATA( array ) = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( "rowWidth = %d\n", GXV_KERN_FMT2_DATA( rowWidth ) ));
+
+
+ GXV_LIMIT_CHECK( leftOffsetTable );
+ GXV_LIMIT_CHECK( rightOffsetTable );
+ GXV_LIMIT_CHECK( GXV_KERN_FMT2_DATA( array ) );
+
+ gxv_kern_subtable_fmt2_clstbl_validate( table + leftOffsetTable, limit,
+ GXV_KERN_CLS_L, valid );
+
+ gxv_kern_subtable_fmt2_clstbl_validate( table + rightOffsetTable, limit,
+ GXV_KERN_CLS_R, valid );
+
+ if ( GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_L] ) +
+ GXV_KERN_FMT2_DATA( offset_min[GXV_KERN_CLS_R] )
+ < GXV_KERN_FMT2_DATA( array ) )
+ FT_INVALID_OFFSET;
+
+ gxv_odtect_add_range( table + GXV_KERN_FMT2_DATA( array ),
+ GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_L] )
+ + GXV_KERN_FMT2_DATA( offset_max[GXV_KERN_CLS_R] )
+ - GXV_KERN_FMT2_DATA( array ),
+ "array", odtect );
+
+ gxv_odtect_validate( odtect, valid );
+
+ GXV_EXIT;
+ }
+
+
+ /* ============================= format 3 ============================== */
+
+ static void
+ gxv_kern_subtable_fmt3_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE;
+ FT_UShort glyphCount;
+ FT_Byte kernValueCount;
+ FT_Byte leftClassCount;
+ FT_Byte rightClassCount;
+ FT_Byte flags;
+
+
+ GXV_NAME_ENTER( "kern subtable format 3" );
+
+ GXV_LIMIT_CHECK( 2 + 1 + 1 + 1 + 1 );
+ glyphCount = FT_NEXT_USHORT( p );
+ kernValueCount = FT_NEXT_BYTE( p );
+ leftClassCount = FT_NEXT_BYTE( p );
+ rightClassCount = FT_NEXT_BYTE( p );
+ flags = FT_NEXT_BYTE( p );
+
+ if ( valid->face->num_glyphs != glyphCount )
+ {
+ GXV_TRACE(( "maxGID=%d, but glyphCount=%d\n",
+ valid->face->num_glyphs, glyphCount ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ /*
+ * just skip kernValue[kernValueCount]
+ */
+ GXV_LIMIT_CHECK( 2 * kernValueCount );
+ p += 2 * kernValueCount;
+
+ /*
+ * check leftClass[gid] < leftClassCount
+ */
+ {
+ FT_Byte min, max;
+
+
+ GXV_LIMIT_CHECK( glyphCount );
+ gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, valid );
+ p += valid->subtable_length;
+
+ if ( leftClassCount < max )
+ FT_INVALID_DATA;
+ }
+
+ /*
+ * check rightClass[gid] < rightClassCount
+ */
+ {
+ FT_Byte min, max;
+
+
+ GXV_LIMIT_CHECK( glyphCount );
+ gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, valid );
+ p += valid->subtable_length;
+
+ if ( rightClassCount < max )
+ FT_INVALID_DATA;
+ }
+
+ /*
+ * check kernIndex[i, j] < kernValueCount
+ */
+ {
+ FT_UShort i, j;
+
+
+ for ( i = 0; i < leftClassCount; i++ )
+ {
+ for ( j = 0; j < rightClassCount; j++ )
+ {
+ GXV_LIMIT_CHECK( 1 );
+ if ( kernValueCount < FT_NEXT_BYTE( p ) )
+ FT_INVALID_OFFSET;
+ }
+ }
+ }
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static FT_Bool
+ gxv_kern_coverage_new_apple_validate( FT_UShort coverage,
+ FT_UShort* format,
+ GXV_Validator valid )
+ {
+ /* new Apple-dialect */
+ FT_Bool kernVertical;
+ FT_Bool kernCrossStream;
+ FT_Bool kernVariation;
+
+ FT_UNUSED( valid );
+
+
+ /* reserved bits = 0 */
+ if ( coverage & 0x1FFC )
+ return 0;
+
+ kernVertical = FT_BOOL( ( coverage >> 15 ) & 1 );
+ kernCrossStream = FT_BOOL( ( coverage >> 14 ) & 1 );
+ kernVariation = FT_BOOL( ( coverage >> 13 ) & 1 );
+
+ *format = (FT_UShort)( coverage & 0x0003 );
+
+ GXV_TRACE(( "new Apple-dialect: "
+ "horizontal=%d, cross-stream=%d, variation=%d, format=%d\n",
+ !kernVertical, kernCrossStream, kernVariation, *format ));
+
+ GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" ));
+
+ return 1;
+ }
+
+
+ static FT_Bool
+ gxv_kern_coverage_classic_apple_validate( FT_UShort coverage,
+ FT_UShort* format,
+ GXV_Validator valid )
+ {
+ /* classic Apple-dialect */
+ FT_Bool horizontal;
+ FT_Bool cross_stream;
+
+
+ /* check expected flags, but don't check if MS-dialect is impossible */
+ if ( !( coverage & 0xFD00 ) && KERN_ALLOWS_MS( valid ) )
+ return 0;
+
+ /* reserved bits = 0 */
+ if ( coverage & 0x02FC )
+ return 0;
+
+ horizontal = FT_BOOL( ( coverage >> 15 ) & 1 );
+ cross_stream = FT_BOOL( ( coverage >> 13 ) & 1 );
+
+ *format = (FT_UShort)( coverage & 0x0003 );
+
+ GXV_TRACE(( "classic Apple-dialect: "
+ "horizontal=%d, cross-stream=%d, format=%d\n",
+ horizontal, cross_stream, *format ));
+
+ /* format 1 requires GX State Machine, too new for classic */
+ if ( *format == 1 )
+ return 0;
+
+ GXV_TRACE(( "kerning values in Apple format subtable are ignored\n" ));
+
+ return 1;
+ }
+
+
+ static FT_Bool
+ gxv_kern_coverage_classic_microsoft_validate( FT_UShort coverage,
+ FT_UShort* format,
+ GXV_Validator valid )
+ {
+ /* classic Microsoft-dialect */
+ FT_Bool horizontal;
+ FT_Bool minimum;
+ FT_Bool cross_stream;
+ FT_Bool override;
+
+ FT_UNUSED( valid );
+
+
+ /* reserved bits = 0 */
+ if ( coverage & 0xFDF0 )
+ return 0;
+
+ horizontal = FT_BOOL( coverage & 1 );
+ minimum = FT_BOOL( ( coverage >> 1 ) & 1 );
+ cross_stream = FT_BOOL( ( coverage >> 2 ) & 1 );
+ override = FT_BOOL( ( coverage >> 3 ) & 1 );
+
+ *format = (FT_UShort)( ( coverage >> 8 ) & 0x0003 );
+
+ GXV_TRACE(( "classic Microsoft-dialect: "
+ "horizontal=%d, minimum=%d, cross-stream=%d, "
+ "override=%d, format=%d\n",
+ horizontal, minimum, cross_stream, override, *format ));
+
+ if ( *format == 2 )
+ GXV_TRACE((
+ "kerning values in Microsoft format 2 subtable are ignored\n" ));
+
+ return 1;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** MAIN *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static GXV_kern_Dialect
+ gxv_kern_coverage_validate( FT_UShort coverage,
+ FT_UShort* format,
+ GXV_Validator valid )
+ {
+ GXV_kern_Dialect result = KERN_DIALECT_UNKNOWN;
+
+
+ GXV_NAME_ENTER( "validating coverage" );
+
+ GXV_TRACE(( "interprete coverage 0x%04x by Apple style\n", coverage ));
+
+ if ( KERN_IS_NEW( valid ) )
+ {
+ if ( gxv_kern_coverage_new_apple_validate( coverage,
+ format,
+ valid ) )
+ {
+ result = KERN_DIALECT_APPLE;
+ goto Exit;
+ }
+ }
+
+ if ( KERN_IS_CLASSIC( valid ) && KERN_ALLOWS_APPLE( valid ) )
+ {
+ if ( gxv_kern_coverage_classic_apple_validate( coverage,
+ format,
+ valid ) )
+ {
+ result = KERN_DIALECT_APPLE;
+ goto Exit;
+ }
+ }
+
+ if ( KERN_IS_CLASSIC( valid ) && KERN_ALLOWS_MS( valid ) )
+ {
+ if ( gxv_kern_coverage_classic_microsoft_validate( coverage,
+ format,
+ valid ) )
+ {
+ result = KERN_DIALECT_MS;
+ goto Exit;
+ }
+ }
+
+ GXV_TRACE(( "cannot interprete coverage, broken kern subtable\n" ));
+
+ Exit:
+ GXV_EXIT;
+ return result;
+ }
+
+
+ static void
+ gxv_kern_subtable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort version = 0; /* MS only: subtable version, unused */
+ FT_ULong length; /* MS: 16bit, Apple: 32bit*/
+ FT_UShort coverage;
+ FT_UShort tupleIndex = 0; /* Apple only */
+ FT_UShort u16[2];
+ FT_UShort format = 255; /* subtable format */
+
+
+ GXV_NAME_ENTER( "kern subtable" );
+
+ GXV_LIMIT_CHECK( 2 + 2 + 2 );
+ u16[0] = FT_NEXT_USHORT( p ); /* Apple: length_hi MS: version */
+ u16[1] = FT_NEXT_USHORT( p ); /* Apple: length_lo MS: length */
+ coverage = FT_NEXT_USHORT( p );
+
+ switch ( gxv_kern_coverage_validate( coverage, &format, valid ) )
+ {
+ case KERN_DIALECT_MS:
+ version = u16[0];
+ length = u16[1];
+ tupleIndex = 0;
+ GXV_TRACE(( "Subtable version = %d\n", version ));
+ GXV_TRACE(( "Subtable length = %d\n", length ));
+ break;
+
+ case KERN_DIALECT_APPLE:
+ version = 0;
+ length = ( u16[0] << 16 ) + u16[1];
+ tupleIndex = 0;
+ GXV_TRACE(( "Subtable length = %d\n", length ));
+
+ if ( KERN_IS_NEW( valid ) )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ tupleIndex = FT_NEXT_USHORT( p );
+ GXV_TRACE(( "Subtable tupleIndex = %d\n", tupleIndex ));
+ }
+ break;
+
+ default:
+ length = u16[1];
+ GXV_TRACE(( "cannot detect subtable dialect, "
+ "just skip %d byte\n", length ));
+ goto Exit;
+ }
+
+ /* formats 1, 2, 3 require the position of the start of this subtable */
+ if ( format == 0 )
+ gxv_kern_subtable_fmt0_validate( table, table + length, valid );
+ else if ( format == 1 )
+ gxv_kern_subtable_fmt1_validate( table, table + length, valid );
+ else if ( format == 2 )
+ gxv_kern_subtable_fmt2_validate( table, table + length, valid );
+ else if ( format == 3 )
+ gxv_kern_subtable_fmt3_validate( table, table + length, valid );
+ else
+ FT_INVALID_DATA;
+
+ Exit:
+ valid->subtable_length = length;
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** kern TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_kern_validate_generic( FT_Bytes table,
+ FT_Face face,
+ FT_Bool classic_only,
+ GXV_kern_Dialect dialect_request,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+
+ GXV_kern_DataRec kernrec;
+ GXV_kern_Data kern = &kernrec;
+
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+
+ FT_ULong nTables = 0;
+ FT_UInt i;
+
+
+ valid->root = ftvalid;
+ valid->table_data = kern;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `kern' table\n" ));
+ GXV_INIT;
+ KERN_DIALECT( valid ) = dialect_request;
+
+ GXV_LIMIT_CHECK( 2 );
+ GXV_KERN_DATA( version ) = (GXV_kern_Version)FT_NEXT_USHORT( p );
+ GXV_TRACE(( "version 0x%04x (higher 16bit)\n",
+ GXV_KERN_DATA( version ) ));
+
+ if ( 0x0001 < GXV_KERN_DATA( version ) )
+ FT_INVALID_FORMAT;
+ else if ( KERN_IS_CLASSIC( valid ) )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ nTables = FT_NEXT_USHORT( p );
+ }
+ else if ( KERN_IS_NEW( valid ) )
+ {
+ if ( classic_only )
+ FT_INVALID_FORMAT;
+
+ if ( 0x0000 != FT_NEXT_USHORT( p ) )
+ FT_INVALID_FORMAT;
+
+ GXV_LIMIT_CHECK( 4 );
+ nTables = FT_NEXT_ULONG( p );
+ }
+
+ for ( i = 0; i < nTables; i++ )
+ {
+ GXV_TRACE(( "validating subtable %d/%d\n", i, nTables ));
+ /* p should be 32bit-aligned? */
+ gxv_kern_subtable_validate( p, 0, valid );
+ p += valid->subtable_length;
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_kern_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ gxv_kern_validate_generic( table, face, 0, KERN_DIALECT_ANY, ftvalid );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_kern_validate_classic( FT_Bytes table,
+ FT_Face face,
+ FT_Int dialect_flags,
+ FT_Validator ftvalid )
+ {
+ GXV_kern_Dialect dialect_request;
+
+
+ dialect_request = (GXV_kern_Dialect)dialect_flags;
+ gxv_kern_validate_generic( table, face, 1, dialect_request, ftvalid );
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvlcar.c b/src/gxvalid/gxvlcar.c
new file mode 100644
index 0000000..48821ea
--- /dev/null
+++ b/src/gxvalid/gxvlcar.c
@@ -0,0 +1,223 @@
+/***************************************************************************/
+/* */
+/* gxvlcar.c */
+/* */
+/* TrueTypeGX/AAT lcar table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvlcar
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_lcar_DataRec_
+ {
+ FT_UShort format;
+
+ } GXV_lcar_DataRec, *GXV_lcar_Data;
+
+
+#define GXV_LCAR_DATA( FIELD ) GXV_TABLE_DATA( lcar, FIELD )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_lcar_partial_validate( FT_UShort partial,
+ FT_UShort glyph,
+ GXV_Validator valid )
+ {
+ GXV_NAME_ENTER( "partial" );
+
+ if ( GXV_LCAR_DATA( format ) != 1 )
+ goto Exit;
+
+ gxv_ctlPoint_validate( glyph, partial, valid );
+
+ Exit:
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_lcar_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = valid->root->base + value.u;
+ FT_Bytes limit = valid->root->limit;
+ FT_UShort count;
+ FT_Short partial;
+ FT_UShort i;
+
+
+ GXV_NAME_ENTER( "element in lookupTable" );
+
+ GXV_LIMIT_CHECK( 2 );
+ count = FT_NEXT_USHORT( p );
+
+ GXV_LIMIT_CHECK( 2 * count );
+ for ( i = 0; i < count; i++ )
+ {
+ partial = FT_NEXT_SHORT( p );
+ gxv_lcar_partial_validate( partial, glyph, valid );
+ }
+
+ GXV_EXIT;
+ }
+
+
+ /*
+ +------ lcar --------------------+
+ | |
+ | +===============+ |
+ | | looup header | |
+ | +===============+ |
+ | | BinSrchHeader | |
+ | +===============+ |
+ | | lastGlyph[0] | |
+ | +---------------+ |
+ | | firstGlyph[0] | | head of lcar sfnt table
+ | +---------------+ | +
+ | | offset[0] | -> | offset [byte]
+ | +===============+ | +
+ | | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte]
+ | +---------------+ |
+ | | firstGlyph[1] | |
+ | +---------------+ |
+ | | offset[1] | |
+ | +===============+ |
+ | |
+ | .... |
+ | |
+ | 16bit value array |
+ | +===============+ |
+ +------| value | <-------+
+ | ....
+ |
+ |
+ |
+ |
+ |
+ +----> lcar values...handled by lcar callback function
+ */
+
+ static GXV_LookupValueDesc
+ gxv_lcar_LookupFmt4_transit( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ FT_UNUSED( lookuptbl_limit );
+
+ /* XXX: check range? */
+ offset = (FT_UShort)( base_value.u +
+ relative_gindex * sizeof ( FT_UShort ) );
+ p = valid->root->base + offset;
+ limit = valid->root->limit;
+
+ GXV_LIMIT_CHECK ( 2 );
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** lcar TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_lcar_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+
+ GXV_lcar_DataRec lcarrec;
+ GXV_lcar_Data lcar = &lcarrec;
+
+ FT_Fixed version;
+
+
+ valid->root = ftvalid;
+ valid->table_data = lcar;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `lcar' table\n" ));
+ GXV_INIT;
+
+ GXV_LIMIT_CHECK( 4 + 2 );
+ version = FT_NEXT_ULONG( p );
+ GXV_LCAR_DATA( format ) = FT_NEXT_USHORT( p );
+
+ if ( version != 0x00010000UL)
+ FT_INVALID_FORMAT;
+
+ if ( GXV_LCAR_DATA( format ) > 1 )
+ FT_INVALID_FORMAT;
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_lcar_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_lcar_LookupFmt4_transit;
+ gxv_LookupTable_validate( p, limit, valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmod.c b/src/gxvalid/gxvmod.c
new file mode 100644
index 0000000..b2b16b1
--- /dev/null
+++ b/src/gxvalid/gxvmod.c
@@ -0,0 +1,285 @@
+/***************************************************************************/
+/* */
+/* gxvmod.c */
+/* */
+/* FreeType's TrueTypeGX/AAT validation module implementation (body). */
+/* */
+/* Copyright 2004, 2005, 2006 */
+/* by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_GX_VALIDATE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_GX_VALIDATE_H
+
+#include "gxvmod.h"
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmodule
+
+
+ static FT_Error
+ gxv_load_table( FT_Face face,
+ FT_Tag tag,
+ FT_Byte* volatile* table,
+ FT_ULong* table_len )
+ {
+ FT_Error error;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ error = FT_Load_Sfnt_Table( face, tag, 0, NULL, table_len );
+ if ( error == GXV_Err_Table_Missing )
+ return GXV_Err_Ok;
+ if ( error )
+ goto Exit;
+
+ if ( FT_ALLOC( *table, *table_len ) )
+ goto Exit;
+
+ error = FT_Load_Sfnt_Table( face, tag, 0, *table, table_len );
+
+ Exit:
+ return error;
+ }
+
+
+#define GXV_TABLE_DECL( _sfnt ) \
+ FT_Byte* volatile _sfnt = NULL; \
+ FT_ULong len_ ## _sfnt = 0
+
+#define GXV_TABLE_LOAD( _sfnt ) \
+ if ( ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count ) && \
+ ( gx_flags & FT_VALIDATE_ ## _sfnt ) ) \
+ { \
+ error = gxv_load_table( face, TTAG_ ## _sfnt, \
+ &_sfnt, &len_ ## _sfnt ); \
+ if ( error ) \
+ goto Exit; \
+ }
+
+#define GXV_TABLE_VALIDATE( _sfnt ) \
+ if ( _sfnt ) \
+ { \
+ ft_validator_init( &valid, _sfnt, _sfnt + len_ ## _sfnt, \
+ FT_VALIDATE_DEFAULT ); \
+ if ( ft_setjmp( valid.jump_buffer ) == 0 ) \
+ gxv_ ## _sfnt ## _validate( _sfnt, face, &valid ); \
+ error = valid.error; \
+ if ( error ) \
+ goto Exit; \
+ }
+
+#define GXV_TABLE_SET( _sfnt ) \
+ if ( FT_VALIDATE_ ## _sfnt ## _INDEX < table_count ) \
+ tables[FT_VALIDATE_ ## _sfnt ## _INDEX] = (FT_Bytes)_sfnt
+
+
+ static FT_Error
+ gxv_validate( FT_Face face,
+ FT_UInt gx_flags,
+ FT_Bytes tables[FT_VALIDATE_GX_LENGTH],
+ FT_UInt table_count )
+ {
+ FT_Memory volatile memory = FT_FACE_MEMORY( face );
+
+ FT_Error error = GXV_Err_Ok;
+ FT_ValidatorRec volatile valid;
+
+ FT_UInt i;
+
+
+ GXV_TABLE_DECL( feat );
+ GXV_TABLE_DECL( bsln );
+ GXV_TABLE_DECL( trak );
+ GXV_TABLE_DECL( just );
+ GXV_TABLE_DECL( mort );
+ GXV_TABLE_DECL( morx );
+ GXV_TABLE_DECL( kern );
+ GXV_TABLE_DECL( opbd );
+ GXV_TABLE_DECL( prop );
+ GXV_TABLE_DECL( lcar );
+
+ for ( i = 0; i < table_count; i++ )
+ tables[i] = 0;
+
+ /* load tables */
+ GXV_TABLE_LOAD( feat );
+ GXV_TABLE_LOAD( bsln );
+ GXV_TABLE_LOAD( trak );
+ GXV_TABLE_LOAD( just );
+ GXV_TABLE_LOAD( mort );
+ GXV_TABLE_LOAD( morx );
+ GXV_TABLE_LOAD( kern );
+ GXV_TABLE_LOAD( opbd );
+ GXV_TABLE_LOAD( prop );
+ GXV_TABLE_LOAD( lcar );
+
+ /* validate tables */
+ GXV_TABLE_VALIDATE( feat );
+ GXV_TABLE_VALIDATE( bsln );
+ GXV_TABLE_VALIDATE( trak );
+ GXV_TABLE_VALIDATE( just );
+ GXV_TABLE_VALIDATE( mort );
+ GXV_TABLE_VALIDATE( morx );
+ GXV_TABLE_VALIDATE( kern );
+ GXV_TABLE_VALIDATE( opbd );
+ GXV_TABLE_VALIDATE( prop );
+ GXV_TABLE_VALIDATE( lcar );
+
+ /* Set results */
+ GXV_TABLE_SET( feat );
+ GXV_TABLE_SET( mort );
+ GXV_TABLE_SET( morx );
+ GXV_TABLE_SET( bsln );
+ GXV_TABLE_SET( just );
+ GXV_TABLE_SET( kern );
+ GXV_TABLE_SET( opbd );
+ GXV_TABLE_SET( trak );
+ GXV_TABLE_SET( prop );
+ GXV_TABLE_SET( lcar );
+
+ Exit:
+ if ( error )
+ {
+ FT_FREE( feat );
+ FT_FREE( bsln );
+ FT_FREE( trak );
+ FT_FREE( just );
+ FT_FREE( mort );
+ FT_FREE( morx );
+ FT_FREE( kern );
+ FT_FREE( opbd );
+ FT_FREE( prop );
+ FT_FREE( lcar );
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ classic_kern_validate( FT_Face face,
+ FT_UInt ckern_flags,
+ FT_Bytes* ckern_table )
+ {
+ FT_Memory volatile memory = FT_FACE_MEMORY( face );
+
+ FT_Byte* volatile ckern = NULL;
+ FT_ULong len_ckern = 0;
+
+ /* without volatile on `error' GCC 4.1.1. emits: */
+ /* warning: variable 'error' might be clobbered by 'longjmp' or 'vfork' */
+ /* this warning seems spurious but --- */
+ FT_Error volatile error = GXV_Err_Ok;
+ FT_ValidatorRec volatile valid;
+
+
+ *ckern_table = NULL;
+
+ error = gxv_load_table( face, TTAG_kern, &ckern, &len_ckern );
+ if ( error )
+ goto Exit;
+
+ if ( ckern )
+ {
+ ft_validator_init( &valid, ckern, ckern + len_ckern,
+ FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ gxv_kern_validate_classic( ckern, face,
+ ckern_flags & FT_VALIDATE_CKERN, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ *ckern_table = ckern;
+
+ Exit:
+ if ( error )
+ FT_FREE( ckern );
+
+ return error;
+ }
+
+
+ static
+ const FT_Service_GXvalidateRec gxvalid_interface =
+ {
+ gxv_validate
+ };
+
+
+ static
+ const FT_Service_CKERNvalidateRec ckernvalid_interface =
+ {
+ classic_kern_validate
+ };
+
+
+ static
+ const FT_ServiceDescRec gxvalid_services[] =
+ {
+ { FT_SERVICE_ID_GX_VALIDATE, &gxvalid_interface },
+ { FT_SERVICE_ID_CLASSICKERN_VALIDATE, &ckernvalid_interface },
+ { NULL, NULL }
+ };
+
+
+ static FT_Pointer
+ gxvalid_get_service( FT_Module module,
+ const char* service_id )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( gxvalid_services, service_id );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class gxv_module_class =
+ {
+ 0,
+ sizeof( FT_ModuleRec ),
+ "gxvalid",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module-specific interface */
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) gxvalid_get_service
+ };
+
+
+/* END */
diff --git a/src/gxvalid/gxvmod.h b/src/gxvalid/gxvmod.h
new file mode 100644
index 0000000..466584e
--- /dev/null
+++ b/src/gxvalid/gxvmod.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* gxvmod.h */
+/* */
+/* FreeType's TrueTypeGX/AAT validation module implementation */
+/* (specification). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __GXVMOD_H__
+#define __GXVMOD_H__
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) gxv_module_class;
+
+
+FT_END_HEADER
+
+#endif /* __GXVMOD_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort.c b/src/gxvalid/gxvmort.c
new file mode 100644
index 0000000..6fb71b9
--- /dev/null
+++ b/src/gxvalid/gxvmort.c
@@ -0,0 +1,285 @@
+/***************************************************************************/
+/* */
+/* gxvmort.c */
+/* */
+/* TrueTypeGX/AAT mort table validation (body). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+#include "gxvfeat.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ static void
+ gxv_mort_feature_validate( GXV_mort_feature f,
+ GXV_Validator valid )
+ {
+ if ( f->featureType > gxv_feat_registry_length )
+ {
+ GXV_TRACE(( "featureType %d is out of registered range, "
+ "setting %d is unchecked\n",
+ f->featureType, f->featureSetting ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+ else if ( !gxv_feat_registry[f->featureType].existence )
+ {
+ GXV_TRACE(( "featureType %d is within registered area "
+ "but undefined, setting %d is unchecked\n",
+ f->featureType, f->featureSetting ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+ else
+ {
+ FT_Byte nSettings_max;
+
+
+ /* nSettings in gxvfeat.c is halved for exclusive on/off settings */
+ nSettings_max = gxv_feat_registry[f->featureType].nSettings;
+ if ( gxv_feat_registry[f->featureType].exclusive )
+ nSettings_max = (FT_Byte)( 2 * nSettings_max );
+
+ GXV_TRACE(( "featureType %d is registered", f->featureType ));
+ GXV_TRACE(( "setting %d", f->featureSetting ));
+
+ if ( f->featureSetting > nSettings_max )
+ {
+ GXV_TRACE(( "out of defined range %d", nSettings_max ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+ GXV_TRACE(( "\n" ));
+ }
+
+ /* TODO: enableFlags must be unique value in specified chain? */
+ }
+
+
+ /*
+ * nFeatureFlags is typed to FT_UInt to accept that in
+ * mort (typed FT_UShort) and morx (typed FT_ULong).
+ */
+ FT_LOCAL_DEF( void )
+ gxv_mort_featurearray_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UInt nFeatureFlags,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt i;
+
+ GXV_mort_featureRec f = GXV_MORT_FEATURE_OFF;
+
+
+ GXV_NAME_ENTER( "mort feature list" );
+ for ( i = 0; i < nFeatureFlags; i++ )
+ {
+ GXV_LIMIT_CHECK( 2 + 2 + 4 + 4 );
+ f.featureType = FT_NEXT_USHORT( p );
+ f.featureSetting = FT_NEXT_USHORT( p );
+ f.enableFlags = FT_NEXT_ULONG( p );
+ f.disableFlags = FT_NEXT_ULONG( p );
+
+ gxv_mort_feature_validate( &f, valid );
+ }
+
+ if ( !IS_GXV_MORT_FEATURE_OFF( f ) )
+ FT_INVALID_DATA;
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_coverage_validate( FT_UShort coverage,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( valid );
+
+ if ( coverage & 0x8000U )
+ GXV_TRACE(( " this subtable is for vertical text only\n" ));
+ else
+ GXV_TRACE(( " this subtable is for horizontal text only\n" ));
+
+ if ( coverage & 0x4000 )
+ GXV_TRACE(( " this subtable is applied to glyph array "
+ "in descending order\n" ));
+ else
+ GXV_TRACE(( " this subtable is applied to glyph array "
+ "in ascending order\n" ));
+
+ if ( coverage & 0x2000 )
+ GXV_TRACE(( " this subtable is forcibly applied to "
+ "vertical/horizontal text\n" ));
+
+ if ( coverage & 0x1FF8 )
+ GXV_TRACE(( " coverage has non-zero bits in reserved area\n" ));
+ }
+
+
+ static void
+ gxv_mort_subtables_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort nSubtables,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_Validate_Func fmt_funcs_table[] =
+ {
+ gxv_mort_subtable_type0_validate, /* 0 */
+ gxv_mort_subtable_type1_validate, /* 1 */
+ gxv_mort_subtable_type2_validate, /* 2 */
+ NULL, /* 3 */
+ gxv_mort_subtable_type4_validate, /* 4 */
+ gxv_mort_subtable_type5_validate, /* 5 */
+
+ };
+
+ GXV_Validate_Func func;
+ FT_UShort i;
+
+
+ GXV_NAME_ENTER( "subtables in a chain" );
+
+ for ( i = 0; i < nSubtables; i++ )
+ {
+ FT_UShort length;
+ FT_UShort coverage;
+ FT_ULong subFeatureFlags;
+ FT_UInt type;
+ FT_UInt rest;
+
+
+ GXV_LIMIT_CHECK( 2 + 2 + 4 );
+ length = FT_NEXT_USHORT( p );
+ coverage = FT_NEXT_USHORT( p );
+ subFeatureFlags = FT_NEXT_ULONG( p );
+
+ GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n",
+ i + 1, nSubtables, length ));
+ type = coverage & 0x0007;
+ rest = length - ( 2 + 2 + 4 );
+
+ GXV_LIMIT_CHECK( rest );
+ gxv_mort_coverage_validate( coverage, valid );
+
+ if ( type > 5 )
+ FT_INVALID_FORMAT;
+
+ func = fmt_funcs_table[type];
+ if ( func == NULL )
+ GXV_TRACE(( "morx type %d is reserved\n", type ));
+
+ func( p, p + rest, valid );
+
+ p += rest;
+ }
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_mort_chain_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong defaultFlags;
+ FT_ULong chainLength;
+ FT_UShort nFeatureFlags;
+ FT_UShort nSubtables;
+
+
+ GXV_NAME_ENTER( "mort chain header" );
+
+ GXV_LIMIT_CHECK( 4 + 4 + 2 + 2 );
+ defaultFlags = FT_NEXT_ULONG( p );
+ chainLength = FT_NEXT_ULONG( p );
+ nFeatureFlags = FT_NEXT_USHORT( p );
+ nSubtables = FT_NEXT_USHORT( p );
+
+ gxv_mort_featurearray_validate( p, table + chainLength,
+ nFeatureFlags, valid );
+ p += valid->subtable_length;
+ gxv_mort_subtables_validate( p, table + chainLength, nSubtables, valid );
+ valid->subtable_length = chainLength;
+
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ FT_ULong version;
+ FT_ULong nChains;
+ FT_ULong i;
+
+
+ valid->root = ftvalid;
+ valid->face = face;
+ limit = valid->root->limit;
+
+ FT_TRACE3(( "validating `mort' table\n" ));
+ GXV_INIT;
+
+ GXV_LIMIT_CHECK( 4 + 4 );
+ version = FT_NEXT_ULONG( p );
+ nChains = FT_NEXT_ULONG( p );
+
+ if (version != 0x00010000UL)
+ FT_INVALID_FORMAT;
+
+ for ( i = 0; i < nChains; i++ )
+ {
+ GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains ));
+ GXV_32BIT_ALIGNMENT_VALIDATE( p - table );
+ gxv_mort_chain_validate( p, limit, valid );
+ p += valid->subtable_length;
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort.h b/src/gxvalid/gxvmort.h
new file mode 100644
index 0000000..1d64e69
--- /dev/null
+++ b/src/gxvalid/gxvmort.h
@@ -0,0 +1,93 @@
+/***************************************************************************/
+/* */
+/* gxvmort.h */
+/* */
+/* TrueTypeGX/AAT common definition for mort table (specification). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __GXVMORT_H__
+#define __GXVMORT_H__
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+#include FT_SFNT_NAMES_H
+
+
+ typedef struct GXV_mort_featureRec_
+ {
+ FT_UShort featureType;
+ FT_UShort featureSetting;
+ FT_ULong enableFlags;
+ FT_ULong disableFlags;
+
+ } GXV_mort_featureRec, *GXV_mort_feature;
+
+#define GXV_MORT_FEATURE_OFF {0, 1, 0x00000000UL, 0x00000000UL}
+
+#define IS_GXV_MORT_FEATURE_OFF( f ) \
+ ( (f).featureType == 0 || \
+ (f).featureSetting == 1 || \
+ (f).enableFlags == 0x00000000UL || \
+ (f).disableFlags == 0x00000000UL )
+
+
+ FT_LOCAL( void )
+ gxv_mort_featurearray_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UInt nFeatureFlags,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_coverage_validate( FT_UShort coverage,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_subtable_type0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_subtable_type1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_subtable_type2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_subtable_type4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_mort_subtable_type5_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+
+#endif /* __GXVMORT_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort0.c b/src/gxvalid/gxvmort0.c
new file mode 100644
index 0000000..0902056
--- /dev/null
+++ b/src/gxvalid/gxvmort0.c
@@ -0,0 +1,137 @@
+/***************************************************************************/
+/* */
+/* gxvmort0.c */
+/* */
+/* TrueTypeGX/AAT mort table validation */
+/* body for type0 (Indic Script Rearrangement) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ static const char* GXV_Mort_IndicScript_Msg[] =
+ {
+ "no change",
+ "Ax => xA",
+ "xD => Dx",
+ "AxD => DxA",
+ "ABx => xAB",
+ "ABx => xBA",
+ "xCD => CDx",
+ "xCD => DCx",
+ "AxCD => CDxA",
+ "AxCD => DCxA",
+ "ABxD => DxAB",
+ "ABxD => DxBA",
+ "ABxCD => CDxAB",
+ "ABxCD => CDxBA",
+ "ABxCD => DCxAB",
+ "ABxCD => DCxBA",
+
+ };
+
+
+ static void
+ gxv_mort_subtable_type0_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort markFirst;
+ FT_UShort dontAdvance;
+ FT_UShort markLast;
+ FT_UShort reserved;
+ FT_UShort verb = 0;
+
+ FT_UNUSED( state );
+ FT_UNUSED( table );
+ FT_UNUSED( limit );
+
+ FT_UNUSED( GXV_Mort_IndicScript_Msg[verb] ); /* for the non-debugging */
+ FT_UNUSED( glyphOffset ); /* case */
+
+
+ markFirst = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ markLast = (FT_UShort)( ( flags >> 13 ) & 1 );
+
+ reserved = (FT_UShort)( flags & 0x1FF0 );
+ verb = (FT_UShort)( flags & 0x000F );
+
+ GXV_TRACE(( " IndicScript MorphRule for glyphOffset 0x%04x",
+ glyphOffset.u ));
+ GXV_TRACE(( " markFirst=%01d", markFirst ));
+ GXV_TRACE(( " dontAdvance=%01d", dontAdvance ));
+ GXV_TRACE(( " markLast=%01d", markLast ));
+ GXV_TRACE(( " %02d", verb ));
+ GXV_TRACE(( " %s\n", GXV_Mort_IndicScript_Msg[verb] ));
+
+ if ( 0 < reserved )
+ {
+ GXV_TRACE(( " non-zero bits found in reserved range\n" ));
+ FT_INVALID_DATA;
+ }
+ else
+ GXV_TRACE(( "\n" ));
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER(
+ "mort chain subtable type0 (Indic-Script Rearrangement)" );
+
+ GXV_LIMIT_CHECK( GXV_STATETABLE_HEADER_SIZE );
+
+ valid->statetable.optdata = NULL;
+ valid->statetable.optdata_load_func = NULL;
+ valid->statetable.subtable_setup_func = NULL;
+ valid->statetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE;
+ valid->statetable.entry_validate_func =
+ gxv_mort_subtable_type0_entry_validate;
+
+ gxv_StateTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort1.c b/src/gxvalid/gxvmort1.c
new file mode 100644
index 0000000..0575b12
--- /dev/null
+++ b/src/gxvalid/gxvmort1.c
@@ -0,0 +1,258 @@
+/***************************************************************************/
+/* */
+/* gxvmort1.c */
+/* */
+/* TrueTypeGX/AAT mort table validation */
+/* body for type1 (Contextual Substitution) subtable. */
+/* */
+/* Copyright 2005, 2007 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ typedef struct GXV_mort_subtable_type1_StateOptRec_
+ {
+ FT_UShort substitutionTable;
+ FT_UShort substitutionTable_length;
+
+ } GXV_mort_subtable_type1_StateOptRec,
+ *GXV_mort_subtable_type1_StateOptRecData;
+
+#define GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE \
+ ( GXV_STATETABLE_HEADER_SIZE + 2 )
+
+
+ static void
+ gxv_mort_subtable_type1_substitutionTable_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_mort_subtable_type1_StateOptRecData optdata =
+ (GXV_mort_subtable_type1_StateOptRecData)valid->statetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ optdata->substitutionTable = FT_NEXT_USHORT( p );
+ }
+
+
+ static void
+ gxv_mort_subtable_type1_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_UShort o[4];
+ FT_UShort *l[4];
+ FT_UShort buff[5];
+
+ GXV_mort_subtable_type1_StateOptRecData optdata =
+ (GXV_mort_subtable_type1_StateOptRecData)valid->statetable.optdata;
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->substitutionTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &( optdata->substitutionTable_length );
+
+ gxv_set_length_by_ushort_offset( o, l, buff, 4, table_size, valid );
+ }
+
+
+ static void
+ gxv_mort_subtable_type1_offset_to_subst_validate(
+ FT_Short wordOffset,
+ const FT_String* tag,
+ FT_Byte state,
+ GXV_Validator valid )
+ {
+ FT_UShort substTable;
+ FT_UShort substTable_limit;
+ FT_UShort min_gid;
+ FT_UShort max_gid;
+
+ FT_UNUSED( tag );
+ FT_UNUSED( state );
+
+
+ substTable =
+ ((GXV_mort_subtable_type1_StateOptRec *)
+ (valid->statetable.optdata))->substitutionTable;
+ substTable_limit =
+ (FT_UShort)( substTable +
+ ((GXV_mort_subtable_type1_StateOptRec *)
+ (valid->statetable.optdata))->substitutionTable_length );
+
+ min_gid = (FT_UShort)( ( substTable - wordOffset * 2 ) / 2 );
+ max_gid = (FT_UShort)( ( substTable_limit - wordOffset * 2 ) / 2 );
+ max_gid = (FT_UShort)( FT_MAX( max_gid, valid->face->num_glyphs ) );
+
+ /* XXX: check range? */
+
+ /* TODO: min_gid & max_gid comparison with ClassTable contents */
+ }
+
+
+ static void
+ gxv_mort_subtable_type1_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort setMark;
+ FT_UShort dontAdvance;
+ FT_UShort reserved;
+ FT_Short markOffset;
+ FT_Short currentOffset;
+
+ FT_UNUSED( table );
+ FT_UNUSED( limit );
+
+
+ setMark = (FT_UShort)( flags >> 15 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ reserved = (FT_Short)( flags & 0x3FFF );
+
+ markOffset = (FT_Short)( glyphOffset.ul >> 16 );
+ currentOffset = (FT_Short)( glyphOffset.ul );
+
+ if ( 0 < reserved )
+ {
+ GXV_TRACE(( " non-zero bits found in reserved range\n" ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+
+ gxv_mort_subtable_type1_offset_to_subst_validate( markOffset,
+ "markOffset",
+ state,
+ valid );
+
+ gxv_mort_subtable_type1_offset_to_subst_validate( currentOffset,
+ "currentOffset",
+ state,
+ valid );
+ }
+
+
+ static void
+ gxv_mort_subtable_type1_substTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort num_gids = (FT_UShort)(
+ ((GXV_mort_subtable_type1_StateOptRec *)
+ (valid->statetable.optdata))->substitutionTable_length / 2 );
+ FT_UShort i;
+
+
+ GXV_NAME_ENTER( "validating contents of substitutionTable" );
+ for ( i = 0; i < num_gids ; i ++ )
+ {
+ FT_UShort dst_gid;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ dst_gid = FT_NEXT_USHORT( p );
+
+ if ( dst_gid >= 0xFFFFU )
+ continue;
+
+ if ( dst_gid > valid->face->num_glyphs )
+ {
+ GXV_TRACE(( "substTable include toolarge gid[%d]=%d >"
+ " max defined gid #%d\n",
+ i, dst_gid, valid->face->num_glyphs ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ GXV_EXIT;
+ }
+
+
+ /*
+ * subtable for Contextual glyph substitution is a modified StateTable.
+ * In addition to classTable, stateArray, and entryTable, the field
+ * `substitutionTable' is added.
+ */
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_mort_subtable_type1_StateOptRec st_rec;
+
+
+ GXV_NAME_ENTER( "mort chain subtable type1 (Contextual Glyph Subst)" );
+
+ GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE1_HEADER_SIZE );
+
+ valid->statetable.optdata =
+ &st_rec;
+ valid->statetable.optdata_load_func =
+ gxv_mort_subtable_type1_substitutionTable_load;
+ valid->statetable.subtable_setup_func =
+ gxv_mort_subtable_type1_subtable_setup;
+ valid->statetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_ULONG;
+ valid->statetable.entry_validate_func =
+
+ gxv_mort_subtable_type1_entry_validate;
+ gxv_StateTable_validate( p, limit, valid );
+
+ gxv_mort_subtable_type1_substTable_validate(
+ table + st_rec.substitutionTable,
+ table + st_rec.substitutionTable + st_rec.substitutionTable_length,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort2.c b/src/gxvalid/gxvmort2.c
new file mode 100644
index 0000000..f19d15d
--- /dev/null
+++ b/src/gxvalid/gxvmort2.c
@@ -0,0 +1,282 @@
+/***************************************************************************/
+/* */
+/* gxvmort2.c */
+/* */
+/* TrueTypeGX/AAT mort table validation */
+/* body for type2 (Ligature Substitution) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ typedef struct GXV_mort_subtable_type2_StateOptRec_
+ {
+ FT_UShort ligActionTable;
+ FT_UShort componentTable;
+ FT_UShort ligatureTable;
+ FT_UShort ligActionTable_length;
+ FT_UShort componentTable_length;
+ FT_UShort ligatureTable_length;
+
+ } GXV_mort_subtable_type2_StateOptRec,
+ *GXV_mort_subtable_type2_StateOptRecData;
+
+#define GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE \
+ ( GXV_STATETABLE_HEADER_SIZE + 2 + 2 + 2 )
+
+
+ static void
+ gxv_mort_subtable_type2_opttable_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ GXV_mort_subtable_type2_StateOptRecData optdata =
+ (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 2 + 2 + 2 );
+ optdata->ligActionTable = FT_NEXT_USHORT( p );
+ optdata->componentTable = FT_NEXT_USHORT( p );
+ optdata->ligatureTable = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( "offset to ligActionTable=0x%04x\n",
+ optdata->ligActionTable ));
+ GXV_TRACE(( "offset to componentTable=0x%04x\n",
+ optdata->componentTable ));
+ GXV_TRACE(( "offset to ligatureTable=0x%04x\n",
+ optdata->ligatureTable ));
+ }
+
+
+ static void
+ gxv_mort_subtable_type2_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort *classTable_length_p,
+ FT_UShort *stateArray_length_p,
+ FT_UShort *entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_UShort o[6];
+ FT_UShort *l[6];
+ FT_UShort buff[7];
+
+ GXV_mort_subtable_type2_StateOptRecData optdata =
+ (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;
+
+
+ GXV_NAME_ENTER( "subtable boundaries setup" );
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->ligActionTable;
+ o[4] = optdata->componentTable;
+ o[5] = optdata->ligatureTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &(optdata->ligActionTable_length);
+ l[4] = &(optdata->componentTable_length);
+ l[5] = &(optdata->ligatureTable_length);
+
+ gxv_set_length_by_ushort_offset( o, l, buff, 6, table_size, valid );
+
+ GXV_TRACE(( "classTable: offset=0x%04x length=0x%04x\n",
+ classTable, *classTable_length_p ));
+ GXV_TRACE(( "stateArray: offset=0x%04x length=0x%04x\n",
+ stateArray, *stateArray_length_p ));
+ GXV_TRACE(( "entryTable: offset=0x%04x length=0x%04x\n",
+ entryTable, *entryTable_length_p ));
+ GXV_TRACE(( "ligActionTable: offset=0x%04x length=0x%04x\n",
+ optdata->ligActionTable,
+ optdata->ligActionTable_length ));
+ GXV_TRACE(( "componentTable: offset=0x%04x length=0x%04x\n",
+ optdata->componentTable,
+ optdata->componentTable_length ));
+ GXV_TRACE(( "ligatureTable: offset=0x%04x length=0x%04x\n",
+ optdata->ligatureTable,
+ optdata->ligatureTable_length ));
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_mort_subtable_type2_ligActionOffset_validate(
+ FT_Bytes table,
+ FT_UShort ligActionOffset,
+ GXV_Validator valid )
+ {
+ /* access ligActionTable */
+ GXV_mort_subtable_type2_StateOptRecData optdata =
+ (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;
+
+ FT_Bytes lat_base = table + optdata->ligActionTable;
+ FT_Bytes p = table + ligActionOffset;
+ FT_Bytes lat_limit = lat_base + optdata->ligActionTable;
+
+
+ GXV_32BIT_ALIGNMENT_VALIDATE( ligActionOffset );
+ if ( p < lat_base )
+ {
+ GXV_TRACE(( "too short offset 0x%04x: p < lat_base (%d byte rewind)\n",
+ ligActionOffset, lat_base - p ));
+
+ /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ }
+ else if ( lat_limit < p )
+ {
+ GXV_TRACE(( "too large offset 0x%04x: lat_limit < p (%d byte overrun)\n",
+ ligActionOffset, p - lat_limit ));
+
+ /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_OFFSET;
+ }
+ else
+ {
+ /* validate entry in ligActionTable */
+ FT_ULong lig_action;
+ FT_UShort last;
+ FT_UShort store;
+ FT_ULong offset;
+
+
+ lig_action = FT_NEXT_ULONG( p );
+ last = (FT_UShort)( ( lig_action >> 31 ) & 1 );
+ store = (FT_UShort)( ( lig_action >> 30 ) & 1 );
+
+ offset = lig_action & 0x3FFFFFFFUL;
+ }
+ }
+
+
+ static void
+ gxv_mort_subtable_type2_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort setComponent;
+ FT_UShort dontAdvance;
+ FT_UShort offset;
+
+ FT_UNUSED( state );
+ FT_UNUSED( glyphOffset );
+ FT_UNUSED( limit );
+
+
+ setComponent = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+
+ offset = (FT_UShort)( flags & 0x3FFFU );
+
+ if ( 0 < offset )
+ gxv_mort_subtable_type2_ligActionOffset_validate( table, offset,
+ valid );
+ }
+
+
+ static void
+ gxv_mort_subtable_type2_ligatureTable_validate( FT_Bytes table,
+ GXV_Validator valid )
+ {
+ GXV_mort_subtable_type2_StateOptRecData optdata =
+ (GXV_mort_subtable_type2_StateOptRecData)valid->statetable.optdata;
+
+ FT_Bytes p = table + optdata->ligatureTable;
+ FT_Bytes limit = table + optdata->ligatureTable
+ + optdata->ligatureTable_length;
+
+
+ GXV_NAME_ENTER( "mort chain subtable type2 - substitutionTable" );
+ if ( 0 != optdata->ligatureTable )
+ {
+ /* Apple does not give specification of ligatureTable format */
+ while ( p < limit )
+ {
+ FT_UShort lig_gid;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ lig_gid = FT_NEXT_USHORT( p );
+ }
+ }
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_mort_subtable_type2_StateOptRec lig_rec;
+
+
+ GXV_NAME_ENTER( "mort chain subtable type2 (Ligature Substitution)" );
+
+ GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE );
+
+ valid->statetable.optdata =
+ &lig_rec;
+ valid->statetable.optdata_load_func =
+ gxv_mort_subtable_type2_opttable_load;
+ valid->statetable.subtable_setup_func =
+ gxv_mort_subtable_type2_subtable_setup;
+ valid->statetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_NONE;
+ valid->statetable.entry_validate_func =
+ gxv_mort_subtable_type2_entry_validate;
+
+ gxv_StateTable_validate( p, limit, valid );
+
+ p += valid->subtable_length;
+ gxv_mort_subtable_type2_ligatureTable_validate( table, valid );
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort4.c b/src/gxvalid/gxvmort4.c
new file mode 100644
index 0000000..a04bc1e
--- /dev/null
+++ b/src/gxvalid/gxvmort4.c
@@ -0,0 +1,125 @@
+/***************************************************************************/
+/* */
+/* gxvmort4.c */
+/* */
+/* TrueTypeGX/AAT mort table validation */
+/* body for type4 (Non-Contextual Glyph Substitution) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ static void
+ gxv_mort_subtable_type4_lookupval_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( glyph );
+
+ gxv_glyphid_validate( value.u, valid );
+ }
+
+ /*
+ +===============+ --------+
+ | lookup header | |
+ +===============+ |
+ | BinSrchHeader | |
+ +===============+ |
+ | lastGlyph[0] | |
+ +---------------+ |
+ | firstGlyph[0] | | head of lookup table
+ +---------------+ | +
+ | offset[0] | -> | offset [byte]
+ +===============+ | +
+ | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte]
+ +---------------+ |
+ | firstGlyph[1] | |
+ +---------------+ |
+ | offset[1] | |
+ +===============+ |
+ |
+ .... |
+ |
+ 16bit value array |
+ +===============+ |
+ | value | <-------+
+ ....
+ */
+
+ static GXV_LookupValueDesc
+ gxv_mort_subtable_type4_lookupfmt4_transit(
+ FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ /* XXX: check range? */
+ offset = (FT_UShort)( base_value.u +
+ relative_gindex * sizeof ( FT_UShort ) );
+
+ p = valid->lookuptbl_head + offset;
+ limit = lookuptbl_limit;
+
+ GXV_LIMIT_CHECK( 2 );
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER( "mort chain subtable type4 "
+ "(Non-Contextual Glyph Substitution)" );
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_mort_subtable_type4_lookupval_validate;
+ valid->lookupfmt4_trans = gxv_mort_subtable_type4_lookupfmt4_transit;
+
+ gxv_LookupTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmort5.c b/src/gxvalid/gxvmort5.c
new file mode 100644
index 0000000..a7cabc3
--- /dev/null
+++ b/src/gxvalid/gxvmort5.c
@@ -0,0 +1,226 @@
+/***************************************************************************/
+/* */
+/* gxvmort5.c */
+/* */
+/* TrueTypeGX/AAT mort table validation */
+/* body for type5 (Contextual Glyph Insertion) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmort.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmort
+
+
+ /*
+ * mort subtable type5 (Contextual Glyph Insertion)
+ * has the format of StateTable with insertion-glyph-list,
+ * but without name. The offset is given by glyphOffset in
+ * entryTable. There is no table location declaration
+ * like xxxTable.
+ */
+
+ typedef struct GXV_mort_subtable_type5_StateOptRec_
+ {
+ FT_UShort classTable;
+ FT_UShort stateArray;
+ FT_UShort entryTable;
+
+#define GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE GXV_STATETABLE_HEADER_SIZE
+
+ FT_UShort* classTable_length_p;
+ FT_UShort* stateArray_length_p;
+ FT_UShort* entryTable_length_p;
+
+ } GXV_mort_subtable_type5_StateOptRec,
+ *GXV_mort_subtable_type5_StateOptRecData;
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type5_subtable_setup( FT_UShort table_size,
+ FT_UShort classTable,
+ FT_UShort stateArray,
+ FT_UShort entryTable,
+ FT_UShort* classTable_length_p,
+ FT_UShort* stateArray_length_p,
+ FT_UShort* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ GXV_mort_subtable_type5_StateOptRecData optdata =
+ (GXV_mort_subtable_type5_StateOptRecData)valid->statetable.optdata;
+
+
+ gxv_StateTable_subtable_setup( table_size,
+ classTable,
+ stateArray,
+ entryTable,
+ classTable_length_p,
+ stateArray_length_p,
+ entryTable_length_p,
+ valid );
+
+ optdata->classTable = classTable;
+ optdata->stateArray = stateArray;
+ optdata->entryTable = entryTable;
+
+ optdata->classTable_length_p = classTable_length_p;
+ optdata->stateArray_length_p = stateArray_length_p;
+ optdata->entryTable_length_p = entryTable_length_p;
+ }
+
+
+ static void
+ gxv_mort_subtable_type5_InsertList_validate( FT_UShort offset,
+ FT_UShort count,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ /*
+ * We don't know the range of insertion-glyph-list.
+ * Set range by whole of state table.
+ */
+ FT_Bytes p = table + offset;
+
+ GXV_mort_subtable_type5_StateOptRecData optdata =
+ (GXV_mort_subtable_type5_StateOptRecData)valid->statetable.optdata;
+
+ if ( optdata->classTable < offset &&
+ offset < optdata->classTable + *(optdata->classTable_length_p) )
+ GXV_TRACE(( " offset runs into ClassTable" ));
+ if ( optdata->stateArray < offset &&
+ offset < optdata->stateArray + *(optdata->stateArray_length_p) )
+ GXV_TRACE(( " offset runs into StateArray" ));
+ if ( optdata->entryTable < offset &&
+ offset < optdata->entryTable + *(optdata->entryTable_length_p) )
+ GXV_TRACE(( " offset runs into EntryTable" ));
+
+ while ( p < table + offset + ( count * 2 ) )
+ {
+ FT_UShort insert_glyphID;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ insert_glyphID = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " 0x%04x", insert_glyphID ));
+ }
+
+ GXV_TRACE(( "\n" ));
+ }
+
+
+ static void
+ gxv_mort_subtable_type5_entry_validate(
+ FT_Byte state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bool setMark;
+ FT_Bool dontAdvance;
+ FT_Bool currentIsKashidaLike;
+ FT_Bool markedIsKashidaLike;
+ FT_Bool currentInsertBefore;
+ FT_Bool markedInsertBefore;
+ FT_Byte currentInsertCount;
+ FT_Byte markedInsertCount;
+ FT_UShort currentInsertList;
+ FT_UShort markedInsertList;
+
+ FT_UNUSED( state );
+
+
+ setMark = FT_BOOL( ( flags >> 15 ) & 1 );
+ dontAdvance = FT_BOOL( ( flags >> 14 ) & 1 );
+ currentIsKashidaLike = FT_BOOL( ( flags >> 13 ) & 1 );
+ markedIsKashidaLike = FT_BOOL( ( flags >> 12 ) & 1 );
+ currentInsertBefore = FT_BOOL( ( flags >> 11 ) & 1 );
+ markedInsertBefore = FT_BOOL( ( flags >> 10 ) & 1 );
+
+ currentInsertCount = (FT_Byte)( ( flags >> 5 ) & 0x1F );
+ markedInsertCount = (FT_Byte)( flags & 0x001F );
+
+ currentInsertList = (FT_UShort)( glyphOffset.ul >> 16 );
+ markedInsertList = (FT_UShort)( glyphOffset.ul );
+
+ if ( 0 != currentInsertList && 0 != currentInsertCount )
+ {
+ gxv_mort_subtable_type5_InsertList_validate( currentInsertList,
+ currentInsertCount,
+ table,
+ limit,
+ valid );
+ }
+
+ if ( 0 != markedInsertList && 0 != markedInsertCount )
+ {
+ gxv_mort_subtable_type5_InsertList_validate( markedInsertList,
+ markedInsertCount,
+ table,
+ limit,
+ valid );
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_mort_subtable_type5_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_mort_subtable_type5_StateOptRec et_rec;
+ GXV_mort_subtable_type5_StateOptRecData et = &et_rec;
+
+
+ GXV_NAME_ENTER( "mort chain subtable type5 (Glyph Insertion)" );
+
+ GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE5_HEADER_SIZE );
+
+ valid->statetable.optdata =
+ et;
+ valid->statetable.optdata_load_func =
+ NULL;
+ valid->statetable.subtable_setup_func =
+ gxv_mort_subtable_type5_subtable_setup;
+ valid->statetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_ULONG;
+ valid->statetable.entry_validate_func =
+ gxv_mort_subtable_type5_entry_validate;
+
+ gxv_StateTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx.c b/src/gxvalid/gxvmorx.c
new file mode 100644
index 0000000..849d5e9
--- /dev/null
+++ b/src/gxvalid/gxvmorx.c
@@ -0,0 +1,183 @@
+/***************************************************************************/
+/* */
+/* gxvmorx.c */
+/* */
+/* TrueTypeGX/AAT morx table validation (body). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ static void
+ gxv_morx_subtables_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort nSubtables,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_Validate_Func fmt_funcs_table[] =
+ {
+ gxv_morx_subtable_type0_validate, /* 0 */
+ gxv_morx_subtable_type1_validate, /* 1 */
+ gxv_morx_subtable_type2_validate, /* 2 */
+ NULL, /* 3 */
+ gxv_morx_subtable_type4_validate, /* 4 */
+ gxv_morx_subtable_type5_validate, /* 5 */
+
+ };
+
+ GXV_Validate_Func func;
+
+ FT_UShort i;
+
+
+ GXV_NAME_ENTER( "subtables in a chain" );
+
+ for ( i = 0; i < nSubtables; i++ )
+ {
+ FT_ULong length;
+ FT_ULong coverage;
+ FT_ULong subFeatureFlags;
+ FT_UInt type;
+ FT_UInt rest;
+
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 );
+ length = FT_NEXT_ULONG( p );
+ coverage = FT_NEXT_ULONG( p );
+ subFeatureFlags = FT_NEXT_ULONG( p );
+
+ GXV_TRACE(( "validating chain subtable %d/%d (%d bytes)\n",
+ i + 1, nSubtables, length ));
+
+ type = coverage & 0x0007;
+ rest = length - ( 4 + 4 + 4 );
+ GXV_LIMIT_CHECK( rest );
+
+ /* morx coverage consists of mort_coverage & 16bit padding */
+ gxv_mort_coverage_validate( (FT_UShort)( ( coverage >> 16 ) | coverage ),
+ valid );
+ if ( type > 5 )
+ FT_INVALID_FORMAT;
+
+ func = fmt_funcs_table[type];
+ if ( func == NULL )
+ GXV_TRACE(( "morx type %d is reserved\n", type ));
+
+ func( p, p + rest, valid );
+
+ p += rest;
+ }
+
+ valid->subtable_length = p - table;
+
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_morx_chain_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_ULong defaultFlags;
+ FT_ULong chainLength;
+ FT_ULong nFeatureFlags;
+ FT_ULong nSubtables;
+
+
+ GXV_NAME_ENTER( "morx chain header" );
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 + 4 );
+ defaultFlags = FT_NEXT_ULONG( p );
+ chainLength = FT_NEXT_ULONG( p );
+ nFeatureFlags = FT_NEXT_ULONG( p );
+ nSubtables = FT_NEXT_ULONG( p );
+
+ /* feature-array of morx is same with that of mort */
+ gxv_mort_featurearray_validate( p, limit, nFeatureFlags, valid );
+ p += valid->subtable_length;
+
+ if ( nSubtables >= 0x10000 )
+ FT_INVALID_DATA;
+
+ gxv_morx_subtables_validate( p, table + chainLength,
+ (FT_UShort)nSubtables, valid );
+
+ valid->subtable_length = chainLength;
+
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_morx_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ FT_ULong version;
+ FT_ULong nChains;
+ FT_ULong i;
+
+
+ valid->root = ftvalid;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `morx' table\n" ));
+ GXV_INIT;
+
+ GXV_LIMIT_CHECK( 4 + 4 );
+ version = FT_NEXT_ULONG( p );
+ nChains = FT_NEXT_ULONG( p );
+
+ if ( version != 0x00020000UL )
+ FT_INVALID_FORMAT;
+
+ for ( i = 0; i < nChains; i++ )
+ {
+ GXV_TRACE(( "validating chain %d/%d\n", i + 1, nChains ));
+ GXV_32BIT_ALIGNMENT_VALIDATE( p - table );
+ gxv_morx_chain_validate( p, limit, valid );
+ p += valid->subtable_length;
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx.h b/src/gxvalid/gxvmorx.h
new file mode 100644
index 0000000..28c1a44
--- /dev/null
+++ b/src/gxvalid/gxvmorx.h
@@ -0,0 +1,67 @@
+/***************************************************************************/
+/* */
+/* gxvmorx.h */
+/* */
+/* TrueTypeGX/AAT common definition for morx table (specification). */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __GXVMORX_H__
+#define __GXVMORX_H__
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+#include "gxvmort.h"
+
+#include FT_SFNT_NAMES_H
+
+
+ FT_LOCAL( void )
+ gxv_morx_subtable_type0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_morx_subtable_type1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_morx_subtable_type2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_morx_subtable_type4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+ FT_LOCAL( void )
+ gxv_morx_subtable_type5_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid );
+
+
+#endif /* __GXVMORX_H__ */
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx0.c b/src/gxvalid/gxvmorx0.c
new file mode 100644
index 0000000..ca92b6c
--- /dev/null
+++ b/src/gxvalid/gxvmorx0.c
@@ -0,0 +1,103 @@
+/***************************************************************************/
+/* */
+/* gxvmorx0.c */
+/* */
+/* TrueTypeGX/AAT morx table validation */
+/* body for type0 (Indic Script Rearrangement) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ static void
+ gxv_morx_subtable_type0_entry_validate(
+ FT_UShort state,
+ FT_UShort flags,
+ GXV_XStateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort markFirst;
+ FT_UShort dontAdvance;
+ FT_UShort markLast;
+ FT_UShort reserved;
+ FT_UShort verb;
+
+ FT_UNUSED( state );
+ FT_UNUSED( glyphOffset );
+ FT_UNUSED( table );
+ FT_UNUSED( limit );
+
+
+ markFirst = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ markLast = (FT_UShort)( ( flags >> 13 ) & 1 );
+
+ reserved = (FT_UShort)( flags & 0x1FF0 );
+ verb = (FT_UShort)( flags & 0x000F );
+
+ if ( 0 < reserved )
+ {
+ GXV_TRACE(( " non-zero bits found in reserved range\n" ));
+ FT_INVALID_DATA;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_morx_subtable_type0_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+
+ GXV_NAME_ENTER(
+ "morx chain subtable type0 (Indic-Script Rearrangement)" );
+
+ GXV_LIMIT_CHECK( GXV_STATETABLE_HEADER_SIZE );
+
+ valid->xstatetable.optdata = NULL;
+ valid->xstatetable.optdata_load_func = NULL;
+ valid->xstatetable.subtable_setup_func = NULL;
+ valid->xstatetable.entry_glyphoffset_fmt = GXV_GLYPHOFFSET_NONE;
+ valid->xstatetable.entry_validate_func =
+ gxv_morx_subtable_type0_entry_validate;
+
+ gxv_XStateTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx1.c b/src/gxvalid/gxvmorx1.c
new file mode 100644
index 0000000..331d4cc
--- /dev/null
+++ b/src/gxvalid/gxvmorx1.c
@@ -0,0 +1,274 @@
+/***************************************************************************/
+/* */
+/* gxvmorx1.c */
+/* */
+/* TrueTypeGX/AAT morx table validation */
+/* body for type1 (Contextual Substitution) subtable. */
+/* */
+/* Copyright 2005, 2007 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ typedef struct GXV_morx_subtable_type1_StateOptRec_
+ {
+ FT_ULong substitutionTable;
+ FT_ULong substitutionTable_length;
+ FT_UShort substitutionTable_num_lookupTables;
+
+ } GXV_morx_subtable_type1_StateOptRec,
+ *GXV_morx_subtable_type1_StateOptRecData;
+
+
+#define GXV_MORX_SUBTABLE_TYPE1_HEADER_SIZE \
+ ( GXV_STATETABLE_HEADER_SIZE + 2 )
+
+
+ static void
+ gxv_morx_subtable_type1_substitutionTable_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type1_StateOptRecData optdata =
+ (GXV_morx_subtable_type1_StateOptRecData)valid->xstatetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ optdata->substitutionTable = FT_NEXT_USHORT( p );
+ }
+
+
+ static void
+ gxv_morx_subtable_type1_subtable_setup( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_ULong o[4];
+ FT_ULong *l[4];
+ FT_ULong buff[5];
+
+ GXV_morx_subtable_type1_StateOptRecData optdata =
+ (GXV_morx_subtable_type1_StateOptRecData)valid->xstatetable.optdata;
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->substitutionTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &(optdata->substitutionTable_length);
+
+ gxv_set_length_by_ulong_offset( o, l, buff, 4, table_size, valid );
+ }
+
+
+ static void
+ gxv_morx_subtable_type1_entry_validate(
+ FT_UShort state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort setMark;
+ FT_UShort dontAdvance;
+ FT_UShort reserved;
+ FT_Short markIndex;
+ FT_Short currentIndex;
+
+ GXV_morx_subtable_type1_StateOptRecData optdata =
+ (GXV_morx_subtable_type1_StateOptRecData)valid->xstatetable.optdata;
+
+ FT_UNUSED( state );
+ FT_UNUSED( table );
+ FT_UNUSED( limit );
+
+
+ setMark = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+
+ reserved = (FT_UShort)( flags & 0x3FFF );
+
+ markIndex = (FT_Short)( glyphOffset.ul >> 16 );
+ currentIndex = (FT_Short)( glyphOffset.ul );
+
+ GXV_TRACE(( " setMark=%01d dontAdvance=%01d\n",
+ setMark, dontAdvance ));
+
+ if ( 0 < reserved )
+ {
+ GXV_TRACE(( " non-zero bits found in reserved range\n" ));
+ if ( valid->root->level >= FT_VALIDATE_PARANOID )
+ FT_INVALID_DATA;
+ }
+
+ GXV_TRACE(( "markIndex = %d, currentIndex = %d\n",
+ markIndex, currentIndex ));
+
+ if ( optdata->substitutionTable_num_lookupTables < markIndex + 1 )
+ optdata->substitutionTable_num_lookupTables =
+ (FT_Short)( markIndex + 1 );
+
+ if ( optdata->substitutionTable_num_lookupTables < currentIndex + 1 )
+ optdata->substitutionTable_num_lookupTables =
+ (FT_Short)( currentIndex + 1 );
+ }
+
+
+ static void
+ gxv_morx_subtable_type1_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ FT_UNUSED( glyph ); /* for the non-debugging case */
+
+ GXV_TRACE(( "morx subtable type1 subst.: %d -> %d\n", glyph, value.u ));
+
+ if ( value.u > valid->face->num_glyphs )
+ FT_INVALID_GLYPH_ID;
+ }
+
+
+ static GXV_LookupValueDesc
+ gxv_morx_subtable_type1_LookupFmt4_transit(
+ FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ /* XXX: check range? */
+ offset = (FT_UShort)( base_value.u +
+ relative_gindex * sizeof ( FT_UShort ) );
+
+ p = valid->lookuptbl_head + offset;
+ limit = lookuptbl_limit;
+
+ GXV_LIMIT_CHECK ( 2 );
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ /*
+ * TODO: length should be limit?
+ **/
+ static void
+ gxv_morx_subtable_type1_substitutionTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort i;
+
+ GXV_morx_subtable_type1_StateOptRecData optdata =
+ (GXV_morx_subtable_type1_StateOptRecData)valid->xstatetable.optdata;
+
+
+ /* TODO: calculate offset/length for each lookupTables */
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_morx_subtable_type1_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_morx_subtable_type1_LookupFmt4_transit;
+
+ for ( i = 0; i < optdata->substitutionTable_num_lookupTables; i++ )
+ {
+ FT_ULong offset;
+
+
+ GXV_LIMIT_CHECK( 4 );
+ offset = FT_NEXT_ULONG( p );
+
+ gxv_LookupTable_validate( table + offset, limit, valid );
+ }
+
+ /* TODO: overlapping of lookupTables in substitutionTable */
+ }
+
+
+ /*
+ * subtable for Contextual glyph substitution is a modified StateTable.
+ * In addition to classTable, stateArray, entryTable, the field
+ * `substitutionTable' is added.
+ */
+ FT_LOCAL_DEF( void )
+ gxv_morx_subtable_type1_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type1_StateOptRec st_rec;
+
+
+ GXV_NAME_ENTER( "morx chain subtable type1 (Contextual Glyph Subst)" );
+
+ GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE1_HEADER_SIZE );
+
+ st_rec.substitutionTable_num_lookupTables = 0;
+
+ valid->xstatetable.optdata =
+ &st_rec;
+ valid->xstatetable.optdata_load_func =
+ gxv_morx_subtable_type1_substitutionTable_load;
+ valid->xstatetable.subtable_setup_func =
+ gxv_morx_subtable_type1_subtable_setup;
+ valid->xstatetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_ULONG;
+ valid->xstatetable.entry_validate_func =
+ gxv_morx_subtable_type1_entry_validate;
+
+ gxv_XStateTable_validate( p, limit, valid );
+
+ gxv_morx_subtable_type1_substitutionTable_validate(
+ table + st_rec.substitutionTable,
+ table + st_rec.substitutionTable + st_rec.substitutionTable_length,
+ valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx2.c b/src/gxvalid/gxvmorx2.c
new file mode 100644
index 0000000..5cad516
--- /dev/null
+++ b/src/gxvalid/gxvmorx2.c
@@ -0,0 +1,285 @@
+/***************************************************************************/
+/* */
+/* gxvmorx2.c */
+/* */
+/* TrueTypeGX/AAT morx table validation */
+/* body for type2 (Ligature Substitution) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ typedef struct GXV_morx_subtable_type2_StateOptRec_
+ {
+ FT_ULong ligActionTable;
+ FT_ULong componentTable;
+ FT_ULong ligatureTable;
+ FT_ULong ligActionTable_length;
+ FT_ULong componentTable_length;
+ FT_ULong ligatureTable_length;
+
+ } GXV_morx_subtable_type2_StateOptRec,
+ *GXV_morx_subtable_type2_StateOptRecData;
+
+
+#define GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE \
+ ( GXV_XSTATETABLE_HEADER_SIZE + 4 + 4 + 4 )
+
+
+ static void
+ gxv_morx_subtable_type2_opttable_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type2_StateOptRecData optdata =
+ (GXV_morx_subtable_type2_StateOptRecData)valid->xstatetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 4 + 4 + 4 );
+ optdata->ligActionTable = FT_NEXT_ULONG( p );
+ optdata->componentTable = FT_NEXT_ULONG( p );
+ optdata->ligatureTable = FT_NEXT_ULONG( p );
+
+ GXV_TRACE(( "offset to ligActionTable=0x%08x\n",
+ optdata->ligActionTable ));
+ GXV_TRACE(( "offset to componentTable=0x%08x\n",
+ optdata->componentTable ));
+ GXV_TRACE(( "offset to ligatureTable=0x%08x\n",
+ optdata->ligatureTable ));
+ }
+
+
+ static void
+ gxv_morx_subtable_type2_subtable_setup( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_ULong o[6];
+ FT_ULong* l[6];
+ FT_ULong buff[7];
+
+ GXV_morx_subtable_type2_StateOptRecData optdata =
+ (GXV_morx_subtable_type2_StateOptRecData)valid->xstatetable.optdata;
+
+
+ GXV_NAME_ENTER( "subtable boundaries setup" );
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->ligActionTable;
+ o[4] = optdata->componentTable;
+ o[5] = optdata->ligatureTable;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &(optdata->ligActionTable_length);
+ l[4] = &(optdata->componentTable_length);
+ l[5] = &(optdata->ligatureTable_length);
+
+ gxv_set_length_by_ulong_offset( o, l, buff, 6, table_size, valid );
+
+ GXV_TRACE(( "classTable: offset=0x%08x length=0x%08x\n",
+ classTable, *classTable_length_p ));
+ GXV_TRACE(( "stateArray: offset=0x%08x length=0x%08x\n",
+ stateArray, *stateArray_length_p ));
+ GXV_TRACE(( "entryTable: offset=0x%08x length=0x%08x\n",
+ entryTable, *entryTable_length_p ));
+ GXV_TRACE(( "ligActionTable: offset=0x%08x length=0x%08x\n",
+ optdata->ligActionTable,
+ optdata->ligActionTable_length ));
+ GXV_TRACE(( "componentTable: offset=0x%08x length=0x%08x\n",
+ optdata->componentTable,
+ optdata->componentTable_length ));
+ GXV_TRACE(( "ligatureTable: offset=0x%08x length=0x%08x\n",
+ optdata->ligatureTable,
+ optdata->ligatureTable_length ));
+
+ GXV_EXIT;
+ }
+
+
+#define GXV_MORX_LIGACTION_ENTRY_SIZE 4
+
+
+ static void
+ gxv_morx_subtable_type2_ligActionIndex_validate(
+ FT_Bytes table,
+ FT_UShort ligActionIndex,
+ GXV_Validator valid )
+ {
+ /* access ligActionTable */
+ GXV_morx_subtable_type2_StateOptRecData optdata =
+ (GXV_morx_subtable_type2_StateOptRecData)valid->xstatetable.optdata;
+
+ FT_Bytes lat_base = table + optdata->ligActionTable;
+ FT_Bytes p = lat_base +
+ ligActionIndex * GXV_MORX_LIGACTION_ENTRY_SIZE;
+ FT_Bytes lat_limit = lat_base + optdata->ligActionTable;
+
+
+ if ( p < lat_base )
+ {
+ GXV_TRACE(( "p < lat_base (%d byte rewind)\n", lat_base - p ));
+ FT_INVALID_OFFSET;
+ }
+ else if ( lat_limit < p )
+ {
+ GXV_TRACE(( "lat_limit < p (%d byte overrun)\n", p - lat_limit ));
+ FT_INVALID_OFFSET;
+ }
+
+ {
+ /* validate entry in ligActionTable */
+ FT_ULong lig_action;
+ FT_UShort last;
+ FT_UShort store;
+ FT_ULong offset;
+
+
+ lig_action = FT_NEXT_ULONG( p );
+ last = (FT_UShort)( ( lig_action >> 31 ) & 1 );
+ store = (FT_UShort)( ( lig_action >> 30 ) & 1 );
+
+ offset = lig_action & 0x3FFFFFFFUL;
+ }
+ }
+
+
+ static void
+ gxv_morx_subtable_type2_entry_validate(
+ FT_UShort state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_UShort setComponent;
+ FT_UShort dontAdvance;
+ FT_UShort performAction;
+ FT_UShort reserved;
+ FT_UShort ligActionIndex;
+
+ FT_UNUSED( state );
+ FT_UNUSED( limit );
+
+
+ setComponent = (FT_UShort)( ( flags >> 15 ) & 1 );
+ dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 );
+ performAction = (FT_UShort)( ( flags >> 13 ) & 1 );
+
+ reserved = (FT_UShort)( flags & 0x1FFF );
+ ligActionIndex = glyphOffset.u;
+
+ if ( reserved > 0 )
+ GXV_TRACE(( " reserved 14bit is non-zero\n" ));
+
+ if ( 0 < ligActionIndex )
+ gxv_morx_subtable_type2_ligActionIndex_validate(
+ table, ligActionIndex, valid );
+ }
+
+
+ static void
+ gxv_morx_subtable_type2_ligatureTable_validate( FT_Bytes table,
+ GXV_Validator valid )
+ {
+ GXV_morx_subtable_type2_StateOptRecData optdata =
+ (GXV_morx_subtable_type2_StateOptRecData)valid->xstatetable.optdata;
+
+ FT_Bytes p = table + optdata->ligatureTable;
+ FT_Bytes limit = table + optdata->ligatureTable
+ + optdata->ligatureTable_length;
+
+
+ GXV_NAME_ENTER( "morx chain subtable type2 - substitutionTable" );
+
+ if ( 0 != optdata->ligatureTable )
+ {
+ /* Apple does not give specification of ligatureTable format */
+ while ( p < limit )
+ {
+ FT_UShort lig_gid;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ lig_gid = FT_NEXT_USHORT( p );
+ }
+ }
+
+ GXV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_morx_subtable_type2_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type2_StateOptRec lig_rec;
+
+
+ GXV_NAME_ENTER( "morx chain subtable type2 (Ligature Substitution)" );
+
+ GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE2_HEADER_SIZE );
+
+ valid->xstatetable.optdata =
+ &lig_rec;
+ valid->xstatetable.optdata_load_func =
+ gxv_morx_subtable_type2_opttable_load;
+ valid->xstatetable.subtable_setup_func =
+ gxv_morx_subtable_type2_subtable_setup;
+ valid->xstatetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_USHORT;
+ valid->xstatetable.entry_validate_func =
+ gxv_morx_subtable_type2_entry_validate;
+
+ gxv_XStateTable_validate( p, limit, valid );
+
+ p += valid->subtable_length;
+ gxv_morx_subtable_type2_ligatureTable_validate( table, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx4.c b/src/gxvalid/gxvmorx4.c
new file mode 100644
index 0000000..c0d2f78
--- /dev/null
+++ b/src/gxvalid/gxvmorx4.c
@@ -0,0 +1,55 @@
+/***************************************************************************/
+/* */
+/* gxvmorx4.c */
+/* */
+/* TrueTypeGX/AAT morx table validation */
+/* body for "morx" type4 (Non-Contextual Glyph Substitution) subtable. */
+/* */
+/* Copyright 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ FT_LOCAL_DEF( void )
+ gxv_morx_subtable_type4_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ GXV_NAME_ENTER( "morx chain subtable type4 "
+ "(Non-Contextual Glyph Substitution)" );
+
+ gxv_mort_subtable_type4_validate( table, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvmorx5.c b/src/gxvalid/gxvmorx5.c
new file mode 100644
index 0000000..d911561
--- /dev/null
+++ b/src/gxvalid/gxvmorx5.c
@@ -0,0 +1,217 @@
+/***************************************************************************/
+/* */
+/* gxvmorx5.c */
+/* */
+/* TrueTypeGX/AAT morx table validation */
+/* body for type5 (Contextual Glyph Insertion) subtable. */
+/* */
+/* Copyright 2005, 2007 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvmorx.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvmorx
+
+
+ /*
+ * `morx' subtable type5 (Contextual Glyph Insertion)
+ * has format of a StateTable with insertion-glyph-list
+ * without name. However, the 32bit offset from the head
+ * of subtable to the i-g-l is given after `entryTable',
+ * without variable name specification (the existence of
+ * this offset to the table is different from mort type5).
+ */
+
+
+ typedef struct GXV_morx_subtable_type5_StateOptRec_
+ {
+ FT_ULong insertionGlyphList;
+ FT_ULong insertionGlyphList_length;
+
+ } GXV_morx_subtable_type5_StateOptRec,
+ *GXV_morx_subtable_type5_StateOptRecData;
+
+
+#define GXV_MORX_SUBTABLE_TYPE5_HEADER_SIZE \
+ ( GXV_STATETABLE_HEADER_SIZE + 4 )
+
+
+ static void
+ gxv_morx_subtable_type5_insertionGlyphList_load( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type5_StateOptRecData optdata =
+ (GXV_morx_subtable_type5_StateOptRecData)valid->xstatetable.optdata;
+
+
+ GXV_LIMIT_CHECK( 4 );
+ optdata->insertionGlyphList = FT_NEXT_ULONG( p );
+ }
+
+
+ static void
+ gxv_morx_subtable_type5_subtable_setup( FT_ULong table_size,
+ FT_ULong classTable,
+ FT_ULong stateArray,
+ FT_ULong entryTable,
+ FT_ULong* classTable_length_p,
+ FT_ULong* stateArray_length_p,
+ FT_ULong* entryTable_length_p,
+ GXV_Validator valid )
+ {
+ FT_ULong o[4];
+ FT_ULong* l[4];
+ FT_ULong buff[5];
+
+ GXV_morx_subtable_type5_StateOptRecData optdata =
+ (GXV_morx_subtable_type5_StateOptRecData)valid->xstatetable.optdata;
+
+
+ o[0] = classTable;
+ o[1] = stateArray;
+ o[2] = entryTable;
+ o[3] = optdata->insertionGlyphList;
+ l[0] = classTable_length_p;
+ l[1] = stateArray_length_p;
+ l[2] = entryTable_length_p;
+ l[3] = &(optdata->insertionGlyphList_length);
+
+ gxv_set_length_by_ulong_offset( o, l, buff, 4, table_size, valid );
+ }
+
+
+ static void
+ gxv_morx_subtable_type5_InsertList_validate( FT_UShort table_index,
+ FT_UShort count,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table + table_index * 2;
+
+
+ while ( p < table + count * 2 + table_index * 2 )
+ {
+ FT_UShort insert_glyphID;
+
+
+ GXV_LIMIT_CHECK( 2 );
+ insert_glyphID = FT_NEXT_USHORT( p );
+ GXV_TRACE(( " 0x%04x", insert_glyphID ));
+ }
+
+ GXV_TRACE(( "\n" ));
+ }
+
+
+ static void
+ gxv_morx_subtable_type5_entry_validate(
+ FT_UShort state,
+ FT_UShort flags,
+ GXV_StateTable_GlyphOffsetDesc glyphOffset,
+ FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bool setMark;
+ FT_Bool dontAdvance;
+ FT_Bool currentIsKashidaLike;
+ FT_Bool markedIsKashidaLike;
+ FT_Bool currentInsertBefore;
+ FT_Bool markedInsertBefore;
+ FT_Byte currentInsertCount;
+ FT_Byte markedInsertCount;
+ FT_Byte currentInsertList;
+ FT_UShort markedInsertList;
+
+ FT_UNUSED( state );
+
+
+ setMark = FT_BOOL( ( flags >> 15 ) & 1 );
+ dontAdvance = FT_BOOL( ( flags >> 14 ) & 1 );
+ currentIsKashidaLike = FT_BOOL( ( flags >> 13 ) & 1 );
+ markedIsKashidaLike = FT_BOOL( ( flags >> 12 ) & 1 );
+ currentInsertBefore = FT_BOOL( ( flags >> 11 ) & 1 );
+ markedInsertBefore = FT_BOOL( ( flags >> 10 ) & 1 );
+
+ currentInsertCount = (FT_Byte)( ( flags >> 5 ) & 0x1F );
+ markedInsertCount = (FT_Byte)( flags & 0x001F );
+
+ currentInsertList = (FT_Byte) ( glyphOffset.ul >> 16 );
+ markedInsertList = (FT_UShort)( glyphOffset.ul );
+
+ if ( currentInsertList && 0 != currentInsertCount )
+ gxv_morx_subtable_type5_InsertList_validate( currentInsertList,
+ currentInsertCount,
+ table, limit,
+ valid );
+
+ if ( markedInsertList && 0 != markedInsertCount )
+ gxv_morx_subtable_type5_InsertList_validate( markedInsertList,
+ markedInsertCount,
+ table, limit,
+ valid );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ gxv_morx_subtable_type5_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ GXV_morx_subtable_type5_StateOptRec et_rec;
+ GXV_morx_subtable_type5_StateOptRecData et = &et_rec;
+
+
+ GXV_NAME_ENTER( "morx chain subtable type5 (Glyph Insertion)" );
+
+ GXV_LIMIT_CHECK( GXV_MORX_SUBTABLE_TYPE5_HEADER_SIZE );
+
+ valid->xstatetable.optdata =
+ et;
+ valid->xstatetable.optdata_load_func =
+ gxv_morx_subtable_type5_insertionGlyphList_load;
+ valid->xstatetable.subtable_setup_func =
+ gxv_morx_subtable_type5_subtable_setup;
+ valid->xstatetable.entry_glyphoffset_fmt =
+ GXV_GLYPHOFFSET_ULONG;
+ valid->xstatetable.entry_validate_func =
+ gxv_morx_subtable_type5_entry_validate;
+
+ gxv_XStateTable_validate( p, limit, valid );
+
+ GXV_EXIT;
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvopbd.c b/src/gxvalid/gxvopbd.c
new file mode 100644
index 0000000..8d6fe66
--- /dev/null
+++ b/src/gxvalid/gxvopbd.c
@@ -0,0 +1,217 @@
+/***************************************************************************/
+/* */
+/* gxvopbd.c */
+/* */
+/* TrueTypeGX/AAT opbd table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvopbd
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct GXV_opbd_DataRec_
+ {
+ FT_UShort format;
+ FT_UShort valueOffset_min;
+
+ } GXV_opbd_DataRec, *GXV_opbd_Data;
+
+
+#define GXV_OPBD_DATA( FIELD ) GXV_TABLE_DATA( opbd, FIELD )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_opbd_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ /* offset in LookupTable is measured from the head of opbd table */
+ FT_Bytes p = valid->root->base + value.u;
+ FT_Bytes limit = valid->root->limit;
+ FT_Short delta_value;
+ int i;
+
+
+ if ( value.u < GXV_OPBD_DATA( valueOffset_min ) )
+ GXV_OPBD_DATA( valueOffset_min ) = value.u;
+
+ for ( i = 0; i < 4; i++ )
+ {
+ GXV_LIMIT_CHECK( 2 );
+ delta_value = FT_NEXT_SHORT( p );
+
+ if ( GXV_OPBD_DATA( format ) ) /* format 1, value is ctrl pt. */
+ {
+ if ( delta_value == -1 )
+ continue;
+
+ gxv_ctlPoint_validate( glyph, delta_value, valid );
+ }
+ else /* format 0, value is distance */
+ continue;
+ }
+ }
+
+
+ /*
+ opbd ---------------------+
+ |
+ +===============+ |
+ | lookup header | |
+ +===============+ |
+ | BinSrchHeader | |
+ +===============+ |
+ | lastGlyph[0] | |
+ +---------------+ |
+ | firstGlyph[0] | | head of opbd sfnt table
+ +---------------+ | +
+ | offset[0] | -> | offset [byte]
+ +===============+ | +
+ | lastGlyph[1] | | (glyphID - firstGlyph) * 4 * sizeof(FT_Short) [byte]
+ +---------------+ |
+ | firstGlyph[1] | |
+ +---------------+ |
+ | offset[1] | |
+ +===============+ |
+ |
+ .... |
+ |
+ 48bit value array |
+ +===============+ |
+ | value | <-------+
+ | |
+ | |
+ | |
+ +---------------+
+ .... */
+
+ static GXV_LookupValueDesc
+ gxv_opbd_LookupFmt4_transit( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ GXV_LookupValueDesc value;
+
+ FT_UNUSED( lookuptbl_limit );
+ FT_UNUSED( valid );
+
+ /* XXX: check range? */
+ value.u = (FT_UShort)( base_value.u +
+ relative_gindex * 4 * sizeof ( FT_Short ) );
+
+ return value;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** opbd TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_opbd_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+ GXV_opbd_DataRec opbdrec;
+ GXV_opbd_Data opbd = &opbdrec;
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+
+ FT_ULong version;
+
+
+ valid->root = ftvalid;
+ valid->table_data = opbd;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `opbd' table\n" ));
+ GXV_INIT;
+ GXV_OPBD_DATA( valueOffset_min ) = 0xFFFFU;
+
+
+ GXV_LIMIT_CHECK( 4 + 2 );
+ version = FT_NEXT_ULONG( p );
+ GXV_OPBD_DATA( format ) = FT_NEXT_USHORT( p );
+
+
+ /* only 0x00010000 is defined (1996) */
+ GXV_TRACE(( "(version=0x%08x)\n", version ));
+ if ( 0x00010000UL != version )
+ FT_INVALID_FORMAT;
+
+ /* only values 0 and 1 are defined (1996) */
+ GXV_TRACE(( "(format=0x%04x)\n", GXV_OPBD_DATA( format ) ));
+ if ( 0x0001 < GXV_OPBD_DATA( format ) )
+ FT_INVALID_FORMAT;
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_opbd_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_opbd_LookupFmt4_transit;
+
+ gxv_LookupTable_validate( p, limit, valid );
+ p += valid->subtable_length;
+
+ if ( p > table + GXV_OPBD_DATA( valueOffset_min ) )
+ {
+ GXV_TRACE((
+ "found overlap between LookupTable and opbd_value array\n" ));
+ FT_INVALID_OFFSET;
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvprop.c b/src/gxvalid/gxvprop.c
new file mode 100644
index 0000000..010eeda
--- /dev/null
+++ b/src/gxvalid/gxvprop.c
@@ -0,0 +1,301 @@
+/***************************************************************************/
+/* */
+/* gxvprop.c */
+/* */
+/* TrueTypeGX/AAT prop table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvprop
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define GXV_PROP_HEADER_SIZE ( 4 + 2 + 2 )
+#define GXV_PROP_SIZE_MIN GXV_PROP_HEADER_SIZE
+
+ typedef struct GXV_prop_DataRec_
+ {
+ FT_Fixed version;
+
+ } GXV_prop_DataRec, *GXV_prop_Data;
+
+#define GXV_PROP_DATA( field ) GXV_TABLE_DATA( prop, field )
+
+#define GXV_PROP_FLOATER 0x8000U
+#define GXV_PROP_USE_COMPLEMENTARY_BRACKET 0x1000U
+#define GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET 0x0F00U
+#define GXV_PROP_ATTACHING_TO_RIGHT 0x0080U
+#define GXV_PROP_RESERVED 0x0060U
+#define GXV_PROP_DIRECTIONALITY_CLASS 0x001FU
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_prop_zero_advance_validate( FT_UShort gid,
+ GXV_Validator valid )
+ {
+ FT_Face face;
+ FT_Error error;
+ FT_GlyphSlot glyph;
+
+
+ GXV_NAME_ENTER( "zero advance" );
+
+ face = valid->face;
+
+ error = FT_Load_Glyph( face,
+ gid,
+ FT_LOAD_IGNORE_TRANSFORM );
+ if ( error )
+ FT_INVALID_GLYPH_ID;
+
+ glyph = face->glyph;
+
+ if ( glyph->advance.x != (FT_Pos)0 ||
+ glyph->advance.y != (FT_Pos)0 )
+ FT_INVALID_DATA;
+
+ GXV_EXIT;
+ }
+
+
+ /* Pass 0 as GLYPH to check the default property */
+ static void
+ gxv_prop_property_validate( FT_UShort property,
+ FT_UShort glyph,
+ GXV_Validator valid )
+ {
+ if ( glyph != 0 && ( property & GXV_PROP_FLOATER ) )
+ gxv_prop_zero_advance_validate( glyph, valid );
+
+ if ( property & GXV_PROP_USE_COMPLEMENTARY_BRACKET )
+ {
+ FT_UShort offset;
+ char complement;
+
+
+ offset = (FT_UShort)( property & GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET );
+ if ( offset == 0 )
+ FT_INVALID_DATA;
+
+ complement = (char)( offset >> 8 );
+ if ( complement & 0x08 )
+ {
+ /* Top bit is set: negative */
+
+ /* Calculate the absolute offset */
+ complement = (char)( ( complement & 0x07 ) + 1 );
+
+ /* The gid for complement must be greater than 0 */
+ if ( glyph <= complement )
+ FT_INVALID_DATA;
+ }
+ else
+ {
+ /* The gid for complement must be the face. */
+ gxv_glyphid_validate( (FT_UShort)( glyph + complement ), valid );
+ }
+ }
+ else
+ {
+ if ( property & GXV_PROP_COMPLEMENTARY_BRACKET_OFFSET )
+ GXV_TRACE(( "glyph %d cannot have complementary bracketing\n",
+ glyph ));
+ }
+
+ /* this is introduced in version 2.0 */
+ if ( property & GXV_PROP_ATTACHING_TO_RIGHT )
+ {
+ if ( GXV_PROP_DATA( version ) == 0x00010000UL )
+ FT_INVALID_DATA;
+ }
+
+ if ( property & GXV_PROP_RESERVED )
+ FT_INVALID_DATA;
+
+ if ( ( property & GXV_PROP_DIRECTIONALITY_CLASS ) > 11 )
+ {
+ /* TODO: Too restricted. Use the validation level. */
+ if ( GXV_PROP_DATA( version ) == 0x00010000UL ||
+ GXV_PROP_DATA( version ) == 0x00020000UL )
+ FT_INVALID_DATA;
+ }
+ }
+
+
+ static void
+ gxv_prop_LookupValue_validate( FT_UShort glyph,
+ GXV_LookupValueDesc value,
+ GXV_Validator valid )
+ {
+ gxv_prop_property_validate( value.u, glyph, valid );
+ }
+
+
+ /*
+ +===============+ --------+
+ | lookup header | |
+ +===============+ |
+ | BinSrchHeader | |
+ +===============+ |
+ | lastGlyph[0] | |
+ +---------------+ |
+ | firstGlyph[0] | | head of lookup table
+ +---------------+ | +
+ | offset[0] | -> | offset [byte]
+ +===============+ | +
+ | lastGlyph[1] | | (glyphID - firstGlyph) * 2 [byte]
+ +---------------+ |
+ | firstGlyph[1] | |
+ +---------------+ |
+ | offset[1] | |
+ +===============+ |
+ |
+ ... |
+ |
+ 16bit value array |
+ +===============+ |
+ | value | <-------+
+ ...
+ */
+
+ static GXV_LookupValueDesc
+ gxv_prop_LookupFmt4_transit( FT_UShort relative_gindex,
+ GXV_LookupValueDesc base_value,
+ FT_Bytes lookuptbl_limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p;
+ FT_Bytes limit;
+ FT_UShort offset;
+ GXV_LookupValueDesc value;
+
+ /* XXX: check range? */
+ offset = (FT_UShort)( base_value.u +
+ relative_gindex * sizeof( FT_UShort ) );
+ p = valid->lookuptbl_head + offset;
+ limit = lookuptbl_limit;
+
+ GXV_LIMIT_CHECK ( 2 );
+ value.u = FT_NEXT_USHORT( p );
+
+ return value;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** prop TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_prop_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+
+ GXV_prop_DataRec proprec;
+ GXV_prop_Data prop = &proprec;
+
+ FT_Fixed version;
+ FT_UShort format;
+ FT_UShort defaultProp;
+
+
+ valid->root = ftvalid;
+ valid->table_data = prop;
+ valid->face = face;
+
+ FT_TRACE3(( "validating `prop' table\n" ));
+ GXV_INIT;
+
+ GXV_LIMIT_CHECK( 4 + 2 + 2 );
+ version = FT_NEXT_ULONG( p );
+ format = FT_NEXT_USHORT( p );
+ defaultProp = FT_NEXT_USHORT( p );
+
+ /* only versions 1.0, 2.0, 3.0 are defined (1996) */
+ if ( version != 0x00010000UL &&
+ version != 0x00020000UL &&
+ version != 0x00030000UL )
+ FT_INVALID_FORMAT;
+
+
+ /* only formats 0x0000, 0x0001 are defined (1996) */
+ if ( format > 1 )
+ FT_INVALID_FORMAT;
+
+ gxv_prop_property_validate( defaultProp, 0, valid );
+
+ if ( format == 0 )
+ {
+ FT_TRACE3(( "(format 0, no per-glyph properties, "
+ "remaining %d bytes are skipped)", limit - p ));
+ goto Exit;
+ }
+
+ /* format == 1 */
+ GXV_PROP_DATA( version ) = version;
+
+ valid->lookupval_sign = GXV_LOOKUPVALUE_UNSIGNED;
+ valid->lookupval_func = gxv_prop_LookupValue_validate;
+ valid->lookupfmt4_trans = gxv_prop_LookupFmt4_transit;
+
+ gxv_LookupTable_validate( p, limit, valid );
+
+ Exit:
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/gxvtrak.c b/src/gxvalid/gxvtrak.c
new file mode 100644
index 0000000..432ee4e
--- /dev/null
+++ b/src/gxvalid/gxvtrak.c
@@ -0,0 +1,277 @@
+/***************************************************************************/
+/* */
+/* gxvtrak.c */
+/* */
+/* TrueTypeGX/AAT trak table validation (body). */
+/* */
+/* Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K., */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+/***************************************************************************/
+/* */
+/* gxvalid is derived from both gxlayout module and otvalid module. */
+/* Development of gxlayout is supported by the Information-technology */
+/* Promotion Agency(IPA), Japan. */
+/* */
+/***************************************************************************/
+
+
+#include "gxvalid.h"
+#include "gxvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_gxvtrak
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Data and Types *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*
+ * referred track table format specification:
+ * http://developer.apple.com/fonts/TTRefMan/RM06/Chap6trak.html
+ * last update was 1996.
+ * ----------------------------------------------
+ * [MINIMUM HEADER]: GXV_TRAK_SIZE_MIN
+ * version (fixed: 32bit) = 0x00010000
+ * format (uint16: 16bit) = 0 is only defined (1996)
+ * horizOffset (uint16: 16bit)
+ * vertOffset (uint16: 16bit)
+ * reserved (uint16: 16bit) = 0
+ * ----------------------------------------------
+ * [VARIABLE BODY]:
+ * horizData
+ * header ( 2 + 2 + 4
+ * trackTable + nTracks * ( 4 + 2 + 2 )
+ * sizeTable + nSizes * 4 )
+ * ----------------------------------------------
+ * vertData
+ * header ( 2 + 2 + 4
+ * trackTable + nTracks * ( 4 + 2 + 2 )
+ * sizeTable + nSizes * 4 )
+ * ----------------------------------------------
+ */
+ typedef struct GXV_trak_DataRec_
+ {
+ FT_UShort trackValueOffset_min;
+ FT_UShort trackValueOffset_max;
+
+ } GXV_trak_DataRec, *GXV_trak_Data;
+
+
+#define GXV_TRAK_DATA( FIELD ) GXV_TABLE_DATA( trak, FIELD )
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ gxv_trak_trackTable_validate( FT_Bytes table,
+ FT_Bytes limit,
+ FT_UShort nTracks,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+
+ FT_Fixed track;
+ FT_UShort nameIndex;
+ FT_UShort offset;
+ FT_UShort i;
+
+
+ GXV_NAME_ENTER( "trackTable" );
+
+ GXV_TRAK_DATA( trackValueOffset_min ) = 0xFFFFU;
+ GXV_TRAK_DATA( trackValueOffset_max ) = 0x0000;
+
+ for ( i = 0; i < nTracks; i ++ )
+ {
+ GXV_LIMIT_CHECK( 4 + 2 + 2 );
+ track = FT_NEXT_LONG( p );
+ nameIndex = FT_NEXT_USHORT( p );
+ offset = FT_NEXT_USHORT( p );
+
+ if ( offset < GXV_TRAK_DATA( trackValueOffset_min ) )
+ GXV_TRAK_DATA( trackValueOffset_min ) = offset;
+ if ( offset > GXV_TRAK_DATA( trackValueOffset_max ) )
+ GXV_TRAK_DATA( trackValueOffset_max ) = offset;
+
+ gxv_sfntName_validate( nameIndex, 256, 32767, valid );
+ }
+
+ valid->subtable_length = p - table;
+ GXV_EXIT;
+ }
+
+
+ static void
+ gxv_trak_trackData_validate( FT_Bytes table,
+ FT_Bytes limit,
+ GXV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UShort nTracks;
+ FT_UShort nSizes;
+ FT_ULong sizeTableOffset;
+
+ GXV_ODTECT( 4, odtect );
+
+
+ GXV_ODTECT_INIT( odtect );
+ GXV_NAME_ENTER( "trackData" );
+
+ /* read the header of trackData */
+ GXV_LIMIT_CHECK( 2 + 2 + 4 );
+ nTracks = FT_NEXT_USHORT( p );
+ nSizes = FT_NEXT_USHORT( p );
+ sizeTableOffset = FT_NEXT_ULONG( p );
+
+ gxv_odtect_add_range( table, p - table, "trackData header", odtect );
+
+ /* validate trackTable */
+ gxv_trak_trackTable_validate( p, limit, nTracks, valid );
+ gxv_odtect_add_range( p, valid->subtable_length,
+ "trackTable", odtect );
+
+ /* sizeTable is array of FT_Fixed, don't check contents */
+ p = valid->root->base + sizeTableOffset;
+ GXV_LIMIT_CHECK( nSizes * 4 );
+ gxv_odtect_add_range( p, nSizes * 4, "sizeTable", odtect );
+
+ /* validate trackValueOffet */
+ p = valid->root->base + GXV_TRAK_DATA( trackValueOffset_min );
+ if ( limit - p < nTracks * nSizes * 2 )
+ GXV_TRACE(( "too short trackValue array\n" ));
+
+ p = valid->root->base + GXV_TRAK_DATA( trackValueOffset_max );
+ GXV_LIMIT_CHECK( nSizes * 2 );
+
+ gxv_odtect_add_range( valid->root->base
+ + GXV_TRAK_DATA( trackValueOffset_min ),
+ GXV_TRAK_DATA( trackValueOffset_max )
+ - GXV_TRAK_DATA( trackValueOffset_min )
+ + nSizes * 2,
+ "trackValue array", odtect );
+
+ gxv_odtect_validate( odtect, valid );
+
+ GXV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** trak TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ gxv_trak_validate( FT_Bytes table,
+ FT_Face face,
+ FT_Validator ftvalid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes limit = 0;
+ FT_UInt table_size;
+
+ GXV_ValidatorRec validrec;
+ GXV_Validator valid = &validrec;
+ GXV_trak_DataRec trakrec;
+ GXV_trak_Data trak = &trakrec;
+
+ FT_ULong version;
+ FT_UShort format;
+ FT_UShort horizOffset;
+ FT_UShort vertOffset;
+ FT_UShort reserved;
+
+
+ GXV_ODTECT( 3, odtect );
+
+ GXV_ODTECT_INIT( odtect );
+ valid->root = ftvalid;
+ valid->table_data = trak;
+ valid->face = face;
+
+ limit = valid->root->limit;
+ table_size = limit - table;
+
+ FT_TRACE3(( "validating `trak' table\n" ));
+ GXV_INIT;
+
+ GXV_LIMIT_CHECK( 4 + 2 + 2 + 2 + 2 );
+ version = FT_NEXT_ULONG( p );
+ format = FT_NEXT_USHORT( p );
+ horizOffset = FT_NEXT_USHORT( p );
+ vertOffset = FT_NEXT_USHORT( p );
+ reserved = FT_NEXT_USHORT( p );
+
+ GXV_TRACE(( " (version = 0x%08x)\n", version ));
+ GXV_TRACE(( " (format = 0x%04x)\n", format ));
+ GXV_TRACE(( " (horizOffset = 0x%04x)\n", horizOffset ));
+ GXV_TRACE(( " (vertOffset = 0x%04x)\n", vertOffset ));
+ GXV_TRACE(( " (reserved = 0x%04x)\n", reserved ));
+
+ /* Version 1.0 (always:1996) */
+ if ( version != 0x00010000UL )
+ FT_INVALID_FORMAT;
+
+ /* format 0 (always:1996) */
+ if ( format != 0x0000 )
+ FT_INVALID_FORMAT;
+
+ GXV_32BIT_ALIGNMENT_VALIDATE( horizOffset );
+ GXV_32BIT_ALIGNMENT_VALIDATE( vertOffset );
+
+ /* Reserved Fixed Value (always) */
+ if ( reserved != 0x0000 )
+ FT_INVALID_DATA;
+
+ /* validate trackData */
+ if ( 0 < horizOffset )
+ {
+ gxv_trak_trackData_validate( table + horizOffset, limit, valid );
+ gxv_odtect_add_range( table + horizOffset, valid->subtable_length,
+ "horizJustData", odtect );
+ }
+
+ if ( 0 < vertOffset )
+ {
+ gxv_trak_trackData_validate( table + vertOffset, limit, valid );
+ gxv_odtect_add_range( table + vertOffset, valid->subtable_length,
+ "vertJustData", odtect );
+ }
+
+ gxv_odtect_validate( odtect, valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/gxvalid/module.mk b/src/gxvalid/module.mk
new file mode 100644
index 0000000..44ef94a
--- /dev/null
+++ b/src/gxvalid/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 gxvalid module definition
+#
+
+# Copyright 2004, 2005, 2006
+# by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += GXVALID_MODULE
+
+define GXVALID_MODULE
+$(OPEN_DRIVER)gxv_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)gxvalid $(ECHO_DRIVER_DESC)TrueTypeGX/AAT validation module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/gxvalid/rules.mk b/src/gxvalid/rules.mk
new file mode 100644
index 0000000..57bc082
--- /dev/null
+++ b/src/gxvalid/rules.mk
@@ -0,0 +1,94 @@
+#
+# FreeType 2 TrueTypeGX/AAT validation driver configuration rules
+#
+
+
+# Copyright 2004, 2005 by suzuki toshiya, Masatake YAMATO, Red Hat K.K.,
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# GXV driver directory
+#
+GXV_DIR := $(SRC_DIR)/gxvalid
+
+
+# compilation flags for the driver
+#
+GXV_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(GXV_DIR))
+
+
+# GXV driver sources (i.e., C files)
+#
+GXV_DRV_SRC := $(GXV_DIR)/gxvcommn.c \
+ $(GXV_DIR)/gxvfeat.c \
+ $(GXV_DIR)/gxvbsln.c \
+ $(GXV_DIR)/gxvtrak.c \
+ $(GXV_DIR)/gxvopbd.c \
+ $(GXV_DIR)/gxvprop.c \
+ $(GXV_DIR)/gxvjust.c \
+ $(GXV_DIR)/gxvmort.c \
+ $(GXV_DIR)/gxvmort0.c \
+ $(GXV_DIR)/gxvmort1.c \
+ $(GXV_DIR)/gxvmort2.c \
+ $(GXV_DIR)/gxvmort4.c \
+ $(GXV_DIR)/gxvmort5.c \
+ $(GXV_DIR)/gxvmorx.c \
+ $(GXV_DIR)/gxvmorx0.c \
+ $(GXV_DIR)/gxvmorx1.c \
+ $(GXV_DIR)/gxvmorx2.c \
+ $(GXV_DIR)/gxvmorx4.c \
+ $(GXV_DIR)/gxvmorx5.c \
+ $(GXV_DIR)/gxvlcar.c \
+ $(GXV_DIR)/gxvkern.c \
+ $(GXV_DIR)/gxvmod.c
+
+# GXV driver headers
+#
+GXV_DRV_H := $(GXV_DIR)/gxvalid.h \
+ $(GXV_DIR)/gxverror.h \
+ $(GXV_DIR)/gxvcommn.h \
+ $(GXV_DIR)/gxvfeat.h \
+ $(GXV_DIR)/gxvmod.h \
+ $(GXV_DIR)/gxvmort.h \
+ $(GXV_DIR)/gxvmorx.h
+
+
+# GXV driver object(s)
+#
+# GXV_DRV_OBJ_M is used during `multi' builds.
+# GXV_DRV_OBJ_S is used during `single' builds.
+#
+GXV_DRV_OBJ_M := $(GXV_DRV_SRC:$(GXV_DIR)/%.c=$(OBJ_DIR)/%.$O)
+GXV_DRV_OBJ_S := $(OBJ_DIR)/gxvalid.$O
+
+# GXV driver source file for single build
+#
+GXV_DRV_SRC_S := $(GXV_DIR)/gxvalid.c
+
+
+# GXV driver - single object
+#
+$(GXV_DRV_OBJ_S): $(GXV_DRV_SRC_S) $(GXV_DRV_SRC) \
+ $(FREETYPE_H) $(GXV_DRV_H)
+ $(GXV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(GXV_DRV_SRC_S))
+
+
+# GXV driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(GXV_DIR)/%.c $(FREETYPE_H) $(GXV_DRV_H)
+ $(GXV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(GXV_DRV_OBJ_S)
+DRV_OBJS_M += $(GXV_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/gzip/Jamfile b/src/gzip/Jamfile
new file mode 100644
index 0000000..a7aafa0
--- /dev/null
+++ b/src/gzip/Jamfile
@@ -0,0 +1,16 @@
+# FreeType 2 src/gzip Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) gzip ;
+
+Library $(FT2_LIB) : ftgzip.c ;
+
+# end of src/pcf Jamfile
diff --git a/src/gzip/adler32.c b/src/gzip/adler32.c
new file mode 100644
index 0000000..36f6a43
--- /dev/null
+++ b/src/gzip/adler32.c
@@ -0,0 +1,48 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: adler32.c,v 1.5 2007/06/01 06:56:17 wl Exp $ */
+
+#include "zlib.h"
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {s1 += buf[i]; s2 += s1;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+/* ========================================================================= */
+ZEXPORT(uLong) adler32( /* adler, buf, len) */
+ uLong adler,
+ const Bytef *buf,
+ uInt len )
+{
+ unsigned long s1 = adler & 0xffff;
+ unsigned long s2 = (adler >> 16) & 0xffff;
+ int k;
+
+ if (buf == Z_NULL) return 1L;
+
+ while (len > 0) {
+ k = len < NMAX ? len : NMAX;
+ len -= k;
+ while (k >= 16) {
+ DO16(buf);
+ buf += 16;
+ k -= 16;
+ }
+ if (k != 0) do {
+ s1 += *buf++;
+ s2 += s1;
+ } while (--k);
+ s1 %= BASE;
+ s2 %= BASE;
+ }
+ return (s2 << 16) | s1;
+}
diff --git a/src/gzip/ftgzip.c b/src/gzip/ftgzip.c
new file mode 100644
index 0000000..af2022d
--- /dev/null
+++ b/src/gzip/ftgzip.c
@@ -0,0 +1,682 @@
+/***************************************************************************/
+/* */
+/* ftgzip.c */
+/* */
+/* FreeType support for .gz compressed files. */
+/* */
+/* This optional component relies on zlib. It should mainly be used to */
+/* parse compressed PCF fonts, as found with many X11 server */
+/* distributions. */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_GZIP_H
+#include <string.h>
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX Gzip_Err_
+#define FT_ERR_BASE FT_Mod_Err_Gzip
+
+#include FT_ERRORS_H
+
+
+#ifdef FT_CONFIG_OPTION_USE_ZLIB
+
+#ifdef FT_CONFIG_OPTION_SYSTEM_ZLIB
+
+#include <zlib.h>
+
+#else /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
+
+ /* In this case, we include our own modified sources of the ZLib */
+ /* within the "ftgzip" component. The modifications were necessary */
+ /* to #include all files without conflicts, as well as preventing */
+ /* the definition of "extern" functions that may cause linking */
+ /* conflicts when a program is linked with both FreeType and the */
+ /* original ZLib. */
+
+#define NO_DUMMY_DECL
+#define MY_ZCALLOC
+
+#include "zlib.h"
+
+#undef SLOW
+#define SLOW 1 /* we can't use asm-optimized sources here! */
+
+ /* Urgh. `inflate_mask' must not be declared twice -- C++ doesn't like
+ this. We temporarily disable it and load all necessary header files. */
+#define NO_INFLATE_MASK
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+#undef NO_INFLATE_MASK
+
+ /* infutil.c must be included before infcodes.c */
+#include "zutil.c"
+#include "inftrees.c"
+#include "infutil.c"
+#include "infcodes.c"
+#include "infblock.c"
+#include "inflate.c"
+#include "adler32.c"
+
+#endif /* !FT_CONFIG_OPTION_SYSTEM_ZLIB */
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** Z L I B M E M O R Y M A N A G E M E N T *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+ /* it is better to use FreeType memory routines instead of raw
+ 'malloc/free' */
+
+ static voidpf
+ ft_gzip_alloc( FT_Memory memory,
+ uInt items,
+ uInt size )
+ {
+ FT_ULong sz = (FT_ULong)size * items;
+ FT_Error error;
+ FT_Pointer p;
+
+
+ (void)FT_ALLOC( p, sz );
+ return p;
+ }
+
+
+ static void
+ ft_gzip_free( FT_Memory memory,
+ voidpf address )
+ {
+ FT_MEM_FREE( address );
+ }
+
+
+#ifndef FT_CONFIG_OPTION_SYSTEM_ZLIB
+
+ local voidpf
+ zcalloc ( voidpf opaque,
+ unsigned items,
+ unsigned size )
+ {
+ return ft_gzip_alloc( (FT_Memory)opaque, items, size );
+ }
+
+ local void
+ zcfree( voidpf opaque,
+ voidpf ptr )
+ {
+ ft_gzip_free( (FT_Memory)opaque, ptr );
+ }
+
+#endif /* !SYSTEM_ZLIB */
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** Z L I B F I L E D E S C R I P T O R *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+#define FT_GZIP_BUFFER_SIZE 4096
+
+ typedef struct FT_GZipFileRec_
+ {
+ FT_Stream source; /* parent/source stream */
+ FT_Stream stream; /* embedding stream */
+ FT_Memory memory; /* memory allocator */
+ z_stream zstream; /* zlib input stream */
+
+ FT_ULong start; /* starting position, after .gz header */
+ FT_Byte input[FT_GZIP_BUFFER_SIZE]; /* input read buffer */
+
+ FT_Byte buffer[FT_GZIP_BUFFER_SIZE]; /* output buffer */
+ FT_ULong pos; /* position in output */
+ FT_Byte* cursor;
+ FT_Byte* limit;
+
+ } FT_GZipFileRec, *FT_GZipFile;
+
+
+ /* gzip flag byte */
+#define FT_GZIP_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define FT_GZIP_HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define FT_GZIP_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define FT_GZIP_ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define FT_GZIP_COMMENT 0x10 /* bit 4 set: file comment present */
+#define FT_GZIP_RESERVED 0xE0 /* bits 5..7: reserved */
+
+
+ /* check and skip .gz header - we don't support `transparent' compression */
+ static FT_Error
+ ft_gzip_check_header( FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Byte head[4];
+
+
+ if ( FT_STREAM_SEEK( 0 ) ||
+ FT_STREAM_READ( head, 4 ) )
+ goto Exit;
+
+ /* head[0] && head[1] are the magic numbers; */
+ /* head[2] is the method, and head[3] the flags */
+ if ( head[0] != 0x1f ||
+ head[1] != 0x8b ||
+ head[2] != Z_DEFLATED ||
+ (head[3] & FT_GZIP_RESERVED) )
+ {
+ error = Gzip_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* skip time, xflags and os code */
+ (void)FT_STREAM_SKIP( 6 );
+
+ /* skip the extra field */
+ if ( head[3] & FT_GZIP_EXTRA_FIELD )
+ {
+ FT_UInt len;
+
+
+ if ( FT_READ_USHORT_LE( len ) ||
+ FT_STREAM_SKIP( len ) )
+ goto Exit;
+ }
+
+ /* skip original file name */
+ if ( head[3] & FT_GZIP_ORIG_NAME )
+ for (;;)
+ {
+ FT_UInt c;
+
+
+ if ( FT_READ_BYTE( c ) )
+ goto Exit;
+
+ if ( c == 0 )
+ break;
+ }
+
+ /* skip .gz comment */
+ if ( head[3] & FT_GZIP_COMMENT )
+ for (;;)
+ {
+ FT_UInt c;
+
+
+ if ( FT_READ_BYTE( c ) )
+ goto Exit;
+
+ if ( c == 0 )
+ break;
+ }
+
+ /* skip CRC */
+ if ( head[3] & FT_GZIP_HEAD_CRC )
+ if ( FT_STREAM_SKIP( 2 ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ ft_gzip_file_init( FT_GZipFile zip,
+ FT_Stream stream,
+ FT_Stream source )
+ {
+ z_stream* zstream = &zip->zstream;
+ FT_Error error = Gzip_Err_Ok;
+
+
+ zip->stream = stream;
+ zip->source = source;
+ zip->memory = stream->memory;
+
+ zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+
+ /* check and skip .gz header */
+ {
+ stream = source;
+
+ error = ft_gzip_check_header( stream );
+ if ( error )
+ goto Exit;
+
+ zip->start = FT_STREAM_POS();
+ }
+
+ /* initialize zlib -- there is no zlib header in the compressed stream */
+ zstream->zalloc = (alloc_func)ft_gzip_alloc;
+ zstream->zfree = (free_func) ft_gzip_free;
+ zstream->opaque = stream->memory;
+
+ zstream->avail_in = 0;
+ zstream->next_in = zip->buffer;
+
+ if ( inflateInit2( zstream, -MAX_WBITS ) != Z_OK ||
+ zstream->next_in == NULL )
+ error = Gzip_Err_Invalid_File_Format;
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_gzip_file_done( FT_GZipFile zip )
+ {
+ z_stream* zstream = &zip->zstream;
+
+
+ inflateEnd( zstream );
+
+ /* clear the rest */
+ zstream->zalloc = NULL;
+ zstream->zfree = NULL;
+ zstream->opaque = NULL;
+ zstream->next_in = NULL;
+ zstream->next_out = NULL;
+ zstream->avail_in = 0;
+ zstream->avail_out = 0;
+
+ zip->memory = NULL;
+ zip->source = NULL;
+ zip->stream = NULL;
+ }
+
+
+ static FT_Error
+ ft_gzip_file_reset( FT_GZipFile zip )
+ {
+ FT_Stream stream = zip->source;
+ FT_Error error;
+
+
+ if ( !FT_STREAM_SEEK( zip->start ) )
+ {
+ z_stream* zstream = &zip->zstream;
+
+
+ inflateReset( zstream );
+
+ zstream->avail_in = 0;
+ zstream->next_in = zip->input;
+ zstream->avail_out = 0;
+ zstream->next_out = zip->buffer;
+
+ zip->limit = zip->buffer + FT_GZIP_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_gzip_file_fill_input( FT_GZipFile zip )
+ {
+ z_stream* zstream = &zip->zstream;
+ FT_Stream stream = zip->source;
+ FT_ULong size;
+
+
+ if ( stream->read )
+ {
+ size = stream->read( stream, stream->pos, zip->input,
+ FT_GZIP_BUFFER_SIZE );
+ if ( size == 0 )
+ return Gzip_Err_Invalid_Stream_Operation;
+ }
+ else
+ {
+ size = stream->size - stream->pos;
+ if ( size > FT_GZIP_BUFFER_SIZE )
+ size = FT_GZIP_BUFFER_SIZE;
+
+ if ( size == 0 )
+ return Gzip_Err_Invalid_Stream_Operation;
+
+ FT_MEM_COPY( zip->input, stream->base + stream->pos, size );
+ }
+ stream->pos += size;
+
+ zstream->next_in = zip->input;
+ zstream->avail_in = size;
+
+ return Gzip_Err_Ok;
+ }
+
+
+ static FT_Error
+ ft_gzip_file_fill_output( FT_GZipFile zip )
+ {
+ z_stream* zstream = &zip->zstream;
+ FT_Error error = 0;
+
+
+ zip->cursor = zip->buffer;
+ zstream->next_out = zip->cursor;
+ zstream->avail_out = FT_GZIP_BUFFER_SIZE;
+
+ while ( zstream->avail_out > 0 )
+ {
+ int err;
+
+
+ if ( zstream->avail_in == 0 )
+ {
+ error = ft_gzip_file_fill_input( zip );
+ if ( error )
+ break;
+ }
+
+ err = inflate( zstream, Z_NO_FLUSH );
+
+ if ( err == Z_STREAM_END )
+ {
+ zip->limit = zstream->next_out;
+ if ( zip->limit == zip->cursor )
+ error = Gzip_Err_Invalid_Stream_Operation;
+ break;
+ }
+ else if ( err != Z_OK )
+ {
+ error = Gzip_Err_Invalid_Stream_Operation;
+ break;
+ }
+ }
+
+ return error;
+ }
+
+
+ /* fill output buffer; `count' must be <= FT_GZIP_BUFFER_SIZE */
+ static FT_Error
+ ft_gzip_file_skip_output( FT_GZipFile zip,
+ FT_ULong count )
+ {
+ FT_Error error = Gzip_Err_Ok;
+ FT_ULong delta;
+
+
+ for (;;)
+ {
+ delta = (FT_ULong)( zip->limit - zip->cursor );
+ if ( delta >= count )
+ delta = count;
+
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ if ( count == 0 )
+ break;
+
+ error = ft_gzip_file_fill_output( zip );
+ if ( error )
+ break;
+ }
+
+ return error;
+ }
+
+
+ static FT_ULong
+ ft_gzip_file_io( FT_GZipFile zip,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_ULong result = 0;
+ FT_Error error;
+
+
+ /* Reset inflate stream if we're seeking backwards. */
+ /* Yes, that is not too efficient, but it saves memory :-) */
+ if ( pos < zip->pos )
+ {
+ error = ft_gzip_file_reset( zip );
+ if ( error )
+ goto Exit;
+ }
+
+ /* skip unwanted bytes */
+ if ( pos > zip->pos )
+ {
+ error = ft_gzip_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( count == 0 )
+ goto Exit;
+
+ /* now read the data */
+ for (;;)
+ {
+ FT_ULong delta;
+
+
+ delta = (FT_ULong)( zip->limit - zip->cursor );
+ if ( delta >= count )
+ delta = count;
+
+ FT_MEM_COPY( buffer, zip->cursor, delta );
+ buffer += delta;
+ result += delta;
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ if ( count == 0 )
+ break;
+
+ error = ft_gzip_file_fill_output( zip );
+ if ( error )
+ break;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** G Z E M B E D D I N G S T R E A M *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+ static void
+ ft_gzip_stream_close( FT_Stream stream )
+ {
+ FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer;
+ FT_Memory memory = stream->memory;
+
+
+ if ( zip )
+ {
+ /* finalize gzip file descriptor */
+ ft_gzip_file_done( zip );
+
+ FT_FREE( zip );
+
+ stream->descriptor.pointer = NULL;
+ }
+ }
+
+
+ static FT_ULong
+ ft_gzip_stream_io( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_GZipFile zip = (FT_GZipFile)stream->descriptor.pointer;
+
+
+ return ft_gzip_file_io( zip, pos, buffer, count );
+ }
+
+
+ static FT_ULong
+ ft_gzip_get_uncompressed_size( FT_Stream stream )
+ {
+ FT_Error error;
+ FT_ULong old_pos;
+ FT_ULong result = 0;
+
+
+ old_pos = stream->pos;
+ if ( !FT_Stream_Seek( stream, stream->size - 4 ) )
+ {
+ result = (FT_ULong)FT_Stream_ReadLong( stream, &error );
+ if ( error )
+ result = 0;
+
+ FT_Stream_Seek( stream, old_pos );
+ }
+
+ return result;
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenGzip( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_Error error;
+ FT_Memory memory = source->memory;
+ FT_GZipFile zip;
+
+
+ /*
+ * check the header right now; this prevents allocating un-necessary
+ * objects when we don't need them
+ */
+ error = ft_gzip_check_header( source );
+ if ( error )
+ goto Exit;
+
+ FT_ZERO( stream );
+ stream->memory = memory;
+
+ if ( !FT_QNEW( zip ) )
+ {
+ error = ft_gzip_file_init( zip, stream, source );
+ if ( error )
+ {
+ FT_FREE( zip );
+ goto Exit;
+ }
+
+ stream->descriptor.pointer = zip;
+ }
+
+ /*
+ * We use the following trick to try to dramatically improve the
+ * performance while dealing with small files. If the original stream
+ * size is less than a certain threshold, we try to load the whole font
+ * file into memory. This saves us from using the 32KB buffer needed
+ * to inflate the file, plus the two 4KB intermediate input/output
+ * buffers used in the `FT_GZipFile' structure.
+ */
+ {
+ FT_ULong zip_size = ft_gzip_get_uncompressed_size( source );
+
+
+ if ( zip_size != 0 && zip_size < 40 * 1024 )
+ {
+ FT_Byte* zip_buff;
+
+
+ if ( !FT_ALLOC( zip_buff, zip_size ) )
+ {
+ FT_ULong count;
+
+
+ count = ft_gzip_file_io( zip, 0, zip_buff, zip_size );
+ if ( count == zip_size )
+ {
+ ft_gzip_file_done( zip );
+ FT_FREE( zip );
+
+ stream->descriptor.pointer = NULL;
+
+ stream->size = zip_size;
+ stream->pos = 0;
+ stream->base = zip_buff;
+ stream->read = NULL;
+ stream->close = ft_gzip_stream_close;
+
+ goto Exit;
+ }
+
+ ft_gzip_file_io( zip, 0, NULL, 0 );
+ FT_FREE( zip_buff );
+ }
+ error = 0;
+ }
+ }
+
+ stream->size = 0x7FFFFFFFL; /* don't know the real size! */
+ stream->pos = 0;
+ stream->base = 0;
+ stream->read = ft_gzip_stream_io;
+ stream->close = ft_gzip_stream_close;
+
+ Exit:
+ return error;
+ }
+
+#else /* !FT_CONFIG_OPTION_USE_ZLIB */
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenGzip( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_UNUSED( stream );
+ FT_UNUSED( source );
+
+ return Gzip_Err_Unimplemented_Feature;
+ }
+
+#endif /* !FT_CONFIG_OPTION_USE_ZLIB */
+
+
+/* END */
diff --git a/src/gzip/infblock.c b/src/gzip/infblock.c
new file mode 100644
index 0000000..d6e2dc2
--- /dev/null
+++ b/src/gzip/infblock.c
@@ -0,0 +1,387 @@
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local const uInt border[] = { /* Order of the bit length code lengths */
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+ Notes beyond the 1.93a appnote.txt:
+
+ 1. Distance pointers never point before the beginning of the output
+ stream.
+ 2. Distance pointers can point back across blocks, up to 32k away.
+ 3. There is an implied maximum of 7 bits for the bit length table and
+ 15 bits for the actual data.
+ 4. If only one code exists, then it is encoded using one bit. (Zero
+ would be more efficient, but perhaps a little confusing.) If two
+ codes exist, they are coded using one bit each (0 and 1).
+ 5. There is no way of sending zero distance codes--a dummy must be
+ sent if there are none. (History: a pre 2.0 version of PKZIP would
+ store blocks with no distance codes, but this was discovered to be
+ too harsh a criterion.) Valid only for 1.93a. 2.04c does allow
+ zero distance codes, which is sent as one code of zero bits in
+ length.
+ 6. There are up to 286 literal/length codes. Code 256 represents the
+ end-of-block. Note however that the static length tree defines
+ 288 codes just to fill out the Huffman codes. Codes 286 and 287
+ cannot be used though, since there is no length base or extra bits
+ defined for them. Similarily, there are up to 30 distance codes.
+ However, static trees define 32 codes (all 5 bits) to fill out the
+ Huffman codes, but the last two had better not show up in the data.
+ 7. Unzip can check dynamic Huffman blocks for complete code sets.
+ The exception is that a single code would not be complete (see #4).
+ 8. The five bits following the block type is really the number of
+ literal codes sent minus 257.
+ 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+ (1+6+6). Therefore, to output three times the length, you output
+ three codes (1+1+1), whereas to output four times the same length,
+ you only need two codes (1+3). Hmm.
+ 10. In the tree reconstruction algorithm, Code = Code + Increment
+ only if BitLength(i) is not zero. (Pretty obvious.)
+ 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19)
+ 12. Note: length code 284 can represent 227-258, but length code 285
+ really is 258. The last length deserves its own, short code
+ since it gets used a lot in very redundant files. The length
+ 258 is special since 258 - 3 (the min match length) is 255.
+ 13. The literal/length and distance code bit lengths are read as a
+ single stream of lengths. It is possible (and advantageous) for
+ a repeat code (16, 17, or 18) to go across the boundary between
+ the two sets of lengths.
+ */
+
+
+local void inflate_blocks_reset( /* s, z, c) */
+inflate_blocks_statef *s,
+z_streamp z,
+uLongf *c )
+{
+ if (c != Z_NULL)
+ *c = s->check;
+ if (s->mode == BTREE || s->mode == DTREE)
+ ZFREE(z, s->sub.trees.blens);
+ if (s->mode == CODES)
+ inflate_codes_free(s->sub.decode.codes, z);
+ s->mode = TYPE;
+ s->bitk = 0;
+ s->bitb = 0;
+ s->read = s->write = s->window;
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(0L, (const Bytef *)Z_NULL, 0);
+ Tracev((stderr, "inflate: blocks reset\n"));
+}
+
+
+local inflate_blocks_statef *inflate_blocks_new( /* z, c, w) */
+z_streamp z,
+check_func c,
+uInt w )
+{
+ inflate_blocks_statef *s;
+
+ if ((s = (inflate_blocks_statef *)ZALLOC
+ (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+ return s;
+ if ((s->hufts =
+ (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL)
+ {
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+ {
+ ZFREE(z, s->hufts);
+ ZFREE(z, s);
+ return Z_NULL;
+ }
+ s->end = s->window + w;
+ s->checkfn = c;
+ s->mode = TYPE;
+ Tracev((stderr, "inflate: blocks allocated\n"));
+ inflate_blocks_reset(s, z, Z_NULL);
+ return s;
+}
+
+
+local int inflate_blocks( /* s, z, r) */
+inflate_blocks_statef *s,
+z_streamp z,
+int r )
+{
+ uInt t; /* temporary storage */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input based on current state */
+ while (1) switch (s->mode)
+ {
+ case TYPE:
+ NEEDBITS(3)
+ t = (uInt)b & 7;
+ s->last = t & 1;
+ switch (t >> 1)
+ {
+ case 0: /* stored */
+ Tracev((stderr, "inflate: stored block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ t = k & 7; /* go to byte boundary */
+ DUMPBITS(t)
+ s->mode = LENS; /* get length of stored block */
+ break;
+ case 1: /* fixed */
+ Tracev((stderr, "inflate: fixed codes block%s\n",
+ s->last ? " (last)" : ""));
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+
+ inflate_trees_fixed(&bl, &bd, (const inflate_huft**)&tl,
+ (const inflate_huft**)&td, z);
+ s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+ if (s->sub.decode.codes == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ }
+ DUMPBITS(3)
+ s->mode = CODES;
+ break;
+ case 2: /* dynamic */
+ Tracev((stderr, "inflate: dynamic codes block%s\n",
+ s->last ? " (last)" : ""));
+ DUMPBITS(3)
+ s->mode = TABLE;
+ break;
+ case 3: /* illegal */
+ DUMPBITS(3)
+ s->mode = BAD;
+ z->msg = (char*)"invalid block type";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ break;
+ case LENS:
+ NEEDBITS(32)
+ if ((((~b) >> 16) & 0xffff) != (b & 0xffff))
+ {
+ s->mode = BAD;
+ z->msg = (char*)"invalid stored block lengths";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ s->sub.left = (uInt)b & 0xffff;
+ b = k = 0; /* dump bits */
+ Tracev((stderr, "inflate: stored length %u\n", s->sub.left));
+ s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE);
+ break;
+ case STORED:
+ if (n == 0)
+ LEAVE
+ NEEDOUT
+ t = s->sub.left;
+ if (t > n) t = n;
+ if (t > m) t = m;
+ zmemcpy(q, p, t);
+ p += t; n -= t;
+ q += t; m -= t;
+ if ((s->sub.left -= t) != 0)
+ break;
+ Tracev((stderr, "inflate: stored end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ s->mode = s->last ? DRY : TYPE;
+ break;
+ case TABLE:
+ NEEDBITS(14)
+ s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+ if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+ {
+ s->mode = BAD;
+ z->msg = (char*)"too many length or distance symbols";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+#endif
+ t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+ if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ DUMPBITS(14)
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: table sizes ok\n"));
+ s->mode = BTREE;
+ case BTREE:
+ while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+ {
+ NEEDBITS(3)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+ DUMPBITS(3)
+ }
+ while (s->sub.trees.index < 19)
+ s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+ s->sub.trees.bb = 7;
+ t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+ &s->sub.trees.tb, s->hufts, z);
+ if (t != Z_OK)
+ {
+ r = t;
+ if (r == Z_DATA_ERROR)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ }
+ LEAVE
+ }
+ s->sub.trees.index = 0;
+ Tracev((stderr, "inflate: bits tree ok\n"));
+ s->mode = DTREE;
+ case DTREE:
+ while (t = s->sub.trees.table,
+ s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+ {
+ inflate_huft *h;
+ uInt i, j, c;
+
+ t = s->sub.trees.bb;
+ NEEDBITS(t)
+ h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+ t = h->bits;
+ c = h->base;
+ if (c < 16)
+ {
+ DUMPBITS(t)
+ s->sub.trees.blens[s->sub.trees.index++] = c;
+ }
+ else /* c == 16..18 */
+ {
+ i = c == 18 ? 7 : c - 14;
+ j = c == 18 ? 11 : 3;
+ NEEDBITS(t + i)
+ DUMPBITS(t)
+ j += (uInt)b & inflate_mask[i];
+ DUMPBITS(i)
+ i = s->sub.trees.index;
+ t = s->sub.trees.table;
+ if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+ (c == 16 && i < 1))
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ z->msg = (char*)"invalid bit length repeat";
+ r = Z_DATA_ERROR;
+ LEAVE
+ }
+ c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+ do {
+ s->sub.trees.blens[i++] = c;
+ } while (--j);
+ s->sub.trees.index = i;
+ }
+ }
+ s->sub.trees.tb = Z_NULL;
+ {
+ uInt bl, bd;
+ inflate_huft *tl, *td;
+ inflate_codes_statef *c;
+
+ bl = 9; /* must be <= 9 for lookahead assumptions */
+ bd = 6; /* must be <= 9 for lookahead assumptions */
+ t = s->sub.trees.table;
+ t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+ s->sub.trees.blens, &bl, &bd, &tl, &td,
+ s->hufts, z);
+ if (t != Z_OK)
+ {
+ if (t == (uInt)Z_DATA_ERROR)
+ {
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = BAD;
+ }
+ r = t;
+ LEAVE
+ }
+ Tracev((stderr, "inflate: trees ok\n"));
+ if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+ {
+ r = Z_MEM_ERROR;
+ LEAVE
+ }
+ s->sub.decode.codes = c;
+ }
+ ZFREE(z, s->sub.trees.blens);
+ s->mode = CODES;
+ case CODES:
+ UPDATE
+ if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+ return inflate_flush(s, z, r);
+ r = Z_OK;
+ inflate_codes_free(s->sub.decode.codes, z);
+ LOAD
+ Tracev((stderr, "inflate: codes end, %lu total out\n",
+ z->total_out + (q >= s->read ? q - s->read :
+ (s->end - s->read) + (q - s->window))));
+ if (!s->last)
+ {
+ s->mode = TYPE;
+ break;
+ }
+ s->mode = DRY;
+ case DRY:
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ s->mode = DONE;
+ case DONE:
+ r = Z_STREAM_END;
+ LEAVE
+ case BAD:
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+#ifdef NEED_DUMMY_RETURN
+ return 0;
+#endif
+}
+
+
+local int inflate_blocks_free( /* s, z) */
+inflate_blocks_statef *s,
+z_streamp z )
+{
+ inflate_blocks_reset(s, z, Z_NULL);
+ ZFREE(z, s->window);
+ ZFREE(z, s->hufts);
+ ZFREE(z, s);
+ Tracev((stderr, "inflate: blocks freed\n"));
+ return Z_OK;
+}
+
+
diff --git a/src/gzip/infblock.h b/src/gzip/infblock.h
new file mode 100644
index 0000000..c2535a1
--- /dev/null
+++ b/src/gzip/infblock.h
@@ -0,0 +1,36 @@
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFBLOCK_H
+#define _INFBLOCK_H
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+local inflate_blocks_statef * inflate_blocks_new OF((
+ z_streamp z,
+ check_func c, /* check function */
+ uInt w)); /* window size */
+
+local int inflate_blocks OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int)); /* initial return code */
+
+local void inflate_blocks_reset OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ uLongf *)); /* check value on output */
+
+local int inflate_blocks_free OF((
+ inflate_blocks_statef *,
+ z_streamp));
+
+#endif /* _INFBLOCK_H */
diff --git a/src/gzip/infcodes.c b/src/gzip/infcodes.c
new file mode 100644
index 0000000..f7bfd58
--- /dev/null
+++ b/src/gzip/infcodes.c
@@ -0,0 +1,250 @@
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "infblock.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ START, /* x: set up for LEN */
+ LEN, /* i: get length/literal/eob next */
+ LENEXT, /* i: getting length extra (have base) */
+ DIST, /* i: get distance next */
+ DISTEXT, /* i: getting distance extra */
+ COPY, /* o: copying bytes in window, waiting for space */
+ LIT, /* o: got literal, waiting for output space */
+ WASH, /* o: got eob, possibly still output waiting */
+ END, /* x: got eob and all data flushed */
+ BADCODE} /* x: got error */
+inflate_codes_mode;
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+ /* mode */
+ inflate_codes_mode mode; /* current inflate_codes mode */
+
+ /* mode dependent information */
+ uInt len;
+ union {
+ struct {
+ inflate_huft *tree; /* pointer into tree */
+ uInt need; /* bits needed */
+ } code; /* if LEN or DIST, where in tree */
+ uInt lit; /* if LIT, literal */
+ struct {
+ uInt get; /* bits to get for extra */
+ uInt dist; /* distance back to copy from */
+ } copy; /* if EXT or COPY, where and how much */
+ } sub; /* submode */
+
+ /* mode independent information */
+ Byte lbits; /* ltree bits decoded per branch */
+ Byte dbits; /* dtree bits decoder per branch */
+ inflate_huft *ltree; /* literal/length/eob tree */
+ inflate_huft *dtree; /* distance tree */
+
+};
+
+
+local inflate_codes_statef *inflate_codes_new( /* bl, bd, tl, td, z) */
+uInt bl, uInt bd,
+inflate_huft *tl,
+inflate_huft *td, /* need separate declaration for Borland C++ */
+z_streamp z )
+{
+ inflate_codes_statef *c;
+
+ if ((c = (inflate_codes_statef *)
+ ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+ {
+ c->mode = START;
+ c->lbits = (Byte)bl;
+ c->dbits = (Byte)bd;
+ c->ltree = tl;
+ c->dtree = td;
+ Tracev((stderr, "inflate: codes new\n"));
+ }
+ return c;
+}
+
+
+local int inflate_codes( /* s, z, r) */
+inflate_blocks_statef *s,
+z_streamp z,
+int r )
+{
+ uInt j; /* temporary storage */
+ inflate_huft *t; /* temporary pointer */
+ uInt e; /* extra bits or operation */
+ uLong b; /* bit buffer */
+ uInt k; /* bits in bit buffer */
+ Bytef *p; /* input data pointer */
+ uInt n; /* bytes available there */
+ Bytef *q; /* output window write pointer */
+ uInt m; /* bytes to end of window or read pointer */
+ Bytef *f; /* pointer to copy strings from */
+ inflate_codes_statef *c = s->sub.decode.codes; /* codes state */
+
+ /* copy input/output information to locals (UPDATE macro restores) */
+ LOAD
+
+ /* process input and output based on current state */
+ while (1) switch (c->mode)
+ { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+ case START: /* x: set up for LEN */
+#ifndef SLOW
+ if (m >= 258 && n >= 10)
+ {
+ UPDATE
+ r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+ LOAD
+ if (r != Z_OK)
+ {
+ c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+ break;
+ }
+ }
+#endif /* !SLOW */
+ c->sub.code.need = c->lbits;
+ c->sub.code.tree = c->ltree;
+ c->mode = LEN;
+ case LEN: /* i: get length/literal/eob next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e == 0) /* literal */
+ {
+ c->sub.lit = t->base;
+ Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", t->base));
+ c->mode = LIT;
+ break;
+ }
+ if (e & 16) /* length */
+ {
+ c->sub.copy.get = e & 15;
+ c->len = t->base;
+ c->mode = LENEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ if (e & 32) /* end of block */
+ {
+ Tracevv((stderr, "inflate: end of block\n"));
+ c->mode = WASH;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid literal/length code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case LENEXT: /* i: getting length extra (have base) */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->len += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ c->sub.code.need = c->dbits;
+ c->sub.code.tree = c->dtree;
+ Tracevv((stderr, "inflate: length %u\n", c->len));
+ c->mode = DIST;
+ case DIST: /* i: get distance next */
+ j = c->sub.code.need;
+ NEEDBITS(j)
+ t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+ DUMPBITS(t->bits)
+ e = (uInt)(t->exop);
+ if (e & 16) /* distance */
+ {
+ c->sub.copy.get = e & 15;
+ c->sub.copy.dist = t->base;
+ c->mode = DISTEXT;
+ break;
+ }
+ if ((e & 64) == 0) /* next table */
+ {
+ c->sub.code.need = e;
+ c->sub.code.tree = t + t->base;
+ break;
+ }
+ c->mode = BADCODE; /* invalid code */
+ z->msg = (char*)"invalid distance code";
+ r = Z_DATA_ERROR;
+ LEAVE
+ case DISTEXT: /* i: getting distance extra */
+ j = c->sub.copy.get;
+ NEEDBITS(j)
+ c->sub.copy.dist += (uInt)b & inflate_mask[j];
+ DUMPBITS(j)
+ Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist));
+ c->mode = COPY;
+ case COPY: /* o: copying bytes in window, waiting for space */
+ f = q - c->sub.copy.dist;
+ while (f < s->window) /* modulo window size-"while" instead */
+ f += s->end - s->window; /* of "if" handles invalid distances */
+ while (c->len)
+ {
+ NEEDOUT
+ OUTBYTE(*f++)
+ if (f == s->end)
+ f = s->window;
+ c->len--;
+ }
+ c->mode = START;
+ break;
+ case LIT: /* o: got literal, waiting for output space */
+ NEEDOUT
+ OUTBYTE(c->sub.lit)
+ c->mode = START;
+ break;
+ case WASH: /* o: got eob, possibly more output */
+ if (k > 7) /* return unused byte, if any */
+ {
+ Assert(k < 16, "inflate_codes grabbed too many bytes")
+ k -= 8;
+ n++;
+ p--; /* can always return one */
+ }
+ FLUSH
+ if (s->read != s->write)
+ LEAVE
+ c->mode = END;
+ case END:
+ r = Z_STREAM_END;
+ LEAVE
+ case BADCODE: /* x: got error */
+ r = Z_DATA_ERROR;
+ LEAVE
+ default:
+ r = Z_STREAM_ERROR;
+ LEAVE
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
+
+local void inflate_codes_free( /* c, z) */
+inflate_codes_statef *c,
+z_streamp z )
+{
+ ZFREE(z, c);
+ Tracev((stderr, "inflate: codes free\n"));
+}
diff --git a/src/gzip/infcodes.h b/src/gzip/infcodes.h
new file mode 100644
index 0000000..154d7f8
--- /dev/null
+++ b/src/gzip/infcodes.h
@@ -0,0 +1,31 @@
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFCODES_H
+#define _INFCODES_H
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+local inflate_codes_statef *inflate_codes_new OF((
+ uInt, uInt,
+ inflate_huft *, inflate_huft *,
+ z_streamp ));
+
+local int inflate_codes OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+local void inflate_codes_free OF((
+ inflate_codes_statef *,
+ z_streamp ));
+
+#endif /* _INFCODES_H */
diff --git a/src/gzip/inffixed.h b/src/gzip/inffixed.h
new file mode 100644
index 0000000..4d4760e
--- /dev/null
+++ b/src/gzip/inffixed.h
@@ -0,0 +1,151 @@
+/* inffixed.h -- table for decoding fixed codes
+ * Generated automatically by the maketree.c program
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+local const uInt fixed_bl = 9;
+local const uInt fixed_bd = 5;
+local const inflate_huft fixed_tl[] = {
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254},
+ {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115},
+ {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193},
+ {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161},
+ {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225},
+ {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145},
+ {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209},
+ {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177},
+ {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241},
+ {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227},
+ {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201},
+ {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169},
+ {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233},
+ {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153},
+ {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217},
+ {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185},
+ {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249},
+ {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163},
+ {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197},
+ {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165},
+ {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229},
+ {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149},
+ {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213},
+ {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181},
+ {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245},
+ {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205},
+ {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173},
+ {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237},
+ {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157},
+ {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221},
+ {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189},
+ {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253},
+ {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131},
+ {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195},
+ {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163},
+ {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227},
+ {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147},
+ {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211},
+ {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179},
+ {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243},
+ {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258},
+ {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203},
+ {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171},
+ {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235},
+ {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155},
+ {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219},
+ {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187},
+ {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251},
+ {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195},
+ {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199},
+ {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167},
+ {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231},
+ {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151},
+ {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215},
+ {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183},
+ {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247},
+ {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0},
+ {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207},
+ {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175},
+ {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239},
+ {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159},
+ {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223},
+ {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191},
+ {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255}
+ };
+local const inflate_huft fixed_td[] = {
+ {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097},
+ {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385},
+ {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193},
+ {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577},
+ {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145},
+ {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577},
+ {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289},
+ {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577}
+ };
diff --git a/src/gzip/inflate.c b/src/gzip/inflate.c
new file mode 100644
index 0000000..8877fa3
--- /dev/null
+++ b/src/gzip/inflate.c
@@ -0,0 +1,273 @@
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+
+#define DONE INFLATE_DONE
+#define BAD INFLATE_BAD
+
+typedef enum {
+ METHOD, /* waiting for method byte */
+ FLAG, /* waiting for flag byte */
+ DICT4, /* four dictionary check bytes to go */
+ DICT3, /* three dictionary check bytes to go */
+ DICT2, /* two dictionary check bytes to go */
+ DICT1, /* one dictionary check byte to go */
+ DICT0, /* waiting for inflateSetDictionary */
+ BLOCKS, /* decompressing blocks */
+ CHECK4, /* four check bytes to go */
+ CHECK3, /* three check bytes to go */
+ CHECK2, /* two check bytes to go */
+ CHECK1, /* one check byte to go */
+ DONE, /* finished check, done */
+ BAD} /* got an error--stay here */
+inflate_mode;
+
+/* inflate private state */
+struct internal_state {
+
+ /* mode */
+ inflate_mode mode; /* current inflate mode */
+
+ /* mode dependent information */
+ union {
+ uInt method; /* if FLAGS, method byte */
+ struct {
+ uLong was; /* computed check value */
+ uLong need; /* stream check value */
+ } check; /* if CHECK, check values to compare */
+ uInt marker; /* if BAD, inflateSync's marker bytes count */
+ } sub; /* submode */
+
+ /* mode independent information */
+ int nowrap; /* flag for no wrapper */
+ uInt wbits; /* log2(window size) (8..15, defaults to 15) */
+ inflate_blocks_statef
+ *blocks; /* current inflate_blocks state */
+
+};
+
+
+ZEXPORT(int) inflateReset( /* z) */
+z_streamp z )
+{
+ if (z == Z_NULL || z->state == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->total_in = z->total_out = 0;
+ z->msg = Z_NULL;
+ z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+ inflate_blocks_reset(z->state->blocks, z, Z_NULL);
+ Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+
+ZEXPORT(int) inflateEnd( /* z) */
+z_streamp z )
+{
+ if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+ return Z_STREAM_ERROR;
+ if (z->state->blocks != Z_NULL)
+ inflate_blocks_free(z->state->blocks, z);
+ ZFREE(z, z->state);
+ z->state = Z_NULL;
+ Tracev((stderr, "inflate: end\n"));
+ return Z_OK;
+}
+
+
+ZEXPORT(int) inflateInit2_( /* z, w, version, stream_size) */
+z_streamp z,
+int w,
+const char *version,
+int stream_size )
+{
+ if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+ stream_size != sizeof(z_stream))
+ return Z_VERSION_ERROR;
+
+ /* initialize state */
+ if (z == Z_NULL)
+ return Z_STREAM_ERROR;
+ z->msg = Z_NULL;
+ if (z->zalloc == Z_NULL)
+ {
+ z->zalloc = zcalloc;
+ z->opaque = (voidpf)0;
+ }
+ if (z->zfree == Z_NULL) z->zfree = zcfree;
+ if ((z->state = (struct internal_state FAR *)
+ ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+ return Z_MEM_ERROR;
+ z->state->blocks = Z_NULL;
+
+ /* handle undocumented nowrap option (no zlib header or check) */
+ z->state->nowrap = 0;
+ if (w < 0)
+ {
+ w = - w;
+ z->state->nowrap = 1;
+ }
+
+ /* set window size */
+ if (w < 8 || w > 15)
+ {
+ inflateEnd(z);
+ return Z_STREAM_ERROR;
+ }
+ z->state->wbits = (uInt)w;
+
+ /* create inflate_blocks state */
+ if ((z->state->blocks =
+ inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w))
+ == Z_NULL)
+ {
+ inflateEnd(z);
+ return Z_MEM_ERROR;
+ }
+ Tracev((stderr, "inflate: allocated\n"));
+
+ /* reset state */
+ inflateReset(z);
+ return Z_OK;
+}
+
+
+
+#undef NEEDBYTE
+#define NEEDBYTE {if(z->avail_in==0)return r;r=f;}
+
+#undef NEXTBYTE
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+
+ZEXPORT(int) inflate( /* z, f) */
+z_streamp z,
+int f )
+{
+ int r;
+ uInt b;
+
+ if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL)
+ return Z_STREAM_ERROR;
+ f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
+ r = Z_BUF_ERROR;
+ while (1) switch (z->state->mode)
+ {
+ case METHOD:
+ NEEDBYTE
+ if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"unknown compression method";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"invalid window size";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ z->state->mode = FLAG;
+ case FLAG:
+ NEEDBYTE
+ b = NEXTBYTE;
+ if (((z->state->sub.method << 8) + b) % 31)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect header check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib header ok\n"));
+ if (!(b & PRESET_DICT))
+ {
+ z->state->mode = BLOCKS;
+ break;
+ }
+ z->state->mode = DICT4;
+ case DICT4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = DICT3;
+ case DICT3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = DICT2;
+ case DICT2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = DICT1;
+ case DICT1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+ z->adler = z->state->sub.check.need;
+ z->state->mode = DICT0;
+ return Z_NEED_DICT;
+ case DICT0:
+ z->state->mode = BAD;
+ z->msg = (char*)"need dictionary";
+ z->state->sub.marker = 0; /* can try inflateSync */
+ return Z_STREAM_ERROR;
+ case BLOCKS:
+ r = inflate_blocks(z->state->blocks, z, r);
+ if (r == Z_DATA_ERROR)
+ {
+ z->state->mode = BAD;
+ z->state->sub.marker = 0; /* can try inflateSync */
+ break;
+ }
+ if (r == Z_OK)
+ r = f;
+ if (r != Z_STREAM_END)
+ return r;
+ r = f;
+ inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+ if (z->state->nowrap)
+ {
+ z->state->mode = DONE;
+ break;
+ }
+ z->state->mode = CHECK4;
+ case CHECK4:
+ NEEDBYTE
+ z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+ z->state->mode = CHECK3;
+ case CHECK3:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+ z->state->mode = CHECK2;
+ case CHECK2:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+ z->state->mode = CHECK1;
+ case CHECK1:
+ NEEDBYTE
+ z->state->sub.check.need += (uLong)NEXTBYTE;
+
+ if (z->state->sub.check.was != z->state->sub.check.need)
+ {
+ z->state->mode = BAD;
+ z->msg = (char*)"incorrect data check";
+ z->state->sub.marker = 5; /* can't try inflateSync */
+ break;
+ }
+ Tracev((stderr, "inflate: zlib check ok\n"));
+ z->state->mode = DONE;
+ case DONE:
+ return Z_STREAM_END;
+ case BAD:
+ return Z_DATA_ERROR;
+ default:
+ return Z_STREAM_ERROR;
+ }
+#ifdef NEED_DUMMY_RETURN
+ return Z_STREAM_ERROR; /* Some dumb compilers complain without this */
+#endif
+}
+
diff --git a/src/gzip/inftrees.c b/src/gzip/inftrees.c
new file mode 100644
index 0000000..3c39aca
--- /dev/null
+++ b/src/gzip/inftrees.c
@@ -0,0 +1,465 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#if !defined(BUILDFIXED) && !defined(STDC)
+# define BUILDFIXED /* non ANSI compilers may not accept inffixed.h */
+#endif
+
+
+#if 0
+local const char inflate_copyright[] =
+ " inflate 1.1.4 Copyright 1995-2002 Mark Adler ";
+#endif
+/*
+ If you use the zlib library in a product, an acknowledgment is welcome
+ in the documentation of your product. If for some reason you cannot
+ include such an acknowledgment, I would appreciate that you keep this
+ copyright string in the executable of your product.
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+ uIntf *, /* code lengths in bits */
+ uInt, /* number of codes */
+ uInt, /* number of "simple" codes */
+ const uIntf *, /* list of base values for non-simple codes */
+ const uIntf *, /* list of extra bits for non-simple codes */
+ inflate_huft * FAR*,/* result: starting table */
+ uIntf *, /* maximum lookup bits (returns actual) */
+ inflate_huft *, /* space for trees */
+ uInt *, /* hufts used in space */
+ uIntf * )); /* space for values */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+ /* see note #13 above about 258 */
+local const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */
+local const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+local const uInt cpdext[30] = { /* Extra bits for distance codes */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+/*
+ Huffman code decoding is performed using a multi-level table lookup.
+ The fastest way to decode is to simply build a lookup table whose
+ size is determined by the longest code. However, the time it takes
+ to build this table can also be a factor if the data being decoded
+ is not very long. The most common codes are necessarily the
+ shortest codes, so those codes dominate the decoding time, and hence
+ the speed. The idea is you can have a shorter table that decodes the
+ shorter, more probable codes, and then point to subsidiary tables for
+ the longer codes. The time it costs to decode the longer codes is
+ then traded against the time it takes to make longer tables.
+
+ This results of this trade are in the variables lbits and dbits
+ below. lbits is the number of bits the first level table for literal/
+ length codes can decode in one step, and dbits is the same thing for
+ the distance codes. Subsequent tables are also less than or equal to
+ those sizes. These values may be adjusted either when all of the
+ codes are shorter than that, in which case the longest code length in
+ bits is used, or when the shortest code is *longer* than the requested
+ table size, in which case the length of the shortest code in bits is
+ used.
+
+ There are two different values for the two tables, since they code a
+ different number of possibilities each. The literal/length table
+ codes 286 possible values, or in a flat code, a little over eight
+ bits. The distance table codes 30 possible values, or a little less
+ than five bits, flat. The optimum values for speed end up being
+ about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+ The optimum values may differ though from machine to machine, and
+ possibly even between compilers. Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15 /* maximum bit length of any code */
+
+local int huft_build( /* b, n, s, d, e, t, m, hp, hn, v) */
+uIntf *b, /* code lengths in bits (all assumed <= BMAX) */
+uInt n, /* number of codes (assumed <= 288) */
+uInt s, /* number of simple-valued codes (0..s-1) */
+const uIntf *d, /* list of base values for non-simple codes */
+const uIntf *e, /* list of extra bits for non-simple codes */
+inflate_huft * FAR *t, /* result: starting table */
+uIntf *m, /* maximum lookup bits, returns actual */
+inflate_huft *hp, /* space for trees */
+uInt *hn, /* hufts used in space */
+uIntf *v /* working area: values in order of bit length */
+/* Given a list of code lengths and a maximum table size, make a set of
+ tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR
+ if the given code set is incomplete (the tables are still built in this
+ case), or Z_DATA_ERROR if the input is invalid. */
+)
+{
+
+ uInt a; /* counter for codes of length k */
+ uInt c[BMAX+1]; /* bit length count table */
+ uInt f; /* i repeats in table every f entries */
+ int g; /* maximum code length */
+ int h; /* table level */
+ register uInt i; /* counter, current code */
+ register uInt j; /* counter */
+ register int k; /* number of bits in current code */
+ int l; /* bits per table (returned in m) */
+ uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */
+ register uIntf *p; /* pointer into c[], b[], or v[] */
+ inflate_huft *q; /* points to current table */
+ struct inflate_huft_s r; /* table entry for structure assignment */
+ inflate_huft *u[BMAX]; /* table stack */
+ register int w; /* bits before this table == (l * h) */
+ uInt x[BMAX+1]; /* bit offsets, then code stack */
+ uIntf *xp; /* pointer into x */
+ int y; /* number of dummy codes added */
+ uInt z; /* number of entries in current table */
+
+
+ /* Make compiler happy */
+ r.base = 0;
+
+ /* Generate counts for each bit length */
+ p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+ C4 /* clear c[]--assume BMAX+1 is 16 */
+ p = b; i = n;
+ do {
+ c[*p++]++; /* assume all entries <= BMAX */
+ } while (--i);
+ if (c[0] == n) /* null input--all zero length codes */
+ {
+ *t = (inflate_huft *)Z_NULL;
+ *m = 0;
+ return Z_OK;
+ }
+
+
+ /* Find minimum and maximum length, bound *m by those */
+ l = *m;
+ for (j = 1; j <= BMAX; j++)
+ if (c[j])
+ break;
+ k = j; /* minimum code length */
+ if ((uInt)l < j)
+ l = j;
+ for (i = BMAX; i; i--)
+ if (c[i])
+ break;
+ g = i; /* maximum code length */
+ if ((uInt)l > i)
+ l = i;
+ *m = l;
+
+
+ /* Adjust last length count to fill out codes, if needed */
+ for (y = 1 << j; j < i; j++, y <<= 1)
+ if ((y -= c[j]) < 0)
+ return Z_DATA_ERROR;
+ if ((y -= c[i]) < 0)
+ return Z_DATA_ERROR;
+ c[i] += y;
+
+
+ /* Generate starting offsets into the value table for each length */
+ x[1] = j = 0;
+ p = c + 1; xp = x + 2;
+ while (--i) { /* note that i == g from above */
+ *xp++ = (j += *p++);
+ }
+
+
+ /* Make a table of values in order of bit lengths */
+ p = b; i = 0;
+ do {
+ if ((j = *p++) != 0)
+ v[x[j]++] = i;
+ } while (++i < n);
+ n = x[g]; /* set n to length of v */
+
+
+ /* Generate the Huffman codes and for each, make the table entries */
+ x[0] = i = 0; /* first Huffman code is zero */
+ p = v; /* grab values in bit order */
+ h = -1; /* no tables yet--level -1 */
+ w = -l; /* bits decoded == (l * h) */
+ u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */
+ q = (inflate_huft *)Z_NULL; /* ditto */
+ z = 0; /* ditto */
+
+ /* go through the bit lengths (k already is bits in shortest code) */
+ for (; k <= g; k++)
+ {
+ a = c[k];
+ while (a--)
+ {
+ /* here i is the Huffman code of length k bits for value *p */
+ /* make tables up to required level */
+ while (k > w + l)
+ {
+ h++;
+ w += l; /* previous table always l bits */
+
+ /* compute minimum size table less than or equal to l bits */
+ z = g - w;
+ z = z > (uInt)l ? (uInt)l : z; /* table size upper limit */
+ if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
+ { /* too few codes for k-w bit table */
+ f -= a + 1; /* deduct codes from patterns left */
+ xp = c + k;
+ if (j < z)
+ while (++j < z) /* try smaller tables up to z bits */
+ {
+ if ((f <<= 1) <= *++xp)
+ break; /* enough codes to use up j bits */
+ f -= *xp; /* else deduct codes from patterns */
+ }
+ }
+ z = 1 << j; /* table entries for j-bit table */
+
+ /* allocate new table */
+ if (*hn + z > MANY) /* (note: doesn't matter for fixed) */
+ return Z_DATA_ERROR; /* overflow of MANY */
+ u[h] = q = hp + *hn;
+ *hn += z;
+
+ /* connect to last table, if there is one */
+ if (h)
+ {
+ x[h] = i; /* save pattern for backing up */
+ r.bits = (Byte)l; /* bits to dump before this table */
+ r.exop = (Byte)j; /* bits in this table */
+ j = i >> (w - l);
+ r.base = (uInt)(q - u[h-1] - j); /* offset to this table */
+ u[h-1][j] = r; /* connect to last table */
+ }
+ else
+ *t = q; /* first table is returned result */
+ }
+
+ /* set up table entry in r */
+ r.bits = (Byte)(k - w);
+ if (p >= v + n)
+ r.exop = 128 + 64; /* out of values--invalid code */
+ else if (*p < s)
+ {
+ r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */
+ r.base = *p++; /* simple code is just the value */
+ }
+ else
+ {
+ r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */
+ r.base = d[*p++ - s];
+ }
+
+ /* fill code-like entries with r */
+ f = 1 << (k - w);
+ for (j = i >> w; j < z; j += f)
+ q[j] = r;
+
+ /* backwards increment the k-bit code i */
+ for (j = 1 << (k - 1); i & j; j >>= 1)
+ i ^= j;
+ i ^= j;
+
+ /* backup over finished tables */
+ mask = (1 << w) - 1; /* needed on HP, cc -O bug */
+ while ((i & mask) != x[h])
+ {
+ h--; /* don't need to update q */
+ w -= l;
+ mask = (1 << w) - 1;
+ }
+ }
+ }
+
+
+ /* Return Z_BUF_ERROR if we were given an incomplete table */
+ return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+local int inflate_trees_bits( /* c, bb, tb, hp, z) */
+uIntf *c, /* 19 code lengths */
+uIntf *bb, /* bits tree desired/actual depth */
+inflate_huft * FAR *tb, /* bits tree result */
+inflate_huft *hp, /* space for trees */
+z_streamp z /* for messages */
+)
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+
+ if ((v = (uIntf*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+ r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL,
+ tb, bb, hp, &hn, v);
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed dynamic bit lengths tree";
+ else if (r == Z_BUF_ERROR || *bb == 0)
+ {
+ z->msg = (char*)"incomplete dynamic bit lengths tree";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+}
+
+
+local int inflate_trees_dynamic( /* nl, nd, c, bl, bd, tl, td, hp, z) */
+uInt nl, /* number of literal/length codes */
+uInt nd, /* number of distance codes */
+uIntf *c, /* that many (total) code lengths */
+uIntf *bl, /* literal desired/actual bit depth */
+uIntf *bd, /* distance desired/actual bit depth */
+inflate_huft * FAR *tl, /* literal/length tree result */
+inflate_huft * FAR *td, /* distance tree result */
+inflate_huft *hp, /* space for trees */
+z_streamp z /* for messages */
+)
+{
+ int r;
+ uInt hn = 0; /* hufts used in space */
+ uIntf *v; /* work area for huft_build */
+
+ /* allocate work area */
+ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+
+ /* build literal/length tree */
+ r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v);
+ if (r != Z_OK || *bl == 0)
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed literal/length tree";
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"incomplete literal/length tree";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+ }
+
+ /* build distance tree */
+ r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v);
+ if (r != Z_OK || (*bd == 0 && nl > 257))
+ {
+ if (r == Z_DATA_ERROR)
+ z->msg = (char*)"oversubscribed distance tree";
+ else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+ r = Z_OK;
+ }
+#else
+ z->msg = (char*)"incomplete distance tree";
+ r = Z_DATA_ERROR;
+ }
+ else if (r != Z_MEM_ERROR)
+ {
+ z->msg = (char*)"empty distance tree with lengths";
+ r = Z_DATA_ERROR;
+ }
+ ZFREE(z, v);
+ return r;
+#endif
+ }
+
+ /* done */
+ ZFREE(z, v);
+ return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+#ifdef BUILDFIXED
+local int fixed_built = 0;
+#define FIXEDH 544 /* number of hufts used by fixed tables */
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+#else
+#include "inffixed.h"
+#endif
+
+
+local int inflate_trees_fixed( /* bl, bd, tl, td, z) */
+uIntf *bl, /* literal desired/actual bit depth */
+uIntf *bd, /* distance desired/actual bit depth */
+const inflate_huft * FAR *tl, /* literal/length tree result */
+const inflate_huft * FAR *td, /* distance tree result */
+z_streamp z /* for memory allocation */
+)
+{
+#ifdef BUILDFIXED
+ /* build fixed tables if not already */
+ if (!fixed_built)
+ {
+ int k; /* temporary variable */
+ uInt f = 0; /* number of hufts used in fixed_mem */
+ uIntf *c; /* length list for huft_build */
+ uIntf *v; /* work area for huft_build */
+
+ /* allocate memory */
+ if ((c = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ return Z_MEM_ERROR;
+ if ((v = (uIntf*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL)
+ {
+ ZFREE(z, c);
+ return Z_MEM_ERROR;
+ }
+
+ /* literal table */
+ for (k = 0; k < 144; k++)
+ c[k] = 8;
+ for (; k < 256; k++)
+ c[k] = 9;
+ for (; k < 280; k++)
+ c[k] = 7;
+ for (; k < 288; k++)
+ c[k] = 8;
+ fixed_bl = 9;
+ huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl,
+ fixed_mem, &f, v);
+
+ /* distance table */
+ for (k = 0; k < 30; k++)
+ c[k] = 5;
+ fixed_bd = 5;
+ huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd,
+ fixed_mem, &f, v);
+
+ /* done */
+ ZFREE(z, v);
+ ZFREE(z, c);
+ fixed_built = 1;
+ }
+#else
+ FT_UNUSED(z);
+#endif
+ *bl = fixed_bl;
+ *bd = fixed_bd;
+ *tl = fixed_tl;
+ *td = fixed_td;
+ return Z_OK;
+}
diff --git a/src/gzip/inftrees.h b/src/gzip/inftrees.h
new file mode 100644
index 0000000..07bf2aa
--- /dev/null
+++ b/src/gzip/inftrees.h
@@ -0,0 +1,63 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+ that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+#ifndef _INFTREES_H
+#define _INFTREES_H
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+ union {
+ struct {
+ Byte Exop; /* number of extra bits or operation */
+ Byte Bits; /* number of bits in this code or subcode */
+ } what;
+ uInt pad; /* pad structure to a power of 2 (4 bytes for */
+ } word; /* 16-bit, 8 bytes for 32-bit int's) */
+ uInt base; /* literal, length base, distance base,
+ or table offset */
+};
+
+/* Maximum size of dynamic tree. The maximum found in a long but non-
+ exhaustive search was 1004 huft structures (850 for length/literals
+ and 154 for distances, the latter actually the result of an
+ exhaustive search). The actual maximum is not known, but the
+ value below is more than safe. */
+#define MANY 1440
+
+local int inflate_trees_bits OF((
+ uIntf *, /* 19 code lengths */
+ uIntf *, /* bits tree desired/actual depth */
+ inflate_huft * FAR *, /* bits tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+local int inflate_trees_dynamic OF((
+ uInt, /* number of literal/length codes */
+ uInt, /* number of distance codes */
+ uIntf *, /* that many (total) code lengths */
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ inflate_huft * FAR *, /* literal/length tree result */
+ inflate_huft * FAR *, /* distance tree result */
+ inflate_huft *, /* space for trees */
+ z_streamp)); /* for messages */
+
+local int inflate_trees_fixed OF((
+ uIntf *, /* literal desired/actual bit depth */
+ uIntf *, /* distance desired/actual bit depth */
+ const inflate_huft * FAR *, /* literal/length tree result */
+ const inflate_huft * FAR *, /* distance tree result */
+ z_streamp)); /* for memory allocation */
+
+#endif /* _INFTREES_H */
diff --git a/src/gzip/infutil.c b/src/gzip/infutil.c
new file mode 100644
index 0000000..6087b40
--- /dev/null
+++ b/src/gzip/infutil.c
@@ -0,0 +1,86 @@
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "infblock.h"
+#include "inftrees.h"
+#include "infcodes.h"
+#include "infutil.h"
+
+
+/* And'ing with mask[n] masks the lower n bits */
+local const uInt inflate_mask[17] = {
+ 0x0000,
+ 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush( /* s, z, r) */
+inflate_blocks_statef *s,
+z_streamp z,
+int r )
+{
+ uInt n;
+ Bytef *p;
+ Bytef *q;
+
+ /* local copies of source and destination pointers */
+ p = z->next_out;
+ q = s->read;
+
+ /* compute number of bytes to copy as far as end of window */
+ n = (uInt)((q <= s->write ? s->write : s->end) - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy as far as end of window */
+ zmemcpy(p, q, n);
+ p += n;
+ q += n;
+
+ /* see if more to copy at beginning of window */
+ if (q == s->end)
+ {
+ /* wrap pointers */
+ q = s->window;
+ if (s->write == s->end)
+ s->write = s->window;
+
+ /* compute bytes to copy */
+ n = (uInt)(s->write - q);
+ if (n > z->avail_out) n = z->avail_out;
+ if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+ /* update counters */
+ z->avail_out -= n;
+ z->total_out += n;
+
+ /* update check information */
+ if (s->checkfn != Z_NULL)
+ z->adler = s->check = (*s->checkfn)(s->check, q, n);
+
+ /* copy */
+ zmemcpy(p, q, n);
+ p += n;
+ q += n;
+ }
+
+ /* update pointers */
+ z->next_out = p;
+ s->read = q;
+
+ /* done */
+ return r;
+}
diff --git a/src/gzip/infutil.h b/src/gzip/infutil.h
new file mode 100644
index 0000000..7174b6d
--- /dev/null
+++ b/src/gzip/infutil.h
@@ -0,0 +1,98 @@
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995-2002 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+#ifndef _INFUTIL_H
+#define _INFUTIL_H
+
+typedef enum {
+ TYPE, /* get type bits (3, including end bit) */
+ LENS, /* get lengths for stored */
+ STORED, /* processing stored block */
+ TABLE, /* get table lengths */
+ BTREE, /* get bit lengths tree for a dynamic block */
+ DTREE, /* get length, distance trees for a dynamic block */
+ CODES, /* processing fixed or dynamic block */
+ DRY, /* output remaining window bytes */
+ DONE, /* finished last block, done */
+ BAD} /* got a data error--stuck here */
+inflate_block_mode;
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+ /* mode */
+ inflate_block_mode mode; /* current inflate_block mode */
+
+ /* mode dependent information */
+ union {
+ uInt left; /* if STORED, bytes left to copy */
+ struct {
+ uInt table; /* table lengths (14 bits) */
+ uInt index; /* index into blens (or border) */
+ uIntf *blens; /* bit lengths of codes */
+ uInt bb; /* bit length tree depth */
+ inflate_huft *tb; /* bit length decoding tree */
+ } trees; /* if DTREE, decoding info for trees */
+ struct {
+ inflate_codes_statef
+ *codes;
+ } decode; /* if CODES, current state */
+ } sub; /* submode */
+ uInt last; /* true if this block is the last block */
+
+ /* mode independent information */
+ uInt bitk; /* bits in bit buffer */
+ uLong bitb; /* bit buffer */
+ inflate_huft *hufts; /* single malloc for tree space */
+ Bytef *window; /* sliding window */
+ Bytef *end; /* one byte after sliding window */
+ Bytef *read; /* window read pointer */
+ Bytef *write; /* window write pointer */
+ check_func checkfn; /* check function */
+ uLong check; /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/* update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/* get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/* output bytes */
+#define WAVAIL (uInt)(q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=(uInt)WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/* load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/* masks for lower bits (size given to avoid silly warnings with Visual C++) */
+#ifndef NO_INFLATE_MASK
+local uInt inflate_mask[17];
+#endif
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush OF((
+ inflate_blocks_statef *,
+ z_streamp ,
+ int));
+
+#endif
diff --git a/src/gzip/rules.mk b/src/gzip/rules.mk
new file mode 100644
index 0000000..d2a43a6
--- /dev/null
+++ b/src/gzip/rules.mk
@@ -0,0 +1,75 @@
+#
+# FreeType 2 GZip support configuration rules
+#
+
+
+# Copyright 2002, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# gzip driver directory
+#
+GZIP_DIR := $(SRC_DIR)/gzip
+
+
+# compilation flags for the driver
+#
+ifeq ($(SYSTEM_ZLIB),)
+ GZIP_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(GZIP_DIR))
+else
+ GZIP_COMPILE := $(FT_COMPILE)
+endif
+
+
+# gzip support sources (i.e., C files)
+#
+GZIP_DRV_SRC := $(GZIP_DIR)/ftgzip.c
+
+# gzip support headers
+#
+GZIP_DRV_H :=
+
+
+# gzip driver object(s)
+#
+# GZIP_DRV_OBJ_M is used during `multi' builds
+# GZIP_DRV_OBJ_S is used during `single' builds
+#
+ifeq ($(SYSTEM_ZLIB),)
+ GZIP_DRV_OBJ_M := $(GZIP_DRV_SRC:$(GZIP_DIR)/%.c=$(OBJ_DIR)/%.$O)
+else
+ GZIP_DRV_OBJ_M := $(OBJ_DIR)/ftgzip.$O
+endif
+GZIP_DRV_OBJ_S := $(OBJ_DIR)/ftgzip.$O
+
+# gzip support source file for single build
+#
+GZIP_DRV_SRC_S := $(GZIP_DIR)/ftgzip.c
+
+
+# gzip support - single object
+#
+$(GZIP_DRV_OBJ_S): $(GZIP_DRV_SRC_S) $(GZIP_DRV_SRC) $(FREETYPE_H) \
+ $(GZIP_DRV_H)
+ $(GZIP_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(GZIP_DRV_SRC_S))
+
+
+# gzip support - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(GZIP_DIR)/%.c $(FREETYPE_H) $(GZIP_DRV_H)
+ $(GZIP_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(GZIP_DRV_OBJ_S)
+DRV_OBJS_M += $(GZIP_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/gzip/zconf.h b/src/gzip/zconf.h
new file mode 100644
index 0000000..3ccc3a6
--- /dev/null
+++ b/src/gzip/zconf.h
@@ -0,0 +1,278 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zconf.h,v 1.4 2007/06/01 06:56:17 wl Exp $ */
+
+#ifndef _ZCONF_H
+#define _ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+# define deflateInit_ z_deflateInit_
+# define deflate z_deflate
+# define deflateEnd z_deflateEnd
+# define inflateInit_ z_inflateInit_
+# define inflate z_inflate
+# define inflateEnd z_inflateEnd
+# define deflateInit2_ z_deflateInit2_
+# define deflateSetDictionary z_deflateSetDictionary
+# define deflateCopy z_deflateCopy
+# define deflateReset z_deflateReset
+# define deflateParams z_deflateParams
+# define inflateInit2_ z_inflateInit2_
+# define inflateSetDictionary z_inflateSetDictionary
+# define inflateSync z_inflateSync
+# define inflateSyncPoint z_inflateSyncPoint
+# define inflateReset z_inflateReset
+# define compress z_compress
+# define compress2 z_compress2
+# define uncompress z_uncompress
+# define adler32 z_adler32
+# define crc32 z_crc32
+# define get_crc_table z_get_crc_table
+
+# define Byte z_Byte
+# define uInt z_uInt
+# define uLong z_uLong
+# define Bytef z_Bytef
+# define charf z_charf
+# define intf z_intf
+# define uIntf z_uIntf
+# define uLongf z_uLongf
+# define voidpf z_voidpf
+# define voidp z_voidp
+#endif
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386)
+# ifndef __32BIT__
+# define __32BIT__
+# endif
+#endif
+#if defined(__MSDOS__) && !defined(MSDOS)
+# define MSDOS
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#if defined(MSDOS) && !defined(__32BIT__)
+# define MAXSEG_64K
+#endif
+#ifdef MSDOS
+# define UNALIGNED_OK
+#endif
+
+#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC)
+# define STDC
+#endif
+#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__)
+# ifndef STDC
+# define STDC
+# endif
+#endif
+
+#ifndef STDC
+# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+# define const
+# endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
+# define NO_DUMMY_DECL
+#endif
+
+/* Old Borland C and LCC incorrectly complains about missing returns: */
+#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500)
+# define NEED_DUMMY_RETURN
+#endif
+
+#if defined(__LCC__)
+# define NEED_DUMMY_RETURN
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+# ifdef MAXSEG_64K
+# define MAX_MEM_LEVEL 8
+# else
+# define MAX_MEM_LEVEL 9
+# endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+# define MAX_WBITS 15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+ (1 << (windowBits+2)) + (1 << (memLevel+9))
+ that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+ make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+ The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+ /* Type declarations */
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__)
+ /* MSC small or medium model */
+# define SMALL_MEDIUM
+# ifdef _MSC_VER
+# define FAR _far
+# else
+# define FAR far
+# endif
+#endif
+#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__))
+# ifndef __32BIT__
+# define SMALL_MEDIUM
+# define FAR _far
+# endif
+#endif
+
+/* Compile with -DZLIB_DLL for Windows DLL support */
+#if defined(ZLIB_DLL)
+# if defined(_WINDOWS) || defined(WINDOWS)
+# ifdef FAR
+# undef FAR
+# endif
+# include <windows.h>
+# define ZEXPORT(x) x WINAPI
+# ifdef WIN32
+# define ZEXPORTVA(x) x WINAPIV
+# else
+# define ZEXPORTVA(x) x FAR _cdecl _export
+# endif
+# endif
+# if defined (__BORLANDC__)
+# if (__BORLANDC__ >= 0x0500) && defined (WIN32)
+# include <windows.h>
+# define ZEXPORT(x) x __declspec(dllexport) WINAPI
+# define ZEXPORTRVA(x) x __declspec(dllexport) WINAPIV
+# else
+# if defined (_Windows) && defined (__DLL__)
+# define ZEXPORT(x) x _export
+# define ZEXPORTVA(x) x _export
+# endif
+# endif
+# endif
+#endif
+
+
+#ifndef ZEXPORT
+# define ZEXPORT(x) static x
+#endif
+#ifndef ZEXPORTVA
+# define ZEXPORTVA(x) static x
+#endif
+#ifndef ZEXTERN
+# define ZEXTERN(x) static x
+#endif
+#ifndef ZEXTERNDEF
+# define ZEXTERNDEF(x) static x
+#endif
+
+#ifndef FAR
+# define FAR
+#endif
+
+#if !defined(MACOS) && !defined(TARGET_OS_MAC)
+typedef unsigned char Byte; /* 8 bits */
+#endif
+typedef unsigned int uInt; /* 16 bits or more */
+typedef unsigned long uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+ /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+# define Bytef Byte FAR
+#else
+ typedef Byte FAR Bytef;
+#endif
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+ typedef void FAR *voidpf;
+ typedef void *voidp;
+#else
+ typedef Byte FAR *voidpf;
+ typedef Byte *voidp;
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+# define SEEK_SET 0 /* Seek from beginning of file. */
+# define SEEK_CUR 1 /* Seek from current position. */
+# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+# define z_off_t long
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+# pragma map(deflateInit_,"DEIN")
+# pragma map(deflateInit2_,"DEIN2")
+# pragma map(deflateEnd,"DEEND")
+# pragma map(inflateInit_,"ININ")
+# pragma map(inflateInit2_,"ININ2")
+# pragma map(inflateEnd,"INEND")
+# pragma map(inflateSync,"INSY")
+# pragma map(inflateSetDictionary,"INSEDI")
+# pragma map(inflate_blocks,"INBL")
+# pragma map(inflate_blocks_new,"INBLNE")
+# pragma map(inflate_blocks_free,"INBLFR")
+# pragma map(inflate_blocks_reset,"INBLRE")
+# pragma map(inflate_codes_free,"INCOFR")
+# pragma map(inflate_codes,"INCO")
+# pragma map(inflate_fast,"INFA")
+# pragma map(inflate_flush,"INFLU")
+# pragma map(inflate_mask,"INMA")
+# pragma map(inflate_set_dictionary,"INSEDI2")
+# pragma map(inflate_copyright,"INCOPY")
+# pragma map(inflate_trees_bits,"INTRBI")
+# pragma map(inflate_trees_dynamic,"INTRDY")
+# pragma map(inflate_trees_fixed,"INTRFI")
+# pragma map(inflate_trees_free,"INTRFR")
+#endif
+
+#endif /* _ZCONF_H */
diff --git a/src/gzip/zlib.h b/src/gzip/zlib.h
new file mode 100644
index 0000000..50d0d3f
--- /dev/null
+++ b/src/gzip/zlib.h
@@ -0,0 +1,830 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.1.4, March 11th, 2002
+
+ Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
+ (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.1.4"
+
+/*
+ The 'zlib' compression library provides in-memory compression and
+ decompression functions, including integrity checks of the uncompressed
+ data. This version of the library supports only one compression method
+ (deflation) but other algorithms will be added later and will have the same
+ stream interface.
+
+ Compression can be done in a single step if the buffers are large
+ enough (for example if an input file is mmap'ed), or can be done by
+ repeated calls of the compression function. In the latter case, the
+ application must provide more input and/or consume the output
+ (providing more output space) before each call.
+
+ The library also supports reading and writing files in gzip (.gz) format
+ with an interface similar to that of stdio.
+
+ The library does not install any signal handler. The decoder checks
+ the consistency of the compressed data, so the library should never
+ crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void (*free_func) OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+ Bytef *next_in; /* next input byte */
+ uInt avail_in; /* number of bytes available at next_in */
+ uLong total_in; /* total nb of input bytes read so far */
+
+ Bytef *next_out; /* next output byte should be put there */
+ uInt avail_out; /* remaining free space at next_out */
+ uLong total_out; /* total nb of bytes output so far */
+
+ char *msg; /* last error message, NULL if no error */
+ struct internal_state FAR *state; /* not visible by applications */
+
+ alloc_func zalloc; /* used to allocate the internal state */
+ free_func zfree; /* used to free the internal state */
+ voidpf opaque; /* private data object passed to zalloc and zfree */
+
+ int data_type; /* best guess about the data type: ascii or binary */
+ uLong adler; /* adler32 value of the uncompressed data */
+ uLong reserved; /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+ The application must update next_in and avail_in when avail_in has
+ dropped to zero. It must update next_out and avail_out when avail_out
+ has dropped to zero. The application must initialize zalloc, zfree and
+ opaque before calling the init function. All other fields are set by the
+ compression library and must not be updated by the application.
+
+ The opaque value provided by the application will be passed as the first
+ parameter for calls of zalloc and zfree. This can be useful for custom
+ memory management. The compression library attaches no meaning to the
+ opaque value.
+
+ zalloc must return Z_NULL if there is not enough memory for the object.
+ If zlib is used in a multi-threaded application, zalloc and zfree must be
+ thread safe.
+
+ On 16-bit systems, the functions zalloc and zfree must be able to allocate
+ exactly 65536 bytes, but will not be required to allocate more than this
+ if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+ pointers returned by zalloc for objects of exactly 65536 bytes *must*
+ have their offset normalized to zero. The default allocation function
+ provided by this library ensures this (see zutil.c). To reduce memory
+ requirements and avoid any allocation of 64K objects, at the expense of
+ compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+ The fields total_in and total_out can be used for statistics or
+ progress reports. After compression, total_in holds the total size of
+ the uncompressed data and may be saved for use in the decompressor
+ (particularly if the decompressor wants to decompress everything in
+ a single step).
+*/
+
+ /* constants */
+
+#define Z_NO_FLUSH 0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH 2
+#define Z_FULL_FLUSH 3
+#define Z_FINISH 4
+/* Allowed flush values; see deflate() below for details */
+
+#define Z_OK 0
+#define Z_STREAM_END 1
+#define Z_NEED_DICT 2
+#define Z_ERRNO (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR (-3)
+#define Z_MEM_ERROR (-4)
+#define Z_BUF_ERROR (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION 0
+#define Z_BEST_SPEED 1
+#define Z_BEST_COMPRESSION 9
+#define Z_DEFAULT_COMPRESSION (-1)
+/* compression levels */
+
+#define Z_FILTERED 1
+#define Z_HUFFMAN_ONLY 2
+#define Z_DEFAULT_STRATEGY 0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY 0
+#define Z_ASCII 1
+#define Z_UNKNOWN 2
+/* Possible values of the data_type field */
+
+#define Z_DEFLATED 8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */
+
+
+ /* basic functions */
+
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+ If the first character differs, the library code actually used is
+ not compatible with the zlib.h header file used by the application.
+ This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN(int) deflateInit OF((z_streamp strm, int level));
+
+ Initializes the internal stream state for compression. The fields
+ zalloc, zfree and opaque must be initialized before by the caller.
+ If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+ use default allocation functions.
+
+ The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+ 1 gives best speed, 9 gives best compression, 0 gives no compression at
+ all (the input data is simply copied a block at a time).
+ Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+ compression (currently equivalent to level 6).
+
+ deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+ Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+ with the version assumed by the caller (ZLIB_VERSION).
+ msg is set to null if there is no error message. deflateInit does not
+ perform any compression: this will be done by deflate().
+*/
+
+
+/*
+ deflate compresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may introduce some
+ output latency (reading input without producing any output) except when
+ forced to flush.
+
+ The detailed semantics are as follows. deflate performs one or both of the
+ following actions:
+
+ - Compress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in and avail_in are updated and
+ processing will resume at this point for the next call of deflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. This action is forced if the parameter flush is non zero.
+ Forcing flush frequently degrades the compression ratio, so this parameter
+ should be set only when necessary (in interactive applications).
+ Some output may be provided even if flush is not set.
+
+ Before the call of deflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating avail_in or avail_out accordingly; avail_out
+ should never be zero before the call. The application can consume the
+ compressed output when it wants, for example when the output buffer is full
+ (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+ and with zero avail_out, it must be called again after making room in the
+ output buffer because there might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+ flushed to the output buffer and the output is aligned on a byte boundary, so
+ that the decompressor can get all input data available so far. (In particular
+ avail_in is zero after the call if enough output space has been provided
+ before the call.) Flushing may degrade compression for some compression
+ algorithms and so it should be used only when necessary.
+
+ If flush is set to Z_FULL_FLUSH, all output is flushed as with
+ Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+ restart from this point if previous compressed data has been damaged or if
+ random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+ the compression.
+
+ If deflate returns with avail_out == 0, this function must be called again
+ with the same value of the flush parameter and more output space (updated
+ avail_out), until the flush is complete (deflate returns with non-zero
+ avail_out).
+
+ If the parameter flush is set to Z_FINISH, pending input is processed,
+ pending output is flushed and deflate returns with Z_STREAM_END if there
+ was enough output space; if deflate returns with Z_OK, this function must be
+ called again with Z_FINISH and more output space (updated avail_out) but no
+ more input data, until it returns with Z_STREAM_END or an error. After
+ deflate has returned Z_STREAM_END, the only possible operations on the
+ stream are deflateReset or deflateEnd.
+
+ Z_FINISH can be used immediately after deflateInit if all the compression
+ is to be done in a single step. In this case, avail_out must be at least
+ 0.1% larger than avail_in plus 12 bytes. If deflate does not return
+ Z_STREAM_END, then it must be called again as described above.
+
+ deflate() sets strm->adler to the adler32 checksum of all input read
+ so far (that is, total_in bytes).
+
+ deflate() may update data_type if it can make a good guess about
+ the input data type (Z_ASCII or Z_BINARY). In doubt, the data is considered
+ binary. This field is only for information purposes and does not affect
+ the compression algorithm in any manner.
+
+ deflate() returns Z_OK if some progress has been made (more input
+ processed or more output produced), Z_STREAM_END if all input has been
+ consumed and all output has been produced (only when flush is set to
+ Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+ if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+ (for example avail_in or avail_out was zero).
+*/
+
+
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+ stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+ prematurely (some input or output was discarded). In the error case,
+ msg may be set but then points to a static string (which must not be
+ deallocated).
+*/
+
+
+/*
+ZEXTERN(int) inflateInit OF((z_streamp strm));
+
+ Initializes the internal stream state for decompression. The fields
+ next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+ the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+ value depends on the compression method), inflateInit determines the
+ compression method from the zlib header and allocates all data structures
+ accordingly; otherwise the allocation will be deferred to the first call of
+ inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+ use default allocation functions.
+
+ inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+ version assumed by the caller. msg is set to null if there is no error
+ message. inflateInit does not perform any decompression apart from reading
+ the zlib header if present: this will be done by inflate(). (So next_in and
+ avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN(int) inflate OF((z_streamp strm, int flush));
+/*
+ inflate decompresses as much data as possible, and stops when the input
+ buffer becomes empty or the output buffer becomes full. It may some
+ introduce some output latency (reading input without producing any output)
+ except when forced to flush.
+
+ The detailed semantics are as follows. inflate performs one or both of the
+ following actions:
+
+ - Decompress more input starting at next_in and update next_in and avail_in
+ accordingly. If not all input can be processed (because there is not
+ enough room in the output buffer), next_in is updated and processing
+ will resume at this point for the next call of inflate().
+
+ - Provide more output starting at next_out and update next_out and avail_out
+ accordingly. inflate() provides as much output as possible, until there
+ is no more input data or no more space in the output buffer (see below
+ about the flush parameter).
+
+ Before the call of inflate(), the application should ensure that at least
+ one of the actions is possible, by providing more input and/or consuming
+ more output, and updating the next_* and avail_* values accordingly.
+ The application can consume the uncompressed output when it wants, for
+ example when the output buffer is full (avail_out == 0), or after each
+ call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+ must be called again after making room in the output buffer because there
+ might be more output pending.
+
+ If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much
+ output as possible to the output buffer. The flushing behavior of inflate is
+ not specified for values of the flush parameter other than Z_SYNC_FLUSH
+ and Z_FINISH, but the current implementation actually flushes as much output
+ as possible anyway.
+
+ inflate() should normally be called until it returns Z_STREAM_END or an
+ error. However if all decompression is to be performed in a single step
+ (a single call of inflate), the parameter flush should be set to
+ Z_FINISH. In this case all pending input is processed and all pending
+ output is flushed; avail_out must be large enough to hold all the
+ uncompressed data. (The size of the uncompressed data may have been saved
+ by the compressor for this purpose.) The next operation on this stream must
+ be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+ is never required, but can be used to inform inflate that a faster routine
+ may be used for the single inflate() call.
+
+ If a preset dictionary is needed at this point (see inflateSetDictionary
+ below), inflate sets strm-adler to the adler32 checksum of the
+ dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise
+ it sets strm->adler to the adler32 checksum of all output produced
+ so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or
+ an error code as described below. At the end of the stream, inflate()
+ checks that its computed adler32 checksum is equal to that saved by the
+ compressor and returns Z_STREAM_END only if the checksum is correct.
+
+ inflate() returns Z_OK if some progress has been made (more input processed
+ or more output produced), Z_STREAM_END if the end of the compressed data has
+ been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+ preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+ corrupted (input stream not conforming to the zlib format or incorrect
+ adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent
+ (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if no progress is possible or if there was not
+ enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR
+ case, the application may then call inflateSync to look for a good
+ compression block.
+*/
+
+
+ZEXTERN(int) inflateEnd OF((z_streamp strm));
+/*
+ All dynamically allocated data structures for this stream are freed.
+ This function discards any unprocessed input and does not flush any
+ pending output.
+
+ inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+ was inconsistent. In the error case, msg may be set but then points to a
+ static string (which must not be deallocated).
+*/
+
+ /* Advanced functions */
+
+/*
+ The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN(int) deflateInit2 OF((z_streamp strm,
+ int level,
+ int method,
+ int windowBits,
+ int memLevel,
+ int strategy));
+
+ This is another version of deflateInit with more compression options. The
+ fields next_in, zalloc, zfree and opaque must be initialized before by
+ the caller.
+
+ The method parameter is the compression method. It must be Z_DEFLATED in
+ this version of the library.
+
+ The windowBits parameter is the base two logarithm of the window size
+ (the size of the history buffer). It should be in the range 8..15 for this
+ version of the library. Larger values of this parameter result in better
+ compression at the expense of memory usage. The default value is 15 if
+ deflateInit is used instead.
+
+ The memLevel parameter specifies how much memory should be allocated
+ for the internal compression state. memLevel=1 uses minimum memory but
+ is slow and reduces compression ratio; memLevel=9 uses maximum memory
+ for optimal speed. The default value is 8. See zconf.h for total memory
+ usage as a function of windowBits and memLevel.
+
+ The strategy parameter is used to tune the compression algorithm. Use the
+ value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+ filter (or predictor), or Z_HUFFMAN_ONLY to force Huffman encoding only (no
+ string match). Filtered data consists mostly of small values with a
+ somewhat random distribution. In this case, the compression algorithm is
+ tuned to compress them better. The effect of Z_FILTERED is to force more
+ Huffman coding and less string matching; it is somewhat intermediate
+ between Z_DEFAULT and Z_HUFFMAN_ONLY. The strategy parameter only affects
+ the compression ratio but not the correctness of the compressed output even
+ if it is not set appropriately.
+
+ deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+ method). msg is set to null if there is no error message. deflateInit2 does
+ not perform any compression: this will be done by deflate().
+*/
+
+/*
+ Initializes the compression dictionary from the given byte sequence
+ without producing any compressed output. This function must be called
+ immediately after deflateInit, deflateInit2 or deflateReset, before any
+ call of deflate. The compressor and decompressor must use exactly the same
+ dictionary (see inflateSetDictionary).
+
+ The dictionary should consist of strings (byte sequences) that are likely
+ to be encountered later in the data to be compressed, with the most commonly
+ used strings preferably put towards the end of the dictionary. Using a
+ dictionary is most useful when the data to be compressed is short and can be
+ predicted with good accuracy; the data can then be compressed better than
+ with the default empty dictionary.
+
+ Depending on the size of the compression data structures selected by
+ deflateInit or deflateInit2, a part of the dictionary may in effect be
+ discarded, for example if the dictionary is larger than the window size in
+ deflate or deflate2. Thus the strings most likely to be useful should be
+ put at the end of the dictionary, not at the front.
+
+ Upon return of this function, strm->adler is set to the Adler32 value
+ of the dictionary; the decompressor may later use this value to determine
+ which dictionary has been used by the compressor. (The Adler32 value
+ applies to the whole dictionary even if only a subset of the dictionary is
+ actually used by the compressor.)
+
+ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent (for example if deflate has already been called for this stream
+ or if the compression method is bsort). deflateSetDictionary does not
+ perform any compression: this will be done by deflate().
+*/
+
+/*
+ Sets the destination stream as a complete copy of the source stream.
+
+ This function can be useful when several compression strategies will be
+ tried, for example when there are several ways of pre-processing the input
+ data with a filter. The streams that will be discarded should then be freed
+ by calling deflateEnd. Note that deflateCopy duplicates the internal
+ compression state which can be quite large, so this strategy is slow and
+ can consume lots of memory.
+
+ deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+ (such as zalloc being NULL). msg is left unchanged in both source and
+ destination.
+*/
+
+/*
+ This function is equivalent to deflateEnd followed by deflateInit,
+ but does not free and reallocate all the internal compression state.
+ The stream will keep the same compression level and any other attributes
+ that may have been set by deflateInit2.
+
+ deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+/*
+ Dynamically update the compression level and compression strategy. The
+ interpretation of level and strategy is as in deflateInit2. This can be
+ used to switch between compression and straight copy of the input data, or
+ to switch to a different kind of input data requiring a different
+ strategy. If the compression level is changed, the input available so far
+ is compressed with the old level (and may be flushed); the new level will
+ take effect only at the next call of deflate().
+
+ Before the call of deflateParams, the stream state must be set as for
+ a call of deflate(), since the currently available input may have to
+ be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+ deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+ stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+ if strm->avail_out was zero.
+*/
+
+/*
+ZEXTERN(int) inflateInit2 OF((z_streamp strm,
+ int windowBits));
+
+ This is another version of inflateInit with an extra parameter. The
+ fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+ before by the caller.
+
+ The windowBits parameter is the base two logarithm of the maximum window
+ size (the size of the history buffer). It should be in the range 8..15 for
+ this version of the library. The default value is 15 if inflateInit is used
+ instead. If a compressed stream with a larger window size is given as
+ input, inflate() will return with the error code Z_DATA_ERROR instead of
+ trying to allocate a larger window.
+
+ inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative
+ memLevel). msg is set to null if there is no error message. inflateInit2
+ does not perform any decompression apart from reading the zlib header if
+ present: this will be done by inflate(). (So next_in and avail_in may be
+ modified, but next_out and avail_out are unchanged.)
+*/
+
+/*
+ Initializes the decompression dictionary from the given uncompressed byte
+ sequence. This function must be called immediately after a call of inflate
+ if this call returned Z_NEED_DICT. The dictionary chosen by the compressor
+ can be determined from the Adler32 value returned by this call of
+ inflate. The compressor and decompressor must use exactly the same
+ dictionary (see deflateSetDictionary).
+
+ inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+ parameter is invalid (such as NULL dictionary) or the stream state is
+ inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+ expected one (incorrect Adler32 value). inflateSetDictionary does not
+ perform any decompression: this will be done by subsequent calls of
+ inflate().
+*/
+
+/*
+ Skips invalid compressed data until a full flush point (see above the
+ description of deflate with Z_FULL_FLUSH) can be found, or until all
+ available input is skipped. No output is provided.
+
+ inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+ if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+ or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+ case, the application may save the current current value of total_in which
+ indicates where valid compressed data was found. In the error case, the
+ application may repeatedly call inflateSync, providing more input each time,
+ until success or end of the input data.
+*/
+
+ZEXTERN(int) inflateReset OF((z_streamp strm));
+/*
+ This function is equivalent to inflateEnd followed by inflateInit,
+ but does not free and reallocate all the internal decompression state.
+ The stream will keep attributes that may have been set by inflateInit2.
+
+ inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+ stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+
+ /* utility functions */
+
+/*
+ The following utility functions are implemented on top of the
+ basic stream-oriented functions. To simplify the interface, some
+ default options are assumed (compression level and memory usage,
+ standard memory allocation functions). The source code of these
+ utility functions can easily be modified if you need special options.
+*/
+
+/*
+ Compresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be at least 0.1% larger than
+ sourceLen plus 12 bytes. Upon exit, destLen is the actual size of the
+ compressed buffer.
+ This function can be used to compress a whole file at once if the
+ input file is mmap'ed.
+ compress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer.
+*/
+
+/*
+ Compresses the source buffer into the destination buffer. The level
+ parameter has the same meaning as in deflateInit. sourceLen is the byte
+ length of the source buffer. Upon entry, destLen is the total size of the
+ destination buffer, which must be at least 0.1% larger than sourceLen plus
+ 12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+ compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+ memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+ Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+/*
+ Decompresses the source buffer into the destination buffer. sourceLen is
+ the byte length of the source buffer. Upon entry, destLen is the total
+ size of the destination buffer, which must be large enough to hold the
+ entire uncompressed data. (The size of the uncompressed data must have
+ been saved previously by the compressor and transmitted to the decompressor
+ by some mechanism outside the scope of this compression library.)
+ Upon exit, destLen is the actual size of the compressed buffer.
+ This function can be used to decompress a whole file at once if the
+ input file is mmap'ed.
+
+ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+ enough memory, Z_BUF_ERROR if there was not enough room in the output
+ buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+
+
+/*
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb") but can also include a compression level
+ ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+ Huffman only compression as in "wb1h". (See the description
+ of deflateInit2 for more information about the strategy parameter.)
+
+ gzopen can be used to read a file which is not in gzip format; in this
+ case gzread will directly read from the file without decompression.
+
+ gzopen returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR). */
+
+/*
+ gzdopen() associates a gzFile with the file descriptor fd. File
+ descriptors are obtained from calls like open, dup, creat, pipe or
+ fileno (in the file has been previously opened with fopen).
+ The mode parameter is as in gzopen.
+ The next call of gzclose on the returned gzFile will also close the
+ file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+ descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+ gzdopen returns NULL if there was insufficient memory to allocate
+ the (de)compression state.
+*/
+
+/*
+ Dynamically update the compression level or strategy. See the description
+ of deflateInit2 for the meaning of these parameters.
+ gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+ opened for writing.
+*/
+
+/*
+ Reads the given number of uncompressed bytes from the compressed file.
+ If the input file was not in gzip format, gzread copies the given number
+ of bytes into the buffer.
+ gzread returns the number of uncompressed bytes actually read (0 for
+ end of file, -1 for error). */
+
+/*
+ Writes the given number of uncompressed bytes into the compressed file.
+ gzwrite returns the number of uncompressed bytes actually written
+ (0 in case of error).
+*/
+
+/*
+ Converts, formats, and writes the args to the compressed file under
+ control of the format string, as in fprintf. gzprintf returns the number of
+ uncompressed bytes actually written (0 in case of error).
+*/
+
+/*
+ Writes the given null-terminated string to the compressed file, excluding
+ the terminating null character.
+ gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+/*
+ Reads bytes from the compressed file until len-1 characters are read, or
+ a newline character is read and transferred to buf, or an end-of-file
+ condition is encountered. The string is then terminated with a null
+ character.
+ gzgets returns buf, or Z_NULL in case of error.
+*/
+
+/*
+ Writes c, converted to an unsigned char, into the compressed file.
+ gzputc returns the value that was written, or -1 in case of error.
+*/
+
+/*
+ Reads one byte from the compressed file. gzgetc returns this byte
+ or -1 in case of end of file or error.
+*/
+
+/*
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function. The return value is the zlib
+ error number (see function gzerror below). gzflush returns Z_OK if
+ the flush parameter is Z_FINISH and all output could be flushed.
+ gzflush should be called only when strictly necessary because it can
+ degrade compression.
+*/
+
+/*
+ Sets the starting position for the next gzread or gzwrite on the
+ given compressed file. The offset represents a number of bytes in the
+ uncompressed data stream. The whence parameter is defined as in lseek(2);
+ the value SEEK_END is not supported.
+ If the file is opened for reading, this function is emulated but can be
+ extremely slow. If the file is opened for writing, only forward seeks are
+ supported; gzseek then compresses a sequence of zeroes up to the new
+ starting position.
+
+ gzseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error, in
+ particular if the file is opened for writing and the new starting position
+ would be before the current position.
+*/
+
+/*
+ Rewinds the given file. This function is supported only for reading.
+
+ gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+/*
+ Returns the starting position for the next gzread or gzwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+
+ gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ Returns 1 when EOF has previously been detected reading the given
+ input stream, otherwise zero.
+*/
+
+/*
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state. The return value is the zlib
+ error number (see function gzerror below).
+*/
+
+/*
+ Returns the error message for the last error which occurred on the
+ given compressed file. errnum is set to zlib error number. If an
+ error occurred in the file system and not in the compression library,
+ errnum is set to Z_ERRNO and the application may consult errno
+ to get the exact error code.
+*/
+
+ /* checksum functions */
+
+/*
+ These functions are not related to compression but are exported
+ anyway because they might be useful in applications using the
+ compression library.
+*/
+
+ZEXTERN(uLong) adler32 OF((uLong adler, const Bytef *buf, uInt len));
+
+/*
+ Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+ return the updated checksum. If buf is NULL, this function returns
+ the required initial value for the checksum.
+ An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+ much faster. Usage example:
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ adler = adler32(adler, buffer, length);
+ }
+ if (adler != original_adler) error();
+*/
+
+/*
+ Update a running crc with the bytes buf[0..len-1] and return the updated
+ crc. If buf is NULL, this function returns the required initial value
+ for the crc. Pre- and post-conditioning (one's complement) is performed
+ within this function so it shouldn't be done by the application.
+ Usage example:
+
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ while (read_buffer(buffer, length) != EOF) {
+ crc = crc32(crc, buffer, length);
+ }
+ if (crc != original_crc) error();
+*/
+
+
+ /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN(int) inflateInit2_ OF((z_streamp strm, int windowBits,
+ const char *version, int stream_size));
+#define deflateInit(strm, level) \
+ deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+ inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+ (strategy), ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+ inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZLIB_H */
diff --git a/src/gzip/zutil.c b/src/gzip/zutil.c
new file mode 100644
index 0000000..5ed2da0
--- /dev/null
+++ b/src/gzip/zutil.c
@@ -0,0 +1,181 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id: zutil.c,v 1.3 2006/04/29 07:31:16 wl Exp $ */
+
+#include "zutil.h"
+
+#ifndef STDC
+extern void exit OF((int));
+#endif
+
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+ Bytef* dest;
+ const Bytef* source;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = *source++; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+ const Bytef* s1;
+ const Bytef* s2;
+ uInt len;
+{
+ uInt j;
+
+ for (j = 0; j < len; j++) {
+ if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+ }
+ return 0;
+}
+
+void zmemzero(dest, len)
+ Bytef* dest;
+ uInt len;
+{
+ if (len == 0) return;
+ do {
+ *dest++ = 0; /* ??? to be unrolled */
+ } while (--len != 0);
+}
+#endif
+
+#ifdef __TURBOC__
+#if (defined( __BORLANDC__) || !defined(SMALL_MEDIUM)) && !defined(__32BIT__)
+/* Small and medium model in Turbo C are for now limited to near allocation
+ * with reduced MAX_WBITS and MAX_MEM_LEVEL
+ */
+# define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+ voidpf org_ptr;
+ voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ voidpf buf = opaque; /* just to make some compilers happy */
+ ulg bsize = (ulg)items*size;
+
+ /* If we allocate less than 65520 bytes, we assume that farmalloc
+ * will return a usable pointer which doesn't have to be normalized.
+ */
+ if (bsize < 65520L) {
+ buf = farmalloc(bsize);
+ if (*(ush*)&buf != 0) return buf;
+ } else {
+ buf = farmalloc(bsize + 16L);
+ }
+ if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+ table[next_ptr].org_ptr = buf;
+
+ /* Normalize the pointer to seg:0 */
+ *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+ *(ush*)&buf = 0;
+ table[next_ptr++].new_ptr = buf;
+ return buf;
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ int n;
+ if (*(ush*)&ptr != 0) { /* object < 64K */
+ farfree(ptr);
+ return;
+ }
+ /* Find the original pointer */
+ for (n = 0; n < next_ptr; n++) {
+ if (ptr != table[n].new_ptr) continue;
+
+ farfree(table[n].org_ptr);
+ while (++n < next_ptr) {
+ table[n-1] = table[n];
+ }
+ next_ptr--;
+ return;
+ }
+ ptr = opaque; /* just to make some compilers happy */
+ Assert(0, "zcfree: ptr not found");
+}
+#endif
+#endif /* __TURBOC__ */
+
+
+#if defined(M_I86) && !defined(__32BIT__)
+/* Microsoft C in 16-bit mode */
+
+# define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+# define _halloc halloc
+# define _hfree hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ return _halloc((long)items, size);
+}
+
+void zcfree (voidpf opaque, voidpf ptr)
+{
+ if (opaque) opaque = 0; /* to make compiler happy */
+ _hfree(ptr);
+}
+
+#endif /* MSC */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp ft_scalloc OF((uInt items, uInt size));
+extern void ft_sfree OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+ voidpf opaque;
+ unsigned items;
+ unsigned size;
+{
+ if (opaque) items += size - size; /* make compiler happy */
+ return (voidpf)ft_scalloc(items, size);
+}
+
+void zcfree (opaque, ptr)
+ voidpf opaque;
+ voidpf ptr;
+{
+ ft_sfree(ptr);
+ if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/src/gzip/zutil.h b/src/gzip/zutil.h
new file mode 100644
index 0000000..8e3c69a
--- /dev/null
+++ b/src/gzip/zutil.h
@@ -0,0 +1,215 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2002 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+ part of the implementation of the compression library and is
+ subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id: zutil.h,v 1.6 2007/06/01 06:56:17 wl Exp $ */
+
+#ifndef _Z_UTIL_H
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long ulg;
+
+
+#define ERR_RETURN(strm,err) \
+ return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+ /* common constants */
+
+#ifndef DEF_WBITS
+# define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES 2
+/* The three kinds of block type */
+
+#define MIN_MATCH 3
+#define MAX_MATCH 258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+ /* target dependencies */
+
+#ifdef MSDOS
+# define OS_CODE 0x00
+# if defined(__TURBOC__) || defined(__BORLANDC__)
+# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+ /* Allow compilation with ANSI keywords only enabled */
+ void _Cdecl farfree( void *block );
+ void *_Cdecl farmalloc( unsigned long nbytes );
+# else
+# include <alloc.h>
+# endif
+# else /* MSC or DJGPP */
+# endif
+#endif
+
+#ifdef OS2
+# define OS_CODE 0x06
+#endif
+
+#ifdef WIN32 /* Window 95 & Windows NT */
+# define OS_CODE 0x0b
+#endif
+
+#if defined(VAXC) || defined(VMS)
+# define OS_CODE 0x02
+# define F_OPEN(name, mode) \
+ ft_fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#ifdef AMIGA
+# define OS_CODE 0x01
+#endif
+
+#if defined(ATARI) || defined(atarist)
+# define OS_CODE 0x05
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+# define OS_CODE 0x07
+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+# include <unix.h> /* for fdopen */
+# else
+# ifndef fdopen
+# define fdopen(fd,mode) NULL /* No fdopen() */
+# endif
+# endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+# define OS_CODE 0x0F
+#endif
+
+#ifdef TOPS20
+# define OS_CODE 0x0a
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+# define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+# define fdopen(fd,type) _fdopen(fd,type)
+#endif
+
+
+ /* Common defaults */
+
+#ifndef OS_CODE
+# define OS_CODE 0x03 /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+# define F_OPEN(name, mode) ft_fopen((name), (mode))
+#endif
+
+ /* functions */
+
+#ifdef HAVE_STRERROR
+ extern char *strerror OF((int));
+# define zstrerror(errnum) strerror(errnum)
+#else
+# define zstrerror(errnum) ""
+#endif
+
+#if defined(pyr)
+# define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+ * You may have to use the same strategy for Borland C (untested).
+ * The __SC__ check is for Symantec.
+ */
+# define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+# define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+# define zmemcpy _fmemcpy
+# define zmemcmp _fmemcmp
+# define zmemzero(dest, len) _fmemset(dest, 0, len)
+# else
+# define zmemcpy ft_memcpy
+# define zmemcmp ft_memcmp
+# define zmemzero(dest, len) ft_memset(dest, 0, len)
+# endif
+#else
+ extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+ extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+ extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+# include <stdio.h>
+ extern int z_verbose;
+ extern void z_error OF((char *m));
+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+# define Trace(x) {if (z_verbose>=0) fprintf x ;}
+# define Tracev(x) {if (z_verbose>0) fprintf x ;}
+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+# define Assert(cond,msg)
+# define Trace(x)
+# define Tracev(x)
+# define Tracevv(x)
+# define Tracec(c,x)
+# define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, const Bytef *buf,
+ uInt len));
+local voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+local void zcfree OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+ (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* _Z_UTIL_H */
diff --git a/src/lzw/Jamfile b/src/lzw/Jamfile
new file mode 100644
index 0000000..6f1f516
--- /dev/null
+++ b/src/lzw/Jamfile
@@ -0,0 +1,16 @@
+# FreeType 2 src/lzw Jamfile
+#
+# Copyright 2004, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) lzw ;
+
+Library $(FT2_LIB) : ftlzw.c ;
+
+# end of src/lzw Jamfile
diff --git a/src/lzw/ftlzw.c b/src/lzw/ftlzw.c
new file mode 100644
index 0000000..45fbf7b
--- /dev/null
+++ b/src/lzw/ftlzw.c
@@ -0,0 +1,413 @@
+/***************************************************************************/
+/* */
+/* ftlzw.c */
+/* */
+/* FreeType support for .Z compressed files. */
+/* */
+/* This optional component relies on NetBSD's zopen(). It should mainly */
+/* be used to parse compressed PCF fonts, as found with many X11 server */
+/* distributions. */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* Albert Chin-A-Young. */
+/* */
+/* Based on code in src/gzip/ftgzip.c, Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_LZW_H
+#include <string.h>
+#include <stdio.h>
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX LZW_Err_
+#define FT_ERR_BASE FT_Mod_Err_LZW
+
+#include FT_ERRORS_H
+
+
+#ifdef FT_CONFIG_OPTION_USE_LZW
+
+#include "ftzopen.h"
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** M E M O R Y M A N A G E M E N T *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** F I L E D E S C R I P T O R *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+#define FT_LZW_BUFFER_SIZE 4096
+
+ typedef struct FT_LZWFileRec_
+ {
+ FT_Stream source; /* parent/source stream */
+ FT_Stream stream; /* embedding stream */
+ FT_Memory memory; /* memory allocator */
+ FT_LzwStateRec lzw; /* lzw decompressor state */
+
+ FT_Byte buffer[FT_LZW_BUFFER_SIZE]; /* output buffer */
+ FT_ULong pos; /* position in output */
+ FT_Byte* cursor;
+ FT_Byte* limit;
+
+ } FT_LZWFileRec, *FT_LZWFile;
+
+
+ /* check and skip .Z header */
+ static FT_Error
+ ft_lzw_check_header( FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Byte head[2];
+
+
+ if ( FT_STREAM_SEEK( 0 ) ||
+ FT_STREAM_READ( head, 2 ) )
+ goto Exit;
+
+ /* head[0] && head[1] are the magic numbers */
+ if ( head[0] != 0x1f ||
+ head[1] != 0x9d )
+ error = LZW_Err_Invalid_File_Format;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_init( FT_LZWFile zip,
+ FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_LzwState lzw = &zip->lzw;
+ FT_Error error = LZW_Err_Ok;
+
+
+ zip->stream = stream;
+ zip->source = source;
+ zip->memory = stream->memory;
+
+ zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+
+ /* check and skip .Z header */
+ {
+ stream = source;
+
+ error = ft_lzw_check_header( source );
+ if ( error )
+ goto Exit;
+ }
+
+ /* initialize internal lzw variable */
+ ft_lzwstate_init( lzw, source );
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ ft_lzw_file_done( FT_LZWFile zip )
+ {
+ /* clear the rest */
+ ft_lzwstate_done( &zip->lzw );
+
+ zip->memory = NULL;
+ zip->source = NULL;
+ zip->stream = NULL;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_reset( FT_LZWFile zip )
+ {
+ FT_Stream stream = zip->source;
+ FT_Error error;
+
+
+ if ( !FT_STREAM_SEEK( 0 ) )
+ {
+ ft_lzwstate_reset( &zip->lzw );
+
+ zip->limit = zip->buffer + FT_LZW_BUFFER_SIZE;
+ zip->cursor = zip->limit;
+ zip->pos = 0;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ ft_lzw_file_fill_output( FT_LZWFile zip )
+ {
+ FT_LzwState lzw = &zip->lzw;
+ FT_ULong count;
+ FT_Error error = 0;
+
+
+ zip->cursor = zip->buffer;
+
+ count = ft_lzwstate_io( lzw, zip->buffer, FT_LZW_BUFFER_SIZE );
+
+ zip->limit = zip->cursor + count;
+
+ if ( count == 0 )
+ error = LZW_Err_Invalid_Stream_Operation;
+
+ return error;
+ }
+
+
+ /* fill output buffer; `count' must be <= FT_LZW_BUFFER_SIZE */
+ static FT_Error
+ ft_lzw_file_skip_output( FT_LZWFile zip,
+ FT_ULong count )
+ {
+ FT_Error error = LZW_Err_Ok;
+
+
+ /* first, we skip what we can from the output buffer */
+ {
+ FT_ULong delta = (FT_ULong)( zip->limit - zip->cursor );
+
+
+ if ( delta >= count )
+ delta = count;
+
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ }
+
+ /* next, we skip as many bytes remaining as possible */
+ while ( count > 0 )
+ {
+ FT_ULong delta = FT_LZW_BUFFER_SIZE;
+ FT_ULong numread;
+
+
+ if ( delta > count )
+ delta = count;
+
+ numread = ft_lzwstate_io( &zip->lzw, NULL, delta );
+ if ( numread < delta )
+ {
+ /* not enough bytes */
+ error = LZW_Err_Invalid_Stream_Operation;
+ break;
+ }
+
+ zip->pos += delta;
+ count -= delta;
+ }
+
+ return error;
+ }
+
+
+ static FT_ULong
+ ft_lzw_file_io( FT_LZWFile zip,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_ULong result = 0;
+ FT_Error error;
+
+
+ /* seeking backwards. */
+ if ( pos < zip->pos )
+ {
+ /* If the new position is within the output buffer, simply */
+ /* decrement pointers, otherwise we reset the stream completely! */
+ if ( ( zip->pos - pos ) <= (FT_ULong)( zip->cursor - zip->buffer ) )
+ {
+ zip->cursor -= zip->pos - pos;
+ zip->pos = pos;
+ }
+ else
+ {
+ error = ft_lzw_file_reset( zip );
+ if ( error )
+ goto Exit;
+ }
+ }
+
+ /* skip unwanted bytes */
+ if ( pos > zip->pos )
+ {
+ error = ft_lzw_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( count == 0 )
+ goto Exit;
+
+ /* now read the data */
+ for (;;)
+ {
+ FT_ULong delta;
+
+
+ delta = (FT_ULong)( zip->limit - zip->cursor );
+ if ( delta >= count )
+ delta = count;
+
+ FT_MEM_COPY( buffer + result, zip->cursor, delta );
+ result += delta;
+ zip->cursor += delta;
+ zip->pos += delta;
+
+ count -= delta;
+ if ( count == 0 )
+ break;
+
+ error = ft_lzw_file_fill_output( zip );
+ if ( error )
+ break;
+ }
+
+ Exit:
+ return result;
+ }
+
+
+/***************************************************************************/
+/***************************************************************************/
+/***** *****/
+/***** L Z W E M B E D D I N G S T R E A M *****/
+/***** *****/
+/***************************************************************************/
+/***************************************************************************/
+
+ static void
+ ft_lzw_stream_close( FT_Stream stream )
+ {
+ FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer;
+ FT_Memory memory = stream->memory;
+
+
+ if ( zip )
+ {
+ /* finalize lzw file descriptor */
+ ft_lzw_file_done( zip );
+
+ FT_FREE( zip );
+
+ stream->descriptor.pointer = NULL;
+ }
+ }
+
+
+ static FT_ULong
+ ft_lzw_stream_io( FT_Stream stream,
+ FT_ULong pos,
+ FT_Byte* buffer,
+ FT_ULong count )
+ {
+ FT_LZWFile zip = (FT_LZWFile)stream->descriptor.pointer;
+
+
+ return ft_lzw_file_io( zip, pos, buffer, count );
+ }
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenLZW( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_Error error;
+ FT_Memory memory = source->memory;
+ FT_LZWFile zip;
+
+
+ /*
+ * Check the header right now; this prevents allocation of a huge
+ * LZWFile object (400 KByte of heap memory) if not necessary.
+ *
+ * Did I mention that you should never use .Z compressed font
+ * files?
+ */
+ error = ft_lzw_check_header( source );
+ if ( error )
+ goto Exit;
+
+ FT_ZERO( stream );
+ stream->memory = memory;
+
+ if ( !FT_NEW( zip ) )
+ {
+ error = ft_lzw_file_init( zip, stream, source );
+ if ( error )
+ {
+ FT_FREE( zip );
+ goto Exit;
+ }
+
+ stream->descriptor.pointer = zip;
+ }
+
+ stream->size = 0x7FFFFFFFL; /* don't know the real size! */
+ stream->pos = 0;
+ stream->base = 0;
+ stream->read = ft_lzw_stream_io;
+ stream->close = ft_lzw_stream_close;
+
+ Exit:
+ return error;
+ }
+
+
+#include "ftzopen.c"
+
+
+#else /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+ FT_EXPORT_DEF( FT_Error )
+ FT_Stream_OpenLZW( FT_Stream stream,
+ FT_Stream source )
+ {
+ FT_UNUSED( stream );
+ FT_UNUSED( source );
+
+ return LZW_Err_Unimplemented_Feature;
+ }
+
+
+#endif /* !FT_CONFIG_OPTION_USE_LZW */
+
+
+/* END */
diff --git a/src/lzw/ftzopen.c b/src/lzw/ftzopen.c
new file mode 100644
index 0000000..fc78315
--- /dev/null
+++ b/src/lzw/ftzopen.c
@@ -0,0 +1,398 @@
+/***************************************************************************/
+/* */
+/* ftzopen.c */
+/* */
+/* FreeType support for .Z compressed files. */
+/* */
+/* This optional component relies on NetBSD's zopen(). It should mainly */
+/* be used to parse compressed PCF fonts, as found with many X11 server */
+/* distributions. */
+/* */
+/* Copyright 2005, 2006, 2007 by David Turner. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include "ftzopen.h"
+#include FT_INTERNAL_MEMORY_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_DEBUG_H
+
+
+ static int
+ ft_lzwstate_refill( FT_LzwState state )
+ {
+ FT_ULong count;
+
+
+ if ( state->in_eof )
+ return -1;
+
+ count = FT_Stream_TryRead( state->source,
+ state->buf_tab,
+ state->num_bits ); /* WHY? */
+
+ state->buf_size = (FT_UInt)count;
+ state->buf_total += count;
+ state->in_eof = FT_BOOL( count < state->num_bits );
+ state->buf_offset = 0;
+ state->buf_size = ( state->buf_size << 3 ) - ( state->num_bits - 1 );
+
+ if ( count == 0 ) /* end of file */
+ return -1;
+
+ return 0;
+ }
+
+
+ static FT_Int32
+ ft_lzwstate_get_code( FT_LzwState state )
+ {
+ FT_UInt num_bits = state->num_bits;
+ FT_Int offset = state->buf_offset;
+ FT_Byte* p;
+ FT_Int result;
+
+
+ if ( state->buf_clear ||
+ offset >= state->buf_size ||
+ state->free_ent >= state->free_bits )
+ {
+ if ( state->free_ent >= state->free_bits )
+ {
+ state->num_bits = ++num_bits;
+ state->free_bits = state->num_bits < state->max_bits
+ ? (FT_UInt)( ( 1UL << num_bits ) - 256 )
+ : state->max_free + 1;
+ }
+
+ if ( state->buf_clear )
+ {
+ state->num_bits = num_bits = LZW_INIT_BITS;
+ state->free_bits = (FT_UInt)( ( 1UL << num_bits ) - 256 );
+ state->buf_clear = 0;
+ }
+
+ if ( ft_lzwstate_refill( state ) < 0 )
+ return -1;
+
+ offset = 0;
+ }
+
+ state->buf_offset = offset + num_bits;
+
+ p = &state->buf_tab[offset >> 3];
+ offset &= 7;
+ result = *p++ >> offset;
+ offset = 8 - offset;
+ num_bits -= offset;
+
+ if ( num_bits >= 8 )
+ {
+ result |= *p++ << offset;
+ offset += 8;
+ num_bits -= 8;
+ }
+ if ( num_bits > 0 )
+ result |= ( *p & LZW_MASK( num_bits ) ) << offset;
+
+ return result;
+ }
+
+
+ /* grow the character stack */
+ static int
+ ft_lzwstate_stack_grow( FT_LzwState state )
+ {
+ if ( state->stack_top >= state->stack_size )
+ {
+ FT_Memory memory = state->memory;
+ FT_Error error;
+ FT_UInt old_size = state->stack_size;
+ FT_UInt new_size = old_size;
+
+ new_size = new_size + ( new_size >> 1 ) + 4;
+
+ if ( state->stack == state->stack_0 )
+ {
+ state->stack = NULL;
+ old_size = 0;
+ }
+
+ if ( FT_RENEW_ARRAY( state->stack, old_size, new_size ) )
+ return -1;
+
+ state->stack_size = new_size;
+ }
+ return 0;
+ }
+
+
+ /* grow the prefix/suffix arrays */
+ static int
+ ft_lzwstate_prefix_grow( FT_LzwState state )
+ {
+ FT_UInt old_size = state->prefix_size;
+ FT_UInt new_size = old_size;
+ FT_Memory memory = state->memory;
+ FT_Error error;
+
+
+ if ( new_size == 0 ) /* first allocation -> 9 bits */
+ new_size = 512;
+ else
+ new_size += new_size >> 2; /* don't grow too fast */
+
+ /*
+ * Note that the `suffix' array is located in the same memory block
+ * pointed to by `prefix'.
+ *
+ * I know that sizeof(FT_Byte) == 1 by definition, but it is clearer
+ * to write it literally.
+ *
+ */
+ if ( FT_REALLOC_MULT( state->prefix, old_size, new_size,
+ sizeof ( FT_UShort ) + sizeof ( FT_Byte ) ) )
+ return -1;
+
+ /* now adjust `suffix' and move the data accordingly */
+ state->suffix = (FT_Byte*)( state->prefix + new_size );
+
+ FT_MEM_MOVE( state->suffix,
+ state->prefix + old_size,
+ old_size * sizeof ( FT_Byte ) );
+
+ state->prefix_size = new_size;
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ft_lzwstate_reset( FT_LzwState state )
+ {
+ state->in_eof = 0;
+ state->buf_offset = 0;
+ state->buf_size = 0;
+ state->buf_clear = 0;
+ state->buf_total = 0;
+ state->stack_top = 0;
+ state->num_bits = LZW_INIT_BITS;
+ state->phase = FT_LZW_PHASE_START;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ft_lzwstate_init( FT_LzwState state,
+ FT_Stream source )
+ {
+ FT_ZERO( state );
+
+ state->source = source;
+ state->memory = source->memory;
+
+ state->prefix = NULL;
+ state->suffix = NULL;
+ state->prefix_size = 0;
+
+ state->stack = state->stack_0;
+ state->stack_size = sizeof ( state->stack_0 );
+
+ ft_lzwstate_reset( state );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ft_lzwstate_done( FT_LzwState state )
+ {
+ FT_Memory memory = state->memory;
+
+
+ ft_lzwstate_reset( state );
+
+ if ( state->stack != state->stack_0 )
+ FT_FREE( state->stack );
+
+ FT_FREE( state->prefix );
+ state->suffix = NULL;
+
+ FT_ZERO( state );
+ }
+
+
+#define FTLZW_STACK_PUSH( c ) \
+ FT_BEGIN_STMNT \
+ if ( state->stack_top >= state->stack_size && \
+ ft_lzwstate_stack_grow( state ) < 0 ) \
+ goto Eof; \
+ \
+ state->stack[state->stack_top++] = (FT_Byte)(c); \
+ FT_END_STMNT
+
+
+ FT_LOCAL_DEF( FT_ULong )
+ ft_lzwstate_io( FT_LzwState state,
+ FT_Byte* buffer,
+ FT_ULong out_size )
+ {
+ FT_ULong result = 0;
+
+ FT_UInt old_char = state->old_char;
+ FT_UInt old_code = state->old_code;
+ FT_UInt in_code = state->in_code;
+
+
+ if ( out_size == 0 )
+ goto Exit;
+
+ switch ( state->phase )
+ {
+ case FT_LZW_PHASE_START:
+ {
+ FT_Byte max_bits;
+ FT_Int32 c;
+
+
+ /* skip magic bytes, and read max_bits + block_flag */
+ if ( FT_Stream_Seek( state->source, 2 ) != 0 ||
+ FT_Stream_TryRead( state->source, &max_bits, 1 ) != 1 )
+ goto Eof;
+
+ state->max_bits = max_bits & LZW_BIT_MASK;
+ state->block_mode = max_bits & LZW_BLOCK_MASK;
+ state->max_free = (FT_UInt)( ( 1UL << state->max_bits ) - 256 );
+
+ if ( state->max_bits > LZW_MAX_BITS )
+ goto Eof;
+
+ state->num_bits = LZW_INIT_BITS;
+ state->free_ent = ( state->block_mode ? LZW_FIRST
+ : LZW_CLEAR ) - 256;
+ in_code = 0;
+
+ state->free_bits = state->num_bits < state->max_bits
+ ? (FT_UInt)( ( 1UL << state->num_bits ) - 256 )
+ : state->max_free + 1;
+
+ c = ft_lzwstate_get_code( state );
+ if ( c < 0 )
+ goto Eof;
+
+ old_code = old_char = (FT_UInt)c;
+
+ if ( buffer )
+ buffer[result] = (FT_Byte)old_char;
+
+ if ( ++result >= out_size )
+ goto Exit;
+
+ state->phase = FT_LZW_PHASE_CODE;
+ }
+ /* fall-through */
+
+ case FT_LZW_PHASE_CODE:
+ {
+ FT_Int32 c;
+ FT_UInt code;
+
+
+ NextCode:
+ c = ft_lzwstate_get_code( state );
+ if ( c < 0 )
+ goto Eof;
+
+ code = (FT_UInt)c;
+
+ if ( code == LZW_CLEAR && state->block_mode )
+ {
+ /* why not LZW_FIRST-256 ? */
+ state->free_ent = ( LZW_FIRST - 1 ) - 256;
+ state->buf_clear = 1;
+ c = ft_lzwstate_get_code( state );
+ if ( c < 0 )
+ goto Eof;
+
+ code = (FT_UInt)c;
+ }
+
+ in_code = code; /* save code for later */
+
+ if ( code >= 256U )
+ {
+ /* special case for KwKwKwK */
+ if ( code - 256U >= state->free_ent )
+ {
+ FTLZW_STACK_PUSH( old_char );
+ code = old_code;
+ }
+
+ while ( code >= 256U )
+ {
+ FTLZW_STACK_PUSH( state->suffix[code - 256] );
+ code = state->prefix[code - 256];
+ }
+ }
+
+ old_char = code;
+ FTLZW_STACK_PUSH( old_char );
+
+ state->phase = FT_LZW_PHASE_STACK;
+ }
+ /* fall-through */
+
+ case FT_LZW_PHASE_STACK:
+ {
+ while ( state->stack_top > 0 )
+ {
+ --state->stack_top;
+
+ if ( buffer )
+ buffer[result] = state->stack[state->stack_top];
+
+ if ( ++result == out_size )
+ goto Exit;
+ }
+
+ /* now create new entry */
+ if ( state->free_ent < state->max_free )
+ {
+ if ( state->free_ent >= state->prefix_size &&
+ ft_lzwstate_prefix_grow( state ) < 0 )
+ goto Eof;
+
+ FT_ASSERT( state->free_ent < state->prefix_size );
+
+ state->prefix[state->free_ent] = (FT_UShort)old_code;
+ state->suffix[state->free_ent] = (FT_Byte) old_char;
+
+ state->free_ent += 1;
+ }
+
+ old_code = in_code;
+
+ state->phase = FT_LZW_PHASE_CODE;
+ goto NextCode;
+ }
+
+ default: /* state == EOF */
+ ;
+ }
+
+ Exit:
+ state->old_code = old_code;
+ state->old_char = old_char;
+ state->in_code = in_code;
+
+ return result;
+
+ Eof:
+ state->phase = FT_LZW_PHASE_EOF;
+ goto Exit;
+ }
+
+
+/* END */
diff --git a/src/lzw/ftzopen.h b/src/lzw/ftzopen.h
new file mode 100644
index 0000000..9788114
--- /dev/null
+++ b/src/lzw/ftzopen.h
@@ -0,0 +1,171 @@
+/***************************************************************************/
+/* */
+/* ftzopen.h */
+/* */
+/* FreeType support for .Z compressed files. */
+/* */
+/* This optional component relies on NetBSD's zopen(). It should mainly */
+/* be used to parse compressed PCF fonts, as found with many X11 server */
+/* distributions. */
+/* */
+/* Copyright 2005, 2006, 2007 by David Turner. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#ifndef __FT_ZOPEN_H__
+#define __FT_ZOPEN_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+ /*
+ * This is a complete re-implementation of the LZW file reader,
+ * since the old one was incredibly badly written, using
+ * 400 KByte of heap memory before decompressing anything.
+ *
+ */
+
+#define FT_LZW_IN_BUFF_SIZE 64
+#define FT_LZW_DEFAULT_STACK_SIZE 64
+
+#define LZW_INIT_BITS 9
+#define LZW_MAX_BITS 16
+
+#define LZW_CLEAR 256
+#define LZW_FIRST 257
+
+#define LZW_BIT_MASK 0x1f
+#define LZW_BLOCK_MASK 0x80
+#define LZW_MASK( n ) ( ( 1U << (n) ) - 1U )
+
+
+ typedef enum
+ {
+ FT_LZW_PHASE_START = 0,
+ FT_LZW_PHASE_CODE,
+ FT_LZW_PHASE_STACK,
+ FT_LZW_PHASE_EOF
+
+ } FT_LzwPhase;
+
+
+ /*
+ * state of LZW decompressor
+ *
+ * small technical note
+ * --------------------
+ *
+ * We use a few tricks in this implementation that are explained here to
+ * ease debugging and maintenance.
+ *
+ * - First of all, the `prefix' and `suffix' arrays contain the suffix
+ * and prefix for codes over 256; this means that
+ *
+ * prefix_of(code) == state->prefix[code-256]
+ * suffix_of(code) == state->suffix[code-256]
+ *
+ * Each prefix is a 16-bit code, and each suffix an 8-bit byte.
+ *
+ * Both arrays are stored in a single memory block, pointed to by
+ * `state->prefix'. This means that the following equality is always
+ * true:
+ *
+ * state->suffix == (FT_Byte*)(state->prefix + state->prefix_size)
+ *
+ * Of course, state->prefix_size is the number of prefix/suffix slots
+ * in the arrays, corresponding to codes 256..255+prefix_size.
+ *
+ * - `free_ent' is the index of the next free entry in the `prefix'
+ * and `suffix' arrays. This means that the corresponding `next free
+ * code' is really `256+free_ent'.
+ *
+ * Moreover, `max_free' is the maximum value that `free_ent' can reach.
+ *
+ * `max_free' corresponds to `(1 << max_bits) - 256'. Note that this
+ * value is always <= 0xFF00, which means that both `free_ent' and
+ * `max_free' can be stored in an FT_UInt variable, even on 16-bit
+ * machines.
+ *
+ * If `free_ent == max_free', you cannot add new codes to the
+ * prefix/suffix table.
+ *
+ * - `num_bits' is the current number of code bits, starting at 9 and
+ * growing each time `free_ent' reaches the value of `free_bits'. The
+ * latter is computed as follows
+ *
+ * if num_bits < max_bits:
+ * free_bits = (1 << num_bits)-256
+ * else:
+ * free_bits = max_free + 1
+ *
+ * Since the value of `max_free + 1' can never be reached by
+ * `free_ent', `num_bits' cannot grow larger than `max_bits'.
+ */
+
+ typedef struct _FT_LzwStateRec
+ {
+ FT_LzwPhase phase;
+ FT_Int in_eof;
+
+ FT_Byte buf_tab[16];
+ FT_Int buf_offset;
+ FT_Int buf_size;
+ FT_Bool buf_clear;
+ FT_Int buf_total;
+
+ FT_UInt max_bits; /* max code bits, from file header */
+ FT_Int block_mode; /* block mode flag, from file header */
+ FT_UInt max_free; /* (1 << max_bits) - 256 */
+
+ FT_UInt num_bits; /* current code bit number */
+ FT_UInt free_ent; /* index of next free entry */
+ FT_UInt free_bits; /* if reached by free_ent, increment num_bits */
+ FT_UInt old_code;
+ FT_UInt old_char;
+ FT_UInt in_code;
+
+ FT_UShort* prefix; /* always dynamically allocated / reallocated */
+ FT_Byte* suffix; /* suffix = (FT_Byte*)(prefix + prefix_size) */
+ FT_UInt prefix_size; /* number of slots in `prefix' or `suffix' */
+
+ FT_Byte* stack; /* character stack */
+ FT_UInt stack_top;
+ FT_UInt stack_size;
+ FT_Byte stack_0[FT_LZW_DEFAULT_STACK_SIZE]; /* minimize heap alloc */
+
+ FT_Stream source; /* source stream */
+ FT_Memory memory;
+
+ } FT_LzwStateRec, *FT_LzwState;
+
+
+ FT_LOCAL( void )
+ ft_lzwstate_init( FT_LzwState state,
+ FT_Stream source );
+
+ FT_LOCAL( void )
+ ft_lzwstate_done( FT_LzwState state );
+
+
+ FT_LOCAL( void )
+ ft_lzwstate_reset( FT_LzwState state );
+
+
+ FT_LOCAL( FT_ULong )
+ ft_lzwstate_io( FT_LzwState state,
+ FT_Byte* buffer,
+ FT_ULong out_size );
+
+/* */
+
+#endif /* __FT_ZOPEN_H__ */
+
+
+/* END */
diff --git a/src/lzw/rules.mk b/src/lzw/rules.mk
new file mode 100644
index 0000000..5550a48
--- /dev/null
+++ b/src/lzw/rules.mk
@@ -0,0 +1,70 @@
+#
+# FreeType 2 LZW support configuration rules
+#
+
+
+# Copyright 2004, 2005, 2006 by
+# Albert Chin-A-Young.
+#
+# Based on src/lzw/rules.mk, Copyright 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# LZW driver directory
+#
+LZW_DIR := $(SRC_DIR)/lzw
+
+
+# compilation flags for the driver
+#
+LZW_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(LZW_DIR))
+
+
+# LZW support sources (i.e., C files)
+#
+LZW_DRV_SRC := $(LZW_DIR)/ftlzw.c
+
+# LZW support headers
+#
+LZW_DRV_H := $(LZW_DIR)/ftzopen.h \
+ $(LZW_DIR)/ftzopen.c
+
+
+# LZW driver object(s)
+#
+# LZW_DRV_OBJ_M is used during `multi' builds
+# LZW_DRV_OBJ_S is used during `single' builds
+#
+LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw.$O
+LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw.$O
+
+# LZW support source file for single build
+#
+LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw.c
+
+
+# LZW support - single object
+#
+$(LZW_DRV_OBJ_S): $(LZW_DRV_SRC_S) $(LZW_DRV_SRC) $(FREETYPE_H) $(LZW_DRV_H)
+ $(LZW_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(LZW_DRV_SRC_S))
+
+
+# LZW support - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(LZW_DIR)/%.c $(FREETYPE_H) $(LZW_DRV_H)
+ $(LZW_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(LZW_DRV_OBJ_S)
+DRV_OBJS_M += $(LZW_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/otvalid/Jamfile b/src/otvalid/Jamfile
new file mode 100644
index 0000000..35a14c6
--- /dev/null
+++ b/src/otvalid/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/otvalid Jamfile
+#
+# Copyright 2004 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) otvalid ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = otvbase otvcommn otvgdef otvgpos otvgsub otvjstf otvmod ;
+ }
+ else
+ {
+ _sources = otvalid ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/otvalid Jamfile
diff --git a/src/otvalid/module.mk b/src/otvalid/module.mk
new file mode 100644
index 0000000..aa4db04
--- /dev/null
+++ b/src/otvalid/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 otvalid module definition
+#
+
+
+# Copyright 2004, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += OTVALID_MODULE
+
+define OTVALID_MODULE
+$(OPEN_DRIVER)otv_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)otvalid $(ECHO_DRIVER_DESC)OpenType validation module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/otvalid/otvalid.c b/src/otvalid/otvalid.c
new file mode 100644
index 0000000..2f85f60
--- /dev/null
+++ b/src/otvalid/otvalid.c
@@ -0,0 +1,30 @@
+/***************************************************************************/
+/* */
+/* otvalid.c */
+/* */
+/* FreeType validator for OpenType tables (body only). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+
+#include "otvbase.c"
+#include "otvcommn.c"
+#include "otvgdef.c"
+#include "otvgpos.c"
+#include "otvgsub.c"
+#include "otvjstf.c"
+#include "otvmod.c"
+
+/* END */
diff --git a/src/otvalid/otvalid.h b/src/otvalid/otvalid.h
new file mode 100644
index 0000000..38f030f
--- /dev/null
+++ b/src/otvalid/otvalid.h
@@ -0,0 +1,72 @@
+/***************************************************************************/
+/* */
+/* otvalid.h */
+/* */
+/* OpenType table validation (specification only). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __OTVALID_H__
+#define __OTVALID_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include "otverror.h" /* must come before FT_INTERNAL_VALIDATE_H */
+
+#include FT_INTERNAL_VALIDATE_H
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ otv_BASE_validate( FT_Bytes table,
+ FT_Validator valid );
+
+ /* GSUB and GPOS tables should already be validated; */
+ /* if missing, set corresponding argument to 0 */
+ FT_LOCAL( void )
+ otv_GDEF_validate( FT_Bytes table,
+ FT_Bytes gsub,
+ FT_Bytes gpos,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ otv_GPOS_validate( FT_Bytes table,
+ FT_UInt glyph_count,
+ FT_Validator valid );
+
+ FT_LOCAL( void )
+ otv_GSUB_validate( FT_Bytes table,
+ FT_UInt glyph_count,
+ FT_Validator valid );
+
+ /* GSUB and GPOS tables should already be validated; */
+ /* if missing, set corresponding argument to 0 */
+ FT_LOCAL( void )
+ otv_JSTF_validate( FT_Bytes table,
+ FT_Bytes gsub,
+ FT_Bytes gpos,
+ FT_UInt glyph_count,
+ FT_Validator valid );
+
+
+FT_END_HEADER
+
+#endif /* __OTVALID_H__ */
+
+
+/* END */
diff --git a/src/otvalid/otvbase.c b/src/otvalid/otvbase.c
new file mode 100644
index 0000000..8ad2238
--- /dev/null
+++ b/src/otvalid/otvbase.c
@@ -0,0 +1,318 @@
+/***************************************************************************/
+/* */
+/* otvbase.c */
+/* */
+/* OpenType BASE table validation (body). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvbase
+
+
+ static void
+ otv_BaseCoord_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BaseCoordFormat;
+
+
+ OTV_NAME_ENTER( "BaseCoord" );
+
+ OTV_LIMIT_CHECK( 4 );
+ BaseCoordFormat = FT_NEXT_USHORT( p );
+ p += 2; /* skip Coordinate */
+
+ OTV_TRACE(( " (format %d)\n", BaseCoordFormat ));
+
+ switch ( BaseCoordFormat )
+ {
+ case 1: /* BaseCoordFormat1 */
+ break;
+
+ case 2: /* BaseCoordFormat2 */
+ OTV_LIMIT_CHECK( 4 ); /* ReferenceGlyph, BaseCoordPoint */
+ break;
+
+ case 3: /* BaseCoordFormat3 */
+ OTV_LIMIT_CHECK( 2 );
+ /* DeviceTable */
+ otv_Device_validate( table + FT_NEXT_USHORT( p ), valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_BaseTagList_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BaseTagCount;
+
+
+ OTV_NAME_ENTER( "BaseTagList" );
+
+ OTV_LIMIT_CHECK( 2 );
+
+ BaseTagCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BaseTagCount = %d)\n", BaseTagCount ));
+
+ OTV_LIMIT_CHECK( BaseTagCount * 4 ); /* BaselineTag */
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_BaseValues_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BaseCoordCount;
+
+
+ OTV_NAME_ENTER( "BaseValues" );
+
+ OTV_LIMIT_CHECK( 4 );
+
+ p += 2; /* skip DefaultIndex */
+ BaseCoordCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BaseCoordCount = %d)\n", BaseCoordCount ));
+
+ OTV_LIMIT_CHECK( BaseCoordCount * 2 );
+
+ /* BaseCoord */
+ for ( ; BaseCoordCount > 0; BaseCoordCount-- )
+ otv_BaseCoord_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_MinMax_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UInt FeatMinMaxCount;
+
+ OTV_OPTIONAL_TABLE( MinCoord );
+ OTV_OPTIONAL_TABLE( MaxCoord );
+
+
+ OTV_NAME_ENTER( "MinMax" );
+
+ OTV_LIMIT_CHECK( 6 );
+
+ OTV_OPTIONAL_OFFSET( MinCoord );
+ OTV_OPTIONAL_OFFSET( MaxCoord );
+ FeatMinMaxCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (FeatMinMaxCount = %d)\n", FeatMinMaxCount ));
+
+ table_size = FeatMinMaxCount * 8 + 6;
+
+ OTV_SIZE_CHECK( MinCoord );
+ if ( MinCoord )
+ otv_BaseCoord_validate( table + MinCoord, valid );
+
+ OTV_SIZE_CHECK( MaxCoord );
+ if ( MaxCoord )
+ otv_BaseCoord_validate( table + MaxCoord, valid );
+
+ OTV_LIMIT_CHECK( FeatMinMaxCount * 8 );
+
+ /* FeatMinMaxRecord */
+ for ( ; FeatMinMaxCount > 0; FeatMinMaxCount-- )
+ {
+ p += 4; /* skip FeatureTableTag */
+
+ OTV_OPTIONAL_OFFSET( MinCoord );
+ OTV_OPTIONAL_OFFSET( MaxCoord );
+
+ OTV_SIZE_CHECK( MinCoord );
+ if ( MinCoord )
+ otv_BaseCoord_validate( table + MinCoord, valid );
+
+ OTV_SIZE_CHECK( MaxCoord );
+ if ( MaxCoord )
+ otv_BaseCoord_validate( table + MaxCoord, valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_BaseScript_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UInt BaseLangSysCount;
+
+ OTV_OPTIONAL_TABLE( BaseValues );
+ OTV_OPTIONAL_TABLE( DefaultMinMax );
+
+
+ OTV_NAME_ENTER( "BaseScript" );
+
+ OTV_LIMIT_CHECK( 6 );
+ OTV_OPTIONAL_OFFSET( BaseValues );
+ OTV_OPTIONAL_OFFSET( DefaultMinMax );
+ BaseLangSysCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BaseLangSysCount = %d)\n", BaseLangSysCount ));
+
+ table_size = BaseLangSysCount * 6 + 6;
+
+ OTV_SIZE_CHECK( BaseValues );
+ if ( BaseValues )
+ otv_BaseValues_validate( table + BaseValues, valid );
+
+ OTV_SIZE_CHECK( DefaultMinMax );
+ if ( DefaultMinMax )
+ otv_MinMax_validate( table + DefaultMinMax, valid );
+
+ OTV_LIMIT_CHECK( BaseLangSysCount * 6 );
+
+ /* BaseLangSysRecord */
+ for ( ; BaseLangSysCount > 0; BaseLangSysCount-- )
+ {
+ p += 4; /* skip BaseLangSysTag */
+
+ otv_MinMax_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_BaseScriptList_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BaseScriptCount;
+
+
+ OTV_NAME_ENTER( "BaseScriptList" );
+
+ OTV_LIMIT_CHECK( 2 );
+ BaseScriptCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BaseScriptCount = %d)\n", BaseScriptCount ));
+
+ OTV_LIMIT_CHECK( BaseScriptCount * 6 );
+
+ /* BaseScriptRecord */
+ for ( ; BaseScriptCount > 0; BaseScriptCount-- )
+ {
+ p += 4; /* skip BaseScriptTag */
+
+ /* BaseScript */
+ otv_BaseScript_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_Axis_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt table_size;
+
+ OTV_OPTIONAL_TABLE( BaseTagList );
+
+
+ OTV_NAME_ENTER( "Axis" );
+
+ OTV_LIMIT_CHECK( 4 );
+ OTV_OPTIONAL_OFFSET( BaseTagList );
+
+ table_size = 4;
+
+ OTV_SIZE_CHECK( BaseTagList );
+ if ( BaseTagList )
+ otv_BaseTagList_validate( table + BaseTagList, valid );
+
+ /* BaseScriptList */
+ otv_BaseScriptList_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ OTV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ otv_BASE_validate( FT_Bytes table,
+ FT_Validator ftvalid )
+ {
+ OTV_ValidatorRec validrec;
+ OTV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_UInt table_size;
+
+ OTV_OPTIONAL_TABLE( HorizAxis );
+ OTV_OPTIONAL_TABLE( VertAxis );
+
+
+ valid->root = ftvalid;
+
+ FT_TRACE3(( "validating BASE table\n" ));
+ OTV_INIT;
+
+ OTV_LIMIT_CHECK( 6 );
+
+ if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ FT_INVALID_DATA;
+
+ table_size = 6;
+
+ OTV_OPTIONAL_OFFSET( HorizAxis );
+ OTV_SIZE_CHECK( HorizAxis );
+ if ( HorizAxis )
+ otv_Axis_validate( table + HorizAxis, valid );
+
+ OTV_OPTIONAL_OFFSET( VertAxis );
+ OTV_SIZE_CHECK( VertAxis );
+ if ( VertAxis )
+ otv_Axis_validate( table + VertAxis, valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvcommn.c b/src/otvalid/otvcommn.c
new file mode 100644
index 0000000..d94e4f3
--- /dev/null
+++ b/src/otvalid/otvcommn.c
@@ -0,0 +1,1055 @@
+/***************************************************************************/
+/* */
+/* otvcommn.c */
+/* */
+/* OpenType common tables validation (body). */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvcommon
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** COVERAGE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ otv_Coverage_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt CoverageFormat;
+
+
+ OTV_NAME_ENTER( "Coverage" );
+
+ OTV_LIMIT_CHECK( 4 );
+ CoverageFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", CoverageFormat ));
+
+ switch ( CoverageFormat )
+ {
+ case 1: /* CoverageFormat1 */
+ {
+ FT_UInt GlyphCount;
+
+
+ GlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 ); /* GlyphArray */
+ }
+ break;
+
+ case 2: /* CoverageFormat2 */
+ {
+ FT_UInt n, RangeCount;
+ FT_UInt Start, End, StartCoverageIndex, total = 0, last = 0;
+
+
+ RangeCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (RangeCount = %d)\n", RangeCount ));
+
+ OTV_LIMIT_CHECK( RangeCount * 6 );
+
+ /* RangeRecord */
+ for ( n = 0; n < RangeCount; n++ )
+ {
+ Start = FT_NEXT_USHORT( p );
+ End = FT_NEXT_USHORT( p );
+ StartCoverageIndex = FT_NEXT_USHORT( p );
+
+ if ( Start > End || StartCoverageIndex != total )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && Start <= last )
+ FT_INVALID_DATA;
+
+ total += End - Start + 1;
+ last = End;
+ }
+ }
+ break;
+
+ default:
+ FT_INVALID_FORMAT;
+ }
+
+ /* no need to check glyph indices used as input to coverage tables */
+ /* since even invalid glyph indices return a meaningful result */
+
+ OTV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ otv_Coverage_get_first( FT_Bytes table )
+ {
+ FT_Bytes p = table;
+
+
+ p += 4; /* skip CoverageFormat and Glyph/RangeCount */
+
+ return FT_NEXT_USHORT( p );
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ otv_Coverage_get_last( FT_Bytes table )
+ {
+ FT_Bytes p = table;
+ FT_UInt CoverageFormat = FT_NEXT_USHORT( p );
+ FT_UInt count = FT_NEXT_USHORT( p ); /* Glyph/RangeCount */
+ FT_UInt result = 0;
+
+
+ switch ( CoverageFormat )
+ {
+ case 1:
+ p += ( count - 1 ) * 2;
+ result = FT_NEXT_USHORT( p );
+ break;
+
+ case 2:
+ p += ( count - 1 ) * 6 + 2;
+ result = FT_NEXT_USHORT( p );
+ break;
+
+ default:
+ ;
+ }
+
+ return result;
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ otv_Coverage_get_count( FT_Bytes table )
+ {
+ FT_Bytes p = table;
+ FT_UInt CoverageFormat = FT_NEXT_USHORT( p );
+ FT_UInt count = FT_NEXT_USHORT( p ); /* Glyph/RangeCount */
+ FT_UInt result = 0;
+
+
+ switch ( CoverageFormat )
+ {
+ case 1:
+ return count;
+
+ case 2:
+ {
+ FT_UInt Start, End;
+
+
+ for ( ; count > 0; count-- )
+ {
+ Start = FT_NEXT_USHORT( p );
+ End = FT_NEXT_USHORT( p );
+ p += 2; /* skip StartCoverageIndex */
+
+ result += End - Start + 1;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return result;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CLASS DEFINITION TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ otv_ClassDef_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt ClassFormat;
+
+
+ OTV_NAME_ENTER( "ClassDef" );
+
+ OTV_LIMIT_CHECK( 4 );
+ ClassFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", ClassFormat ));
+
+ switch ( ClassFormat )
+ {
+ case 1: /* ClassDefFormat1 */
+ {
+ FT_UInt GlyphCount;
+
+
+ p += 2; /* skip StartGlyph */
+
+ OTV_LIMIT_CHECK( 2 );
+
+ GlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 ); /* ClassValueArray */
+ }
+ break;
+
+ case 2: /* ClassDefFormat2 */
+ {
+ FT_UInt n, ClassRangeCount;
+ FT_UInt Start, End, last = 0;
+
+
+ ClassRangeCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ClassRangeCount = %d)\n", ClassRangeCount ));
+
+ OTV_LIMIT_CHECK( ClassRangeCount * 6 );
+
+ /* ClassRangeRecord */
+ for ( n = 0; n < ClassRangeCount; n++ )
+ {
+ Start = FT_NEXT_USHORT( p );
+ End = FT_NEXT_USHORT( p );
+ p += 2; /* skip Class */
+
+ if ( Start > End || ( n > 0 && Start <= last ) )
+ FT_INVALID_DATA;
+
+ last = End;
+ }
+ }
+ break;
+
+ default:
+ FT_INVALID_FORMAT;
+ }
+
+ /* no need to check glyph indices used as input to class definition */
+ /* tables since even invalid glyph indices return a meaningful result */
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** DEVICE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ otv_Device_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt StartSize, EndSize, DeltaFormat, count;
+
+
+ OTV_NAME_ENTER( "Device" );
+
+ OTV_LIMIT_CHECK( 8 );
+ StartSize = FT_NEXT_USHORT( p );
+ EndSize = FT_NEXT_USHORT( p );
+ DeltaFormat = FT_NEXT_USHORT( p );
+
+ if ( DeltaFormat < 1 || DeltaFormat > 3 || EndSize < StartSize )
+ FT_INVALID_DATA;
+
+ count = EndSize - StartSize + 1;
+ OTV_LIMIT_CHECK( ( 1 << DeltaFormat ) * count / 8 ); /* DeltaValue */
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LOOKUPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->type_count */
+ /* uses valid->type_funcs */
+
+ FT_LOCAL_DEF( void )
+ otv_Lookup_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt LookupType, SubTableCount;
+ OTV_Validate_Func validate;
+
+
+ OTV_NAME_ENTER( "Lookup" );
+
+ OTV_LIMIT_CHECK( 6 );
+ LookupType = FT_NEXT_USHORT( p );
+ p += 2; /* skip LookupFlag */
+ SubTableCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (type %d)\n", LookupType ));
+
+ if ( LookupType == 0 || LookupType >= valid->type_count )
+ FT_INVALID_DATA;
+
+ validate = valid->type_funcs[LookupType - 1];
+
+ OTV_TRACE(( " (SubTableCount = %d)\n", SubTableCount ));
+
+ OTV_LIMIT_CHECK( SubTableCount * 2 );
+
+ /* SubTable */
+ for ( ; SubTableCount > 0; SubTableCount-- )
+ validate( table + FT_NEXT_USHORT( p ), valid );
+
+ OTV_EXIT;
+ }
+
+
+ /* uses valid->lookup_count */
+
+ FT_LOCAL_DEF( void )
+ otv_LookupList_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt LookupCount;
+
+
+ OTV_NAME_ENTER( "LookupList" );
+
+ OTV_LIMIT_CHECK( 2 );
+ LookupCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LookupCount = %d)\n", LookupCount ));
+
+ OTV_LIMIT_CHECK( LookupCount * 2 );
+
+ valid->lookup_count = LookupCount;
+
+ /* Lookup */
+ for ( ; LookupCount > 0; LookupCount-- )
+ otv_Lookup_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ OTV_EXIT;
+ }
+
+
+ static FT_UInt
+ otv_LookupList_get_count( FT_Bytes table )
+ {
+ return FT_NEXT_USHORT( table );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FEATURES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->lookup_count */
+
+ FT_LOCAL_DEF( void )
+ otv_Feature_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt LookupCount;
+
+
+ OTV_NAME_ENTER( "Feature" );
+
+ OTV_LIMIT_CHECK( 4 );
+ p += 2; /* skip FeatureParams (unused) */
+ LookupCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LookupCount = %d)\n", LookupCount ));
+
+ OTV_LIMIT_CHECK( LookupCount * 2 );
+
+ /* LookupListIndex */
+ for ( ; LookupCount > 0; LookupCount-- )
+ if ( FT_NEXT_USHORT( p ) >= valid->lookup_count )
+ FT_INVALID_DATA;
+
+ OTV_EXIT;
+ }
+
+
+ static FT_UInt
+ otv_Feature_get_count( FT_Bytes table )
+ {
+ return FT_NEXT_USHORT( table );
+ }
+
+
+ /* sets valid->lookup_count */
+
+ FT_LOCAL_DEF( void )
+ otv_FeatureList_validate( FT_Bytes table,
+ FT_Bytes lookups,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt FeatureCount;
+
+
+ OTV_NAME_ENTER( "FeatureList" );
+
+ OTV_LIMIT_CHECK( 2 );
+ FeatureCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (FeatureCount = %d)\n", FeatureCount ));
+
+ OTV_LIMIT_CHECK( FeatureCount * 2 );
+
+ valid->lookup_count = otv_LookupList_get_count( lookups );
+
+ /* FeatureRecord */
+ for ( ; FeatureCount > 0; FeatureCount-- )
+ {
+ p += 4; /* skip FeatureTag */
+
+ /* Feature */
+ otv_Feature_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LANGUAGE SYSTEM *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* uses valid->extra1 (number of features) */
+
+ FT_LOCAL_DEF( void )
+ otv_LangSys_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt ReqFeatureIndex;
+ FT_UInt FeatureCount;
+
+
+ OTV_NAME_ENTER( "LangSys" );
+
+ OTV_LIMIT_CHECK( 6 );
+ p += 2; /* skip LookupOrder (unused) */
+ ReqFeatureIndex = FT_NEXT_USHORT( p );
+ FeatureCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ReqFeatureIndex = %d)\n", ReqFeatureIndex ));
+ OTV_TRACE(( " (FeatureCount = %d)\n", FeatureCount ));
+
+ if ( ReqFeatureIndex != 0xFFFFU && ReqFeatureIndex >= valid->extra1 )
+ FT_INVALID_DATA;
+
+ OTV_LIMIT_CHECK( FeatureCount * 2 );
+
+ /* FeatureIndex */
+ for ( ; FeatureCount > 0; FeatureCount-- )
+ if ( FT_NEXT_USHORT( p ) >= valid->extra1 )
+ FT_INVALID_DATA;
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SCRIPTS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ otv_Script_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_UInt DefaultLangSys, LangSysCount;
+ FT_Bytes p = table;
+
+
+ OTV_NAME_ENTER( "Script" );
+
+ OTV_LIMIT_CHECK( 4 );
+ DefaultLangSys = FT_NEXT_USHORT( p );
+ LangSysCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LangSysCount = %d)\n", LangSysCount ));
+
+ if ( DefaultLangSys != 0 )
+ otv_LangSys_validate( table + DefaultLangSys, valid );
+
+ OTV_LIMIT_CHECK( LangSysCount * 6 );
+
+ /* LangSysRecord */
+ for ( ; LangSysCount > 0; LangSysCount-- )
+ {
+ p += 4; /* skip LangSysTag */
+
+ /* LangSys */
+ otv_LangSys_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra1 (number of features) */
+
+ FT_LOCAL_DEF( void )
+ otv_ScriptList_validate( FT_Bytes table,
+ FT_Bytes features,
+ OTV_Validator valid )
+ {
+ FT_UInt ScriptCount;
+ FT_Bytes p = table;
+
+
+ OTV_NAME_ENTER( "ScriptList" );
+
+ OTV_LIMIT_CHECK( 2 );
+ ScriptCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ScriptCount = %d)\n", ScriptCount ));
+
+ OTV_LIMIT_CHECK( ScriptCount * 6 );
+
+ valid->extra1 = otv_Feature_get_count( features );
+
+ /* ScriptRecord */
+ for ( ; ScriptCount > 0; ScriptCount-- )
+ {
+ p += 4; /* skip ScriptTag */
+
+ otv_Script_validate( table + FT_NEXT_USHORT( p ), valid ); /* Script */
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*
+ u: uint16
+ ux: unit16 [x]
+
+ s: struct
+ sx: struct [x]
+ sxy: struct [x], using external y count
+
+ x: uint16 x
+
+ C: Coverage
+
+ O: Offset
+ On: Offset (NULL)
+ Ox: Offset [x]
+ Onx: Offset (NULL) [x]
+ */
+
+ FT_LOCAL_DEF( void )
+ otv_x_Ox( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Count;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 2 );
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ OTV_LIMIT_CHECK( Count * 2 );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+
+ for ( ; Count > 0; Count-- )
+ func( table + FT_NEXT_USHORT( p ), valid );
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ otv_u_C_x_Ox( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Count, Coverage;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = FT_NEXT_USHORT( p );
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ OTV_LIMIT_CHECK( Count * 2 );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+
+ for ( ; Count > 0; Count-- )
+ func( table + FT_NEXT_USHORT( p ), valid );
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ /* uses valid->extra1 (if > 0: array value limit) */
+
+ FT_LOCAL_DEF( void )
+ otv_x_ux( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Count;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 2 );
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ OTV_LIMIT_CHECK( Count * 2 );
+
+ if ( valid->extra1 )
+ {
+ for ( ; Count > 0; Count-- )
+ if ( FT_NEXT_USHORT( p ) >= valid->extra1 )
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* `ux' in the function's name is not really correct since only x-1 */
+ /* elements are tested */
+
+ /* uses valid->extra1 (array value limit) */
+
+ FT_LOCAL_DEF( void )
+ otv_x_y_ux_sy( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Count1, Count2;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 4 );
+ Count1 = FT_NEXT_USHORT( p );
+ Count2 = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count1 = %d)\n", Count1 ));
+ OTV_TRACE(( " (Count2 = %d)\n", Count2 ));
+
+ if ( Count1 == 0 )
+ FT_INVALID_DATA;
+
+ OTV_LIMIT_CHECK( ( Count1 - 1 ) * 2 + Count2 * 4 );
+
+ for ( ; Count2 > 0; Count2-- )
+ {
+ if ( FT_NEXT_USHORT( p ) >= Count1 )
+ FT_INVALID_DATA;
+
+ if ( FT_NEXT_USHORT( p ) >= valid->extra1 )
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* `uy' in the function's name is not really correct since only y-1 */
+ /* elements are tested */
+
+ /* uses valid->extra1 (array value limit) */
+
+ FT_LOCAL_DEF( void )
+ otv_x_ux_y_uy_z_uz_p_sp( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BacktrackCount, InputCount, LookaheadCount;
+ FT_UInt Count;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 2 );
+ BacktrackCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BacktrackCount = %d)\n", BacktrackCount ));
+
+ OTV_LIMIT_CHECK( BacktrackCount * 2 + 2 );
+ p += BacktrackCount * 2;
+
+ InputCount = FT_NEXT_USHORT( p );
+ if ( InputCount == 0 )
+ FT_INVALID_DATA;
+
+ OTV_TRACE(( " (InputCount = %d)\n", InputCount ));
+
+ OTV_LIMIT_CHECK( InputCount * 2 );
+ p += ( InputCount - 1 ) * 2;
+
+ LookaheadCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LookaheadCount = %d)\n", LookaheadCount ));
+
+ OTV_LIMIT_CHECK( LookaheadCount * 2 + 2 );
+ p += LookaheadCount * 2;
+
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ OTV_LIMIT_CHECK( Count * 4 );
+
+ for ( ; Count > 0; Count-- )
+ {
+ if ( FT_NEXT_USHORT( p ) >= InputCount )
+ FT_INVALID_DATA;
+
+ if ( FT_NEXT_USHORT( p ) >= valid->extra1 )
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra1 (valid->lookup_count) */
+
+ FT_LOCAL_DEF( void )
+ otv_u_O_O_x_Onx( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Coverage, ClassDef, ClassSetCount;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 6 );
+ Coverage = FT_NEXT_USHORT( p );
+ ClassDef = FT_NEXT_USHORT( p );
+ ClassSetCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ClassSetCount = %d)\n", ClassSetCount ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+ otv_ClassDef_validate( table + ClassDef, valid );
+
+ OTV_LIMIT_CHECK( ClassSetCount * 2 );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+ valid->extra1 = valid->lookup_count;
+
+ for ( ; ClassSetCount > 0; ClassSetCount-- )
+ {
+ FT_UInt offset = FT_NEXT_USHORT( p );
+
+
+ if ( offset )
+ func( table + offset, valid );
+ }
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ /* uses valid->lookup_count */
+
+ FT_LOCAL_DEF( void )
+ otv_u_x_y_Ox_sy( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt GlyphCount, Count, count1;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 4 );
+ GlyphCount = FT_NEXT_USHORT( p );
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 + Count * 4 );
+
+ for ( count1 = GlyphCount; count1 > 0; count1-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ for ( ; Count > 0; Count-- )
+ {
+ if ( FT_NEXT_USHORT( p ) >= GlyphCount )
+ FT_INVALID_DATA;
+
+ if ( FT_NEXT_USHORT( p ) >= valid->lookup_count )
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra1 (valid->lookup_count) */
+
+ FT_LOCAL_DEF( void )
+ otv_u_O_O_O_O_x_Onx( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Coverage;
+ FT_UInt BacktrackClassDef, InputClassDef, LookaheadClassDef;
+ FT_UInt ChainClassSetCount;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 10 );
+ Coverage = FT_NEXT_USHORT( p );
+ BacktrackClassDef = FT_NEXT_USHORT( p );
+ InputClassDef = FT_NEXT_USHORT( p );
+ LookaheadClassDef = FT_NEXT_USHORT( p );
+ ChainClassSetCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ChainClassSetCount = %d)\n", ChainClassSetCount ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ otv_ClassDef_validate( table + BacktrackClassDef, valid );
+ otv_ClassDef_validate( table + InputClassDef, valid );
+ otv_ClassDef_validate( table + LookaheadClassDef, valid );
+
+ OTV_LIMIT_CHECK( ChainClassSetCount * 2 );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+ valid->extra1 = valid->lookup_count;
+
+ for ( ; ChainClassSetCount > 0; ChainClassSetCount-- )
+ {
+ FT_UInt offset = FT_NEXT_USHORT( p );
+
+
+ if ( offset )
+ func( table + offset, valid );
+ }
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ /* uses valid->lookup_count */
+
+ FT_LOCAL_DEF( void )
+ otv_u_x_Ox_y_Oy_z_Oz_p_sp( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt BacktrackGlyphCount, InputGlyphCount, LookaheadGlyphCount;
+ FT_UInt count1, count2;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip Format */
+
+ OTV_LIMIT_CHECK( 2 );
+ BacktrackGlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount ));
+
+ OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 );
+
+ for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ InputGlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (InputGlyphCount = %d)\n", InputGlyphCount ));
+
+ OTV_LIMIT_CHECK( InputGlyphCount * 2 + 2 );
+
+ for ( count1 = InputGlyphCount; count1 > 0; count1-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ LookaheadGlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount ));
+
+ OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 );
+
+ for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ count2 = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", count2 ));
+
+ OTV_LIMIT_CHECK( count2 * 4 );
+
+ for ( ; count2 > 0; count2-- )
+ {
+ if ( FT_NEXT_USHORT( p ) >= InputGlyphCount )
+ FT_INVALID_DATA;
+
+ if ( FT_NEXT_USHORT( p ) >= valid->lookup_count )
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ otv_GSUBGPOS_get_Lookup_count( FT_Bytes table )
+ {
+ FT_Bytes p = table + 8;
+
+
+ return otv_LookupList_get_count( table + FT_NEXT_USHORT( p ) );
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( FT_Bytes table )
+ {
+ FT_Bytes p, lookup;
+ FT_UInt count;
+
+
+ if ( !table )
+ return 0;
+
+ /* LookupList */
+ p = table + 8;
+ table += FT_NEXT_USHORT( p );
+
+ /* LookupCount */
+ p = table;
+ count = FT_NEXT_USHORT( p );
+
+ for ( ; count > 0; count-- )
+ {
+ FT_Bytes oldp;
+
+
+ /* Lookup */
+ lookup = table + FT_NEXT_USHORT( p );
+
+ oldp = p;
+
+ /* LookupFlag */
+ p = lookup + 2;
+ if ( FT_NEXT_USHORT( p ) & 0xFF00U )
+ return 1;
+
+ p = oldp;
+ }
+
+ return 0;
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvcommn.h b/src/otvalid/otvcommn.h
new file mode 100644
index 0000000..be6ac69
--- /dev/null
+++ b/src/otvalid/otvcommn.h
@@ -0,0 +1,436 @@
+/***************************************************************************/
+/* */
+/* otvcommn.h */
+/* */
+/* OpenType common tables validation (specification). */
+/* */
+/* Copyright 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __OTVCOMMN_H__
+#define __OTVCOMMN_H__
+
+
+#include <ft2build.h>
+#include "otvalid.h"
+#include FT_INTERNAL_DEBUG_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** VALIDATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct OTV_ValidatorRec_* OTV_Validator;
+
+ typedef void (*OTV_Validate_Func)( FT_Bytes table,
+ OTV_Validator valid );
+
+ typedef struct OTV_ValidatorRec_
+ {
+ FT_Validator root;
+ FT_UInt type_count;
+ OTV_Validate_Func* type_funcs;
+
+ FT_UInt lookup_count;
+ FT_UInt glyph_count;
+
+ FT_UInt nesting_level;
+
+ OTV_Validate_Func func[3];
+
+ FT_UInt extra1; /* for passing parameters */
+ FT_UInt extra2;
+ FT_Bytes extra3;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_UInt debug_indent;
+ const FT_String* debug_function_name[3];
+#endif
+
+ } OTV_ValidatorRec;
+
+
+#undef FT_INVALID_
+#define FT_INVALID_( _prefix, _error ) \
+ ft_validator_error( valid->root, _prefix ## _error )
+
+#define OTV_OPTIONAL_TABLE( _table ) FT_UShort _table; \
+ FT_Bytes _table ## _p
+
+#define OTV_OPTIONAL_OFFSET( _offset ) \
+ FT_BEGIN_STMNT \
+ _offset ## _p = p; \
+ _offset = FT_NEXT_USHORT( p ); \
+ FT_END_STMNT
+
+#define OTV_LIMIT_CHECK( _count ) \
+ FT_BEGIN_STMNT \
+ if ( p + (_count) > valid->root->limit ) \
+ FT_INVALID_TOO_SHORT; \
+ FT_END_STMNT
+
+#define OTV_SIZE_CHECK( _size ) \
+ FT_BEGIN_STMNT \
+ if ( _size > 0 && _size < table_size ) \
+ { \
+ if ( valid->root->level == FT_VALIDATE_PARANOID ) \
+ FT_INVALID_OFFSET; \
+ else \
+ { \
+ /* strip off `const' */ \
+ FT_Byte* pp = (FT_Byte*)_size ## _p; \
+ \
+ \
+ FT_TRACE3(( "\n" \
+ "Invalid offset to optional table `%s'!\n" \
+ "Set to zero.\n" \
+ "\n", #_size )); \
+ \
+ /* always assume 16bit entities */ \
+ _size = pp[0] = pp[1] = 0; \
+ } \
+ } \
+ FT_END_STMNT
+
+
+#define OTV_NAME_(x) #x
+#define OTV_NAME(x) OTV_NAME_(x)
+
+#define OTV_FUNC_(x) x##Func
+#define OTV_FUNC(x) OTV_FUNC_(x)
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+#define OTV_NEST1( x ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ valid->debug_function_name[0] = OTV_NAME( x ); \
+ FT_END_STMNT
+
+#define OTV_NEST2( x, y ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ valid->func[1] = OTV_FUNC( y ); \
+ valid->debug_function_name[0] = OTV_NAME( x ); \
+ valid->debug_function_name[1] = OTV_NAME( y ); \
+ FT_END_STMNT
+
+#define OTV_NEST3( x, y, z ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ valid->func[1] = OTV_FUNC( y ); \
+ valid->func[2] = OTV_FUNC( z ); \
+ valid->debug_function_name[0] = OTV_NAME( x ); \
+ valid->debug_function_name[1] = OTV_NAME( y ); \
+ valid->debug_function_name[2] = OTV_NAME( z ); \
+ FT_END_STMNT
+
+#define OTV_INIT valid->debug_indent = 0
+
+#define OTV_ENTER \
+ FT_BEGIN_STMNT \
+ valid->debug_indent += 2; \
+ FT_TRACE4(( "%*.s", valid->debug_indent, 0 )); \
+ FT_TRACE4(( "%s table\n", \
+ valid->debug_function_name[valid->nesting_level] )); \
+ FT_END_STMNT
+
+#define OTV_NAME_ENTER( name ) \
+ FT_BEGIN_STMNT \
+ valid->debug_indent += 2; \
+ FT_TRACE4(( "%*.s", valid->debug_indent, 0 )); \
+ FT_TRACE4(( "%s table\n", name )); \
+ FT_END_STMNT
+
+#define OTV_EXIT valid->debug_indent -= 2
+
+#define OTV_TRACE( s ) \
+ FT_BEGIN_STMNT \
+ FT_TRACE4(( "%*.s", valid->debug_indent, 0 )); \
+ FT_TRACE4( s ); \
+ FT_END_STMNT
+
+#else /* !FT_DEBUG_LEVEL_TRACE */
+
+#define OTV_NEST1( x ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ FT_END_STMNT
+
+#define OTV_NEST2( x, y ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ valid->func[1] = OTV_FUNC( y ); \
+ FT_END_STMNT
+
+#define OTV_NEST3( x, y, z ) \
+ FT_BEGIN_STMNT \
+ valid->nesting_level = 0; \
+ valid->func[0] = OTV_FUNC( x ); \
+ valid->func[1] = OTV_FUNC( y ); \
+ valid->func[2] = OTV_FUNC( z ); \
+ FT_END_STMNT
+
+#define OTV_INIT do ; while ( 0 )
+#define OTV_ENTER do ; while ( 0 )
+#define OTV_NAME_ENTER( name ) do ; while ( 0 )
+#define OTV_EXIT do ; while ( 0 )
+
+#define OTV_TRACE( s ) do ; while ( 0 )
+
+#endif /* !FT_DEBUG_LEVEL_TRACE */
+
+
+#define OTV_RUN valid->func[0]
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** COVERAGE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_Coverage_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+ /* return first covered glyph */
+ FT_LOCAL( FT_UInt )
+ otv_Coverage_get_first( FT_Bytes table );
+
+ /* return last covered glyph */
+ FT_LOCAL( FT_UInt )
+ otv_Coverage_get_last( FT_Bytes table );
+
+ /* return number of covered glyphs */
+ FT_LOCAL( FT_UInt )
+ otv_Coverage_get_count( FT_Bytes table );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** CLASS DEFINITION TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_ClassDef_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** DEVICE TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_Device_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LOOKUPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_Lookup_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+ FT_LOCAL( void )
+ otv_LookupList_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FEATURES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_Feature_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+ /* lookups must already be validated */
+ FT_LOCAL( void )
+ otv_FeatureList_validate( FT_Bytes table,
+ FT_Bytes lookups,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LANGUAGE SYSTEM *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_LangSys_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SCRIPTS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ otv_Script_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+ /* features must already be validated */
+ FT_LOCAL( void )
+ otv_ScriptList_validate( FT_Bytes table,
+ FT_Bytes features,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define ChainPosClassSetFunc otv_x_Ox
+#define ChainPosRuleSetFunc otv_x_Ox
+#define ChainSubClassSetFunc otv_x_Ox
+#define ChainSubRuleSetFunc otv_x_Ox
+#define JstfLangSysFunc otv_x_Ox
+#define JstfMaxFunc otv_x_Ox
+#define LigGlyphFunc otv_x_Ox
+#define LigatureArrayFunc otv_x_Ox
+#define LigatureSetFunc otv_x_Ox
+#define PosClassSetFunc otv_x_Ox
+#define PosRuleSetFunc otv_x_Ox
+#define SubClassSetFunc otv_x_Ox
+#define SubRuleSetFunc otv_x_Ox
+
+ FT_LOCAL( void )
+ otv_x_Ox ( FT_Bytes table,
+ OTV_Validator valid );
+
+#define AlternateSubstFormat1Func otv_u_C_x_Ox
+#define ChainContextPosFormat1Func otv_u_C_x_Ox
+#define ChainContextSubstFormat1Func otv_u_C_x_Ox
+#define ContextPosFormat1Func otv_u_C_x_Ox
+#define ContextSubstFormat1Func otv_u_C_x_Ox
+#define LigatureSubstFormat1Func otv_u_C_x_Ox
+#define MultipleSubstFormat1Func otv_u_C_x_Ox
+
+ FT_LOCAL( void )
+ otv_u_C_x_Ox( FT_Bytes table,
+ OTV_Validator valid );
+
+#define AlternateSetFunc otv_x_ux
+#define AttachPointFunc otv_x_ux
+#define ExtenderGlyphFunc otv_x_ux
+#define JstfGPOSModListFunc otv_x_ux
+#define JstfGSUBModListFunc otv_x_ux
+#define SequenceFunc otv_x_ux
+
+ FT_LOCAL( void )
+ otv_x_ux( FT_Bytes table,
+ OTV_Validator valid );
+
+#define PosClassRuleFunc otv_x_y_ux_sy
+#define PosRuleFunc otv_x_y_ux_sy
+#define SubClassRuleFunc otv_x_y_ux_sy
+#define SubRuleFunc otv_x_y_ux_sy
+
+ FT_LOCAL( void )
+ otv_x_y_ux_sy( FT_Bytes table,
+ OTV_Validator valid );
+
+#define ChainPosClassRuleFunc otv_x_ux_y_uy_z_uz_p_sp
+#define ChainPosRuleFunc otv_x_ux_y_uy_z_uz_p_sp
+#define ChainSubClassRuleFunc otv_x_ux_y_uy_z_uz_p_sp
+#define ChainSubRuleFunc otv_x_ux_y_uy_z_uz_p_sp
+
+ FT_LOCAL( void )
+ otv_x_ux_y_uy_z_uz_p_sp( FT_Bytes table,
+ OTV_Validator valid );
+
+#define ContextPosFormat2Func otv_u_O_O_x_Onx
+#define ContextSubstFormat2Func otv_u_O_O_x_Onx
+
+ FT_LOCAL( void )
+ otv_u_O_O_x_Onx( FT_Bytes table,
+ OTV_Validator valid );
+
+#define ContextPosFormat3Func otv_u_x_y_Ox_sy
+#define ContextSubstFormat3Func otv_u_x_y_Ox_sy
+
+ FT_LOCAL( void )
+ otv_u_x_y_Ox_sy( FT_Bytes table,
+ OTV_Validator valid );
+
+#define ChainContextPosFormat2Func otv_u_O_O_O_O_x_Onx
+#define ChainContextSubstFormat2Func otv_u_O_O_O_O_x_Onx
+
+ FT_LOCAL( void )
+ otv_u_O_O_O_O_x_Onx( FT_Bytes table,
+ OTV_Validator valid );
+
+#define ChainContextPosFormat3Func otv_u_x_Ox_y_Oy_z_Oz_p_sp
+#define ChainContextSubstFormat3Func otv_u_x_Ox_y_Oy_z_Oz_p_sp
+
+ FT_LOCAL( void )
+ otv_u_x_Ox_y_Oy_z_Oz_p_sp( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ FT_LOCAL( FT_UInt )
+ otv_GSUBGPOS_get_Lookup_count( FT_Bytes table );
+
+ FT_LOCAL( FT_UInt )
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( FT_Bytes table );
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __OTVCOMMN_H__ */
+
+
+/* END */
diff --git a/src/otvalid/otverror.h b/src/otvalid/otverror.h
new file mode 100644
index 0000000..041b538
--- /dev/null
+++ b/src/otvalid/otverror.h
@@ -0,0 +1,43 @@
+/***************************************************************************/
+/* */
+/* otverror.h */
+/* */
+/* OpenType validation module error codes (specification only). */
+/* */
+/* Copyright 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the OpenType validation module error */
+ /* enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __OTVERROR_H__
+#define __OTVERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX OTV_Err_
+#define FT_ERR_BASE FT_Mod_Err_OTvalid
+
+#define FT_KEEP_ERR_PREFIX
+
+#include FT_ERRORS_H
+
+#endif /* __OTVERROR_H__ */
+
+
+/* END */
diff --git a/src/otvalid/otvgdef.c b/src/otvalid/otvgdef.c
new file mode 100644
index 0000000..7d24902
--- /dev/null
+++ b/src/otvalid/otvgdef.c
@@ -0,0 +1,219 @@
+/***************************************************************************/
+/* */
+/* otvgdef.c */
+/* */
+/* OpenType GDEF table validation (body). */
+/* */
+/* Copyright 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvgdef
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define AttachListFunc otv_O_x_Ox
+#define LigCaretListFunc otv_O_x_Ox
+
+ /* sets valid->extra1 (0) */
+
+ static void
+ otv_O_x_Ox( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_Bytes Coverage;
+ FT_UInt GlyphCount;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = table + FT_NEXT_USHORT( p );
+ GlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+
+ otv_Coverage_validate( Coverage, valid );
+ if ( GlyphCount != otv_Coverage_get_count( Coverage ) )
+ FT_INVALID_DATA;
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+ valid->extra1 = 0;
+
+ for ( ; GlyphCount > 0; GlyphCount-- )
+ func( table + FT_NEXT_USHORT( p ), valid );
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** LIGATURE CARETS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define CaretValueFunc otv_CaretValue_validate
+
+ static void
+ otv_CaretValue_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt CaretValueFormat;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 4 );
+
+ CaretValueFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format = %d)\n", CaretValueFormat ));
+
+ switch ( CaretValueFormat )
+ {
+ case 1: /* CaretValueFormat1 */
+ /* skip Coordinate, no test */
+ break;
+
+ case 2: /* CaretValueFormat2 */
+ /* skip CaretValuePoint, no test */
+ break;
+
+ case 3: /* CaretValueFormat3 */
+ p += 2; /* skip Coordinate */
+
+ OTV_LIMIT_CHECK( 2 );
+
+ /* DeviceTable */
+ otv_Device_validate( table + FT_NEXT_USHORT( p ), valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GDEF TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ otv_GDEF_validate( FT_Bytes table,
+ FT_Bytes gsub,
+ FT_Bytes gpos,
+ FT_Validator ftvalid )
+ {
+ OTV_ValidatorRec validrec;
+ OTV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_Bool need_MarkAttachClassDef;
+
+ OTV_OPTIONAL_TABLE( GlyphClassDef );
+ OTV_OPTIONAL_TABLE( AttachListOffset );
+ OTV_OPTIONAL_TABLE( LigCaretListOffset );
+ OTV_OPTIONAL_TABLE( MarkAttachClassDef );
+
+
+ valid->root = ftvalid;
+
+ FT_TRACE3(( "validating GDEF table\n" ));
+ OTV_INIT;
+
+ OTV_LIMIT_CHECK( 12 );
+
+ if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ FT_INVALID_FORMAT;
+
+ /* MarkAttachClassDef has been added to the OpenType */
+ /* specification without increasing GDEF's version, */
+ /* so we use this ugly hack to find out whether the */
+ /* table is needed actually. */
+
+ need_MarkAttachClassDef = FT_BOOL(
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( gsub ) ||
+ otv_GSUBGPOS_have_MarkAttachmentType_flag( gpos ) );
+
+ if ( need_MarkAttachClassDef )
+ table_size = 12; /* OpenType >= 1.2 */
+ else
+ table_size = 10; /* OpenType < 1.2 */
+
+ OTV_OPTIONAL_OFFSET( GlyphClassDef );
+ OTV_SIZE_CHECK( GlyphClassDef );
+ if ( GlyphClassDef )
+ otv_ClassDef_validate( table + GlyphClassDef, valid );
+
+ OTV_OPTIONAL_OFFSET( AttachListOffset );
+ OTV_SIZE_CHECK( AttachListOffset );
+ if ( AttachListOffset )
+ {
+ OTV_NEST2( AttachList, AttachPoint );
+ OTV_RUN( table + AttachListOffset, valid );
+ }
+
+ OTV_OPTIONAL_OFFSET( LigCaretListOffset );
+ OTV_SIZE_CHECK( LigCaretListOffset );
+ if ( LigCaretListOffset )
+ {
+ OTV_NEST3( LigCaretList, LigGlyph, CaretValue );
+ OTV_RUN( table + LigCaretListOffset, valid );
+ }
+
+ if ( need_MarkAttachClassDef )
+ {
+ OTV_OPTIONAL_OFFSET( MarkAttachClassDef );
+ OTV_SIZE_CHECK( MarkAttachClassDef );
+ if ( MarkAttachClassDef )
+ otv_ClassDef_validate( table + MarkAttachClassDef, valid );
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvgpos.c b/src/otvalid/otvgpos.c
new file mode 100644
index 0000000..ed34705
--- /dev/null
+++ b/src/otvalid/otvgpos.c
@@ -0,0 +1,1013 @@
+/***************************************************************************/
+/* */
+/* otvgpos.c */
+/* */
+/* OpenType GPOS table validation (body). */
+/* */
+/* Copyright 2002, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+#include "otvgpos.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvgpos
+
+
+ static void
+ otv_Anchor_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+ static void
+ otv_MarkArray_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** UTILITY FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define BaseArrayFunc otv_x_sxy
+#define LigatureAttachFunc otv_x_sxy
+#define Mark2ArrayFunc otv_x_sxy
+
+ /* uses valid->extra1 (counter) */
+ /* uses valid->extra2 (boolean to handle NULL anchor field) */
+
+ static void
+ otv_x_sxy( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Count, count1, table_size;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 2 );
+
+ Count = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (Count = %d)\n", Count ));
+
+ OTV_LIMIT_CHECK( Count * valid->extra1 * 2 );
+
+ table_size = Count * valid->extra1 * 2 + 2;
+
+ for ( ; Count > 0; Count-- )
+ for ( count1 = valid->extra1; count1 > 0; count1-- )
+ {
+ OTV_OPTIONAL_TABLE( anchor_offset );
+
+
+ OTV_OPTIONAL_OFFSET( anchor_offset );
+
+ if ( valid->extra2 )
+ {
+ OTV_SIZE_CHECK( anchor_offset );
+ if ( anchor_offset )
+ otv_Anchor_validate( table + anchor_offset, valid );
+ }
+ else
+ otv_Anchor_validate( table + anchor_offset, valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+#define MarkBasePosFormat1Func otv_u_O_O_u_O_O
+#define MarkLigPosFormat1Func otv_u_O_O_u_O_O
+#define MarkMarkPosFormat1Func otv_u_O_O_u_O_O
+
+ /* sets valid->extra1 (class count) */
+
+ static void
+ otv_u_O_O_u_O_O( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt Coverage1, Coverage2, ClassCount;
+ FT_UInt Array1, Array2;
+ OTV_Validate_Func func;
+
+
+ OTV_ENTER;
+
+ p += 2; /* skip PosFormat */
+
+ OTV_LIMIT_CHECK( 10 );
+ Coverage1 = FT_NEXT_USHORT( p );
+ Coverage2 = FT_NEXT_USHORT( p );
+ ClassCount = FT_NEXT_USHORT( p );
+ Array1 = FT_NEXT_USHORT( p );
+ Array2 = FT_NEXT_USHORT( p );
+
+ otv_Coverage_validate( table + Coverage1, valid );
+ otv_Coverage_validate( table + Coverage2, valid );
+
+ otv_MarkArray_validate( table + Array1, valid );
+
+ valid->nesting_level++;
+ func = valid->func[valid->nesting_level];
+ valid->extra1 = ClassCount;
+
+ func( table + Array2, valid );
+
+ valid->nesting_level--;
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** VALUE RECORDS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_UInt
+ otv_value_length( FT_UInt format )
+ {
+ FT_UInt count;
+
+
+ count = ( ( format & 0xAA ) >> 1 ) + ( format & 0x55 );
+ count = ( ( count & 0xCC ) >> 2 ) + ( count & 0x33 );
+ count = ( ( count & 0xF0 ) >> 4 ) + ( count & 0x0F );
+
+ return count * 2;
+ }
+
+
+ /* uses valid->extra3 (pointer to base table) */
+
+ static void
+ otv_ValueRecord_validate( FT_Bytes table,
+ FT_UInt format,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt count;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+ FT_Int loop;
+ FT_ULong res = 0;
+
+
+ OTV_NAME_ENTER( "ValueRecord" );
+
+ /* display `format' in dual representation */
+ for ( loop = 7; loop >= 0; loop-- )
+ {
+ res <<= 4;
+ res += ( format >> loop ) & 1;
+ }
+
+ OTV_TRACE(( " (format 0b%08lx)\n", res ));
+#endif
+
+ if ( format >= 0x100 )
+ FT_INVALID_DATA;
+
+ for ( count = 4; count > 0; count-- )
+ {
+ if ( format & 1 )
+ {
+ /* XPlacement, YPlacement, XAdvance, YAdvance */
+ OTV_LIMIT_CHECK( 2 );
+ p += 2;
+ }
+
+ format >>= 1;
+ }
+
+ for ( count = 4; count > 0; count-- )
+ {
+ if ( format & 1 )
+ {
+ FT_UInt table_size;
+
+ OTV_OPTIONAL_TABLE( device );
+
+
+ /* XPlaDevice, YPlaDevice, XAdvDevice, YAdvDevice */
+ OTV_LIMIT_CHECK( 2 );
+ OTV_OPTIONAL_OFFSET( device );
+
+ /* XXX: this value is usually too small, especially if the current */
+ /* ValueRecord is part of an array -- getting the correct table */
+ /* size is probably not worth the trouble */
+
+ table_size = p - valid->extra3;
+
+ OTV_SIZE_CHECK( device );
+ if ( device )
+ otv_Device_validate( valid->extra3 + device, valid );
+ }
+ format >>= 1;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** ANCHORS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ otv_Anchor_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt AnchorFormat;
+
+
+ OTV_NAME_ENTER( "Anchor");
+
+ OTV_LIMIT_CHECK( 6 );
+ AnchorFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", AnchorFormat ));
+
+ p += 4; /* skip XCoordinate and YCoordinate */
+
+ switch ( AnchorFormat )
+ {
+ case 1:
+ break;
+
+ case 2:
+ OTV_LIMIT_CHECK( 2 ); /* AnchorPoint */
+ break;
+
+ case 3:
+ {
+ FT_UInt table_size;
+
+ OTV_OPTIONAL_TABLE( XDeviceTable );
+ OTV_OPTIONAL_TABLE( YDeviceTable );
+
+
+ OTV_LIMIT_CHECK( 4 );
+ OTV_OPTIONAL_OFFSET( XDeviceTable );
+ OTV_OPTIONAL_OFFSET( YDeviceTable );
+
+ table_size = 6 + 4;
+
+ OTV_SIZE_CHECK( XDeviceTable );
+ if ( XDeviceTable )
+ otv_Device_validate( table + XDeviceTable, valid );
+
+ OTV_SIZE_CHECK( YDeviceTable );
+ if ( YDeviceTable )
+ otv_Device_validate( table + YDeviceTable, valid );
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** MARK ARRAYS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ otv_MarkArray_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt MarkCount;
+
+
+ OTV_NAME_ENTER( "MarkArray" );
+
+ OTV_LIMIT_CHECK( 2 );
+ MarkCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (MarkCount = %d)\n", MarkCount ));
+
+ OTV_LIMIT_CHECK( MarkCount * 4 );
+
+ /* MarkRecord */
+ for ( ; MarkCount > 0; MarkCount-- )
+ {
+ p += 2; /* skip Class */
+ /* MarkAnchor */
+ otv_Anchor_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 1 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra3 (pointer to base table) */
+
+ static void
+ otv_SinglePos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "SinglePos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ valid->extra3 = table;
+
+ switch ( PosFormat )
+ {
+ case 1: /* SinglePosFormat1 */
+ {
+ FT_UInt Coverage, ValueFormat;
+
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = FT_NEXT_USHORT( p );
+ ValueFormat = FT_NEXT_USHORT( p );
+
+ otv_Coverage_validate( table + Coverage, valid );
+ otv_ValueRecord_validate( p, ValueFormat, valid ); /* Value */
+ }
+ break;
+
+ case 2: /* SinglePosFormat2 */
+ {
+ FT_UInt Coverage, ValueFormat, ValueCount, len_value;
+
+
+ OTV_LIMIT_CHECK( 6 );
+ Coverage = FT_NEXT_USHORT( p );
+ ValueFormat = FT_NEXT_USHORT( p );
+ ValueCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ValueCount = %d)\n", ValueCount ));
+
+ len_value = otv_value_length( ValueFormat );
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ OTV_LIMIT_CHECK( ValueCount * len_value );
+
+ /* Value */
+ for ( ; ValueCount > 0; ValueCount-- )
+ {
+ otv_ValueRecord_validate( p, ValueFormat, valid );
+ p += len_value;
+ }
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 2 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ otv_PairSet_validate( FT_Bytes table,
+ FT_UInt format1,
+ FT_UInt format2,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt value_len1, value_len2, PairValueCount;
+
+
+ OTV_NAME_ENTER( "PairSet" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PairValueCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (PairValueCount = %d)\n", PairValueCount ));
+
+ value_len1 = otv_value_length( format1 );
+ value_len2 = otv_value_length( format2 );
+
+ OTV_LIMIT_CHECK( PairValueCount * ( value_len1 + value_len2 + 2 ) );
+
+ /* PairValueRecord */
+ for ( ; PairValueCount > 0; PairValueCount-- )
+ {
+ p += 2; /* skip SecondGlyph */
+
+ if ( format1 )
+ otv_ValueRecord_validate( p, format1, valid ); /* Value1 */
+ p += value_len1;
+
+ if ( format2 )
+ otv_ValueRecord_validate( p, format2, valid ); /* Value2 */
+ p += value_len2;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra3 (pointer to base table) */
+
+ static void
+ otv_PairPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "PairPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ valid->extra3 = table;
+
+ switch ( PosFormat )
+ {
+ case 1: /* PairPosFormat1 */
+ {
+ FT_UInt Coverage, ValueFormat1, ValueFormat2, PairSetCount;
+
+
+ OTV_LIMIT_CHECK( 8 );
+ Coverage = FT_NEXT_USHORT( p );
+ ValueFormat1 = FT_NEXT_USHORT( p );
+ ValueFormat2 = FT_NEXT_USHORT( p );
+ PairSetCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (PairSetCount = %d)\n", PairSetCount ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ OTV_LIMIT_CHECK( PairSetCount * 2 );
+
+ /* PairSetOffset */
+ for ( ; PairSetCount > 0; PairSetCount-- )
+ otv_PairSet_validate( table + FT_NEXT_USHORT( p ),
+ ValueFormat1, ValueFormat2, valid );
+ }
+ break;
+
+ case 2: /* PairPosFormat2 */
+ {
+ FT_UInt Coverage, ValueFormat1, ValueFormat2, ClassDef1, ClassDef2;
+ FT_UInt ClassCount1, ClassCount2, len_value1, len_value2, count;
+
+
+ OTV_LIMIT_CHECK( 14 );
+ Coverage = FT_NEXT_USHORT( p );
+ ValueFormat1 = FT_NEXT_USHORT( p );
+ ValueFormat2 = FT_NEXT_USHORT( p );
+ ClassDef1 = FT_NEXT_USHORT( p );
+ ClassDef2 = FT_NEXT_USHORT( p );
+ ClassCount1 = FT_NEXT_USHORT( p );
+ ClassCount2 = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (ClassCount1 = %d)\n", ClassCount1 ));
+ OTV_TRACE(( " (ClassCount2 = %d)\n", ClassCount2 ));
+
+ len_value1 = otv_value_length( ValueFormat1 );
+ len_value2 = otv_value_length( ValueFormat2 );
+
+ otv_Coverage_validate( table + Coverage, valid );
+ otv_ClassDef_validate( table + ClassDef1, valid );
+ otv_ClassDef_validate( table + ClassDef2, valid );
+
+ OTV_LIMIT_CHECK( ClassCount1 * ClassCount2 *
+ ( len_value1 + len_value2 ) );
+
+ /* Class1Record */
+ for ( ; ClassCount1 > 0; ClassCount1-- )
+ {
+ /* Class2Record */
+ for ( count = ClassCount2; count > 0; count-- )
+ {
+ if ( ValueFormat1 )
+ /* Value1 */
+ otv_ValueRecord_validate( p, ValueFormat1, valid );
+ p += len_value1;
+
+ if ( ValueFormat2 )
+ /* Value2 */
+ otv_ValueRecord_validate( p, ValueFormat2, valid );
+ p += len_value2;
+ }
+ }
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 3 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ otv_CursivePos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "CursivePos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1: /* CursivePosFormat1 */
+ {
+ FT_UInt table_size;
+ FT_UInt Coverage, EntryExitCount;
+
+ OTV_OPTIONAL_TABLE( EntryAnchor );
+ OTV_OPTIONAL_TABLE( ExitAnchor );
+
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = FT_NEXT_USHORT( p );
+ EntryExitCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (EntryExitCount = %d)\n", EntryExitCount ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ OTV_LIMIT_CHECK( EntryExitCount * 4 );
+
+ table_size = EntryExitCount * 4 + 4;
+
+ /* EntryExitRecord */
+ for ( ; EntryExitCount > 0; EntryExitCount-- )
+ {
+ OTV_OPTIONAL_OFFSET( EntryAnchor );
+ OTV_OPTIONAL_OFFSET( ExitAnchor );
+
+ OTV_SIZE_CHECK( EntryAnchor );
+ if ( EntryAnchor )
+ otv_Anchor_validate( table + EntryAnchor, valid );
+
+ OTV_SIZE_CHECK( ExitAnchor );
+ if ( ExitAnchor )
+ otv_Anchor_validate( table + ExitAnchor, valid );
+ }
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 4 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra2 (0) */
+
+ static void
+ otv_MarkBasePos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "MarkBasePos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1:
+ valid->extra2 = 0;
+ OTV_NEST2( MarkBasePosFormat1, BaseArray );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 5 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra2 (1) */
+
+ static void
+ otv_MarkLigPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "MarkLigPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1:
+ valid->extra2 = 1;
+ OTV_NEST3( MarkLigPosFormat1, LigatureArray, LigatureAttach );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 6 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra2 (0) */
+
+ static void
+ otv_MarkMarkPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "MarkMarkPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1:
+ valid->extra2 = 0;
+ OTV_NEST2( MarkMarkPosFormat1, Mark2Array );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 7 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (lookup count) */
+
+ static void
+ otv_ContextPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "ContextPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ valid->extra1 = valid->lookup_count;
+ OTV_NEST3( ContextPosFormat1, PosRuleSet, PosRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 2:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ OTV_NEST3( ContextPosFormat2, PosClassSet, PosClassRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 3:
+ OTV_NEST1( ContextPosFormat3 );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 8 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (lookup count) */
+
+ static void
+ otv_ChainContextPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "ChainContextPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ valid->extra1 = valid->lookup_count;
+ OTV_NEST3( ChainContextPosFormat1,
+ ChainPosRuleSet, ChainPosRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 2:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ OTV_NEST3( ChainContextPosFormat2,
+ ChainPosClassSet, ChainPosClassRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 3:
+ OTV_NEST1( ChainContextPosFormat3 );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS LOOKUP TYPE 9 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->type_funcs */
+
+ static void
+ otv_ExtensionPos_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt PosFormat;
+
+
+ OTV_NAME_ENTER( "ExtensionPos" );
+
+ OTV_LIMIT_CHECK( 2 );
+ PosFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", PosFormat ));
+
+ switch ( PosFormat )
+ {
+ case 1: /* ExtensionPosFormat1 */
+ {
+ FT_UInt ExtensionLookupType, ExtensionOffset;
+ OTV_Validate_Func validate;
+
+
+ OTV_LIMIT_CHECK( 6 );
+ ExtensionLookupType = FT_NEXT_USHORT( p );
+ ExtensionOffset = FT_NEXT_ULONG( p );
+
+ if ( ExtensionLookupType == 0 || ExtensionLookupType >= 9 )
+ FT_INVALID_DATA;
+
+ validate = valid->type_funcs[ExtensionLookupType - 1];
+ validate( table + ExtensionOffset, valid );
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static const OTV_Validate_Func otv_gpos_validate_funcs[9] =
+ {
+ otv_SinglePos_validate,
+ otv_PairPos_validate,
+ otv_CursivePos_validate,
+ otv_MarkBasePos_validate,
+ otv_MarkLigPos_validate,
+ otv_MarkMarkPos_validate,
+ otv_ContextPos_validate,
+ otv_ChainContextPos_validate,
+ otv_ExtensionPos_validate
+ };
+
+
+ /* sets valid->type_count */
+ /* sets valid->type_funcs */
+
+ FT_LOCAL_DEF( void )
+ otv_GPOS_subtable_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ valid->type_count = 9;
+ valid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs;
+
+ otv_Lookup_validate( table, valid );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GPOS TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->glyph_count */
+
+ FT_LOCAL_DEF( void )
+ otv_GPOS_validate( FT_Bytes table,
+ FT_UInt glyph_count,
+ FT_Validator ftvalid )
+ {
+ OTV_ValidatorRec validrec;
+ OTV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_UInt ScriptList, FeatureList, LookupList;
+
+
+ valid->root = ftvalid;
+
+ FT_TRACE3(( "validating GPOS table\n" ));
+ OTV_INIT;
+
+ OTV_LIMIT_CHECK( 10 );
+
+ if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ FT_INVALID_DATA;
+
+ ScriptList = FT_NEXT_USHORT( p );
+ FeatureList = FT_NEXT_USHORT( p );
+ LookupList = FT_NEXT_USHORT( p );
+
+ valid->type_count = 9;
+ valid->type_funcs = (OTV_Validate_Func*)otv_gpos_validate_funcs;
+ valid->glyph_count = glyph_count;
+
+ otv_LookupList_validate( table + LookupList,
+ valid );
+ otv_FeatureList_validate( table + FeatureList, table + LookupList,
+ valid );
+ otv_ScriptList_validate( table + ScriptList, table + FeatureList,
+ valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvgpos.h b/src/otvalid/otvgpos.h
new file mode 100644
index 0000000..14ca408
--- /dev/null
+++ b/src/otvalid/otvgpos.h
@@ -0,0 +1,36 @@
+/***************************************************************************/
+/* */
+/* otvgpos.h */
+/* */
+/* OpenType GPOS table validator (specification). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __OTVGPOS_H__
+#define __OTVGPOS_H__
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ otv_GPOS_subtable_validate( FT_Bytes table,
+ OTV_Validator valid );
+
+
+FT_END_HEADER
+
+#endif /* __OTVGPOS_H__ */
+
+
+/* END */
diff --git a/src/otvalid/otvgsub.c b/src/otvalid/otvgsub.c
new file mode 100644
index 0000000..91dae0b
--- /dev/null
+++ b/src/otvalid/otvgsub.c
@@ -0,0 +1,584 @@
+/***************************************************************************/
+/* */
+/* otvgsub.c */
+/* */
+/* OpenType GSUB table validation (body). */
+/* */
+/* Copyright 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvgsub
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 1 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->glyph_count */
+
+ static void
+ otv_SingleSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "SingleSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1: /* SingleSubstFormat1 */
+ {
+ FT_Bytes Coverage;
+ FT_Int DeltaGlyphID;
+ FT_Long idx;
+
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = table + FT_NEXT_USHORT( p );
+ DeltaGlyphID = FT_NEXT_SHORT( p );
+
+ otv_Coverage_validate( Coverage, valid );
+
+ idx = otv_Coverage_get_first( Coverage ) + DeltaGlyphID;
+ if ( idx < 0 )
+ FT_INVALID_DATA;
+
+ idx = otv_Coverage_get_last( Coverage ) + DeltaGlyphID;
+ if ( (FT_UInt)idx >= valid->glyph_count )
+ FT_INVALID_DATA;
+ }
+ break;
+
+ case 2: /* SingleSubstFormat2 */
+ {
+ FT_UInt Coverage, GlyphCount;
+
+
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = FT_NEXT_USHORT( p );
+ GlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+
+ otv_Coverage_validate( table + Coverage, valid );
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 );
+
+ /* Substitute */
+ for ( ; GlyphCount > 0; GlyphCount-- )
+ if ( FT_NEXT_USHORT( p ) >= valid->glyph_count )
+ FT_INVALID_DATA;
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 2 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (glyph count) */
+
+ static void
+ otv_MultipleSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "MultipleSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1:
+ valid->extra1 = valid->glyph_count;
+ OTV_NEST2( MultipleSubstFormat1, Sequence );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 3 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (glyph count) */
+
+ static void
+ otv_AlternateSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "AlternateSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1:
+ valid->extra1 = valid->glyph_count;
+ OTV_NEST2( AlternateSubstFormat1, AlternateSet );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 4 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define LigatureFunc otv_Ligature_validate
+
+ /* uses valid->glyph_count */
+
+ static void
+ otv_Ligature_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt LigatureGlyph, CompCount;
+
+
+ OTV_ENTER;
+
+ OTV_LIMIT_CHECK( 4 );
+ LigatureGlyph = FT_NEXT_USHORT( p );
+ if ( LigatureGlyph >= valid->glyph_count )
+ FT_INVALID_DATA;
+
+ CompCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (CompCount = %d)\n", CompCount ));
+
+ if ( CompCount == 0 )
+ FT_INVALID_DATA;
+
+ CompCount--;
+
+ OTV_LIMIT_CHECK( CompCount * 2 ); /* Component */
+
+ /* no need to check the Component glyph indices */
+
+ OTV_EXIT;
+ }
+
+
+ static void
+ otv_LigatureSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "LigatureSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1:
+ OTV_NEST3( LigatureSubstFormat1, LigatureSet, Ligature );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 5 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (lookup count) */
+
+ static void
+ otv_ContextSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "ContextSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ valid->extra1 = valid->lookup_count;
+ OTV_NEST3( ContextSubstFormat1, SubRuleSet, SubRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 2:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ OTV_NEST3( ContextSubstFormat2, SubClassSet, SubClassRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 3:
+ OTV_NEST1( ContextSubstFormat3 );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 6 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->extra1 (lookup count) */
+
+ static void
+ otv_ChainContextSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "ChainContextSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ valid->extra1 = valid->lookup_count;
+ OTV_NEST3( ChainContextSubstFormat1,
+ ChainSubRuleSet, ChainSubRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 2:
+ /* no need to check glyph indices/classes used as input for these */
+ /* context rules since even invalid glyph indices/classes return */
+ /* meaningful results */
+
+ OTV_NEST3( ChainContextSubstFormat2,
+ ChainSubClassSet, ChainSubClassRule );
+ OTV_RUN( table, valid );
+ break;
+
+ case 3:
+ OTV_NEST1( ChainContextSubstFormat3 );
+ OTV_RUN( table, valid );
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 7 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->type_funcs */
+
+ static void
+ otv_ExtensionSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt SubstFormat;
+
+
+ OTV_NAME_ENTER( "ExtensionSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1: /* ExtensionSubstFormat1 */
+ {
+ FT_UInt ExtensionLookupType, ExtensionOffset;
+ OTV_Validate_Func validate;
+
+
+ OTV_LIMIT_CHECK( 6 );
+ ExtensionLookupType = FT_NEXT_USHORT( p );
+ ExtensionOffset = FT_NEXT_ULONG( p );
+
+ if ( ExtensionLookupType == 0 ||
+ ExtensionLookupType == 7 ||
+ ExtensionLookupType > 8 )
+ FT_INVALID_DATA;
+
+ validate = valid->type_funcs[ExtensionLookupType - 1];
+ validate( table + ExtensionOffset, valid );
+ }
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB LOOKUP TYPE 8 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* uses valid->glyph_count */
+
+ static void
+ otv_ReverseChainSingleSubst_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table, Coverage;
+ FT_UInt SubstFormat;
+ FT_UInt BacktrackGlyphCount, LookaheadGlyphCount, GlyphCount;
+
+
+ OTV_NAME_ENTER( "ReverseChainSingleSubst" );
+
+ OTV_LIMIT_CHECK( 2 );
+ SubstFormat = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (format %d)\n", SubstFormat ));
+
+ switch ( SubstFormat )
+ {
+ case 1: /* ReverseChainSingleSubstFormat1 */
+ OTV_LIMIT_CHECK( 4 );
+ Coverage = table + FT_NEXT_USHORT( p );
+ BacktrackGlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (BacktrackGlyphCount = %d)\n", BacktrackGlyphCount ));
+
+ otv_Coverage_validate( Coverage, valid );
+
+ OTV_LIMIT_CHECK( BacktrackGlyphCount * 2 + 2 );
+
+ for ( ; BacktrackGlyphCount > 0; BacktrackGlyphCount-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ LookaheadGlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (LookaheadGlyphCount = %d)\n", LookaheadGlyphCount ));
+
+ OTV_LIMIT_CHECK( LookaheadGlyphCount * 2 + 2 );
+
+ for ( ; LookaheadGlyphCount > 0; LookaheadGlyphCount-- )
+ otv_Coverage_validate( table + FT_NEXT_USHORT( p ), valid );
+
+ GlyphCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (GlyphCount = %d)\n", GlyphCount ));
+
+ if ( GlyphCount != otv_Coverage_get_count( Coverage ) )
+ FT_INVALID_DATA;
+
+ OTV_LIMIT_CHECK( GlyphCount * 2 );
+
+ /* Substitute */
+ for ( ; GlyphCount > 0; GlyphCount-- )
+ if ( FT_NEXT_USHORT( p ) >= valid->glyph_count )
+ FT_INVALID_DATA;
+
+ break;
+
+ default:
+ FT_INVALID_DATA;
+ }
+
+ OTV_EXIT;
+ }
+
+
+ static const OTV_Validate_Func otv_gsub_validate_funcs[8] =
+ {
+ otv_SingleSubst_validate,
+ otv_MultipleSubst_validate,
+ otv_AlternateSubst_validate,
+ otv_LigatureSubst_validate,
+ otv_ContextSubst_validate,
+ otv_ChainContextSubst_validate,
+ otv_ExtensionSubst_validate,
+ otv_ReverseChainSingleSubst_validate
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GSUB TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* sets valid->type_count */
+ /* sets valid->type_funcs */
+ /* sets valid->glyph_count */
+
+ FT_LOCAL_DEF( void )
+ otv_GSUB_validate( FT_Bytes table,
+ FT_UInt glyph_count,
+ FT_Validator ftvalid )
+ {
+ OTV_ValidatorRec validrec;
+ OTV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_UInt ScriptList, FeatureList, LookupList;
+
+
+ valid->root = ftvalid;
+
+ FT_TRACE3(( "validating GSUB table\n" ));
+ OTV_INIT;
+
+ OTV_LIMIT_CHECK( 10 );
+
+ if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ FT_INVALID_DATA;
+
+ ScriptList = FT_NEXT_USHORT( p );
+ FeatureList = FT_NEXT_USHORT( p );
+ LookupList = FT_NEXT_USHORT( p );
+
+ valid->type_count = 8;
+ valid->type_funcs = (OTV_Validate_Func*)otv_gsub_validate_funcs;
+ valid->glyph_count = glyph_count;
+
+ otv_LookupList_validate( table + LookupList,
+ valid );
+ otv_FeatureList_validate( table + FeatureList, table + LookupList,
+ valid );
+ otv_ScriptList_validate( table + ScriptList, table + FeatureList,
+ valid );
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvjstf.c b/src/otvalid/otvjstf.c
new file mode 100644
index 0000000..80b8dd6
--- /dev/null
+++ b/src/otvalid/otvjstf.c
@@ -0,0 +1,258 @@
+/***************************************************************************/
+/* */
+/* otvjstf.c */
+/* */
+/* OpenType JSTF table validation (body). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "otvalid.h"
+#include "otvcommn.h"
+#include "otvgpos.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvjstf
+
+
+#define JstfPriorityFunc otv_JstfPriority_validate
+#define JstfLookupFunc otv_GPOS_subtable_validate
+
+ /* uses valid->extra1 (GSUB lookup count) */
+ /* uses valid->extra2 (GPOS lookup count) */
+ /* sets valid->extra1 (counter) */
+
+ static void
+ otv_JstfPriority_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UInt gsub_lookup_count, gpos_lookup_count;
+
+ OTV_OPTIONAL_TABLE( ShrinkageEnableGSUB );
+ OTV_OPTIONAL_TABLE( ShrinkageDisableGSUB );
+ OTV_OPTIONAL_TABLE( ShrinkageEnableGPOS );
+ OTV_OPTIONAL_TABLE( ShrinkageDisableGPOS );
+ OTV_OPTIONAL_TABLE( ExtensionEnableGSUB );
+ OTV_OPTIONAL_TABLE( ExtensionDisableGSUB );
+ OTV_OPTIONAL_TABLE( ExtensionEnableGPOS );
+ OTV_OPTIONAL_TABLE( ExtensionDisableGPOS );
+ OTV_OPTIONAL_TABLE( ShrinkageJstfMax );
+ OTV_OPTIONAL_TABLE( ExtensionJstfMax );
+
+
+ OTV_ENTER;
+ OTV_TRACE(( "JstfPriority table\n" ));
+
+ OTV_LIMIT_CHECK( 20 );
+
+ gsub_lookup_count = valid->extra1;
+ gpos_lookup_count = valid->extra2;
+
+ table_size = 20;
+
+ valid->extra1 = gsub_lookup_count;
+
+ OTV_OPTIONAL_OFFSET( ShrinkageEnableGSUB );
+ OTV_SIZE_CHECK( ShrinkageEnableGSUB );
+ if ( ShrinkageEnableGSUB )
+ otv_x_ux( table + ShrinkageEnableGSUB, valid );
+
+ OTV_OPTIONAL_OFFSET( ShrinkageDisableGSUB );
+ OTV_SIZE_CHECK( ShrinkageDisableGSUB );
+ if ( ShrinkageDisableGSUB )
+ otv_x_ux( table + ShrinkageDisableGSUB, valid );
+
+ valid->extra1 = gpos_lookup_count;
+
+ OTV_OPTIONAL_OFFSET( ShrinkageEnableGPOS );
+ OTV_SIZE_CHECK( ShrinkageEnableGPOS );
+ if ( ShrinkageEnableGPOS )
+ otv_x_ux( table + ShrinkageEnableGPOS, valid );
+
+ OTV_OPTIONAL_OFFSET( ShrinkageDisableGPOS );
+ OTV_SIZE_CHECK( ShrinkageDisableGPOS );
+ if ( ShrinkageDisableGPOS )
+ otv_x_ux( table + ShrinkageDisableGPOS, valid );
+
+ OTV_OPTIONAL_OFFSET( ShrinkageJstfMax );
+ OTV_SIZE_CHECK( ShrinkageJstfMax );
+ if ( ShrinkageJstfMax )
+ {
+ /* XXX: check lookup types? */
+ OTV_NEST2( JstfMax, JstfLookup );
+ OTV_RUN( table + ShrinkageJstfMax, valid );
+ }
+
+ valid->extra1 = gsub_lookup_count;
+
+ OTV_OPTIONAL_OFFSET( ExtensionEnableGSUB );
+ OTV_SIZE_CHECK( ExtensionEnableGSUB );
+ if ( ExtensionEnableGSUB )
+ otv_x_ux( table + ExtensionEnableGSUB, valid );
+
+ OTV_OPTIONAL_OFFSET( ExtensionDisableGSUB );
+ OTV_SIZE_CHECK( ExtensionDisableGSUB );
+ if ( ExtensionDisableGSUB )
+ otv_x_ux( table + ExtensionDisableGSUB, valid );
+
+ valid->extra1 = gpos_lookup_count;
+
+ OTV_OPTIONAL_OFFSET( ExtensionEnableGPOS );
+ OTV_SIZE_CHECK( ExtensionEnableGPOS );
+ if ( ExtensionEnableGPOS )
+ otv_x_ux( table + ExtensionEnableGPOS, valid );
+
+ OTV_OPTIONAL_OFFSET( ExtensionDisableGPOS );
+ OTV_SIZE_CHECK( ExtensionDisableGPOS );
+ if ( ExtensionDisableGPOS )
+ otv_x_ux( table + ExtensionDisableGPOS, valid );
+
+ OTV_OPTIONAL_OFFSET( ExtensionJstfMax );
+ OTV_SIZE_CHECK( ExtensionJstfMax );
+ if ( ExtensionJstfMax )
+ {
+ /* XXX: check lookup types? */
+ OTV_NEST2( JstfMax, JstfLookup );
+ OTV_RUN( table + ExtensionJstfMax, valid );
+ }
+
+ valid->extra1 = gsub_lookup_count;
+ valid->extra2 = gpos_lookup_count;
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra (glyph count) */
+ /* sets valid->func1 (otv_JstfPriority_validate) */
+
+ static void
+ otv_JstfScript_validate( FT_Bytes table,
+ OTV_Validator valid )
+ {
+ FT_Bytes p = table;
+ FT_UInt table_size;
+ FT_UInt JstfLangSysCount;
+
+ OTV_OPTIONAL_TABLE( ExtGlyph );
+ OTV_OPTIONAL_TABLE( DefJstfLangSys );
+
+
+ OTV_NAME_ENTER( "JstfScript" );
+
+ OTV_LIMIT_CHECK( 6 );
+ OTV_OPTIONAL_OFFSET( ExtGlyph );
+ OTV_OPTIONAL_OFFSET( DefJstfLangSys );
+ JstfLangSysCount = FT_NEXT_USHORT( p );
+
+ OTV_TRACE(( " (JstfLangSysCount = %d)\n", JstfLangSysCount ));
+
+ table_size = JstfLangSysCount * 6 + 6;
+
+ OTV_SIZE_CHECK( ExtGlyph );
+ if ( ExtGlyph )
+ {
+ valid->extra1 = valid->glyph_count;
+ OTV_NEST1( ExtenderGlyph );
+ OTV_RUN( table + ExtGlyph, valid );
+ }
+
+ OTV_SIZE_CHECK( DefJstfLangSys );
+ if ( DefJstfLangSys )
+ {
+ OTV_NEST2( JstfLangSys, JstfPriority );
+ OTV_RUN( table + DefJstfLangSys, valid );
+ }
+
+ OTV_LIMIT_CHECK( 6 * JstfLangSysCount );
+
+ /* JstfLangSysRecord */
+ OTV_NEST2( JstfLangSys, JstfPriority );
+ for ( ; JstfLangSysCount > 0; JstfLangSysCount-- )
+ {
+ p += 4; /* skip JstfLangSysTag */
+
+ OTV_RUN( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ OTV_EXIT;
+ }
+
+
+ /* sets valid->extra1 (GSUB lookup count) */
+ /* sets valid->extra2 (GPOS lookup count) */
+ /* sets valid->glyph_count */
+
+ FT_LOCAL_DEF( void )
+ otv_JSTF_validate( FT_Bytes table,
+ FT_Bytes gsub,
+ FT_Bytes gpos,
+ FT_UInt glyph_count,
+ FT_Validator ftvalid )
+ {
+ OTV_ValidatorRec validrec;
+ OTV_Validator valid = &validrec;
+ FT_Bytes p = table;
+ FT_UInt JstfScriptCount;
+
+
+ valid->root = ftvalid;
+
+ FT_TRACE3(( "validating JSTF table\n" ));
+ OTV_INIT;
+
+ OTV_LIMIT_CHECK( 6 );
+
+ if ( FT_NEXT_ULONG( p ) != 0x10000UL ) /* Version */
+ FT_INVALID_DATA;
+
+ JstfScriptCount = FT_NEXT_USHORT( p );
+
+ FT_TRACE3(( " (JstfScriptCount = %d)\n", JstfScriptCount ));
+
+ OTV_LIMIT_CHECK( JstfScriptCount * 6 );
+
+ if ( gsub )
+ valid->extra1 = otv_GSUBGPOS_get_Lookup_count( gsub );
+ else
+ valid->extra1 = 0;
+
+ if ( gpos )
+ valid->extra2 = otv_GSUBGPOS_get_Lookup_count( gpos );
+ else
+ valid->extra2 = 0;
+
+ valid->glyph_count = glyph_count;
+
+ /* JstfScriptRecord */
+ for ( ; JstfScriptCount > 0; JstfScriptCount-- )
+ {
+ p += 4; /* skip JstfScriptTag */
+
+ /* JstfScript */
+ otv_JstfScript_validate( table + FT_NEXT_USHORT( p ), valid );
+ }
+
+ FT_TRACE4(( "\n" ));
+ }
+
+
+/* END */
diff --git a/src/otvalid/otvmod.c b/src/otvalid/otvmod.c
new file mode 100644
index 0000000..157272f
--- /dev/null
+++ b/src/otvalid/otvmod.c
@@ -0,0 +1,242 @@
+/***************************************************************************/
+/* */
+/* otvmod.c */
+/* */
+/* FreeType's OpenType validation module implementation (body). */
+/* */
+/* Copyright 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_OPENTYPE_VALIDATE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_OPENTYPE_VALIDATE_H
+
+#include "otvmod.h"
+#include "otvalid.h"
+#include "otvcommn.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_otvmodule
+
+
+ static FT_Error
+ otv_load_table( FT_Face face,
+ FT_Tag tag,
+ FT_Byte* volatile* table,
+ FT_ULong* table_len )
+ {
+ FT_Error error;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ error = FT_Load_Sfnt_Table( face, tag, 0, NULL, table_len );
+ if ( error == OTV_Err_Table_Missing )
+ return OTV_Err_Ok;
+ if ( error )
+ goto Exit;
+
+ if ( FT_ALLOC( *table, *table_len ) )
+ goto Exit;
+
+ error = FT_Load_Sfnt_Table( face, tag, 0, *table, table_len );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ otv_validate( FT_Face volatile face,
+ FT_UInt ot_flags,
+ FT_Bytes *ot_base,
+ FT_Bytes *ot_gdef,
+ FT_Bytes *ot_gpos,
+ FT_Bytes *ot_gsub,
+ FT_Bytes *ot_jstf )
+ {
+ FT_Error error = OTV_Err_Ok;
+ FT_Byte* volatile base;
+ FT_Byte* volatile gdef;
+ FT_Byte* volatile gpos;
+ FT_Byte* volatile gsub;
+ FT_Byte* volatile jstf;
+ FT_ULong len_base, len_gdef, len_gpos, len_gsub, len_jstf;
+ FT_ValidatorRec volatile valid;
+
+
+ base = gdef = gpos = gsub = jstf = NULL;
+ len_base = len_gdef = len_gpos = len_gsub = len_jstf = 0;
+
+ /* load tables */
+
+ if ( ot_flags & FT_VALIDATE_BASE )
+ {
+ error = otv_load_table( face, TTAG_BASE, &base, &len_base );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( ot_flags & FT_VALIDATE_GDEF )
+ {
+ error = otv_load_table( face, TTAG_GDEF, &gdef, &len_gdef );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( ot_flags & FT_VALIDATE_GPOS )
+ {
+ error = otv_load_table( face, TTAG_GPOS, &gpos, &len_gpos );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( ot_flags & FT_VALIDATE_GSUB )
+ {
+ error = otv_load_table( face, TTAG_GSUB, &gsub, &len_gsub );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( ot_flags & FT_VALIDATE_JSTF )
+ {
+ error = otv_load_table( face, TTAG_JSTF, &jstf, &len_jstf );
+ if ( error )
+ goto Exit;
+ }
+
+ /* validate tables */
+
+ if ( base )
+ {
+ ft_validator_init( &valid, base, base + len_base, FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ otv_BASE_validate( base, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ if ( gpos )
+ {
+ ft_validator_init( &valid, gpos, gpos + len_gpos, FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ otv_GPOS_validate( gpos, face->num_glyphs, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ if ( gsub )
+ {
+ ft_validator_init( &valid, gsub, gsub + len_gsub, FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ otv_GSUB_validate( gsub, face->num_glyphs, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ if ( gdef )
+ {
+ ft_validator_init( &valid, gdef, gdef + len_gdef, FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ otv_GDEF_validate( gdef, gsub, gpos, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ if ( jstf )
+ {
+ ft_validator_init( &valid, jstf, jstf + len_jstf, FT_VALIDATE_DEFAULT );
+ if ( ft_setjmp( valid.jump_buffer ) == 0 )
+ otv_JSTF_validate( jstf, gsub, gpos, face->num_glyphs, &valid );
+ error = valid.error;
+ if ( error )
+ goto Exit;
+ }
+
+ *ot_base = (FT_Bytes)base;
+ *ot_gdef = (FT_Bytes)gdef;
+ *ot_gpos = (FT_Bytes)gpos;
+ *ot_gsub = (FT_Bytes)gsub;
+ *ot_jstf = (FT_Bytes)jstf;
+
+ Exit:
+ if ( error ) {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( base );
+ FT_FREE( gdef );
+ FT_FREE( gpos );
+ FT_FREE( gsub );
+ FT_FREE( jstf );
+ }
+
+ return error;
+ }
+
+
+ static
+ const FT_Service_OTvalidateRec otvalid_interface =
+ {
+ otv_validate
+ };
+
+
+ static
+ const FT_ServiceDescRec otvalid_services[] =
+ {
+ { FT_SERVICE_ID_OPENTYPE_VALIDATE, &otvalid_interface },
+ { NULL, NULL }
+ };
+
+
+ static FT_Pointer
+ otvalid_get_service( FT_Module module,
+ const char* service_id )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( otvalid_services, service_id );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class otv_module_class =
+ {
+ 0,
+ sizeof( FT_ModuleRec ),
+ "otvalid",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module-specific interface */
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) otvalid_get_service
+ };
+
+
+/* END */
diff --git a/src/otvalid/otvmod.h b/src/otvalid/otvmod.h
new file mode 100644
index 0000000..1bfc189
--- /dev/null
+++ b/src/otvalid/otvmod.h
@@ -0,0 +1,39 @@
+/***************************************************************************/
+/* */
+/* otvmod.h */
+/* */
+/* FreeType's OpenType validation module implementation */
+/* (specification). */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __OTVMOD_H__
+#define __OTVMOD_H__
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) otv_module_class;
+
+
+FT_END_HEADER
+
+#endif /* __OTVMOD_H__ */
+
+
+/* END */
diff --git a/src/otvalid/rules.mk b/src/otvalid/rules.mk
new file mode 100644
index 0000000..48f1233
--- /dev/null
+++ b/src/otvalid/rules.mk
@@ -0,0 +1,77 @@
+#
+# FreeType 2 OpenType validation driver configuration rules
+#
+
+
+# Copyright 2004 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# OTV driver directory
+#
+OTV_DIR := $(SRC_DIR)/otvalid
+
+
+# compilation flags for the driver
+#
+OTV_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(OTV_DIR))
+
+
+# OTV driver sources (i.e., C files)
+#
+OTV_DRV_SRC := $(OTV_DIR)/otvbase.c \
+ $(OTV_DIR)/otvcommn.c \
+ $(OTV_DIR)/otvgdef.c \
+ $(OTV_DIR)/otvgpos.c \
+ $(OTV_DIR)/otvgsub.c \
+ $(OTV_DIR)/otvjstf.c \
+ $(OTV_DIR)/otvmod.c
+
+# OTV driver headers
+#
+OTV_DRV_H := $(OTV_DIR)/otvalid.h \
+ $(OTV_DIR)/otverror.h \
+ $(OTV_DIR)/otvcommn.h \
+ $(OTV_DIR)/otvgpos.h \
+ $(OTV_DIR)/otvmod.h
+
+
+# OTV driver object(s)
+#
+# OTV_DRV_OBJ_M is used during `multi' builds.
+# OTV_DRV_OBJ_S is used during `single' builds.
+#
+OTV_DRV_OBJ_M := $(OTV_DRV_SRC:$(OTV_DIR)/%.c=$(OBJ_DIR)/%.$O)
+OTV_DRV_OBJ_S := $(OBJ_DIR)/otvalid.$O
+
+# OTV driver source file for single build
+#
+OTV_DRV_SRC_S := $(OTV_DIR)/otvalid.c
+
+
+# OTV driver - single object
+#
+$(OTV_DRV_OBJ_S): $(OTV_DRV_SRC_S) $(OTV_DRV_SRC) \
+ $(FREETYPE_H) $(OTV_DRV_H)
+ $(OTV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(OTV_DRV_SRC_S))
+
+
+# OTV driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(OTV_DIR)/%.c $(FREETYPE_H) $(OTV_DRV_H)
+ $(OTV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(OTV_DRV_OBJ_S)
+DRV_OBJS_M += $(OTV_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/pcf/Jamfile b/src/pcf/Jamfile
new file mode 100644
index 0000000..752fcac
--- /dev/null
+++ b/src/pcf/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/pcf Jamfile
+#
+# Copyright 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) pcf ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = pcfdrivr pcfread pcfutil ;
+ }
+ else
+ {
+ _sources = pcf ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/pcf Jamfile
diff --git a/src/pcf/README b/src/pcf/README
new file mode 100644
index 0000000..cc1480b
--- /dev/null
+++ b/src/pcf/README
@@ -0,0 +1,114 @@
+ FreeType font driver for PCF fonts
+
+ Francesco Zappa Nardelli
+ <francesco.zappa.nardelli@ens.fr>
+
+
+Introduction
+************
+
+PCF (Portable Compiled Format) is a binary bitmap font format, largely used
+in X world. This code implements a PCF driver for the FreeType library.
+Glyph images are loaded into memory only on demand, thus leading to a small
+memory footprint.
+
+Information on the PCF font format can only be worked out from
+`pcfread.c', and `pcfwrite.c', to be found, for instance, in the XFree86
+(www.xfree86.org) source tree (xc/lib/font/bitmap/).
+
+Many good bitmap fonts in bdf format come with XFree86: they can be
+compiled into the pcf format using the `bdftopcf' utility.
+
+
+Supported hardware
+******************
+
+The driver has been tested on linux/x86 and sunos5.5/sparc. In both
+cases the compiler was gcc. When back in Paris, I will test it also
+on linux/alpha.
+
+
+Encodings
+*********
+
+The variety of encodings that accompanies pcf fonts appears to encompass the
+small set defined in freetype.h. On the other hand, each pcf font defines
+two properties that specify encoding and registry.
+
+I decided to make these two properties directly accessible, leaving to the
+client application the work of interpreting them. For instance:
+
+ #include "pcftypes.h" /* include/freetype/internal/pcftypes.h */
+
+ FT_Face face;
+ PCF_Public_Face pcfface;
+
+ FT_New_Face( library,..., &face );
+
+ pcfface = (PCF_Public_Face)face;
+
+ if ((pcfface->charset_registry == "ISO10646") &&
+ (pcfface->charset_encoding) == "1")) [..]
+
+Thus the driver always export `ft_encoding_none' as
+face->charmap.encoding. FT_Get_Char_Index() behavior is unmodified, that
+is, it converts the ULong value given as argument into the corresponding
+glyph number.
+
+
+Known problems
+**************
+
+- dealing explicitly with encodings breaks the uniformity of freetype2
+ api.
+
+- except for encodings properties, client applications have no
+ visibility of the PCF_Face object. This means that applications
+ cannot directly access font tables and are obliged to trust
+ FreeType.
+
+- currently, glyph names and ink_metrics are ignored.
+
+I plan to give full visibility of the PCF_Face object in the next
+release of the driver, thus implementing also glyph names and
+ink_metrics.
+
+- height is defined as (ascent - descent). Is this correct?
+
+- if unable to read size information from the font, PCF_Init_Face
+ sets available_size->width and available_size->height to 12.
+
+- too many english grammar errors in the readme file :-(
+
+
+License
+*******
+
+Copyright (C) 2000 by Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+Credits
+*******
+
+Keith Packard wrote the pcf driver found in XFree86. His work is at
+the same time the specification and the sample implementation of the
+PCF format. Undoubtedly, this driver is inspired from his work.
diff --git a/src/pcf/module.mk b/src/pcf/module.mk
new file mode 100644
index 0000000..0c51cd6
--- /dev/null
+++ b/src/pcf/module.mk
@@ -0,0 +1,34 @@
+#
+# FreeType 2 PCF module definition
+#
+
+# Copyright 2000, 2006 by
+# Francesco Zappa Nardelli
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+FTMODULE_H_COMMANDS += PCF_DRIVER
+
+define PCF_DRIVER
+$(OPEN_DRIVER)pcf_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)pcf $(ECHO_DRIVER_DESC)pcf bitmap fonts$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/pcf/pcf.c b/src/pcf/pcf.c
new file mode 100644
index 0000000..11d5b7b
--- /dev/null
+++ b/src/pcf/pcf.c
@@ -0,0 +1,36 @@
+/* pcf.c
+
+ FreeType font driver for pcf fonts
+
+ Copyright 2000-2001, 2003 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+
+#include <ft2build.h>
+#include "pcfutil.c"
+#include "pcfread.c"
+#include "pcfdrivr.c"
+
+/* END */
diff --git a/src/pcf/pcf.h b/src/pcf/pcf.h
new file mode 100644
index 0000000..9d2d8e0
--- /dev/null
+++ b/src/pcf/pcf.h
@@ -0,0 +1,237 @@
+/* pcf.h
+
+ FreeType font driver for pcf fonts
+
+ Copyright (C) 2000, 2001, 2002, 2003, 2006 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __PCF_H__
+#define __PCF_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+ typedef struct PCF_TableRec_
+ {
+ FT_ULong type;
+ FT_ULong format;
+ FT_ULong size;
+ FT_ULong offset;
+
+ } PCF_TableRec, *PCF_Table;
+
+
+ typedef struct PCF_TocRec_
+ {
+ FT_ULong version;
+ FT_ULong count;
+ PCF_Table tables;
+
+ } PCF_TocRec, *PCF_Toc;
+
+
+ typedef struct PCF_ParsePropertyRec_
+ {
+ FT_Long name;
+ FT_Byte isString;
+ FT_Long value;
+
+ } PCF_ParsePropertyRec, *PCF_ParseProperty;
+
+
+ typedef struct PCF_PropertyRec_
+ {
+ FT_String* name;
+ FT_Byte isString;
+
+ union
+ {
+ FT_String* atom;
+ FT_Long integer;
+ FT_ULong cardinal;
+
+ } value;
+
+ } PCF_PropertyRec, *PCF_Property;
+
+
+ typedef struct PCF_Compressed_MetricRec_
+ {
+ FT_Byte leftSideBearing;
+ FT_Byte rightSideBearing;
+ FT_Byte characterWidth;
+ FT_Byte ascent;
+ FT_Byte descent;
+
+ } PCF_Compressed_MetricRec, *PCF_Compressed_Metric;
+
+
+ typedef struct PCF_MetricRec_
+ {
+ FT_Short leftSideBearing;
+ FT_Short rightSideBearing;
+ FT_Short characterWidth;
+ FT_Short ascent;
+ FT_Short descent;
+ FT_Short attributes;
+ FT_ULong bits;
+
+ } PCF_MetricRec, *PCF_Metric;
+
+
+ typedef struct PCF_AccelRec_
+ {
+ FT_Byte noOverlap;
+ FT_Byte constantMetrics;
+ FT_Byte terminalFont;
+ FT_Byte constantWidth;
+ FT_Byte inkInside;
+ FT_Byte inkMetrics;
+ FT_Byte drawDirection;
+ FT_Long fontAscent;
+ FT_Long fontDescent;
+ FT_Long maxOverlap;
+ PCF_MetricRec minbounds;
+ PCF_MetricRec maxbounds;
+ PCF_MetricRec ink_minbounds;
+ PCF_MetricRec ink_maxbounds;
+
+ } PCF_AccelRec, *PCF_Accel;
+
+
+ typedef struct PCF_EncodingRec_
+ {
+ FT_Long enc;
+ FT_UShort glyph;
+
+ } PCF_EncodingRec, *PCF_Encoding;
+
+
+ typedef struct PCF_FaceRec_
+ {
+ FT_FaceRec root;
+
+ FT_StreamRec gzip_stream;
+ FT_Stream gzip_source;
+
+ char* charset_encoding;
+ char* charset_registry;
+
+ PCF_TocRec toc;
+ PCF_AccelRec accel;
+
+ int nprops;
+ PCF_Property properties;
+
+ FT_Long nmetrics;
+ PCF_Metric metrics;
+ FT_Long nencodings;
+ PCF_Encoding encodings;
+
+ FT_Short defaultChar;
+
+ FT_ULong bitmapsFormat;
+
+ FT_CharMap charmap_handle;
+ FT_CharMapRec charmap; /* a single charmap per face */
+
+ } PCF_FaceRec, *PCF_Face;
+
+
+ /* macros for pcf font format */
+
+#define LSBFirst 0
+#define MSBFirst 1
+
+#define PCF_FILE_VERSION ( ( 'p' << 24 ) | \
+ ( 'c' << 16 ) | \
+ ( 'f' << 8 ) | 1 )
+#define PCF_FORMAT_MASK 0xFFFFFF00UL
+
+#define PCF_DEFAULT_FORMAT 0x00000000UL
+#define PCF_INKBOUNDS 0x00000200UL
+#define PCF_ACCEL_W_INKBOUNDS 0x00000100UL
+#define PCF_COMPRESSED_METRICS 0x00000100UL
+
+#define PCF_FORMAT_MATCH( a, b ) \
+ ( ( (a) & PCF_FORMAT_MASK ) == ( (b) & PCF_FORMAT_MASK ) )
+
+#define PCF_GLYPH_PAD_MASK ( 3 << 0 )
+#define PCF_BYTE_MASK ( 1 << 2 )
+#define PCF_BIT_MASK ( 1 << 3 )
+#define PCF_SCAN_UNIT_MASK ( 3 << 4 )
+
+#define PCF_BYTE_ORDER( f ) \
+ ( ( (f) & PCF_BYTE_MASK ) ? MSBFirst : LSBFirst )
+#define PCF_BIT_ORDER( f ) \
+ ( ( (f) & PCF_BIT_MASK ) ? MSBFirst : LSBFirst )
+#define PCF_GLYPH_PAD_INDEX( f ) \
+ ( (f) & PCF_GLYPH_PAD_MASK )
+#define PCF_GLYPH_PAD( f ) \
+ ( 1 << PCF_GLYPH_PAD_INDEX( f ) )
+#define PCF_SCAN_UNIT_INDEX( f ) \
+ ( ( (f) & PCF_SCAN_UNIT_MASK ) >> 4 )
+#define PCF_SCAN_UNIT( f ) \
+ ( 1 << PCF_SCAN_UNIT_INDEX( f ) )
+#define PCF_FORMAT_BITS( f ) \
+ ( (f) & ( PCF_GLYPH_PAD_MASK | \
+ PCF_BYTE_MASK | \
+ PCF_BIT_MASK | \
+ PCF_SCAN_UNIT_MASK ) )
+
+#define PCF_SIZE_TO_INDEX( s ) ( (s) == 4 ? 2 : (s) == 2 ? 1 : 0 )
+#define PCF_INDEX_TO_SIZE( b ) ( 1 << b )
+
+#define PCF_FORMAT( bit, byte, glyph, scan ) \
+ ( ( PCF_SIZE_TO_INDEX( scan ) << 4 ) | \
+ ( ( (bit) == MSBFirst ? 1 : 0 ) << 3 ) | \
+ ( ( (byte) == MSBFirst ? 1 : 0 ) << 2 ) | \
+ ( PCF_SIZE_TO_INDEX( glyph ) << 0 ) )
+
+#define PCF_PROPERTIES ( 1 << 0 )
+#define PCF_ACCELERATORS ( 1 << 1 )
+#define PCF_METRICS ( 1 << 2 )
+#define PCF_BITMAPS ( 1 << 3 )
+#define PCF_INK_METRICS ( 1 << 4 )
+#define PCF_BDF_ENCODINGS ( 1 << 5 )
+#define PCF_SWIDTHS ( 1 << 6 )
+#define PCF_GLYPH_NAMES ( 1 << 7 )
+#define PCF_BDF_ACCELERATORS ( 1 << 8 )
+
+#define GLYPHPADOPTIONS 4 /* I'm not sure about this */
+
+ FT_LOCAL( FT_Error )
+ pcf_load_font( FT_Stream,
+ PCF_Face );
+
+FT_END_HEADER
+
+#endif /* __PCF_H__ */
+
+
+/* END */
diff --git a/src/pcf/pcfdrivr.c b/src/pcf/pcfdrivr.c
new file mode 100644
index 0000000..c0f0e49
--- /dev/null
+++ b/src/pcf/pcfdrivr.c
@@ -0,0 +1,668 @@
+/* pcfdrivr.c
+
+ FreeType font driver for pcf files
+
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2006 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#include <ft2build.h>
+
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_GZIP_H
+#include FT_LZW_H
+#include FT_ERRORS_H
+#include FT_BDF_H
+
+#include "pcf.h"
+#include "pcfdrivr.h"
+#include "pcfread.h"
+
+#include "pcferror.h"
+#include "pcfutil.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pcfread
+
+#include FT_SERVICE_BDF_H
+#include FT_SERVICE_XFREE86_NAME_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pcfdriver
+
+
+ typedef struct PCF_CMapRec_
+ {
+ FT_CMapRec root;
+ FT_UInt num_encodings;
+ PCF_Encoding encodings;
+
+ } PCF_CMapRec, *PCF_CMap;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ pcf_cmap_init( FT_CMap pcfcmap, /* PCF_CMap */
+ FT_Pointer init_data )
+ {
+ PCF_CMap cmap = (PCF_CMap)pcfcmap;
+ PCF_Face face = (PCF_Face)FT_CMAP_FACE( pcfcmap );
+
+ FT_UNUSED( init_data );
+
+
+ cmap->num_encodings = (FT_UInt)face->nencodings;
+ cmap->encodings = face->encodings;
+
+ return PCF_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ pcf_cmap_done( FT_CMap pcfcmap ) /* PCF_CMap */
+ {
+ PCF_CMap cmap = (PCF_CMap)pcfcmap;
+
+
+ cmap->encodings = NULL;
+ cmap->num_encodings = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ pcf_cmap_char_index( FT_CMap pcfcmap, /* PCF_CMap */
+ FT_UInt32 charcode )
+ {
+ PCF_CMap cmap = (PCF_CMap)pcfcmap;
+ PCF_Encoding encodings = cmap->encodings;
+ FT_UInt min, max, mid;
+ FT_UInt result = 0;
+
+
+ min = 0;
+ max = cmap->num_encodings;
+
+ while ( min < max )
+ {
+ FT_UInt32 code;
+
+
+ mid = ( min + max ) >> 1;
+ code = encodings[mid].enc;
+
+ if ( charcode == code )
+ {
+ result = encodings[mid].glyph + 1;
+ break;
+ }
+
+ if ( charcode < code )
+ max = mid;
+ else
+ min = mid + 1;
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ pcf_cmap_char_next( FT_CMap pcfcmap, /* PCF_CMap */
+ FT_UInt32 *acharcode )
+ {
+ PCF_CMap cmap = (PCF_CMap)pcfcmap;
+ PCF_Encoding encodings = cmap->encodings;
+ FT_UInt min, max, mid;
+ FT_UInt32 charcode = *acharcode + 1;
+ FT_UInt result = 0;
+
+
+ min = 0;
+ max = cmap->num_encodings;
+
+ while ( min < max )
+ {
+ FT_UInt32 code;
+
+
+ mid = ( min + max ) >> 1;
+ code = encodings[mid].enc;
+
+ if ( charcode == code )
+ {
+ result = encodings[mid].glyph + 1;
+ goto Exit;
+ }
+
+ if ( charcode < code )
+ max = mid;
+ else
+ min = mid + 1;
+ }
+
+ charcode = 0;
+ if ( min < cmap->num_encodings )
+ {
+ charcode = encodings[min].enc;
+ result = encodings[min].glyph + 1;
+ }
+
+ Exit:
+ *acharcode = charcode;
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_CMap_ClassRec pcf_cmap_class =
+ {
+ sizeof ( PCF_CMapRec ),
+ pcf_cmap_init,
+ pcf_cmap_done,
+ pcf_cmap_char_index,
+ pcf_cmap_char_next
+ };
+
+
+ FT_CALLBACK_DEF( void )
+ PCF_Face_Done( FT_Face pcfface ) /* PCF_Face */
+ {
+ PCF_Face face = (PCF_Face)pcfface;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( face->encodings );
+ FT_FREE( face->metrics );
+
+ /* free properties */
+ {
+ PCF_Property prop;
+ FT_Int i;
+
+
+ if ( face->properties )
+ {
+ for ( i = 0; i < face->nprops; i++ )
+ {
+ prop = &face->properties[i];
+
+ if ( prop ) {
+ FT_FREE( prop->name );
+ if ( prop->isString )
+ FT_FREE( prop->value.atom );
+ }
+ }
+ }
+ FT_FREE( face->properties );
+ }
+
+ FT_FREE( face->toc.tables );
+ FT_FREE( pcfface->family_name );
+ FT_FREE( pcfface->style_name );
+ FT_FREE( pcfface->available_sizes );
+ FT_FREE( face->charset_encoding );
+ FT_FREE( face->charset_registry );
+
+ FT_TRACE4(( "PCF_Face_Done: done face\n" ));
+
+ /* close gzip/LZW stream if any */
+ if ( pcfface->stream == &face->gzip_stream )
+ {
+ FT_Stream_Close( &face->gzip_stream );
+ pcfface->stream = face->gzip_source;
+ }
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ PCF_Face_Init( FT_Stream stream,
+ FT_Face pcfface, /* PCF_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ PCF_Face face = (PCF_Face)pcfface;
+ FT_Error error = PCF_Err_Ok;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+ FT_UNUSED( face_index );
+
+
+ error = pcf_load_font( stream, face );
+ if ( error )
+ {
+ FT_Error error2;
+
+
+ PCF_Face_Done( pcfface );
+
+ /* this didn't work, try gzip support! */
+ error2 = FT_Stream_OpenGzip( &face->gzip_stream, stream );
+ if ( FT_ERROR_BASE( error2 ) == FT_Err_Unimplemented_Feature )
+ goto Fail;
+
+ error = error2;
+ if ( error )
+ {
+ FT_Error error3;
+
+
+ /* this didn't work, try LZW support! */
+ error3 = FT_Stream_OpenLZW( &face->gzip_stream, stream );
+ if ( FT_ERROR_BASE( error3 ) == FT_Err_Unimplemented_Feature )
+ goto Fail;
+
+ error = error3;
+ if ( error )
+ goto Fail;
+
+ face->gzip_source = stream;
+ pcfface->stream = &face->gzip_stream;
+
+ stream = pcfface->stream;
+
+ error = pcf_load_font( stream, face );
+ if ( error )
+ goto Fail;
+ }
+ else
+ {
+ face->gzip_source = stream;
+ pcfface->stream = &face->gzip_stream;
+
+ stream = pcfface->stream;
+
+ error = pcf_load_font( stream, face );
+ if ( error )
+ goto Fail;
+ }
+ }
+
+ /* set up charmap */
+ {
+ FT_String *charset_registry = face->charset_registry;
+ FT_String *charset_encoding = face->charset_encoding;
+ FT_Bool unicode_charmap = 0;
+
+
+ if ( charset_registry && charset_encoding )
+ {
+ char* s = charset_registry;
+
+
+ /* Uh, oh, compare first letters manually to avoid dependency
+ on locales. */
+ if ( ( s[0] == 'i' || s[0] == 'I' ) &&
+ ( s[1] == 's' || s[1] == 'S' ) &&
+ ( s[2] == 'o' || s[2] == 'O' ) )
+ {
+ s += 3;
+ if ( !ft_strcmp( s, "10646" ) ||
+ ( !ft_strcmp( s, "8859" ) &&
+ !ft_strcmp( face->charset_encoding, "1" ) ) )
+ unicode_charmap = 1;
+ }
+ }
+
+ {
+ FT_CharMapRec charmap;
+
+
+ charmap.face = FT_FACE( face );
+ charmap.encoding = FT_ENCODING_NONE;
+ charmap.platform_id = 0;
+ charmap.encoding_id = 0;
+
+ if ( unicode_charmap )
+ {
+ charmap.encoding = FT_ENCODING_UNICODE;
+ charmap.platform_id = 3;
+ charmap.encoding_id = 1;
+ }
+
+ error = FT_CMap_New( &pcf_cmap_class, NULL, &charmap, NULL );
+
+#if 0
+ /* Select default charmap */
+ if ( pcfface->num_charmaps )
+ pcfface->charmap = pcfface->charmaps[0];
+#endif
+ }
+ }
+
+ Exit:
+ return error;
+
+ Fail:
+ FT_TRACE2(( "[not a valid PCF file]\n" ));
+ PCF_Face_Done( pcfface );
+ error = PCF_Err_Unknown_File_Format; /* error */
+ goto Exit;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ PCF_Size_Select( FT_Size size,
+ FT_ULong strike_index )
+ {
+ PCF_Accel accel = &( (PCF_Face)size->face )->accel;
+
+
+ FT_Select_Metrics( size->face, strike_index );
+
+ size->metrics.ascender = accel->fontAscent << 6;
+ size->metrics.descender = -accel->fontDescent << 6;
+ size->metrics.max_advance = accel->maxbounds.characterWidth << 6;
+
+ return PCF_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ PCF_Size_Request( FT_Size size,
+ FT_Size_Request req )
+ {
+ PCF_Face face = (PCF_Face)size->face;
+ FT_Bitmap_Size* bsize = size->face->available_sizes;
+ FT_Error error = PCF_Err_Invalid_Pixel_Size;
+ FT_Long height;
+
+
+ height = FT_REQUEST_HEIGHT( req );
+ height = ( height + 32 ) >> 6;
+
+ switch ( req->type )
+ {
+ case FT_SIZE_REQUEST_TYPE_NOMINAL:
+ if ( height == ( bsize->y_ppem + 32 ) >> 6 )
+ error = PCF_Err_Ok;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_REAL_DIM:
+ if ( height == ( face->accel.fontAscent +
+ face->accel.fontDescent ) )
+ error = PCF_Err_Ok;
+ break;
+
+ default:
+ error = PCF_Err_Unimplemented_Feature;
+ break;
+ }
+
+ if ( error )
+ return error;
+ else
+ return PCF_Size_Select( size, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ PCF_Glyph_Load( FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ PCF_Face face = (PCF_Face)FT_SIZE_FACE( size );
+ FT_Stream stream = face->root.stream;
+ FT_Error error = PCF_Err_Ok;
+ FT_Bitmap* bitmap = &slot->bitmap;
+ PCF_Metric metric;
+ int bytes;
+
+ FT_UNUSED( load_flags );
+
+
+ FT_TRACE4(( "load_glyph %d ---", glyph_index ));
+
+ if ( !face || glyph_index >= (FT_UInt)face->root.num_glyphs )
+ {
+ error = PCF_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( glyph_index > 0 )
+ glyph_index--;
+
+ metric = face->metrics + glyph_index;
+
+ bitmap->rows = metric->ascent + metric->descent;
+ bitmap->width = metric->rightSideBearing - metric->leftSideBearing;
+ bitmap->num_grays = 1;
+ bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
+
+ FT_TRACE6(( "BIT_ORDER %d ; BYTE_ORDER %d ; GLYPH_PAD %d\n",
+ PCF_BIT_ORDER( face->bitmapsFormat ),
+ PCF_BYTE_ORDER( face->bitmapsFormat ),
+ PCF_GLYPH_PAD( face->bitmapsFormat ) ));
+
+ switch ( PCF_GLYPH_PAD( face->bitmapsFormat ) )
+ {
+ case 1:
+ bitmap->pitch = ( bitmap->width + 7 ) >> 3;
+ break;
+
+ case 2:
+ bitmap->pitch = ( ( bitmap->width + 15 ) >> 4 ) << 1;
+ break;
+
+ case 4:
+ bitmap->pitch = ( ( bitmap->width + 31 ) >> 5 ) << 2;
+ break;
+
+ case 8:
+ bitmap->pitch = ( ( bitmap->width + 63 ) >> 6 ) << 3;
+ break;
+
+ default:
+ return PCF_Err_Invalid_File_Format;
+ }
+
+ /* XXX: to do: are there cases that need repadding the bitmap? */
+ bytes = bitmap->pitch * bitmap->rows;
+
+ error = ft_glyphslot_alloc_bitmap( slot, bytes );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_SEEK( metric->bits ) ||
+ FT_STREAM_READ( bitmap->buffer, bytes ) )
+ goto Exit;
+
+ if ( PCF_BIT_ORDER( face->bitmapsFormat ) != MSBFirst )
+ BitOrderInvert( bitmap->buffer, bytes );
+
+ if ( ( PCF_BYTE_ORDER( face->bitmapsFormat ) !=
+ PCF_BIT_ORDER( face->bitmapsFormat ) ) )
+ {
+ switch ( PCF_SCAN_UNIT( face->bitmapsFormat ) )
+ {
+ case 1:
+ break;
+
+ case 2:
+ TwoByteSwap( bitmap->buffer, bytes );
+ break;
+
+ case 4:
+ FourByteSwap( bitmap->buffer, bytes );
+ break;
+ }
+ }
+
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ slot->bitmap_left = metric->leftSideBearing;
+ slot->bitmap_top = metric->ascent;
+
+ slot->metrics.horiAdvance = metric->characterWidth << 6;
+ slot->metrics.horiBearingX = metric->leftSideBearing << 6;
+ slot->metrics.horiBearingY = metric->ascent << 6;
+ slot->metrics.width = ( metric->rightSideBearing -
+ metric->leftSideBearing ) << 6;
+ slot->metrics.height = bitmap->rows << 6;
+
+ ft_synthesize_vertical_metrics( &slot->metrics,
+ ( face->accel.fontAscent +
+ face->accel.fontDescent ) << 6 );
+
+ FT_TRACE4(( " --- ok\n" ));
+
+ Exit:
+ return error;
+ }
+
+
+ /*
+ *
+ * BDF SERVICE
+ *
+ */
+
+ static FT_Error
+ pcf_get_bdf_property( PCF_Face face,
+ const char* prop_name,
+ BDF_PropertyRec *aproperty )
+ {
+ PCF_Property prop;
+
+
+ prop = pcf_find_property( face, prop_name );
+ if ( prop != NULL )
+ {
+ if ( prop->isString )
+ {
+ aproperty->type = BDF_PROPERTY_TYPE_ATOM;
+ aproperty->u.atom = prop->value.atom;
+ }
+ else
+ {
+ /* Apparently, the PCF driver loads all properties as signed integers!
+ * This really doesn't seem to be a problem, because this is
+ * sufficient for any meaningful values.
+ */
+ aproperty->type = BDF_PROPERTY_TYPE_INTEGER;
+ aproperty->u.integer = prop->value.integer;
+ }
+ return 0;
+ }
+
+ return PCF_Err_Invalid_Argument;
+ }
+
+
+ static FT_Error
+ pcf_get_charset_id( PCF_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ *acharset_encoding = face->charset_encoding;
+ *acharset_registry = face->charset_registry;
+
+ return 0;
+ }
+
+
+ static const FT_Service_BDFRec pcf_service_bdf =
+ {
+ (FT_BDF_GetCharsetIdFunc)pcf_get_charset_id,
+ (FT_BDF_GetPropertyFunc) pcf_get_bdf_property
+ };
+
+
+ /*
+ *
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec pcf_services[] =
+ {
+ { FT_SERVICE_ID_BDF, &pcf_service_bdf },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_PCF },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ pcf_driver_requester( FT_Module module,
+ const char* name )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( pcf_services, name );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec pcf_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_NO_OUTLINES,
+ sizeof ( FT_DriverRec ),
+
+ "pcf",
+ 0x10000L,
+ 0x20000L,
+
+ 0,
+
+ 0,
+ 0,
+ pcf_driver_requester
+ },
+
+ sizeof ( PCF_FaceRec ),
+ sizeof ( FT_SizeRec ),
+ sizeof ( FT_GlyphSlotRec ),
+
+ PCF_Face_Init,
+ PCF_Face_Done,
+ 0, /* FT_Size_InitFunc */
+ 0, /* FT_Size_DoneFunc */
+ 0, /* FT_Slot_InitFunc */
+ 0, /* FT_Slot_DoneFunc */
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ PCF_Glyph_Load,
+
+ 0, /* FT_Face_GetKerningFunc */
+ 0, /* FT_Face_AttachFunc */
+ 0, /* FT_Face_GetAdvancesFunc */
+
+ PCF_Size_Request,
+ PCF_Size_Select
+ };
+
+
+/* END */
diff --git a/src/pcf/pcfdrivr.h b/src/pcf/pcfdrivr.h
new file mode 100644
index 0000000..7ddf697
--- /dev/null
+++ b/src/pcf/pcfdrivr.h
@@ -0,0 +1,44 @@
+/* pcfdrivr.h
+
+ FreeType font driver for pcf fonts
+
+ Copyright 2000-2001, 2002 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __PCFDRIVR_H__
+#define __PCFDRIVR_H__
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+FT_BEGIN_HEADER
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) pcf_driver_class;
+
+FT_END_HEADER
+
+
+#endif /* __PCFDRIVR_H__ */
+
+
+/* END */
diff --git a/src/pcf/pcferror.h b/src/pcf/pcferror.h
new file mode 100644
index 0000000..d75c067
--- /dev/null
+++ b/src/pcf/pcferror.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* pcferror.h */
+/* */
+/* PCF error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the PCF error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __PCFERROR_H__
+#define __PCFERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX PCF_Err_
+#define FT_ERR_BASE FT_Mod_Err_PCF
+
+#include FT_ERRORS_H
+
+#endif /* __PCFERROR_H__ */
+
+
+/* END */
diff --git a/src/pcf/pcfread.c b/src/pcf/pcfread.c
new file mode 100644
index 0000000..b9123cf
--- /dev/null
+++ b/src/pcf/pcfread.c
@@ -0,0 +1,1267 @@
+/* pcfread.c
+
+ FreeType font driver for pcf fonts
+
+ Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#include <ft2build.h>
+
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_OBJECTS_H
+
+#include "pcf.h"
+#include "pcfdrivr.h"
+#include "pcfread.h"
+
+#include "pcferror.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pcfread
+
+
+#if defined( FT_DEBUG_LEVEL_TRACE )
+ static const char* const tableNames[] =
+ {
+ "prop", "accl", "mtrcs", "bmps", "imtrcs",
+ "enc", "swidth", "names", "accel"
+ };
+#endif
+
+
+ static
+ const FT_Frame_Field pcf_toc_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_TocRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_ULONG_LE( version ),
+ FT_FRAME_ULONG_LE( count ),
+ FT_FRAME_END
+ };
+
+
+ static
+ const FT_Frame_Field pcf_table_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_TableRec
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_ULONG_LE( type ),
+ FT_FRAME_ULONG_LE( format ),
+ FT_FRAME_ULONG_LE( size ),
+ FT_FRAME_ULONG_LE( offset ),
+ FT_FRAME_END
+ };
+
+
+ static FT_Error
+ pcf_read_TOC( FT_Stream stream,
+ PCF_Face face )
+ {
+ FT_Error error;
+ PCF_Toc toc = &face->toc;
+ PCF_Table tables;
+
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_UInt n;
+
+
+ if ( FT_STREAM_SEEK ( 0 ) ||
+ FT_STREAM_READ_FIELDS ( pcf_toc_header, toc ) )
+ return PCF_Err_Cannot_Open_Resource;
+
+ if ( toc->version != PCF_FILE_VERSION ||
+ toc->count > FT_ARRAY_MAX( face->toc.tables ) ||
+ toc->count == 0 )
+ return PCF_Err_Invalid_File_Format;
+
+ if ( FT_NEW_ARRAY( face->toc.tables, toc->count ) )
+ return PCF_Err_Out_Of_Memory;
+
+ tables = face->toc.tables;
+ for ( n = 0; n < toc->count; n++ )
+ {
+ if ( FT_STREAM_READ_FIELDS( pcf_table_header, tables ) )
+ goto Exit;
+ tables++;
+ }
+
+ /* Sort tables and check for overlaps. Because they are almost */
+ /* always ordered already, an in-place bubble sort with simultaneous */
+ /* boundary checking seems appropriate. */
+ tables = face->toc.tables;
+
+ for ( n = 0; n < toc->count - 1; n++ )
+ {
+ FT_UInt i, have_change;
+
+
+ have_change = 0;
+
+ for ( i = 0; i < toc->count - 1 - n; i++ )
+ {
+ PCF_TableRec tmp;
+
+
+ if ( tables[i].offset > tables[i + 1].offset )
+ {
+ tmp = tables[i];
+ tables[i] = tables[i + 1];
+ tables[i + 1] = tmp;
+
+ have_change = 1;
+ }
+
+ if ( ( tables[i].size > tables[i + 1].offset ) ||
+ ( tables[i].offset > tables[i + 1].offset - tables[i].size ) )
+ return PCF_Err_Invalid_Offset;
+ }
+
+ if ( !have_change )
+ break;
+ }
+
+#if defined( FT_DEBUG_LEVEL_TRACE )
+
+ {
+ FT_UInt i, j;
+ const char* name = "?";
+
+
+ FT_TRACE4(( "pcf_read_TOC:\n" ));
+
+ FT_TRACE4(( " number of tables: %ld\n", face->toc.count ));
+
+ tables = face->toc.tables;
+ for ( i = 0; i < toc->count; i++ )
+ {
+ for ( j = 0; j < sizeof ( tableNames ) / sizeof ( tableNames[0] );
+ j++ )
+ if ( tables[i].type == (FT_UInt)( 1 << j ) )
+ name = tableNames[j];
+
+ FT_TRACE4(( " %d: type=%s, format=0x%X, "
+ "size=%ld (0x%lX), offset=%ld (0x%lX)\n",
+ i, name,
+ tables[i].format,
+ tables[i].size, tables[i].size,
+ tables[i].offset, tables[i].offset ));
+ }
+ }
+
+#endif
+
+ return PCF_Err_Ok;
+
+ Exit:
+ FT_FREE( face->toc.tables );
+ return error;
+ }
+
+
+#define PCF_METRIC_SIZE 12
+
+ static
+ const FT_Frame_Field pcf_metric_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_MetricRec
+
+ FT_FRAME_START( PCF_METRIC_SIZE ),
+ FT_FRAME_SHORT_LE( leftSideBearing ),
+ FT_FRAME_SHORT_LE( rightSideBearing ),
+ FT_FRAME_SHORT_LE( characterWidth ),
+ FT_FRAME_SHORT_LE( ascent ),
+ FT_FRAME_SHORT_LE( descent ),
+ FT_FRAME_SHORT_LE( attributes ),
+ FT_FRAME_END
+ };
+
+
+ static
+ const FT_Frame_Field pcf_metric_msb_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_MetricRec
+
+ FT_FRAME_START( PCF_METRIC_SIZE ),
+ FT_FRAME_SHORT( leftSideBearing ),
+ FT_FRAME_SHORT( rightSideBearing ),
+ FT_FRAME_SHORT( characterWidth ),
+ FT_FRAME_SHORT( ascent ),
+ FT_FRAME_SHORT( descent ),
+ FT_FRAME_SHORT( attributes ),
+ FT_FRAME_END
+ };
+
+
+#define PCF_COMPRESSED_METRIC_SIZE 5
+
+ static
+ const FT_Frame_Field pcf_compressed_metric_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_Compressed_MetricRec
+
+ FT_FRAME_START( PCF_COMPRESSED_METRIC_SIZE ),
+ FT_FRAME_BYTE( leftSideBearing ),
+ FT_FRAME_BYTE( rightSideBearing ),
+ FT_FRAME_BYTE( characterWidth ),
+ FT_FRAME_BYTE( ascent ),
+ FT_FRAME_BYTE( descent ),
+ FT_FRAME_END
+ };
+
+
+ static FT_Error
+ pcf_get_metric( FT_Stream stream,
+ FT_ULong format,
+ PCF_Metric metric )
+ {
+ FT_Error error = PCF_Err_Ok;
+
+
+ if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ {
+ const FT_Frame_Field* fields;
+
+
+ /* parsing normal metrics */
+ fields = PCF_BYTE_ORDER( format ) == MSBFirst
+ ? pcf_metric_msb_header
+ : pcf_metric_header;
+
+ /* the following sets `error' but doesn't return in case of failure */
+ (void)FT_STREAM_READ_FIELDS( fields, metric );
+ }
+ else
+ {
+ PCF_Compressed_MetricRec compr;
+
+
+ /* parsing compressed metrics */
+ if ( FT_STREAM_READ_FIELDS( pcf_compressed_metric_header, &compr ) )
+ goto Exit;
+
+ metric->leftSideBearing = (FT_Short)( compr.leftSideBearing - 0x80 );
+ metric->rightSideBearing = (FT_Short)( compr.rightSideBearing - 0x80 );
+ metric->characterWidth = (FT_Short)( compr.characterWidth - 0x80 );
+ metric->ascent = (FT_Short)( compr.ascent - 0x80 );
+ metric->descent = (FT_Short)( compr.descent - 0x80 );
+ metric->attributes = 0;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ pcf_seek_to_table_type( FT_Stream stream,
+ PCF_Table tables,
+ FT_Int ntables,
+ FT_ULong type,
+ FT_ULong *aformat,
+ FT_ULong *asize )
+ {
+ FT_Error error = PCF_Err_Invalid_File_Format;
+ FT_Int i;
+
+
+ for ( i = 0; i < ntables; i++ )
+ if ( tables[i].type == type )
+ {
+ if ( stream->pos > tables[i].offset )
+ {
+ error = PCF_Err_Invalid_Stream_Skip;
+ goto Fail;
+ }
+
+ if ( FT_STREAM_SKIP( tables[i].offset - stream->pos ) )
+ {
+ error = PCF_Err_Invalid_Stream_Skip;
+ goto Fail;
+ }
+
+ *asize = tables[i].size;
+ *aformat = tables[i].format;
+
+ return PCF_Err_Ok;
+ }
+
+ Fail:
+ *asize = 0;
+ return error;
+ }
+
+
+ static FT_Bool
+ pcf_has_table_type( PCF_Table tables,
+ FT_Int ntables,
+ FT_ULong type )
+ {
+ FT_Int i;
+
+
+ for ( i = 0; i < ntables; i++ )
+ if ( tables[i].type == type )
+ return TRUE;
+
+ return FALSE;
+ }
+
+
+#define PCF_PROPERTY_SIZE 9
+
+ static
+ const FT_Frame_Field pcf_property_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_ParsePropertyRec
+
+ FT_FRAME_START( PCF_PROPERTY_SIZE ),
+ FT_FRAME_LONG_LE( name ),
+ FT_FRAME_BYTE ( isString ),
+ FT_FRAME_LONG_LE( value ),
+ FT_FRAME_END
+ };
+
+
+ static
+ const FT_Frame_Field pcf_property_msb_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_ParsePropertyRec
+
+ FT_FRAME_START( PCF_PROPERTY_SIZE ),
+ FT_FRAME_LONG( name ),
+ FT_FRAME_BYTE( isString ),
+ FT_FRAME_LONG( value ),
+ FT_FRAME_END
+ };
+
+
+ FT_LOCAL_DEF( PCF_Property )
+ pcf_find_property( PCF_Face face,
+ const FT_String* prop )
+ {
+ PCF_Property properties = face->properties;
+ FT_Bool found = 0;
+ int i;
+
+
+ for ( i = 0 ; i < face->nprops && !found; i++ )
+ {
+ if ( !ft_strcmp( properties[i].name, prop ) )
+ found = 1;
+ }
+
+ if ( found )
+ return properties + i - 1;
+ else
+ return NULL;
+ }
+
+
+ static FT_Error
+ pcf_get_properties( FT_Stream stream,
+ PCF_Face face )
+ {
+ PCF_ParseProperty props = 0;
+ PCF_Property properties;
+ FT_UInt nprops, i;
+ FT_ULong format, size;
+ FT_Error error;
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_ULong string_size;
+ FT_String* strings = 0;
+
+
+ error = pcf_seek_to_table_type( stream,
+ face->toc.tables,
+ face->toc.count,
+ PCF_PROPERTIES,
+ &format,
+ &size );
+ if ( error )
+ goto Bail;
+
+ if ( FT_READ_ULONG_LE( format ) )
+ goto Bail;
+
+ FT_TRACE4(( "pcf_get_properties:\n" ));
+
+ FT_TRACE4(( " format = %ld\n", format ));
+
+ if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ goto Bail;
+
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_ULONG( nprops );
+ else
+ (void)FT_READ_ULONG_LE( nprops );
+ if ( error )
+ goto Bail;
+
+ FT_TRACE4(( " nprop = %d\n", nprops ));
+
+ /* rough estimate */
+ if ( nprops > size / PCF_PROPERTY_SIZE )
+ {
+ error = PCF_Err_Invalid_Table;
+ goto Bail;
+ }
+
+ face->nprops = nprops;
+
+ if ( FT_NEW_ARRAY( props, nprops ) )
+ goto Bail;
+
+ for ( i = 0; i < nprops; i++ )
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ {
+ if ( FT_STREAM_READ_FIELDS( pcf_property_msb_header, props + i ) )
+ goto Bail;
+ }
+ else
+ {
+ if ( FT_STREAM_READ_FIELDS( pcf_property_header, props + i ) )
+ goto Bail;
+ }
+ }
+
+ /* pad the property array */
+ /* */
+ /* clever here - nprops is the same as the number of odd-units read, */
+ /* as only isStringProp are odd length (Keith Packard) */
+ /* */
+ if ( nprops & 3 )
+ {
+ i = 4 - ( nprops & 3 );
+ FT_Stream_Skip( stream, i );
+ }
+
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_ULONG( string_size );
+ else
+ (void)FT_READ_ULONG_LE( string_size );
+ if ( error )
+ goto Bail;
+
+ FT_TRACE4(( " string_size = %ld\n", string_size ));
+
+ /* rough estimate */
+ if ( string_size > size - nprops * PCF_PROPERTY_SIZE )
+ {
+ error = PCF_Err_Invalid_Table;
+ goto Bail;
+ }
+
+ if ( FT_NEW_ARRAY( strings, string_size ) )
+ goto Bail;
+
+ error = FT_Stream_Read( stream, (FT_Byte*)strings, string_size );
+ if ( error )
+ goto Bail;
+
+ if ( FT_NEW_ARRAY( properties, nprops ) )
+ goto Bail;
+
+ face->properties = properties;
+
+ for ( i = 0; i < nprops; i++ )
+ {
+ FT_Long name_offset = props[i].name;
+
+
+ if ( ( name_offset < 0 ) ||
+ ( (FT_ULong)name_offset > string_size ) )
+ {
+ error = PCF_Err_Invalid_Offset;
+ goto Bail;
+ }
+
+ if ( FT_STRDUP( properties[i].name, strings + name_offset ) )
+ goto Bail;
+
+ FT_TRACE4(( " %s:", properties[i].name ));
+
+ properties[i].isString = props[i].isString;
+
+ if ( props[i].isString )
+ {
+ FT_Long value_offset = props[i].value;
+
+
+ if ( ( value_offset < 0 ) ||
+ ( (FT_ULong)value_offset > string_size ) )
+ {
+ error = PCF_Err_Invalid_Offset;
+ goto Bail;
+ }
+
+ if ( FT_STRDUP( properties[i].value.atom, strings + value_offset ) )
+ goto Bail;
+
+ FT_TRACE4(( " `%s'\n", properties[i].value.atom ));
+ }
+ else
+ {
+ properties[i].value.integer = props[i].value;
+
+ FT_TRACE4(( " %d\n", properties[i].value.integer ));
+ }
+ }
+
+ error = PCF_Err_Ok;
+
+ Bail:
+ FT_FREE( props );
+ FT_FREE( strings );
+
+ return error;
+ }
+
+
+ static FT_Error
+ pcf_get_metrics( FT_Stream stream,
+ PCF_Face face )
+ {
+ FT_Error error = PCF_Err_Ok;
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_ULong format, size;
+ PCF_Metric metrics = 0;
+ FT_ULong nmetrics, i;
+
+
+ error = pcf_seek_to_table_type( stream,
+ face->toc.tables,
+ face->toc.count,
+ PCF_METRICS,
+ &format,
+ &size );
+ if ( error )
+ return error;
+
+ if ( FT_READ_ULONG_LE( format ) )
+ goto Bail;
+
+ if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) &&
+ !PCF_FORMAT_MATCH( format, PCF_COMPRESSED_METRICS ) )
+ return PCF_Err_Invalid_File_Format;
+
+ if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_ULONG( nmetrics );
+ else
+ (void)FT_READ_ULONG_LE( nmetrics );
+ }
+ else
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_USHORT( nmetrics );
+ else
+ (void)FT_READ_USHORT_LE( nmetrics );
+ }
+ if ( error )
+ return PCF_Err_Invalid_File_Format;
+
+ face->nmetrics = nmetrics;
+
+ FT_TRACE4(( "pcf_get_metrics:\n" ));
+
+ FT_TRACE4(( " number of metrics: %d\n", nmetrics ));
+
+ /* rough estimate */
+ if ( PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ {
+ if ( nmetrics > size / PCF_METRIC_SIZE )
+ return PCF_Err_Invalid_Table;
+ }
+ else
+ {
+ if ( nmetrics > size / PCF_COMPRESSED_METRIC_SIZE )
+ return PCF_Err_Invalid_Table;
+ }
+
+ if ( FT_NEW_ARRAY( face->metrics, nmetrics ) )
+ return PCF_Err_Out_Of_Memory;
+
+ metrics = face->metrics;
+ for ( i = 0; i < nmetrics; i++ )
+ {
+ pcf_get_metric( stream, format, metrics + i );
+
+ metrics[i].bits = 0;
+
+ FT_TRACE5(( " idx %d: width=%d, "
+ "lsb=%d, rsb=%d, ascent=%d, descent=%d, swidth=%d\n",
+ i,
+ ( metrics + i )->characterWidth,
+ ( metrics + i )->leftSideBearing,
+ ( metrics + i )->rightSideBearing,
+ ( metrics + i )->ascent,
+ ( metrics + i )->descent,
+ ( metrics + i )->attributes ));
+
+ if ( error )
+ break;
+ }
+
+ if ( error )
+ FT_FREE( face->metrics );
+
+ Bail:
+ return error;
+ }
+
+
+ static FT_Error
+ pcf_get_bitmaps( FT_Stream stream,
+ PCF_Face face )
+ {
+ FT_Error error = PCF_Err_Ok;
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_Long* offsets;
+ FT_Long bitmapSizes[GLYPHPADOPTIONS];
+ FT_ULong format, size;
+ int nbitmaps, i, sizebitmaps = 0;
+
+
+ error = pcf_seek_to_table_type( stream,
+ face->toc.tables,
+ face->toc.count,
+ PCF_BITMAPS,
+ &format,
+ &size );
+ if ( error )
+ return error;
+
+ error = FT_Stream_EnterFrame( stream, 8 );
+ if ( error )
+ return error;
+
+ format = FT_GET_ULONG_LE();
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ nbitmaps = FT_GET_ULONG();
+ else
+ nbitmaps = FT_GET_ULONG_LE();
+
+ FT_Stream_ExitFrame( stream );
+
+ if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ return PCF_Err_Invalid_File_Format;
+
+ FT_TRACE4(( "pcf_get_bitmaps:\n" ));
+
+ FT_TRACE4(( " number of bitmaps: %d\n", nbitmaps ));
+
+ if ( nbitmaps != face->nmetrics )
+ return PCF_Err_Invalid_File_Format;
+
+ if ( FT_NEW_ARRAY( offsets, nbitmaps ) )
+ return error;
+
+ for ( i = 0; i < nbitmaps; i++ )
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_LONG( offsets[i] );
+ else
+ (void)FT_READ_LONG_LE( offsets[i] );
+
+ FT_TRACE5(( " bitmap %d: offset %ld (0x%lX)\n",
+ i, offsets[i], offsets[i] ));
+ }
+ if ( error )
+ goto Bail;
+
+ for ( i = 0; i < GLYPHPADOPTIONS; i++ )
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ (void)FT_READ_LONG( bitmapSizes[i] );
+ else
+ (void)FT_READ_LONG_LE( bitmapSizes[i] );
+ if ( error )
+ goto Bail;
+
+ sizebitmaps = bitmapSizes[PCF_GLYPH_PAD_INDEX( format )];
+
+ FT_TRACE4(( " padding %d implies a size of %ld\n", i, bitmapSizes[i] ));
+ }
+
+ FT_TRACE4(( " %d bitmaps, padding index %ld\n",
+ nbitmaps,
+ PCF_GLYPH_PAD_INDEX( format ) ));
+ FT_TRACE4(( " bitmap size = %d\n", sizebitmaps ));
+
+ FT_UNUSED( sizebitmaps ); /* only used for debugging */
+
+ for ( i = 0; i < nbitmaps; i++ )
+ {
+ /* rough estimate */
+ if ( ( offsets[i] < 0 ) ||
+ ( (FT_ULong)offsets[i] > size ) )
+ {
+ FT_ERROR(( "pcf_get_bitmaps:"));
+ FT_ERROR(( " invalid offset to bitmap data of glyph %d\n", i ));
+ }
+ else
+ face->metrics[i].bits = stream->pos + offsets[i];
+ }
+
+ face->bitmapsFormat = format;
+
+ Bail:
+ FT_FREE( offsets );
+ return error;
+ }
+
+
+ static FT_Error
+ pcf_get_encodings( FT_Stream stream,
+ PCF_Face face )
+ {
+ FT_Error error = PCF_Err_Ok;
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_ULong format, size;
+ int firstCol, lastCol;
+ int firstRow, lastRow;
+ int nencoding, encodingOffset;
+ int i, j;
+ PCF_Encoding tmpEncoding, encoding = 0;
+
+
+ error = pcf_seek_to_table_type( stream,
+ face->toc.tables,
+ face->toc.count,
+ PCF_BDF_ENCODINGS,
+ &format,
+ &size );
+ if ( error )
+ return error;
+
+ error = FT_Stream_EnterFrame( stream, 14 );
+ if ( error )
+ return error;
+
+ format = FT_GET_ULONG_LE();
+
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ {
+ firstCol = FT_GET_SHORT();
+ lastCol = FT_GET_SHORT();
+ firstRow = FT_GET_SHORT();
+ lastRow = FT_GET_SHORT();
+ face->defaultChar = FT_GET_SHORT();
+ }
+ else
+ {
+ firstCol = FT_GET_SHORT_LE();
+ lastCol = FT_GET_SHORT_LE();
+ firstRow = FT_GET_SHORT_LE();
+ lastRow = FT_GET_SHORT_LE();
+ face->defaultChar = FT_GET_SHORT_LE();
+ }
+
+ FT_Stream_ExitFrame( stream );
+
+ if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) )
+ return PCF_Err_Invalid_File_Format;
+
+ FT_TRACE4(( "pdf_get_encodings:\n" ));
+
+ FT_TRACE4(( " firstCol %d, lastCol %d, firstRow %d, lastRow %d\n",
+ firstCol, lastCol, firstRow, lastRow ));
+
+ nencoding = ( lastCol - firstCol + 1 ) * ( lastRow - firstRow + 1 );
+
+ if ( FT_NEW_ARRAY( tmpEncoding, nencoding ) )
+ return PCF_Err_Out_Of_Memory;
+
+ error = FT_Stream_EnterFrame( stream, 2 * nencoding );
+ if ( error )
+ goto Bail;
+
+ for ( i = 0, j = 0 ; i < nencoding; i++ )
+ {
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ encodingOffset = FT_GET_SHORT();
+ else
+ encodingOffset = FT_GET_SHORT_LE();
+
+ if ( encodingOffset != -1 )
+ {
+ tmpEncoding[j].enc = ( ( ( i / ( lastCol - firstCol + 1 ) ) +
+ firstRow ) * 256 ) +
+ ( ( i % ( lastCol - firstCol + 1 ) ) +
+ firstCol );
+
+ tmpEncoding[j].glyph = (FT_Short)encodingOffset;
+
+ FT_TRACE5(( " code %d (0x%04X): idx %d\n",
+ tmpEncoding[j].enc, tmpEncoding[j].enc,
+ tmpEncoding[j].glyph ));
+
+ j++;
+ }
+ }
+ FT_Stream_ExitFrame( stream );
+
+ if ( FT_NEW_ARRAY( encoding, j ) )
+ goto Bail;
+
+ for ( i = 0; i < j; i++ )
+ {
+ encoding[i].enc = tmpEncoding[i].enc;
+ encoding[i].glyph = tmpEncoding[i].glyph;
+ }
+
+ face->nencodings = j;
+ face->encodings = encoding;
+ FT_FREE( tmpEncoding );
+
+ return error;
+
+ Bail:
+ FT_FREE( encoding );
+ FT_FREE( tmpEncoding );
+ return error;
+ }
+
+
+ static
+ const FT_Frame_Field pcf_accel_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_AccelRec
+
+ FT_FRAME_START( 20 ),
+ FT_FRAME_BYTE ( noOverlap ),
+ FT_FRAME_BYTE ( constantMetrics ),
+ FT_FRAME_BYTE ( terminalFont ),
+ FT_FRAME_BYTE ( constantWidth ),
+ FT_FRAME_BYTE ( inkInside ),
+ FT_FRAME_BYTE ( inkMetrics ),
+ FT_FRAME_BYTE ( drawDirection ),
+ FT_FRAME_SKIP_BYTES( 1 ),
+ FT_FRAME_LONG_LE ( fontAscent ),
+ FT_FRAME_LONG_LE ( fontDescent ),
+ FT_FRAME_LONG_LE ( maxOverlap ),
+ FT_FRAME_END
+ };
+
+
+ static
+ const FT_Frame_Field pcf_accel_msb_header[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PCF_AccelRec
+
+ FT_FRAME_START( 20 ),
+ FT_FRAME_BYTE ( noOverlap ),
+ FT_FRAME_BYTE ( constantMetrics ),
+ FT_FRAME_BYTE ( terminalFont ),
+ FT_FRAME_BYTE ( constantWidth ),
+ FT_FRAME_BYTE ( inkInside ),
+ FT_FRAME_BYTE ( inkMetrics ),
+ FT_FRAME_BYTE ( drawDirection ),
+ FT_FRAME_SKIP_BYTES( 1 ),
+ FT_FRAME_LONG ( fontAscent ),
+ FT_FRAME_LONG ( fontDescent ),
+ FT_FRAME_LONG ( maxOverlap ),
+ FT_FRAME_END
+ };
+
+
+ static FT_Error
+ pcf_get_accel( FT_Stream stream,
+ PCF_Face face,
+ FT_ULong type )
+ {
+ FT_ULong format, size;
+ FT_Error error = PCF_Err_Ok;
+ PCF_Accel accel = &face->accel;
+
+
+ error = pcf_seek_to_table_type( stream,
+ face->toc.tables,
+ face->toc.count,
+ type,
+ &format,
+ &size );
+ if ( error )
+ goto Bail;
+
+ if ( FT_READ_ULONG_LE( format ) )
+ goto Bail;
+
+ if ( !PCF_FORMAT_MATCH( format, PCF_DEFAULT_FORMAT ) &&
+ !PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
+ goto Bail;
+
+ if ( PCF_BYTE_ORDER( format ) == MSBFirst )
+ {
+ if ( FT_STREAM_READ_FIELDS( pcf_accel_msb_header, accel ) )
+ goto Bail;
+ }
+ else
+ {
+ if ( FT_STREAM_READ_FIELDS( pcf_accel_header, accel ) )
+ goto Bail;
+ }
+
+ error = pcf_get_metric( stream,
+ format & ( ~PCF_FORMAT_MASK ),
+ &(accel->minbounds) );
+ if ( error )
+ goto Bail;
+
+ error = pcf_get_metric( stream,
+ format & ( ~PCF_FORMAT_MASK ),
+ &(accel->maxbounds) );
+ if ( error )
+ goto Bail;
+
+ if ( PCF_FORMAT_MATCH( format, PCF_ACCEL_W_INKBOUNDS ) )
+ {
+ error = pcf_get_metric( stream,
+ format & ( ~PCF_FORMAT_MASK ),
+ &(accel->ink_minbounds) );
+ if ( error )
+ goto Bail;
+
+ error = pcf_get_metric( stream,
+ format & ( ~PCF_FORMAT_MASK ),
+ &(accel->ink_maxbounds) );
+ if ( error )
+ goto Bail;
+ }
+ else
+ {
+ accel->ink_minbounds = accel->minbounds; /* I'm not sure about this */
+ accel->ink_maxbounds = accel->maxbounds;
+ }
+
+ Bail:
+ return error;
+ }
+
+
+ static FT_Error
+ pcf_interpret_style( PCF_Face pcf )
+ {
+ FT_Error error = PCF_Err_Ok;
+ FT_Face face = FT_FACE( pcf );
+ FT_Memory memory = face->memory;
+
+ PCF_Property prop;
+
+ int nn, len;
+ char* strings[4] = { NULL, NULL, NULL, NULL };
+ int lengths[4];
+
+
+ face->style_flags = 0;
+
+ prop = pcf_find_property( pcf, "SLANT" );
+ if ( prop && prop->isString &&
+ ( *(prop->value.atom) == 'O' || *(prop->value.atom) == 'o' ||
+ *(prop->value.atom) == 'I' || *(prop->value.atom) == 'i' ) )
+ {
+ face->style_flags |= FT_STYLE_FLAG_ITALIC;
+ strings[2] = ( *(prop->value.atom) == 'O' ||
+ *(prop->value.atom) == 'o' ) ? (char *)"Oblique"
+ : (char *)"Italic";
+ }
+
+ prop = pcf_find_property( pcf, "WEIGHT_NAME" );
+ if ( prop && prop->isString &&
+ ( *(prop->value.atom) == 'B' || *(prop->value.atom) == 'b' ) )
+ {
+ face->style_flags |= FT_STYLE_FLAG_BOLD;
+ strings[1] = (char *)"Bold";
+ }
+
+ prop = pcf_find_property( pcf, "SETWIDTH_NAME" );
+ if ( prop && prop->isString &&
+ *(prop->value.atom) &&
+ !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
+ strings[3] = (char *)(prop->value.atom);
+
+ prop = pcf_find_property( pcf, "ADD_STYLE_NAME" );
+ if ( prop && prop->isString &&
+ *(prop->value.atom) &&
+ !( *(prop->value.atom) == 'N' || *(prop->value.atom) == 'n' ) )
+ strings[0] = (char *)(prop->value.atom);
+
+ for ( len = 0, nn = 0; nn < 4; nn++ )
+ {
+ lengths[nn] = 0;
+ if ( strings[nn] )
+ {
+ lengths[nn] = ft_strlen( strings[nn] );
+ len += lengths[nn] + 1;
+ }
+ }
+
+ if ( len == 0 )
+ {
+ strings[0] = (char *)"Regular";
+ lengths[0] = ft_strlen( strings[0] );
+ len = lengths[0] + 1;
+ }
+
+ {
+ char* s;
+
+
+ if ( FT_ALLOC( face->style_name, len ) )
+ return error;
+
+ s = face->style_name;
+
+ for ( nn = 0; nn < 4; nn++ )
+ {
+ char* src = strings[nn];
+
+
+ len = lengths[nn];
+
+ if ( src == NULL )
+ continue;
+
+ /* separate elements with a space */
+ if ( s != face->style_name )
+ *s++ = ' ';
+
+ ft_memcpy( s, src, len );
+
+ /* need to convert spaces to dashes for */
+ /* add_style_name and setwidth_name */
+ if ( nn == 0 || nn == 3 )
+ {
+ int mm;
+
+
+ for ( mm = 0; mm < len; mm++ )
+ if (s[mm] == ' ')
+ s[mm] = '-';
+ }
+
+ s += len;
+ }
+ *s = 0;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pcf_load_font( FT_Stream stream,
+ PCF_Face face )
+ {
+ FT_Error error = PCF_Err_Ok;
+ FT_Memory memory = FT_FACE(face)->memory;
+ FT_Bool hasBDFAccelerators;
+
+
+ error = pcf_read_TOC( stream, face );
+ if ( error )
+ goto Exit;
+
+ error = pcf_get_properties( stream, face );
+ if ( error )
+ goto Exit;
+
+ /* Use the old accelerators if no BDF accelerators are in the file. */
+ hasBDFAccelerators = pcf_has_table_type( face->toc.tables,
+ face->toc.count,
+ PCF_BDF_ACCELERATORS );
+ if ( !hasBDFAccelerators )
+ {
+ error = pcf_get_accel( stream, face, PCF_ACCELERATORS );
+ if ( error )
+ goto Exit;
+ }
+
+ /* metrics */
+ error = pcf_get_metrics( stream, face );
+ if ( error )
+ goto Exit;
+
+ /* bitmaps */
+ error = pcf_get_bitmaps( stream, face );
+ if ( error )
+ goto Exit;
+
+ /* encodings */
+ error = pcf_get_encodings( stream, face );
+ if ( error )
+ goto Exit;
+
+ /* BDF style accelerators (i.e. bounds based on encoded glyphs) */
+ if ( hasBDFAccelerators )
+ {
+ error = pcf_get_accel( stream, face, PCF_BDF_ACCELERATORS );
+ if ( error )
+ goto Exit;
+ }
+
+ /* XXX: TO DO: inkmetrics and glyph_names are missing */
+
+ /* now construct the face object */
+ {
+ FT_Face root = FT_FACE( face );
+ PCF_Property prop;
+
+
+ root->num_faces = 1;
+ root->face_index = 0;
+ root->face_flags = FT_FACE_FLAG_FIXED_SIZES |
+ FT_FACE_FLAG_HORIZONTAL |
+ FT_FACE_FLAG_FAST_GLYPHS;
+
+ if ( face->accel.constantWidth )
+ root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ if ( ( error = pcf_interpret_style( face ) ) != 0 )
+ goto Exit;
+
+ prop = pcf_find_property( face, "FAMILY_NAME" );
+ if ( prop && prop->isString )
+ {
+ if ( FT_STRDUP( root->family_name, prop->value.atom ) )
+ goto Exit;
+ }
+ else
+ root->family_name = NULL;
+
+ /*
+ * Note: We shift all glyph indices by +1 since we must
+ * respect the convention that glyph 0 always corresponds
+ * to the `missing glyph'.
+ *
+ * This implies bumping the number of `available' glyphs by 1.
+ */
+ root->num_glyphs = face->nmetrics + 1;
+
+ root->num_fixed_sizes = 1;
+ if ( FT_NEW_ARRAY( root->available_sizes, 1 ) )
+ goto Exit;
+
+ {
+ FT_Bitmap_Size* bsize = root->available_sizes;
+ FT_Short resolution_x = 0, resolution_y = 0;
+
+
+ FT_MEM_ZERO( bsize, sizeof ( FT_Bitmap_Size ) );
+
+#if 0
+ bsize->height = face->accel.maxbounds.ascent << 6;
+#endif
+ bsize->height = (FT_Short)( face->accel.fontAscent +
+ face->accel.fontDescent );
+
+ prop = pcf_find_property( face, "AVERAGE_WIDTH" );
+ if ( prop )
+ bsize->width = (FT_Short)( ( prop->value.integer + 5 ) / 10 );
+ else
+ bsize->width = (FT_Short)( bsize->height * 2/3 );
+
+ prop = pcf_find_property( face, "POINT_SIZE" );
+ if ( prop )
+ /* convert from 722.7 decipoints to 72 points per inch */
+ bsize->size =
+ (FT_Pos)( ( prop->value.integer * 64 * 7200 + 36135L ) / 72270L );
+
+ prop = pcf_find_property( face, "PIXEL_SIZE" );
+ if ( prop )
+ bsize->y_ppem = (FT_Short)prop->value.integer << 6;
+
+ prop = pcf_find_property( face, "RESOLUTION_X" );
+ if ( prop )
+ resolution_x = (FT_Short)prop->value.integer;
+
+ prop = pcf_find_property( face, "RESOLUTION_Y" );
+ if ( prop )
+ resolution_y = (FT_Short)prop->value.integer;
+
+ if ( bsize->y_ppem == 0 )
+ {
+ bsize->y_ppem = bsize->size;
+ if ( resolution_y )
+ bsize->y_ppem = bsize->y_ppem * resolution_y / 72;
+ }
+ if ( resolution_x && resolution_y )
+ bsize->x_ppem = bsize->y_ppem * resolution_x / resolution_y;
+ else
+ bsize->x_ppem = bsize->y_ppem;
+ }
+
+ /* set up charset */
+ {
+ PCF_Property charset_registry = 0, charset_encoding = 0;
+
+
+ charset_registry = pcf_find_property( face, "CHARSET_REGISTRY" );
+ charset_encoding = pcf_find_property( face, "CHARSET_ENCODING" );
+
+ if ( charset_registry && charset_registry->isString &&
+ charset_encoding && charset_encoding->isString )
+ {
+ if ( FT_STRDUP( face->charset_encoding,
+ charset_encoding->value.atom ) ||
+ FT_STRDUP( face->charset_registry,
+ charset_registry->value.atom ) )
+ goto Exit;
+ }
+ }
+ }
+
+ Exit:
+ if ( error )
+ {
+ /* This is done to respect the behaviour of the original */
+ /* PCF font driver. */
+ error = PCF_Err_Invalid_File_Format;
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/pcf/pcfread.h b/src/pcf/pcfread.h
new file mode 100644
index 0000000..c9524f1
--- /dev/null
+++ b/src/pcf/pcfread.h
@@ -0,0 +1,45 @@
+/* pcfread.h
+
+ FreeType font driver for pcf fonts
+
+ Copyright 2003 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __PCFREAD_H__
+#define __PCFREAD_H__
+
+
+#include <ft2build.h>
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( PCF_Property )
+ pcf_find_property( PCF_Face face,
+ const FT_String* prop );
+
+FT_END_HEADER
+
+#endif /* __PCFREAD_H__ */
+
+
+/* END */
diff --git a/src/pcf/pcfutil.c b/src/pcf/pcfutil.c
new file mode 100644
index 0000000..67ddbe8
--- /dev/null
+++ b/src/pcf/pcfutil.c
@@ -0,0 +1,104 @@
+/*
+
+Copyright 1990, 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+*/
+/* $XFree86: xc/lib/font/util/utilbitmap.c,v 1.3 1999/08/22 08:58:58 dawes Exp $ */
+
+/*
+ * Author: Keith Packard, MIT X Consortium
+ */
+
+/* Modified for use with FreeType */
+
+
+#include <ft2build.h>
+#include "pcfutil.h"
+
+
+ /*
+ * Invert bit order within each BYTE of an array.
+ */
+
+ FT_LOCAL_DEF( void )
+ BitOrderInvert( unsigned char* buf,
+ int nbytes )
+ {
+ for ( ; --nbytes >= 0; buf++ )
+ {
+ unsigned int val = *buf;
+
+
+ val = ( ( val >> 1 ) & 0x55 ) | ( ( val << 1 ) & 0xAA );
+ val = ( ( val >> 2 ) & 0x33 ) | ( ( val << 2 ) & 0xCC );
+ val = ( ( val >> 4 ) & 0x0F ) | ( ( val << 4 ) & 0xF0 );
+
+ *buf = (unsigned char)val;
+ }
+ }
+
+
+ /*
+ * Invert byte order within each 16-bits of an array.
+ */
+
+ FT_LOCAL_DEF( void )
+ TwoByteSwap( unsigned char* buf,
+ int nbytes )
+ {
+ unsigned char c;
+
+
+ for ( ; nbytes >= 2; nbytes -= 2, buf += 2 )
+ {
+ c = buf[0];
+ buf[0] = buf[1];
+ buf[1] = c;
+ }
+ }
+
+ /*
+ * Invert byte order within each 32-bits of an array.
+ */
+
+ FT_LOCAL_DEF( void )
+ FourByteSwap( unsigned char* buf,
+ int nbytes )
+ {
+ unsigned char c;
+
+
+ for ( ; nbytes >= 4; nbytes -= 4, buf += 4 )
+ {
+ c = buf[0];
+ buf[0] = buf[3];
+ buf[3] = c;
+
+ c = buf[1];
+ buf[1] = buf[2];
+ buf[2] = c;
+ }
+ }
+
+
+/* END */
diff --git a/src/pcf/pcfutil.h b/src/pcf/pcfutil.h
new file mode 100644
index 0000000..1557be3
--- /dev/null
+++ b/src/pcf/pcfutil.h
@@ -0,0 +1,55 @@
+/* pcfutil.h
+
+ FreeType font driver for pcf fonts
+
+ Copyright 2000, 2001, 2004 by
+ Francesco Zappa Nardelli
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+
+#ifndef __PCFUTIL_H__
+#define __PCFUTIL_H__
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( void )
+ BitOrderInvert( unsigned char* buf,
+ int nbytes );
+
+ FT_LOCAL( void )
+ TwoByteSwap( unsigned char* buf,
+ int nbytes );
+
+ FT_LOCAL( void )
+ FourByteSwap( unsigned char* buf,
+ int nbytes );
+
+FT_END_HEADER
+
+#endif /* __PCFUTIL_H__ */
+
+
+/* END */
diff --git a/src/pcf/rules.mk b/src/pcf/rules.mk
new file mode 100644
index 0000000..1ad4ba8
--- /dev/null
+++ b/src/pcf/rules.mk
@@ -0,0 +1,80 @@
+#
+# FreeType 2 pcf driver configuration rules
+#
+
+
+# Copyright (C) 2000, 2001, 2003 by
+# Francesco Zappa Nardelli
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+# pcf driver directory
+#
+PCF_DIR := $(SRC_DIR)/pcf
+
+
+PCF_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PCF_DIR))
+
+
+# pcf driver sources (i.e., C files)
+#
+PCF_DRV_SRC := $(PCF_DIR)/pcfread.c \
+ $(PCF_DIR)/pcfdrivr.c \
+ $(PCF_DIR)/pcfutil.c
+
+# pcf driver headers
+#
+PCF_DRV_H := $(PCF_DIR)/pcf.h \
+ $(PCF_DIR)/pcfdrivr.h \
+ $(PCF_DIR)/pcfutil.h \
+ $(PCF_DIR)/pcferror.h
+
+# pcf driver object(s)
+#
+# PCF_DRV_OBJ_M is used during `multi' builds
+# PCF_DRV_OBJ_S is used during `single' builds
+#
+PCF_DRV_OBJ_M := $(PCF_DRV_SRC:$(PCF_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PCF_DRV_OBJ_S := $(OBJ_DIR)/pcf.$O
+
+# pcf driver source file for single build
+#
+PCF_DRV_SRC_S := $(PCF_DIR)/pcf.c
+
+
+# pcf driver - single object
+#
+$(PCF_DRV_OBJ_S): $(PCF_DRV_SRC_S) $(PCF_DRV_SRC) $(FREETYPE_H) $(PCF_DRV_H)
+ $(PCF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PCF_DRV_SRC_S))
+
+
+# pcf driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PCF_DIR)/%.c $(FREETYPE_H) $(PCF_DRV_H)
+ $(PCF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PCF_DRV_OBJ_S)
+DRV_OBJS_M += $(PCF_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/pfr/Jamfile b/src/pfr/Jamfile
new file mode 100644
index 0000000..9e2f2b8
--- /dev/null
+++ b/src/pfr/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/pfr Jamfile
+#
+# Copyright 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) pfr ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = pfrdrivr pfrgload pfrload pfrobjs pfrcmap pfrsbit ;
+ }
+ else
+ {
+ _sources = pfr ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/pfr Jamfile
diff --git a/src/pfr/module.mk b/src/pfr/module.mk
new file mode 100644
index 0000000..53ab34a
--- /dev/null
+++ b/src/pfr/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 PFR module definition
+#
+
+
+# Copyright 2002, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += PFR_DRIVER
+
+define PFR_DRIVER
+$(OPEN_DRIVER)pfr_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)pfr $(ECHO_DRIVER_DESC)PFR/TrueDoc font files with extension *.pfr$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/pfr/pfr.c b/src/pfr/pfr.c
new file mode 100644
index 0000000..eb2c4ed
--- /dev/null
+++ b/src/pfr/pfr.c
@@ -0,0 +1,29 @@
+/***************************************************************************/
+/* */
+/* pfr.c */
+/* */
+/* FreeType PFR driver component. */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+
+#include "pfrload.c"
+#include "pfrgload.c"
+#include "pfrcmap.c"
+#include "pfrobjs.c"
+#include "pfrdrivr.c"
+#include "pfrsbit.c"
+
+/* END */
diff --git a/src/pfr/pfrcmap.c b/src/pfr/pfrcmap.c
new file mode 100644
index 0000000..c8faee0
--- /dev/null
+++ b/src/pfr/pfrcmap.c
@@ -0,0 +1,163 @@
+/***************************************************************************/
+/* */
+/* pfrcmap.c */
+/* */
+/* FreeType PFR cmap handling (body). */
+/* */
+/* Copyright 2002, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "pfrcmap.h"
+#include "pfrobjs.h"
+#include FT_INTERNAL_DEBUG_H
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_cmap_init( PFR_CMap cmap )
+ {
+ FT_Error error = PFR_Err_Ok;
+ PFR_Face face = (PFR_Face)FT_CMAP_FACE( cmap );
+
+
+ cmap->num_chars = face->phy_font.num_chars;
+ cmap->chars = face->phy_font.chars;
+
+ /* just for safety, check that the character entries are correctly */
+ /* sorted in increasing character code order */
+ {
+ FT_UInt n;
+
+
+ for ( n = 1; n < cmap->num_chars; n++ )
+ {
+ if ( cmap->chars[n - 1].char_code >= cmap->chars[n].char_code )
+ {
+ error = PFR_Err_Invalid_Table;
+ goto Exit;
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ pfr_cmap_done( PFR_CMap cmap )
+ {
+ cmap->chars = NULL;
+ cmap->num_chars = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ pfr_cmap_char_index( PFR_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt min = 0;
+ FT_UInt max = cmap->num_chars;
+ FT_UInt mid;
+ PFR_Char gchar;
+
+
+ while ( min < max )
+ {
+ mid = min + ( max - min ) / 2;
+ gchar = cmap->chars + mid;
+
+ if ( gchar->char_code == char_code )
+ return mid + 1;
+
+ if ( gchar->char_code < char_code )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ pfr_cmap_char_next( PFR_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+
+
+ Restart:
+ {
+ FT_UInt min = 0;
+ FT_UInt max = cmap->num_chars;
+ FT_UInt mid;
+ PFR_Char gchar;
+
+
+ while ( min < max )
+ {
+ mid = min + ( ( max - min ) >> 1 );
+ gchar = cmap->chars + mid;
+
+ if ( gchar->char_code == char_code )
+ {
+ result = mid;
+ if ( result != 0 )
+ {
+ result++;
+ goto Exit;
+ }
+
+ char_code++;
+ goto Restart;
+ }
+
+ if ( gchar->char_code < char_code )
+ min = mid+1;
+ else
+ max = mid;
+ }
+
+ /* we didn't find it, but we have a pair just above it */
+ char_code = 0;
+
+ if ( min < cmap->num_chars )
+ {
+ gchar = cmap->chars + min;
+ result = min;
+ if ( result != 0 )
+ {
+ result++;
+ char_code = gchar->char_code;
+ }
+ }
+ }
+
+ Exit:
+ *pchar_code = char_code;
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ pfr_cmap_class_rec =
+ {
+ sizeof ( PFR_CMapRec ),
+
+ (FT_CMap_InitFunc) pfr_cmap_init,
+ (FT_CMap_DoneFunc) pfr_cmap_done,
+ (FT_CMap_CharIndexFunc)pfr_cmap_char_index,
+ (FT_CMap_CharNextFunc) pfr_cmap_char_next
+ };
+
+
+/* END */
diff --git a/src/pfr/pfrcmap.h b/src/pfr/pfrcmap.h
new file mode 100644
index 0000000..a626953
--- /dev/null
+++ b/src/pfr/pfrcmap.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* pfrcmap.h */
+/* */
+/* FreeType PFR cmap handling (specification). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRCMAP_H__
+#define __PFRCMAP_H__
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include "pfrtypes.h"
+
+
+FT_BEGIN_HEADER
+
+ typedef struct PFR_CMapRec_
+ {
+ FT_CMapRec cmap;
+ FT_UInt num_chars;
+ PFR_Char chars;
+
+ } PFR_CMapRec, *PFR_CMap;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec pfr_cmap_class_rec;
+
+FT_END_HEADER
+
+
+#endif /* __PFRCMAP_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrdrivr.c b/src/pfr/pfrdrivr.c
new file mode 100644
index 0000000..4020672
--- /dev/null
+++ b/src/pfr/pfrdrivr.c
@@ -0,0 +1,207 @@
+/***************************************************************************/
+/* */
+/* pfrdrivr.c */
+/* */
+/* FreeType PFR driver interface (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_SERVICE_PFR_H
+#include FT_SERVICE_XFREE86_NAME_H
+#include "pfrdrivr.h"
+#include "pfrobjs.h"
+
+#include "pfrerror.h"
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_get_kerning( FT_Face pfrface, /* PFR_Face */
+ FT_UInt left,
+ FT_UInt right,
+ FT_Vector *avector )
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ PFR_PhyFont phys = &face->phy_font;
+
+
+ pfr_face_get_kerning( pfrface, left, right, avector );
+
+ /* convert from metrics to outline units when necessary */
+ if ( phys->outline_resolution != phys->metrics_resolution )
+ {
+ if ( avector->x != 0 )
+ avector->x = FT_MulDiv( avector->x, phys->outline_resolution,
+ phys->metrics_resolution );
+
+ if ( avector->y != 0 )
+ avector->y = FT_MulDiv( avector->x, phys->outline_resolution,
+ phys->metrics_resolution );
+ }
+
+ return PFR_Err_Ok;
+ }
+
+
+ /*
+ * PFR METRICS SERVICE
+ *
+ */
+
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_get_advance( FT_Face pfrface, /* PFR_Face */
+ FT_UInt gindex,
+ FT_Pos *anadvance )
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ FT_Error error = PFR_Err_Bad_Argument;
+
+
+ *anadvance = 0;
+ if ( face )
+ {
+ PFR_PhyFont phys = &face->phy_font;
+
+
+ if ( gindex < phys->num_chars )
+ {
+ *anadvance = phys->chars[gindex].advance;
+ error = 0;
+ }
+ }
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_get_metrics( FT_Face pfrface, /* PFR_Face */
+ FT_UInt *anoutline_resolution,
+ FT_UInt *ametrics_resolution,
+ FT_Fixed *ametrics_x_scale,
+ FT_Fixed *ametrics_y_scale )
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ PFR_PhyFont phys = &face->phy_font;
+ FT_Fixed x_scale, y_scale;
+ FT_Size size = face->root.size;
+
+
+ if ( anoutline_resolution )
+ *anoutline_resolution = phys->outline_resolution;
+
+ if ( ametrics_resolution )
+ *ametrics_resolution = phys->metrics_resolution;
+
+ x_scale = 0x10000L;
+ y_scale = 0x10000L;
+
+ if ( size )
+ {
+ x_scale = FT_DivFix( size->metrics.x_ppem << 6,
+ phys->metrics_resolution );
+
+ y_scale = FT_DivFix( size->metrics.y_ppem << 6,
+ phys->metrics_resolution );
+ }
+
+ if ( ametrics_x_scale )
+ *ametrics_x_scale = x_scale;
+
+ if ( ametrics_y_scale )
+ *ametrics_y_scale = y_scale;
+
+ return PFR_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Service_PfrMetricsRec pfr_metrics_service_rec =
+ {
+ pfr_get_metrics,
+ pfr_face_get_kerning,
+ pfr_get_advance
+ };
+
+
+ /*
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec pfr_services[] =
+ {
+ { FT_SERVICE_ID_PFR_METRICS, &pfr_metrics_service_rec },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_PFR },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ pfr_get_service( FT_Module module,
+ const FT_String* service_id )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( pfr_services, service_id );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec pfr_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE,
+
+ sizeof( FT_DriverRec ),
+
+ "pfr",
+ 0x10000L,
+ 0x20000L,
+
+ NULL,
+
+ 0,
+ 0,
+ pfr_get_service
+ },
+
+ sizeof( PFR_FaceRec ),
+ sizeof( PFR_SizeRec ),
+ sizeof( PFR_SlotRec ),
+
+ pfr_face_init,
+ pfr_face_done,
+ 0, /* FT_Size_InitFunc */
+ 0, /* FT_Size_DoneFunc */
+ pfr_slot_init,
+ pfr_slot_done,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ pfr_slot_load,
+
+ pfr_get_kerning,
+ 0, /* FT_Face_AttachFunc */
+ 0, /* FT_Face_GetAdvancesFunc */
+ 0, /* FT_Size_RequestFunc */
+ 0, /* FT_Size_SelectFunc */
+ };
+
+
+/* END */
diff --git a/src/pfr/pfrdrivr.h b/src/pfr/pfrdrivr.h
new file mode 100644
index 0000000..36f1205
--- /dev/null
+++ b/src/pfr/pfrdrivr.h
@@ -0,0 +1,39 @@
+/***************************************************************************/
+/* */
+/* pfrdrivr.h */
+/* */
+/* High-level Type PFR driver interface (specification). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRDRIVR_H__
+#define __PFRDRIVR_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) pfr_driver_class;
+
+
+FT_END_HEADER
+
+
+#endif /* __PFRDRIVR_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrerror.h b/src/pfr/pfrerror.h
new file mode 100644
index 0000000..2e1c401
--- /dev/null
+++ b/src/pfr/pfrerror.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* pfrerror.h */
+/* */
+/* PFR error codes (specification only). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the PFR error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __PFRERROR_H__
+#define __PFRERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX PFR_Err_
+#define FT_ERR_BASE FT_Mod_Err_PFR
+
+#include FT_ERRORS_H
+
+#endif /* __PFRERROR_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrgload.c b/src/pfr/pfrgload.c
new file mode 100644
index 0000000..3bb1733
--- /dev/null
+++ b/src/pfr/pfrgload.c
@@ -0,0 +1,828 @@
+/***************************************************************************/
+/* */
+/* pfrgload.c */
+/* */
+/* FreeType PFR glyph loader (body). */
+/* */
+/* Copyright 2002, 2003, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "pfrgload.h"
+#include "pfrsbit.h"
+#include "pfrload.h" /* for macro definitions */
+#include FT_INTERNAL_DEBUG_H
+
+#include "pfrerror.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pfr
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PFR GLYPH BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ pfr_glyph_init( PFR_Glyph glyph,
+ FT_GlyphLoader loader )
+ {
+ FT_ZERO( glyph );
+
+ glyph->loader = loader;
+ glyph->path_begun = 0;
+
+ FT_GlyphLoader_Rewind( loader );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ pfr_glyph_done( PFR_Glyph glyph )
+ {
+ FT_Memory memory = glyph->loader->memory;
+
+
+ FT_FREE( glyph->x_control );
+ glyph->y_control = NULL;
+
+ glyph->max_xy_control = 0;
+#if 0
+ glyph->num_x_control = 0;
+ glyph->num_y_control = 0;
+#endif
+
+ FT_FREE( glyph->subs );
+
+ glyph->max_subs = 0;
+ glyph->num_subs = 0;
+
+ glyph->loader = NULL;
+ glyph->path_begun = 0;
+ }
+
+
+ /* close current contour, if any */
+ static void
+ pfr_glyph_close_contour( PFR_Glyph glyph )
+ {
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Outline* outline = &loader->current.outline;
+ FT_Int last, first;
+
+
+ if ( !glyph->path_begun )
+ return;
+
+ /* compute first and last point indices in current glyph outline */
+ last = outline->n_points - 1;
+ first = 0;
+ if ( outline->n_contours > 0 )
+ first = outline->contours[outline->n_contours - 1];
+
+ /* if the last point falls on the same location than the first one */
+ /* we need to delete it */
+ if ( last > first )
+ {
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + last;
+
+
+ if ( p1->x == p2->x && p1->y == p2->y )
+ {
+ outline->n_points--;
+ last--;
+ }
+ }
+
+ /* don't add empty contours */
+ if ( last >= first )
+ outline->contours[outline->n_contours++] = (short)last;
+
+ glyph->path_begun = 0;
+ }
+
+
+ /* reset glyph to start the loading of a new glyph */
+ static void
+ pfr_glyph_start( PFR_Glyph glyph )
+ {
+ glyph->path_begun = 0;
+ }
+
+
+ static FT_Error
+ pfr_glyph_line_to( PFR_Glyph glyph,
+ FT_Vector* to )
+ {
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Outline* outline = &loader->current.outline;
+ FT_Error error;
+
+
+ /* check that we have begun a new path */
+ if ( !glyph->path_begun )
+ {
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
+ goto Exit;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 );
+ if ( !error )
+ {
+ FT_UInt n = outline->n_points;
+
+
+ outline->points[n] = *to;
+ outline->tags [n] = FT_CURVE_TAG_ON;
+
+ outline->n_points++;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ pfr_glyph_curve_to( PFR_Glyph glyph,
+ FT_Vector* control1,
+ FT_Vector* control2,
+ FT_Vector* to )
+ {
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Outline* outline = &loader->current.outline;
+ FT_Error error;
+
+
+ /* check that we have begun a new path */
+ if ( !glyph->path_begun )
+ {
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
+ goto Exit;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 );
+ if ( !error )
+ {
+ FT_Vector* vec = outline->points + outline->n_points;
+ FT_Byte* tag = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ vec[0] = *control1;
+ vec[1] = *control2;
+ vec[2] = *to;
+ tag[0] = FT_CURVE_TAG_CUBIC;
+ tag[1] = FT_CURVE_TAG_CUBIC;
+ tag[2] = FT_CURVE_TAG_ON;
+
+ outline->n_points = (FT_Short)( outline->n_points + 3 );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ pfr_glyph_move_to( PFR_Glyph glyph,
+ FT_Vector* to )
+ {
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Error error;
+
+
+ /* close current contour if any */
+ pfr_glyph_close_contour( glyph );
+
+ /* indicate that a new contour has started */
+ glyph->path_begun = 1;
+
+ /* check that there is space for a new contour and a new point */
+ error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 );
+ if ( !error )
+ /* add new start point */
+ error = pfr_glyph_line_to( glyph, to );
+
+ return error;
+ }
+
+
+ static void
+ pfr_glyph_end( PFR_Glyph glyph )
+ {
+ /* close current contour if any */
+ pfr_glyph_close_contour( glyph );
+
+ /* merge the current glyph into the stack */
+ FT_GlyphLoader_Add( glyph->loader );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PFR GLYPH LOADER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* load a simple glyph */
+ static FT_Error
+ pfr_glyph_load_simple( PFR_Glyph glyph,
+ FT_Byte* p,
+ FT_Byte* limit )
+ {
+ FT_Error error = 0;
+ FT_Memory memory = glyph->loader->memory;
+ FT_UInt flags, x_count, y_count, i, count, mask;
+ FT_Int x;
+
+
+ PFR_CHECK( 1 );
+ flags = PFR_NEXT_BYTE( p );
+
+ /* test for composite glyphs */
+ if ( flags & PFR_GLYPH_IS_COMPOUND )
+ goto Failure;
+
+ x_count = 0;
+ y_count = 0;
+
+ if ( flags & PFR_GLYPH_1BYTE_XYCOUNT )
+ {
+ PFR_CHECK( 1 );
+ count = PFR_NEXT_BYTE( p );
+ x_count = ( count & 15 );
+ y_count = ( count >> 4 );
+ }
+ else
+ {
+ if ( flags & PFR_GLYPH_XCOUNT )
+ {
+ PFR_CHECK( 1 );
+ x_count = PFR_NEXT_BYTE( p );
+ }
+
+ if ( flags & PFR_GLYPH_YCOUNT )
+ {
+ PFR_CHECK( 1 );
+ y_count = PFR_NEXT_BYTE( p );
+ }
+ }
+
+ count = x_count + y_count;
+
+ /* re-allocate array when necessary */
+ if ( count > glyph->max_xy_control )
+ {
+ FT_UInt new_max = FT_PAD_CEIL( count, 8 );
+
+
+ if ( FT_RENEW_ARRAY( glyph->x_control,
+ glyph->max_xy_control,
+ new_max ) )
+ goto Exit;
+
+ glyph->max_xy_control = new_max;
+ }
+
+ glyph->y_control = glyph->x_control + x_count;
+
+ mask = 0;
+ x = 0;
+
+ for ( i = 0; i < count; i++ )
+ {
+ if ( ( i & 7 ) == 0 )
+ {
+ PFR_CHECK( 1 );
+ mask = PFR_NEXT_BYTE( p );
+ }
+
+ if ( mask & 1 )
+ {
+ PFR_CHECK( 2 );
+ x = PFR_NEXT_SHORT( p );
+ }
+ else
+ {
+ PFR_CHECK( 1 );
+ x += PFR_NEXT_BYTE( p );
+ }
+
+ glyph->x_control[i] = x;
+
+ mask >>= 1;
+ }
+
+ /* XXX: for now we ignore the secondary stroke and edge definitions */
+ /* since we don't want to support native PFR hinting */
+ /* */
+ if ( flags & PFR_GLYPH_EXTRA_ITEMS )
+ {
+ error = pfr_extra_items_skip( &p, limit );
+ if ( error )
+ goto Exit;
+ }
+
+ pfr_glyph_start( glyph );
+
+ /* now load a simple glyph */
+ {
+ FT_Vector pos[4];
+ FT_Vector* cur;
+
+
+ pos[0].x = pos[0].y = 0;
+ pos[3] = pos[0];
+
+ for (;;)
+ {
+ FT_UInt format, format_low, args_format = 0, args_count, n;
+
+
+ /***************************************************************/
+ /* read instruction */
+ /* */
+ PFR_CHECK( 1 );
+ format = PFR_NEXT_BYTE( p );
+ format_low = format & 15;
+
+ switch ( format >> 4 )
+ {
+ case 0: /* end glyph */
+ FT_TRACE6(( "- end glyph" ));
+ args_count = 0;
+ break;
+
+ case 1: /* general line operation */
+ FT_TRACE6(( "- general line" ));
+ goto Line1;
+
+ case 4: /* move to inside contour */
+ FT_TRACE6(( "- move to inside" ));
+ goto Line1;
+
+ case 5: /* move to outside contour */
+ FT_TRACE6(( "- move to outside" ));
+ Line1:
+ args_format = format_low;
+ args_count = 1;
+ break;
+
+ case 2: /* horizontal line to */
+ FT_TRACE6(( "- horizontal line to cx.%d", format_low ));
+ if ( format_low > x_count )
+ goto Failure;
+ pos[0].x = glyph->x_control[format_low];
+ pos[0].y = pos[3].y;
+ pos[3] = pos[0];
+ args_count = 0;
+ break;
+
+ case 3: /* vertical line to */
+ FT_TRACE6(( "- vertical line to cy.%d", format_low ));
+ if ( format_low > y_count )
+ goto Failure;
+ pos[0].x = pos[3].x;
+ pos[0].y = glyph->y_control[format_low];
+ pos[3] = pos[0];
+ args_count = 0;
+ break;
+
+ case 6: /* horizontal to vertical curve */
+ FT_TRACE6(( "- hv curve " ));
+ args_format = 0xB8E;
+ args_count = 3;
+ break;
+
+ case 7: /* vertical to horizontal curve */
+ FT_TRACE6(( "- vh curve" ));
+ args_format = 0xE2B;
+ args_count = 3;
+ break;
+
+ default: /* general curve to */
+ FT_TRACE6(( "- general curve" ));
+ args_count = 4;
+ args_format = format_low;
+ }
+
+ /***********************************************************/
+ /* now read arguments */
+ /* */
+ cur = pos;
+ for ( n = 0; n < args_count; n++ )
+ {
+ FT_UInt idx;
+ FT_Int delta;
+
+
+ /* read the X argument */
+ switch ( args_format & 3 )
+ {
+ case 0: /* 8-bit index */
+ PFR_CHECK( 1 );
+ idx = PFR_NEXT_BYTE( p );
+ if ( idx > x_count )
+ goto Failure;
+ cur->x = glyph->x_control[idx];
+ FT_TRACE7(( " cx#%d", idx ));
+ break;
+
+ case 1: /* 16-bit value */
+ PFR_CHECK( 2 );
+ cur->x = PFR_NEXT_SHORT( p );
+ FT_TRACE7(( " x.%d", cur->x ));
+ break;
+
+ case 2: /* 8-bit delta */
+ PFR_CHECK( 1 );
+ delta = PFR_NEXT_INT8( p );
+ cur->x = pos[3].x + delta;
+ FT_TRACE7(( " dx.%d", delta ));
+ break;
+
+ default:
+ FT_TRACE7(( " |" ));
+ cur->x = pos[3].x;
+ }
+
+ /* read the Y argument */
+ switch ( ( args_format >> 2 ) & 3 )
+ {
+ case 0: /* 8-bit index */
+ PFR_CHECK( 1 );
+ idx = PFR_NEXT_BYTE( p );
+ if ( idx > y_count )
+ goto Failure;
+ cur->y = glyph->y_control[idx];
+ FT_TRACE7(( " cy#%d", idx ));
+ break;
+
+ case 1: /* 16-bit absolute value */
+ PFR_CHECK( 2 );
+ cur->y = PFR_NEXT_SHORT( p );
+ FT_TRACE7(( " y.%d", cur->y ));
+ break;
+
+ case 2: /* 8-bit delta */
+ PFR_CHECK( 1 );
+ delta = PFR_NEXT_INT8( p );
+ cur->y = pos[3].y + delta;
+ FT_TRACE7(( " dy.%d", delta ));
+ break;
+
+ default:
+ FT_TRACE7(( " -" ));
+ cur->y = pos[3].y;
+ }
+
+ /* read the additional format flag for the general curve */
+ if ( n == 0 && args_count == 4 )
+ {
+ PFR_CHECK( 1 );
+ args_format = PFR_NEXT_BYTE( p );
+ args_count--;
+ }
+ else
+ args_format >>= 4;
+
+ /* save the previous point */
+ pos[3] = cur[0];
+ cur++;
+ }
+
+ FT_TRACE7(( "\n" ));
+
+ /***********************************************************/
+ /* finally, execute instruction */
+ /* */
+ switch ( format >> 4 )
+ {
+ case 0: /* end glyph => EXIT */
+ pfr_glyph_end( glyph );
+ goto Exit;
+
+ case 1: /* line operations */
+ case 2:
+ case 3:
+ error = pfr_glyph_line_to( glyph, pos );
+ goto Test_Error;
+
+ case 4: /* move to inside contour */
+ case 5: /* move to outside contour */
+ error = pfr_glyph_move_to( glyph, pos );
+ goto Test_Error;
+
+ default: /* curve operations */
+ error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 );
+
+ Test_Error: /* test error condition */
+ if ( error )
+ goto Exit;
+ }
+ } /* for (;;) */
+ }
+
+ Exit:
+ return error;
+
+ Failure:
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" ));
+ goto Exit;
+ }
+
+
+ /* load a composite/compound glyph */
+ static FT_Error
+ pfr_glyph_load_compound( PFR_Glyph glyph,
+ FT_Byte* p,
+ FT_Byte* limit )
+ {
+ FT_Error error = 0;
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Memory memory = loader->memory;
+ PFR_SubGlyph subglyph;
+ FT_UInt flags, i, count, org_count;
+ FT_Int x_pos, y_pos;
+
+
+ PFR_CHECK( 1 );
+ flags = PFR_NEXT_BYTE( p );
+
+ /* test for composite glyphs */
+ if ( !( flags & PFR_GLYPH_IS_COMPOUND ) )
+ goto Failure;
+
+ count = flags & 0x3F;
+
+ /* ignore extra items when present */
+ /* */
+ if ( flags & PFR_GLYPH_EXTRA_ITEMS )
+ {
+ error = pfr_extra_items_skip( &p, limit );
+ if (error) goto Exit;
+ }
+
+ /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because */
+ /* the PFR format is dumb, using direct file offsets to point to the */
+ /* sub-glyphs (instead of glyph indices). Sigh. */
+ /* */
+ /* For now, we load the list of sub-glyphs into a different array */
+ /* but this will prevent us from using the auto-hinter at its best */
+ /* quality. */
+ /* */
+ org_count = glyph->num_subs;
+
+ if ( org_count + count > glyph->max_subs )
+ {
+ FT_UInt new_max = ( org_count + count + 3 ) & -4;
+
+
+ if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) )
+ goto Exit;
+
+ glyph->max_subs = new_max;
+ }
+
+ subglyph = glyph->subs + org_count;
+
+ for ( i = 0; i < count; i++, subglyph++ )
+ {
+ FT_UInt format;
+
+
+ x_pos = 0;
+ y_pos = 0;
+
+ PFR_CHECK( 1 );
+ format = PFR_NEXT_BYTE( p );
+
+ /* read scale when available */
+ subglyph->x_scale = 0x10000L;
+ if ( format & PFR_SUBGLYPH_XSCALE )
+ {
+ PFR_CHECK( 2 );
+ subglyph->x_scale = PFR_NEXT_SHORT( p ) << 4;
+ }
+
+ subglyph->y_scale = 0x10000L;
+ if ( format & PFR_SUBGLYPH_YSCALE )
+ {
+ PFR_CHECK( 2 );
+ subglyph->y_scale = PFR_NEXT_SHORT( p ) << 4;
+ }
+
+ /* read offset */
+ switch ( format & 3 )
+ {
+ case 1:
+ PFR_CHECK( 2 );
+ x_pos = PFR_NEXT_SHORT( p );
+ break;
+
+ case 2:
+ PFR_CHECK( 1 );
+ x_pos += PFR_NEXT_INT8( p );
+ break;
+
+ default:
+ ;
+ }
+
+ switch ( ( format >> 2 ) & 3 )
+ {
+ case 1:
+ PFR_CHECK( 2 );
+ y_pos = PFR_NEXT_SHORT( p );
+ break;
+
+ case 2:
+ PFR_CHECK( 1 );
+ y_pos += PFR_NEXT_INT8( p );
+ break;
+
+ default:
+ ;
+ }
+
+ subglyph->x_delta = x_pos;
+ subglyph->y_delta = y_pos;
+
+ /* read glyph position and size now */
+ if ( format & PFR_SUBGLYPH_2BYTE_SIZE )
+ {
+ PFR_CHECK( 2 );
+ subglyph->gps_size = PFR_NEXT_USHORT( p );
+ }
+ else
+ {
+ PFR_CHECK( 1 );
+ subglyph->gps_size = PFR_NEXT_BYTE( p );
+ }
+
+ if ( format & PFR_SUBGLYPH_3BYTE_OFFSET )
+ {
+ PFR_CHECK( 3 );
+ subglyph->gps_offset = PFR_NEXT_LONG( p );
+ }
+ else
+ {
+ PFR_CHECK( 2 );
+ subglyph->gps_offset = PFR_NEXT_USHORT( p );
+ }
+
+ glyph->num_subs++;
+ }
+
+ Exit:
+ return error;
+
+ Failure:
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" ));
+ goto Exit;
+ }
+
+
+ static FT_Error
+ pfr_glyph_load_rec( PFR_Glyph glyph,
+ FT_Stream stream,
+ FT_ULong gps_offset,
+ FT_ULong offset,
+ FT_ULong size )
+ {
+ FT_Error error;
+ FT_Byte* p;
+ FT_Byte* limit;
+
+
+ if ( FT_STREAM_SEEK( gps_offset + offset ) ||
+ FT_FRAME_ENTER( size ) )
+ goto Exit;
+
+ p = (FT_Byte*)stream->cursor;
+ limit = p + size;
+
+ if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND )
+ {
+ FT_Int n, old_count, count;
+ FT_GlyphLoader loader = glyph->loader;
+ FT_Outline* base = &loader->base.outline;
+
+
+ old_count = glyph->num_subs;
+
+ /* this is a compound glyph - load it */
+ error = pfr_glyph_load_compound( glyph, p, limit );
+
+ FT_FRAME_EXIT();
+
+ if ( error )
+ goto Exit;
+
+ count = glyph->num_subs - old_count;
+
+ /* now, load each individual glyph */
+ for ( n = 0; n < count; n++ )
+ {
+ FT_Int i, old_points, num_points;
+ PFR_SubGlyph subglyph;
+
+
+ subglyph = glyph->subs + old_count + n;
+ old_points = base->n_points;
+
+ error = pfr_glyph_load_rec( glyph, stream, gps_offset,
+ subglyph->gps_offset,
+ subglyph->gps_size );
+ if ( error )
+ goto Exit;
+
+ /* note that `glyph->subs' might have been re-allocated */
+ subglyph = glyph->subs + old_count + n;
+ num_points = base->n_points - old_points;
+
+ /* translate and eventually scale the new glyph points */
+ if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L )
+ {
+ FT_Vector* vec = base->points + old_points;
+
+
+ for ( i = 0; i < num_points; i++, vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, subglyph->x_scale ) +
+ subglyph->x_delta;
+ vec->y = FT_MulFix( vec->y, subglyph->y_scale ) +
+ subglyph->y_delta;
+ }
+ }
+ else
+ {
+ FT_Vector* vec = loader->base.outline.points + old_points;
+
+
+ for ( i = 0; i < num_points; i++, vec++ )
+ {
+ vec->x += subglyph->x_delta;
+ vec->y += subglyph->y_delta;
+ }
+ }
+
+ /* proceed to next sub-glyph */
+ }
+ }
+ else
+ {
+ /* load a simple glyph */
+ error = pfr_glyph_load_simple( glyph, p, limit );
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+
+
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_glyph_load( PFR_Glyph glyph,
+ FT_Stream stream,
+ FT_ULong gps_offset,
+ FT_ULong offset,
+ FT_ULong size )
+ {
+ /* initialize glyph loader */
+ FT_GlyphLoader_Rewind( glyph->loader );
+
+ glyph->num_subs = 0;
+
+ /* load the glyph, recursively when needed */
+ return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
+ }
+
+
+/* END */
diff --git a/src/pfr/pfrgload.h b/src/pfr/pfrgload.h
new file mode 100644
index 0000000..7cc7a87
--- /dev/null
+++ b/src/pfr/pfrgload.h
@@ -0,0 +1,49 @@
+/***************************************************************************/
+/* */
+/* pfrgload.h */
+/* */
+/* FreeType PFR glyph loader (specification). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRGLOAD_H__
+#define __PFRGLOAD_H__
+
+#include "pfrtypes.h"
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ pfr_glyph_init( PFR_Glyph glyph,
+ FT_GlyphLoader loader );
+
+ FT_LOCAL( void )
+ pfr_glyph_done( PFR_Glyph glyph );
+
+
+ FT_LOCAL( FT_Error )
+ pfr_glyph_load( PFR_Glyph glyph,
+ FT_Stream stream,
+ FT_ULong gps_offset,
+ FT_ULong offset,
+ FT_ULong size );
+
+
+FT_END_HEADER
+
+
+#endif /* __PFRGLOAD_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrload.c b/src/pfr/pfrload.c
new file mode 100644
index 0000000..1ee2c1f
--- /dev/null
+++ b/src/pfr/pfrload.c
@@ -0,0 +1,938 @@
+/***************************************************************************/
+/* */
+/* pfrload.c */
+/* */
+/* FreeType PFR loader (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "pfrload.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+
+#include "pfrerror.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pfr
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** EXTRA ITEMS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_extra_items_skip( FT_Byte* *pp,
+ FT_Byte* limit )
+ {
+ return pfr_extra_items_parse( pp, limit, NULL, NULL );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_extra_items_parse( FT_Byte* *pp,
+ FT_Byte* limit,
+ PFR_ExtraItem item_list,
+ FT_Pointer item_data )
+ {
+ FT_Error error = 0;
+ FT_Byte* p = *pp;
+ FT_UInt num_items, item_type, item_size;
+
+
+ PFR_CHECK( 1 );
+ num_items = PFR_NEXT_BYTE( p );
+
+ for ( ; num_items > 0; num_items-- )
+ {
+ PFR_CHECK( 2 );
+ item_size = PFR_NEXT_BYTE( p );
+ item_type = PFR_NEXT_BYTE( p );
+
+ PFR_CHECK( item_size );
+
+ if ( item_list )
+ {
+ PFR_ExtraItem extra = item_list;
+
+
+ for ( extra = item_list; extra->parser != NULL; extra++ )
+ {
+ if ( extra->type == item_type )
+ {
+ error = extra->parser( p, p + item_size, item_data );
+ if ( error ) goto Exit;
+
+ break;
+ }
+ }
+ }
+
+ p += item_size;
+ }
+
+ Exit:
+ *pp = p;
+ return error;
+
+ Too_Short:
+ FT_ERROR(( "pfr_extra_items_parse: invalid extra items table\n" ));
+ error = PFR_Err_Invalid_Table;
+ goto Exit;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PFR HEADER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static const FT_Frame_Field pfr_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PFR_HeaderRec
+
+ FT_FRAME_START( 58 ),
+ FT_FRAME_ULONG ( signature ),
+ FT_FRAME_USHORT( version ),
+ FT_FRAME_USHORT( signature2 ),
+ FT_FRAME_USHORT( header_size ),
+
+ FT_FRAME_USHORT( log_dir_size ),
+ FT_FRAME_USHORT( log_dir_offset ),
+
+ FT_FRAME_USHORT( log_font_max_size ),
+ FT_FRAME_UOFF3 ( log_font_section_size ),
+ FT_FRAME_UOFF3 ( log_font_section_offset ),
+
+ FT_FRAME_USHORT( phy_font_max_size ),
+ FT_FRAME_UOFF3 ( phy_font_section_size ),
+ FT_FRAME_UOFF3 ( phy_font_section_offset ),
+
+ FT_FRAME_USHORT( gps_max_size ),
+ FT_FRAME_UOFF3 ( gps_section_size ),
+ FT_FRAME_UOFF3 ( gps_section_offset ),
+
+ FT_FRAME_BYTE ( max_blue_values ),
+ FT_FRAME_BYTE ( max_x_orus ),
+ FT_FRAME_BYTE ( max_y_orus ),
+
+ FT_FRAME_BYTE ( phy_font_max_size_high ),
+ FT_FRAME_BYTE ( color_flags ),
+
+ FT_FRAME_UOFF3 ( bct_max_size ),
+ FT_FRAME_UOFF3 ( bct_set_max_size ),
+ FT_FRAME_UOFF3 ( phy_bct_set_max_size ),
+
+ FT_FRAME_USHORT( num_phy_fonts ),
+ FT_FRAME_BYTE ( max_vert_stem_snap ),
+ FT_FRAME_BYTE ( max_horz_stem_snap ),
+ FT_FRAME_USHORT( max_chars ),
+ FT_FRAME_END
+ };
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_header_load( PFR_Header header,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ /* read header directly */
+ if ( !FT_STREAM_SEEK( 0 ) &&
+ !FT_STREAM_READ_FIELDS( pfr_header_fields, header ) )
+ {
+ /* make a few adjustments to the header */
+ header->phy_font_max_size +=
+ (FT_UInt32)header->phy_font_max_size_high << 16;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ pfr_header_check( PFR_Header header )
+ {
+ FT_Bool result = 1;
+
+
+ /* check signature and header size */
+ if ( header->signature != 0x50465230L || /* "PFR0" */
+ header->version > 4 ||
+ header->header_size < 58 ||
+ header->signature2 != 0x0d0a ) /* CR/LF */
+ {
+ result = 0;
+ }
+ return result;
+ }
+
+
+ /***********************************************************************/
+ /***********************************************************************/
+ /***** *****/
+ /***** PFR LOGICAL FONTS *****/
+ /***** *****/
+ /***********************************************************************/
+ /***********************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_log_font_count( FT_Stream stream,
+ FT_UInt32 section_offset,
+ FT_UInt *acount )
+ {
+ FT_Error error;
+ FT_UInt count;
+ FT_UInt result = 0;
+
+
+ if ( FT_STREAM_SEEK( section_offset ) || FT_READ_USHORT( count ) )
+ goto Exit;
+
+ result = count;
+
+ Exit:
+ *acount = result;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_log_font_load( PFR_LogFont log_font,
+ FT_Stream stream,
+ FT_UInt idx,
+ FT_UInt32 section_offset,
+ FT_Bool size_increment )
+ {
+ FT_UInt num_log_fonts;
+ FT_UInt flags;
+ FT_UInt32 offset;
+ FT_UInt32 size;
+ FT_Error error;
+
+
+ if ( FT_STREAM_SEEK( section_offset ) ||
+ FT_READ_USHORT( num_log_fonts ) )
+ goto Exit;
+
+ if ( idx >= num_log_fonts )
+ return PFR_Err_Invalid_Argument;
+
+ if ( FT_STREAM_SKIP( idx * 5 ) ||
+ FT_READ_USHORT( size ) ||
+ FT_READ_UOFF3 ( offset ) )
+ goto Exit;
+
+ /* save logical font size and offset */
+ log_font->size = size;
+ log_font->offset = offset;
+
+ /* now, check the rest of the table before loading it */
+ {
+ FT_Byte* p;
+ FT_Byte* limit;
+ FT_UInt local;
+
+
+ if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( size ) )
+ goto Exit;
+
+ p = stream->cursor;
+ limit = p + size;
+
+ PFR_CHECK(13);
+
+ log_font->matrix[0] = PFR_NEXT_LONG( p );
+ log_font->matrix[1] = PFR_NEXT_LONG( p );
+ log_font->matrix[2] = PFR_NEXT_LONG( p );
+ log_font->matrix[3] = PFR_NEXT_LONG( p );
+
+ flags = PFR_NEXT_BYTE( p );
+
+ local = 0;
+ if ( flags & PFR_LOG_STROKE )
+ {
+ local++;
+ if ( flags & PFR_LOG_2BYTE_STROKE )
+ local++;
+
+ if ( (flags & PFR_LINE_JOIN_MASK) == PFR_LINE_JOIN_MITER )
+ local += 3;
+ }
+ if ( flags & PFR_LOG_BOLD )
+ {
+ local++;
+ if ( flags & PFR_LOG_2BYTE_BOLD )
+ local++;
+ }
+
+ PFR_CHECK( local );
+
+ if ( flags & PFR_LOG_STROKE )
+ {
+ log_font->stroke_thickness = ( flags & PFR_LOG_2BYTE_STROKE )
+ ? PFR_NEXT_SHORT( p )
+ : PFR_NEXT_BYTE( p );
+
+ if ( ( flags & PFR_LINE_JOIN_MASK ) == PFR_LINE_JOIN_MITER )
+ log_font->miter_limit = PFR_NEXT_LONG( p );
+ }
+
+ if ( flags & PFR_LOG_BOLD )
+ {
+ log_font->bold_thickness = ( flags & PFR_LOG_2BYTE_BOLD )
+ ? PFR_NEXT_SHORT( p )
+ : PFR_NEXT_BYTE( p );
+ }
+
+ if ( flags & PFR_LOG_EXTRA_ITEMS )
+ {
+ error = pfr_extra_items_skip( &p, limit );
+ if (error) goto Fail;
+ }
+
+ PFR_CHECK(5);
+ log_font->phys_size = PFR_NEXT_USHORT( p );
+ log_font->phys_offset = PFR_NEXT_ULONG( p );
+ if ( size_increment )
+ {
+ PFR_CHECK( 1 );
+ log_font->phys_size += (FT_UInt32)PFR_NEXT_BYTE( p ) << 16;
+ }
+ }
+
+ Fail:
+ FT_FRAME_EXIT();
+
+ Exit:
+ return error;
+
+ Too_Short:
+ FT_ERROR(( "pfr_log_font_load: invalid logical font table\n" ));
+ error = PFR_Err_Invalid_Table;
+ goto Fail;
+ }
+
+
+ /***********************************************************************/
+ /***********************************************************************/
+ /***** *****/
+ /***** PFR PHYSICAL FONTS *****/
+ /***** *****/
+ /***********************************************************************/
+ /***********************************************************************/
+
+
+ /* load bitmap strikes lists */
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_extra_item_load_bitmap_info( FT_Byte* p,
+ FT_Byte* limit,
+ PFR_PhyFont phy_font )
+ {
+ FT_Memory memory = phy_font->memory;
+ PFR_Strike strike;
+ FT_UInt flags0;
+ FT_UInt n, count, size1;
+ FT_Error error = 0;
+
+
+ PFR_CHECK( 5 );
+
+ p += 3; /* skip bctSize */
+ flags0 = PFR_NEXT_BYTE( p );
+ count = PFR_NEXT_BYTE( p );
+
+ /* re-allocate when needed */
+ if ( phy_font->num_strikes + count > phy_font->max_strikes )
+ {
+ FT_UInt new_max = FT_PAD_CEIL( phy_font->num_strikes + count, 4 );
+
+
+ if ( FT_RENEW_ARRAY( phy_font->strikes,
+ phy_font->num_strikes,
+ new_max ) )
+ goto Exit;
+
+ phy_font->max_strikes = new_max;
+ }
+
+ size1 = 1 + 1 + 1 + 2 + 2 + 1;
+ if ( flags0 & PFR_STRIKE_2BYTE_XPPM )
+ size1++;
+
+ if ( flags0 & PFR_STRIKE_2BYTE_YPPM )
+ size1++;
+
+ if ( flags0 & PFR_STRIKE_3BYTE_SIZE )
+ size1++;
+
+ if ( flags0 & PFR_STRIKE_3BYTE_OFFSET )
+ size1++;
+
+ if ( flags0 & PFR_STRIKE_2BYTE_COUNT )
+ size1++;
+
+ strike = phy_font->strikes + phy_font->num_strikes;
+
+ PFR_CHECK( count * size1 );
+
+ for ( n = 0; n < count; n++, strike++ )
+ {
+ strike->x_ppm = ( flags0 & PFR_STRIKE_2BYTE_XPPM )
+ ? PFR_NEXT_USHORT( p )
+ : PFR_NEXT_BYTE( p );
+
+ strike->y_ppm = ( flags0 & PFR_STRIKE_2BYTE_YPPM )
+ ? PFR_NEXT_USHORT( p )
+ : PFR_NEXT_BYTE( p );
+
+ strike->flags = PFR_NEXT_BYTE( p );
+
+ strike->bct_size = ( flags0 & PFR_STRIKE_3BYTE_SIZE )
+ ? PFR_NEXT_ULONG( p )
+ : PFR_NEXT_USHORT( p );
+
+ strike->bct_offset = ( flags0 & PFR_STRIKE_3BYTE_OFFSET )
+ ? PFR_NEXT_ULONG( p )
+ : PFR_NEXT_USHORT( p );
+
+ strike->num_bitmaps = ( flags0 & PFR_STRIKE_2BYTE_COUNT )
+ ? PFR_NEXT_USHORT( p )
+ : PFR_NEXT_BYTE( p );
+ }
+
+ phy_font->num_strikes += count;
+
+ Exit:
+ return error;
+
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_extra_item_load_bitmap_info: invalid bitmap info table\n" ));
+ goto Exit;
+ }
+
+
+ /* Load font ID. This is a so-called "unique" name that is rather
+ * long and descriptive (like "Tiresias ScreenFont v7.51").
+ *
+ * Note that a PFR font's family name is contained in an *undocumented*
+ * string of the "auxiliary data" portion of a physical font record. This
+ * may also contain the "real" style name!
+ *
+ * If no family name is present, the font ID is used instead for the
+ * family.
+ */
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_extra_item_load_font_id( FT_Byte* p,
+ FT_Byte* limit,
+ PFR_PhyFont phy_font )
+ {
+ FT_Error error = 0;
+ FT_Memory memory = phy_font->memory;
+ FT_PtrDist len = limit - p;
+
+
+ if ( phy_font->font_id != NULL )
+ goto Exit;
+
+ if ( FT_ALLOC( phy_font->font_id, len + 1 ) )
+ goto Exit;
+
+ /* copy font ID name, and terminate it for safety */
+ FT_MEM_COPY( phy_font->font_id, p, len );
+ phy_font->font_id[len] = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /* load stem snap tables */
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_extra_item_load_stem_snaps( FT_Byte* p,
+ FT_Byte* limit,
+ PFR_PhyFont phy_font )
+ {
+ FT_UInt count, num_vert, num_horz;
+ FT_Int* snaps;
+ FT_Error error = 0;
+ FT_Memory memory = phy_font->memory;
+
+
+ if ( phy_font->vertical.stem_snaps != NULL )
+ goto Exit;
+
+ PFR_CHECK( 1 );
+ count = PFR_NEXT_BYTE( p );
+
+ num_vert = count & 15;
+ num_horz = count >> 4;
+ count = num_vert + num_horz;
+
+ PFR_CHECK( count * 2 );
+
+ if ( FT_NEW_ARRAY( snaps, count ) )
+ goto Exit;
+
+ phy_font->vertical.stem_snaps = snaps;
+ phy_font->horizontal.stem_snaps = snaps + num_vert;
+
+ for ( ; count > 0; count--, snaps++ )
+ *snaps = FT_NEXT_SHORT( p );
+
+ Exit:
+ return error;
+
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_exta_item_load_stem_snaps: invalid stem snaps table\n" ));
+ goto Exit;
+ }
+
+
+
+ /* load kerning pair data */
+ FT_CALLBACK_DEF( FT_Error )
+ pfr_extra_item_load_kerning_pairs( FT_Byte* p,
+ FT_Byte* limit,
+ PFR_PhyFont phy_font )
+ {
+ PFR_KernItem item;
+ FT_Error error = 0;
+ FT_Memory memory = phy_font->memory;
+
+
+ FT_TRACE2(( "pfr_extra_item_load_kerning_pairs()\n" ));
+
+ if ( FT_NEW( item ) )
+ goto Exit;
+
+ PFR_CHECK( 4 );
+
+ item->pair_count = PFR_NEXT_BYTE( p );
+ item->base_adj = PFR_NEXT_SHORT( p );
+ item->flags = PFR_NEXT_BYTE( p );
+ item->offset = phy_font->offset + ( p - phy_font->cursor );
+
+#ifndef PFR_CONFIG_NO_CHECKS
+ item->pair_size = 3;
+
+ if ( item->flags & PFR_KERN_2BYTE_CHAR )
+ item->pair_size += 2;
+
+ if ( item->flags & PFR_KERN_2BYTE_ADJ )
+ item->pair_size += 1;
+
+ PFR_CHECK( item->pair_count * item->pair_size );
+#endif
+
+ /* load first and last pairs into the item to speed up */
+ /* lookup later... */
+ if ( item->pair_count > 0 )
+ {
+ FT_UInt char1, char2;
+ FT_Byte* q;
+
+
+ if ( item->flags & PFR_KERN_2BYTE_CHAR )
+ {
+ q = p;
+ char1 = PFR_NEXT_USHORT( q );
+ char2 = PFR_NEXT_USHORT( q );
+
+ item->pair1 = PFR_KERN_INDEX( char1, char2 );
+
+ q = p + item->pair_size * ( item->pair_count - 1 );
+ char1 = PFR_NEXT_USHORT( q );
+ char2 = PFR_NEXT_USHORT( q );
+
+ item->pair2 = PFR_KERN_INDEX( char1, char2 );
+ }
+ else
+ {
+ q = p;
+ char1 = PFR_NEXT_BYTE( q );
+ char2 = PFR_NEXT_BYTE( q );
+
+ item->pair1 = PFR_KERN_INDEX( char1, char2 );
+
+ q = p + item->pair_size * ( item->pair_count - 1 );
+ char1 = PFR_NEXT_BYTE( q );
+ char2 = PFR_NEXT_BYTE( q );
+
+ item->pair2 = PFR_KERN_INDEX( char1, char2 );
+ }
+
+ /* add new item to the current list */
+ item->next = NULL;
+ *phy_font->kern_items_tail = item;
+ phy_font->kern_items_tail = &item->next;
+ phy_font->num_kern_pairs += item->pair_count;
+ }
+ else
+ {
+ /* empty item! */
+ FT_FREE( item );
+ }
+
+ Exit:
+ return error;
+
+ Too_Short:
+ FT_FREE( item );
+
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_extra_item_load_kerning_pairs: "
+ "invalid kerning pairs table\n" ));
+ goto Exit;
+ }
+
+
+
+ static const PFR_ExtraItemRec pfr_phy_font_extra_items[] =
+ {
+ { 1, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_bitmap_info },
+ { 2, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_font_id },
+ { 3, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_stem_snaps },
+ { 4, (PFR_ExtraItem_ParseFunc)pfr_extra_item_load_kerning_pairs },
+ { 0, NULL }
+ };
+
+
+ /* Loads a name from the auxiliary data. Since this extracts undocumented
+ * strings from the font file, we need to be careful here.
+ */
+ static FT_Error
+ pfr_aux_name_load( FT_Byte* p,
+ FT_UInt len,
+ FT_Memory memory,
+ FT_String* *astring )
+ {
+ FT_Error error = 0;
+ FT_String* result = NULL;
+ FT_UInt n, ok;
+
+
+ if ( len > 0 && p[len - 1] == 0 )
+ len--;
+
+ /* check that each character is ASCII for making sure not to
+ load garbage
+ */
+ ok = ( len > 0 );
+ for ( n = 0; n < len; n++ )
+ if ( p[n] < 32 || p[n] > 127 )
+ {
+ ok = 0;
+ break;
+ }
+
+ if ( ok )
+ {
+ if ( FT_ALLOC( result, len + 1 ) )
+ goto Exit;
+
+ FT_MEM_COPY( result, p, len );
+ result[len] = 0;
+ }
+ Exit:
+ *astring = result;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ pfr_phy_font_done( PFR_PhyFont phy_font,
+ FT_Memory memory )
+ {
+ FT_FREE( phy_font->font_id );
+ FT_FREE( phy_font->family_name );
+ FT_FREE( phy_font->style_name );
+
+ FT_FREE( phy_font->vertical.stem_snaps );
+ phy_font->vertical.num_stem_snaps = 0;
+
+ phy_font->horizontal.stem_snaps = NULL;
+ phy_font->horizontal.num_stem_snaps = 0;
+
+ FT_FREE( phy_font->strikes );
+ phy_font->num_strikes = 0;
+ phy_font->max_strikes = 0;
+
+ FT_FREE( phy_font->chars );
+ phy_font->num_chars = 0;
+ phy_font->chars_offset = 0;
+
+ FT_FREE( phy_font->blue_values );
+ phy_font->num_blue_values = 0;
+
+ {
+ PFR_KernItem item, next;
+
+
+ item = phy_font->kern_items;
+ while ( item )
+ {
+ next = item->next;
+ FT_FREE( item );
+ item = next;
+ }
+ phy_font->kern_items = NULL;
+ phy_font->kern_items_tail = NULL;
+ }
+
+ phy_font->num_kern_pairs = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_phy_font_load( PFR_PhyFont phy_font,
+ FT_Stream stream,
+ FT_UInt32 offset,
+ FT_UInt32 size )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UInt flags, num_aux;
+ FT_Byte* p;
+ FT_Byte* limit;
+
+
+ phy_font->memory = memory;
+ phy_font->offset = offset;
+
+ phy_font->kern_items = NULL;
+ phy_font->kern_items_tail = &phy_font->kern_items;
+
+ if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( size ) )
+ goto Exit;
+
+ phy_font->cursor = stream->cursor;
+
+ p = stream->cursor;
+ limit = p + size;
+
+ PFR_CHECK( 15 );
+ phy_font->font_ref_number = PFR_NEXT_USHORT( p );
+ phy_font->outline_resolution = PFR_NEXT_USHORT( p );
+ phy_font->metrics_resolution = PFR_NEXT_USHORT( p );
+ phy_font->bbox.xMin = PFR_NEXT_SHORT( p );
+ phy_font->bbox.yMin = PFR_NEXT_SHORT( p );
+ phy_font->bbox.xMax = PFR_NEXT_SHORT( p );
+ phy_font->bbox.yMax = PFR_NEXT_SHORT( p );
+ phy_font->flags = flags = PFR_NEXT_BYTE( p );
+
+ /* get the standard advance for non-proportional fonts */
+ if ( !(flags & PFR_PHY_PROPORTIONAL) )
+ {
+ PFR_CHECK( 2 );
+ phy_font->standard_advance = PFR_NEXT_SHORT( p );
+ }
+
+ /* load the extra items when present */
+ if ( flags & PFR_PHY_EXTRA_ITEMS )
+ {
+ error = pfr_extra_items_parse( &p, limit,
+ pfr_phy_font_extra_items, phy_font );
+
+ if ( error )
+ goto Fail;
+ }
+
+ /* In certain fonts, the auxiliary bytes contain interesting */
+ /* information. These are not in the specification but can be */
+ /* guessed by looking at the content of a few PFR0 fonts. */
+ PFR_CHECK( 3 );
+ num_aux = PFR_NEXT_ULONG( p );
+
+ if ( num_aux > 0 )
+ {
+ FT_Byte* q = p;
+ FT_Byte* q2;
+
+
+ PFR_CHECK( num_aux );
+ p += num_aux;
+
+ while ( num_aux > 0 )
+ {
+ FT_UInt length, type;
+
+
+ if ( q + 4 > p )
+ break;
+
+ length = PFR_NEXT_USHORT( q );
+ if ( length < 4 || length > num_aux )
+ break;
+
+ q2 = q + length - 2;
+ type = PFR_NEXT_USHORT( q );
+
+ switch ( type )
+ {
+ case 1:
+ /* this seems to correspond to the font's family name,
+ * padded to 16-bits with one zero when necessary
+ */
+ error = pfr_aux_name_load( q, length - 4U, memory,
+ &phy_font->family_name );
+ if ( error )
+ goto Exit;
+ break;
+
+ case 2:
+ if ( q + 32 > q2 )
+ break;
+
+ q += 10;
+ phy_font->ascent = PFR_NEXT_SHORT( q );
+ phy_font->descent = PFR_NEXT_SHORT( q );
+ phy_font->leading = PFR_NEXT_SHORT( q );
+ q += 16;
+ break;
+
+ case 3:
+ /* this seems to correspond to the font's style name,
+ * padded to 16-bits with one zero when necessary
+ */
+ error = pfr_aux_name_load( q, length - 4U, memory,
+ &phy_font->style_name );
+ if ( error )
+ goto Exit;
+ break;
+
+ default:
+ ;
+ }
+
+ q = q2;
+ num_aux -= length;
+ }
+ }
+
+ /* read the blue values */
+ {
+ FT_UInt n, count;
+
+
+ PFR_CHECK( 1 );
+ phy_font->num_blue_values = count = PFR_NEXT_BYTE( p );
+
+ PFR_CHECK( count * 2 );
+
+ if ( FT_NEW_ARRAY( phy_font->blue_values, count ) )
+ goto Fail;
+
+ for ( n = 0; n < count; n++ )
+ phy_font->blue_values[n] = PFR_NEXT_SHORT( p );
+ }
+
+ PFR_CHECK( 8 );
+ phy_font->blue_fuzz = PFR_NEXT_BYTE( p );
+ phy_font->blue_scale = PFR_NEXT_BYTE( p );
+
+ phy_font->vertical.standard = PFR_NEXT_USHORT( p );
+ phy_font->horizontal.standard = PFR_NEXT_USHORT( p );
+
+ /* read the character descriptors */
+ {
+ FT_UInt n, count, Size;
+
+
+ phy_font->num_chars = count = PFR_NEXT_USHORT( p );
+ phy_font->chars_offset = offset + ( p - stream->cursor );
+
+ if ( FT_NEW_ARRAY( phy_font->chars, count ) )
+ goto Fail;
+
+ Size = 1 + 1 + 2;
+ if ( flags & PFR_PHY_2BYTE_CHARCODE )
+ Size += 1;
+
+ if ( flags & PFR_PHY_PROPORTIONAL )
+ Size += 2;
+
+ if ( flags & PFR_PHY_ASCII_CODE )
+ Size += 1;
+
+ if ( flags & PFR_PHY_2BYTE_GPS_SIZE )
+ Size += 1;
+
+ if ( flags & PFR_PHY_3BYTE_GPS_OFFSET )
+ Size += 1;
+
+ PFR_CHECK( count * Size );
+
+ for ( n = 0; n < count; n++ )
+ {
+ PFR_Char cur = &phy_font->chars[n];
+
+
+ cur->char_code = ( flags & PFR_PHY_2BYTE_CHARCODE )
+ ? PFR_NEXT_USHORT( p )
+ : PFR_NEXT_BYTE( p );
+
+ cur->advance = ( flags & PFR_PHY_PROPORTIONAL )
+ ? PFR_NEXT_SHORT( p )
+ : (FT_Int) phy_font->standard_advance;
+
+#if 0
+ cur->ascii = ( flags & PFR_PHY_ASCII_CODE )
+ ? PFR_NEXT_BYTE( p )
+ : 0;
+#else
+ if ( flags & PFR_PHY_ASCII_CODE )
+ p += 1;
+#endif
+ cur->gps_size = ( flags & PFR_PHY_2BYTE_GPS_SIZE )
+ ? PFR_NEXT_USHORT( p )
+ : PFR_NEXT_BYTE( p );
+
+ cur->gps_offset = ( flags & PFR_PHY_3BYTE_GPS_OFFSET )
+ ? PFR_NEXT_ULONG( p )
+ : PFR_NEXT_USHORT( p );
+ }
+ }
+
+ /* that's it! */
+
+ Fail:
+ FT_FRAME_EXIT();
+
+ /* save position of bitmap info */
+ phy_font->bct_offset = FT_STREAM_POS();
+ phy_font->cursor = NULL;
+
+ Exit:
+ return error;
+
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_phy_font_load: invalid physical font table\n" ));
+ goto Fail;
+ }
+
+
+/* END */
diff --git a/src/pfr/pfrload.h b/src/pfr/pfrload.h
new file mode 100644
index 0000000..ed01071
--- /dev/null
+++ b/src/pfr/pfrload.h
@@ -0,0 +1,118 @@
+/***************************************************************************/
+/* */
+/* pfrload.h */
+/* */
+/* FreeType PFR loader (specification). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRLOAD_H__
+#define __PFRLOAD_H__
+
+#include "pfrobjs.h"
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+#ifdef PFR_CONFIG_NO_CHECKS
+#define PFR_CHECK( x ) do { } while ( 0 )
+#else
+#define PFR_CHECK( x ) do \
+ { \
+ if ( p + (x) > limit ) \
+ goto Too_Short; \
+ } while ( 0 )
+#endif
+
+#define PFR_NEXT_BYTE( p ) FT_NEXT_BYTE( p )
+#define PFR_NEXT_INT8( p ) FT_NEXT_CHAR( p )
+#define PFR_NEXT_SHORT( p ) FT_NEXT_SHORT( p )
+#define PFR_NEXT_USHORT( p ) FT_NEXT_USHORT( p )
+#define PFR_NEXT_LONG( p ) FT_NEXT_OFF3( p )
+#define PFR_NEXT_ULONG( p ) FT_NEXT_UOFF3( p )
+
+
+ /* handling extra items */
+
+ typedef FT_Error
+ (*PFR_ExtraItem_ParseFunc)( FT_Byte* p,
+ FT_Byte* limit,
+ FT_Pointer data );
+
+ typedef struct PFR_ExtraItemRec_
+ {
+ FT_UInt type;
+ PFR_ExtraItem_ParseFunc parser;
+
+ } PFR_ExtraItemRec;
+
+ typedef const struct PFR_ExtraItemRec_* PFR_ExtraItem;
+
+
+ FT_LOCAL( FT_Error )
+ pfr_extra_items_skip( FT_Byte* *pp,
+ FT_Byte* limit );
+
+ FT_LOCAL( FT_Error )
+ pfr_extra_items_parse( FT_Byte* *pp,
+ FT_Byte* limit,
+ PFR_ExtraItem item_list,
+ FT_Pointer item_data );
+
+
+ /* load a PFR header */
+ FT_LOCAL( FT_Error )
+ pfr_header_load( PFR_Header header,
+ FT_Stream stream );
+
+ /* check a PFR header */
+ FT_LOCAL( FT_Bool )
+ pfr_header_check( PFR_Header header );
+
+
+ /* return number of logical fonts in this file */
+ FT_LOCAL( FT_Error )
+ pfr_log_font_count( FT_Stream stream,
+ FT_UInt32 log_section_offset,
+ FT_UInt *acount );
+
+ /* load a pfr logical font entry */
+ FT_LOCAL( FT_Error )
+ pfr_log_font_load( PFR_LogFont log_font,
+ FT_Stream stream,
+ FT_UInt face_index,
+ FT_UInt32 section_offset,
+ FT_Bool size_increment );
+
+
+ /* load a physical font entry */
+ FT_LOCAL( FT_Error )
+ pfr_phy_font_load( PFR_PhyFont phy_font,
+ FT_Stream stream,
+ FT_UInt32 offset,
+ FT_UInt32 size );
+
+ /* finalize a physical font */
+ FT_LOCAL( void )
+ pfr_phy_font_done( PFR_PhyFont phy_font,
+ FT_Memory memory );
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __PFRLOAD_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrobjs.c b/src/pfr/pfrobjs.c
new file mode 100644
index 0000000..180446d
--- /dev/null
+++ b/src/pfr/pfrobjs.c
@@ -0,0 +1,576 @@
+/***************************************************************************/
+/* */
+/* pfrobjs.c */
+/* */
+/* FreeType PFR object methods (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "pfrobjs.h"
+#include "pfrload.h"
+#include "pfrgload.h"
+#include "pfrcmap.h"
+#include "pfrsbit.h"
+#include FT_OUTLINE_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "pfrerror.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pfr
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FACE OBJECT METHODS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ pfr_face_done( FT_Face pfrface ) /* PFR_Face */
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ FT_Memory memory = pfrface->driver->root.memory;
+
+
+ /* we don't want dangling pointers */
+ pfrface->family_name = NULL;
+ pfrface->style_name = NULL;
+
+ /* finalize the physical font record */
+ pfr_phy_font_done( &face->phy_font, FT_FACE_MEMORY( face ) );
+
+ /* no need to finalize the logical font or the header */
+ FT_FREE( pfrface->available_sizes );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_face_init( FT_Stream stream,
+ FT_Face pfrface,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ FT_Error error;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ /* load the header and check it */
+ error = pfr_header_load( &face->header, stream );
+ if ( error )
+ goto Exit;
+
+ if ( !pfr_header_check( &face->header ) )
+ {
+ FT_TRACE4(( "pfr_face_init: not a valid PFR font\n" ));
+ error = PFR_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* check face index */
+ {
+ FT_UInt num_faces;
+
+
+ error = pfr_log_font_count( stream,
+ face->header.log_dir_offset,
+ &num_faces );
+ if ( error )
+ goto Exit;
+
+ pfrface->num_faces = num_faces;
+ }
+
+ if ( face_index < 0 )
+ goto Exit;
+
+ if ( face_index >= pfrface->num_faces )
+ {
+ FT_ERROR(( "pfr_face_init: invalid face index\n" ));
+ error = PFR_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* load the face */
+ error = pfr_log_font_load(
+ &face->log_font, stream, face_index,
+ face->header.log_dir_offset,
+ FT_BOOL( face->header.phy_font_max_size_high != 0 ) );
+ if ( error )
+ goto Exit;
+
+ /* now load the physical font descriptor */
+ error = pfr_phy_font_load( &face->phy_font, stream,
+ face->log_font.phys_offset,
+ face->log_font.phys_size );
+ if ( error )
+ goto Exit;
+
+ /* now set up all root face fields */
+ {
+ PFR_PhyFont phy_font = &face->phy_font;
+
+
+ pfrface->face_index = face_index;
+ pfrface->num_glyphs = phy_font->num_chars + 1;
+ pfrface->face_flags = FT_FACE_FLAG_SCALABLE;
+
+ /* if all characters point to the same gps_offset 0, we */
+ /* assume that the font only contains bitmaps */
+ {
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < phy_font->num_chars; nn++ )
+ if ( phy_font->chars[nn].gps_offset != 0 )
+ break;
+
+ if ( nn == phy_font->num_chars )
+ pfrface->face_flags = 0; /* not scalable */
+ }
+
+ if ( (phy_font->flags & PFR_PHY_PROPORTIONAL) == 0 )
+ pfrface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ if ( phy_font->flags & PFR_PHY_VERTICAL )
+ pfrface->face_flags |= FT_FACE_FLAG_VERTICAL;
+ else
+ pfrface->face_flags |= FT_FACE_FLAG_HORIZONTAL;
+
+ if ( phy_font->num_strikes > 0 )
+ pfrface->face_flags |= FT_FACE_FLAG_FIXED_SIZES;
+
+ if ( phy_font->num_kern_pairs > 0 )
+ pfrface->face_flags |= FT_FACE_FLAG_KERNING;
+
+ /* If no family name was found in the "undocumented" auxiliary
+ * data, use the font ID instead. This sucks but is better than
+ * nothing.
+ */
+ pfrface->family_name = phy_font->family_name;
+ if ( pfrface->family_name == NULL )
+ pfrface->family_name = phy_font->font_id;
+
+ /* note that the style name can be NULL in certain PFR fonts,
+ * probably meaning "Regular"
+ */
+ pfrface->style_name = phy_font->style_name;
+
+ pfrface->num_fixed_sizes = 0;
+ pfrface->available_sizes = 0;
+
+ pfrface->bbox = phy_font->bbox;
+ pfrface->units_per_EM = (FT_UShort)phy_font->outline_resolution;
+ pfrface->ascender = (FT_Short) phy_font->bbox.yMax;
+ pfrface->descender = (FT_Short) phy_font->bbox.yMin;
+
+ pfrface->height = (FT_Short)( ( pfrface->units_per_EM * 12 ) / 10 );
+ if ( pfrface->height < pfrface->ascender - pfrface->descender )
+ pfrface->height = (FT_Short)(pfrface->ascender - pfrface->descender);
+
+ if ( phy_font->num_strikes > 0 )
+ {
+ FT_UInt n, count = phy_font->num_strikes;
+ FT_Bitmap_Size* size;
+ PFR_Strike strike;
+ FT_Memory memory = pfrface->stream->memory;
+
+
+ if ( FT_NEW_ARRAY( pfrface->available_sizes, count ) )
+ goto Exit;
+
+ size = pfrface->available_sizes;
+ strike = phy_font->strikes;
+ for ( n = 0; n < count; n++, size++, strike++ )
+ {
+ size->height = (FT_UShort)strike->y_ppm;
+ size->width = (FT_UShort)strike->x_ppm;
+ size->size = strike->y_ppm << 6;
+ size->x_ppem = strike->x_ppm << 6;
+ size->y_ppem = strike->y_ppm << 6;
+ }
+ pfrface->num_fixed_sizes = count;
+ }
+
+ /* now compute maximum advance width */
+ if ( ( phy_font->flags & PFR_PHY_PROPORTIONAL ) == 0 )
+ pfrface->max_advance_width = (FT_Short)phy_font->standard_advance;
+ else
+ {
+ FT_Int max = 0;
+ FT_UInt count = phy_font->num_chars;
+ PFR_Char gchar = phy_font->chars;
+
+
+ for ( ; count > 0; count--, gchar++ )
+ {
+ if ( max < gchar->advance )
+ max = gchar->advance;
+ }
+
+ pfrface->max_advance_width = (FT_Short)max;
+ }
+
+ pfrface->max_advance_height = pfrface->height;
+
+ pfrface->underline_position = (FT_Short)( -pfrface->units_per_EM / 10 );
+ pfrface->underline_thickness = (FT_Short)( pfrface->units_per_EM / 30 );
+
+ /* create charmap */
+ {
+ FT_CharMapRec charmap;
+
+
+ charmap.face = pfrface;
+ charmap.platform_id = 3;
+ charmap.encoding_id = 1;
+ charmap.encoding = FT_ENCODING_UNICODE;
+
+ FT_CMap_New( &pfr_cmap_class_rec, NULL, &charmap, NULL );
+
+#if 0
+ /* Select default charmap */
+ if ( pfrface->num_charmaps )
+ pfrface->charmap = pfrface->charmaps[0];
+#endif
+ }
+
+ /* check whether we've loaded any kerning pairs */
+ if ( phy_font->num_kern_pairs )
+ pfrface->face_flags |= FT_FACE_FLAG_KERNING;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** SLOT OBJECT METHOD *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_slot_init( FT_GlyphSlot pfrslot ) /* PFR_Slot */
+ {
+ PFR_Slot slot = (PFR_Slot)pfrslot;
+ FT_GlyphLoader loader = pfrslot->internal->loader;
+
+
+ pfr_glyph_init( &slot->glyph, loader );
+
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ pfr_slot_done( FT_GlyphSlot pfrslot ) /* PFR_Slot */
+ {
+ PFR_Slot slot = (PFR_Slot)pfrslot;
+
+
+ pfr_glyph_done( &slot->glyph );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_slot_load( FT_GlyphSlot pfrslot, /* PFR_Slot */
+ FT_Size pfrsize, /* PFR_Size */
+ FT_UInt gindex,
+ FT_Int32 load_flags )
+ {
+ PFR_Slot slot = (PFR_Slot)pfrslot;
+ PFR_Size size = (PFR_Size)pfrsize;
+ FT_Error error;
+ PFR_Face face = (PFR_Face)pfrslot->face;
+ PFR_Char gchar;
+ FT_Outline* outline = &pfrslot->outline;
+ FT_ULong gps_offset;
+
+
+ if ( gindex > 0 )
+ gindex--;
+
+ if ( !face || gindex >= face->phy_font.num_chars )
+ {
+ error = PFR_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* try to load an embedded bitmap */
+ if ( ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ) ) == 0 )
+ {
+ error = pfr_slot_load_bitmap( slot, size, gindex );
+ if ( error == 0 )
+ goto Exit;
+ }
+
+ if ( load_flags & FT_LOAD_SBITS_ONLY )
+ {
+ error = PFR_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ gchar = face->phy_font.chars + gindex;
+ pfrslot->format = FT_GLYPH_FORMAT_OUTLINE;
+ outline->n_points = 0;
+ outline->n_contours = 0;
+ gps_offset = face->header.gps_section_offset;
+
+ /* load the glyph outline (FT_LOAD_NO_RECURSE isn't supported) */
+ error = pfr_glyph_load( &slot->glyph, face->root.stream,
+ gps_offset, gchar->gps_offset, gchar->gps_size );
+
+ if ( !error )
+ {
+ FT_BBox cbox;
+ FT_Glyph_Metrics* metrics = &pfrslot->metrics;
+ FT_Pos advance;
+ FT_Int em_metrics, em_outline;
+ FT_Bool scaling;
+
+
+ scaling = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 );
+
+ /* copy outline data */
+ *outline = slot->glyph.loader->base.outline;
+
+ outline->flags &= ~FT_OUTLINE_OWNER;
+ outline->flags |= FT_OUTLINE_REVERSE_FILL;
+
+ if ( size && pfrsize->metrics.y_ppem < 24 )
+ outline->flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ /* compute the advance vector */
+ metrics->horiAdvance = 0;
+ metrics->vertAdvance = 0;
+
+ advance = gchar->advance;
+ em_metrics = face->phy_font.metrics_resolution;
+ em_outline = face->phy_font.outline_resolution;
+
+ if ( em_metrics != em_outline )
+ advance = FT_MulDiv( advance, em_outline, em_metrics );
+
+ if ( face->phy_font.flags & PFR_PHY_VERTICAL )
+ metrics->vertAdvance = advance;
+ else
+ metrics->horiAdvance = advance;
+
+ pfrslot->linearHoriAdvance = metrics->horiAdvance;
+ pfrslot->linearVertAdvance = metrics->vertAdvance;
+
+ /* make-up vertical metrics(?) */
+ metrics->vertBearingX = 0;
+ metrics->vertBearingY = 0;
+
+#if 0 /* some fonts seem to be broken here! */
+
+ /* Apply the font matrix, if any. */
+ /* TODO: Test existing fonts with unusual matrix */
+ /* whether we have to adjust Units per EM. */
+ {
+ FT_Matrix font_matrix;
+
+
+ font_matrix.xx = face->log_font.matrix[0] << 8;
+ font_matrix.yx = face->log_font.matrix[1] << 8;
+ font_matrix.xy = face->log_font.matrix[2] << 8;
+ font_matrix.yy = face->log_font.matrix[3] << 8;
+
+ FT_Outline_Transform( outline, &font_matrix );
+ }
+#endif
+
+ /* scale when needed */
+ if ( scaling )
+ {
+ FT_Int n;
+ FT_Fixed x_scale = pfrsize->metrics.x_scale;
+ FT_Fixed y_scale = pfrsize->metrics.y_scale;
+ FT_Vector* vec = outline->points;
+
+
+ /* scale outline points */
+ for ( n = 0; n < outline->n_points; n++, vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, x_scale );
+ vec->y = FT_MulFix( vec->y, y_scale );
+ }
+
+ /* scale the advance */
+ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
+ metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
+ }
+
+ /* compute the rest of the metrics */
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ metrics->width = cbox.xMax - cbox.xMin;
+ metrics->height = cbox.yMax - cbox.yMin;
+ metrics->horiBearingX = cbox.xMin;
+ metrics->horiBearingY = cbox.yMax - metrics->height;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** KERNING METHOD *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( FT_Error )
+ pfr_face_get_kerning( FT_Face pfrface, /* PFR_Face */
+ FT_UInt glyph1,
+ FT_UInt glyph2,
+ FT_Vector* kerning )
+ {
+ PFR_Face face = (PFR_Face)pfrface;
+ FT_Error error = PFR_Err_Ok;
+ PFR_PhyFont phy_font = &face->phy_font;
+ FT_UInt32 code1, code2, pair;
+
+
+ kerning->x = 0;
+ kerning->y = 0;
+
+ if ( glyph1 > 0 )
+ glyph1--;
+
+ if ( glyph2 > 0 )
+ glyph2--;
+
+ /* convert glyph indices to character codes */
+ if ( glyph1 > phy_font->num_chars ||
+ glyph2 > phy_font->num_chars )
+ goto Exit;
+
+ code1 = phy_font->chars[glyph1].char_code;
+ code2 = phy_font->chars[glyph2].char_code;
+ pair = PFR_KERN_INDEX( code1, code2 );
+
+ /* now search the list of kerning items */
+ {
+ PFR_KernItem item = phy_font->kern_items;
+ FT_Stream stream = pfrface->stream;
+
+
+ for ( ; item; item = item->next )
+ {
+ if ( pair >= item->pair1 && pair <= item->pair2 )
+ goto FoundPair;
+ }
+ goto Exit;
+
+ FoundPair: /* we found an item, now parse it and find the value if any */
+ if ( FT_STREAM_SEEK( item->offset ) ||
+ FT_FRAME_ENTER( item->pair_count * item->pair_size ) )
+ goto Exit;
+
+ {
+ FT_UInt count = item->pair_count;
+ FT_UInt size = item->pair_size;
+ FT_UInt power = (FT_UInt)ft_highpow2( (FT_UInt32)count );
+ FT_UInt probe = power * size;
+ FT_UInt extra = count - power;
+ FT_Byte* base = stream->cursor;
+ FT_Bool twobytes = FT_BOOL( item->flags & 1 );
+ FT_Bool twobyte_adj = FT_BOOL( item->flags & 2 );
+ FT_Byte* p;
+ FT_UInt32 cpair;
+
+
+ if ( extra > 0 )
+ {
+ p = base + extra * size;
+
+ if ( twobytes )
+ cpair = FT_NEXT_ULONG( p );
+ else
+ cpair = PFR_NEXT_KPAIR( p );
+
+ if ( cpair == pair )
+ goto Found;
+
+ if ( cpair < pair )
+ {
+ if ( twobyte_adj )
+ p += 2;
+ else
+ p++;
+ base = p;
+ }
+ }
+
+ while ( probe > size )
+ {
+ probe >>= 1;
+ p = base + probe;
+
+ if ( twobytes )
+ cpair = FT_NEXT_ULONG( p );
+ else
+ cpair = PFR_NEXT_KPAIR( p );
+
+ if ( cpair == pair )
+ goto Found;
+
+ if ( cpair < pair )
+ base += probe;
+ }
+
+ p = base;
+
+ if ( twobytes )
+ cpair = FT_NEXT_ULONG( p );
+ else
+ cpair = PFR_NEXT_KPAIR( p );
+
+ if ( cpair == pair )
+ {
+ FT_Int value;
+
+
+ Found:
+ if ( twobyte_adj )
+ value = FT_PEEK_SHORT( p );
+ else
+ value = p[0];
+
+ kerning->x = item->base_adj + value;
+ }
+ }
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+/* END */
diff --git a/src/pfr/pfrobjs.h b/src/pfr/pfrobjs.h
new file mode 100644
index 0000000..f6aa8b4
--- /dev/null
+++ b/src/pfr/pfrobjs.h
@@ -0,0 +1,96 @@
+/***************************************************************************/
+/* */
+/* pfrobjs.h */
+/* */
+/* FreeType PFR object methods (specification). */
+/* */
+/* Copyright 2002, 2003, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFROBJS_H__
+#define __PFROBJS_H__
+
+#include "pfrtypes.h"
+
+
+FT_BEGIN_HEADER
+
+ typedef struct PFR_FaceRec_* PFR_Face;
+
+ typedef struct PFR_SizeRec_* PFR_Size;
+
+ typedef struct PFR_SlotRec_* PFR_Slot;
+
+
+ typedef struct PFR_FaceRec_
+ {
+ FT_FaceRec root;
+ PFR_HeaderRec header;
+ PFR_LogFontRec log_font;
+ PFR_PhyFontRec phy_font;
+
+ } PFR_FaceRec;
+
+
+ typedef struct PFR_SizeRec_
+ {
+ FT_SizeRec root;
+
+ } PFR_SizeRec;
+
+
+ typedef struct PFR_SlotRec_
+ {
+ FT_GlyphSlotRec root;
+ PFR_GlyphRec glyph;
+
+ } PFR_SlotRec;
+
+
+ FT_LOCAL( FT_Error )
+ pfr_face_init( FT_Stream stream,
+ FT_Face face, /* PFR_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ pfr_face_done( FT_Face face ); /* PFR_Face */
+
+
+ FT_LOCAL( FT_Error )
+ pfr_face_get_kerning( FT_Face face, /* PFR_Face */
+ FT_UInt glyph1,
+ FT_UInt glyph2,
+ FT_Vector* kerning );
+
+
+ FT_LOCAL( FT_Error )
+ pfr_slot_init( FT_GlyphSlot slot ); /* PFR_Slot */
+
+ FT_LOCAL( void )
+ pfr_slot_done( FT_GlyphSlot slot ); /* PFR_Slot */
+
+
+ FT_LOCAL( FT_Error )
+ pfr_slot_load( FT_GlyphSlot slot, /* PFR_Slot */
+ FT_Size size, /* PFR_Size */
+ FT_UInt gindex,
+ FT_Int32 load_flags );
+
+
+FT_END_HEADER
+
+#endif /* __PFROBJS_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrsbit.c b/src/pfr/pfrsbit.c
new file mode 100644
index 0000000..45ff666
--- /dev/null
+++ b/src/pfr/pfrsbit.c
@@ -0,0 +1,680 @@
+/***************************************************************************/
+/* */
+/* pfrsbit.c */
+/* */
+/* FreeType PFR bitmap loader (body). */
+/* */
+/* Copyright 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "pfrsbit.h"
+#include "pfrload.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+
+#include "pfrerror.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pfr
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PFR BIT WRITER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct PFR_BitWriter_
+ {
+ FT_Byte* line; /* current line start */
+ FT_Int pitch; /* line size in bytes */
+ FT_Int width; /* width in pixels/bits */
+ FT_Int rows; /* number of remaining rows to scan */
+ FT_Int total; /* total number of bits to draw */
+
+ } PFR_BitWriterRec, *PFR_BitWriter;
+
+
+ static void
+ pfr_bitwriter_init( PFR_BitWriter writer,
+ FT_Bitmap* target,
+ FT_Bool decreasing )
+ {
+ writer->line = target->buffer;
+ writer->pitch = target->pitch;
+ writer->width = target->width;
+ writer->rows = target->rows;
+ writer->total = writer->width * writer->rows;
+
+ if ( !decreasing )
+ {
+ writer->line += writer->pitch * ( target->rows-1 );
+ writer->pitch = -writer->pitch;
+ }
+ }
+
+
+ static void
+ pfr_bitwriter_decode_bytes( PFR_BitWriter writer,
+ FT_Byte* p,
+ FT_Byte* limit )
+ {
+ FT_Int n, reload;
+ FT_Int left = writer->width;
+ FT_Byte* cur = writer->line;
+ FT_UInt mask = 0x80;
+ FT_UInt val = 0;
+ FT_UInt c = 0;
+
+
+ n = (FT_Int)( limit - p ) * 8;
+ if ( n > writer->total )
+ n = writer->total;
+
+ reload = n & 7;
+
+ for ( ; n > 0; n-- )
+ {
+ if ( ( n & 7 ) == reload )
+ val = *p++;
+
+ if ( val & 0x80 )
+ c |= mask;
+
+ val <<= 1;
+ mask >>= 1;
+
+ if ( --left <= 0 )
+ {
+ cur[0] = (FT_Byte)c;
+ left = writer->width;
+ mask = 0x80;
+
+ writer->line += writer->pitch;
+ cur = writer->line;
+ c = 0;
+ }
+ else if ( mask == 0 )
+ {
+ cur[0] = (FT_Byte)c;
+ mask = 0x80;
+ c = 0;
+ cur ++;
+ }
+ }
+
+ if ( mask != 0x80 )
+ cur[0] = (FT_Byte)c;
+ }
+
+
+ static void
+ pfr_bitwriter_decode_rle1( PFR_BitWriter writer,
+ FT_Byte* p,
+ FT_Byte* limit )
+ {
+ FT_Int n, phase, count, counts[2], reload;
+ FT_Int left = writer->width;
+ FT_Byte* cur = writer->line;
+ FT_UInt mask = 0x80;
+ FT_UInt c = 0;
+
+
+ n = writer->total;
+
+ phase = 1;
+ counts[0] = 0;
+ counts[1] = 0;
+ count = 0;
+ reload = 1;
+
+ for ( ; n > 0; n-- )
+ {
+ if ( reload )
+ {
+ do
+ {
+ if ( phase )
+ {
+ FT_Int v;
+
+
+ if ( p >= limit )
+ break;
+
+ v = *p++;
+ counts[0] = v >> 4;
+ counts[1] = v & 15;
+ phase = 0;
+ count = counts[0];
+ }
+ else
+ {
+ phase = 1;
+ count = counts[1];
+ }
+
+ } while ( count == 0 );
+ }
+
+ if ( phase )
+ c |= mask;
+
+ mask >>= 1;
+
+ if ( --left <= 0 )
+ {
+ cur[0] = (FT_Byte) c;
+ left = writer->width;
+ mask = 0x80;
+
+ writer->line += writer->pitch;
+ cur = writer->line;
+ c = 0;
+ }
+ else if ( mask == 0 )
+ {
+ cur[0] = (FT_Byte)c;
+ mask = 0x80;
+ c = 0;
+ cur ++;
+ }
+
+ reload = ( --count <= 0 );
+ }
+
+ if ( mask != 0x80 )
+ cur[0] = (FT_Byte) c;
+ }
+
+
+ static void
+ pfr_bitwriter_decode_rle2( PFR_BitWriter writer,
+ FT_Byte* p,
+ FT_Byte* limit )
+ {
+ FT_Int n, phase, count, reload;
+ FT_Int left = writer->width;
+ FT_Byte* cur = writer->line;
+ FT_UInt mask = 0x80;
+ FT_UInt c = 0;
+
+
+ n = writer->total;
+
+ phase = 1;
+ count = 0;
+ reload = 1;
+
+ for ( ; n > 0; n-- )
+ {
+ if ( reload )
+ {
+ do
+ {
+ if ( p >= limit )
+ break;
+
+ count = *p++;
+ phase = phase ^ 1;
+
+ } while ( count == 0 );
+ }
+
+ if ( phase )
+ c |= mask;
+
+ mask >>= 1;
+
+ if ( --left <= 0 )
+ {
+ cur[0] = (FT_Byte) c;
+ c = 0;
+ mask = 0x80;
+ left = writer->width;
+
+ writer->line += writer->pitch;
+ cur = writer->line;
+ }
+ else if ( mask == 0 )
+ {
+ cur[0] = (FT_Byte)c;
+ c = 0;
+ mask = 0x80;
+ cur ++;
+ }
+
+ reload = ( --count <= 0 );
+ }
+
+ if ( mask != 0x80 )
+ cur[0] = (FT_Byte) c;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BITMAP DATA DECODING *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ pfr_lookup_bitmap_data( FT_Byte* base,
+ FT_Byte* limit,
+ FT_UInt count,
+ FT_UInt flags,
+ FT_UInt char_code,
+ FT_ULong* found_offset,
+ FT_ULong* found_size )
+ {
+ FT_UInt left, right, char_len;
+ FT_Bool two = FT_BOOL( flags & 1 );
+ FT_Byte* buff;
+
+
+ char_len = 4;
+ if ( two ) char_len += 1;
+ if ( flags & 2 ) char_len += 1;
+ if ( flags & 4 ) char_len += 1;
+
+ left = 0;
+ right = count;
+
+ while ( left < right )
+ {
+ FT_UInt middle, code;
+
+
+ middle = ( left + right ) >> 1;
+ buff = base + middle * char_len;
+
+ /* check that we are not outside of the table -- */
+ /* this is possible with broken fonts... */
+ if ( buff + char_len > limit )
+ goto Fail;
+
+ if ( two )
+ code = PFR_NEXT_USHORT( buff );
+ else
+ code = PFR_NEXT_BYTE( buff );
+
+ if ( code == char_code )
+ goto Found_It;
+
+ if ( code < char_code )
+ left = middle;
+ else
+ right = middle;
+ }
+
+ Fail:
+ /* Not found */
+ *found_size = 0;
+ *found_offset = 0;
+ return;
+
+ Found_It:
+ if ( flags & 2 )
+ *found_size = PFR_NEXT_USHORT( buff );
+ else
+ *found_size = PFR_NEXT_BYTE( buff );
+
+ if ( flags & 4 )
+ *found_offset = PFR_NEXT_ULONG( buff );
+ else
+ *found_offset = PFR_NEXT_USHORT( buff );
+ }
+
+
+ /* load bitmap metrics. "*padvance" must be set to the default value */
+ /* before calling this function... */
+ /* */
+ static FT_Error
+ pfr_load_bitmap_metrics( FT_Byte** pdata,
+ FT_Byte* limit,
+ FT_Long scaled_advance,
+ FT_Long *axpos,
+ FT_Long *aypos,
+ FT_UInt *axsize,
+ FT_UInt *aysize,
+ FT_Long *aadvance,
+ FT_UInt *aformat )
+ {
+ FT_Error error = 0;
+ FT_Byte flags;
+ FT_Char b;
+ FT_Byte* p = *pdata;
+ FT_Long xpos, ypos, advance;
+ FT_UInt xsize, ysize;
+
+
+ PFR_CHECK( 1 );
+ flags = PFR_NEXT_BYTE( p );
+
+ xpos = 0;
+ ypos = 0;
+ xsize = 0;
+ ysize = 0;
+ advance = 0;
+
+ switch ( flags & 3 )
+ {
+ case 0:
+ PFR_CHECK( 1 );
+ b = PFR_NEXT_INT8( p );
+ xpos = b >> 4;
+ ypos = ( (FT_Char)( b << 4 ) ) >> 4;
+ break;
+
+ case 1:
+ PFR_CHECK( 2 );
+ xpos = PFR_NEXT_INT8( p );
+ ypos = PFR_NEXT_INT8( p );
+ break;
+
+ case 2:
+ PFR_CHECK( 4 );
+ xpos = PFR_NEXT_SHORT( p );
+ ypos = PFR_NEXT_SHORT( p );
+ break;
+
+ case 3:
+ PFR_CHECK( 6 );
+ xpos = PFR_NEXT_LONG( p );
+ ypos = PFR_NEXT_LONG( p );
+ break;
+
+ default:
+ ;
+ }
+
+ flags >>= 2;
+ switch ( flags & 3 )
+ {
+ case 0:
+ /* blank image */
+ xsize = 0;
+ ysize = 0;
+ break;
+
+ case 1:
+ PFR_CHECK( 1 );
+ b = PFR_NEXT_BYTE( p );
+ xsize = ( b >> 4 ) & 0xF;
+ ysize = b & 0xF;
+ break;
+
+ case 2:
+ PFR_CHECK( 2 );
+ xsize = PFR_NEXT_BYTE( p );
+ ysize = PFR_NEXT_BYTE( p );
+ break;
+
+ case 3:
+ PFR_CHECK( 4 );
+ xsize = PFR_NEXT_USHORT( p );
+ ysize = PFR_NEXT_USHORT( p );
+ break;
+
+ default:
+ ;
+ }
+
+ flags >>= 2;
+ switch ( flags & 3 )
+ {
+ case 0:
+ advance = scaled_advance;
+ break;
+
+ case 1:
+ PFR_CHECK( 1 );
+ advance = PFR_NEXT_INT8( p ) << 8;
+ break;
+
+ case 2:
+ PFR_CHECK( 2 );
+ advance = PFR_NEXT_SHORT( p );
+ break;
+
+ case 3:
+ PFR_CHECK( 3 );
+ advance = PFR_NEXT_LONG( p );
+ break;
+
+ default:
+ ;
+ }
+
+ *axpos = xpos;
+ *aypos = ypos;
+ *axsize = xsize;
+ *aysize = ysize;
+ *aadvance = advance;
+ *aformat = flags >> 2;
+ *pdata = p;
+
+ Exit:
+ return error;
+
+ Too_Short:
+ error = PFR_Err_Invalid_Table;
+ FT_ERROR(( "pfr_load_bitmap_metrics: invalid glyph data\n" ));
+ goto Exit;
+ }
+
+
+ static FT_Error
+ pfr_load_bitmap_bits( FT_Byte* p,
+ FT_Byte* limit,
+ FT_UInt format,
+ FT_Bool decreasing,
+ FT_Bitmap* target )
+ {
+ FT_Error error = 0;
+ PFR_BitWriterRec writer;
+
+
+ if ( target->rows > 0 && target->width > 0 )
+ {
+ pfr_bitwriter_init( &writer, target, decreasing );
+
+ switch ( format )
+ {
+ case 0: /* packed bits */
+ pfr_bitwriter_decode_bytes( &writer, p, limit );
+ break;
+
+ case 1: /* RLE1 */
+ pfr_bitwriter_decode_rle1( &writer, p, limit );
+ break;
+
+ case 2: /* RLE2 */
+ pfr_bitwriter_decode_rle2( &writer, p, limit );
+ break;
+
+ default:
+ FT_ERROR(( "pfr_read_bitmap_data: invalid image type\n" ));
+ error = PFR_Err_Invalid_File_Format;
+ }
+ }
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BITMAP LOADING *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( FT_Error )
+ pfr_slot_load_bitmap( PFR_Slot glyph,
+ PFR_Size size,
+ FT_UInt glyph_index )
+ {
+ FT_Error error;
+ PFR_Face face = (PFR_Face) glyph->root.face;
+ FT_Stream stream = face->root.stream;
+ PFR_PhyFont phys = &face->phy_font;
+ FT_ULong gps_offset;
+ FT_ULong gps_size;
+ PFR_Char character;
+ PFR_Strike strike;
+
+
+ character = &phys->chars[glyph_index];
+
+ /* Look-up a bitmap strike corresponding to the current */
+ /* character dimensions */
+ {
+ FT_UInt n;
+
+
+ strike = phys->strikes;
+ for ( n = 0; n < phys->num_strikes; n++ )
+ {
+ if ( strike->x_ppm == (FT_UInt)size->root.metrics.x_ppem &&
+ strike->y_ppm == (FT_UInt)size->root.metrics.y_ppem )
+ {
+ goto Found_Strike;
+ }
+
+ strike++;
+ }
+
+ /* couldn't find it */
+ return PFR_Err_Invalid_Argument;
+ }
+
+ Found_Strike:
+
+ /* Now lookup the glyph's position within the file */
+ {
+ FT_UInt char_len;
+
+
+ char_len = 4;
+ if ( strike->flags & 1 ) char_len += 1;
+ if ( strike->flags & 2 ) char_len += 1;
+ if ( strike->flags & 4 ) char_len += 1;
+
+ /* Access data directly in the frame to speed lookups */
+ if ( FT_STREAM_SEEK( phys->bct_offset + strike->bct_offset ) ||
+ FT_FRAME_ENTER( char_len * strike->num_bitmaps ) )
+ goto Exit;
+
+ pfr_lookup_bitmap_data( stream->cursor,
+ stream->limit,
+ strike->num_bitmaps,
+ strike->flags,
+ character->char_code,
+ &gps_offset,
+ &gps_size );
+
+ FT_FRAME_EXIT();
+
+ if ( gps_size == 0 )
+ {
+ /* Could not find a bitmap program string for this glyph */
+ error = PFR_Err_Invalid_Argument;
+ goto Exit;
+ }
+ }
+
+ /* get the bitmap metrics */
+ {
+ FT_Long xpos, ypos, advance;
+ FT_UInt xsize, ysize, format;
+ FT_Byte* p;
+
+
+ /* compute linear advance */
+ advance = character->advance;
+ if ( phys->metrics_resolution != phys->outline_resolution )
+ advance = FT_MulDiv( advance,
+ phys->outline_resolution,
+ phys->metrics_resolution );
+
+ glyph->root.linearHoriAdvance = advance;
+
+ /* compute default advance, i.e., scaled advance. This can be */
+ /* overridden in the bitmap header of certain glyphs. */
+ advance = FT_MulDiv( (FT_Fixed)size->root.metrics.x_ppem << 8,
+ character->advance,
+ phys->metrics_resolution );
+
+ if ( FT_STREAM_SEEK( face->header.gps_section_offset + gps_offset ) ||
+ FT_FRAME_ENTER( gps_size ) )
+ goto Exit;
+
+ p = stream->cursor;
+ error = pfr_load_bitmap_metrics( &p, stream->limit,
+ advance,
+ &xpos, &ypos,
+ &xsize, &ysize,
+ &advance, &format );
+ if ( !error )
+ {
+ glyph->root.format = FT_GLYPH_FORMAT_BITMAP;
+
+ /* Set up glyph bitmap and metrics */
+ glyph->root.bitmap.width = (FT_Int)xsize;
+ glyph->root.bitmap.rows = (FT_Int)ysize;
+ glyph->root.bitmap.pitch = (FT_Long)( xsize + 7 ) >> 3;
+ glyph->root.bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
+
+ glyph->root.metrics.width = (FT_Long)xsize << 6;
+ glyph->root.metrics.height = (FT_Long)ysize << 6;
+ glyph->root.metrics.horiBearingX = xpos << 6;
+ glyph->root.metrics.horiBearingY = ypos << 6;
+ glyph->root.metrics.horiAdvance = FT_PIX_ROUND( ( advance >> 2 ) );
+ glyph->root.metrics.vertBearingX = - glyph->root.metrics.width >> 1;
+ glyph->root.metrics.vertBearingY = 0;
+ glyph->root.metrics.vertAdvance = size->root.metrics.height;
+
+ glyph->root.bitmap_left = xpos;
+ glyph->root.bitmap_top = ypos + ysize;
+
+ /* Allocate and read bitmap data */
+ {
+ FT_ULong len = glyph->root.bitmap.pitch * ysize;
+
+
+ error = ft_glyphslot_alloc_bitmap( &glyph->root, len );
+ if ( !error )
+ {
+ error = pfr_load_bitmap_bits(
+ p,
+ stream->limit,
+ format,
+ FT_BOOL(face->header.color_flags & 2),
+ &glyph->root.bitmap );
+ }
+ }
+ }
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+/* END */
diff --git a/src/pfr/pfrsbit.h b/src/pfr/pfrsbit.h
new file mode 100644
index 0000000..015e9e6
--- /dev/null
+++ b/src/pfr/pfrsbit.h
@@ -0,0 +1,36 @@
+/***************************************************************************/
+/* */
+/* pfrsbit.h */
+/* */
+/* FreeType PFR bitmap loader (specification). */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRSBIT_H__
+#define __PFRSBIT_H__
+
+#include "pfrobjs.h"
+
+FT_BEGIN_HEADER
+
+ FT_LOCAL( FT_Error )
+ pfr_slot_load_bitmap( PFR_Slot glyph,
+ PFR_Size size,
+ FT_UInt glyph_index );
+
+FT_END_HEADER
+
+#endif /* __PFR_SBIT_H__ */
+
+
+/* END */
diff --git a/src/pfr/pfrtypes.h b/src/pfr/pfrtypes.h
new file mode 100644
index 0000000..c0ae042
--- /dev/null
+++ b/src/pfr/pfrtypes.h
@@ -0,0 +1,362 @@
+/***************************************************************************/
+/* */
+/* pfrtypes.h */
+/* */
+/* FreeType PFR data structures (specification only). */
+/* */
+/* Copyright 2002, 2003, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PFRTYPES_H__
+#define __PFRTYPES_H__
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+
+FT_BEGIN_HEADER
+
+ /************************************************************************/
+
+ /* the PFR Header structure */
+ typedef struct PFR_HeaderRec_
+ {
+ FT_UInt32 signature;
+ FT_UInt version;
+ FT_UInt signature2;
+ FT_UInt header_size;
+
+ FT_UInt log_dir_size;
+ FT_UInt log_dir_offset;
+
+ FT_UInt log_font_max_size;
+ FT_UInt32 log_font_section_size;
+ FT_UInt32 log_font_section_offset;
+
+ FT_UInt32 phy_font_max_size;
+ FT_UInt32 phy_font_section_size;
+ FT_UInt32 phy_font_section_offset;
+
+ FT_UInt gps_max_size;
+ FT_UInt32 gps_section_size;
+ FT_UInt32 gps_section_offset;
+
+ FT_UInt max_blue_values;
+ FT_UInt max_x_orus;
+ FT_UInt max_y_orus;
+
+ FT_UInt phy_font_max_size_high;
+ FT_UInt color_flags;
+
+ FT_UInt32 bct_max_size;
+ FT_UInt32 bct_set_max_size;
+ FT_UInt32 phy_bct_set_max_size;
+
+ FT_UInt num_phy_fonts;
+ FT_UInt max_vert_stem_snap;
+ FT_UInt max_horz_stem_snap;
+ FT_UInt max_chars;
+
+ } PFR_HeaderRec, *PFR_Header;
+
+
+ /* used in `color_flags' field of the PFR_Header */
+ typedef enum PFR_HeaderFlags_
+ {
+ PFR_FLAG_BLACK_PIXEL = 1,
+ PFR_FLAG_INVERT_BITMAP = 2
+
+ } PFR_HeaderFlags;
+
+
+ /************************************************************************/
+
+ typedef struct PFR_LogFontRec_
+ {
+ FT_UInt32 size;
+ FT_UInt32 offset;
+
+ FT_Int32 matrix[4];
+ FT_UInt stroke_flags;
+ FT_Int stroke_thickness;
+ FT_Int bold_thickness;
+ FT_Int32 miter_limit;
+
+ FT_UInt32 phys_size;
+ FT_UInt32 phys_offset;
+
+ } PFR_LogFontRec, *PFR_LogFont;
+
+
+ typedef enum PFR_LogFlags_
+ {
+ PFR_LOG_EXTRA_ITEMS = 0x40,
+ PFR_LOG_2BYTE_BOLD = 0x20,
+ PFR_LOG_BOLD = 0x10,
+ PFR_LOG_2BYTE_STROKE = 8,
+ PFR_LOG_STROKE = 4,
+ PFR_LINE_JOIN_MASK = 3
+
+ } PFR_LogFlags;
+
+
+ typedef enum PFR_LineJoinFlags_
+ {
+ PFR_LINE_JOIN_MITER = 0,
+ PFR_LINE_JOIN_ROUND = 1,
+ PFR_LINE_JOIN_BEVEL = 2
+
+ } PFR_LineJoinFlags;
+
+
+ /************************************************************************/
+
+ typedef enum PFR_BitmapFlags_
+ {
+ PFR_BITMAP_3BYTE_OFFSET = 4,
+ PFR_BITMAP_2BYTE_SIZE = 2,
+ PFR_BITMAP_2BYTE_CHARCODE = 1
+
+ } PFR_BitmapFlags;
+
+
+ typedef struct PFR_BitmapCharRec_
+ {
+ FT_UInt char_code;
+ FT_UInt gps_size;
+ FT_UInt32 gps_offset;
+
+ } PFR_BitmapCharRec, *PFR_BitmapChar;
+
+
+ typedef enum PFR_StrikeFlags_
+ {
+ PFR_STRIKE_2BYTE_COUNT = 0x10,
+ PFR_STRIKE_3BYTE_OFFSET = 0x08,
+ PFR_STRIKE_3BYTE_SIZE = 0x04,
+ PFR_STRIKE_2BYTE_YPPM = 0x02,
+ PFR_STRIKE_2BYTE_XPPM = 0x01
+
+ } PFR_StrikeFlags;
+
+
+ typedef struct PFR_StrikeRec_
+ {
+ FT_UInt x_ppm;
+ FT_UInt y_ppm;
+ FT_UInt flags;
+
+ FT_UInt32 gps_size;
+ FT_UInt32 gps_offset;
+
+ FT_UInt32 bct_size;
+ FT_UInt32 bct_offset;
+
+ /* optional */
+ FT_UInt num_bitmaps;
+ PFR_BitmapChar bitmaps;
+
+ } PFR_StrikeRec, *PFR_Strike;
+
+
+ /************************************************************************/
+
+ typedef struct PFR_CharRec_
+ {
+ FT_UInt char_code;
+ FT_Int advance;
+ FT_UInt gps_size;
+ FT_UInt32 gps_offset;
+
+ } PFR_CharRec, *PFR_Char;
+
+
+ /************************************************************************/
+
+ typedef struct PFR_DimensionRec_
+ {
+ FT_UInt standard;
+ FT_UInt num_stem_snaps;
+ FT_Int* stem_snaps;
+
+ } PFR_DimensionRec, *PFR_Dimension;
+
+ /************************************************************************/
+
+ typedef struct PFR_KernItemRec_* PFR_KernItem;
+
+ typedef struct PFR_KernItemRec_
+ {
+ PFR_KernItem next;
+ FT_Byte pair_count;
+ FT_Byte flags;
+ FT_Short base_adj;
+ FT_UInt pair_size;
+ FT_UInt32 offset;
+ FT_UInt32 pair1;
+ FT_UInt32 pair2;
+
+ } PFR_KernItemRec;
+
+
+#define PFR_KERN_INDEX( g1, g2 ) \
+ ( ( (FT_UInt32)(g1) << 16 ) | (FT_UInt16)(g2) )
+
+#define PFR_KERN_PAIR_INDEX( pair ) \
+ PFR_KERN_INDEX( (pair)->glyph1, (pair)->glyph2 )
+
+#define PFR_NEXT_KPAIR( p ) ( p += 2, \
+ ( (FT_UInt32)p[-2] << 16 ) | p[-1] )
+
+
+ /************************************************************************/
+
+ typedef struct PFR_PhyFontRec_
+ {
+ FT_Memory memory;
+ FT_UInt32 offset;
+
+ FT_UInt font_ref_number;
+ FT_UInt outline_resolution;
+ FT_UInt metrics_resolution;
+ FT_BBox bbox;
+ FT_UInt flags;
+ FT_UInt standard_advance;
+
+ FT_Int ascent; /* optional, bbox.yMax if not present */
+ FT_Int descent; /* optional, bbox.yMin if not present */
+ FT_Int leading; /* optional, 0 if not present */
+
+ PFR_DimensionRec horizontal;
+ PFR_DimensionRec vertical;
+
+ FT_String* font_id;
+ FT_String* family_name;
+ FT_String* style_name;
+
+ FT_UInt num_strikes;
+ FT_UInt max_strikes;
+ PFR_StrikeRec* strikes;
+
+ FT_UInt num_blue_values;
+ FT_Int *blue_values;
+ FT_UInt blue_fuzz;
+ FT_UInt blue_scale;
+
+ FT_UInt num_chars;
+ FT_UInt32 chars_offset;
+ PFR_Char chars;
+
+ FT_UInt num_kern_pairs;
+ PFR_KernItem kern_items;
+ PFR_KernItem* kern_items_tail;
+
+ /* not part of the spec, but used during load */
+ FT_UInt32 bct_offset;
+ FT_Byte* cursor;
+
+ } PFR_PhyFontRec, *PFR_PhyFont;
+
+
+ typedef enum PFR_PhyFlags_
+ {
+ PFR_PHY_EXTRA_ITEMS = 0x80,
+ PFR_PHY_3BYTE_GPS_OFFSET = 0x20,
+ PFR_PHY_2BYTE_GPS_SIZE = 0x10,
+ PFR_PHY_ASCII_CODE = 0x08,
+ PFR_PHY_PROPORTIONAL = 0x04,
+ PFR_PHY_2BYTE_CHARCODE = 0x02,
+ PFR_PHY_VERTICAL = 0x01
+
+ } PFR_PhyFlags;
+
+
+ typedef enum PFR_KernFlags_
+ {
+ PFR_KERN_2BYTE_CHAR = 0x01,
+ PFR_KERN_2BYTE_ADJ = 0x02
+
+ } PFR_KernFlags;
+
+
+ /************************************************************************/
+
+ typedef enum PFR_GlyphFlags_
+ {
+ PFR_GLYPH_IS_COMPOUND = 0x80,
+ PFR_GLYPH_EXTRA_ITEMS = 0x08,
+ PFR_GLYPH_1BYTE_XYCOUNT = 0x04,
+ PFR_GLYPH_XCOUNT = 0x02,
+ PFR_GLYPH_YCOUNT = 0x01
+
+ } PFR_GlyphFlags;
+
+
+ /* controlled coordinate */
+ typedef struct PFR_CoordRec_
+ {
+ FT_UInt org;
+ FT_UInt cur;
+
+ } PFR_CoordRec, *PFR_Coord;
+
+
+ typedef struct PFR_SubGlyphRec_
+ {
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+ FT_Int x_delta;
+ FT_Int y_delta;
+ FT_UInt32 gps_offset;
+ FT_UInt gps_size;
+
+ } PFR_SubGlyphRec, *PFR_SubGlyph;
+
+
+ typedef enum PFR_SubgGlyphFlags_
+ {
+ PFR_SUBGLYPH_3BYTE_OFFSET = 0x80,
+ PFR_SUBGLYPH_2BYTE_SIZE = 0x40,
+ PFR_SUBGLYPH_YSCALE = 0x20,
+ PFR_SUBGLYPH_XSCALE = 0x10
+
+ } PFR_SubGlyphFlags;
+
+
+ typedef struct PFR_GlyphRec_
+ {
+ FT_Byte format;
+
+#if 0
+ FT_UInt num_x_control;
+ FT_UInt num_y_control;
+#endif
+ FT_UInt max_xy_control;
+ FT_Pos* x_control;
+ FT_Pos* y_control;
+
+
+ FT_UInt num_subs;
+ FT_UInt max_subs;
+ PFR_SubGlyphRec* subs;
+
+ FT_GlyphLoader loader;
+ FT_Bool path_begun;
+
+ } PFR_GlyphRec, *PFR_Glyph;
+
+
+FT_END_HEADER
+
+#endif /* __PFRTYPES_H__ */
+
+
+/* END */
diff --git a/src/pfr/rules.mk b/src/pfr/rules.mk
new file mode 100644
index 0000000..60b96c7
--- /dev/null
+++ b/src/pfr/rules.mk
@@ -0,0 +1,73 @@
+#
+# FreeType 2 PFR driver configuration rules
+#
+
+
+# Copyright 2002, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# pfr driver directory
+#
+PFR_DIR := $(SRC_DIR)/pfr
+
+
+# compilation flags for the driver
+#
+PFR_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PFR_DIR))
+
+
+# pfr driver sources (i.e., C files)
+#
+PFR_DRV_SRC := $(PFR_DIR)/pfrload.c \
+ $(PFR_DIR)/pfrgload.c \
+ $(PFR_DIR)/pfrcmap.c \
+ $(PFR_DIR)/pfrdrivr.c \
+ $(PFR_DIR)/pfrsbit.c \
+ $(PFR_DIR)/pfrobjs.c
+
+# pfr driver headers
+#
+PFR_DRV_H := $(PFR_DRV_SRC:%.c=%.h) \
+ $(PFR_DIR)/pfrerror.h \
+ $(PFR_DIR)/pfrtypes.h
+
+
+# Pfr driver object(s)
+#
+# PFR_DRV_OBJ_M is used during `multi' builds
+# PFR_DRV_OBJ_S is used during `single' builds
+#
+PFR_DRV_OBJ_M := $(PFR_DRV_SRC:$(PFR_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PFR_DRV_OBJ_S := $(OBJ_DIR)/pfr.$O
+
+# pfr driver source file for single build
+#
+PFR_DRV_SRC_S := $(PFR_DIR)/pfr.c
+
+
+# pfr driver - single object
+#
+$(PFR_DRV_OBJ_S): $(PFR_DRV_SRC_S) $(PFR_DRV_SRC) $(FREETYPE_H) $(PFR_DRV_H)
+ $(PFR_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PFR_DRV_SRC_S))
+
+
+# pfr driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PFR_DIR)/%.c $(FREETYPE_H) $(PFR_DRV_H)
+ $(PFR_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PFR_DRV_OBJ_S)
+DRV_OBJS_M += $(PFR_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/psaux/Jamfile b/src/psaux/Jamfile
new file mode 100644
index 0000000..faeded9
--- /dev/null
+++ b/src/psaux/Jamfile
@@ -0,0 +1,31 @@
+# FreeType 2 src/psaux Jamfile
+#
+# Copyright 2001, 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) psaux ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = psauxmod psobjs t1decode t1cmap
+ psconv afmparse
+ ;
+ }
+ else
+ {
+ _sources = psaux ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/psaux Jamfile
diff --git a/src/psaux/afmparse.c b/src/psaux/afmparse.c
new file mode 100644
index 0000000..0528fe6
--- /dev/null
+++ b/src/psaux/afmparse.c
@@ -0,0 +1,960 @@
+/***************************************************************************/
+/* */
+/* afmparse.c */
+/* */
+/* AFM parser (body). */
+/* */
+/* Copyright 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "afmparse.h"
+#include "psconv.h"
+
+#include "psauxerr.h"
+
+
+/***************************************************************************/
+/* */
+/* AFM_Stream */
+/* */
+/* The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib. */
+/* */
+/* */
+
+ enum
+ {
+ AFM_STREAM_STATUS_NORMAL,
+ AFM_STREAM_STATUS_EOC,
+ AFM_STREAM_STATUS_EOL,
+ AFM_STREAM_STATUS_EOF
+ };
+
+
+ typedef struct AFM_StreamRec_
+ {
+ FT_Byte* cursor;
+ FT_Byte* base;
+ FT_Byte* limit;
+
+ FT_Int status;
+
+ } AFM_StreamRec;
+
+
+#ifndef EOF
+#define EOF -1
+#endif
+
+
+ /* this works because empty lines are ignored */
+#define AFM_IS_NEWLINE( ch ) ( (ch) == '\r' || (ch) == '\n' )
+
+#define AFM_IS_EOF( ch ) ( (ch) == EOF || (ch) == '\x1a' )
+#define AFM_IS_SPACE( ch ) ( (ch) == ' ' || (ch) == '\t' )
+
+ /* column separator; there is no `column' in the spec actually */
+#define AFM_IS_SEP( ch ) ( (ch) == ';' )
+
+#define AFM_GETC() \
+ ( ( (stream)->cursor < (stream)->limit ) ? *(stream)->cursor++ \
+ : EOF )
+
+#define AFM_STREAM_KEY_BEGIN( stream ) \
+ (char*)( (stream)->cursor - 1 )
+
+#define AFM_STREAM_KEY_LEN( stream, key ) \
+ ( (char*)(stream)->cursor - key - 1 )
+
+#define AFM_STATUS_EOC( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOC )
+
+#define AFM_STATUS_EOL( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOL )
+
+#define AFM_STATUS_EOF( stream ) \
+ ( (stream)->status >= AFM_STREAM_STATUS_EOF )
+
+
+ static int
+ afm_stream_skip_spaces( AFM_Stream stream )
+ {
+ int ch = 0; /* make stupid compiler happy */
+
+
+ if ( AFM_STATUS_EOC( stream ) )
+ return ';';
+
+ while ( 1 )
+ {
+ ch = AFM_GETC();
+ if ( !AFM_IS_SPACE( ch ) )
+ break;
+ }
+
+ if ( AFM_IS_NEWLINE( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOL;
+ else if ( AFM_IS_SEP( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOC;
+ else if ( AFM_IS_EOF( ch ) )
+ stream->status = AFM_STREAM_STATUS_EOF;
+
+ return ch;
+ }
+
+
+ /* read a key or value in current column */
+ static char*
+ afm_stream_read_one( AFM_Stream stream )
+ {
+ char* str;
+ int ch;
+
+
+ afm_stream_skip_spaces( stream );
+ if ( AFM_STATUS_EOC( stream ) )
+ return NULL;
+
+ str = AFM_STREAM_KEY_BEGIN( stream );
+
+ while ( 1 )
+ {
+ ch = AFM_GETC();
+ if ( AFM_IS_SPACE( ch ) )
+ break;
+ else if ( AFM_IS_NEWLINE( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOL;
+ break;
+ }
+ else if ( AFM_IS_SEP( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOC;
+ break;
+ }
+ else if ( AFM_IS_EOF( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOF;
+ break;
+ }
+ }
+
+ return str;
+ }
+
+
+ /* read a string (i.e., read to EOL) */
+ static char*
+ afm_stream_read_string( AFM_Stream stream )
+ {
+ char* str;
+ int ch;
+
+
+ afm_stream_skip_spaces( stream );
+ if ( AFM_STATUS_EOL( stream ) )
+ return NULL;
+
+ str = AFM_STREAM_KEY_BEGIN( stream );
+
+ /* scan to eol */
+ while ( 1 )
+ {
+ ch = AFM_GETC();
+ if ( AFM_IS_NEWLINE( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOL;
+ break;
+ }
+ else if ( AFM_IS_EOF( ch ) )
+ {
+ stream->status = AFM_STREAM_STATUS_EOF;
+ break;
+ }
+ }
+
+ return str;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* AFM_Parser */
+ /* */
+ /* */
+
+ /* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */
+ typedef enum AFM_Token_
+ {
+ AFM_TOKEN_ASCENDER,
+ AFM_TOKEN_AXISLABEL,
+ AFM_TOKEN_AXISTYPE,
+ AFM_TOKEN_B,
+ AFM_TOKEN_BLENDAXISTYPES,
+ AFM_TOKEN_BLENDDESIGNMAP,
+ AFM_TOKEN_BLENDDESIGNPOSITIONS,
+ AFM_TOKEN_C,
+ AFM_TOKEN_CC,
+ AFM_TOKEN_CH,
+ AFM_TOKEN_CAPHEIGHT,
+ AFM_TOKEN_CHARWIDTH,
+ AFM_TOKEN_CHARACTERSET,
+ AFM_TOKEN_CHARACTERS,
+ AFM_TOKEN_DESCENDER,
+ AFM_TOKEN_ENCODINGSCHEME,
+ AFM_TOKEN_ENDAXIS,
+ AFM_TOKEN_ENDCHARMETRICS,
+ AFM_TOKEN_ENDCOMPOSITES,
+ AFM_TOKEN_ENDDIRECTION,
+ AFM_TOKEN_ENDFONTMETRICS,
+ AFM_TOKEN_ENDKERNDATA,
+ AFM_TOKEN_ENDKERNPAIRS,
+ AFM_TOKEN_ENDTRACKKERN,
+ AFM_TOKEN_ESCCHAR,
+ AFM_TOKEN_FAMILYNAME,
+ AFM_TOKEN_FONTBBOX,
+ AFM_TOKEN_FONTNAME,
+ AFM_TOKEN_FULLNAME,
+ AFM_TOKEN_ISBASEFONT,
+ AFM_TOKEN_ISCIDFONT,
+ AFM_TOKEN_ISFIXEDPITCH,
+ AFM_TOKEN_ISFIXEDV,
+ AFM_TOKEN_ITALICANGLE,
+ AFM_TOKEN_KP,
+ AFM_TOKEN_KPH,
+ AFM_TOKEN_KPX,
+ AFM_TOKEN_KPY,
+ AFM_TOKEN_L,
+ AFM_TOKEN_MAPPINGSCHEME,
+ AFM_TOKEN_METRICSSETS,
+ AFM_TOKEN_N,
+ AFM_TOKEN_NOTICE,
+ AFM_TOKEN_PCC,
+ AFM_TOKEN_STARTAXIS,
+ AFM_TOKEN_STARTCHARMETRICS,
+ AFM_TOKEN_STARTCOMPOSITES,
+ AFM_TOKEN_STARTDIRECTION,
+ AFM_TOKEN_STARTFONTMETRICS,
+ AFM_TOKEN_STARTKERNDATA,
+ AFM_TOKEN_STARTKERNPAIRS,
+ AFM_TOKEN_STARTKERNPAIRS0,
+ AFM_TOKEN_STARTKERNPAIRS1,
+ AFM_TOKEN_STARTTRACKKERN,
+ AFM_TOKEN_STDHW,
+ AFM_TOKEN_STDVW,
+ AFM_TOKEN_TRACKKERN,
+ AFM_TOKEN_UNDERLINEPOSITION,
+ AFM_TOKEN_UNDERLINETHICKNESS,
+ AFM_TOKEN_VV,
+ AFM_TOKEN_VVECTOR,
+ AFM_TOKEN_VERSION,
+ AFM_TOKEN_W,
+ AFM_TOKEN_W0,
+ AFM_TOKEN_W0X,
+ AFM_TOKEN_W0Y,
+ AFM_TOKEN_W1,
+ AFM_TOKEN_W1X,
+ AFM_TOKEN_W1Y,
+ AFM_TOKEN_WX,
+ AFM_TOKEN_WY,
+ AFM_TOKEN_WEIGHT,
+ AFM_TOKEN_WEIGHTVECTOR,
+ AFM_TOKEN_XHEIGHT,
+ N_AFM_TOKENS,
+ AFM_TOKEN_UNKNOWN
+
+ } AFM_Token;
+
+
+ static const char* const afm_key_table[N_AFM_TOKENS] =
+ {
+ "Ascender",
+ "AxisLabel",
+ "AxisType",
+ "B",
+ "BlendAxisTypes",
+ "BlendDesignMap",
+ "BlendDesignPositions",
+ "C",
+ "CC",
+ "CH",
+ "CapHeight",
+ "CharWidth",
+ "CharacterSet",
+ "Characters",
+ "Descender",
+ "EncodingScheme",
+ "EndAxis",
+ "EndCharMetrics",
+ "EndComposites",
+ "EndDirection",
+ "EndFontMetrics",
+ "EndKernData",
+ "EndKernPairs",
+ "EndTrackKern",
+ "EscChar",
+ "FamilyName",
+ "FontBBox",
+ "FontName",
+ "FullName",
+ "IsBaseFont",
+ "IsCIDFont",
+ "IsFixedPitch",
+ "IsFixedV",
+ "ItalicAngle",
+ "KP",
+ "KPH",
+ "KPX",
+ "KPY",
+ "L",
+ "MappingScheme",
+ "MetricsSets",
+ "N",
+ "Notice",
+ "PCC",
+ "StartAxis",
+ "StartCharMetrics",
+ "StartComposites",
+ "StartDirection",
+ "StartFontMetrics",
+ "StartKernData",
+ "StartKernPairs",
+ "StartKernPairs0",
+ "StartKernPairs1",
+ "StartTrackKern",
+ "StdHW",
+ "StdVW",
+ "TrackKern",
+ "UnderlinePosition",
+ "UnderlineThickness",
+ "VV",
+ "VVector",
+ "Version",
+ "W",
+ "W0",
+ "W0X",
+ "W0Y",
+ "W1",
+ "W1X",
+ "W1Y",
+ "WX",
+ "WY",
+ "Weight",
+ "WeightVector",
+ "XHeight"
+ };
+
+
+ /*
+ * `afm_parser_read_vals' and `afm_parser_next_key' provide
+ * high-level operations to an AFM_Stream. The rest of the
+ * parser functions should use them without accessing the
+ * AFM_Stream directly.
+ */
+
+ FT_LOCAL_DEF( FT_Int )
+ afm_parser_read_vals( AFM_Parser parser,
+ AFM_Value vals,
+ FT_Int n )
+ {
+ AFM_Stream stream = parser->stream;
+ char* str;
+ FT_Int i;
+
+
+ if ( n > AFM_MAX_ARGUMENTS )
+ return 0;
+
+ for ( i = 0; i < n; i++ )
+ {
+ FT_UInt len;
+ AFM_Value val = vals + i;
+
+
+ if ( val->type == AFM_VALUE_TYPE_STRING )
+ str = afm_stream_read_string( stream );
+ else
+ str = afm_stream_read_one( stream );
+
+ if ( !str )
+ break;
+
+ len = AFM_STREAM_KEY_LEN( stream, str );
+
+ switch ( val->type )
+ {
+ case AFM_VALUE_TYPE_STRING:
+ case AFM_VALUE_TYPE_NAME:
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( !FT_QALLOC( val->u.s, len + 1 ) )
+ {
+ ft_memcpy( val->u.s, str, len );
+ val->u.s[len] = '\0';
+ }
+ }
+ break;
+
+ case AFM_VALUE_TYPE_FIXED:
+ val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str,
+ (FT_Byte*)str + len, 0 );
+ break;
+
+ case AFM_VALUE_TYPE_INTEGER:
+ val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str,
+ (FT_Byte*)str + len );
+ break;
+
+ case AFM_VALUE_TYPE_BOOL:
+ val->u.b = FT_BOOL( len == 4 &&
+ !ft_strncmp( str, "true", 4 ) );
+ break;
+
+ case AFM_VALUE_TYPE_INDEX:
+ if ( parser->get_index )
+ val->u.i = parser->get_index( str, len, parser->user_data );
+ else
+ val->u.i = 0;
+ break;
+ }
+ }
+
+ return i;
+ }
+
+
+ FT_LOCAL_DEF( char* )
+ afm_parser_next_key( AFM_Parser parser,
+ FT_Bool line,
+ FT_UInt* len )
+ {
+ AFM_Stream stream = parser->stream;
+ char* key = 0; /* make stupid compiler happy */
+
+
+ if ( line )
+ {
+ while ( 1 )
+ {
+ /* skip current line */
+ if ( !AFM_STATUS_EOL( stream ) )
+ afm_stream_read_string( stream );
+
+ stream->status = AFM_STREAM_STATUS_NORMAL;
+ key = afm_stream_read_one( stream );
+
+ /* skip empty line */
+ if ( !key &&
+ !AFM_STATUS_EOF( stream ) &&
+ AFM_STATUS_EOL( stream ) )
+ continue;
+
+ break;
+ }
+ }
+ else
+ {
+ while ( 1 )
+ {
+ /* skip current column */
+ while ( !AFM_STATUS_EOC( stream ) )
+ afm_stream_read_one( stream );
+
+ stream->status = AFM_STREAM_STATUS_NORMAL;
+ key = afm_stream_read_one( stream );
+
+ /* skip empty column */
+ if ( !key &&
+ !AFM_STATUS_EOF( stream ) &&
+ AFM_STATUS_EOC( stream ) )
+ continue;
+
+ break;
+ }
+ }
+
+ if ( len )
+ *len = ( key ) ? AFM_STREAM_KEY_LEN( stream, key )
+ : 0;
+
+ return key;
+ }
+
+
+ static AFM_Token
+ afm_tokenize( const char* key,
+ FT_UInt len )
+ {
+ int n;
+
+
+ for ( n = 0; n < N_AFM_TOKENS; n++ )
+ {
+ if ( *( afm_key_table[n] ) == *key )
+ {
+ for ( ; n < N_AFM_TOKENS; n++ )
+ {
+ if ( *( afm_key_table[n] ) != *key )
+ return AFM_TOKEN_UNKNOWN;
+
+ if ( ft_strncmp( afm_key_table[n], key, len ) == 0 )
+ return (AFM_Token) n;
+ }
+ }
+ }
+
+ return AFM_TOKEN_UNKNOWN;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ afm_parser_init( AFM_Parser parser,
+ FT_Memory memory,
+ FT_Byte* base,
+ FT_Byte* limit )
+ {
+ AFM_Stream stream;
+ FT_Error error;
+
+
+ if ( FT_NEW( stream ) )
+ return error;
+
+ stream->cursor = stream->base = base;
+ stream->limit = limit;
+
+ /* don't skip the first line during the first call */
+ stream->status = AFM_STREAM_STATUS_EOL;
+
+ parser->memory = memory;
+ parser->stream = stream;
+ parser->FontInfo = NULL;
+ parser->get_index = NULL;
+
+ return PSaux_Err_Ok;
+ }
+
+
+ FT_LOCAL( void )
+ afm_parser_done( AFM_Parser parser )
+ {
+ FT_Memory memory = parser->memory;
+
+
+ FT_FREE( parser->stream );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ afm_parser_read_int( AFM_Parser parser,
+ FT_Int* aint )
+ {
+ AFM_ValueRec val;
+
+
+ val.type = AFM_VALUE_TYPE_INTEGER;
+
+ if ( afm_parser_read_vals( parser, &val, 1 ) == 1 )
+ {
+ *aint = val.u.i;
+
+ return PSaux_Err_Ok;
+ }
+ else
+ return PSaux_Err_Syntax_Error;
+ }
+
+
+ static FT_Error
+ afm_parse_track_kern( AFM_Parser parser )
+ {
+ AFM_FontInfo fi = parser->FontInfo;
+ AFM_TrackKern tk;
+ char* key;
+ FT_UInt len;
+ int n = -1;
+
+
+ if ( afm_parser_read_int( parser, &fi->NumTrackKern ) )
+ goto Fail;
+
+ if ( fi->NumTrackKern )
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) )
+ return error;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_ValueRec shared_vals[5];
+
+
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_TRACKKERN:
+ n++;
+
+ if ( n >= fi->NumTrackKern )
+ goto Fail;
+
+ tk = fi->TrackKerns + n;
+
+ shared_vals[0].type = AFM_VALUE_TYPE_INTEGER;
+ shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[4].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 )
+ goto Fail;
+
+ tk->degree = shared_vals[0].u.i;
+ tk->min_ptsize = shared_vals[1].u.f;
+ tk->min_kern = shared_vals[2].u.f;
+ tk->max_ptsize = shared_vals[3].u.f;
+ tk->max_kern = shared_vals[4].u.f;
+
+ /* is this correct? */
+ if ( tk->degree < 0 && tk->min_kern > 0 )
+ tk->min_kern = -tk->min_kern;
+ break;
+
+ case AFM_TOKEN_ENDTRACKKERN:
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ fi->NumTrackKern = n + 1;
+ return PSaux_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return PSaux_Err_Syntax_Error;
+ }
+
+
+#undef KERN_INDEX
+#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 )
+
+
+ /* compare two kerning pairs */
+ FT_CALLBACK_DEF( int )
+ afm_compare_kern_pairs( const void* a,
+ const void* b )
+ {
+ AFM_KernPair kp1 = (AFM_KernPair)a;
+ AFM_KernPair kp2 = (AFM_KernPair)b;
+
+ FT_ULong index1 = KERN_INDEX( kp1->index1, kp1->index2 );
+ FT_ULong index2 = KERN_INDEX( kp2->index1, kp2->index2 );
+
+
+ return (int)( index1 - index2 );
+ }
+
+
+ static FT_Error
+ afm_parse_kern_pairs( AFM_Parser parser )
+ {
+ AFM_FontInfo fi = parser->FontInfo;
+ AFM_KernPair kp;
+ char* key;
+ FT_UInt len;
+ int n = -1;
+
+
+ if ( afm_parser_read_int( parser, &fi->NumKernPair ) )
+ goto Fail;
+
+ if ( fi->NumKernPair )
+ {
+ FT_Memory memory = parser->memory;
+ FT_Error error;
+
+
+ if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
+ return error;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_Token token = afm_tokenize( key, len );
+
+
+ switch ( token )
+ {
+ case AFM_TOKEN_KP:
+ case AFM_TOKEN_KPX:
+ case AFM_TOKEN_KPY:
+ {
+ FT_Int r;
+ AFM_ValueRec shared_vals[4];
+
+
+ n++;
+
+ if ( n >= fi->NumKernPair )
+ goto Fail;
+
+ kp = fi->KernPairs + n;
+
+ shared_vals[0].type = AFM_VALUE_TYPE_INDEX;
+ shared_vals[1].type = AFM_VALUE_TYPE_INDEX;
+ shared_vals[2].type = AFM_VALUE_TYPE_INTEGER;
+ shared_vals[3].type = AFM_VALUE_TYPE_INTEGER;
+ r = afm_parser_read_vals( parser, shared_vals, 4 );
+ if ( r < 3 )
+ goto Fail;
+
+ kp->index1 = shared_vals[0].u.i;
+ kp->index2 = shared_vals[1].u.i;
+ if ( token == AFM_TOKEN_KPY )
+ {
+ kp->x = 0;
+ kp->y = shared_vals[2].u.i;
+ }
+ else
+ {
+ kp->x = shared_vals[2].u.i;
+ kp->y = ( token == AFM_TOKEN_KP && r == 4 )
+ ? shared_vals[3].u.i : 0;
+ }
+ }
+ break;
+
+ case AFM_TOKEN_ENDKERNPAIRS:
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ fi->NumKernPair = n + 1;
+ ft_qsort( fi->KernPairs, fi->NumKernPair,
+ sizeof( AFM_KernPairRec ),
+ afm_compare_kern_pairs );
+ return PSaux_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return PSaux_Err_Syntax_Error;
+ }
+
+
+ static FT_Error
+ afm_parse_kern_data( AFM_Parser parser )
+ {
+ FT_Error error;
+ char* key;
+ FT_UInt len;
+
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_STARTTRACKKERN:
+ error = afm_parse_track_kern( parser );
+ if ( error )
+ return error;
+ break;
+
+ case AFM_TOKEN_STARTKERNPAIRS:
+ case AFM_TOKEN_STARTKERNPAIRS0:
+ error = afm_parse_kern_pairs( parser );
+ if ( error )
+ return error;
+ break;
+
+ case AFM_TOKEN_ENDKERNDATA:
+ case AFM_TOKEN_ENDFONTMETRICS:
+ return PSaux_Err_Ok;
+
+ case AFM_TOKEN_UNKNOWN:
+ break;
+
+ default:
+ goto Fail;
+ }
+ }
+
+ Fail:
+ return PSaux_Err_Syntax_Error;
+ }
+
+
+ static FT_Error
+ afm_parser_skip_section( AFM_Parser parser,
+ FT_UInt n,
+ AFM_Token end_section )
+ {
+ char* key;
+ FT_UInt len;
+
+
+ while ( n-- > 0 )
+ {
+ key = afm_parser_next_key( parser, 1, NULL );
+ if ( !key )
+ goto Fail;
+ }
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_Token token = afm_tokenize( key, len );
+
+
+ if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS )
+ return PSaux_Err_Ok;
+ }
+
+ Fail:
+ return PSaux_Err_Syntax_Error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ afm_parser_parse( AFM_Parser parser )
+ {
+ FT_Memory memory = parser->memory;
+ AFM_FontInfo fi = parser->FontInfo;
+ FT_Error error = PSaux_Err_Syntax_Error;
+ char* key;
+ FT_UInt len;
+ FT_Int metrics_sets = 0;
+
+
+ if ( !fi )
+ return PSaux_Err_Invalid_Argument;
+
+ key = afm_parser_next_key( parser, 1, &len );
+ if ( !key || len != 16 ||
+ ft_strncmp( key, "StartFontMetrics", 16 ) != 0 )
+ return PSaux_Err_Unknown_File_Format;
+
+ while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
+ {
+ AFM_ValueRec shared_vals[4];
+
+
+ switch ( afm_tokenize( key, len ) )
+ {
+ case AFM_TOKEN_METRICSSETS:
+ if ( afm_parser_read_int( parser, &metrics_sets ) )
+ goto Fail;
+
+ if ( metrics_sets != 0 && metrics_sets != 2 )
+ {
+ error = PSaux_Err_Unimplemented_Feature;
+
+ goto Fail;
+ }
+ break;
+
+ case AFM_TOKEN_ISCIDFONT:
+ shared_vals[0].type = AFM_VALUE_TYPE_BOOL;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->IsCIDFont = shared_vals[0].u.b;
+ break;
+
+ case AFM_TOKEN_FONTBBOX:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
+ shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 )
+ goto Fail;
+
+ fi->FontBBox.xMin = shared_vals[0].u.f;
+ fi->FontBBox.yMin = shared_vals[1].u.f;
+ fi->FontBBox.xMax = shared_vals[2].u.f;
+ fi->FontBBox.yMax = shared_vals[3].u.f;
+ break;
+
+ case AFM_TOKEN_ASCENDER:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->Ascender = shared_vals[0].u.f;
+ break;
+
+ case AFM_TOKEN_DESCENDER:
+ shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
+ if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+ goto Fail;
+
+ fi->Descender = shared_vals[0].u.f;
+ break;
+
+ case AFM_TOKEN_STARTCHARMETRICS:
+ {
+ FT_Int n = 0;
+
+
+ if ( afm_parser_read_int( parser, &n ) )
+ goto Fail;
+
+ error = afm_parser_skip_section( parser, n,
+ AFM_TOKEN_ENDCHARMETRICS );
+ if ( error )
+ return error;
+ }
+ break;
+
+ case AFM_TOKEN_STARTKERNDATA:
+ error = afm_parse_kern_data( parser );
+ if ( error )
+ goto Fail;
+ /* fall through since we only support kern data */
+
+ case AFM_TOKEN_ENDFONTMETRICS:
+ return PSaux_Err_Ok;
+
+ default:
+ break;
+ }
+ }
+
+ Fail:
+ FT_FREE( fi->TrackKerns );
+ fi->NumTrackKern = 0;
+
+ FT_FREE( fi->KernPairs );
+ fi->NumKernPair = 0;
+
+ fi->IsCIDFont = 0;
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/psaux/afmparse.h b/src/psaux/afmparse.h
new file mode 100644
index 0000000..c2fce75
--- /dev/null
+++ b/src/psaux/afmparse.h
@@ -0,0 +1,87 @@
+/***************************************************************************/
+/* */
+/* afmparse.h */
+/* */
+/* AFM parser (specification). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __AFMPARSE_H__
+#define __AFMPARSE_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ afm_parser_init( AFM_Parser parser,
+ FT_Memory memory,
+ FT_Byte* base,
+ FT_Byte* limit );
+
+
+ FT_LOCAL( void )
+ afm_parser_done( AFM_Parser parser );
+
+
+ FT_LOCAL( FT_Error )
+ afm_parser_parse( AFM_Parser parser );
+
+
+ enum AFM_ValueType_
+ {
+ AFM_VALUE_TYPE_STRING,
+ AFM_VALUE_TYPE_NAME,
+ AFM_VALUE_TYPE_FIXED, /* real number */
+ AFM_VALUE_TYPE_INTEGER,
+ AFM_VALUE_TYPE_BOOL,
+ AFM_VALUE_TYPE_INDEX /* glyph index */
+ };
+
+
+ typedef struct AFM_ValueRec_
+ {
+ enum AFM_ValueType_ type;
+ union {
+ char* s;
+ FT_Fixed f;
+ FT_Int i;
+ FT_Bool b;
+
+ } u;
+
+ } AFM_ValueRec, *AFM_Value;
+
+#define AFM_MAX_ARGUMENTS 5
+
+ FT_LOCAL( FT_Int )
+ afm_parser_read_vals( AFM_Parser parser,
+ AFM_Value vals,
+ FT_Int n );
+
+ /* read the next key from the next line or column */
+ FT_LOCAL( char* )
+ afm_parser_next_key( AFM_Parser parser,
+ FT_Bool line,
+ FT_UInt* len );
+
+FT_END_HEADER
+
+#endif /* __AFMPARSE_H__ */
+
+
+/* END */
diff --git a/src/psaux/module.mk b/src/psaux/module.mk
new file mode 100644
index 0000000..5431522
--- /dev/null
+++ b/src/psaux/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 PSaux module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += PSAUX_MODULE
+
+define PSAUX_MODULE
+$(OPEN_DRIVER)psaux_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)psaux $(ECHO_DRIVER_DESC)Postscript Type 1 & Type 2 helper module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/psaux/psaux.c b/src/psaux/psaux.c
new file mode 100644
index 0000000..a4b9c5c
--- /dev/null
+++ b/src/psaux/psaux.c
@@ -0,0 +1,34 @@
+/***************************************************************************/
+/* */
+/* psaux.c */
+/* */
+/* FreeType auxiliary PostScript driver component (body only). */
+/* */
+/* Copyright 1996-2001, 2002, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "psobjs.c"
+#include "psauxmod.c"
+#include "t1decode.c"
+#include "t1cmap.c"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "afmparse.c"
+#endif
+
+#include "psconv.c"
+
+
+/* END */
diff --git a/src/psaux/psauxerr.h b/src/psaux/psauxerr.h
new file mode 100644
index 0000000..d0baa3c
--- /dev/null
+++ b/src/psaux/psauxerr.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* psauxerr.h */
+/* */
+/* PS auxiliary module error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the PS auxiliary module error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __PSAUXERR_H__
+#define __PSAUXERR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX PSaux_Err_
+#define FT_ERR_BASE FT_Mod_Err_PSaux
+
+#include FT_ERRORS_H
+
+#endif /* __PSAUXERR_H__ */
+
+
+/* END */
diff --git a/src/psaux/psauxmod.c b/src/psaux/psauxmod.c
new file mode 100644
index 0000000..4c3579f
--- /dev/null
+++ b/src/psaux/psauxmod.c
@@ -0,0 +1,139 @@
+/***************************************************************************/
+/* */
+/* psauxmod.c */
+/* */
+/* FreeType auxiliary PostScript module implementation (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "psauxmod.h"
+#include "psobjs.h"
+#include "t1decode.h"
+#include "t1cmap.h"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "afmparse.h"
+#endif
+
+
+ FT_CALLBACK_TABLE_DEF
+ const PS_Table_FuncsRec ps_table_funcs =
+ {
+ ps_table_new,
+ ps_table_done,
+ ps_table_add,
+ ps_table_release
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const PS_Parser_FuncsRec ps_parser_funcs =
+ {
+ ps_parser_init,
+ ps_parser_done,
+ ps_parser_skip_spaces,
+ ps_parser_skip_PS_token,
+ ps_parser_to_int,
+ ps_parser_to_fixed,
+ ps_parser_to_bytes,
+ ps_parser_to_coord_array,
+ ps_parser_to_fixed_array,
+ ps_parser_to_token,
+ ps_parser_to_token_array,
+ ps_parser_load_field,
+ ps_parser_load_field_table
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_Builder_FuncsRec t1_builder_funcs =
+ {
+ t1_builder_init,
+ t1_builder_done,
+ t1_builder_check_points,
+ t1_builder_add_point,
+ t1_builder_add_point1,
+ t1_builder_add_contour,
+ t1_builder_start_point,
+ t1_builder_close_contour
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_Decoder_FuncsRec t1_decoder_funcs =
+ {
+ t1_decoder_init,
+ t1_decoder_done,
+ t1_decoder_parse_charstrings
+ };
+
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ FT_CALLBACK_TABLE_DEF
+ const AFM_Parser_FuncsRec afm_parser_funcs =
+ {
+ afm_parser_init,
+ afm_parser_done,
+ afm_parser_parse
+ };
+#endif
+
+
+ FT_CALLBACK_TABLE_DEF
+ const T1_CMap_ClassesRec t1_cmap_classes =
+ {
+ &t1_cmap_standard_class_rec,
+ &t1_cmap_expert_class_rec,
+ &t1_cmap_custom_class_rec,
+ &t1_cmap_unicode_class_rec
+ };
+
+
+ static
+ const PSAux_Interface psaux_interface =
+ {
+ &ps_table_funcs,
+ &ps_parser_funcs,
+ &t1_builder_funcs,
+ &t1_decoder_funcs,
+ t1_decrypt,
+
+ (const T1_CMap_ClassesRec*) &t1_cmap_classes,
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ &afm_parser_funcs,
+#else
+ 0,
+#endif
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class psaux_module_class =
+ {
+ 0,
+ sizeof( FT_ModuleRec ),
+ "psaux",
+ 0x20000L,
+ 0x20000L,
+
+ &psaux_interface, /* module-specific interface */
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ };
+
+
+/* END */
diff --git a/src/psaux/psauxmod.h b/src/psaux/psauxmod.h
new file mode 100644
index 0000000..92ac056
--- /dev/null
+++ b/src/psaux/psauxmod.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* psauxmod.h */
+/* */
+/* FreeType auxiliary PostScript module implementation (specification). */
+/* */
+/* Copyright 2000-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSAUXMOD_H__
+#define __PSAUXMOD_H__
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) psaux_driver_class;
+
+
+FT_END_HEADER
+
+#endif /* __PSAUXMOD_H__ */
+
+
+/* END */
diff --git a/src/psaux/psconv.c b/src/psaux/psconv.c
new file mode 100644
index 0000000..3bbeab6
--- /dev/null
+++ b/src/psaux/psconv.c
@@ -0,0 +1,466 @@
+/***************************************************************************/
+/* */
+/* psconv.c */
+/* */
+/* Some convenience conversions (body). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "psconv.h"
+#include "psobjs.h"
+#include "psauxerr.h"
+
+
+ /* The following array is used by various functions to quickly convert */
+ /* digits (both decimal and non-decimal) into numbers. */
+
+#if 'A' == 65
+ /* ASCII */
+
+ static const FT_Char ft_char_table[128] =
+ {
+ /* 0x00 */
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+ };
+
+ /* no character >= 0x80 can represent a valid number */
+#define OP >=
+
+#endif /* 'A' == 65 */
+
+#if 'A' == 193
+ /* EBCDIC */
+
+ static const FT_Char ft_char_table[128] =
+ {
+ /* 0x80 */
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+ -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+ -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+ -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+ -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ };
+
+ /* no character < 0x80 can represent a valid number */
+#define OP <
+
+#endif /* 'A' == 193 */
+
+
+ FT_LOCAL_DEF( FT_Int )
+ PS_Conv_Strtol( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Int base )
+ {
+ FT_Byte* p = *cursor;
+ FT_Int num = 0;
+ FT_Bool sign = 0;
+
+
+ if ( p == limit || base < 2 || base > 36 )
+ return 0;
+
+ if ( *p == '-' || *p == '+' )
+ {
+ sign = FT_BOOL( *p == '-' );
+
+ p++;
+ if ( p == limit )
+ return 0;
+ }
+
+ for ( ; p < limit; p++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7f];
+
+ if ( c < 0 || c >= base )
+ break;
+
+ num = num * base + c;
+ }
+
+ if ( sign )
+ num = -num;
+
+ *cursor = p;
+
+ return num;
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ PS_Conv_ToInt( FT_Byte** cursor,
+ FT_Byte* limit )
+
+ {
+ FT_Byte* p;
+ FT_Int num;
+
+
+ num = PS_Conv_Strtol( cursor, limit, 10 );
+ p = *cursor;
+
+ if ( p < limit && *p == '#' )
+ {
+ *cursor = p + 1;
+
+ return PS_Conv_Strtol( cursor, limit, num );
+ }
+ else
+ return num;
+ }
+
+
+ FT_LOCAL_DEF( FT_Fixed )
+ PS_Conv_ToFixed( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Int power_ten )
+ {
+ FT_Byte* p = *cursor;
+ FT_Fixed integral;
+ FT_Long decimal = 0, divider = 1;
+ FT_Bool sign = 0;
+
+
+ if ( p == limit )
+ return 0;
+
+ if ( *p == '-' || *p == '+' )
+ {
+ sign = FT_BOOL( *p == '-' );
+
+ p++;
+ if ( p == limit )
+ return 0;
+ }
+
+ if ( *p != '.' )
+ integral = PS_Conv_ToInt( &p, limit ) << 16;
+ else
+ integral = 0;
+
+ /* read the decimal part */
+ if ( p < limit && *p == '.' )
+ {
+ p++;
+
+ for ( ; p < limit; p++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7f];
+
+ if ( c < 0 || c >= 10 )
+ break;
+
+ if ( divider < 10000000L )
+ {
+ decimal = decimal * 10 + c;
+ divider *= 10;
+ }
+ }
+ }
+
+ /* read exponent, if any */
+ if ( p + 1 < limit && ( *p == 'e' || *p == 'E' ) )
+ {
+ p++;
+ power_ten += PS_Conv_ToInt( &p, limit );
+ }
+
+ while ( power_ten > 0 )
+ {
+ integral *= 10;
+ decimal *= 10;
+ power_ten--;
+ }
+
+ while ( power_ten < 0 )
+ {
+ integral /= 10;
+ divider *= 10;
+ power_ten++;
+ }
+
+ if ( decimal )
+ integral += FT_DivFix( decimal, divider );
+
+ if ( sign )
+ integral = -integral;
+
+ *cursor = p;
+
+ return integral;
+ }
+
+
+#if 0
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_StringDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n )
+ {
+ FT_Byte* p;
+ FT_UInt r = 0;
+
+
+ for ( p = *cursor; r < n && p < limit; p++ )
+ {
+ FT_Byte b;
+
+
+ if ( *p != '\\' )
+ {
+ buffer[r++] = *p;
+
+ continue;
+ }
+
+ p++;
+
+ switch ( *p )
+ {
+ case 'n':
+ b = '\n';
+ break;
+ case 'r':
+ b = '\r';
+ break;
+ case 't':
+ b = '\t';
+ break;
+ case 'b':
+ b = '\b';
+ break;
+ case 'f':
+ b = '\f';
+ break;
+ case '\r':
+ p++;
+ if ( *p != '\n' )
+ {
+ b = *p;
+
+ break;
+ }
+ /* no break */
+ case '\n':
+ continue;
+ break;
+ default:
+ if ( IS_PS_DIGIT( *p ) )
+ {
+ b = *p - '0';
+
+ p++;
+
+ if ( IS_PS_DIGIT( *p ) )
+ {
+ b = b * 8 + *p - '0';
+
+ p++;
+
+ if ( IS_PS_DIGIT( *p ) )
+ b = b * 8 + *p - '0';
+ else
+ {
+ buffer[r++] = b;
+ b = *p;
+ }
+ }
+ else
+ {
+ buffer[r++] = b;
+ b = *p;
+ }
+ }
+ else
+ b = *p;
+ break;
+ }
+
+ buffer[r++] = b;
+ }
+
+ *cursor = p;
+
+ return r;
+ }
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_ASCIIHexDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n )
+ {
+ FT_Byte* p;
+ FT_UInt r = 0;
+ FT_UInt w = 0;
+ FT_UInt pad = 0x01;
+
+
+ n *= 2;
+
+#if 1
+
+ p = *cursor;
+ if ( n > (FT_UInt)( limit - p ) )
+ n = (FT_UInt)( limit - p );
+
+ /* we try to process two nibbles at a time to be as fast as possible */
+ for ( ; r < n; r++ )
+ {
+ FT_UInt c = p[r];
+
+
+ if ( IS_PS_SPACE( c ) )
+ continue;
+
+ if ( c OP 0x80 )
+ break;
+
+ c = ft_char_table[c & 0x7F];
+ if ( (unsigned)c >= 16 )
+ break;
+
+ pad = ( pad << 4 ) | c;
+ if ( pad & 0x100 )
+ {
+ buffer[w++] = (FT_Byte)pad;
+ pad = 0x01;
+ }
+ }
+
+ if ( pad != 0x01 )
+ buffer[w++] = (FT_Byte)( pad << 4 );
+
+ *cursor = p + r;
+
+ return w;
+
+#else /* 0 */
+
+ for ( r = 0; r < n; r++ )
+ {
+ FT_Char c;
+
+
+ if ( IS_PS_SPACE( *p ) )
+ continue;
+
+ if ( *p OP 0x80 )
+ break;
+
+ c = ft_char_table[*p & 0x7f];
+
+ if ( (unsigned)c >= 16 )
+ break;
+
+ if ( r & 1 )
+ {
+ *buffer = (FT_Byte)(*buffer + c);
+ buffer++;
+ }
+ else
+ *buffer = (FT_Byte)(c << 4);
+
+ r++;
+ }
+
+ *cursor = p;
+
+ return ( r + 1 ) / 2;
+
+#endif /* 0 */
+
+ }
+
+
+ FT_LOCAL_DEF( FT_UInt )
+ PS_Conv_EexecDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n,
+ FT_UShort* seed )
+ {
+ FT_Byte* p;
+ FT_UInt r;
+ FT_UInt s = *seed;
+
+
+#if 1
+
+ p = *cursor;
+ if ( n > (FT_UInt)(limit - p) )
+ n = (FT_UInt)(limit - p);
+
+ for ( r = 0; r < n; r++ )
+ {
+ FT_UInt val = p[r];
+ FT_UInt b = ( val ^ ( s >> 8 ) );
+
+
+ s = ( (val + s)*52845U + 22719 ) & 0xFFFFU;
+ buffer[r] = (FT_Byte) b;
+ }
+
+ *cursor = p + n;
+ *seed = (FT_UShort)s;
+
+#else /* 0 */
+
+ for ( r = 0, p = *cursor; r < n && p < limit; r++, p++ )
+ {
+ FT_Byte b = (FT_Byte)( *p ^ ( s >> 8 ) );
+
+
+ s = (FT_UShort)( ( *p + s ) * 52845U + 22719 );
+ *buffer++ = b;
+ }
+ *cursor = p;
+ *seed = s;
+
+#endif /* 0 */
+
+ return r;
+ }
+
+
+/* END */
diff --git a/src/psaux/psconv.h b/src/psaux/psconv.h
new file mode 100644
index 0000000..e511241
--- /dev/null
+++ b/src/psaux/psconv.h
@@ -0,0 +1,71 @@
+/***************************************************************************/
+/* */
+/* psconv.h */
+/* */
+/* Some convenience conversions (specification). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSCONV_H__
+#define __PSCONV_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Int )
+ PS_Conv_Strtol( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Int base );
+
+
+ FT_LOCAL( FT_Int )
+ PS_Conv_ToInt( FT_Byte** cursor,
+ FT_Byte* limit );
+
+ FT_LOCAL( FT_Fixed )
+ PS_Conv_ToFixed( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Int power_ten );
+
+#if 0
+ FT_LOCAL( FT_UInt )
+ PS_Conv_StringDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n );
+#endif
+
+ FT_LOCAL( FT_UInt )
+ PS_Conv_ASCIIHexDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n );
+
+ FT_LOCAL( FT_UInt )
+ PS_Conv_EexecDecode( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Byte* buffer,
+ FT_UInt n,
+ FT_UShort* seed );
+
+
+FT_END_HEADER
+
+#endif /* __PSCONV_H__ */
+
+
+/* END */
diff --git a/src/psaux/psobjs.c b/src/psaux/psobjs.c
new file mode 100644
index 0000000..9570856
--- /dev/null
+++ b/src/psaux/psobjs.c
@@ -0,0 +1,1698 @@
+/***************************************************************************/
+/* */
+/* psobjs.c */
+/* */
+/* Auxiliary functions for PostScript fonts (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "psobjs.h"
+#include "psconv.h"
+
+#include "psauxerr.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_psobjs
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_table_new */
+ /* */
+ /* <Description> */
+ /* Initializes a PS_Table. */
+ /* */
+ /* <InOut> */
+ /* table :: The address of the target table. */
+ /* */
+ /* <Input> */
+ /* count :: The table size = the maximum number of elements. */
+ /* */
+ /* memory :: The memory object to use for all subsequent */
+ /* reallocations. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ ps_table_new( PS_Table table,
+ FT_Int count,
+ FT_Memory memory )
+ {
+ FT_Error error;
+
+
+ table->memory = memory;
+ if ( FT_NEW_ARRAY( table->elements, count ) ||
+ FT_NEW_ARRAY( table->lengths, count ) )
+ goto Exit;
+
+ table->max_elems = count;
+ table->init = 0xDEADBEEFUL;
+ table->num_elems = 0;
+ table->block = 0;
+ table->capacity = 0;
+ table->cursor = 0;
+
+ *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
+
+ Exit:
+ if ( error )
+ FT_FREE( table->elements );
+
+ return error;
+ }
+
+
+ static void
+ shift_elements( PS_Table table,
+ FT_Byte* old_base )
+ {
+ FT_PtrDist delta = table->block - old_base;
+ FT_Byte** offset = table->elements;
+ FT_Byte** limit = offset + table->max_elems;
+
+
+ for ( ; offset < limit; offset++ )
+ {
+ if ( offset[0] )
+ offset[0] += delta;
+ }
+ }
+
+
+ static FT_Error
+ reallocate_t1_table( PS_Table table,
+ FT_Long new_size )
+ {
+ FT_Memory memory = table->memory;
+ FT_Byte* old_base = table->block;
+ FT_Error error;
+
+
+ /* allocate new base block */
+ if ( FT_ALLOC( table->block, new_size ) )
+ {
+ table->block = old_base;
+ return error;
+ }
+
+ /* copy elements and shift offsets */
+ if ( old_base )
+ {
+ FT_MEM_COPY( table->block, old_base, table->capacity );
+ shift_elements( table, old_base );
+ FT_FREE( old_base );
+ }
+
+ table->capacity = new_size;
+
+ return PSaux_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_table_add */
+ /* */
+ /* <Description> */
+ /* Adds an object to a PS_Table, possibly growing its memory block. */
+ /* */
+ /* <InOut> */
+ /* table :: The target table. */
+ /* */
+ /* <Input> */
+ /* idx :: The index of the object in the table. */
+ /* */
+ /* object :: The address of the object to copy in memory. */
+ /* */
+ /* length :: The length in bytes of the source object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. An error is returned if a */
+ /* reallocation fails. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ ps_table_add( PS_Table table,
+ FT_Int idx,
+ void* object,
+ FT_PtrDist length )
+ {
+ if ( idx < 0 || idx > table->max_elems )
+ {
+ FT_ERROR(( "ps_table_add: invalid index\n" ));
+ return PSaux_Err_Invalid_Argument;
+ }
+
+ /* grow the base block if needed */
+ if ( table->cursor + length > table->capacity )
+ {
+ FT_Error error;
+ FT_Offset new_size = table->capacity;
+ FT_Long in_offset;
+
+
+ in_offset = (FT_Long)((FT_Byte*)object - table->block);
+ if ( (FT_ULong)in_offset >= table->capacity )
+ in_offset = -1;
+
+ while ( new_size < table->cursor + length )
+ {
+ /* increase size by 25% and round up to the nearest multiple
+ of 1024 */
+ new_size += ( new_size >> 2 ) + 1;
+ new_size = FT_PAD_CEIL( new_size, 1024 );
+ }
+
+ error = reallocate_t1_table( table, new_size );
+ if ( error )
+ return error;
+
+ if ( in_offset >= 0 )
+ object = table->block + in_offset;
+ }
+
+ /* add the object to the base block and adjust offset */
+ table->elements[idx] = table->block + table->cursor;
+ table->lengths [idx] = length;
+ FT_MEM_COPY( table->block + table->cursor, object, length );
+
+ table->cursor += length;
+ return PSaux_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ps_table_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a PS_TableRec (i.e., reallocate it to its current */
+ /* cursor). */
+ /* */
+ /* <InOut> */
+ /* table :: The target table. */
+ /* */
+ /* <Note> */
+ /* This function does NOT release the heap's memory block. It is up */
+ /* to the caller to clean it, or reference it in its own structures. */
+ /* */
+ FT_LOCAL_DEF( void )
+ ps_table_done( PS_Table table )
+ {
+ FT_Memory memory = table->memory;
+ FT_Error error;
+ FT_Byte* old_base = table->block;
+
+
+ /* should never fail, because rec.cursor <= rec.size */
+ if ( !old_base )
+ return;
+
+ if ( FT_ALLOC( table->block, table->cursor ) )
+ return;
+ FT_MEM_COPY( table->block, old_base, table->cursor );
+ shift_elements( table, old_base );
+
+ table->capacity = table->cursor;
+ FT_FREE( old_base );
+
+ FT_UNUSED( error );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_table_release( PS_Table table )
+ {
+ FT_Memory memory = table->memory;
+
+
+ if ( (FT_ULong)table->init == 0xDEADBEEFUL )
+ {
+ FT_FREE( table->block );
+ FT_FREE( table->elements );
+ FT_FREE( table->lengths );
+ table->init = 0;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* first character must be already part of the comment */
+
+ static void
+ skip_comment( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+
+
+ while ( cur < limit )
+ {
+ if ( IS_PS_NEWLINE( *cur ) )
+ break;
+ cur++;
+ }
+
+ *acur = cur;
+ }
+
+
+ static void
+ skip_spaces( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+
+
+ while ( cur < limit )
+ {
+ if ( !IS_PS_SPACE( *cur ) )
+ {
+ if ( *cur == '%' )
+ /* According to the PLRM, a comment is equal to a space. */
+ skip_comment( &cur, limit );
+ else
+ break;
+ }
+ cur++;
+ }
+
+ *acur = cur;
+ }
+
+
+#define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
+
+
+ /* first character must be `('; */
+ /* *acur is positioned at the character after the closing `)' */
+
+ static FT_Error
+ skip_literal_string( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int embed = 0;
+ FT_Error error = PSaux_Err_Invalid_File_Format;
+ unsigned int i;
+
+
+ while ( cur < limit )
+ {
+ FT_Byte c = *cur;
+
+
+ ++cur;
+
+ if ( c == '\\' )
+ {
+ /* Red Book 3rd ed., section `Literal Text Strings', p. 29: */
+ /* A backslash can introduce three different types */
+ /* of escape sequences: */
+ /* - a special escaped char like \r, \n, etc. */
+ /* - a one-, two-, or three-digit octal number */
+ /* - none of the above in which case the backslash is ignored */
+
+ if ( cur == limit )
+ /* error (or to be ignored?) */
+ break;
+
+ switch ( *cur )
+ {
+ /* skip `special' escape */
+ case 'n':
+ case 'r':
+ case 't':
+ case 'b':
+ case 'f':
+ case '\\':
+ case '(':
+ case ')':
+ ++cur;
+ break;
+
+ default:
+ /* skip octal escape or ignore backslash */
+ for ( i = 0; i < 3 && cur < limit; ++i )
+ {
+ if ( ! IS_OCTAL_DIGIT( *cur ) )
+ break;
+
+ ++cur;
+ }
+ }
+ }
+ else if ( c == '(' )
+ embed++;
+ else if ( c == ')' )
+ {
+ embed--;
+ if ( embed == 0 )
+ {
+ error = PSaux_Err_Ok;
+ break;
+ }
+ }
+ }
+
+ *acur = cur;
+
+ return error;
+ }
+
+
+ /* first character must be `<' */
+
+ static FT_Error
+ skip_string( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Error err = PSaux_Err_Ok;
+
+
+ while ( ++cur < limit )
+ {
+ /* All whitespace characters are ignored. */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ break;
+
+ if ( !IS_PS_XDIGIT( *cur ) )
+ break;
+ }
+
+ if ( cur < limit && *cur != '>' )
+ {
+ FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
+ err = PSaux_Err_Invalid_File_Format;
+ }
+ else
+ cur++;
+
+ *acur = cur;
+ return err;
+ }
+
+
+ /* first character must be the opening brace that */
+ /* starts the procedure */
+
+ /* NB: [ and ] need not match: */
+ /* `/foo {[} def' is a valid PostScript fragment, */
+ /* even within a Type1 font */
+
+ static FT_Error
+ skip_procedure( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur;
+ FT_Int embed = 0;
+ FT_Error error = PSaux_Err_Ok;
+
+
+ FT_ASSERT( **acur == '{' );
+
+ for ( cur = *acur; cur < limit && error == PSaux_Err_Ok; ++cur )
+ {
+ switch ( *cur )
+ {
+ case '{':
+ ++embed;
+ break;
+
+ case '}':
+ --embed;
+ if ( embed == 0 )
+ {
+ ++cur;
+ goto end;
+ }
+ break;
+
+ case '(':
+ error = skip_literal_string( &cur, limit );
+ break;
+
+ case '<':
+ error = skip_string( &cur, limit );
+ break;
+
+ case '%':
+ skip_comment( &cur, limit );
+ break;
+ }
+ }
+
+ end:
+ if ( embed != 0 )
+ error = PSaux_Err_Invalid_File_Format;
+
+ *acur = cur;
+
+ return error;
+ }
+
+
+ /***********************************************************************/
+ /* */
+ /* All exported parsing routines handle leading whitespace and stop at */
+ /* the first character which isn't part of the just handled token. */
+ /* */
+ /***********************************************************************/
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_skip_PS_token( PS_Parser parser )
+ {
+ /* Note: PostScript allows any non-delimiting, non-whitespace */
+ /* character in a name (PS Ref Manual, 3rd ed, p31). */
+ /* PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
+
+ FT_Byte* cur = parser->cursor;
+ FT_Byte* limit = parser->limit;
+ FT_Error error = PSaux_Err_Ok;
+
+
+ skip_spaces( &cur, limit ); /* this also skips comments */
+ if ( cur >= limit )
+ goto Exit;
+
+ /* self-delimiting, single-character tokens */
+ if ( *cur == '[' || *cur == ']' )
+ {
+ cur++;
+ goto Exit;
+ }
+
+ /* skip balanced expressions (procedures and strings) */
+
+ if ( *cur == '{' ) /* {...} */
+ {
+ error = skip_procedure( &cur, limit );
+ goto Exit;
+ }
+
+ if ( *cur == '(' ) /* (...) */
+ {
+ error = skip_literal_string( &cur, limit );
+ goto Exit;
+ }
+
+ if ( *cur == '<' ) /* <...> */
+ {
+ if ( cur + 1 < limit && *(cur + 1) == '<' ) /* << */
+ {
+ cur++;
+ cur++;
+ }
+ else
+ error = skip_string( &cur, limit );
+
+ goto Exit;
+ }
+
+ if ( *cur == '>' )
+ {
+ cur++;
+ if ( cur >= limit || *cur != '>' ) /* >> */
+ {
+ FT_ERROR(( "ps_parser_skip_PS_token: "
+ "unexpected closing delimiter `>'\n" ));
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+ cur++;
+ goto Exit;
+ }
+
+ if ( *cur == '/' )
+ cur++;
+
+ /* anything else */
+ while ( cur < limit )
+ {
+ /* *cur might be invalid (e.g., ')' or '}'), but this */
+ /* is handled by the test `cur == parser->cursor' below */
+ if ( IS_PS_DELIM( *cur ) )
+ break;
+
+ cur++;
+ }
+
+ Exit:
+ if ( cur == parser->cursor )
+ {
+ FT_ERROR(( "ps_parser_skip_PS_token: "
+ "current token is `%c', which is self-delimiting "
+ "but invalid at this point\n",
+ *cur ));
+
+ error = PSaux_Err_Invalid_File_Format;
+ }
+
+ parser->error = error;
+ parser->cursor = cur;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_skip_spaces( PS_Parser parser )
+ {
+ skip_spaces( &parser->cursor, parser->limit );
+ }
+
+
+ /* `token' here means either something between balanced delimiters */
+ /* or the next token; the delimiters are not removed. */
+
+ FT_LOCAL_DEF( void )
+ ps_parser_to_token( PS_Parser parser,
+ T1_Token token )
+ {
+ FT_Byte* cur;
+ FT_Byte* limit;
+ FT_Int embed;
+
+
+ token->type = T1_TOKEN_TYPE_NONE;
+ token->start = 0;
+ token->limit = 0;
+
+ /* first of all, skip leading whitespace */
+ ps_parser_skip_spaces( parser );
+
+ cur = parser->cursor;
+ limit = parser->limit;
+
+ if ( cur >= limit )
+ return;
+
+ switch ( *cur )
+ {
+ /************* check for literal string *****************/
+ case '(':
+ token->type = T1_TOKEN_TYPE_STRING;
+ token->start = cur;
+
+ if ( skip_literal_string( &cur, limit ) == PSaux_Err_Ok )
+ token->limit = cur;
+ break;
+
+ /************* check for programs/array *****************/
+ case '{':
+ token->type = T1_TOKEN_TYPE_ARRAY;
+ token->start = cur;
+
+ if ( skip_procedure( &cur, limit ) == PSaux_Err_Ok )
+ token->limit = cur;
+ break;
+
+ /************* check for table/array ********************/
+ /* XXX: in theory we should also look for "<<" */
+ /* since this is semantically equivalent to "["; */
+ /* in practice it doesn't matter (?) */
+ case '[':
+ token->type = T1_TOKEN_TYPE_ARRAY;
+ embed = 1;
+ token->start = cur++;
+
+ /* we need this to catch `[ ]' */
+ parser->cursor = cur;
+ ps_parser_skip_spaces( parser );
+ cur = parser->cursor;
+
+ while ( cur < limit && !parser->error )
+ {
+ /* XXX: this is wrong because it does not */
+ /* skip comments, procedures, and strings */
+ if ( *cur == '[' )
+ embed++;
+ else if ( *cur == ']' )
+ {
+ embed--;
+ if ( embed <= 0 )
+ {
+ token->limit = ++cur;
+ break;
+ }
+ }
+
+ parser->cursor = cur;
+ ps_parser_skip_PS_token( parser );
+ /* we need this to catch `[XXX ]' */
+ ps_parser_skip_spaces ( parser );
+ cur = parser->cursor;
+ }
+ break;
+
+ /* ************ otherwise, it is any token **************/
+ default:
+ token->start = cur;
+ token->type = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY );
+ ps_parser_skip_PS_token( parser );
+ cur = parser->cursor;
+ if ( !parser->error )
+ token->limit = cur;
+ }
+
+ if ( !token->limit )
+ {
+ token->start = 0;
+ token->type = T1_TOKEN_TYPE_NONE;
+ }
+
+ parser->cursor = cur;
+ }
+
+
+ /* NB: `tokens' can be NULL if we only want to count */
+ /* the number of array elements */
+
+ FT_LOCAL_DEF( void )
+ ps_parser_to_token_array( PS_Parser parser,
+ T1_Token tokens,
+ FT_UInt max_tokens,
+ FT_Int* pnum_tokens )
+ {
+ T1_TokenRec master;
+
+
+ *pnum_tokens = -1;
+
+ /* this also handles leading whitespace */
+ ps_parser_to_token( parser, &master );
+
+ if ( master.type == T1_TOKEN_TYPE_ARRAY )
+ {
+ FT_Byte* old_cursor = parser->cursor;
+ FT_Byte* old_limit = parser->limit;
+ T1_Token cur = tokens;
+ T1_Token limit = cur + max_tokens;
+
+
+ /* don't include outermost delimiters */
+ parser->cursor = master.start + 1;
+ parser->limit = master.limit - 1;
+
+ while ( parser->cursor < parser->limit )
+ {
+ T1_TokenRec token;
+
+
+ ps_parser_to_token( parser, &token );
+ if ( !token.type )
+ break;
+
+ if ( tokens != NULL && cur < limit )
+ *cur = token;
+
+ cur++;
+ }
+
+ *pnum_tokens = (FT_Int)( cur - tokens );
+
+ parser->cursor = old_cursor;
+ parser->limit = old_limit;
+ }
+ }
+
+
+ /* first character must be a delimiter or a part of a number */
+ /* NB: `coords' can be NULL if we just want to skip the */
+ /* array; in this case we ignore `max_coords' */
+
+ static FT_Int
+ ps_tocoordarray( FT_Byte* *acur,
+ FT_Byte* limit,
+ FT_Int max_coords,
+ FT_Short* coords )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int count = 0;
+ FT_Byte c, ender;
+
+
+ if ( cur >= limit )
+ goto Exit;
+
+ /* check for the beginning of an array; otherwise, only one number */
+ /* will be read */
+ c = *cur;
+ ender = 0;
+
+ if ( c == '[' )
+ ender = ']';
+ else if ( c == '{' )
+ ender = '}';
+
+ if ( ender )
+ cur++;
+
+ /* now, read the coordinates */
+ while ( cur < limit )
+ {
+ FT_Short dummy;
+ FT_Byte* old_cur;
+
+
+ /* skip whitespace in front of data */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ goto Exit;
+
+ if ( *cur == ender )
+ {
+ cur++;
+ break;
+ }
+
+ old_cur = cur;
+
+ if ( coords != NULL && count >= max_coords )
+ break;
+
+ /* call PS_Conv_ToFixed() even if coords == NULL */
+ /* to properly parse number at `cur' */
+ *( coords != NULL ? &coords[count] : &dummy ) =
+ (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
+
+ if ( old_cur == cur )
+ {
+ count = -1;
+ goto Exit;
+ }
+ else
+ count++;
+
+ if ( !ender )
+ break;
+ }
+
+ Exit:
+ *acur = cur;
+ return count;
+ }
+
+
+ /* first character must be a delimiter or a part of a number */
+ /* NB: `values' can be NULL if we just want to skip the */
+ /* array; in this case we ignore `max_values' */
+
+ static FT_Int
+ ps_tofixedarray( FT_Byte* *acur,
+ FT_Byte* limit,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten )
+ {
+ FT_Byte* cur = *acur;
+ FT_Int count = 0;
+ FT_Byte c, ender;
+
+
+ if ( cur >= limit )
+ goto Exit;
+
+ /* Check for the beginning of an array. Otherwise, only one number */
+ /* will be read. */
+ c = *cur;
+ ender = 0;
+
+ if ( c == '[' )
+ ender = ']';
+ else if ( c == '{' )
+ ender = '}';
+
+ if ( ender )
+ cur++;
+
+ /* now, read the values */
+ while ( cur < limit )
+ {
+ FT_Fixed dummy;
+ FT_Byte* old_cur;
+
+
+ /* skip whitespace in front of data */
+ skip_spaces( &cur, limit );
+ if ( cur >= limit )
+ goto Exit;
+
+ if ( *cur == ender )
+ {
+ cur++;
+ break;
+ }
+
+ old_cur = cur;
+
+ if ( values != NULL && count >= max_values )
+ break;
+
+ /* call PS_Conv_ToFixed() even if coords == NULL */
+ /* to properly parse number at `cur' */
+ *( values != NULL ? &values[count] : &dummy ) =
+ PS_Conv_ToFixed( &cur, limit, power_ten );
+
+ if ( old_cur == cur )
+ {
+ count = -1;
+ goto Exit;
+ }
+ else
+ count++;
+
+ if ( !ender )
+ break;
+ }
+
+ Exit:
+ *acur = cur;
+ return count;
+ }
+
+
+#if 0
+
+ static FT_String*
+ ps_tostring( FT_Byte** cursor,
+ FT_Byte* limit,
+ FT_Memory memory )
+ {
+ FT_Byte* cur = *cursor;
+ FT_PtrDist len = 0;
+ FT_Int count;
+ FT_String* result;
+ FT_Error error;
+
+
+ /* XXX: some stupid fonts have a `Notice' or `Copyright' string */
+ /* that simply doesn't begin with an opening parenthesis, even */
+ /* though they have a closing one! E.g. "amuncial.pfb" */
+ /* */
+ /* We must deal with these ill-fated cases there. Note that */
+ /* these fonts didn't work with the old Type 1 driver as the */
+ /* notice/copyright was not recognized as a valid string token */
+ /* and made the old token parser commit errors. */
+
+ while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
+ cur++;
+ if ( cur + 1 >= limit )
+ return 0;
+
+ if ( *cur == '(' )
+ cur++; /* skip the opening parenthesis, if there is one */
+
+ *cursor = cur;
+ count = 0;
+
+ /* then, count its length */
+ for ( ; cur < limit; cur++ )
+ {
+ if ( *cur == '(' )
+ count++;
+
+ else if ( *cur == ')' )
+ {
+ count--;
+ if ( count < 0 )
+ break;
+ }
+ }
+
+ len = cur - *cursor;
+ if ( cur >= limit || FT_ALLOC( result, len + 1 ) )
+ return 0;
+
+ /* now copy the string */
+ FT_MEM_COPY( result, *cursor, len );
+ result[len] = '\0';
+ *cursor = cur;
+ return result;
+ }
+
+#endif /* 0 */
+
+
+ static int
+ ps_tobool( FT_Byte* *acur,
+ FT_Byte* limit )
+ {
+ FT_Byte* cur = *acur;
+ FT_Bool result = 0;
+
+
+ /* return 1 if we find `true', 0 otherwise */
+ if ( cur + 3 < limit &&
+ cur[0] == 't' &&
+ cur[1] == 'r' &&
+ cur[2] == 'u' &&
+ cur[3] == 'e' )
+ {
+ result = 1;
+ cur += 5;
+ }
+ else if ( cur + 4 < limit &&
+ cur[0] == 'f' &&
+ cur[1] == 'a' &&
+ cur[2] == 'l' &&
+ cur[3] == 's' &&
+ cur[4] == 'e' )
+ {
+ result = 0;
+ cur += 6;
+ }
+
+ *acur = cur;
+ return result;
+ }
+
+
+ /* load a simple field (i.e. non-table) into the current list of objects */
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_load_field( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags )
+ {
+ T1_TokenRec token;
+ FT_Byte* cur;
+ FT_Byte* limit;
+ FT_UInt count;
+ FT_UInt idx;
+ FT_Error error;
+
+
+ /* this also skips leading whitespace */
+ ps_parser_to_token( parser, &token );
+ if ( !token.type )
+ goto Fail;
+
+ count = 1;
+ idx = 0;
+ cur = token.start;
+ limit = token.limit;
+
+ /* we must detect arrays in /FontBBox */
+ if ( field->type == T1_FIELD_TYPE_BBOX )
+ {
+ T1_TokenRec token2;
+ FT_Byte* old_cur = parser->cursor;
+ FT_Byte* old_limit = parser->limit;
+
+
+ /* don't include delimiters */
+ parser->cursor = token.start + 1;
+ parser->limit = token.limit - 1;
+
+ ps_parser_to_token( parser, &token2 );
+ parser->cursor = old_cur;
+ parser->limit = old_limit;
+
+ if ( token2.type == T1_TOKEN_TYPE_ARRAY )
+ goto FieldArray;
+ }
+ else if ( token.type == T1_TOKEN_TYPE_ARRAY )
+ {
+ FieldArray:
+ /* if this is an array and we have no blend, an error occurs */
+ if ( max_objects == 0 )
+ goto Fail;
+
+ count = max_objects;
+ idx = 1;
+
+ /* don't include delimiters */
+ cur++;
+ limit--;
+ }
+
+ for ( ; count > 0; count--, idx++ )
+ {
+ FT_Byte* q = (FT_Byte*)objects[idx] + field->offset;
+ FT_Long val;
+ FT_String* string;
+
+
+ skip_spaces( &cur, limit );
+
+ switch ( field->type )
+ {
+ case T1_FIELD_TYPE_BOOL:
+ val = ps_tobool( &cur, limit );
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_FIXED:
+ val = PS_Conv_ToFixed( &cur, limit, 0 );
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_FIXED_1000:
+ val = PS_Conv_ToFixed( &cur, limit, 3 );
+ goto Store_Integer;
+
+ case T1_FIELD_TYPE_INTEGER:
+ val = PS_Conv_ToInt( &cur, limit );
+ /* fall through */
+
+ Store_Integer:
+ switch ( field->size )
+ {
+ case (8 / FT_CHAR_BIT):
+ *(FT_Byte*)q = (FT_Byte)val;
+ break;
+
+ case (16 / FT_CHAR_BIT):
+ *(FT_UShort*)q = (FT_UShort)val;
+ break;
+
+ case (32 / FT_CHAR_BIT):
+ *(FT_UInt32*)q = (FT_UInt32)val;
+ break;
+
+ default: /* for 64-bit systems */
+ *(FT_Long*)q = val;
+ }
+ break;
+
+ case T1_FIELD_TYPE_STRING:
+ case T1_FIELD_TYPE_KEY:
+ {
+ FT_Memory memory = parser->memory;
+ FT_UInt len = (FT_UInt)( limit - cur );
+
+
+ if ( cur >= limit )
+ break;
+
+ /* we allow both a string or a name */
+ /* for cases like /FontName (foo) def */
+ if ( token.type == T1_TOKEN_TYPE_KEY )
+ {
+ /* don't include leading `/' */
+ len--;
+ cur++;
+ }
+ else if ( token.type == T1_TOKEN_TYPE_STRING )
+ {
+ /* don't include delimiting parentheses */
+ /* XXX we don't handle <<...>> here */
+ /* XXX should we convert octal escapes? */
+ /* if so, what encoding should we use? */
+ cur++;
+ len -= 2;
+ }
+ else
+ {
+ FT_ERROR(( "ps_parser_load_field: expected a name or string "
+ "but found token of type %d instead\n",
+ token.type ));
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* for this to work (FT_String**)q must have been */
+ /* initialized to NULL */
+ if ( *(FT_String**)q != NULL )
+ {
+ FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
+ field->ident ));
+ FT_FREE( *(FT_String**)q );
+ *(FT_String**)q = NULL;
+ }
+
+ if ( FT_ALLOC( string, len + 1 ) )
+ goto Exit;
+
+ FT_MEM_COPY( string, cur, len );
+ string[len] = 0;
+
+ *(FT_String**)q = string;
+ }
+ break;
+
+ case T1_FIELD_TYPE_BBOX:
+ {
+ FT_Fixed temp[4];
+ FT_BBox* bbox = (FT_BBox*)q;
+ FT_Int result;
+
+
+ result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
+
+ if ( result < 0 )
+ {
+ FT_ERROR(( "ps_parser_load_field: "
+ "expected four integers in bounding box\n" ));
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ bbox->xMin = FT_RoundFix( temp[0] );
+ bbox->yMin = FT_RoundFix( temp[1] );
+ bbox->xMax = FT_RoundFix( temp[2] );
+ bbox->yMax = FT_RoundFix( temp[3] );
+ }
+ break;
+
+ default:
+ /* an error occurred */
+ goto Fail;
+ }
+ }
+
+#if 0 /* obsolete -- keep for reference */
+ if ( pflags )
+ *pflags |= 1L << field->flag_bit;
+#else
+ FT_UNUSED( pflags );
+#endif
+
+ error = PSaux_Err_Ok;
+
+ Exit:
+ return error;
+
+ Fail:
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+
+#define T1_MAX_TABLE_ELEMENTS 32
+
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_load_field_table( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags )
+ {
+ T1_TokenRec elements[T1_MAX_TABLE_ELEMENTS];
+ T1_Token token;
+ FT_Int num_elements;
+ FT_Error error = PSaux_Err_Ok;
+ FT_Byte* old_cursor;
+ FT_Byte* old_limit;
+ T1_FieldRec fieldrec = *(T1_Field)field;
+
+
+ fieldrec.type = T1_FIELD_TYPE_INTEGER;
+ if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
+ field->type == T1_FIELD_TYPE_BBOX )
+ fieldrec.type = T1_FIELD_TYPE_FIXED;
+
+ ps_parser_to_token_array( parser, elements,
+ T1_MAX_TABLE_ELEMENTS, &num_elements );
+ if ( num_elements < 0 )
+ {
+ error = PSaux_Err_Ignore;
+ goto Exit;
+ }
+ if ( (FT_UInt)num_elements > field->array_max )
+ num_elements = field->array_max;
+
+ old_cursor = parser->cursor;
+ old_limit = parser->limit;
+
+ /* we store the elements count if necessary */
+ if ( field->type != T1_FIELD_TYPE_BBOX )
+ *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
+ (FT_Byte)num_elements;
+
+ /* we now load each element, adjusting the field.offset on each one */
+ token = elements;
+ for ( ; num_elements > 0; num_elements--, token++ )
+ {
+ parser->cursor = token->start;
+ parser->limit = token->limit;
+ ps_parser_load_field( parser, &fieldrec, objects, max_objects, 0 );
+ fieldrec.offset += fieldrec.size;
+ }
+
+#if 0 /* obsolete -- keep for reference */
+ if ( pflags )
+ *pflags |= 1L << field->flag_bit;
+#else
+ FT_UNUSED( pflags );
+#endif
+
+ parser->cursor = old_cursor;
+ parser->limit = old_limit;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Long )
+ ps_parser_to_int( PS_Parser parser )
+ {
+ ps_parser_skip_spaces( parser );
+ return PS_Conv_ToInt( &parser->cursor, parser->limit );
+ }
+
+
+ /* first character must be `<' if `delimiters' is non-zero */
+
+ FT_LOCAL_DEF( FT_Error )
+ ps_parser_to_bytes( PS_Parser parser,
+ FT_Byte* bytes,
+ FT_Long max_bytes,
+ FT_Long* pnum_bytes,
+ FT_Bool delimiters )
+ {
+ FT_Error error = PSaux_Err_Ok;
+ FT_Byte* cur;
+
+
+ ps_parser_skip_spaces( parser );
+ cur = parser->cursor;
+
+ if ( cur >= parser->limit )
+ goto Exit;
+
+ if ( delimiters )
+ {
+ if ( *cur != '<' )
+ {
+ FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ cur++;
+ }
+
+ *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
+ parser->limit,
+ bytes,
+ max_bytes );
+
+ if ( delimiters )
+ {
+ if ( cur < parser->limit && *cur != '>' )
+ {
+ FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
+ error = PSaux_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ cur++;
+ }
+
+ parser->cursor = cur;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Fixed )
+ ps_parser_to_fixed( PS_Parser parser,
+ FT_Int power_ten )
+ {
+ ps_parser_skip_spaces( parser );
+ return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ ps_parser_to_coord_array( PS_Parser parser,
+ FT_Int max_coords,
+ FT_Short* coords )
+ {
+ ps_parser_skip_spaces( parser );
+ return ps_tocoordarray( &parser->cursor, parser->limit,
+ max_coords, coords );
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ ps_parser_to_fixed_array( PS_Parser parser,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten )
+ {
+ ps_parser_skip_spaces( parser );
+ return ps_tofixedarray( &parser->cursor, parser->limit,
+ max_values, values, power_ten );
+ }
+
+
+#if 0
+
+ FT_LOCAL_DEF( FT_String* )
+ T1_ToString( PS_Parser parser )
+ {
+ return ps_tostring( &parser->cursor, parser->limit, parser->memory );
+ }
+
+
+ FT_LOCAL_DEF( FT_Bool )
+ T1_ToBool( PS_Parser parser )
+ {
+ return ps_tobool( &parser->cursor, parser->limit );
+ }
+
+#endif /* 0 */
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_init( PS_Parser parser,
+ FT_Byte* base,
+ FT_Byte* limit,
+ FT_Memory memory )
+ {
+ parser->error = PSaux_Err_Ok;
+ parser->base = base;
+ parser->limit = limit;
+ parser->cursor = base;
+ parser->memory = memory;
+ parser->funcs = ps_parser_funcs;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ ps_parser_done( PS_Parser parser )
+ {
+ FT_UNUSED( parser );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1_builder_init */
+ /* */
+ /* <Description> */
+ /* Initializes a given glyph builder. */
+ /* */
+ /* <InOut> */
+ /* builder :: A pointer to the glyph builder to initialize. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* size :: The current size object. */
+ /* */
+ /* glyph :: The current glyph object. */
+ /* */
+ /* hinting :: Whether hinting should be applied. */
+ /* */
+ FT_LOCAL_DEF( void )
+ t1_builder_init( T1_Builder builder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot glyph,
+ FT_Bool hinting )
+ {
+ builder->parse_state = T1_Parse_Start;
+ builder->load_points = 1;
+
+ builder->face = face;
+ builder->glyph = glyph;
+ builder->memory = face->memory;
+
+ if ( glyph )
+ {
+ FT_GlyphLoader loader = glyph->internal->loader;
+
+
+ builder->loader = loader;
+ builder->base = &loader->base.outline;
+ builder->current = &loader->current.outline;
+ FT_GlyphLoader_Rewind( loader );
+
+ builder->hints_globals = size->internal;
+ builder->hints_funcs = 0;
+
+ if ( hinting )
+ builder->hints_funcs = glyph->internal->glyph_hints;
+ }
+
+ if ( size )
+ {
+ builder->scale_x = size->metrics.x_scale;
+ builder->scale_y = size->metrics.y_scale;
+ }
+
+ builder->pos_x = 0;
+ builder->pos_y = 0;
+
+ builder->left_bearing.x = 0;
+ builder->left_bearing.y = 0;
+ builder->advance.x = 0;
+ builder->advance.y = 0;
+
+ builder->funcs = t1_builder_funcs;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1_builder_done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given glyph builder. Its contents can still be used */
+ /* after the call, but the function saves important information */
+ /* within the corresponding glyph slot. */
+ /* */
+ /* <Input> */
+ /* builder :: A pointer to the glyph builder to finalize. */
+ /* */
+ FT_LOCAL_DEF( void )
+ t1_builder_done( T1_Builder builder )
+ {
+ FT_GlyphSlot glyph = builder->glyph;
+
+
+ if ( glyph )
+ glyph->outline = *builder->base;
+ }
+
+
+ /* check that there is enough space for `count' more points */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_check_points( T1_Builder builder,
+ FT_Int count )
+ {
+ return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
+ }
+
+
+ /* add a new point, do not check space */
+ FT_LOCAL_DEF( void )
+ t1_builder_add_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( builder->load_points )
+ {
+ FT_Vector* point = outline->points + outline->n_points;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
+
+
+ if ( builder->shift )
+ {
+ x >>= 16;
+ y >>= 16;
+ }
+ point->x = x;
+ point->y = y;
+ *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
+
+ builder->last = *point;
+ }
+ outline->n_points++;
+ }
+
+
+ /* check space for a new on-curve point, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_add_point1( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error;
+
+
+ error = t1_builder_check_points( builder, 1 );
+ if ( !error )
+ t1_builder_add_point( builder, x, y, 1 );
+
+ return error;
+ }
+
+
+ /* check space for a new contour, then add it */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_add_contour( T1_Builder builder )
+ {
+ FT_Outline* outline = builder->current;
+ FT_Error error;
+
+
+ if ( !builder->load_points )
+ {
+ outline->n_contours++;
+ return PSaux_Err_Ok;
+ }
+
+ error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
+ if ( !error )
+ {
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+
+ outline->n_contours++;
+ }
+
+ return error;
+ }
+
+
+ /* if a path was begun, add its first on-curve point */
+ FT_LOCAL_DEF( FT_Error )
+ t1_builder_start_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y )
+ {
+ FT_Error error = PSaux_Err_Invalid_File_Format;
+
+
+ /* test whether we are building a new contour */
+
+ if ( builder->parse_state == T1_Parse_Have_Path )
+ error = PSaux_Err_Ok;
+ else if ( builder->parse_state == T1_Parse_Have_Moveto )
+ {
+ builder->parse_state = T1_Parse_Have_Path;
+ error = t1_builder_add_contour( builder );
+ if ( !error )
+ error = t1_builder_add_point1( builder, x, y );
+ }
+
+ return error;
+ }
+
+
+ /* close the current contour */
+ FT_LOCAL_DEF( void )
+ t1_builder_close_contour( T1_Builder builder )
+ {
+ FT_Outline* outline = builder->current;
+
+
+ if ( !outline )
+ return;
+
+ /* XXXX: We must not include the last point in the path if it */
+ /* is located on the first point. */
+ if ( outline->n_points > 1 )
+ {
+ FT_Int first = 0;
+ FT_Vector* p1 = outline->points + first;
+ FT_Vector* p2 = outline->points + outline->n_points - 1;
+ FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
+
+
+ if ( outline->n_contours > 1 )
+ {
+ first = outline->contours[outline->n_contours - 2] + 1;
+ p1 = outline->points + first;
+ }
+
+ /* `delete' last point only if it coincides with the first */
+ /* point and it is not a control point (which can happen). */
+ if ( p1->x == p2->x && p1->y == p2->y )
+ if ( *control == FT_CURVE_TAG_ON )
+ outline->n_points--;
+ }
+
+ if ( outline->n_contours > 0 )
+ outline->contours[outline->n_contours - 1] =
+ (short)( outline->n_points - 1 );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** OTHER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ t1_decrypt( FT_Byte* buffer,
+ FT_Offset length,
+ FT_UShort seed )
+ {
+ PS_Conv_EexecDecode( &buffer,
+ buffer + length,
+ buffer,
+ length,
+ &seed );
+ }
+
+
+/* END */
diff --git a/src/psaux/psobjs.h b/src/psaux/psobjs.h
new file mode 100644
index 0000000..c2cbf2c
--- /dev/null
+++ b/src/psaux/psobjs.h
@@ -0,0 +1,212 @@
+/***************************************************************************/
+/* */
+/* psobjs.h */
+/* */
+/* Auxiliary functions for PostScript fonts (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSOBJS_H__
+#define __PSOBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1_TABLE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_TABLE
+ const PS_Table_FuncsRec ps_table_funcs;
+
+ FT_CALLBACK_TABLE
+ const PS_Parser_FuncsRec ps_parser_funcs;
+
+ FT_CALLBACK_TABLE
+ const T1_Builder_FuncsRec t1_builder_funcs;
+
+
+ FT_LOCAL( FT_Error )
+ ps_table_new( PS_Table table,
+ FT_Int count,
+ FT_Memory memory );
+
+ FT_LOCAL( FT_Error )
+ ps_table_add( PS_Table table,
+ FT_Int idx,
+ void* object,
+ FT_PtrDist length );
+
+ FT_LOCAL( void )
+ ps_table_done( PS_Table table );
+
+
+ FT_LOCAL( void )
+ ps_table_release( PS_Table table );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL( void )
+ ps_parser_skip_spaces( PS_Parser parser );
+
+ FT_LOCAL( void )
+ ps_parser_skip_PS_token( PS_Parser parser );
+
+ FT_LOCAL( void )
+ ps_parser_to_token( PS_Parser parser,
+ T1_Token token );
+
+ FT_LOCAL( void )
+ ps_parser_to_token_array( PS_Parser parser,
+ T1_Token tokens,
+ FT_UInt max_tokens,
+ FT_Int* pnum_tokens );
+
+ FT_LOCAL( FT_Error )
+ ps_parser_load_field( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags );
+
+ FT_LOCAL( FT_Error )
+ ps_parser_load_field_table( PS_Parser parser,
+ const T1_Field field,
+ void** objects,
+ FT_UInt max_objects,
+ FT_ULong* pflags );
+
+ FT_LOCAL( FT_Long )
+ ps_parser_to_int( PS_Parser parser );
+
+
+ FT_LOCAL( FT_Error )
+ ps_parser_to_bytes( PS_Parser parser,
+ FT_Byte* bytes,
+ FT_Long max_bytes,
+ FT_Long* pnum_bytes,
+ FT_Bool delimiters );
+
+
+ FT_LOCAL( FT_Fixed )
+ ps_parser_to_fixed( PS_Parser parser,
+ FT_Int power_ten );
+
+
+ FT_LOCAL( FT_Int )
+ ps_parser_to_coord_array( PS_Parser parser,
+ FT_Int max_coords,
+ FT_Short* coords );
+
+ FT_LOCAL( FT_Int )
+ ps_parser_to_fixed_array( PS_Parser parser,
+ FT_Int max_values,
+ FT_Fixed* values,
+ FT_Int power_ten );
+
+
+ FT_LOCAL( void )
+ ps_parser_init( PS_Parser parser,
+ FT_Byte* base,
+ FT_Byte* limit,
+ FT_Memory memory );
+
+ FT_LOCAL( void )
+ ps_parser_done( PS_Parser parser );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** T1 BUILDER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ t1_builder_init( T1_Builder builder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot glyph,
+ FT_Bool hinting );
+
+ FT_LOCAL( void )
+ t1_builder_done( T1_Builder builder );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_check_points( T1_Builder builder,
+ FT_Int count );
+
+ FT_LOCAL( void )
+ t1_builder_add_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y,
+ FT_Byte flag );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_add_point1( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y );
+
+ FT_LOCAL( FT_Error )
+ t1_builder_add_contour( T1_Builder builder );
+
+
+ FT_LOCAL( FT_Error )
+ t1_builder_start_point( T1_Builder builder,
+ FT_Pos x,
+ FT_Pos y );
+
+
+ FT_LOCAL( void )
+ t1_builder_close_contour( T1_Builder builder );
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** OTHER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_LOCAL( void )
+ t1_decrypt( FT_Byte* buffer,
+ FT_Offset length,
+ FT_UShort seed );
+
+
+FT_END_HEADER
+
+#endif /* __PSOBJS_H__ */
+
+
+/* END */
diff --git a/src/psaux/rules.mk b/src/psaux/rules.mk
new file mode 100644
index 0000000..7a1be37
--- /dev/null
+++ b/src/psaux/rules.mk
@@ -0,0 +1,73 @@
+#
+# FreeType 2 PSaux driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2002, 2003, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# PSAUX driver directory
+#
+PSAUX_DIR := $(SRC_DIR)/psaux
+
+
+# compilation flags for the driver
+#
+PSAUX_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PSAUX_DIR))
+
+
+# PSAUX driver sources (i.e., C files)
+#
+PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c \
+ $(PSAUX_DIR)/t1decode.c \
+ $(PSAUX_DIR)/t1cmap.c \
+ $(PSAUX_DIR)/afmparse.c \
+ $(PSAUX_DIR)/psconv.c \
+ $(PSAUX_DIR)/psauxmod.c
+
+# PSAUX driver headers
+#
+PSAUX_DRV_H := $(PSAUX_DRV_SRC:%c=%h) \
+ $(PSAUX_DIR)/psauxerr.h
+
+
+# PSAUX driver object(s)
+#
+# PSAUX_DRV_OBJ_M is used during `multi' builds.
+# PSAUX_DRV_OBJ_S is used during `single' builds.
+#
+PSAUX_DRV_OBJ_M := $(PSAUX_DRV_SRC:$(PSAUX_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PSAUX_DRV_OBJ_S := $(OBJ_DIR)/psaux.$O
+
+# PSAUX driver source file for single build
+#
+PSAUX_DRV_SRC_S := $(PSAUX_DIR)/psaux.c
+
+
+# PSAUX driver - single object
+#
+$(PSAUX_DRV_OBJ_S): $(PSAUX_DRV_SRC_S) $(PSAUX_DRV_SRC) \
+ $(FREETYPE_H) $(PSAUX_DRV_H)
+ $(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSAUX_DRV_SRC_S))
+
+
+# PSAUX driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PSAUX_DIR)/%.c $(FREETYPE_H) $(PSAUX_DRV_H)
+ $(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PSAUX_DRV_OBJ_S)
+DRV_OBJS_M += $(PSAUX_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/psaux/t1cmap.c b/src/psaux/t1cmap.c
new file mode 100644
index 0000000..2934686
--- /dev/null
+++ b/src/psaux/t1cmap.c
@@ -0,0 +1,333 @@
+/***************************************************************************/
+/* */
+/* t1cmap.c */
+/* */
+/* Type 1 character map support (body). */
+/* */
+/* Copyright 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "t1cmap.h"
+
+#include FT_INTERNAL_DEBUG_H
+
+#include "psauxerr.h"
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ t1_cmap_std_init( T1_CMapStd cmap,
+ FT_Int is_expert )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ cmap->num_glyphs = face->type1.num_glyphs;
+ cmap->glyph_names = (const char* const*)face->type1.glyph_names;
+ cmap->sid_to_string = psnames->adobe_std_strings;
+ cmap->code_to_sid = is_expert ? psnames->adobe_expert_encoding
+ : psnames->adobe_std_encoding;
+
+ FT_ASSERT( cmap->code_to_sid != NULL );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_std_done( T1_CMapStd cmap )
+ {
+ cmap->num_glyphs = 0;
+ cmap->glyph_names = NULL;
+ cmap->sid_to_string = NULL;
+ cmap->code_to_sid = NULL;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_std_char_index( T1_CMapStd cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt result = 0;
+
+
+ if ( char_code < 256 )
+ {
+ FT_UInt code, n;
+ const char* glyph_name;
+
+
+ /* convert character code to Adobe SID string */
+ code = cmap->code_to_sid[char_code];
+ glyph_name = cmap->sid_to_string( code );
+
+ /* look for the corresponding glyph name */
+ for ( n = 0; n < cmap->num_glyphs; n++ )
+ {
+ const char* gname = cmap->glyph_names[n];
+
+
+ if ( gname && gname[0] == glyph_name[0] &&
+ ft_strcmp( gname, glyph_name ) == 0 )
+ {
+ result = n;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_std_char_next( T1_CMapStd cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+
+
+ while ( char_code < 256 )
+ {
+ result = t1_cmap_std_char_index( cmap, char_code );
+ if ( result != 0 )
+ goto Exit;
+
+ char_code++;
+ }
+ char_code = 0;
+
+ Exit:
+ *pchar_code = char_code;
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_standard_init( T1_CMapStd cmap )
+ {
+ t1_cmap_std_init( cmap, 0 );
+ return 0;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_standard_class_rec =
+ {
+ sizeof ( T1_CMapStdRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_standard_init,
+ (FT_CMap_DoneFunc) t1_cmap_std_done,
+ (FT_CMap_CharIndexFunc)t1_cmap_std_char_index,
+ (FT_CMap_CharNextFunc) t1_cmap_std_char_next
+ };
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_expert_init( T1_CMapStd cmap )
+ {
+ t1_cmap_std_init( cmap, 1 );
+ return 0;
+ }
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_expert_class_rec =
+ {
+ sizeof ( T1_CMapStdRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_expert_init,
+ (FT_CMap_DoneFunc) t1_cmap_std_done,
+ (FT_CMap_CharIndexFunc)t1_cmap_std_char_index,
+ (FT_CMap_CharNextFunc) t1_cmap_std_char_next
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 CUSTOM ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_custom_init( T1_CMapCustom cmap )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( cmap );
+ T1_Encoding encoding = &face->type1.encoding;
+
+
+ cmap->first = encoding->code_first;
+ cmap->count = (FT_UInt)( encoding->code_last - cmap->first + 1 );
+ cmap->indices = encoding->char_index;
+
+ FT_ASSERT( cmap->indices != NULL );
+ FT_ASSERT( encoding->code_first <= encoding->code_last );
+
+ return 0;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_custom_done( T1_CMapCustom cmap )
+ {
+ cmap->indices = NULL;
+ cmap->first = 0;
+ cmap->count = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_custom_char_index( T1_CMapCustom cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt result = 0;
+
+
+ if ( ( char_code >= cmap->first ) &&
+ ( char_code < ( cmap->first + cmap->count ) ) )
+ result = cmap->indices[char_code];
+
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_custom_char_next( T1_CMapCustom cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *pchar_code;
+
+
+ ++char_code;
+
+ if ( char_code < cmap->first )
+ char_code = cmap->first;
+
+ for ( ; char_code < ( cmap->first + cmap->count ); char_code++ )
+ {
+ result = cmap->indices[char_code];
+ if ( result != 0 )
+ goto Exit;
+ }
+
+ char_code = 0;
+
+ Exit:
+ *pchar_code = char_code;
+ return result;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_custom_class_rec =
+ {
+ sizeof ( T1_CMapCustomRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_custom_init,
+ (FT_CMap_DoneFunc) t1_cmap_custom_done,
+ (FT_CMap_CharIndexFunc)t1_cmap_custom_char_index,
+ (FT_CMap_CharNextFunc) t1_cmap_custom_char_next
+ };
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( const char * )
+ t1_get_glyph_name( T1_Face face,
+ FT_UInt idx )
+ {
+ return face->type1.glyph_names[idx];
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ t1_cmap_unicode_init( PS_Unicodes unicodes )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_init( memory,
+ unicodes,
+ face->type1.num_glyphs,
+ (PS_GetGlyphNameFunc)&t1_get_glyph_name,
+ (PS_FreeGlyphNameFunc)NULL,
+ (FT_Pointer)face );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ t1_cmap_unicode_done( PS_Unicodes unicodes )
+ {
+ FT_Face face = FT_CMAP_FACE( unicodes );
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ FT_FREE( unicodes->maps );
+ unicodes->num_maps = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_unicode_char_index( PS_Unicodes unicodes,
+ FT_UInt32 char_code )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_index( unicodes, char_code );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ t1_cmap_unicode_char_next( PS_Unicodes unicodes,
+ FT_UInt32 *pchar_code )
+ {
+ T1_Face face = (T1_Face)FT_CMAP_FACE( unicodes );
+ FT_Service_PsCMaps psnames = (FT_Service_PsCMaps)face->psnames;
+
+
+ return psnames->unicodes_char_next( unicodes, pchar_code );
+ }
+
+
+ FT_CALLBACK_TABLE_DEF const FT_CMap_ClassRec
+ t1_cmap_unicode_class_rec =
+ {
+ sizeof ( PS_UnicodesRec ),
+
+ (FT_CMap_InitFunc) t1_cmap_unicode_init,
+ (FT_CMap_DoneFunc) t1_cmap_unicode_done,
+ (FT_CMap_CharIndexFunc)t1_cmap_unicode_char_index,
+ (FT_CMap_CharNextFunc) t1_cmap_unicode_char_next
+ };
+
+
+/* END */
diff --git a/src/psaux/t1cmap.h b/src/psaux/t1cmap.h
new file mode 100644
index 0000000..7ae65d2
--- /dev/null
+++ b/src/psaux/t1cmap.h
@@ -0,0 +1,105 @@
+/***************************************************************************/
+/* */
+/* t1cmap.h */
+/* */
+/* Type 1 character map support (specification). */
+/* */
+/* Copyright 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1CMAP_H__
+#define __T1CMAP_H__
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 STANDARD (AND EXPERT) ENCODING CMAPS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* standard (and expert) encoding cmaps */
+ typedef struct T1_CMapStdRec_* T1_CMapStd;
+
+ typedef struct T1_CMapStdRec_
+ {
+ FT_CMapRec cmap;
+
+ const FT_UShort* code_to_sid;
+ PS_Adobe_Std_StringsFunc sid_to_string;
+
+ FT_UInt num_glyphs;
+ const char* const* glyph_names;
+
+ } T1_CMapStdRec;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_standard_class_rec;
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_expert_class_rec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 CUSTOM ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef struct T1_CMapCustomRec_* T1_CMapCustom;
+
+ typedef struct T1_CMapCustomRec_
+ {
+ FT_CMapRec cmap;
+ FT_UInt first;
+ FT_UInt count;
+ FT_UShort* indices;
+
+ } T1_CMapCustomRec;
+
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_custom_class_rec;
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE1 SYNTHETIC UNICODE ENCODING CMAP *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* unicode (synthetic) cmaps */
+
+ FT_CALLBACK_TABLE const FT_CMap_ClassRec
+ t1_cmap_unicode_class_rec;
+
+ /* */
+
+
+FT_END_HEADER
+
+#endif /* __T1CMAP_H__ */
+
+
+/* END */
diff --git a/src/psaux/t1decode.c b/src/psaux/t1decode.c
new file mode 100644
index 0000000..f790643
--- /dev/null
+++ b/src/psaux/t1decode.c
@@ -0,0 +1,1474 @@
+/***************************************************************************/
+/* */
+/* t1decode.c */
+/* */
+/* PostScript Type 1 decoding routines (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+#include FT_OUTLINE_H
+
+#include "t1decode.h"
+#include "psobjs.h"
+
+#include "psauxerr.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1decode
+
+
+ typedef enum T1_Operator_
+ {
+ op_none = 0,
+ op_endchar,
+ op_hsbw,
+ op_seac,
+ op_sbw,
+ op_closepath,
+ op_hlineto,
+ op_hmoveto,
+ op_hvcurveto,
+ op_rlineto,
+ op_rmoveto,
+ op_rrcurveto,
+ op_vhcurveto,
+ op_vlineto,
+ op_vmoveto,
+ op_dotsection,
+ op_hstem,
+ op_hstem3,
+ op_vstem,
+ op_vstem3,
+ op_div,
+ op_callothersubr,
+ op_callsubr,
+ op_pop,
+ op_return,
+ op_setcurrentpoint,
+ op_unknown15,
+
+ op_max /* never remove this one */
+
+ } T1_Operator;
+
+
+ static
+ const FT_Int t1_args_count[op_max] =
+ {
+ 0, /* none */
+ 0, /* endchar */
+ 2, /* hsbw */
+ 5, /* seac */
+ 4, /* sbw */
+ 0, /* closepath */
+ 1, /* hlineto */
+ 1, /* hmoveto */
+ 4, /* hvcurveto */
+ 2, /* rlineto */
+ 2, /* rmoveto */
+ 6, /* rrcurveto */
+ 4, /* vhcurveto */
+ 1, /* vlineto */
+ 1, /* vmoveto */
+ 0, /* dotsection */
+ 2, /* hstem */
+ 6, /* hstem3 */
+ 2, /* vstem */
+ 6, /* vstem3 */
+ 2, /* div */
+ -1, /* callothersubr */
+ 1, /* callsubr */
+ 0, /* pop */
+ 0, /* return */
+ 2, /* setcurrentpoint */
+ 2 /* opcode 15 (undocumented and obsolete) */
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1_lookup_glyph_by_stdcharcode */
+ /* */
+ /* <Description> */
+ /* Looks up a given glyph by its StandardEncoding charcode. Used to */
+ /* implement the SEAC Type 1 operator. */
+ /* */
+ /* <Input> */
+ /* face :: The current face object. */
+ /* */
+ /* charcode :: The character code to look for. */
+ /* */
+ /* <Return> */
+ /* A glyph index in the font face. Returns -1 if the corresponding */
+ /* glyph wasn't found. */
+ /* */
+ static FT_Int
+ t1_lookup_glyph_by_stdcharcode( T1_Decoder decoder,
+ FT_Int charcode )
+ {
+ FT_UInt n;
+ const FT_String* glyph_name;
+ FT_Service_PsCMaps psnames = decoder->psnames;
+
+
+ /* check range of standard char code */
+ if ( charcode < 0 || charcode > 255 )
+ return -1;
+
+ glyph_name = psnames->adobe_std_strings(
+ psnames->adobe_std_encoding[charcode]);
+
+ for ( n = 0; n < decoder->num_glyphs; n++ )
+ {
+ FT_String* name = (FT_String*)decoder->glyph_names[n];
+
+
+ if ( name && name[0] == glyph_name[0] &&
+ ft_strcmp( name, glyph_name ) == 0 )
+ return n;
+ }
+
+ return -1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1operator_seac */
+ /* */
+ /* <Description> */
+ /* Implements the `seac' Type 1 operator for a Type 1 decoder. */
+ /* */
+ /* <Input> */
+ /* decoder :: The current CID decoder. */
+ /* */
+ /* asb :: The accent's side bearing. */
+ /* */
+ /* adx :: The horizontal offset of the accent. */
+ /* */
+ /* ady :: The vertical offset of the accent. */
+ /* */
+ /* bchar :: The base character's StandardEncoding charcode. */
+ /* */
+ /* achar :: The accent character's StandardEncoding charcode. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ t1operator_seac( T1_Decoder decoder,
+ FT_Pos asb,
+ FT_Pos adx,
+ FT_Pos ady,
+ FT_Int bchar,
+ FT_Int achar )
+ {
+ FT_Error error;
+ FT_Int bchar_index, achar_index;
+#if 0
+ FT_Int n_base_points;
+ FT_Outline* base = decoder->builder.base;
+#endif
+ FT_Vector left_bearing, advance;
+
+
+ /* seac weirdness */
+ adx += decoder->builder.left_bearing.x;
+
+ /* `glyph_names' is set to 0 for CID fonts which do not */
+ /* include an encoding. How can we deal with these? */
+ if ( decoder->glyph_names == 0 )
+ {
+ FT_ERROR(( "t1operator_seac:" ));
+ FT_ERROR(( " glyph names table not available in this font!\n" ));
+ return PSaux_Err_Syntax_Error;
+ }
+
+ bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar );
+ achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar );
+
+ if ( bchar_index < 0 || achar_index < 0 )
+ {
+ FT_ERROR(( "t1operator_seac:" ));
+ FT_ERROR(( " invalid seac character code arguments\n" ));
+ return PSaux_Err_Syntax_Error;
+ }
+
+ /* if we are trying to load a composite glyph, do not load the */
+ /* accent character and return the array of subglyphs. */
+ if ( decoder->builder.no_recurse )
+ {
+ FT_GlyphSlot glyph = (FT_GlyphSlot)decoder->builder.glyph;
+ FT_GlyphLoader loader = glyph->internal->loader;
+ FT_SubGlyph subg;
+
+
+ /* reallocate subglyph array if necessary */
+ error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
+ if ( error )
+ goto Exit;
+
+ subg = loader->current.subglyphs;
+
+ /* subglyph 0 = base character */
+ subg->index = bchar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
+ FT_SUBGLYPH_FLAG_USE_MY_METRICS;
+ subg->arg1 = 0;
+ subg->arg2 = 0;
+ subg++;
+
+ /* subglyph 1 = accent character */
+ subg->index = achar_index;
+ subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
+ subg->arg1 = (FT_Int)( adx - asb );
+ subg->arg2 = (FT_Int)ady;
+
+ /* set up remaining glyph fields */
+ glyph->num_subglyphs = 2;
+ glyph->subglyphs = loader->base.subglyphs;
+ glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ loader->current.num_subglyphs = 2;
+ goto Exit;
+ }
+
+ /* First load `bchar' in builder */
+ /* now load the unscaled outline */
+
+ FT_GlyphLoader_Prepare( decoder->builder.loader ); /* prepare loader */
+
+ error = t1_decoder_parse_glyph( decoder, bchar_index );
+ if ( error )
+ goto Exit;
+
+ /* save the left bearing and width of the base character */
+ /* as they will be erased by the next load. */
+
+ left_bearing = decoder->builder.left_bearing;
+ advance = decoder->builder.advance;
+
+ decoder->builder.left_bearing.x = 0;
+ decoder->builder.left_bearing.y = 0;
+
+ decoder->builder.pos_x = adx - asb;
+ decoder->builder.pos_y = ady;
+
+ /* Now load `achar' on top of */
+ /* the base outline */
+ error = t1_decoder_parse_glyph( decoder, achar_index );
+ if ( error )
+ goto Exit;
+
+ /* restore the left side bearing and */
+ /* advance width of the base character */
+
+ decoder->builder.left_bearing = left_bearing;
+ decoder->builder.advance = advance;
+
+ decoder->builder.pos_x = 0;
+ decoder->builder.pos_y = 0;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* t1_decoder_parse_charstrings */
+ /* */
+ /* <Description> */
+ /* Parses a given Type 1 charstrings program. */
+ /* */
+ /* <Input> */
+ /* decoder :: The current Type 1 decoder. */
+ /* */
+ /* charstring_base :: The base address of the charstring stream. */
+ /* */
+ /* charstring_len :: The length in bytes of the charstring stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_charstrings( T1_Decoder decoder,
+ FT_Byte* charstring_base,
+ FT_UInt charstring_len )
+ {
+ FT_Error error;
+ T1_Decoder_Zone zone;
+ FT_Byte* ip;
+ FT_Byte* limit;
+ T1_Builder builder = &decoder->builder;
+ FT_Pos x, y, orig_x, orig_y;
+ FT_Int known_othersubr_result_cnt = 0;
+ FT_Int unknown_othersubr_result_cnt = 0;
+
+ T1_Hints_Funcs hinter;
+
+
+ /* we don't want to touch the source code -- use macro trick */
+#define start_point t1_builder_start_point
+#define check_points t1_builder_check_points
+#define add_point t1_builder_add_point
+#define add_point1 t1_builder_add_point1
+#define add_contour t1_builder_add_contour
+#define close_contour t1_builder_close_contour
+
+ /* First of all, initialize the decoder */
+ decoder->top = decoder->stack;
+ decoder->zone = decoder->zones;
+ zone = decoder->zones;
+
+ builder->parse_state = T1_Parse_Start;
+
+ hinter = (T1_Hints_Funcs)builder->hints_funcs;
+
+ /* a font that reads BuildCharArray without setting */
+ /* its values first is buggy, but ... */
+ FT_ASSERT( ( decoder->len_buildchar == 0 ) ==
+ ( decoder->buildchar == NULL ) );
+
+ if ( decoder->len_buildchar > 0 )
+ memset( &decoder->buildchar[0],
+ 0,
+ sizeof( decoder->buildchar[0] ) *
+ decoder->len_buildchar );
+
+ FT_TRACE4(( "\nStart charstring\n" ));
+
+ zone->base = charstring_base;
+ limit = zone->limit = charstring_base + charstring_len;
+ ip = zone->cursor = zone->base;
+
+ error = PSaux_Err_Ok;
+
+ x = orig_x = builder->pos_x;
+ y = orig_y = builder->pos_y;
+
+ /* begin hints recording session, if any */
+ if ( hinter )
+ hinter->open( hinter->hints );
+
+ /* now, execute loop */
+ while ( ip < limit )
+ {
+ FT_Long* top = decoder->top;
+ T1_Operator op = op_none;
+ FT_Long value = 0;
+
+
+ FT_ASSERT( known_othersubr_result_cnt == 0 ||
+ unknown_othersubr_result_cnt == 0 );
+
+ FT_TRACE5(( " (%d)", decoder->top - decoder->stack ));
+
+ /*********************************************************************/
+ /* */
+ /* Decode operator or operand */
+ /* */
+ /* */
+
+ /* first of all, decompress operator or value */
+ switch ( *ip++ )
+ {
+ case 1:
+ op = op_hstem;
+ break;
+
+ case 3:
+ op = op_vstem;
+ break;
+ case 4:
+ op = op_vmoveto;
+ break;
+ case 5:
+ op = op_rlineto;
+ break;
+ case 6:
+ op = op_hlineto;
+ break;
+ case 7:
+ op = op_vlineto;
+ break;
+ case 8:
+ op = op_rrcurveto;
+ break;
+ case 9:
+ op = op_closepath;
+ break;
+ case 10:
+ op = op_callsubr;
+ break;
+ case 11:
+ op = op_return;
+ break;
+
+ case 13:
+ op = op_hsbw;
+ break;
+ case 14:
+ op = op_endchar;
+ break;
+
+ case 15: /* undocumented, obsolete operator */
+ op = op_unknown15;
+ break;
+
+ case 21:
+ op = op_rmoveto;
+ break;
+ case 22:
+ op = op_hmoveto;
+ break;
+
+ case 30:
+ op = op_vhcurveto;
+ break;
+ case 31:
+ op = op_hvcurveto;
+ break;
+
+ case 12:
+ if ( ip > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invalid escape (12+EOF)\n" ));
+ goto Syntax_Error;
+ }
+
+ switch ( *ip++ )
+ {
+ case 0:
+ op = op_dotsection;
+ break;
+ case 1:
+ op = op_vstem3;
+ break;
+ case 2:
+ op = op_hstem3;
+ break;
+ case 6:
+ op = op_seac;
+ break;
+ case 7:
+ op = op_sbw;
+ break;
+ case 12:
+ op = op_div;
+ break;
+ case 16:
+ op = op_callothersubr;
+ break;
+ case 17:
+ op = op_pop;
+ break;
+ case 33:
+ op = op_setcurrentpoint;
+ break;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invalid escape (12+%d)\n",
+ ip[-1] ));
+ goto Syntax_Error;
+ }
+ break;
+
+ case 255: /* four bytes integer */
+ if ( ip + 4 > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ value = (FT_Int32)( ((FT_Long)ip[0] << 24) |
+ ((FT_Long)ip[1] << 16) |
+ ((FT_Long)ip[2] << 8 ) |
+ ip[3] );
+ ip += 4;
+ break;
+
+ default:
+ if ( ip[-1] >= 32 )
+ {
+ if ( ip[-1] < 247 )
+ value = (FT_Long)ip[-1] - 139;
+ else
+ {
+ if ( ++ip > limit )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: " ));
+ FT_ERROR(( "unexpected EOF in integer\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( ip[-2] < 251 )
+ value = ( ( (FT_Long)ip[-2] - 247 ) << 8 ) + ip[-1] + 108;
+ else
+ value = -( ( ( (FT_Long)ip[-2] - 251 ) << 8 ) + ip[-1] + 108 );
+ }
+ }
+ else
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invalid byte (%d)\n", ip[-1] ));
+ goto Syntax_Error;
+ }
+ }
+
+ if ( unknown_othersubr_result_cnt > 0 )
+ {
+ switch ( op )
+ {
+ case op_callsubr:
+ case op_return:
+ case op_none:
+ case op_pop:
+ break;
+
+ default:
+ /* all operands have been transferred by previous pops */
+ unknown_othersubr_result_cnt = 0;
+ break;
+ }
+ }
+
+ /*********************************************************************/
+ /* */
+ /* Push value on stack, or process operator */
+ /* */
+ /* */
+ if ( op == op_none )
+ {
+ if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow!\n" ));
+ goto Syntax_Error;
+ }
+
+ FT_TRACE4(( " %ld", value ));
+
+ *top++ = value;
+ decoder->top = top;
+ }
+ else if ( op == op_callothersubr ) /* callothersubr */
+ {
+ FT_Int subr_no;
+ FT_Int arg_cnt;
+
+
+ FT_TRACE4(( " callothersubr" ));
+
+ if ( top - decoder->stack < 2 )
+ goto Stack_Underflow;
+
+ top -= 2;
+
+ subr_no = (FT_Int)top[1];
+ arg_cnt = (FT_Int)top[0];
+
+ /***********************************************************/
+ /* */
+ /* remove all operands to callothersubr from the stack */
+ /* */
+ /* for handled othersubrs, where we know the number of */
+ /* arguments, we increase the stack by the value of */
+ /* known_othersubr_result_cnt */
+ /* */
+ /* for unhandled othersubrs the following pops adjust the */
+ /* stack pointer as necessary */
+
+ if ( arg_cnt > top - decoder->stack )
+ goto Stack_Underflow;
+
+ top -= arg_cnt;
+
+ known_othersubr_result_cnt = 0;
+ unknown_othersubr_result_cnt = 0;
+
+ /* XXX TODO: The checks to `arg_count == <whatever>' */
+ /* might not be correct; an othersubr expects a certain */
+ /* number of operands on the PostScript stack (as opposed */
+ /* to the T1 stack) but it doesn't have to put them there */
+ /* by itself; previous othersubrs might have left the */
+ /* operands there if they were not followed by an */
+ /* appropriate number of pops */
+ /* */
+ /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */
+ /* accept a font that contains charstrings like */
+ /* */
+ /* 100 200 2 20 callothersubr */
+ /* 300 1 20 callothersubr pop */
+ /* */
+ /* Perhaps this is the reason why BuildCharArray exists. */
+
+ switch ( subr_no )
+ {
+ case 1: /* start flex feature */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ decoder->flex_state = 1;
+ decoder->num_flex_vectors = 0;
+ if ( start_point( builder, x, y ) ||
+ check_points( builder, 6 ) )
+ goto Fail;
+ break;
+
+ case 2: /* add flex vectors */
+ {
+ FT_Int idx;
+
+
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ /* note that we should not add a point for index 0; */
+ /* this will move our current position to the flex */
+ /* point without adding any point to the outline */
+ idx = decoder->num_flex_vectors++;
+ if ( idx > 0 && idx < 7 )
+ add_point( builder,
+ x,
+ y,
+ (FT_Byte)( idx == 3 || idx == 6 ) );
+ }
+ break;
+
+ case 0: /* end flex feature */
+ if ( arg_cnt != 3 )
+ goto Unexpected_OtherSubr;
+
+ if ( decoder->flex_state == 0 ||
+ decoder->num_flex_vectors != 7 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "unexpected flex end\n" ));
+ goto Syntax_Error;
+ }
+
+ /* the two `results' are popped by the following setcurrentpoint */
+ known_othersubr_result_cnt = 2;
+ break;
+
+ case 3: /* change hints */
+ if ( arg_cnt != 1 )
+ goto Unexpected_OtherSubr;
+
+ known_othersubr_result_cnt = 1;
+
+ if ( hinter )
+ hinter->reset( hinter->hints, builder->current->n_points );
+
+ break;
+
+ case 12:
+ case 13:
+ /* counter control hints, clear stack */
+ top = decoder->stack;
+ break;
+
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18: /* multiple masters */
+ {
+ PS_Blend blend = decoder->blend;
+ FT_UInt num_points, nn, mm;
+ FT_Long* delta;
+ FT_Long* values;
+
+
+ if ( !blend )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: " ));
+ FT_ERROR(( "unexpected multiple masters operator!\n" ));
+ goto Syntax_Error;
+ }
+
+ num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 );
+ if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: " ));
+ FT_ERROR(( "incorrect number of mm arguments\n" ));
+ goto Syntax_Error;
+ }
+
+ /* we want to compute: */
+ /* */
+ /* a0*w0 + a1*w1 + ... + ak*wk */
+ /* */
+ /* but we only have the a0, a1-a0, a2-a0, .. ak-a0 */
+ /* however, given that w0 + w1 + ... + wk == 1, we can */
+ /* rewrite it easily as: */
+ /* */
+ /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + .. + (ak-a0)*wk */
+ /* */
+ /* where k == num_designs-1 */
+ /* */
+ /* I guess that's why it's written in this `compact' */
+ /* form. */
+ /* */
+ delta = top + num_points;
+ values = top;
+ for ( nn = 0; nn < num_points; nn++ )
+ {
+ FT_Long tmp = values[0];
+
+
+ for ( mm = 1; mm < blend->num_designs; mm++ )
+ tmp += FT_MulFix( *delta++, blend->weight_vector[mm] );
+
+ *values++ = tmp;
+ }
+
+ known_othersubr_result_cnt = num_points;
+ break;
+ }
+
+#ifdef CAN_HANDLE_NON_INTEGRAL_T1_OPERANDS
+
+ /* We cannot yet enable these since currently */
+ /* our T1 stack stores integers which lack the */
+ /* precision to express the values */
+
+ case 19:
+ /* <idx> 1 19 callothersubr */
+ /* => replace elements starting from index cvi( <idx> ) */
+ /* of BuildCharArray with WeightVector */
+ {
+ FT_Int idx;
+ PS_Blend blend = decoder->blend;
+
+
+ if ( arg_cnt != 1 || blend == NULL )
+ goto Unexpected_OtherSubr;
+
+ idx = top[0];
+
+ if ( idx < 0 ||
+ idx + blend->num_designs > decoder->face->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ memcpy( &decoder->buildchar[idx],
+ blend->weight_vector,
+ blend->num_designs *
+ sizeof( blend->weight_vector[ 0 ] ) );
+ }
+ break;
+
+ case 20:
+ /* <arg1> <arg2> 2 20 callothersubr pop */
+ /* ==> push <arg1> + <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] += top[1]; /* XXX (over|under)flow */
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 21:
+ /* <arg1> <arg2> 2 21 callothersubr pop */
+ /* ==> push <arg1> - <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] -= top[1]; /* XXX (over|under)flow */
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 22:
+ /* <arg1> <arg2> 2 22 callothersubr pop */
+ /* ==> push <arg1> * <arg2> onto T1 stack */
+ if ( arg_cnt != 2 )
+ goto Unexpected_OtherSubr;
+
+ top[0] *= top[1]; /* XXX (over|under)flow */
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+ case 23:
+ /* <arg1> <arg2> 2 23 callothersubr pop */
+ /* ==> push <arg1> / <arg2> onto T1 stack */
+ if ( arg_cnt != 2 || top[1] == 0 )
+ goto Unexpected_OtherSubr;
+
+ top[0] /= top[1]; /* XXX (over|under)flow */
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+#endif /* CAN_HANDLE_NON_INTEGRAL_T1_OPERANDS */
+
+ case 24:
+ /* <val> <idx> 2 24 callothersubr */
+ /* => set BuildCharArray[cvi( <idx> )] = <val> */
+ {
+ FT_Int idx;
+ PS_Blend blend = decoder->blend;
+
+ if ( arg_cnt != 2 || blend == NULL )
+ goto Unexpected_OtherSubr;
+
+ idx = top[1];
+
+ if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ decoder->buildchar[idx] = top[0];
+ }
+ break;
+
+ case 25:
+ /* <idx> 1 25 callothersubr pop */
+ /* => push BuildCharArray[cvi( idx )] */
+ /* onto T1 stack */
+ {
+ FT_Int idx;
+ PS_Blend blend = decoder->blend;
+
+ if ( arg_cnt != 1 || blend == NULL )
+ goto Unexpected_OtherSubr;
+
+ idx = top[0];
+
+ if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar )
+ goto Unexpected_OtherSubr;
+
+ top[0] = decoder->buildchar[idx];
+ }
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+#if 0
+ case 26:
+ /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */
+ /* leave mark on T1 stack */
+ /* <val> <idx> ==> set BuildCharArray[cvi( <idx> )] = <val> */
+ XXX who has left his mark on the (PostScript) stack ?;
+ break;
+#endif
+
+ case 27:
+ /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
+ /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
+ /* otherwise push <res2> */
+ if ( arg_cnt != 4 )
+ goto Unexpected_OtherSubr;
+
+ if ( top[2] > top[3] )
+ top[0] = top[1];
+
+ known_othersubr_result_cnt = 1;
+ break;
+
+#ifdef CAN_HANDLE_NON_INTEGRAL_T1_OPERANDS
+ case 28:
+ /* 0 28 callothersubr pop */
+ /* => push random value from interval [0, 1) onto stack */
+ if ( arg_cnt != 0 )
+ goto Unexpected_OtherSubr;
+
+ top[0] = FT_rand();
+ known_othersubr_result_cnt = 1;
+ break;
+#endif
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "unknown othersubr [%d %d], wish me luck!\n",
+ arg_cnt, subr_no ));
+ unknown_othersubr_result_cnt = arg_cnt;
+ break;
+
+ Unexpected_OtherSubr:
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invalid othersubr [%d %d]!\n", arg_cnt, subr_no ));
+ goto Syntax_Error;
+ }
+
+ top += known_othersubr_result_cnt;
+
+ decoder->top = top;
+ }
+ else /* general operator */
+ {
+ FT_Int num_args = t1_args_count[op];
+
+
+ FT_ASSERT( num_args >= 0 );
+
+ if ( top - decoder->stack < num_args )
+ goto Stack_Underflow;
+
+ /* XXX Operators usually take their operands from the */
+ /* bottom of the stack, i.e., the operands are */
+ /* decoder->stack[0], ..., decoder->stack[num_args - 1]; */
+ /* only div, callsubr, and callothersubr are different. */
+ /* In practice it doesn't matter (?). */
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ switch ( op )
+ {
+ case op_callsubr:
+ case op_div:
+ case op_callothersubr:
+ case op_pop:
+ case op_return:
+ break;
+
+ default:
+ if ( top - decoder->stack != num_args )
+ FT_TRACE0(( "t1_decoder_parse_charstrings: "
+ "too much operands on the stack "
+ "(seen %d, expected %d)\n",
+ top - decoder->stack, num_args ));
+ break;
+ }
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ top -= num_args;
+
+ switch ( op )
+ {
+ case op_endchar:
+ FT_TRACE4(( " endchar" ));
+
+ close_contour( builder );
+
+ /* close hints recording session */
+ if ( hinter )
+ {
+ if (hinter->close( hinter->hints, builder->current->n_points ))
+ goto Syntax_Error;
+
+ /* apply hints to the loaded glyph outline now */
+ hinter->apply( hinter->hints,
+ builder->current,
+ (PSH_Globals) builder->hints_globals,
+ decoder->hint_mode );
+ }
+
+ /* add current outline to the glyph slot */
+ FT_GlyphLoader_Add( builder->loader );
+
+ FT_TRACE4(( "\n" ));
+
+ /* the compiler should optimize away this empty loop but ... */
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+ if ( decoder->len_buildchar > 0 )
+ {
+ FT_UInt i;
+
+
+ FT_TRACE4(( "BuildCharArray = [ " ));
+
+ for ( i = 0; i < decoder->len_buildchar; ++i )
+ FT_TRACE4(( "%d ", decoder->buildchar[ i ] ));
+
+ FT_TRACE4(( "]\n" ));
+ }
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+ FT_TRACE4(( "\n" ));
+
+ /* return now! */
+ return PSaux_Err_Ok;
+
+ case op_hsbw:
+ FT_TRACE4(( " hsbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x += top[0];
+ builder->advance.x = top[1];
+ builder->advance.y = 0;
+
+ orig_x = builder->last.x = x = builder->pos_x + top[0];
+ orig_y = builder->last.y = y = builder->pos_y;
+
+ FT_UNUSED( orig_y );
+
+ /* the `metrics_only' indicates that we only want to compute */
+ /* the glyph's metrics (lsb + advance width), not load the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ return PSaux_Err_Ok;
+
+ break;
+
+ case op_seac:
+ /* return immediately after the processing */
+ return t1operator_seac( decoder, top[0], top[1], top[2],
+ (FT_Int)top[3], (FT_Int)top[4] );
+
+ case op_sbw:
+ FT_TRACE4(( " sbw" ));
+
+ builder->parse_state = T1_Parse_Have_Width;
+
+ builder->left_bearing.x += top[0];
+ builder->left_bearing.y += top[1];
+ builder->advance.x = top[2];
+ builder->advance.y = top[3];
+
+ builder->last.x = x = builder->pos_x + top[0];
+ builder->last.y = y = builder->pos_y + top[1];
+
+ /* the `metrics_only' indicates that we only want to compute */
+ /* the glyph's metrics (lsb + advance width), not load the */
+ /* rest of it; so exit immediately */
+ if ( builder->metrics_only )
+ return PSaux_Err_Ok;
+
+ break;
+
+ case op_closepath:
+ FT_TRACE4(( " closepath" ));
+
+ close_contour( builder );
+ if ( !( builder->parse_state == T1_Parse_Have_Path ||
+ builder->parse_state == T1_Parse_Have_Moveto ) )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Width;
+ break;
+
+ case op_hlineto:
+ FT_TRACE4(( " hlineto" ));
+
+ if ( start_point( builder, x, y ) )
+ goto Fail;
+
+ x += top[0];
+ goto Add_Line;
+
+ case op_hmoveto:
+ FT_TRACE4(( " hmoveto" ));
+
+ x += top[0];
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_hvcurveto:
+ FT_TRACE4(( " hvcurveto" ));
+
+ if ( start_point( builder, x, y ) ||
+ check_points( builder, 3 ) )
+ goto Fail;
+
+ x += top[0];
+ add_point( builder, x, y, 0 );
+ x += top[1];
+ y += top[2];
+ add_point( builder, x, y, 0 );
+ y += top[3];
+ add_point( builder, x, y, 1 );
+ break;
+
+ case op_rlineto:
+ FT_TRACE4(( " rlineto" ));
+
+ if ( start_point( builder, x, y ) )
+ goto Fail;
+
+ x += top[0];
+ y += top[1];
+
+ Add_Line:
+ if ( add_point1( builder, x, y ) )
+ goto Fail;
+ break;
+
+ case op_rmoveto:
+ FT_TRACE4(( " rmoveto" ));
+
+ x += top[0];
+ y += top[1];
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_rrcurveto:
+ FT_TRACE4(( " rcurveto" ));
+
+ if ( start_point( builder, x, y ) ||
+ check_points( builder, 3 ) )
+ goto Fail;
+
+ x += top[0];
+ y += top[1];
+ add_point( builder, x, y, 0 );
+
+ x += top[2];
+ y += top[3];
+ add_point( builder, x, y, 0 );
+
+ x += top[4];
+ y += top[5];
+ add_point( builder, x, y, 1 );
+ break;
+
+ case op_vhcurveto:
+ FT_TRACE4(( " vhcurveto" ));
+
+ if ( start_point( builder, x, y ) ||
+ check_points( builder, 3 ) )
+ goto Fail;
+
+ y += top[0];
+ add_point( builder, x, y, 0 );
+ x += top[1];
+ y += top[2];
+ add_point( builder, x, y, 0 );
+ x += top[3];
+ add_point( builder, x, y, 1 );
+ break;
+
+ case op_vlineto:
+ FT_TRACE4(( " vlineto" ));
+
+ if ( start_point( builder, x, y ) )
+ goto Fail;
+
+ y += top[0];
+ goto Add_Line;
+
+ case op_vmoveto:
+ FT_TRACE4(( " vmoveto" ));
+
+ y += top[0];
+ if ( !decoder->flex_state )
+ {
+ if ( builder->parse_state == T1_Parse_Start )
+ goto Syntax_Error;
+ builder->parse_state = T1_Parse_Have_Moveto;
+ }
+ break;
+
+ case op_div:
+ FT_TRACE4(( " div" ));
+
+ if ( top[1] )
+ {
+ *top = top[0] / top[1];
+ ++top;
+ }
+ else
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: division by 0\n" ));
+ goto Syntax_Error;
+ }
+ break;
+
+ case op_callsubr:
+ {
+ FT_Int idx;
+
+
+ FT_TRACE4(( " callsubr" ));
+
+ idx = (FT_Int)top[0];
+ if ( idx < 0 || idx >= (FT_Int)decoder->num_subrs )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invalid subrs index\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "too many nested subrs\n" ));
+ goto Syntax_Error;
+ }
+
+ zone->cursor = ip; /* save current instruction pointer */
+
+ zone++;
+
+ /* The Type 1 driver stores subroutines without the seed bytes. */
+ /* The CID driver stores subroutines with seed bytes. This */
+ /* case is taken care of when decoder->subrs_len == 0. */
+ zone->base = decoder->subrs[idx];
+
+ if ( decoder->subrs_len )
+ zone->limit = zone->base + decoder->subrs_len[idx];
+ else
+ {
+ /* We are using subroutines from a CID font. We must adjust */
+ /* for the seed bytes. */
+ zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
+ zone->limit = decoder->subrs[idx + 1];
+ }
+
+ zone->cursor = zone->base;
+
+ if ( !zone->base )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "invoking empty subrs!\n" ));
+ goto Syntax_Error;
+ }
+
+ decoder->zone = zone;
+ ip = zone->base;
+ limit = zone->limit;
+ break;
+ }
+
+ case op_pop:
+ FT_TRACE4(( " pop" ));
+
+ if ( known_othersubr_result_cnt > 0 )
+ {
+ known_othersubr_result_cnt--;
+ /* ignore, we pushed the operands ourselves */
+ break;
+ }
+
+ if ( unknown_othersubr_result_cnt == 0 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "no more operands for othersubr!\n" ));
+ goto Syntax_Error;
+ }
+
+ unknown_othersubr_result_cnt--;
+ top++; /* `push' the operand to callothersubr onto the stack */
+ break;
+
+ case op_return:
+ FT_TRACE4(( " return" ));
+
+ if ( zone <= decoder->zones )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: unexpected return\n" ));
+ goto Syntax_Error;
+ }
+
+ zone--;
+ ip = zone->cursor;
+ limit = zone->limit;
+ decoder->zone = zone;
+ break;
+
+ case op_dotsection:
+ FT_TRACE4(( " dotsection" ));
+
+ break;
+
+ case op_hstem:
+ FT_TRACE4(( " hstem" ));
+
+ /* record horizontal hint */
+ if ( hinter )
+ {
+ /* top[0] += builder->left_bearing.y; */
+ hinter->stem( hinter->hints, 1, top );
+ }
+
+ break;
+
+ case op_hstem3:
+ FT_TRACE4(( " hstem3" ));
+
+ /* record horizontal counter-controlled hints */
+ if ( hinter )
+ hinter->stem3( hinter->hints, 1, top );
+
+ break;
+
+ case op_vstem:
+ FT_TRACE4(( " vstem" ));
+
+ /* record vertical hint */
+ if ( hinter )
+ {
+ top[0] += orig_x;
+ hinter->stem( hinter->hints, 0, top );
+ }
+
+ break;
+
+ case op_vstem3:
+ FT_TRACE4(( " vstem3" ));
+
+ /* record vertical counter-controlled hints */
+ if ( hinter )
+ {
+ FT_Pos dx = orig_x;
+
+
+ top[0] += dx;
+ top[2] += dx;
+ top[4] += dx;
+ hinter->stem3( hinter->hints, 0, top );
+ }
+ break;
+
+ case op_setcurrentpoint:
+ FT_TRACE4(( " setcurrentpoint" ));
+
+ /* From the T1 specs, section 6.4: */
+ /* */
+ /* The setcurrentpoint command is used only in */
+ /* conjunction with results from OtherSubrs procedures. */
+
+ /* known_othersubr_result_cnt != 0 is already handled above */
+ if ( decoder->flex_state != 1 )
+ {
+ FT_ERROR(( "t1_decoder_parse_charstrings: " ));
+ FT_ERROR(( "unexpected `setcurrentpoint'\n" ));
+
+ goto Syntax_Error;
+ }
+ else
+ decoder->flex_state = 0;
+ break;
+
+ case op_unknown15:
+ FT_TRACE4(( " opcode_15" ));
+ /* nothing to do except to pop the two arguments */
+ break;
+
+ default:
+ FT_ERROR(( "t1_decoder_parse_charstrings: "
+ "unhandled opcode %d\n", op ));
+ goto Syntax_Error;
+ }
+
+ /* XXX Operators usually clear the operand stack; */
+ /* only div, callsubr, callothersubr, pop, and */
+ /* return are different. */
+ /* In practice it doesn't matter (?). */
+
+ decoder->top = top;
+
+ } /* general operator processing */
+
+ } /* while ip < limit */
+
+ FT_TRACE4(( "..end..\n\n" ));
+
+ Fail:
+ return error;
+
+ Syntax_Error:
+ return PSaux_Err_Syntax_Error;
+
+ Stack_Underflow:
+ return PSaux_Err_Stack_Underflow;
+ }
+
+
+ /* parse a single Type 1 glyph */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_parse_glyph( T1_Decoder decoder,
+ FT_UInt glyph )
+ {
+ return decoder->parse_callback( decoder, glyph );
+ }
+
+
+ /* initialize T1 decoder */
+ FT_LOCAL_DEF( FT_Error )
+ t1_decoder_init( T1_Decoder decoder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot slot,
+ FT_Byte** glyph_names,
+ PS_Blend blend,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ T1_Decoder_Callback parse_callback )
+ {
+ FT_MEM_ZERO( decoder, sizeof ( *decoder ) );
+
+ /* retrieve PSNames interface from list of current modules */
+ {
+ FT_Service_PsCMaps psnames = 0;
+
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ if ( !psnames )
+ {
+ FT_ERROR(( "t1_decoder_init: " ));
+ FT_ERROR(( "the `psnames' module is not available\n" ));
+ return PSaux_Err_Unimplemented_Feature;
+ }
+
+ decoder->psnames = psnames;
+ }
+
+ t1_builder_init( &decoder->builder, face, size, slot, hinting );
+
+ /* decoder->buildchar and decoder->len_buildchar have to be */
+ /* initialized by the caller since we cannot know the length */
+ /* of the BuildCharArray */
+
+ decoder->num_glyphs = (FT_UInt)face->num_glyphs;
+ decoder->glyph_names = glyph_names;
+ decoder->hint_mode = hint_mode;
+ decoder->blend = blend;
+ decoder->parse_callback = parse_callback;
+
+ decoder->funcs = t1_decoder_funcs;
+
+ return PSaux_Err_Ok;
+ }
+
+
+ /* finalize T1 decoder */
+ FT_LOCAL_DEF( void )
+ t1_decoder_done( T1_Decoder decoder )
+ {
+ t1_builder_done( &decoder->builder );
+ }
+
+
+/* END */
diff --git a/src/psaux/t1decode.h b/src/psaux/t1decode.h
new file mode 100644
index 0000000..00728db
--- /dev/null
+++ b/src/psaux/t1decode.h
@@ -0,0 +1,64 @@
+/***************************************************************************/
+/* */
+/* t1decode.h */
+/* */
+/* PostScript Type 1 decoding routines (specification). */
+/* */
+/* Copyright 2000-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1DECODE_H__
+#define __T1DECODE_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_CALLBACK_TABLE
+ const T1_Decoder_FuncsRec t1_decoder_funcs;
+
+
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_glyph( T1_Decoder decoder,
+ FT_UInt glyph_index );
+
+ FT_LOCAL( FT_Error )
+ t1_decoder_parse_charstrings( T1_Decoder decoder,
+ FT_Byte* base,
+ FT_UInt len );
+
+ FT_LOCAL( FT_Error )
+ t1_decoder_init( T1_Decoder decoder,
+ FT_Face face,
+ FT_Size size,
+ FT_GlyphSlot slot,
+ FT_Byte** glyph_names,
+ PS_Blend blend,
+ FT_Bool hinting,
+ FT_Render_Mode hint_mode,
+ T1_Decoder_Callback parse_glyph );
+
+ FT_LOCAL( void )
+ t1_decoder_done( T1_Decoder decoder );
+
+
+FT_END_HEADER
+
+#endif /* __T1DECODE_H__ */
+
+
+/* END */
diff --git a/src/pshinter/Jamfile b/src/pshinter/Jamfile
new file mode 100644
index 0000000..769dcc4
--- /dev/null
+++ b/src/pshinter/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/pshinter Jamfile
+#
+# Copyright 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) pshinter ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = pshrec pshglob pshalgo pshmod ;
+ }
+ else
+ {
+ _sources = pshinter ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/pshinter Jamfile
diff --git a/src/pshinter/module.mk b/src/pshinter/module.mk
new file mode 100644
index 0000000..cd171d0
--- /dev/null
+++ b/src/pshinter/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 PSHinter module definition
+#
+
+
+# Copyright 1996-2001, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += PSHINTER_MODULE
+
+define PSHINTER_MODULE
+$(OPEN_DRIVER)pshinter_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)pshinter $(ECHO_DRIVER_DESC)Postscript hinter module$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/pshinter/pshalgo.c b/src/pshinter/pshalgo.c
new file mode 100644
index 0000000..505d95c
--- /dev/null
+++ b/src/pshinter/pshalgo.c
@@ -0,0 +1,2291 @@
+/***************************************************************************/
+/* */
+/* pshalgo.c */
+/* */
+/* PostScript hinting algorithm (body). */
+/* */
+/* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used */
+/* modified and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include "pshalgo.h"
+
+#include "pshnterr.h"
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pshalgo2
+
+
+#ifdef DEBUG_HINTER
+ PSH_Hint_Table ps_debug_hint_table = 0;
+ PSH_HintFunc ps_debug_hint_func = 0;
+ PSH_Glyph ps_debug_glyph = 0;
+#endif
+
+
+#define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */
+ /* and similar glyphs */
+#define STRONGER /* slightly increase the contrast of smooth */
+ /* hinting */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BASIC HINTS RECORDINGS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* return true if two stem hints overlap */
+ static FT_Int
+ psh_hint_overlap( PSH_Hint hint1,
+ PSH_Hint hint2 )
+ {
+ return hint1->org_pos + hint1->org_len >= hint2->org_pos &&
+ hint2->org_pos + hint2->org_len >= hint1->org_pos;
+ }
+
+
+ /* destroy hints table */
+ static void
+ psh_hint_table_done( PSH_Hint_Table table,
+ FT_Memory memory )
+ {
+ FT_FREE( table->zones );
+ table->num_zones = 0;
+ table->zone = 0;
+
+ FT_FREE( table->sort );
+ FT_FREE( table->hints );
+ table->num_hints = 0;
+ table->max_hints = 0;
+ table->sort_global = 0;
+ }
+
+
+ /* deactivate all hints in a table */
+ static void
+ psh_hint_table_deactivate( PSH_Hint_Table table )
+ {
+ FT_UInt count = table->max_hints;
+ PSH_Hint hint = table->hints;
+
+
+ for ( ; count > 0; count--, hint++ )
+ {
+ psh_hint_deactivate( hint );
+ hint->order = -1;
+ }
+ }
+
+
+ /* internal function to record a new hint */
+ static void
+ psh_hint_table_record( PSH_Hint_Table table,
+ FT_UInt idx )
+ {
+ PSH_Hint hint = table->hints + idx;
+
+
+ if ( idx >= table->max_hints )
+ {
+ FT_ERROR(( "psh_hint_table_record: invalid hint index %d\n", idx ));
+ return;
+ }
+
+ /* ignore active hints */
+ if ( psh_hint_is_active( hint ) )
+ return;
+
+ psh_hint_activate( hint );
+
+ /* now scan the current active hint set to check */
+ /* whether `hint' overlaps with another hint */
+ {
+ PSH_Hint* sorted = table->sort_global;
+ FT_UInt count = table->num_hints;
+ PSH_Hint hint2;
+
+
+ hint->parent = 0;
+ for ( ; count > 0; count--, sorted++ )
+ {
+ hint2 = sorted[0];
+
+ if ( psh_hint_overlap( hint, hint2 ) )
+ {
+ hint->parent = hint2;
+ break;
+ }
+ }
+ }
+
+ if ( table->num_hints < table->max_hints )
+ table->sort_global[table->num_hints++] = hint;
+ else
+ FT_ERROR(( "psh_hint_table_record: too many sorted hints! BUG!\n" ));
+ }
+
+
+ static void
+ psh_hint_table_record_mask( PSH_Hint_Table table,
+ PS_Mask hint_mask )
+ {
+ FT_Int mask = 0, val = 0;
+ FT_Byte* cursor = hint_mask->bytes;
+ FT_UInt idx, limit;
+
+
+ limit = hint_mask->num_bits;
+
+ for ( idx = 0; idx < limit; idx++ )
+ {
+ if ( mask == 0 )
+ {
+ val = *cursor++;
+ mask = 0x80;
+ }
+
+ if ( val & mask )
+ psh_hint_table_record( table, idx );
+
+ mask >>= 1;
+ }
+ }
+
+
+ /* create hints table */
+ static FT_Error
+ psh_hint_table_init( PSH_Hint_Table table,
+ PS_Hint_Table hints,
+ PS_Mask_Table hint_masks,
+ PS_Mask_Table counter_masks,
+ FT_Memory memory )
+ {
+ FT_UInt count;
+ FT_Error error;
+
+ FT_UNUSED( counter_masks );
+
+
+ count = hints->num_hints;
+
+ /* allocate our tables */
+ if ( FT_NEW_ARRAY( table->sort, 2 * count ) ||
+ FT_NEW_ARRAY( table->hints, count ) ||
+ FT_NEW_ARRAY( table->zones, 2 * count + 1 ) )
+ goto Exit;
+
+ table->max_hints = count;
+ table->sort_global = table->sort + count;
+ table->num_hints = 0;
+ table->num_zones = 0;
+ table->zone = 0;
+
+ /* initialize the `table->hints' array */
+ {
+ PSH_Hint write = table->hints;
+ PS_Hint read = hints->hints;
+
+
+ for ( ; count > 0; count--, write++, read++ )
+ {
+ write->org_pos = read->pos;
+ write->org_len = read->len;
+ write->flags = read->flags;
+ }
+ }
+
+ /* we now need to determine the initial `parent' stems; first */
+ /* activate the hints that are given by the initial hint masks */
+ if ( hint_masks )
+ {
+ PS_Mask mask = hint_masks->masks;
+
+
+ count = hint_masks->num_masks;
+ table->hint_masks = hint_masks;
+
+ for ( ; count > 0; count--, mask++ )
+ psh_hint_table_record_mask( table, mask );
+ }
+
+ /* finally, do a linear parse in case some hints were left alone */
+ if ( table->num_hints != table->max_hints )
+ {
+ FT_UInt idx;
+
+
+ FT_ERROR(( "psh_hint_table_init: missing/incorrect hint masks!\n" ));
+
+ count = table->max_hints;
+ for ( idx = 0; idx < count; idx++ )
+ psh_hint_table_record( table, idx );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ psh_hint_table_activate_mask( PSH_Hint_Table table,
+ PS_Mask hint_mask )
+ {
+ FT_Int mask = 0, val = 0;
+ FT_Byte* cursor = hint_mask->bytes;
+ FT_UInt idx, limit, count;
+
+
+ limit = hint_mask->num_bits;
+ count = 0;
+
+ psh_hint_table_deactivate( table );
+
+ for ( idx = 0; idx < limit; idx++ )
+ {
+ if ( mask == 0 )
+ {
+ val = *cursor++;
+ mask = 0x80;
+ }
+
+ if ( val & mask )
+ {
+ PSH_Hint hint = &table->hints[idx];
+
+
+ if ( !psh_hint_is_active( hint ) )
+ {
+ FT_UInt count2;
+
+#if 0
+ PSH_Hint* sort = table->sort;
+ PSH_Hint hint2;
+
+
+ for ( count2 = count; count2 > 0; count2--, sort++ )
+ {
+ hint2 = sort[0];
+ if ( psh_hint_overlap( hint, hint2 ) )
+ FT_ERROR(( "psh_hint_table_activate_mask:"
+ " found overlapping hints\n" ))
+ }
+#else
+ count2 = 0;
+#endif
+
+ if ( count2 == 0 )
+ {
+ psh_hint_activate( hint );
+ if ( count < table->max_hints )
+ table->sort[count++] = hint;
+ else
+ FT_ERROR(( "psh_hint_tableactivate_mask:"
+ " too many active hints\n" ));
+ }
+ }
+ }
+
+ mask >>= 1;
+ }
+ table->num_hints = count;
+
+ /* now, sort the hints; they are guaranteed to not overlap */
+ /* so we can compare their "org_pos" field directly */
+ {
+ FT_Int i1, i2;
+ PSH_Hint hint1, hint2;
+ PSH_Hint* sort = table->sort;
+
+
+ /* a simple bubble sort will do, since in 99% of cases, the hints */
+ /* will be already sorted -- and the sort will be linear */
+ for ( i1 = 1; i1 < (FT_Int)count; i1++ )
+ {
+ hint1 = sort[i1];
+ for ( i2 = i1 - 1; i2 >= 0; i2-- )
+ {
+ hint2 = sort[i2];
+
+ if ( hint2->org_pos < hint1->org_pos )
+ break;
+
+ sort[i2 + 1] = hint2;
+ sort[i2] = hint1;
+ }
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** HINTS GRID-FITTING AND OPTIMIZATION *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#if 1
+ static FT_Pos
+ psh_dimension_quantize_len( PSH_Dimension dim,
+ FT_Pos len,
+ FT_Bool do_snapping )
+ {
+ if ( len <= 64 )
+ len = 64;
+ else
+ {
+ FT_Pos delta = len - dim->stdw.widths[0].cur;
+
+
+ if ( delta < 0 )
+ delta = -delta;
+
+ if ( delta < 40 )
+ {
+ len = dim->stdw.widths[0].cur;
+ if ( len < 48 )
+ len = 48;
+ }
+
+ if ( len < 3 * 64 )
+ {
+ delta = ( len & 63 );
+ len &= -64;
+
+ if ( delta < 10 )
+ len += delta;
+
+ else if ( delta < 32 )
+ len += 10;
+
+ else if ( delta < 54 )
+ len += 54;
+
+ else
+ len += delta;
+ }
+ else
+ len = FT_PIX_ROUND( len );
+ }
+
+ if ( do_snapping )
+ len = FT_PIX_ROUND( len );
+
+ return len;
+ }
+#endif /* 0 */
+
+
+#ifdef DEBUG_HINTER
+
+ static void
+ ps_simple_scale( PSH_Hint_Table table,
+ FT_Fixed scale,
+ FT_Fixed delta,
+ FT_Int dimension )
+ {
+ PSH_Hint hint;
+ FT_UInt count;
+
+
+ for ( count = 0; count < table->max_hints; count++ )
+ {
+ hint = table->hints + count;
+
+ hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta;
+ hint->cur_len = FT_MulFix( hint->org_len, scale );
+
+ if ( ps_debug_hint_func )
+ ps_debug_hint_func( hint, dimension );
+ }
+ }
+
+#endif /* DEBUG_HINTER */
+
+
+ static FT_Fixed
+ psh_hint_snap_stem_side_delta( FT_Fixed pos,
+ FT_Fixed len )
+ {
+ FT_Fixed delta1 = FT_PIX_ROUND( pos ) - pos;
+ FT_Fixed delta2 = FT_PIX_ROUND( pos + len ) - pos - len;
+
+
+ if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) )
+ return delta1;
+ else
+ return delta2;
+ }
+
+
+ static void
+ psh_hint_align( PSH_Hint hint,
+ PSH_Globals globals,
+ FT_Int dimension,
+ PSH_Glyph glyph )
+ {
+ PSH_Dimension dim = &globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Fixed delta = dim->scale_delta;
+
+
+ if ( !psh_hint_is_fitted( hint ) )
+ {
+ FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
+ FT_Pos len = FT_MulFix( hint->org_len, scale );
+
+ FT_Int do_snapping;
+ FT_Pos fit_len;
+ PSH_AlignmentRec align;
+
+
+ /* ignore stem alignments when requested through the hint flags */
+ if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
+ ( dimension == 1 && !glyph->do_vert_hints ) )
+ {
+ hint->cur_pos = pos;
+ hint->cur_len = len;
+
+ psh_hint_set_fitted( hint );
+ return;
+ }
+
+ /* perform stem snapping when requested - this is necessary
+ * for monochrome and LCD hinting modes only
+ */
+ do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) ||
+ ( dimension == 1 && glyph->do_vert_snapping );
+
+ hint->cur_len = fit_len = len;
+
+ /* check blue zones for horizontal stems */
+ align.align = PSH_BLUE_ALIGN_NONE;
+ align.align_bot = align.align_top = 0;
+
+ if ( dimension == 1 )
+ psh_blues_snap_stem( &globals->blues,
+ hint->org_pos + hint->org_len,
+ hint->org_pos,
+ &align );
+
+ switch ( align.align )
+ {
+ case PSH_BLUE_ALIGN_TOP:
+ /* the top of the stem is aligned against a blue zone */
+ hint->cur_pos = align.align_top - fit_len;
+ break;
+
+ case PSH_BLUE_ALIGN_BOT:
+ /* the bottom of the stem is aligned against a blue zone */
+ hint->cur_pos = align.align_bot;
+ break;
+
+ case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
+ /* both edges of the stem are aligned against blue zones */
+ hint->cur_pos = align.align_bot;
+ hint->cur_len = align.align_top - align.align_bot;
+ break;
+
+ default:
+ {
+ PSH_Hint parent = hint->parent;
+
+
+ if ( parent )
+ {
+ FT_Pos par_org_center, par_cur_center;
+ FT_Pos cur_org_center, cur_delta;
+
+
+ /* ensure that parent is already fitted */
+ if ( !psh_hint_is_fitted( parent ) )
+ psh_hint_align( parent, globals, dimension, glyph );
+
+ /* keep original relation between hints, this is, use the */
+ /* scaled distance between the centers of the hints to */
+ /* compute the new position */
+ par_org_center = parent->org_pos + ( parent->org_len >> 1 );
+ par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 );
+ cur_org_center = hint->org_pos + ( hint->org_len >> 1 );
+
+ cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
+ pos = par_cur_center + cur_delta - ( len >> 1 );
+ }
+
+ hint->cur_pos = pos;
+ hint->cur_len = fit_len;
+
+ /* Stem adjustment tries to snap stem widths to standard
+ * ones. This is important to prevent unpleasant rounding
+ * artefacts.
+ */
+ if ( glyph->do_stem_adjust )
+ {
+ if ( len <= 64 )
+ {
+ /* the stem is less than one pixel; we will center it
+ * around the nearest pixel center
+ */
+ if ( len >= 32 )
+ {
+ /* This is a special case where we also widen the stem
+ * and align it to the pixel grid.
+ *
+ * stem_center = pos + (len/2)
+ * nearest_pixel_center = FT_ROUND(stem_center-32)+32
+ * new_pos = nearest_pixel_center-32
+ * = FT_ROUND(stem_center-32)
+ * = FT_FLOOR(stem_center-32+32)
+ * = FT_FLOOR(stem_center)
+ * new_len = 64
+ */
+ pos = FT_PIX_FLOOR( pos + ( len >> 1 ) );
+ len = 64;
+ }
+ else if ( len > 0 )
+ {
+ /* This is a very small stem; we simply align it to the
+ * pixel grid, trying to find the minimal displacement.
+ *
+ * left = pos
+ * right = pos + len
+ * left_nearest_edge = ROUND(pos)
+ * right_nearest_edge = ROUND(right)
+ *
+ * if ( ABS(left_nearest_edge - left) <=
+ * ABS(right_nearest_edge - right) )
+ * new_pos = left
+ * else
+ * new_pos = right
+ */
+ FT_Pos left_nearest = FT_PIX_ROUND( pos );
+ FT_Pos right_nearest = FT_PIX_ROUND( pos + len );
+ FT_Pos left_disp = left_nearest - pos;
+ FT_Pos right_disp = right_nearest - ( pos + len );
+
+
+ if ( left_disp < 0 )
+ left_disp = -left_disp;
+ if ( right_disp < 0 )
+ right_disp = -right_disp;
+ if ( left_disp <= right_disp )
+ pos = left_nearest;
+ else
+ pos = right_nearest;
+ }
+ else
+ {
+ /* this is a ghost stem; we simply round it */
+ pos = FT_PIX_ROUND( pos );
+ }
+ }
+ else
+ {
+ len = psh_dimension_quantize_len( dim, len, 0 );
+ }
+ }
+
+ /* now that we have a good hinted stem width, try to position */
+ /* the stem along a pixel grid integer coordinate */
+ hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len );
+ hint->cur_len = len;
+ }
+ }
+
+ if ( do_snapping )
+ {
+ pos = hint->cur_pos;
+ len = hint->cur_len;
+
+ if ( len < 64 )
+ len = 64;
+ else
+ len = FT_PIX_ROUND( len );
+
+ switch ( align.align )
+ {
+ case PSH_BLUE_ALIGN_TOP:
+ hint->cur_pos = align.align_top - len;
+ hint->cur_len = len;
+ break;
+
+ case PSH_BLUE_ALIGN_BOT:
+ hint->cur_len = len;
+ break;
+
+ case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP:
+ /* don't touch */
+ break;
+
+
+ default:
+ hint->cur_len = len;
+ if ( len & 64 )
+ pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32;
+ else
+ pos = FT_PIX_ROUND( pos + ( len >> 1 ) );
+
+ hint->cur_pos = pos - ( len >> 1 );
+ hint->cur_len = len;
+ }
+ }
+
+ psh_hint_set_fitted( hint );
+
+#ifdef DEBUG_HINTER
+ if ( ps_debug_hint_func )
+ ps_debug_hint_func( hint, dimension );
+#endif
+ }
+ }
+
+
+#if 0 /* not used for now, experimental */
+
+ /*
+ * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT)
+ * of stems
+ */
+ static void
+ psh_hint_align_light( PSH_Hint hint,
+ PSH_Globals globals,
+ FT_Int dimension,
+ PSH_Glyph glyph )
+ {
+ PSH_Dimension dim = &globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Fixed delta = dim->scale_delta;
+
+
+ if ( !psh_hint_is_fitted( hint ) )
+ {
+ FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta;
+ FT_Pos len = FT_MulFix( hint->org_len, scale );
+
+ FT_Pos fit_len;
+
+ PSH_AlignmentRec align;
+
+
+ /* ignore stem alignments when requested through the hint flags */
+ if ( ( dimension == 0 && !glyph->do_horz_hints ) ||
+ ( dimension == 1 && !glyph->do_vert_hints ) )
+ {
+ hint->cur_pos = pos;
+ hint->cur_len = len;
+
+ psh_hint_set_fitted( hint );
+ return;
+ }
+
+ fit_len = len;
+
+ hint->cur_len = fit_len;
+
+ /* check blue zones for horizontal stems */
+ align.align = PSH_BLUE_ALIGN_NONE;
+ align.align_bot = align.align_top = 0;
+
+ if ( dimension == 1 )
+ psh_blues_snap_stem( &globals->blues,
+ hint->org_pos + hint->org_len,
+ hint->org_pos,
+ &align );
+
+ switch ( align.align )
+ {
+ case PSH_BLUE_ALIGN_TOP:
+ /* the top of the stem is aligned against a blue zone */
+ hint->cur_pos = align.align_top - fit_len;
+ break;
+
+ case PSH_BLUE_ALIGN_BOT:
+ /* the bottom of the stem is aligned against a blue zone */
+ hint->cur_pos = align.align_bot;
+ break;
+
+ case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT:
+ /* both edges of the stem are aligned against blue zones */
+ hint->cur_pos = align.align_bot;
+ hint->cur_len = align.align_top - align.align_bot;
+ break;
+
+ default:
+ {
+ PSH_Hint parent = hint->parent;
+
+
+ if ( parent )
+ {
+ FT_Pos par_org_center, par_cur_center;
+ FT_Pos cur_org_center, cur_delta;
+
+
+ /* ensure that parent is already fitted */
+ if ( !psh_hint_is_fitted( parent ) )
+ psh_hint_align_light( parent, globals, dimension, glyph );
+
+ par_org_center = parent->org_pos + ( parent->org_len / 2 );
+ par_cur_center = parent->cur_pos + ( parent->cur_len / 2 );
+ cur_org_center = hint->org_pos + ( hint->org_len / 2 );
+
+ cur_delta = FT_MulFix( cur_org_center - par_org_center, scale );
+ pos = par_cur_center + cur_delta - ( len >> 1 );
+ }
+
+ /* Stems less than one pixel wide are easy -- we want to
+ * make them as dark as possible, so they must fall within
+ * one pixel. If the stem is split between two pixels
+ * then snap the edge that is nearer to the pixel boundary
+ * to the pixel boundary.
+ */
+ if ( len <= 64 )
+ {
+ if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 )
+ pos += psh_hint_snap_stem_side_delta ( pos, len );
+ }
+
+ /* Position stems other to minimize the amount of mid-grays.
+ * There are, in general, two positions that do this,
+ * illustrated as A) and B) below.
+ *
+ * + + + +
+ *
+ * A) |--------------------------------|
+ * B) |--------------------------------|
+ * C) |--------------------------------|
+ *
+ * Position A) (split the excess stem equally) should be better
+ * for stems of width N + f where f < 0.5.
+ *
+ * Position B) (split the deficiency equally) should be better
+ * for stems of width N + f where f > 0.5.
+ *
+ * It turns out though that minimizing the total number of lit
+ * pixels is also important, so position C), with one edge
+ * aligned with a pixel boundary is actually preferable
+ * to A). There are also more possibile positions for C) than
+ * for A) or B), so it involves less distortion of the overall
+ * character shape.
+ */
+ else /* len > 64 */
+ {
+ FT_Fixed frac_len = len & 63;
+ FT_Fixed center = pos + ( len >> 1 );
+ FT_Fixed delta_a, delta_b;
+
+
+ if ( ( len / 64 ) & 1 )
+ {
+ delta_a = FT_PIX_FLOOR( center ) + 32 - center;
+ delta_b = FT_PIX_ROUND( center ) - center;
+ }
+ else
+ {
+ delta_a = FT_PIX_ROUND( center ) - center;
+ delta_b = FT_PIX_FLOOR( center ) + 32 - center;
+ }
+
+ /* We choose between B) and C) above based on the amount
+ * of fractinal stem width; for small amounts, choose
+ * C) always, for large amounts, B) always, and inbetween,
+ * pick whichever one involves less stem movement.
+ */
+ if ( frac_len < 32 )
+ {
+ pos += psh_hint_snap_stem_side_delta ( pos, len );
+ }
+ else if ( frac_len < 48 )
+ {
+ FT_Fixed side_delta = psh_hint_snap_stem_side_delta ( pos,
+ len );
+
+ if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) )
+ pos += side_delta;
+ else
+ pos += delta_b;
+ }
+ else
+ {
+ pos += delta_b;
+ }
+ }
+
+ hint->cur_pos = pos;
+ }
+ } /* switch */
+
+ psh_hint_set_fitted( hint );
+
+#ifdef DEBUG_HINTER
+ if ( ps_debug_hint_func )
+ ps_debug_hint_func( hint, dimension );
+#endif
+ }
+ }
+
+#endif /* 0 */
+
+
+ static void
+ psh_hint_table_align_hints( PSH_Hint_Table table,
+ PSH_Globals globals,
+ FT_Int dimension,
+ PSH_Glyph glyph )
+ {
+ PSH_Hint hint;
+ FT_UInt count;
+
+#ifdef DEBUG_HINTER
+
+ PSH_Dimension dim = &globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Fixed delta = dim->scale_delta;
+
+
+ if ( ps_debug_no_vert_hints && dimension == 0 )
+ {
+ ps_simple_scale( table, scale, delta, dimension );
+ return;
+ }
+
+ if ( ps_debug_no_horz_hints && dimension == 1 )
+ {
+ ps_simple_scale( table, scale, delta, dimension );
+ return;
+ }
+
+#endif /* DEBUG_HINTER*/
+
+ hint = table->hints;
+ count = table->max_hints;
+
+ for ( ; count > 0; count--, hint++ )
+ psh_hint_align( hint, globals, dimension, glyph );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** POINTS INTERPOLATION ROUTINES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#define PSH_ZONE_MIN -3200000L
+#define PSH_ZONE_MAX +3200000L
+
+#define xxDEBUG_ZONES
+
+
+#ifdef DEBUG_ZONES
+
+#include <stdio.h>
+
+ static void
+ psh_print_zone( PSH_Zone zone )
+ {
+ printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n",
+ zone->scale / 65536.0,
+ zone->delta / 64.0,
+ zone->min,
+ zone->max );
+ }
+
+#else
+
+#define psh_print_zone( x ) do { } while ( 0 )
+
+#endif /* DEBUG_ZONES */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** HINTER GLYPH MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#if 1
+
+#define psh_corner_is_flat ft_corner_is_flat
+#define psh_corner_orientation ft_corner_orientation
+
+#else
+
+ FT_LOCAL_DEF( FT_Int )
+ psh_corner_is_flat( FT_Pos x_in,
+ FT_Pos y_in,
+ FT_Pos x_out,
+ FT_Pos y_out )
+ {
+ FT_Pos ax = x_in;
+ FT_Pos ay = y_in;
+
+ FT_Pos d_in, d_out, d_corner;
+
+
+ if ( ax < 0 )
+ ax = -ax;
+ if ( ay < 0 )
+ ay = -ay;
+ d_in = ax + ay;
+
+ ax = x_out;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = y_out;
+ if ( ay < 0 )
+ ay = -ay;
+ d_out = ax + ay;
+
+ ax = x_out + x_in;
+ if ( ax < 0 )
+ ax = -ax;
+ ay = y_out + y_in;
+ if ( ay < 0 )
+ ay = -ay;
+ d_corner = ax + ay;
+
+ return ( d_in + d_out - d_corner ) < ( d_corner >> 4 );
+ }
+
+ static FT_Int
+ psh_corner_orientation( FT_Pos in_x,
+ FT_Pos in_y,
+ FT_Pos out_x,
+ FT_Pos out_y )
+ {
+ FT_Int result;
+
+
+ /* deal with the trivial cases quickly */
+ if ( in_y == 0 )
+ {
+ if ( in_x >= 0 )
+ result = out_y;
+ else
+ result = -out_y;
+ }
+ else if ( in_x == 0 )
+ {
+ if ( in_y >= 0 )
+ result = -out_x;
+ else
+ result = out_x;
+ }
+ else if ( out_y == 0 )
+ {
+ if ( out_x >= 0 )
+ result = in_y;
+ else
+ result = -in_y;
+ }
+ else if ( out_x == 0 )
+ {
+ if ( out_y >= 0 )
+ result = -in_x;
+ else
+ result = in_x;
+ }
+ else /* general case */
+ {
+ long long delta = (long long)in_x * out_y - (long long)in_y * out_x;
+
+ if ( delta == 0 )
+ result = 0;
+ else
+ result = 1 - 2 * ( delta < 0 );
+ }
+
+ return result;
+ }
+
+#endif /* !1 */
+
+
+#ifdef COMPUTE_INFLEXS
+
+ /* compute all inflex points in a given glyph */
+ static void
+ psh_glyph_compute_inflections( PSH_Glyph glyph )
+ {
+ FT_UInt n;
+
+
+ for ( n = 0; n < glyph->num_contours; n++ )
+ {
+ PSH_Point first, start, end, before, after;
+ FT_Pos in_x, in_y, out_x, out_y;
+ FT_Int orient_prev, orient_cur;
+ FT_Int finished = 0;
+
+
+ /* we need at least 4 points to create an inflection point */
+ if ( glyph->contours[n].count < 4 )
+ continue;
+
+ /* compute first segment in contour */
+ first = glyph->contours[n].start;
+
+ start = end = first;
+ do
+ {
+ end = end->next;
+ if ( end == first )
+ goto Skip;
+
+ in_x = end->org_u - start->org_u;
+ in_y = end->org_v - start->org_v;
+
+ } while ( in_x == 0 && in_y == 0 );
+
+ /* extend the segment start whenever possible */
+ before = start;
+ do
+ {
+ do
+ {
+ start = before;
+ before = before->prev;
+ if ( before == first )
+ goto Skip;
+
+ out_x = start->org_u - before->org_u;
+ out_y = start->org_v - before->org_v;
+
+ } while ( out_x == 0 && out_y == 0 );
+
+ orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y );
+
+ } while ( orient_prev == 0 );
+
+ first = start;
+ in_x = out_x;
+ in_y = out_y;
+
+ /* now, process all segments in the contour */
+ do
+ {
+ /* first, extend current segment's end whenever possible */
+ after = end;
+ do
+ {
+ do
+ {
+ end = after;
+ after = after->next;
+ if ( after == first )
+ finished = 1;
+
+ out_x = after->org_u - end->org_u;
+ out_y = after->org_v - end->org_v;
+
+ } while ( out_x == 0 && out_y == 0 );
+
+ orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y );
+
+ } while ( orient_cur == 0 );
+
+ if ( ( orient_cur ^ orient_prev ) < 0 )
+ {
+ do
+ {
+ psh_point_set_inflex( start );
+ start = start->next;
+ }
+ while ( start != end );
+
+ psh_point_set_inflex( start );
+ }
+
+ start = end;
+ end = after;
+ orient_prev = orient_cur;
+ in_x = out_x;
+ in_y = out_y;
+
+ } while ( !finished );
+
+ Skip:
+ ;
+ }
+ }
+
+#endif /* COMPUTE_INFLEXS */
+
+
+ static void
+ psh_glyph_done( PSH_Glyph glyph )
+ {
+ FT_Memory memory = glyph->memory;
+
+
+ psh_hint_table_done( &glyph->hint_tables[1], memory );
+ psh_hint_table_done( &glyph->hint_tables[0], memory );
+
+ FT_FREE( glyph->points );
+ FT_FREE( glyph->contours );
+
+ glyph->num_points = 0;
+ glyph->num_contours = 0;
+
+ glyph->memory = 0;
+ }
+
+
+ static int
+ psh_compute_dir( FT_Pos dx,
+ FT_Pos dy )
+ {
+ FT_Pos ax, ay;
+ int result = PSH_DIR_NONE;
+
+
+ ax = ( dx >= 0 ) ? dx : -dx;
+ ay = ( dy >= 0 ) ? dy : -dy;
+
+ if ( ay * 12 < ax )
+ {
+ /* |dy| <<< |dx| means a near-horizontal segment */
+ result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT;
+ }
+ else if ( ax * 12 < ay )
+ {
+ /* |dx| <<< |dy| means a near-vertical segment */
+ result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN;
+ }
+
+ return result;
+ }
+
+
+ /* load outline point coordinates into hinter glyph */
+ static void
+ psh_glyph_load_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+ FT_Vector* vec = glyph->outline->points;
+ PSH_Point point = glyph->points;
+ FT_UInt count = glyph->num_points;
+
+
+ for ( ; count > 0; count--, point++, vec++ )
+ {
+ point->flags2 = 0;
+ point->hint = NULL;
+ if ( dimension == 0 )
+ {
+ point->org_u = vec->x;
+ point->org_v = vec->y;
+ }
+ else
+ {
+ point->org_u = vec->y;
+ point->org_v = vec->x;
+ }
+
+#ifdef DEBUG_HINTER
+ point->org_x = vec->x;
+ point->org_y = vec->y;
+#endif
+
+ }
+ }
+
+
+ /* save hinted point coordinates back to outline */
+ static void
+ psh_glyph_save_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+ FT_UInt n;
+ PSH_Point point = glyph->points;
+ FT_Vector* vec = glyph->outline->points;
+ char* tags = glyph->outline->tags;
+
+
+ for ( n = 0; n < glyph->num_points; n++ )
+ {
+ if ( dimension == 0 )
+ vec[n].x = point->cur_u;
+ else
+ vec[n].y = point->cur_u;
+
+ if ( psh_point_is_strong( point ) )
+ tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 );
+
+#ifdef DEBUG_HINTER
+
+ if ( dimension == 0 )
+ {
+ point->cur_x = point->cur_u;
+ point->flags_x = point->flags2 | point->flags;
+ }
+ else
+ {
+ point->cur_y = point->cur_u;
+ point->flags_y = point->flags2 | point->flags;
+ }
+
+#endif
+
+ point++;
+ }
+ }
+
+
+ static FT_Error
+ psh_glyph_init( PSH_Glyph glyph,
+ FT_Outline* outline,
+ PS_Hints ps_hints,
+ PSH_Globals globals )
+ {
+ FT_Error error;
+ FT_Memory memory;
+
+
+ /* clear all fields */
+ FT_MEM_ZERO( glyph, sizeof ( *glyph ) );
+
+ memory = glyph->memory = globals->memory;
+
+ /* allocate and setup points + contours arrays */
+ if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) ||
+ FT_NEW_ARRAY( glyph->contours, outline->n_contours ) )
+ goto Exit;
+
+ glyph->num_points = outline->n_points;
+ glyph->num_contours = outline->n_contours;
+
+ {
+ FT_UInt first = 0, next, n;
+ PSH_Point points = glyph->points;
+ PSH_Contour contour = glyph->contours;
+
+
+ for ( n = 0; n < glyph->num_contours; n++ )
+ {
+ FT_Int count;
+ PSH_Point point;
+
+
+ next = outline->contours[n] + 1;
+ count = next - first;
+
+ contour->start = points + first;
+ contour->count = (FT_UInt)count;
+
+ if ( count > 0 )
+ {
+ point = points + first;
+
+ point->prev = points + next - 1;
+ point->contour = contour;
+
+ for ( ; count > 1; count-- )
+ {
+ point[0].next = point + 1;
+ point[1].prev = point;
+ point++;
+ point->contour = contour;
+ }
+ point->next = points + first;
+ }
+
+ contour++;
+ first = next;
+ }
+ }
+
+ {
+ PSH_Point points = glyph->points;
+ PSH_Point point = points;
+ FT_Vector* vec = outline->points;
+ FT_UInt n;
+
+
+ for ( n = 0; n < glyph->num_points; n++, point++ )
+ {
+ FT_Int n_prev = (FT_Int)( point->prev - points );
+ FT_Int n_next = (FT_Int)( point->next - points );
+ FT_Pos dxi, dyi, dxo, dyo;
+
+
+ if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) )
+ point->flags = PSH_POINT_OFF;
+
+ dxi = vec[n].x - vec[n_prev].x;
+ dyi = vec[n].y - vec[n_prev].y;
+
+ point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi );
+
+ dxo = vec[n_next].x - vec[n].x;
+ dyo = vec[n_next].y - vec[n].y;
+
+ point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo );
+
+ /* detect smooth points */
+ if ( point->flags & PSH_POINT_OFF )
+ point->flags |= PSH_POINT_SMOOTH;
+
+ else if ( point->dir_in == point->dir_out )
+ {
+ if ( point->dir_out != PSH_DIR_NONE ||
+ psh_corner_is_flat( dxi, dyi, dxo, dyo ) )
+ point->flags |= PSH_POINT_SMOOTH;
+ }
+ }
+ }
+
+ glyph->outline = outline;
+ glyph->globals = globals;
+
+#ifdef COMPUTE_INFLEXS
+ psh_glyph_load_points( glyph, 0 );
+ psh_glyph_compute_inflections( glyph );
+#endif /* COMPUTE_INFLEXS */
+
+ /* now deal with hints tables */
+ error = psh_hint_table_init( &glyph->hint_tables [0],
+ &ps_hints->dimension[0].hints,
+ &ps_hints->dimension[0].masks,
+ &ps_hints->dimension[0].counters,
+ memory );
+ if ( error )
+ goto Exit;
+
+ error = psh_hint_table_init( &glyph->hint_tables [1],
+ &ps_hints->dimension[1].hints,
+ &ps_hints->dimension[1].masks,
+ &ps_hints->dimension[1].counters,
+ memory );
+ if ( error )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /* compute all extrema in a glyph for a given dimension */
+ static void
+ psh_glyph_compute_extrema( PSH_Glyph glyph )
+ {
+ FT_UInt n;
+
+
+ /* first of all, compute all local extrema */
+ for ( n = 0; n < glyph->num_contours; n++ )
+ {
+ PSH_Point first = glyph->contours[n].start;
+ PSH_Point point, before, after;
+
+
+ if ( glyph->contours[n].count == 0 )
+ continue;
+
+ point = first;
+ before = point;
+ after = point;
+
+ do
+ {
+ before = before->prev;
+ if ( before == first )
+ goto Skip;
+
+ } while ( before->org_u == point->org_u );
+
+ first = point = before->next;
+
+ for (;;)
+ {
+ after = point;
+ do
+ {
+ after = after->next;
+ if ( after == first )
+ goto Next;
+
+ } while ( after->org_u == point->org_u );
+
+ if ( before->org_u < point->org_u )
+ {
+ if ( after->org_u < point->org_u )
+ {
+ /* local maximum */
+ goto Extremum;
+ }
+ }
+ else /* before->org_u > point->org_u */
+ {
+ if ( after->org_u > point->org_u )
+ {
+ /* local minimum */
+ Extremum:
+ do
+ {
+ psh_point_set_extremum( point );
+ point = point->next;
+
+ } while ( point != after );
+ }
+ }
+
+ before = after->prev;
+ point = after;
+
+ } /* for */
+
+ Next:
+ ;
+ }
+
+ /* for each extremum, determine its direction along the */
+ /* orthogonal axis */
+ for ( n = 0; n < glyph->num_points; n++ )
+ {
+ PSH_Point point, before, after;
+
+
+ point = &glyph->points[n];
+ before = point;
+ after = point;
+
+ if ( psh_point_is_extremum( point ) )
+ {
+ do
+ {
+ before = before->prev;
+ if ( before == point )
+ goto Skip;
+
+ } while ( before->org_v == point->org_v );
+
+ do
+ {
+ after = after->next;
+ if ( after == point )
+ goto Skip;
+
+ } while ( after->org_v == point->org_v );
+ }
+
+ if ( before->org_v < point->org_v &&
+ after->org_v > point->org_v )
+ {
+ psh_point_set_positive( point );
+ }
+ else if ( before->org_v > point->org_v &&
+ after->org_v < point->org_v )
+ {
+ psh_point_set_negative( point );
+ }
+
+ Skip:
+ ;
+ }
+ }
+
+
+ /* major_dir is the direction for points on the bottom/left of the stem; */
+ /* Points on the top/right of the stem will have a direction of */
+ /* -major_dir. */
+
+ static void
+ psh_hint_table_find_strong_points( PSH_Hint_Table table,
+ PSH_Point point,
+ FT_UInt count,
+ FT_Int threshold,
+ FT_Int major_dir )
+ {
+ PSH_Hint* sort = table->sort;
+ FT_UInt num_hints = table->num_hints;
+
+
+ for ( ; count > 0; count--, point++ )
+ {
+ FT_Int point_dir = 0;
+ FT_Pos org_u = point->org_u;
+
+
+ if ( psh_point_is_strong( point ) )
+ continue;
+
+ if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) )
+ point_dir = point->dir_in;
+
+ else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) )
+ point_dir = point->dir_out;
+
+ if ( point_dir )
+ {
+ if ( point_dir == major_dir )
+ {
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < num_hints; nn++ )
+ {
+ PSH_Hint hint = sort[nn];
+ FT_Pos d = org_u - hint->org_pos;
+
+
+ if ( d < threshold && -d < threshold )
+ {
+ psh_point_set_strong( point );
+ point->flags2 |= PSH_POINT_EDGE_MIN;
+ point->hint = hint;
+ break;
+ }
+ }
+ }
+ else if ( point_dir == -major_dir )
+ {
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < num_hints; nn++ )
+ {
+ PSH_Hint hint = sort[nn];
+ FT_Pos d = org_u - hint->org_pos - hint->org_len;
+
+
+ if ( d < threshold && -d < threshold )
+ {
+ psh_point_set_strong( point );
+ point->flags2 |= PSH_POINT_EDGE_MAX;
+ point->hint = hint;
+ break;
+ }
+ }
+ }
+ }
+
+#if 1
+ else if ( psh_point_is_extremum( point ) )
+ {
+ /* treat extrema as special cases for stem edge alignment */
+ FT_UInt nn, min_flag, max_flag;
+
+
+ if ( major_dir == PSH_DIR_HORIZONTAL )
+ {
+ min_flag = PSH_POINT_POSITIVE;
+ max_flag = PSH_POINT_NEGATIVE;
+ }
+ else
+ {
+ min_flag = PSH_POINT_NEGATIVE;
+ max_flag = PSH_POINT_POSITIVE;
+ }
+
+ if ( point->flags2 & min_flag )
+ {
+ for ( nn = 0; nn < num_hints; nn++ )
+ {
+ PSH_Hint hint = sort[nn];
+ FT_Pos d = org_u - hint->org_pos;
+
+
+ if ( d < threshold && -d < threshold )
+ {
+ point->flags2 |= PSH_POINT_EDGE_MIN;
+ point->hint = hint;
+ psh_point_set_strong( point );
+ break;
+ }
+ }
+ }
+ else if ( point->flags2 & max_flag )
+ {
+ for ( nn = 0; nn < num_hints; nn++ )
+ {
+ PSH_Hint hint = sort[nn];
+ FT_Pos d = org_u - hint->org_pos - hint->org_len;
+
+
+ if ( d < threshold && -d < threshold )
+ {
+ point->flags2 |= PSH_POINT_EDGE_MAX;
+ point->hint = hint;
+ psh_point_set_strong( point );
+ break;
+ }
+ }
+ }
+
+ if ( point->hint == NULL )
+ {
+ for ( nn = 0; nn < num_hints; nn++ )
+ {
+ PSH_Hint hint = sort[nn];
+
+
+ if ( org_u >= hint->org_pos &&
+ org_u <= hint->org_pos + hint->org_len )
+ {
+ point->hint = hint;
+ break;
+ }
+ }
+ }
+ }
+
+#endif /* 1 */
+ }
+ }
+
+
+ /* the accepted shift for strong points in fractional pixels */
+#define PSH_STRONG_THRESHOLD 32
+
+ /* the maximum shift value in font units */
+#define PSH_STRONG_THRESHOLD_MAXIMUM 30
+
+
+ /* find strong points in a glyph */
+ static void
+ psh_glyph_find_strong_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+ /* a point is `strong' if it is located on a stem edge and */
+ /* has an `in' or `out' tangent parallel to the hint's direction */
+
+ PSH_Hint_Table table = &glyph->hint_tables[dimension];
+ PS_Mask mask = table->hint_masks->masks;
+ FT_UInt num_masks = table->hint_masks->num_masks;
+ FT_UInt first = 0;
+ FT_Int major_dir = dimension == 0 ? PSH_DIR_VERTICAL
+ : PSH_DIR_HORIZONTAL;
+ PSH_Dimension dim = &glyph->globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Int threshold;
+
+
+ threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale );
+ if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM )
+ threshold = PSH_STRONG_THRESHOLD_MAXIMUM;
+
+ /* process secondary hints to `selected' points */
+ if ( num_masks > 1 && glyph->num_points > 0 )
+ {
+ first = mask->end_point;
+ mask++;
+ for ( ; num_masks > 1; num_masks--, mask++ )
+ {
+ FT_UInt next;
+ FT_Int count;
+
+
+ next = mask->end_point;
+ count = next - first;
+ if ( count > 0 )
+ {
+ PSH_Point point = glyph->points + first;
+
+
+ psh_hint_table_activate_mask( table, mask );
+
+ psh_hint_table_find_strong_points( table, point, count,
+ threshold, major_dir );
+ }
+ first = next;
+ }
+ }
+
+ /* process primary hints for all points */
+ if ( num_masks == 1 )
+ {
+ FT_UInt count = glyph->num_points;
+ PSH_Point point = glyph->points;
+
+
+ psh_hint_table_activate_mask( table, table->hint_masks->masks );
+
+ psh_hint_table_find_strong_points( table, point, count,
+ threshold, major_dir );
+ }
+
+ /* now, certain points may have been attached to a hint and */
+ /* not marked as strong; update their flags then */
+ {
+ FT_UInt count = glyph->num_points;
+ PSH_Point point = glyph->points;
+
+
+ for ( ; count > 0; count--, point++ )
+ if ( point->hint && !psh_point_is_strong( point ) )
+ psh_point_set_strong( point );
+ }
+ }
+
+
+ /* find points in a glyph which are in a blue zone and have `in' or */
+ /* `out' tangents parallel to the horizontal axis */
+ static void
+ psh_glyph_find_blue_points( PSH_Blues blues,
+ PSH_Glyph glyph )
+ {
+ PSH_Blue_Table table;
+ PSH_Blue_Zone zone;
+ FT_UInt glyph_count = glyph->num_points;
+ FT_UInt blue_count;
+ PSH_Point point = glyph->points;
+
+
+ for ( ; glyph_count > 0; glyph_count--, point++ )
+ {
+ FT_Pos y;
+
+
+ /* check tangents */
+ if ( !PSH_DIR_COMPARE( point->dir_in, PSH_DIR_HORIZONTAL ) &&
+ !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) )
+ continue;
+
+ /* skip strong points */
+ if ( psh_point_is_strong( point ) )
+ continue;
+
+ y = point->org_u;
+
+ /* look up top zones */
+ table = &blues->normal_top;
+ blue_count = table->count;
+ zone = table->zones;
+
+ for ( ; blue_count > 0; blue_count--, zone++ )
+ {
+ FT_Pos delta = y - zone->org_bottom;
+
+
+ if ( delta < -blues->blue_fuzz )
+ break;
+
+ if ( y <= zone->org_top + blues->blue_fuzz )
+ if ( blues->no_overshoots || delta <= blues->blue_threshold )
+ {
+ point->cur_u = zone->cur_bottom;
+ psh_point_set_strong( point );
+ psh_point_set_fitted( point );
+ }
+ }
+
+ /* look up bottom zones */
+ table = &blues->normal_bottom;
+ blue_count = table->count;
+ zone = table->zones + blue_count - 1;
+
+ for ( ; blue_count > 0; blue_count--, zone-- )
+ {
+ FT_Pos delta = zone->org_top - y;
+
+
+ if ( delta < -blues->blue_fuzz )
+ break;
+
+ if ( y >= zone->org_bottom - blues->blue_fuzz )
+ if ( blues->no_overshoots || delta < blues->blue_threshold )
+ {
+ point->cur_u = zone->cur_top;
+ psh_point_set_strong( point );
+ psh_point_set_fitted( point );
+ }
+ }
+ }
+ }
+
+
+ /* interpolate strong points with the help of hinted coordinates */
+ static void
+ psh_glyph_interpolate_strong_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+ PSH_Dimension dim = &glyph->globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+
+ FT_UInt count = glyph->num_points;
+ PSH_Point point = glyph->points;
+
+
+ for ( ; count > 0; count--, point++ )
+ {
+ PSH_Hint hint = point->hint;
+
+
+ if ( hint )
+ {
+ FT_Pos delta;
+
+
+ if ( psh_point_is_edge_min( point ) )
+ point->cur_u = hint->cur_pos;
+
+ else if ( psh_point_is_edge_max( point ) )
+ point->cur_u = hint->cur_pos + hint->cur_len;
+
+ else
+ {
+ delta = point->org_u - hint->org_pos;
+
+ if ( delta <= 0 )
+ point->cur_u = hint->cur_pos + FT_MulFix( delta, scale );
+
+ else if ( delta >= hint->org_len )
+ point->cur_u = hint->cur_pos + hint->cur_len +
+ FT_MulFix( delta - hint->org_len, scale );
+
+ else if ( hint->org_len > 0 )
+ point->cur_u = hint->cur_pos +
+ FT_MulDiv( delta, hint->cur_len,
+ hint->org_len );
+ else
+ point->cur_u = hint->cur_pos;
+ }
+ psh_point_set_fitted( point );
+ }
+ }
+ }
+
+
+#define PSH_MAX_STRONG_INTERNAL 16
+
+ static void
+ psh_glyph_interpolate_normal_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+
+#if 1
+ /* first technique: a point is strong if it is a local extremum */
+
+ PSH_Dimension dim = &glyph->globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Memory memory = glyph->memory;
+
+ PSH_Point* strongs = NULL;
+ PSH_Point strongs_0[PSH_MAX_STRONG_INTERNAL];
+ FT_UInt num_strongs = 0;
+
+ PSH_Point points = glyph->points;
+ PSH_Point points_end = points + glyph->num_points;
+ PSH_Point point;
+
+
+ /* first count the number of strong points */
+ for ( point = points; point < points_end; point++ )
+ {
+ if ( psh_point_is_strong( point ) )
+ num_strongs++;
+ }
+
+ if ( num_strongs == 0 ) /* nothing to do here */
+ return;
+
+ /* allocate an array to store a list of points, */
+ /* stored in increasing org_u order */
+ if ( num_strongs <= PSH_MAX_STRONG_INTERNAL )
+ strongs = strongs_0;
+ else
+ {
+ FT_Error error;
+
+
+ if ( FT_NEW_ARRAY( strongs, num_strongs ) )
+ return;
+ }
+
+ num_strongs = 0;
+ for ( point = points; point < points_end; point++ )
+ {
+ PSH_Point* insert;
+
+
+ if ( !psh_point_is_strong( point ) )
+ continue;
+
+ for ( insert = strongs + num_strongs; insert > strongs; insert-- )
+ {
+ if ( insert[-1]->org_u <= point->org_u )
+ break;
+
+ insert[0] = insert[-1];
+ }
+ insert[0] = point;
+ num_strongs++;
+ }
+
+ /* now try to interpolate all normal points */
+ for ( point = points; point < points_end; point++ )
+ {
+ if ( psh_point_is_strong( point ) )
+ continue;
+
+ /* sometimes, some local extrema are smooth points */
+ if ( psh_point_is_smooth( point ) )
+ {
+ if ( point->dir_in == PSH_DIR_NONE ||
+ point->dir_in != point->dir_out )
+ continue;
+
+ if ( !psh_point_is_extremum( point ) &&
+ !psh_point_is_inflex( point ) )
+ continue;
+
+ point->flags &= ~PSH_POINT_SMOOTH;
+ }
+
+ /* find best enclosing point coordinates then interpolate */
+ {
+ PSH_Point before, after;
+ FT_UInt nn;
+
+
+ for ( nn = 0; nn < num_strongs; nn++ )
+ if ( strongs[nn]->org_u > point->org_u )
+ break;
+
+ if ( nn == 0 ) /* point before the first strong point */
+ {
+ after = strongs[0];
+
+ point->cur_u = after->cur_u +
+ FT_MulFix( point->org_u - after->org_u,
+ scale );
+ }
+ else
+ {
+ before = strongs[nn - 1];
+
+ for ( nn = num_strongs; nn > 0; nn-- )
+ if ( strongs[nn - 1]->org_u < point->org_u )
+ break;
+
+ if ( nn == num_strongs ) /* point is after last strong point */
+ {
+ before = strongs[nn - 1];
+
+ point->cur_u = before->cur_u +
+ FT_MulFix( point->org_u - before->org_u,
+ scale );
+ }
+ else
+ {
+ FT_Pos u;
+
+
+ after = strongs[nn];
+
+ /* now interpolate point between before and after */
+ u = point->org_u;
+
+ if ( u == before->org_u )
+ point->cur_u = before->cur_u;
+
+ else if ( u == after->org_u )
+ point->cur_u = after->cur_u;
+
+ else
+ point->cur_u = before->cur_u +
+ FT_MulDiv( u - before->org_u,
+ after->cur_u - before->cur_u,
+ after->org_u - before->org_u );
+ }
+ }
+ psh_point_set_fitted( point );
+ }
+ }
+
+ if ( strongs != strongs_0 )
+ FT_FREE( strongs );
+
+#endif /* 1 */
+
+ }
+
+
+ /* interpolate other points */
+ static void
+ psh_glyph_interpolate_other_points( PSH_Glyph glyph,
+ FT_Int dimension )
+ {
+ PSH_Dimension dim = &glyph->globals->dimension[dimension];
+ FT_Fixed scale = dim->scale_mult;
+ FT_Fixed delta = dim->scale_delta;
+ PSH_Contour contour = glyph->contours;
+ FT_UInt num_contours = glyph->num_contours;
+
+
+ for ( ; num_contours > 0; num_contours--, contour++ )
+ {
+ PSH_Point start = contour->start;
+ PSH_Point first, next, point;
+ FT_UInt fit_count;
+
+
+ /* count the number of strong points in this contour */
+ next = start + contour->count;
+ fit_count = 0;
+ first = 0;
+
+ for ( point = start; point < next; point++ )
+ if ( psh_point_is_fitted( point ) )
+ {
+ if ( !first )
+ first = point;
+
+ fit_count++;
+ }
+
+ /* if there are less than 2 fitted points in the contour, we */
+ /* simply scale and eventually translate the contour points */
+ if ( fit_count < 2 )
+ {
+ if ( fit_count == 1 )
+ delta = first->cur_u - FT_MulFix( first->org_u, scale );
+
+ for ( point = start; point < next; point++ )
+ if ( point != first )
+ point->cur_u = FT_MulFix( point->org_u, scale ) + delta;
+
+ goto Next_Contour;
+ }
+
+ /* there are more than 2 strong points in this contour; we */
+ /* need to interpolate weak points between them */
+ start = first;
+ do
+ {
+ point = first;
+
+ /* skip consecutive fitted points */
+ for (;;)
+ {
+ next = first->next;
+ if ( next == start )
+ goto Next_Contour;
+
+ if ( !psh_point_is_fitted( next ) )
+ break;
+
+ first = next;
+ }
+
+ /* find next fitted point after unfitted one */
+ for (;;)
+ {
+ next = next->next;
+ if ( psh_point_is_fitted( next ) )
+ break;
+ }
+
+ /* now interpolate between them */
+ {
+ FT_Pos org_a, org_ab, cur_a, cur_ab;
+ FT_Pos org_c, org_ac, cur_c;
+ FT_Fixed scale_ab;
+
+
+ if ( first->org_u <= next->org_u )
+ {
+ org_a = first->org_u;
+ cur_a = first->cur_u;
+ org_ab = next->org_u - org_a;
+ cur_ab = next->cur_u - cur_a;
+ }
+ else
+ {
+ org_a = next->org_u;
+ cur_a = next->cur_u;
+ org_ab = first->org_u - org_a;
+ cur_ab = first->cur_u - cur_a;
+ }
+
+ scale_ab = 0x10000L;
+ if ( org_ab > 0 )
+ scale_ab = FT_DivFix( cur_ab, org_ab );
+
+ point = first->next;
+ do
+ {
+ org_c = point->org_u;
+ org_ac = org_c - org_a;
+
+ if ( org_ac <= 0 )
+ {
+ /* on the left of the interpolation zone */
+ cur_c = cur_a + FT_MulFix( org_ac, scale );
+ }
+ else if ( org_ac >= org_ab )
+ {
+ /* on the right on the interpolation zone */
+ cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale );
+ }
+ else
+ {
+ /* within the interpolation zone */
+ cur_c = cur_a + FT_MulFix( org_ac, scale_ab );
+ }
+
+ point->cur_u = cur_c;
+
+ point = point->next;
+
+ } while ( point != next );
+ }
+
+ /* keep going until all points in the contours have been processed */
+ first = next;
+
+ } while ( first != start );
+
+ Next_Contour:
+ ;
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** HIGH-LEVEL INTERFACE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ FT_Error
+ ps_hints_apply( PS_Hints ps_hints,
+ FT_Outline* outline,
+ PSH_Globals globals,
+ FT_Render_Mode hint_mode )
+ {
+ PSH_GlyphRec glyphrec;
+ PSH_Glyph glyph = &glyphrec;
+ FT_Error error;
+#ifdef DEBUG_HINTER
+ FT_Memory memory;
+#endif
+ FT_Int dimension;
+
+
+ /* something to do? */
+ if ( outline->n_points == 0 || outline->n_contours == 0 )
+ return PSH_Err_Ok;
+
+#ifdef DEBUG_HINTER
+
+ memory = globals->memory;
+
+ if ( ps_debug_glyph )
+ {
+ psh_glyph_done( ps_debug_glyph );
+ FT_FREE( ps_debug_glyph );
+ }
+
+ if ( FT_NEW( glyph ) )
+ return error;
+
+ ps_debug_glyph = glyph;
+
+#endif /* DEBUG_HINTER */
+
+ error = psh_glyph_init( glyph, outline, ps_hints, globals );
+ if ( error )
+ goto Exit;
+
+ /* try to optimize the y_scale so that the top of non-capital letters
+ * is aligned on a pixel boundary whenever possible
+ */
+ {
+ PSH_Dimension dim_x = &glyph->globals->dimension[0];
+ PSH_Dimension dim_y = &glyph->globals->dimension[1];
+
+ FT_Fixed x_scale = dim_x->scale_mult;
+ FT_Fixed y_scale = dim_y->scale_mult;
+
+ FT_Fixed scaled;
+ FT_Fixed fitted;
+
+
+ scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale );
+ fitted = FT_PIX_ROUND( scaled );
+
+ if ( fitted != 0 && scaled != fitted )
+ {
+ y_scale = FT_MulDiv( y_scale, fitted, scaled );
+
+ if ( fitted < scaled )
+ x_scale -= x_scale / 50;
+
+ psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 );
+ }
+ }
+
+ glyph->do_horz_hints = 1;
+ glyph->do_vert_hints = 1;
+
+ glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
+ hint_mode == FT_RENDER_MODE_LCD );
+
+ glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
+ hint_mode == FT_RENDER_MODE_LCD_V );
+
+ glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
+
+ for ( dimension = 0; dimension < 2; dimension++ )
+ {
+ /* load outline coordinates into glyph */
+ psh_glyph_load_points( glyph, dimension );
+
+ /* compute local extrema */
+ psh_glyph_compute_extrema( glyph );
+
+ /* compute aligned stem/hints positions */
+ psh_hint_table_align_hints( &glyph->hint_tables[dimension],
+ glyph->globals,
+ dimension,
+ glyph );
+
+ /* find strong points, align them, then interpolate others */
+ psh_glyph_find_strong_points( glyph, dimension );
+ if ( dimension == 1 )
+ psh_glyph_find_blue_points( &globals->blues, glyph );
+ psh_glyph_interpolate_strong_points( glyph, dimension );
+ psh_glyph_interpolate_normal_points( glyph, dimension );
+ psh_glyph_interpolate_other_points( glyph, dimension );
+
+ /* save hinted coordinates back to outline */
+ psh_glyph_save_points( glyph, dimension );
+ }
+
+ Exit:
+
+#ifndef DEBUG_HINTER
+ psh_glyph_done( glyph );
+#endif
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/pshinter/pshalgo.h b/src/pshinter/pshalgo.h
new file mode 100644
index 0000000..f68de71
--- /dev/null
+++ b/src/pshinter/pshalgo.h
@@ -0,0 +1,255 @@
+/***************************************************************************/
+/* */
+/* pshalgo.h */
+/* */
+/* PostScript hinting algorithm (specification). */
+/* */
+/* Copyright 2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSHALGO_H__
+#define __PSHALGO_H__
+
+
+#include "pshrec.h"
+#include "pshglob.h"
+#include FT_TRIGONOMETRY_H
+
+
+FT_BEGIN_HEADER
+
+
+ /* handle to Hint structure */
+ typedef struct PSH_HintRec_* PSH_Hint;
+
+ /* hint bit-flags */
+ typedef enum
+ {
+ PSH_HINT_GHOST = PS_HINT_FLAG_GHOST,
+ PSH_HINT_BOTTOM = PS_HINT_FLAG_BOTTOM,
+ PSH_HINT_ACTIVE = 4,
+ PSH_HINT_FITTED = 8
+
+ } PSH_Hint_Flags;
+
+
+#define psh_hint_is_active( x ) ( ( (x)->flags & PSH_HINT_ACTIVE ) != 0 )
+#define psh_hint_is_ghost( x ) ( ( (x)->flags & PSH_HINT_GHOST ) != 0 )
+#define psh_hint_is_fitted( x ) ( ( (x)->flags & PSH_HINT_FITTED ) != 0 )
+
+#define psh_hint_activate( x ) (x)->flags |= PSH_HINT_ACTIVE
+#define psh_hint_deactivate( x ) (x)->flags &= ~PSH_HINT_ACTIVE
+#define psh_hint_set_fitted( x ) (x)->flags |= PSH_HINT_FITTED
+
+ /* hint structure */
+ typedef struct PSH_HintRec_
+ {
+ FT_Int org_pos;
+ FT_Int org_len;
+ FT_Pos cur_pos;
+ FT_Pos cur_len;
+ FT_UInt flags;
+ PSH_Hint parent;
+ FT_Int order;
+
+ } PSH_HintRec;
+
+
+ /* this is an interpolation zone used for strong points; */
+ /* weak points are interpolated according to their strong */
+ /* neighbours */
+ typedef struct PSH_ZoneRec_
+ {
+ FT_Fixed scale;
+ FT_Fixed delta;
+ FT_Pos min;
+ FT_Pos max;
+
+ } PSH_ZoneRec, *PSH_Zone;
+
+
+ typedef struct PSH_Hint_TableRec_
+ {
+ FT_UInt max_hints;
+ FT_UInt num_hints;
+ PSH_Hint hints;
+ PSH_Hint* sort;
+ PSH_Hint* sort_global;
+ FT_UInt num_zones;
+ PSH_ZoneRec* zones;
+ PSH_Zone zone;
+ PS_Mask_Table hint_masks;
+ PS_Mask_Table counter_masks;
+
+ } PSH_Hint_TableRec, *PSH_Hint_Table;
+
+
+ typedef struct PSH_PointRec_* PSH_Point;
+ typedef struct PSH_ContourRec_* PSH_Contour;
+
+ enum
+ {
+ PSH_DIR_NONE = 4,
+ PSH_DIR_UP = -1,
+ PSH_DIR_DOWN = 1,
+ PSH_DIR_LEFT = -2,
+ PSH_DIR_RIGHT = 2
+ };
+
+#define PSH_DIR_HORIZONTAL 2
+#define PSH_DIR_VERTICAL 1
+
+#define PSH_DIR_COMPARE( d1, d2 ) ( (d1) == (d2) || (d1) == -(d2) )
+#define PSH_DIR_IS_HORIZONTAL( d ) PSH_DIR_COMPARE( d, PSH_DIR_HORIZONTAL )
+#define PSH_DIR_IS_VERTICAL( d ) PSH_DIR_COMPARE( d, PSH_DIR_VERTICAL )
+
+
+ /* the following bit-flags are computed once by the glyph */
+ /* analyzer, for both dimensions */
+ enum
+ {
+ PSH_POINT_OFF = 1, /* point is off the curve */
+ PSH_POINT_SMOOTH = 2, /* point is smooth */
+ PSH_POINT_INFLEX = 4 /* point is inflection */
+ };
+
+#define psh_point_is_smooth( p ) ( (p)->flags & PSH_POINT_SMOOTH )
+#define psh_point_is_off( p ) ( (p)->flags & PSH_POINT_OFF )
+#define psh_point_is_inflex( p ) ( (p)->flags & PSH_POINT_INFLEX )
+
+#define psh_point_set_smooth( p ) (p)->flags |= PSH_POINT_SMOOTH
+#define psh_point_set_off( p ) (p)->flags |= PSH_POINT_OFF
+#define psh_point_set_inflex( p ) (p)->flags |= PSH_POINT_INFLEX
+
+ /* the following bit-flags are re-computed for each dimension */
+ enum
+ {
+ PSH_POINT_STRONG = 16, /* point is strong */
+ PSH_POINT_FITTED = 32, /* point is already fitted */
+ PSH_POINT_EXTREMUM = 64, /* point is local extremum */
+ PSH_POINT_POSITIVE = 128, /* extremum has positive contour flow */
+ PSH_POINT_NEGATIVE = 256, /* extremum has negative contour flow */
+ PSH_POINT_EDGE_MIN = 512, /* point is aligned to left/bottom stem edge */
+ PSH_POINT_EDGE_MAX = 1024 /* point is aligned to top/right stem edge */
+ };
+
+#define psh_point_is_strong( p ) ( (p)->flags2 & PSH_POINT_STRONG )
+#define psh_point_is_fitted( p ) ( (p)->flags2 & PSH_POINT_FITTED )
+#define psh_point_is_extremum( p ) ( (p)->flags2 & PSH_POINT_EXTREMUM )
+#define psh_point_is_positive( p ) ( (p)->flags2 & PSH_POINT_POSITIVE )
+#define psh_point_is_negative( p ) ( (p)->flags2 & PSH_POINT_NEGATIVE )
+#define psh_point_is_edge_min( p ) ( (p)->flags2 & PSH_POINT_EDGE_MIN )
+#define psh_point_is_edge_max( p ) ( (p)->flags2 & PSH_POINT_EDGE_MAX )
+
+#define psh_point_set_strong( p ) (p)->flags2 |= PSH_POINT_STRONG
+#define psh_point_set_fitted( p ) (p)->flags2 |= PSH_POINT_FITTED
+#define psh_point_set_extremum( p ) (p)->flags2 |= PSH_POINT_EXTREMUM
+#define psh_point_set_positive( p ) (p)->flags2 |= PSH_POINT_POSITIVE
+#define psh_point_set_negative( p ) (p)->flags2 |= PSH_POINT_NEGATIVE
+#define psh_point_set_edge_min( p ) (p)->flags2 |= PSH_POINT_EDGE_MIN
+#define psh_point_set_edge_max( p ) (p)->flags2 |= PSH_POINT_EDGE_MAX
+
+
+ typedef struct PSH_PointRec_
+ {
+ PSH_Point prev;
+ PSH_Point next;
+ PSH_Contour contour;
+ FT_UInt flags;
+ FT_UInt flags2;
+ FT_Char dir_in;
+ FT_Char dir_out;
+ FT_Angle angle_in;
+ FT_Angle angle_out;
+ PSH_Hint hint;
+ FT_Pos org_u;
+ FT_Pos org_v;
+ FT_Pos cur_u;
+#ifdef DEBUG_HINTER
+ FT_Pos org_x;
+ FT_Pos cur_x;
+ FT_Pos org_y;
+ FT_Pos cur_y;
+ FT_UInt flags_x;
+ FT_UInt flags_y;
+#endif
+
+ } PSH_PointRec;
+
+
+#define PSH_POINT_EQUAL_ORG( a, b ) ( (a)->org_u == (b)->org_u && \
+ (a)->org_v == (b)->org_v )
+
+#define PSH_POINT_ANGLE( a, b ) FT_Atan2( (b)->org_u - (a)->org_u, \
+ (b)->org_v - (a)->org_v )
+
+ typedef struct PSH_ContourRec_
+ {
+ PSH_Point start;
+ FT_UInt count;
+
+ } PSH_ContourRec;
+
+
+ typedef struct PSH_GlyphRec_
+ {
+ FT_UInt num_points;
+ FT_UInt num_contours;
+
+ PSH_Point points;
+ PSH_Contour contours;
+
+ FT_Memory memory;
+ FT_Outline* outline;
+ PSH_Globals globals;
+ PSH_Hint_TableRec hint_tables[2];
+
+ FT_Bool vertical;
+ FT_Int major_dir;
+ FT_Int minor_dir;
+
+ FT_Bool do_horz_hints;
+ FT_Bool do_vert_hints;
+ FT_Bool do_horz_snapping;
+ FT_Bool do_vert_snapping;
+ FT_Bool do_stem_adjust;
+
+ } PSH_GlyphRec, *PSH_Glyph;
+
+
+#ifdef DEBUG_HINTER
+ extern PSH_Hint_Table ps_debug_hint_table;
+
+ typedef void
+ (*PSH_HintFunc)( PSH_Hint hint,
+ FT_Bool vertical );
+
+ extern PSH_HintFunc ps_debug_hint_func;
+
+ extern PSH_Glyph ps_debug_glyph;
+#endif
+
+
+ extern FT_Error
+ ps_hints_apply( PS_Hints ps_hints,
+ FT_Outline* outline,
+ PSH_Globals globals,
+ FT_Render_Mode hint_mode );
+
+
+FT_END_HEADER
+
+
+#endif /* __PSHALGO_H__ */
+
+
+/* END */
diff --git a/src/pshinter/pshglob.c b/src/pshinter/pshglob.c
new file mode 100644
index 0000000..8a69aa1
--- /dev/null
+++ b/src/pshinter/pshglob.c
@@ -0,0 +1,750 @@
+/***************************************************************************/
+/* */
+/* pshglob.c */
+/* */
+/* PostScript hinter global hinting management (body). */
+/* Inspired by the new auto-hinter module. */
+/* */
+/* Copyright 2001, 2002, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used */
+/* modified and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_OBJECTS_H
+#include "pshglob.h"
+
+#ifdef DEBUG_HINTER
+ PSH_Globals ps_debug_globals = 0;
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** STANDARD WIDTHS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* scale the widths/heights table */
+ static void
+ psh_globals_scale_widths( PSH_Globals globals,
+ FT_UInt direction )
+ {
+ PSH_Dimension dim = &globals->dimension[direction];
+ PSH_Widths stdw = &dim->stdw;
+ FT_UInt count = stdw->count;
+ PSH_Width width = stdw->widths;
+ PSH_Width stand = width; /* standard width/height */
+ FT_Fixed scale = dim->scale_mult;
+
+
+ if ( count > 0 )
+ {
+ width->cur = FT_MulFix( width->org, scale );
+ width->fit = FT_PIX_ROUND( width->cur );
+
+ width++;
+ count--;
+
+ for ( ; count > 0; count--, width++ )
+ {
+ FT_Pos w, dist;
+
+
+ w = FT_MulFix( width->org, scale );
+ dist = w - stand->cur;
+
+ if ( dist < 0 )
+ dist = -dist;
+
+ if ( dist < 128 )
+ w = stand->cur;
+
+ width->cur = w;
+ width->fit = FT_PIX_ROUND( w );
+ }
+ }
+ }
+
+
+#if 0
+
+ /* org_width is is font units, result in device pixels, 26.6 format */
+ FT_LOCAL_DEF( FT_Pos )
+ psh_dimension_snap_width( PSH_Dimension dimension,
+ FT_Int org_width )
+ {
+ FT_UInt n;
+ FT_Pos width = FT_MulFix( org_width, dimension->scale_mult );
+ FT_Pos best = 64 + 32 + 2;
+ FT_Pos reference = width;
+
+
+ for ( n = 0; n < dimension->stdw.count; n++ )
+ {
+ FT_Pos w;
+ FT_Pos dist;
+
+
+ w = dimension->stdw.widths[n].cur;
+ dist = width - w;
+ if ( dist < 0 )
+ dist = -dist;
+ if ( dist < best )
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+
+ if ( width >= reference )
+ {
+ width -= 0x21;
+ if ( width < reference )
+ width = reference;
+ }
+ else
+ {
+ width += 0x21;
+ if ( width > reference )
+ width = reference;
+ }
+
+ return width;
+ }
+
+#endif /* 0 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** BLUE ZONES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ psh_blues_set_zones_0( PSH_Blues target,
+ FT_Bool is_others,
+ FT_UInt read_count,
+ FT_Short* read,
+ PSH_Blue_Table top_table,
+ PSH_Blue_Table bot_table )
+ {
+ FT_UInt count_top = top_table->count;
+ FT_UInt count_bot = bot_table->count;
+ FT_Bool first = 1;
+
+ FT_UNUSED( target );
+
+
+ for ( ; read_count > 1; read_count -= 2 )
+ {
+ FT_Int reference, delta;
+ FT_UInt count;
+ PSH_Blue_Zone zones, zone;
+ FT_Bool top;
+
+
+ /* read blue zone entry, and select target top/bottom zone */
+ top = 0;
+ if ( first || is_others )
+ {
+ reference = read[1];
+ delta = read[0] - reference;
+
+ zones = bot_table->zones;
+ count = count_bot;
+ first = 0;
+ }
+ else
+ {
+ reference = read[0];
+ delta = read[1] - reference;
+
+ zones = top_table->zones;
+ count = count_top;
+ top = 1;
+ }
+
+ /* insert into sorted table */
+ zone = zones;
+ for ( ; count > 0; count--, zone++ )
+ {
+ if ( reference < zone->org_ref )
+ break;
+
+ if ( reference == zone->org_ref )
+ {
+ FT_Int delta0 = zone->org_delta;
+
+
+ /* we have two zones on the same reference position -- */
+ /* only keep the largest one */
+ if ( delta < 0 )
+ {
+ if ( delta < delta0 )
+ zone->org_delta = delta;
+ }
+ else
+ {
+ if ( delta > delta0 )
+ zone->org_delta = delta;
+ }
+ goto Skip;
+ }
+ }
+
+ for ( ; count > 0; count-- )
+ zone[count] = zone[count-1];
+
+ zone->org_ref = reference;
+ zone->org_delta = delta;
+
+ if ( top )
+ count_top++;
+ else
+ count_bot++;
+
+ Skip:
+ read += 2;
+ }
+
+ top_table->count = count_top;
+ bot_table->count = count_bot;
+ }
+
+
+ /* Re-read blue zones from the original fonts and store them into out */
+ /* private structure. This function re-orders, sanitizes and */
+ /* fuzz-expands the zones as well. */
+ static void
+ psh_blues_set_zones( PSH_Blues target,
+ FT_UInt count,
+ FT_Short* blues,
+ FT_UInt count_others,
+ FT_Short* other_blues,
+ FT_Int fuzz,
+ FT_Int family )
+ {
+ PSH_Blue_Table top_table, bot_table;
+ FT_Int count_top, count_bot;
+
+
+ if ( family )
+ {
+ top_table = &target->family_top;
+ bot_table = &target->family_bottom;
+ }
+ else
+ {
+ top_table = &target->normal_top;
+ bot_table = &target->normal_bottom;
+ }
+
+ /* read the input blue zones, and build two sorted tables */
+ /* (one for the top zones, the other for the bottom zones) */
+ top_table->count = 0;
+ bot_table->count = 0;
+
+ /* first, the blues */
+ psh_blues_set_zones_0( target, 0,
+ count, blues, top_table, bot_table );
+ psh_blues_set_zones_0( target, 1,
+ count_others, other_blues, top_table, bot_table );
+
+ count_top = top_table->count;
+ count_bot = bot_table->count;
+
+ /* sanitize top table */
+ if ( count_top > 0 )
+ {
+ PSH_Blue_Zone zone = top_table->zones;
+
+
+ for ( count = count_top; count > 0; count--, zone++ )
+ {
+ FT_Int delta;
+
+
+ if ( count > 1 )
+ {
+ delta = zone[1].org_ref - zone[0].org_ref;
+ if ( zone->org_delta > delta )
+ zone->org_delta = delta;
+ }
+
+ zone->org_bottom = zone->org_ref;
+ zone->org_top = zone->org_delta + zone->org_ref;
+ }
+ }
+
+ /* sanitize bottom table */
+ if ( count_bot > 0 )
+ {
+ PSH_Blue_Zone zone = bot_table->zones;
+
+
+ for ( count = count_bot; count > 0; count--, zone++ )
+ {
+ FT_Int delta;
+
+
+ if ( count > 1 )
+ {
+ delta = zone[0].org_ref - zone[1].org_ref;
+ if ( zone->org_delta < delta )
+ zone->org_delta = delta;
+ }
+
+ zone->org_top = zone->org_ref;
+ zone->org_bottom = zone->org_delta + zone->org_ref;
+ }
+ }
+
+ /* expand top and bottom tables with blue fuzz */
+ {
+ FT_Int dim, top, bot, delta;
+ PSH_Blue_Zone zone;
+
+
+ zone = top_table->zones;
+ count = count_top;
+
+ for ( dim = 1; dim >= 0; dim-- )
+ {
+ if ( count > 0 )
+ {
+ /* expand the bottom of the lowest zone normally */
+ zone->org_bottom -= fuzz;
+
+ /* expand the top and bottom of intermediate zones; */
+ /* checking that the interval is smaller than the fuzz */
+ top = zone->org_top;
+
+ for ( count--; count > 0; count-- )
+ {
+ bot = zone[1].org_bottom;
+ delta = bot - top;
+
+ if ( delta < 2 * fuzz )
+ zone[0].org_top = zone[1].org_bottom = top + delta / 2;
+ else
+ {
+ zone[0].org_top = top + fuzz;
+ zone[1].org_bottom = bot - fuzz;
+ }
+
+ zone++;
+ top = zone->org_top;
+ }
+
+ /* expand the top of the highest zone normally */
+ zone->org_top = top + fuzz;
+ }
+ zone = bot_table->zones;
+ count = count_bot;
+ }
+ }
+ }
+
+
+ /* reset the blues table when the device transform changes */
+ static void
+ psh_blues_scale_zones( PSH_Blues blues,
+ FT_Fixed scale,
+ FT_Pos delta )
+ {
+ FT_UInt count;
+ FT_UInt num;
+ PSH_Blue_Table table = 0;
+
+ /* */
+ /* Determine whether we need to suppress overshoots or */
+ /* not. We simply need to compare the vertical scale */
+ /* parameter to the raw bluescale value. Here is why: */
+ /* */
+ /* We need to suppress overshoots for all pointsizes. */
+ /* At 300dpi that satisfies: */
+ /* */
+ /* pointsize < 240*bluescale + 0.49 */
+ /* */
+ /* This corresponds to: */
+ /* */
+ /* pixelsize < 1000*bluescale + 49/24 */
+ /* */
+ /* scale*EM_Size < 1000*bluescale + 49/24 */
+ /* */
+ /* However, for normal Type 1 fonts, EM_Size is 1000! */
+ /* We thus only check: */
+ /* */
+ /* scale < bluescale + 49/24000 */
+ /* */
+ /* which we shorten to */
+ /* */
+ /* "scale < bluescale" */
+ /* */
+ /* Note that `blue_scale' is stored 1000 times its real */
+ /* value, and that `scale' converts from font units to */
+ /* fractional pixels. */
+ /* */
+
+ /* 1000 / 64 = 125 / 8 */
+ if ( scale >= 0x20C49BAL )
+ blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 );
+ else
+ blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 );
+
+ /* */
+ /* The blue threshold is the font units distance under */
+ /* which overshoots are suppressed due to the BlueShift */
+ /* even if the scale is greater than BlueScale. */
+ /* */
+ /* It is the smallest distance such that */
+ /* */
+ /* dist <= BlueShift && dist*scale <= 0.5 pixels */
+ /* */
+ {
+ FT_Int threshold = blues->blue_shift;
+
+
+ while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 )
+ threshold--;
+
+ blues->blue_threshold = threshold;
+ }
+
+ for ( num = 0; num < 4; num++ )
+ {
+ PSH_Blue_Zone zone;
+
+
+ switch ( num )
+ {
+ case 0:
+ table = &blues->normal_top;
+ break;
+ case 1:
+ table = &blues->normal_bottom;
+ break;
+ case 2:
+ table = &blues->family_top;
+ break;
+ default:
+ table = &blues->family_bottom;
+ break;
+ }
+
+ zone = table->zones;
+ count = table->count;
+ for ( ; count > 0; count--, zone++ )
+ {
+ zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta;
+ zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta;
+ zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta;
+ zone->cur_delta = FT_MulFix( zone->org_delta, scale );
+
+ /* round scaled reference position */
+ zone->cur_ref = FT_PIX_ROUND( zone->cur_ref );
+
+#if 0
+ if ( zone->cur_ref > zone->cur_top )
+ zone->cur_ref -= 64;
+ else if ( zone->cur_ref < zone->cur_bottom )
+ zone->cur_ref += 64;
+#endif
+ }
+ }
+
+ /* process the families now */
+
+ for ( num = 0; num < 2; num++ )
+ {
+ PSH_Blue_Zone zone1, zone2;
+ FT_UInt count1, count2;
+ PSH_Blue_Table normal, family;
+
+
+ switch ( num )
+ {
+ case 0:
+ normal = &blues->normal_top;
+ family = &blues->family_top;
+ break;
+
+ default:
+ normal = &blues->normal_bottom;
+ family = &blues->family_bottom;
+ }
+
+ zone1 = normal->zones;
+ count1 = normal->count;
+
+ for ( ; count1 > 0; count1--, zone1++ )
+ {
+ /* try to find a family zone whose reference position is less */
+ /* than 1 pixel far from the current zone */
+ zone2 = family->zones;
+ count2 = family->count;
+
+ for ( ; count2 > 0; count2--, zone2++ )
+ {
+ FT_Pos Delta;
+
+
+ Delta = zone1->org_ref - zone2->org_ref;
+ if ( Delta < 0 )
+ Delta = -Delta;
+
+ if ( FT_MulFix( Delta, scale ) < 64 )
+ {
+ zone1->cur_top = zone2->cur_top;
+ zone1->cur_bottom = zone2->cur_bottom;
+ zone1->cur_ref = zone2->cur_ref;
+ zone1->cur_delta = zone2->cur_delta;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ psh_blues_snap_stem( PSH_Blues blues,
+ FT_Int stem_top,
+ FT_Int stem_bot,
+ PSH_Alignment alignment )
+ {
+ PSH_Blue_Table table;
+ FT_UInt count;
+ FT_Pos delta;
+ PSH_Blue_Zone zone;
+ FT_Int no_shoots;
+
+
+ alignment->align = PSH_BLUE_ALIGN_NONE;
+
+ no_shoots = blues->no_overshoots;
+
+ /* look up stem top in top zones table */
+ table = &blues->normal_top;
+ count = table->count;
+ zone = table->zones;
+
+ for ( ; count > 0; count--, zone++ )
+ {
+ delta = stem_top - zone->org_bottom;
+ if ( delta < -blues->blue_fuzz )
+ break;
+
+ if ( stem_top <= zone->org_top + blues->blue_fuzz )
+ {
+ if ( no_shoots || delta <= blues->blue_threshold )
+ {
+ alignment->align |= PSH_BLUE_ALIGN_TOP;
+ alignment->align_top = zone->cur_ref;
+ }
+ break;
+ }
+ }
+
+ /* look up stem bottom in bottom zones table */
+ table = &blues->normal_bottom;
+ count = table->count;
+ zone = table->zones + count-1;
+
+ for ( ; count > 0; count--, zone-- )
+ {
+ delta = zone->org_top - stem_bot;
+ if ( delta < -blues->blue_fuzz )
+ break;
+
+ if ( stem_bot >= zone->org_bottom - blues->blue_fuzz )
+ {
+ if ( no_shoots || delta < blues->blue_threshold )
+ {
+ alignment->align |= PSH_BLUE_ALIGN_BOT;
+ alignment->align_bot = zone->cur_ref;
+ }
+ break;
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GLOBAL HINTS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ psh_globals_destroy( PSH_Globals globals )
+ {
+ if ( globals )
+ {
+ FT_Memory memory;
+
+
+ memory = globals->memory;
+ globals->dimension[0].stdw.count = 0;
+ globals->dimension[1].stdw.count = 0;
+
+ globals->blues.normal_top.count = 0;
+ globals->blues.normal_bottom.count = 0;
+ globals->blues.family_top.count = 0;
+ globals->blues.family_bottom.count = 0;
+
+ FT_FREE( globals );
+
+#ifdef DEBUG_HINTER
+ ps_debug_globals = 0;
+#endif
+ }
+ }
+
+
+ static FT_Error
+ psh_globals_new( FT_Memory memory,
+ T1_Private* priv,
+ PSH_Globals *aglobals )
+ {
+ PSH_Globals globals;
+ FT_Error error;
+
+
+ if ( !FT_NEW( globals ) )
+ {
+ FT_UInt count;
+ FT_Short* read;
+
+
+ globals->memory = memory;
+
+ /* copy standard widths */
+ {
+ PSH_Dimension dim = &globals->dimension[1];
+ PSH_Width write = dim->stdw.widths;
+
+
+ write->org = priv->standard_width[0];
+ write++;
+
+ read = priv->snap_widths;
+ for ( count = priv->num_snap_widths; count > 0; count-- )
+ {
+ write->org = *read;
+ write++;
+ read++;
+ }
+
+ dim->stdw.count = priv->num_snap_widths + 1;
+ }
+
+ /* copy standard heights */
+ {
+ PSH_Dimension dim = &globals->dimension[0];
+ PSH_Width write = dim->stdw.widths;
+
+
+ write->org = priv->standard_height[0];
+ write++;
+ read = priv->snap_heights;
+ for ( count = priv->num_snap_heights; count > 0; count-- )
+ {
+ write->org = *read;
+ write++;
+ read++;
+ }
+
+ dim->stdw.count = priv->num_snap_heights + 1;
+ }
+
+ /* copy blue zones */
+ psh_blues_set_zones( &globals->blues, priv->num_blue_values,
+ priv->blue_values, priv->num_other_blues,
+ priv->other_blues, priv->blue_fuzz, 0 );
+
+ psh_blues_set_zones( &globals->blues, priv->num_family_blues,
+ priv->family_blues, priv->num_family_other_blues,
+ priv->family_other_blues, priv->blue_fuzz, 1 );
+
+ globals->blues.blue_scale = priv->blue_scale;
+ globals->blues.blue_shift = priv->blue_shift;
+ globals->blues.blue_fuzz = priv->blue_fuzz;
+
+ globals->dimension[0].scale_mult = 0;
+ globals->dimension[0].scale_delta = 0;
+ globals->dimension[1].scale_mult = 0;
+ globals->dimension[1].scale_delta = 0;
+
+#ifdef DEBUG_HINTER
+ ps_debug_globals = globals;
+#endif
+ }
+
+ *aglobals = globals;
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ psh_globals_set_scale( PSH_Globals globals,
+ FT_Fixed x_scale,
+ FT_Fixed y_scale,
+ FT_Fixed x_delta,
+ FT_Fixed y_delta )
+ {
+ PSH_Dimension dim = &globals->dimension[0];
+
+
+ dim = &globals->dimension[0];
+ if ( x_scale != dim->scale_mult ||
+ x_delta != dim->scale_delta )
+ {
+ dim->scale_mult = x_scale;
+ dim->scale_delta = x_delta;
+
+ psh_globals_scale_widths( globals, 0 );
+ }
+
+ dim = &globals->dimension[1];
+ if ( y_scale != dim->scale_mult ||
+ y_delta != dim->scale_delta )
+ {
+ dim->scale_mult = y_scale;
+ dim->scale_delta = y_delta;
+
+ psh_globals_scale_widths( globals, 1 );
+ psh_blues_scale_zones( &globals->blues, y_scale, y_delta );
+ }
+
+ return 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs )
+ {
+ funcs->create = psh_globals_new;
+ funcs->set_scale = psh_globals_set_scale;
+ funcs->destroy = psh_globals_destroy;
+ }
+
+
+/* END */
diff --git a/src/pshinter/pshglob.h b/src/pshinter/pshglob.h
new file mode 100644
index 0000000..c511626
--- /dev/null
+++ b/src/pshinter/pshglob.h
@@ -0,0 +1,196 @@
+/***************************************************************************/
+/* */
+/* pshglob.h */
+/* */
+/* PostScript hinter global hinting management. */
+/* */
+/* Copyright 2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSHGLOB_H__
+#define __PSHGLOB_H__
+
+
+#include FT_FREETYPE_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GLOBAL HINTS INTERNALS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* @constant: */
+ /* PS_GLOBALS_MAX_BLUE_ZONES */
+ /* */
+ /* @description: */
+ /* The maximum number of blue zones in a font global hints structure. */
+ /* See @PS_Globals_BluesRec. */
+ /* */
+#define PS_GLOBALS_MAX_BLUE_ZONES 16
+
+
+ /*************************************************************************/
+ /* */
+ /* @constant: */
+ /* PS_GLOBALS_MAX_STD_WIDTHS */
+ /* */
+ /* @description: */
+ /* The maximum number of standard and snap widths in either the */
+ /* horizontal or vertical direction. See @PS_Globals_WidthsRec. */
+ /* */
+#define PS_GLOBALS_MAX_STD_WIDTHS 16
+
+
+ /* standard and snap width */
+ typedef struct PSH_WidthRec_
+ {
+ FT_Int org;
+ FT_Pos cur;
+ FT_Pos fit;
+
+ } PSH_WidthRec, *PSH_Width;
+
+
+ /* standard and snap widths table */
+ typedef struct PSH_WidthsRec_
+ {
+ FT_UInt count;
+ PSH_WidthRec widths[PS_GLOBALS_MAX_STD_WIDTHS];
+
+ } PSH_WidthsRec, *PSH_Widths;
+
+
+ typedef struct PSH_DimensionRec_
+ {
+ PSH_WidthsRec stdw;
+ FT_Fixed scale_mult;
+ FT_Fixed scale_delta;
+
+ } PSH_DimensionRec, *PSH_Dimension;
+
+
+ /* blue zone descriptor */
+ typedef struct PSH_Blue_ZoneRec_
+ {
+ FT_Int org_ref;
+ FT_Int org_delta;
+ FT_Int org_top;
+ FT_Int org_bottom;
+
+ FT_Pos cur_ref;
+ FT_Pos cur_delta;
+ FT_Pos cur_bottom;
+ FT_Pos cur_top;
+
+ } PSH_Blue_ZoneRec, *PSH_Blue_Zone;
+
+
+ typedef struct PSH_Blue_TableRec_
+ {
+ FT_UInt count;
+ PSH_Blue_ZoneRec zones[PS_GLOBALS_MAX_BLUE_ZONES];
+
+ } PSH_Blue_TableRec, *PSH_Blue_Table;
+
+
+ /* blue zones table */
+ typedef struct PSH_BluesRec_
+ {
+ PSH_Blue_TableRec normal_top;
+ PSH_Blue_TableRec normal_bottom;
+ PSH_Blue_TableRec family_top;
+ PSH_Blue_TableRec family_bottom;
+
+ FT_Fixed blue_scale;
+ FT_Int blue_shift;
+ FT_Int blue_threshold;
+ FT_Int blue_fuzz;
+ FT_Bool no_overshoots;
+
+ } PSH_BluesRec, *PSH_Blues;
+
+
+ /* font globals. */
+ /* dimension 0 => X coordinates + vertical hints/stems */
+ /* dimension 1 => Y coordinates + horizontal hints/stems */
+ typedef struct PSH_GlobalsRec_
+ {
+ FT_Memory memory;
+ PSH_DimensionRec dimension[2];
+ PSH_BluesRec blues;
+
+ } PSH_GlobalsRec;
+
+
+#define PSH_BLUE_ALIGN_NONE 0
+#define PSH_BLUE_ALIGN_TOP 1
+#define PSH_BLUE_ALIGN_BOT 2
+
+
+ typedef struct PSH_AlignmentRec_
+ {
+ int align;
+ FT_Pos align_top;
+ FT_Pos align_bot;
+
+ } PSH_AlignmentRec, *PSH_Alignment;
+
+
+ FT_LOCAL( void )
+ psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs );
+
+
+#if 0
+ /* snap a stem width to fitter coordinates. `org_width' is in font */
+ /* units. The result is in device pixels (26.6 format). */
+ FT_LOCAL( FT_Pos )
+ psh_dimension_snap_width( PSH_Dimension dimension,
+ FT_Int org_width );
+#endif
+
+ FT_LOCAL( FT_Error )
+ psh_globals_set_scale( PSH_Globals globals,
+ FT_Fixed x_scale,
+ FT_Fixed y_scale,
+ FT_Fixed x_delta,
+ FT_Fixed y_delta );
+
+ /* snap a stem to one or two blue zones */
+ FT_LOCAL( void )
+ psh_blues_snap_stem( PSH_Blues blues,
+ FT_Int stem_top,
+ FT_Int stem_bot,
+ PSH_Alignment alignment );
+ /* */
+
+#ifdef DEBUG_HINTER
+ extern PSH_Globals ps_debug_globals;
+#endif
+
+
+FT_END_HEADER
+
+
+#endif /* __PSHGLOB_H__ */
+
+
+/* END */
diff --git a/src/pshinter/pshinter.c b/src/pshinter/pshinter.c
new file mode 100644
index 0000000..8e3f193
--- /dev/null
+++ b/src/pshinter/pshinter.c
@@ -0,0 +1,28 @@
+/***************************************************************************/
+/* */
+/* pshinter.c */
+/* */
+/* FreeType PostScript Hinting module */
+/* */
+/* Copyright 2001, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "pshrec.c"
+#include "pshglob.c"
+#include "pshalgo.c"
+#include "pshmod.c"
+
+
+/* END */
diff --git a/src/pshinter/pshmod.c b/src/pshinter/pshmod.c
new file mode 100644
index 0000000..4eb3d91
--- /dev/null
+++ b/src/pshinter/pshmod.c
@@ -0,0 +1,121 @@
+/***************************************************************************/
+/* */
+/* pshmod.c */
+/* */
+/* FreeType PostScript hinter module implementation (body). */
+/* */
+/* Copyright 2001, 2002, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include "pshrec.h"
+#include "pshalgo.h"
+
+
+ /* the Postscript Hinter module structure */
+ typedef struct PS_Hinter_Module_Rec_
+ {
+ FT_ModuleRec root;
+ PS_HintsRec ps_hints;
+
+ PSH_Globals_FuncsRec globals_funcs;
+ T1_Hints_FuncsRec t1_funcs;
+ T2_Hints_FuncsRec t2_funcs;
+
+ } PS_Hinter_ModuleRec, *PS_Hinter_Module;
+
+
+ /* finalize module */
+ FT_CALLBACK_DEF( void )
+ ps_hinter_done( PS_Hinter_Module module )
+ {
+ module->t1_funcs.hints = NULL;
+ module->t2_funcs.hints = NULL;
+
+ ps_hints_done( &module->ps_hints );
+ }
+
+
+ /* initialize module, create hints recorder and the interface */
+ FT_CALLBACK_DEF( FT_Error )
+ ps_hinter_init( PS_Hinter_Module module )
+ {
+ FT_Memory memory = module->root.memory;
+ void* ph = &module->ps_hints;
+
+
+ ps_hints_init( &module->ps_hints, memory );
+
+ psh_globals_funcs_init( &module->globals_funcs );
+
+ t1_hints_funcs_init( &module->t1_funcs );
+ module->t1_funcs.hints = (T1_Hints)ph;
+
+ t2_hints_funcs_init( &module->t2_funcs );
+ module->t2_funcs.hints = (T2_Hints)ph;
+
+ return 0;
+ }
+
+
+ /* returns global hints interface */
+ FT_CALLBACK_DEF( PSH_Globals_Funcs )
+ pshinter_get_globals_funcs( FT_Module module )
+ {
+ return &((PS_Hinter_Module)module)->globals_funcs;
+ }
+
+
+ /* return Type 1 hints interface */
+ FT_CALLBACK_DEF( T1_Hints_Funcs )
+ pshinter_get_t1_funcs( FT_Module module )
+ {
+ return &((PS_Hinter_Module)module)->t1_funcs;
+ }
+
+
+ /* return Type 2 hints interface */
+ FT_CALLBACK_DEF( T2_Hints_Funcs )
+ pshinter_get_t2_funcs( FT_Module module )
+ {
+ return &((PS_Hinter_Module)module)->t2_funcs;
+ }
+
+
+ static
+ const PSHinter_Interface pshinter_interface =
+ {
+ pshinter_get_globals_funcs,
+ pshinter_get_t1_funcs,
+ pshinter_get_t2_funcs
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class pshinter_module_class =
+ {
+ 0,
+ sizeof ( PS_Hinter_ModuleRec ),
+ "pshinter",
+ 0x10000L,
+ 0x20000L,
+
+ &pshinter_interface, /* module-specific interface */
+
+ (FT_Module_Constructor)ps_hinter_init,
+ (FT_Module_Destructor) ps_hinter_done,
+ (FT_Module_Requester) 0 /* no additional interface for now */
+ };
+
+
+/* END */
diff --git a/src/pshinter/pshmod.h b/src/pshinter/pshmod.h
new file mode 100644
index 0000000..1a91025
--- /dev/null
+++ b/src/pshinter/pshmod.h
@@ -0,0 +1,39 @@
+/***************************************************************************/
+/* */
+/* pshmod.h */
+/* */
+/* PostScript hinter module interface (specification). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSHMOD_H__
+#define __PSHMOD_H__
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) pshinter_module_class;
+
+
+FT_END_HEADER
+
+
+#endif /* __PSHMOD_H__ */
+
+
+/* END */
diff --git a/src/pshinter/pshnterr.h b/src/pshinter/pshnterr.h
new file mode 100644
index 0000000..3c0029f
--- /dev/null
+++ b/src/pshinter/pshnterr.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* pshnterr.h */
+/* */
+/* PS Hinter error codes (specification only). */
+/* */
+/* Copyright 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the PSHinter error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __PSHNTERR_H__
+#define __PSHNTERR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX PSH_Err_
+#define FT_ERR_BASE FT_Mod_Err_PShinter
+
+#include FT_ERRORS_H
+
+#endif /* __PSHNTERR_H__ */
+
+
+/* END */
diff --git a/src/pshinter/pshrec.c b/src/pshinter/pshrec.c
new file mode 100644
index 0000000..2a885ef
--- /dev/null
+++ b/src/pshinter/pshrec.c
@@ -0,0 +1,1215 @@
+/***************************************************************************/
+/* */
+/* pshrec.c */
+/* */
+/* FreeType PostScript hints recorder (body). */
+/* */
+/* Copyright 2001, 2002, 2003, 2004, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include "pshrec.h"
+#include "pshalgo.h"
+
+#include "pshnterr.h"
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_pshrec
+
+#ifdef DEBUG_HINTER
+ PS_Hints ps_debug_hints = 0;
+ int ps_debug_no_horz_hints = 0;
+ int ps_debug_no_vert_hints = 0;
+#endif
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_HINT MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* destroy hints table */
+ static void
+ ps_hint_table_done( PS_Hint_Table table,
+ FT_Memory memory )
+ {
+ FT_FREE( table->hints );
+ table->num_hints = 0;
+ table->max_hints = 0;
+ }
+
+
+ /* ensure that a table can contain "count" elements */
+ static FT_Error
+ ps_hint_table_ensure( PS_Hint_Table table,
+ FT_UInt count,
+ FT_Memory memory )
+ {
+ FT_UInt old_max = table->max_hints;
+ FT_UInt new_max = count;
+ FT_Error error = 0;
+
+
+ if ( new_max > old_max )
+ {
+ /* try to grow the table */
+ new_max = FT_PAD_CEIL( new_max, 8 );
+ if ( !FT_RENEW_ARRAY( table->hints, old_max, new_max ) )
+ table->max_hints = new_max;
+ }
+ return error;
+ }
+
+
+ static FT_Error
+ ps_hint_table_alloc( PS_Hint_Table table,
+ FT_Memory memory,
+ PS_Hint *ahint )
+ {
+ FT_Error error = 0;
+ FT_UInt count;
+ PS_Hint hint = 0;
+
+
+ count = table->num_hints;
+ count++;
+
+ if ( count >= table->max_hints )
+ {
+ error = ps_hint_table_ensure( table, count, memory );
+ if ( error )
+ goto Exit;
+ }
+
+ hint = table->hints + count - 1;
+ hint->pos = 0;
+ hint->len = 0;
+ hint->flags = 0;
+
+ table->num_hints = count;
+
+ Exit:
+ *ahint = hint;
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_MASK MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* destroy mask */
+ static void
+ ps_mask_done( PS_Mask mask,
+ FT_Memory memory )
+ {
+ FT_FREE( mask->bytes );
+ mask->num_bits = 0;
+ mask->max_bits = 0;
+ mask->end_point = 0;
+ }
+
+
+ /* ensure that a mask can contain "count" bits */
+ static FT_Error
+ ps_mask_ensure( PS_Mask mask,
+ FT_UInt count,
+ FT_Memory memory )
+ {
+ FT_UInt old_max = ( mask->max_bits + 7 ) >> 3;
+ FT_UInt new_max = ( count + 7 ) >> 3;
+ FT_Error error = 0;
+
+
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 8 );
+ if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) )
+ mask->max_bits = new_max * 8;
+ }
+ return error;
+ }
+
+
+ /* test a bit value in a given mask */
+ static FT_Int
+ ps_mask_test_bit( PS_Mask mask,
+ FT_Int idx )
+ {
+ if ( (FT_UInt)idx >= mask->num_bits )
+ return 0;
+
+ return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) );
+ }
+
+
+ /* clear a given bit */
+ static void
+ ps_mask_clear_bit( PS_Mask mask,
+ FT_Int idx )
+ {
+ FT_Byte* p;
+
+
+ if ( (FT_UInt)idx >= mask->num_bits )
+ return;
+
+ p = mask->bytes + ( idx >> 3 );
+ p[0] = (FT_Byte)( p[0] & ~( 0x80 >> ( idx & 7 ) ) );
+ }
+
+
+ /* set a given bit, possibly grow the mask */
+ static FT_Error
+ ps_mask_set_bit( PS_Mask mask,
+ FT_Int idx,
+ FT_Memory memory )
+ {
+ FT_Error error = 0;
+ FT_Byte* p;
+
+
+ if ( idx < 0 )
+ goto Exit;
+
+ if ( (FT_UInt)idx >= mask->num_bits )
+ {
+ error = ps_mask_ensure( mask, idx + 1, memory );
+ if ( error )
+ goto Exit;
+
+ mask->num_bits = idx + 1;
+ }
+
+ p = mask->bytes + ( idx >> 3 );
+ p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) );
+
+ Exit:
+ return error;
+ }
+
+
+ /* destroy mask table */
+ static void
+ ps_mask_table_done( PS_Mask_Table table,
+ FT_Memory memory )
+ {
+ FT_UInt count = table->max_masks;
+ PS_Mask mask = table->masks;
+
+
+ for ( ; count > 0; count--, mask++ )
+ ps_mask_done( mask, memory );
+
+ FT_FREE( table->masks );
+ table->num_masks = 0;
+ table->max_masks = 0;
+ }
+
+
+ /* ensure that a mask table can contain "count" masks */
+ static FT_Error
+ ps_mask_table_ensure( PS_Mask_Table table,
+ FT_UInt count,
+ FT_Memory memory )
+ {
+ FT_UInt old_max = table->max_masks;
+ FT_UInt new_max = count;
+ FT_Error error = 0;
+
+
+ if ( new_max > old_max )
+ {
+ new_max = FT_PAD_CEIL( new_max, 8 );
+ if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) )
+ table->max_masks = new_max;
+ }
+ return error;
+ }
+
+
+ /* allocate a new mask in a table */
+ static FT_Error
+ ps_mask_table_alloc( PS_Mask_Table table,
+ FT_Memory memory,
+ PS_Mask *amask )
+ {
+ FT_UInt count;
+ FT_Error error = 0;
+ PS_Mask mask = 0;
+
+
+ count = table->num_masks;
+ count++;
+
+ if ( count > table->max_masks )
+ {
+ error = ps_mask_table_ensure( table, count, memory );
+ if ( error )
+ goto Exit;
+ }
+
+ mask = table->masks + count - 1;
+ mask->num_bits = 0;
+ mask->end_point = 0;
+ table->num_masks = count;
+
+ Exit:
+ *amask = mask;
+ return error;
+ }
+
+
+ /* return last hint mask in a table, create one if the table is empty */
+ static FT_Error
+ ps_mask_table_last( PS_Mask_Table table,
+ FT_Memory memory,
+ PS_Mask *amask )
+ {
+ FT_Error error = 0;
+ FT_UInt count;
+ PS_Mask mask;
+
+
+ count = table->num_masks;
+ if ( count == 0 )
+ {
+ error = ps_mask_table_alloc( table, memory, &mask );
+ if ( error )
+ goto Exit;
+ }
+ else
+ mask = table->masks + count - 1;
+
+ Exit:
+ *amask = mask;
+ return error;
+ }
+
+
+ /* set a new mask to a given bit range */
+ static FT_Error
+ ps_mask_table_set_bits( PS_Mask_Table table,
+ const FT_Byte* source,
+ FT_UInt bit_pos,
+ FT_UInt bit_count,
+ FT_Memory memory )
+ {
+ FT_Error error = 0;
+ PS_Mask mask;
+
+
+ error = ps_mask_table_last( table, memory, &mask );
+ if ( error )
+ goto Exit;
+
+ error = ps_mask_ensure( mask, bit_count, memory );
+ if ( error )
+ goto Exit;
+
+ mask->num_bits = bit_count;
+
+ /* now, copy bits */
+ {
+ FT_Byte* read = (FT_Byte*)source + ( bit_pos >> 3 );
+ FT_Int rmask = 0x80 >> ( bit_pos & 7 );
+ FT_Byte* write = mask->bytes;
+ FT_Int wmask = 0x80;
+ FT_Int val;
+
+
+ for ( ; bit_count > 0; bit_count-- )
+ {
+ val = write[0] & ~wmask;
+
+ if ( read[0] & rmask )
+ val |= wmask;
+
+ write[0] = (FT_Byte)val;
+
+ rmask >>= 1;
+ if ( rmask == 0 )
+ {
+ read++;
+ rmask = 0x80;
+ }
+
+ wmask >>= 1;
+ if ( wmask == 0 )
+ {
+ write++;
+ wmask = 0x80;
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* test whether two masks in a table intersect */
+ static FT_Int
+ ps_mask_table_test_intersect( PS_Mask_Table table,
+ FT_Int index1,
+ FT_Int index2 )
+ {
+ PS_Mask mask1 = table->masks + index1;
+ PS_Mask mask2 = table->masks + index2;
+ FT_Byte* p1 = mask1->bytes;
+ FT_Byte* p2 = mask2->bytes;
+ FT_UInt count1 = mask1->num_bits;
+ FT_UInt count2 = mask2->num_bits;
+ FT_UInt count;
+
+
+ count = ( count1 <= count2 ) ? count1 : count2;
+ for ( ; count >= 8; count -= 8 )
+ {
+ if ( p1[0] & p2[0] )
+ return 1;
+
+ p1++;
+ p2++;
+ }
+
+ if ( count == 0 )
+ return 0;
+
+ return ( p1[0] & p2[0] ) & ~( 0xFF >> count );
+ }
+
+
+ /* merge two masks, used by ps_mask_table_merge_all */
+ static FT_Error
+ ps_mask_table_merge( PS_Mask_Table table,
+ FT_Int index1,
+ FT_Int index2,
+ FT_Memory memory )
+ {
+ FT_UInt temp;
+ FT_Error error = 0;
+
+
+ /* swap index1 and index2 so that index1 < index2 */
+ if ( index1 > index2 )
+ {
+ temp = index1;
+ index1 = index2;
+ index2 = temp;
+ }
+
+ if ( index1 < index2 && index1 >= 0 && index2 < (FT_Int)table->num_masks )
+ {
+ /* we need to merge the bitsets of index1 and index2 with a */
+ /* simple union */
+ PS_Mask mask1 = table->masks + index1;
+ PS_Mask mask2 = table->masks + index2;
+ FT_UInt count1 = mask1->num_bits;
+ FT_UInt count2 = mask2->num_bits;
+ FT_Int delta;
+
+
+ if ( count2 > 0 )
+ {
+ FT_UInt pos;
+ FT_Byte* read;
+ FT_Byte* write;
+
+
+ /* if "count2" is greater than "count1", we need to grow the */
+ /* first bitset, and clear the highest bits */
+ if ( count2 > count1 )
+ {
+ error = ps_mask_ensure( mask1, count2, memory );
+ if ( error )
+ goto Exit;
+
+ for ( pos = count1; pos < count2; pos++ )
+ ps_mask_clear_bit( mask1, pos );
+ }
+
+ /* merge (unite) the bitsets */
+ read = mask2->bytes;
+ write = mask1->bytes;
+ pos = (FT_UInt)( ( count2 + 7 ) >> 3 );
+
+ for ( ; pos > 0; pos-- )
+ {
+ write[0] = (FT_Byte)( write[0] | read[0] );
+ write++;
+ read++;
+ }
+ }
+
+ /* Now, remove "mask2" from the list. We need to keep the masks */
+ /* sorted in order of importance, so move table elements. */
+ mask2->num_bits = 0;
+ mask2->end_point = 0;
+
+ delta = table->num_masks - 1 - index2; /* number of masks to move */
+ if ( delta > 0 )
+ {
+ /* move to end of table for reuse */
+ PS_MaskRec dummy = *mask2;
+
+
+ ft_memmove( mask2, mask2 + 1, delta * sizeof ( PS_MaskRec ) );
+
+ mask2[delta] = dummy;
+ }
+
+ table->num_masks--;
+ }
+ else
+ FT_ERROR(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n",
+ index1, index2 ));
+
+ Exit:
+ return error;
+ }
+
+
+ /* Try to merge all masks in a given table. This is used to merge */
+ /* all counter masks into independent counter "paths". */
+ /* */
+ static FT_Error
+ ps_mask_table_merge_all( PS_Mask_Table table,
+ FT_Memory memory )
+ {
+ FT_Int index1, index2;
+ FT_Error error = 0;
+
+
+ for ( index1 = table->num_masks - 1; index1 > 0; index1-- )
+ {
+ for ( index2 = index1 - 1; index2 >= 0; index2-- )
+ {
+ if ( ps_mask_table_test_intersect( table, index1, index2 ) )
+ {
+ error = ps_mask_table_merge( table, index2, index1, memory );
+ if ( error )
+ goto Exit;
+
+ break;
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_DIMENSION MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* finalize a given dimension */
+ static void
+ ps_dimension_done( PS_Dimension dimension,
+ FT_Memory memory )
+ {
+ ps_mask_table_done( &dimension->counters, memory );
+ ps_mask_table_done( &dimension->masks, memory );
+ ps_hint_table_done( &dimension->hints, memory );
+ }
+
+
+ /* initialize a given dimension */
+ static void
+ ps_dimension_init( PS_Dimension dimension )
+ {
+ dimension->hints.num_hints = 0;
+ dimension->masks.num_masks = 0;
+ dimension->counters.num_masks = 0;
+ }
+
+
+#if 0
+
+ /* set a bit at a given index in the current hint mask */
+ static FT_Error
+ ps_dimension_set_mask_bit( PS_Dimension dim,
+ FT_UInt idx,
+ FT_Memory memory )
+ {
+ PS_Mask mask;
+ FT_Error error = 0;
+
+
+ /* get last hint mask */
+ error = ps_mask_table_last( &dim->masks, memory, &mask );
+ if ( error )
+ goto Exit;
+
+ error = ps_mask_set_bit( mask, idx, memory );
+
+ Exit:
+ return error;
+ }
+
+#endif
+
+ /* set the end point in a mask, called from "End" & "Reset" methods */
+ static void
+ ps_dimension_end_mask( PS_Dimension dim,
+ FT_UInt end_point )
+ {
+ FT_UInt count = dim->masks.num_masks;
+ PS_Mask mask;
+
+
+ if ( count > 0 )
+ {
+ mask = dim->masks.masks + count - 1;
+ mask->end_point = end_point;
+ }
+ }
+
+
+ /* set the end point in the current mask, then create a new empty one */
+ /* (called by "Reset" method) */
+ static FT_Error
+ ps_dimension_reset_mask( PS_Dimension dim,
+ FT_UInt end_point,
+ FT_Memory memory )
+ {
+ PS_Mask mask;
+
+
+ /* end current mask */
+ ps_dimension_end_mask( dim, end_point );
+
+ /* allocate new one */
+ return ps_mask_table_alloc( &dim->masks, memory, &mask );
+ }
+
+
+ /* set a new mask, called from the "T2Stem" method */
+ static FT_Error
+ ps_dimension_set_mask_bits( PS_Dimension dim,
+ const FT_Byte* source,
+ FT_UInt source_pos,
+ FT_UInt source_bits,
+ FT_UInt end_point,
+ FT_Memory memory )
+ {
+ FT_Error error = 0;
+
+
+ /* reset current mask, if any */
+ error = ps_dimension_reset_mask( dim, end_point, memory );
+ if ( error )
+ goto Exit;
+
+ /* set bits in new mask */
+ error = ps_mask_table_set_bits( &dim->masks, source,
+ source_pos, source_bits, memory );
+
+ Exit:
+ return error;
+ }
+
+
+ /* add a new single stem (called from "T1Stem" method) */
+ static FT_Error
+ ps_dimension_add_t1stem( PS_Dimension dim,
+ FT_Int pos,
+ FT_Int len,
+ FT_Memory memory,
+ FT_Int *aindex )
+ {
+ FT_Error error = 0;
+ FT_UInt flags = 0;
+
+
+ /* detect ghost stem */
+ if ( len < 0 )
+ {
+ flags |= PS_HINT_FLAG_GHOST;
+ if ( len == -21 )
+ {
+ flags |= PS_HINT_FLAG_BOTTOM;
+ pos += len;
+ }
+ len = 0;
+ }
+
+ if ( aindex )
+ *aindex = -1;
+
+ /* now, lookup stem in the current hints table */
+ {
+ PS_Mask mask;
+ FT_UInt idx;
+ FT_UInt max = dim->hints.num_hints;
+ PS_Hint hint = dim->hints.hints;
+
+
+ for ( idx = 0; idx < max; idx++, hint++ )
+ {
+ if ( hint->pos == pos && hint->len == len )
+ break;
+ }
+
+ /* we need to create a new hint in the table */
+ if ( idx >= max )
+ {
+ error = ps_hint_table_alloc( &dim->hints, memory, &hint );
+ if ( error )
+ goto Exit;
+
+ hint->pos = pos;
+ hint->len = len;
+ hint->flags = flags;
+ }
+
+ /* now, store the hint in the current mask */
+ error = ps_mask_table_last( &dim->masks, memory, &mask );
+ if ( error )
+ goto Exit;
+
+ error = ps_mask_set_bit( mask, idx, memory );
+ if ( error )
+ goto Exit;
+
+ if ( aindex )
+ *aindex = (FT_Int)idx;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /* add a "hstem3/vstem3" counter to our dimension table */
+ static FT_Error
+ ps_dimension_add_counter( PS_Dimension dim,
+ FT_Int hint1,
+ FT_Int hint2,
+ FT_Int hint3,
+ FT_Memory memory )
+ {
+ FT_Error error = 0;
+ FT_UInt count = dim->counters.num_masks;
+ PS_Mask counter = dim->counters.masks;
+
+
+ /* try to find an existing counter mask that already uses */
+ /* one of these stems here */
+ for ( ; count > 0; count--, counter++ )
+ {
+ if ( ps_mask_test_bit( counter, hint1 ) ||
+ ps_mask_test_bit( counter, hint2 ) ||
+ ps_mask_test_bit( counter, hint3 ) )
+ break;
+ }
+
+ /* create a new counter when needed */
+ if ( count == 0 )
+ {
+ error = ps_mask_table_alloc( &dim->counters, memory, &counter );
+ if ( error )
+ goto Exit;
+ }
+
+ /* now, set the bits for our hints in the counter mask */
+ error = ps_mask_set_bit( counter, hint1, memory );
+ if ( error )
+ goto Exit;
+
+ error = ps_mask_set_bit( counter, hint2, memory );
+ if ( error )
+ goto Exit;
+
+ error = ps_mask_set_bit( counter, hint3, memory );
+ if ( error )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /* end of recording session for a given dimension */
+ static FT_Error
+ ps_dimension_end( PS_Dimension dim,
+ FT_UInt end_point,
+ FT_Memory memory )
+ {
+ /* end hint mask table */
+ ps_dimension_end_mask( dim, end_point );
+
+ /* merge all counter masks into independent "paths" */
+ return ps_mask_table_merge_all( &dim->counters, memory );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** PS_RECORDER MANAGEMENT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /* destroy hints */
+ FT_LOCAL( void )
+ ps_hints_done( PS_Hints hints )
+ {
+ FT_Memory memory = hints->memory;
+
+
+ ps_dimension_done( &hints->dimension[0], memory );
+ ps_dimension_done( &hints->dimension[1], memory );
+
+ hints->error = 0;
+ hints->memory = 0;
+ }
+
+
+ FT_LOCAL( FT_Error )
+ ps_hints_init( PS_Hints hints,
+ FT_Memory memory )
+ {
+ FT_MEM_ZERO( hints, sizeof ( *hints ) );
+ hints->memory = memory;
+ return 0;
+ }
+
+
+ /* initialize a hints for a new session */
+ static void
+ ps_hints_open( PS_Hints hints,
+ PS_Hint_Type hint_type )
+ {
+ switch ( hint_type )
+ {
+ case PS_HINT_TYPE_1:
+ case PS_HINT_TYPE_2:
+ hints->error = 0;
+ hints->hint_type = hint_type;
+
+ ps_dimension_init( &hints->dimension[0] );
+ ps_dimension_init( &hints->dimension[1] );
+ break;
+
+ default:
+ hints->error = PSH_Err_Invalid_Argument;
+ hints->hint_type = hint_type;
+
+ FT_ERROR(( "ps_hints_open: invalid charstring type!\n" ));
+ break;
+ }
+ }
+
+
+ /* add one or more stems to the current hints table */
+ static void
+ ps_hints_stem( PS_Hints hints,
+ FT_Int dimension,
+ FT_UInt count,
+ FT_Long* stems )
+ {
+ if ( !hints->error )
+ {
+ /* limit "dimension" to 0..1 */
+ if ( dimension < 0 || dimension > 1 )
+ {
+ FT_ERROR(( "ps_hints_stem: invalid dimension (%d) used\n",
+ dimension ));
+ dimension = ( dimension != 0 );
+ }
+
+ /* record the stems in the current hints/masks table */
+ switch ( hints->hint_type )
+ {
+ case PS_HINT_TYPE_1: /* Type 1 "hstem" or "vstem" operator */
+ case PS_HINT_TYPE_2: /* Type 2 "hstem" or "vstem" operator */
+ {
+ PS_Dimension dim = &hints->dimension[dimension];
+
+
+ for ( ; count > 0; count--, stems += 2 )
+ {
+ FT_Error error;
+ FT_Memory memory = hints->memory;
+
+
+ error = ps_dimension_add_t1stem(
+ dim, (FT_Int)stems[0], (FT_Int)stems[1],
+ memory, NULL );
+ if ( error )
+ {
+ FT_ERROR(( "ps_hints_stem: could not add stem"
+ " (%d,%d) to hints table\n", stems[0], stems[1] ));
+
+ hints->error = error;
+ return;
+ }
+ }
+ break;
+ }
+
+ default:
+ FT_ERROR(( "ps_hints_stem: called with invalid hint type (%d)\n",
+ hints->hint_type ));
+ break;
+ }
+ }
+ }
+
+
+ /* add one Type1 counter stem to the current hints table */
+ static void
+ ps_hints_t1stem3( PS_Hints hints,
+ FT_Int dimension,
+ FT_Long* stems )
+ {
+ FT_Error error = 0;
+
+
+ if ( !hints->error )
+ {
+ PS_Dimension dim;
+ FT_Memory memory = hints->memory;
+ FT_Int count;
+ FT_Int idx[3];
+
+
+ /* limit "dimension" to 0..1 */
+ if ( dimension < 0 || dimension > 1 )
+ {
+ FT_ERROR(( "ps_hints_t1stem3: invalid dimension (%d) used\n",
+ dimension ));
+ dimension = ( dimension != 0 );
+ }
+
+ dim = &hints->dimension[dimension];
+
+ /* there must be 6 elements in the 'stem' array */
+ if ( hints->hint_type == PS_HINT_TYPE_1 )
+ {
+ /* add the three stems to our hints/masks table */
+ for ( count = 0; count < 3; count++, stems += 2 )
+ {
+ error = ps_dimension_add_t1stem(
+ dim, (FT_Int)stems[0], (FT_Int)stems[1],
+ memory, &idx[count] );
+ if ( error )
+ goto Fail;
+ }
+
+ /* now, add the hints to the counters table */
+ error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2],
+ memory );
+ if ( error )
+ goto Fail;
+ }
+ else
+ {
+ FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type!\n" ));
+ error = PSH_Err_Invalid_Argument;
+ goto Fail;
+ }
+ }
+
+ return;
+
+ Fail:
+ FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" ));
+ hints->error = error;
+ }
+
+
+ /* reset hints (only with Type 1 hints) */
+ static void
+ ps_hints_t1reset( PS_Hints hints,
+ FT_UInt end_point )
+ {
+ FT_Error error = 0;
+
+
+ if ( !hints->error )
+ {
+ FT_Memory memory = hints->memory;
+
+
+ if ( hints->hint_type == PS_HINT_TYPE_1 )
+ {
+ error = ps_dimension_reset_mask( &hints->dimension[0],
+ end_point, memory );
+ if ( error )
+ goto Fail;
+
+ error = ps_dimension_reset_mask( &hints->dimension[1],
+ end_point, memory );
+ if ( error )
+ goto Fail;
+ }
+ else
+ {
+ /* invalid hint type */
+ error = PSH_Err_Invalid_Argument;
+ goto Fail;
+ }
+ }
+ return;
+
+ Fail:
+ hints->error = error;
+ }
+
+
+ /* Type2 "hintmask" operator, add a new hintmask to each direction */
+ static void
+ ps_hints_t2mask( PS_Hints hints,
+ FT_UInt end_point,
+ FT_UInt bit_count,
+ const FT_Byte* bytes )
+ {
+ FT_Error error;
+
+
+ if ( !hints->error )
+ {
+ PS_Dimension dim = hints->dimension;
+ FT_Memory memory = hints->memory;
+ FT_UInt count1 = dim[0].hints.num_hints;
+ FT_UInt count2 = dim[1].hints.num_hints;
+
+
+ /* check bit count; must be equal to current total hint count */
+ if ( bit_count != count1 + count2 )
+ {
+ FT_ERROR(( "ps_hints_t2mask: "
+ "called with invalid bitcount %d (instead of %d)\n",
+ bit_count, count1 + count2 ));
+
+ /* simply ignore the operator */
+ return;
+ }
+
+ /* set-up new horizontal and vertical hint mask now */
+ error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1,
+ end_point, memory );
+ if ( error )
+ goto Fail;
+
+ error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2,
+ end_point, memory );
+ if ( error )
+ goto Fail;
+ }
+ return;
+
+ Fail:
+ hints->error = error;
+ }
+
+
+ static void
+ ps_hints_t2counter( PS_Hints hints,
+ FT_UInt bit_count,
+ const FT_Byte* bytes )
+ {
+ FT_Error error;
+
+
+ if ( !hints->error )
+ {
+ PS_Dimension dim = hints->dimension;
+ FT_Memory memory = hints->memory;
+ FT_UInt count1 = dim[0].hints.num_hints;
+ FT_UInt count2 = dim[1].hints.num_hints;
+
+
+ /* check bit count, must be equal to current total hint count */
+ if ( bit_count != count1 + count2 )
+ {
+ FT_ERROR(( "ps_hints_t2counter: "
+ "called with invalid bitcount %d (instead of %d)\n",
+ bit_count, count1 + count2 ));
+
+ /* simply ignore the operator */
+ return;
+ }
+
+ /* set-up new horizontal and vertical hint mask now */
+ error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1,
+ 0, memory );
+ if ( error )
+ goto Fail;
+
+ error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2,
+ 0, memory );
+ if ( error )
+ goto Fail;
+ }
+ return;
+
+ Fail:
+ hints->error = error;
+ }
+
+
+ /* end recording session */
+ static FT_Error
+ ps_hints_close( PS_Hints hints,
+ FT_UInt end_point )
+ {
+ FT_Error error;
+
+
+ error = hints->error;
+ if ( !error )
+ {
+ FT_Memory memory = hints->memory;
+ PS_Dimension dim = hints->dimension;
+
+
+ error = ps_dimension_end( &dim[0], end_point, memory );
+ if ( !error )
+ {
+ error = ps_dimension_end( &dim[1], end_point, memory );
+ }
+ }
+
+#ifdef DEBUG_HINTER
+ if ( !error )
+ ps_debug_hints = hints;
+#endif
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE 1 HINTS RECORDING INTERFACE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ t1_hints_open( T1_Hints hints )
+ {
+ ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
+ }
+
+ static void
+ t1_hints_stem( T1_Hints hints,
+ FT_Int dimension,
+ FT_Long* coords )
+ {
+ ps_hints_stem( (PS_Hints)hints, dimension, 1, coords );
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t1_hints_funcs_init( T1_Hints_FuncsRec* funcs )
+ {
+ FT_MEM_ZERO( (char*)funcs, sizeof ( *funcs ) );
+
+ funcs->open = (T1_Hints_OpenFunc) t1_hints_open;
+ funcs->close = (T1_Hints_CloseFunc) ps_hints_close;
+ funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem;
+ funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3;
+ funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset;
+ funcs->apply = (T1_Hints_ApplyFunc) ps_hints_apply;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE 2 HINTS RECORDING INTERFACE *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static void
+ t2_hints_open( T2_Hints hints )
+ {
+ ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 );
+ }
+
+
+ static void
+ t2_hints_stems( T2_Hints hints,
+ FT_Int dimension,
+ FT_Int count,
+ FT_Fixed* coords )
+ {
+ FT_Pos stems[32], y, n;
+ FT_Int total = count;
+
+
+ y = 0;
+ while ( total > 0 )
+ {
+ /* determine number of stems to write */
+ count = total;
+ if ( count > 16 )
+ count = 16;
+
+ /* compute integer stem positions in font units */
+ for ( n = 0; n < count * 2; n++ )
+ {
+ y += coords[n];
+ stems[n] = ( y + 0x8000L ) >> 16;
+ }
+
+ /* compute lengths */
+ for ( n = 0; n < count * 2; n += 2 )
+ stems[n + 1] = stems[n + 1] - stems[n];
+
+ /* add them to the current dimension */
+ ps_hints_stem( (PS_Hints)hints, dimension, count, stems );
+
+ total -= count;
+ }
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t2_hints_funcs_init( T2_Hints_FuncsRec* funcs )
+ {
+ FT_MEM_ZERO( funcs, sizeof ( *funcs ) );
+
+ funcs->open = (T2_Hints_OpenFunc) t2_hints_open;
+ funcs->close = (T2_Hints_CloseFunc) ps_hints_close;
+ funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems;
+ funcs->hintmask= (T2_Hints_MaskFunc) ps_hints_t2mask;
+ funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter;
+ funcs->apply = (T2_Hints_ApplyFunc) ps_hints_apply;
+ }
+
+
+/* END */
diff --git a/src/pshinter/pshrec.h b/src/pshinter/pshrec.h
new file mode 100644
index 0000000..f7ef900
--- /dev/null
+++ b/src/pshinter/pshrec.h
@@ -0,0 +1,176 @@
+/***************************************************************************/
+/* */
+/* pshrec.h */
+/* */
+/* Postscript (Type1/Type2) hints recorder (specification). */
+/* */
+/* Copyright 2001, 2002, 2003, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /**************************************************************************/
+ /* */
+ /* The functions defined here are called from the Type 1, CID and CFF */
+ /* font drivers to record the hints of a given character/glyph. */
+ /* */
+ /* The hints are recorded in a unified format, and are later processed */
+ /* by the `optimizer' and `fitter' to adjust the outlines to the pixel */
+ /* grid. */
+ /* */
+ /**************************************************************************/
+
+
+#ifndef __PSHREC_H__
+#define __PSHREC_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+#include "pshglob.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GLYPH HINTS RECORDER INTERNALS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* handle to hint record */
+ typedef struct PS_HintRec_* PS_Hint;
+
+ /* hint types */
+ typedef enum
+ {
+ PS_HINT_TYPE_1 = 1,
+ PS_HINT_TYPE_2 = 2
+
+ } PS_Hint_Type;
+
+
+ /* hint flags */
+ typedef enum
+ {
+ PS_HINT_FLAG_GHOST = 1,
+ PS_HINT_FLAG_BOTTOM = 2
+
+ } PS_Hint_Flags;
+
+
+ /* hint descriptor */
+ typedef struct PS_HintRec_
+ {
+ FT_Int pos;
+ FT_Int len;
+ FT_UInt flags;
+
+ } PS_HintRec;
+
+
+#define ps_hint_is_active( x ) ( (x)->flags & PS_HINT_FLAG_ACTIVE )
+#define ps_hint_is_ghost( x ) ( (x)->flags & PS_HINT_FLAG_GHOST )
+#define ps_hint_is_bottom( x ) ( (x)->flags & PS_HINT_FLAG_BOTTOM )
+
+
+ /* hints table descriptor */
+ typedef struct PS_Hint_TableRec_
+ {
+ FT_UInt num_hints;
+ FT_UInt max_hints;
+ PS_Hint hints;
+
+ } PS_Hint_TableRec, *PS_Hint_Table;
+
+
+ /* hint and counter mask descriptor */
+ typedef struct PS_MaskRec_
+ {
+ FT_UInt num_bits;
+ FT_UInt max_bits;
+ FT_Byte* bytes;
+ FT_UInt end_point;
+
+ } PS_MaskRec, *PS_Mask;
+
+
+ /* masks and counters table descriptor */
+ typedef struct PS_Mask_TableRec_
+ {
+ FT_UInt num_masks;
+ FT_UInt max_masks;
+ PS_Mask masks;
+
+ } PS_Mask_TableRec, *PS_Mask_Table;
+
+
+ /* dimension-specific hints descriptor */
+ typedef struct PS_DimensionRec_
+ {
+ PS_Hint_TableRec hints;
+ PS_Mask_TableRec masks;
+ PS_Mask_TableRec counters;
+
+ } PS_DimensionRec, *PS_Dimension;
+
+
+ /* glyph hints descriptor */
+ /* dimension 0 => X coordinates + vertical hints/stems */
+ /* dimension 1 => Y coordinates + horizontal hints/stems */
+ typedef struct PS_HintsRec_
+ {
+ FT_Memory memory;
+ FT_Error error;
+ FT_UInt32 magic;
+ PS_Hint_Type hint_type;
+ PS_DimensionRec dimension[2];
+
+ } PS_HintsRec, *PS_Hints;
+
+ /* */
+
+ /* initialize hints recorder */
+ FT_LOCAL( FT_Error )
+ ps_hints_init( PS_Hints hints,
+ FT_Memory memory );
+
+ /* finalize hints recorder */
+ FT_LOCAL( void )
+ ps_hints_done( PS_Hints hints );
+
+ /* initialize Type1 hints recorder interface */
+ FT_LOCAL( void )
+ t1_hints_funcs_init( T1_Hints_FuncsRec* funcs );
+
+ /* initialize Type2 hints recorder interface */
+ FT_LOCAL( void )
+ t2_hints_funcs_init( T2_Hints_FuncsRec* funcs );
+
+
+#ifdef DEBUG_HINTER
+ extern PS_Hints ps_debug_hints;
+ extern int ps_debug_no_horz_hints;
+ extern int ps_debug_no_vert_hints;
+#endif
+
+ /* */
+
+
+FT_END_HEADER
+
+
+#endif /* __PS_HINTER_RECORD_H__ */
+
+
+/* END */
diff --git a/src/pshinter/rules.mk b/src/pshinter/rules.mk
new file mode 100644
index 0000000..5777339
--- /dev/null
+++ b/src/pshinter/rules.mk
@@ -0,0 +1,72 @@
+#
+# FreeType 2 PSHinter driver configuration rules
+#
+
+
+# Copyright 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# PSHINTER driver directory
+#
+PSHINTER_DIR := $(SRC_DIR)/pshinter
+
+
+# compilation flags for the driver
+#
+PSHINTER_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PSHINTER_DIR))
+
+
+# PSHINTER driver sources (i.e., C files)
+#
+PSHINTER_DRV_SRC := $(PSHINTER_DIR)/pshrec.c \
+ $(PSHINTER_DIR)/pshglob.c \
+ $(PSHINTER_DIR)/pshmod.c \
+ $(PSHINTER_DIR)/pshalgo.c
+
+
+# PSHINTER driver headers
+#
+PSHINTER_DRV_H := $(PSHINTER_DRV_SRC:%c=%h) \
+ $(PSHINTER_DIR)/pshnterr.h
+
+
+# PSHINTER driver object(s)
+#
+# PSHINTER_DRV_OBJ_M is used during `multi' builds.
+# PSHINTER_DRV_OBJ_S is used during `single' builds.
+#
+PSHINTER_DRV_OBJ_M := $(PSHINTER_DRV_SRC:$(PSHINTER_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PSHINTER_DRV_OBJ_S := $(OBJ_DIR)/pshinter.$O
+
+# PSHINTER driver source file for single build
+#
+PSHINTER_DRV_SRC_S := $(PSHINTER_DIR)/pshinter.c
+
+
+# PSHINTER driver - single object
+#
+$(PSHINTER_DRV_OBJ_S): $(PSHINTER_DRV_SRC_S) $(PSHINTER_DRV_SRC) \
+ $(FREETYPE_H) $(PSHINTER_DRV_H)
+ $(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSHINTER_DRV_SRC_S))
+
+
+# PSHINTER driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PSHINTER_DIR)/%.c $(FREETYPE_H) $(PSHINTER_DRV_H)
+ $(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PSHINTER_DRV_OBJ_S)
+DRV_OBJS_M += $(PSHINTER_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/psnames/Jamfile b/src/psnames/Jamfile
new file mode 100644
index 0000000..d85c1e9
--- /dev/null
+++ b/src/psnames/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/psnames Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) psnames ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = psmodule ;
+ }
+ else
+ {
+ _sources = psnames ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/psnames Jamfile
diff --git a/src/psnames/module.mk b/src/psnames/module.mk
new file mode 100644
index 0000000..a93063b
--- /dev/null
+++ b/src/psnames/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 PSnames module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += PSNAMES_MODULE
+
+define PSNAMES_MODULE
+$(OPEN_DRIVER)psnames_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)psnames $(ECHO_DRIVER_DESC)Postscript & Unicode Glyph name handling$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/psnames/psmodule.c b/src/psnames/psmodule.c
new file mode 100644
index 0000000..8d8c476
--- /dev/null
+++ b/src/psnames/psmodule.c
@@ -0,0 +1,458 @@
+/***************************************************************************/
+/* */
+/* psmodule.c */
+/* */
+/* PSNames module implementation (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+
+#include "psmodule.h"
+#include "pstables.h"
+
+#include "psnamerr.h"
+
+
+#ifndef FT_CONFIG_OPTION_NO_POSTSCRIPT_NAMES
+
+
+#ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
+
+
+#define VARIANT_BIT 0x80000000UL
+#define BASE_GLYPH( code ) ( (code) & ~VARIANT_BIT )
+
+
+ /* Return the Unicode value corresponding to a given glyph. Note that */
+ /* we do deal with glyph variants by detecting a non-initial dot in */
+ /* the name, as in `A.swash' or `e.final'; in this case, the */
+ /* VARIANT_BIT is set in the return value. */
+ /* */
+ static FT_UInt32
+ ps_unicode_value( const char* glyph_name )
+ {
+ /* If the name begins with `uni', then the glyph name may be a */
+ /* hard-coded unicode character code. */
+ if ( glyph_name[0] == 'u' &&
+ glyph_name[1] == 'n' &&
+ glyph_name[2] == 'i' )
+ {
+ /* determine whether the next four characters following are */
+ /* hexadecimal. */
+
+ /* XXX: Add code to deal with ligatures, i.e. glyph names like */
+ /* `uniXXXXYYYYZZZZ'... */
+
+ FT_Int count;
+ FT_ULong value = 0;
+ const char* p = glyph_name + 3;
+
+
+ for ( count = 4; count > 0; count--, p++ )
+ {
+ char c = *p;
+ unsigned int d;
+
+
+ d = (unsigned char)c - '0';
+ if ( d >= 10 )
+ {
+ d = (unsigned char)c - 'A';
+ if ( d >= 6 )
+ d = 16;
+ else
+ d += 10;
+ }
+
+ /* Exit if a non-uppercase hexadecimal character was found */
+ /* -- this also catches character codes below `0' since such */
+ /* negative numbers cast to `unsigned int' are far too big. */
+ if ( d >= 16 )
+ break;
+
+ value = ( value << 4 ) + d;
+ }
+
+ /* there must be exactly four hex digits */
+ if ( count == 0 )
+ {
+ if ( *p == '\0' )
+ return value;
+ if ( *p == '.' )
+ return value | VARIANT_BIT;
+ }
+ }
+
+ /* If the name begins with `u', followed by four to six uppercase */
+ /* hexadecimal digits, it is a hard-coded unicode character code. */
+ if ( glyph_name[0] == 'u' )
+ {
+ FT_Int count;
+ FT_ULong value = 0;
+ const char* p = glyph_name + 1;
+
+
+ for ( count = 6; count > 0; count--, p++ )
+ {
+ char c = *p;
+ unsigned int d;
+
+
+ d = (unsigned char)c - '0';
+ if ( d >= 10 )
+ {
+ d = (unsigned char)c - 'A';
+ if ( d >= 6 )
+ d = 16;
+ else
+ d += 10;
+ }
+
+ if ( d >= 16 )
+ break;
+
+ value = ( value << 4 ) + d;
+ }
+
+ if ( count <= 2 )
+ {
+ if ( *p == '\0' )
+ return value;
+ if ( *p == '.' )
+ return value | VARIANT_BIT;
+ }
+ }
+
+ /* Look for a non-initial dot in the glyph name in order to */
+ /* find variants like `A.swash', `e.final', etc. */
+ {
+ const char* p = glyph_name;
+ const char* dot = NULL;
+
+
+ for ( ; *p; p++ )
+ {
+ if ( *p == '.' && p > glyph_name )
+ {
+ dot = p;
+ break;
+ }
+ }
+
+ /* now look up the glyph in the Adobe Glyph List */
+ if ( !dot )
+ return ft_get_adobe_glyph_index( glyph_name, p );
+ else
+ return ft_get_adobe_glyph_index( glyph_name, dot ) | VARIANT_BIT;
+ }
+ }
+
+
+ /* ft_qsort callback to sort the unicode map */
+ FT_CALLBACK_DEF( int )
+ compare_uni_maps( const void* a,
+ const void* b )
+ {
+ PS_UniMap* map1 = (PS_UniMap*)a;
+ PS_UniMap* map2 = (PS_UniMap*)b;
+ FT_UInt32 unicode1 = BASE_GLYPH( map1->unicode );
+ FT_UInt32 unicode2 = BASE_GLYPH( map2->unicode );
+
+
+ /* sort base glyphs before glyph variants */
+ if ( unicode1 == unicode2 )
+ return map1->unicode - map2->unicode;
+ else
+ return unicode1 - unicode2;
+ }
+
+
+ /* Build a table that maps Unicode values to glyph indices. */
+ static FT_Error
+ ps_unicodes_init( FT_Memory memory,
+ PS_Unicodes table,
+ FT_UInt num_glyphs,
+ PS_GetGlyphNameFunc get_glyph_name,
+ PS_FreeGlyphNameFunc free_glyph_name,
+ FT_Pointer glyph_data )
+ {
+ FT_Error error;
+
+
+ /* we first allocate the table */
+ table->num_maps = 0;
+ table->maps = 0;
+
+ if ( !FT_NEW_ARRAY( table->maps, num_glyphs ) )
+ {
+ FT_UInt n;
+ FT_UInt count;
+ PS_UniMap* map;
+ FT_UInt32 uni_char;
+
+
+ map = table->maps;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ const char* gname = get_glyph_name( glyph_data, n );
+
+
+ if ( gname )
+ {
+ uni_char = ps_unicode_value( gname );
+
+ if ( BASE_GLYPH( uni_char ) != 0 )
+ {
+ map->unicode = uni_char;
+ map->glyph_index = n;
+ map++;
+ }
+
+ if ( free_glyph_name )
+ free_glyph_name( glyph_data, gname );
+ }
+ }
+
+ /* now compress the table a bit */
+ count = (FT_UInt)( map - table->maps );
+
+ if ( count == 0 )
+ {
+ FT_FREE( table->maps );
+ if ( !error )
+ error = PSnames_Err_Invalid_Argument; /* No unicode chars here! */
+ }
+ else {
+ /* Reallocate if the number of used entries is much smaller. */
+ if ( count < num_glyphs / 2 )
+ {
+ (void)FT_RENEW_ARRAY( table->maps, num_glyphs, count );
+ error = PSnames_Err_Ok;
+ }
+
+ /* Sort the table in increasing order of unicode values, */
+ /* taking care of glyph variants. */
+ ft_qsort( table->maps, count, sizeof ( PS_UniMap ),
+ compare_uni_maps );
+ }
+
+ table->num_maps = count;
+ }
+
+ return error;
+ }
+
+
+ static FT_UInt
+ ps_unicodes_char_index( PS_Unicodes table,
+ FT_UInt32 unicode )
+ {
+ PS_UniMap *min, *max, *mid, *result = NULL;
+
+
+ /* Perform a binary search on the table. */
+
+ min = table->maps;
+ max = min + table->num_maps - 1;
+
+ while ( min <= max )
+ {
+ FT_UInt32 base_glyph;
+
+
+ mid = min + ( ( max - min ) >> 1 );
+
+ if ( mid->unicode == unicode )
+ {
+ result = mid;
+ break;
+ }
+
+ base_glyph = BASE_GLYPH( mid->unicode );
+
+ if ( base_glyph == unicode )
+ result = mid; /* remember match but continue search for base glyph */
+
+ if ( min == max )
+ break;
+
+ if ( base_glyph < unicode )
+ min = mid + 1;
+ else
+ max = mid - 1;
+ }
+
+ if ( result )
+ return result->glyph_index;
+ else
+ return 0;
+ }
+
+
+ static FT_ULong
+ ps_unicodes_char_next( PS_Unicodes table,
+ FT_UInt32 *unicode )
+ {
+ FT_UInt result = 0;
+ FT_UInt32 char_code = *unicode + 1;
+
+
+ {
+ FT_UInt min = 0;
+ FT_UInt max = table->num_maps;
+ FT_UInt mid;
+ PS_UniMap* map;
+ FT_UInt32 base_glyph;
+
+
+ while ( min < max )
+ {
+ mid = min + ( ( max - min ) >> 1 );
+ map = table->maps + mid;
+
+ if ( map->unicode == char_code )
+ {
+ result = map->glyph_index;
+ goto Exit;
+ }
+
+ base_glyph = BASE_GLYPH( map->unicode );
+
+ if ( base_glyph == char_code )
+ result = map->glyph_index;
+
+ if ( base_glyph < char_code )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+
+ if ( result )
+ goto Exit; /* we have a variant glyph */
+
+ /* we didn't find it; check whether we have a map just above it */
+ char_code = 0;
+
+ if ( min < table->num_maps )
+ {
+ map = table->maps + min;
+ result = map->glyph_index;
+ char_code = BASE_GLYPH( map->unicode );
+ }
+ }
+
+ Exit:
+ *unicode = char_code;
+ return result;
+ }
+
+
+#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */
+
+
+ static const char*
+ ps_get_macintosh_name( FT_UInt name_index )
+ {
+ if ( name_index >= FT_NUM_MAC_NAMES )
+ name_index = 0;
+
+ return ft_standard_glyph_names + ft_mac_names[name_index];
+ }
+
+
+ static const char*
+ ps_get_standard_strings( FT_UInt sid )
+ {
+ if ( sid >= FT_NUM_SID_NAMES )
+ return 0;
+
+ return ft_standard_glyph_names + ft_sid_names[sid];
+ }
+
+
+ static
+ const FT_Service_PsCMapsRec pscmaps_interface =
+ {
+#ifdef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST
+
+ (PS_Unicode_ValueFunc) ps_unicode_value,
+ (PS_Unicodes_InitFunc) ps_unicodes_init,
+ (PS_Unicodes_CharIndexFunc)ps_unicodes_char_index,
+ (PS_Unicodes_CharNextFunc) ps_unicodes_char_next,
+
+#else
+
+ 0,
+ 0,
+ 0,
+ 0,
+
+#endif /* FT_CONFIG_OPTION_ADOBE_GLYPH_LIST */
+
+ (PS_Macintosh_NameFunc) ps_get_macintosh_name,
+ (PS_Adobe_Std_StringsFunc) ps_get_standard_strings,
+
+ t1_standard_encoding,
+ t1_expert_encoding
+ };
+
+
+ static const FT_ServiceDescRec pscmaps_services[] =
+ {
+ { FT_SERVICE_ID_POSTSCRIPT_CMAPS, &pscmaps_interface },
+ { NULL, NULL }
+ };
+
+
+ static FT_Pointer
+ psnames_get_service( FT_Module module,
+ const char* service_id )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( pscmaps_services, service_id );
+ }
+
+#endif /* !FT_CONFIG_OPTION_NO_POSTSCRIPT_NAMES */
+
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class psnames_module_class =
+ {
+ 0, /* this is not a font driver, nor a renderer */
+ sizeof ( FT_ModuleRec ),
+
+ "psnames", /* driver name */
+ 0x10000L, /* driver version */
+ 0x20000L, /* driver requires FreeType 2 or above */
+
+#ifdef FT_CONFIG_OPTION_NO_POSTSCRIPT_NAMES
+ 0,
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+#else
+ (void*)&pscmaps_interface, /* module specific interface */
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) psnames_get_service
+#endif
+ };
+
+
+/* END */
diff --git a/src/psnames/psmodule.h b/src/psnames/psmodule.h
new file mode 100644
index 0000000..232fdfb
--- /dev/null
+++ b/src/psnames/psmodule.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* psmodule.h */
+/* */
+/* High-level PSNames module interface (specification). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __PSMODULE_H__
+#define __PSMODULE_H__
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) psnames_module_class;
+
+
+FT_END_HEADER
+
+#endif /* __PSMODULE_H__ */
+
+
+/* END */
diff --git a/src/psnames/psnamerr.h b/src/psnames/psnamerr.h
new file mode 100644
index 0000000..ae1541d
--- /dev/null
+++ b/src/psnames/psnamerr.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* psnamerr.h */
+/* */
+/* PS names module error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the PS names module error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __PSNAMERR_H__
+#define __PSNAMERR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX PSnames_Err_
+#define FT_ERR_BASE FT_Mod_Err_PSnames
+
+#include FT_ERRORS_H
+
+#endif /* __PSNAMERR_H__ */
+
+
+/* END */
diff --git a/src/psnames/psnames.c b/src/psnames/psnames.c
new file mode 100644
index 0000000..d6ed998
--- /dev/null
+++ b/src/psnames/psnames.c
@@ -0,0 +1,25 @@
+/***************************************************************************/
+/* */
+/* psnames.c */
+/* */
+/* FreeType PSNames module component (body only). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "psmodule.c"
+
+
+/* END */
diff --git a/src/psnames/pstables.h b/src/psnames/pstables.h
new file mode 100644
index 0000000..cc40ef7
--- /dev/null
+++ b/src/psnames/pstables.h
@@ -0,0 +1,4090 @@
+/***************************************************************************/
+/* */
+/* pstables.h */
+/* */
+/* PostScript glyph names. */
+/* */
+/* Copyright 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /* This file has been generated automatically -- do not edit! */
+
+
+ static const char ft_standard_glyph_names[3696] =
+ {
+ '.','n','u','l','l', 0,
+ 'n','o','n','m','a','r','k','i','n','g','r','e','t','u','r','n', 0,
+ 'n','o','t','e','q','u','a','l', 0,
+ 'i','n','f','i','n','i','t','y', 0,
+ 'l','e','s','s','e','q','u','a','l', 0,
+ 'g','r','e','a','t','e','r','e','q','u','a','l', 0,
+ 'p','a','r','t','i','a','l','d','i','f','f', 0,
+ 's','u','m','m','a','t','i','o','n', 0,
+ 'p','r','o','d','u','c','t', 0,
+ 'p','i', 0,
+ 'i','n','t','e','g','r','a','l', 0,
+ 'O','m','e','g','a', 0,
+ 'r','a','d','i','c','a','l', 0,
+ 'a','p','p','r','o','x','e','q','u','a','l', 0,
+ 'D','e','l','t','a', 0,
+ 'n','o','n','b','r','e','a','k','i','n','g','s','p','a','c','e', 0,
+ 'l','o','z','e','n','g','e', 0,
+ 'a','p','p','l','e', 0,
+ 'f','r','a','n','c', 0,
+ 'G','b','r','e','v','e', 0,
+ 'g','b','r','e','v','e', 0,
+ 'I','d','o','t','a','c','c','e','n','t', 0,
+ 'S','c','e','d','i','l','l','a', 0,
+ 's','c','e','d','i','l','l','a', 0,
+ 'C','a','c','u','t','e', 0,
+ 'c','a','c','u','t','e', 0,
+ 'C','c','a','r','o','n', 0,
+ 'c','c','a','r','o','n', 0,
+ 'd','c','r','o','a','t', 0,
+ '.','n','o','t','d','e','f', 0,
+ 's','p','a','c','e', 0,
+ 'e','x','c','l','a','m', 0,
+ 'q','u','o','t','e','d','b','l', 0,
+ 'n','u','m','b','e','r','s','i','g','n', 0,
+ 'd','o','l','l','a','r', 0,
+ 'p','e','r','c','e','n','t', 0,
+ 'a','m','p','e','r','s','a','n','d', 0,
+ 'q','u','o','t','e','r','i','g','h','t', 0,
+ 'p','a','r','e','n','l','e','f','t', 0,
+ 'p','a','r','e','n','r','i','g','h','t', 0,
+ 'a','s','t','e','r','i','s','k', 0,
+ 'p','l','u','s', 0,
+ 'c','o','m','m','a', 0,
+ 'h','y','p','h','e','n', 0,
+ 'p','e','r','i','o','d', 0,
+ 's','l','a','s','h', 0,
+ 'z','e','r','o', 0,
+ 'o','n','e', 0,
+ 't','w','o', 0,
+ 't','h','r','e','e', 0,
+ 'f','o','u','r', 0,
+ 'f','i','v','e', 0,
+ 's','i','x', 0,
+ 's','e','v','e','n', 0,
+ 'e','i','g','h','t', 0,
+ 'n','i','n','e', 0,
+ 'c','o','l','o','n', 0,
+ 's','e','m','i','c','o','l','o','n', 0,
+ 'l','e','s','s', 0,
+ 'e','q','u','a','l', 0,
+ 'g','r','e','a','t','e','r', 0,
+ 'q','u','e','s','t','i','o','n', 0,
+ 'a','t', 0,
+ 'A', 0,
+ 'B', 0,
+ 'C', 0,
+ 'D', 0,
+ 'E', 0,
+ 'F', 0,
+ 'G', 0,
+ 'H', 0,
+ 'I', 0,
+ 'J', 0,
+ 'K', 0,
+ 'L', 0,
+ 'M', 0,
+ 'N', 0,
+ 'O', 0,
+ 'P', 0,
+ 'Q', 0,
+ 'R', 0,
+ 'S', 0,
+ 'T', 0,
+ 'U', 0,
+ 'V', 0,
+ 'W', 0,
+ 'X', 0,
+ 'Y', 0,
+ 'Z', 0,
+ 'b','r','a','c','k','e','t','l','e','f','t', 0,
+ 'b','a','c','k','s','l','a','s','h', 0,
+ 'b','r','a','c','k','e','t','r','i','g','h','t', 0,
+ 'a','s','c','i','i','c','i','r','c','u','m', 0,
+ 'u','n','d','e','r','s','c','o','r','e', 0,
+ 'q','u','o','t','e','l','e','f','t', 0,
+ 'a', 0,
+ 'b', 0,
+ 'c', 0,
+ 'd', 0,
+ 'e', 0,
+ 'f', 0,
+ 'g', 0,
+ 'h', 0,
+ 'i', 0,
+ 'j', 0,
+ 'k', 0,
+ 'l', 0,
+ 'm', 0,
+ 'n', 0,
+ 'o', 0,
+ 'p', 0,
+ 'q', 0,
+ 'r', 0,
+ 's', 0,
+ 't', 0,
+ 'u', 0,
+ 'v', 0,
+ 'w', 0,
+ 'x', 0,
+ 'y', 0,
+ 'z', 0,
+ 'b','r','a','c','e','l','e','f','t', 0,
+ 'b','a','r', 0,
+ 'b','r','a','c','e','r','i','g','h','t', 0,
+ 'a','s','c','i','i','t','i','l','d','e', 0,
+ 'e','x','c','l','a','m','d','o','w','n', 0,
+ 'c','e','n','t', 0,
+ 's','t','e','r','l','i','n','g', 0,
+ 'f','r','a','c','t','i','o','n', 0,
+ 'y','e','n', 0,
+ 'f','l','o','r','i','n', 0,
+ 's','e','c','t','i','o','n', 0,
+ 'c','u','r','r','e','n','c','y', 0,
+ 'q','u','o','t','e','s','i','n','g','l','e', 0,
+ 'q','u','o','t','e','d','b','l','l','e','f','t', 0,
+ 'g','u','i','l','l','e','m','o','t','l','e','f','t', 0,
+ 'g','u','i','l','s','i','n','g','l','l','e','f','t', 0,
+ 'g','u','i','l','s','i','n','g','l','r','i','g','h','t', 0,
+ 'f','i', 0,
+ 'f','l', 0,
+ 'e','n','d','a','s','h', 0,
+ 'd','a','g','g','e','r', 0,
+ 'd','a','g','g','e','r','d','b','l', 0,
+ 'p','e','r','i','o','d','c','e','n','t','e','r','e','d', 0,
+ 'p','a','r','a','g','r','a','p','h', 0,
+ 'b','u','l','l','e','t', 0,
+ 'q','u','o','t','e','s','i','n','g','l','b','a','s','e', 0,
+ 'q','u','o','t','e','d','b','l','b','a','s','e', 0,
+ 'q','u','o','t','e','d','b','l','r','i','g','h','t', 0,
+ 'g','u','i','l','l','e','m','o','t','r','i','g','h','t', 0,
+ 'e','l','l','i','p','s','i','s', 0,
+ 'p','e','r','t','h','o','u','s','a','n','d', 0,
+ 'q','u','e','s','t','i','o','n','d','o','w','n', 0,
+ 'g','r','a','v','e', 0,
+ 'a','c','u','t','e', 0,
+ 'c','i','r','c','u','m','f','l','e','x', 0,
+ 't','i','l','d','e', 0,
+ 'm','a','c','r','o','n', 0,
+ 'b','r','e','v','e', 0,
+ 'd','o','t','a','c','c','e','n','t', 0,
+ 'd','i','e','r','e','s','i','s', 0,
+ 'r','i','n','g', 0,
+ 'c','e','d','i','l','l','a', 0,
+ 'h','u','n','g','a','r','u','m','l','a','u','t', 0,
+ 'o','g','o','n','e','k', 0,
+ 'c','a','r','o','n', 0,
+ 'e','m','d','a','s','h', 0,
+ 'A','E', 0,
+ 'o','r','d','f','e','m','i','n','i','n','e', 0,
+ 'L','s','l','a','s','h', 0,
+ 'O','s','l','a','s','h', 0,
+ 'O','E', 0,
+ 'o','r','d','m','a','s','c','u','l','i','n','e', 0,
+ 'a','e', 0,
+ 'd','o','t','l','e','s','s','i', 0,
+ 'l','s','l','a','s','h', 0,
+ 'o','s','l','a','s','h', 0,
+ 'o','e', 0,
+ 'g','e','r','m','a','n','d','b','l','s', 0,
+ 'o','n','e','s','u','p','e','r','i','o','r', 0,
+ 'l','o','g','i','c','a','l','n','o','t', 0,
+ 'm','u', 0,
+ 't','r','a','d','e','m','a','r','k', 0,
+ 'E','t','h', 0,
+ 'o','n','e','h','a','l','f', 0,
+ 'p','l','u','s','m','i','n','u','s', 0,
+ 'T','h','o','r','n', 0,
+ 'o','n','e','q','u','a','r','t','e','r', 0,
+ 'd','i','v','i','d','e', 0,
+ 'b','r','o','k','e','n','b','a','r', 0,
+ 'd','e','g','r','e','e', 0,
+ 't','h','o','r','n', 0,
+ 't','h','r','e','e','q','u','a','r','t','e','r','s', 0,
+ 't','w','o','s','u','p','e','r','i','o','r', 0,
+ 'r','e','g','i','s','t','e','r','e','d', 0,
+ 'm','i','n','u','s', 0,
+ 'e','t','h', 0,
+ 'm','u','l','t','i','p','l','y', 0,
+ 't','h','r','e','e','s','u','p','e','r','i','o','r', 0,
+ 'c','o','p','y','r','i','g','h','t', 0,
+ 'A','a','c','u','t','e', 0,
+ 'A','c','i','r','c','u','m','f','l','e','x', 0,
+ 'A','d','i','e','r','e','s','i','s', 0,
+ 'A','g','r','a','v','e', 0,
+ 'A','r','i','n','g', 0,
+ 'A','t','i','l','d','e', 0,
+ 'C','c','e','d','i','l','l','a', 0,
+ 'E','a','c','u','t','e', 0,
+ 'E','c','i','r','c','u','m','f','l','e','x', 0,
+ 'E','d','i','e','r','e','s','i','s', 0,
+ 'E','g','r','a','v','e', 0,
+ 'I','a','c','u','t','e', 0,
+ 'I','c','i','r','c','u','m','f','l','e','x', 0,
+ 'I','d','i','e','r','e','s','i','s', 0,
+ 'I','g','r','a','v','e', 0,
+ 'N','t','i','l','d','e', 0,
+ 'O','a','c','u','t','e', 0,
+ 'O','c','i','r','c','u','m','f','l','e','x', 0,
+ 'O','d','i','e','r','e','s','i','s', 0,
+ 'O','g','r','a','v','e', 0,
+ 'O','t','i','l','d','e', 0,
+ 'S','c','a','r','o','n', 0,
+ 'U','a','c','u','t','e', 0,
+ 'U','c','i','r','c','u','m','f','l','e','x', 0,
+ 'U','d','i','e','r','e','s','i','s', 0,
+ 'U','g','r','a','v','e', 0,
+ 'Y','a','c','u','t','e', 0,
+ 'Y','d','i','e','r','e','s','i','s', 0,
+ 'Z','c','a','r','o','n', 0,
+ 'a','a','c','u','t','e', 0,
+ 'a','c','i','r','c','u','m','f','l','e','x', 0,
+ 'a','d','i','e','r','e','s','i','s', 0,
+ 'a','g','r','a','v','e', 0,
+ 'a','r','i','n','g', 0,
+ 'a','t','i','l','d','e', 0,
+ 'c','c','e','d','i','l','l','a', 0,
+ 'e','a','c','u','t','e', 0,
+ 'e','c','i','r','c','u','m','f','l','e','x', 0,
+ 'e','d','i','e','r','e','s','i','s', 0,
+ 'e','g','r','a','v','e', 0,
+ 'i','a','c','u','t','e', 0,
+ 'i','c','i','r','c','u','m','f','l','e','x', 0,
+ 'i','d','i','e','r','e','s','i','s', 0,
+ 'i','g','r','a','v','e', 0,
+ 'n','t','i','l','d','e', 0,
+ 'o','a','c','u','t','e', 0,
+ 'o','c','i','r','c','u','m','f','l','e','x', 0,
+ 'o','d','i','e','r','e','s','i','s', 0,
+ 'o','g','r','a','v','e', 0,
+ 'o','t','i','l','d','e', 0,
+ 's','c','a','r','o','n', 0,
+ 'u','a','c','u','t','e', 0,
+ 'u','c','i','r','c','u','m','f','l','e','x', 0,
+ 'u','d','i','e','r','e','s','i','s', 0,
+ 'u','g','r','a','v','e', 0,
+ 'y','a','c','u','t','e', 0,
+ 'y','d','i','e','r','e','s','i','s', 0,
+ 'z','c','a','r','o','n', 0,
+ 'e','x','c','l','a','m','s','m','a','l','l', 0,
+ 'H','u','n','g','a','r','u','m','l','a','u','t','s','m','a','l','l', 0,
+ 'd','o','l','l','a','r','o','l','d','s','t','y','l','e', 0,
+ 'd','o','l','l','a','r','s','u','p','e','r','i','o','r', 0,
+ 'a','m','p','e','r','s','a','n','d','s','m','a','l','l', 0,
+ 'A','c','u','t','e','s','m','a','l','l', 0,
+ 'p','a','r','e','n','l','e','f','t','s','u','p','e','r','i','o','r', 0,
+ 'p','a','r','e','n','r','i','g','h','t','s','u','p','e','r','i','o','r', 0,
+ 't','w','o','d','o','t','e','n','l','e','a','d','e','r', 0,
+ 'o','n','e','d','o','t','e','n','l','e','a','d','e','r', 0,
+ 'z','e','r','o','o','l','d','s','t','y','l','e', 0,
+ 'o','n','e','o','l','d','s','t','y','l','e', 0,
+ 't','w','o','o','l','d','s','t','y','l','e', 0,
+ 't','h','r','e','e','o','l','d','s','t','y','l','e', 0,
+ 'f','o','u','r','o','l','d','s','t','y','l','e', 0,
+ 'f','i','v','e','o','l','d','s','t','y','l','e', 0,
+ 's','i','x','o','l','d','s','t','y','l','e', 0,
+ 's','e','v','e','n','o','l','d','s','t','y','l','e', 0,
+ 'e','i','g','h','t','o','l','d','s','t','y','l','e', 0,
+ 'n','i','n','e','o','l','d','s','t','y','l','e', 0,
+ 'c','o','m','m','a','s','u','p','e','r','i','o','r', 0,
+ 't','h','r','e','e','q','u','a','r','t','e','r','s','e','m','d','a','s','h', 0,
+ 'p','e','r','i','o','d','s','u','p','e','r','i','o','r', 0,
+ 'q','u','e','s','t','i','o','n','s','m','a','l','l', 0,
+ 'a','s','u','p','e','r','i','o','r', 0,
+ 'b','s','u','p','e','r','i','o','r', 0,
+ 'c','e','n','t','s','u','p','e','r','i','o','r', 0,
+ 'd','s','u','p','e','r','i','o','r', 0,
+ 'e','s','u','p','e','r','i','o','r', 0,
+ 'i','s','u','p','e','r','i','o','r', 0,
+ 'l','s','u','p','e','r','i','o','r', 0,
+ 'm','s','u','p','e','r','i','o','r', 0,
+ 'n','s','u','p','e','r','i','o','r', 0,
+ 'o','s','u','p','e','r','i','o','r', 0,
+ 'r','s','u','p','e','r','i','o','r', 0,
+ 's','s','u','p','e','r','i','o','r', 0,
+ 't','s','u','p','e','r','i','o','r', 0,
+ 'f','f', 0,
+ 'f','f','i', 0,
+ 'f','f','l', 0,
+ 'p','a','r','e','n','l','e','f','t','i','n','f','e','r','i','o','r', 0,
+ 'p','a','r','e','n','r','i','g','h','t','i','n','f','e','r','i','o','r', 0,
+ 'C','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'h','y','p','h','e','n','s','u','p','e','r','i','o','r', 0,
+ 'G','r','a','v','e','s','m','a','l','l', 0,
+ 'A','s','m','a','l','l', 0,
+ 'B','s','m','a','l','l', 0,
+ 'C','s','m','a','l','l', 0,
+ 'D','s','m','a','l','l', 0,
+ 'E','s','m','a','l','l', 0,
+ 'F','s','m','a','l','l', 0,
+ 'G','s','m','a','l','l', 0,
+ 'H','s','m','a','l','l', 0,
+ 'I','s','m','a','l','l', 0,
+ 'J','s','m','a','l','l', 0,
+ 'K','s','m','a','l','l', 0,
+ 'L','s','m','a','l','l', 0,
+ 'M','s','m','a','l','l', 0,
+ 'N','s','m','a','l','l', 0,
+ 'O','s','m','a','l','l', 0,
+ 'P','s','m','a','l','l', 0,
+ 'Q','s','m','a','l','l', 0,
+ 'R','s','m','a','l','l', 0,
+ 'S','s','m','a','l','l', 0,
+ 'T','s','m','a','l','l', 0,
+ 'U','s','m','a','l','l', 0,
+ 'V','s','m','a','l','l', 0,
+ 'W','s','m','a','l','l', 0,
+ 'X','s','m','a','l','l', 0,
+ 'Y','s','m','a','l','l', 0,
+ 'Z','s','m','a','l','l', 0,
+ 'c','o','l','o','n','m','o','n','e','t','a','r','y', 0,
+ 'o','n','e','f','i','t','t','e','d', 0,
+ 'r','u','p','i','a','h', 0,
+ 'T','i','l','d','e','s','m','a','l','l', 0,
+ 'e','x','c','l','a','m','d','o','w','n','s','m','a','l','l', 0,
+ 'c','e','n','t','o','l','d','s','t','y','l','e', 0,
+ 'L','s','l','a','s','h','s','m','a','l','l', 0,
+ 'S','c','a','r','o','n','s','m','a','l','l', 0,
+ 'Z','c','a','r','o','n','s','m','a','l','l', 0,
+ 'D','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'B','r','e','v','e','s','m','a','l','l', 0,
+ 'C','a','r','o','n','s','m','a','l','l', 0,
+ 'D','o','t','a','c','c','e','n','t','s','m','a','l','l', 0,
+ 'M','a','c','r','o','n','s','m','a','l','l', 0,
+ 'f','i','g','u','r','e','d','a','s','h', 0,
+ 'h','y','p','h','e','n','i','n','f','e','r','i','o','r', 0,
+ 'O','g','o','n','e','k','s','m','a','l','l', 0,
+ 'R','i','n','g','s','m','a','l','l', 0,
+ 'C','e','d','i','l','l','a','s','m','a','l','l', 0,
+ 'q','u','e','s','t','i','o','n','d','o','w','n','s','m','a','l','l', 0,
+ 'o','n','e','e','i','g','h','t','h', 0,
+ 't','h','r','e','e','e','i','g','h','t','h','s', 0,
+ 'f','i','v','e','e','i','g','h','t','h','s', 0,
+ 's','e','v','e','n','e','i','g','h','t','h','s', 0,
+ 'o','n','e','t','h','i','r','d', 0,
+ 't','w','o','t','h','i','r','d','s', 0,
+ 'z','e','r','o','s','u','p','e','r','i','o','r', 0,
+ 'f','o','u','r','s','u','p','e','r','i','o','r', 0,
+ 'f','i','v','e','s','u','p','e','r','i','o','r', 0,
+ 's','i','x','s','u','p','e','r','i','o','r', 0,
+ 's','e','v','e','n','s','u','p','e','r','i','o','r', 0,
+ 'e','i','g','h','t','s','u','p','e','r','i','o','r', 0,
+ 'n','i','n','e','s','u','p','e','r','i','o','r', 0,
+ 'z','e','r','o','i','n','f','e','r','i','o','r', 0,
+ 'o','n','e','i','n','f','e','r','i','o','r', 0,
+ 't','w','o','i','n','f','e','r','i','o','r', 0,
+ 't','h','r','e','e','i','n','f','e','r','i','o','r', 0,
+ 'f','o','u','r','i','n','f','e','r','i','o','r', 0,
+ 'f','i','v','e','i','n','f','e','r','i','o','r', 0,
+ 's','i','x','i','n','f','e','r','i','o','r', 0,
+ 's','e','v','e','n','i','n','f','e','r','i','o','r', 0,
+ 'e','i','g','h','t','i','n','f','e','r','i','o','r', 0,
+ 'n','i','n','e','i','n','f','e','r','i','o','r', 0,
+ 'c','e','n','t','i','n','f','e','r','i','o','r', 0,
+ 'd','o','l','l','a','r','i','n','f','e','r','i','o','r', 0,
+ 'p','e','r','i','o','d','i','n','f','e','r','i','o','r', 0,
+ 'c','o','m','m','a','i','n','f','e','r','i','o','r', 0,
+ 'A','g','r','a','v','e','s','m','a','l','l', 0,
+ 'A','a','c','u','t','e','s','m','a','l','l', 0,
+ 'A','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'A','t','i','l','d','e','s','m','a','l','l', 0,
+ 'A','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'A','r','i','n','g','s','m','a','l','l', 0,
+ 'A','E','s','m','a','l','l', 0,
+ 'C','c','e','d','i','l','l','a','s','m','a','l','l', 0,
+ 'E','g','r','a','v','e','s','m','a','l','l', 0,
+ 'E','a','c','u','t','e','s','m','a','l','l', 0,
+ 'E','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'E','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'I','g','r','a','v','e','s','m','a','l','l', 0,
+ 'I','a','c','u','t','e','s','m','a','l','l', 0,
+ 'I','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'I','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'E','t','h','s','m','a','l','l', 0,
+ 'N','t','i','l','d','e','s','m','a','l','l', 0,
+ 'O','g','r','a','v','e','s','m','a','l','l', 0,
+ 'O','a','c','u','t','e','s','m','a','l','l', 0,
+ 'O','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'O','t','i','l','d','e','s','m','a','l','l', 0,
+ 'O','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'O','E','s','m','a','l','l', 0,
+ 'O','s','l','a','s','h','s','m','a','l','l', 0,
+ 'U','g','r','a','v','e','s','m','a','l','l', 0,
+ 'U','a','c','u','t','e','s','m','a','l','l', 0,
+ 'U','c','i','r','c','u','m','f','l','e','x','s','m','a','l','l', 0,
+ 'U','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ 'Y','a','c','u','t','e','s','m','a','l','l', 0,
+ 'T','h','o','r','n','s','m','a','l','l', 0,
+ 'Y','d','i','e','r','e','s','i','s','s','m','a','l','l', 0,
+ '0','0','1','.','0','0','0', 0,
+ '0','0','1','.','0','0','1', 0,
+ '0','0','1','.','0','0','2', 0,
+ '0','0','1','.','0','0','3', 0,
+ 'B','l','a','c','k', 0,
+ 'B','o','l','d', 0,
+ 'B','o','o','k', 0,
+ 'L','i','g','h','t', 0,
+ 'M','e','d','i','u','m', 0,
+ 'R','e','g','u','l','a','r', 0,
+ 'R','o','m','a','n', 0,
+ 'S','e','m','i','b','o','l','d', 0,
+ };
+
+
+#define FT_NUM_MAC_NAMES 258
+
+ /* Values are offsets into the `ft_standard_glyph_names' table */
+
+ static const short ft_mac_names[FT_NUM_MAC_NAMES] =
+ {
+ 253, 0, 6, 261, 267, 274, 283, 294, 301, 309, 758, 330, 340, 351,
+ 360, 365, 371, 378, 385, 391, 396, 400, 404, 410, 415, 420, 424, 430,
+ 436, 441, 447, 457, 462, 468, 476, 485, 488, 490, 492, 494, 496, 498,
+ 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526,
+ 528, 530, 532, 534, 536, 538, 540, 552, 562, 575, 587, 979, 608, 610,
+ 612, 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638,
+ 640, 642, 644, 646, 648, 650, 652, 654, 656, 658, 660, 670, 674, 685,
+ 1375,1392,1405,1414,1486,1512,1562,1603,1632,1610,1622,1645,1639,1652,
+ 1661,1690,1668,1680,1697,1726,1704,1716,1733,1740,1769,1747,1759,1776,
+ 1790,1819,1797,1809, 839,1263, 707, 712, 741, 881, 871,1160,1302,1346,
+ 1197, 985,1031, 23,1086,1108, 32,1219, 41, 51, 730,1194, 64, 76,
+ 86, 94, 97,1089,1118, 106,1131,1150, 966, 696,1183, 112, 734, 120,
+ 132, 783, 930, 945, 138,1385,1398,1529,1115,1157, 832,1079, 770, 916,
+ 598, 319,1246, 155,1833,1586, 721, 749, 797, 811, 826, 829, 846, 856,
+ 888, 903, 954,1363,1421,1356,1433,1443,1450,1457,1469,1479,1493,1500,
+ 163,1522,1543,1550,1572,1134, 991,1002,1008,1015,1021,1040,1045,1053,
+ 1066,1073,1101,1143,1536,1783,1596,1843,1253,1207,1319,1579,1826,1229,
+ 1270,1313,1323,1171,1290,1332,1211,1235,1276, 169, 175, 182, 189, 200,
+ 209, 218, 225, 232, 239, 246
+ };
+
+
+#define FT_NUM_SID_NAMES 391
+
+ /* Values are offsets into the `ft_standard_glyph_names' table */
+
+ static const short ft_sid_names[FT_NUM_SID_NAMES] =
+ {
+ 253, 261, 267, 274, 283, 294, 301, 309, 319, 330, 340, 351, 360, 365,
+ 371, 378, 385, 391, 396, 400, 404, 410, 415, 420, 424, 430, 436, 441,
+ 447, 457, 462, 468, 476, 485, 488, 490, 492, 494, 496, 498, 500, 502,
+ 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, 528, 530,
+ 532, 534, 536, 538, 540, 552, 562, 575, 587, 598, 608, 610, 612, 614,
+ 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 642,
+ 644, 646, 648, 650, 652, 654, 656, 658, 660, 670, 674, 685, 696, 707,
+ 712, 721, 730, 734, 741, 749, 758, 770, 783, 797, 811, 826, 829, 832,
+ 839, 846, 856, 871, 881, 888, 903, 916, 930, 945, 954, 966, 979, 985,
+ 991,1002,1008,1015,1021,1031,1040,1045,1053,1066,1073,1079,1086,1089,
+ 1101,1108,1115,1118,1131,1134,1143,1150,1157,1160,1171,1183,1194,1197,
+ 1207,1211,1219,1229,1235,1246,1253,1263,1270,1276,1290,1302,1313,1319,
+ 1323,1332,1346,1356,1363,1375,1385,1392,1398,1405,1414,1421,1433,1443,
+ 1450,1457,1469,1479,1486,1493,1500,1512,1522,1529,1536,1543,1550,1562,
+ 1572,1579,1586,1596,1603,1610,1622,1632,1639,1645,1652,1661,1668,1680,
+ 1690,1697,1704,1716,1726,1733,1740,1747,1759,1769,1776,1783,1790,1797,
+ 1809,1819,1826,1833,1843,1850,1862,1880,1895,1910,1925,1936,1954,1973,
+ 1988,2003,2016,2028,2040,2054,2067,2080,2092,2106,2120,2133,2147,2167,
+ 2182,2196,2206,2216,2229,2239,2249,2259,2269,2279,2289,2299,2309,2319,
+ 2329,2332,2336,2340,2358,2377,2393,2408,2419,2426,2433,2440,2447,2454,
+ 2461,2468,2475,2482,2489,2496,2503,2510,2517,2524,2531,2538,2545,2552,
+ 2559,2566,2573,2580,2587,2594,2601,2615,2625,2632,2643,2659,2672,2684,
+ 2696,2708,2722,2733,2744,2759,2771,2782,2797,2809,2819,2832,2850,2860,
+ 2873,2885,2898,2907,2917,2930,2943,2956,2968,2982,2996,3009,3022,3034,
+ 3046,3060,3073,3086,3098,3112,3126,3139,3152,3167,3182,3196,3208,3220,
+ 3237,3249,3264,3275,3283,3297,3309,3321,3338,3353,3365,3377,3394,3409,
+ 3418,3430,3442,3454,3471,3483,3498,3506,3518,3530,3542,3559,3574,3586,
+ 3597,3612,3620,3628,3636,3644,3650,3655,3660,3666,3673,3681,3687
+ };
+
+
+ /* the following are indices into the SID name table */
+ static const unsigned short t1_standard_encoding[256] =
+ {
+ 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,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 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, 0,
+ 0, 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,
+ 0,111,112,113,114, 0,115,116,117,118,119,120,121,122, 0,123,
+ 0,124,125,126,127,128,129,130,131, 0,132,133, 0,134,135,136,
+ 137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,138, 0,139, 0, 0, 0, 0,140,141,142,143, 0, 0, 0, 0,
+ 0,144, 0, 0, 0,145, 0, 0,146,147,148,149, 0, 0, 0, 0
+ };
+
+
+ /* the following are indices into the SID name table */
+ static const unsigned short t1_expert_encoding[256] =
+ {
+ 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,
+ 1,229,230, 0,231,232,233,234,235,236,237,238, 13, 14, 15, 99,
+ 239,240,241,242,243,244,245,246,247,248, 27, 28,249,250,251,252,
+ 0,253,254,255,256,257, 0, 0, 0,258, 0, 0,259,260,261,262,
+ 0, 0,263,264,265, 0,266,109,110,267,268,269, 0,270,271,272,
+ 273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,
+ 289,290,291,292,293,294,295,296,297,298,299,300,301,302,303, 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, 0,
+ 0,304,305,306, 0, 0,307,308,309,310,311, 0,312, 0, 0,313,
+ 0, 0,314,315, 0, 0,316,317,318, 0, 0, 0,158,155,163,319,
+ 320,321,322,323,324,325, 0, 0,326,150,164,169,327,328,329,330,
+ 331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,
+ 347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,
+ 363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378
+ };
+
+
+ /*
+ * This table is a compressed version of the Adobe Glyph List (AGL),
+ * optimized for efficient searching. It has been generated by the
+ * `glnames.py' python script located in the `src/tools' directory.
+ *
+ * The lookup function to get the Unicode value for a given string
+ * is defined below the table.
+ */
+ static const unsigned char ft_adobe_glyph_list[54791] =
+ {
+ 0, 52, 0,106, 2,167, 3, 63, 4,220, 6,125, 9,143, 10, 23,
+ 11,137, 12,199, 14,246, 15, 87, 16,233, 17,219, 18,104, 19, 88,
+ 22,110, 23, 32, 23, 71, 24, 77, 27,156, 29, 73, 31,247, 32,107,
+ 32,222, 33, 55, 34,154, 35,218, 53, 84, 59,196, 68, 6, 75,183,
+ 83,178, 88,135, 93,242,101,165,109,185,111, 55,117,254,123, 73,
+ 130,238,138,206,145, 31,153,182,156,189,163,249,178,221,193, 17,
+ 197, 99,199,240,204, 27,204,155,210,100, 65,143, 0, 65, 0,140,
+ 0,175, 0,193, 1, 15, 1,147, 1,233, 1,251, 2, 7, 2, 40,
+ 2, 57, 2, 82, 2, 91, 2,128, 2,136, 2,154, 69,131, 0,198,
+ 0,150, 0,158, 0,167,225,227,245,244,101,128, 1,252,237,225,
+ 227,242,239,110,128, 1,226,243,237,225,236,108,128,247,230,225,
+ 227,245,244,101,129, 0,193, 0,185,243,237,225,236,108,128,247,
+ 225,226,242,229,246,101,134, 1, 2, 0,213, 0,221, 0,232, 0,
+ 243, 0,251, 1, 7,225,227,245,244,101,128, 30,174,227,249,242,
+ 233,236,236,233, 99,128, 4,208,228,239,244,226,229,236,239,119,
+ 128, 30,182,231,242,225,246,101,128, 30,176,232,239,239,235,225,
+ 226,239,246,101,128, 30,178,244,233,236,228,101,128, 30,180, 99,
+ 4, 1, 25, 1, 32, 1,121, 1,137,225,242,239,110,128, 1,205,
+ 233,242, 99, 2, 1, 40, 1, 45,236,101,128, 36,182,245,237,230,
+ 236,229,120,134, 0,194, 1, 66, 1, 74, 1, 85, 1, 93, 1,105,
+ 1,113,225,227,245,244,101,128, 30,164,228,239,244,226,229,236,
+ 239,119,128, 30,172,231,242,225,246,101,128, 30,166,232,239,239,
+ 235,225,226,239,246,101,128, 30,168,243,237,225,236,108,128,247,
+ 226,244,233,236,228,101,128, 30,170,245,244,101,129,246,201, 1,
+ 129,243,237,225,236,108,128,247,180,249,242,233,236,236,233, 99,
+ 128, 4, 16,100, 3, 1,155, 1,165, 1,209,226,236,231,242,225,
+ 246,101,128, 2, 0,233,229,242,229,243,233,115,131, 0,196, 1,
+ 181, 1,192, 1,201,227,249,242,233,236,236,233, 99,128, 4,210,
+ 237,225,227,242,239,110,128, 1,222,243,237,225,236,108,128,247,
+ 228,239,116, 2, 1,216, 1,224,226,229,236,239,119,128, 30,160,
+ 237,225,227,242,239,110,128, 1,224,231,242,225,246,101,129, 0,
+ 192, 1,243,243,237,225,236,108,128,247,224,232,239,239,235,225,
+ 226,239,246,101,128, 30,162,105, 2, 2, 13, 2, 25,229,227,249,
+ 242,233,236,236,233, 99,128, 4,212,238,246,229,242,244,229,228,
+ 226,242,229,246,101,128, 2, 2,236,240,232, 97,129, 3,145, 2,
+ 49,244,239,238,239,115,128, 3,134,109, 2, 2, 63, 2, 71,225,
+ 227,242,239,110,128, 1, 0,239,238,239,243,240,225,227,101,128,
+ 255, 33,239,231,239,238,229,107,128, 1, 4,242,233,238,103,131,
+ 0,197, 2,104, 2,112, 2,120,225,227,245,244,101,128, 1,250,
+ 226,229,236,239,119,128, 30, 0,243,237,225,236,108,128,247,229,
+ 243,237,225,236,108,128,247, 97,244,233,236,228,101,129, 0,195,
+ 2,146,243,237,225,236,108,128,247,227,249,226,225,242,237,229,
+ 238,233,225,110,128, 5, 49, 66,137, 0, 66, 2,189, 2,198, 2,
+ 223, 3, 3, 3, 10, 3, 22, 3, 34, 3, 46, 3, 54,227,233,242,
+ 227,236,101,128, 36,183,228,239,116, 2, 2,206, 2,215,225,227,
+ 227,229,238,116,128, 30, 2,226,229,236,239,119,128, 30, 4,101,
+ 3, 2,231, 2,242, 2,254,227,249,242,233,236,236,233, 99,128,
+ 4, 17,238,225,242,237,229,238,233,225,110,128, 5, 50,244, 97,
+ 128, 3,146,232,239,239,107,128, 1,129,236,233,238,229,226,229,
+ 236,239,119,128, 30, 6,237,239,238,239,243,240,225,227,101,128,
+ 255, 34,242,229,246,229,243,237,225,236,108,128,246,244,243,237,
+ 225,236,108,128,247, 98,244,239,240,226,225,114,128, 1,130, 67,
+ 137, 0, 67, 3, 85, 3,127, 3,193, 3,210, 3,224, 4,171, 4,
+ 188, 4,200, 4,212, 97, 3, 3, 93, 3,104, 3,111,225,242,237,
+ 229,238,233,225,110,128, 5, 62,227,245,244,101,128, 1, 6,242,
+ 239,110,129,246,202, 3,119,243,237,225,236,108,128,246,245, 99,
+ 3, 3,135, 3,142, 3,171,225,242,239,110,128, 1, 12,229,228,
+ 233,236,236, 97,130, 0,199, 3,155, 3,163,225,227,245,244,101,
+ 128, 30, 8,243,237,225,236,108,128,247,231,233,242, 99, 2, 3,
+ 179, 3,184,236,101,128, 36,184,245,237,230,236,229,120,128, 1,
+ 8,228,239,116,129, 1, 10, 3,201,225,227,227,229,238,116,128,
+ 1, 10,229,228,233,236,236,225,243,237,225,236,108,128,247,184,
+ 104, 4, 3,234, 3,246, 4,161, 4,165,225,225,242,237,229,238,
+ 233,225,110,128, 5, 73,101, 6, 4, 4, 4, 24, 4, 35, 4,103,
+ 4,115, 4,136,225,226,235,232,225,243,233,225,238,227,249,242,
+ 233,236,236,233, 99,128, 4,188,227,249,242,233,236,236,233, 99,
+ 128, 4, 39,100, 2, 4, 41, 4, 85,229,243,227,229,238,228,229,
+ 114, 2, 4, 54, 4, 74,225,226,235,232,225,243,233,225,238,227,
+ 249,242,233,236,236,233, 99,128, 4,190,227,249,242,233,236,236,
+ 233, 99,128, 4,182,233,229,242,229,243,233,243,227,249,242,233,
+ 236,236,233, 99,128, 4,244,232,225,242,237,229,238,233,225,110,
+ 128, 5, 67,235,232,225,235,225,243,243,233,225,238,227,249,242,
+ 233,236,236,233, 99,128, 4,203,246,229,242,244,233,227,225,236,
+ 243,244,242,239,235,229,227,249,242,233,236,236,233, 99,128, 4,
+ 184,105,128, 3,167,239,239,107,128, 1,135,233,242,227,245,237,
+ 230,236,229,248,243,237,225,236,108,128,246,246,237,239,238,239,
+ 243,240,225,227,101,128,255, 35,239,225,242,237,229,238,233,225,
+ 110,128, 5, 81,243,237,225,236,108,128,247, 99, 68,142, 0, 68,
+ 4,252, 5, 10, 5, 36, 5, 96, 5,121, 5,166, 5,173, 5,231,
+ 5,244, 6, 0, 6, 12, 6, 28, 6, 48, 6, 57, 90,129, 1,241,
+ 5, 2,227,225,242,239,110,128, 1,196, 97, 2, 5, 16, 5, 27,
+ 225,242,237,229,238,233,225,110,128, 5, 52,230,242,233,227,225,
+ 110,128, 1,137, 99, 4, 5, 46, 5, 53, 5, 62, 5, 89,225,242,
+ 239,110,128, 1, 14,229,228,233,236,236, 97,128, 30, 16,233,242,
+ 99, 2, 5, 70, 5, 75,236,101,128, 36,185,245,237,230,236,229,
+ 248,226,229,236,239,119,128, 30, 18,242,239,225,116,128, 1, 16,
+ 228,239,116, 2, 5,104, 5,113,225,227,227,229,238,116,128, 30,
+ 10,226,229,236,239,119,128, 30, 12,101, 3, 5,129, 5,140, 5,
+ 150,227,249,242,233,236,236,233, 99,128, 4, 20,233,227,239,240,
+ 244,233, 99,128, 3,238,236,244, 97,129, 34, 6, 5,158,231,242,
+ 229,229,107,128, 3,148,232,239,239,107,128, 1,138,105, 2, 5,
+ 179, 5,218,229,242,229,243,233,115,131,246,203, 5,194, 5,202,
+ 5,210,193,227,245,244,101,128,246,204,199,242,225,246,101,128,
+ 246,205,243,237,225,236,108,128,247,168,231,225,237,237,225,231,
+ 242,229,229,107,128, 3,220,234,229,227,249,242,233,236,236,233,
+ 99,128, 4, 2,236,233,238,229,226,229,236,239,119,128, 30, 14,
+ 237,239,238,239,243,240,225,227,101,128,255, 36,239,244,225,227,
+ 227,229,238,244,243,237,225,236,108,128,246,247,115, 2, 6, 34,
+ 6, 41,236,225,243,104,128, 1, 16,237,225,236,108,128,247,100,
+ 244,239,240,226,225,114,128, 1,139,122,131, 1,242, 6, 67, 6,
+ 75, 6,112,227,225,242,239,110,128, 1,197,101, 2, 6, 81, 6,
+ 101,225,226,235,232,225,243,233,225,238,227,249,242,233,236,236,
+ 233, 99,128, 4,224,227,249,242,233,236,236,233, 99,128, 4, 5,
+ 232,229,227,249,242,233,236,236,233, 99,128, 4, 15, 69,146, 0,
+ 69, 6,165, 6,183, 6,191, 7, 89, 7,153, 7,165, 7,183, 7,
+ 211, 8, 7, 8, 36, 8, 94, 8,169, 8,189, 8,208, 8,248, 9,
+ 44, 9,109, 9,115,225,227,245,244,101,129, 0,201, 6,175,243,
+ 237,225,236,108,128,247,233,226,242,229,246,101,128, 1, 20, 99,
+ 5, 6,203, 6,210, 6,224, 6,236, 7, 79,225,242,239,110,128,
+ 1, 26,229,228,233,236,236,225,226,242,229,246,101,128, 30, 28,
+ 232,225,242,237,229,238,233,225,110,128, 5, 53,233,242, 99, 2,
+ 6,244, 6,249,236,101,128, 36,186,245,237,230,236,229,120,135,
+ 0,202, 7, 16, 7, 24, 7, 32, 7, 43, 7, 51, 7, 63, 7, 71,
+ 225,227,245,244,101,128, 30,190,226,229,236,239,119,128, 30, 24,
+ 228,239,244,226,229,236,239,119,128, 30,198,231,242,225,246,101,
+ 128, 30,192,232,239,239,235,225,226,239,246,101,128, 30,194,243,
+ 237,225,236,108,128,247,234,244,233,236,228,101,128, 30,196,249,
+ 242,233,236,236,233, 99,128, 4, 4,100, 3, 7, 97, 7,107, 7,
+ 127,226,236,231,242,225,246,101,128, 2, 4,233,229,242,229,243,
+ 233,115,129, 0,203, 7,119,243,237,225,236,108,128,247,235,239,
+ 116,130, 1, 22, 7,136, 7,145,225,227,227,229,238,116,128, 1,
+ 22,226,229,236,239,119,128, 30,184,230,227,249,242,233,236,236,
+ 233, 99,128, 4, 36,231,242,225,246,101,129, 0,200, 7,175,243,
+ 237,225,236,108,128,247,232,104, 2, 7,189, 7,200,225,242,237,
+ 229,238,233,225,110,128, 5, 55,239,239,235,225,226,239,246,101,
+ 128, 30,186,105, 3, 7,219, 7,230, 7,245,231,232,244,242,239,
+ 237,225,110,128, 33,103,238,246,229,242,244,229,228,226,242,229,
+ 246,101,128, 2, 6,239,244,233,230,233,229,228,227,249,242,233,
+ 236,236,233, 99,128, 4,100,108, 2, 8, 13, 8, 24,227,249,242,
+ 233,236,236,233, 99,128, 4, 27,229,246,229,238,242,239,237,225,
+ 110,128, 33,106,109, 3, 8, 44, 8, 72, 8, 83,225,227,242,239,
+ 110,130, 1, 18, 8, 56, 8, 64,225,227,245,244,101,128, 30, 22,
+ 231,242,225,246,101,128, 30, 20,227,249,242,233,236,236,233, 99,
+ 128, 4, 28,239,238,239,243,240,225,227,101,128,255, 37,110, 4,
+ 8,104, 8,115, 8,135, 8,154,227,249,242,233,236,236,233, 99,
+ 128, 4, 29,228,229,243,227,229,238,228,229,242,227,249,242,233,
+ 236,236,233, 99,128, 4,162,103,129, 1, 74, 8,141,232,229,227,
+ 249,242,233,236,236,233, 99,128, 4,164,232,239,239,235,227,249,
+ 242,233,236,236,233, 99,128, 4,199,111, 2, 8,175, 8,183,231,
+ 239,238,229,107,128, 1, 24,240,229,110,128, 1,144,240,243,233,
+ 236,239,110,129, 3,149, 8,200,244,239,238,239,115,128, 3,136,
+ 114, 2, 8,214, 8,225,227,249,242,233,236,236,233, 99,128, 4,
+ 32,229,246,229,242,243,229,100,129, 1,142, 8,237,227,249,242,
+ 233,236,236,233, 99,128, 4, 45,115, 4, 9, 2, 9, 13, 9, 33,
+ 9, 37,227,249,242,233,236,236,233, 99,128, 4, 33,228,229,243,
+ 227,229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,
+ 170,104,128, 1,169,237,225,236,108,128,247,101,116, 3, 9, 52,
+ 9, 78, 9, 92, 97,130, 3,151, 9, 60, 9, 70,242,237,229,238,
+ 233,225,110,128, 5, 56,244,239,238,239,115,128, 3,137,104,129,
+ 0,208, 9, 84,243,237,225,236,108,128,247,240,233,236,228,101,
+ 129, 30,188, 9,101,226,229,236,239,119,128, 30, 26,245,242,111,
+ 128, 32,172,250,104,130, 1,183, 9,124, 9,132,227,225,242,239,
+ 110,128, 1,238,242,229,246,229,242,243,229,100,128, 1,184, 70,
+ 136, 0, 70, 9,163, 9,172, 9,184, 9,212, 9,219, 9,248, 10,
+ 4, 10, 15,227,233,242,227,236,101,128, 36,187,228,239,244,225,
+ 227,227,229,238,116,128, 30, 30,101, 2, 9,190, 9,202,232,225,
+ 242,237,229,238,233,225,110,128, 5, 86,233,227,239,240,244,233,
+ 99,128, 3,228,232,239,239,107,128, 1,145,105, 2, 9,225, 9,
+ 238,244,225,227,249,242,233,236,236,233, 99,128, 4,114,246,229,
+ 242,239,237,225,110,128, 33,100,237,239,238,239,243,240,225,227,
+ 101,128,255, 38,239,245,242,242,239,237,225,110,128, 33, 99,243,
+ 237,225,236,108,128,247,102, 71,140, 0, 71, 10, 51, 10, 61, 10,
+ 107, 10,115, 10,176, 10,193, 10,205, 11, 39, 11, 52, 11, 65, 11,
+ 90, 11,107,194,243,241,245,225,242,101,128, 51,135, 97, 3, 10,
+ 69, 10, 76, 10, 94,227,245,244,101,128, 1,244,237,237, 97,129,
+ 3,147, 10, 84,225,230,242,233,227,225,110,128, 1,148,238,231,
+ 233,225,227,239,240,244,233, 99,128, 3,234,226,242,229,246,101,
+ 128, 1, 30, 99, 4, 10,125, 10,132, 10,141, 10,163,225,242,239,
+ 110,128, 1,230,229,228,233,236,236, 97,128, 1, 34,233,242, 99,
+ 2, 10,149, 10,154,236,101,128, 36,188,245,237,230,236,229,120,
+ 128, 1, 28,239,237,237,225,225,227,227,229,238,116,128, 1, 34,
+ 228,239,116,129, 1, 32, 10,184,225,227,227,229,238,116,128, 1,
+ 32,229,227,249,242,233,236,236,233, 99,128, 4, 19,104, 3, 10,
+ 213, 10,226, 11, 33,225,228,225,242,237,229,238,233,225,110,128,
+ 5, 66,101, 3, 10,234, 10,255, 11, 16,237,233,228,228,236,229,
+ 232,239,239,235,227,249,242,233,236,236,233, 99,128, 4,148,243,
+ 244,242,239,235,229,227,249,242,233,236,236,233, 99,128, 4,146,
+ 245,240,244,245,242,238,227,249,242,233,236,236,233, 99,128, 4,
+ 144,239,239,107,128, 1,147,233,237,225,242,237,229,238,233,225,
+ 110,128, 5, 51,234,229,227,249,242,233,236,236,233, 99,128, 4,
+ 3,109, 2, 11, 71, 11, 79,225,227,242,239,110,128, 30, 32,239,
+ 238,239,243,240,225,227,101,128,255, 39,242,225,246,101,129,246,
+ 206, 11, 99,243,237,225,236,108,128,247, 96,115, 2, 11,113, 11,
+ 129,237,225,236,108,129,247,103, 11,122,232,239,239,107,128, 2,
+ 155,244,242,239,235,101,128, 1,228, 72,140, 0, 72, 11,165, 11,
+ 190, 11,198, 11,208, 12, 17, 12, 40, 12, 77, 12,117, 12,129, 12,
+ 157, 12,165, 12,189,177,184, 53, 3, 11,175, 11,180, 11,185,179,
+ 51,128, 37,207,180, 51,128, 37,170,181, 49,128, 37,171,178,178,
+ 176,183, 51,128, 37,161,208,243,241,245,225,242,101,128, 51,203,
+ 97, 3, 11,216, 11,236, 12, 0,225,226,235,232,225,243,233,225,
+ 238,227,249,242,233,236,236,233, 99,128, 4,168,228,229,243,227,
+ 229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,178,
+ 242,228,243,233,231,238,227,249,242,233,236,236,233, 99,128, 4,
+ 42, 98, 2, 12, 23, 12, 28,225,114,128, 1, 38,242,229,246,229,
+ 226,229,236,239,119,128, 30, 42, 99, 2, 12, 46, 12, 55,229,228,
+ 233,236,236, 97,128, 30, 40,233,242, 99, 2, 12, 63, 12, 68,236,
+ 101,128, 36,189,245,237,230,236,229,120,128, 1, 36,100, 2, 12,
+ 83, 12, 93,233,229,242,229,243,233,115,128, 30, 38,239,116, 2,
+ 12,100, 12,109,225,227,227,229,238,116,128, 30, 34,226,229,236,
+ 239,119,128, 30, 36,237,239,238,239,243,240,225,227,101,128,255,
+ 40,111, 2, 12,135, 12,146,225,242,237,229,238,233,225,110,128,
+ 5, 64,242,233,227,239,240,244,233, 99,128, 3,232,243,237,225,
+ 236,108,128,247,104,245,238,231,225,242,245,237,236,225,245,116,
+ 129,246,207, 12,181,243,237,225,236,108,128,246,248,250,243,241,
+ 245,225,242,101,128, 51,144, 73,146, 0, 73, 12,239, 12,251, 12,
+ 255, 13, 11, 13, 29, 13, 37, 13, 94, 13,181, 13,214, 13,224, 13,
+ 242, 13,254, 14, 48, 14, 86, 14, 99, 14,166, 14,187, 14,205,193,
+ 227,249,242,233,236,236,233, 99,128, 4, 47, 74,128, 1, 50,213,
+ 227,249,242,233,236,236,233, 99,128, 4, 46,225,227,245,244,101,
+ 129, 0,205, 13, 21,243,237,225,236,108,128,247,237,226,242,229,
+ 246,101,128, 1, 44, 99, 3, 13, 45, 13, 52, 13, 84,225,242,239,
+ 110,128, 1,207,233,242, 99, 2, 13, 60, 13, 65,236,101,128, 36,
+ 190,245,237,230,236,229,120,129, 0,206, 13, 76,243,237,225,236,
+ 108,128,247,238,249,242,233,236,236,233, 99,128, 4, 6,100, 3,
+ 13,102, 13,112, 13,155,226,236,231,242,225,246,101,128, 2, 8,
+ 233,229,242,229,243,233,115,131, 0,207, 13,128, 13,136, 13,147,
+ 225,227,245,244,101,128, 30, 46,227,249,242,233,236,236,233, 99,
+ 128, 4,228,243,237,225,236,108,128,247,239,239,116,130, 1, 48,
+ 13,164, 13,173,225,227,227,229,238,116,128, 1, 48,226,229,236,
+ 239,119,128, 30,202,101, 2, 13,187, 13,203,226,242,229,246,229,
+ 227,249,242,233,236,236,233, 99,128, 4,214,227,249,242,233,236,
+ 236,233, 99,128, 4, 21,230,242,225,235,244,245,114,128, 33, 17,
+ 231,242,225,246,101,129, 0,204, 13,234,243,237,225,236,108,128,
+ 247,236,232,239,239,235,225,226,239,246,101,128, 30,200,105, 3,
+ 14, 6, 14, 17, 14, 32,227,249,242,233,236,236,233, 99,128, 4,
+ 24,238,246,229,242,244,229,228,226,242,229,246,101,128, 2, 10,
+ 243,232,239,242,244,227,249,242,233,236,236,233, 99,128, 4, 25,
+ 109, 2, 14, 54, 14, 75,225,227,242,239,110,129, 1, 42, 14, 64,
+ 227,249,242,233,236,236,233, 99,128, 4,226,239,238,239,243,240,
+ 225,227,101,128,255, 41,238,233,225,242,237,229,238,233,225,110,
+ 128, 5, 59,111, 3, 14,107, 14,118, 14,126,227,249,242,233,236,
+ 236,233, 99,128, 4, 1,231,239,238,229,107,128, 1, 46,244, 97,
+ 131, 3,153, 14,137, 14,147, 14,158,225,230,242,233,227,225,110,
+ 128, 1,150,228,233,229,242,229,243,233,115,128, 3,170,244,239,
+ 238,239,115,128, 3,138,115, 2, 14,172, 14,179,237,225,236,108,
+ 128,247,105,244,242,239,235,101,128, 1,151,244,233,236,228,101,
+ 129, 1, 40, 14,197,226,229,236,239,119,128, 30, 44,250,232,233,
+ 244,243, 97, 2, 14,216, 14,227,227,249,242,233,236,236,233, 99,
+ 128, 4,116,228,226,236,231,242,225,246,229,227,249,242,233,236,
+ 236,233, 99,128, 4,118, 74,134, 0, 74, 15, 6, 15, 18, 15, 41,
+ 15, 53, 15, 67, 15, 79,225,225,242,237,229,238,233,225,110,128,
+ 5, 65,227,233,242, 99, 2, 15, 27, 15, 32,236,101,128, 36,191,
+ 245,237,230,236,229,120,128, 1, 52,229,227,249,242,233,236,236,
+ 233, 99,128, 4, 8,232,229,232,225,242,237,229,238,233,225,110,
+ 128, 5, 75,237,239,238,239,243,240,225,227,101,128,255, 42,243,
+ 237,225,236,108,128,247,106, 75,140, 0, 75, 15,115, 15,125, 15,
+ 135, 16, 18, 16, 65, 16, 76, 16,106, 16,143, 16,156, 16,168, 16,
+ 180, 16,208,194,243,241,245,225,242,101,128, 51,133,203,243,241,
+ 245,225,242,101,128, 51,205, 97, 7, 15,151, 15,169, 15,191, 15,
+ 211, 15,226, 15,232, 15,249,226,225,243,232,235,233,242,227,249,
+ 242,233,236,236,233, 99,128, 4,160, 99, 2, 15,175, 15,181,245,
+ 244,101,128, 30, 48,249,242,233,236,236,233, 99,128, 4, 26,228,
+ 229,243,227,229,238,228,229,242,227,249,242,233,236,236,233, 99,
+ 128, 4,154,232,239,239,235,227,249,242,233,236,236,233, 99,128,
+ 4,195,240,240, 97,128, 3,154,243,244,242,239,235,229,227,249,
+ 242,233,236,236,233, 99,128, 4,158,246,229,242,244,233,227,225,
+ 236,243,244,242,239,235,229,227,249,242,233,236,236,233, 99,128,
+ 4,156, 99, 4, 16, 28, 16, 35, 16, 44, 16, 52,225,242,239,110,
+ 128, 1,232,229,228,233,236,236, 97,128, 1, 54,233,242,227,236,
+ 101,128, 36,192,239,237,237,225,225,227,227,229,238,116,128, 1,
+ 54,228,239,244,226,229,236,239,119,128, 30, 50,101, 2, 16, 82,
+ 16, 94,232,225,242,237,229,238,233,225,110,128, 5, 84,238,225,
+ 242,237,229,238,233,225,110,128, 5, 63,104, 3, 16,114, 16,126,
+ 16,137,225,227,249,242,233,236,236,233, 99,128, 4, 37,229,233,
+ 227,239,240,244,233, 99,128, 3,230,239,239,107,128, 1,152,234,
+ 229,227,249,242,233,236,236,233, 99,128, 4, 12,236,233,238,229,
+ 226,229,236,239,119,128, 30, 52,237,239,238,239,243,240,225,227,
+ 101,128,255, 43,239,240,240, 97, 2, 16,189, 16,200,227,249,242,
+ 233,236,236,233, 99,128, 4,128,231,242,229,229,107,128, 3,222,
+ 115, 2, 16,214, 16,226,233,227,249,242,233,236,236,233, 99,128,
+ 4,110,237,225,236,108,128,247,107, 76,138, 0, 76, 17, 1, 17,
+ 5, 17, 9, 17, 29, 17, 95, 17,133, 17,147, 17,165, 17,177, 17,
+ 189, 74,128, 1,199, 76,128,246,191, 97, 2, 17, 15, 17, 22,227,
+ 245,244,101,128, 1, 57,237,226,228, 97,128, 3,155, 99, 4, 17,
+ 39, 17, 46, 17, 55, 17, 82,225,242,239,110,128, 1, 61,229,228,
+ 233,236,236, 97,128, 1, 59,233,242, 99, 2, 17, 63, 17, 68,236,
+ 101,128, 36,193,245,237,230,236,229,248,226,229,236,239,119,128,
+ 30, 60,239,237,237,225,225,227,227,229,238,116,128, 1, 59,228,
+ 239,116,130, 1, 63, 17,105, 17,114,225,227,227,229,238,116,128,
+ 1, 63,226,229,236,239,119,129, 30, 54, 17,124,237,225,227,242,
+ 239,110,128, 30, 56,233,247,238,225,242,237,229,238,233,225,110,
+ 128, 5, 60,106,129, 1,200, 17,153,229,227,249,242,233,236,236,
+ 233, 99,128, 4, 9,236,233,238,229,226,229,236,239,119,128, 30,
+ 58,237,239,238,239,243,240,225,227,101,128,255, 44,115, 2, 17,
+ 195, 17,212,236,225,243,104,129, 1, 65, 17,204,243,237,225,236,
+ 108,128,246,249,237,225,236,108,128,247,108, 77,137, 0, 77, 17,
+ 241, 17,251, 18, 24, 18, 33, 18, 58, 18, 71, 18, 83, 18, 91, 18,
+ 100,194,243,241,245,225,242,101,128, 51,134,225, 99, 2, 18, 2,
+ 18, 18,242,239,110,129,246,208, 18, 10,243,237,225,236,108,128,
+ 247,175,245,244,101,128, 30, 62,227,233,242,227,236,101,128, 36,
+ 194,228,239,116, 2, 18, 41, 18, 50,225,227,227,229,238,116,128,
+ 30, 64,226,229,236,239,119,128, 30, 66,229,238,225,242,237,229,
+ 238,233,225,110,128, 5, 68,237,239,238,239,243,240,225,227,101,
+ 128,255, 45,243,237,225,236,108,128,247,109,244,245,242,238,229,
+ 100,128, 1,156,117,128, 3,156, 78,141, 0, 78, 18,134, 18,138,
+ 18,146, 18,212, 18,237, 18,248, 19, 3, 19, 21, 19, 33, 19, 45,
+ 19, 58, 19, 66, 19, 84, 74,128, 1,202,225,227,245,244,101,128,
+ 1, 67, 99, 4, 18,156, 18,163, 18,172, 18,199,225,242,239,110,
+ 128, 1, 71,229,228,233,236,236, 97,128, 1, 69,233,242, 99, 2,
+ 18,180, 18,185,236,101,128, 36,195,245,237,230,236,229,248,226,
+ 229,236,239,119,128, 30, 74,239,237,237,225,225,227,227,229,238,
+ 116,128, 1, 69,228,239,116, 2, 18,220, 18,229,225,227,227,229,
+ 238,116,128, 30, 68,226,229,236,239,119,128, 30, 70,232,239,239,
+ 235,236,229,230,116,128, 1,157,233,238,229,242,239,237,225,110,
+ 128, 33,104,106,129, 1,203, 19, 9,229,227,249,242,233,236,236,
+ 233, 99,128, 4, 10,236,233,238,229,226,229,236,239,119,128, 30,
+ 72,237,239,238,239,243,240,225,227,101,128,255, 46,239,247,225,
+ 242,237,229,238,233,225,110,128, 5, 70,243,237,225,236,108,128,
+ 247,110,244,233,236,228,101,129, 0,209, 19, 76,243,237,225,236,
+ 108,128,247,241,117,128, 3,157, 79,141, 0, 79, 19,118, 19,132,
+ 19,150, 19,203, 20, 78, 20,152, 20,187, 21, 48, 21, 69, 21,213,
+ 21,223, 21,254, 22, 53, 69,129, 1, 82, 19,124,243,237,225,236,
+ 108,128,246,250,225,227,245,244,101,129, 0,211, 19,142,243,237,
+ 225,236,108,128,247,243, 98, 2, 19,156, 19,196,225,242,242,229,
+ 100, 2, 19,166, 19,177,227,249,242,233,236,236,233, 99,128, 4,
+ 232,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
+ 99,128, 4,234,242,229,246,101,128, 1, 78, 99, 4, 19,213, 19,
+ 220, 19,235, 20, 68,225,242,239,110,128, 1,209,229,238,244,229,
+ 242,229,228,244,233,236,228,101,128, 1,159,233,242, 99, 2, 19,
+ 243, 19,248,236,101,128, 36,196,245,237,230,236,229,120,134, 0,
+ 212, 20, 13, 20, 21, 20, 32, 20, 40, 20, 52, 20, 60,225,227,245,
+ 244,101,128, 30,208,228,239,244,226,229,236,239,119,128, 30,216,
+ 231,242,225,246,101,128, 30,210,232,239,239,235,225,226,239,246,
+ 101,128, 30,212,243,237,225,236,108,128,247,244,244,233,236,228,
+ 101,128, 30,214,249,242,233,236,236,233, 99,128, 4, 30,100, 3,
+ 20, 86, 20,109, 20,142,226,108, 2, 20, 93, 20,101,225,227,245,
+ 244,101,128, 1, 80,231,242,225,246,101,128, 2, 12,233,229,242,
+ 229,243,233,115,130, 0,214, 20,123, 20,134,227,249,242,233,236,
+ 236,233, 99,128, 4,230,243,237,225,236,108,128,247,246,239,244,
+ 226,229,236,239,119,128, 30,204,103, 2, 20,158, 20,170,239,238,
+ 229,235,243,237,225,236,108,128,246,251,242,225,246,101,129, 0,
+ 210, 20,179,243,237,225,236,108,128,247,242,104, 4, 20,197, 20,
+ 208, 20,212, 21, 34,225,242,237,229,238,233,225,110,128, 5, 85,
+ 109,128, 33, 38,111, 2, 20,218, 20,228,239,235,225,226,239,246,
+ 101,128, 30,206,242,110,133, 1,160, 20,243, 20,251, 21, 6, 21,
+ 14, 21, 26,225,227,245,244,101,128, 30,218,228,239,244,226,229,
+ 236,239,119,128, 30,226,231,242,225,246,101,128, 30,220,232,239,
+ 239,235,225,226,239,246,101,128, 30,222,244,233,236,228,101,128,
+ 30,224,245,238,231,225,242,245,237,236,225,245,116,128, 1, 80,
+ 105,129, 1,162, 21, 54,238,246,229,242,244,229,228,226,242,229,
+ 246,101,128, 2, 14,109, 4, 21, 79, 21,107, 21,184, 21,202,225,
+ 227,242,239,110,130, 1, 76, 21, 91, 21, 99,225,227,245,244,101,
+ 128, 30, 82,231,242,225,246,101,128, 30, 80,229,231, 97,132, 33,
+ 38, 21,121, 21,132, 21,140, 21,156,227,249,242,233,236,236,233,
+ 99,128, 4, 96,231,242,229,229,107,128, 3,169,242,239,245,238,
+ 228,227,249,242,233,236,236,233, 99,128, 4,122,116, 2, 21,162,
+ 21,177,233,244,236,239,227,249,242,233,236,236,233, 99,128, 4,
+ 124,239,238,239,115,128, 3,143,233,227,242,239,110,129, 3,159,
+ 21,194,244,239,238,239,115,128, 3,140,239,238,239,243,240,225,
+ 227,101,128,255, 47,238,229,242,239,237,225,110,128, 33, 96,111,
+ 2, 21,229, 21,248,231,239,238,229,107,129, 1,234, 21,239,237,
+ 225,227,242,239,110,128, 1,236,240,229,110,128, 1,134,115, 3,
+ 22, 6, 22, 33, 22, 40,236,225,243,104,130, 0,216, 22, 17, 22,
+ 25,225,227,245,244,101,128, 1,254,243,237,225,236,108,128,247,
+ 248,237,225,236,108,128,247,111,244,242,239,235,229,225,227,245,
+ 244,101,128, 1,254,116, 2, 22, 59, 22, 70,227,249,242,233,236,
+ 236,233, 99,128, 4,126,233,236,228,101,131, 0,213, 22, 83, 22,
+ 91, 22,102,225,227,245,244,101,128, 30, 76,228,233,229,242,229,
+ 243,233,115,128, 30, 78,243,237,225,236,108,128,247,245, 80,136,
+ 0, 80, 22,130, 22,138, 22,147, 22,159, 22,211, 22,227, 22,246,
+ 23, 2,225,227,245,244,101,128, 30, 84,227,233,242,227,236,101,
+ 128, 36,197,228,239,244,225,227,227,229,238,116,128, 30, 86,101,
+ 3, 22,167, 22,178, 22,190,227,249,242,233,236,236,233, 99,128,
+ 4, 31,232,225,242,237,229,238,233,225,110,128, 5, 74,237,233,
+ 228,228,236,229,232,239,239,235,227,249,242,233,236,236,233, 99,
+ 128, 4,166,104, 2, 22,217, 22,221,105,128, 3,166,239,239,107,
+ 128, 1,164,105,129, 3,160, 22,233,247,242,225,242,237,229,238,
+ 233,225,110,128, 5, 83,237,239,238,239,243,240,225,227,101,128,
+ 255, 48,115, 2, 23, 8, 23, 25,105,129, 3,168, 23, 14,227,249,
+ 242,233,236,236,233, 99,128, 4,112,237,225,236,108,128,247,112,
+ 81,131, 0, 81, 23, 42, 23, 51, 23, 63,227,233,242,227,236,101,
+ 128, 36,198,237,239,238,239,243,240,225,227,101,128,255, 49,243,
+ 237,225,236,108,128,247,113, 82,138, 0, 82, 23, 95, 23,119, 23,
+ 166, 23,217, 23,230, 23,240, 23,245, 24, 19, 24, 31, 24, 43, 97,
+ 2, 23,101, 23,112,225,242,237,229,238,233,225,110,128, 5, 76,
+ 227,245,244,101,128, 1, 84, 99, 4, 23,129, 23,136, 23,145, 23,
+ 153,225,242,239,110,128, 1, 88,229,228,233,236,236, 97,128, 1,
+ 86,233,242,227,236,101,128, 36,199,239,237,237,225,225,227,227,
+ 229,238,116,128, 1, 86,100, 2, 23,172, 23,182,226,236,231,242,
+ 225,246,101,128, 2, 16,239,116, 2, 23,189, 23,198,225,227,227,
+ 229,238,116,128, 30, 88,226,229,236,239,119,129, 30, 90, 23,208,
+ 237,225,227,242,239,110,128, 30, 92,229,232,225,242,237,229,238,
+ 233,225,110,128, 5, 80,230,242,225,235,244,245,114,128, 33, 28,
+ 232,111,128, 3,161,233,110, 2, 23,252, 24, 5,231,243,237,225,
+ 236,108,128,246,252,246,229,242,244,229,228,226,242,229,246,101,
+ 128, 2, 18,236,233,238,229,226,229,236,239,119,128, 30, 94,237,
+ 239,238,239,243,240,225,227,101,128,255, 50,243,237,225,236,108,
+ 129,247,114, 24, 53,233,238,246,229,242,244,229,100,129, 2,129,
+ 24, 66,243,245,240,229,242,233,239,114,128, 2,182, 83,139, 0,
+ 83, 24,103, 26, 17, 26, 55, 26,182, 26,221, 26,250, 27, 84, 27,
+ 105, 27,117, 27,135, 27,143, 70, 6, 24,117, 24,209, 24,241, 25,
+ 77, 25,119, 25,221, 48, 9, 24,137, 24,145, 24,153, 24,161, 24,
+ 169, 24,177, 24,185, 24,193, 24,201,177,176,176,176, 48,128, 37,
+ 12,178,176,176,176, 48,128, 37, 20,179,176,176,176, 48,128, 37,
+ 16,180,176,176,176, 48,128, 37, 24,181,176,176,176, 48,128, 37,
+ 60,182,176,176,176, 48,128, 37, 44,183,176,176,176, 48,128, 37,
+ 52,184,176,176,176, 48,128, 37, 28,185,176,176,176, 48,128, 37,
+ 36, 49, 3, 24,217, 24,225, 24,233,176,176,176,176, 48,128, 37,
+ 0,177,176,176,176, 48,128, 37, 2,185,176,176,176, 48,128, 37,
+ 97, 50, 9, 25, 5, 25, 13, 25, 21, 25, 29, 25, 37, 25, 45, 25,
+ 53, 25, 61, 25, 69,176,176,176,176, 48,128, 37, 98,177,176,176,
+ 176, 48,128, 37, 86,178,176,176,176, 48,128, 37, 85,179,176,176,
+ 176, 48,128, 37, 99,180,176,176,176, 48,128, 37, 81,181,176,176,
+ 176, 48,128, 37, 87,182,176,176,176, 48,128, 37, 93,183,176,176,
+ 176, 48,128, 37, 92,184,176,176,176, 48,128, 37, 91, 51, 4, 25,
+ 87, 25, 95, 25,103, 25,111,182,176,176,176, 48,128, 37, 94,183,
+ 176,176,176, 48,128, 37, 95,184,176,176,176, 48,128, 37, 90,185,
+ 176,176,176, 48,128, 37, 84, 52, 10, 25,141, 25,149, 25,157, 25,
+ 165, 25,173, 25,181, 25,189, 25,197, 25,205, 25,213,176,176,176,
+ 176, 48,128, 37,105,177,176,176,176, 48,128, 37,102,178,176,176,
+ 176, 48,128, 37, 96,179,176,176,176, 48,128, 37, 80,180,176,176,
+ 176, 48,128, 37,108,181,176,176,176, 48,128, 37,103,182,176,176,
+ 176, 48,128, 37,104,183,176,176,176, 48,128, 37,100,184,176,176,
+ 176, 48,128, 37,101,185,176,176,176, 48,128, 37, 89, 53, 5, 25,
+ 233, 25,241, 25,249, 26, 1, 26, 9,176,176,176,176, 48,128, 37,
+ 88,177,176,176,176, 48,128, 37, 82,178,176,176,176, 48,128, 37,
+ 83,179,176,176,176, 48,128, 37,107,180,176,176,176, 48,128, 37,
+ 106, 97, 2, 26, 23, 26, 44,227,245,244,101,129, 1, 90, 26, 32,
+ 228,239,244,225,227,227,229,238,116,128, 30,100,237,240,233,231,
+ 242,229,229,107,128, 3,224, 99, 5, 26, 67, 26, 98, 26,107, 26,
+ 147, 26,169,225,242,239,110,130, 1, 96, 26, 78, 26, 90,228,239,
+ 244,225,227,227,229,238,116,128, 30,102,243,237,225,236,108,128,
+ 246,253,229,228,233,236,236, 97,128, 1, 94,232,247, 97,130, 1,
+ 143, 26,117, 26,128,227,249,242,233,236,236,233, 99,128, 4,216,
+ 228,233,229,242,229,243,233,243,227,249,242,233,236,236,233, 99,
+ 128, 4,218,233,242, 99, 2, 26,155, 26,160,236,101,128, 36,200,
+ 245,237,230,236,229,120,128, 1, 92,239,237,237,225,225,227,227,
+ 229,238,116,128, 2, 24,228,239,116, 2, 26,190, 26,199,225,227,
+ 227,229,238,116,128, 30, 96,226,229,236,239,119,129, 30, 98, 26,
+ 209,228,239,244,225,227,227,229,238,116,128, 30,104,101, 2, 26,
+ 227, 26,239,232,225,242,237,229,238,233,225,110,128, 5, 77,246,
+ 229,238,242,239,237,225,110,128, 33,102,104, 5, 27, 6, 27, 34,
+ 27, 48, 27, 59, 27, 72, 97, 2, 27, 12, 27, 23,225,242,237,229,
+ 238,233,225,110,128, 5, 71,227,249,242,233,236,236,233, 99,128,
+ 4, 40,227,232,225,227,249,242,233,236,236,233, 99,128, 4, 41,
+ 229,233,227,239,240,244,233, 99,128, 3,226,232,225,227,249,242,
+ 233,236,236,233, 99,128, 4,186,233,237,225,227,239,240,244,233,
+ 99,128, 3,236,105, 2, 27, 90, 27, 96,231,237, 97,128, 3,163,
+ 248,242,239,237,225,110,128, 33,101,237,239,238,239,243,240,225,
+ 227,101,128,255, 51,239,230,244,243,233,231,238,227,249,242,233,
+ 236,236,233, 99,128, 4, 44,243,237,225,236,108,128,247,115,244,
+ 233,231,237,225,231,242,229,229,107,128, 3,218, 84,141, 0, 84,
+ 27,186, 27,191, 27,197, 28, 7, 28, 32, 28, 96, 28,147, 28,177,
+ 28,189, 28,201, 28,246, 29, 6, 29, 46,225,117,128, 3,164,226,
+ 225,114,128, 1,102, 99, 4, 27,207, 27,214, 27,223, 27,250,225,
+ 242,239,110,128, 1,100,229,228,233,236,236, 97,128, 1, 98,233,
+ 242, 99, 2, 27,231, 27,236,236,101,128, 36,201,245,237,230,236,
+ 229,248,226,229,236,239,119,128, 30,112,239,237,237,225,225,227,
+ 227,229,238,116,128, 1, 98,228,239,116, 2, 28, 15, 28, 24,225,
+ 227,227,229,238,116,128, 30,106,226,229,236,239,119,128, 30,108,
+ 101, 4, 28, 42, 28, 53, 28, 73, 28, 82,227,249,242,233,236,236,
+ 233, 99,128, 4, 34,228,229,243,227,229,238,228,229,242,227,249,
+ 242,233,236,236,233, 99,128, 4,172,238,242,239,237,225,110,128,
+ 33,105,244,243,229,227,249,242,233,236,236,233, 99,128, 4,180,
+ 104, 3, 28,104, 28,110, 28,136,229,244, 97,128, 3,152,111, 2,
+ 28,116, 28,121,239,107,128, 1,172,242,110,129, 0,222, 28,128,
+ 243,237,225,236,108,128,247,254,242,229,229,242,239,237,225,110,
+ 128, 33, 98,105, 2, 28,153, 28,164,236,228,229,243,237,225,236,
+ 108,128,246,254,247,238,225,242,237,229,238,233,225,110,128, 5,
+ 79,236,233,238,229,226,229,236,239,119,128, 30,110,237,239,238,
+ 239,243,240,225,227,101,128,255, 52,111, 2, 28,207, 28,218,225,
+ 242,237,229,238,233,225,110,128, 5, 57,238,101, 3, 28,227, 28,
+ 234, 28,240,230,233,246,101,128, 1,188,243,233,120,128, 1,132,
+ 244,247,111,128, 1,167,242,229,244,242,239,230,236,229,248,232,
+ 239,239,107,128, 1,174,115, 3, 29, 14, 29, 26, 29, 39,229,227,
+ 249,242,233,236,236,233, 99,128, 4, 38,232,229,227,249,242,233,
+ 236,236,233, 99,128, 4, 11,237,225,236,108,128,247,116,119, 2,
+ 29, 52, 29, 64,229,236,246,229,242,239,237,225,110,128, 33,107,
+ 239,242,239,237,225,110,128, 33, 97, 85,142, 0, 85, 29,105, 29,
+ 123, 29,131, 29,198, 30, 69, 30, 87, 30,198, 30,214, 30,226, 31,
+ 21, 31, 30, 31,142, 31,149, 31,219,225,227,245,244,101,129, 0,
+ 218, 29,115,243,237,225,236,108,128,247,250,226,242,229,246,101,
+ 128, 1,108, 99, 3, 29,139, 29,146, 29,188,225,242,239,110,128,
+ 1,211,233,242, 99, 2, 29,154, 29,159,236,101,128, 36,202,245,
+ 237,230,236,229,120,130, 0,219, 29,172, 29,180,226,229,236,239,
+ 119,128, 30,118,243,237,225,236,108,128,247,251,249,242,233,236,
+ 236,233, 99,128, 4, 35,100, 3, 29,206, 29,229, 30, 59,226,108,
+ 2, 29,213, 29,221,225,227,245,244,101,128, 1,112,231,242,225,
+ 246,101,128, 2, 20,233,229,242,229,243,233,115,134, 0,220, 29,
+ 251, 30, 3, 30, 11, 30, 34, 30, 42, 30, 51,225,227,245,244,101,
+ 128, 1,215,226,229,236,239,119,128, 30,114, 99, 2, 30, 17, 30,
+ 24,225,242,239,110,128, 1,217,249,242,233,236,236,233, 99,128,
+ 4,240,231,242,225,246,101,128, 1,219,237,225,227,242,239,110,
+ 128, 1,213,243,237,225,236,108,128,247,252,239,244,226,229,236,
+ 239,119,128, 30,228,231,242,225,246,101,129, 0,217, 30, 79,243,
+ 237,225,236,108,128,247,249,104, 2, 30, 93, 30,171,111, 2, 30,
+ 99, 30,109,239,235,225,226,239,246,101,128, 30,230,242,110,133,
+ 1,175, 30,124, 30,132, 30,143, 30,151, 30,163,225,227,245,244,
+ 101,128, 30,232,228,239,244,226,229,236,239,119,128, 30,240,231,
+ 242,225,246,101,128, 30,234,232,239,239,235,225,226,239,246,101,
+ 128, 30,236,244,233,236,228,101,128, 30,238,245,238,231,225,242,
+ 245,237,236,225,245,116,129, 1,112, 30,187,227,249,242,233,236,
+ 236,233, 99,128, 4,242,233,238,246,229,242,244,229,228,226,242,
+ 229,246,101,128, 2, 22,235,227,249,242,233,236,236,233, 99,128,
+ 4,120,109, 2, 30,232, 31, 10,225,227,242,239,110,130, 1,106,
+ 30,244, 30,255,227,249,242,233,236,236,233, 99,128, 4,238,228,
+ 233,229,242,229,243,233,115,128, 30,122,239,238,239,243,240,225,
+ 227,101,128,255, 53,239,231,239,238,229,107,128, 1,114,240,243,
+ 233,236,239,110,133, 3,165, 31, 49, 31, 53, 31, 90, 31,121, 31,
+ 134, 49,128, 3,210, 97, 2, 31, 59, 31, 81,227,245,244,229,232,
+ 239,239,235,243,249,237,226,239,236,231,242,229,229,107,128, 3,
+ 211,230,242,233,227,225,110,128, 1,177,228,233,229,242,229,243,
+ 233,115,129, 3,171, 31,103,232,239,239,235,243,249,237,226,239,
+ 236,231,242,229,229,107,128, 3,212,232,239,239,235,243,249,237,
+ 226,239,108,128, 3,210,244,239,238,239,115,128, 3,142,242,233,
+ 238,103,128, 1,110,115, 3, 31,157, 31,172, 31,179,232,239,242,
+ 244,227,249,242,233,236,236,233, 99,128, 4, 14,237,225,236,108,
+ 128,247,117,244,242,225,233,231,232,116, 2, 31,191, 31,202,227,
+ 249,242,233,236,236,233, 99,128, 4,174,243,244,242,239,235,229,
+ 227,249,242,233,236,236,233, 99,128, 4,176,244,233,236,228,101,
+ 130, 1,104, 31,231, 31,239,225,227,245,244,101,128, 30,120,226,
+ 229,236,239,119,128, 30,116, 86,136, 0, 86, 32, 11, 32, 20, 32,
+ 31, 32, 60, 32, 67, 32, 79, 32, 91, 32, 99,227,233,242,227,236,
+ 101,128, 36,203,228,239,244,226,229,236,239,119,128, 30,126,101,
+ 2, 32, 37, 32, 48,227,249,242,233,236,236,233, 99,128, 4, 18,
+ 247,225,242,237,229,238,233,225,110,128, 5, 78,232,239,239,107,
+ 128, 1,178,237,239,238,239,243,240,225,227,101,128,255, 54,239,
+ 225,242,237,229,238,233,225,110,128, 5, 72,243,237,225,236,108,
+ 128,247,118,244,233,236,228,101,128, 30,124, 87,134, 0, 87, 32,
+ 123, 32,131, 32,154, 32,194, 32,202, 32,214,225,227,245,244,101,
+ 128, 30,130,227,233,242, 99, 2, 32,140, 32,145,236,101,128, 36,
+ 204,245,237,230,236,229,120,128, 1,116,100, 2, 32,160, 32,170,
+ 233,229,242,229,243,233,115,128, 30,132,239,116, 2, 32,177, 32,
+ 186,225,227,227,229,238,116,128, 30,134,226,229,236,239,119,128,
+ 30,136,231,242,225,246,101,128, 30,128,237,239,238,239,243,240,
+ 225,227,101,128,255, 55,243,237,225,236,108,128,247,119, 88,134,
+ 0, 88, 32,238, 32,247, 33, 18, 33, 31, 33, 35, 33, 47,227,233,
+ 242,227,236,101,128, 36,205,100, 2, 32,253, 33, 7,233,229,242,
+ 229,243,233,115,128, 30,140,239,244,225,227,227,229,238,116,128,
+ 30,138,229,232,225,242,237,229,238,233,225,110,128, 5, 61,105,
+ 128, 3,158,237,239,238,239,243,240,225,227,101,128,255, 56,243,
+ 237,225,236,108,128,247,120, 89,139, 0, 89, 33, 81, 33,116, 33,
+ 139, 33,189, 33,228, 33,236, 33,253, 34, 40, 34, 52, 34, 60, 34,
+ 68, 97, 2, 33, 87, 33,104,227,245,244,101,129, 0,221, 33, 96,
+ 243,237,225,236,108,128,247,253,244,227,249,242,233,236,236,233,
+ 99,128, 4, 98,227,233,242, 99, 2, 33,125, 33,130,236,101,128,
+ 36,206,245,237,230,236,229,120,128, 1,118,100, 2, 33,145, 33,
+ 165,233,229,242,229,243,233,115,129, 1,120, 33,157,243,237,225,
+ 236,108,128,247,255,239,116, 2, 33,172, 33,181,225,227,227,229,
+ 238,116,128, 30,142,226,229,236,239,119,128, 30,244,229,114, 2,
+ 33,196, 33,208,233,227,249,242,233,236,236,233, 99,128, 4, 43,
+ 245,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
+ 99,128, 4,248,231,242,225,246,101,128, 30,242,232,239,239,107,
+ 129, 1,179, 33,245,225,226,239,246,101,128, 30,246,105, 3, 34,
+ 5, 34, 16, 34, 27,225,242,237,229,238,233,225,110,128, 5, 69,
+ 227,249,242,233,236,236,233, 99,128, 4, 7,247,238,225,242,237,
+ 229,238,233,225,110,128, 5, 82,237,239,238,239,243,240,225,227,
+ 101,128,255, 57,243,237,225,236,108,128,247,121,244,233,236,228,
+ 101,128, 30,248,245,115, 2, 34, 75, 34,113,226,233,103, 2, 34,
+ 83, 34, 94,227,249,242,233,236,236,233, 99,128, 4,106,233,239,
+ 244,233,230,233,229,228,227,249,242,233,236,236,233, 99,128, 4,
+ 108,236,233,244,244,236,101, 2, 34,124, 34,135,227,249,242,233,
+ 236,236,233, 99,128, 4,102,233,239,244,233,230,233,229,228,227,
+ 249,242,233,236,236,233, 99,128, 4,104, 90,136, 0, 90, 34,174,
+ 34,198, 34,243, 35, 14, 35, 81, 35,173, 35,185, 35,197, 97, 2,
+ 34,180, 34,191,225,242,237,229,238,233,225,110,128, 5, 54,227,
+ 245,244,101,128, 1,121, 99, 2, 34,204, 34,221,225,242,239,110,
+ 129, 1,125, 34,213,243,237,225,236,108,128,246,255,233,242, 99,
+ 2, 34,229, 34,234,236,101,128, 36,207,245,237,230,236,229,120,
+ 128, 30,144,228,239,116,130, 1,123, 34,253, 35, 6,225,227,227,
+ 229,238,116,128, 1,123,226,229,236,239,119,128, 30,146,101, 3,
+ 35, 22, 35, 33, 35, 76,227,249,242,233,236,236,233, 99,128, 4,
+ 23,100, 2, 35, 39, 35, 58,229,243,227,229,238,228,229,242,227,
+ 249,242,233,236,236,233, 99,128, 4,152,233,229,242,229,243,233,
+ 243,227,249,242,233,236,236,233, 99,128, 4,222,244, 97,128, 3,
+ 150,232,101, 4, 35, 92, 35,103, 35,119, 35,130,225,242,237,229,
+ 238,233,225,110,128, 5, 58,226,242,229,246,229,227,249,242,233,
+ 236,236,233, 99,128, 4,193,227,249,242,233,236,236,233, 99,128,
+ 4, 22,100, 2, 35,136, 35,155,229,243,227,229,238,228,229,242,
+ 227,249,242,233,236,236,233, 99,128, 4,150,233,229,242,229,243,
+ 233,243,227,249,242,233,236,236,233, 99,128, 4,220,236,233,238,
+ 229,226,229,236,239,119,128, 30,148,237,239,238,239,243,240,225,
+ 227,101,128,255, 58,115, 2, 35,203, 35,210,237,225,236,108,128,
+ 247,122,244,242,239,235,101,128, 1,181, 97,149, 0, 97, 36, 8,
+ 36,144, 37, 35, 37,211, 38, 55, 38, 91, 45, 10, 45, 47, 45, 74,
+ 46, 43, 46, 81, 47,170, 47,242, 48,197, 48,206, 49, 79, 51, 87,
+ 52, 77, 52,124, 53, 19, 53, 33, 97, 7, 36, 24, 36, 34, 36, 41,
+ 36, 48, 36, 73, 36, 89, 36,100,226,229,238,231,225,236,105,128,
+ 9,134,227,245,244,101,128, 0,225,228,229,246, 97,128, 9, 6,
+ 231,117, 2, 36, 55, 36, 64,234,225,242,225,244,105,128, 10,134,
+ 242,237,245,235,232,105,128, 10, 6,237,225,244,242,225,231,245,
+ 242,237,245,235,232,105,128, 10, 62,242,245,243,241,245,225,242,
+ 101,128, 51, 3,246,239,247,229,236,243,233,231,110, 3, 36,116,
+ 36,126, 36,133,226,229,238,231,225,236,105,128, 9,190,228,229,
+ 246, 97,128, 9, 62,231,245,234,225,242,225,244,105,128, 10,190,
+ 98, 4, 36,154, 36,195, 36,204, 36,214,226,242,229,246,233,225,
+ 244,233,239,110, 2, 36,169, 36,184,237,225,242,235,225,242,237,
+ 229,238,233,225,110,128, 5, 95,243,233,231,238,228,229,246, 97,
+ 128, 9,112,229,238,231,225,236,105,128, 9,133,239,240,239,237,
+ 239,230,111,128, 49, 26,242,229,246,101,134, 1, 3, 36,233, 36,
+ 241, 36,252, 37, 7, 37, 15, 37, 27,225,227,245,244,101,128, 30,
+ 175,227,249,242,233,236,236,233, 99,128, 4,209,228,239,244,226,
+ 229,236,239,119,128, 30,183,231,242,225,246,101,128, 30,177,232,
+ 239,239,235,225,226,239,246,101,128, 30,179,244,233,236,228,101,
+ 128, 30,181, 99, 4, 37, 45, 37, 52, 37,131, 37,201,225,242,239,
+ 110,128, 1,206,233,242, 99, 2, 37, 60, 37, 65,236,101,128, 36,
+ 208,245,237,230,236,229,120,133, 0,226, 37, 84, 37, 92, 37,103,
+ 37,111, 37,123,225,227,245,244,101,128, 30,165,228,239,244,226,
+ 229,236,239,119,128, 30,173,231,242,225,246,101,128, 30,167,232,
+ 239,239,235,225,226,239,246,101,128, 30,169,244,233,236,228,101,
+ 128, 30,171,245,244,101,133, 0,180, 37,147, 37,158, 37,175, 37,
+ 182, 37,191,226,229,236,239,247,227,237, 98,128, 3, 23, 99, 2,
+ 37,164, 37,169,237, 98,128, 3, 1,239,237, 98,128, 3, 1,228,
+ 229,246, 97,128, 9, 84,236,239,247,237,239,100,128, 2,207,244,
+ 239,238,229,227,237, 98,128, 3, 65,249,242,233,236,236,233, 99,
+ 128, 4, 48,100, 5, 37,223, 37,233, 37,247, 37,253, 38, 31,226,
+ 236,231,242,225,246,101,128, 2, 1,228,225,235,231,245,242,237,
+ 245,235,232,105,128, 10,113,229,246, 97,128, 9, 5,233,229,242,
+ 229,243,233,115,130, 0,228, 38, 11, 38, 22,227,249,242,233,236,
+ 236,233, 99,128, 4,211,237,225,227,242,239,110,128, 1,223,239,
+ 116, 2, 38, 38, 38, 46,226,229,236,239,119,128, 30,161,237,225,
+ 227,242,239,110,128, 1,225,101,131, 0,230, 38, 65, 38, 73, 38,
+ 82,225,227,245,244,101,128, 1,253,235,239,242,229,225,110,128,
+ 49, 80,237,225,227,242,239,110,128, 1,227,230,233,105, 6, 38,
+ 107, 38,127, 41, 64, 41, 70, 41, 85, 44,185, 48, 2, 38,113, 38,
+ 120,176,178,176, 56,128, 32, 21,184,185,180, 49,128, 32,164,177,
+ 48, 3, 38,136, 40,160, 41, 39, 48, 9, 38,156, 38,176, 38,238,
+ 39, 44, 39,106, 39,168, 39,230, 40, 36, 40, 98, 49, 3, 38,164,
+ 38,168, 38,172, 55,128, 4, 16, 56,128, 4, 17, 57,128, 4, 18,
+ 50, 10, 38,198, 38,202, 38,206, 38,210, 38,214, 38,218, 38,222,
+ 38,226, 38,230, 38,234, 48,128, 4, 19, 49,128, 4, 20, 50,128,
+ 4, 21, 51,128, 4, 1, 52,128, 4, 22, 53,128, 4, 23, 54,128,
+ 4, 24, 55,128, 4, 25, 56,128, 4, 26, 57,128, 4, 27, 51, 10,
+ 39, 4, 39, 8, 39, 12, 39, 16, 39, 20, 39, 24, 39, 28, 39, 32,
+ 39, 36, 39, 40, 48,128, 4, 28, 49,128, 4, 29, 50,128, 4, 30,
+ 51,128, 4, 31, 52,128, 4, 32, 53,128, 4, 33, 54,128, 4, 34,
+ 55,128, 4, 35, 56,128, 4, 36, 57,128, 4, 37, 52, 10, 39, 66,
+ 39, 70, 39, 74, 39, 78, 39, 82, 39, 86, 39, 90, 39, 94, 39, 98,
+ 39,102, 48,128, 4, 38, 49,128, 4, 39, 50,128, 4, 40, 51,128,
+ 4, 41, 52,128, 4, 42, 53,128, 4, 43, 54,128, 4, 44, 55,128,
+ 4, 45, 56,128, 4, 46, 57,128, 4, 47, 53, 10, 39,128, 39,132,
+ 39,136, 39,140, 39,144, 39,148, 39,152, 39,156, 39,160, 39,164,
+ 48,128, 4,144, 49,128, 4, 2, 50,128, 4, 3, 51,128, 4, 4,
+ 52,128, 4, 5, 53,128, 4, 6, 54,128, 4, 7, 55,128, 4, 8,
+ 56,128, 4, 9, 57,128, 4, 10, 54, 10, 39,190, 39,194, 39,198,
+ 39,202, 39,206, 39,210, 39,214, 39,218, 39,222, 39,226, 48,128,
+ 4, 11, 49,128, 4, 12, 50,128, 4, 14, 51,128,246,196, 52,128,
+ 246,197, 53,128, 4, 48, 54,128, 4, 49, 55,128, 4, 50, 56,128,
+ 4, 51, 57,128, 4, 52, 55, 10, 39,252, 40, 0, 40, 4, 40, 8,
+ 40, 12, 40, 16, 40, 20, 40, 24, 40, 28, 40, 32, 48,128, 4, 53,
+ 49,128, 4, 81, 50,128, 4, 54, 51,128, 4, 55, 52,128, 4, 56,
+ 53,128, 4, 57, 54,128, 4, 58, 55,128, 4, 59, 56,128, 4, 60,
+ 57,128, 4, 61, 56, 10, 40, 58, 40, 62, 40, 66, 40, 70, 40, 74,
+ 40, 78, 40, 82, 40, 86, 40, 90, 40, 94, 48,128, 4, 62, 49,128,
+ 4, 63, 50,128, 4, 64, 51,128, 4, 65, 52,128, 4, 66, 53,128,
+ 4, 67, 54,128, 4, 68, 55,128, 4, 69, 56,128, 4, 70, 57,128,
+ 4, 71, 57, 10, 40,120, 40,124, 40,128, 40,132, 40,136, 40,140,
+ 40,144, 40,148, 40,152, 40,156, 48,128, 4, 72, 49,128, 4, 73,
+ 50,128, 4, 74, 51,128, 4, 75, 52,128, 4, 76, 53,128, 4, 77,
+ 54,128, 4, 78, 55,128, 4, 79, 56,128, 4,145, 57,128, 4, 82,
+ 49, 4, 40,170, 40,232, 40,237, 41, 7, 48, 10, 40,192, 40,196,
+ 40,200, 40,204, 40,208, 40,212, 40,216, 40,220, 40,224, 40,228,
+ 48,128, 4, 83, 49,128, 4, 84, 50,128, 4, 85, 51,128, 4, 86,
+ 52,128, 4, 87, 53,128, 4, 88, 54,128, 4, 89, 55,128, 4, 90,
+ 56,128, 4, 91, 57,128, 4, 92,177, 48,128, 4, 94, 52, 4, 40,
+ 247, 40,251, 40,255, 41, 3, 53,128, 4, 15, 54,128, 4, 98, 55,
+ 128, 4,114, 56,128, 4,116, 57, 5, 41, 19, 41, 23, 41, 27, 41,
+ 31, 41, 35, 50,128,246,198, 51,128, 4, 95, 52,128, 4, 99, 53,
+ 128, 4,115, 54,128, 4,117, 56, 2, 41, 45, 41, 59, 51, 2, 41,
+ 51, 41, 55, 49,128,246,199, 50,128,246,200,180, 54,128, 4,217,
+ 178,185, 57,128, 32, 14,179, 48, 2, 41, 77, 41, 81, 48,128, 32,
+ 15, 49,128, 32, 13,181, 55, 7, 41,102, 41,172, 42,237, 43, 58,
+ 44, 15, 44,108, 44,179, 51, 2, 41,108, 41,122, 56, 2, 41,114,
+ 41,118, 49,128, 6,106, 56,128, 6, 12, 57, 8, 41,140, 41,144,
+ 41,148, 41,152, 41,156, 41,160, 41,164, 41,168, 50,128, 6, 96,
+ 51,128, 6, 97, 52,128, 6, 98, 53,128, 6, 99, 54,128, 6,100,
+ 55,128, 6,101, 56,128, 6,102, 57,128, 6,103, 52, 7, 41,188,
+ 41,220, 42, 26, 42, 88, 42,120, 42,176, 42,232, 48, 5, 41,200,
+ 41,204, 41,208, 41,212, 41,216, 48,128, 6,104, 49,128, 6,105,
+ 51,128, 6, 27, 55,128, 6, 31, 57,128, 6, 33, 49, 10, 41,242,
+ 41,246, 41,250, 41,254, 42, 2, 42, 6, 42, 10, 42, 14, 42, 18,
+ 42, 22, 48,128, 6, 34, 49,128, 6, 35, 50,128, 6, 36, 51,128,
+ 6, 37, 52,128, 6, 38, 53,128, 6, 39, 54,128, 6, 40, 55,128,
+ 6, 41, 56,128, 6, 42, 57,128, 6, 43, 50, 10, 42, 48, 42, 52,
+ 42, 56, 42, 60, 42, 64, 42, 68, 42, 72, 42, 76, 42, 80, 42, 84,
+ 48,128, 6, 44, 49,128, 6, 45, 50,128, 6, 46, 51,128, 6, 47,
+ 52,128, 6, 48, 53,128, 6, 49, 54,128, 6, 50, 55,128, 6, 51,
+ 56,128, 6, 52, 57,128, 6, 53, 51, 5, 42,100, 42,104, 42,108,
+ 42,112, 42,116, 48,128, 6, 54, 49,128, 6, 55, 50,128, 6, 56,
+ 51,128, 6, 57, 52,128, 6, 58, 52, 9, 42,140, 42,144, 42,148,
+ 42,152, 42,156, 42,160, 42,164, 42,168, 42,172, 48,128, 6, 64,
+ 49,128, 6, 65, 50,128, 6, 66, 51,128, 6, 67, 52,128, 6, 68,
+ 53,128, 6, 69, 54,128, 6, 70, 56,128, 6, 72, 57,128, 6, 73,
+ 53, 9, 42,196, 42,200, 42,204, 42,208, 42,212, 42,216, 42,220,
+ 42,224, 42,228, 48,128, 6, 74, 49,128, 6, 75, 50,128, 6, 76,
+ 51,128, 6, 77, 52,128, 6, 78, 53,128, 6, 79, 54,128, 6, 80,
+ 55,128, 6, 81, 56,128, 6, 82,183, 48,128, 6, 71, 53, 3, 42,
+ 245, 43, 21, 43, 53, 48, 5, 43, 1, 43, 5, 43, 9, 43, 13, 43,
+ 17, 53,128, 6,164, 54,128, 6,126, 55,128, 6,134, 56,128, 6,
+ 152, 57,128, 6,175, 49, 5, 43, 33, 43, 37, 43, 41, 43, 45, 43,
+ 49, 49,128, 6,121, 50,128, 6,136, 51,128, 6,145, 52,128, 6,
+ 186, 57,128, 6,210,179, 52,128, 6,213, 54, 7, 43, 74, 43, 79,
+ 43, 84, 43, 89, 43,127, 43,189, 43,251,179, 54,128, 32,170,180,
+ 53,128, 5,190,181, 56,128, 5,195, 54, 6, 43,103, 43,107, 43,
+ 111, 43,115, 43,119, 43,123, 52,128, 5,208, 53,128, 5,209, 54,
+ 128, 5,210, 55,128, 5,211, 56,128, 5,212, 57,128, 5,213, 55,
+ 10, 43,149, 43,153, 43,157, 43,161, 43,165, 43,169, 43,173, 43,
+ 177, 43,181, 43,185, 48,128, 5,214, 49,128, 5,215, 50,128, 5,
+ 216, 51,128, 5,217, 52,128, 5,218, 53,128, 5,219, 54,128, 5,
+ 220, 55,128, 5,221, 56,128, 5,222, 57,128, 5,223, 56, 10, 43,
+ 211, 43,215, 43,219, 43,223, 43,227, 43,231, 43,235, 43,239, 43,
+ 243, 43,247, 48,128, 5,224, 49,128, 5,225, 50,128, 5,226, 51,
+ 128, 5,227, 52,128, 5,228, 53,128, 5,229, 54,128, 5,230, 55,
+ 128, 5,231, 56,128, 5,232, 57,128, 5,233, 57, 3, 44, 3, 44,
+ 7, 44, 11, 48,128, 5,234, 52,128,251, 42, 53,128,251, 43, 55,
+ 4, 44, 25, 44, 39, 44, 59, 44, 64, 48, 2, 44, 31, 44, 35, 48,
+ 128,251, 75, 53,128,251, 31, 49, 3, 44, 47, 44, 51, 44, 55, 54,
+ 128, 5,240, 55,128, 5,241, 56,128, 5,242,178, 51,128,251, 53,
+ 57, 7, 44, 80, 44, 84, 44, 88, 44, 92, 44, 96, 44,100, 44,104,
+ 51,128, 5,180, 52,128, 5,181, 53,128, 5,182, 54,128, 5,187,
+ 55,128, 5,184, 56,128, 5,183, 57,128, 5,176, 56, 3, 44,116,
+ 44,160, 44,165, 48, 7, 44,132, 44,136, 44,140, 44,144, 44,148,
+ 44,152, 44,156, 48,128, 5,178, 49,128, 5,177, 50,128, 5,179,
+ 51,128, 5,194, 52,128, 5,193, 54,128, 5,185, 55,128, 5,188,
+ 179, 57,128, 5,189, 52, 2, 44,171, 44,175, 49,128, 5,191, 50,
+ 128, 5,192,185,178, 57,128, 2,188, 54, 3, 44,193, 44,252, 45,
+ 3, 49, 4, 44,203, 44,219, 44,225, 44,246, 50, 2, 44,209, 44,
+ 214,180, 56,128, 33, 5,184, 57,128, 33, 19,179,181, 50,128, 33,
+ 22,181, 55, 3, 44,234, 44,238, 44,242, 51,128, 32, 44, 52,128,
+ 32, 45, 53,128, 32, 46,182,182, 52,128, 32, 12,179,177,182, 55,
+ 128, 6,109,180,185,179, 55,128, 2,189,103, 2, 45, 16, 45, 23,
+ 242,225,246,101,128, 0,224,117, 2, 45, 29, 45, 38,234,225,242,
+ 225,244,105,128, 10,133,242,237,245,235,232,105,128, 10, 5,104,
+ 2, 45, 53, 45, 63,233,242,225,231,225,238, 97,128, 48, 66,239,
+ 239,235,225,226,239,246,101,128, 30,163,105, 7, 45, 90, 45,115,
+ 45,122, 45,134, 45,159, 45,175, 45,255, 98, 2, 45, 96, 45,105,
+ 229,238,231,225,236,105,128, 9,144,239,240,239,237,239,230,111,
+ 128, 49, 30,228,229,246, 97,128, 9, 16,229,227,249,242,233,236,
+ 236,233, 99,128, 4,213,231,117, 2, 45,141, 45,150,234,225,242,
+ 225,244,105,128, 10,144,242,237,245,235,232,105,128, 10, 16,237,
+ 225,244,242,225,231,245,242,237,245,235,232,105,128, 10, 72,110,
+ 5, 45,187, 45,196, 45,210, 45,226, 45,241,225,242,225,226,233,
+ 99,128, 6, 57,230,233,238,225,236,225,242,225,226,233, 99,128,
+ 254,202,233,238,233,244,233,225,236,225,242,225,226,233, 99,128,
+ 254,203,237,229,228,233,225,236,225,242,225,226,233, 99,128,254,
+ 204,246,229,242,244,229,228,226,242,229,246,101,128, 2, 3,246,
+ 239,247,229,236,243,233,231,110, 3, 46, 15, 46, 25, 46, 32,226,
+ 229,238,231,225,236,105,128, 9,200,228,229,246, 97,128, 9, 72,
+ 231,245,234,225,242,225,244,105,128, 10,200,107, 2, 46, 49, 46,
+ 73,225,244,225,235,225,238, 97,129, 48,162, 46, 61,232,225,236,
+ 230,247,233,228,244,104,128,255,113,239,242,229,225,110,128, 49,
+ 79,108, 3, 46, 89, 47,145, 47,154,101, 2, 46, 95, 47,140,102,
+ 136, 5,208, 46,115, 46,124, 46,139, 46,153, 46,242, 47, 0, 47,
+ 111, 47,125,225,242,225,226,233, 99,128, 6, 39,228,225,231,229,
+ 243,232,232,229,226,242,229,119,128,251, 48,230,233,238,225,236,
+ 225,242,225,226,233, 99,128,254,142,104, 2, 46,159, 46,234,225,
+ 237,250, 97, 2, 46,168, 46,201,225,226,239,246,101, 2, 46,178,
+ 46,187,225,242,225,226,233, 99,128, 6, 35,230,233,238,225,236,
+ 225,242,225,226,233, 99,128,254,132,226,229,236,239,119, 2, 46,
+ 211, 46,220,225,242,225,226,233, 99,128, 6, 37,230,233,238,225,
+ 236,225,242,225,226,233, 99,128,254,136,229,226,242,229,119,128,
+ 5,208,236,225,237,229,228,232,229,226,242,229,119,128,251, 79,
+ 237, 97, 2, 47, 7, 47, 43,228,228,225,225,226,239,246,101, 2,
+ 47, 20, 47, 29,225,242,225,226,233, 99,128, 6, 34,230,233,238,
+ 225,236,225,242,225,226,233, 99,128,254,130,235,243,245,242, 97,
+ 4, 47, 57, 47, 66, 47, 80, 47, 96,225,242,225,226,233, 99,128,
+ 6, 73,230,233,238,225,236,225,242,225,226,233, 99,128,254,240,
+ 233,238,233,244,233,225,236,225,242,225,226,233, 99,128,254,243,
+ 237,229,228,233,225,236,225,242,225,226,233, 99,128,254,244,240,
+ 225,244,225,232,232,229,226,242,229,119,128,251, 46,241,225,237,
+ 225,244,243,232,229,226,242,229,119,128,251, 47,240,104,128, 33,
+ 53,236,229,241,245,225,108,128, 34, 76,240,232, 97,129, 3,177,
+ 47,162,244,239,238,239,115,128, 3,172,109, 4, 47,180, 47,188,
+ 47,199, 47,233,225,227,242,239,110,128, 1, 1,239,238,239,243,
+ 240,225,227,101,128,255, 65,240,229,242,243,225,238,100,130, 0,
+ 38, 47,213, 47,225,237,239,238,239,243,240,225,227,101,128,255,
+ 6,243,237,225,236,108,128,247, 38,243,241,245,225,242,101,128,
+ 51,194,110, 4, 47,252, 48, 7, 48,129, 48,139,226,239,240,239,
+ 237,239,230,111,128, 49, 34,103, 4, 48, 17, 48, 28, 48, 42, 48,
+ 121,226,239,240,239,237,239,230,111,128, 49, 36,235,232,225,238,
+ 235,232,245,244,232,225,105,128, 14, 90,236,101,131, 34, 32, 48,
+ 53, 48,106, 48,113,226,242,225,227,235,229,116, 2, 48, 65, 48,
+ 85,236,229,230,116,129, 48, 8, 48, 74,246,229,242,244,233,227,
+ 225,108,128,254, 63,242,233,231,232,116,129, 48, 9, 48, 95,246,
+ 229,242,244,233,227,225,108,128,254, 64,236,229,230,116,128, 35,
+ 41,242,233,231,232,116,128, 35, 42,243,244,242,239,109,128, 33,
+ 43,239,244,229,236,229,233, 97,128, 3,135,117, 2, 48,145, 48,
+ 157,228,225,244,244,225,228,229,246, 97,128, 9, 82,243,246,225,
+ 242, 97, 3, 48,169, 48,179, 48,186,226,229,238,231,225,236,105,
+ 128, 9,130,228,229,246, 97,128, 9, 2,231,245,234,225,242,225,
+ 244,105,128, 10,130,239,231,239,238,229,107,128, 1, 5,112, 3,
+ 48,214, 48,238, 49, 12, 97, 2, 48,220, 48,232,225,244,239,243,
+ 241,245,225,242,101,128, 51, 0,242,229,110,128, 36,156,239,243,
+ 244,242,239,240,232,101, 2, 48,251, 49, 6,225,242,237,229,238,
+ 233,225,110,128, 5, 90,237,239,100,128, 2,188,112, 2, 49, 18,
+ 49, 23,236,101,128,248,255,242,111, 2, 49, 30, 49, 38,225,227,
+ 232,229,115,128, 34, 80,120, 2, 49, 44, 49, 64,229,241,245,225,
+ 108,129, 34, 72, 49, 54,239,242,233,237,225,231,101,128, 34, 82,
+ 233,237,225,244,229,236,249,229,241,245,225,108,128, 34, 69,114,
+ 4, 49, 89, 49,116, 49,120, 49,165,225,229, 97, 2, 49, 97, 49,
+ 107,229,235,239,242,229,225,110,128, 49,142,235,239,242,229,225,
+ 110,128, 49,141, 99,128, 35, 18,105, 2, 49,126, 49,140,231,232,
+ 244,232,225,236,230,242,233,238,103,128, 30,154,238,103,130, 0,
+ 229, 49,149, 49,157,225,227,245,244,101,128, 1,251,226,229,236,
+ 239,119,128, 30, 1,242,239,119, 8, 49,185, 49,192, 50, 65, 50,
+ 131, 50,181, 50,236, 51, 3, 51, 78,226,239,244,104,128, 33,148,
+ 100, 3, 49,200, 49,239, 50, 30,225,243,104, 4, 49,212, 49,219,
+ 49,226, 49,234,228,239,247,110,128, 33,227,236,229,230,116,128,
+ 33,224,242,233,231,232,116,128, 33,226,245,112,128, 33,225,226,
+ 108, 5, 49,252, 50, 3, 50, 10, 50, 17, 50, 25,226,239,244,104,
+ 128, 33,212,228,239,247,110,128, 33,211,236,229,230,116,128, 33,
+ 208,242,233,231,232,116,128, 33,210,245,112,128, 33,209,239,247,
+ 110,131, 33,147, 50, 42, 50, 49, 50, 57,236,229,230,116,128, 33,
+ 153,242,233,231,232,116,128, 33,152,247,232,233,244,101,128, 33,
+ 233,104, 2, 50, 71, 50,122,229,225,100, 4, 50, 83, 50, 93, 50,
+ 103, 50,114,228,239,247,238,237,239,100,128, 2,197,236,229,230,
+ 244,237,239,100,128, 2,194,242,233,231,232,244,237,239,100,128,
+ 2,195,245,240,237,239,100,128, 2,196,239,242,233,250,229,120,
+ 128,248,231,236,229,230,116,131, 33,144, 50,144, 50,161, 50,173,
+ 228,226,108,129, 33,208, 50,152,243,244,242,239,235,101,128, 33,
+ 205,239,246,229,242,242,233,231,232,116,128, 33,198,247,232,233,
+ 244,101,128, 33,230,242,233,231,232,116,132, 33,146, 50,197, 50,
+ 209, 50,217, 50,228,228,226,236,243,244,242,239,235,101,128, 33,
+ 207,232,229,225,246,121,128, 39,158,239,246,229,242,236,229,230,
+ 116,128, 33,196,247,232,233,244,101,128, 33,232,244,225, 98, 2,
+ 50,244, 50,251,236,229,230,116,128, 33,228,242,233,231,232,116,
+ 128, 33,229,245,112,132, 33,145, 51, 16, 51, 44, 51, 62, 51, 70,
+ 100, 2, 51, 22, 51, 34,110,129, 33,149, 51, 28,226,243,101,128,
+ 33,168,239,247,238,226,225,243,101,128, 33,168,236,229,230,116,
+ 129, 33,150, 51, 53,239,230,228,239,247,110,128, 33,197,242,233,
+ 231,232,116,128, 33,151,247,232,233,244,101,128, 33,231,246,229,
+ 242,244,229,120,128,248,230,115, 5, 51, 99, 51,175, 51,220, 52,
+ 47, 52, 57, 99, 2, 51,105, 51,157,233,105, 2, 51,112, 51,135,
+ 227,233,242,227,245,109,129, 0, 94, 51,123,237,239,238,239,243,
+ 240,225,227,101,128,255, 62,244,233,236,228,101,129, 0,126, 51,
+ 145,237,239,238,239,243,240,225,227,101,128,255, 94,242,233,240,
+ 116,129, 2, 81, 51,166,244,245,242,238,229,100,128, 2, 82,237,
+ 225,236,108, 2, 51,184, 51,195,232,233,242,225,231,225,238, 97,
+ 128, 48, 65,235,225,244,225,235,225,238, 97,129, 48,161, 51,208,
+ 232,225,236,230,247,233,228,244,104,128,255,103,244,229,242,233,
+ 115, 2, 51,230, 52, 43,107,131, 0, 42, 51,240, 52, 12, 52, 35,
+ 97, 2, 51,246, 52, 4,236,244,239,238,229,225,242,225,226,233,
+ 99,128, 6,109,242,225,226,233, 99,128, 6,109,109, 2, 52, 18,
+ 52, 24,225,244,104,128, 34, 23,239,238,239,243,240,225,227,101,
+ 128,255, 10,243,237,225,236,108,128,254, 97,109,128, 32, 66,245,
+ 240,229,242,233,239,114,128,246,233,249,237,240,244,239,244,233,
+ 227,225,236,236,249,229,241,245,225,108,128, 34, 67,116,132, 0,
+ 64, 52, 89, 52, 96, 52,108, 52,116,233,236,228,101,128, 0,227,
+ 237,239,238,239,243,240,225,227,101,128,255, 32,243,237,225,236,
+ 108,128,254,107,245,242,238,229,100,128, 2, 80,117, 6, 52,138,
+ 52,163, 52,170, 52,195, 52,215, 52,231, 98, 2, 52,144, 52,153,
+ 229,238,231,225,236,105,128, 9,148,239,240,239,237,239,230,111,
+ 128, 49, 32,228,229,246, 97,128, 9, 20,231,117, 2, 52,177, 52,
+ 186,234,225,242,225,244,105,128, 10,148,242,237,245,235,232,105,
+ 128, 10, 20,236,229,238,231,244,232,237,225,242,235,226,229,238,
+ 231,225,236,105,128, 9,215,237,225,244,242,225,231,245,242,237,
+ 245,235,232,105,128, 10, 76,246,239,247,229,236,243,233,231,110,
+ 3, 52,247, 53, 1, 53, 8,226,229,238,231,225,236,105,128, 9,
+ 204,228,229,246, 97,128, 9, 76,231,245,234,225,242,225,244,105,
+ 128, 10,204,246,225,231,242,225,232,225,228,229,246, 97,128, 9,
+ 61,121, 2, 53, 39, 53, 51,226,225,242,237,229,238,233,225,110,
+ 128, 5, 97,233,110,130, 5,226, 53, 60, 53, 75,225,236,244,239,
+ 238,229,232,229,226,242,229,119,128,251, 32,232,229,226,242,229,
+ 119,128, 5,226, 98,144, 0, 98, 53,120, 53,255, 54, 10, 54, 19,
+ 54, 44, 55, 85, 55,147, 55,220, 57,146, 57,158, 57,201, 57,209,
+ 57,219, 59, 89, 59,113, 59,122, 97, 7, 53,136, 53,146, 53,170,
+ 53,177, 53,202, 53,226, 53,237,226,229,238,231,225,236,105,128,
+ 9,172,227,235,243,236,225,243,104,129, 0, 92, 53,158,237,239,
+ 238,239,243,240,225,227,101,128,255, 60,228,229,246, 97,128, 9,
+ 44,231,117, 2, 53,184, 53,193,234,225,242,225,244,105,128, 10,
+ 172,242,237,245,235,232,105,128, 10, 44,104, 2, 53,208, 53,218,
+ 233,242,225,231,225,238, 97,128, 48,112,244,244,232,225,105,128,
+ 14, 63,235,225,244,225,235,225,238, 97,128, 48,208,114,129, 0,
+ 124, 53,243,237,239,238,239,243,240,225,227,101,128,255, 92,226,
+ 239,240,239,237,239,230,111,128, 49, 5,227,233,242,227,236,101,
+ 128, 36,209,228,239,116, 2, 54, 27, 54, 36,225,227,227,229,238,
+ 116,128, 30, 3,226,229,236,239,119,128, 30, 5,101, 6, 54, 58,
+ 54, 79, 54,102, 54,244, 54,255, 55, 11,225,237,229,228,243,233,
+ 248,244,229,229,238,244,232,238,239,244,229,115,128, 38,108, 99,
+ 2, 54, 85, 54, 92,225,245,243,101,128, 34, 53,249,242,233,236,
+ 236,233, 99,128, 4, 49,104, 5, 54,114, 54,123, 54,137, 54,167,
+ 54,226,225,242,225,226,233, 99,128, 6, 40,230,233,238,225,236,
+ 225,242,225,226,233, 99,128,254,144,105, 2, 54,143, 54,158,238,
+ 233,244,233,225,236,225,242,225,226,233, 99,128,254,145,242,225,
+ 231,225,238, 97,128, 48,121,237,101, 2, 54,174, 54,187,228,233,
+ 225,236,225,242,225,226,233, 99,128,254,146,229,237,105, 2, 54,
+ 195, 54,210,238,233,244,233,225,236,225,242,225,226,233, 99,128,
+ 252,159,243,239,236,225,244,229,228,225,242,225,226,233, 99,128,
+ 252, 8,238,239,239,238,230,233,238,225,236,225,242,225,226,233,
+ 99,128,252,109,235,225,244,225,235,225,238, 97,128, 48,217,238,
+ 225,242,237,229,238,233,225,110,128, 5, 98,116,132, 5,209, 55,
+ 23, 55, 43, 55, 63, 55, 72, 97,129, 3,178, 55, 29,243,249,237,
+ 226,239,236,231,242,229,229,107,128, 3,208,228,225,231,229,243,
+ 104,129,251, 49, 55, 54,232,229,226,242,229,119,128,251, 49,232,
+ 229,226,242,229,119,128, 5,209,242,225,230,229,232,229,226,242,
+ 229,119,128,251, 76,104, 2, 55, 91, 55,141, 97, 3, 55, 99, 55,
+ 109, 55,116,226,229,238,231,225,236,105,128, 9,173,228,229,246,
+ 97,128, 9, 45,231,117, 2, 55,123, 55,132,234,225,242,225,244,
+ 105,128, 10,173,242,237,245,235,232,105,128, 10, 45,239,239,107,
+ 128, 2, 83,105, 5, 55,159, 55,170, 55,181, 55,195, 55,209,232,
+ 233,242,225,231,225,238, 97,128, 48,115,235,225,244,225,235,225,
+ 238, 97,128, 48,211,236,225,226,233,225,236,227,236,233,227,107,
+ 128, 2,152,238,228,233,231,245,242,237,245,235,232,105,128, 10,
+ 2,242,245,243,241,245,225,242,101,128, 51, 49,108, 3, 55,228,
+ 57,129, 57,140, 97, 2, 55,234, 57,124,227,107, 6, 55,249, 56,
+ 2, 56, 39, 56,188, 56,243, 57, 39,227,233,242,227,236,101,128,
+ 37,207,100, 2, 56, 8, 56, 17,233,225,237,239,238,100,128, 37,
+ 198,239,247,238,240,239,233,238,244,233,238,231,244,242,233,225,
+ 238,231,236,101,128, 37,188,108, 2, 56, 45, 56,148,101, 2, 56,
+ 51, 56, 87,230,244,240,239,233,238,244,233,238,103, 2, 56, 66,
+ 56, 76,240,239,233,238,244,229,114,128, 37,196,244,242,233,225,
+ 238,231,236,101,128, 37,192,238,244,233,227,245,236,225,242,226,
+ 242,225,227,235,229,116, 2, 56,107, 56,127,236,229,230,116,129,
+ 48, 16, 56,116,246,229,242,244,233,227,225,108,128,254, 59,242,
+ 233,231,232,116,129, 48, 17, 56,137,246,229,242,244,233,227,225,
+ 108,128,254, 60,239,247,229,114, 2, 56,157, 56,172,236,229,230,
+ 244,244,242,233,225,238,231,236,101,128, 37,227,242,233,231,232,
+ 244,244,242,233,225,238,231,236,101,128, 37,226,114, 2, 56,194,
+ 56,205,229,227,244,225,238,231,236,101,128, 37,172,233,231,232,
+ 244,240,239,233,238,244,233,238,103, 2, 56,222, 56,232,240,239,
+ 233,238,244,229,114,128, 37,186,244,242,233,225,238,231,236,101,
+ 128, 37,182,115, 3, 56,251, 57, 25, 57, 33,109, 2, 57, 1, 57,
+ 13,225,236,236,243,241,245,225,242,101,128, 37,170,233,236,233,
+ 238,231,230,225,227,101,128, 38, 59,241,245,225,242,101,128, 37,
+ 160,244,225,114,128, 38, 5,245,240,112, 2, 57, 47, 57, 85,229,
+ 114, 2, 57, 54, 57, 69,236,229,230,244,244,242,233,225,238,231,
+ 236,101,128, 37,228,242,233,231,232,244,244,242,233,225,238,231,
+ 236,101,128, 37,229,239,233,238,244,233,238,103, 2, 57, 97, 57,
+ 113,243,237,225,236,236,244,242,233,225,238,231,236,101,128, 37,
+ 180,244,242,233,225,238,231,236,101,128, 37,178,238,107,128, 36,
+ 35,233,238,229,226,229,236,239,119,128, 30, 7,239,227,107,128,
+ 37,136,237,239,238,239,243,240,225,227,101,128,255, 66,111, 3,
+ 57,166, 57,179, 57,190,226,225,233,237,225,233,244,232,225,105,
+ 128, 14, 26,232,233,242,225,231,225,238, 97,128, 48,124,235,225,
+ 244,225,235,225,238, 97,128, 48,220,240,225,242,229,110,128, 36,
+ 157,241,243,241,245,225,242,101,128, 51,195,114, 4, 57,229, 58,
+ 223, 59, 40, 59, 79,225, 99, 2, 57,236, 58,130,101, 3, 57,244,
+ 57,249, 58, 61,229,120,128,248,244,236,229,230,116,133, 0,123,
+ 58, 10, 58, 15, 58, 37, 58, 45, 58, 50,226,116,128,248,243,109,
+ 2, 58, 21, 58, 26,233,100,128,248,242,239,238,239,243,240,225,
+ 227,101,128,255, 91,243,237,225,236,108,128,254, 91,244,112,128,
+ 248,241,246,229,242,244,233,227,225,108,128,254, 55,242,233,231,
+ 232,116,133, 0,125, 58, 79, 58, 84, 58,106, 58,114, 58,119,226,
+ 116,128,248,254,109, 2, 58, 90, 58, 95,233,100,128,248,253,239,
+ 238,239,243,240,225,227,101,128,255, 93,243,237,225,236,108,128,
+ 254, 92,244,112,128,248,252,246,229,242,244,233,227,225,108,128,
+ 254, 56,235,229,116, 2, 58,138, 58,180,236,229,230,116,132, 0,
+ 91, 58,153, 58,158, 58,163, 58,175,226,116,128,248,240,229,120,
+ 128,248,239,237,239,238,239,243,240,225,227,101,128,255, 59,244,
+ 112,128,248,238,242,233,231,232,116,132, 0, 93, 58,196, 58,201,
+ 58,206, 58,218,226,116,128,248,251,229,120,128,248,250,237,239,
+ 238,239,243,240,225,227,101,128,255, 61,244,112,128,248,249,229,
+ 246,101,131, 2,216, 58,235, 58,246, 58,252,226,229,236,239,247,
+ 227,237, 98,128, 3, 46,227,237, 98,128, 3, 6,233,238,246,229,
+ 242,244,229,100, 3, 59, 11, 59, 22, 59, 28,226,229,236,239,247,
+ 227,237, 98,128, 3, 47,227,237, 98,128, 3, 17,228,239,245,226,
+ 236,229,227,237, 98,128, 3, 97,233,228,231,101, 2, 59, 49, 59,
+ 60,226,229,236,239,247,227,237, 98,128, 3, 42,233,238,246,229,
+ 242,244,229,228,226,229,236,239,247,227,237, 98,128, 3, 58,239,
+ 235,229,238,226,225,114,128, 0,166,115, 2, 59, 95, 59,103,244,
+ 242,239,235,101,128, 1,128,245,240,229,242,233,239,114,128,246,
+ 234,244,239,240,226,225,114,128, 1,131,117, 3, 59,130, 59,141,
+ 59,152,232,233,242,225,231,225,238, 97,128, 48,118,235,225,244,
+ 225,235,225,238, 97,128, 48,214,236,108, 2, 59,159, 59,189,229,
+ 116,130, 32, 34, 59,168, 59,178,233,238,246,229,242,243,101,128,
+ 37,216,239,240,229,242,225,244,239,114,128, 34, 25,243,229,249,
+ 101,128, 37,206, 99,143, 0, 99, 59,230, 60,179, 60,190, 60,254,
+ 61, 29, 61,122, 63, 33, 64, 17, 64,117, 64,166, 67,158, 67,166,
+ 67,176, 67,188, 67,221, 97, 9, 59,250, 60, 5, 60, 15, 60, 22,
+ 60, 29, 60, 54, 60, 64, 60,116, 60,125,225,242,237,229,238,233,
+ 225,110,128, 5,110,226,229,238,231,225,236,105,128, 9,154,227,
+ 245,244,101,128, 1, 7,228,229,246, 97,128, 9, 26,231,117, 2,
+ 60, 36, 60, 45,234,225,242,225,244,105,128, 10,154,242,237,245,
+ 235,232,105,128, 10, 26,236,243,241,245,225,242,101,128, 51,136,
+ 238,228,242,225,226,233,238,228,117, 4, 60, 82, 60, 92, 60, 98,
+ 60,105,226,229,238,231,225,236,105,128, 9,129,227,237, 98,128,
+ 3, 16,228,229,246, 97,128, 9, 1,231,245,234,225,242,225,244,
+ 105,128, 10,129,240,243,236,239,227,107,128, 33,234,114, 3, 60,
+ 133, 60,139, 60,165,229,239,102,128, 33, 5,239,110,130, 2,199,
+ 60,148, 60,159,226,229,236,239,247,227,237, 98,128, 3, 44,227,
+ 237, 98,128, 3, 12,242,233,225,231,229,242,229,244,245,242,110,
+ 128, 33,181,226,239,240,239,237,239,230,111,128, 49, 24, 99, 4,
+ 60,200, 60,207, 60,226, 60,248,225,242,239,110,128, 1, 13,229,
+ 228,233,236,236, 97,129, 0,231, 60,218,225,227,245,244,101,128,
+ 30, 9,233,242, 99, 2, 60,234, 60,239,236,101,128, 36,210,245,
+ 237,230,236,229,120,128, 1, 9,245,242,108,128, 2, 85,100, 2,
+ 61, 4, 61, 20,239,116,129, 1, 11, 61, 11,225,227,227,229,238,
+ 116,128, 1, 11,243,241,245,225,242,101,128, 51,197,101, 2, 61,
+ 35, 61, 51,228,233,236,236, 97,129, 0,184, 61, 45,227,237, 98,
+ 128, 3, 39,238,116,132, 0,162, 61, 64, 61, 88, 61,100, 61,111,
+ 105, 2, 61, 70, 61, 78,231,242,225,228,101,128, 33, 3,238,230,
+ 229,242,233,239,114,128,246,223,237,239,238,239,243,240,225,227,
+ 101,128,255,224,239,236,228,243,244,249,236,101,128,247,162,243,
+ 245,240,229,242,233,239,114,128,246,224,104, 5, 61,134, 61,197,
+ 61,208, 62,136, 62,228, 97, 4, 61,144, 61,155, 61,165, 61,172,
+ 225,242,237,229,238,233,225,110,128, 5,121,226,229,238,231,225,
+ 236,105,128, 9,155,228,229,246, 97,128, 9, 27,231,117, 2, 61,
+ 179, 61,188,234,225,242,225,244,105,128, 10,155,242,237,245,235,
+ 232,105,128, 10, 27,226,239,240,239,237,239,230,111,128, 49, 20,
+ 101, 6, 61,222, 61,242, 62, 10, 62, 78, 62, 90, 62,111,225,226,
+ 235,232,225,243,233,225,238,227,249,242,233,236,236,233, 99,128,
+ 4,189, 99, 2, 61,248, 62, 0,235,237,225,242,107,128, 39, 19,
+ 249,242,233,236,236,233, 99,128, 4, 71,100, 2, 62, 16, 62, 60,
+ 229,243,227,229,238,228,229,114, 2, 62, 29, 62, 49,225,226,235,
+ 232,225,243,233,225,238,227,249,242,233,236,236,233, 99,128, 4,
+ 191,227,249,242,233,236,236,233, 99,128, 4,183,233,229,242,229,
+ 243,233,243,227,249,242,233,236,236,233, 99,128, 4,245,232,225,
+ 242,237,229,238,233,225,110,128, 5,115,235,232,225,235,225,243,
+ 243,233,225,238,227,249,242,233,236,236,233, 99,128, 4,204,246,
+ 229,242,244,233,227,225,236,243,244,242,239,235,229,227,249,242,
+ 233,236,236,233, 99,128, 4,185,105,129, 3,199, 62,142,229,245,
+ 227,104, 4, 62,155, 62,190, 62,205, 62,214, 97, 2, 62,161, 62,
+ 176,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,119,
+ 240,225,242,229,238,235,239,242,229,225,110,128, 50, 23,227,233,
+ 242,227,236,229,235,239,242,229,225,110,128, 50,105,235,239,242,
+ 229,225,110,128, 49, 74,240,225,242,229,238,235,239,242,229,225,
+ 110,128, 50, 9,111, 2, 62,234, 63, 28,227,104, 3, 62,243, 63,
+ 9, 63, 19,225,110, 2, 62,250, 63, 2,231,244,232,225,105,128,
+ 14, 10,244,232,225,105,128, 14, 8,233,238,231,244,232,225,105,
+ 128, 14, 9,239,229,244,232,225,105,128, 14, 12,239,107,128, 1,
+ 136,105, 2, 63, 39, 63,141,229,245, 99, 5, 63, 53, 63, 88, 63,
+ 103, 63,112, 63,126, 97, 2, 63, 59, 63, 74,227,233,242,227,236,
+ 229,235,239,242,229,225,110,128, 50,118,240,225,242,229,238,235,
+ 239,242,229,225,110,128, 50, 22,227,233,242,227,236,229,235,239,
+ 242,229,225,110,128, 50,104,235,239,242,229,225,110,128, 49, 72,
+ 240,225,242,229,238,235,239,242,229,225,110,128, 50, 8,245,240,
+ 225,242,229,238,235,239,242,229,225,110,128, 50, 28,242, 99, 2,
+ 63,148, 63,243,236,101,132, 37,203, 63,161, 63,172, 63,177, 63,
+ 201,237,245,236,244,233,240,236,121,128, 34,151,239,116,128, 34,
+ 153,112, 2, 63,183, 63,189,236,245,115,128, 34,149,239,243,244,
+ 225,236,237,225,242,107,128, 48, 54,247,233,244,104, 2, 63,210,
+ 63,226,236,229,230,244,232,225,236,230,226,236,225,227,107,128,
+ 37,208,242,233,231,232,244,232,225,236,230,226,236,225,227,107,
+ 128, 37,209,245,237,230,236,229,120,130, 2,198, 64, 0, 64, 11,
+ 226,229,236,239,247,227,237, 98,128, 3, 45,227,237, 98,128, 3,
+ 2,108, 3, 64, 25, 64, 31, 64, 85,229,225,114,128, 35, 39,233,
+ 227,107, 4, 64, 43, 64, 54, 64, 63, 64, 73,225,236,246,229,239,
+ 236,225,114,128, 1,194,228,229,238,244,225,108,128, 1,192,236,
+ 225,244,229,242,225,108,128, 1,193,242,229,244,242,239,230,236,
+ 229,120,128, 1,195,245, 98,129, 38, 99, 64, 92,243,245,233,116,
+ 2, 64,101, 64,109,226,236,225,227,107,128, 38, 99,247,232,233,
+ 244,101,128, 38,103,109, 3, 64,125, 64,139, 64,150,227,245,226,
+ 229,228,243,241,245,225,242,101,128, 51,164,239,238,239,243,240,
+ 225,227,101,128,255, 67,243,241,245,225,242,229,228,243,241,245,
+ 225,242,101,128, 51,160,111, 8, 64,184, 64,195, 65, 26, 65,224,
+ 66,253, 67, 28, 67,135, 67,144,225,242,237,229,238,233,225,110,
+ 128, 5,129,236,239,110,131, 0, 58, 64,207, 64,232, 64,251,237,
+ 239,110, 2, 64,215, 64,223,229,244,225,242,121,128, 32,161,239,
+ 243,240,225,227,101,128,255, 26,115, 2, 64,238, 64,244,233,231,
+ 110,128, 32,161,237,225,236,108,128,254, 85,244,242,233,225,238,
+ 231,245,236,225,114, 2, 65, 10, 65, 20,232,225,236,230,237,239,
+ 100,128, 2,209,237,239,100,128, 2,208,109, 2, 65, 32, 65,217,
+ 237, 97,134, 0, 44, 65, 49, 65,113, 65,124, 65,136, 65,166, 65,
+ 189, 97, 3, 65, 57, 65, 83, 65, 91,226,239,246,101, 2, 65, 66,
+ 65, 72,227,237, 98,128, 3, 19,242,233,231,232,244,227,237, 98,
+ 128, 3, 21,227,227,229,238,116,128,246,195,114, 2, 65, 97, 65,
+ 104,225,226,233, 99,128, 6, 12,237,229,238,233,225,110,128, 5,
+ 93,233,238,230,229,242,233,239,114,128,246,225,237,239,238,239,
+ 243,240,225,227,101,128,255, 12,242,229,246,229,242,243,229,100,
+ 2, 65,149, 65,160,225,226,239,246,229,227,237, 98,128, 3, 20,
+ 237,239,100,128, 2,189,115, 2, 65,172, 65,179,237,225,236,108,
+ 128,254, 80,245,240,229,242,233,239,114,128,246,226,244,245,242,
+ 238,229,100, 2, 65,200, 65,211,225,226,239,246,229,227,237, 98,
+ 128, 3, 18,237,239,100,128, 2,187,240,225,243,115,128, 38, 60,
+ 110, 2, 65,230, 65,239,231,242,245,229,238,116,128, 34, 69,116,
+ 2, 65,245, 66, 3,239,245,242,233,238,244,229,231,242,225,108,
+ 128, 34, 46,242,239,108,142, 35, 3, 66, 37, 66, 43, 66, 58, 66,
+ 73, 66,117, 66,162, 66,176, 66,181, 66,186, 66,191, 66,197, 66,
+ 202, 66,243, 66,248,193,195, 75,128, 0, 6, 66, 2, 66, 49, 66,
+ 54,197, 76,128, 0, 7, 83,128, 0, 8, 67, 2, 66, 64, 66, 69,
+ 193, 78,128, 0, 24, 82,128, 0, 13, 68, 3, 66, 81, 66,107, 66,
+ 112, 67, 4, 66, 91, 66, 95, 66, 99, 66,103, 49,128, 0, 17, 50,
+ 128, 0, 18, 51,128, 0, 19, 52,128, 0, 20,197, 76,128, 0,127,
+ 204, 69,128, 0, 16, 69, 5, 66,129, 66,133, 66,138, 66,143, 66,
+ 148, 77,128, 0, 25,206, 81,128, 0, 5,207, 84,128, 0, 4,211,
+ 67,128, 0, 27, 84, 2, 66,154, 66,158, 66,128, 0, 23, 88,128,
+ 0, 3, 70, 2, 66,168, 66,172, 70,128, 0, 12, 83,128, 0, 28,
+ 199, 83,128, 0, 29,200, 84,128, 0, 9,204, 70,128, 0, 10,206,
+ 193, 75,128, 0, 21,210, 83,128, 0, 30, 83, 5, 66,214, 66,218,
+ 66,228, 66,233, 66,238, 73,128, 0, 15, 79,129, 0, 14, 66,224,
+ 84,128, 0, 2,212, 88,128, 0, 1,213, 66,128, 0, 26,217, 78,
+ 128, 0, 22,213, 83,128, 0, 31,214, 84,128, 0, 11,240,249,242,
+ 233,231,232,116,129, 0,169, 67, 9,115, 2, 67, 15, 67, 21,225,
+ 238,115,128,248,233,229,242,233,102,128,246,217,114, 2, 67, 34,
+ 67,118,238,229,242,226,242,225,227,235,229,116, 2, 67, 49, 67,
+ 83,236,229,230,116,130, 48, 12, 67, 60, 67, 72,232,225,236,230,
+ 247,233,228,244,104,128,255, 98,246,229,242,244,233,227,225,108,
+ 128,254, 65,242,233,231,232,116,130, 48, 13, 67, 95, 67,107,232,
+ 225,236,230,247,233,228,244,104,128,255, 99,246,229,242,244,233,
+ 227,225,108,128,254, 66,240,239,242,225,244,233,239,238,243,241,
+ 245,225,242,101,128, 51,127,243,241,245,225,242,101,128, 51,199,
+ 246,229,242,235,231,243,241,245,225,242,101,128, 51,198,240,225,
+ 242,229,110,128, 36,158,242,245,250,229,233,242,111,128, 32,162,
+ 243,244,242,229,244,227,232,229,100,128, 2,151,245,114, 2, 67,
+ 195, 67,213,236,121, 2, 67,202, 67,208,225,238,100,128, 34,207,
+ 239,114,128, 34,206,242,229,238,227,121,128, 0,164,249,114, 4,
+ 67,232, 67,240, 67,247, 67,255,194,242,229,246,101,128,246,209,
+ 198,236,229,120,128,246,210,226,242,229,246,101,128,246,212,230,
+ 236,229,120,128,246,213,100,146, 0,100, 68, 46, 69,184, 70,208,
+ 71, 12, 71,188, 72,142, 72,204, 73,133, 73,146, 73,155, 73,181,
+ 73,206, 73,215, 75, 26, 75, 34, 75, 45, 75, 65, 75, 93, 97, 11,
+ 68, 70, 68, 81, 68, 91, 68,163, 68,226, 68,237, 68,248, 69, 61,
+ 69,123, 69,129, 69,159,225,242,237,229,238,233,225,110,128, 5,
+ 100,226,229,238,231,225,236,105,128, 9,166,100, 5, 68,103, 68,
+ 112, 68,118, 68,132, 68,148,225,242,225,226,233, 99,128, 6, 54,
+ 229,246, 97,128, 9, 38,230,233,238,225,236,225,242,225,226,233,
+ 99,128,254,190,233,238,233,244,233,225,236,225,242,225,226,233,
+ 99,128,254,191,237,229,228,233,225,236,225,242,225,226,233, 99,
+ 128,254,192,103, 3, 68,171, 68,188, 68,202,229,243,104,129, 5,
+ 188, 68,179,232,229,226,242,229,119,128, 5,188,231,229,114,129,
+ 32, 32, 68,196,228,226,108,128, 32, 33,117, 2, 68,208, 68,217,
+ 234,225,242,225,244,105,128, 10,166,242,237,245,235,232,105,128,
+ 10, 38,232,233,242,225,231,225,238, 97,128, 48, 96,235,225,244,
+ 225,235,225,238, 97,128, 48,192,108, 3, 69, 0, 69, 9, 69, 47,
+ 225,242,225,226,233, 99,128, 6, 47,229,116,130, 5,211, 69, 18,
+ 69, 38,228,225,231,229,243,104,129,251, 51, 69, 29,232,229,226,
+ 242,229,119,128,251, 51,232,229,226,242,229,119,128, 5,211,230,
+ 233,238,225,236,225,242,225,226,233, 99,128,254,170,237,237, 97,
+ 3, 69, 71, 69, 80, 69, 92,225,242,225,226,233, 99,128, 6, 79,
+ 236,239,247,225,242,225,226,233, 99,128, 6, 79,244,225,238, 97,
+ 2, 69,101, 69,115,236,244,239,238,229,225,242,225,226,233, 99,
+ 128, 6, 76,242,225,226,233, 99,128, 6, 76,238,228, 97,128, 9,
+ 100,242,231, 97, 2, 69,137, 69,146,232,229,226,242,229,119,128,
+ 5,167,236,229,230,244,232,229,226,242,229,119,128, 5,167,243,
+ 233,225,240,238,229,245,237,225,244,225,227,249,242,233,236,236,
+ 233,227,227,237, 98,128, 4,133, 98, 3, 69,192, 70,189, 70,199,
+ 108, 9, 69,212, 69,220, 70, 77, 70, 85, 70,101, 70,112, 70,130,
+ 70,144, 70,155,199,242,225,246,101,128,246,211, 97, 2, 69,226,
+ 70, 27,238,231,236,229,226,242,225,227,235,229,116, 2, 69,242,
+ 70, 6,236,229,230,116,129, 48, 10, 69,251,246,229,242,244,233,
+ 227,225,108,128,254, 61,242,233,231,232,116,129, 48, 11, 70, 16,
+ 246,229,242,244,233,227,225,108,128,254, 62,114, 2, 70, 33, 70,
+ 54,227,232,233,238,246,229,242,244,229,228,226,229,236,239,247,
+ 227,237, 98,128, 3, 43,242,239,119, 2, 70, 62, 70, 69,236,229,
+ 230,116,128, 33,212,242,233,231,232,116,128, 33,210,228,225,238,
+ 228, 97,128, 9,101,231,242,225,246,101,129,246,214, 70, 95,227,
+ 237, 98,128, 3, 15,233,238,244,229,231,242,225,108,128, 34, 44,
+ 236,239,247,236,233,238,101,129, 32, 23, 70,124,227,237, 98,128,
+ 3, 51,239,246,229,242,236,233,238,229,227,237, 98,128, 3, 63,
+ 240,242,233,237,229,237,239,100,128, 2,186,246,229,242,244,233,
+ 227,225,108, 2, 70,168, 70,174,226,225,114,128, 32, 22,236,233,
+ 238,229,225,226,239,246,229,227,237, 98,128, 3, 14,239,240,239,
+ 237,239,230,111,128, 49, 9,243,241,245,225,242,101,128, 51,200,
+ 99, 4, 70,218, 70,225, 70,234, 71, 5,225,242,239,110,128, 1,
+ 15,229,228,233,236,236, 97,128, 30, 17,233,242, 99, 2, 70,242,
+ 70,247,236,101,128, 36,211,245,237,230,236,229,248,226,229,236,
+ 239,119,128, 30, 19,242,239,225,116,128, 1, 17,100, 4, 71, 22,
+ 71,103, 71,113, 71,164, 97, 4, 71, 32, 71, 42, 71, 49, 71, 74,
+ 226,229,238,231,225,236,105,128, 9,161,228,229,246, 97,128, 9,
+ 33,231,117, 2, 71, 56, 71, 65,234,225,242,225,244,105,128, 10,
+ 161,242,237,245,235,232,105,128, 10, 33,108, 2, 71, 80, 71, 89,
+ 225,242,225,226,233, 99,128, 6,136,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,251,137,228,232,225,228,229,246, 97,128, 9,
+ 92,232, 97, 3, 71,122, 71,132, 71,139,226,229,238,231,225,236,
+ 105,128, 9,162,228,229,246, 97,128, 9, 34,231,117, 2, 71,146,
+ 71,155,234,225,242,225,244,105,128, 10,162,242,237,245,235,232,
+ 105,128, 10, 34,239,116, 2, 71,171, 71,180,225,227,227,229,238,
+ 116,128, 30, 11,226,229,236,239,119,128, 30, 13,101, 8, 71,206,
+ 72, 3, 72, 10, 72, 35, 72, 45, 72, 56, 72,101, 72,137, 99, 2,
+ 71,212, 71,249,233,237,225,236,243,229,240,225,242,225,244,239,
+ 114, 2, 71,230, 71,239,225,242,225,226,233, 99,128, 6,107,240,
+ 229,242,243,233,225,110,128, 6,107,249,242,233,236,236,233, 99,
+ 128, 4, 52,231,242,229,101,128, 0,176,232,105, 2, 72, 17, 72,
+ 26,232,229,226,242,229,119,128, 5,173,242,225,231,225,238, 97,
+ 128, 48,103,233,227,239,240,244,233, 99,128, 3,239,235,225,244,
+ 225,235,225,238, 97,128, 48,199,108, 2, 72, 62, 72, 85,229,244,
+ 101, 2, 72, 70, 72, 77,236,229,230,116,128, 35, 43,242,233,231,
+ 232,116,128, 35, 38,244, 97,129, 3,180, 72, 92,244,245,242,238,
+ 229,100,128, 1,141,238,239,237,233,238,225,244,239,242,237,233,
+ 238,245,243,239,238,229,238,245,237,229,242,225,244,239,242,226,
+ 229,238,231,225,236,105,128, 9,248,250,104,128, 2,164,104, 2,
+ 72,148, 72,198, 97, 3, 72,156, 72,166, 72,173,226,229,238,231,
+ 225,236,105,128, 9,167,228,229,246, 97,128, 9, 39,231,117, 2,
+ 72,180, 72,189,234,225,242,225,244,105,128, 10,167,242,237,245,
+ 235,232,105,128, 10, 39,239,239,107,128, 2, 87,105, 6, 72,218,
+ 73, 11, 73, 71, 73, 82, 73, 93, 73,103, 97, 2, 72,224, 72,246,
+ 236,249,244,233,235,225,244,239,238,239,115,129, 3,133, 72,240,
+ 227,237, 98,128, 3, 68,237,239,238,100,129, 38,102, 72,255,243,
+ 245,233,244,247,232,233,244,101,128, 38, 98,229,242,229,243,233,
+ 115,133, 0,168, 73, 30, 73, 38, 73, 49, 73, 55, 73, 63,225,227,
+ 245,244,101,128,246,215,226,229,236,239,247,227,237, 98,128, 3,
+ 36,227,237, 98,128, 3, 8,231,242,225,246,101,128,246,216,244,
+ 239,238,239,115,128, 3,133,232,233,242,225,231,225,238, 97,128,
+ 48, 98,235,225,244,225,235,225,238, 97,128, 48,194,244,244,239,
+ 237,225,242,107,128, 48, 3,246,105, 2, 73,110, 73,121,228,101,
+ 129, 0,247, 73,117,115,128, 34, 35,243,233,239,238,243,236,225,
+ 243,104,128, 34, 21,234,229,227,249,242,233,236,236,233, 99,128,
+ 4, 82,235,243,232,225,228,101,128, 37,147,108, 2, 73,161, 73,
+ 172,233,238,229,226,229,236,239,119,128, 30, 15,243,241,245,225,
+ 242,101,128, 51,151,109, 2, 73,187, 73,195,225,227,242,239,110,
+ 128, 1, 17,239,238,239,243,240,225,227,101,128,255, 68,238,226,
+ 236,239,227,107,128, 37,132,111, 10, 73,237, 73,249, 74, 3, 74,
+ 14, 74, 25, 74, 97, 74,102, 74,113, 74,228, 74,254,227,232,225,
+ 228,225,244,232,225,105,128, 14, 14,228,229,235,244,232,225,105,
+ 128, 14, 20,232,233,242,225,231,225,238, 97,128, 48,105,235,225,
+ 244,225,235,225,238, 97,128, 48,201,236,236,225,114,132, 0, 36,
+ 74, 40, 74, 51, 74, 63, 74, 74,233,238,230,229,242,233,239,114,
+ 128,246,227,237,239,238,239,243,240,225,227,101,128,255, 4,239,
+ 236,228,243,244,249,236,101,128,247, 36,115, 2, 74, 80, 74, 87,
+ 237,225,236,108,128,254,105,245,240,229,242,233,239,114,128,246,
+ 228,238,103,128, 32,171,242,245,243,241,245,225,242,101,128, 51,
+ 38,116, 6, 74,127, 74,144, 74,166, 74,177, 74,209, 74,216,225,
+ 227,227,229,238,116,129, 2,217, 74,138,227,237, 98,128, 3, 7,
+ 226,229,236,239,247, 99, 2, 74,155, 74,160,237, 98,128, 3, 35,
+ 239,237, 98,128, 3, 35,235,225,244,225,235,225,238, 97,128, 48,
+ 251,236,229,243,115, 2, 74,186, 74,190,105,128, 1, 49,106,129,
+ 246,190, 74,196,243,244,242,239,235,229,232,239,239,107,128, 2,
+ 132,237,225,244,104,128, 34,197,244,229,228,227,233,242,227,236,
+ 101,128, 37,204,245,226,236,229,249,239,228,240,225,244,225,104,
+ 129,251, 31, 74,245,232,229,226,242,229,119,128,251, 31,247,238,
+ 244,225,227,107, 2, 75, 9, 75, 20,226,229,236,239,247,227,237,
+ 98,128, 3, 30,237,239,100,128, 2,213,240,225,242,229,110,128,
+ 36,159,243,245,240,229,242,233,239,114,128,246,235,116, 2, 75,
+ 51, 75, 57,225,233,108,128, 2, 86,239,240,226,225,114,128, 1,
+ 140,117, 2, 75, 71, 75, 82,232,233,242,225,231,225,238, 97,128,
+ 48,101,235,225,244,225,235,225,238, 97,128, 48,197,122,132, 1,
+ 243, 75,105, 75,114, 75,133, 75,170,225,236,244,239,238,101,128,
+ 2,163, 99, 2, 75,120, 75,127,225,242,239,110,128, 1,198,245,
+ 242,108,128, 2,165,101, 2, 75,139, 75,159,225,226,235,232,225,
+ 243,233,225,238,227,249,242,233,236,236,233, 99,128, 4,225,227,
+ 249,242,233,236,236,233, 99,128, 4, 85,232,229,227,249,242,233,
+ 236,236,233, 99,128, 4, 95,101,151, 0,101, 75,233, 75,252, 76,
+ 30, 77, 4, 77, 66, 77, 99, 77,111, 77,134, 77,187, 79, 43, 79,
+ 101, 79,203, 80, 63, 80,198, 81, 17, 81, 48, 81,110, 81,163, 82,
+ 98, 82,231, 82,251, 83, 39, 83,130, 97, 2, 75,239, 75,246,227,
+ 245,244,101,128, 0,233,242,244,104,128, 38, 65, 98, 3, 76, 4,
+ 76, 13, 76, 23,229,238,231,225,236,105,128, 9,143,239,240,239,
+ 237,239,230,111,128, 49, 28,242,229,246,101,128, 1, 21, 99, 5,
+ 76, 42, 76,115, 76,129, 76,161, 76,250, 97, 2, 76, 48, 76,109,
+ 238,228,242, 97, 3, 76, 59, 76, 66, 76, 77,228,229,246, 97,128,
+ 9, 13,231,245,234,225,242,225,244,105,128, 10,141,246,239,247,
+ 229,236,243,233,231,110, 2, 76, 91, 76, 98,228,229,246, 97,128,
+ 9, 69,231,245,234,225,242,225,244,105,128, 10,197,242,239,110,
+ 128, 1, 27,229,228,233,236,236,225,226,242,229,246,101,128, 30,
+ 29,104, 2, 76,135, 76,146,225,242,237,229,238,233,225,110,128,
+ 5,101,249,233,247,238,225,242,237,229,238,233,225,110,128, 5,
+ 135,233,242, 99, 2, 76,169, 76,174,236,101,128, 36,212,245,237,
+ 230,236,229,120,134, 0,234, 76,195, 76,203, 76,211, 76,222, 76,
+ 230, 76,242,225,227,245,244,101,128, 30,191,226,229,236,239,119,
+ 128, 30, 25,228,239,244,226,229,236,239,119,128, 30,199,231,242,
+ 225,246,101,128, 30,193,232,239,239,235,225,226,239,246,101,128,
+ 30,195,244,233,236,228,101,128, 30,197,249,242,233,236,236,233,
+ 99,128, 4, 84,100, 4, 77, 14, 77, 24, 77, 30, 77, 40,226,236,
+ 231,242,225,246,101,128, 2, 5,229,246, 97,128, 9, 15,233,229,
+ 242,229,243,233,115,128, 0,235,239,116,130, 1, 23, 77, 49, 77,
+ 58,225,227,227,229,238,116,128, 1, 23,226,229,236,239,119,128,
+ 30,185,101, 2, 77, 72, 77, 83,231,245,242,237,245,235,232,105,
+ 128, 10, 15,237,225,244,242,225,231,245,242,237,245,235,232,105,
+ 128, 10, 71,230,227,249,242,233,236,236,233, 99,128, 4, 68,103,
+ 2, 77,117, 77,124,242,225,246,101,128, 0,232,245,234,225,242,
+ 225,244,105,128, 10,143,104, 4, 77,144, 77,155, 77,166, 77,176,
+ 225,242,237,229,238,233,225,110,128, 5,103,226,239,240,239,237,
+ 239,230,111,128, 49, 29,233,242,225,231,225,238, 97,128, 48, 72,
+ 239,239,235,225,226,239,246,101,128, 30,187,105, 4, 77,197, 77,
+ 208, 79, 10, 79, 25,226,239,240,239,237,239,230,111,128, 49, 31,
+ 231,232,116,142, 0, 56, 77,242, 77,251, 78, 5, 78, 35, 78, 42,
+ 78, 80, 78,105, 78,150, 78,184, 78,196, 78,207, 78,240, 78,248,
+ 79, 3,225,242,225,226,233, 99,128, 6,104,226,229,238,231,225,
+ 236,105,128, 9,238,227,233,242,227,236,101,129, 36,103, 78, 16,
+ 233,238,246,229,242,243,229,243,225,238,243,243,229,242,233,102,
+ 128, 39,145,228,229,246, 97,128, 9,110,229,229,110, 2, 78, 50,
+ 78, 59,227,233,242,227,236,101,128, 36,113,112, 2, 78, 65, 78,
+ 72,225,242,229,110,128, 36,133,229,242,233,239,100,128, 36,153,
+ 231,117, 2, 78, 87, 78, 96,234,225,242,225,244,105,128, 10,238,
+ 242,237,245,235,232,105,128, 10,110,104, 2, 78,111, 78,137, 97,
+ 2, 78,117, 78,128,227,235,225,242,225,226,233, 99,128, 6,104,
+ 238,231,250,232,239,117,128, 48, 40,238,239,244,229,226,229,225,
+ 237,229,100,128, 38,107,105, 2, 78,156, 78,174,228,229,239,231,
+ 242,225,240,232,233,227,240,225,242,229,110,128, 50, 39,238,230,
+ 229,242,233,239,114,128, 32,136,237,239,238,239,243,240,225,227,
+ 101,128,255, 24,239,236,228,243,244,249,236,101,128,247, 56,112,
+ 2, 78,213, 78,220,225,242,229,110,128, 36,123,229,114, 2, 78,
+ 227, 78,233,233,239,100,128, 36,143,243,233,225,110,128, 6,248,
+ 242,239,237,225,110,128, 33,119,243,245,240,229,242,233,239,114,
+ 128, 32,120,244,232,225,105,128, 14, 88,238,246,229,242,244,229,
+ 228,226,242,229,246,101,128, 2, 7,239,244,233,230,233,229,228,
+ 227,249,242,233,236,236,233, 99,128, 4,101,107, 2, 79, 49, 79,
+ 73,225,244,225,235,225,238, 97,129, 48,168, 79, 61,232,225,236,
+ 230,247,233,228,244,104,128,255,116,111, 2, 79, 79, 79, 94,238,
+ 235,225,242,231,245,242,237,245,235,232,105,128, 10,116,242,229,
+ 225,110,128, 49, 84,108, 3, 79,109, 79,120, 79,181,227,249,242,
+ 233,236,236,233, 99,128, 4, 59,101, 2, 79,126, 79,133,237,229,
+ 238,116,128, 34, 8,246,229,110, 3, 79,143, 79,152, 79,173,227,
+ 233,242,227,236,101,128, 36,106,112, 2, 79,158, 79,165,225,242,
+ 229,110,128, 36,126,229,242,233,239,100,128, 36,146,242,239,237,
+ 225,110,128, 33,122,236,233,240,243,233,115,129, 32, 38, 79,192,
+ 246,229,242,244,233,227,225,108,128, 34,238,109, 5, 79,215, 79,
+ 243, 79,254, 80, 18, 80, 29,225,227,242,239,110,130, 1, 19, 79,
+ 227, 79,235,225,227,245,244,101,128, 30, 23,231,242,225,246,101,
+ 128, 30, 21,227,249,242,233,236,236,233, 99,128, 4, 60,228,225,
+ 243,104,129, 32, 20, 80, 7,246,229,242,244,233,227,225,108,128,
+ 254, 49,239,238,239,243,240,225,227,101,128,255, 69,112, 2, 80,
+ 35, 80, 55,232,225,243,233,243,237,225,242,235,225,242,237,229,
+ 238,233,225,110,128, 5, 91,244,249,243,229,116,128, 34, 5,110,
+ 6, 80, 77, 80, 88, 80, 99, 80,143, 80,175, 80,190,226,239,240,
+ 239,237,239,230,111,128, 49, 35,227,249,242,233,236,236,233, 99,
+ 128, 4, 61,100, 2, 80,105, 80,124,225,243,104,129, 32, 19, 80,
+ 113,246,229,242,244,233,227,225,108,128,254, 50,229,243,227,229,
+ 238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,163,103,
+ 130, 1, 75, 80,151, 80,162,226,239,240,239,237,239,230,111,128,
+ 49, 37,232,229,227,249,242,233,236,236,233, 99,128, 4,165,232,
+ 239,239,235,227,249,242,233,236,236,233, 99,128, 4,200,243,240,
+ 225,227,101,128, 32, 2,111, 3, 80,206, 80,214, 80,223,231,239,
+ 238,229,107,128, 1, 25,235,239,242,229,225,110,128, 49, 83,240,
+ 229,110,130, 2, 91, 80,233, 80,242,227,236,239,243,229,100,128,
+ 2,154,242,229,246,229,242,243,229,100,130, 2, 92, 81, 1, 81,
+ 10,227,236,239,243,229,100,128, 2, 94,232,239,239,107,128, 2,
+ 93,112, 2, 81, 23, 81, 30,225,242,229,110,128, 36,160,243,233,
+ 236,239,110,129, 3,181, 81, 40,244,239,238,239,115,128, 3,173,
+ 241,117, 2, 81, 55, 81, 99,225,108,130, 0, 61, 81, 64, 81, 76,
+ 237,239,238,239,243,240,225,227,101,128,255, 29,115, 2, 81, 82,
+ 81, 89,237,225,236,108,128,254,102,245,240,229,242,233,239,114,
+ 128, 32,124,233,246,225,236,229,238,227,101,128, 34, 97,114, 3,
+ 81,118, 81,129, 81,140,226,239,240,239,237,239,230,111,128, 49,
+ 38,227,249,242,233,236,236,233, 99,128, 4, 64,229,246,229,242,
+ 243,229,100,129, 2, 88, 81,152,227,249,242,233,236,236,233, 99,
+ 128, 4, 77,115, 6, 81,177, 81,188, 81,208, 82, 33, 82, 78, 82,
+ 88,227,249,242,233,236,236,233, 99,128, 4, 65,228,229,243,227,
+ 229,238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,171,
+ 104,132, 2,131, 81,220, 81,227, 82, 2, 82, 17,227,245,242,108,
+ 128, 2,134,239,242,116, 2, 81,235, 81,242,228,229,246, 97,128,
+ 9, 14,246,239,247,229,236,243,233,231,238,228,229,246, 97,128,
+ 9, 70,242,229,246,229,242,243,229,228,236,239,239,112,128, 1,
+ 170,243,241,245,225,244,242,229,246,229,242,243,229,100,128, 2,
+ 133,237,225,236,108, 2, 82, 42, 82, 53,232,233,242,225,231,225,
+ 238, 97,128, 48, 71,235,225,244,225,235,225,238, 97,129, 48,167,
+ 82, 66,232,225,236,230,247,233,228,244,104,128,255,106,244,233,
+ 237,225,244,229,100,128, 33, 46,245,240,229,242,233,239,114,128,
+ 246,236,116, 5, 82,110, 82,136, 82,140, 82,157, 82,223, 97,130,
+ 3,183, 82,118, 82,128,242,237,229,238,233,225,110,128, 5,104,
+ 244,239,238,239,115,128, 3,174,104,128, 0,240,233,236,228,101,
+ 129, 30,189, 82,149,226,229,236,239,119,128, 30, 27,238,225,232,
+ 244, 97, 3, 82,169, 82,201, 82,210,230,239,245,235,104, 2, 82,
+ 179, 82,188,232,229,226,242,229,119,128, 5,145,236,229,230,244,
+ 232,229,226,242,229,119,128, 5,145,232,229,226,242,229,119,128,
+ 5,145,236,229,230,244,232,229,226,242,229,119,128, 5,145,245,
+ 242,238,229,100,128, 1,221,117, 2, 82,237, 82,246,235,239,242,
+ 229,225,110,128, 49, 97,242,111,128, 32,172,246,239,247,229,236,
+ 243,233,231,110, 3, 83, 11, 83, 21, 83, 28,226,229,238,231,225,
+ 236,105,128, 9,199,228,229,246, 97,128, 9, 71,231,245,234,225,
+ 242,225,244,105,128, 10,199,120, 2, 83, 45, 83,118,227,236,225,
+ 109,132, 0, 33, 83, 60, 83, 71, 83, 98, 83,110,225,242,237,229,
+ 238,233,225,110,128, 5, 92,100, 2, 83, 77, 83, 82,226,108,128,
+ 32, 60,239,247,110,129, 0,161, 83, 90,243,237,225,236,108,128,
+ 247,161,237,239,238,239,243,240,225,227,101,128,255, 1,243,237,
+ 225,236,108,128,247, 33,233,243,244,229,238,244,233,225,108,128,
+ 34, 3,250,104,131, 2,146, 83,141, 83,160, 83,171, 99, 2, 83,
+ 147, 83,154,225,242,239,110,128, 1,239,245,242,108,128, 2,147,
+ 242,229,246,229,242,243,229,100,128, 1,185,244,225,233,108,128,
+ 1,186,102,140, 0,102, 83,206, 84, 32, 84, 43, 84, 52, 84, 64,
+ 84,167, 84,183, 86,191, 86,204, 86,230, 88,107, 88,115, 97, 4,
+ 83,216, 83,223, 83,234, 83,245,228,229,246, 97,128, 9, 94,231,
+ 245,242,237,245,235,232,105,128, 10, 94,232,242,229,238,232,229,
+ 233,116,128, 33, 9,244,232, 97, 3, 83,255, 84, 8, 84, 20,225,
+ 242,225,226,233, 99,128, 6, 78,236,239,247,225,242,225,226,233,
+ 99,128, 6, 78,244,225,238,225,242,225,226,233, 99,128, 6, 75,
+ 226,239,240,239,237,239,230,111,128, 49, 8,227,233,242,227,236,
+ 101,128, 36,213,228,239,244,225,227,227,229,238,116,128, 30, 31,
+ 101, 3, 84, 72, 84,150, 84,160,104, 4, 84, 82, 84,105, 84,119,
+ 84,135,225,114, 2, 84, 89, 84, 96,225,226,233, 99,128, 6, 65,
+ 237,229,238,233,225,110,128, 5,134,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,254,210,233,238,233,244,233,225,236,225,242,
+ 225,226,233, 99,128,254,211,237,229,228,233,225,236,225,242,225,
+ 226,233, 99,128,254,212,233,227,239,240,244,233, 99,128, 3,229,
+ 237,225,236,101,128, 38, 64,102,130,251, 0, 84,175, 84,179,105,
+ 128,251, 3,108,128,251, 4,105,136,251, 1, 84,203, 84,243, 84,
+ 254, 85, 20, 85,142, 85,159, 85,167, 85,180,230,244,229,229,110,
+ 2, 84,213, 84,222,227,233,242,227,236,101,128, 36,110,112, 2,
+ 84,228, 84,235,225,242,229,110,128, 36,130,229,242,233,239,100,
+ 128, 36,150,231,245,242,229,228,225,243,104,128, 32, 18,236,236,
+ 229,100, 2, 85, 7, 85, 13,226,239,120,128, 37,160,242,229,227,
+ 116,128, 37,172,238,225,108, 5, 85, 34, 85, 73, 85, 90, 85,107,
+ 85,123,235,225,102,130, 5,218, 85, 44, 85, 64,228,225,231,229,
+ 243,104,129,251, 58, 85, 55,232,229,226,242,229,119,128,251, 58,
+ 232,229,226,242,229,119,128, 5,218,237,229,109,129, 5,221, 85,
+ 81,232,229,226,242,229,119,128, 5,221,238,245,110,129, 5,223,
+ 85, 98,232,229,226,242,229,119,128, 5,223,240,101,129, 5,227,
+ 85,114,232,229,226,242,229,119,128, 5,227,244,243,225,228,105,
+ 129, 5,229, 85,133,232,229,226,242,229,119,128, 5,229,242,243,
+ 244,244,239,238,229,227,232,233,238,229,243,101,128, 2,201,243,
+ 232,229,249,101,128, 37,201,244,225,227,249,242,233,236,236,233,
+ 99,128, 4,115,246,101,142, 0, 53, 85,213, 85,222, 85,232, 86,
+ 6, 86, 13, 86, 23, 86, 48, 86, 75, 86,109, 86,121, 86,132, 86,
+ 165, 86,173, 86,184,225,242,225,226,233, 99,128, 6,101,226,229,
+ 238,231,225,236,105,128, 9,235,227,233,242,227,236,101,129, 36,
+ 100, 85,243,233,238,246,229,242,243,229,243,225,238,243,243,229,
+ 242,233,102,128, 39,142,228,229,246, 97,128, 9,107,229,233,231,
+ 232,244,232,115,128, 33, 93,231,117, 2, 86, 30, 86, 39,234,225,
+ 242,225,244,105,128, 10,235,242,237,245,235,232,105,128, 10,107,
+ 232, 97, 2, 86, 55, 86, 66,227,235,225,242,225,226,233, 99,128,
+ 6,101,238,231,250,232,239,117,128, 48, 37,105, 2, 86, 81, 86,
+ 99,228,229,239,231,242,225,240,232,233,227,240,225,242,229,110,
+ 128, 50, 36,238,230,229,242,233,239,114,128, 32,133,237,239,238,
+ 239,243,240,225,227,101,128,255, 21,239,236,228,243,244,249,236,
+ 101,128,247, 53,112, 2, 86,138, 86,145,225,242,229,110,128, 36,
+ 120,229,114, 2, 86,152, 86,158,233,239,100,128, 36,140,243,233,
+ 225,110,128, 6,245,242,239,237,225,110,128, 33,116,243,245,240,
+ 229,242,233,239,114,128, 32,117,244,232,225,105,128, 14, 85,108,
+ 129,251, 2, 86,197,239,242,233,110,128, 1,146,109, 2, 86,210,
+ 86,221,239,238,239,243,240,225,227,101,128,255, 70,243,241,245,
+ 225,242,101,128, 51,153,111, 4, 86,240, 87, 6, 87, 18, 87, 25,
+ 230, 97, 2, 86,247, 86,255,238,244,232,225,105,128, 14, 31,244,
+ 232,225,105,128, 14, 29,238,231,237,225,238,244,232,225,105,128,
+ 14, 79,242,225,236,108,128, 34, 0,245,114,142, 0, 52, 87, 58,
+ 87, 67, 87, 77, 87,107, 87,114, 87,139, 87,166, 87,200, 87,212,
+ 87,231, 87,242, 88, 19, 88, 27, 88, 38,225,242,225,226,233, 99,
+ 128, 6,100,226,229,238,231,225,236,105,128, 9,234,227,233,242,
+ 227,236,101,129, 36, 99, 87, 88,233,238,246,229,242,243,229,243,
+ 225,238,243,243,229,242,233,102,128, 39,141,228,229,246, 97,128,
+ 9,106,231,117, 2, 87,121, 87,130,234,225,242,225,244,105,128,
+ 10,234,242,237,245,235,232,105,128, 10,106,232, 97, 2, 87,146,
+ 87,157,227,235,225,242,225,226,233, 99,128, 6,100,238,231,250,
+ 232,239,117,128, 48, 36,105, 2, 87,172, 87,190,228,229,239,231,
+ 242,225,240,232,233,227,240,225,242,229,110,128, 50, 35,238,230,
+ 229,242,233,239,114,128, 32,132,237,239,238,239,243,240,225,227,
+ 101,128,255, 20,238,245,237,229,242,225,244,239,242,226,229,238,
+ 231,225,236,105,128, 9,247,239,236,228,243,244,249,236,101,128,
+ 247, 52,112, 2, 87,248, 87,255,225,242,229,110,128, 36,119,229,
+ 114, 2, 88, 6, 88, 12,233,239,100,128, 36,139,243,233,225,110,
+ 128, 6,244,242,239,237,225,110,128, 33,115,243,245,240,229,242,
+ 233,239,114,128, 32,116,116, 2, 88, 44, 88, 82,229,229,110, 2,
+ 88, 52, 88, 61,227,233,242,227,236,101,128, 36,109,112, 2, 88,
+ 67, 88, 74,225,242,229,110,128, 36,129,229,242,233,239,100,128,
+ 36,149,104, 2, 88, 88, 88, 93,225,105,128, 14, 84,244,239,238,
+ 229,227,232,233,238,229,243,101,128, 2,203,240,225,242,229,110,
+ 128, 36,161,242, 97, 2, 88,122, 88,130,227,244,233,239,110,128,
+ 32, 68,238, 99,128, 32,163,103,144, 0,103, 88,171, 89,117, 89,
+ 140, 89,201, 89,218, 90,139, 91,132, 91,217, 91,230, 92, 88, 92,
+ 113, 92,141, 92,163, 93,108, 93,130, 93,232, 97, 9, 88,191, 88,
+ 201, 88,208, 88,215, 89, 23, 89, 48, 89, 59, 89, 70, 89,104,226,
+ 229,238,231,225,236,105,128, 9,151,227,245,244,101,128, 1,245,
+ 228,229,246, 97,128, 9, 23,102, 4, 88,225, 88,234, 88,248, 89,
+ 8,225,242,225,226,233, 99,128, 6,175,230,233,238,225,236,225,
+ 242,225,226,233, 99,128,251,147,233,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,251,148,237,229,228,233,225,236,225,242,
+ 225,226,233, 99,128,251,149,231,117, 2, 89, 30, 89, 39,234,225,
+ 242,225,244,105,128, 10,151,242,237,245,235,232,105,128, 10, 23,
+ 232,233,242,225,231,225,238, 97,128, 48, 76,235,225,244,225,235,
+ 225,238, 97,128, 48,172,237,237, 97,130, 3,179, 89, 80, 89, 93,
+ 236,225,244,233,238,243,237,225,236,108,128, 2, 99,243,245,240,
+ 229,242,233,239,114,128, 2,224,238,231,233,225,227,239,240,244,
+ 233, 99,128, 3,235, 98, 2, 89,123, 89,133,239,240,239,237,239,
+ 230,111,128, 49, 13,242,229,246,101,128, 1, 31, 99, 4, 89,150,
+ 89,157, 89,166, 89,188,225,242,239,110,128, 1,231,229,228,233,
+ 236,236, 97,128, 1, 35,233,242, 99, 2, 89,174, 89,179,236,101,
+ 128, 36,214,245,237,230,236,229,120,128, 1, 29,239,237,237,225,
+ 225,227,227,229,238,116,128, 1, 35,228,239,116,129, 1, 33, 89,
+ 209,225,227,227,229,238,116,128, 1, 33,101, 6, 89,232, 89,243,
+ 89,254, 90, 9, 90, 28, 90,130,227,249,242,233,236,236,233, 99,
+ 128, 4, 51,232,233,242,225,231,225,238, 97,128, 48, 82,235,225,
+ 244,225,235,225,238, 97,128, 48,178,239,237,229,244,242,233,227,
+ 225,236,236,249,229,241,245,225,108,128, 34, 81,114, 3, 90, 36,
+ 90, 85, 90, 95,229,243,104, 3, 90, 46, 90, 61, 90, 70,225,227,
+ 227,229,238,244,232,229,226,242,229,119,128, 5,156,232,229,226,
+ 242,229,119,128, 5,243,237,245,241,228,225,237,232,229,226,242,
+ 229,119,128, 5,157,237,225,238,228,226,236,115,128, 0,223,243,
+ 232,225,249,233,109, 2, 90,106, 90,121,225,227,227,229,238,244,
+ 232,229,226,242,229,119,128, 5,158,232,229,226,242,229,119,128,
+ 5,244,244,225,237,225,242,107,128, 48, 19,104, 5, 90,151, 91,
+ 28, 91, 91, 91,116, 91,122, 97, 4, 90,161, 90,171, 90,194, 90,
+ 219,226,229,238,231,225,236,105,128, 9,152,100, 2, 90,177, 90,
+ 188,225,242,237,229,238,233,225,110,128, 5,114,229,246, 97,128,
+ 9, 24,231,117, 2, 90,201, 90,210,234,225,242,225,244,105,128,
+ 10,152,242,237,245,235,232,105,128, 10, 24,233,110, 4, 90,230,
+ 90,239, 90,253, 91, 13,225,242,225,226,233, 99,128, 6, 58,230,
+ 233,238,225,236,225,242,225,226,233, 99,128,254,206,233,238,233,
+ 244,233,225,236,225,242,225,226,233, 99,128,254,207,237,229,228,
+ 233,225,236,225,242,225,226,233, 99,128,254,208,101, 3, 91, 36,
+ 91, 57, 91, 74,237,233,228,228,236,229,232,239,239,235,227,249,
+ 242,233,236,236,233, 99,128, 4,149,243,244,242,239,235,229,227,
+ 249,242,233,236,236,233, 99,128, 4,147,245,240,244,245,242,238,
+ 227,249,242,233,236,236,233, 99,128, 4,145,232, 97, 2, 91, 98,
+ 91,105,228,229,246, 97,128, 9, 90,231,245,242,237,245,235,232,
+ 105,128, 10, 90,239,239,107,128, 2, 96,250,243,241,245,225,242,
+ 101,128, 51,147,105, 3, 91,140, 91,151, 91,162,232,233,242,225,
+ 231,225,238, 97,128, 48, 78,235,225,244,225,235,225,238, 97,128,
+ 48,174,109, 2, 91,168, 91,179,225,242,237,229,238,233,225,110,
+ 128, 5, 99,229,108,130, 5,210, 91,188, 91,208,228,225,231,229,
+ 243,104,129,251, 50, 91,199,232,229,226,242,229,119,128,251, 50,
+ 232,229,226,242,229,119,128, 5,210,234,229,227,249,242,233,236,
+ 236,233, 99,128, 4, 83,236,239,244,244,225,108, 2, 91,241, 92,
+ 2,233,238,246,229,242,244,229,228,243,244,242,239,235,101,128,
+ 1,190,243,244,239,112,132, 2,148, 92, 17, 92, 28, 92, 34, 92,
+ 66,233,238,246,229,242,244,229,100,128, 2,150,237,239,100,128,
+ 2,192,242,229,246,229,242,243,229,100,130, 2,149, 92, 49, 92,
+ 55,237,239,100,128, 2,193,243,245,240,229,242,233,239,114,128,
+ 2,228,243,244,242,239,235,101,129, 2,161, 92, 77,242,229,246,
+ 229,242,243,229,100,128, 2,162,109, 2, 92, 94, 92,102,225,227,
+ 242,239,110,128, 30, 33,239,238,239,243,240,225,227,101,128,255,
+ 71,111, 2, 92,119, 92,130,232,233,242,225,231,225,238, 97,128,
+ 48, 84,235,225,244,225,235,225,238, 97,128, 48,180,240, 97, 2,
+ 92,148, 92,154,242,229,110,128, 36,162,243,241,245,225,242,101,
+ 128, 51,172,114, 2, 92,169, 93, 10, 97, 2, 92,175, 92,183,228,
+ 233,229,238,116,128, 34, 7,246,101,134, 0, 96, 92,200, 92,211,
+ 92,228, 92,235, 92,244, 93, 0,226,229,236,239,247,227,237, 98,
+ 128, 3, 22, 99, 2, 92,217, 92,222,237, 98,128, 3, 0,239,237,
+ 98,128, 3, 0,228,229,246, 97,128, 9, 83,236,239,247,237,239,
+ 100,128, 2,206,237,239,238,239,243,240,225,227,101,128,255, 64,
+ 244,239,238,229,227,237, 98,128, 3, 64,229,225,244,229,114,132,
+ 0, 62, 93, 26, 93, 45, 93, 57, 93,100,229,241,245,225,108,129,
+ 34,101, 93, 36,239,242,236,229,243,115,128, 34,219,237,239,238,
+ 239,243,240,225,227,101,128,255, 30,111, 2, 93, 63, 93, 89,114,
+ 2, 93, 69, 93, 82,229,241,245,233,246,225,236,229,238,116,128,
+ 34,115,236,229,243,115,128, 34,119,246,229,242,229,241,245,225,
+ 108,128, 34,103,243,237,225,236,108,128,254,101,115, 2, 93,114,
+ 93,122,227,242,233,240,116,128, 2, 97,244,242,239,235,101,128,
+ 1,229,117, 4, 93,140, 93,151, 93,208, 93,219,232,233,242,225,
+ 231,225,238, 97,128, 48, 80,233,108, 2, 93,158, 93,183,236,229,
+ 237,239,116, 2, 93,168, 93,175,236,229,230,116,128, 0,171,242,
+ 233,231,232,116,128, 0,187,243,233,238,231,108, 2, 93,193, 93,
+ 200,236,229,230,116,128, 32, 57,242,233,231,232,116,128, 32, 58,
+ 235,225,244,225,235,225,238, 97,128, 48,176,242,225,237,245,243,
+ 241,245,225,242,101,128, 51, 24,249,243,241,245,225,242,101,128,
+ 51,201,104,144, 0,104, 94, 22, 96,164, 96,199, 96,236, 97, 20,
+ 98,164, 98,184, 99,149, 99,161, 99,173,100,241,100,249,101, 4,
+ 101, 13,101, 93,101, 97, 97, 13, 94, 50, 94, 89, 94, 99, 94,129,
+ 94,154, 94,232, 94,244, 95, 13, 95, 28, 95, 57, 95, 70, 95,128,
+ 95,137, 97, 2, 94, 56, 94, 75,226,235,232,225,243,233,225,238,
+ 227,249,242,233,236,236,233, 99,128, 4,169,236,244,239,238,229,
+ 225,242,225,226,233, 99,128, 6,193,226,229,238,231,225,236,105,
+ 128, 9,185,228,101, 2, 94,106, 94,124,243,227,229,238,228,229,
+ 242,227,249,242,233,236,236,233, 99,128, 4,179,246, 97,128, 9,
+ 57,231,117, 2, 94,136, 94,145,234,225,242,225,244,105,128, 10,
+ 185,242,237,245,235,232,105,128, 10, 57,104, 4, 94,164, 94,173,
+ 94,187, 94,217,225,242,225,226,233, 99,128, 6, 45,230,233,238,
+ 225,236,225,242,225,226,233, 99,128,254,162,105, 2, 94,193, 94,
+ 208,238,233,244,233,225,236,225,242,225,226,233, 99,128,254,163,
+ 242,225,231,225,238, 97,128, 48,111,237,229,228,233,225,236,225,
+ 242,225,226,233, 99,128,254,164,233,244,245,243,241,245,225,242,
+ 101,128, 51, 42,235,225,244,225,235,225,238, 97,129, 48,207, 95,
+ 1,232,225,236,230,247,233,228,244,104,128,255,138,236,225,238,
+ 244,231,245,242,237,245,235,232,105,128, 10, 77,237,250, 97, 2,
+ 95, 36, 95, 45,225,242,225,226,233, 99,128, 6, 33,236,239,247,
+ 225,242,225,226,233, 99,128, 6, 33,238,231,245,236,230,233,236,
+ 236,229,114,128, 49,100,114, 2, 95, 76, 95, 92,228,243,233,231,
+ 238,227,249,242,233,236,236,233, 99,128, 4, 74,240,239,239,110,
+ 2, 95,101, 95,114,236,229,230,244,226,225,242,226,245,112,128,
+ 33,188,242,233,231,232,244,226,225,242,226,245,112,128, 33,192,
+ 243,241,245,225,242,101,128, 51,202,244,225,102, 3, 95,147, 95,
+ 239, 96, 74,240,225,244,225,104,134, 5,178, 95,167, 95,172, 95,
+ 186, 95,195, 95,210, 95,226,177, 54,128, 5,178, 50, 2, 95,178,
+ 95,182, 51,128, 5,178,102,128, 5,178,232,229,226,242,229,119,
+ 128, 5,178,238,225,242,242,239,247,232,229,226,242,229,119,128,
+ 5,178,241,245,225,242,244,229,242,232,229,226,242,229,119,128,
+ 5,178,247,233,228,229,232,229,226,242,229,119,128, 5,178,241,
+ 225,237,225,244,115,135, 5,179, 96, 6, 96, 11, 96, 16, 96, 21,
+ 96, 30, 96, 45, 96, 61,177, 98,128, 5,179,178, 56,128, 5,179,
+ 179, 52,128, 5,179,232,229,226,242,229,119,128, 5,179,238,225,
+ 242,242,239,247,232,229,226,242,229,119,128, 5,179,241,245,225,
+ 242,244,229,242,232,229,226,242,229,119,128, 5,179,247,233,228,
+ 229,232,229,226,242,229,119,128, 5,179,243,229,231,239,108,135,
+ 5,177, 96, 96, 96,101, 96,106, 96,111, 96,120, 96,135, 96,151,
+ 177, 55,128, 5,177,178, 52,128, 5,177,179, 48,128, 5,177,232,
+ 229,226,242,229,119,128, 5,177,238,225,242,242,239,247,232,229,
+ 226,242,229,119,128, 5,177,241,245,225,242,244,229,242,232,229,
+ 226,242,229,119,128, 5,177,247,233,228,229,232,229,226,242,229,
+ 119,128, 5,177, 98, 3, 96,172, 96,177, 96,187,225,114,128, 1,
+ 39,239,240,239,237,239,230,111,128, 49, 15,242,229,246,229,226,
+ 229,236,239,119,128, 30, 43, 99, 2, 96,205, 96,214,229,228,233,
+ 236,236, 97,128, 30, 41,233,242, 99, 2, 96,222, 96,227,236,101,
+ 128, 36,215,245,237,230,236,229,120,128, 1, 37,100, 2, 96,242,
+ 96,252,233,229,242,229,243,233,115,128, 30, 39,239,116, 2, 97,
+ 3, 97, 12,225,227,227,229,238,116,128, 30, 35,226,229,236,239,
+ 119,128, 30, 37,101,136, 5,212, 97, 40, 97, 73, 97, 93, 98, 66,
+ 98, 82, 98,127, 98,136, 98,149,225,242,116,129, 38,101, 97, 48,
+ 243,245,233,116, 2, 97, 57, 97, 65,226,236,225,227,107,128, 38,
+ 101,247,232,233,244,101,128, 38, 97,228,225,231,229,243,104,129,
+ 251, 52, 97, 84,232,229,226,242,229,119,128,251, 52,104, 6, 97,
+ 107, 97,135, 97,143, 97,193, 97,239, 98, 32, 97, 2, 97,113, 97,
+ 127,236,244,239,238,229,225,242,225,226,233, 99,128, 6,193,242,
+ 225,226,233, 99,128, 6, 71,229,226,242,229,119,128, 5,212,230,
+ 233,238,225,236, 97, 2, 97,154, 97,185,236,116, 2, 97,161, 97,
+ 173,239,238,229,225,242,225,226,233, 99,128,251,167,244,247,239,
+ 225,242,225,226,233, 99,128,254,234,242,225,226,233, 99,128,254,
+ 234,232,225,237,250,225,225,226,239,246,101, 2, 97,208, 97,222,
+ 230,233,238,225,236,225,242,225,226,233, 99,128,251,165,233,243,
+ 239,236,225,244,229,228,225,242,225,226,233, 99,128,251,164,105,
+ 2, 97,245, 98, 23,238,233,244,233,225,236, 97, 2, 98, 1, 98,
+ 15,236,244,239,238,229,225,242,225,226,233, 99,128,251,168,242,
+ 225,226,233, 99,128,254,235,242,225,231,225,238, 97,128, 48,120,
+ 237,229,228,233,225,236, 97, 2, 98, 44, 98, 58,236,244,239,238,
+ 229,225,242,225,226,233, 99,128,251,169,242,225,226,233, 99,128,
+ 254,236,233,243,229,233,229,242,225,243,241,245,225,242,101,128,
+ 51,123,107, 2, 98, 88, 98,112,225,244,225,235,225,238, 97,129,
+ 48,216, 98,100,232,225,236,230,247,233,228,244,104,128,255,141,
+ 245,244,225,225,242,245,243,241,245,225,242,101,128, 51, 54,238,
+ 231,232,239,239,107,128, 2,103,242,245,244,245,243,241,245,225,
+ 242,101,128, 51, 57,116,129, 5,215, 98,155,232,229,226,242,229,
+ 119,128, 5,215,232,239,239,107,129, 2,102, 98,173,243,245,240,
+ 229,242,233,239,114,128, 2,177,105, 4, 98,194, 99, 23, 99, 34,
+ 99, 59,229,245,104, 4, 98,206, 98,241, 99, 0, 99, 9, 97, 2,
+ 98,212, 98,227,227,233,242,227,236,229,235,239,242,229,225,110,
+ 128, 50,123,240,225,242,229,238,235,239,242,229,225,110,128, 50,
+ 27,227,233,242,227,236,229,235,239,242,229,225,110,128, 50,109,
+ 235,239,242,229,225,110,128, 49, 78,240,225,242,229,238,235,239,
+ 242,229,225,110,128, 50, 13,232,233,242,225,231,225,238, 97,128,
+ 48,114,235,225,244,225,235,225,238, 97,129, 48,210, 99, 47,232,
+ 225,236,230,247,233,228,244,104,128,255,139,242,233,113,134, 5,
+ 180, 99, 77, 99, 82, 99, 96, 99,105, 99,120, 99,136,177, 52,128,
+ 5,180, 50, 2, 99, 88, 99, 92, 49,128, 5,180,100,128, 5,180,
+ 232,229,226,242,229,119,128, 5,180,238,225,242,242,239,247,232,
+ 229,226,242,229,119,128, 5,180,241,245,225,242,244,229,242,232,
+ 229,226,242,229,119,128, 5,180,247,233,228,229,232,229,226,242,
+ 229,119,128, 5,180,236,233,238,229,226,229,236,239,119,128, 30,
+ 150,237,239,238,239,243,240,225,227,101,128,255, 72,111, 9, 99,
+ 193, 99,204, 99,228, 99,253,100, 85,100, 98,100,184,100,224,100,
+ 235,225,242,237,229,238,233,225,110,128, 5,112,232,105, 2, 99,
+ 211, 99,219,240,244,232,225,105,128, 14, 43,242,225,231,225,238,
+ 97,128, 48,123,235,225,244,225,235,225,238, 97,129, 48,219, 99,
+ 241,232,225,236,230,247,233,228,244,104,128,255,142,236,225,109,
+ 135, 5,185,100, 17,100, 22,100, 27,100, 32,100, 41,100, 56,100,
+ 72,177, 57,128, 5,185,178, 54,128, 5,185,179, 50,128, 5,185,
+ 232,229,226,242,229,119,128, 5,185,238,225,242,242,239,247,232,
+ 229,226,242,229,119,128, 5,185,241,245,225,242,244,229,242,232,
+ 229,226,242,229,119,128, 5,185,247,233,228,229,232,229,226,242,
+ 229,119,128, 5,185,238,239,235,232,245,235,244,232,225,105,128,
+ 14, 46,111, 2,100,104,100,174,107, 4,100,114,100,126,100,132,
+ 100,154,225,226,239,246,229,227,239,237, 98,128, 3, 9,227,237,
+ 98,128, 3, 9,240,225,236,225,244,225,236,233,250,229,228,226,
+ 229,236,239,247,227,237, 98,128, 3, 33,242,229,244,242,239,230,
+ 236,229,248,226,229,236,239,247,227,237, 98,128, 3, 34,238,243,
+ 241,245,225,242,101,128, 51, 66,114, 2,100,190,100,217,105, 2,
+ 100,196,100,205,227,239,240,244,233, 99,128, 3,233,250,239,238,
+ 244,225,236,226,225,114,128, 32, 21,238,227,237, 98,128, 3, 27,
+ 244,243,240,242,233,238,231,115,128, 38,104,245,243,101,128, 35,
+ 2,240,225,242,229,110,128, 36,163,243,245,240,229,242,233,239,
+ 114,128, 2,176,244,245,242,238,229,100,128, 2,101,117, 4,101,
+ 23,101, 34,101, 47,101, 72,232,233,242,225,231,225,238, 97,128,
+ 48,117,233,233,244,239,243,241,245,225,242,101,128, 51, 51,235,
+ 225,244,225,235,225,238, 97,129, 48,213,101, 60,232,225,236,230,
+ 247,233,228,244,104,128,255,140,238,231,225,242,245,237,236,225,
+ 245,116,129, 2,221,101, 87,227,237, 98,128, 3, 11,118,128, 1,
+ 149,249,240,232,229,110,132, 0, 45,101,113,101,124,101,136,101,
+ 159,233,238,230,229,242,233,239,114,128,246,229,237,239,238,239,
+ 243,240,225,227,101,128,255, 13,115, 2,101,142,101,149,237,225,
+ 236,108,128,254, 99,245,240,229,242,233,239,114,128,246,230,244,
+ 247,111,128, 32, 16,105,149, 0,105,101,211,101,234,102, 12,102,
+ 59,105,197,106, 61,106, 98,106,125,107, 31,107, 35,107, 73,107,
+ 95,107,179,108, 88,108,163,108,171,108,184,109, 15,109, 72,109,
+ 100,109,144,225, 99, 2,101,218,101,224,245,244,101,128, 0,237,
+ 249,242,233,236,236,233, 99,128, 4, 79, 98, 3,101,242,101,251,
+ 102, 5,229,238,231,225,236,105,128, 9,135,239,240,239,237,239,
+ 230,111,128, 49, 39,242,229,246,101,128, 1, 45, 99, 3,102, 20,
+ 102, 27,102, 49,225,242,239,110,128, 1,208,233,242, 99, 2,102,
+ 35,102, 40,236,101,128, 36,216,245,237,230,236,229,120,128, 0,
+ 238,249,242,233,236,236,233, 99,128, 4, 86,100, 4,102, 69,102,
+ 79,105,154,105,187,226,236,231,242,225,246,101,128, 2, 9,101,
+ 2,102, 85,105,149,239,231,242,225,240,104, 7,102,106,102,120,
+ 102,133,105, 62,105, 93,105,106,105,118,229,225,242,244,232,227,
+ 233,242,227,236,101,128, 50,143,230,233,242,229,227,233,242,227,
+ 236,101,128, 50,139,233, 99, 14,102,164,102,180,103, 23,103, 77,
+ 103,143,103,172,103,188,103,245,104, 38,104, 50,104, 77,104,144,
+ 105, 26,105, 55,225,236,236,233,225,238,227,229,240,225,242,229,
+ 110,128, 50, 63, 99, 4,102,190,102,201,102,215,102,222,225,236,
+ 236,240,225,242,229,110,128, 50, 58,229,238,244,242,229,227,233,
+ 242,227,236,101,128, 50,165,236,239,243,101,128, 48, 6,111, 3,
+ 102,230,102,245,103, 9,237,237, 97,129, 48, 1,102,238,236,229,
+ 230,116,128,255,100,238,231,242,225,244,245,236,225,244,233,239,
+ 238,240,225,242,229,110,128, 50, 55,242,242,229,227,244,227,233,
+ 242,227,236,101,128, 50,163,101, 3,103, 31,103, 43,103, 60,225,
+ 242,244,232,240,225,242,229,110,128, 50, 47,238,244,229,242,240,
+ 242,233,243,229,240,225,242,229,110,128, 50, 61,248,227,229,236,
+ 236,229,238,244,227,233,242,227,236,101,128, 50,157,102, 2,103,
+ 83,103, 98,229,243,244,233,246,225,236,240,225,242,229,110,128,
+ 50, 64,105, 2,103,104,103,133,238,225,238,227,233,225,108, 2,
+ 103,116,103,125,227,233,242,227,236,101,128, 50,150,240,225,242,
+ 229,110,128, 50, 54,242,229,240,225,242,229,110,128, 50, 43,104,
+ 2,103,149,103,160,225,246,229,240,225,242,229,110,128, 50, 50,
+ 233,231,232,227,233,242,227,236,101,128, 50,164,233,244,229,242,
+ 225,244,233,239,238,237,225,242,107,128, 48, 5,108, 3,103,196,
+ 103,222,103,234,225,226,239,114, 2,103,205,103,214,227,233,242,
+ 227,236,101,128, 50,152,240,225,242,229,110,128, 50, 56,229,230,
+ 244,227,233,242,227,236,101,128, 50,167,239,247,227,233,242,227,
+ 236,101,128, 50,166,109, 2,103,251,104, 27,101, 2,104, 1,104,
+ 16,228,233,227,233,238,229,227,233,242,227,236,101,128, 50,169,
+ 244,225,236,240,225,242,229,110,128, 50, 46,239,239,238,240,225,
+ 242,229,110,128, 50, 42,238,225,237,229,240,225,242,229,110,128,
+ 50, 52,112, 2,104, 56,104, 64,229,242,233,239,100,128, 48, 2,
+ 242,233,238,244,227,233,242,227,236,101,128, 50,158,114, 2,104,
+ 83,104,131,101, 3,104, 91,104,102,104,117,225,227,232,240,225,
+ 242,229,110,128, 50, 67,240,242,229,243,229,238,244,240,225,242,
+ 229,110,128, 50, 57,243,239,245,242,227,229,240,225,242,229,110,
+ 128, 50, 62,233,231,232,244,227,233,242,227,236,101,128, 50,168,
+ 115, 5,104,156,104,185,104,199,104,224,104,252,101, 2,104,162,
+ 104,175,227,242,229,244,227,233,242,227,236,101,128, 50,153,236,
+ 230,240,225,242,229,110,128, 50, 66,239,227,233,229,244,249,240,
+ 225,242,229,110,128, 50, 51,112, 2,104,205,104,211,225,227,101,
+ 128, 48, 0,229,227,233,225,236,240,225,242,229,110,128, 50, 53,
+ 116, 2,104,230,104,241,239,227,235,240,225,242,229,110,128, 50,
+ 49,245,228,249,240,225,242,229,110,128, 50, 59,117, 2,105, 2,
+ 105, 11,238,240,225,242,229,110,128, 50, 48,240,229,242,246,233,
+ 243,229,240,225,242,229,110,128, 50, 60,119, 2,105, 32,105, 44,
+ 225,244,229,242,240,225,242,229,110,128, 50, 44,239,239,228,240,
+ 225,242,229,110,128, 50, 45,250,229,242,111,128, 48, 7,109, 2,
+ 105, 68,105, 81,229,244,225,236,227,233,242,227,236,101,128, 50,
+ 142,239,239,238,227,233,242,227,236,101,128, 50,138,238,225,237,
+ 229,227,233,242,227,236,101,128, 50,148,243,245,238,227,233,242,
+ 227,236,101,128, 50,144,119, 2,105,124,105,137,225,244,229,242,
+ 227,233,242,227,236,101,128, 50,140,239,239,228,227,233,242,227,
+ 236,101,128, 50,141,246, 97,128, 9, 7,233,229,242,229,243,233,
+ 115,130, 0,239,105,168,105,176,225,227,245,244,101,128, 30, 47,
+ 227,249,242,233,236,236,233, 99,128, 4,229,239,244,226,229,236,
+ 239,119,128, 30,203,101, 3,105,205,105,221,105,232,226,242,229,
+ 246,229,227,249,242,233,236,236,233, 99,128, 4,215,227,249,242,
+ 233,236,236,233, 99,128, 4, 53,245,238,103, 4,105,244,106, 23,
+ 106, 38,106, 47, 97, 2,105,250,106, 9,227,233,242,227,236,229,
+ 235,239,242,229,225,110,128, 50,117,240,225,242,229,238,235,239,
+ 242,229,225,110,128, 50, 21,227,233,242,227,236,229,235,239,242,
+ 229,225,110,128, 50,103,235,239,242,229,225,110,128, 49, 71,240,
+ 225,242,229,238,235,239,242,229,225,110,128, 50, 7,103, 2,106,
+ 67,106, 74,242,225,246,101,128, 0,236,117, 2,106, 80,106, 89,
+ 234,225,242,225,244,105,128, 10,135,242,237,245,235,232,105,128,
+ 10, 7,104, 2,106,104,106,114,233,242,225,231,225,238, 97,128,
+ 48, 68,239,239,235,225,226,239,246,101,128, 30,201,105, 8,106,
+ 143,106,153,106,164,106,171,106,196,106,212,106,227,106,243,226,
+ 229,238,231,225,236,105,128, 9,136,227,249,242,233,236,236,233,
+ 99,128, 4, 56,228,229,246, 97,128, 9, 8,231,117, 2,106,178,
+ 106,187,234,225,242,225,244,105,128, 10,136,242,237,245,235,232,
+ 105,128, 10, 8,237,225,244,242,225,231,245,242,237,245,235,232,
+ 105,128, 10, 64,238,246,229,242,244,229,228,226,242,229,246,101,
+ 128, 2, 11,243,232,239,242,244,227,249,242,233,236,236,233, 99,
+ 128, 4, 57,246,239,247,229,236,243,233,231,110, 3,107, 3,107,
+ 13,107, 20,226,229,238,231,225,236,105,128, 9,192,228,229,246,
+ 97,128, 9, 64,231,245,234,225,242,225,244,105,128, 10,192,106,
+ 128, 1, 51,107, 2,107, 41,107, 65,225,244,225,235,225,238, 97,
+ 129, 48,164,107, 53,232,225,236,230,247,233,228,244,104,128,255,
+ 114,239,242,229,225,110,128, 49, 99,108, 2,107, 79,107, 84,228,
+ 101,128, 2,220,245,249,232,229,226,242,229,119,128, 5,172,109,
+ 2,107,101,107,168, 97, 3,107,109,107,129,107,154,227,242,239,
+ 110,129, 1, 43,107,118,227,249,242,233,236,236,233, 99,128, 4,
+ 227,231,229,239,242,225,240,240,242,239,248,233,237,225,244,229,
+ 236,249,229,241,245,225,108,128, 34, 83,244,242,225,231,245,242,
+ 237,245,235,232,105,128, 10, 63,239,238,239,243,240,225,227,101,
+ 128,255, 73,110, 5,107,191,107,201,107,210,107,222,108, 50,227,
+ 242,229,237,229,238,116,128, 34, 6,230,233,238,233,244,121,128,
+ 34, 30,233,225,242,237,229,238,233,225,110,128, 5,107,116, 2,
+ 107,228,108, 40,101, 2,107,234,108, 29,231,242,225,108,131, 34,
+ 43,107,247,108, 9,108, 14, 98, 2,107,253,108, 5,239,244,244,
+ 239,109,128, 35, 33,116,128, 35, 33,229,120,128,248,245,116, 2,
+ 108, 20,108, 25,239,112,128, 35, 32,112,128, 35, 32,242,243,229,
+ 227,244,233,239,110,128, 34, 41,233,243,241,245,225,242,101,128,
+ 51, 5,118, 3,108, 58,108, 67,108, 76,226,245,236,236,229,116,
+ 128, 37,216,227,233,242,227,236,101,128, 37,217,243,237,233,236,
+ 229,230,225,227,101,128, 38, 59,111, 3,108, 96,108,107,108,115,
+ 227,249,242,233,236,236,233, 99,128, 4, 81,231,239,238,229,107,
+ 128, 1, 47,244, 97,131, 3,185,108,126,108,147,108,155,228,233,
+ 229,242,229,243,233,115,129, 3,202,108,139,244,239,238,239,115,
+ 128, 3,144,236,225,244,233,110,128, 2,105,244,239,238,239,115,
+ 128, 3,175,240,225,242,229,110,128, 36,164,242,233,231,245,242,
+ 237,245,235,232,105,128, 10,114,115, 4,108,194,108,239,108,253,
+ 109, 5,237,225,236,108, 2,108,203,108,214,232,233,242,225,231,
+ 225,238, 97,128, 48, 67,235,225,244,225,235,225,238, 97,129, 48,
+ 163,108,227,232,225,236,230,247,233,228,244,104,128,255,104,243,
+ 232,225,242,226,229,238,231,225,236,105,128, 9,250,244,242,239,
+ 235,101,128, 2,104,245,240,229,242,233,239,114,128,246,237,116,
+ 2,109, 21,109, 55,229,242,225,244,233,239,110, 2,109, 33,109,
+ 44,232,233,242,225,231,225,238, 97,128, 48,157,235,225,244,225,
+ 235,225,238, 97,128, 48,253,233,236,228,101,129, 1, 41,109, 64,
+ 226,229,236,239,119,128, 30, 45,117, 2,109, 78,109, 89,226,239,
+ 240,239,237,239,230,111,128, 49, 41,227,249,242,233,236,236,233,
+ 99,128, 4, 78,246,239,247,229,236,243,233,231,110, 3,109,116,
+ 109,126,109,133,226,229,238,231,225,236,105,128, 9,191,228,229,
+ 246, 97,128, 9, 63,231,245,234,225,242,225,244,105,128, 10,191,
+ 250,232,233,244,243, 97, 2,109,155,109,166,227,249,242,233,236,
+ 236,233, 99,128, 4,117,228,226,236,231,242,225,246,229,227,249,
+ 242,233,236,236,233, 99,128, 4,119,106,138, 0,106,109,209,110,
+ 16,110, 27,110, 77,110, 93,110,206,111, 19,111, 24,111, 36,111,
+ 44, 97, 4,109,219,109,230,109,240,109,247,225,242,237,229,238,
+ 233,225,110,128, 5,113,226,229,238,231,225,236,105,128, 9,156,
+ 228,229,246, 97,128, 9, 28,231,117, 2,109,254,110, 7,234,225,
+ 242,225,244,105,128, 10,156,242,237,245,235,232,105,128, 10, 28,
+ 226,239,240,239,237,239,230,111,128, 49, 16, 99, 3,110, 35,110,
+ 42,110, 64,225,242,239,110,128, 1,240,233,242, 99, 2,110, 50,
+ 110, 55,236,101,128, 36,217,245,237,230,236,229,120,128, 1, 53,
+ 242,239,243,243,229,228,244,225,233,108,128, 2,157,228,239,244,
+ 236,229,243,243,243,244,242,239,235,101,128, 2, 95,101, 3,110,
+ 101,110,112,110,177,227,249,242,233,236,236,233, 99,128, 4, 88,
+ 229,109, 4,110,123,110,132,110,146,110,162,225,242,225,226,233,
+ 99,128, 6, 44,230,233,238,225,236,225,242,225,226,233, 99,128,
+ 254,158,233,238,233,244,233,225,236,225,242,225,226,233, 99,128,
+ 254,159,237,229,228,233,225,236,225,242,225,226,233, 99,128,254,
+ 160,104, 2,110,183,110,192,225,242,225,226,233, 99,128, 6,152,
+ 230,233,238,225,236,225,242,225,226,233, 99,128,251,139,104, 2,
+ 110,212,111, 6, 97, 3,110,220,110,230,110,237,226,229,238,231,
+ 225,236,105,128, 9,157,228,229,246, 97,128, 9, 29,231,117, 2,
+ 110,244,110,253,234,225,242,225,244,105,128, 10,157,242,237,245,
+ 235,232,105,128, 10, 29,229,232,225,242,237,229,238,233,225,110,
+ 128, 5,123,233,115,128, 48, 4,237,239,238,239,243,240,225,227,
+ 101,128,255, 74,240,225,242,229,110,128, 36,165,243,245,240,229,
+ 242,233,239,114,128, 2,178,107,146, 0,107,111, 95,113,184,113,
+ 195,114, 1,114, 12,114,102,114,116,115,224,116,164,116,177,116,
+ 203,116,252,117,134,117,156,117,169,117,192,117,234,117,244, 97,
+ 12,111,121,111,153,111,175,111,205,112, 63,112, 88,112,118,112,
+ 143,112,249,113, 7,113,130,113,159, 98, 2,111,127,111,144,225,
+ 243,232,235,233,242,227,249,242,233,236,236,233, 99,128, 4,161,
+ 229,238,231,225,236,105,128, 9,149, 99, 2,111,159,111,165,245,
+ 244,101,128, 30, 49,249,242,233,236,236,233, 99,128, 4, 58,228,
+ 101, 2,111,182,111,200,243,227,229,238,228,229,242,227,249,242,
+ 233,236,236,233, 99,128, 4,155,246, 97,128, 9, 21,102,135, 5,
+ 219,111,223,111,232,111,252,112, 10,112, 19,112, 35,112, 50,225,
+ 242,225,226,233, 99,128, 6, 67,228,225,231,229,243,104,129,251,
+ 59,111,243,232,229,226,242,229,119,128,251, 59,230,233,238,225,
+ 236,225,242,225,226,233, 99,128,254,218,232,229,226,242,229,119,
+ 128, 5,219,233,238,233,244,233,225,236,225,242,225,226,233, 99,
+ 128,254,219,237,229,228,233,225,236,225,242,225,226,233, 99,128,
+ 254,220,242,225,230,229,232,229,226,242,229,119,128,251, 77,231,
+ 117, 2,112, 70,112, 79,234,225,242,225,244,105,128, 10,149,242,
+ 237,245,235,232,105,128, 10, 21,104, 2,112, 94,112,104,233,242,
+ 225,231,225,238, 97,128, 48, 75,239,239,235,227,249,242,233,236,
+ 236,233, 99,128, 4,196,235,225,244,225,235,225,238, 97,129, 48,
+ 171,112,131,232,225,236,230,247,233,228,244,104,128,255,118,112,
+ 2,112,149,112,170,240, 97,129, 3,186,112,156,243,249,237,226,
+ 239,236,231,242,229,229,107,128, 3,240,249,229,239,245,110, 3,
+ 112,182,112,196,112,230,237,233,229,245,237,235,239,242,229,225,
+ 110,128, 49,113,112, 2,112,202,112,217,232,233,229,245,240,232,
+ 235,239,242,229,225,110,128, 49,132,233,229,245,240,235,239,242,
+ 229,225,110,128, 49,120,243,243,225,238,231,240,233,229,245,240,
+ 235,239,242,229,225,110,128, 49,121,242,239,242,233,233,243,241,
+ 245,225,242,101,128, 51, 13,115, 5,113, 19,113, 63,113, 78,113,
+ 86,113,114,232,233,228,225,225,245,244,111, 2,113, 32,113, 41,
+ 225,242,225,226,233, 99,128, 6, 64,238,239,243,233,228,229,226,
+ 229,225,242,233,238,231,225,242,225,226,233, 99,128, 6, 64,237,
+ 225,236,236,235,225,244,225,235,225,238, 97,128, 48,245,241,245,
+ 225,242,101,128, 51,132,242, 97, 2,113, 93,113,102,225,242,225,
+ 226,233, 99,128, 6, 80,244,225,238,225,242,225,226,233, 99,128,
+ 6, 77,244,242,239,235,229,227,249,242,233,236,236,233, 99,128,
+ 4,159,244,225,232,233,242,225,240,242,239,236,239,238,231,237,
+ 225,242,235,232,225,236,230,247,233,228,244,104,128,255,112,246,
+ 229,242,244,233,227,225,236,243,244,242,239,235,229,227,249,242,
+ 233,236,236,233, 99,128, 4,157,226,239,240,239,237,239,230,111,
+ 128, 49, 14, 99, 4,113,205,113,227,113,236,113,244, 97, 2,113,
+ 211,113,221,236,243,241,245,225,242,101,128, 51,137,242,239,110,
+ 128, 1,233,229,228,233,236,236, 97,128, 1, 55,233,242,227,236,
+ 101,128, 36,218,239,237,237,225,225,227,227,229,238,116,128, 1,
+ 55,228,239,244,226,229,236,239,119,128, 30, 51,101, 4,114, 22,
+ 114, 49,114, 74,114, 86,104, 2,114, 28,114, 39,225,242,237,229,
+ 238,233,225,110,128, 5,132,233,242,225,231,225,238, 97,128, 48,
+ 81,235,225,244,225,235,225,238, 97,129, 48,177,114, 62,232,225,
+ 236,230,247,233,228,244,104,128,255,121,238,225,242,237,229,238,
+ 233,225,110,128, 5,111,243,237,225,236,236,235,225,244,225,235,
+ 225,238, 97,128, 48,246,231,242,229,229,238,236,225,238,228,233,
+ 99,128, 1, 56,104, 6,114,130,115, 3,115, 14,115, 39,115,126,
+ 115,214, 97, 5,114,142,114,152,114,163,114,170,114,195,226,229,
+ 238,231,225,236,105,128, 9,150,227,249,242,233,236,236,233, 99,
+ 128, 4, 69,228,229,246, 97,128, 9, 22,231,117, 2,114,177,114,
+ 186,234,225,242,225,244,105,128, 10,150,242,237,245,235,232,105,
+ 128, 10, 22,104, 4,114,205,114,214,114,228,114,244,225,242,225,
+ 226,233, 99,128, 6, 46,230,233,238,225,236,225,242,225,226,233,
+ 99,128,254,166,233,238,233,244,233,225,236,225,242,225,226,233,
+ 99,128,254,167,237,229,228,233,225,236,225,242,225,226,233, 99,
+ 128,254,168,229,233,227,239,240,244,233, 99,128, 3,231,232, 97,
+ 2,115, 21,115, 28,228,229,246, 97,128, 9, 89,231,245,242,237,
+ 245,235,232,105,128, 10, 89,233,229,245,235,104, 4,115, 53,115,
+ 88,115,103,115,112, 97, 2,115, 59,115, 74,227,233,242,227,236,
+ 229,235,239,242,229,225,110,128, 50,120,240,225,242,229,238,235,
+ 239,242,229,225,110,128, 50, 24,227,233,242,227,236,229,235,239,
+ 242,229,225,110,128, 50,106,235,239,242,229,225,110,128, 49, 75,
+ 240,225,242,229,238,235,239,242,229,225,110,128, 50, 10,111, 4,
+ 115,136,115,185,115,195,115,200,235,104, 4,115,147,115,156,115,
+ 165,115,175,225,233,244,232,225,105,128, 14, 2,239,238,244,232,
+ 225,105,128, 14, 5,245,225,244,244,232,225,105,128, 14, 3,247,
+ 225,233,244,232,225,105,128, 14, 4,237,245,244,244,232,225,105,
+ 128, 14, 91,239,107,128, 1,153,242,225,235,232,225,238,231,244,
+ 232,225,105,128, 14, 6,250,243,241,245,225,242,101,128, 51,145,
+ 105, 4,115,234,115,245,116, 14,116, 63,232,233,242,225,231,225,
+ 238, 97,128, 48, 77,235,225,244,225,235,225,238, 97,129, 48,173,
+ 116, 2,232,225,236,230,247,233,228,244,104,128,255,119,242,111,
+ 3,116, 23,116, 38,116, 54,231,245,242,225,237,245,243,241,245,
+ 225,242,101,128, 51, 21,237,229,229,244,239,242,245,243,241,245,
+ 225,242,101,128, 51, 22,243,241,245,225,242,101,128, 51, 20,249,
+ 229,239,107, 5,116, 78,116,113,116,128,116,137,116,151, 97, 2,
+ 116, 84,116, 99,227,233,242,227,236,229,235,239,242,229,225,110,
+ 128, 50,110,240,225,242,229,238,235,239,242,229,225,110,128, 50,
+ 14,227,233,242,227,236,229,235,239,242,229,225,110,128, 50, 96,
+ 235,239,242,229,225,110,128, 49, 49,240,225,242,229,238,235,239,
+ 242,229,225,110,128, 50, 0,243,233,239,243,235,239,242,229,225,
+ 110,128, 49, 51,234,229,227,249,242,233,236,236,233, 99,128, 4,
+ 92,108, 2,116,183,116,194,233,238,229,226,229,236,239,119,128,
+ 30, 53,243,241,245,225,242,101,128, 51,152,109, 3,116,211,116,
+ 225,116,236,227,245,226,229,228,243,241,245,225,242,101,128, 51,
+ 166,239,238,239,243,240,225,227,101,128,255, 75,243,241,245,225,
+ 242,229,228,243,241,245,225,242,101,128, 51,162,111, 5,117, 8,
+ 117, 34,117, 72,117, 84,117, 98,104, 2,117, 14,117, 24,233,242,
+ 225,231,225,238, 97,128, 48, 83,237,243,241,245,225,242,101,128,
+ 51,192,235, 97, 2,117, 41,117, 49,233,244,232,225,105,128, 14,
+ 1,244,225,235,225,238, 97,129, 48,179,117, 60,232,225,236,230,
+ 247,233,228,244,104,128,255,122,239,240,239,243,241,245,225,242,
+ 101,128, 51, 30,240,240,225,227,249,242,233,236,236,233, 99,128,
+ 4,129,114, 2,117,104,117,124,229,225,238,243,244,225,238,228,
+ 225,242,228,243,249,237,226,239,108,128, 50,127,239,238,233,243,
+ 227,237, 98,128, 3, 67,240, 97, 2,117,141,117,147,242,229,110,
+ 128, 36,166,243,241,245,225,242,101,128, 51,170,243,233,227,249,
+ 242,233,236,236,233, 99,128, 4,111,116, 2,117,175,117,184,243,
+ 241,245,225,242,101,128, 51,207,245,242,238,229,100,128, 2,158,
+ 117, 2,117,198,117,209,232,233,242,225,231,225,238, 97,128, 48,
+ 79,235,225,244,225,235,225,238, 97,129, 48,175,117,222,232,225,
+ 236,230,247,233,228,244,104,128,255,120,246,243,241,245,225,242,
+ 101,128, 51,184,247,243,241,245,225,242,101,128, 51,190,108,146,
+ 0,108,118, 38,120, 65,120, 94,120,160,120,198,121, 94,121,103,
+ 121,119,121,143,121,161,122, 23,122, 64,122,199,122,207,122,240,
+ 122,249,123, 1,123, 63, 97, 7,118, 54,118, 64,118, 71,118, 78,
+ 118,103,118,119,120, 53,226,229,238,231,225,236,105,128, 9,178,
+ 227,245,244,101,128, 1, 58,228,229,246, 97,128, 9, 50,231,117,
+ 2,118, 85,118, 94,234,225,242,225,244,105,128, 10,178,242,237,
+ 245,235,232,105,128, 10, 50,235,235,232,225,238,231,249,225,239,
+ 244,232,225,105,128, 14, 69,109, 10,118,141,119, 80,119, 97,119,
+ 135,119,149,119,168,119,184,119,204,119,224,119,247, 97, 2,118,
+ 147,119, 72,236,229,102, 4,118,159,118,173,119, 9,119, 26,230,
+ 233,238,225,236,225,242,225,226,233, 99,128,254,252,232,225,237,
+ 250, 97, 2,118,183,118,224,225,226,239,246,101, 2,118,193,118,
+ 207,230,233,238,225,236,225,242,225,226,233, 99,128,254,248,233,
+ 243,239,236,225,244,229,228,225,242,225,226,233, 99,128,254,247,
+ 226,229,236,239,119, 2,118,234,118,248,230,233,238,225,236,225,
+ 242,225,226,233, 99,128,254,250,233,243,239,236,225,244,229,228,
+ 225,242,225,226,233, 99,128,254,249,233,243,239,236,225,244,229,
+ 228,225,242,225,226,233, 99,128,254,251,237,225,228,228,225,225,
+ 226,239,246,101, 2,119, 41,119, 55,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,254,246,233,243,239,236,225,244,229,228,225,
+ 242,225,226,233, 99,128,254,245,242,225,226,233, 99,128, 6, 68,
+ 226,228, 97,129, 3,187,119, 88,243,244,242,239,235,101,128, 1,
+ 155,229,100,130, 5,220,119,106,119,126,228,225,231,229,243,104,
+ 129,251, 60,119,117,232,229,226,242,229,119,128,251, 60,232,229,
+ 226,242,229,119,128, 5,220,230,233,238,225,236,225,242,225,226,
+ 233, 99,128,254,222,232,225,232,233,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,252,202,233,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,254,223,234,229,229,237,233,238,233,244,
+ 233,225,236,225,242,225,226,233, 99,128,252,201,235,232,225,232,
+ 233,238,233,244,233,225,236,225,242,225,226,233, 99,128,252,203,
+ 236,225,237,232,229,232,233,243,239,236,225,244,229,228,225,242,
+ 225,226,233, 99,128,253,242,237,101, 2,119,254,120, 11,228,233,
+ 225,236,225,242,225,226,233, 99,128,254,224,229,109, 2,120, 18,
+ 120, 37,232,225,232,233,238,233,244,233,225,236,225,242,225,226,
+ 233, 99,128,253,136,233,238,233,244,233,225,236,225,242,225,226,
+ 233, 99,128,252,204,242,231,229,227,233,242,227,236,101,128, 37,
+ 239, 98, 3,120, 73,120, 78,120, 84,225,114,128, 1,154,229,236,
+ 116,128, 2,108,239,240,239,237,239,230,111,128, 49, 12, 99, 4,
+ 120,104,120,111,120,120,120,147,225,242,239,110,128, 1, 62,229,
+ 228,233,236,236, 97,128, 1, 60,233,242, 99, 2,120,128,120,133,
+ 236,101,128, 36,219,245,237,230,236,229,248,226,229,236,239,119,
+ 128, 30, 61,239,237,237,225,225,227,227,229,238,116,128, 1, 60,
+ 228,239,116,130, 1, 64,120,170,120,179,225,227,227,229,238,116,
+ 128, 1, 64,226,229,236,239,119,129, 30, 55,120,189,237,225,227,
+ 242,239,110,128, 30, 57,101, 3,120,206,120,244,121, 89,230,116,
+ 2,120,213,120,229,225,238,231,236,229,225,226,239,246,229,227,
+ 237, 98,128, 3, 26,244,225,227,235,226,229,236,239,247,227,237,
+ 98,128, 3, 24,243,115,132, 0, 60,121, 1,121, 23,121, 35,121,
+ 81,229,241,245,225,108,129, 34,100,121, 11,239,242,231,242,229,
+ 225,244,229,114,128, 34,218,237,239,238,239,243,240,225,227,101,
+ 128,255, 28,111, 2,121, 41,121, 70,114, 2,121, 47,121, 60,229,
+ 241,245,233,246,225,236,229,238,116,128, 34,114,231,242,229,225,
+ 244,229,114,128, 34,118,246,229,242,229,241,245,225,108,128, 34,
+ 102,243,237,225,236,108,128,254,100,250,104,128, 2,110,230,226,
+ 236,239,227,107,128, 37,140,232,239,239,235,242,229,244,242,239,
+ 230,236,229,120,128, 2,109,105, 2,121,125,121,130,242, 97,128,
+ 32,164,247,238,225,242,237,229,238,233,225,110,128, 5,108,106,
+ 129, 1,201,121,149,229,227,249,242,233,236,236,233, 99,128, 4,
+ 89,108,132,246,192,121,173,121,197,121,208,121,217, 97, 2,121,
+ 179,121,186,228,229,246, 97,128, 9, 51,231,245,234,225,242,225,
+ 244,105,128, 10,179,233,238,229,226,229,236,239,119,128, 30, 59,
+ 236,225,228,229,246, 97,128, 9, 52,246,239,227,225,236,233, 99,
+ 3,121,231,121,241,121,248,226,229,238,231,225,236,105,128, 9,
+ 225,228,229,246, 97,128, 9, 97,246,239,247,229,236,243,233,231,
+ 110, 2,122, 6,122, 16,226,229,238,231,225,236,105,128, 9,227,
+ 228,229,246, 97,128, 9, 99,109, 3,122, 31,122, 44,122, 55,233,
+ 228,228,236,229,244,233,236,228,101,128, 2,107,239,238,239,243,
+ 240,225,227,101,128,255, 76,243,241,245,225,242,101,128, 51,208,
+ 111, 6,122, 78,122, 90,122,132,122,143,122,149,122,191,227,232,
+ 245,236,225,244,232,225,105,128, 14, 44,231,233,227,225,108, 3,
+ 122,102,122,108,122,127,225,238,100,128, 34, 39,238,239,116,129,
+ 0,172,122,116,242,229,246,229,242,243,229,100,128, 35, 16,239,
+ 114,128, 34, 40,236,233,238,231,244,232,225,105,128, 14, 37,238,
+ 231,115,128, 1,127,247,236,233,238,101, 2,122,159,122,182, 99,
+ 2,122,165,122,177,229,238,244,229,242,236,233,238,101,128,254,
+ 78,237, 98,128, 3, 50,228,225,243,232,229,100,128,254, 77,250,
+ 229,238,231,101,128, 37,202,240,225,242,229,110,128, 36,167,115,
+ 3,122,215,122,222,122,230,236,225,243,104,128, 1, 66,241,245,
+ 225,242,101,128, 33, 19,245,240,229,242,233,239,114,128,246,238,
+ 244,243,232,225,228,101,128, 37,145,245,244,232,225,105,128, 14,
+ 38,246,239,227,225,236,233, 99, 3,123, 15,123, 25,123, 32,226,
+ 229,238,231,225,236,105,128, 9,140,228,229,246, 97,128, 9, 12,
+ 246,239,247,229,236,243,233,231,110, 2,123, 46,123, 56,226,229,
+ 238,231,225,236,105,128, 9,226,228,229,246, 97,128, 9, 98,248,
+ 243,241,245,225,242,101,128, 51,211,109,144, 0,109,123,109,125,
+ 218,125,243,126, 14,126, 39,127, 92,127,114,128,169,128,199,128,
+ 248,129, 99,129,121,129,146,129,155,130,182,130,210, 97, 12,123,
+ 135,123,145,123,209,123,216,123,241,124, 33,125,125,125,150,125,
+ 155,125,169,125,181,125,186,226,229,238,231,225,236,105,128, 9,
+ 174, 99, 2,123,151,123,203,242,239,110,132, 0,175,123,165,123,
+ 176,123,182,123,191,226,229,236,239,247,227,237, 98,128, 3, 49,
+ 227,237, 98,128, 3, 4,236,239,247,237,239,100,128, 2,205,237,
+ 239,238,239,243,240,225,227,101,128,255,227,245,244,101,128, 30,
+ 63,228,229,246, 97,128, 9, 46,231,117, 2,123,223,123,232,234,
+ 225,242,225,244,105,128, 10,174,242,237,245,235,232,105,128, 10,
+ 46,104, 2,123,247,124, 23,225,240,225,235,104, 2,124, 1,124,
+ 10,232,229,226,242,229,119,128, 5,164,236,229,230,244,232,229,
+ 226,242,229,119,128, 5,164,233,242,225,231,225,238, 97,128, 48,
+ 126,105, 5,124, 45,124,114,124,177,124,207,125,113,227,232,225,
+ 244,244,225,247, 97, 3,124, 60,124, 91,124, 98,236,239,119, 2,
+ 124, 68,124, 79,236,229,230,244,244,232,225,105,128,248,149,242,
+ 233,231,232,244,244,232,225,105,128,248,148,244,232,225,105,128,
+ 14, 75,245,240,240,229,242,236,229,230,244,244,232,225,105,128,
+ 248,147,229,107, 3,124,123,124,154,124,161,236,239,119, 2,124,
+ 131,124,142,236,229,230,244,244,232,225,105,128,248,140,242,233,
+ 231,232,244,244,232,225,105,128,248,139,244,232,225,105,128, 14,
+ 72,245,240,240,229,242,236,229,230,244,244,232,225,105,128,248,
+ 138,232,225,238,225,235,225,116, 2,124,189,124,200,236,229,230,
+ 244,244,232,225,105,128,248,132,244,232,225,105,128, 14, 49,116,
+ 3,124,215,124,243,125, 50,225,233,235,232,117, 2,124,225,124,
+ 236,236,229,230,244,244,232,225,105,128,248,137,244,232,225,105,
+ 128, 14, 71,232,111, 3,124,252,125, 27,125, 34,236,239,119, 2,
+ 125, 4,125, 15,236,229,230,244,244,232,225,105,128,248,143,242,
+ 233,231,232,244,244,232,225,105,128,248,142,244,232,225,105,128,
+ 14, 73,245,240,240,229,242,236,229,230,244,244,232,225,105,128,
+ 248,141,242,105, 3,125, 59,125, 90,125, 97,236,239,119, 2,125,
+ 67,125, 78,236,229,230,244,244,232,225,105,128,248,146,242,233,
+ 231,232,244,244,232,225,105,128,248,145,244,232,225,105,128, 14,
+ 74,245,240,240,229,242,236,229,230,244,244,232,225,105,128,248,
+ 144,249,225,237,239,235,244,232,225,105,128, 14, 70,235,225,244,
+ 225,235,225,238, 97,129, 48,222,125,138,232,225,236,230,247,233,
+ 228,244,104,128,255,143,236,101,128, 38, 66,238,243,249,239,238,
+ 243,241,245,225,242,101,128, 51, 71,241,225,230,232,229,226,242,
+ 229,119,128, 5,190,242,115,128, 38, 66,115, 2,125,192,125,210,
+ 239,242,225,227,233,242,227,236,229,232,229,226,242,229,119,128,
+ 5,175,241,245,225,242,101,128, 51,131, 98, 2,125,224,125,234,
+ 239,240,239,237,239,230,111,128, 49, 7,243,241,245,225,242,101,
+ 128, 51,212, 99, 2,125,249,126, 1,233,242,227,236,101,128, 36,
+ 220,245,226,229,228,243,241,245,225,242,101,128, 51,165,228,239,
+ 116, 2,126, 22,126, 31,225,227,227,229,238,116,128, 30, 65,226,
+ 229,236,239,119,128, 30, 67,101, 7,126, 55,126,182,126,193,126,
+ 208,126,233,127, 14,127, 26,101, 2,126, 61,126,169,109, 4,126,
+ 71,126, 80,126, 94,126,110,225,242,225,226,233, 99,128, 6, 69,
+ 230,233,238,225,236,225,242,225,226,233, 99,128,254,226,233,238,
+ 233,244,233,225,236,225,242,225,226,233, 99,128,254,227,237,101,
+ 2,126,117,126,130,228,233,225,236,225,242,225,226,233, 99,128,
+ 254,228,229,237,105, 2,126,138,126,153,238,233,244,233,225,236,
+ 225,242,225,226,233, 99,128,252,209,243,239,236,225,244,229,228,
+ 225,242,225,226,233, 99,128,252, 72,244,239,242,245,243,241,245,
+ 225,242,101,128, 51, 77,232,233,242,225,231,225,238, 97,128, 48,
+ 129,233,250,233,229,242,225,243,241,245,225,242,101,128, 51,126,
+ 235,225,244,225,235,225,238, 97,129, 48,225,126,221,232,225,236,
+ 230,247,233,228,244,104,128,255,146,109,130, 5,222,126,241,127,
+ 5,228,225,231,229,243,104,129,251, 62,126,252,232,229,226,242,
+ 229,119,128,251, 62,232,229,226,242,229,119,128, 5,222,238,225,
+ 242,237,229,238,233,225,110,128, 5,116,242,235,232, 97, 3,127,
+ 37,127, 46,127, 79,232,229,226,242,229,119,128, 5,165,235,229,
+ 230,245,236, 97, 2,127, 57,127, 66,232,229,226,242,229,119,128,
+ 5,166,236,229,230,244,232,229,226,242,229,119,128, 5,166,236,
+ 229,230,244,232,229,226,242,229,119,128, 5,165,104, 2,127, 98,
+ 127,104,239,239,107,128, 2,113,250,243,241,245,225,242,101,128,
+ 51,146,105, 6,127,128,127,165,128, 46,128, 57,128, 82,128,139,
+ 228,100, 2,127,135,127,160,236,229,228,239,244,235,225,244,225,
+ 235,225,238,225,232,225,236,230,247,233,228,244,104,128,255,101,
+ 239,116,128, 0,183,229,245,109, 5,127,179,127,214,127,229,127,
+ 238,128, 33, 97, 2,127,185,127,200,227,233,242,227,236,229,235,
+ 239,242,229,225,110,128, 50,114,240,225,242,229,238,235,239,242,
+ 229,225,110,128, 50, 18,227,233,242,227,236,229,235,239,242,229,
+ 225,110,128, 50,100,235,239,242,229,225,110,128, 49, 65,112, 2,
+ 127,244,128, 20, 97, 2,127,250,128, 8,238,243,233,239,243,235,
+ 239,242,229,225,110,128, 49,112,242,229,238,235,239,242,229,225,
+ 110,128, 50, 4,233,229,245,240,235,239,242,229,225,110,128, 49,
+ 110,243,233,239,243,235,239,242,229,225,110,128, 49,111,232,233,
+ 242,225,231,225,238, 97,128, 48,127,235,225,244,225,235,225,238,
+ 97,129, 48,223,128, 70,232,225,236,230,247,233,228,244,104,128,
+ 255,144,238,117, 2,128, 89,128,134,115,132, 34, 18,128,101,128,
+ 112,128,121,128,127,226,229,236,239,247,227,237, 98,128, 3, 32,
+ 227,233,242,227,236,101,128, 34,150,237,239,100,128, 2,215,240,
+ 236,245,115,128, 34, 19,244,101,128, 32, 50,242,105, 2,128,146,
+ 128,160,226,225,225,242,245,243,241,245,225,242,101,128, 51, 74,
+ 243,241,245,225,242,101,128, 51, 73,108, 2,128,175,128,190,239,
+ 238,231,236,229,231,244,245,242,238,229,100,128, 2,112,243,241,
+ 245,225,242,101,128, 51,150,109, 3,128,207,128,221,128,232,227,
+ 245,226,229,228,243,241,245,225,242,101,128, 51,163,239,238,239,
+ 243,240,225,227,101,128,255, 77,243,241,245,225,242,229,228,243,
+ 241,245,225,242,101,128, 51,159,111, 5,129, 4,129, 30,129, 55,
+ 129, 65,129, 74,104, 2,129, 10,129, 20,233,242,225,231,225,238,
+ 97,128, 48,130,237,243,241,245,225,242,101,128, 51,193,235,225,
+ 244,225,235,225,238, 97,129, 48,226,129, 43,232,225,236,230,247,
+ 233,228,244,104,128,255,147,236,243,241,245,225,242,101,128, 51,
+ 214,237,225,244,232,225,105,128, 14, 33,246,229,242,243,243,241,
+ 245,225,242,101,129, 51,167,129, 89,228,243,241,245,225,242,101,
+ 128, 51,168,240, 97, 2,129,106,129,112,242,229,110,128, 36,168,
+ 243,241,245,225,242,101,128, 51,171,115, 2,129,127,129,136,243,
+ 241,245,225,242,101,128, 51,179,245,240,229,242,233,239,114,128,
+ 246,239,244,245,242,238,229,100,128, 2,111,117,141, 0,181,129,
+ 185,129,189,129,199,129,223,129,233,129,255,130, 10,130, 35,130,
+ 58,130, 68,130, 98,130,162,130,172, 49,128, 0,181,225,243,241,
+ 245,225,242,101,128, 51,130,227,104, 2,129,206,129,216,231,242,
+ 229,225,244,229,114,128, 34,107,236,229,243,115,128, 34,106,230,
+ 243,241,245,225,242,101,128, 51,140,103, 2,129,239,129,246,242,
+ 229,229,107,128, 3,188,243,241,245,225,242,101,128, 51,141,232,
+ 233,242,225,231,225,238, 97,128, 48,128,235,225,244,225,235,225,
+ 238, 97,129, 48,224,130, 23,232,225,236,230,247,233,228,244,104,
+ 128,255,145,108, 2,130, 41,130, 50,243,241,245,225,242,101,128,
+ 51,149,244,233,240,236,121,128, 0,215,237,243,241,245,225,242,
+ 101,128, 51,155,238,225,104, 2,130, 76,130, 85,232,229,226,242,
+ 229,119,128, 5,163,236,229,230,244,232,229,226,242,229,119,128,
+ 5,163,115, 2,130,104,130,153,233, 99, 3,130,113,130,130,130,
+ 141,225,236,238,239,244,101,129, 38,106,130,124,228,226,108,128,
+ 38,107,230,236,225,244,243,233,231,110,128, 38,109,243,232,225,
+ 242,240,243,233,231,110,128, 38,111,243,241,245,225,242,101,128,
+ 51,178,246,243,241,245,225,242,101,128, 51,182,247,243,241,245,
+ 225,242,101,128, 51,188,118, 2,130,188,130,201,237,229,231,225,
+ 243,241,245,225,242,101,128, 51,185,243,241,245,225,242,101,128,
+ 51,183,119, 2,130,216,130,229,237,229,231,225,243,241,245,225,
+ 242,101,128, 51,191,243,241,245,225,242,101,128, 51,189,110,150,
+ 0,110,131, 30,131,164,131,188,131,254,132, 23,132, 81,132, 91,
+ 132,158,132,201,134,235,134,253,135, 22,135, 53,135, 79,135,144,
+ 137,126,137,134,137,159,137,167,138,135,138,145,138,155, 97, 8,
+ 131, 48,131, 68,131, 75,131, 82,131,107,131,118,131,143,131,155,
+ 98, 2,131, 54,131, 63,229,238,231,225,236,105,128, 9,168,236,
+ 97,128, 34, 7,227,245,244,101,128, 1, 68,228,229,246, 97,128,
+ 9, 40,231,117, 2,131, 89,131, 98,234,225,242,225,244,105,128,
+ 10,168,242,237,245,235,232,105,128, 10, 40,232,233,242,225,231,
+ 225,238, 97,128, 48,106,235,225,244,225,235,225,238, 97,129, 48,
+ 202,131,131,232,225,236,230,247,233,228,244,104,128,255,133,240,
+ 239,243,244,242,239,240,232,101,128, 1, 73,243,241,245,225,242,
+ 101,128, 51,129, 98, 2,131,170,131,180,239,240,239,237,239,230,
+ 111,128, 49, 11,243,240,225,227,101,128, 0,160, 99, 4,131,198,
+ 131,205,131,214,131,241,225,242,239,110,128, 1, 72,229,228,233,
+ 236,236, 97,128, 1, 70,233,242, 99, 2,131,222,131,227,236,101,
+ 128, 36,221,245,237,230,236,229,248,226,229,236,239,119,128, 30,
+ 75,239,237,237,225,225,227,227,229,238,116,128, 1, 70,228,239,
+ 116, 2,132, 6,132, 15,225,227,227,229,238,116,128, 30, 69,226,
+ 229,236,239,119,128, 30, 71,101, 3,132, 31,132, 42,132, 67,232,
+ 233,242,225,231,225,238, 97,128, 48,109,235,225,244,225,235,225,
+ 238, 97,129, 48,205,132, 55,232,225,236,230,247,233,228,244,104,
+ 128,255,136,247,243,232,229,241,229,236,243,233,231,110,128, 32,
+ 170,230,243,241,245,225,242,101,128, 51,139,103, 2,132, 97,132,
+ 147, 97, 3,132,105,132,115,132,122,226,229,238,231,225,236,105,
+ 128, 9,153,228,229,246, 97,128, 9, 25,231,117, 2,132,129,132,
+ 138,234,225,242,225,244,105,128, 10,153,242,237,245,235,232,105,
+ 128, 10, 25,239,238,231,245,244,232,225,105,128, 14, 7,104, 2,
+ 132,164,132,174,233,242,225,231,225,238, 97,128, 48,147,239,239,
+ 107, 2,132,182,132,189,236,229,230,116,128, 2,114,242,229,244,
+ 242,239,230,236,229,120,128, 2,115,105, 4,132,211,133,124,133,
+ 135,133,193,229,245,110, 7,132,229,133, 8,133, 40,133, 54,133,
+ 63,133, 96,133,109, 97, 2,132,235,132,250,227,233,242,227,236,
+ 229,235,239,242,229,225,110,128, 50,111,240,225,242,229,238,235,
+ 239,242,229,225,110,128, 50, 15,227,105, 2,133, 15,133, 27,229,
+ 245,227,235,239,242,229,225,110,128, 49, 53,242,227,236,229,235,
+ 239,242,229,225,110,128, 50, 97,232,233,229,245,232,235,239,242,
+ 229,225,110,128, 49, 54,235,239,242,229,225,110,128, 49, 52,240,
+ 97, 2,133, 70,133, 84,238,243,233,239,243,235,239,242,229,225,
+ 110,128, 49,104,242,229,238,235,239,242,229,225,110,128, 50, 1,
+ 243,233,239,243,235,239,242,229,225,110,128, 49,103,244,233,235,
+ 229,245,244,235,239,242,229,225,110,128, 49,102,232,233,242,225,
+ 231,225,238, 97,128, 48,107,107, 2,133,141,133,165,225,244,225,
+ 235,225,238, 97,129, 48,203,133,153,232,225,236,230,247,233,228,
+ 244,104,128,255,134,232,225,232,233,116, 2,133,175,133,186,236,
+ 229,230,244,244,232,225,105,128,248,153,244,232,225,105,128, 14,
+ 77,238,101,141, 0, 57,133,224,133,233,133,243,134, 17,134, 24,
+ 134, 49,134, 76,134,110,134,122,134,133,134,166,134,174,134,185,
+ 225,242,225,226,233, 99,128, 6,105,226,229,238,231,225,236,105,
+ 128, 9,239,227,233,242,227,236,101,129, 36,104,133,254,233,238,
+ 246,229,242,243,229,243,225,238,243,243,229,242,233,102,128, 39,
+ 146,228,229,246, 97,128, 9,111,231,117, 2,134, 31,134, 40,234,
+ 225,242,225,244,105,128, 10,239,242,237,245,235,232,105,128, 10,
+ 111,232, 97, 2,134, 56,134, 67,227,235,225,242,225,226,233, 99,
+ 128, 6,105,238,231,250,232,239,117,128, 48, 41,105, 2,134, 82,
+ 134,100,228,229,239,231,242,225,240,232,233,227,240,225,242,229,
+ 110,128, 50, 40,238,230,229,242,233,239,114,128, 32,137,237,239,
+ 238,239,243,240,225,227,101,128,255, 25,239,236,228,243,244,249,
+ 236,101,128,247, 57,112, 2,134,139,134,146,225,242,229,110,128,
+ 36,124,229,114, 2,134,153,134,159,233,239,100,128, 36,144,243,
+ 233,225,110,128, 6,249,242,239,237,225,110,128, 33,120,243,245,
+ 240,229,242,233,239,114,128, 32,121,116, 2,134,191,134,229,229,
+ 229,110, 2,134,199,134,208,227,233,242,227,236,101,128, 36,114,
+ 112, 2,134,214,134,221,225,242,229,110,128, 36,134,229,242,233,
+ 239,100,128, 36,154,232,225,105,128, 14, 89,106,129, 1,204,134,
+ 241,229,227,249,242,233,236,236,233, 99,128, 4, 90,235,225,244,
+ 225,235,225,238, 97,129, 48,243,135, 10,232,225,236,230,247,233,
+ 228,244,104,128,255,157,108, 2,135, 28,135, 42,229,231,242,233,
+ 231,232,244,236,239,238,103,128, 1,158,233,238,229,226,229,236,
+ 239,119,128, 30, 73,109, 2,135, 59,135, 70,239,238,239,243,240,
+ 225,227,101,128,255, 78,243,241,245,225,242,101,128, 51,154,110,
+ 2,135, 85,135,135, 97, 3,135, 93,135,103,135,110,226,229,238,
+ 231,225,236,105,128, 9,163,228,229,246, 97,128, 9, 35,231,117,
+ 2,135,117,135,126,234,225,242,225,244,105,128, 10,163,242,237,
+ 245,235,232,105,128, 10, 35,238,225,228,229,246, 97,128, 9, 41,
+ 111, 6,135,158,135,169,135,194,135,235,136,187,137,114,232,233,
+ 242,225,231,225,238, 97,128, 48,110,235,225,244,225,235,225,238,
+ 97,129, 48,206,135,182,232,225,236,230,247,233,228,244,104,128,
+ 255,137,110, 3,135,202,135,218,135,227,226,242,229,225,235,233,
+ 238,231,243,240,225,227,101,128, 0,160,229,238,244,232,225,105,
+ 128, 14, 19,245,244,232,225,105,128, 14, 25,239,110, 7,135,252,
+ 136, 5,136, 19,136, 53,136, 69,136,110,136,169,225,242,225,226,
+ 233, 99,128, 6, 70,230,233,238,225,236,225,242,225,226,233, 99,
+ 128,254,230,231,232,245,238,238, 97, 2,136, 30,136, 39,225,242,
+ 225,226,233, 99,128, 6,186,230,233,238,225,236,225,242,225,226,
+ 233, 99,128,251,159,233,238,233,244,233,225,236,225,242,225,226,
+ 233, 99,128,254,231,234,229,229,237,105, 2,136, 79,136, 94,238,
+ 233,244,233,225,236,225,242,225,226,233, 99,128,252,210,243,239,
+ 236,225,244,229,228,225,242,225,226,233, 99,128,252, 75,237,101,
+ 2,136,117,136,130,228,233,225,236,225,242,225,226,233, 99,128,
+ 254,232,229,237,105, 2,136,138,136,153,238,233,244,233,225,236,
+ 225,242,225,226,233, 99,128,252,213,243,239,236,225,244,229,228,
+ 225,242,225,226,233, 99,128,252, 78,238,239,239,238,230,233,238,
+ 225,236,225,242,225,226,233, 99,128,252,141,116, 7,136,203,136,
+ 214,136,243,137, 22,137, 34,137, 54,137, 80,227,239,238,244,225,
+ 233,238,115,128, 34, 12,101, 2,136,220,136,236,236,229,237,229,
+ 238,116,129, 34, 9,136,231,239,102,128, 34, 9,241,245,225,108,
+ 128, 34, 96,231,242,229,225,244,229,114,129, 34,111,136,255,238,
+ 239,114, 2,137, 7,137, 15,229,241,245,225,108,128, 34,113,236,
+ 229,243,115,128, 34,121,233,228,229,238,244,233,227,225,108,128,
+ 34, 98,236,229,243,115,129, 34,110,137, 43,238,239,242,229,241,
+ 245,225,108,128, 34,112,112, 2,137, 60,137, 70,225,242,225,236,
+ 236,229,108,128, 34, 38,242,229,227,229,228,229,115,128, 34,128,
+ 243,117, 3,137, 89,137, 96,137,105,226,243,229,116,128, 34,132,
+ 227,227,229,229,228,115,128, 34,129,240,229,242,243,229,116,128,
+ 34,133,247,225,242,237,229,238,233,225,110,128, 5,118,240,225,
+ 242,229,110,128, 36,169,115, 2,137,140,137,149,243,241,245,225,
+ 242,101,128, 51,177,245,240,229,242,233,239,114,128, 32,127,244,
+ 233,236,228,101,128, 0,241,117,132, 3,189,137,179,137,190,138,
+ 15,138, 98,232,233,242,225,231,225,238, 97,128, 48,108,107, 2,
+ 137,196,137,220,225,244,225,235,225,238, 97,129, 48,204,137,208,
+ 232,225,236,230,247,233,228,244,104,128,255,135,244, 97, 3,137,
+ 229,137,239,137,246,226,229,238,231,225,236,105,128, 9,188,228,
+ 229,246, 97,128, 9, 60,231,117, 2,137,253,138, 6,234,225,242,
+ 225,244,105,128, 10,188,242,237,245,235,232,105,128, 10, 60,109,
+ 2,138, 21,138, 55,226,229,242,243,233,231,110,130, 0, 35,138,
+ 35,138, 47,237,239,238,239,243,240,225,227,101,128,255, 3,243,
+ 237,225,236,108,128,254, 95,229,114, 2,138, 62,138, 94,225,236,
+ 243,233,231,110, 2,138, 73,138, 81,231,242,229,229,107,128, 3,
+ 116,236,239,247,229,242,231,242,229,229,107,128, 3,117,111,128,
+ 33, 22,110,130, 5,224,138,106,138,126,228,225,231,229,243,104,
+ 129,251, 64,138,117,232,229,226,242,229,119,128,251, 64,232,229,
+ 226,242,229,119,128, 5,224,246,243,241,245,225,242,101,128, 51,
+ 181,247,243,241,245,225,242,101,128, 51,187,249, 97, 3,138,164,
+ 138,174,138,181,226,229,238,231,225,236,105,128, 9,158,228,229,
+ 246, 97,128, 9, 30,231,117, 2,138,188,138,197,234,225,242,225,
+ 244,105,128, 10,158,242,237,245,235,232,105,128, 10, 30,111,147,
+ 0,111,138,248,139, 14,139, 92,140, 6,140, 78,140, 93,140,133,
+ 141, 0,141, 21,141, 59,141, 70,141,248,143, 82,143,146,143,179,
+ 143,225,144, 98,144,145,144,157, 97, 2,138,254,139, 5,227,245,
+ 244,101,128, 0,243,238,231,244,232,225,105,128, 14, 45, 98, 4,
+ 139, 24,139, 66,139, 75,139, 85,225,242,242,229,100,130, 2,117,
+ 139, 36,139, 47,227,249,242,233,236,236,233, 99,128, 4,233,228,
+ 233,229,242,229,243,233,243,227,249,242,233,236,236,233, 99,128,
+ 4,235,229,238,231,225,236,105,128, 9,147,239,240,239,237,239,
+ 230,111,128, 49, 27,242,229,246,101,128, 1, 79, 99, 3,139,100,
+ 139,173,139,252, 97, 2,139,106,139,167,238,228,242, 97, 3,139,
+ 117,139,124,139,135,228,229,246, 97,128, 9, 17,231,245,234,225,
+ 242,225,244,105,128, 10,145,246,239,247,229,236,243,233,231,110,
+ 2,139,149,139,156,228,229,246, 97,128, 9, 73,231,245,234,225,
+ 242,225,244,105,128, 10,201,242,239,110,128, 1,210,233,242, 99,
+ 2,139,181,139,186,236,101,128, 36,222,245,237,230,236,229,120,
+ 133, 0,244,139,205,139,213,139,224,139,232,139,244,225,227,245,
+ 244,101,128, 30,209,228,239,244,226,229,236,239,119,128, 30,217,
+ 231,242,225,246,101,128, 30,211,232,239,239,235,225,226,239,246,
+ 101,128, 30,213,244,233,236,228,101,128, 30,215,249,242,233,236,
+ 236,233, 99,128, 4, 62,100, 4,140, 16,140, 39,140, 45,140, 68,
+ 226,108, 2,140, 23,140, 31,225,227,245,244,101,128, 1, 81,231,
+ 242,225,246,101,128, 2, 13,229,246, 97,128, 9, 19,233,229,242,
+ 229,243,233,115,129, 0,246,140, 57,227,249,242,233,236,236,233,
+ 99,128, 4,231,239,244,226,229,236,239,119,128, 30,205,101,129,
+ 1, 83,140, 84,235,239,242,229,225,110,128, 49, 90,103, 3,140,
+ 101,140,116,140,123,239,238,229,107,129, 2,219,140,110,227,237,
+ 98,128, 3, 40,242,225,246,101,128, 0,242,245,234,225,242,225,
+ 244,105,128, 10,147,104, 4,140,143,140,154,140,164,140,242,225,
+ 242,237,229,238,233,225,110,128, 5,133,233,242,225,231,225,238,
+ 97,128, 48, 74,111, 2,140,170,140,180,239,235,225,226,239,246,
+ 101,128, 30,207,242,110,133, 1,161,140,195,140,203,140,214,140,
+ 222,140,234,225,227,245,244,101,128, 30,219,228,239,244,226,229,
+ 236,239,119,128, 30,227,231,242,225,246,101,128, 30,221,232,239,
+ 239,235,225,226,239,246,101,128, 30,223,244,233,236,228,101,128,
+ 30,225,245,238,231,225,242,245,237,236,225,245,116,128, 1, 81,
+ 105,129, 1,163,141, 6,238,246,229,242,244,229,228,226,242,229,
+ 246,101,128, 2, 15,107, 2,141, 27,141, 51,225,244,225,235,225,
+ 238, 97,129, 48,170,141, 39,232,225,236,230,247,233,228,244,104,
+ 128,255,117,239,242,229,225,110,128, 49, 87,236,229,232,229,226,
+ 242,229,119,128, 5,171,109, 6,141, 84,141,112,141,119,141,208,
+ 141,219,141,237,225,227,242,239,110,130, 1, 77,141, 96,141,104,
+ 225,227,245,244,101,128, 30, 83,231,242,225,246,101,128, 30, 81,
+ 228,229,246, 97,128, 9, 80,229,231, 97,133, 3,201,141,135,141,
+ 139,141,150,141,164,141,180, 49,128, 3,214,227,249,242,233,236,
+ 236,233, 99,128, 4, 97,236,225,244,233,238,227,236,239,243,229,
+ 100,128, 2,119,242,239,245,238,228,227,249,242,233,236,236,233,
+ 99,128, 4,123,116, 2,141,186,141,201,233,244,236,239,227,249,
+ 242,233,236,236,233, 99,128, 4,125,239,238,239,115,128, 3,206,
+ 231,245,234,225,242,225,244,105,128, 10,208,233,227,242,239,110,
+ 129, 3,191,141,229,244,239,238,239,115,128, 3,204,239,238,239,
+ 243,240,225,227,101,128,255, 79,238,101,145, 0, 49,142, 31,142,
+ 40,142, 50,142, 80,142,105,142,114,142,123,142,148,142,182,142,
+ 216,142,228,142,247,143, 2,143, 35,143, 45,143, 53,143, 64,225,
+ 242,225,226,233, 99,128, 6, 97,226,229,238,231,225,236,105,128,
+ 9,231,227,233,242,227,236,101,129, 36, 96,142, 61,233,238,246,
+ 229,242,243,229,243,225,238,243,243,229,242,233,102,128, 39,138,
+ 100, 2,142, 86,142, 92,229,246, 97,128, 9,103,239,244,229,238,
+ 236,229,225,228,229,114,128, 32, 36,229,233,231,232,244,104,128,
+ 33, 91,230,233,244,244,229,100,128,246,220,231,117, 2,142,130,
+ 142,139,234,225,242,225,244,105,128, 10,231,242,237,245,235,232,
+ 105,128, 10,103,232, 97, 3,142,157,142,168,142,173,227,235,225,
+ 242,225,226,233, 99,128, 6, 97,236,102,128, 0,189,238,231,250,
+ 232,239,117,128, 48, 33,105, 2,142,188,142,206,228,229,239,231,
+ 242,225,240,232,233,227,240,225,242,229,110,128, 50, 32,238,230,
+ 229,242,233,239,114,128, 32,129,237,239,238,239,243,240,225,227,
+ 101,128,255, 17,238,245,237,229,242,225,244,239,242,226,229,238,
+ 231,225,236,105,128, 9,244,239,236,228,243,244,249,236,101,128,
+ 247, 49,112, 2,143, 8,143, 15,225,242,229,110,128, 36,116,229,
+ 114, 2,143, 22,143, 28,233,239,100,128, 36,136,243,233,225,110,
+ 128, 6,241,241,245,225,242,244,229,114,128, 0,188,242,239,237,
+ 225,110,128, 33,112,243,245,240,229,242,233,239,114,128, 0,185,
+ 244,104, 2,143, 71,143, 76,225,105,128, 14, 81,233,242,100,128,
+ 33, 83,111, 3,143, 90,143,124,143,140,103, 2,143, 96,143,114,
+ 239,238,229,107,129, 1,235,143,105,237,225,227,242,239,110,128,
+ 1,237,245,242,237,245,235,232,105,128, 10, 19,237,225,244,242,
+ 225,231,245,242,237,245,235,232,105,128, 10, 75,240,229,110,128,
+ 2, 84,112, 3,143,154,143,161,143,172,225,242,229,110,128, 36,
+ 170,229,238,226,245,236,236,229,116,128, 37,230,244,233,239,110,
+ 128, 35, 37,114, 2,143,185,143,214,100, 2,143,191,143,202,230,
+ 229,237,233,238,233,238,101,128, 0,170,237,225,243,227,245,236,
+ 233,238,101,128, 0,186,244,232,239,231,239,238,225,108,128, 34,
+ 31,115, 5,143,237,144, 13,144, 30,144, 75,144, 88,232,239,242,
+ 116, 2,143,246,143,253,228,229,246, 97,128, 9, 18,246,239,247,
+ 229,236,243,233,231,238,228,229,246, 97,128, 9, 74,236,225,243,
+ 104,129, 0,248,144, 22,225,227,245,244,101,128, 1,255,237,225,
+ 236,108, 2,144, 39,144, 50,232,233,242,225,231,225,238, 97,128,
+ 48, 73,235,225,244,225,235,225,238, 97,129, 48,169,144, 63,232,
+ 225,236,230,247,233,228,244,104,128,255,107,244,242,239,235,229,
+ 225,227,245,244,101,128, 1,255,245,240,229,242,233,239,114,128,
+ 246,240,116, 2,144,104,144,115,227,249,242,233,236,236,233, 99,
+ 128, 4,127,233,236,228,101,130, 0,245,144,126,144,134,225,227,
+ 245,244,101,128, 30, 77,228,233,229,242,229,243,233,115,128, 30,
+ 79,245,226,239,240,239,237,239,230,111,128, 49, 33,118, 2,144,
+ 163,144,244,229,114, 2,144,170,144,236,236,233,238,101,131, 32,
+ 62,144,183,144,206,144,229, 99, 2,144,189,144,201,229,238,244,
+ 229,242,236,233,238,101,128,254, 74,237, 98,128, 3, 5,100, 2,
+ 144,212,144,220,225,243,232,229,100,128,254, 73,226,236,247,225,
+ 246,121,128,254, 76,247,225,246,121,128,254, 75,243,227,239,242,
+ 101,128, 0,175,239,247,229,236,243,233,231,110, 3,145, 3,145,
+ 13,145, 20,226,229,238,231,225,236,105,128, 9,203,228,229,246,
+ 97,128, 9, 75,231,245,234,225,242,225,244,105,128, 10,203,112,
+ 145, 0,112,145, 69,147,197,147,208,147,217,147,229,149,154,149,
+ 164,150,156,151,175,152, 9,152, 35,152,166,152,174,153, 76,153,
+ 134,153,162,153,172, 97, 14,145, 99,145,131,145,141,145,148,145,
+ 155,145,203,145,214,145,228,145,239,146, 30,146, 44,147, 56,147,
+ 95,147,185, 97, 2,145,105,145,117,237,240,243,243,241,245,225,
+ 242,101,128, 51,128,243,229,238,244,239,243,241,245,225,242,101,
+ 128, 51, 43,226,229,238,231,225,236,105,128, 9,170,227,245,244,
+ 101,128, 30, 85,228,229,246, 97,128, 9, 42,103, 2,145,161,145,
+ 179,101, 2,145,167,145,174,228,239,247,110,128, 33,223,245,112,
+ 128, 33,222,117, 2,145,185,145,194,234,225,242,225,244,105,128,
+ 10,170,242,237,245,235,232,105,128, 10, 42,232,233,242,225,231,
+ 225,238, 97,128, 48,113,233,249,225,238,238,239,233,244,232,225,
+ 105,128, 14, 47,235,225,244,225,235,225,238, 97,128, 48,209,108,
+ 2,145,245,146, 14,225,244,225,236,233,250,225,244,233,239,238,
+ 227,249,242,233,236,236,233,227,227,237, 98,128, 4,132,239,227,
+ 232,235,225,227,249,242,233,236,236,233, 99,128, 4,192,238,243,
+ 233,239,243,235,239,242,229,225,110,128, 49,127,114, 3,146, 52,
+ 146, 73,147, 45, 97, 2,146, 58,146, 66,231,242,225,240,104,128,
+ 0,182,236,236,229,108,128, 34, 37,229,110, 2,146, 80,146,190,
+ 236,229,230,116,136, 0, 40,146,103,146,118,146,123,146,128,146,
+ 139,146,151,146,174,146,179,225,236,244,239,238,229,225,242,225,
+ 226,233, 99,128,253, 62,226,116,128,248,237,229,120,128,248,236,
+ 233,238,230,229,242,233,239,114,128, 32,141,237,239,238,239,243,
+ 240,225,227,101,128,255, 8,115, 2,146,157,146,164,237,225,236,
+ 108,128,254, 89,245,240,229,242,233,239,114,128, 32,125,244,112,
+ 128,248,235,246,229,242,244,233,227,225,108,128,254, 53,242,233,
+ 231,232,116,136, 0, 41,146,214,146,229,146,234,146,239,146,250,
+ 147, 6,147, 29,147, 34,225,236,244,239,238,229,225,242,225,226,
+ 233, 99,128,253, 63,226,116,128,248,248,229,120,128,248,247,233,
+ 238,230,229,242,233,239,114,128, 32,142,237,239,238,239,243,240,
+ 225,227,101,128,255, 9,115, 2,147, 12,147, 19,237,225,236,108,
+ 128,254, 90,245,240,229,242,233,239,114,128, 32,126,244,112,128,
+ 248,246,246,229,242,244,233,227,225,108,128,254, 54,244,233,225,
+ 236,228,233,230,102,128, 34, 2,115, 3,147, 64,147, 75,147, 87,
+ 229,241,232,229,226,242,229,119,128, 5,192,232,244,225,232,229,
+ 226,242,229,119,128, 5,153,241,245,225,242,101,128, 51,169,244,
+ 225,104,134, 5,183,147,113,147,127,147,132,147,141,147,156,147,
+ 172, 49, 2,147,119,147,123, 49,128, 5,183,100,128, 5,183,178,
+ 97,128, 5,183,232,229,226,242,229,119,128, 5,183,238,225,242,
+ 242,239,247,232,229,226,242,229,119,128, 5,183,241,245,225,242,
+ 244,229,242,232,229,226,242,229,119,128, 5,183,247,233,228,229,
+ 232,229,226,242,229,119,128, 5,183,250,229,242,232,229,226,242,
+ 229,119,128, 5,161,226,239,240,239,237,239,230,111,128, 49, 6,
+ 227,233,242,227,236,101,128, 36,223,228,239,244,225,227,227,229,
+ 238,116,128, 30, 87,101,137, 5,228,147,251,148, 6,148, 26,148,
+ 38,148, 58,148,160,148,171,148,192,149,147,227,249,242,233,236,
+ 236,233, 99,128, 4, 63,228,225,231,229,243,104,129,251, 68,148,
+ 17,232,229,226,242,229,119,128,251, 68,229,250,233,243,241,245,
+ 225,242,101,128, 51, 59,230,233,238,225,236,228,225,231,229,243,
+ 232,232,229,226,242,229,119,128,251, 67,104, 5,148, 70,148, 93,
+ 148,101,148,115,148,145,225,114, 2,148, 77,148, 84,225,226,233,
+ 99,128, 6,126,237,229,238,233,225,110,128, 5,122,229,226,242,
+ 229,119,128, 5,228,230,233,238,225,236,225,242,225,226,233, 99,
+ 128,251, 87,105, 2,148,121,148,136,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,251, 88,242,225,231,225,238, 97,128, 48,
+ 122,237,229,228,233,225,236,225,242,225,226,233, 99,128,251, 89,
+ 235,225,244,225,235,225,238, 97,128, 48,218,237,233,228,228,236,
+ 229,232,239,239,235,227,249,242,233,236,236,233, 99,128, 4,167,
+ 114, 5,148,204,148,216,149, 2,149,123,149,136,225,230,229,232,
+ 229,226,242,229,119,128,251, 78,227,229,238,116,131, 0, 37,148,
+ 229,148,238,148,250,225,242,225,226,233, 99,128, 6,106,237,239,
+ 238,239,243,240,225,227,101,128,255, 5,243,237,225,236,108,128,
+ 254,106,105, 2,149, 8,149,105,239,100,134, 0, 46,149, 25,149,
+ 36,149, 47,149, 59,149, 70,149, 82,225,242,237,229,238,233,225,
+ 110,128, 5,137,227,229,238,244,229,242,229,100,128, 0,183,232,
+ 225,236,230,247,233,228,244,104,128,255, 97,233,238,230,229,242,
+ 233,239,114,128,246,231,237,239,238,239,243,240,225,227,101,128,
+ 255, 14,115, 2,149, 88,149, 95,237,225,236,108,128,254, 82,245,
+ 240,229,242,233,239,114,128,246,232,243,240,239,237,229,238,233,
+ 231,242,229,229,235,227,237, 98,128, 3, 66,240,229,238,228,233,
+ 227,245,236,225,114,128, 34,165,244,232,239,245,243,225,238,100,
+ 128, 32, 48,243,229,244, 97,128, 32,167,230,243,241,245,225,242,
+ 101,128, 51,138,104, 3,149,172,149,222,150,103, 97, 3,149,180,
+ 149,190,149,197,226,229,238,231,225,236,105,128, 9,171,228,229,
+ 246, 97,128, 9, 43,231,117, 2,149,204,149,213,234,225,242,225,
+ 244,105,128, 10,171,242,237,245,235,232,105,128, 10, 43,105,133,
+ 3,198,149,236,149,240,150, 70,150, 78,150, 89, 49,128, 3,213,
+ 229,245,240,104, 4,149,253,150, 32,150, 47,150, 56, 97, 2,150,
+ 3,150, 18,227,233,242,227,236,229,235,239,242,229,225,110,128,
+ 50,122,240,225,242,229,238,235,239,242,229,225,110,128, 50, 26,
+ 227,233,242,227,236,229,235,239,242,229,225,110,128, 50,108,235,
+ 239,242,229,225,110,128, 49, 77,240,225,242,229,238,235,239,242,
+ 229,225,110,128, 50, 12,236,225,244,233,110,128, 2,120,238,244,
+ 232,245,244,232,225,105,128, 14, 58,243,249,237,226,239,236,231,
+ 242,229,229,107,128, 3,213,111, 3,150,111,150,116,150,142,239,
+ 107,128, 1,165,240,104, 2,150,123,150,132,225,238,244,232,225,
+ 105,128, 14, 30,245,238,231,244,232,225,105,128, 14, 28,243,225,
+ 237,240,232,225,239,244,232,225,105,128, 14, 32,105,133, 3,192,
+ 150,170,151,126,151,137,151,148,151,162,229,245,112, 6,150,186,
+ 150,221,150,253,151, 25,151, 39,151, 91, 97, 2,150,192,150,207,
+ 227,233,242,227,236,229,235,239,242,229,225,110,128, 50,115,240,
+ 225,242,229,238,235,239,242,229,225,110,128, 50, 19,227,105, 2,
+ 150,228,150,240,229,245,227,235,239,242,229,225,110,128, 49,118,
+ 242,227,236,229,235,239,242,229,225,110,128, 50,101,107, 2,151,
+ 3,151, 17,233,249,229,239,235,235,239,242,229,225,110,128, 49,
+ 114,239,242,229,225,110,128, 49, 66,240,225,242,229,238,235,239,
+ 242,229,225,110,128, 50, 5,243,233,239,115, 2,151, 48,151, 76,
+ 107, 2,151, 54,151, 68,233,249,229,239,235,235,239,242,229,225,
+ 110,128, 49,116,239,242,229,225,110,128, 49, 68,244,233,235,229,
+ 245,244,235,239,242,229,225,110,128, 49,117,116, 2,151, 97,151,
+ 112,232,233,229,245,244,232,235,239,242,229,225,110,128, 49,119,
+ 233,235,229,245,244,235,239,242,229,225,110,128, 49,115,232,233,
+ 242,225,231,225,238, 97,128, 48,116,235,225,244,225,235,225,238,
+ 97,128, 48,212,243,249,237,226,239,236,231,242,229,229,107,128,
+ 3,214,247,242,225,242,237,229,238,233,225,110,128, 5,131,236,
+ 245,115,132, 0, 43,151,189,151,200,151,209,151,242,226,229,236,
+ 239,247,227,237, 98,128, 3, 31,227,233,242,227,236,101,128, 34,
+ 149,109, 2,151,215,151,222,233,238,245,115,128, 0,177,111, 2,
+ 151,228,151,232,100,128, 2,214,238,239,243,240,225,227,101,128,
+ 255, 11,115, 2,151,248,151,255,237,225,236,108,128,254, 98,245,
+ 240,229,242,233,239,114,128, 32,122,109, 2,152, 15,152, 26,239,
+ 238,239,243,240,225,227,101,128,255, 80,243,241,245,225,242,101,
+ 128, 51,216,111, 5,152, 47,152, 58,152,125,152,136,152,146,232,
+ 233,242,225,231,225,238, 97,128, 48,125,233,238,244,233,238,231,
+ 233,238,228,229,120, 4,152, 78,152, 90,152,102,152,115,228,239,
+ 247,238,247,232,233,244,101,128, 38, 31,236,229,230,244,247,232,
+ 233,244,101,128, 38, 28,242,233,231,232,244,247,232,233,244,101,
+ 128, 38, 30,245,240,247,232,233,244,101,128, 38, 29,235,225,244,
+ 225,235,225,238, 97,128, 48,221,240,236,225,244,232,225,105,128,
+ 14, 27,243,244,225,236,237,225,242,107,129, 48, 18,152,159,230,
+ 225,227,101,128, 48, 32,240,225,242,229,110,128, 36,171,114, 3,
+ 152,182,152,208,152,233,101, 2,152,188,152,196,227,229,228,229,
+ 115,128, 34,122,243,227,242,233,240,244,233,239,110,128, 33, 30,
+ 233,237,101, 2,152,216,152,222,237,239,100,128, 2,185,242,229,
+ 246,229,242,243,229,100,128, 32, 53,111, 4,152,243,152,250,153,
+ 4,153, 17,228,245,227,116,128, 34, 15,234,229,227,244,233,246,
+ 101,128, 35, 5,236,239,238,231,229,228,235,225,238, 97,128, 48,
+ 252,112, 2,153, 23,153, 60,101, 2,153, 29,153, 36,236,236,239,
+ 114,128, 35, 24,242,243,117, 2,153, 44,153, 51,226,243,229,116,
+ 128, 34,130,240,229,242,243,229,116,128, 34,131,239,242,244,233,
+ 239,110,129, 34, 55,153, 71,225,108,128, 34, 29,115, 2,153, 82,
+ 153,125,105,130, 3,200,153, 90,153,101,227,249,242,233,236,236,
+ 233, 99,128, 4,113,236,233,240,238,229,245,237,225,244,225,227,
+ 249,242,233,236,236,233,227,227,237, 98,128, 4,134,243,241,245,
+ 225,242,101,128, 51,176,117, 2,153,140,153,151,232,233,242,225,
+ 231,225,238, 97,128, 48,119,235,225,244,225,235,225,238, 97,128,
+ 48,215,246,243,241,245,225,242,101,128, 51,180,247,243,241,245,
+ 225,242,101,128, 51,186,113,136, 0,113,153,202,154,251,155, 6,
+ 155, 15,155, 22,155, 34,155, 72,155, 80, 97, 4,153,212,153,235,
+ 154, 43,154,234,100, 2,153,218,153,224,229,246, 97,128, 9, 88,
+ 237,225,232,229,226,242,229,119,128, 5,168,102, 4,153,245,153,
+ 254,154, 12,154, 28,225,242,225,226,233, 99,128, 6, 66,230,233,
+ 238,225,236,225,242,225,226,233, 99,128,254,214,233,238,233,244,
+ 233,225,236,225,242,225,226,233, 99,128,254,215,237,229,228,233,
+ 225,236,225,242,225,226,233, 99,128,254,216,237,225,244,115,136,
+ 5,184,154, 66,154, 86,154,100,154,105,154,110,154,119,154,134,
+ 154,221, 49, 3,154, 74,154, 78,154, 82, 48,128, 5,184, 97,128,
+ 5,184, 99,128, 5,184, 50, 2,154, 92,154, 96, 55,128, 5,184,
+ 57,128, 5,184,179, 51,128, 5,184,228,101,128, 5,184,232,229,
+ 226,242,229,119,128, 5,184,238,225,242,242,239,247,232,229,226,
+ 242,229,119,128, 5,184,113, 2,154,140,154,206,225,244,225,110,
+ 4,154,153,154,162,154,177,154,193,232,229,226,242,229,119,128,
+ 5,184,238,225,242,242,239,247,232,229,226,242,229,119,128, 5,
+ 184,241,245,225,242,244,229,242,232,229,226,242,229,119,128, 5,
+ 184,247,233,228,229,232,229,226,242,229,119,128, 5,184,245,225,
+ 242,244,229,242,232,229,226,242,229,119,128, 5,184,247,233,228,
+ 229,232,229,226,242,229,119,128, 5,184,242,238,229,249,240,225,
+ 242,225,232,229,226,242,229,119,128, 5,159,226,239,240,239,237,
+ 239,230,111,128, 49, 17,227,233,242,227,236,101,128, 36,224,232,
+ 239,239,107,128, 2,160,237,239,238,239,243,240,225,227,101,128,
+ 255, 81,239,102,130, 5,231,155, 43,155, 63,228,225,231,229,243,
+ 104,129,251, 71,155, 54,232,229,226,242,229,119,128,251, 71,232,
+ 229,226,242,229,119,128, 5,231,240,225,242,229,110,128, 36,172,
+ 117, 4,155, 90,155,102,155,191,156, 22,225,242,244,229,242,238,
+ 239,244,101,128, 38,105,226,245,244,115,135, 5,187,155,123,155,
+ 128,155,133,155,138,155,147,155,162,155,178,177, 56,128, 5,187,
+ 178, 53,128, 5,187,179, 49,128, 5,187,232,229,226,242,229,119,
+ 128, 5,187,238,225,242,242,239,247,232,229,226,242,229,119,128,
+ 5,187,241,245,225,242,244,229,242,232,229,226,242,229,119,128,
+ 5,187,247,233,228,229,232,229,226,242,229,119,128, 5,187,229,
+ 243,244,233,239,110,133, 0, 63,155,210,155,233,155,250,156, 2,
+ 156, 14,225,114, 2,155,217,155,224,225,226,233, 99,128, 6, 31,
+ 237,229,238,233,225,110,128, 5, 94,228,239,247,110,129, 0,191,
+ 155,242,243,237,225,236,108,128,247,191,231,242,229,229,107,128,
+ 3,126,237,239,238,239,243,240,225,227,101,128,255, 31,243,237,
+ 225,236,108,128,247, 63,239,244,101, 4,156, 34,156,105,156,125,
+ 156,154,228,226,108,133, 0, 34,156, 50,156, 57,156, 64,156, 76,
+ 156, 97,226,225,243,101,128, 32, 30,236,229,230,116,128, 32, 28,
+ 237,239,238,239,243,240,225,227,101,128,255, 2,240,242,233,237,
+ 101,129, 48, 30,156, 86,242,229,246,229,242,243,229,100,128, 48,
+ 29,242,233,231,232,116,128, 32, 29,236,229,230,116,129, 32, 24,
+ 156,114,242,229,246,229,242,243,229,100,128, 32, 27,114, 2,156,
+ 131,156,141,229,246,229,242,243,229,100,128, 32, 27,233,231,232,
+ 116,129, 32, 25,156,150,110,128, 1, 73,243,233,238,231,108, 2,
+ 156,164,156,171,226,225,243,101,128, 32, 26,101,129, 0, 39,156,
+ 177,237,239,238,239,243,240,225,227,101,128,255, 7,114,145, 0,
+ 114,156,227,157,231,157,242,158, 33,158, 84,159,101,159,125,159,
+ 220,161,254,162, 35,162, 47,162,101,162,109,163, 15,163, 26,163,
+ 61,163,161, 97, 11,156,251,157, 6,157, 16,157, 23,157, 88,157,
+ 104,157,129,157,140,157,165,157,188,157,225,225,242,237,229,238,
+ 233,225,110,128, 5,124,226,229,238,231,225,236,105,128, 9,176,
+ 227,245,244,101,128, 1, 85,100, 4,157, 33,157, 39,157, 53,157,
+ 79,229,246, 97,128, 9, 48,233,227,225,108,129, 34, 26,157, 48,
+ 229,120,128,248,229,239,246,229,242,243,243,241,245,225,242,101,
+ 129, 51,174,157, 69,228,243,241,245,225,242,101,128, 51,175,243,
+ 241,245,225,242,101,128, 51,173,230,101,129, 5,191,157, 95,232,
+ 229,226,242,229,119,128, 5,191,231,117, 2,157,111,157,120,234,
+ 225,242,225,244,105,128, 10,176,242,237,245,235,232,105,128, 10,
+ 48,232,233,242,225,231,225,238, 97,128, 48,137,235,225,244,225,
+ 235,225,238, 97,129, 48,233,157,153,232,225,236,230,247,233,228,
+ 244,104,128,255,151,236,239,247,229,242,228,233,225,231,239,238,
+ 225,236,226,229,238,231,225,236,105,128, 9,241,109, 2,157,194,
+ 157,217,233,228,228,236,229,228,233,225,231,239,238,225,236,226,
+ 229,238,231,225,236,105,128, 9,240,243,232,239,242,110,128, 2,
+ 100,244,233,111,128, 34, 54,226,239,240,239,237,239,230,111,128,
+ 49, 22, 99, 4,157,252,158, 3,158, 12,158, 20,225,242,239,110,
+ 128, 1, 89,229,228,233,236,236, 97,128, 1, 87,233,242,227,236,
+ 101,128, 36,225,239,237,237,225,225,227,227,229,238,116,128, 1,
+ 87,100, 2,158, 39,158, 49,226,236,231,242,225,246,101,128, 2,
+ 17,239,116, 2,158, 56,158, 65,225,227,227,229,238,116,128, 30,
+ 89,226,229,236,239,119,129, 30, 91,158, 75,237,225,227,242,239,
+ 110,128, 30, 93,101, 6,158, 98,158,143,158,178,158,233,159, 2,
+ 159, 35,102, 2,158,104,158,117,229,242,229,238,227,229,237,225,
+ 242,107,128, 32, 59,236,229,248,243,117, 2,158,127,158,134,226,
+ 243,229,116,128, 34,134,240,229,242,243,229,116,128, 34,135,231,
+ 233,243,244,229,114, 2,158,154,158,159,229,100,128, 0,174,115,
+ 2,158,165,158,171,225,238,115,128,248,232,229,242,233,102,128,
+ 246,218,104, 3,158,186,158,209,158,223,225,114, 2,158,193,158,
+ 200,225,226,233, 99,128, 6, 49,237,229,238,233,225,110,128, 5,
+ 128,230,233,238,225,236,225,242,225,226,233, 99,128,254,174,233,
+ 242,225,231,225,238, 97,128, 48,140,235,225,244,225,235,225,238,
+ 97,129, 48,236,158,246,232,225,236,230,247,233,228,244,104,128,
+ 255,154,243,104,130, 5,232,159, 11,159, 26,228,225,231,229,243,
+ 232,232,229,226,242,229,119,128,251, 72,232,229,226,242,229,119,
+ 128, 5,232,118, 3,159, 43,159, 56,159, 88,229,242,243,229,228,
+ 244,233,236,228,101,128, 34, 61,233, 97, 2,159, 63,159, 72,232,
+ 229,226,242,229,119,128, 5,151,237,245,231,242,225,243,232,232,
+ 229,226,242,229,119,128, 5,151,236,239,231,233,227,225,236,238,
+ 239,116,128, 35, 16,230,233,243,232,232,239,239,107,129, 2,126,
+ 159,114,242,229,246,229,242,243,229,100,128, 2,127,104, 2,159,
+ 131,159,154, 97, 2,159,137,159,147,226,229,238,231,225,236,105,
+ 128, 9,221,228,229,246, 97,128, 9, 93,111,131, 3,193,159,164,
+ 159,193,159,207,239,107,129, 2,125,159,171,244,245,242,238,229,
+ 100,129, 2,123,159,182,243,245,240,229,242,233,239,114,128, 2,
+ 181,243,249,237,226,239,236,231,242,229,229,107,128, 3,241,244,
+ 233,227,232,239,239,235,237,239,100,128, 2,222,105, 6,159,234,
+ 161, 22,161, 68,161, 79,161,104,161,240,229,245,108, 9,160, 0,
+ 160, 35,160, 50,160, 64,160,110,160,124,160,210,160,223,161, 2,
+ 97, 2,160, 6,160, 21,227,233,242,227,236,229,235,239,242,229,
+ 225,110,128, 50,113,240,225,242,229,238,235,239,242,229,225,110,
+ 128, 50, 17,227,233,242,227,236,229,235,239,242,229,225,110,128,
+ 50, 99,232,233,229,245,232,235,239,242,229,225,110,128, 49, 64,
+ 107, 2,160, 70,160,102,233,249,229,239,107, 2,160, 80,160, 89,
+ 235,239,242,229,225,110,128, 49, 58,243,233,239,243,235,239,242,
+ 229,225,110,128, 49,105,239,242,229,225,110,128, 49, 57,237,233,
+ 229,245,237,235,239,242,229,225,110,128, 49, 59,112, 3,160,132,
+ 160,164,160,179, 97, 2,160,138,160,152,238,243,233,239,243,235,
+ 239,242,229,225,110,128, 49,108,242,229,238,235,239,242,229,225,
+ 110,128, 50, 3,232,233,229,245,240,232,235,239,242,229,225,110,
+ 128, 49, 63,233,229,245,112, 2,160,188,160,197,235,239,242,229,
+ 225,110,128, 49, 60,243,233,239,243,235,239,242,229,225,110,128,
+ 49,107,243,233,239,243,235,239,242,229,225,110,128, 49, 61,116,
+ 2,160,229,160,244,232,233,229,245,244,232,235,239,242,229,225,
+ 110,128, 49, 62,233,235,229,245,244,235,239,242,229,225,110,128,
+ 49,106,249,229,239,242,233,238,232,233,229,245,232,235,239,242,
+ 229,225,110,128, 49,109,231,232,116, 2,161, 30,161, 38,225,238,
+ 231,236,101,128, 34, 31,116, 2,161, 44,161, 58,225,227,235,226,
+ 229,236,239,247,227,237, 98,128, 3, 25,242,233,225,238,231,236,
+ 101,128, 34,191,232,233,242,225,231,225,238, 97,128, 48,138,235,
+ 225,244,225,235,225,238, 97,129, 48,234,161, 92,232,225,236,230,
+ 247,233,228,244,104,128,255,152,110, 2,161,110,161,226,103,131,
+ 2,218,161,120,161,131,161,137,226,229,236,239,247,227,237, 98,
+ 128, 3, 37,227,237, 98,128, 3, 10,232,225,236,102, 2,161,146,
+ 161,192,236,229,230,116,131, 2,191,161,159,161,170,161,181,225,
+ 242,237,229,238,233,225,110,128, 5, 89,226,229,236,239,247,227,
+ 237, 98,128, 3, 28,227,229,238,244,229,242,229,100,128, 2,211,
+ 242,233,231,232,116,130, 2,190,161,204,161,215,226,229,236,239,
+ 247,227,237, 98,128, 3, 57,227,229,238,244,229,242,229,100,128,
+ 2,210,246,229,242,244,229,228,226,242,229,246,101,128, 2, 19,
+ 244,244,239,242,245,243,241,245,225,242,101,128, 51, 81,108, 2,
+ 162, 4,162, 15,233,238,229,226,229,236,239,119,128, 30, 95,239,
+ 238,231,236,229,103,129, 2,124,162, 26,244,245,242,238,229,100,
+ 128, 2,122,237,239,238,239,243,240,225,227,101,128,255, 82,111,
+ 3,162, 55,162, 66,162, 91,232,233,242,225,231,225,238, 97,128,
+ 48,141,235,225,244,225,235,225,238, 97,129, 48,237,162, 79,232,
+ 225,236,230,247,233,228,244,104,128,255,155,242,245,225,244,232,
+ 225,105,128, 14, 35,240,225,242,229,110,128, 36,173,114, 3,162,
+ 117,162,153,162,183, 97, 3,162,125,162,135,162,142,226,229,238,
+ 231,225,236,105,128, 9,220,228,229,246, 97,128, 9, 49,231,245,
+ 242,237,245,235,232,105,128, 10, 92,229,104, 2,162,160,162,169,
+ 225,242,225,226,233, 99,128, 6,145,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,251,141,246,239,227,225,236,233, 99, 4,162,
+ 199,162,209,162,216,162,227,226,229,238,231,225,236,105,128, 9,
+ 224,228,229,246, 97,128, 9, 96,231,245,234,225,242,225,244,105,
+ 128, 10,224,246,239,247,229,236,243,233,231,110, 3,162,243,162,
+ 253,163, 4,226,229,238,231,225,236,105,128, 9,196,228,229,246,
+ 97,128, 9, 68,231,245,234,225,242,225,244,105,128, 10,196,243,
+ 245,240,229,242,233,239,114,128,246,241,116, 2,163, 32,163, 40,
+ 226,236,239,227,107,128, 37,144,245,242,238,229,100,129, 2,121,
+ 163, 50,243,245,240,229,242,233,239,114,128, 2,180,117, 4,163,
+ 71,163, 82,163,107,163,154,232,233,242,225,231,225,238, 97,128,
+ 48,139,235,225,244,225,235,225,238, 97,129, 48,235,163, 95,232,
+ 225,236,230,247,233,228,244,104,128,255,153,112, 2,163,113,163,
+ 148,229,101, 2,163,120,163,134,237,225,242,235,226,229,238,231,
+ 225,236,105,128, 9,242,243,233,231,238,226,229,238,231,225,236,
+ 105,128, 9,243,233,225,104,128,246,221,244,232,225,105,128, 14,
+ 36,246,239,227,225,236,233, 99, 4,163,177,163,187,163,194,163,
+ 205,226,229,238,231,225,236,105,128, 9,139,228,229,246, 97,128,
+ 9, 11,231,245,234,225,242,225,244,105,128, 10,139,246,239,247,
+ 229,236,243,233,231,110, 3,163,221,163,231,163,238,226,229,238,
+ 231,225,236,105,128, 9,195,228,229,246, 97,128, 9, 67,231,245,
+ 234,225,242,225,244,105,128, 10,195,115,147, 0,115,164, 35,166,
+ 5,166, 16,166,142,166,181,169,123,169,134,172, 21,174,159,174,
+ 205,174,232,175,167,175,234,177, 11,177, 21,177,207,178, 24,178,
+ 194,178,204, 97, 9,164, 55,164, 65,164, 86,164,158,164,183,164,
+ 194,164,219,164,251,165, 35,226,229,238,231,225,236,105,128, 9,
+ 184,227,245,244,101,129, 1, 91,164, 74,228,239,244,225,227,227,
+ 229,238,116,128, 30,101,100, 5,164, 98,164,107,164,113,164,127,
+ 164,143,225,242,225,226,233, 99,128, 6, 53,229,246, 97,128, 9,
+ 56,230,233,238,225,236,225,242,225,226,233, 99,128,254,186,233,
+ 238,233,244,233,225,236,225,242,225,226,233, 99,128,254,187,237,
+ 229,228,233,225,236,225,242,225,226,233, 99,128,254,188,231,117,
+ 2,164,165,164,174,234,225,242,225,244,105,128, 10,184,242,237,
+ 245,235,232,105,128, 10, 56,232,233,242,225,231,225,238, 97,128,
+ 48, 85,235,225,244,225,235,225,238, 97,129, 48,181,164,207,232,
+ 225,236,230,247,233,228,244,104,128,255,123,236,236,225,236,236,
+ 225,232,239,245,225,236,225,249,232,229,247,225,243,225,236,236,
+ 225,237,225,242,225,226,233, 99,128,253,250,237,229,235,104,130,
+ 5,225,165, 6,165, 26,228,225,231,229,243,104,129,251, 65,165,
+ 17,232,229,226,242,229,119,128,251, 65,232,229,226,242,229,119,
+ 128, 5,225,242, 97, 5,165, 48,165,122,165,130,165,180,165,188,
+ 97, 5,165, 60,165, 68,165, 76,165,107,165,115,225,244,232,225,
+ 105,128, 14, 50,229,244,232,225,105,128, 14, 65,233,237,225,233,
+ 109, 2,165, 86,165, 97,225,236,225,233,244,232,225,105,128, 14,
+ 68,245,225,238,244,232,225,105,128, 14, 67,237,244,232,225,105,
+ 128, 14, 51,244,232,225,105,128, 14, 48,229,244,232,225,105,128,
+ 14, 64,105, 3,165,138,165,162,165,173,105, 2,165,144,165,155,
+ 236,229,230,244,244,232,225,105,128,248,134,244,232,225,105,128,
+ 14, 53,236,229,230,244,244,232,225,105,128,248,133,244,232,225,
+ 105,128, 14, 52,239,244,232,225,105,128, 14, 66,117, 3,165,196,
+ 165,246,165,253,101, 3,165,204,165,228,165,239,101, 2,165,210,
+ 165,221,236,229,230,244,244,232,225,105,128,248,136,244,232,225,
+ 105,128, 14, 55,236,229,230,244,244,232,225,105,128,248,135,244,
+ 232,225,105,128, 14, 54,244,232,225,105,128, 14, 56,245,244,232,
+ 225,105,128, 14, 57,226,239,240,239,237,239,230,111,128, 49, 25,
+ 99, 5,166, 28,166, 49,166, 58,166,107,166,129,225,242,239,110,
+ 129, 1, 97,166, 37,228,239,244,225,227,227,229,238,116,128, 30,
+ 103,229,228,233,236,236, 97,128, 1, 95,232,247, 97,131, 2, 89,
+ 166, 70,166, 81,166,100,227,249,242,233,236,236,233, 99,128, 4,
+ 217,228,233,229,242,229,243,233,243,227,249,242,233,236,236,233,
+ 99,128, 4,219,232,239,239,107,128, 2, 90,233,242, 99, 2,166,
+ 115,166,120,236,101,128, 36,226,245,237,230,236,229,120,128, 1,
+ 93,239,237,237,225,225,227,227,229,238,116,128, 2, 25,228,239,
+ 116, 2,166,150,166,159,225,227,227,229,238,116,128, 30, 97,226,
+ 229,236,239,119,129, 30, 99,166,169,228,239,244,225,227,227,229,
+ 238,116,128, 30,105,101, 9,166,201,166,217,166,252,167, 61,167,
+ 164,167,191,167,216,168, 41,168, 68,225,231,245,236,236,226,229,
+ 236,239,247,227,237, 98,128, 3, 60, 99, 2,166,223,166,245,239,
+ 238,100,129, 32, 51,166,231,244,239,238,229,227,232,233,238,229,
+ 243,101,128, 2,202,244,233,239,110,128, 0,167,229,110, 4,167,
+ 7,167, 16,167, 30,167, 46,225,242,225,226,233, 99,128, 6, 51,
+ 230,233,238,225,236,225,242,225,226,233, 99,128,254,178,233,238,
+ 233,244,233,225,236,225,242,225,226,233, 99,128,254,179,237,229,
+ 228,233,225,236,225,242,225,226,233, 99,128,254,180,231,239,108,
+ 135, 5,182,167, 81,167, 95,167,100,167,109,167,124,167,140,167,
+ 151, 49, 2,167, 87,167, 91, 51,128, 5,182,102,128, 5,182,178,
+ 99,128, 5,182,232,229,226,242,229,119,128, 5,182,238,225,242,
+ 242,239,247,232,229,226,242,229,119,128, 5,182,241,245,225,242,
+ 244,229,242,232,229,226,242,229,119,128, 5,182,244,225,232,229,
+ 226,242,229,119,128, 5,146,247,233,228,229,232,229,226,242,229,
+ 119,128, 5,182,104, 2,167,170,167,181,225,242,237,229,238,233,
+ 225,110,128, 5,125,233,242,225,231,225,238, 97,128, 48, 91,235,
+ 225,244,225,235,225,238, 97,129, 48,187,167,204,232,225,236,230,
+ 247,233,228,244,104,128,255,126,237,105, 2,167,223,168, 10,227,
+ 239,236,239,110,131, 0, 59,167,237,167,246,168, 2,225,242,225,
+ 226,233, 99,128, 6, 27,237,239,238,239,243,240,225,227,101,128,
+ 255, 27,243,237,225,236,108,128,254, 84,246,239,233,227,229,228,
+ 237,225,242,235,235,225,238, 97,129, 48,156,168, 29,232,225,236,
+ 230,247,233,228,244,104,128,255,159,238,116, 2,168, 48,168, 58,
+ 233,243,241,245,225,242,101,128, 51, 34,239,243,241,245,225,242,
+ 101,128, 51, 35,246,229,110,142, 0, 55,168,102,168,111,168,121,
+ 168,151,168,158,168,168,168,193,168,220,168,254,169, 10,169, 21,
+ 169, 54,169, 62,169, 73,225,242,225,226,233, 99,128, 6,103,226,
+ 229,238,231,225,236,105,128, 9,237,227,233,242,227,236,101,129,
+ 36,102,168,132,233,238,246,229,242,243,229,243,225,238,243,243,
+ 229,242,233,102,128, 39,144,228,229,246, 97,128, 9,109,229,233,
+ 231,232,244,232,115,128, 33, 94,231,117, 2,168,175,168,184,234,
+ 225,242,225,244,105,128, 10,237,242,237,245,235,232,105,128, 10,
+ 109,232, 97, 2,168,200,168,211,227,235,225,242,225,226,233, 99,
+ 128, 6,103,238,231,250,232,239,117,128, 48, 39,105, 2,168,226,
+ 168,244,228,229,239,231,242,225,240,232,233,227,240,225,242,229,
+ 110,128, 50, 38,238,230,229,242,233,239,114,128, 32,135,237,239,
+ 238,239,243,240,225,227,101,128,255, 23,239,236,228,243,244,249,
+ 236,101,128,247, 55,112, 2,169, 27,169, 34,225,242,229,110,128,
+ 36,122,229,114, 2,169, 41,169, 47,233,239,100,128, 36,142,243,
+ 233,225,110,128, 6,247,242,239,237,225,110,128, 33,118,243,245,
+ 240,229,242,233,239,114,128, 32,119,116, 2,169, 79,169,117,229,
+ 229,110, 2,169, 87,169, 96,227,233,242,227,236,101,128, 36,112,
+ 112, 2,169,102,169,109,225,242,229,110,128, 36,132,229,242,233,
+ 239,100,128, 36,152,232,225,105,128, 14, 87,230,244,232,249,240,
+ 232,229,110,128, 0,173,104, 7,169,150,170,124,170,135,170,149,
+ 171, 94,171,107,172, 15, 97, 6,169,164,169,175,169,185,169,196,
+ 170, 83,170,108,225,242,237,229,238,233,225,110,128, 5,119,226,
+ 229,238,231,225,236,105,128, 9,182,227,249,242,233,236,236,233,
+ 99,128, 4, 72,100, 2,169,202,170, 42,228, 97, 4,169,213,169,
+ 222,169,253,170, 11,225,242,225,226,233, 99,128, 6, 81,228,225,
+ 237,237, 97, 2,169,232,169,241,225,242,225,226,233, 99,128,252,
+ 97,244,225,238,225,242,225,226,233, 99,128,252, 94,230,225,244,
+ 232,225,225,242,225,226,233, 99,128,252, 96,235,225,243,242, 97,
+ 2,170, 21,170, 30,225,242,225,226,233, 99,128,252, 98,244,225,
+ 238,225,242,225,226,233, 99,128,252, 95,101,132, 37,146,170, 54,
+ 170, 61,170, 69,170, 78,228,225,242,107,128, 37,147,236,233,231,
+ 232,116,128, 37,145,237,229,228,233,245,109,128, 37,146,246, 97,
+ 128, 9, 54,231,117, 2,170, 90,170, 99,234,225,242,225,244,105,
+ 128, 10,182,242,237,245,235,232,105,128, 10, 54,236,243,232,229,
+ 236,229,244,232,229,226,242,229,119,128, 5,147,226,239,240,239,
+ 237,239,230,111,128, 49, 21,227,232,225,227,249,242,233,236,236,
+ 233, 99,128, 4, 73,101, 4,170,159,170,224,170,234,170,251,229,
+ 110, 4,170,170,170,179,170,193,170,209,225,242,225,226,233, 99,
+ 128, 6, 52,230,233,238,225,236,225,242,225,226,233, 99,128,254,
+ 182,233,238,233,244,233,225,236,225,242,225,226,233, 99,128,254,
+ 183,237,229,228,233,225,236,225,242,225,226,233, 99,128,254,184,
+ 233,227,239,240,244,233, 99,128, 3,227,241,229,108,129, 32,170,
+ 170,242,232,229,226,242,229,119,128, 32,170,246, 97,134, 5,176,
+ 171, 12,171, 27,171, 41,171, 50,171, 65,171, 81, 49, 2,171, 18,
+ 171, 23,177, 53,128, 5,176, 53,128, 5,176, 50, 2,171, 33,171,
+ 37, 50,128, 5,176,101,128, 5,176,232,229,226,242,229,119,128,
+ 5,176,238,225,242,242,239,247,232,229,226,242,229,119,128, 5,
+ 176,241,245,225,242,244,229,242,232,229,226,242,229,119,128, 5,
+ 176,247,233,228,229,232,229,226,242,229,119,128, 5,176,232,225,
+ 227,249,242,233,236,236,233, 99,128, 4,187,105, 2,171,113,171,
+ 124,237,225,227,239,240,244,233, 99,128, 3,237,110,131, 5,233,
+ 171,134,171,217,171,226,100, 2,171,140,171,206,225,231,229,243,
+ 104,130,251, 73,171,152,171,161,232,229,226,242,229,119,128,251,
+ 73,115, 2,171,167,171,187,232,233,238,228,239,116,129,251, 44,
+ 171,178,232,229,226,242,229,119,128,251, 44,233,238,228,239,116,
+ 129,251, 45,171,197,232,229,226,242,229,119,128,251, 45,239,244,
+ 232,229,226,242,229,119,128, 5,193,232,229,226,242,229,119,128,
+ 5,233,115, 2,171,232,171,252,232,233,238,228,239,116,129,251,
+ 42,171,243,232,229,226,242,229,119,128,251, 42,233,238,228,239,
+ 116,129,251, 43,172, 6,232,229,226,242,229,119,128,251, 43,239,
+ 239,107,128, 2,130,105, 8,172, 39,172, 83,172, 94,172,119,172,
+ 149,172,157,172,170,173, 85,231,237, 97,131, 3,195,172, 51,172,
+ 55,172, 63, 49,128, 3,194,230,233,238,225,108,128, 3,194,236,
+ 245,238,225,244,229,243,249,237,226,239,236,231,242,229,229,107,
+ 128, 3,242,232,233,242,225,231,225,238, 97,128, 48, 87,235,225,
+ 244,225,235,225,238, 97,129, 48,183,172,107,232,225,236,230,247,
+ 233,228,244,104,128,255,124,236,245,113, 2,172,127,172,136,232,
+ 229,226,242,229,119,128, 5,189,236,229,230,244,232,229,226,242,
+ 229,119,128, 5,189,237,233,236,225,114,128, 34, 60,238,228,239,
+ 244,232,229,226,242,229,119,128, 5,194,239,115, 6,172,185,172,
+ 220,172,252,173, 24,173, 38,173, 70, 97, 2,172,191,172,206,227,
+ 233,242,227,236,229,235,239,242,229,225,110,128, 50,116,240,225,
+ 242,229,238,235,239,242,229,225,110,128, 50, 20,227,105, 2,172,
+ 227,172,239,229,245,227,235,239,242,229,225,110,128, 49,126,242,
+ 227,236,229,235,239,242,229,225,110,128, 50,102,107, 2,173, 2,
+ 173, 16,233,249,229,239,235,235,239,242,229,225,110,128, 49,122,
+ 239,242,229,225,110,128, 49, 69,238,233,229,245,238,235,239,242,
+ 229,225,110,128, 49,123,112, 2,173, 44,173, 57,225,242,229,238,
+ 235,239,242,229,225,110,128, 50, 6,233,229,245,240,235,239,242,
+ 229,225,110,128, 49,125,244,233,235,229,245,244,235,239,242,229,
+ 225,110,128, 49,124,120,141, 0, 54,173,115,173,124,173,134,173,
+ 164,173,171,173,196,173,223,174, 1,174, 13,174, 24,174, 57,174,
+ 65,174, 76,225,242,225,226,233, 99,128, 6,102,226,229,238,231,
+ 225,236,105,128, 9,236,227,233,242,227,236,101,129, 36,101,173,
+ 145,233,238,246,229,242,243,229,243,225,238,243,243,229,242,233,
+ 102,128, 39,143,228,229,246, 97,128, 9,108,231,117, 2,173,178,
+ 173,187,234,225,242,225,244,105,128, 10,236,242,237,245,235,232,
+ 105,128, 10,108,232, 97, 2,173,203,173,214,227,235,225,242,225,
+ 226,233, 99,128, 6,102,238,231,250,232,239,117,128, 48, 38,105,
+ 2,173,229,173,247,228,229,239,231,242,225,240,232,233,227,240,
+ 225,242,229,110,128, 50, 37,238,230,229,242,233,239,114,128, 32,
+ 134,237,239,238,239,243,240,225,227,101,128,255, 22,239,236,228,
+ 243,244,249,236,101,128,247, 54,112, 2,174, 30,174, 37,225,242,
+ 229,110,128, 36,121,229,114, 2,174, 44,174, 50,233,239,100,128,
+ 36,141,243,233,225,110,128, 6,246,242,239,237,225,110,128, 33,
+ 117,243,245,240,229,242,233,239,114,128, 32,118,116, 2,174, 82,
+ 174,153,229,229,110, 2,174, 90,174,132, 99, 2,174, 96,174,104,
+ 233,242,227,236,101,128, 36,111,245,242,242,229,238,227,249,228,
+ 229,238,239,237,233,238,225,244,239,242,226,229,238,231,225,236,
+ 105,128, 9,249,112, 2,174,138,174,145,225,242,229,110,128, 36,
+ 131,229,242,233,239,100,128, 36,151,232,225,105,128, 14, 86,108,
+ 2,174,165,174,185,225,243,104,129, 0, 47,174,173,237,239,238,
+ 239,243,240,225,227,101,128,255, 15,239,238,103,129, 1,127,174,
+ 193,228,239,244,225,227,227,229,238,116,128, 30,155,109, 2,174,
+ 211,174,221,233,236,229,230,225,227,101,128, 38, 58,239,238,239,
+ 243,240,225,227,101,128,255, 83,111, 6,174,246,175, 40,175, 51,
+ 175, 76,175,121,175,132,102, 2,174,252,175, 10,240,225,243,245,
+ 241,232,229,226,242,229,119,128, 5,195,116, 2,175, 16,175, 25,
+ 232,249,240,232,229,110,128, 0,173,243,233,231,238,227,249,242,
+ 233,236,236,233, 99,128, 4, 76,232,233,242,225,231,225,238, 97,
+ 128, 48, 93,235,225,244,225,235,225,238, 97,129, 48,189,175, 64,
+ 232,225,236,230,247,233,228,244,104,128,255,127,236,233,228,245,
+ 115, 2,175, 86,175,103,236,239,238,231,239,246,229,242,236,225,
+ 249,227,237, 98,128, 3, 56,243,232,239,242,244,239,246,229,242,
+ 236,225,249,227,237, 98,128, 3, 55,242,245,243,233,244,232,225,
+ 105,128, 14, 41,115, 3,175,140,175,150,175,158,225,236,225,244,
+ 232,225,105,128, 14, 40,239,244,232,225,105,128, 14, 11,245,225,
+ 244,232,225,105,128, 14, 42,240, 97, 3,175,176,175,196,175,228,
+ 227,101,129, 0, 32,175,183,232,225,227,235,225,242,225,226,233,
+ 99,128, 0, 32,228,101,129, 38, 96,175,203,243,245,233,116, 2,
+ 175,212,175,220,226,236,225,227,107,128, 38, 96,247,232,233,244,
+ 101,128, 38,100,242,229,110,128, 36,174,241,245,225,242,101, 11,
+ 176, 6,176, 17,176, 31,176, 56,176, 73,176, 99,176,114,176,147,
+ 176,174,176,230,176,245,226,229,236,239,247,227,237, 98,128, 3,
+ 59, 99, 2,176, 23,176, 27, 99,128, 51,196,109,128, 51,157,228,
+ 233,225,231,239,238,225,236,227,242,239,243,243,232,225,244,227,
+ 232,230,233,236,108,128, 37,169,232,239,242,233,250,239,238,244,
+ 225,236,230,233,236,108,128, 37,164,107, 2,176, 79,176, 83,103,
+ 128, 51,143,109,129, 51,158,176, 89,227,225,240,233,244,225,108,
+ 128, 51,206,108, 2,176,105,176,109,110,128, 51,209,239,103,128,
+ 51,210,109, 4,176,124,176,128,176,133,176,137,103,128, 51,142,
+ 233,108,128, 51,213,109,128, 51,156,243,241,245,225,242,229,100,
+ 128, 51,161,239,242,244,232,239,231,239,238,225,236,227,242,239,
+ 243,243,232,225,244,227,232,230,233,236,108,128, 37,166,245,240,
+ 240,229,114, 2,176,184,176,207,236,229,230,244,244,239,236,239,
+ 247,229,242,242,233,231,232,244,230,233,236,108,128, 37,167,242,
+ 233,231,232,244,244,239,236,239,247,229,242,236,229,230,244,230,
+ 233,236,108,128, 37,168,246,229,242,244,233,227,225,236,230,233,
+ 236,108,128, 37,165,247,232,233,244,229,247,233,244,232,243,237,
+ 225,236,236,226,236,225,227,107,128, 37,163,242,243,241,245,225,
+ 242,101,128, 51,219,115, 2,177, 27,177,197, 97, 4,177, 37,177,
+ 47,177, 54,177, 65,226,229,238,231,225,236,105,128, 9,183,228,
+ 229,246, 97,128, 9, 55,231,245,234,225,242,225,244,105,128, 10,
+ 183,238,103, 8,177, 84,177, 98,177,112,177,126,177,141,177,155,
+ 177,169,177,182,227,233,229,245,227,235,239,242,229,225,110,128,
+ 49, 73,232,233,229,245,232,235,239,242,229,225,110,128, 49,133,
+ 233,229,245,238,231,235,239,242,229,225,110,128, 49,128,235,233,
+ 249,229,239,235,235,239,242,229,225,110,128, 49, 50,238,233,229,
+ 245,238,235,239,242,229,225,110,128, 49,101,240,233,229,245,240,
+ 235,239,242,229,225,110,128, 49, 67,243,233,239,243,235,239,242,
+ 229,225,110,128, 49, 70,244,233,235,229,245,244,235,239,242,229,
+ 225,110,128, 49, 56,245,240,229,242,233,239,114,128,246,242,116,
+ 2,177,213,177,236,229,242,236,233,238,103,129, 0,163,177,224,
+ 237,239,238,239,243,240,225,227,101,128,255,225,242,239,235,101,
+ 2,177,245,178, 6,236,239,238,231,239,246,229,242,236,225,249,
+ 227,237, 98,128, 3, 54,243,232,239,242,244,239,246,229,242,236,
+ 225,249,227,237, 98,128, 3, 53,117, 7,178, 40,178, 72,178, 94,
+ 178,105,178,146,178,156,178,160,226,243,229,116,130, 34,130,178,
+ 51,178, 62,238,239,244,229,241,245,225,108,128, 34,138,239,242,
+ 229,241,245,225,108,128, 34,134, 99, 2,178, 78,178, 86,227,229,
+ 229,228,115,128, 34,123,232,244,232,225,116,128, 34, 11,232,233,
+ 242,225,231,225,238, 97,128, 48, 89,107, 2,178,111,178,135,225,
+ 244,225,235,225,238, 97,129, 48,185,178,123,232,225,236,230,247,
+ 233,228,244,104,128,255,125,245,238,225,242,225,226,233, 99,128,
+ 6, 82,237,237,225,244,233,239,110,128, 34, 17,110,128, 38, 60,
+ 240,229,242,243,229,116,130, 34,131,178,173,178,184,238,239,244,
+ 229,241,245,225,108,128, 34,139,239,242,229,241,245,225,108,128,
+ 34,135,246,243,241,245,225,242,101,128, 51,220,249,239,245,247,
+ 225,229,242,225,243,241,245,225,242,101,128, 51,124,116,144, 0,
+ 116,179, 1,180, 10,180, 31,180,174,180,214,183, 6,186,144,187,
+ 219,187,231,187,243,189, 20,189, 45,189,131,190, 55,190,239,191,
+ 73, 97, 10,179, 23,179, 33,179, 54,179, 61,179, 86,179,164,179,
+ 181,179,206,179,220,179,224,226,229,238,231,225,236,105,128, 9,
+ 164,227,107, 2,179, 40,179, 47,228,239,247,110,128, 34,164,236,
+ 229,230,116,128, 34,163,228,229,246, 97,128, 9, 36,231,117, 2,
+ 179, 68,179, 77,234,225,242,225,244,105,128, 10,164,242,237,245,
+ 235,232,105,128, 10, 36,104, 4,179, 96,179,105,179,119,179,149,
+ 225,242,225,226,233, 99,128, 6, 55,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,254,194,105, 2,179,125,179,140,238,233,244,
+ 233,225,236,225,242,225,226,233, 99,128,254,195,242,225,231,225,
+ 238, 97,128, 48, 95,237,229,228,233,225,236,225,242,225,226,233,
+ 99,128,254,196,233,243,249,239,245,229,242,225,243,241,245,225,
+ 242,101,128, 51,125,235,225,244,225,235,225,238, 97,129, 48,191,
+ 179,194,232,225,236,230,247,233,228,244,104,128,255,128,244,247,
+ 229,229,236,225,242,225,226,233, 99,128, 6, 64,117,128, 3,196,
+ 118,130, 5,234,179,232,180, 1,228,225,231,229,115,129,251, 74,
+ 179,242,104,129,251, 74,179,248,232,229,226,242,229,119,128,251,
+ 74,232,229,226,242,229,119,128, 5,234, 98, 2,180, 16,180, 21,
+ 225,114,128, 1,103,239,240,239,237,239,230,111,128, 49, 10, 99,
+ 6,180, 45,180, 52,180, 59,180, 68,180,134,180,161,225,242,239,
+ 110,128, 1,101,227,245,242,108,128, 2,168,229,228,233,236,236,
+ 97,128, 1, 99,232,229,104, 4,180, 80,180, 89,180,103,180,119,
+ 225,242,225,226,233, 99,128, 6,134,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,251,123,233,238,233,244,233,225,236,225,242,
+ 225,226,233, 99,128,251,124,237,229,228,233,225,236,225,242,225,
+ 226,233, 99,128,251,125,233,242, 99, 2,180,142,180,147,236,101,
+ 128, 36,227,245,237,230,236,229,248,226,229,236,239,119,128, 30,
+ 113,239,237,237,225,225,227,227,229,238,116,128, 1, 99,100, 2,
+ 180,180,180,190,233,229,242,229,243,233,115,128, 30,151,239,116,
+ 2,180,197,180,206,225,227,227,229,238,116,128, 30,107,226,229,
+ 236,239,119,128, 30,109,101, 9,180,234,180,245,181, 9,182, 19,
+ 182, 44,182,108,182,175,182,180,182,232,227,249,242,233,236,236,
+ 233, 99,128, 4, 66,228,229,243,227,229,238,228,229,242,227,249,
+ 242,233,236,236,233, 99,128, 4,173,104, 7,181, 25,181, 34,181,
+ 48,181, 88,181,118,181,159,182, 1,225,242,225,226,233, 99,128,
+ 6, 42,230,233,238,225,236,225,242,225,226,233, 99,128,254,150,
+ 232,225,232,105, 2,181, 57,181, 72,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,252,162,243,239,236,225,244,229,228,225,
+ 242,225,226,233, 99,128,252, 12,105, 2,181, 94,181,109,238,233,
+ 244,233,225,236,225,242,225,226,233, 99,128,254,151,242,225,231,
+ 225,238, 97,128, 48,102,234,229,229,237,105, 2,181,128,181,143,
+ 238,233,244,233,225,236,225,242,225,226,233, 99,128,252,161,243,
+ 239,236,225,244,229,228,225,242,225,226,233, 99,128,252, 11,109,
+ 2,181,165,181,199,225,242,226,245,244, 97, 2,181,176,181,185,
+ 225,242,225,226,233, 99,128, 6, 41,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,254,148,101, 2,181,205,181,218,228,233,225,
+ 236,225,242,225,226,233, 99,128,254,152,229,237,105, 2,181,226,
+ 181,241,238,233,244,233,225,236,225,242,225,226,233, 99,128,252,
+ 164,243,239,236,225,244,229,228,225,242,225,226,233, 99,128,252,
+ 14,238,239,239,238,230,233,238,225,236,225,242,225,226,233, 99,
+ 128,252,115,235,225,244,225,235,225,238, 97,129, 48,198,182, 32,
+ 232,225,236,230,247,233,228,244,104,128,255,131,108, 2,182, 50,
+ 182, 69,229,240,232,239,238,101,129, 33, 33,182, 61,226,236,225,
+ 227,107,128, 38, 14,233,243,232, 97, 2,182, 78,182, 93,231,229,
+ 228,239,236,225,232,229,226,242,229,119,128, 5,160,241,229,244,
+ 225,238,225,232,229,226,242,229,119,128, 5,169,110, 4,182,118,
+ 182,127,182,146,182,167,227,233,242,227,236,101,128, 36,105,233,
+ 228,229,239,231,242,225,240,232,233,227,240,225,242,229,110,128,
+ 50, 41,112, 2,182,152,182,159,225,242,229,110,128, 36,125,229,
+ 242,233,239,100,128, 36,145,242,239,237,225,110,128, 33,121,243,
+ 104,128, 2,167,116,131, 5,216,182,190,182,210,182,219,228,225,
+ 231,229,243,104,129,251, 56,182,201,232,229,226,242,229,119,128,
+ 251, 56,232,229,226,242,229,119,128, 5,216,243,229,227,249,242,
+ 233,236,236,233, 99,128, 4,181,246,233,114, 2,182,240,182,249,
+ 232,229,226,242,229,119,128, 5,155,236,229,230,244,232,229,226,
+ 242,229,119,128, 5,155,104, 6,183, 20,183,172,184, 38,184,170,
+ 185, 77,186,134, 97, 5,183, 32,183, 42,183, 49,183, 74,183,103,
+ 226,229,238,231,225,236,105,128, 9,165,228,229,246, 97,128, 9,
+ 37,231,117, 2,183, 56,183, 65,234,225,242,225,244,105,128, 10,
+ 165,242,237,245,235,232,105,128, 10, 37,108, 2,183, 80,183, 89,
+ 225,242,225,226,233, 99,128, 6, 48,230,233,238,225,236,225,242,
+ 225,226,233, 99,128,254,172,238,244,232,225,235,232,225,116, 3,
+ 183,118,183,149,183,156,236,239,119, 2,183,126,183,137,236,229,
+ 230,244,244,232,225,105,128,248,152,242,233,231,232,244,244,232,
+ 225,105,128,248,151,244,232,225,105,128, 14, 76,245,240,240,229,
+ 242,236,229,230,244,244,232,225,105,128,248,150,101, 3,183,180,
+ 183,244,184, 11,104, 4,183,190,183,199,183,213,183,229,225,242,
+ 225,226,233, 99,128, 6, 43,230,233,238,225,236,225,242,225,226,
+ 233, 99,128,254,154,233,238,233,244,233,225,236,225,242,225,226,
+ 233, 99,128,254,155,237,229,228,233,225,236,225,242,225,226,233,
+ 99,128,254,156,242,101, 2,183,251,184, 4,229,248,233,243,244,
+ 115,128, 34, 3,230,239,242,101,128, 34, 52,244, 97,130, 3,184,
+ 184, 20,184, 24, 49,128, 3,209,243,249,237,226,239,236,231,242,
+ 229,229,107,128, 3,209,105, 2,184, 44,184,130,229,245,244,104,
+ 4,184, 57,184, 92,184,107,184,116, 97, 2,184, 63,184, 78,227,
+ 233,242,227,236,229,235,239,242,229,225,110,128, 50,121,240,225,
+ 242,229,238,235,239,242,229,225,110,128, 50, 25,227,233,242,227,
+ 236,229,235,239,242,229,225,110,128, 50,107,235,239,242,229,225,
+ 110,128, 49, 76,240,225,242,229,238,235,239,242,229,225,110,128,
+ 50, 11,242,244,229,229,110, 2,184,140,184,149,227,233,242,227,
+ 236,101,128, 36,108,112, 2,184,155,184,162,225,242,229,110,128,
+ 36,128,229,242,233,239,100,128, 36,148,111, 6,184,184,184,201,
+ 184,206,184,220,184,225,185, 22,238,225,238,231,237,239,238,244,
+ 232,239,244,232,225,105,128, 14, 17,239,107,128, 1,173,240,232,
+ 245,244,232,225,239,244,232,225,105,128, 14, 18,242,110,128, 0,
+ 254,244,104, 3,184,234,185, 2,185, 12, 97, 2,184,240,184,250,
+ 232,225,238,244,232,225,105,128, 14, 23,238,244,232,225,105,128,
+ 14, 16,239,238,231,244,232,225,105,128, 14, 24,245,238,231,244,
+ 232,225,105,128, 14, 22,245,243,225,238,100, 2,185, 32,185, 43,
+ 227,249,242,233,236,236,233, 99,128, 4,130,243,243,229,240,225,
+ 242,225,244,239,114, 2,185, 58,185, 67,225,242,225,226,233, 99,
+ 128, 6,108,240,229,242,243,233,225,110,128, 6,108,242,229,101,
+ 144, 0, 51,185,115,185,124,185,134,185,164,185,171,185,181,185,
+ 206,185,233,186, 11,186, 23,186, 42,186, 53,186, 86,186,108,186,
+ 116,186,127,225,242,225,226,233, 99,128, 6, 99,226,229,238,231,
+ 225,236,105,128, 9,233,227,233,242,227,236,101,129, 36, 98,185,
+ 145,233,238,246,229,242,243,229,243,225,238,243,243,229,242,233,
+ 102,128, 39,140,228,229,246, 97,128, 9,105,229,233,231,232,244,
+ 232,115,128, 33, 92,231,117, 2,185,188,185,197,234,225,242,225,
+ 244,105,128, 10,233,242,237,245,235,232,105,128, 10,105,232, 97,
+ 2,185,213,185,224,227,235,225,242,225,226,233, 99,128, 6, 99,
+ 238,231,250,232,239,117,128, 48, 35,105, 2,185,239,186, 1,228,
+ 229,239,231,242,225,240,232,233,227,240,225,242,229,110,128, 50,
+ 34,238,230,229,242,233,239,114,128, 32,131,237,239,238,239,243,
+ 240,225,227,101,128,255, 19,238,245,237,229,242,225,244,239,242,
+ 226,229,238,231,225,236,105,128, 9,246,239,236,228,243,244,249,
+ 236,101,128,247, 51,112, 2,186, 59,186, 66,225,242,229,110,128,
+ 36,118,229,114, 2,186, 73,186, 79,233,239,100,128, 36,138,243,
+ 233,225,110,128, 6,243,241,245,225,242,244,229,242,115,129, 0,
+ 190,186, 99,229,237,228,225,243,104,128,246,222,242,239,237,225,
+ 110,128, 33,114,243,245,240,229,242,233,239,114,128, 0,179,244,
+ 232,225,105,128, 14, 83,250,243,241,245,225,242,101,128, 51,148,
+ 105, 7,186,160,186,171,187, 30,187,128,187,140,187,189,187,206,
+ 232,233,242,225,231,225,238, 97,128, 48, 97,107, 2,186,177,186,
+ 201,225,244,225,235,225,238, 97,129, 48,193,186,189,232,225,236,
+ 230,247,233,228,244,104,128,255,129,229,245,116, 4,186,213,186,
+ 248,187, 7,187, 16, 97, 2,186,219,186,234,227,233,242,227,236,
+ 229,235,239,242,229,225,110,128, 50,112,240,225,242,229,238,235,
+ 239,242,229,225,110,128, 50, 16,227,233,242,227,236,229,235,239,
+ 242,229,225,110,128, 50, 98,235,239,242,229,225,110,128, 49, 55,
+ 240,225,242,229,238,235,239,242,229,225,110,128, 50, 2,236,228,
+ 101,133, 2,220,187, 46,187, 57,187, 74,187, 86,187,114,226,229,
+ 236,239,247,227,237, 98,128, 3, 48, 99, 2,187, 63,187, 68,237,
+ 98,128, 3, 3,239,237, 98,128, 3, 3,228,239,245,226,236,229,
+ 227,237, 98,128, 3, 96,111, 2,187, 92,187,102,240,229,242,225,
+ 244,239,114,128, 34, 60,246,229,242,236,225,249,227,237, 98,128,
+ 3, 52,246,229,242,244,233,227,225,236,227,237, 98,128, 3, 62,
+ 237,229,243,227,233,242,227,236,101,128, 34,151,112, 2,187,146,
+ 187,176,229,232, 97, 2,187,154,187,163,232,229,226,242,229,119,
+ 128, 5,150,236,229,230,244,232,229,226,242,229,119,128, 5,150,
+ 240,233,231,245,242,237,245,235,232,105,128, 10,112,244,236,239,
+ 227,249,242,233,236,236,233,227,227,237, 98,128, 4,131,247,238,
+ 225,242,237,229,238,233,225,110,128, 5,127,236,233,238,229,226,
+ 229,236,239,119,128, 30,111,237,239,238,239,243,240,225,227,101,
+ 128,255, 84,111, 7,188, 3,188, 14,188, 25,188, 50,188,170,188,
+ 182,189, 10,225,242,237,229,238,233,225,110,128, 5,105,232,233,
+ 242,225,231,225,238, 97,128, 48,104,235,225,244,225,235,225,238,
+ 97,129, 48,200,188, 38,232,225,236,230,247,233,228,244,104,128,
+ 255,132,110, 3,188, 58,188,156,188,161,101, 4,188, 68,188,137,
+ 188,144,188,150,226,225,114, 4,188, 80,188,109,188,119,188,128,
+ 229,248,244,242, 97, 2,188, 90,188,100,232,233,231,232,237,239,
+ 100,128, 2,229,236,239,247,237,239,100,128, 2,233,232,233,231,
+ 232,237,239,100,128, 2,230,236,239,247,237,239,100,128, 2,232,
+ 237,233,228,237,239,100,128, 2,231,230,233,246,101,128, 1,189,
+ 243,233,120,128, 1,133,244,247,111,128, 1,168,239,115,128, 3,
+ 132,243,241,245,225,242,101,128, 51, 39,240,225,244,225,235,244,
+ 232,225,105,128, 14, 15,242,244,239,233,243,229,243,232,229,236,
+ 236,226,242,225,227,235,229,116, 2,188,205,188,235,236,229,230,
+ 116,130, 48, 20,188,216,188,224,243,237,225,236,108,128,254, 93,
+ 246,229,242,244,233,227,225,108,128,254, 57,242,233,231,232,116,
+ 130, 48, 21,188,247,188,255,243,237,225,236,108,128,254, 94,246,
+ 229,242,244,233,227,225,108,128,254, 58,244,225,239,244,232,225,
+ 105,128, 14, 21,240, 97, 2,189, 27,189, 39,236,225,244,225,236,
+ 232,239,239,107,128, 1,171,242,229,110,128, 36,175,114, 3,189,
+ 53,189, 84,189, 99,225,228,229,237,225,242,107,129, 33, 34,189,
+ 65,115, 2,189, 71,189, 77,225,238,115,128,248,234,229,242,233,
+ 102,128,246,219,229,244,242,239,230,236,229,248,232,239,239,107,
+ 128, 2,136,233,225,103, 4,189,111,189,116,189,121,189,126,228,
+ 110,128, 37,188,236,102,128, 37,196,242,116,128, 37,186,245,112,
+ 128, 37,178,115,132, 2,166,189,143,189,182,190, 32,190, 45,225,
+ 228,105,130, 5,230,189,153,189,173,228,225,231,229,243,104,129,
+ 251, 70,189,164,232,229,226,242,229,119,128,251, 70,232,229,226,
+ 242,229,119,128, 5,230,101, 2,189,188,189,199,227,249,242,233,
+ 236,236,233, 99,128, 4, 70,242,101,134, 5,181,189,216,189,230,
+ 189,235,189,244,190, 3,190, 19, 49, 2,189,222,189,226, 50,128,
+ 5,181,101,128, 5,181,178, 98,128, 5,181,232,229,226,242,229,
+ 119,128, 5,181,238,225,242,242,239,247,232,229,226,242,229,119,
+ 128, 5,181,241,245,225,242,244,229,242,232,229,226,242,229,119,
+ 128, 5,181,247,233,228,229,232,229,226,242,229,119,128, 5,181,
+ 232,229,227,249,242,233,236,236,233, 99,128, 4, 91,245,240,229,
+ 242,233,239,114,128,246,243,116, 4,190, 65,190,115,190,180,190,
+ 231, 97, 3,190, 73,190, 83,190, 90,226,229,238,231,225,236,105,
+ 128, 9,159,228,229,246, 97,128, 9, 31,231,117, 2,190, 97,190,
+ 106,234,225,242,225,244,105,128, 10,159,242,237,245,235,232,105,
+ 128, 10, 31,229,104, 4,190,126,190,135,190,149,190,165,225,242,
+ 225,226,233, 99,128, 6,121,230,233,238,225,236,225,242,225,226,
+ 233, 99,128,251,103,233,238,233,244,233,225,236,225,242,225,226,
+ 233, 99,128,251,104,237,229,228,233,225,236,225,242,225,226,233,
+ 99,128,251,105,232, 97, 3,190,189,190,199,190,206,226,229,238,
+ 231,225,236,105,128, 9,160,228,229,246, 97,128, 9, 32,231,117,
+ 2,190,213,190,222,234,225,242,225,244,105,128, 10,160,242,237,
+ 245,235,232,105,128, 10, 32,245,242,238,229,100,128, 2,135,117,
+ 3,190,247,191, 2,191, 27,232,233,242,225,231,225,238, 97,128,
+ 48,100,235,225,244,225,235,225,238, 97,129, 48,196,191, 15,232,
+ 225,236,230,247,233,228,244,104,128,255,130,243,237,225,236,108,
+ 2,191, 37,191, 48,232,233,242,225,231,225,238, 97,128, 48, 99,
+ 235,225,244,225,235,225,238, 97,129, 48,195,191, 61,232,225,236,
+ 230,247,233,228,244,104,128,255,111,119, 2,191, 79,191,184,101,
+ 2,191, 85,191,133,236,246,101, 3,191, 95,191,104,191,125,227,
+ 233,242,227,236,101,128, 36,107,112, 2,191,110,191,117,225,242,
+ 229,110,128, 36,127,229,242,233,239,100,128, 36,147,242,239,237,
+ 225,110,128, 33,123,238,244,121, 3,191,143,191,152,191,163,227,
+ 233,242,227,236,101,128, 36,115,232,225,238,231,250,232,239,117,
+ 128, 83, 68,112, 2,191,169,191,176,225,242,229,110,128, 36,135,
+ 229,242,233,239,100,128, 36,155,111,142, 0, 50,191,216,191,225,
+ 191,235,192, 9,192, 61,192, 86,192,113,192,147,192,159,192,178,
+ 192,189,192,222,192,230,192,254,225,242,225,226,233, 99,128, 6,
+ 98,226,229,238,231,225,236,105,128, 9,232,227,233,242,227,236,
+ 101,129, 36, 97,191,246,233,238,246,229,242,243,229,243,225,238,
+ 243,243,229,242,233,102,128, 39,139,100, 2,192, 15,192, 21,229,
+ 246, 97,128, 9,104,239,116, 2,192, 28,192, 39,229,238,236,229,
+ 225,228,229,114,128, 32, 37,236,229,225,228,229,114,129, 32, 37,
+ 192, 50,246,229,242,244,233,227,225,108,128,254, 48,231,117, 2,
+ 192, 68,192, 77,234,225,242,225,244,105,128, 10,232,242,237,245,
+ 235,232,105,128, 10,104,232, 97, 2,192, 93,192,104,227,235,225,
+ 242,225,226,233, 99,128, 6, 98,238,231,250,232,239,117,128, 48,
+ 34,105, 2,192,119,192,137,228,229,239,231,242,225,240,232,233,
+ 227,240,225,242,229,110,128, 50, 33,238,230,229,242,233,239,114,
+ 128, 32,130,237,239,238,239,243,240,225,227,101,128,255, 18,238,
+ 245,237,229,242,225,244,239,242,226,229,238,231,225,236,105,128,
+ 9,245,239,236,228,243,244,249,236,101,128,247, 50,112, 2,192,
+ 195,192,202,225,242,229,110,128, 36,117,229,114, 2,192,209,192,
+ 215,233,239,100,128, 36,137,243,233,225,110,128, 6,242,242,239,
+ 237,225,110,128, 33,113,115, 2,192,236,192,244,244,242,239,235,
+ 101,128, 1,187,245,240,229,242,233,239,114,128, 0,178,244,104,
+ 2,193, 5,193, 10,225,105,128, 14, 82,233,242,228,115,128, 33,
+ 84,117,145, 0,117,193, 55,193, 63,193,104,193,161,194, 43,194,
+ 80,194,203,194,219,195, 14,195, 84,195,165,195,174,196, 37,196,
+ 61,196,169,196,197,197, 55,225,227,245,244,101,128, 0,250, 98,
+ 4,193, 73,193, 78,193, 87,193, 97,225,114,128, 2,137,229,238,
+ 231,225,236,105,128, 9,137,239,240,239,237,239,230,111,128, 49,
+ 40,242,229,246,101,128, 1,109, 99, 3,193,112,193,119,193,151,
+ 225,242,239,110,128, 1,212,233,242, 99, 2,193,127,193,132,236,
+ 101,128, 36,228,245,237,230,236,229,120,129, 0,251,193,143,226,
+ 229,236,239,119,128, 30,119,249,242,233,236,236,233, 99,128, 4,
+ 67,100, 5,193,173,193,184,193,207,193,213,194, 33,225,244,244,
+ 225,228,229,246, 97,128, 9, 81,226,108, 2,193,191,193,199,225,
+ 227,245,244,101,128, 1,113,231,242,225,246,101,128, 2, 21,229,
+ 246, 97,128, 9, 9,233,229,242,229,243,233,115,133, 0,252,193,
+ 233,193,241,193,249,194, 16,194, 24,225,227,245,244,101,128, 1,
+ 216,226,229,236,239,119,128, 30,115, 99, 2,193,255,194, 6,225,
+ 242,239,110,128, 1,218,249,242,233,236,236,233, 99,128, 4,241,
+ 231,242,225,246,101,128, 1,220,237,225,227,242,239,110,128, 1,
+ 214,239,244,226,229,236,239,119,128, 30,229,103, 2,194, 49,194,
+ 56,242,225,246,101,128, 0,249,117, 2,194, 62,194, 71,234,225,
+ 242,225,244,105,128, 10,137,242,237,245,235,232,105,128, 10, 9,
+ 104, 3,194, 88,194, 98,194,176,233,242,225,231,225,238, 97,128,
+ 48, 70,111, 2,194,104,194,114,239,235,225,226,239,246,101,128,
+ 30,231,242,110,133, 1,176,194,129,194,137,194,148,194,156,194,
+ 168,225,227,245,244,101,128, 30,233,228,239,244,226,229,236,239,
+ 119,128, 30,241,231,242,225,246,101,128, 30,235,232,239,239,235,
+ 225,226,239,246,101,128, 30,237,244,233,236,228,101,128, 30,239,
+ 245,238,231,225,242,245,237,236,225,245,116,129, 1,113,194,192,
+ 227,249,242,233,236,236,233, 99,128, 4,243,233,238,246,229,242,
+ 244,229,228,226,242,229,246,101,128, 2, 23,107, 3,194,227,194,
+ 251,195, 6,225,244,225,235,225,238, 97,129, 48,166,194,239,232,
+ 225,236,230,247,233,228,244,104,128,255,115,227,249,242,233,236,
+ 236,233, 99,128, 4,121,239,242,229,225,110,128, 49, 92,109, 2,
+ 195, 20,195, 73, 97, 2,195, 26,195, 59,227,242,239,110,130, 1,
+ 107,195, 37,195, 48,227,249,242,233,236,236,233, 99,128, 4,239,
+ 228,233,229,242,229,243,233,115,128, 30,123,244,242,225,231,245,
+ 242,237,245,235,232,105,128, 10, 65,239,238,239,243,240,225,227,
+ 101,128,255, 85,110, 2,195, 90,195,145,228,229,242,243,227,239,
+ 242,101,132, 0, 95,195,109,195,115,195,127,195,138,228,226,108,
+ 128, 32, 23,237,239,238,239,243,240,225,227,101,128,255, 63,246,
+ 229,242,244,233,227,225,108,128,254, 51,247,225,246,121,128,254,
+ 79,105, 2,195,151,195,156,239,110,128, 34, 42,246,229,242,243,
+ 225,108,128, 34, 0,239,231,239,238,229,107,128, 1,115,112, 5,
+ 195,186,195,193,195,201,195,216,196, 11,225,242,229,110,128, 36,
+ 176,226,236,239,227,107,128, 37,128,240,229,242,228,239,244,232,
+ 229,226,242,229,119,128, 5,196,243,233,236,239,110,131, 3,197,
+ 195,230,195,251,196, 3,228,233,229,242,229,243,233,115,129, 3,
+ 203,195,243,244,239,238,239,115,128, 3,176,236,225,244,233,110,
+ 128, 2,138,244,239,238,239,115,128, 3,205,244,225,227,107, 2,
+ 196, 20,196, 31,226,229,236,239,247,227,237, 98,128, 3, 29,237,
+ 239,100,128, 2,212,114, 2,196, 43,196, 55,225,231,245,242,237,
+ 245,235,232,105,128, 10,115,233,238,103,128, 1,111,115, 3,196,
+ 69,196, 84,196,129,232,239,242,244,227,249,242,233,236,236,233,
+ 99,128, 4, 94,237,225,236,108, 2,196, 93,196,104,232,233,242,
+ 225,231,225,238, 97,128, 48, 69,235,225,244,225,235,225,238, 97,
+ 129, 48,165,196,117,232,225,236,230,247,233,228,244,104,128,255,
+ 105,244,242,225,233,231,232,116, 2,196,141,196,152,227,249,242,
+ 233,236,236,233, 99,128, 4,175,243,244,242,239,235,229,227,249,
+ 242,233,236,236,233, 99,128, 4,177,244,233,236,228,101,130, 1,
+ 105,196,181,196,189,225,227,245,244,101,128, 30,121,226,229,236,
+ 239,119,128, 30,117,117, 5,196,209,196,219,196,226,196,251,197,
+ 11,226,229,238,231,225,236,105,128, 9,138,228,229,246, 97,128,
+ 9, 10,231,117, 2,196,233,196,242,234,225,242,225,244,105,128,
+ 10,138,242,237,245,235,232,105,128, 10, 10,237,225,244,242,225,
+ 231,245,242,237,245,235,232,105,128, 10, 66,246,239,247,229,236,
+ 243,233,231,110, 3,197, 27,197, 37,197, 44,226,229,238,231,225,
+ 236,105,128, 9,194,228,229,246, 97,128, 9, 66,231,245,234,225,
+ 242,225,244,105,128, 10,194,246,239,247,229,236,243,233,231,110,
+ 3,197, 71,197, 81,197, 88,226,229,238,231,225,236,105,128, 9,
+ 193,228,229,246, 97,128, 9, 65,231,245,234,225,242,225,244,105,
+ 128, 10,193,118,139, 0,118,197,125,198, 17,198, 26,198, 37,198,
+ 222,198,229,199, 71,199, 83,199,183,199,191,199,212, 97, 4,197,
+ 135,197,142,197,167,197,178,228,229,246, 97,128, 9, 53,231,117,
+ 2,197,149,197,158,234,225,242,225,244,105,128, 10,181,242,237,
+ 245,235,232,105,128, 10, 53,235,225,244,225,235,225,238, 97,128,
+ 48,247,118,132, 5,213,197,190,197,217,197,249,198, 5,228,225,
+ 231,229,243,104,130,251, 53,197,203,197,208,182, 53,128,251, 53,
+ 232,229,226,242,229,119,128,251, 53,104, 2,197,223,197,231,229,
+ 226,242,229,119,128, 5,213,239,236,225,109,129,251, 75,197,240,
+ 232,229,226,242,229,119,128,251, 75,246,225,246,232,229,226,242,
+ 229,119,128, 5,240,249,239,228,232,229,226,242,229,119,128, 5,
+ 241,227,233,242,227,236,101,128, 36,229,228,239,244,226,229,236,
+ 239,119,128, 30,127,101, 6,198, 51,198, 62,198,126,198,137,198,
+ 143,198,210,227,249,242,233,236,236,233, 99,128, 4, 50,104, 4,
+ 198, 72,198, 81,198, 95,198,111,225,242,225,226,233, 99,128, 6,
+ 164,230,233,238,225,236,225,242,225,226,233, 99,128,251,107,233,
+ 238,233,244,233,225,236,225,242,225,226,233, 99,128,251,108,237,
+ 229,228,233,225,236,225,242,225,226,233, 99,128,251,109,235,225,
+ 244,225,235,225,238, 97,128, 48,249,238,245,115,128, 38, 64,242,
+ 244,233,227,225,108, 2,198,154,198,160,226,225,114,128, 0,124,
+ 236,233,238,101, 4,198,173,198,184,198,195,198,204,225,226,239,
+ 246,229,227,237, 98,128, 3, 13,226,229,236,239,247,227,237, 98,
+ 128, 3, 41,236,239,247,237,239,100,128, 2,204,237,239,100,128,
+ 2,200,247,225,242,237,229,238,233,225,110,128, 5,126,232,239,
+ 239,107,128, 2,139,105, 3,198,237,198,248,199, 31,235,225,244,
+ 225,235,225,238, 97,128, 48,248,242,225,237, 97, 3,199, 3,199,
+ 13,199, 20,226,229,238,231,225,236,105,128, 9,205,228,229,246,
+ 97,128, 9, 77,231,245,234,225,242,225,244,105,128, 10,205,243,
+ 225,242,231, 97, 3,199, 43,199, 53,199, 60,226,229,238,231,225,
+ 236,105,128, 9,131,228,229,246, 97,128, 9, 3,231,245,234,225,
+ 242,225,244,105,128, 10,131,237,239,238,239,243,240,225,227,101,
+ 128,255, 86,111, 3,199, 91,199,102,199,172,225,242,237,229,238,
+ 233,225,110,128, 5,120,233,227,229,100, 2,199,111,199,147,233,
+ 244,229,242,225,244,233,239,110, 2,199,125,199,136,232,233,242,
+ 225,231,225,238, 97,128, 48,158,235,225,244,225,235,225,238, 97,
+ 128, 48,254,237,225,242,235,235,225,238, 97,129, 48,155,199,160,
+ 232,225,236,230,247,233,228,244,104,128,255,158,235,225,244,225,
+ 235,225,238, 97,128, 48,250,240,225,242,229,110,128, 36,177,116,
+ 2,199,197,199,204,233,236,228,101,128, 30,125,245,242,238,229,
+ 100,128, 2,140,117, 2,199,218,199,229,232,233,242,225,231,225,
+ 238, 97,128, 48,148,235,225,244,225,235,225,238, 97,128, 48,244,
+ 119,143, 0,119,200, 18,200,251,201, 5,201, 28,201, 68,201,135,
+ 201,143,203,114,203,155,203,167,203,242,203,250,204, 1,204, 12,
+ 204, 21, 97, 8,200, 36,200, 43,200, 53,200, 64,200,102,200,134,
+ 200,146,200,182,227,245,244,101,128, 30,131,229,235,239,242,229,
+ 225,110,128, 49, 89,232,233,242,225,231,225,238, 97,128, 48,143,
+ 107, 2,200, 70,200, 94,225,244,225,235,225,238, 97,129, 48,239,
+ 200, 82,232,225,236,230,247,233,228,244,104,128,255,156,239,242,
+ 229,225,110,128, 49, 88,243,237,225,236,108, 2,200,112,200,123,
+ 232,233,242,225,231,225,238, 97,128, 48,142,235,225,244,225,235,
+ 225,238, 97,128, 48,238,244,244,239,243,241,245,225,242,101,128,
+ 51, 87,118, 2,200,152,200,160,229,228,225,243,104,128, 48, 28,
+ 249,245,238,228,229,242,243,227,239,242,229,246,229,242,244,233,
+ 227,225,108,128,254, 52,119, 3,200,190,200,199,200,213,225,242,
+ 225,226,233, 99,128, 6, 72,230,233,238,225,236,225,242,225,226,
+ 233, 99,128,254,238,232,225,237,250,225,225,226,239,246,101, 2,
+ 200,228,200,237,225,242,225,226,233, 99,128, 6, 36,230,233,238,
+ 225,236,225,242,225,226,233, 99,128,254,134,226,243,241,245,225,
+ 242,101,128, 51,221,227,233,242, 99, 2,201, 14,201, 19,236,101,
+ 128, 36,230,245,237,230,236,229,120,128, 1,117,100, 2,201, 34,
+ 201, 44,233,229,242,229,243,233,115,128, 30,133,239,116, 2,201,
+ 51,201, 60,225,227,227,229,238,116,128, 30,135,226,229,236,239,
+ 119,128, 30,137,101, 4,201, 78,201, 89,201,101,201,125,232,233,
+ 242,225,231,225,238, 97,128, 48,145,233,229,242,243,244,242,225,
+ 243,115,128, 33, 24,107, 2,201,107,201,117,225,244,225,235,225,
+ 238, 97,128, 48,241,239,242,229,225,110,128, 49, 94,239,235,239,
+ 242,229,225,110,128, 49, 93,231,242,225,246,101,128, 30,129,232,
+ 233,244,101, 8,201,164,201,173,202, 1,202, 91,202,175,202,220,
+ 203, 16,203, 72,226,245,236,236,229,116,128, 37,230, 99, 2,201,
+ 179,201,199,233,242,227,236,101,129, 37,203,201,189,233,238,246,
+ 229,242,243,101,128, 37,217,239,242,238,229,242,226,242,225,227,
+ 235,229,116, 2,201,216,201,236,236,229,230,116,129, 48, 14,201,
+ 225,246,229,242,244,233,227,225,108,128,254, 67,242,233,231,232,
+ 116,129, 48, 15,201,246,246,229,242,244,233,227,225,108,128,254,
+ 68,100, 2,202, 7,202, 48,233,225,237,239,238,100,129, 37,199,
+ 202, 18,227,239,238,244,225,233,238,233,238,231,226,236,225,227,
+ 235,243,237,225,236,236,228,233,225,237,239,238,100,128, 37,200,
+ 239,247,238,240,239,233,238,244,233,238,103, 2,202, 64,202, 80,
+ 243,237,225,236,236,244,242,233,225,238,231,236,101,128, 37,191,
+ 244,242,233,225,238,231,236,101,128, 37,189,236,101, 2,202, 98,
+ 202,140,230,244,240,239,233,238,244,233,238,103, 2,202,113,202,
+ 129,243,237,225,236,236,244,242,233,225,238,231,236,101,128, 37,
+ 195,244,242,233,225,238,231,236,101,128, 37,193,238,244,233,227,
+ 245,236,225,242,226,242,225,227,235,229,116, 2,202,160,202,167,
+ 236,229,230,116,128, 48, 22,242,233,231,232,116,128, 48, 23,242,
+ 233,231,232,244,240,239,233,238,244,233,238,103, 2,202,193,202,
+ 209,243,237,225,236,236,244,242,233,225,238,231,236,101,128, 37,
+ 185,244,242,233,225,238,231,236,101,128, 37,183,115, 3,202,228,
+ 203, 2,203, 10,109, 2,202,234,202,246,225,236,236,243,241,245,
+ 225,242,101,128, 37,171,233,236,233,238,231,230,225,227,101,128,
+ 38, 58,241,245,225,242,101,128, 37,161,244,225,114,128, 38, 6,
+ 116, 2,203, 22,203, 33,229,236,229,240,232,239,238,101,128, 38,
+ 15,239,242,244,239,233,243,229,243,232,229,236,236,226,242,225,
+ 227,235,229,116, 2,203, 57,203, 64,236,229,230,116,128, 48, 24,
+ 242,233,231,232,116,128, 48, 25,245,240,240,239,233,238,244,233,
+ 238,103, 2,203, 87,203,103,243,237,225,236,236,244,242,233,225,
+ 238,231,236,101,128, 37,181,244,242,233,225,238,231,236,101,128,
+ 37,179,105, 2,203,120,203,131,232,233,242,225,231,225,238, 97,
+ 128, 48,144,107, 2,203,137,203,147,225,244,225,235,225,238, 97,
+ 128, 48,240,239,242,229,225,110,128, 49, 95,237,239,238,239,243,
+ 240,225,227,101,128,255, 87,111, 4,203,177,203,188,203,213,203,
+ 231,232,233,242,225,231,225,238, 97,128, 48,146,235,225,244,225,
+ 235,225,238, 97,129, 48,242,203,201,232,225,236,230,247,233,228,
+ 244,104,128,255,102,110,129, 32,169,203,219,237,239,238,239,243,
+ 240,225,227,101,128,255,230,247,225,229,238,244,232,225,105,128,
+ 14, 39,240,225,242,229,110,128, 36,178,242,233,238,103,128, 30,
+ 152,243,245,240,229,242,233,239,114,128, 2,183,244,245,242,238,
+ 229,100,128, 2,141,249,238,110,128, 1,191,120,137, 0,120,204,
+ 49,204, 60,204, 71,204, 80,204,107,204,120,204,124,204,136,204,
+ 144,225,226,239,246,229,227,237, 98,128, 3, 61,226,239,240,239,
+ 237,239,230,111,128, 49, 18,227,233,242,227,236,101,128, 36,231,
+ 100, 2,204, 86,204, 96,233,229,242,229,243,233,115,128, 30,141,
+ 239,244,225,227,227,229,238,116,128, 30,139,229,232,225,242,237,
+ 229,238,233,225,110,128, 5,109,105,128, 3,190,237,239,238,239,
+ 243,240,225,227,101,128,255, 88,240,225,242,229,110,128, 36,179,
+ 243,245,240,229,242,233,239,114,128, 2,227,121,143, 0,121,204,
+ 189,205,148,205,171,205,211,207,177,207,185,207,202,208, 10,208,
+ 22,209, 19,209, 59,209, 71,209, 82,209,103,210, 76, 97, 11,204,
+ 213,204,225,204,235,204,242,204,249,205, 3,205, 28,205, 39,205,
+ 77,205, 90,205,136,225,228,239,243,241,245,225,242,101,128, 51,
+ 78,226,229,238,231,225,236,105,128, 9,175,227,245,244,101,128,
+ 0,253,228,229,246, 97,128, 9, 47,229,235,239,242,229,225,110,
+ 128, 49, 82,231,117, 2,205, 10,205, 19,234,225,242,225,244,105,
+ 128, 10,175,242,237,245,235,232,105,128, 10, 47,232,233,242,225,
+ 231,225,238, 97,128, 48,132,107, 2,205, 45,205, 69,225,244,225,
+ 235,225,238, 97,129, 48,228,205, 57,232,225,236,230,247,233,228,
+ 244,104,128,255,148,239,242,229,225,110,128, 49, 81,237,225,235,
+ 235,225,238,244,232,225,105,128, 14, 78,243,237,225,236,108, 2,
+ 205,100,205,111,232,233,242,225,231,225,238, 97,128, 48,131,235,
+ 225,244,225,235,225,238, 97,129, 48,227,205,124,232,225,236,230,
+ 247,233,228,244,104,128,255,108,244,227,249,242,233,236,236,233,
+ 99,128, 4, 99,227,233,242, 99, 2,205,157,205,162,236,101,128,
+ 36,232,245,237,230,236,229,120,128, 1,119,100, 2,205,177,205,
+ 187,233,229,242,229,243,233,115,128, 0,255,239,116, 2,205,194,
+ 205,203,225,227,227,229,238,116,128, 30,143,226,229,236,239,119,
+ 128, 30,245,101, 7,205,227,206,235,206,244,207, 6,207, 38,207,
+ 114,207,165,104, 8,205,245,205,254,206, 32,206, 46,206,119,206,
+ 135,206,194,206,212,225,242,225,226,233, 99,128, 6, 74,226,225,
+ 242,242,229,101, 2,206, 9,206, 18,225,242,225,226,233, 99,128,
+ 6,210,230,233,238,225,236,225,242,225,226,233, 99,128,251,175,
+ 230,233,238,225,236,225,242,225,226,233, 99,128,254,242,232,225,
+ 237,250,225,225,226,239,246,101, 4,206, 65,206, 74,206, 88,206,
+ 104,225,242,225,226,233, 99,128, 6, 38,230,233,238,225,236,225,
+ 242,225,226,233, 99,128,254,138,233,238,233,244,233,225,236,225,
+ 242,225,226,233, 99,128,254,139,237,229,228,233,225,236,225,242,
+ 225,226,233, 99,128,254,140,233,238,233,244,233,225,236,225,242,
+ 225,226,233, 99,128,254,243,237,101, 2,206,142,206,155,228,233,
+ 225,236,225,242,225,226,233, 99,128,254,244,229,237,105, 2,206,
+ 163,206,178,238,233,244,233,225,236,225,242,225,226,233, 99,128,
+ 252,221,243,239,236,225,244,229,228,225,242,225,226,233, 99,128,
+ 252, 88,238,239,239,238,230,233,238,225,236,225,242,225,226,233,
+ 99,128,252,148,244,232,242,229,229,228,239,244,243,226,229,236,
+ 239,247,225,242,225,226,233, 99,128, 6,209,235,239,242,229,225,
+ 110,128, 49, 86,110,129, 0,165,206,250,237,239,238,239,243,240,
+ 225,227,101,128,255,229,111, 2,207, 12,207, 21,235,239,242,229,
+ 225,110,128, 49, 85,242,233,238,232,233,229,245,232,235,239,242,
+ 229,225,110,128, 49,134,114, 3,207, 46,207, 82,207, 94,225,232,
+ 226,229,238,249,239,237,111, 2,207, 60,207, 69,232,229,226,242,
+ 229,119,128, 5,170,236,229,230,244,232,229,226,242,229,119,128,
+ 5,170,233,227,249,242,233,236,236,233, 99,128, 4, 75,245,228,
+ 233,229,242,229,243,233,243,227,249,242,233,236,236,233, 99,128,
+ 4,249,243,233,229,245,238,103, 3,207,127,207,136,207,152,235,
+ 239,242,229,225,110,128, 49,129,240,225,238,243,233,239,243,235,
+ 239,242,229,225,110,128, 49,131,243,233,239,243,235,239,242,229,
+ 225,110,128, 49,130,244,233,246,232,229,226,242,229,119,128, 5,
+ 154,231,242,225,246,101,128, 30,243,232,239,239,107,129, 1,180,
+ 207,194,225,226,239,246,101,128, 30,247,105, 5,207,214,207,225,
+ 207,236,207,245,207,253,225,242,237,229,238,233,225,110,128, 5,
+ 117,227,249,242,233,236,236,233, 99,128, 4, 87,235,239,242,229,
+ 225,110,128, 49, 98,238,249,225,238,103,128, 38, 47,247,238,225,
+ 242,237,229,238,233,225,110,128, 5,130,237,239,238,239,243,240,
+ 225,227,101,128,255, 89,111, 7,208, 38,208,108,208,119,208,129,
+ 208,167,208,213,208,222,100,131, 5,217,208, 48,208, 68,208, 77,
+ 228,225,231,229,243,104,129,251, 57,208, 59,232,229,226,242,229,
+ 119,128,251, 57,232,229,226,242,229,119,128, 5,217,249,239,100,
+ 2,208, 85,208, 94,232,229,226,242,229,119,128, 5,242,240,225,
+ 244,225,232,232,229,226,242,229,119,128,251, 31,232,233,242,225,
+ 231,225,238, 97,128, 48,136,233,235,239,242,229,225,110,128, 49,
+ 137,107, 2,208,135,208,159,225,244,225,235,225,238, 97,129, 48,
+ 232,208,147,232,225,236,230,247,233,228,244,104,128,255,150,239,
+ 242,229,225,110,128, 49, 91,243,237,225,236,108, 2,208,177,208,
+ 188,232,233,242,225,231,225,238, 97,128, 48,135,235,225,244,225,
+ 235,225,238, 97,129, 48,231,208,201,232,225,236,230,247,233,228,
+ 244,104,128,255,110,244,231,242,229,229,107,128, 3,243,121, 2,
+ 208,228,209, 9, 97, 2,208,234,208,244,229,235,239,242,229,225,
+ 110,128, 49,136,107, 2,208,250,209, 2,239,242,229,225,110,128,
+ 49,135,244,232,225,105,128, 14, 34,233,238,231,244,232,225,105,
+ 128, 14, 13,112, 2,209, 25,209, 32,225,242,229,110,128, 36,180,
+ 239,231,229,231,242,225,237,237,229,238,105,129, 3,122,209, 48,
+ 231,242,229,229,235,227,237, 98,128, 3, 69,114,129, 1,166,209,
+ 65,233,238,103,128, 30,153,243,245,240,229,242,233,239,114,128,
+ 2,184,116, 2,209, 88,209, 95,233,236,228,101,128, 30,249,245,
+ 242,238,229,100,128, 2,142,117, 5,209,115,209,126,209,136,209,
+ 174,210, 50,232,233,242,225,231,225,238, 97,128, 48,134,233,235,
+ 239,242,229,225,110,128, 49,140,107, 2,209,142,209,166,225,244,
+ 225,235,225,238, 97,129, 48,230,209,154,232,225,236,230,247,233,
+ 228,244,104,128,255,149,239,242,229,225,110,128, 49, 96,115, 3,
+ 209,182,209,220,210, 5,226,233,103, 2,209,190,209,201,227,249,
+ 242,233,236,236,233, 99,128, 4,107,233,239,244,233,230,233,229,
+ 228,227,249,242,233,236,236,233, 99,128, 4,109,236,233,244,244,
+ 236,101, 2,209,231,209,242,227,249,242,233,236,236,233, 99,128,
+ 4,103,233,239,244,233,230,233,229,228,227,249,242,233,236,236,
+ 233, 99,128, 4,105,237,225,236,108, 2,210, 14,210, 25,232,233,
+ 242,225,231,225,238, 97,128, 48,133,235,225,244,225,235,225,238,
+ 97,129, 48,229,210, 38,232,225,236,230,247,233,228,244,104,128,
+ 255,109,249,101, 2,210, 57,210, 66,235,239,242,229,225,110,128,
+ 49,139,239,235,239,242,229,225,110,128, 49,138,249, 97, 2,210,
+ 83,210, 93,226,229,238,231,225,236,105,128, 9,223,228,229,246,
+ 97,128, 9, 95,122,142, 0,122,210,132,211,140,211,151,211,194,
+ 211,221,213, 0,213,108,213,150,213,162,213,174,213,202,213,210,
+ 213,226,213,235, 97, 10,210,154,210,165,210,172,210,179,210,190,
+ 211, 12,211, 42,211, 53,211, 89,211,101,225,242,237,229,238,233,
+ 225,110,128, 5,102,227,245,244,101,128, 1,122,228,229,246, 97,
+ 128, 9, 91,231,245,242,237,245,235,232,105,128, 10, 91,104, 4,
+ 210,200,210,209,210,223,210,253,225,242,225,226,233, 99,128, 6,
+ 56,230,233,238,225,236,225,242,225,226,233, 99,128,254,198,105,
+ 2,210,229,210,244,238,233,244,233,225,236,225,242,225,226,233,
+ 99,128,254,199,242,225,231,225,238, 97,128, 48, 86,237,229,228,
+ 233,225,236,225,242,225,226,233, 99,128,254,200,233,110, 2,211,
+ 19,211, 28,225,242,225,226,233, 99,128, 6, 50,230,233,238,225,
+ 236,225,242,225,226,233, 99,128,254,176,235,225,244,225,235,225,
+ 238, 97,128, 48,182,241,229,102, 2,211, 61,211, 75,231,225,228,
+ 239,236,232,229,226,242,229,119,128, 5,149,241,225,244,225,238,
+ 232,229,226,242,229,119,128, 5,148,242,241,225,232,229,226,242,
+ 229,119,128, 5,152,249,233,110,130, 5,214,211,111,211,131,228,
+ 225,231,229,243,104,129,251, 54,211,122,232,229,226,242,229,119,
+ 128,251, 54,232,229,226,242,229,119,128, 5,214,226,239,240,239,
+ 237,239,230,111,128, 49, 23, 99, 3,211,159,211,166,211,188,225,
+ 242,239,110,128, 1,126,233,242, 99, 2,211,174,211,179,236,101,
+ 128, 36,233,245,237,230,236,229,120,128, 30,145,245,242,108,128,
+ 2,145,228,239,116,130, 1,124,211,204,211,213,225,227,227,229,
+ 238,116,128, 1,124,226,229,236,239,119,128, 30,147,101, 6,211,
+ 235,211,246,212, 33,212, 44,212, 55,212,251,227,249,242,233,236,
+ 236,233, 99,128, 4, 55,100, 2,211,252,212, 15,229,243,227,229,
+ 238,228,229,242,227,249,242,233,236,236,233, 99,128, 4,153,233,
+ 229,242,229,243,233,243,227,249,242,233,236,236,233, 99,128, 4,
+ 223,232,233,242,225,231,225,238, 97,128, 48, 92,235,225,244,225,
+ 235,225,238, 97,128, 48,188,242,111,140, 0, 48,212, 84,212, 93,
+ 212,103,212,110,212,135,212,148,212,159,212,171,212,182,212,192,
+ 212,203,212,210,225,242,225,226,233, 99,128, 6, 96,226,229,238,
+ 231,225,236,105,128, 9,230,228,229,246, 97,128, 9,102,231,117,
+ 2,212,117,212,126,234,225,242,225,244,105,128, 10,230,242,237,
+ 245,235,232,105,128, 10,102,232,225,227,235,225,242,225,226,233,
+ 99,128, 6, 96,233,238,230,229,242,233,239,114,128, 32,128,237,
+ 239,238,239,243,240,225,227,101,128,255, 16,239,236,228,243,244,
+ 249,236,101,128,247, 48,240,229,242,243,233,225,110,128, 6,240,
+ 243,245,240,229,242,233,239,114,128, 32,112,244,232,225,105,128,
+ 14, 80,247,233,228,244,104, 3,212,222,212,231,212,243,234,239,
+ 233,238,229,114,128,254,255,238,239,238,234,239,233,238,229,114,
+ 128, 32, 12,243,240,225,227,101,128, 32, 11,244, 97,128, 3,182,
+ 104, 2,213, 6,213, 17,226,239,240,239,237,239,230,111,128, 49,
+ 19,101, 4,213, 27,213, 38,213, 54,213, 65,225,242,237,229,238,
+ 233,225,110,128, 5,106,226,242,229,246,229,227,249,242,233,236,
+ 236,233, 99,128, 4,194,227,249,242,233,236,236,233, 99,128, 4,
+ 54,100, 2,213, 71,213, 90,229,243,227,229,238,228,229,242,227,
+ 249,242,233,236,236,233, 99,128, 4,151,233,229,242,229,243,233,
+ 243,227,249,242,233,236,236,233, 99,128, 4,221,105, 3,213,116,
+ 213,127,213,138,232,233,242,225,231,225,238, 97,128, 48, 88,235,
+ 225,244,225,235,225,238, 97,128, 48,184,238,239,242,232,229,226,
+ 242,229,119,128, 5,174,236,233,238,229,226,229,236,239,119,128,
+ 30,149,237,239,238,239,243,240,225,227,101,128,255, 90,111, 2,
+ 213,180,213,191,232,233,242,225,231,225,238, 97,128, 48, 94,235,
+ 225,244,225,235,225,238, 97,128, 48,190,240,225,242,229,110,128,
+ 36,181,242,229,244,242,239,230,236,229,248,232,239,239,107,128,
+ 2,144,243,244,242,239,235,101,128, 1,182,117, 2,213,241,213,
+ 252,232,233,242,225,231,225,238, 97,128, 48, 90,235,225,244,225,
+ 235,225,238, 97,128, 48,186
+ };
+
+
+ /*
+ * This function searches the compressed table efficiently.
+ */
+ static unsigned long
+ ft_get_adobe_glyph_index( const char* name,
+ const char* limit )
+ {
+ int c = 0;
+ int count, min, max;
+ const unsigned char* p = ft_adobe_glyph_list;
+
+
+ if ( name == 0 || name >= limit )
+ goto NotFound;
+
+ c = *name++;
+ count = p[1];
+ p += 2;
+
+ min = 0;
+ max = count;
+
+ while ( min < max )
+ {
+ int mid = ( min + max ) >> 1;
+ const unsigned char* q = p + mid * 2;
+ int c2;
+
+
+ q = ft_adobe_glyph_list + ( ( (int)q[0] << 8 ) | q[1] );
+
+ c2 = q[0] & 127;
+ if ( c2 == c )
+ {
+ p = q;
+ goto Found;
+ }
+ if ( c2 < c )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+ goto NotFound;
+
+ Found:
+ for (;;)
+ {
+ /* assert (*p & 127) == c */
+
+ if ( name >= limit )
+ {
+ if ( (p[0] & 128) == 0 &&
+ (p[1] & 128) != 0 )
+ return (unsigned long)( ( (int)p[2] << 8 ) | p[3] );
+
+ goto NotFound;
+ }
+ c = *name++;
+ if ( p[0] & 128 )
+ {
+ p++;
+ if ( c != (p[0] & 127) )
+ goto NotFound;
+
+ continue;
+ }
+
+ p++;
+ count = p[0] & 127;
+ if ( p[0] & 128 )
+ p += 2;
+
+ p++;
+
+ for ( ; count > 0; count--, p += 2 )
+ {
+ int offset = ( (int)p[0] << 8 ) | p[1];
+ const unsigned char* q = ft_adobe_glyph_list + offset;
+
+ if ( c == ( q[0] & 127 ) )
+ {
+ p = q;
+ goto NextIter;
+ }
+ }
+ goto NotFound;
+
+ NextIter:
+ ;
+ }
+
+ NotFound:
+ return 0;
+ }
+
+
+/* END */
diff --git a/src/psnames/rules.mk b/src/psnames/rules.mk
new file mode 100644
index 0000000..06bd161
--- /dev/null
+++ b/src/psnames/rules.mk
@@ -0,0 +1,70 @@
+#
+# FreeType 2 PSNames driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# PSNames driver directory
+#
+PSNAMES_DIR := $(SRC_DIR)/psnames
+
+
+# compilation flags for the driver
+#
+PSNAMES_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PSNAMES_DIR))
+
+
+# PSNames driver sources (i.e., C files)
+#
+PSNAMES_DRV_SRC := $(PSNAMES_DIR)/psmodule.c
+
+
+# PSNames driver headers
+#
+PSNAMES_DRV_H := $(PSNAMES_DRV_SRC:%.c=%.h) \
+ $(PSNAMES_DIR)/pstables.h \
+ $(PSNAMES_DIR)/psnamerr.h
+
+
+# PSNames driver object(s)
+#
+# PSNAMES_DRV_OBJ_M is used during `multi' builds
+# PSNAMES_DRV_OBJ_S is used during `single' builds
+#
+PSNAMES_DRV_OBJ_M := $(PSNAMES_DRV_SRC:$(PSNAMES_DIR)/%.c=$(OBJ_DIR)/%.$O)
+PSNAMES_DRV_OBJ_S := $(OBJ_DIR)/psnames.$O
+
+# PSNames driver source file for single build
+#
+PSNAMES_DRV_SRC_S := $(PSNAMES_DIR)/psmodule.c
+
+
+# PSNames driver - single object
+#
+$(PSNAMES_DRV_OBJ_S): $(PSNAMES_DRV_SRC_S) $(PSNAMES_DRV_SRC) \
+ $(FREETYPE_H) $(PSNAMES_DRV_H)
+ $(PSNAMES_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSNAMES_DRV_SRC_S))
+
+
+# PSNames driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(PSNAMES_DIR)/%.c $(FREETYPE_H) $(PSNAMES_DRV_H)
+ $(PSNAMES_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(PSNAMES_DRV_OBJ_S)
+DRV_OBJS_M += $(PSNAMES_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/raster/Jamfile b/src/raster/Jamfile
new file mode 100644
index 0000000..f6e4251
--- /dev/null
+++ b/src/raster/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/raster Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) raster ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = ftraster ftrend1 ;
+ }
+ else
+ {
+ _sources = raster ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/raster Jamfile
diff --git a/src/raster/ftmisc.h b/src/raster/ftmisc.h
new file mode 100644
index 0000000..c5dbd50
--- /dev/null
+++ b/src/raster/ftmisc.h
@@ -0,0 +1,83 @@
+/***************************************************************************/
+/* */
+/* ftmisc.h */
+/* */
+/* Miscellaneous macros for stand-alone rasterizer (specification */
+/* only). */
+/* */
+/* Copyright 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used */
+/* modified and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /***************************************************/
+ /* */
+ /* This file is *not* portable! You have to adapt */
+ /* its definitions to your platform. */
+ /* */
+ /***************************************************/
+
+#ifndef __FTMISC_H__
+#define __FTMISC_H__
+
+#include <string.h> /* memset */
+
+#define FT_BEGIN_HEADER
+#define FT_END_HEADER
+
+#define FT_LOCAL_DEF( x ) static x
+
+ /* from include/freetype2/fttypes.h */
+
+ typedef unsigned char FT_Byte;
+ typedef signed int FT_Int;
+ typedef unsigned int FT_UInt;
+ typedef signed long FT_Long;
+ typedef unsigned long FT_ULong;
+ typedef signed long FT_F26Dot6;
+ typedef int FT_Error;
+
+#define FT_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
+ ( ( (FT_ULong)_x1 << 24 ) | \
+ ( (FT_ULong)_x2 << 16 ) | \
+ ( (FT_ULong)_x3 << 8 ) | \
+ (FT_ULong)_x4 )
+
+
+ /* from src/ftcalc.c */
+
+#include <inttypes.h>
+
+ typedef int64_t FT_Int64;
+
+ static FT_Long
+ FT_MulDiv( FT_Long a,
+ FT_Long b,
+ FT_Long c )
+ {
+ FT_Int s;
+ FT_Long d;
+
+
+ s = 1;
+ if ( a < 0 ) { a = -a; s = -1; }
+ if ( b < 0 ) { b = -b; s = -s; }
+ if ( c < 0 ) { c = -c; s = -s; }
+
+ d = (FT_Long)( c > 0 ? ( (FT_Int64)a * b + ( c >> 1 ) ) / c
+ : 0x7FFFFFFFL );
+
+ return ( s > 0 ) ? d : -d;
+ }
+
+#endif /* __FTMISC_H__ */
+
+
+/* END */
diff --git a/src/raster/ftraster.c b/src/raster/ftraster.c
new file mode 100644
index 0000000..4cfca4e
--- /dev/null
+++ b/src/raster/ftraster.c
@@ -0,0 +1,3369 @@
+/***************************************************************************/
+/* */
+/* ftraster.c */
+/* */
+/* The FreeType glyph rasterizer (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file can be compiled without the rest of the FreeType engine, by */
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */
+ /* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */
+ /* directory. Typically, you should do something like */
+ /* */
+ /* - copy `src/raster/ftraster.c' (this file) to your current directory */
+ /* */
+ /* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' */
+ /* to your current directory */
+ /* */
+ /* - compile `ftraster' with the _STANDALONE_ macro defined, as in */
+ /* */
+ /* cc -c -D_STANDALONE_ ftraster.c */
+ /* */
+ /* The renderer can be initialized with a call to */
+ /* `ft_standard_raster.raster_new'; a bitmap can be generated */
+ /* with a call to `ft_standard_raster.raster_render'. */
+ /* */
+ /* See the comments and documentation in the file `ftimage.h' for more */
+ /* details on how the raster works. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This is a rewrite of the FreeType 1.x scan-line converter */
+ /* */
+ /*************************************************************************/
+
+#ifdef _STANDALONE_
+
+#include "ftmisc.h"
+#include "ftimage.h"
+
+#else /* !_STANDALONE_ */
+
+#include <ft2build.h>
+#include "ftraster.h"
+#include FT_INTERNAL_CALC_H /* for FT_MulDiv only */
+
+#endif /* !_STANDALONE_ */
+
+
+ /*************************************************************************/
+ /* */
+ /* A simple technical note on how the raster works */
+ /* ----------------------------------------------- */
+ /* */
+ /* Converting an outline into a bitmap is achieved in several steps: */
+ /* */
+ /* 1 - Decomposing the outline into successive `profiles'. Each */
+ /* profile is simply an array of scanline intersections on a given */
+ /* dimension. A profile's main attributes are */
+ /* */
+ /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */
+ /* */
+ /* o an array of intersection coordinates for each scanline */
+ /* between `Ymin' and `Ymax'. */
+ /* */
+ /* o a direction, indicating whether it was built going `up' or */
+ /* `down', as this is very important for filling rules. */
+ /* */
+ /* 2 - Sweeping the target map's scanlines in order to compute segment */
+ /* `spans' which are then filled. Additionally, this pass */
+ /* performs drop-out control. */
+ /* */
+ /* The outline data is parsed during step 1 only. The profiles are */
+ /* built from the bottom of the render pool, used as a stack. The */
+ /* following graphics shows the profile list under construction: */
+ /* */
+ /* ____________________________________________________________ _ _ */
+ /* | | | | | */
+ /* | profile | coordinates for | profile | coordinates for |--> */
+ /* | 1 | profile 1 | 2 | profile 2 |--> */
+ /* |_________|___________________|_________|_________________|__ _ _ */
+ /* */
+ /* ^ ^ */
+ /* | | */
+ /* start of render pool top */
+ /* */
+ /* The top of the profile stack is kept in the `top' variable. */
+ /* */
+ /* As you can see, a profile record is pushed on top of the render */
+ /* pool, which is then followed by its coordinates/intersections. If */
+ /* a change of direction is detected in the outline, a new profile is */
+ /* generated until the end of the outline. */
+ /* */
+ /* Note that when all profiles have been generated, the function */
+ /* Finalize_Profile_Table() is used to record, for each profile, its */
+ /* bottom-most scanline as well as the scanline above its upmost */
+ /* boundary. These positions are called `y-turns' because they (sort */
+ /* of) correspond to local extrema. They are stored in a sorted list */
+ /* built from the top of the render pool as a downwards stack: */
+ /* */
+ /* _ _ _______________________________________ */
+ /* | | */
+ /* <--| sorted list of | */
+ /* <--| extrema scanlines | */
+ /* _ _ __________________|____________________| */
+ /* */
+ /* ^ ^ */
+ /* | | */
+ /* maxBuff sizeBuff = end of pool */
+ /* */
+ /* This list is later used during the sweep phase in order to */
+ /* optimize performance (see technical note on the sweep below). */
+ /* */
+ /* Of course, the raster detects whether the two stacks collide and */
+ /* handles the situation properly. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** CONFIGURATION MACROS **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /* define DEBUG_RASTER if you want to compile a debugging version */
+#define xxxDEBUG_RASTER
+
+ /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
+ /* 5-levels anti-aliasing */
+#ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
+#define FT_RASTER_OPTION_ANTI_ALIASING
+#endif
+
+ /* The size of the two-lines intermediate bitmap used */
+ /* for anti-aliasing, in bytes. */
+#define RASTER_GRAY_LINES 2048
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** OTHER MACROS (do not change) **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_raster
+
+
+#ifdef _STANDALONE_
+
+
+ /* This macro is used to indicate that a function parameter is unused. */
+ /* Its purpose is simply to reduce compiler warnings. Note also that */
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+ /* ANSI compilers (e.g. LCC). */
+#define FT_UNUSED( x ) (x) = (x)
+
+ /* Disable the tracing mechanism for simplicity -- developers can */
+ /* activate it easily by redefining these two macros. */
+#ifndef FT_ERROR
+#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef FT_TRACE
+#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
+#define FT_TRACE1( x ) do ; while ( 0 ) /* nothing */
+#define FT_TRACE6( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#define Raster_Err_None 0
+#define Raster_Err_Not_Ini -1
+#define Raster_Err_Overflow -2
+#define Raster_Err_Neg_Height -3
+#define Raster_Err_Invalid -4
+#define Raster_Err_Unsupported -5
+
+#define ft_memset memset
+
+#else /* _STANDALONE_ */
+
+
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H /* for FT_TRACE() and FT_ERROR() */
+
+#include "rasterrs.h"
+
+#define Raster_Err_None Raster_Err_Ok
+#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
+#define Raster_Err_Overflow Raster_Err_Raster_Overflow
+#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
+#define Raster_Err_Invalid Raster_Err_Invalid_Outline
+#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
+
+
+#endif /* _STANDALONE_ */
+
+
+#ifndef FT_MEM_SET
+#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
+#endif
+
+#ifndef FT_MEM_ZERO
+#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
+#endif
+
+ /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
+ /* typically a small value and the result of a*b is known to fit into */
+ /* 32 bits. */
+#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
+
+ /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
+ /* for clipping computations. It simply uses the FT_MulDiv() function */
+ /* defined in `ftcalc.h'. */
+#define SMulDiv FT_MulDiv
+
+ /* The rasterizer is a very general purpose component; please leave */
+ /* the following redefinitions there (you never know your target */
+ /* environment). */
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+#ifndef SUCCESS
+#define SUCCESS 0
+#endif
+
+#ifndef FAILURE
+#define FAILURE 1
+#endif
+
+
+#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
+ /* Setting this constant to more than 32 is a */
+ /* pure waste of space. */
+
+#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** SIMPLE TYPE DECLARATIONS **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ typedef int Int;
+ typedef unsigned int UInt;
+ typedef short Short;
+ typedef unsigned short UShort, *PUShort;
+ typedef long Long, *PLong;
+ typedef unsigned long ULong;
+
+ typedef unsigned char Byte, *PByte;
+ typedef char Bool;
+
+
+ typedef union Alignment_
+ {
+ long l;
+ void* p;
+ void (*f)(void);
+
+ } Alignment, *PAlignment;
+
+
+ typedef struct TPoint_
+ {
+ Long x;
+ Long y;
+
+ } TPoint;
+
+
+ typedef enum TFlow_
+ {
+ Flow_None = 0,
+ Flow_Up = 1,
+ Flow_Down = -1
+
+ } TFlow;
+
+
+ /* States of each line, arc, and profile */
+ typedef enum TStates_
+ {
+ Unknown_State,
+ Ascending_State,
+ Descending_State,
+ Flat_State
+
+ } TStates;
+
+
+ typedef struct TProfile_ TProfile;
+ typedef TProfile* PProfile;
+
+ struct TProfile_
+ {
+ FT_F26Dot6 X; /* current coordinate during sweep */
+ PProfile link; /* link to next profile - various purpose */
+ PLong offset; /* start of profile's data in render pool */
+ int flow; /* Profile orientation: Asc/Descending */
+ long height; /* profile's height in scanlines */
+ long start; /* profile's starting scanline */
+
+ unsigned countL; /* number of lines to step before this */
+ /* profile becomes drawable */
+
+ PProfile next; /* next profile in same contour, used */
+ /* during drop-out control */
+ };
+
+ typedef PProfile TProfileList;
+ typedef PProfile* PProfileList;
+
+
+ /* Simple record used to implement a stack of bands, required */
+ /* by the sub-banding mechanism */
+ typedef struct TBand_
+ {
+ Short y_min; /* band's minimum */
+ Short y_max; /* band's maximum */
+
+ } TBand;
+
+
+#define AlignProfileSize \
+ ( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( long ) )
+
+
+#ifdef FT_STATIC_RASTER
+
+
+#define RAS_ARGS /* void */
+#define RAS_ARG /* void */
+
+#define RAS_VARS /* void */
+#define RAS_VAR /* void */
+
+#define FT_UNUSED_RASTER do ; while ( 0 )
+
+
+#else /* FT_STATIC_RASTER */
+
+
+#define RAS_ARGS PWorker worker,
+#define RAS_ARG PWorker worker
+
+#define RAS_VARS worker,
+#define RAS_VAR worker
+
+#define FT_UNUSED_RASTER FT_UNUSED( worker )
+
+
+#endif /* FT_STATIC_RASTER */
+
+
+ typedef struct TWorker_ TWorker, *PWorker;
+
+
+ /* prototypes used for sweep function dispatch */
+ typedef void
+ Function_Sweep_Init( RAS_ARGS Short* min,
+ Short* max );
+
+ typedef void
+ Function_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right );
+
+ typedef void
+ Function_Sweep_Step( RAS_ARG );
+
+
+ /* NOTE: These operations are only valid on 2's complement processors */
+
+#define FLOOR( x ) ( (x) & -ras.precision )
+#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
+#define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
+#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
+#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
+
+ /* Note that I have moved the location of some fields in the */
+ /* structure to ensure that the most used variables are used */
+ /* at the top. Thus, their offset can be coded with less */
+ /* opcodes, and it results in a smaller executable. */
+
+ struct TWorker_
+ {
+ Int precision_bits; /* precision related variables */
+ Int precision;
+ Int precision_half;
+ Long precision_mask;
+ Int precision_shift;
+ Int precision_step;
+ Int precision_jitter;
+
+ Int scale_shift; /* == precision_shift for bitmaps */
+ /* == precision_shift+1 for pixmaps */
+
+ PLong buff; /* The profiles buffer */
+ PLong sizeBuff; /* Render pool size */
+ PLong maxBuff; /* Profiles buffer size */
+ PLong top; /* Current cursor in buffer */
+
+ FT_Error error;
+
+ Int numTurns; /* number of Y-turns in outline */
+
+ TPoint* arc; /* current Bezier arc pointer */
+
+ UShort bWidth; /* target bitmap width */
+ PByte bTarget; /* target bitmap buffer */
+ PByte gTarget; /* target pixmap buffer */
+
+ Long lastX, lastY, minY, maxY;
+
+ UShort num_Profs; /* current number of profiles */
+
+ Bool fresh; /* signals a fresh new profile which */
+ /* 'start' field must be completed */
+ Bool joint; /* signals that the last arc ended */
+ /* exactly on a scanline. Allows */
+ /* removal of doublets */
+ PProfile cProfile; /* current profile */
+ PProfile fProfile; /* head of linked list of profiles */
+ PProfile gProfile; /* contour's first profile in case */
+ /* of impact */
+
+ TStates state; /* rendering state */
+
+ FT_Bitmap target; /* description of target bit/pixmap */
+ FT_Outline outline;
+
+ Long traceOfs; /* current offset in target bitmap */
+ Long traceG; /* current offset in target pixmap */
+
+ Short traceIncr; /* sweep's increment in target bitmap */
+
+ Short gray_min_x; /* current min x during gray rendering */
+ Short gray_max_x; /* current max x during gray rendering */
+
+ /* dispatch variables */
+
+ Function_Sweep_Init* Proc_Sweep_Init;
+ Function_Sweep_Span* Proc_Sweep_Span;
+ Function_Sweep_Span* Proc_Sweep_Drop;
+ Function_Sweep_Step* Proc_Sweep_Step;
+
+ Byte dropOutControl; /* current drop_out control method */
+
+ Bool second_pass; /* indicates whether a horizontal pass */
+ /* should be performed to control */
+ /* drop-out accurately when calling */
+ /* Render_Glyph. Note that there is */
+ /* no horizontal pass during gray */
+ /* rendering. */
+
+ TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
+
+ TBand band_stack[16]; /* band stack used for sub-banding */
+ Int band_top; /* band stack top */
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+ Byte* grays;
+
+ Byte gray_lines[RASTER_GRAY_LINES];
+ /* Intermediate table used to render the */
+ /* graylevels pixmaps. */
+ /* gray_lines is a buffer holding two */
+ /* monochrome scanlines */
+
+ Short gray_width; /* width in bytes of one monochrome */
+ /* intermediate scanline of gray_lines. */
+ /* Each gray pixel takes 2 bits long there */
+
+ /* The gray_lines must hold 2 lines, thus with size */
+ /* in bytes of at least `gray_width*2'. */
+
+#endif /* FT_RASTER_ANTI_ALIASING */
+
+ };
+
+
+ typedef struct TRaster_
+ {
+ char* buffer;
+ long buffer_size;
+ void* memory;
+ PWorker worker;
+ Byte grays[5];
+ Short gray_width;
+
+ } TRaster, *PRaster;
+
+#ifdef FT_STATIC_RASTER
+
+ static TWorker cur_ras;
+#define ras cur_ras
+
+#else
+
+#define ras (*worker)
+
+#endif /* FT_STATIC_RASTER */
+
+
+static const char count_table[256] =
+{
+ 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7,
+ 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 };
+
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** PROFILES COMPUTATION **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Set_High_Precision */
+ /* */
+ /* <Description> */
+ /* Sets precision variables according to param flag. */
+ /* */
+ /* <Input> */
+ /* High :: Set to True for high precision (typically for ppem < 18), */
+ /* false otherwise. */
+ /* */
+ static void
+ Set_High_Precision( RAS_ARGS Int High )
+ {
+ if ( High )
+ {
+ ras.precision_bits = 10;
+ ras.precision_step = 128;
+ ras.precision_jitter = 24;
+ }
+ else
+ {
+ ras.precision_bits = 6;
+ ras.precision_step = 32;
+ ras.precision_jitter = 2;
+ }
+
+ FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
+
+ ras.precision = 1 << ras.precision_bits;
+ ras.precision_half = ras.precision / 2;
+ ras.precision_shift = ras.precision_bits - Pixel_Bits;
+ ras.precision_mask = -ras.precision;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* New_Profile */
+ /* */
+ /* <Description> */
+ /* Creates a new profile in the render pool. */
+ /* */
+ /* <Input> */
+ /* aState :: The state/orientation of the new profile. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
+ /* profile. */
+ /* */
+ static Bool
+ New_Profile( RAS_ARGS TStates aState )
+ {
+ if ( !ras.fProfile )
+ {
+ ras.cProfile = (PProfile)ras.top;
+ ras.fProfile = ras.cProfile;
+ ras.top += AlignProfileSize;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ switch ( aState )
+ {
+ case Ascending_State:
+ ras.cProfile->flow = Flow_Up;
+ FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ case Descending_State:
+ ras.cProfile->flow = Flow_Down;
+ FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));
+ break;
+
+ default:
+ FT_ERROR(( "New_Profile: invalid profile direction!\n" ));
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ ras.cProfile->start = 0;
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ ras.cProfile->link = (PProfile)0;
+ ras.cProfile->next = (PProfile)0;
+
+ if ( !ras.gProfile )
+ ras.gProfile = ras.cProfile;
+
+ ras.state = aState;
+ ras.fresh = TRUE;
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* End_Profile */
+ /* */
+ /* <Description> */
+ /* Finalizes the current profile. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
+ /* */
+ static Bool
+ End_Profile( RAS_ARG )
+ {
+ Long h;
+ PProfile oldProfile;
+
+
+ h = (Long)( ras.top - ras.cProfile->offset );
+
+ if ( h < 0 )
+ {
+ FT_ERROR(( "End_Profile: negative height encountered!\n" ));
+ ras.error = Raster_Err_Neg_Height;
+ return FAILURE;
+ }
+
+ if ( h > 0 )
+ {
+ FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
+ (long)ras.cProfile, ras.cProfile->start, h ));
+
+ oldProfile = ras.cProfile;
+ ras.cProfile->height = h;
+ ras.cProfile = (PProfile)ras.top;
+
+ ras.top += AlignProfileSize;
+
+ ras.cProfile->height = 0;
+ ras.cProfile->offset = ras.top;
+ oldProfile->next = ras.cProfile;
+ ras.num_Profs++;
+ }
+
+ if ( ras.top >= ras.maxBuff )
+ {
+ FT_TRACE1(( "overflow in End_Profile\n" ));
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ ras.joint = FALSE;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Insert_Y_Turn */
+ /* */
+ /* <Description> */
+ /* Inserts a salient into the sorted list placed on top of the render */
+ /* pool. */
+ /* */
+ /* <Input> */
+ /* New y scanline position. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow. */
+ /* */
+ static Bool
+ Insert_Y_Turn( RAS_ARGS Int y )
+ {
+ PLong y_turns;
+ Int y2, n;
+
+
+ n = ras.numTurns - 1;
+ y_turns = ras.sizeBuff - ras.numTurns;
+
+ /* look for first y value that is <= */
+ while ( n >= 0 && y < y_turns[n] )
+ n--;
+
+ /* if it is <, simply insert it, ignore if == */
+ if ( n >= 0 && y > y_turns[n] )
+ while ( n >= 0 )
+ {
+ y2 = (Int)y_turns[n];
+ y_turns[n] = y;
+ y = y2;
+ n--;
+ }
+
+ if ( n < 0 )
+ {
+ ras.maxBuff--;
+ if ( ras.maxBuff <= ras.top )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+ ras.numTurns++;
+ ras.sizeBuff[-ras.numTurns] = y;
+ }
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Finalize_Profile_Table */
+ /* */
+ /* <Description> */
+ /* Adjusts all links in the profiles list. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success. FAILURE in case of overflow. */
+ /* */
+ static Bool
+ Finalize_Profile_Table( RAS_ARG )
+ {
+ Int bottom, top;
+ UShort n;
+ PProfile p;
+
+
+ n = ras.num_Profs;
+
+ if ( n > 1 )
+ {
+ p = ras.fProfile;
+ while ( n > 0 )
+ {
+ if ( n > 1 )
+ p->link = (PProfile)( p->offset + p->height );
+ else
+ p->link = NULL;
+
+ switch ( p->flow )
+ {
+ case Flow_Down:
+ bottom = (Int)( p->start - p->height + 1 );
+ top = (Int)p->start;
+ p->start = bottom;
+ p->offset += p->height - 1;
+ break;
+
+ case Flow_Up:
+ default:
+ bottom = (Int)p->start;
+ top = (Int)( p->start + p->height - 1 );
+ }
+
+ if ( Insert_Y_Turn( RAS_VARS bottom ) ||
+ Insert_Y_Turn( RAS_VARS top + 1 ) )
+ return FAILURE;
+
+ p = p->link;
+ n--;
+ }
+ }
+ else
+ ras.fProfile = NULL;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Split_Conic */
+ /* */
+ /* <Description> */
+ /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */
+ /* stack. */
+ /* */
+ /* <Input> */
+ /* None (subdivided Bezier is taken from the top of the stack). */
+ /* */
+ /* <Note> */
+ /* This routine is the `beef' of this component. It is _the_ inner */
+ /* loop that should be optimized to hell to get the best performance. */
+ /* */
+ static void
+ Split_Conic( TPoint* base )
+ {
+ Long a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+
+ /* hand optimized. gcc doesn't seem to be too good at common */
+ /* expression substitution and instruction scheduling ;-) */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Split_Cubic */
+ /* */
+ /* <Description> */
+ /* Subdivides a third-order Bezier arc into two joint sub-arcs in the */
+ /* Bezier stack. */
+ /* */
+ /* <Note> */
+ /* This routine is the `beef' of the component. It is one of _the_ */
+ /* inner loops that should be optimized like hell to get the best */
+ /* performance. */
+ /* */
+ static void
+ Split_Cubic( TPoint* base )
+ {
+ Long a, b, c, d;
+
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c + 1 ) >> 1;
+ base[5].x = b = ( base[3].x + d + 1 ) >> 1;
+ c = ( c + d + 1 ) >> 1;
+ base[2].x = a = ( a + c + 1 ) >> 1;
+ base[4].x = b = ( b + c + 1 ) >> 1;
+ base[3].x = ( a + b + 1 ) >> 1;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c + 1 ) >> 1;
+ base[5].y = b = ( base[3].y + d + 1 ) >> 1;
+ c = ( c + d + 1 ) >> 1;
+ base[2].y = a = ( a + c + 1 ) >> 1;
+ base[4].y = b = ( b + c + 1 ) >> 1;
+ base[3].y = ( a + b + 1 ) >> 1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Line_Up */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an ascending line segment and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* x1 :: The x-coordinate of the segment's start point. */
+ /* */
+ /* y1 :: The y-coordinate of the segment's start point. */
+ /* */
+ /* x2 :: The x-coordinate of the segment's end point. */
+ /* */
+ /* y2 :: The y-coordinate of the segment's end point. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Line_Up( RAS_ARGS Long x1,
+ Long y1,
+ Long x2,
+ Long y2,
+ Long miny,
+ Long maxy )
+ {
+ Long Dx, Dy;
+ Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
+ Long Ix, Rx, Ax;
+
+ PLong top;
+
+
+ Dx = x2 - x1;
+ Dy = y2 - y1;
+
+ if ( Dy <= 0 || y2 < miny || y1 > maxy )
+ return SUCCESS;
+
+ if ( y1 < miny )
+ {
+ /* Take care: miny-y1 can be a very large value; we use */
+ /* a slow MulDiv function to avoid clipping bugs */
+ x1 += SMulDiv( Dx, miny - y1, Dy );
+ e1 = (Int)TRUNC( miny );
+ f1 = 0;
+ }
+ else
+ {
+ e1 = (Int)TRUNC( y1 );
+ f1 = (Int)FRAC( y1 );
+ }
+
+ if ( y2 > maxy )
+ {
+ /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
+ e2 = (Int)TRUNC( maxy );
+ f2 = 0;
+ }
+ else
+ {
+ e2 = (Int)TRUNC( y2 );
+ f2 = (Int)FRAC( y2 );
+ }
+
+ if ( f1 > 0 )
+ {
+ if ( e1 == e2 )
+ return SUCCESS;
+ else
+ {
+ x1 += FMulDiv( Dx, ras.precision - f1, Dy );
+ e1 += 1;
+ }
+ }
+ else
+ if ( ras.joint )
+ {
+ ras.top--;
+ ras.joint = FALSE;
+ }
+
+ ras.joint = (char)( f2 == 0 );
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = e1;
+ ras.fresh = FALSE;
+ }
+
+ size = e2 - e1 + 1;
+ if ( ras.top + size >= ras.maxBuff )
+ {
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ if ( Dx > 0 )
+ {
+ Ix = ( ras.precision * Dx ) / Dy;
+ Rx = ( ras.precision * Dx ) % Dy;
+ Dx = 1;
+ }
+ else
+ {
+ Ix = -( ( ras.precision * -Dx ) / Dy );
+ Rx = ( ras.precision * -Dx ) % Dy;
+ Dx = -1;
+ }
+
+ Ax = -Dy;
+ top = ras.top;
+
+ while ( size > 0 )
+ {
+ *top++ = x1;
+
+ x1 += Ix;
+ Ax += Rx;
+ if ( Ax >= 0 )
+ {
+ Ax -= Dy;
+ x1 += Dx;
+ }
+ size--;
+ }
+
+ ras.top = top;
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Line_Down */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an descending line segment and */
+ /* stores them in the render pool. */
+ /* */
+ /* <Input> */
+ /* x1 :: The x-coordinate of the segment's start point. */
+ /* */
+ /* y1 :: The y-coordinate of the segment's start point. */
+ /* */
+ /* x2 :: The x-coordinate of the segment's end point. */
+ /* */
+ /* y2 :: The y-coordinate of the segment's end point. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Line_Down( RAS_ARGS Long x1,
+ Long y1,
+ Long x2,
+ Long y2,
+ Long miny,
+ Long maxy )
+ {
+ Bool result, fresh;
+
+
+ fresh = ras.fresh;
+
+ result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ return result;
+ }
+
+
+ /* A function type describing the functions used to split Bezier arcs */
+ typedef void (*TSplitter)( TPoint* base );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Bezier_Up */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an ascending Bezier arc and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* degree :: The degree of the Bezier arc (either 2 or 3). */
+ /* */
+ /* splitter :: The function to split Bezier arcs. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Bezier_Up( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ Long y1, y2, e, e2, e0;
+ Short f1;
+
+ TPoint* arc;
+ TPoint* start_arc;
+
+ PLong top;
+
+
+ arc = ras.arc;
+ y1 = arc[degree].y;
+ y2 = arc[0].y;
+ top = ras.top;
+
+ if ( y2 < miny || y1 > maxy )
+ goto Fin;
+
+ e2 = FLOOR( y2 );
+
+ if ( e2 > maxy )
+ e2 = maxy;
+
+ e0 = miny;
+
+ if ( y1 < miny )
+ e = miny;
+ else
+ {
+ e = CEILING( y1 );
+ f1 = (Short)( FRAC( y1 ) );
+ e0 = e;
+
+ if ( f1 == 0 )
+ {
+ if ( ras.joint )
+ {
+ top--;
+ ras.joint = FALSE;
+ }
+
+ *top++ = arc[degree].x;
+
+ e += ras.precision;
+ }
+ }
+
+ if ( ras.fresh )
+ {
+ ras.cProfile->start = TRUNC( e0 );
+ ras.fresh = FALSE;
+ }
+
+ if ( e2 < e )
+ goto Fin;
+
+ if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
+ {
+ ras.top = top;
+ ras.error = Raster_Err_Overflow;
+ return FAILURE;
+ }
+
+ start_arc = arc;
+
+ while ( arc >= start_arc && e <= e2 )
+ {
+ ras.joint = FALSE;
+
+ y2 = arc[0].y;
+
+ if ( y2 > e )
+ {
+ y1 = arc[degree].y;
+ if ( y2 - y1 >= ras.precision_step )
+ {
+ splitter( arc );
+ arc += degree;
+ }
+ else
+ {
+ *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,
+ e - y1, y2 - y1 );
+ arc -= degree;
+ e += ras.precision;
+ }
+ }
+ else
+ {
+ if ( y2 == e )
+ {
+ ras.joint = TRUE;
+ *top++ = arc[0].x;
+
+ e += ras.precision;
+ }
+ arc -= degree;
+ }
+ }
+
+ Fin:
+ ras.top = top;
+ ras.arc -= degree;
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Bezier_Down */
+ /* */
+ /* <Description> */
+ /* Computes the x-coordinates of an descending Bezier arc and stores */
+ /* them in the render pool. */
+ /* */
+ /* <Input> */
+ /* degree :: The degree of the Bezier arc (either 2 or 3). */
+ /* */
+ /* splitter :: The function to split Bezier arcs. */
+ /* */
+ /* miny :: A lower vertical clipping bound value. */
+ /* */
+ /* maxy :: An upper vertical clipping bound value. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow. */
+ /* */
+ static Bool
+ Bezier_Down( RAS_ARGS Int degree,
+ TSplitter splitter,
+ Long miny,
+ Long maxy )
+ {
+ TPoint* arc = ras.arc;
+ Bool result, fresh;
+
+
+ arc[0].y = -arc[0].y;
+ arc[1].y = -arc[1].y;
+ arc[2].y = -arc[2].y;
+ if ( degree > 2 )
+ arc[3].y = -arc[3].y;
+
+ fresh = ras.fresh;
+
+ result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
+
+ if ( fresh && !ras.fresh )
+ ras.cProfile->start = -ras.cProfile->start;
+
+ arc[0].y = -arc[0].y;
+ return result;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Line_To */
+ /* */
+ /* <Description> */
+ /* Injects a new line segment and adjusts Profiles list. */
+ /* */
+ /* <Input> */
+ /* x :: The x-coordinate of the segment's end point (its start point */
+ /* is stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the segment's end point (its start point */
+ /* is stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Line_To( RAS_ARGS Long x,
+ Long y )
+ {
+ /* First, detect a change of direction */
+
+ switch ( ras.state )
+ {
+ case Unknown_State:
+ if ( y > ras.lastY )
+ {
+ if ( New_Profile( RAS_VARS Ascending_State ) )
+ return FAILURE;
+ }
+ else
+ {
+ if ( y < ras.lastY )
+ if ( New_Profile( RAS_VARS Descending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ case Ascending_State:
+ if ( y < ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Descending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ case Descending_State:
+ if ( y > ras.lastY )
+ {
+ if ( End_Profile( RAS_VAR ) ||
+ New_Profile( RAS_VARS Ascending_State ) )
+ return FAILURE;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ /* Then compute the lines */
+
+ switch ( ras.state )
+ {
+ case Ascending_State:
+ if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ case Descending_State:
+ if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
+ x, y, ras.minY, ras.maxY ) )
+ return FAILURE;
+ break;
+
+ default:
+ ;
+ }
+
+ ras.lastX = x;
+ ras.lastY = y;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Conic_To */
+ /* */
+ /* <Description> */
+ /* Injects a new conic arc and adjusts the profile list. */
+ /* */
+ /* <Input> */
+ /* cx :: The x-coordinate of the arc's new control point. */
+ /* */
+ /* cy :: The y-coordinate of the arc's new control point. */
+ /* */
+ /* x :: The x-coordinate of the arc's end point (its start point is */
+ /* stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the arc's end point (its start point is */
+ /* stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Conic_To( RAS_ARGS Long cx,
+ Long cy,
+ Long x,
+ Long y )
+ {
+ Long y1, y2, y3, x3, ymin, ymax;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[2].x = ras.lastX;
+ ras.arc[2].y = ras.lastY;
+ ras.arc[1].x = cx; ras.arc[1].y = cy;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ do
+ {
+ y1 = ras.arc[2].y;
+ y2 = ras.arc[1].y;
+ y3 = ras.arc[0].y;
+ x3 = ras.arc[0].x;
+
+ /* first, categorize the Bezier arc */
+
+ if ( y1 <= y3 )
+ {
+ ymin = y1;
+ ymax = y3;
+ }
+ else
+ {
+ ymin = y3;
+ ymax = y1;
+ }
+
+ if ( y2 < ymin || y2 > ymax )
+ {
+ /* this arc has no given direction, split it! */
+ Split_Conic( ras.arc );
+ ras.arc += 2;
+ }
+ else if ( y1 == y3 )
+ {
+ /* this arc is flat, ignore it and pop it from the Bezier stack */
+ ras.arc -= 2;
+ }
+ else
+ {
+ /* the arc is y-monotonous, either ascending or descending */
+ /* detect a change of direction */
+ state_bez = y1 < y3 ? Ascending_State : Descending_State;
+ if ( ras.state != state_bez )
+ {
+ /* finalize current profile if any */
+ if ( ras.state != Unknown_State &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ /* create a new profile */
+ if ( New_Profile( RAS_VARS state_bez ) )
+ goto Fail;
+ }
+
+ /* now call the appropriate routine */
+ if ( state_bez == Ascending_State )
+ {
+ if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x3;
+ ras.lastY = y3;
+
+ return SUCCESS;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Cubic_To */
+ /* */
+ /* <Description> */
+ /* Injects a new cubic arc and adjusts the profile list. */
+ /* */
+ /* <Input> */
+ /* cx1 :: The x-coordinate of the arc's first new control point. */
+ /* */
+ /* cy1 :: The y-coordinate of the arc's first new control point. */
+ /* */
+ /* cx2 :: The x-coordinate of the arc's second new control point. */
+ /* */
+ /* cy2 :: The y-coordinate of the arc's second new control point. */
+ /* */
+ /* x :: The x-coordinate of the arc's end point (its start point is */
+ /* stored in `lastX'). */
+ /* */
+ /* y :: The y-coordinate of the arc's end point (its start point is */
+ /* stored in `lastY'). */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
+ /* profile. */
+ /* */
+ static Bool
+ Cubic_To( RAS_ARGS Long cx1,
+ Long cy1,
+ Long cx2,
+ Long cy2,
+ Long x,
+ Long y )
+ {
+ Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
+ TStates state_bez;
+
+
+ ras.arc = ras.arcs;
+ ras.arc[3].x = ras.lastX;
+ ras.arc[3].y = ras.lastY;
+ ras.arc[2].x = cx1; ras.arc[2].y = cy1;
+ ras.arc[1].x = cx2; ras.arc[1].y = cy2;
+ ras.arc[0].x = x; ras.arc[0].y = y;
+
+ do
+ {
+ y1 = ras.arc[3].y;
+ y2 = ras.arc[2].y;
+ y3 = ras.arc[1].y;
+ y4 = ras.arc[0].y;
+ x4 = ras.arc[0].x;
+
+ /* first, categorize the Bezier arc */
+
+ if ( y1 <= y4 )
+ {
+ ymin1 = y1;
+ ymax1 = y4;
+ }
+ else
+ {
+ ymin1 = y4;
+ ymax1 = y1;
+ }
+
+ if ( y2 <= y3 )
+ {
+ ymin2 = y2;
+ ymax2 = y3;
+ }
+ else
+ {
+ ymin2 = y3;
+ ymax2 = y2;
+ }
+
+ if ( ymin2 < ymin1 || ymax2 > ymax1 )
+ {
+ /* this arc has no given direction, split it! */
+ Split_Cubic( ras.arc );
+ ras.arc += 3;
+ }
+ else if ( y1 == y4 )
+ {
+ /* this arc is flat, ignore it and pop it from the Bezier stack */
+ ras.arc -= 3;
+ }
+ else
+ {
+ state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
+
+ /* detect a change of direction */
+ if ( ras.state != state_bez )
+ {
+ if ( ras.state != Unknown_State &&
+ End_Profile( RAS_VAR ) )
+ goto Fail;
+
+ if ( New_Profile( RAS_VARS state_bez ) )
+ goto Fail;
+ }
+
+ /* compute intersections */
+ if ( state_bez == Ascending_State )
+ {
+ if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+ else
+ if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
+ goto Fail;
+ }
+
+ } while ( ras.arc >= ras.arcs );
+
+ ras.lastX = x4;
+ ras.lastY = y4;
+
+ return SUCCESS;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+#undef SWAP_
+#define SWAP_( x, y ) do \
+ { \
+ Long swap = x; \
+ \
+ \
+ x = y; \
+ y = swap; \
+ } while ( 0 )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Decompose_Curve */
+ /* */
+ /* <Description> */
+ /* Scans the outline arrays in order to emit individual segments and */
+ /* Beziers by calling Line_To() and Bezier_To(). It handles all */
+ /* weird cases, like when the first point is off the curve, or when */
+ /* there are simply no `on' points in the contour! */
+ /* */
+ /* <Input> */
+ /* first :: The index of the first point in the contour. */
+ /* */
+ /* last :: The index of the last point in the contour. */
+ /* */
+ /* flipped :: If set, flip the direction of the curve. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE on error. */
+ /* */
+ static Bool
+ Decompose_Curve( RAS_ARGS UShort first,
+ UShort last,
+ int flipped )
+ {
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* points;
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ unsigned tag; /* current point's state */
+
+
+ points = ras.outline.points;
+ limit = points + last;
+
+ v_start.x = SCALED( points[first].x );
+ v_start.y = SCALED( points[first].y );
+ v_last.x = SCALED( points[last].x );
+ v_last.y = SCALED( points[last].y );
+
+ if ( flipped )
+ {
+ SWAP_( v_start.x, v_start.y );
+ SWAP_( v_last.x, v_last.y );
+ }
+
+ v_control = v_start;
+
+ point = points + first;
+ tags = ras.outline.tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ ras.lastX = v_start.x;
+ ras.lastY = v_start.y;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ Long x, y;
+
+
+ x = SCALED( point->x );
+ y = SCALED( point->y );
+ if ( flipped )
+ SWAP_( x, y );
+
+ if ( Line_To( RAS_VARS x, y ) )
+ goto Fail;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ v_control.x = SCALED( point[0].x );
+ v_control.y = SCALED( point[0].y );
+
+ if ( flipped )
+ SWAP_( v_control.x, v_control.y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector v_middle;
+ Long x, y;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ x = SCALED( point[0].x );
+ y = SCALED( point[0].y );
+
+ if ( flipped )
+ SWAP_( x, y );
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
+ goto Fail;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + x ) / 2;
+ v_middle.y = ( v_control.y + y ) / 2;
+
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_middle.x, v_middle.y ) )
+ goto Fail;
+
+ v_control.x = x;
+ v_control.y = y;
+
+ goto Do_Conic;
+ }
+
+ if ( Conic_To( RAS_VARS v_control.x, v_control.y,
+ v_start.x, v_start.y ) )
+ goto Fail;
+
+ goto Close;
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ Long x1, y1, x2, y2, x3, y3;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ x1 = SCALED( point[-2].x );
+ y1 = SCALED( point[-2].y );
+ x2 = SCALED( point[-1].x );
+ y2 = SCALED( point[-1].y );
+ x3 = SCALED( point[ 0].x );
+ y3 = SCALED( point[ 0].y );
+
+ if ( flipped )
+ {
+ SWAP_( x1, y1 );
+ SWAP_( x2, y2 );
+ SWAP_( x3, y3 );
+ }
+
+ if ( point <= limit )
+ {
+ if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
+ goto Fail;
+ continue;
+ }
+
+ if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
+ goto Fail;
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
+ goto Fail;
+
+ Close:
+ return SUCCESS;
+
+ Invalid_Outline:
+ ras.error = Raster_Err_Invalid;
+
+ Fail:
+ return FAILURE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Convert_Glyph */
+ /* */
+ /* <Description> */
+ /* Converts a glyph into a series of segments and arcs and makes a */
+ /* profiles list with them. */
+ /* */
+ /* <Input> */
+ /* flipped :: If set, flip the direction of curve. */
+ /* */
+ /* <Return> */
+ /* SUCCESS on success, FAILURE if any error was encountered during */
+ /* rendering. */
+ /* */
+ static Bool
+ Convert_Glyph( RAS_ARGS int flipped )
+ {
+ int i;
+ unsigned start;
+
+ PProfile lastProfile;
+
+
+ ras.fProfile = NULL;
+ ras.joint = FALSE;
+ ras.fresh = FALSE;
+
+ ras.maxBuff = ras.sizeBuff - AlignProfileSize;
+
+ ras.numTurns = 0;
+
+ ras.cProfile = (PProfile)ras.top;
+ ras.cProfile->offset = ras.top;
+ ras.num_Profs = 0;
+
+ start = 0;
+
+ for ( i = 0; i < ras.outline.n_contours; i++ )
+ {
+ ras.state = Unknown_State;
+ ras.gProfile = NULL;
+
+ if ( Decompose_Curve( RAS_VARS (unsigned short)start,
+ ras.outline.contours[i],
+ flipped ) )
+ return FAILURE;
+
+ start = ras.outline.contours[i] + 1;
+
+ /* We must now see whether the extreme arcs join or not */
+ if ( FRAC( ras.lastY ) == 0 &&
+ ras.lastY >= ras.minY &&
+ ras.lastY <= ras.maxY )
+ if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
+ ras.top--;
+ /* Note that ras.gProfile can be nil if the contour was too small */
+ /* to be drawn. */
+
+ lastProfile = ras.cProfile;
+ if ( End_Profile( RAS_VAR ) )
+ return FAILURE;
+
+ /* close the `next profile in contour' linked list */
+ if ( ras.gProfile )
+ lastProfile->next = ras.gProfile;
+ }
+
+ if ( Finalize_Profile_Table( RAS_VAR ) )
+ return FAILURE;
+
+ return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /** **/
+ /** SCAN-LINE SWEEPS AND DRAWING **/
+ /** **/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Init_Linked */
+ /* */
+ /* Initializes an empty linked list. */
+ /* */
+ static void
+ Init_Linked( TProfileList* l )
+ {
+ *l = NULL;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* InsNew */
+ /* */
+ /* Inserts a new profile in a linked list. */
+ /* */
+ static void
+ InsNew( PProfileList list,
+ PProfile profile )
+ {
+ PProfile *old, current;
+ Long x;
+
+
+ old = list;
+ current = *old;
+ x = profile->X;
+
+ while ( current )
+ {
+ if ( x < current->X )
+ break;
+ old = &current->link;
+ current = *old;
+ }
+
+ profile->link = current;
+ *old = profile;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DelOld */
+ /* */
+ /* Removes an old profile from a linked list. */
+ /* */
+ static void
+ DelOld( PProfileList list,
+ PProfile profile )
+ {
+ PProfile *old, current;
+
+
+ old = list;
+ current = *old;
+
+ while ( current )
+ {
+ if ( current == profile )
+ {
+ *old = current->link;
+ return;
+ }
+
+ old = &current->link;
+ current = *old;
+ }
+
+ /* we should never get there, unless the profile was not part of */
+ /* the list. */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Sort */
+ /* */
+ /* Sorts a trace list. In 95%, the list is already sorted. We need */
+ /* an algorithm which is fast in this case. Bubble sort is enough */
+ /* and simple. */
+ /* */
+ static void
+ Sort( PProfileList list )
+ {
+ PProfile *old, current, next;
+
+
+ /* First, set the new X coordinate of each profile */
+ current = *list;
+ while ( current )
+ {
+ current->X = *current->offset;
+ current->offset += current->flow;
+ current->height--;
+ current = current->link;
+ }
+
+ /* Then sort them */
+ old = list;
+ current = *old;
+
+ if ( !current )
+ return;
+
+ next = current->link;
+
+ while ( next )
+ {
+ if ( current->X <= next->X )
+ {
+ old = &current->link;
+ current = *old;
+
+ if ( !current )
+ return;
+ }
+ else
+ {
+ *old = next;
+ current->link = next->link;
+ next->link = current;
+
+ old = list;
+ current = *old;
+ }
+
+ next = current->link;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Vertical Sweep Procedure Set */
+ /* */
+ /* These four routines are used during the vertical black/white sweep */
+ /* phase by the generic Draw_Sweep() function. */
+ /* */
+ /*************************************************************************/
+
+ static void
+ Vertical_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ Long pitch = ras.target.pitch;
+
+ FT_UNUSED( max );
+
+
+ ras.traceIncr = (Short)-pitch;
+ ras.traceOfs = -*min * pitch;
+ if ( pitch > 0 )
+ ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
+
+ ras.gray_min_x = 0;
+ ras.gray_max_x = 0;
+ }
+
+
+ static void
+ Vertical_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ int c1, c2;
+ Byte f1, f2;
+ Byte* target;
+
+ FT_UNUSED( y );
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+
+
+ /* Drop-out control */
+
+ e1 = TRUNC( CEILING( x1 ) );
+
+ if ( x2 - x1 - ras.precision <= ras.precision_jitter )
+ e2 = e1;
+ else
+ e2 = TRUNC( FLOOR( x2 ) );
+
+ if ( e2 >= 0 && e1 < ras.bWidth )
+ {
+ if ( e1 < 0 )
+ e1 = 0;
+ if ( e2 >= ras.bWidth )
+ e2 = ras.bWidth - 1;
+
+ c1 = (Short)( e1 >> 3 );
+ c2 = (Short)( e2 >> 3 );
+
+ f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
+ f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = (short)c1;
+ if ( ras.gray_max_x < c2 ) ras.gray_max_x = (short)c2;
+
+ target = ras.bTarget + ras.traceOfs + c1;
+ c2 -= c1;
+
+ if ( c2 > 0 )
+ {
+ target[0] |= f1;
+
+ /* memset() is slower than the following code on many platforms. */
+ /* This is due to the fact that, in the vast majority of cases, */
+ /* the span length in bytes is relatively small. */
+ c2--;
+ while ( c2 > 0 )
+ {
+ *(++target) = 0xFF;
+ c2--;
+ }
+ target[1] |= f2;
+ }
+ else
+ *target |= ( f1 & f2 );
+ }
+ }
+
+
+ static void
+ Vertical_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ Short c1, f1;
+
+
+ /* Drop-out control */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( (x1 + x2 + 1) / 2 );
+ break;
+
+ case 2:
+ case 5:
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of `stubs'. */
+ /* */
+ /* Here, we only get rid of stubs recognized if: */
+ /* */
+ /* upper stub: */
+ /* */
+ /* - P_Left and P_Right are in the same contour */
+ /* - P_Right is the successor of P_Left in that contour */
+ /* - y is the top of P_Left and P_Right */
+ /* */
+ /* lower stub: */
+ /* */
+ /* - P_Left and P_Right are in the same contour */
+ /* - P_Left is the successor of P_Right in that contour */
+ /* - y is the bottom of P_Left */
+ /* */
+
+ /* FIXXXME: uncommenting this line solves the disappearing */
+ /* bit problem in the `7' of verdana 10pts, but */
+ /* makes a new one in the `C' of arial 14pts */
+
+#if 0
+ if ( x2 - x1 < ras.precision_half )
+#endif
+ {
+ /* upper stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* lower stub test */
+ if ( right->next == left && left->start == y )
+ return;
+ }
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ c1 = (Short)( e1 >> 3 );
+ f1 = (Short)( e1 & 7 );
+
+ if ( e1 >= 0 && e1 < ras.bWidth &&
+ ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.bWidth )
+ {
+ c1 = (Short)( e1 >> 3 );
+ f1 = (Short)( e1 & 7 );
+
+ if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
+ if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
+
+ ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
+ }
+ }
+
+
+ static void
+ Vertical_Sweep_Step( RAS_ARG )
+ {
+ ras.traceOfs += ras.traceIncr;
+ }
+
+
+ /***********************************************************************/
+ /* */
+ /* Horizontal Sweep Procedure Set */
+ /* */
+ /* These four routines are used during the horizontal black/white */
+ /* sweep phase by the generic Draw_Sweep() function. */
+ /* */
+ /***********************************************************************/
+
+ static void
+ Horizontal_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ /* nothing, really */
+ FT_UNUSED_RASTER;
+ FT_UNUSED( min );
+ FT_UNUSED( max );
+ }
+
+
+ static void
+ Horizontal_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+
+
+ if ( x2 - x1 < ras.precision )
+ {
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 == e2 )
+ {
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ PByte p;
+
+
+ p = bits - e1*ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ p += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ p[0] |= f1;
+ }
+ }
+ }
+ }
+
+
+ static void
+ Horizontal_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte bits;
+ Byte f1;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+ break;
+
+ case 2:
+ case 5:
+
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of `stubs'. */
+ /* */
+
+ /* rightmost stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* leftmost stub test */
+ if ( right->next == left && left->start == y )
+ return;
+
+ /* check that the rightmost pixel isn't set */
+
+ e1 = TRUNC( e1 );
+
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ bits -= e1 * ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ bits += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ if ( e1 >= 0 &&
+ e1 < ras.target.rows &&
+ *bits & f1 )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ bits = ras.bTarget + ( y >> 3 );
+ f1 = (Byte)( 0x80 >> ( y & 7 ) );
+
+ e1 = TRUNC( e1 );
+
+ if ( e1 >= 0 && e1 < ras.target.rows )
+ {
+ bits -= e1 * ras.target.pitch;
+ if ( ras.target.pitch > 0 )
+ bits += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ bits[0] |= f1;
+ }
+ }
+
+
+ static void
+ Horizontal_Sweep_Step( RAS_ARG )
+ {
+ /* Nothing, really */
+ FT_UNUSED_RASTER;
+ }
+
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+
+ /*************************************************************************/
+ /* */
+ /* Vertical Gray Sweep Procedure Set */
+ /* */
+ /* These two routines are used during the vertical gray-levels sweep */
+ /* phase by the generic Draw_Sweep() function. */
+ /* */
+ /* NOTES */
+ /* */
+ /* - The target pixmap's width *must* be a multiple of 4. */
+ /* */
+ /* - You have to use the function Vertical_Sweep_Span() for the gray */
+ /* span call. */
+ /* */
+ /*************************************************************************/
+
+ static void
+ Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
+ Short* max )
+ {
+ Long pitch, byte_len;
+
+
+ *min = *min & -2;
+ *max = ( *max + 3 ) & -2;
+
+ ras.traceOfs = 0;
+ pitch = ras.target.pitch;
+ byte_len = -pitch;
+ ras.traceIncr = (Short)byte_len;
+ ras.traceG = ( *min / 2 ) * byte_len;
+
+ if ( pitch > 0 )
+ {
+ ras.traceG += ( ras.target.rows - 1 ) * pitch;
+ byte_len = -byte_len;
+ }
+
+ ras.gray_min_x = (Short)byte_len;
+ ras.gray_max_x = -(Short)byte_len;
+ }
+
+
+ static void
+ Vertical_Gray_Sweep_Step( RAS_ARG )
+ {
+ Int c1, c2;
+ PByte pix, bit, bit2;
+ char* count = (char*)count_table;
+ Byte* grays;
+
+
+ ras.traceOfs += ras.gray_width;
+
+ if ( ras.traceOfs > ras.gray_width )
+ {
+ pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
+ grays = ras.grays;
+
+ if ( ras.gray_max_x >= 0 )
+ {
+ Long last_pixel = ras.target.width - 1;
+ Int last_cell = last_pixel >> 2;
+ Int last_bit = last_pixel & 3;
+ Bool over = 0;
+
+
+ if ( ras.gray_max_x >= last_cell && last_bit != 3 )
+ {
+ ras.gray_max_x = last_cell - 1;
+ over = 1;
+ }
+
+ if ( ras.gray_min_x < 0 )
+ ras.gray_min_x = 0;
+
+ bit = ras.bTarget + ras.gray_min_x;
+ bit2 = bit + ras.gray_width;
+
+ c1 = ras.gray_max_x - ras.gray_min_x;
+
+ while ( c1 >= 0 )
+ {
+ c2 = count[*bit] + count[*bit2];
+
+ if ( c2 )
+ {
+ pix[0] = grays[(c2 >> 12) & 0x000F];
+ pix[1] = grays[(c2 >> 8 ) & 0x000F];
+ pix[2] = grays[(c2 >> 4 ) & 0x000F];
+ pix[3] = grays[ c2 & 0x000F];
+
+ *bit = 0;
+ *bit2 = 0;
+ }
+
+ bit++;
+ bit2++;
+ pix += 4;
+ c1--;
+ }
+
+ if ( over )
+ {
+ c2 = count[*bit] + count[*bit2];
+ if ( c2 )
+ {
+ switch ( last_bit )
+ {
+ case 2:
+ pix[2] = grays[(c2 >> 4 ) & 0x000F];
+ case 1:
+ pix[1] = grays[(c2 >> 8 ) & 0x000F];
+ default:
+ pix[0] = grays[(c2 >> 12) & 0x000F];
+ }
+
+ *bit = 0;
+ *bit2 = 0;
+ }
+ }
+ }
+
+ ras.traceOfs = 0;
+ ras.traceG += ras.traceIncr;
+
+ ras.gray_min_x = 32000;
+ ras.gray_max_x = -32000;
+ }
+ }
+
+
+ static void
+ Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ /* nothing, really */
+ FT_UNUSED_RASTER;
+ FT_UNUSED( y );
+ FT_UNUSED( x1 );
+ FT_UNUSED( x2 );
+ FT_UNUSED( left );
+ FT_UNUSED( right );
+ }
+
+
+ static void
+ Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
+ FT_F26Dot6 x1,
+ FT_F26Dot6 x2,
+ PProfile left,
+ PProfile right )
+ {
+ Long e1, e2;
+ PByte pixel;
+ Byte color;
+
+
+ /* During the horizontal sweep, we only take care of drop-outs */
+ e1 = CEILING( x1 );
+ e2 = FLOOR ( x2 );
+
+ if ( e1 > e2 )
+ {
+ if ( e1 == e2 + ras.precision )
+ {
+ switch ( ras.dropOutControl )
+ {
+ case 1:
+ e1 = e2;
+ break;
+
+ case 4:
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+ break;
+
+ case 2:
+ case 5:
+
+ /* Drop-out Control Rule #4 */
+
+ /* The spec is not very clear regarding rule #4. It */
+ /* presents a method that is way too costly to implement */
+ /* while the general idea seems to get rid of `stubs'. */
+ /* */
+
+ /* rightmost stub test */
+ if ( left->next == right && left->height <= 0 )
+ return;
+
+ /* leftmost stub test */
+ if ( right->next == left && left->start == y )
+ return;
+
+ if ( ras.dropOutControl == 2 )
+ e1 = e2;
+ else
+ e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
+
+ break;
+
+ default:
+ return; /* unsupported mode */
+ }
+ }
+ else
+ return;
+ }
+
+ if ( e1 >= 0 )
+ {
+ if ( x2 - x1 >= ras.precision_half )
+ color = ras.grays[2];
+ else
+ color = ras.grays[1];
+
+ e1 = TRUNC( e1 ) / 2;
+ if ( e1 < ras.target.rows )
+ {
+ pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
+ if ( ras.target.pitch > 0 )
+ pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
+
+ if ( pixel[0] == ras.grays[0] )
+ pixel[0] = color;
+ }
+ }
+ }
+
+
+#endif /* FT_RASTER_OPTION_ANTI_ALIASING */
+
+
+ /*************************************************************************/
+ /* */
+ /* Generic Sweep Drawing routine */
+ /* */
+ /*************************************************************************/
+
+ static Bool
+ Draw_Sweep( RAS_ARG )
+ {
+ Short y, y_change, y_height;
+
+ PProfile P, Q, P_Left, P_Right;
+
+ Short min_Y, max_Y, top, bottom, dropouts;
+
+ Long x1, x2, xs, e1, e2;
+
+ TProfileList waiting;
+ TProfileList draw_left, draw_right;
+
+
+ /* Init empty linked lists */
+
+ Init_Linked( &waiting );
+
+ Init_Linked( &draw_left );
+ Init_Linked( &draw_right );
+
+ /* first, compute min and max Y */
+
+ P = ras.fProfile;
+ max_Y = (Short)TRUNC( ras.minY );
+ min_Y = (Short)TRUNC( ras.maxY );
+
+ while ( P )
+ {
+ Q = P->link;
+
+ bottom = (Short)P->start;
+ top = (Short)( P->start + P->height - 1 );
+
+ if ( min_Y > bottom ) min_Y = bottom;
+ if ( max_Y < top ) max_Y = top;
+
+ P->X = 0;
+ InsNew( &waiting, P );
+
+ P = Q;
+ }
+
+ /* Check the Y-turns */
+ if ( ras.numTurns == 0 )
+ {
+ ras.error = Raster_Err_Invalid;
+ return FAILURE;
+ }
+
+ /* Now inits the sweep */
+
+ ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
+
+ /* Then compute the distance of each profile from min_Y */
+
+ P = waiting;
+
+ while ( P )
+ {
+ P->countL = (UShort)( P->start - min_Y );
+ P = P->link;
+ }
+
+ /* Let's go */
+
+ y = min_Y;
+ y_height = 0;
+
+ if ( ras.numTurns > 0 &&
+ ras.sizeBuff[-ras.numTurns] == min_Y )
+ ras.numTurns--;
+
+ while ( ras.numTurns > 0 )
+ {
+ /* look in the waiting list for new activations */
+
+ P = waiting;
+
+ while ( P )
+ {
+ Q = P->link;
+ P->countL -= y_height;
+ if ( P->countL == 0 )
+ {
+ DelOld( &waiting, P );
+
+ switch ( P->flow )
+ {
+ case Flow_Up:
+ InsNew( &draw_left, P );
+ break;
+
+ case Flow_Down:
+ InsNew( &draw_right, P );
+ break;
+ }
+ }
+
+ P = Q;
+ }
+
+ /* Sort the drawing lists */
+
+ Sort( &draw_left );
+ Sort( &draw_right );
+
+ y_change = (Short)ras.sizeBuff[-ras.numTurns--];
+ y_height = (Short)( y_change - y );
+
+ while ( y < y_change )
+ {
+ /* Let's trace */
+
+ dropouts = 0;
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ x1 = P_Left ->X;
+ x2 = P_Right->X;
+
+ if ( x1 > x2 )
+ {
+ xs = x1;
+ x1 = x2;
+ x2 = xs;
+ }
+
+ if ( x2 - x1 <= ras.precision )
+ {
+ e1 = FLOOR( x1 );
+ e2 = CEILING( x2 );
+
+ if ( ras.dropOutControl != 0 &&
+ ( e1 > e2 || e2 == e1 + ras.precision ) )
+ {
+ /* a drop out was detected */
+
+ P_Left ->X = x1;
+ P_Right->X = x2;
+
+ /* mark profile for drop-out processing */
+ P_Left->countL = 1;
+ dropouts++;
+
+ goto Skip_To_Next;
+ }
+ }
+
+ ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
+
+ Skip_To_Next:
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ /* now perform the dropouts _after_ the span drawing -- */
+ /* drop-outs processing has been moved out of the loop */
+ /* for performance tuning */
+ if ( dropouts > 0 )
+ goto Scan_DropOuts;
+
+ Next_Line:
+
+ ras.Proc_Sweep_Step( RAS_VAR );
+
+ y++;
+
+ if ( y < y_change )
+ {
+ Sort( &draw_left );
+ Sort( &draw_right );
+ }
+ }
+
+ /* Now finalize the profiles that needs it */
+
+ P = draw_left;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_left, P );
+ P = Q;
+ }
+
+ P = draw_right;
+ while ( P )
+ {
+ Q = P->link;
+ if ( P->height == 0 )
+ DelOld( &draw_right, P );
+ P = Q;
+ }
+ }
+
+ /* for gray-scaling, flushes the bitmap scanline cache */
+ while ( y <= max_Y )
+ {
+ ras.Proc_Sweep_Step( RAS_VAR );
+ y++;
+ }
+
+ return SUCCESS;
+
+ Scan_DropOuts:
+
+ P_Left = draw_left;
+ P_Right = draw_right;
+
+ while ( P_Left )
+ {
+ if ( P_Left->countL )
+ {
+ P_Left->countL = 0;
+#if 0
+ dropouts--; /* -- this is useful when debugging only */
+#endif
+ ras.Proc_Sweep_Drop( RAS_VARS y,
+ P_Left->X,
+ P_Right->X,
+ P_Left,
+ P_Right );
+ }
+
+ P_Left = P_Left->link;
+ P_Right = P_Right->link;
+ }
+
+ goto Next_Line;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Single_Pass */
+ /* */
+ /* <Description> */
+ /* Performs one sweep with sub-banding. */
+ /* */
+ /* <Input> */
+ /* flipped :: If set, flip the direction of the outline. */
+ /* */
+ /* <Return> */
+ /* Renderer error code. */
+ /* */
+ static int
+ Render_Single_Pass( RAS_ARGS Bool flipped )
+ {
+ Short i, j, k;
+
+
+ while ( ras.band_top >= 0 )
+ {
+ ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
+ ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
+
+ ras.top = ras.buff;
+
+ ras.error = Raster_Err_None;
+
+ if ( Convert_Glyph( RAS_VARS flipped ) )
+ {
+ if ( ras.error != Raster_Err_Overflow )
+ return FAILURE;
+
+ ras.error = Raster_Err_None;
+
+ /* sub-banding */
+
+#ifdef DEBUG_RASTER
+ ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
+#endif
+
+ i = ras.band_stack[ras.band_top].y_min;
+ j = ras.band_stack[ras.band_top].y_max;
+
+ k = (Short)( ( i + j ) / 2 );
+
+ if ( ras.band_top >= 7 || k < i )
+ {
+ ras.band_top = 0;
+ ras.error = Raster_Err_Invalid;
+
+ return ras.error;
+ }
+
+ ras.band_stack[ras.band_top + 1].y_min = k;
+ ras.band_stack[ras.band_top + 1].y_max = j;
+
+ ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
+
+ ras.band_top++;
+ }
+ else
+ {
+ if ( ras.fProfile )
+ if ( Draw_Sweep( RAS_VAR ) )
+ return ras.error;
+ ras.band_top--;
+ }
+ }
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Glyph */
+ /* */
+ /* <Description> */
+ /* Renders a glyph in a bitmap. Sub-banding if needed. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ Render_Glyph( RAS_ARG )
+ {
+ FT_Error error;
+
+
+ Set_High_Precision( RAS_VARS ras.outline.flags &
+ FT_OUTLINE_HIGH_PRECISION );
+ ras.scale_shift = ras.precision_shift;
+ /* Drop-out mode 2 is hard-coded since this is the only mode used */
+ /* on Windows platforms. Using other modes, as specified by the */
+ /* font, results in misplaced pixels. */
+ ras.dropOutControl = 2;
+ ras.second_pass = (FT_Byte)( !( ras.outline.flags &
+ FT_OUTLINE_SINGLE_PASS ) );
+
+ /* Vertical Sweep */
+ ras.Proc_Sweep_Init = Vertical_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = (short)( ras.target.rows - 1 );
+
+ ras.bWidth = (unsigned short)ras.target.width;
+ ras.bTarget = (Byte*)ras.target.buffer;
+
+ if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
+ return error;
+
+ /* Horizontal Sweep */
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = (short)( ras.target.width - 1 );
+
+ if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
+ return error;
+ }
+
+ return Raster_Err_None;
+ }
+
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Render_Gray_Glyph */
+ /* */
+ /* <Description> */
+ /* Renders a glyph with grayscaling. Sub-banding if needed. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ Render_Gray_Glyph( RAS_ARG )
+ {
+ Long pixel_width;
+ FT_Error error;
+
+
+ Set_High_Precision( RAS_VARS ras.outline.flags &
+ FT_OUTLINE_HIGH_PRECISION );
+ ras.scale_shift = ras.precision_shift + 1;
+ /* Drop-out mode 2 is hard-coded since this is the only mode used */
+ /* on Windows platforms. Using other modes, as specified by the */
+ /* font, results in misplaced pixels. */
+ ras.dropOutControl = 2;
+ ras.second_pass = !( ras.outline.flags & FT_OUTLINE_SINGLE_PASS );
+
+ /* Vertical Sweep */
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
+
+ ras.bWidth = ras.gray_width;
+ pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
+
+ if ( ras.bWidth > pixel_width )
+ ras.bWidth = pixel_width;
+
+ ras.bWidth = ras.bWidth * 8;
+ ras.bTarget = (Byte*)ras.gray_lines;
+ ras.gTarget = (Byte*)ras.target.buffer;
+
+ ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
+ ras.Proc_Sweep_Span = Vertical_Sweep_Span;
+ ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
+ ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
+
+ error = Render_Single_Pass( RAS_VARS 0 );
+ if ( error )
+ return error;
+
+ /* Horizontal Sweep */
+ if ( ras.second_pass && ras.dropOutControl != 0 )
+ {
+ ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
+ ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
+ ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
+ ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
+
+ ras.band_top = 0;
+ ras.band_stack[0].y_min = 0;
+ ras.band_stack[0].y_max = ras.target.width * 2 - 1;
+
+ error = Render_Single_Pass( RAS_VARS 1 );
+ if ( error )
+ return error;
+ }
+
+ return Raster_Err_None;
+ }
+
+#else /* !FT_RASTER_OPTION_ANTI_ALIASING */
+
+ FT_LOCAL_DEF( FT_Error )
+ Render_Gray_Glyph( RAS_ARG )
+ {
+ FT_UNUSED_RASTER;
+
+ return Raster_Err_Unsupported;
+ }
+
+#endif /* !FT_RASTER_OPTION_ANTI_ALIASING */
+
+
+ static void
+ ft_black_init( PRaster raster )
+ {
+ FT_UNUSED( raster );
+
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+ FT_UInt n;
+
+
+ /* set default 5-levels gray palette */
+ for ( n = 0; n < 5; n++ )
+ raster->grays[n] = n * 255 / 4;
+
+ raster->gray_width = RASTER_GRAY_LINES / 2;
+
+#endif
+ }
+
+
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
+ /**** a static object. *****/
+
+
+#ifdef _STANDALONE_
+
+
+ static int
+ ft_black_new( void* memory,
+ FT_Raster *araster )
+ {
+ static TRaster the_raster;
+
+
+ *araster = (FT_Raster)&the_raster;
+ FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
+ ft_black_init( &the_raster );
+
+ return 0;
+ }
+
+
+ static void
+ ft_black_done( FT_Raster raster )
+ {
+ /* nothing */
+ FT_UNUSED( raster );
+ }
+
+
+#else /* _STANDALONE_ */
+
+
+ static int
+ ft_black_new( FT_Memory memory,
+ PRaster *araster )
+ {
+ FT_Error error;
+ PRaster raster;
+
+
+ *araster = 0;
+ if ( !FT_NEW( raster ) )
+ {
+ raster->memory = memory;
+ ft_black_init( raster );
+
+ *araster = raster;
+ }
+
+ return error;
+ }
+
+
+ static void
+ ft_black_done( PRaster raster )
+ {
+ FT_Memory memory = (FT_Memory)raster->memory;
+ FT_FREE( raster );
+ }
+
+
+#endif /* _STANDALONE_ */
+
+
+ static void
+ ft_black_reset( PRaster raster,
+ char* pool_base,
+ long pool_size )
+ {
+ if ( raster )
+ {
+ if ( pool_base && pool_size >= (long)sizeof(TWorker) + 2048 )
+ {
+ PWorker worker = (PWorker)pool_base;
+
+
+ raster->buffer = pool_base + ( (sizeof ( *worker ) + 7 ) & ~7 );
+ raster->buffer_size = ( ( pool_base + pool_size ) -
+ (char*)raster->buffer ) / sizeof ( Long );
+ raster->worker = worker;
+ }
+ else
+ {
+ raster->buffer = NULL;
+ raster->buffer_size = 0;
+ raster->worker = NULL;
+ }
+ }
+ }
+
+
+ static void
+ ft_black_set_mode( PRaster raster,
+ unsigned long mode,
+ const char* palette )
+ {
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+
+ if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
+ {
+ /* set 5-levels gray palette */
+ raster->grays[0] = palette[0];
+ raster->grays[1] = palette[1];
+ raster->grays[2] = palette[2];
+ raster->grays[3] = palette[3];
+ raster->grays[4] = palette[4];
+ }
+
+#else
+
+ FT_UNUSED( raster );
+ FT_UNUSED( mode );
+ FT_UNUSED( palette );
+
+#endif
+ }
+
+
+ static int
+ ft_black_render( PRaster raster,
+ const FT_Raster_Params* params )
+ {
+ const FT_Outline* outline = (const FT_Outline*)params->source;
+ const FT_Bitmap* target_map = params->target;
+ PWorker worker;
+
+
+ if ( !raster || !raster->buffer || !raster->buffer_size )
+ return Raster_Err_Not_Ini;
+
+ /* return immediately if the outline is empty */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ return Raster_Err_None;
+
+ if ( !outline || !outline->contours || !outline->points )
+ return Raster_Err_Invalid;
+
+ if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )
+ return Raster_Err_Invalid;
+
+ worker = raster->worker;
+
+ /* this version of the raster does not support direct rendering, sorry */
+ if ( params->flags & FT_RASTER_FLAG_DIRECT )
+ return Raster_Err_Unsupported;
+
+ if ( !target_map || !target_map->buffer )
+ return Raster_Err_Invalid;
+
+ ras.outline = *outline;
+ ras.target = *target_map;
+
+ worker->buff = (PLong) raster->buffer;
+ worker->sizeBuff = worker->buff +
+ raster->buffer_size / sizeof ( Long );
+#ifdef FT_RASTER_OPTION_ANTI_ALIASING
+ worker->grays = raster->grays;
+ worker->gray_width = raster->gray_width;
+#endif
+
+ return ( ( params->flags & FT_RASTER_FLAG_AA )
+ ? Render_Gray_Glyph( RAS_VAR )
+ : Render_Glyph( RAS_VAR ) );
+ }
+
+
+ const FT_Raster_Funcs ft_standard_raster =
+ {
+ FT_GLYPH_FORMAT_OUTLINE,
+ (FT_Raster_New_Func) ft_black_new,
+ (FT_Raster_Reset_Func) ft_black_reset,
+ (FT_Raster_Set_Mode_Func)ft_black_set_mode,
+ (FT_Raster_Render_Func) ft_black_render,
+ (FT_Raster_Done_Func) ft_black_done
+ };
+
+
+/* END */
diff --git a/src/raster/ftraster.h b/src/raster/ftraster.h
new file mode 100644
index 0000000..80fe46d
--- /dev/null
+++ b/src/raster/ftraster.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* ftraster.h */
+/* */
+/* The FreeType glyph rasterizer (specification). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used */
+/* modified and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTRASTER_H__
+#define __FTRASTER_H__
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_IMAGE_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* Uncomment the following line if you are using ftraster.c as a */
+ /* standalone module, fully independent of FreeType. */
+ /* */
+/* #define _STANDALONE_ */
+
+ FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_standard_raster;
+
+
+FT_END_HEADER
+
+#endif /* __FTRASTER_H__ */
+
+
+/* END */
diff --git a/src/raster/ftrend1.c b/src/raster/ftrend1.c
new file mode 100644
index 0000000..3cc8d07
--- /dev/null
+++ b/src/raster/ftrend1.c
@@ -0,0 +1,273 @@
+/***************************************************************************/
+/* */
+/* ftrend1.c */
+/* */
+/* The FreeType glyph rasterizer interface (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_OUTLINE_H
+#include "ftrend1.h"
+#include "ftraster.h"
+
+#include "rasterrs.h"
+
+
+ /* initialize renderer -- init its raster */
+ static FT_Error
+ ft_raster1_init( FT_Renderer render )
+ {
+ FT_Library library = FT_MODULE_LIBRARY( render );
+
+
+ render->clazz->raster_class->raster_reset( render->raster,
+ library->raster_pool,
+ library->raster_pool_size );
+
+ return Raster_Err_Ok;
+ }
+
+
+ /* set render-specific mode */
+ static FT_Error
+ ft_raster1_set_mode( FT_Renderer render,
+ FT_ULong mode_tag,
+ FT_Pointer data )
+ {
+ /* we simply pass it to the raster */
+ return render->clazz->raster_class->raster_set_mode( render->raster,
+ mode_tag,
+ data );
+ }
+
+
+ /* transform a given glyph image */
+ static FT_Error
+ ft_raster1_transform( FT_Renderer render,
+ FT_GlyphSlot slot,
+ const FT_Matrix* matrix,
+ const FT_Vector* delta )
+ {
+ FT_Error error = Raster_Err_Ok;
+
+
+ if ( slot->format != render->glyph_format )
+ {
+ error = Raster_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( matrix )
+ FT_Outline_Transform( &slot->outline, matrix );
+
+ if ( delta )
+ FT_Outline_Translate( &slot->outline, delta->x, delta->y );
+
+ Exit:
+ return error;
+ }
+
+
+ /* return the glyph's control box */
+ static void
+ ft_raster1_get_cbox( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_BBox* cbox )
+ {
+ FT_MEM_ZERO( cbox, sizeof ( *cbox ) );
+
+ if ( slot->format == render->glyph_format )
+ FT_Outline_Get_CBox( &slot->outline, cbox );
+ }
+
+
+ /* convert a slot's glyph image into a bitmap */
+ static FT_Error
+ ft_raster1_render( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ FT_Error error;
+ FT_Outline* outline;
+ FT_BBox cbox;
+ FT_UInt width, height, pitch;
+ FT_Bitmap* bitmap;
+ FT_Memory memory;
+
+ FT_Raster_Params params;
+
+
+ /* check glyph image format */
+ if ( slot->format != render->glyph_format )
+ {
+ error = Raster_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* check rendering mode */
+ if ( mode != FT_RENDER_MODE_MONO )
+ {
+ /* raster1 is only capable of producing monochrome bitmaps */
+ if ( render->clazz == &ft_raster1_renderer_class )
+ return Raster_Err_Cannot_Render_Glyph;
+ }
+ else
+ {
+ /* raster5 is only capable of producing 5-gray-levels bitmaps */
+ if ( render->clazz == &ft_raster5_renderer_class )
+ return Raster_Err_Cannot_Render_Glyph;
+ }
+
+ outline = &slot->outline;
+
+ /* translate the outline to the new origin if needed */
+ if ( origin )
+ FT_Outline_Translate( outline, origin->x, origin->y );
+
+ /* compute the control box, and grid fit it */
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
+ cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
+ cbox.xMax = FT_PIX_CEIL( cbox.xMax );
+ cbox.yMax = FT_PIX_CEIL( cbox.yMax );
+
+ width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
+ height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
+ bitmap = &slot->bitmap;
+ memory = render->root.memory;
+
+ /* release old bitmap buffer */
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ FT_FREE( bitmap->buffer );
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+
+ /* allocate new one, depends on pixel format */
+ if ( !( mode & FT_RENDER_MODE_MONO ) )
+ {
+ /* we pad to 32 bits, only for backwards compatibility with FT 1.x */
+ pitch = FT_PAD_CEIL( width, 4 );
+ bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+ bitmap->num_grays = 256;
+ }
+ else
+ {
+ pitch = ( ( width + 15 ) >> 4 ) << 1;
+ bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
+ }
+
+ bitmap->width = width;
+ bitmap->rows = height;
+ bitmap->pitch = pitch;
+
+ if ( FT_ALLOC_MULT( bitmap->buffer, pitch, height ) )
+ goto Exit;
+
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+
+ /* translate outline to render it into the bitmap */
+ FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin );
+
+ /* set up parameters */
+ params.target = bitmap;
+ params.source = outline;
+ params.flags = 0;
+
+ if ( bitmap->pixel_mode == FT_PIXEL_MODE_GRAY )
+ params.flags |= FT_RASTER_FLAG_AA;
+
+ /* render outline into the bitmap */
+ error = render->raster_render( render->raster, &params );
+
+ FT_Outline_Translate( outline, cbox.xMin, cbox.yMin );
+
+ if ( error )
+ goto Exit;
+
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ slot->bitmap_left = (FT_Int)( cbox.xMin >> 6 );
+ slot->bitmap_top = (FT_Int)( cbox.yMax >> 6 );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Renderer_Class ft_raster1_renderer_class =
+ {
+ {
+ FT_MODULE_RENDERER,
+ sizeof( FT_RendererRec ),
+
+ "raster1",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module specific interface */
+
+ (FT_Module_Constructor)ft_raster1_init,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ },
+
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Renderer_RenderFunc) ft_raster1_render,
+ (FT_Renderer_TransformFunc)ft_raster1_transform,
+ (FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox,
+ (FT_Renderer_SetModeFunc) ft_raster1_set_mode,
+
+ (FT_Raster_Funcs*) &ft_standard_raster
+ };
+
+
+ /* This renderer is _NOT_ part of the default modules; you will need */
+ /* to register it by hand in your application. It should only be */
+ /* used for backwards-compatibility with FT 1.x anyway. */
+ /* */
+ FT_CALLBACK_TABLE_DEF
+ const FT_Renderer_Class ft_raster5_renderer_class =
+ {
+ {
+ FT_MODULE_RENDERER,
+ sizeof( FT_RendererRec ),
+
+ "raster5",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module specific interface */
+
+ (FT_Module_Constructor)ft_raster1_init,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ },
+
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Renderer_RenderFunc) ft_raster1_render,
+ (FT_Renderer_TransformFunc)ft_raster1_transform,
+ (FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox,
+ (FT_Renderer_SetModeFunc) ft_raster1_set_mode,
+
+ (FT_Raster_Funcs*) &ft_standard_raster
+ };
+
+
+/* END */
diff --git a/src/raster/ftrend1.h b/src/raster/ftrend1.h
new file mode 100644
index 0000000..76e9a5f
--- /dev/null
+++ b/src/raster/ftrend1.h
@@ -0,0 +1,44 @@
+/***************************************************************************/
+/* */
+/* ftrend1.h */
+/* */
+/* The FreeType glyph rasterizer interface (specification). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTREND1_H__
+#define __FTREND1_H__
+
+
+#include <ft2build.h>
+#include FT_RENDER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_raster1_renderer_class;
+
+ /* this renderer is _NOT_ part of the default modules, you'll need */
+ /* to register it by hand in your application. It should only be */
+ /* used for backwards-compatibility with FT 1.x anyway. */
+ /* */
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_raster5_renderer_class;
+
+
+FT_END_HEADER
+
+#endif /* __FTREND1_H__ */
+
+
+/* END */
diff --git a/src/raster/module.mk b/src/raster/module.mk
new file mode 100644
index 0000000..59c737b
--- /dev/null
+++ b/src/raster/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 renderer module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += RASTER_MODULE
+
+define RASTER_MODULE
+$(OPEN_DRIVER)ft_raster1_renderer_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)raster $(ECHO_DRIVER_DESC)monochrome bitmap renderer$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/raster/raster.c b/src/raster/raster.c
new file mode 100644
index 0000000..f13a67a
--- /dev/null
+++ b/src/raster/raster.c
@@ -0,0 +1,26 @@
+/***************************************************************************/
+/* */
+/* raster.c */
+/* */
+/* FreeType monochrome rasterer module component (body only). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "ftraster.c"
+#include "ftrend1.c"
+
+
+/* END */
diff --git a/src/raster/rasterrs.h b/src/raster/rasterrs.h
new file mode 100644
index 0000000..5df9a7a
--- /dev/null
+++ b/src/raster/rasterrs.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* rasterrs.h */
+/* */
+/* monochrome renderer error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the monochrome renderer error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __RASTERRS_H__
+#define __RASTERRS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX Raster_Err_
+#define FT_ERR_BASE FT_Mod_Err_Raster
+
+#include FT_ERRORS_H
+
+#endif /* __RASTERRS_H__ */
+
+
+/* END */
diff --git a/src/raster/rules.mk b/src/raster/rules.mk
new file mode 100644
index 0000000..0dc8782
--- /dev/null
+++ b/src/raster/rules.mk
@@ -0,0 +1,69 @@
+#
+# FreeType 2 renderer module build rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# raster driver directory
+#
+RASTER_DIR := $(SRC_DIR)/raster
+
+# compilation flags for the driver
+#
+RASTER_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(RASTER_DIR))
+
+
+# raster driver sources (i.e., C files)
+#
+RASTER_DRV_SRC := $(RASTER_DIR)/ftraster.c \
+ $(RASTER_DIR)/ftrend1.c
+
+
+# raster driver headers
+#
+RASTER_DRV_H := $(RASTER_DRV_SRC:%.c=%.h) \
+ $(RASTER_DIR)/rasterrs.h
+
+
+# raster driver object(s)
+#
+# RASTER_DRV_OBJ_M is used during `multi' builds.
+# RASTER_DRV_OBJ_S is used during `single' builds.
+#
+RASTER_DRV_OBJ_M := $(RASTER_DRV_SRC:$(RASTER_DIR)/%.c=$(OBJ_DIR)/%.$O)
+RASTER_DRV_OBJ_S := $(OBJ_DIR)/raster.$O
+
+# raster driver source file for single build
+#
+RASTER_DRV_SRC_S := $(RASTER_DIR)/raster.c
+
+
+# raster driver - single object
+#
+$(RASTER_DRV_OBJ_S): $(RASTER_DRV_SRC_S) $(RASTER_DRV_SRC) \
+ $(FREETYPE_H) $(RASTER_DRV_H)
+ $(RASTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(RASTER_DRV_SRC_S))
+
+
+# raster driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(RASTER_DIR)/%.c $(FREETYPE_H) $(RASTER_DRV_H)
+ $(RASTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(RASTER_DRV_OBJ_S)
+DRV_OBJS_M += $(RASTER_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/sfnt/Jamfile b/src/sfnt/Jamfile
new file mode 100644
index 0000000..6b8a401
--- /dev/null
+++ b/src/sfnt/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/sfnt Jamfile
+#
+# Copyright 2001, 2002, 2004, 2005 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) sfnt ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ttkern ttbdf ;
+ }
+ else
+ {
+ _sources = sfnt ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/sfnt Jamfile
diff --git a/src/sfnt/module.mk b/src/sfnt/module.mk
new file mode 100644
index 0000000..d339138
--- /dev/null
+++ b/src/sfnt/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 SFNT module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += SFNT_MODULE
+
+define SFNT_MODULE
+$(OPEN_DRIVER)sfnt_module_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)sfnt $(ECHO_DRIVER_DESC)helper module for TrueType & OpenType formats$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk
new file mode 100644
index 0000000..ff7840e
--- /dev/null
+++ b/src/sfnt/rules.mk
@@ -0,0 +1,76 @@
+#
+# FreeType 2 SFNT driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2002, 2003, 2004, 2005, 2006, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# SFNT driver directory
+#
+SFNT_DIR := $(SRC_DIR)/sfnt
+
+
+# compilation flags for the driver
+#
+SFNT_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(SFNT_DIR))
+
+
+# SFNT driver sources (i.e., C files)
+#
+SFNT_DRV_SRC := $(SFNT_DIR)/ttload.c \
+ $(SFNT_DIR)/ttmtx.c \
+ $(SFNT_DIR)/ttcmap.c \
+ $(SFNT_DIR)/ttsbit.c \
+ $(SFNT_DIR)/ttpost.c \
+ $(SFNT_DIR)/ttkern.c \
+ $(SFNT_DIR)/ttbdf.c \
+ $(SFNT_DIR)/sfobjs.c \
+ $(SFNT_DIR)/sfdriver.c
+
+# SFNT driver headers
+#
+SFNT_DRV_H := $(SFNT_DRV_SRC:%c=%h) \
+ $(SFNT_DIR)/sferrors.h
+
+
+# SFNT driver object(s)
+#
+# SFNT_DRV_OBJ_M is used during `multi' builds.
+# SFNT_DRV_OBJ_S is used during `single' builds.
+#
+SFNT_DRV_OBJ_M := $(SFNT_DRV_SRC:$(SFNT_DIR)/%.c=$(OBJ_DIR)/%.$O)
+SFNT_DRV_OBJ_S := $(OBJ_DIR)/sfnt.$O
+
+# SFNT driver source file for single build
+#
+SFNT_DRV_SRC_S := $(SFNT_DIR)/sfnt.c
+
+
+# SFNT driver - single object
+#
+$(SFNT_DRV_OBJ_S): $(SFNT_DRV_SRC_S) $(SFNT_DRV_SRC) \
+ $(FREETYPE_H) $(SFNT_DRV_H)
+ $(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(SFNT_DRV_SRC_S))
+
+
+# SFNT driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(SFNT_DIR)/%.c $(FREETYPE_H) $(SFNT_DRV_H)
+ $(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(SFNT_DRV_OBJ_S)
+DRV_OBJS_M += $(SFNT_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/sfnt/sfdriver.c b/src/sfnt/sfdriver.c
new file mode 100644
index 0000000..5ba22a6
--- /dev/null
+++ b/src/sfnt/sfdriver.c
@@ -0,0 +1,618 @@
+/***************************************************************************/
+/* */
+/* sfdriver.c */
+/* */
+/* High-level SFNT driver interface (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_SFNT_H
+#include FT_INTERNAL_OBJECTS_H
+
+#include "sfdriver.h"
+#include "ttload.h"
+#include "sfobjs.h"
+
+#include "sferrors.h"
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#include "ttsbit.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+#include "ttpost.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_BDF
+#include "ttbdf.h"
+#include FT_SERVICE_BDF_H
+#endif
+
+#include "ttcmap.h"
+#include "ttkern.h"
+#include "ttmtx.h"
+
+#include FT_SERVICE_GLYPH_DICT_H
+#include FT_SERVICE_POSTSCRIPT_NAME_H
+#include FT_SERVICE_SFNT_H
+#include FT_SERVICE_TT_CMAP_H
+
+
+ /*
+ * SFNT TABLE SERVICE
+ *
+ */
+
+ static void*
+ get_sfnt_table( TT_Face face,
+ FT_Sfnt_Tag tag )
+ {
+ void* table;
+
+
+ switch ( tag )
+ {
+ case ft_sfnt_head:
+ table = &face->header;
+ break;
+
+ case ft_sfnt_hhea:
+ table = &face->horizontal;
+ break;
+
+ case ft_sfnt_vhea:
+ table = face->vertical_info ? &face->vertical : 0;
+ break;
+
+ case ft_sfnt_os2:
+ table = face->os2.version == 0xFFFFU ? 0 : &face->os2;
+ break;
+
+ case ft_sfnt_post:
+ table = &face->postscript;
+ break;
+
+ case ft_sfnt_maxp:
+ table = &face->max_profile;
+ break;
+
+ case ft_sfnt_pclt:
+ table = face->pclt.Version ? &face->pclt : 0;
+ break;
+
+ default:
+ table = 0;
+ }
+
+ return table;
+ }
+
+
+ static FT_Error
+ sfnt_table_info( TT_Face face,
+ FT_UInt idx,
+ FT_ULong *tag,
+ FT_ULong *length )
+ {
+ if ( !tag || !length )
+ return SFNT_Err_Invalid_Argument;
+
+ if ( idx >= face->num_tables )
+ return SFNT_Err_Table_Missing;
+
+ *tag = face->dir_tables[idx].Tag;
+ *length = face->dir_tables[idx].Length;
+
+ return SFNT_Err_Ok;
+ }
+
+
+ static const FT_Service_SFNT_TableRec sfnt_service_sfnt_table =
+ {
+ (FT_SFNT_TableLoadFunc)tt_face_load_any,
+ (FT_SFNT_TableGetFunc) get_sfnt_table,
+ (FT_SFNT_TableInfoFunc)sfnt_table_info
+ };
+
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+ /*
+ * GLYPH DICT SERVICE
+ *
+ */
+
+ static FT_Error
+ sfnt_get_glyph_name( TT_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_String* gname;
+ FT_Error error;
+
+
+ error = tt_face_get_ps_name( face, glyph_index, &gname );
+ if ( !error )
+ FT_STRCPYN( buffer, gname, buffer_max );
+
+ return error;
+ }
+
+
+ static const FT_Service_GlyphDictRec sfnt_service_glyph_dict =
+ {
+ (FT_GlyphDict_GetNameFunc) sfnt_get_glyph_name,
+ (FT_GlyphDict_NameIndexFunc)NULL
+ };
+
+#endif /* TT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ /*
+ * POSTSCRIPT NAME SERVICE
+ *
+ */
+
+ static const char*
+ sfnt_get_ps_name( TT_Face face )
+ {
+ FT_Int n, found_win, found_apple;
+ const char* result = NULL;
+
+
+ /* shouldn't happen, but just in case to avoid memory leaks */
+ if ( face->postscript_name )
+ return face->postscript_name;
+
+ /* scan the name table to see whether we have a Postscript name here, */
+ /* either in Macintosh or Windows platform encodings */
+ found_win = -1;
+ found_apple = -1;
+
+ for ( n = 0; n < face->num_names; n++ )
+ {
+ TT_NameEntryRec* name = face->name_table.names + n;
+
+
+ if ( name->nameID == 6 && name->stringLength > 0 )
+ {
+ if ( name->platformID == 3 &&
+ name->encodingID == 1 &&
+ name->languageID == 0x409 )
+ found_win = n;
+
+ if ( name->platformID == 1 &&
+ name->encodingID == 0 &&
+ name->languageID == 0 )
+ found_apple = n;
+ }
+ }
+
+ if ( found_win != -1 )
+ {
+ FT_Memory memory = face->root.memory;
+ TT_NameEntryRec* name = face->name_table.names + found_win;
+ FT_UInt len = name->stringLength / 2;
+ FT_Error error = SFNT_Err_Ok;
+
+ FT_UNUSED( error );
+
+
+ if ( !FT_ALLOC( result, name->stringLength + 1 ) )
+ {
+ FT_Stream stream = face->name_table.stream;
+ FT_String* r = (FT_String*)result;
+ FT_Byte* p = (FT_Byte*)name->string;
+
+
+ if ( FT_STREAM_SEEK( name->stringOffset ) ||
+ FT_FRAME_ENTER( name->stringLength ) )
+ {
+ FT_FREE( result );
+ name->stringLength = 0;
+ name->stringOffset = 0;
+ FT_FREE( name->string );
+
+ goto Exit;
+ }
+
+ p = (FT_Byte*)stream->cursor;
+
+ for ( ; len > 0; len--, p += 2 )
+ {
+ if ( p[0] == 0 && p[1] >= 32 && p[1] < 128 )
+ *r++ = p[1];
+ }
+ *r = '\0';
+
+ FT_FRAME_EXIT();
+ }
+ goto Exit;
+ }
+
+ if ( found_apple != -1 )
+ {
+ FT_Memory memory = face->root.memory;
+ TT_NameEntryRec* name = face->name_table.names + found_apple;
+ FT_UInt len = name->stringLength;
+ FT_Error error = SFNT_Err_Ok;
+
+ FT_UNUSED( error );
+
+
+ if ( !FT_ALLOC( result, len + 1 ) )
+ {
+ FT_Stream stream = face->name_table.stream;
+
+
+ if ( FT_STREAM_SEEK( name->stringOffset ) ||
+ FT_STREAM_READ( result, len ) )
+ {
+ name->stringOffset = 0;
+ name->stringLength = 0;
+ FT_FREE( name->string );
+ FT_FREE( result );
+ goto Exit;
+ }
+ ((char*)result)[len] = '\0';
+ }
+ }
+
+ Exit:
+ face->postscript_name = result;
+ return result;
+ }
+
+ static const FT_Service_PsFontNameRec sfnt_service_ps_name =
+ {
+ (FT_PsName_GetFunc)sfnt_get_ps_name
+ };
+
+
+ /*
+ * TT CMAP INFO
+ */
+ static const FT_Service_TTCMapsRec tt_service_get_cmap_info =
+ {
+ (TT_CMap_Info_GetFunc)tt_get_cmap_info
+ };
+
+
+#ifdef TT_CONFIG_OPTION_BDF
+
+ static FT_Error
+ sfnt_get_charset_id( TT_Face face,
+ const char* *acharset_encoding,
+ const char* *acharset_registry )
+ {
+ BDF_PropertyRec encoding, registry;
+ FT_Error error;
+
+
+ /* XXX: I don't know whether this is correct, since
+ * tt_face_find_bdf_prop only returns something correct if we have
+ * previously selected a size that is listed in the BDF table.
+ * Should we change the BDF table format to include single offsets
+ * for `CHARSET_REGISTRY' and `CHARSET_ENCODING'?
+ */
+ error = tt_face_find_bdf_prop( face, "CHARSET_REGISTRY", &registry );
+ if ( !error )
+ {
+ error = tt_face_find_bdf_prop( face, "CHARSET_ENCODING", &encoding );
+ if ( !error )
+ {
+ if ( registry.type == BDF_PROPERTY_TYPE_ATOM &&
+ encoding.type == BDF_PROPERTY_TYPE_ATOM )
+ {
+ *acharset_encoding = encoding.u.atom;
+ *acharset_registry = registry.u.atom;
+ }
+ else
+ error = FT_Err_Invalid_Argument;
+ }
+ }
+
+ return error;
+ }
+
+
+ static const FT_Service_BDFRec sfnt_service_bdf =
+ {
+ (FT_BDF_GetCharsetIdFunc) sfnt_get_charset_id,
+ (FT_BDF_GetPropertyFunc) tt_face_find_bdf_prop,
+ };
+
+#endif /* TT_CONFIG_OPTION_BDF */
+
+
+ /*
+ * SERVICE LIST
+ */
+
+ static const FT_ServiceDescRec sfnt_services[] =
+ {
+ { FT_SERVICE_ID_SFNT_TABLE, &sfnt_service_sfnt_table },
+ { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &sfnt_service_ps_name },
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ { FT_SERVICE_ID_GLYPH_DICT, &sfnt_service_glyph_dict },
+#endif
+#ifdef TT_CONFIG_OPTION_BDF
+ { FT_SERVICE_ID_BDF, &sfnt_service_bdf },
+#endif
+ { FT_SERVICE_ID_TT_CMAP, &tt_service_get_cmap_info },
+
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ sfnt_get_interface( FT_Module module,
+ const char* module_interface )
+ {
+ FT_UNUSED( module );
+
+ return ft_service_list_lookup( sfnt_services, module_interface );
+ }
+
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_load_sfnt_header_stub( TT_Face face,
+ FT_Stream stream,
+ FT_Long face_index,
+ SFNT_Header header )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+ FT_UNUSED( face_index );
+ FT_UNUSED( header );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_load_directory_stub( TT_Face face,
+ FT_Stream stream,
+ SFNT_Header header )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+ FT_UNUSED( header );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_load_hdmx_stub( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ tt_face_free_hdmx_stub( TT_Face face )
+ {
+ FT_UNUSED( face );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_set_sbit_strike_stub( TT_Face face,
+ FT_UInt x_ppem,
+ FT_UInt y_ppem,
+ FT_ULong* astrike_index )
+ {
+ /*
+ * We simply forge a FT_Size_Request and call the real function
+ * that does all the work.
+ *
+ * This stub might be called by libXfont in the X.Org Xserver,
+ * compiled against version 2.1.8 or newer.
+ */
+
+ FT_Size_RequestRec req;
+
+
+ req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
+ req.width = (FT_F26Dot6)x_ppem;
+ req.height = (FT_F26Dot6)y_ppem;
+ req.horiResolution = 0;
+ req.vertResolution = 0;
+
+ *astrike_index = 0x7FFFFFFFUL;
+
+ return tt_face_set_sbit_strike( face, &req, astrike_index );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_load_sbit_stub( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+
+ /*
+ * This function was originally implemented to load the sbit table.
+ * However, it has been replaced by `tt_face_load_eblc', and this stub
+ * is only there for some rogue clients which would want to call it
+ * directly (which doesn't make much sense).
+ */
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ tt_face_free_sbit_stub( TT_Face face )
+ {
+ /* nothing to do in this stub */
+ FT_UNUSED( face );
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_load_charmap_stub( TT_Face face,
+ void* cmap,
+ FT_Stream input )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( cmap );
+ FT_UNUSED( input );
+
+ return FT_Err_Unimplemented_Feature;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_face_free_charmap_stub( TT_Face face,
+ void* cmap )
+ {
+ FT_UNUSED( face );
+ FT_UNUSED( cmap );
+
+ return 0;
+ }
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+ static
+ const SFNT_Interface sfnt_interface =
+ {
+ tt_face_goto_table,
+
+ sfnt_init_face,
+ sfnt_load_face,
+ sfnt_done_face,
+ sfnt_get_interface,
+
+ tt_face_load_any,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ tt_face_load_sfnt_header_stub,
+ tt_face_load_directory_stub,
+#endif
+
+ tt_face_load_head,
+ tt_face_load_hhea,
+ tt_face_load_cmap,
+ tt_face_load_maxp,
+ tt_face_load_os2,
+ tt_face_load_post,
+
+ tt_face_load_name,
+ tt_face_free_name,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ tt_face_load_hdmx_stub,
+ tt_face_free_hdmx_stub,
+#endif
+
+ tt_face_load_kern,
+ tt_face_load_gasp,
+ tt_face_load_pclt,
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ /* see `ttload.h' */
+ tt_face_load_bhed,
+#else
+ 0,
+#endif
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ tt_face_set_sbit_strike_stub,
+ tt_face_load_sbit_stub,
+
+ tt_find_sbit_image,
+ tt_load_sbit_metrics,
+#endif
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ tt_face_load_sbit_image,
+#else
+ 0,
+#endif
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ tt_face_free_sbit_stub,
+#endif
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ /* see `ttpost.h' */
+ tt_face_get_ps_name,
+ tt_face_free_ps_names,
+#else
+ 0,
+ 0,
+#endif
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ tt_face_load_charmap_stub,
+ tt_face_free_charmap_stub,
+#endif
+
+ /* since version 2.1.8 */
+
+ tt_face_get_kerning,
+
+ /* since version 2.2 */
+
+ tt_face_load_font_dir,
+ tt_face_load_hmtx,
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ /* see `ttsbit.h' and `sfnt.h' */
+ tt_face_load_eblc,
+ tt_face_free_eblc,
+
+ tt_face_set_sbit_strike,
+ tt_face_load_strike_metrics,
+#else
+ 0,
+ 0,
+ 0,
+ 0,
+#endif
+
+ tt_face_get_metrics
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Module_Class sfnt_module_class =
+ {
+ 0, /* not a font driver or renderer */
+ sizeof( FT_ModuleRec ),
+
+ "sfnt", /* driver name */
+ 0x10000L, /* driver version 1.0 */
+ 0x20000L, /* driver requires FreeType 2.0 or higher */
+
+ (const void*)&sfnt_interface, /* module specific interface */
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) sfnt_get_interface
+ };
+
+
+/* END */
diff --git a/src/sfnt/sfdriver.h b/src/sfnt/sfdriver.h
new file mode 100644
index 0000000..92db796
--- /dev/null
+++ b/src/sfnt/sfdriver.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* sfdriver.h */
+/* */
+/* High-level SFNT driver interface (specification). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __SFDRIVER_H__
+#define __SFDRIVER_H__
+
+
+#include <ft2build.h>
+#include FT_MODULE_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Module_Class ) sfnt_module_class;
+
+
+FT_END_HEADER
+
+#endif /* __SFDRIVER_H__ */
+
+
+/* END */
diff --git a/src/sfnt/sferrors.h b/src/sfnt/sferrors.h
new file mode 100644
index 0000000..27f90de
--- /dev/null
+++ b/src/sfnt/sferrors.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* sferrors.h */
+/* */
+/* SFNT error codes (specification only). */
+/* */
+/* Copyright 2001, 2004 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the SFNT error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __SFERRORS_H__
+#define __SFERRORS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX SFNT_Err_
+#define FT_ERR_BASE FT_Mod_Err_SFNT
+
+#define FT_KEEP_ERR_PREFIX
+
+#include FT_ERRORS_H
+
+#endif /* __SFERRORS_H__ */
+
+/* END */
diff --git a/src/sfnt/sfnt.c b/src/sfnt/sfnt.c
new file mode 100644
index 0000000..45a820b
--- /dev/null
+++ b/src/sfnt/sfnt.c
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* sfnt.c */
+/* */
+/* Single object library component. */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "ttload.c"
+#include "ttmtx.c"
+#include "ttcmap.c"
+#include "ttkern.c"
+#include "sfobjs.c"
+#include "sfdriver.c"
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#include "ttsbit.c"
+#endif
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+#include "ttpost.c"
+#endif
+
+#ifdef TT_CONFIG_OPTION_BDF
+#include "ttbdf.c"
+#endif
+
+/* END */
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
new file mode 100644
index 0000000..cc90110
--- /dev/null
+++ b/src/sfnt/sfobjs.c
@@ -0,0 +1,1070 @@
+/***************************************************************************/
+/* */
+/* sfobjs.c */
+/* */
+/* SFNT object management (base). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "sfobjs.h"
+#include "ttload.h"
+#include "ttcmap.h"
+#include "ttkern.h"
+#include FT_INTERNAL_SFNT_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_TRUETYPE_IDS_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include "sferrors.h"
+
+#ifdef TT_CONFIG_OPTION_BDF
+#include "ttbdf.h"
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_sfobjs
+
+
+
+ /* convert a UTF-16 name entry to ASCII */
+ static FT_String*
+ tt_name_entry_ascii_from_utf16( TT_NameEntry entry,
+ FT_Memory memory )
+ {
+ FT_String* string;
+ FT_UInt len, code, n;
+ FT_Byte* read = (FT_Byte*)entry->string;
+ FT_Error error;
+
+
+ len = (FT_UInt)entry->stringLength / 2;
+
+ if ( FT_NEW_ARRAY( string, len + 1 ) )
+ return NULL;
+
+ for ( n = 0; n < len; n++ )
+ {
+ code = FT_NEXT_USHORT( read );
+ if ( code < 32 || code > 127 )
+ code = '?';
+
+ string[n] = (char)code;
+ }
+
+ string[len] = 0;
+
+ return string;
+ }
+
+
+ /* convert an Apple Roman or symbol name entry to ASCII */
+ static FT_String*
+ tt_name_entry_ascii_from_other( TT_NameEntry entry,
+ FT_Memory memory )
+ {
+ FT_String* string;
+ FT_UInt len, code, n;
+ FT_Byte* read = (FT_Byte*)entry->string;
+ FT_Error error;
+
+
+ len = (FT_UInt)entry->stringLength;
+
+ if ( FT_NEW_ARRAY( string, len + 1 ) )
+ return NULL;
+
+ for ( n = 0; n < len; n++ )
+ {
+ code = *read++;
+ if ( code < 32 || code > 127 )
+ code = '?';
+
+ string[n] = (char)code;
+ }
+
+ string[len] = 0;
+
+ return string;
+ }
+
+
+ typedef FT_String* (*TT_NameEntry_ConvertFunc)( TT_NameEntry entry,
+ FT_Memory memory );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_get_name */
+ /* */
+ /* <Description> */
+ /* Returns a given ENGLISH name record in ASCII. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* nameid :: The name id of the name record to return. */
+ /* */
+ /* <Return> */
+ /* Character string. NULL if no name is present. */
+ /* */
+ static FT_String*
+ tt_face_get_name( TT_Face face,
+ FT_UShort nameid )
+ {
+ FT_Memory memory = face->root.memory;
+ FT_String* result = NULL;
+ FT_UShort n;
+ TT_NameEntryRec* rec;
+ FT_Int found_apple = -1;
+ FT_Int found_apple_roman = -1;
+ FT_Int found_apple_english = -1;
+ FT_Int found_win = -1;
+ FT_Int found_unicode = -1;
+
+ FT_Bool is_english = 0;
+
+ TT_NameEntry_ConvertFunc convert;
+
+
+ rec = face->name_table.names;
+ for ( n = 0; n < face->num_names; n++, rec++ )
+ {
+ /* According to the OpenType 1.3 specification, only Microsoft or */
+ /* Apple platform IDs might be used in the `name' table. The */
+ /* `Unicode' platform is reserved for the `cmap' table, and the */
+ /* `Iso' one is deprecated. */
+ /* */
+ /* However, the Apple TrueType specification doesn't say the same */
+ /* thing and goes to suggest that all Unicode `name' table entries */
+ /* should be coded in UTF-16 (in big-endian format I suppose). */
+ /* */
+ if ( rec->nameID == nameid && rec->stringLength > 0 )
+ {
+ switch ( rec->platformID )
+ {
+ case TT_PLATFORM_APPLE_UNICODE:
+ case TT_PLATFORM_ISO:
+ /* there is `languageID' to check there. We should use this */
+ /* field only as a last solution when nothing else is */
+ /* available. */
+ /* */
+ found_unicode = n;
+ break;
+
+ case TT_PLATFORM_MACINTOSH:
+ /* This is a bit special because some fonts will use either */
+ /* an English language id, or a Roman encoding id, to indicate */
+ /* the English version of its font name. */
+ /* */
+ if ( rec->languageID == TT_MAC_LANGID_ENGLISH )
+ found_apple_english = n;
+ else if ( rec->encodingID == TT_MAC_ID_ROMAN )
+ found_apple_roman = n;
+ break;
+
+ case TT_PLATFORM_MICROSOFT:
+ /* we only take a non-English name when there is nothing */
+ /* else available in the font */
+ /* */
+ if ( found_win == -1 || ( rec->languageID & 0x3FF ) == 0x009 )
+ {
+ switch ( rec->encodingID )
+ {
+ case TT_MS_ID_SYMBOL_CS:
+ case TT_MS_ID_UNICODE_CS:
+ case TT_MS_ID_UCS_4:
+ is_english = FT_BOOL( ( rec->languageID & 0x3FF ) == 0x009 );
+ found_win = n;
+ break;
+
+ default:
+ ;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+ }
+
+ found_apple = found_apple_roman;
+ if ( found_apple_english >= 0 )
+ found_apple = found_apple_english;
+
+ /* some fonts contain invalid Unicode or Macintosh formatted entries; */
+ /* we will thus favor names encoded in Windows formats if available */
+ /* (provided it is an English name) */
+ /* */
+ convert = NULL;
+ if ( found_win >= 0 && !( found_apple >= 0 && !is_english ) )
+ {
+ rec = face->name_table.names + found_win;
+ switch ( rec->encodingID )
+ {
+ /* all Unicode strings are encoded using UTF-16BE */
+ case TT_MS_ID_UNICODE_CS:
+ case TT_MS_ID_SYMBOL_CS:
+ convert = tt_name_entry_ascii_from_utf16;
+ break;
+
+ case TT_MS_ID_UCS_4:
+ /* Apparently, if this value is found in a name table entry, it is */
+ /* documented as `full Unicode repertoire'. Experience with the */
+ /* MsGothic font shipped with Windows Vista shows that this really */
+ /* means UTF-16 encoded names (UCS-4 values are only used within */
+ /* charmaps). */
+ convert = tt_name_entry_ascii_from_utf16;
+ break;
+
+ default:
+ ;
+ }
+ }
+ else if ( found_apple >= 0 )
+ {
+ rec = face->name_table.names + found_apple;
+ convert = tt_name_entry_ascii_from_other;
+ }
+ else if ( found_unicode >= 0 )
+ {
+ rec = face->name_table.names + found_unicode;
+ convert = tt_name_entry_ascii_from_utf16;
+ }
+
+ if ( rec && convert )
+ {
+ if ( rec->string == NULL )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_Stream stream = face->name_table.stream;
+
+ FT_UNUSED( error );
+
+
+ if ( FT_QNEW_ARRAY ( rec->string, rec->stringLength ) ||
+ FT_STREAM_SEEK( rec->stringOffset ) ||
+ FT_STREAM_READ( rec->string, rec->stringLength ) )
+ {
+ FT_FREE( rec->string );
+ rec->stringLength = 0;
+ result = NULL;
+ goto Exit;
+ }
+ }
+
+ result = convert( rec, memory );
+ }
+
+ Exit:
+ return result;
+ }
+
+
+ static FT_Encoding
+ sfnt_find_encoding( int platform_id,
+ int encoding_id )
+ {
+ typedef struct TEncoding
+ {
+ int platform_id;
+ int encoding_id;
+ FT_Encoding encoding;
+
+ } TEncoding;
+
+ static
+ const TEncoding tt_encodings[] =
+ {
+ { TT_PLATFORM_ISO, -1, FT_ENCODING_UNICODE },
+
+ { TT_PLATFORM_APPLE_UNICODE, -1, FT_ENCODING_UNICODE },
+
+ { TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, FT_ENCODING_APPLE_ROMAN },
+
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_SYMBOL_CS, FT_ENCODING_MS_SYMBOL },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_UCS_4, FT_ENCODING_UNICODE },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, FT_ENCODING_UNICODE },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_SJIS, FT_ENCODING_SJIS },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_GB2312, FT_ENCODING_GB2312 },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_BIG_5, FT_ENCODING_BIG5 },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_WANSUNG, FT_ENCODING_WANSUNG },
+ { TT_PLATFORM_MICROSOFT, TT_MS_ID_JOHAB, FT_ENCODING_JOHAB }
+ };
+
+ const TEncoding *cur, *limit;
+
+
+ cur = tt_encodings;
+ limit = cur + sizeof ( tt_encodings ) / sizeof ( tt_encodings[0] );
+
+ for ( ; cur < limit; cur++ )
+ {
+ if ( cur->platform_id == platform_id )
+ {
+ if ( cur->encoding_id == encoding_id ||
+ cur->encoding_id == -1 )
+ return cur->encoding;
+ }
+ }
+
+ return FT_ENCODING_NONE;
+ }
+
+
+ /* Fill in face->ttc_header. If the font is not a TTC, it is */
+ /* synthesized into a TTC with one offset table. */
+ static FT_Error
+ sfnt_open_font( FT_Stream stream,
+ TT_Face face )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+ FT_ULong tag, offset;
+
+ static const FT_Frame_Field ttc_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TTC_HeaderRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_LONG( version ),
+ FT_FRAME_LONG( count ),
+ FT_FRAME_END
+ };
+
+
+ face->ttc_header.tag = 0;
+ face->ttc_header.version = 0;
+ face->ttc_header.count = 0;
+
+ offset = FT_STREAM_POS();
+
+ if ( FT_READ_ULONG( tag ) )
+ return error;
+
+ if ( tag != 0x00010000UL &&
+ tag != TTAG_ttcf &&
+ tag != FT_MAKE_TAG( 'O', 'T', 'T', 'O' ) &&
+ tag != TTAG_true &&
+ tag != 0x00020000UL )
+ return SFNT_Err_Unknown_File_Format;
+
+ face->ttc_header.tag = TTAG_ttcf;
+
+ if ( tag == TTAG_ttcf )
+ {
+ FT_Int n;
+
+
+ FT_TRACE3(( "sfnt_open_font: file is a collection\n" ));
+
+ if ( FT_STREAM_READ_FIELDS( ttc_header_fields, &face->ttc_header ) )
+ return error;
+
+ /* now read the offsets of each font in the file */
+ if ( FT_NEW_ARRAY( face->ttc_header.offsets, face->ttc_header.count ) )
+ return error;
+
+ if ( FT_FRAME_ENTER( face->ttc_header.count * 4L ) )
+ return error;
+
+ for ( n = 0; n < face->ttc_header.count; n++ )
+ face->ttc_header.offsets[n] = FT_GET_ULONG();
+
+ FT_FRAME_EXIT();
+ }
+ else
+ {
+ FT_TRACE3(( "sfnt_open_font: synthesize TTC\n" ));
+
+ face->ttc_header.version = 1 << 16;
+ face->ttc_header.count = 1;
+
+ if ( FT_NEW( face->ttc_header.offsets) )
+ return error;
+
+ face->ttc_header.offsets[0] = offset;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ sfnt_init_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Library library = face->root.driver->root.library;
+ SFNT_Service sfnt;
+
+
+ /* for now, parameters are unused */
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ sfnt = (SFNT_Service)face->sfnt;
+ if ( !sfnt )
+ {
+ sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
+ if ( !sfnt )
+ return SFNT_Err_Invalid_File_Format;
+
+ face->sfnt = sfnt;
+ face->goto_table = sfnt->goto_table;
+ }
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, face->psnames, POSTSCRIPT_CMAPS );
+
+ error = sfnt_open_font( stream, face );
+ if ( error )
+ return error;
+
+ FT_TRACE2(( "sfnt_init_face: %08p, %ld\n", face, face_index ));
+
+ if ( face_index < 0 )
+ face_index = 0;
+
+ if ( face_index >= face->ttc_header.count )
+ return SFNT_Err_Bad_Argument;
+
+ if ( FT_STREAM_SEEK( face->ttc_header.offsets[face_index] ) )
+ return error;
+
+ /* check that we have a valid TrueType file */
+ error = sfnt->load_font_dir( face, stream );
+ if ( error )
+ return error;
+
+ face->root.num_faces = face->ttc_header.count;
+
+ return error;
+ }
+
+
+#define LOAD_( x ) \
+ do { \
+ FT_TRACE2(( "`" #x "' " )); \
+ FT_TRACE3(( "-->\n" )); \
+ \
+ error = sfnt->load_##x( face, stream ); \
+ \
+ FT_TRACE2(( "%s\n", ( !error ) \
+ ? "loaded" \
+ : ( error == SFNT_Err_Table_Missing ) \
+ ? "missing" \
+ : "failed to load" )); \
+ FT_TRACE3(( "\n" )); \
+ } while ( 0 )
+
+#define LOADM_( x, vertical ) \
+ do { \
+ FT_TRACE2(( "`%s" #x "' ", \
+ vertical ? "vertical " : "" )); \
+ FT_TRACE3(( "-->\n" )); \
+ \
+ error = sfnt->load_##x( face, stream, vertical ); \
+ \
+ FT_TRACE2(( "%s\n", ( !error ) \
+ ? "loaded" \
+ : ( error == SFNT_Err_Table_Missing ) \
+ ? "missing" \
+ : "failed to load" )); \
+ FT_TRACE3(( "\n" )); \
+ } while ( 0 )
+
+
+ FT_LOCAL_DEF( FT_Error )
+ sfnt_load_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error, psnames_error;
+ FT_Bool has_outline;
+ FT_Bool is_apple_sbit;
+
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+ FT_UNUSED( face_index );
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ /* Load tables */
+
+ /* We now support two SFNT-based bitmapped font formats. They */
+ /* are recognized easily as they do not include a `glyf' */
+ /* table. */
+ /* */
+ /* The first format comes from Apple, and uses a table named */
+ /* `bhed' instead of `head' to store the font header (using */
+ /* the same format). It also doesn't include horizontal and */
+ /* vertical metrics tables (i.e. `hhea' and `vhea' tables are */
+ /* missing). */
+ /* */
+ /* The other format comes from Microsoft, and is used with */
+ /* WinCE/PocketPC. It looks like a standard TTF, except that */
+ /* it doesn't contain outlines. */
+ /* */
+
+ FT_TRACE2(( "sfnt_load_face: %08p\n\n", face ));
+
+ /* do we have outlines in there? */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ has_outline = FT_BOOL( face->root.internal->incremental_interface != 0 ||
+ tt_face_lookup_table( face, TTAG_glyf ) != 0 ||
+ tt_face_lookup_table( face, TTAG_CFF ) != 0 );
+#else
+ has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) != 0 ||
+ tt_face_lookup_table( face, TTAG_CFF ) != 0 );
+#endif
+
+ is_apple_sbit = 0;
+
+ /* if this font doesn't contain outlines, we try to load */
+ /* a `bhed' table */
+ if ( !has_outline && sfnt->load_bhed )
+ {
+ LOAD_( bhed );
+ is_apple_sbit = FT_BOOL( !error );
+ }
+
+ /* load the font header (`head' table) if this isn't an Apple */
+ /* sbit font file */
+ if ( !is_apple_sbit )
+ {
+ LOAD_( head );
+ if ( error )
+ goto Exit;
+ }
+
+ if ( face->header.Units_Per_EM == 0 )
+ {
+ error = SFNT_Err_Invalid_Table;
+
+ goto Exit;
+ }
+
+ /* the following tables are often not present in embedded TrueType */
+ /* fonts within PDF documents, so don't check for them. */
+ LOAD_( maxp );
+ LOAD_( cmap );
+
+ /* the following tables are optional in PCL fonts -- */
+ /* don't check for errors */
+ LOAD_( name );
+ LOAD_( post );
+ psnames_error = error;
+
+ /* do not load the metrics headers and tables if this is an Apple */
+ /* sbit font file */
+ if ( !is_apple_sbit )
+ {
+ /* load the `hhea' and `hmtx' tables */
+ LOADM_( hhea, 0 );
+ if ( !error )
+ {
+ LOADM_( hmtx, 0 );
+ if ( error == SFNT_Err_Table_Missing )
+ {
+ error = SFNT_Err_Hmtx_Table_Missing;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* If this is an incrementally loaded font and there are */
+ /* overriding metrics, tolerate a missing `hmtx' table. */
+ if ( face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->
+ get_glyph_metrics )
+ {
+ face->horizontal.number_Of_HMetrics = 0;
+ error = SFNT_Err_Ok;
+ }
+#endif
+ }
+ }
+ else if ( error == SFNT_Err_Table_Missing )
+ {
+ /* No `hhea' table necessary for SFNT Mac fonts. */
+ if ( face->format_tag == TTAG_true )
+ {
+ FT_TRACE2(( "This is an SFNT Mac font.\n" ));
+ has_outline = 0;
+ error = SFNT_Err_Ok;
+ }
+ else
+ {
+ error = SFNT_Err_Horiz_Header_Missing;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ /* If this is an incrementally loaded font and there are */
+ /* overriding metrics, tolerate a missing `hhea' table. */
+ if ( face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->
+ get_glyph_metrics )
+ {
+ face->horizontal.number_Of_HMetrics = 0;
+ error = SFNT_Err_Ok;
+ }
+#endif
+
+ }
+ }
+
+ if ( error )
+ goto Exit;
+
+ /* try to load the `vhea' and `vmtx' tables */
+ LOADM_( hhea, 1 );
+ if ( !error )
+ {
+ LOADM_( hmtx, 1 );
+ if ( !error )
+ face->vertical_info = 1;
+ }
+
+ if ( error && error != SFNT_Err_Table_Missing )
+ goto Exit;
+
+ LOAD_( os2 );
+ if ( error )
+ {
+ if ( error != SFNT_Err_Table_Missing )
+ goto Exit;
+
+ face->os2.version = 0xFFFFU;
+ }
+
+ }
+
+ /* the optional tables */
+
+ /* embedded bitmap support. */
+ if ( sfnt->load_eblc )
+ {
+ LOAD_( eblc );
+ if ( error )
+ {
+ /* return an error if this font file has no outlines */
+ if ( error == SFNT_Err_Table_Missing && has_outline )
+ error = SFNT_Err_Ok;
+ else
+ goto Exit;
+ }
+ }
+
+ LOAD_( pclt );
+ if ( error )
+ {
+ if ( error != SFNT_Err_Table_Missing )
+ goto Exit;
+
+ face->pclt.Version = 0;
+ }
+
+ /* consider the kerning and gasp tables as optional */
+ LOAD_( gasp );
+ LOAD_( kern );
+
+ error = SFNT_Err_Ok;
+
+ face->root.num_glyphs = face->max_profile.numGlyphs;
+
+ face->root.family_name = tt_face_get_name( face,
+ TT_NAME_ID_PREFERRED_FAMILY );
+ if ( !face->root.family_name )
+ face->root.family_name = tt_face_get_name( face,
+ TT_NAME_ID_FONT_FAMILY );
+
+ face->root.style_name = tt_face_get_name( face,
+ TT_NAME_ID_PREFERRED_SUBFAMILY );
+ if ( !face->root.style_name )
+ face->root.style_name = tt_face_get_name( face,
+ TT_NAME_ID_FONT_SUBFAMILY );
+
+ /* now set up root fields */
+ {
+ FT_Face root = &face->root;
+ FT_Int32 flags = root->face_flags;
+
+
+ /*********************************************************************/
+ /* */
+ /* Compute face flags. */
+ /* */
+ if ( has_outline == TRUE )
+ flags |= FT_FACE_FLAG_SCALABLE; /* scalable outlines */
+
+ /* The sfnt driver only supports bitmap fonts natively, thus we */
+ /* don't set FT_FACE_FLAG_HINTER. */
+ flags |= FT_FACE_FLAG_SFNT | /* SFNT file format */
+ FT_FACE_FLAG_HORIZONTAL; /* horizontal data */
+
+#ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ if ( psnames_error == SFNT_Err_Ok &&
+ face->postscript.FormatType != 0x00030000L )
+ flags |= FT_FACE_FLAG_GLYPH_NAMES;
+#endif
+
+ /* fixed width font? */
+ if ( face->postscript.isFixedPitch )
+ flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* vertical information? */
+ if ( face->vertical_info )
+ flags |= FT_FACE_FLAG_VERTICAL;
+
+ /* kerning available ? */
+ if ( TT_FACE_HAS_KERNING( face ) )
+ flags |= FT_FACE_FLAG_KERNING;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ /* Don't bother to load the tables unless somebody asks for them. */
+ /* No need to do work which will (probably) not be used. */
+ if ( tt_face_lookup_table( face, TTAG_glyf ) != 0 &&
+ tt_face_lookup_table( face, TTAG_fvar ) != 0 &&
+ tt_face_lookup_table( face, TTAG_gvar ) != 0 )
+ flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
+#endif
+
+ root->face_flags = flags;
+
+ /*********************************************************************/
+ /* */
+ /* Compute style flags. */
+ /* */
+ flags = 0;
+ if ( has_outline == TRUE && face->os2.version != 0xFFFFU )
+ {
+ /* we have an OS/2 table; use the `fsSelection' field */
+ if ( face->os2.fsSelection & 1 )
+ flags |= FT_STYLE_FLAG_ITALIC;
+
+ if ( face->os2.fsSelection & 32 )
+ flags |= FT_STYLE_FLAG_BOLD;
+ }
+ else
+ {
+ /* this is an old Mac font, use the header field */
+ if ( face->header.Mac_Style & 1 )
+ flags |= FT_STYLE_FLAG_BOLD;
+
+ if ( face->header.Mac_Style & 2 )
+ flags |= FT_STYLE_FLAG_ITALIC;
+ }
+
+ root->style_flags = flags;
+
+ /*********************************************************************/
+ /* */
+ /* Polish the charmaps. */
+ /* */
+ /* Try to set the charmap encoding according to the platform & */
+ /* encoding ID of each charmap. */
+ /* */
+
+ tt_face_build_cmaps( face ); /* ignore errors */
+
+
+ /* set the encoding fields */
+ {
+ FT_Int m;
+
+
+ for ( m = 0; m < root->num_charmaps; m++ )
+ {
+ FT_CharMap charmap = root->charmaps[m];
+
+
+ charmap->encoding = sfnt_find_encoding( charmap->platform_id,
+ charmap->encoding_id );
+
+#if 0
+ if ( root->charmap == NULL &&
+ charmap->encoding == FT_ENCODING_UNICODE )
+ {
+ /* set 'root->charmap' to the first Unicode encoding we find */
+ root->charmap = charmap;
+ }
+#endif
+ }
+ }
+
+
+ /*********************************************************************/
+ /* */
+ /* Set up metrics. */
+ /* */
+ if ( has_outline == TRUE )
+ {
+ /* XXX What about if outline header is missing */
+ /* (e.g. sfnt wrapped bitmap)? */
+ root->bbox.xMin = face->header.xMin;
+ root->bbox.yMin = face->header.yMin;
+ root->bbox.xMax = face->header.xMax;
+ root->bbox.yMax = face->header.yMax;
+ root->units_per_EM = face->header.Units_Per_EM;
+
+
+ /* XXX: Computing the ascender/descender/height is very different */
+ /* from what the specification tells you. Apparently, we */
+ /* must be careful because */
+ /* */
+ /* - not all fonts have an OS/2 table; in this case, we take */
+ /* the values in the horizontal header. However, these */
+ /* values very often are not reliable. */
+ /* */
+ /* - otherwise, the correct typographic values are in the */
+ /* sTypoAscender, sTypoDescender & sTypoLineGap fields. */
+ /* */
+ /* However, certain fonts have these fields set to 0. */
+ /* Rather, they have usWinAscent & usWinDescent correctly */
+ /* set (but with different values). */
+ /* */
+ /* As an example, Arial Narrow is implemented through four */
+ /* files ARIALN.TTF, ARIALNI.TTF, ARIALNB.TTF & ARIALNBI.TTF */
+ /* */
+ /* Strangely, all fonts have the same values in their */
+ /* sTypoXXX fields, except ARIALNB which sets them to 0. */
+ /* */
+ /* On the other hand, they all have different */
+ /* usWinAscent/Descent values -- as a conclusion, the OS/2 */
+ /* table cannot be used to compute the text height reliably! */
+ /* */
+
+ /* The ascender/descender/height are computed from the OS/2 table */
+ /* when found. Otherwise, they're taken from the horizontal */
+ /* header. */
+ /* */
+
+ root->ascender = face->horizontal.Ascender;
+ root->descender = face->horizontal.Descender;
+
+ root->height = (FT_Short)( root->ascender - root->descender +
+ face->horizontal.Line_Gap );
+
+#if 0
+ /* if the line_gap is 0, we add an extra 15% to the text height -- */
+ /* this computation is based on various versions of Times New Roman */
+ if ( face->horizontal.Line_Gap == 0 )
+ root->height = (FT_Short)( ( root->height * 115 + 50 ) / 100 );
+#endif
+
+#if 0
+
+ /* some fonts have the OS/2 "sTypoAscender", "sTypoDescender" & */
+ /* "sTypoLineGap" fields set to 0, like ARIALNB.TTF */
+ if ( face->os2.version != 0xFFFFU && root->ascender )
+ {
+ FT_Int height;
+
+
+ root->ascender = face->os2.sTypoAscender;
+ root->descender = -face->os2.sTypoDescender;
+
+ height = root->ascender + root->descender + face->os2.sTypoLineGap;
+ if ( height > root->height )
+ root->height = height;
+ }
+
+#endif /* 0 */
+
+ root->max_advance_width = face->horizontal.advance_Width_Max;
+
+ root->max_advance_height = (FT_Short)( face->vertical_info
+ ? face->vertical.advance_Height_Max
+ : root->height );
+
+ root->underline_position = face->postscript.underlinePosition;
+ root->underline_thickness = face->postscript.underlineThickness;
+ }
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ /*
+ * Now allocate the root array of FT_Bitmap_Size records and
+ * populate them. Unfortunately, it isn't possible to indicate bit
+ * depths in the FT_Bitmap_Size record. This is a design error.
+ */
+ {
+ FT_UInt i, count;
+
+
+#if !defined FT_CONFIG_OPTION_OLD_INTERNALS
+ count = face->sbit_num_strikes;
+#else
+ count = (FT_UInt)face->num_sbit_strikes;
+#endif
+
+ if ( count > 0 )
+ {
+ FT_Memory memory = face->root.stream->memory;
+ FT_UShort em_size = face->header.Units_Per_EM;
+ FT_Short avgwidth = face->os2.xAvgCharWidth;
+ FT_Size_Metrics metrics;
+
+
+ if ( em_size == 0 || face->os2.version == 0xFFFFU )
+ {
+ avgwidth = 0;
+ em_size = 1;
+ }
+
+ if ( FT_NEW_ARRAY( root->available_sizes, count ) )
+ goto Exit;
+
+ for ( i = 0; i < count; i++ )
+ {
+ FT_Bitmap_Size* bsize = root->available_sizes + i;
+
+
+ error = sfnt->load_strike_metrics( face, i, &metrics );
+ if ( error )
+ goto Exit;
+
+ bsize->height = (FT_Short)( metrics.height >> 6 );
+ bsize->width = (FT_Short)(
+ ( avgwidth * metrics.x_ppem + em_size / 2 ) / em_size );
+
+ bsize->x_ppem = metrics.x_ppem << 6;
+ bsize->y_ppem = metrics.y_ppem << 6;
+
+ /* assume 72dpi */
+ bsize->size = metrics.y_ppem << 6;
+ }
+
+ root->face_flags |= FT_FACE_FLAG_FIXED_SIZES;
+ root->num_fixed_sizes = (FT_Int)count;
+ }
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ }
+
+ Exit:
+ FT_TRACE2(( "sfnt_load_face: done\n" ));
+
+ return error;
+ }
+
+
+#undef LOAD_
+#undef LOADM_
+
+
+ FT_LOCAL_DEF( void )
+ sfnt_done_face( TT_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+
+ if ( sfnt )
+ {
+ /* destroy the postscript names table if it is loaded */
+ if ( sfnt->free_psnames )
+ sfnt->free_psnames( face );
+
+ /* destroy the embedded bitmaps table if it is loaded */
+ if ( sfnt->free_eblc )
+ sfnt->free_eblc( face );
+ }
+
+#ifdef TT_CONFIG_OPTION_BDF
+ /* freeing the embedded BDF properties */
+ tt_face_free_bdf_props( face );
+#endif
+
+ /* freeing the kerning table */
+ tt_face_done_kern( face );
+
+ /* freeing the collection table */
+ FT_FREE( face->ttc_header.offsets );
+ face->ttc_header.count = 0;
+
+ /* freeing table directory */
+ FT_FREE( face->dir_tables );
+ face->num_tables = 0;
+
+ {
+ FT_Stream stream = FT_FACE_STREAM( face );
+
+
+ /* simply release the 'cmap' table frame */
+ FT_FRAME_RELEASE( face->cmap_table );
+ face->cmap_size = 0;
+ }
+
+ /* freeing the horizontal metrics */
+#if !defined FT_CONFIG_OPTION_OLD_INTERNALS
+ {
+ FT_Stream stream = FT_FACE_STREAM( face );
+
+
+ FT_FRAME_RELEASE( face->horz_metrics );
+ FT_FRAME_RELEASE( face->vert_metrics );
+ face->horz_metrics_size = 0;
+ face->vert_metrics_size = 0;
+ }
+#else
+ FT_FREE( face->horizontal.long_metrics );
+ FT_FREE( face->horizontal.short_metrics );
+#endif
+
+ /* freeing the vertical ones, if any */
+ if ( face->vertical_info )
+ {
+ FT_FREE( face->vertical.long_metrics );
+ FT_FREE( face->vertical.short_metrics );
+ face->vertical_info = 0;
+ }
+
+ /* freeing the gasp table */
+ FT_FREE( face->gasp.gaspRanges );
+ face->gasp.numRanges = 0;
+
+ /* freeing the name table */
+ sfnt->free_name( face );
+
+ /* freeing family and style name */
+ FT_FREE( face->root.family_name );
+ FT_FREE( face->root.style_name );
+
+ /* freeing sbit size table */
+ FT_FREE( face->root.available_sizes );
+ face->root.num_fixed_sizes = 0;
+
+ FT_FREE( face->postscript_name );
+
+ face->sfnt = 0;
+ }
+
+
+/* END */
diff --git a/src/sfnt/sfobjs.h b/src/sfnt/sfobjs.h
new file mode 100644
index 0000000..6241c93
--- /dev/null
+++ b/src/sfnt/sfobjs.h
@@ -0,0 +1,54 @@
+/***************************************************************************/
+/* */
+/* sfobjs.h */
+/* */
+/* SFNT object management (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __SFOBJS_H__
+#define __SFOBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_SFNT_H
+#include FT_INTERNAL_OBJECTS_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ sfnt_init_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( FT_Error )
+ sfnt_load_face( FT_Stream stream,
+ TT_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ sfnt_done_face( TT_Face face );
+
+
+FT_END_HEADER
+
+#endif /* __SFDRIVER_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttbdf.c b/src/sfnt/ttbdf.c
new file mode 100644
index 0000000..6c95387
--- /dev/null
+++ b/src/sfnt/ttbdf.c
@@ -0,0 +1,250 @@
+/***************************************************************************/
+/* */
+/* ttbdf.c */
+/* */
+/* TrueType and OpenType embedded BDF properties (body). */
+/* */
+/* Copyright 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttbdf.h"
+
+#include "sferrors.h"
+
+
+#ifdef TT_CONFIG_OPTION_BDF
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttbdf
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_bdf_props( TT_Face face )
+ {
+ TT_BDF bdf = &face->bdf;
+
+
+ if ( bdf->loaded )
+ {
+ FT_Stream stream = FT_FACE(face)->stream;
+
+
+ if ( bdf->table != NULL )
+ FT_FRAME_RELEASE( bdf->table );
+
+ bdf->table_end = NULL;
+ bdf->strings = NULL;
+ bdf->strings_size = 0;
+ }
+ }
+
+
+ static FT_Error
+ tt_face_load_bdf_props( TT_Face face,
+ FT_Stream stream )
+ {
+ TT_BDF bdf = &face->bdf;
+ FT_ULong length;
+ FT_Error error;
+
+
+ FT_ZERO( bdf );
+
+ error = tt_face_goto_table( face, TTAG_BDF, stream, &length );
+ if ( error ||
+ length < 8 ||
+ FT_FRAME_EXTRACT( length, bdf->table ) )
+ {
+ error = FT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ bdf->table_end = bdf->table + length;
+
+ {
+ FT_Byte* p = bdf->table;
+ FT_UInt version = FT_NEXT_USHORT( p );
+ FT_UInt num_strikes = FT_NEXT_USHORT( p );
+ FT_UInt32 strings = FT_NEXT_ULONG ( p );
+ FT_UInt count;
+ FT_Byte* strike;
+
+
+ if ( version != 0x0001 ||
+ strings < 8 ||
+ ( strings - 8 ) / 4 < num_strikes ||
+ strings + 1 > length )
+ {
+ goto BadTable;
+ }
+
+ bdf->num_strikes = num_strikes;
+ bdf->strings = bdf->table + strings;
+ bdf->strings_size = length - strings;
+
+ count = bdf->num_strikes;
+ p = bdf->table + 8;
+ strike = p + count * 4;
+
+
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt num_items = FT_PEEK_USHORT( p + 2 );
+
+ /*
+ * We don't need to check the value sets themselves, since this
+ * is done later.
+ */
+ strike += 10 * num_items;
+
+ p += 4;
+ }
+
+ if ( strike > bdf->strings )
+ goto BadTable;
+ }
+
+ bdf->loaded = 1;
+
+ Exit:
+ return error;
+
+ BadTable:
+ FT_FRAME_RELEASE( bdf->table );
+ FT_ZERO( bdf );
+ error = FT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_find_bdf_prop( TT_Face face,
+ const char* property_name,
+ BDF_PropertyRec *aprop )
+ {
+ TT_BDF bdf = &face->bdf;
+ FT_Size size = FT_FACE(face)->size;
+ FT_Error error = 0;
+ FT_Byte* p;
+ FT_UInt count;
+ FT_Byte* strike;
+ FT_UInt property_len;
+
+
+ aprop->type = BDF_PROPERTY_TYPE_NONE;
+
+ if ( bdf->loaded == 0 )
+ {
+ error = tt_face_load_bdf_props( face, FT_FACE( face )->stream );
+ if ( error )
+ goto Exit;
+ }
+
+ count = bdf->num_strikes;
+ p = bdf->table + 8;
+ strike = p + 4 * count;
+
+ error = FT_Err_Invalid_Argument;
+
+ if ( size == NULL || property_name == NULL )
+ goto Exit;
+
+ property_len = ft_strlen( property_name );
+ if ( property_len == 0 )
+ goto Exit;
+
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt _ppem = FT_NEXT_USHORT( p );
+ FT_UInt _count = FT_NEXT_USHORT( p );
+
+ if ( _ppem == size->metrics.y_ppem )
+ {
+ count = _count;
+ goto FoundStrike;
+ }
+
+ strike += 10 * _count;
+ }
+ goto Exit;
+
+ FoundStrike:
+ p = strike;
+ for ( ; count > 0; count-- )
+ {
+ FT_UInt type = FT_PEEK_USHORT( p + 4 );
+
+ if ( ( type & 0x10 ) != 0 )
+ {
+ FT_UInt32 name_offset = FT_PEEK_ULONG( p );
+ FT_UInt32 value = FT_PEEK_ULONG( p + 6 );
+
+ /* be a bit paranoid for invalid entries here */
+ if ( name_offset < bdf->strings_size &&
+ property_len < bdf->strings_size - name_offset &&
+ ft_strncmp( property_name,
+ (const char*)bdf->strings + name_offset,
+ bdf->strings_size - name_offset ) == 0 )
+ {
+ switch ( type & 0x0F )
+ {
+ case 0x00: /* string */
+ case 0x01: /* atoms */
+ /* check that the content is really 0-terminated */
+ if ( value < bdf->strings_size &&
+ ft_memchr( bdf->strings + value, 0, bdf->strings_size ) )
+ {
+ aprop->type = BDF_PROPERTY_TYPE_ATOM;
+ aprop->u.atom = (const char*)bdf->strings + value;
+ error = 0;
+ goto Exit;
+ }
+ break;
+
+ case 0x02:
+ aprop->type = BDF_PROPERTY_TYPE_INTEGER;
+ aprop->u.integer = (FT_Int32)value;
+ error = 0;
+ goto Exit;
+
+ case 0x03:
+ aprop->type = BDF_PROPERTY_TYPE_CARDINAL;
+ aprop->u.cardinal = value;
+ error = 0;
+ goto Exit;
+
+ default:
+ ;
+ }
+ }
+ }
+ p += 10;
+ }
+
+ Exit:
+ return error;
+ }
+
+#endif /* TT_CONFIG_OPTION_BDF */
+
+
+/* END */
diff --git a/src/sfnt/ttbdf.h b/src/sfnt/ttbdf.h
new file mode 100644
index 0000000..48a10d6
--- /dev/null
+++ b/src/sfnt/ttbdf.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* ttbdf.h */
+/* */
+/* TrueType and OpenType embedded BDF properties (specification). */
+/* */
+/* Copyright 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTBDF_H__
+#define __TTBDF_H__
+
+
+#include <ft2build.h>
+#include "ttload.h"
+#include FT_BDF_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ tt_face_free_bdf_props( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_find_bdf_prop( TT_Face face,
+ const char* property_name,
+ BDF_PropertyRec *aprop );
+
+
+FT_END_HEADER
+
+#endif /* __TTBDF_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttcmap.c b/src/sfnt/ttcmap.c
new file mode 100644
index 0000000..854d567
--- /dev/null
+++ b/src/sfnt/ttcmap.c
@@ -0,0 +1,2356 @@
+/***************************************************************************/
+/* */
+/* ttcmap.c */
+/* */
+/* TrueType character mapping table (cmap) support (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+
+#include "sferrors.h" /* must come before FT_INTERNAL_VALIDATE_H */
+
+#include FT_INTERNAL_VALIDATE_H
+#include FT_INTERNAL_STREAM_H
+#include "ttload.h"
+#include "ttcmap.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttcmap
+
+
+#define TT_PEEK_SHORT FT_PEEK_SHORT
+#define TT_PEEK_USHORT FT_PEEK_USHORT
+#define TT_PEEK_LONG FT_PEEK_LONG
+#define TT_PEEK_ULONG FT_PEEK_ULONG
+
+#define TT_NEXT_SHORT FT_NEXT_SHORT
+#define TT_NEXT_USHORT FT_NEXT_USHORT
+#define TT_NEXT_LONG FT_NEXT_LONG
+#define TT_NEXT_ULONG FT_NEXT_ULONG
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap_init( TT_CMap cmap,
+ FT_Byte* table )
+ {
+ cmap->data = table;
+ return SFNT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 0 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 0 */
+ /* length 2 USHORT table length in bytes */
+ /* language 4 USHORT Mac language code */
+ /* glyph_ids 6 BYTE[256] array of glyph indices */
+ /* 262 */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_0
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap0_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 2;
+ FT_UInt length = TT_NEXT_USHORT( p );
+
+
+ if ( table + length > valid->limit || length < 262 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices whenever necessary */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt n, idx;
+
+
+ p = table + 6;
+ for ( n = 0; n < 256; n++ )
+ {
+ idx = *p++;
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap0_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+
+
+ return char_code < 256 ? table[6 + char_code] : 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap0_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 charcode = *pchar_code;
+ FT_UInt32 result = 0;
+ FT_UInt gindex = 0;
+
+
+ table += 6; /* go to glyph ids */
+ while ( ++charcode < 256 )
+ {
+ gindex = table[charcode];
+ if ( gindex != 0 )
+ {
+ result = charcode;
+ break;
+ }
+ }
+
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap0_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 0;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap0_class_rec =
+ {
+ {
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap0_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap0_char_next
+ },
+ 0,
+ (TT_CMap_ValidateFunc) tt_cmap0_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap0_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_0 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 2 *****/
+ /***** *****/
+ /***** This is used for certain CJK encodings that encode text in a *****/
+ /***** mixed 8/16 bits encoding along the following lines: *****/
+ /***** *****/
+ /***** * Certain byte values correspond to an 8-bit character code *****/
+ /***** (typically in the range 0..127 for ASCII compatibility). *****/
+ /***** *****/
+ /***** * Certain byte values signal the first byte of a 2-byte *****/
+ /***** character code (but these values are also valid as the *****/
+ /***** second byte of a 2-byte character). *****/
+ /***** *****/
+ /***** The following charmap lookup and iteration functions all *****/
+ /***** assume that the value "charcode" correspond to following: *****/
+ /***** *****/
+ /***** - For one byte characters, "charcode" is simply the *****/
+ /***** character code. *****/
+ /***** *****/
+ /***** - For two byte characters, "charcode" is the 2-byte *****/
+ /***** character code in big endian format. More exactly: *****/
+ /***** *****/
+ /***** (charcode >> 8) is the first byte value *****/
+ /***** (charcode & 0xFF) is the second byte value *****/
+ /***** *****/
+ /***** Note that not all values of "charcode" are valid according *****/
+ /***** to these rules, and the function moderately check the *****/
+ /***** arguments. *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 2 */
+ /* length 2 USHORT table length in bytes */
+ /* language 4 USHORT Mac language code */
+ /* keys 6 USHORT[256] sub-header keys */
+ /* subs 518 SUBHEAD[NSUBS] sub-headers array */
+ /* glyph_ids 518+NSUB*8 USHORT[] glyph id array */
+ /* */
+ /* The `keys' table is used to map charcode high-bytes to sub-headers. */
+ /* The value of `NSUBS' is the number of sub-headers defined in the */
+ /* table and is computed by finding the maximum of the `keys' table. */
+ /* */
+ /* Note that for any n, `keys[n]' is a byte offset within the `subs' */
+ /* table, i.e., it is the corresponding sub-header index multiplied */
+ /* by 8. */
+ /* */
+ /* Each sub-header has the following format: */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* first 0 USHORT first valid low-byte */
+ /* count 2 USHORT number of valid low-bytes */
+ /* delta 4 SHORT see below */
+ /* offset 6 USHORT see below */
+ /* */
+ /* A sub-header defines, for each high-byte, the range of valid */
+ /* low-bytes within the charmap. Note that the range defined by `first' */
+ /* and `count' must be completely included in the interval [0..255] */
+ /* according to the specification. */
+ /* */
+ /* If a character code is contained within a given sub-header, then */
+ /* mapping it to a glyph index is done as follows: */
+ /* */
+ /* * The value of `offset' is read. This is a _byte_ distance from the */
+ /* location of the `offset' field itself into a slice of the */
+ /* `glyph_ids' table. Let's call it `slice' (it's a USHORT[] too). */
+ /* */
+ /* * The value `slice[char.lo - first]' is read. If it is 0, there is */
+ /* no glyph for the charcode. Otherwise, the value of `delta' is */
+ /* added to it (modulo 65536) to form a new glyph index. */
+ /* */
+ /* It is up to the validation routine to check that all offsets fall */
+ /* within the glyph ids table (and not within the `subs' table itself or */
+ /* outside of the CMap). */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_2
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap2_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 2; /* skip format */
+ FT_UInt length = TT_PEEK_USHORT( p );
+ FT_UInt n, max_subs;
+ FT_Byte* keys; /* keys table */
+ FT_Byte* subs; /* sub-headers */
+ FT_Byte* glyph_ids; /* glyph id array */
+
+
+ if ( table + length > valid->limit || length < 6 + 512 )
+ FT_INVALID_TOO_SHORT;
+
+ keys = table + 6;
+
+ /* parse keys to compute sub-headers count */
+ p = keys;
+ max_subs = 0;
+ for ( n = 0; n < 256; n++ )
+ {
+ FT_UInt idx = TT_NEXT_USHORT( p );
+
+
+ /* value must be multiple of 8 */
+ if ( valid->level >= FT_VALIDATE_PARANOID && ( idx & 7 ) != 0 )
+ FT_INVALID_DATA;
+
+ idx >>= 3;
+
+ if ( idx > max_subs )
+ max_subs = idx;
+ }
+
+ FT_ASSERT( p == table + 518 );
+
+ subs = p;
+ glyph_ids = subs + (max_subs + 1) * 8;
+ if ( glyph_ids > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ /* parse sub-headers */
+ for ( n = 0; n <= max_subs; n++ )
+ {
+ FT_UInt first_code, code_count, offset;
+ FT_Int delta;
+ FT_Byte* ids;
+
+
+ first_code = TT_NEXT_USHORT( p );
+ code_count = TT_NEXT_USHORT( p );
+ delta = TT_NEXT_SHORT( p );
+ offset = TT_NEXT_USHORT( p );
+
+ /* check range within 0..255 */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ if ( first_code >= 256 || first_code + code_count > 256 )
+ FT_INVALID_DATA;
+ }
+
+ /* check offset */
+ if ( offset != 0 )
+ {
+ ids = p - 2 + offset;
+ if ( ids < glyph_ids || ids + code_count*2 > table + length )
+ FT_INVALID_OFFSET;
+
+ /* check glyph ids */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_Byte* limit = p + code_count * 2;
+ FT_UInt idx;
+
+
+ for ( ; p < limit; )
+ {
+ idx = TT_NEXT_USHORT( p );
+ if ( idx != 0 )
+ {
+ idx = ( idx + delta ) & 0xFFFFU;
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+ }
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ /* return sub header corresponding to a given character code */
+ /* NULL on invalid charcode */
+ static FT_Byte*
+ tt_cmap2_get_subheader( FT_Byte* table,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* result = NULL;
+
+
+ if ( char_code < 0x10000UL )
+ {
+ FT_UInt char_lo = (FT_UInt)( char_code & 0xFF );
+ FT_UInt char_hi = (FT_UInt)( char_code >> 8 );
+ FT_Byte* p = table + 6; /* keys table */
+ FT_Byte* subs = table + 518; /* subheaders table */
+ FT_Byte* sub;
+
+
+ if ( char_hi == 0 )
+ {
+ /* an 8-bit character code -- we use subHeader 0 in this case */
+ /* to test whether the character code is in the charmap */
+ /* */
+ sub = subs; /* jump to first sub-header */
+
+ /* check that the sub-header for this byte is 0, which */
+ /* indicates that it's really a valid one-byte value */
+ /* Otherwise, return 0 */
+ /* */
+ p += char_lo * 2;
+ if ( TT_PEEK_USHORT( p ) != 0 )
+ goto Exit;
+ }
+ else
+ {
+ /* a 16-bit character code */
+
+ /* jump to key entry */
+ p += char_hi * 2;
+ /* jump to sub-header */
+ sub = subs + ( FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 8 ) );
+
+ /* check that the high byte isn't a valid one-byte value */
+ if ( sub == subs )
+ goto Exit;
+ }
+ result = sub;
+ }
+ Exit:
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap2_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* subheader;
+
+
+ subheader = tt_cmap2_get_subheader( table, char_code );
+ if ( subheader )
+ {
+ FT_Byte* p = subheader;
+ FT_UInt idx = (FT_UInt)(char_code & 0xFF);
+ FT_UInt start, count;
+ FT_Int delta;
+ FT_UInt offset;
+
+
+ start = TT_NEXT_USHORT( p );
+ count = TT_NEXT_USHORT( p );
+ delta = TT_NEXT_SHORT ( p );
+ offset = TT_PEEK_USHORT( p );
+
+ idx -= start;
+ if ( idx < count && offset != 0 )
+ {
+ p += offset + 2 * idx;
+ idx = TT_PEEK_USHORT( p );
+
+ if ( idx != 0 )
+ result = (FT_UInt)( idx + delta ) & 0xFFFFU;
+ }
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap2_char_next( TT_CMap cmap,
+ FT_UInt32 *pcharcode )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt gindex = 0;
+ FT_UInt32 result = 0;
+ FT_UInt32 charcode = *pcharcode + 1;
+ FT_Byte* subheader;
+
+
+ while ( charcode < 0x10000UL )
+ {
+ subheader = tt_cmap2_get_subheader( table, charcode );
+ if ( subheader )
+ {
+ FT_Byte* p = subheader;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_Int delta = TT_NEXT_SHORT ( p );
+ FT_UInt offset = TT_PEEK_USHORT( p );
+ FT_UInt char_lo = (FT_UInt)( charcode & 0xFF );
+ FT_UInt pos, idx;
+
+
+ if ( offset == 0 )
+ goto Next_SubHeader;
+
+ if ( char_lo < start )
+ {
+ char_lo = start;
+ pos = 0;
+ }
+ else
+ pos = (FT_UInt)( char_lo - start );
+
+ p += offset + pos * 2;
+ charcode = FT_PAD_FLOOR( charcode, 256 ) + char_lo;
+
+ for ( ; pos < count; pos++, charcode++ )
+ {
+ idx = TT_NEXT_USHORT( p );
+
+ if ( idx != 0 )
+ {
+ gindex = ( idx + delta ) & 0xFFFFU;
+ if ( gindex != 0 )
+ {
+ result = charcode;
+ goto Exit;
+ }
+ }
+ }
+ }
+
+ /* jump to next sub-header, i.e. higher byte value */
+ Next_SubHeader:
+ charcode = FT_PAD_FLOOR( charcode, 256 ) + 256;
+ }
+
+ Exit:
+ *pcharcode = result;
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap2_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 2;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap2_class_rec =
+ {
+ {
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap2_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap2_char_next
+ },
+ 2,
+ (TT_CMap_ValidateFunc) tt_cmap2_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap2_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_2 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 4 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 4 */
+ /* length 2 USHORT table length */
+ /* in bytes */
+ /* language 4 USHORT Mac language code */
+ /* */
+ /* segCountX2 6 USHORT 2*NUM_SEGS */
+ /* searchRange 8 USHORT 2*(1 << LOG_SEGS) */
+ /* entrySelector 10 USHORT LOG_SEGS */
+ /* rangeShift 12 USHORT segCountX2 - */
+ /* searchRange */
+ /* */
+ /* endCount 14 USHORT[NUM_SEGS] end charcode for */
+ /* each segment; last */
+ /* is 0xFFFF */
+ /* */
+ /* pad 14+NUM_SEGS*2 USHORT padding */
+ /* */
+ /* startCount 16+NUM_SEGS*2 USHORT[NUM_SEGS] first charcode for */
+ /* each segment */
+ /* */
+ /* idDelta 16+NUM_SEGS*4 SHORT[NUM_SEGS] delta for each */
+ /* segment */
+ /* idOffset 16+NUM_SEGS*6 SHORT[NUM_SEGS] range offset for */
+ /* each segment; can be */
+ /* zero */
+ /* */
+ /* glyphIds 16+NUM_SEGS*8 USHORT[] array of glyph id */
+ /* ranges */
+ /* */
+ /* Character codes are modelled by a series of ordered (increasing) */
+ /* intervals called segments. Each segment has start and end codes, */
+ /* provided by the `startCount' and `endCount' arrays. Segments must */
+ /* not be overlapping and the last segment should always contain the */
+ /* `0xFFFF' endCount. */
+ /* */
+ /* The fields `searchRange', `entrySelector' and `rangeShift' are better */
+ /* ignored (they are traces of over-engineering in the TrueType */
+ /* specification). */
+ /* */
+ /* Each segment also has a signed `delta', as well as an optional offset */
+ /* within the `glyphIds' table. */
+ /* */
+ /* If a segment's idOffset is 0, the glyph index corresponding to any */
+ /* charcode within the segment is obtained by adding the value of */
+ /* `idDelta' directly to the charcode, modulo 65536. */
+ /* */
+ /* Otherwise, a glyph index is taken from the glyph ids sub-array for */
+ /* the segment, and the value of `idDelta' is added to it. */
+ /* */
+ /* */
+ /* Finally, note that certain fonts contain invalid charmaps that */
+ /* contain end=0xFFFF, start=0xFFFF, delta=0x0001, offset=0xFFFF at the */
+ /* of their charmaps (e.g. opens___.ttf which comes with OpenOffice.org) */
+ /* we need special code to deal with them correctly... */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_4
+
+ typedef struct TT_CMap4Rec_
+ {
+ TT_CMapRec cmap;
+ FT_UInt32 cur_charcode; /* current charcode */
+ FT_UInt cur_gindex; /* current glyph index */
+
+ FT_UInt num_ranges;
+ FT_UInt cur_range;
+ FT_UInt cur_start;
+ FT_UInt cur_end;
+ FT_Int cur_delta;
+ FT_Byte* cur_values;
+
+ } TT_CMap4Rec, *TT_CMap4;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_init( TT_CMap4 cmap,
+ FT_Byte* table )
+ {
+ FT_Byte* p;
+
+
+ cmap->cmap.data = table;
+
+ p = table + 6;
+ cmap->num_ranges = FT_PEEK_USHORT( p ) >> 1;
+ cmap->cur_charcode = 0xFFFFFFFFUL;
+ cmap->cur_gindex = 0;
+
+ return SFNT_Err_Ok;
+ }
+
+
+ static FT_Int
+ tt_cmap4_set_range( TT_CMap4 cmap,
+ FT_UInt range_index )
+ {
+ FT_Byte* table = cmap->cmap.data;
+ FT_Byte* p;
+ FT_UInt num_ranges = cmap->num_ranges;
+
+
+ while ( range_index < num_ranges )
+ {
+ FT_UInt offset;
+
+
+ p = table + 14 + range_index * 2;
+ cmap->cur_end = FT_PEEK_USHORT( p );
+
+ p += 2 + num_ranges * 2;
+ cmap->cur_start = FT_PEEK_USHORT( p );
+
+ p += num_ranges * 2;
+ cmap->cur_delta = FT_PEEK_SHORT( p );
+
+ p += num_ranges * 2;
+ offset = FT_PEEK_USHORT( p );
+
+ if ( offset != 0xFFFFU )
+ {
+ cmap->cur_values = offset ? p + offset : NULL;
+ cmap->cur_range = range_index;
+ return 0;
+ }
+
+ /* we skip empty segments */
+ range_index++;
+ }
+
+ return -1;
+ }
+
+
+ /* search the index of the charcode next to cmap->cur_charcode; */
+ /* caller should call tt_cmap4_set_range with proper range */
+ /* before calling this function */
+ /* */
+ static void
+ tt_cmap4_next( TT_CMap4 cmap )
+ {
+ FT_UInt charcode;
+
+
+ if ( cmap->cur_charcode >= 0xFFFFUL )
+ goto Fail;
+
+ charcode = cmap->cur_charcode + 1;
+
+ if ( charcode < cmap->cur_start )
+ charcode = cmap->cur_start;
+
+ for ( ;; )
+ {
+ FT_Byte* values = cmap->cur_values;
+ FT_UInt end = cmap->cur_end;
+ FT_Int delta = cmap->cur_delta;
+
+
+ if ( charcode <= end )
+ {
+ if ( values )
+ {
+ FT_Byte* p = values + 2 * ( charcode - cmap->cur_start );
+
+
+ do
+ {
+ FT_UInt gindex = FT_NEXT_USHORT( p );
+
+
+ if ( gindex != 0 )
+ {
+ gindex = (FT_UInt)( ( gindex + delta ) & 0xFFFFU );
+ if ( gindex != 0 )
+ {
+ cmap->cur_charcode = charcode;
+ cmap->cur_gindex = gindex;
+ return;
+ }
+ }
+ } while ( ++charcode <= end );
+ }
+ else
+ {
+ do
+ {
+ FT_UInt gindex = (FT_UInt)( ( charcode + delta ) & 0xFFFFU );
+
+
+ if ( gindex != 0 )
+ {
+ cmap->cur_charcode = charcode;
+ cmap->cur_gindex = gindex;
+ return;
+ }
+ } while ( ++charcode <= end );
+ }
+ }
+
+ /* we need to find another range */
+ if ( tt_cmap4_set_range( cmap, cmap->cur_range + 1 ) < 0 )
+ break;
+
+ if ( charcode < cmap->cur_start )
+ charcode = cmap->cur_start;
+ }
+
+ Fail:
+ cmap->cur_charcode = 0xFFFFFFFFUL;
+ cmap->cur_gindex = 0;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 2; /* skip format */
+ FT_UInt length = TT_NEXT_USHORT( p );
+ FT_Byte *ends, *starts, *offsets, *deltas, *glyph_ids;
+ FT_UInt num_segs;
+ FT_Error error = SFNT_Err_Ok;
+
+
+ if ( length < 16 )
+ FT_INVALID_TOO_SHORT;
+
+ /* in certain fonts, the `length' field is invalid and goes */
+ /* out of bound. We try to correct this here... */
+ if ( table + length > valid->limit )
+ {
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_TOO_SHORT;
+
+ length = (FT_UInt)( valid->limit - table );
+ }
+
+ p = table + 6;
+ num_segs = TT_NEXT_USHORT( p ); /* read segCountX2 */
+
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ /* check that we have an even value here */
+ if ( num_segs & 1 )
+ FT_INVALID_DATA;
+ }
+
+ num_segs /= 2;
+
+ if ( length < 16 + num_segs * 2 * 4 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check the search parameters - even though we never use them */
+ /* */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ /* check the values of 'searchRange', 'entrySelector', 'rangeShift' */
+ FT_UInt search_range = TT_NEXT_USHORT( p );
+ FT_UInt entry_selector = TT_NEXT_USHORT( p );
+ FT_UInt range_shift = TT_NEXT_USHORT( p );
+
+
+ if ( ( search_range | range_shift ) & 1 ) /* must be even values */
+ FT_INVALID_DATA;
+
+ search_range /= 2;
+ range_shift /= 2;
+
+ /* `search range' is the greatest power of 2 that is <= num_segs */
+
+ if ( search_range > num_segs ||
+ search_range * 2 < num_segs ||
+ search_range + range_shift != num_segs ||
+ search_range != ( 1U << entry_selector ) )
+ FT_INVALID_DATA;
+ }
+
+ ends = table + 14;
+ starts = table + 16 + num_segs * 2;
+ deltas = starts + num_segs * 2;
+ offsets = deltas + num_segs * 2;
+ glyph_ids = offsets + num_segs * 2;
+
+ /* check last segment, its end count must be FFFF */
+ if ( valid->level >= FT_VALIDATE_PARANOID )
+ {
+ p = ends + ( num_segs - 1 ) * 2;
+ if ( TT_PEEK_USHORT( p ) != 0xFFFFU )
+ FT_INVALID_DATA;
+ }
+
+ {
+ FT_UInt start, end, offset, n;
+ FT_UInt last_start = 0, last_end = 0;
+ FT_Int delta;
+ FT_Byte* p_start = starts;
+ FT_Byte* p_end = ends;
+ FT_Byte* p_delta = deltas;
+ FT_Byte* p_offset = offsets;
+
+
+ for ( n = 0; n < num_segs; n++ )
+ {
+ p = p_offset;
+ start = TT_NEXT_USHORT( p_start );
+ end = TT_NEXT_USHORT( p_end );
+ delta = TT_NEXT_SHORT( p_delta );
+ offset = TT_NEXT_USHORT( p_offset );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ /* this test should be performed at default validation level; */
+ /* unfortunately, some popular Asian fonts present overlapping */
+ /* ranges in their charmaps */
+ /* */
+ if ( start <= last_end && n > 0 )
+ {
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ FT_INVALID_DATA;
+ else
+ {
+ /* allow overlapping segments, provided their start points */
+ /* and end points, respectively, are in ascending order. */
+ /* */
+ if ( last_start > start || last_end > end )
+ error |= TT_CMAP_FLAG_UNSORTED;
+ else
+ error |= TT_CMAP_FLAG_OVERLAPPING;
+ }
+ }
+
+ if ( offset && offset != 0xFFFFU )
+ {
+ p += offset; /* start of glyph id array */
+
+ /* check that we point within the glyph ids table only */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ if ( p < glyph_ids ||
+ p + ( end - start + 1 ) * 2 > table + length )
+ FT_INVALID_DATA;
+ }
+ else
+ {
+ if ( p < glyph_ids ||
+ p + ( end - start + 1 ) * 2 > valid->limit )
+ FT_INVALID_DATA;
+ }
+
+ /* check glyph indices within the segment range */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt i, idx;
+
+
+ for ( i = start; i < end; i++ )
+ {
+ idx = FT_NEXT_USHORT( p );
+ if ( idx != 0 )
+ {
+ idx = (FT_UInt)( idx + delta ) & 0xFFFFU;
+
+ if ( idx >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+ }
+ }
+ else if ( offset == 0xFFFFU )
+ {
+ /* Some fonts (erroneously?) use a range offset of 0xFFFF */
+ /* to mean missing glyph in cmap table */
+ /* */
+ if ( valid->level >= FT_VALIDATE_PARANOID ||
+ n != num_segs - 1 ||
+ !( start == 0xFFFFU && end == 0xFFFFU && delta == 0x1U ) )
+ FT_INVALID_DATA;
+ }
+
+ last_start = start;
+ last_end = end;
+ }
+ }
+
+ return error;
+ }
+
+
+ static FT_UInt
+ tt_cmap4_char_map_linear( TT_CMap cmap,
+ FT_UInt* pcharcode,
+ FT_Bool next )
+ {
+ FT_UInt num_segs2, start, end, offset;
+ FT_Int delta;
+ FT_UInt i, num_segs;
+ FT_UInt32 charcode = *pcharcode;
+ FT_UInt gindex = 0;
+ FT_Byte* p;
+
+
+ p = cmap->data + 6;
+ num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
+
+ num_segs = num_segs2 >> 1;
+
+ if ( !num_segs )
+ return 0;
+
+ if ( next )
+ charcode++;
+
+ /* linear search */
+ for ( ; charcode <= 0xFFFFU; charcode++ )
+ {
+ FT_Byte* q;
+
+
+ p = cmap->data + 14; /* ends table */
+ q = cmap->data + 16 + num_segs2; /* starts table */
+
+ for ( i = 0; i < num_segs; i++ )
+ {
+ end = TT_NEXT_USHORT( p );
+ start = TT_NEXT_USHORT( q );
+
+ if ( charcode >= start && charcode <= end )
+ {
+ p = q - 2 + num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ if ( offset == 0xFFFFU )
+ continue;
+
+ if ( offset )
+ {
+ p += offset + ( charcode - start ) * 2;
+ gindex = TT_PEEK_USHORT( p );
+ if ( gindex != 0 )
+ gindex = (FT_UInt)( gindex + delta ) & 0xFFFFU;
+ }
+ else
+ gindex = (FT_UInt)( charcode + delta ) & 0xFFFFU;
+
+ break;
+ }
+ }
+
+ if ( !next || gindex )
+ break;
+ }
+
+ if ( next && gindex )
+ *pcharcode = charcode;
+
+ return gindex;
+ }
+
+
+ static FT_UInt
+ tt_cmap4_char_map_binary( TT_CMap cmap,
+ FT_UInt* pcharcode,
+ FT_Bool next )
+ {
+ FT_UInt num_segs2, start, end, offset;
+ FT_Int delta;
+ FT_UInt max, min, mid, num_segs;
+ FT_UInt charcode = *pcharcode;
+ FT_UInt gindex = 0;
+ FT_Byte* p;
+
+
+ p = cmap->data + 6;
+ num_segs2 = FT_PAD_FLOOR( TT_PEEK_USHORT( p ), 2 );
+
+ if ( !num_segs2 )
+ return 0;
+
+ num_segs = num_segs2 >> 1;
+
+ /* make compiler happy */
+ mid = num_segs;
+ end = 0xFFFFU;
+
+ if ( next )
+ charcode++;
+
+ min = 0;
+ max = num_segs;
+
+ /* binary search */
+ while ( min < max )
+ {
+ mid = ( min + max ) >> 1;
+ p = cmap->data + 14 + mid * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+
+ if ( charcode < start )
+ max = mid;
+ else if ( charcode > end )
+ min = mid + 1;
+ else
+ {
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ /* search the first segment containing `charcode' */
+ if ( cmap->flags & TT_CMAP_FLAG_OVERLAPPING )
+ {
+ FT_UInt i;
+
+
+ /* call the current segment `max' */
+ max = mid;
+
+ if ( offset == 0xFFFFU )
+ mid = max + 1;
+
+ /* search in segments before the current segment */
+ for ( i = max ; i > 0; i-- )
+ {
+ FT_UInt prev_end;
+ FT_Byte* old_p;
+
+
+ old_p = p;
+ p = cmap->data + 14 + ( i - 1 ) * 2;
+ prev_end = TT_PEEK_USHORT( p );
+
+ if ( charcode > prev_end )
+ {
+ p = old_p;
+ break;
+ }
+
+ end = prev_end;
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ if ( offset != 0xFFFFU )
+ mid = i - 1;
+ }
+
+ /* no luck */
+ if ( mid == max + 1 )
+ {
+ if ( i != max )
+ {
+ p = cmap->data + 14 + max * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+ }
+
+ mid = max;
+
+ /* search in segments after the current segment */
+ for ( i = max + 1; i < num_segs; i++ )
+ {
+ FT_UInt next_end, next_start;
+
+
+ p = cmap->data + 14 + i * 2;
+ next_end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ next_start = TT_PEEK_USHORT( p );
+
+ if ( charcode < next_start )
+ break;
+
+ end = next_end;
+ start = next_start;
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+
+ if ( offset != 0xFFFFU )
+ mid = i;
+ }
+ i--;
+
+ /* still no luck */
+ if ( mid == max )
+ {
+ mid = i;
+
+ break;
+ }
+ }
+
+ /* end, start, delta, and offset are for the i'th segment */
+ if ( mid != i )
+ {
+ p = cmap->data + 14 + mid * 2;
+ end = TT_PEEK_USHORT( p );
+ p += 2 + num_segs2;
+ start = TT_PEEK_USHORT( p );
+ p += num_segs2;
+ delta = TT_PEEK_SHORT( p );
+ p += num_segs2;
+ offset = TT_PEEK_USHORT( p );
+ }
+ }
+ else
+ {
+ if ( offset == 0xFFFFU )
+ break;
+ }
+
+ if ( offset )
+ {
+ p += offset + ( charcode - start ) * 2;
+ gindex = TT_PEEK_USHORT( p );
+ if ( gindex != 0 )
+ gindex = (FT_UInt)( gindex + delta ) & 0xFFFFU;
+ }
+ else
+ gindex = (FT_UInt)( charcode + delta ) & 0xFFFFU;
+
+ break;
+ }
+ }
+
+ if ( next )
+ {
+ TT_CMap4 cmap4 = (TT_CMap4)cmap;
+
+
+ /* if `charcode' is not in any segment, then `mid' is */
+ /* the segment nearest to `charcode' */
+ /* */
+
+ if ( charcode > end )
+ {
+ mid++;
+ if ( mid == num_segs )
+ return 0;
+ }
+
+ if ( tt_cmap4_set_range( cmap4, mid ) )
+ {
+ if ( gindex )
+ *pcharcode = charcode;
+ }
+ else
+ {
+ cmap4->cur_charcode = charcode;
+
+ if ( gindex )
+ cmap4->cur_gindex = gindex;
+ else
+ {
+ cmap4->cur_charcode = charcode;
+ tt_cmap4_next( cmap4 );
+ gindex = cmap4->cur_gindex;
+ }
+
+ if ( gindex )
+ *pcharcode = cmap4->cur_charcode;
+ }
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap4_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ if ( char_code >= 0x10000UL )
+ return 0;
+
+ if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
+ return tt_cmap4_char_map_linear( cmap, &char_code, 0 );
+ else
+ return tt_cmap4_char_map_binary( cmap, &char_code, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap4_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt gindex;
+
+
+ if ( *pchar_code >= 0xFFFFU )
+ return 0;
+
+ if ( cmap->flags & TT_CMAP_FLAG_UNSORTED )
+ gindex = tt_cmap4_char_map_linear( cmap, pchar_code, 1 );
+ else
+ {
+ TT_CMap4 cmap4 = (TT_CMap4)cmap;
+
+
+ /* no need to search */
+ if ( *pchar_code == cmap4->cur_charcode )
+ {
+ tt_cmap4_next( cmap4 );
+ gindex = cmap4->cur_gindex;
+ if ( gindex )
+ *pchar_code = cmap4->cur_charcode;
+ }
+ else
+ gindex = tt_cmap4_char_map_binary( cmap, pchar_code, 1 );
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap4_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 4;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap4_class_rec =
+ {
+ {
+ sizeof ( TT_CMap4Rec ),
+ (FT_CMap_InitFunc) tt_cmap4_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap4_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap4_char_next
+ },
+ 4,
+ (TT_CMap_ValidateFunc) tt_cmap4_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap4_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_4 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 6 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 4 */
+ /* length 2 USHORT table length in bytes */
+ /* language 4 USHORT Mac language code */
+ /* */
+ /* first 6 USHORT first segment code */
+ /* count 8 USHORT segment size in chars */
+ /* glyphIds 10 USHORT[count] glyph ids */
+ /* */
+ /* A very simplified segment mapping. */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_6
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap6_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_UInt length, count;
+
+
+ if ( table + 10 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 2;
+ length = TT_NEXT_USHORT( p );
+
+ p = table + 8; /* skip language and start index */
+ count = TT_NEXT_USHORT( p );
+
+ if ( table + length > valid->limit || length < 10 + count * 2 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt gindex;
+
+
+ for ( ; count > 0; count-- )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap6_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 6;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_UInt idx = (FT_UInt)( char_code - start );
+
+
+ if ( idx < count )
+ {
+ p += 2 * idx;
+ result = TT_PEEK_USHORT( p );
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap6_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+ FT_UInt gindex = 0;
+
+ FT_Byte* p = table + 6;
+ FT_UInt start = TT_NEXT_USHORT( p );
+ FT_UInt count = TT_NEXT_USHORT( p );
+ FT_UInt idx;
+
+
+ if ( char_code >= 0x10000UL )
+ goto Exit;
+
+ if ( char_code < start )
+ char_code = start;
+
+ idx = (FT_UInt)( char_code - start );
+ p += 2 * idx;
+
+ for ( ; idx < count; idx++ )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex != 0 )
+ {
+ result = char_code;
+ break;
+ }
+ char_code++;
+ }
+
+ Exit:
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap6_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 4;
+
+
+ cmap_info->format = 6;
+ cmap_info->language = (FT_ULong)TT_PEEK_USHORT( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap6_class_rec =
+ {
+ {
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap6_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap6_char_next
+ },
+ 6,
+ (TT_CMap_ValidateFunc) tt_cmap6_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap6_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_6 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 8 *****/
+ /***** *****/
+ /***** It's hard to completely understand what the OpenType spec *****/
+ /***** says about this format, but here is my conclusion. *****/
+ /***** *****/
+ /***** The purpose of this format is to easily map UTF-16 text to *****/
+ /***** glyph indices. Basically, the `char_code' must be in one of *****/
+ /***** the following formats: *****/
+ /***** *****/
+ /***** - A 16-bit value that isn't part of the Unicode Surrogates *****/
+ /***** Area (i.e. U+D800-U+DFFF). *****/
+ /***** *****/
+ /***** - A 32-bit value, made of two surrogate values, i.e.. if *****/
+ /***** `char_code = (char_hi << 16) | char_lo', then both *****/
+ /***** `char_hi' and `char_lo' must be in the Surrogates Area. *****/
+ /***** Area. *****/
+ /***** *****/
+ /***** The 'is32' table embedded in the charmap indicates whether a *****/
+ /***** given 16-bit value is in the surrogates area or not. *****/
+ /***** *****/
+ /***** So, for any given `char_code', we can assert the following: *****/
+ /***** *****/
+ /***** If `char_hi == 0' then we must have `is32[char_lo] == 0'. *****/
+ /***** *****/
+ /***** If `char_hi != 0' then we must have both *****/
+ /***** `is32[char_hi] != 0' and `is32[char_lo] != 0'. *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 8 */
+ /* reserved 2 USHORT reserved */
+ /* length 4 ULONG length in bytes */
+ /* language 8 ULONG Mac language code */
+ /* is32 12 BYTE[8192] 32-bitness bitmap */
+ /* count 8204 ULONG number of groups */
+ /* */
+ /* This header is followed by 'count' groups of the following format: */
+ /* */
+ /* start 0 ULONG first charcode */
+ /* end 4 ULONG last charcode */
+ /* startId 8 ULONG start glyph id for the group */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_8
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap8_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 4;
+ FT_Byte* is32;
+ FT_UInt32 length;
+ FT_UInt32 num_groups;
+
+
+ if ( table + 16 + 8192 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ length = TT_NEXT_ULONG( p );
+ if ( table + length > valid->limit || length < 8208 )
+ FT_INVALID_TOO_SHORT;
+
+ is32 = table + 12;
+ p = is32 + 8192; /* skip `is32' array */
+ num_groups = TT_NEXT_ULONG( p );
+
+ if ( p + num_groups * 12 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ /* check groups, they must be in increasing order */
+ {
+ FT_UInt32 n, start, end, start_id, count, last = 0;
+
+
+ for ( n = 0; n < num_groups; n++ )
+ {
+ FT_UInt hi, lo;
+
+
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && start <= last )
+ FT_INVALID_DATA;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ if ( start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+
+ count = (FT_UInt32)( end - start + 1 );
+
+ if ( start & ~0xFFFFU )
+ {
+ /* start_hi != 0; check that is32[i] is 1 for each i in */
+ /* the `hi' and `lo' of the range [start..end] */
+ for ( ; count > 0; count--, start++ )
+ {
+ hi = (FT_UInt)( start >> 16 );
+ lo = (FT_UInt)( start & 0xFFFFU );
+
+ if ( (is32[hi >> 3] & ( 0x80 >> ( hi & 7 ) ) ) == 0 )
+ FT_INVALID_DATA;
+
+ if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) == 0 )
+ FT_INVALID_DATA;
+ }
+ }
+ else
+ {
+ /* start_hi == 0; check that is32[i] is 0 for each i in */
+ /* the range [start..end] */
+
+ /* end_hi cannot be != 0! */
+ if ( end & ~0xFFFFU )
+ FT_INVALID_DATA;
+
+ for ( ; count > 0; count--, start++ )
+ {
+ lo = (FT_UInt)( start & 0xFFFFU );
+
+ if ( (is32[lo >> 3] & ( 0x80 >> ( lo & 7 ) ) ) != 0 )
+ FT_INVALID_DATA;
+ }
+ }
+ }
+
+ last = end;
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap8_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 8204;
+ FT_UInt32 num_groups = TT_NEXT_ULONG( p );
+ FT_UInt32 start, end, start_id;
+
+
+ for ( ; num_groups > 0; num_groups-- )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ break;
+
+ if ( char_code <= end )
+ {
+ result = (FT_UInt)( start_id + char_code - start );
+ break;
+ }
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap8_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt32 result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+ FT_UInt gindex = 0;
+ FT_Byte* table = cmap->data;
+ FT_Byte* p = table + 8204;
+ FT_UInt32 num_groups = TT_NEXT_ULONG( p );
+ FT_UInt32 start, end, start_id;
+
+
+ p = table + 8208;
+
+ for ( ; num_groups > 0; num_groups-- )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ char_code = start;
+
+ if ( char_code <= end )
+ {
+ gindex = (FT_UInt)( char_code - start + start_id );
+ if ( gindex != 0 )
+ {
+ result = char_code;
+ goto Exit;
+ }
+ }
+ }
+
+ Exit:
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap8_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 8;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap8_class_rec =
+ {
+ {
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap8_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap8_char_next
+ },
+ 8,
+ (TT_CMap_ValidateFunc) tt_cmap8_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap8_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_8 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 10 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 10 */
+ /* reserved 2 USHORT reserved */
+ /* length 4 ULONG length in bytes */
+ /* language 8 ULONG Mac language code */
+ /* */
+ /* start 12 ULONG first char in range */
+ /* count 16 ULONG number of chars in range */
+ /* glyphIds 20 USHORT[count] glyph indices covered */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_10
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap10_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p = table + 4;
+ FT_ULong length, count;
+
+
+ if ( table + 20 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ length = TT_NEXT_ULONG( p );
+ p = table + 16;
+ count = TT_NEXT_ULONG( p );
+
+ if ( table + length > valid->limit || length < 20 + count * 2 )
+ FT_INVALID_TOO_SHORT;
+
+ /* check glyph indices */
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ FT_UInt gindex;
+
+
+ for ( ; count > 0; count-- )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap10_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt result = 0;
+ FT_Byte* p = table + 12;
+ FT_UInt32 start = TT_NEXT_ULONG( p );
+ FT_UInt32 count = TT_NEXT_ULONG( p );
+ FT_UInt32 idx = (FT_ULong)( char_code - start );
+
+
+ if ( idx < count )
+ {
+ p += 2 * idx;
+ result = TT_PEEK_USHORT( p );
+ }
+ return result;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap10_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_Byte* table = cmap->data;
+ FT_UInt32 char_code = *pchar_code + 1;
+ FT_UInt gindex = 0;
+ FT_Byte* p = table + 12;
+ FT_UInt32 start = TT_NEXT_ULONG( p );
+ FT_UInt32 count = TT_NEXT_ULONG( p );
+ FT_UInt32 idx;
+
+
+ if ( char_code < start )
+ char_code = start;
+
+ idx = (FT_UInt32)( char_code - start );
+ p += 2 * idx;
+
+ for ( ; idx < count; idx++ )
+ {
+ gindex = TT_NEXT_USHORT( p );
+ if ( gindex != 0 )
+ break;
+ char_code++;
+ }
+
+ *pchar_code = char_code;
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap10_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 10;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap10_class_rec =
+ {
+ {
+ sizeof ( TT_CMapRec ),
+
+ (FT_CMap_InitFunc) tt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap10_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap10_char_next
+ },
+ 10,
+ (TT_CMap_ValidateFunc) tt_cmap10_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap10_get_info
+ };
+
+#endif /* TT_CONFIG_CMAP_FORMAT_10 */
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** FORMAT 12 *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* TABLE OVERVIEW */
+ /* -------------- */
+ /* */
+ /* NAME OFFSET TYPE DESCRIPTION */
+ /* */
+ /* format 0 USHORT must be 12 */
+ /* reserved 2 USHORT reserved */
+ /* length 4 ULONG length in bytes */
+ /* language 8 ULONG Mac language code */
+ /* count 12 ULONG number of groups */
+ /* 16 */
+ /* */
+ /* This header is followed by `count' groups of the following format: */
+ /* */
+ /* start 0 ULONG first charcode */
+ /* end 4 ULONG last charcode */
+ /* startId 8 ULONG start glyph id for the group */
+ /* */
+
+#ifdef TT_CONFIG_CMAP_FORMAT_12
+
+ typedef struct TT_CMap12Rec_
+ {
+ TT_CMapRec cmap;
+ FT_Bool valid;
+ FT_ULong cur_charcode;
+ FT_UInt cur_gindex;
+ FT_ULong cur_group;
+ FT_ULong num_groups;
+
+ } TT_CMap12Rec, *TT_CMap12;
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_init( TT_CMap12 cmap,
+ FT_Byte* table )
+ {
+ cmap->cmap.data = table;
+
+ table += 12;
+ cmap->num_groups = FT_PEEK_ULONG( table );
+
+ cmap->valid = 0;
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_validate( FT_Byte* table,
+ FT_Validator valid )
+ {
+ FT_Byte* p;
+ FT_ULong length;
+ FT_ULong num_groups;
+
+
+ if ( table + 16 > valid->limit )
+ FT_INVALID_TOO_SHORT;
+
+ p = table + 4;
+ length = TT_NEXT_ULONG( p );
+
+ p = table + 12;
+ num_groups = TT_NEXT_ULONG( p );
+
+ if ( table + length > valid->limit || length < 16 + 12 * num_groups )
+ FT_INVALID_TOO_SHORT;
+
+ /* check groups, they must be in increasing order */
+ {
+ FT_ULong n, start, end, start_id, last = 0;
+
+
+ for ( n = 0; n < num_groups; n++ )
+ {
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_NEXT_ULONG( p );
+
+ if ( start > end )
+ FT_INVALID_DATA;
+
+ if ( n > 0 && start <= last )
+ FT_INVALID_DATA;
+
+ if ( valid->level >= FT_VALIDATE_TIGHT )
+ {
+ if ( start_id + end - start >= TT_VALID_GLYPH_COUNT( valid ) )
+ FT_INVALID_GLYPH_ID;
+ }
+
+ last = end;
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ /* search the index of the charcode next to cmap->cur_charcode */
+ /* cmap->cur_group should be set up properly by caller */
+ /* */
+ static void
+ tt_cmap12_next( TT_CMap12 cmap )
+ {
+ FT_Byte* p;
+ FT_ULong start, end, start_id, char_code;
+ FT_ULong n;
+ FT_UInt gindex;
+
+
+ if ( cmap->cur_charcode >= 0xFFFFFFFFUL )
+ goto Fail;
+
+ char_code = cmap->cur_charcode + 1;
+
+ n = cmap->cur_group;
+
+ for ( n = cmap->cur_group; n < cmap->num_groups; n++ )
+ {
+ p = cmap->cmap.data + 16 + 12 * n;
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+ start_id = TT_PEEK_ULONG( p );
+
+ if ( char_code < start )
+ char_code = start;
+
+ for ( ; char_code <= end; char_code++ )
+ {
+ gindex = (FT_UInt)( start_id + char_code - start );
+
+ if ( gindex )
+ {
+ cmap->cur_charcode = char_code;;
+ cmap->cur_gindex = gindex;
+ cmap->cur_group = n;
+
+ return;
+ }
+ }
+ }
+
+ Fail:
+ cmap->valid = 0;
+ }
+
+
+ static FT_UInt
+ tt_cmap12_char_map_binary( TT_CMap cmap,
+ FT_UInt32* pchar_code,
+ FT_Bool next )
+ {
+ FT_UInt gindex = 0;
+ FT_Byte* p = cmap->data + 12;
+ FT_UInt32 num_groups = TT_PEEK_ULONG( p );
+ FT_UInt32 char_code = *pchar_code;
+ FT_UInt32 start, end, start_id;
+ FT_UInt32 max, min, mid;
+
+
+ if ( !num_groups )
+ return 0;
+
+ /* make compiler happy */
+ mid = num_groups;
+ end = 0xFFFFFFFFUL;
+
+ if ( next )
+ char_code++;
+
+ min = 0;
+ max = num_groups;
+
+ /* binary search */
+ while ( min < max )
+ {
+ mid = ( min + max ) >> 1;
+ p = cmap->data + 16 + 12 * mid;
+
+ start = TT_NEXT_ULONG( p );
+ end = TT_NEXT_ULONG( p );
+
+ if ( char_code < start )
+ max = mid;
+ else if ( char_code > end )
+ min = mid + 1;
+ else
+ {
+ start_id = TT_PEEK_ULONG( p );
+ gindex = (FT_UInt)( start_id + char_code - start );
+
+ break;
+ }
+ }
+
+ if ( next )
+ {
+ TT_CMap12 cmap12 = (TT_CMap12)cmap;
+
+
+ /* if `char_code' is not in any group, then `mid' is */
+ /* the group nearest to `char_code' */
+ /* */
+
+ if ( char_code > end )
+ {
+ mid++;
+ if ( mid == num_groups )
+ return 0;
+ }
+
+ cmap12->valid = 1;
+ cmap12->cur_charcode = char_code;
+ cmap12->cur_group = mid;
+
+ if ( !gindex )
+ {
+ tt_cmap12_next( cmap12 );
+
+ if ( cmap12->valid )
+ gindex = cmap12->cur_gindex;
+ }
+ else
+ cmap12->cur_gindex = gindex;
+
+ if ( gindex )
+ *pchar_code = cmap12->cur_charcode;
+ }
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap12_char_index( TT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ return tt_cmap12_char_map_binary( cmap, &char_code, 0 );
+ }
+
+
+ FT_CALLBACK_DEF( FT_UInt )
+ tt_cmap12_char_next( TT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ TT_CMap12 cmap12 = (TT_CMap12)cmap;
+ FT_ULong gindex;
+
+
+ if ( cmap12->cur_charcode >= 0xFFFFFFFFUL )
+ return 0;
+
+ /* no need to search */
+ if ( cmap12->valid && cmap12->cur_charcode == *pchar_code )
+ {
+ tt_cmap12_next( cmap12 );
+ if ( cmap12->valid )
+ {
+ gindex = cmap12->cur_gindex;
+ if ( gindex )
+ *pchar_code = cmap12->cur_charcode;
+ }
+ else
+ gindex = 0;
+ }
+ else
+ gindex = tt_cmap12_char_map_binary( cmap, pchar_code, 1 );
+
+ return gindex;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ tt_cmap12_get_info( TT_CMap cmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_Byte* p = cmap->data + 8;
+
+
+ cmap_info->format = 12;
+ cmap_info->language = (FT_ULong)TT_PEEK_ULONG( p );
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const TT_CMap_ClassRec tt_cmap12_class_rec =
+ {
+ {
+ sizeof ( TT_CMap12Rec ),
+
+ (FT_CMap_InitFunc) tt_cmap12_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)tt_cmap12_char_index,
+ (FT_CMap_CharNextFunc) tt_cmap12_char_next
+ },
+ 12,
+ (TT_CMap_ValidateFunc) tt_cmap12_validate,
+ (TT_CMap_Info_GetFunc) tt_cmap12_get_info
+ };
+
+
+#endif /* TT_CONFIG_CMAP_FORMAT_12 */
+
+
+ static const TT_CMap_Class tt_cmap_classes[] =
+ {
+#ifdef TT_CONFIG_CMAP_FORMAT_0
+ &tt_cmap0_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_2
+ &tt_cmap2_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_4
+ &tt_cmap4_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_6
+ &tt_cmap6_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_8
+ &tt_cmap8_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_10
+ &tt_cmap10_class_rec,
+#endif
+
+#ifdef TT_CONFIG_CMAP_FORMAT_12
+ &tt_cmap12_class_rec,
+#endif
+
+ NULL,
+ };
+
+
+ /* parse the `cmap' table and build the corresponding TT_CMap objects */
+ /* in the current face */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_build_cmaps( TT_Face face )
+ {
+ FT_Byte* table = face->cmap_table;
+ FT_Byte* limit = table + face->cmap_size;
+ FT_UInt volatile num_cmaps;
+ FT_Byte* volatile p = table;
+
+
+ if ( p + 4 > limit )
+ return SFNT_Err_Invalid_Table;
+
+ /* only recognize format 0 */
+ if ( TT_NEXT_USHORT( p ) != 0 )
+ {
+ p -= 2;
+ FT_ERROR(( "tt_face_build_cmaps: unsupported `cmap' table format = %d\n",
+ TT_PEEK_USHORT( p ) ));
+ return SFNT_Err_Invalid_Table;
+ }
+
+ num_cmaps = TT_NEXT_USHORT( p );
+
+ for ( ; num_cmaps > 0 && p + 8 <= limit; num_cmaps-- )
+ {
+ FT_CharMapRec charmap;
+ FT_UInt32 offset;
+
+
+ charmap.platform_id = TT_NEXT_USHORT( p );
+ charmap.encoding_id = TT_NEXT_USHORT( p );
+ charmap.face = FT_FACE( face );
+ charmap.encoding = FT_ENCODING_NONE; /* will be filled later */
+ offset = TT_NEXT_ULONG( p );
+
+ if ( offset && offset <= face->cmap_size - 2 )
+ {
+ FT_Byte* volatile cmap = table + offset;
+ volatile FT_UInt format = TT_PEEK_USHORT( cmap );
+ const TT_CMap_Class* volatile pclazz = tt_cmap_classes;
+ TT_CMap_Class volatile clazz;
+
+
+ for ( ; *pclazz; pclazz++ )
+ {
+ clazz = *pclazz;
+ if ( clazz->format == format )
+ {
+ volatile TT_ValidatorRec valid;
+ volatile FT_Error error = SFNT_Err_Ok;
+
+
+ ft_validator_init( FT_VALIDATOR( &valid ), cmap, limit,
+ FT_VALIDATE_DEFAULT );
+
+ valid.num_glyphs = (FT_UInt)face->max_profile.numGlyphs;
+
+ if ( ft_setjmp(
+ *((ft_jmp_buf*)&FT_VALIDATOR( &valid )->jump_buffer) ) == 0 )
+ {
+ /* validate this cmap sub-table */
+ error = clazz->validate( cmap, FT_VALIDATOR( &valid ) );
+ }
+
+ if ( valid.validator.error == 0 )
+ {
+ FT_CMap ttcmap;
+
+
+ if ( !FT_CMap_New( (FT_CMap_Class)clazz,
+ cmap, &charmap, &ttcmap ) )
+ {
+ /* it is simpler to directly set `flags' than adding */
+ /* a parameter to FT_CMap_New */
+ ((TT_CMap)ttcmap)->flags = (FT_Int)error;
+ }
+ }
+ else
+ {
+ FT_ERROR(( "tt_face_build_cmaps:" ));
+ FT_ERROR(( " broken cmap sub-table ignored!\n" ));
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+
+ FT_LOCAL( FT_Error )
+ tt_get_cmap_info( FT_CharMap charmap,
+ TT_CMapInfo *cmap_info )
+ {
+ FT_CMap cmap = (FT_CMap)charmap;
+ TT_CMap_Class clazz = (TT_CMap_Class)cmap->clazz;
+
+
+ return clazz->get_cmap_info( charmap, cmap_info );
+ }
+
+
+/* END */
diff --git a/src/sfnt/ttcmap.h b/src/sfnt/ttcmap.h
new file mode 100644
index 0000000..a10a3e2
--- /dev/null
+++ b/src/sfnt/ttcmap.h
@@ -0,0 +1,85 @@
+/***************************************************************************/
+/* */
+/* ttcmap.h */
+/* */
+/* TrueType character mapping table (cmap) support (specification). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTCMAP_H__
+#define __TTCMAP_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+#include FT_INTERNAL_VALIDATE_H
+#include FT_SERVICE_TT_CMAP_H
+
+FT_BEGIN_HEADER
+
+
+#define TT_CMAP_FLAG_UNSORTED 1
+#define TT_CMAP_FLAG_OVERLAPPING 2
+
+ typedef struct TT_CMapRec_
+ {
+ FT_CMapRec cmap;
+ FT_Byte* data; /* pointer to in-memory cmap table */
+ FT_Int flags; /* for format 4 only */
+
+ } TT_CMapRec, *TT_CMap;
+
+ typedef const struct TT_CMap_ClassRec_* TT_CMap_Class;
+
+
+ typedef FT_Error
+ (*TT_CMap_ValidateFunc)( FT_Byte* data,
+ FT_Validator valid );
+
+ typedef struct TT_CMap_ClassRec_
+ {
+ FT_CMap_ClassRec clazz;
+ FT_UInt format;
+ TT_CMap_ValidateFunc validate;
+ TT_CMap_Info_GetFunc get_cmap_info;
+
+ } TT_CMap_ClassRec;
+
+
+ typedef struct TT_ValidatorRec_
+ {
+ FT_ValidatorRec validator;
+ FT_UInt num_glyphs;
+
+ } TT_ValidatorRec, *TT_Validator;
+
+
+#define TT_VALIDATOR( x ) ((TT_Validator)( x ))
+#define TT_VALID_GLYPH_COUNT( x ) TT_VALIDATOR( x )->num_glyphs
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_build_cmaps( TT_Face face );
+
+ /* used in tt-cmaps service */
+ FT_LOCAL( FT_Error )
+ tt_get_cmap_info( FT_CharMap charmap,
+ TT_CMapInfo *cmap_info );
+
+
+FT_END_HEADER
+
+#endif /* __TTCMAP_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttkern.c b/src/sfnt/ttkern.c
new file mode 100644
index 0000000..28e52c3
--- /dev/null
+++ b/src/sfnt/ttkern.c
@@ -0,0 +1,292 @@
+/***************************************************************************/
+/* */
+/* ttkern.c */
+/* */
+/* Load the basic TrueType kerning table. This doesn't handle */
+/* kerning data within the GPOS table at the moment. */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttkern.h"
+#include "ttload.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttkern
+
+
+#undef TT_KERN_INDEX
+#define TT_KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) )
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_kern( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_ULong table_size;
+ FT_Byte* p;
+ FT_Byte* p_limit;
+ FT_UInt nn, num_tables;
+ FT_UInt32 avail = 0, ordered = 0;
+
+
+ /* the kern table is optional; exit silently if it is missing */
+ error = face->goto_table( face, TTAG_kern, stream, &table_size );
+ if ( error )
+ goto Exit;
+
+ if ( table_size < 4 ) /* the case of a malformed table */
+ {
+ FT_ERROR(( "kerning table is too small - ignored\n" ));
+ error = SFNT_Err_Table_Missing;
+ goto Exit;
+ }
+
+ if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) )
+ {
+ FT_ERROR(( "could not extract kerning table\n" ));
+ goto Exit;
+ }
+
+ face->kern_table_size = table_size;
+
+ p = face->kern_table;
+ p_limit = p + table_size;
+
+ p += 2; /* skip version */
+ num_tables = FT_NEXT_USHORT( p );
+
+ if ( num_tables > 32 ) /* we only support up to 32 sub-tables */
+ num_tables = 32;
+
+ for ( nn = 0; nn < num_tables; nn++ )
+ {
+ FT_UInt num_pairs, length, coverage;
+ FT_Byte* p_next;
+ FT_UInt32 mask = 1UL << nn;
+
+
+ if ( p + 6 > p_limit )
+ break;
+
+ p_next = p;
+
+ p += 2; /* skip version */
+ length = FT_NEXT_USHORT( p );
+ coverage = FT_NEXT_USHORT( p );
+
+ if ( length <= 6 )
+ break;
+
+ p_next += length;
+
+ /* only use horizontal kerning tables */
+ if ( ( coverage & ~8 ) != 0x0001 ||
+ p + 8 > p_limit )
+ goto NextTable;
+
+ num_pairs = FT_NEXT_USHORT( p );
+ p += 6;
+
+ if ( p + 6 * num_pairs > p_limit )
+ goto NextTable;
+
+ avail |= mask;
+
+ /*
+ * Now check whether the pairs in this table are ordered.
+ * We then can use binary search.
+ */
+ if ( num_pairs > 0 )
+ {
+ FT_UInt count;
+ FT_UInt old_pair;
+
+
+ old_pair = FT_NEXT_ULONG( p );
+ p += 2;
+
+ for ( count = num_pairs - 1; count > 0; count-- )
+ {
+ FT_UInt32 cur_pair;
+
+
+ cur_pair = FT_NEXT_ULONG( p );
+ if ( cur_pair <= old_pair )
+ break;
+
+ p += 2;
+ old_pair = cur_pair;
+ }
+
+ if ( count == 0 )
+ ordered |= mask;
+ }
+
+ NextTable:
+ p = p_next;
+ }
+
+ face->num_kern_tables = nn;
+ face->kern_avail_bits = avail;
+ face->kern_order_bits = ordered;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_done_kern( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+
+
+ FT_FRAME_RELEASE( face->kern_table );
+ face->kern_table_size = 0;
+ face->num_kern_tables = 0;
+ face->kern_avail_bits = 0;
+ face->kern_order_bits = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Int )
+ tt_face_get_kerning( TT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph )
+ {
+ FT_Int result = 0;
+ FT_UInt count, mask = 1;
+ FT_Byte* p = face->kern_table;
+
+
+ p += 4;
+ mask = 0x0001;
+
+ for ( count = face->num_kern_tables; count > 0; count--, mask <<= 1 )
+ {
+ FT_Byte* base = p;
+ FT_Byte* next = base;
+ FT_UInt version = FT_NEXT_USHORT( p );
+ FT_UInt length = FT_NEXT_USHORT( p );
+ FT_UInt coverage = FT_NEXT_USHORT( p );
+ FT_Int value = 0;
+
+ FT_UNUSED( version );
+
+
+ next = base + length;
+
+ if ( ( face->kern_avail_bits & mask ) == 0 )
+ goto NextTable;
+
+ if ( p + 8 > next )
+ goto NextTable;
+
+ switch ( coverage >> 8 )
+ {
+ case 0:
+ {
+ FT_UInt num_pairs = FT_NEXT_USHORT( p );
+ FT_ULong key0 = TT_KERN_INDEX( left_glyph, right_glyph );
+
+
+ p += 6;
+
+ if ( face->kern_order_bits & mask ) /* binary search */
+ {
+ FT_UInt min = 0;
+ FT_UInt max = num_pairs;
+
+
+ while ( min < max )
+ {
+ FT_UInt mid = ( min + max ) >> 1;
+ FT_Byte* q = p + 6 * mid;
+ FT_ULong key;
+
+
+ key = FT_NEXT_ULONG( q );
+
+ if ( key == key0 )
+ {
+ value = FT_PEEK_SHORT( q );
+ goto Found;
+ }
+ if ( key < key0 )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+ }
+ else /* linear search */
+ {
+ FT_UInt count2;
+
+
+ for ( count2 = num_pairs; count2 > 0; count2-- )
+ {
+ FT_ULong key = FT_NEXT_ULONG( p );
+
+
+ if ( key == key0 )
+ {
+ value = FT_PEEK_SHORT( p );
+ goto Found;
+ }
+ p += 2;
+ }
+ }
+ }
+ break;
+
+ /*
+ * We don't support format 2 because we haven't seen a single font
+ * using it in real life...
+ */
+
+ default:
+ ;
+ }
+
+ goto NextTable;
+
+ Found:
+ if ( coverage & 8 ) /* override or add */
+ result = value;
+ else
+ result += value;
+
+ NextTable:
+ p = next;
+ }
+
+ return result;
+ }
+
+#undef TT_KERN_INDEX
+
+/* END */
diff --git a/src/sfnt/ttkern.h b/src/sfnt/ttkern.h
new file mode 100644
index 0000000..df1da9b
--- /dev/null
+++ b/src/sfnt/ttkern.h
@@ -0,0 +1,52 @@
+/***************************************************************************/
+/* */
+/* ttkern.h */
+/* */
+/* Load the basic TrueType kerning table. This doesn't handle */
+/* kerning data within the GPOS table at the moment. */
+/* */
+/* Copyright 1996-2001, 2002, 2005, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTKERN_H__
+#define __TTKERN_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_kern( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_done_kern( TT_Face face );
+
+ FT_LOCAL( FT_Int )
+ tt_face_get_kerning( TT_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph );
+
+#define TT_FACE_HAS_KERNING( face ) ( (face)->kern_avail_bits != 0 )
+
+
+FT_END_HEADER
+
+#endif /* __TTKERN_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c
new file mode 100644
index 0000000..abe0278
--- /dev/null
+++ b/src/sfnt/ttload.c
@@ -0,0 +1,1176 @@
+/***************************************************************************/
+/* */
+/* ttload.c */
+/* */
+/* Load the basic TrueType tables, i.e., tables that can be either in */
+/* TTF or OTF fonts (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttload.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttload
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_lookup_table */
+ /* */
+ /* <Description> */
+ /* Looks for a TrueType table by name. */
+ /* */
+ /* <Input> */
+ /* face :: A face object handle. */
+ /* */
+ /* tag :: The searched tag. */
+ /* */
+ /* <Return> */
+ /* A pointer to the table directory entry. 0 if not found. */
+ /* */
+ FT_LOCAL_DEF( TT_Table )
+ tt_face_lookup_table( TT_Face face,
+ FT_ULong tag )
+ {
+ TT_Table entry;
+ TT_Table limit;
+
+
+ FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ",
+ face,
+ (FT_Char)( tag >> 24 ),
+ (FT_Char)( tag >> 16 ),
+ (FT_Char)( tag >> 8 ),
+ (FT_Char)( tag ) ));
+
+ entry = face->dir_tables;
+ limit = entry + face->num_tables;
+
+ for ( ; entry < limit; entry++ )
+ {
+ /* For compatibility with Windows, we consider 0-length */
+ /* tables the same as missing tables. */
+ if ( entry->Tag == tag && entry->Length != 0 )
+ {
+ FT_TRACE4(( "found table.\n" ));
+ return entry;
+ }
+ }
+
+ FT_TRACE4(( "could not find table!\n" ));
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_goto_table */
+ /* */
+ /* <Description> */
+ /* Looks for a TrueType table by name, then seek a stream to it. */
+ /* */
+ /* <Input> */
+ /* face :: A face object handle. */
+ /* */
+ /* tag :: The searched tag. */
+ /* */
+ /* stream :: The stream to seek when the table is found. */
+ /* */
+ /* <Output> */
+ /* length :: The length of the table if found, undefined otherwise. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_goto_table( TT_Face face,
+ FT_ULong tag,
+ FT_Stream stream,
+ FT_ULong* length )
+ {
+ TT_Table table;
+ FT_Error error;
+
+
+ table = tt_face_lookup_table( face, tag );
+ if ( table )
+ {
+ if ( length )
+ *length = table->Length;
+
+ if ( FT_STREAM_SEEK( table->Offset ) )
+ goto Exit;
+ }
+ else
+ error = SFNT_Err_Table_Missing;
+
+ Exit:
+ return error;
+ }
+
+
+ /* Here, we */
+ /* */
+ /* - check that `num_tables' is valid */
+ /* - look for a `head' table, check its size, and parse it to check */
+ /* whether its `magic' field is correctly set */
+ /* */
+ /* When checking directory entries, ignore the tables `glyx' and `locx' */
+ /* which are hacked-out versions of `glyf' and `loca' in some PostScript */
+ /* Type 42 fonts, and which are generally invalid. */
+ /* */
+ static FT_Error
+ check_table_dir( SFNT_Header sfnt,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_UInt nn;
+ FT_UInt has_head = 0, has_sing = 0, has_meta = 0;
+ FT_ULong offset = sfnt->offset + 12;
+
+ const FT_ULong glyx_tag = FT_MAKE_TAG( 'g', 'l', 'y', 'x' );
+ const FT_ULong locx_tag = FT_MAKE_TAG( 'l', 'o', 'c', 'x' );
+
+ static const FT_Frame_Field table_dir_entry_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_TableRec
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_ULONG( Tag ),
+ FT_FRAME_ULONG( CheckSum ),
+ FT_FRAME_ULONG( Offset ),
+ FT_FRAME_ULONG( Length ),
+ FT_FRAME_END
+ };
+
+
+ if ( sfnt->num_tables == 0 ||
+ offset + sfnt->num_tables * 16 > stream->size )
+ return SFNT_Err_Unknown_File_Format;
+
+ if ( FT_STREAM_SEEK( offset ) )
+ return error;
+
+ for ( nn = 0; nn < sfnt->num_tables; nn++ )
+ {
+ TT_TableRec table;
+
+
+ if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
+ return error;
+
+ if ( table.Offset + table.Length > stream->size &&
+ table.Tag != glyx_tag &&
+ table.Tag != locx_tag )
+ return SFNT_Err_Unknown_File_Format;
+
+ if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
+ {
+ FT_UInt32 magic;
+
+
+#ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ if ( table.Tag == TTAG_head )
+#endif
+ has_head = 1;
+
+ /*
+ * The table length should be 0x36, but certain font tools make it
+ * 0x38, so we will just check that it is greater.
+ *
+ * Note that according to the specification, the table must be
+ * padded to 32-bit lengths, but this doesn't apply to the value of
+ * its `Length' field!
+ *
+ */
+ if ( table.Length < 0x36 )
+ return SFNT_Err_Unknown_File_Format;
+
+ if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
+ FT_READ_ULONG( magic ) )
+ return error;
+
+ if ( magic != 0x5F0F3CF5UL )
+ return SFNT_Err_Unknown_File_Format;
+
+ if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
+ return error;
+ }
+ else if ( table.Tag == TTAG_SING )
+ has_sing = 1;
+ else if ( table.Tag == TTAG_META )
+ has_meta = 1;
+ }
+
+ /* if `sing' and `meta' tables are present, there is no `head' table */
+ if ( has_head || ( has_sing && has_meta ) )
+ return SFNT_Err_Ok;
+ else
+ return SFNT_Err_Unknown_File_Format;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_font_dir */
+ /* */
+ /* <Description> */
+ /* Loads the header of a SFNT font file. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Output> */
+ /* sfnt :: The SFNT header. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The stream cursor must be at the beginning of the font directory. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_font_dir( TT_Face face,
+ FT_Stream stream )
+ {
+ SFNT_HeaderRec sfnt;
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ TT_TableRec* entry;
+ TT_TableRec* limit;
+
+ static const FT_Frame_Field offset_table_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE SFNT_HeaderRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_USHORT( num_tables ),
+ FT_FRAME_USHORT( search_range ),
+ FT_FRAME_USHORT( entry_selector ),
+ FT_FRAME_USHORT( range_shift ),
+ FT_FRAME_END
+ };
+
+
+ FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face ));
+
+ /* read the offset table */
+
+ sfnt.offset = FT_STREAM_POS();
+
+ if ( FT_READ_ULONG( sfnt.format_tag ) ||
+ FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
+ return error;
+
+ /* many fonts don't have these fields set correctly */
+#if 0
+ if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 ) ||
+ sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
+ return SFNT_Err_Unknown_File_Format;
+#endif
+
+ /* load the table directory */
+
+ FT_TRACE2(( "-- Tables count: %12u\n", sfnt.num_tables ));
+ FT_TRACE2(( "-- Format version: %08lx\n", sfnt.format_tag ));
+
+ /* check first */
+ error = check_table_dir( &sfnt, stream );
+ if ( error )
+ {
+ FT_TRACE2(( "tt_face_load_font_dir: invalid table directory!\n" ));
+
+ return error;
+ }
+
+ face->num_tables = sfnt.num_tables;
+ face->format_tag = sfnt.format_tag;
+
+ if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
+ return error;
+
+ if ( FT_STREAM_SEEK( sfnt.offset + 12 ) ||
+ FT_FRAME_ENTER( face->num_tables * 16L ) )
+ return error;
+
+ entry = face->dir_tables;
+ limit = entry + face->num_tables;
+
+ for ( ; entry < limit; entry++ )
+ {
+ entry->Tag = FT_GET_TAG4();
+ entry->CheckSum = FT_GET_ULONG();
+ entry->Offset = FT_GET_LONG();
+ entry->Length = FT_GET_LONG();
+
+ FT_TRACE2(( " %c%c%c%c - %08lx - %08lx\n",
+ (FT_Char)( entry->Tag >> 24 ),
+ (FT_Char)( entry->Tag >> 16 ),
+ (FT_Char)( entry->Tag >> 8 ),
+ (FT_Char)( entry->Tag ),
+ entry->Offset,
+ entry->Length ));
+ }
+
+ FT_FRAME_EXIT();
+
+ FT_TRACE2(( "table directory loaded\n\n" ));
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_any */
+ /* */
+ /* <Description> */
+ /* Loads any font table into client memory. */
+ /* */
+ /* <Input> */
+ /* face :: The face object to look for. */
+ /* */
+ /* tag :: The tag of table to load. Use the value 0 if you want */
+ /* to access the whole font file, else set this parameter */
+ /* to a valid TrueType table tag that you can forge with */
+ /* the MAKE_TT_TAG macro. */
+ /* */
+ /* offset :: The starting offset in the table (or the file if */
+ /* tag == 0). */
+ /* */
+ /* length :: The address of the decision variable: */
+ /* */
+ /* If length == NULL: */
+ /* Loads the whole table. Returns an error if */
+ /* `offset' == 0! */
+ /* */
+ /* If *length == 0: */
+ /* Exits immediately; returning the length of the given */
+ /* table or of the font file, depending on the value of */
+ /* `tag'. */
+ /* */
+ /* If *length != 0: */
+ /* Loads the next `length' bytes of table or font, */
+ /* starting at offset `offset' (in table or font too). */
+ /* */
+ /* <Output> */
+ /* buffer :: The address of target buffer. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_any( TT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length )
+ {
+ FT_Error error;
+ FT_Stream stream;
+ TT_Table table;
+ FT_ULong size;
+
+
+ if ( tag != 0 )
+ {
+ /* look for tag in font directory */
+ table = tt_face_lookup_table( face, tag );
+ if ( !table )
+ {
+ error = SFNT_Err_Table_Missing;
+ goto Exit;
+ }
+
+ offset += table->Offset;
+ size = table->Length;
+ }
+ else
+ /* tag == 0 -- the user wants to access the font file directly */
+ size = face->root.stream->size;
+
+ if ( length && *length == 0 )
+ {
+ *length = size;
+
+ return SFNT_Err_Ok;
+ }
+
+ if ( length )
+ size = *length;
+
+ stream = face->root.stream;
+ /* the `if' is syntactic sugar for picky compilers */
+ if ( FT_STREAM_READ_AT( offset, buffer, size ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_generic_header */
+ /* */
+ /* <Description> */
+ /* Loads the TrueType table `head' or `bhed'. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ tt_face_load_generic_header( TT_Face face,
+ FT_Stream stream,
+ FT_ULong tag )
+ {
+ FT_Error error;
+ TT_Header* header;
+
+ static const FT_Frame_Field header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_Header
+
+ FT_FRAME_START( 54 ),
+ FT_FRAME_ULONG ( Table_Version ),
+ FT_FRAME_ULONG ( Font_Revision ),
+ FT_FRAME_LONG ( CheckSum_Adjust ),
+ FT_FRAME_LONG ( Magic_Number ),
+ FT_FRAME_USHORT( Flags ),
+ FT_FRAME_USHORT( Units_Per_EM ),
+ FT_FRAME_LONG ( Created[0] ),
+ FT_FRAME_LONG ( Created[1] ),
+ FT_FRAME_LONG ( Modified[0] ),
+ FT_FRAME_LONG ( Modified[1] ),
+ FT_FRAME_SHORT ( xMin ),
+ FT_FRAME_SHORT ( yMin ),
+ FT_FRAME_SHORT ( xMax ),
+ FT_FRAME_SHORT ( yMax ),
+ FT_FRAME_USHORT( Mac_Style ),
+ FT_FRAME_USHORT( Lowest_Rec_PPEM ),
+ FT_FRAME_SHORT ( Font_Direction ),
+ FT_FRAME_SHORT ( Index_To_Loc_Format ),
+ FT_FRAME_SHORT ( Glyph_Data_Format ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, tag, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ header = &face->header;
+
+ if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
+ goto Exit;
+
+ FT_TRACE3(( "Units per EM: %4u\n", header->Units_Per_EM ));
+ FT_TRACE3(( "IndexToLoc: %4d\n", header->Index_To_Loc_Format ));
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_head( TT_Face face,
+ FT_Stream stream )
+ {
+ return tt_face_load_generic_header( face, stream, TTAG_head );
+ }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_bhed( TT_Face face,
+ FT_Stream stream )
+ {
+ return tt_face_load_generic_header( face, stream, TTAG_bhed );
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_max_profile */
+ /* */
+ /* <Description> */
+ /* Loads the maximum profile into a face object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_maxp( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_MaxProfile* maxProfile = &face->max_profile;
+
+ const FT_Frame_Field maxp_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_MaxProfile
+
+ FT_FRAME_START( 6 ),
+ FT_FRAME_LONG ( version ),
+ FT_FRAME_USHORT( numGlyphs ),
+ FT_FRAME_END
+ };
+
+ const FT_Frame_Field maxp_fields_extra[] =
+ {
+ FT_FRAME_START( 26 ),
+ FT_FRAME_USHORT( maxPoints ),
+ FT_FRAME_USHORT( maxContours ),
+ FT_FRAME_USHORT( maxCompositePoints ),
+ FT_FRAME_USHORT( maxCompositeContours ),
+ FT_FRAME_USHORT( maxZones ),
+ FT_FRAME_USHORT( maxTwilightPoints ),
+ FT_FRAME_USHORT( maxStorage ),
+ FT_FRAME_USHORT( maxFunctionDefs ),
+ FT_FRAME_USHORT( maxInstructionDefs ),
+ FT_FRAME_USHORT( maxStackElements ),
+ FT_FRAME_USHORT( maxSizeOfInstructions ),
+ FT_FRAME_USHORT( maxComponentElements ),
+ FT_FRAME_USHORT( maxComponentDepth ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, TTAG_maxp, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
+ goto Exit;
+
+ maxProfile->maxPoints = 0;
+ maxProfile->maxContours = 0;
+ maxProfile->maxCompositePoints = 0;
+ maxProfile->maxCompositeContours = 0;
+ maxProfile->maxZones = 0;
+ maxProfile->maxTwilightPoints = 0;
+ maxProfile->maxStorage = 0;
+ maxProfile->maxFunctionDefs = 0;
+ maxProfile->maxInstructionDefs = 0;
+ maxProfile->maxStackElements = 0;
+ maxProfile->maxSizeOfInstructions = 0;
+ maxProfile->maxComponentElements = 0;
+ maxProfile->maxComponentDepth = 0;
+
+ if ( maxProfile->version >= 0x10000L )
+ {
+ if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
+ goto Exit;
+
+ /* XXX: an adjustment that is necessary to load certain */
+ /* broken fonts like `Keystrokes MT' :-( */
+ /* */
+ /* We allocate 64 function entries by default when */
+ /* the maxFunctionDefs field is null. */
+
+ if ( maxProfile->maxFunctionDefs == 0 )
+ maxProfile->maxFunctionDefs = 64;
+ }
+
+ FT_TRACE3(( "numGlyphs: %u\n", maxProfile->numGlyphs ));
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_names */
+ /* */
+ /* <Description> */
+ /* Loads the name records. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_name( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_ULong table_pos, table_len;
+ FT_ULong storage_start, storage_limit;
+ FT_UInt count;
+ TT_NameTable table;
+
+ static const FT_Frame_Field name_table_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_NameTableRec
+
+ FT_FRAME_START( 6 ),
+ FT_FRAME_USHORT( format ),
+ FT_FRAME_USHORT( numNameRecords ),
+ FT_FRAME_USHORT( storageOffset ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field name_record_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_NameEntryRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_USHORT( platformID ),
+ FT_FRAME_USHORT( encodingID ),
+ FT_FRAME_USHORT( languageID ),
+ FT_FRAME_USHORT( nameID ),
+ FT_FRAME_USHORT( stringLength ),
+ FT_FRAME_USHORT( stringOffset ),
+ FT_FRAME_END
+ };
+
+
+ table = &face->name_table;
+ table->stream = stream;
+
+ error = face->goto_table( face, TTAG_name, stream, &table_len );
+ if ( error )
+ goto Exit;
+
+ table_pos = FT_STREAM_POS();
+
+
+ if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
+ goto Exit;
+
+ /* Some popular Asian fonts have an invalid `storageOffset' value */
+ /* (it should be at least "6 + 12*num_names"). However, the string */
+ /* offsets, computed as "storageOffset + entry->stringOffset", are */
+ /* valid pointers within the name table... */
+ /* */
+ /* We thus can't check `storageOffset' right now. */
+ /* */
+ storage_start = table_pos + 6 + 12*table->numNameRecords;
+ storage_limit = table_pos + table_len;
+
+ if ( storage_start > storage_limit )
+ {
+ FT_ERROR(( "invalid `name' table\n" ));
+ error = SFNT_Err_Name_Table_Missing;
+ goto Exit;
+ }
+
+ /* Allocate the array of name records. */
+ count = table->numNameRecords;
+ table->numNameRecords = 0;
+
+ if ( FT_NEW_ARRAY( table->names, count ) ||
+ FT_FRAME_ENTER( count * 12 ) )
+ goto Exit;
+
+ /* Load the name records and determine how much storage is needed */
+ /* to hold the strings themselves. */
+ {
+ TT_NameEntryRec* entry = table->names;
+
+
+ for ( ; count > 0; count-- )
+ {
+ if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
+ continue;
+
+ /* check that the name is not empty */
+ if ( entry->stringLength == 0 )
+ continue;
+
+ /* check that the name string is within the table */
+ entry->stringOffset += table_pos + table->storageOffset;
+ if ( entry->stringOffset < storage_start ||
+ entry->stringOffset + entry->stringLength > storage_limit )
+ {
+ /* invalid entry - ignore it */
+ entry->stringOffset = 0;
+ entry->stringLength = 0;
+ continue;
+ }
+
+ entry++;
+ }
+
+ table->numNameRecords = (FT_UInt)( entry - table->names );
+ }
+
+ FT_FRAME_EXIT();
+
+ /* everything went well, update face->num_names */
+ face->num_names = (FT_UShort) table->numNameRecords;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_free_names */
+ /* */
+ /* <Description> */
+ /* Frees the name records. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_face_free_name( TT_Face face )
+ {
+ FT_Memory memory = face->root.driver->root.memory;
+ TT_NameTable table = &face->name_table;
+ TT_NameEntry entry = table->names;
+ FT_UInt count = table->numNameRecords;
+
+
+ if ( table->names )
+ {
+ for ( ; count > 0; count--, entry++ )
+ {
+ FT_FREE( entry->string );
+ entry->stringLength = 0;
+ }
+
+ /* free strings table */
+ FT_FREE( table->names );
+ }
+
+ table->numNameRecords = 0;
+ table->format = 0;
+ table->storageOffset = 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_cmap */
+ /* */
+ /* <Description> */
+ /* Loads the cmap directory in a face object. The cmaps themselves */
+ /* are loaded on demand in the `ttcmap.c' module. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_cmap( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
+ if ( error )
+ goto Exit;
+
+ if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
+ face->cmap_size = 0;
+
+ Exit:
+ return error;
+ }
+
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_os2 */
+ /* */
+ /* <Description> */
+ /* Loads the OS2 table. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_os2( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_OS2* os2;
+
+ const FT_Frame_Field os2_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_OS2
+
+ FT_FRAME_START( 78 ),
+ FT_FRAME_USHORT( version ),
+ FT_FRAME_SHORT ( xAvgCharWidth ),
+ FT_FRAME_USHORT( usWeightClass ),
+ FT_FRAME_USHORT( usWidthClass ),
+ FT_FRAME_SHORT ( fsType ),
+ FT_FRAME_SHORT ( ySubscriptXSize ),
+ FT_FRAME_SHORT ( ySubscriptYSize ),
+ FT_FRAME_SHORT ( ySubscriptXOffset ),
+ FT_FRAME_SHORT ( ySubscriptYOffset ),
+ FT_FRAME_SHORT ( ySuperscriptXSize ),
+ FT_FRAME_SHORT ( ySuperscriptYSize ),
+ FT_FRAME_SHORT ( ySuperscriptXOffset ),
+ FT_FRAME_SHORT ( ySuperscriptYOffset ),
+ FT_FRAME_SHORT ( yStrikeoutSize ),
+ FT_FRAME_SHORT ( yStrikeoutPosition ),
+ FT_FRAME_SHORT ( sFamilyClass ),
+ FT_FRAME_BYTE ( panose[0] ),
+ FT_FRAME_BYTE ( panose[1] ),
+ FT_FRAME_BYTE ( panose[2] ),
+ FT_FRAME_BYTE ( panose[3] ),
+ FT_FRAME_BYTE ( panose[4] ),
+ FT_FRAME_BYTE ( panose[5] ),
+ FT_FRAME_BYTE ( panose[6] ),
+ FT_FRAME_BYTE ( panose[7] ),
+ FT_FRAME_BYTE ( panose[8] ),
+ FT_FRAME_BYTE ( panose[9] ),
+ FT_FRAME_ULONG ( ulUnicodeRange1 ),
+ FT_FRAME_ULONG ( ulUnicodeRange2 ),
+ FT_FRAME_ULONG ( ulUnicodeRange3 ),
+ FT_FRAME_ULONG ( ulUnicodeRange4 ),
+ FT_FRAME_BYTE ( achVendID[0] ),
+ FT_FRAME_BYTE ( achVendID[1] ),
+ FT_FRAME_BYTE ( achVendID[2] ),
+ FT_FRAME_BYTE ( achVendID[3] ),
+
+ FT_FRAME_USHORT( fsSelection ),
+ FT_FRAME_USHORT( usFirstCharIndex ),
+ FT_FRAME_USHORT( usLastCharIndex ),
+ FT_FRAME_SHORT ( sTypoAscender ),
+ FT_FRAME_SHORT ( sTypoDescender ),
+ FT_FRAME_SHORT ( sTypoLineGap ),
+ FT_FRAME_USHORT( usWinAscent ),
+ FT_FRAME_USHORT( usWinDescent ),
+ FT_FRAME_END
+ };
+
+ const FT_Frame_Field os2_fields_extra[] =
+ {
+ FT_FRAME_START( 8 ),
+ FT_FRAME_ULONG( ulCodePageRange1 ),
+ FT_FRAME_ULONG( ulCodePageRange2 ),
+ FT_FRAME_END
+ };
+
+ const FT_Frame_Field os2_fields_extra2[] =
+ {
+ FT_FRAME_START( 10 ),
+ FT_FRAME_SHORT ( sxHeight ),
+ FT_FRAME_SHORT ( sCapHeight ),
+ FT_FRAME_USHORT( usDefaultChar ),
+ FT_FRAME_USHORT( usBreakChar ),
+ FT_FRAME_USHORT( usMaxContext ),
+ FT_FRAME_END
+ };
+
+
+ /* We now support old Mac fonts where the OS/2 table doesn't */
+ /* exist. Simply put, we set the `version' field to 0xFFFF */
+ /* and test this value each time we need to access the table. */
+ error = face->goto_table( face, TTAG_OS2, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ os2 = &face->os2;
+
+ if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
+ goto Exit;
+
+ os2->ulCodePageRange1 = 0;
+ os2->ulCodePageRange2 = 0;
+ os2->sxHeight = 0;
+ os2->sCapHeight = 0;
+ os2->usDefaultChar = 0;
+ os2->usBreakChar = 0;
+ os2->usMaxContext = 0;
+
+ if ( os2->version >= 0x0001 )
+ {
+ /* only version 1 tables */
+ if ( FT_STREAM_READ_FIELDS( os2_fields_extra, os2 ) )
+ goto Exit;
+
+ if ( os2->version >= 0x0002 )
+ {
+ /* only version 2 tables */
+ if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
+ goto Exit;
+ }
+ }
+
+ FT_TRACE3(( "sTypoAscender: %4d\n", os2->sTypoAscender ));
+ FT_TRACE3(( "sTypoDescender: %4d\n", os2->sTypoDescender ));
+ FT_TRACE3(( "usWinAscent: %4u\n", os2->usWinAscent ));
+ FT_TRACE3(( "usWinDescent: %4u\n", os2->usWinDescent ));
+ FT_TRACE3(( "fsSelection: 0x%2x\n", os2->fsSelection ));
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_postscript */
+ /* */
+ /* <Description> */
+ /* Loads the Postscript table. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_post( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ TT_Postscript* post = &face->postscript;
+
+ static const FT_Frame_Field post_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_Postscript
+
+ FT_FRAME_START( 32 ),
+ FT_FRAME_ULONG( FormatType ),
+ FT_FRAME_ULONG( italicAngle ),
+ FT_FRAME_SHORT( underlinePosition ),
+ FT_FRAME_SHORT( underlineThickness ),
+ FT_FRAME_ULONG( isFixedPitch ),
+ FT_FRAME_ULONG( minMemType42 ),
+ FT_FRAME_ULONG( maxMemType42 ),
+ FT_FRAME_ULONG( minMemType1 ),
+ FT_FRAME_ULONG( maxMemType1 ),
+ FT_FRAME_END
+ };
+
+
+ error = face->goto_table( face, TTAG_post, stream, 0 );
+ if ( error )
+ return error;
+
+ if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
+ return error;
+
+ /* we don't load the glyph names, we do that in another */
+ /* module (ttpost). */
+
+ FT_TRACE3(( "FormatType: 0x%x\n", post->FormatType ));
+ FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch
+ ? " yes" : " no" ));
+
+ return SFNT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_pclt */
+ /* */
+ /* <Description> */
+ /* Loads the PCL 5 Table. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_pclt( TT_Face face,
+ FT_Stream stream )
+ {
+ static const FT_Frame_Field pclt_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_PCLT
+
+ FT_FRAME_START( 54 ),
+ FT_FRAME_ULONG ( Version ),
+ FT_FRAME_ULONG ( FontNumber ),
+ FT_FRAME_USHORT( Pitch ),
+ FT_FRAME_USHORT( xHeight ),
+ FT_FRAME_USHORT( Style ),
+ FT_FRAME_USHORT( TypeFamily ),
+ FT_FRAME_USHORT( CapHeight ),
+ FT_FRAME_BYTES ( TypeFace, 16 ),
+ FT_FRAME_BYTES ( CharacterComplement, 8 ),
+ FT_FRAME_BYTES ( FileName, 6 ),
+ FT_FRAME_CHAR ( StrokeWeight ),
+ FT_FRAME_CHAR ( WidthType ),
+ FT_FRAME_BYTE ( SerifStyle ),
+ FT_FRAME_BYTE ( Reserved ),
+ FT_FRAME_END
+ };
+
+ FT_Error error;
+ TT_PCLT* pclt = &face->pclt;
+
+
+ /* optional table */
+ error = face->goto_table( face, TTAG_PCLT, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_gasp */
+ /* */
+ /* <Description> */
+ /* Loads the `gasp' table into a face object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_gasp( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UInt j,num_ranges;
+ TT_GaspRange gaspranges;
+
+
+ /* the gasp table is optional */
+ error = face->goto_table( face, TTAG_gasp, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ if ( FT_FRAME_ENTER( 4L ) )
+ goto Exit;
+
+ face->gasp.version = FT_GET_USHORT();
+ face->gasp.numRanges = FT_GET_USHORT();
+
+ FT_FRAME_EXIT();
+
+ /* only support versions 0 and 1 of the table */
+ if ( face->gasp.version >= 2 )
+ {
+ face->gasp.numRanges = 0;
+ error = SFNT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ num_ranges = face->gasp.numRanges;
+ FT_TRACE3(( "numRanges: %u\n", num_ranges ));
+
+ if ( FT_QNEW_ARRAY( gaspranges, num_ranges ) ||
+ FT_FRAME_ENTER( num_ranges * 4L ) )
+ goto Exit;
+
+ face->gasp.gaspRanges = gaspranges;
+
+ for ( j = 0; j < num_ranges; j++ )
+ {
+ gaspranges[j].maxPPEM = FT_GET_USHORT();
+ gaspranges[j].gaspFlag = FT_GET_USHORT();
+
+ FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n",
+ j,
+ gaspranges[j].maxPPEM,
+ gaspranges[j].gaspFlag ));
+ }
+
+ FT_FRAME_EXIT();
+
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/src/sfnt/ttload.h b/src/sfnt/ttload.h
new file mode 100644
index 0000000..49a1aee
--- /dev/null
+++ b/src/sfnt/ttload.h
@@ -0,0 +1,112 @@
+/***************************************************************************/
+/* */
+/* ttload.h */
+/* */
+/* Load the basic TrueType tables, i.e., tables that can be either in */
+/* TTF or OTF fonts (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTLOAD_H__
+#define __TTLOAD_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( TT_Table )
+ tt_face_lookup_table( TT_Face face,
+ FT_ULong tag );
+
+ FT_LOCAL( FT_Error )
+ tt_face_goto_table( TT_Face face,
+ FT_ULong tag,
+ FT_Stream stream,
+ FT_ULong* length );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_font_dir( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_any( TT_Face face,
+ FT_ULong tag,
+ FT_Long offset,
+ FT_Byte* buffer,
+ FT_ULong* length );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_head( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_cmap( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_maxp( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_name( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_os2( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_post( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_pclt( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_name( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_gasp( TT_Face face,
+ FT_Stream stream );
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_bhed( TT_Face face,
+ FT_Stream stream );
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+FT_END_HEADER
+
+#endif /* __TTLOAD_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttmtx.c b/src/sfnt/ttmtx.c
new file mode 100644
index 0000000..286bd0c
--- /dev/null
+++ b/src/sfnt/ttmtx.c
@@ -0,0 +1,465 @@
+/***************************************************************************/
+/* */
+/* ttmtx.c */
+/* */
+/* Load the metrics tables common to TTF and OTF fonts (body). */
+/* */
+/* Copyright 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttmtx.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttmtx
+
+
+ /*
+ * Unfortunately, we can't enable our memory optimizations if
+ * FT_CONFIG_OPTION_OLD_INTERNALS is defined. This is because at least
+ * one rogue client (libXfont in the X.Org XServer) is directly accessing
+ * the metrics.
+ */
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_hmtx */
+ /* */
+ /* <Description> */
+ /* Load the `hmtx' or `vmtx' table into a face object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* vertical :: A boolean flag. If set, load `vmtx'. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+#if !defined FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hmtx( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical )
+ {
+ FT_Error error;
+ FT_ULong tag, table_size;
+ FT_ULong* ptable_offset;
+ FT_ULong* ptable_size;
+
+
+ if ( vertical )
+ {
+ tag = TTAG_vmtx;
+ ptable_offset = &face->vert_metrics_offset;
+ ptable_size = &face->vert_metrics_size;
+ }
+ else
+ {
+ tag = TTAG_hmtx;
+ ptable_offset = &face->horz_metrics_offset;
+ ptable_size = &face->horz_metrics_size;
+ }
+
+ error = face->goto_table( face, tag, stream, &table_size );
+ if ( error )
+ goto Fail;
+
+ *ptable_size = table_size;
+ *ptable_offset = FT_STREAM_POS();
+
+ Fail:
+ return error;
+ }
+
+#else /* !OPTIMIZE_MEMORY || OLD_INTERNALS */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hmtx( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong table_len;
+ FT_Long num_shorts, num_longs, num_shorts_checked;
+
+ TT_LongMetrics* longs;
+ TT_ShortMetrics** shorts;
+ FT_Byte* p;
+
+
+ if ( vertical )
+ {
+ void* lm = &face->vertical.long_metrics;
+ void** sm = &face->vertical.short_metrics;
+
+
+ error = face->goto_table( face, TTAG_vmtx, stream, &table_len );
+ if ( error )
+ goto Fail;
+
+ num_longs = face->vertical.number_Of_VMetrics;
+ if ( (FT_ULong)num_longs > table_len / 4 )
+ num_longs = (FT_Long)( table_len / 4 );
+
+ face->vertical.number_Of_VMetrics = 0;
+
+ longs = (TT_LongMetrics*)lm;
+ shorts = (TT_ShortMetrics**)sm;
+ }
+ else
+ {
+ void* lm = &face->horizontal.long_metrics;
+ void** sm = &face->horizontal.short_metrics;
+
+
+ error = face->goto_table( face, TTAG_hmtx, stream, &table_len );
+ if ( error )
+ goto Fail;
+
+ num_longs = face->horizontal.number_Of_HMetrics;
+ if ( (FT_ULong)num_longs > table_len / 4 )
+ num_longs = (FT_Long)( table_len / 4 );
+
+ face->horizontal.number_Of_HMetrics = 0;
+
+ longs = (TT_LongMetrics*)lm;
+ shorts = (TT_ShortMetrics**)sm;
+ }
+
+ /* never trust derived values */
+
+ num_shorts = face->max_profile.numGlyphs - num_longs;
+ num_shorts_checked = ( table_len - num_longs * 4L ) / 2;
+
+ if ( num_shorts < 0 )
+ {
+ FT_ERROR(( "%cmtx has more metrics than glyphs.\n" ));
+
+ /* Adobe simply ignores this problem. So we shall do the same. */
+#if 0
+ error = vertical ? SFNT_Err_Invalid_Vert_Metrics
+ : SFNT_Err_Invalid_Horiz_Metrics;
+ goto Exit;
+#else
+ num_shorts = 0;
+#endif
+ }
+
+ if ( FT_QNEW_ARRAY( *longs, num_longs ) ||
+ FT_QNEW_ARRAY( *shorts, num_shorts ) )
+ goto Fail;
+
+ if ( FT_FRAME_ENTER( table_len ) )
+ goto Fail;
+
+ p = stream->cursor;
+
+ {
+ TT_LongMetrics cur = *longs;
+ TT_LongMetrics limit = cur + num_longs;
+
+
+ for ( ; cur < limit; cur++ )
+ {
+ cur->advance = FT_NEXT_USHORT( p );
+ cur->bearing = FT_NEXT_SHORT( p );
+ }
+ }
+
+ /* do we have an inconsistent number of metric values? */
+ {
+ TT_ShortMetrics* cur = *shorts;
+ TT_ShortMetrics* limit = cur +
+ FT_MIN( num_shorts, num_shorts_checked );
+
+
+ for ( ; cur < limit; cur++ )
+ *cur = FT_NEXT_SHORT( p );
+
+ /* We fill up the missing left side bearings with the */
+ /* last valid value. Since this will occur for buggy CJK */
+ /* fonts usually only, nothing serious will happen. */
+ if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 )
+ {
+ FT_Short val = (*shorts)[num_shorts_checked - 1];
+
+
+ limit = *shorts + num_shorts;
+ for ( ; cur < limit; cur++ )
+ *cur = val;
+ }
+ }
+
+ FT_FRAME_EXIT();
+
+ if ( vertical )
+ face->vertical.number_Of_VMetrics = (FT_UShort)num_longs;
+ else
+ face->horizontal.number_Of_HMetrics = (FT_UShort)num_longs;
+
+ Fail:
+ return error;
+ }
+
+#endif /* !OPTIMIZE_MEMORY || OLD_INTERNALS */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_hhea */
+ /* */
+ /* <Description> */
+ /* Load the `hhea' or 'vhea' table into a face object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* vertical :: A boolean flag. If set, load `vhea'. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hhea( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical )
+ {
+ FT_Error error;
+ TT_HoriHeader* header;
+
+ const FT_Frame_Field metrics_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_HoriHeader
+
+ FT_FRAME_START( 36 ),
+ FT_FRAME_ULONG ( Version ),
+ FT_FRAME_SHORT ( Ascender ),
+ FT_FRAME_SHORT ( Descender ),
+ FT_FRAME_SHORT ( Line_Gap ),
+ FT_FRAME_USHORT( advance_Width_Max ),
+ FT_FRAME_SHORT ( min_Left_Side_Bearing ),
+ FT_FRAME_SHORT ( min_Right_Side_Bearing ),
+ FT_FRAME_SHORT ( xMax_Extent ),
+ FT_FRAME_SHORT ( caret_Slope_Rise ),
+ FT_FRAME_SHORT ( caret_Slope_Run ),
+ FT_FRAME_SHORT ( caret_Offset ),
+ FT_FRAME_SHORT ( Reserved[0] ),
+ FT_FRAME_SHORT ( Reserved[1] ),
+ FT_FRAME_SHORT ( Reserved[2] ),
+ FT_FRAME_SHORT ( Reserved[3] ),
+ FT_FRAME_SHORT ( metric_Data_Format ),
+ FT_FRAME_USHORT( number_Of_HMetrics ),
+ FT_FRAME_END
+ };
+
+
+ if ( vertical )
+ {
+ void *v = &face->vertical;
+
+
+ error = face->goto_table( face, TTAG_vhea, stream, 0 );
+ if ( error )
+ goto Fail;
+
+ header = (TT_HoriHeader*)v;
+ }
+ else
+ {
+ error = face->goto_table( face, TTAG_hhea, stream, 0 );
+ if ( error )
+ goto Fail;
+
+ header = &face->horizontal;
+ }
+
+ if ( FT_STREAM_READ_FIELDS( metrics_header_fields, header ) )
+ goto Fail;
+
+ FT_TRACE3(( "Ascender: %5d\n", header->Ascender ));
+ FT_TRACE3(( "Descender: %5d\n", header->Descender ));
+ FT_TRACE3(( "number_Of_Metrics: %5u\n", header->number_Of_HMetrics ));
+
+ header->long_metrics = NULL;
+ header->short_metrics = NULL;
+
+ Fail:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_get_metrics */
+ /* */
+ /* <Description> */
+ /* Returns the horizontal or vertical metrics in font units for a */
+ /* given glyph. The metrics are the left side bearing (resp. top */
+ /* side bearing) and advance width (resp. advance height). */
+ /* */
+ /* <Input> */
+ /* header :: A pointer to either the horizontal or vertical metrics */
+ /* structure. */
+ /* */
+ /* idx :: The glyph index. */
+ /* */
+ /* <Output> */
+ /* bearing :: The bearing, either left side or top side. */
+ /* */
+ /* advance :: The advance width resp. advance height. */
+ /* */
+#if !defined FT_CONFIG_OPTION_OLD_INTERNALS
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_get_metrics( TT_Face face,
+ FT_Bool vertical,
+ FT_UInt gindex,
+ FT_Short *abearing,
+ FT_UShort *aadvance )
+ {
+ FT_Error error;
+ FT_Stream stream = face->root.stream;
+ TT_HoriHeader* header;
+ FT_ULong table_pos, table_size, table_end;
+ FT_UShort k;
+
+
+ if ( vertical )
+ {
+ void* v = &face->vertical;
+
+
+ header = (TT_HoriHeader*)v;
+ table_pos = face->vert_metrics_offset;
+ table_size = face->vert_metrics_size;
+ }
+ else
+ {
+ header = &face->horizontal;
+ table_pos = face->horz_metrics_offset;
+ table_size = face->horz_metrics_size;
+ }
+
+ table_end = table_pos + table_size;
+
+ k = header->number_Of_HMetrics;
+
+ if ( k > 0 )
+ {
+ if ( gindex < (FT_UInt)k )
+ {
+ table_pos += 4 * gindex;
+ if ( table_pos + 4 > table_end )
+ goto NoData;
+
+ if ( FT_STREAM_SEEK( table_pos ) ||
+ FT_READ_USHORT( *aadvance ) ||
+ FT_READ_SHORT( *abearing ) )
+ goto NoData;
+ }
+ else
+ {
+ table_pos += 4 * ( k - 1 );
+ if ( table_pos + 4 > table_end )
+ goto NoData;
+
+ if ( FT_STREAM_SEEK( table_pos ) ||
+ FT_READ_USHORT( *aadvance ) )
+ goto NoData;
+
+ table_pos += 4 + 2 * ( gindex - k );
+ if ( table_pos + 2 > table_end )
+ *abearing = 0;
+ else
+ {
+ if ( !FT_STREAM_SEEK( table_pos ) )
+ (void)FT_READ_SHORT( *abearing );
+ }
+ }
+ }
+ else
+ {
+ NoData:
+ *abearing = 0;
+ *aadvance = 0;
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+#else /* OLD_INTERNALS */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_get_metrics( TT_Face face,
+ FT_Bool vertical,
+ FT_UInt gindex,
+ FT_Short* abearing,
+ FT_UShort* aadvance )
+ {
+ void* v = &face->vertical;
+ void* h = &face->horizontal;
+ TT_HoriHeader* header = vertical ? (TT_HoriHeader*)v : h;
+ TT_LongMetrics longs_m;
+ FT_UShort k = header->number_Of_HMetrics;
+
+
+ if ( k == 0 ||
+ !header->long_metrics ||
+ gindex >= (FT_UInt)face->max_profile.numGlyphs )
+ {
+ *abearing = *aadvance = 0;
+ return SFNT_Err_Ok;
+ }
+
+ if ( gindex < (FT_UInt)k )
+ {
+ longs_m = (TT_LongMetrics)header->long_metrics + gindex;
+ *abearing = longs_m->bearing;
+ *aadvance = longs_m->advance;
+ }
+ else
+ {
+ *abearing = ((TT_ShortMetrics*)header->short_metrics)[gindex - k];
+ *aadvance = ((TT_LongMetrics)header->long_metrics)[k - 1].advance;
+ }
+
+ return SFNT_Err_Ok;
+ }
+
+#endif /* !OPTIMIZE_MEMORY || OLD_INTERNALS */
+
+
+/* END */
diff --git a/src/sfnt/ttmtx.h b/src/sfnt/ttmtx.h
new file mode 100644
index 0000000..8b91a11
--- /dev/null
+++ b/src/sfnt/ttmtx.h
@@ -0,0 +1,55 @@
+/***************************************************************************/
+/* */
+/* ttmtx.h */
+/* */
+/* Load the metrics tables common to TTF and OTF fonts (specification). */
+/* */
+/* Copyright 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTMTX_H__
+#define __TTMTX_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_hhea( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_hmtx( TT_Face face,
+ FT_Stream stream,
+ FT_Bool vertical );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_get_metrics( TT_Face face,
+ FT_Bool vertical,
+ FT_UInt gindex,
+ FT_Short* abearing,
+ FT_UShort* aadvance );
+
+FT_END_HEADER
+
+#endif /* __TTMTX_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttpost.c b/src/sfnt/ttpost.c
new file mode 100644
index 0000000..1e61636
--- /dev/null
+++ b/src/sfnt/ttpost.c
@@ -0,0 +1,521 @@
+/***************************************************************************/
+/* */
+/* ttpost.c */
+/* */
+/* Postcript name table processing for TrueType and OpenType fonts */
+/* (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* The post table is not completely loaded by the core engine. This */
+ /* file loads the missing PS glyph names and implements an API to access */
+ /* them. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttpost.h"
+#include "ttload.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttpost
+
+
+ /* If this configuration macro is defined, we rely on the `PSNames' */
+ /* module to grab the glyph names. */
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+
+
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+
+#define MAC_NAME( x ) ( (FT_String*)psnames->macintosh_name( x ) )
+
+
+#else /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ /* Otherwise, we ignore the `PSNames' module, and provide our own */
+ /* table of Mac names. Thus, it is possible to build a version of */
+ /* FreeType without the Type 1 driver & PSNames module. */
+
+#define MAC_NAME( x ) tt_post_default_names[x]
+
+ /* the 258 default Mac PS glyph names */
+
+ static const FT_String* tt_post_default_names[258] =
+ {
+ /* 0 */
+ ".notdef", ".null", "CR", "space", "exclam",
+ "quotedbl", "numbersign", "dollar", "percent", "ampersand",
+ /* 10 */
+ "quotesingle", "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash", "zero",
+ /* 20 */
+ "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "colon",
+ /* 30 */
+ "semicolon", "less", "equal", "greater", "question",
+ "at", "A", "B", "C", "D",
+ /* 40 */
+ "E", "F", "G", "H", "I",
+ "J", "K", "L", "M", "N",
+ /* 50 */
+ "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X",
+ /* 60 */
+ "Y", "Z", "bracketleft", "backslash", "bracketright",
+ "asciicircum", "underscore", "grave", "a", "b",
+ /* 70 */
+ "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l",
+ /* 80 */
+ "m", "n", "o", "p", "q",
+ "r", "s", "t", "u", "v",
+ /* 90 */
+ "w", "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", "Adieresis", "Aring",
+ /* 100 */
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
+ "aacute", "agrave", "acircumflex", "adieresis", "atilde",
+ /* 110 */
+ "aring", "ccedilla", "eacute", "egrave", "ecircumflex",
+ "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
+ /* 120 */
+ "ntilde", "oacute", "ograve", "ocircumflex", "odieresis",
+ "otilde", "uacute", "ugrave", "ucircumflex", "udieresis",
+ /* 130 */
+ "dagger", "degree", "cent", "sterling", "section",
+ "bullet", "paragraph", "germandbls", "registered", "copyright",
+ /* 140 */
+ "trademark", "acute", "dieresis", "notequal", "AE",
+ "Oslash", "infinity", "plusminus", "lessequal", "greaterequal",
+ /* 150 */
+ "yen", "mu", "partialdiff", "summation", "product",
+ "pi", "integral", "ordfeminine", "ordmasculine", "Omega",
+ /* 160 */
+ "ae", "oslash", "questiondown", "exclamdown", "logicalnot",
+ "radical", "florin", "approxequal", "Delta", "guillemotleft",
+ /* 170 */
+ "guillemotright", "ellipsis", "nbspace", "Agrave", "Atilde",
+ "Otilde", "OE", "oe", "endash", "emdash",
+ /* 180 */
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",
+ "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
+ /* 190 */
+ "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl",
+ "periodcentered", "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+ /* 200 */
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",
+ "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
+ /* 210 */
+ "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave",
+ "dotlessi", "circumflex", "tilde", "macron", "breve",
+ /* 220 */
+ "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek",
+ "caron", "Lslash", "lslash", "Scaron", "scaron",
+ /* 230 */
+ "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
+ "Yacute", "yacute", "Thorn", "thorn", "minus",
+ /* 240 */
+ "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
+ "onequarter", "threequarters", "franc", "Gbreve", "gbreve",
+ /* 250 */
+ "Idot", "Scedilla", "scedilla", "Cacute", "cacute",
+ "Ccaron", "ccaron", "dmacron",
+ };
+
+
+#endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */
+
+
+ static FT_Error
+ load_format_20( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_Int num_glyphs;
+ FT_UShort num_names;
+
+ FT_UShort* glyph_indices = 0;
+ FT_Char** name_strings = 0;
+
+
+ if ( FT_READ_USHORT( num_glyphs ) )
+ goto Exit;
+
+ /* UNDOCUMENTED! The number of glyphs in this table can be smaller */
+ /* than the value in the maxp table (cf. cyberbit.ttf). */
+
+ /* There already exist fonts which have more than 32768 glyph names */
+ /* in this table, so the test for this threshold has been dropped. */
+
+ if ( num_glyphs > face->max_profile.numGlyphs )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* load the indices */
+ {
+ FT_Int n;
+
+
+ if ( FT_NEW_ARRAY ( glyph_indices, num_glyphs ) ||
+ FT_FRAME_ENTER( num_glyphs * 2L ) )
+ goto Fail;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ glyph_indices[n] = FT_GET_USHORT();
+
+ FT_FRAME_EXIT();
+ }
+
+ /* compute number of names stored in table */
+ {
+ FT_Int n;
+
+
+ num_names = 0;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ FT_Int idx;
+
+
+ idx = glyph_indices[n];
+ if ( idx >= 258 )
+ {
+ idx -= 257;
+ if ( idx > num_names )
+ num_names = (FT_UShort)idx;
+ }
+ }
+ }
+
+ /* now load the name strings */
+ {
+ FT_UShort n;
+
+
+ if ( FT_NEW_ARRAY( name_strings, num_names ) )
+ goto Fail;
+
+ for ( n = 0; n < num_names; n++ )
+ {
+ FT_UInt len;
+
+
+ if ( FT_READ_BYTE ( len ) ||
+ FT_NEW_ARRAY( name_strings[n], len + 1 ) ||
+ FT_STREAM_READ ( name_strings[n], len ) )
+ goto Fail1;
+
+ name_strings[n][len] = '\0';
+ }
+ }
+
+ /* all right, set table fields and exit successfully */
+ {
+ TT_Post_20 table = &face->postscript_names.names.format_20;
+
+
+ table->num_glyphs = (FT_UShort)num_glyphs;
+ table->num_names = (FT_UShort)num_names;
+ table->glyph_indices = glyph_indices;
+ table->glyph_names = name_strings;
+ }
+ return SFNT_Err_Ok;
+
+ Fail1:
+ {
+ FT_UShort n;
+
+
+ for ( n = 0; n < num_names; n++ )
+ FT_FREE( name_strings[n] );
+ }
+
+ Fail:
+ FT_FREE( name_strings );
+ FT_FREE( glyph_indices );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_format_25( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_Int num_glyphs;
+ FT_Char* offset_table = 0;
+
+
+ /* UNDOCUMENTED! This value appears only in the Apple TT specs. */
+ if ( FT_READ_USHORT( num_glyphs ) )
+ goto Exit;
+
+ /* check the number of glyphs */
+ if ( num_glyphs > face->max_profile.numGlyphs || num_glyphs > 258 )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( FT_NEW_ARRAY( offset_table, num_glyphs ) ||
+ FT_STREAM_READ( offset_table, num_glyphs ) )
+ goto Fail;
+
+ /* now check the offset table */
+ {
+ FT_Int n;
+
+
+ for ( n = 0; n < num_glyphs; n++ )
+ {
+ FT_Long idx = (FT_Long)n + offset_table[n];
+
+
+ if ( idx < 0 || idx > num_glyphs )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Fail;
+ }
+ }
+ }
+
+ /* OK, set table fields and exit successfully */
+ {
+ TT_Post_25 table = &face->postscript_names.names.format_25;
+
+
+ table->num_glyphs = (FT_UShort)num_glyphs;
+ table->offsets = offset_table;
+ }
+
+ return SFNT_Err_Ok;
+
+ Fail:
+ FT_FREE( offset_table );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ load_post_names( TT_Face face )
+ {
+ FT_Stream stream;
+ FT_Error error;
+ FT_Fixed format;
+
+
+ /* get a stream for the face's resource */
+ stream = face->root.stream;
+
+ /* seek to the beginning of the PS names table */
+ error = face->goto_table( face, TTAG_post, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ format = face->postscript.FormatType;
+
+ /* go to beginning of subtable */
+ if ( FT_STREAM_SKIP( 32 ) )
+ goto Exit;
+
+ /* now read postscript table */
+ if ( format == 0x00020000L )
+ error = load_format_20( face, stream );
+ else if ( format == 0x00028000L )
+ error = load_format_25( face, stream );
+ else
+ error = SFNT_Err_Invalid_File_Format;
+
+ face->postscript_names.loaded = 1;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_ps_names( TT_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ TT_Post_Names names = &face->postscript_names;
+ FT_Fixed format;
+
+
+ if ( names->loaded )
+ {
+ format = face->postscript.FormatType;
+
+ if ( format == 0x00020000L )
+ {
+ TT_Post_20 table = &names->names.format_20;
+ FT_UShort n;
+
+
+ FT_FREE( table->glyph_indices );
+ table->num_glyphs = 0;
+
+ for ( n = 0; n < table->num_names; n++ )
+ FT_FREE( table->glyph_names[n] );
+
+ FT_FREE( table->glyph_names );
+ table->num_names = 0;
+ }
+ else if ( format == 0x00028000L )
+ {
+ TT_Post_25 table = &names->names.format_25;
+
+
+ FT_FREE( table->offsets );
+ table->num_glyphs = 0;
+ }
+ }
+ names->loaded = 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_get_ps_name */
+ /* */
+ /* <Description> */
+ /* Gets the PostScript glyph name of a glyph. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the parent face. */
+ /* */
+ /* idx :: The glyph index. */
+ /* */
+ /* PSname :: The address of a string pointer. Will be NULL in case */
+ /* of error, otherwise it is a pointer to the glyph name. */
+ /* */
+ /* You must not modify the returned string! */
+ /* */
+ /* <Output> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_get_ps_name( TT_Face face,
+ FT_UInt idx,
+ FT_String** PSname )
+ {
+ FT_Error error;
+ TT_Post_Names names;
+ FT_Fixed format;
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ FT_Service_PsCMaps psnames;
+#endif
+
+
+ if ( !face )
+ return SFNT_Err_Invalid_Face_Handle;
+
+ if ( idx >= (FT_UInt)face->max_profile.numGlyphs )
+ return SFNT_Err_Invalid_Glyph_Index;
+
+#ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES
+ psnames = (FT_Service_PsCMaps)face->psnames;
+ if ( !psnames )
+ return SFNT_Err_Unimplemented_Feature;
+#endif
+
+ names = &face->postscript_names;
+
+ /* `.notdef' by default */
+ *PSname = MAC_NAME( 0 );
+
+ format = face->postscript.FormatType;
+
+ if ( format == 0x00010000L )
+ {
+ if ( idx < 258 ) /* paranoid checking */
+ *PSname = MAC_NAME( idx );
+ }
+ else if ( format == 0x00020000L )
+ {
+ TT_Post_20 table = &names->names.format_20;
+
+
+ if ( !names->loaded )
+ {
+ error = load_post_names( face );
+ if ( error )
+ goto End;
+ }
+
+ if ( idx < (FT_UInt)table->num_glyphs )
+ {
+ FT_UShort name_index = table->glyph_indices[idx];
+
+
+ if ( name_index < 258 )
+ *PSname = MAC_NAME( name_index );
+ else
+ *PSname = (FT_String*)table->glyph_names[name_index - 258];
+ }
+ }
+ else if ( format == 0x00028000L )
+ {
+ TT_Post_25 table = &names->names.format_25;
+
+
+ if ( !names->loaded )
+ {
+ error = load_post_names( face );
+ if ( error )
+ goto End;
+ }
+
+ if ( idx < (FT_UInt)table->num_glyphs ) /* paranoid checking */
+ {
+ idx += table->offsets[idx];
+ *PSname = MAC_NAME( idx );
+ }
+ }
+
+ /* nothing to do for format == 0x00030000L */
+
+ End:
+ return SFNT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/sfnt/ttpost.h b/src/sfnt/ttpost.h
new file mode 100644
index 0000000..6f06d75
--- /dev/null
+++ b/src/sfnt/ttpost.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* ttpost.h */
+/* */
+/* Postcript name table processing for TrueType and OpenType fonts */
+/* (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTPOST_H__
+#define __TTPOST_H__
+
+
+#include <ft2build.h>
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_get_ps_name( TT_Face face,
+ FT_UInt idx,
+ FT_String** PSname );
+
+ FT_LOCAL( void )
+ tt_face_free_ps_names( TT_Face face );
+
+
+FT_END_HEADER
+
+#endif /* __TTPOST_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttsbit.c b/src/sfnt/ttsbit.c
new file mode 100644
index 0000000..eff49da
--- /dev/null
+++ b/src/sfnt/ttsbit.c
@@ -0,0 +1,1501 @@
+/***************************************************************************/
+/* */
+/* ttsbit.c */
+/* */
+/* TrueType and OpenType embedded bitmap support (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+
+ /*
+ * Alas, the memory-optimized sbit loader can't be used when implementing
+ * the `old internals' hack
+ */
+#if !defined FT_CONFIG_OPTION_OLD_INTERNALS
+
+#include "ttsbit0.c"
+
+#else /* !FT_CONFIG_OPTION_OLD_INTERNALS */
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttsbit.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttsbit
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* blit_sbit */
+ /* */
+ /* <Description> */
+ /* Blits a bitmap from an input stream into a given target. Supports */
+ /* x and y offsets as well as byte padded lines. */
+ /* */
+ /* <Input> */
+ /* target :: The target bitmap/pixmap. */
+ /* */
+ /* source :: The input packed bitmap data. */
+ /* */
+ /* line_bits :: The number of bits per line. */
+ /* */
+ /* byte_padded :: A flag which is true if lines are byte-padded. */
+ /* */
+ /* x_offset :: The horizontal offset. */
+ /* */
+ /* y_offset :: The vertical offset. */
+ /* */
+ /* <Note> */
+ /* IMPORTANT: The x and y offsets are relative to the top corner of */
+ /* the target bitmap (unlike the normal TrueType */
+ /* convention). A positive y offset indicates a downwards */
+ /* direction! */
+ /* */
+ static void
+ blit_sbit( FT_Bitmap* target,
+ FT_Byte* source,
+ FT_Int line_bits,
+ FT_Bool byte_padded,
+ FT_Int x_offset,
+ FT_Int y_offset )
+ {
+ FT_Byte* line_buff;
+ FT_Int line_incr;
+ FT_Int height;
+
+ FT_UShort acc;
+ FT_UInt loaded;
+
+
+ /* first of all, compute starting write position */
+ line_incr = target->pitch;
+ line_buff = target->buffer;
+
+ if ( line_incr < 0 )
+ line_buff -= line_incr * ( target->rows - 1 );
+
+ line_buff += ( x_offset >> 3 ) + y_offset * line_incr;
+
+ /***********************************************************************/
+ /* */
+ /* We use the extra-classic `accumulator' trick to extract the bits */
+ /* from the source byte stream. */
+ /* */
+ /* Namely, the variable `acc' is a 16-bit accumulator containing the */
+ /* last `loaded' bits from the input stream. The bits are shifted to */
+ /* the upmost position in `acc'. */
+ /* */
+ /***********************************************************************/
+
+ acc = 0; /* clear accumulator */
+ loaded = 0; /* no bits were loaded */
+
+ for ( height = target->rows; height > 0; height-- )
+ {
+ FT_Byte* cur = line_buff; /* current write cursor */
+ FT_Int count = line_bits; /* # of bits to extract per line */
+ FT_Byte shift = (FT_Byte)( x_offset & 7 ); /* current write shift */
+ FT_Byte space = (FT_Byte)( 8 - shift );
+
+
+ /* first of all, read individual source bytes */
+ if ( count >= 8 )
+ {
+ count -= 8;
+ {
+ do
+ {
+ FT_Byte val;
+
+
+ /* ensure that there are at least 8 bits in the accumulator */
+ if ( loaded < 8 )
+ {
+ acc |= (FT_UShort)((FT_UShort)*source++ << ( 8 - loaded ));
+ loaded += 8;
+ }
+
+ /* now write one byte */
+ val = (FT_Byte)( acc >> 8 );
+ if ( shift )
+ {
+ cur[0] |= (FT_Byte)( val >> shift );
+ cur[1] |= (FT_Byte)( val << space );
+ }
+ else
+ cur[0] |= val;
+
+ cur++;
+ acc <<= 8; /* remove bits from accumulator */
+ loaded -= 8;
+ count -= 8;
+
+ } while ( count >= 0 );
+ }
+
+ /* restore `count' to correct value */
+ count += 8;
+ }
+
+ /* now write remaining bits (count < 8) */
+ if ( count > 0 )
+ {
+ FT_Byte val;
+
+
+ /* ensure that there are at least `count' bits in the accumulator */
+ if ( (FT_Int)loaded < count )
+ {
+ acc |= (FT_UShort)((FT_UShort)*source++ << ( 8 - loaded ));
+ loaded += 8;
+ }
+
+ /* now write remaining bits */
+ val = (FT_Byte)( ( (FT_Byte)( acc >> 8 ) ) & ~( 0xFF >> count ) );
+ cur[0] |= (FT_Byte)( val >> shift );
+
+ if ( count > space )
+ cur[1] |= (FT_Byte)( val << space );
+
+ acc <<= count;
+ loaded -= count;
+ }
+
+ /* now, skip to next line */
+ if ( byte_padded )
+ {
+ acc = 0;
+ loaded = 0; /* clear accumulator on byte-padded lines */
+ }
+
+ line_buff += line_incr;
+ }
+ }
+
+
+ static const FT_Frame_Field sbit_metrics_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_MetricsRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_BYTE( height ),
+ FT_FRAME_BYTE( width ),
+
+ FT_FRAME_CHAR( horiBearingX ),
+ FT_FRAME_CHAR( horiBearingY ),
+ FT_FRAME_BYTE( horiAdvance ),
+
+ FT_FRAME_CHAR( vertBearingX ),
+ FT_FRAME_CHAR( vertBearingY ),
+ FT_FRAME_BYTE( vertAdvance ),
+ FT_FRAME_END
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Load_SBit_Const_Metrics */
+ /* */
+ /* <Description> */
+ /* Loads the metrics for `EBLC' index tables format 2 and 5. */
+ /* */
+ /* <Input> */
+ /* range :: The target range. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Load_SBit_Const_Metrics( TT_SBit_Range range,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ if ( FT_READ_ULONG( range->image_size ) )
+ return error;
+
+ return FT_STREAM_READ_FIELDS( sbit_metrics_fields, &range->metrics );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Load_SBit_Range_Codes */
+ /* */
+ /* <Description> */
+ /* Loads the range codes for `EBLC' index tables format 4 and 5. */
+ /* */
+ /* <Input> */
+ /* range :: The target range. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* load_offsets :: A flag whether to load the glyph offset table. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Load_SBit_Range_Codes( TT_SBit_Range range,
+ FT_Stream stream,
+ FT_Bool load_offsets )
+ {
+ FT_Error error;
+ FT_ULong count, n, size;
+ FT_Memory memory = stream->memory;
+
+
+ if ( FT_READ_ULONG( count ) )
+ goto Exit;
+
+ range->num_glyphs = count;
+
+ /* Allocate glyph offsets table if needed */
+ if ( load_offsets )
+ {
+ if ( FT_NEW_ARRAY( range->glyph_offsets, count ) )
+ goto Exit;
+
+ size = count * 4L;
+ }
+ else
+ size = count * 2L;
+
+ /* Allocate glyph codes table and access frame */
+ if ( FT_NEW_ARRAY ( range->glyph_codes, count ) ||
+ FT_FRAME_ENTER( size ) )
+ goto Exit;
+
+ for ( n = 0; n < count; n++ )
+ {
+ range->glyph_codes[n] = FT_GET_USHORT();
+
+ if ( load_offsets )
+ range->glyph_offsets[n] = (FT_ULong)range->image_offset +
+ FT_GET_USHORT();
+ }
+
+ FT_FRAME_EXIT();
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Load_SBit_Range */
+ /* */
+ /* <Description> */
+ /* Loads a given `EBLC' index/range table. */
+ /* */
+ /* <Input> */
+ /* range :: The target range. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Load_SBit_Range( TT_SBit_Range range,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+
+ switch( range->index_format )
+ {
+ case 1: /* variable metrics with 4-byte offsets */
+ case 3: /* variable metrics with 2-byte offsets */
+ {
+ FT_ULong num_glyphs, n;
+ FT_Int size_elem;
+ FT_Bool large = FT_BOOL( range->index_format == 1 );
+
+
+
+ if ( range->last_glyph < range->first_glyph )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ num_glyphs = range->last_glyph - range->first_glyph + 1L;
+ range->num_glyphs = num_glyphs;
+ num_glyphs++; /* XXX: BEWARE - see spec */
+
+ size_elem = large ? 4 : 2;
+
+ if ( FT_NEW_ARRAY( range->glyph_offsets, num_glyphs ) ||
+ FT_FRAME_ENTER( num_glyphs * size_elem ) )
+ goto Exit;
+
+ for ( n = 0; n < num_glyphs; n++ )
+ range->glyph_offsets[n] = (FT_ULong)( range->image_offset +
+ ( large ? FT_GET_ULONG()
+ : FT_GET_USHORT() ) );
+ FT_FRAME_EXIT();
+ }
+ break;
+
+ case 2: /* all glyphs have identical metrics */
+ error = Load_SBit_Const_Metrics( range, stream );
+ break;
+
+ case 4:
+ error = Load_SBit_Range_Codes( range, stream, 1 );
+ break;
+
+ case 5:
+ error = Load_SBit_Const_Metrics( range, stream ) ||
+ Load_SBit_Range_Codes( range, stream, 0 );
+ break;
+
+ default:
+ error = SFNT_Err_Invalid_File_Format;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_eblc */
+ /* */
+ /* <Description> */
+ /* Loads the table of embedded bitmap sizes for this face. */
+ /* */
+ /* <Input> */
+ /* face :: The target face object. */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_eblc( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error = 0;
+ FT_Memory memory = stream->memory;
+ FT_Fixed version;
+ FT_ULong num_strikes;
+ FT_ULong table_base;
+
+ static const FT_Frame_Field sbit_line_metrics_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_LineMetricsRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_CHAR( ascender ),
+ FT_FRAME_CHAR( descender ),
+ FT_FRAME_BYTE( max_width ),
+
+ FT_FRAME_CHAR( caret_slope_numerator ),
+ FT_FRAME_CHAR( caret_slope_denominator ),
+ FT_FRAME_CHAR( caret_offset ),
+
+ FT_FRAME_CHAR( min_origin_SB ),
+ FT_FRAME_CHAR( min_advance_SB ),
+ FT_FRAME_CHAR( max_before_BL ),
+ FT_FRAME_CHAR( min_after_BL ),
+ FT_FRAME_CHAR( pads[0] ),
+ FT_FRAME_CHAR( pads[1] ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field strike_start_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_StrikeRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_ULONG( ranges_offset ),
+ FT_FRAME_SKIP_LONG,
+ FT_FRAME_ULONG( num_ranges ),
+ FT_FRAME_ULONG( color_ref ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field strike_end_fields[] =
+ {
+ /* no FT_FRAME_START */
+ FT_FRAME_USHORT( start_glyph ),
+ FT_FRAME_USHORT( end_glyph ),
+ FT_FRAME_BYTE ( x_ppem ),
+ FT_FRAME_BYTE ( y_ppem ),
+ FT_FRAME_BYTE ( bit_depth ),
+ FT_FRAME_CHAR ( flags ),
+ FT_FRAME_END
+ };
+
+
+ face->num_sbit_strikes = 0;
+
+ /* this table is optional */
+ error = face->goto_table( face, TTAG_EBLC, stream, 0 );
+ if ( error )
+ error = face->goto_table( face, TTAG_bloc, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ table_base = FT_STREAM_POS();
+ if ( FT_FRAME_ENTER( 8L ) )
+ goto Exit;
+
+ version = FT_GET_LONG();
+ num_strikes = FT_GET_ULONG();
+
+ FT_FRAME_EXIT();
+
+ /* check version number and strike count */
+ if ( version != 0x00020000L ||
+ num_strikes >= 0x10000L )
+ {
+ FT_ERROR(( "tt_face_load_sbit_strikes: invalid table version!\n" ));
+ error = SFNT_Err_Invalid_File_Format;
+
+ goto Exit;
+ }
+
+ /* allocate the strikes table */
+ if ( FT_NEW_ARRAY( face->sbit_strikes, num_strikes ) )
+ goto Exit;
+
+ face->num_sbit_strikes = num_strikes;
+
+ /* now read each strike table separately */
+ {
+ TT_SBit_Strike strike = face->sbit_strikes;
+ FT_ULong count = num_strikes;
+
+
+ if ( FT_FRAME_ENTER( 48L * num_strikes ) )
+ goto Exit;
+
+ while ( count > 0 )
+ {
+ if ( FT_STREAM_READ_FIELDS( strike_start_fields, strike ) ||
+ FT_STREAM_READ_FIELDS( sbit_line_metrics_fields, &strike->hori ) ||
+ FT_STREAM_READ_FIELDS( sbit_line_metrics_fields, &strike->vert ) ||
+ FT_STREAM_READ_FIELDS( strike_end_fields, strike ) )
+ break;
+
+ count--;
+ strike++;
+ }
+
+ FT_FRAME_EXIT();
+ }
+
+ /* allocate the index ranges for each strike table */
+ {
+ TT_SBit_Strike strike = face->sbit_strikes;
+ FT_ULong count = num_strikes;
+
+
+ while ( count > 0 )
+ {
+ TT_SBit_Range range;
+ FT_ULong count2 = strike->num_ranges;
+
+
+ /* read each range */
+ if ( FT_STREAM_SEEK( table_base + strike->ranges_offset ) ||
+ FT_FRAME_ENTER( strike->num_ranges * 8L ) )
+ goto Exit;
+
+ if ( FT_NEW_ARRAY( strike->sbit_ranges, strike->num_ranges ) )
+ goto Exit;
+
+ range = strike->sbit_ranges;
+ while ( count2 > 0 )
+ {
+ range->first_glyph = FT_GET_USHORT();
+ range->last_glyph = FT_GET_USHORT();
+ range->table_offset = table_base + strike->ranges_offset +
+ FT_GET_ULONG();
+ count2--;
+ range++;
+ }
+
+ FT_FRAME_EXIT();
+
+ /* Now, read each index table */
+ count2 = strike->num_ranges;
+ range = strike->sbit_ranges;
+ while ( count2 > 0 )
+ {
+ /* Read the header */
+ if ( FT_STREAM_SEEK( range->table_offset ) ||
+ FT_FRAME_ENTER( 8L ) )
+ goto Exit;
+
+ range->index_format = FT_GET_USHORT();
+ range->image_format = FT_GET_USHORT();
+ range->image_offset = FT_GET_ULONG();
+
+ FT_FRAME_EXIT();
+
+ error = Load_SBit_Range( range, stream );
+ if ( error )
+ goto Exit;
+
+ count2--;
+ range++;
+ }
+
+ count--;
+ strike++;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_free_eblc */
+ /* */
+ /* <Description> */
+ /* Releases the embedded bitmap tables. */
+ /* */
+ /* <Input> */
+ /* face :: The target face object. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_face_free_eblc( TT_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ TT_SBit_Strike strike = face->sbit_strikes;
+ TT_SBit_Strike strike_limit = strike + face->num_sbit_strikes;
+
+
+ if ( strike )
+ {
+ for ( ; strike < strike_limit; strike++ )
+ {
+ TT_SBit_Range range = strike->sbit_ranges;
+ TT_SBit_Range range_limit = range + strike->num_ranges;
+
+
+ if ( range )
+ {
+ for ( ; range < range_limit; range++ )
+ {
+ /* release the glyph offsets and codes tables */
+ /* where appropriate */
+ FT_FREE( range->glyph_offsets );
+ FT_FREE( range->glyph_codes );
+ }
+ }
+ FT_FREE( strike->sbit_ranges );
+ strike->num_ranges = 0;
+ }
+ FT_FREE( face->sbit_strikes );
+ }
+ face->num_sbit_strikes = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_set_sbit_strike( TT_Face face,
+ FT_Size_Request req,
+ FT_ULong* astrike_index )
+ {
+ return FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_strike_metrics( TT_Face face,
+ FT_ULong strike_index,
+ FT_Size_Metrics* metrics )
+ {
+ TT_SBit_Strike strike;
+
+
+ if ( strike_index >= face->num_sbit_strikes )
+ return SFNT_Err_Invalid_Argument;
+
+ strike = face->sbit_strikes + strike_index;
+
+ metrics->x_ppem = strike->x_ppem;
+ metrics->y_ppem = strike->y_ppem;
+
+ metrics->ascender = strike->hori.ascender << 6;
+ metrics->descender = strike->hori.descender << 6;
+
+ /* XXX: Is this correct? */
+ metrics->max_advance = ( strike->hori.min_origin_SB +
+ strike->hori.max_width +
+ strike->hori.min_advance_SB ) << 6;
+
+ metrics->height = metrics->ascender - metrics->descender;
+
+ return SFNT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* find_sbit_range */
+ /* */
+ /* <Description> */
+ /* Scans a given strike's ranges and return, for a given glyph */
+ /* index, the corresponding sbit range, and `EBDT' offset. */
+ /* */
+ /* <Input> */
+ /* glyph_index :: The glyph index. */
+ /* */
+ /* strike :: The source/current sbit strike. */
+ /* */
+ /* <Output> */
+ /* arange :: The sbit range containing the glyph index. */
+ /* */
+ /* aglyph_offset :: The offset of the glyph data in `EBDT' table. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means the glyph index was found. */
+ /* */
+ static FT_Error
+ find_sbit_range( FT_UInt glyph_index,
+ TT_SBit_Strike strike,
+ TT_SBit_Range *arange,
+ FT_ULong *aglyph_offset )
+ {
+ TT_SBit_RangeRec *range, *range_limit;
+
+
+ /* check whether the glyph index is within this strike's */
+ /* glyph range */
+ if ( glyph_index < (FT_UInt)strike->start_glyph ||
+ glyph_index > (FT_UInt)strike->end_glyph )
+ goto Fail;
+
+ /* scan all ranges in strike */
+ range = strike->sbit_ranges;
+ range_limit = range + strike->num_ranges;
+ if ( !range )
+ goto Fail;
+
+ for ( ; range < range_limit; range++ )
+ {
+ if ( glyph_index >= (FT_UInt)range->first_glyph &&
+ glyph_index <= (FT_UInt)range->last_glyph )
+ {
+ FT_UShort delta = (FT_UShort)( glyph_index - range->first_glyph );
+
+
+ switch ( range->index_format )
+ {
+ case 1:
+ case 3:
+ *aglyph_offset = range->glyph_offsets[delta];
+ break;
+
+ case 2:
+ *aglyph_offset = range->image_offset +
+ range->image_size * delta;
+ break;
+
+ case 4:
+ case 5:
+ {
+ FT_ULong n;
+
+
+ for ( n = 0; n < range->num_glyphs; n++ )
+ {
+ if ( (FT_UInt)range->glyph_codes[n] == glyph_index )
+ {
+ if ( range->index_format == 4 )
+ *aglyph_offset = range->glyph_offsets[n];
+ else
+ *aglyph_offset = range->image_offset +
+ n * range->image_size;
+ goto Found;
+ }
+ }
+ }
+
+ /* fall-through */
+ default:
+ goto Fail;
+ }
+
+ Found:
+ /* return successfully! */
+ *arange = range;
+ return 0;
+ }
+ }
+
+ Fail:
+ *arange = 0;
+ *aglyph_offset = 0;
+
+ return SFNT_Err_Invalid_Argument;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_find_sbit_image */
+ /* */
+ /* <Description> */
+ /* Checks whether an embedded bitmap (an `sbit') exists for a given */
+ /* glyph, at a given strike. */
+ /* */
+ /* <Input> */
+ /* face :: The target face object. */
+ /* */
+ /* glyph_index :: The glyph index. */
+ /* */
+ /* strike_index :: The current strike index. */
+ /* */
+ /* <Output> */
+ /* arange :: The SBit range containing the glyph index. */
+ /* */
+ /* astrike :: The SBit strike containing the glyph index. */
+ /* */
+ /* aglyph_offset :: The offset of the glyph data in `EBDT' table. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. Returns */
+ /* SFNT_Err_Invalid_Argument if no sbit exists for the requested */
+ /* glyph. */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_find_sbit_image( TT_Face face,
+ FT_UInt glyph_index,
+ FT_ULong strike_index,
+ TT_SBit_Range *arange,
+ TT_SBit_Strike *astrike,
+ FT_ULong *aglyph_offset )
+ {
+ FT_Error error;
+ TT_SBit_Strike strike;
+
+
+ if ( !face->sbit_strikes ||
+ ( face->num_sbit_strikes <= strike_index ) )
+ goto Fail;
+
+ strike = &face->sbit_strikes[strike_index];
+
+ error = find_sbit_range( glyph_index, strike,
+ arange, aglyph_offset );
+ if ( error )
+ goto Fail;
+
+ *astrike = strike;
+
+ return SFNT_Err_Ok;
+
+ Fail:
+ /* no embedded bitmap for this glyph in face */
+ *arange = 0;
+ *astrike = 0;
+ *aglyph_offset = 0;
+
+ return SFNT_Err_Invalid_Argument;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_load_sbit_metrics */
+ /* */
+ /* <Description> */
+ /* Gets the big metrics for a given SBit. */
+ /* */
+ /* <Input> */
+ /* stream :: The input stream. */
+ /* */
+ /* range :: The SBit range containing the glyph. */
+ /* */
+ /* <Output> */
+ /* big_metrics :: A big SBit metrics structure for the glyph. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The stream cursor must be positioned at the glyph's offset within */
+ /* the `EBDT' table before the call. */
+ /* */
+ /* If the image format uses variable metrics, the stream cursor is */
+ /* positioned just after the metrics header in the `EBDT' table on */
+ /* function exit. */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_load_sbit_metrics( FT_Stream stream,
+ TT_SBit_Range range,
+ TT_SBit_Metrics metrics )
+ {
+ FT_Error error = SFNT_Err_Ok;
+
+
+ switch ( range->image_format )
+ {
+ case 1:
+ case 2:
+ case 8:
+ /* variable small metrics */
+ {
+ TT_SBit_SmallMetricsRec smetrics;
+
+ static const FT_Frame_Field sbit_small_metrics_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_SmallMetricsRec
+
+ FT_FRAME_START( 5 ),
+ FT_FRAME_BYTE( height ),
+ FT_FRAME_BYTE( width ),
+ FT_FRAME_CHAR( bearingX ),
+ FT_FRAME_CHAR( bearingY ),
+ FT_FRAME_BYTE( advance ),
+ FT_FRAME_END
+ };
+
+
+ /* read small metrics */
+ if ( FT_STREAM_READ_FIELDS( sbit_small_metrics_fields, &smetrics ) )
+ goto Exit;
+
+ /* convert it to a big metrics */
+ metrics->height = smetrics.height;
+ metrics->width = smetrics.width;
+ metrics->horiBearingX = smetrics.bearingX;
+ metrics->horiBearingY = smetrics.bearingY;
+ metrics->horiAdvance = smetrics.advance;
+
+ /* these metrics are made up at a higher level when */
+ /* needed. */
+ metrics->vertBearingX = 0;
+ metrics->vertBearingY = 0;
+ metrics->vertAdvance = 0;
+ }
+ break;
+
+ case 6:
+ case 7:
+ case 9:
+ /* variable big metrics */
+ if ( FT_STREAM_READ_FIELDS( sbit_metrics_fields, metrics ) )
+ goto Exit;
+ break;
+
+ case 5:
+ default: /* constant metrics */
+ if ( range->index_format == 2 || range->index_format == 5 )
+ *metrics = range->metrics;
+ else
+ return SFNT_Err_Invalid_File_Format;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* crop_bitmap */
+ /* */
+ /* <Description> */
+ /* Crops a bitmap to its tightest bounding box, and adjusts its */
+ /* metrics. */
+ /* */
+ /* <InOut> */
+ /* map :: The bitmap. */
+ /* */
+ /* metrics :: The corresponding metrics structure. */
+ /* */
+ static void
+ crop_bitmap( FT_Bitmap* map,
+ TT_SBit_Metrics metrics )
+ {
+ /***********************************************************************/
+ /* */
+ /* In this situation, some bounding boxes of embedded bitmaps are too */
+ /* large. We need to crop it to a reasonable size. */
+ /* */
+ /* --------- */
+ /* | | ----- */
+ /* | *** | |***| */
+ /* | * | | * | */
+ /* | * | ------> | * | */
+ /* | * | | * | */
+ /* | * | | * | */
+ /* | *** | |***| */
+ /* --------- ----- */
+ /* */
+ /***********************************************************************/
+
+ FT_Int rows, count;
+ FT_Long line_len;
+ FT_Byte* line;
+
+
+ /***********************************************************************/
+ /* */
+ /* first of all, check the top-most lines of the bitmap, and remove */
+ /* them if they're empty. */
+ /* */
+ {
+ line = (FT_Byte*)map->buffer;
+ rows = map->rows;
+ line_len = map->pitch;
+
+
+ for ( count = 0; count < rows; count++ )
+ {
+ FT_Byte* cur = line;
+ FT_Byte* limit = line + line_len;
+
+
+ for ( ; cur < limit; cur++ )
+ if ( cur[0] )
+ goto Found_Top;
+
+ /* the current line was empty - skip to next one */
+ line = limit;
+ }
+
+ Found_Top:
+ /* check that we have at least one filled line */
+ if ( count >= rows )
+ goto Empty_Bitmap;
+
+ /* now, crop the empty upper lines */
+ if ( count > 0 )
+ {
+ line = (FT_Byte*)map->buffer;
+
+ FT_MEM_MOVE( line, line + count * line_len,
+ ( rows - count ) * line_len );
+
+ metrics->height = (FT_Byte)( metrics->height - count );
+ metrics->horiBearingY = (FT_Char)( metrics->horiBearingY - count );
+ metrics->vertBearingY = (FT_Char)( metrics->vertBearingY - count );
+
+ map->rows -= count;
+ rows -= count;
+ }
+ }
+
+ /***********************************************************************/
+ /* */
+ /* second, crop the lower lines */
+ /* */
+ {
+ line = (FT_Byte*)map->buffer + ( rows - 1 ) * line_len;
+
+ for ( count = 0; count < rows; count++ )
+ {
+ FT_Byte* cur = line;
+ FT_Byte* limit = line + line_len;
+
+
+ for ( ; cur < limit; cur++ )
+ if ( cur[0] )
+ goto Found_Bottom;
+
+ /* the current line was empty - skip to previous one */
+ line -= line_len;
+ }
+
+ Found_Bottom:
+ if ( count > 0 )
+ {
+ metrics->height = (FT_Byte)( metrics->height - count );
+ rows -= count;
+ map->rows -= count;
+ }
+ }
+
+ /***********************************************************************/
+ /* */
+ /* third, get rid of the space on the left side of the glyph */
+ /* */
+ do
+ {
+ FT_Byte* limit;
+
+
+ line = (FT_Byte*)map->buffer;
+ limit = line + rows * line_len;
+
+ for ( ; line < limit; line += line_len )
+ if ( line[0] & 0x80 )
+ goto Found_Left;
+
+ /* shift the whole glyph one pixel to the left */
+ line = (FT_Byte*)map->buffer;
+ limit = line + rows * line_len;
+
+ for ( ; line < limit; line += line_len )
+ {
+ FT_Int n, width = map->width;
+ FT_Byte old;
+ FT_Byte* cur = line;
+
+
+ old = (FT_Byte)(cur[0] << 1);
+ for ( n = 8; n < width; n += 8 )
+ {
+ FT_Byte val;
+
+
+ val = cur[1];
+ cur[0] = (FT_Byte)( old | ( val >> 7 ) );
+ old = (FT_Byte)( val << 1 );
+ cur++;
+ }
+ cur[0] = old;
+ }
+
+ map->width--;
+ metrics->horiBearingX++;
+ metrics->vertBearingX++;
+ metrics->width--;
+
+ } while ( map->width > 0 );
+
+ Found_Left:
+
+ /***********************************************************************/
+ /* */
+ /* finally, crop the bitmap width to get rid of the space on the right */
+ /* side of the glyph. */
+ /* */
+ do
+ {
+ FT_Int right = map->width - 1;
+ FT_Byte* limit;
+ FT_Byte mask;
+
+
+ line = (FT_Byte*)map->buffer + ( right >> 3 );
+ limit = line + rows * line_len;
+ mask = (FT_Byte)( 0x80 >> ( right & 7 ) );
+
+ for ( ; line < limit; line += line_len )
+ if ( line[0] & mask )
+ goto Found_Right;
+
+ /* crop the whole glyph to the right */
+ map->width--;
+ metrics->width--;
+
+ } while ( map->width > 0 );
+
+ Found_Right:
+ /* all right, the bitmap was cropped */
+ return;
+
+ Empty_Bitmap:
+ map->width = 0;
+ map->rows = 0;
+ map->pitch = 0;
+ map->pixel_mode = FT_PIXEL_MODE_MONO;
+ }
+
+
+ static FT_Error
+ Load_SBit_Single( FT_Bitmap* map,
+ FT_Int x_offset,
+ FT_Int y_offset,
+ FT_Int pix_bits,
+ FT_UShort image_format,
+ TT_SBit_Metrics metrics,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ /* check that the source bitmap fits into the target pixmap */
+ if ( x_offset < 0 || x_offset + metrics->width > map->width ||
+ y_offset < 0 || y_offset + metrics->height > map->rows )
+ {
+ error = SFNT_Err_Invalid_Argument;
+
+ goto Exit;
+ }
+
+ {
+ FT_Int glyph_width = metrics->width;
+ FT_Int glyph_height = metrics->height;
+ FT_Int glyph_size;
+ FT_Int line_bits = pix_bits * glyph_width;
+ FT_Bool pad_bytes = 0;
+
+
+ /* compute size of glyph image */
+ switch ( image_format )
+ {
+ case 1: /* byte-padded formats */
+ case 6:
+ {
+ FT_Int line_length;
+
+
+ switch ( pix_bits )
+ {
+ case 1:
+ line_length = ( glyph_width + 7 ) >> 3;
+ break;
+ case 2:
+ line_length = ( glyph_width + 3 ) >> 2;
+ break;
+ case 4:
+ line_length = ( glyph_width + 1 ) >> 1;
+ break;
+ default:
+ line_length = glyph_width;
+ }
+
+ glyph_size = glyph_height * line_length;
+ pad_bytes = 1;
+ }
+ break;
+
+ case 2:
+ case 5:
+ case 7:
+ line_bits = glyph_width * pix_bits;
+ glyph_size = ( glyph_height * line_bits + 7 ) >> 3;
+ break;
+
+ default: /* invalid format */
+ return SFNT_Err_Invalid_File_Format;
+ }
+
+ /* Now read data and draw glyph into target pixmap */
+ if ( FT_FRAME_ENTER( glyph_size ) )
+ goto Exit;
+
+ /* don't forget to multiply `x_offset' by `map->pix_bits' as */
+ /* the sbit blitter doesn't make a difference between pixmap */
+ /* depths. */
+ blit_sbit( map, (FT_Byte*)stream->cursor, line_bits, pad_bytes,
+ x_offset * pix_bits, y_offset );
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ Load_SBit_Image( TT_SBit_Strike strike,
+ TT_SBit_Range range,
+ FT_ULong ebdt_pos,
+ FT_ULong glyph_offset,
+ FT_GlyphSlot slot,
+ FT_Int x_offset,
+ FT_Int y_offset,
+ FT_Stream stream,
+ TT_SBit_Metrics metrics,
+ FT_Int depth )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Bitmap* map = &slot->bitmap;
+ FT_Error error;
+
+
+ /* place stream at beginning of glyph data and read metrics */
+ if ( FT_STREAM_SEEK( ebdt_pos + glyph_offset ) )
+ goto Exit;
+
+ error = tt_load_sbit_metrics( stream, range, metrics );
+ if ( error )
+ goto Exit;
+
+ /* This function is recursive. At the top-level call, we */
+ /* compute the dimensions of the higher-level glyph to */
+ /* allocate the final pixmap buffer. */
+ if ( depth == 0 )
+ {
+ FT_Long size;
+
+
+ map->width = metrics->width;
+ map->rows = metrics->height;
+
+ switch ( strike->bit_depth )
+ {
+ case 1:
+ map->pixel_mode = FT_PIXEL_MODE_MONO;
+ map->pitch = ( map->width + 7 ) >> 3;
+ break;
+
+ case 2:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY2;
+ map->pitch = ( map->width + 3 ) >> 2;
+ break;
+
+ case 4:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY4;
+ map->pitch = ( map->width + 1 ) >> 1;
+ break;
+
+ case 8:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY;
+ map->pitch = map->width;
+ break;
+
+ default:
+ return SFNT_Err_Invalid_File_Format;
+ }
+
+ size = map->rows * map->pitch;
+
+ /* check that there is no empty image */
+ if ( size == 0 )
+ goto Exit; /* exit successfully! */
+
+ error = ft_glyphslot_alloc_bitmap( slot, size );
+ if (error)
+ goto Exit;
+ }
+
+ switch ( range->image_format )
+ {
+ case 1: /* single sbit image - load it */
+ case 2:
+ case 5:
+ case 6:
+ case 7:
+ return Load_SBit_Single( map, x_offset, y_offset, strike->bit_depth,
+ range->image_format, metrics, stream );
+
+ case 8: /* compound format */
+ FT_Stream_Skip( stream, 1L );
+ /* fallthrough */
+
+ case 9:
+ break;
+
+ default: /* invalid image format */
+ return SFNT_Err_Invalid_File_Format;
+ }
+
+ /* All right, we have a compound format. First of all, read */
+ /* the array of elements. */
+ {
+ TT_SBit_Component components;
+ TT_SBit_Component comp;
+ FT_UShort num_components, count;
+
+
+ if ( FT_READ_USHORT( num_components ) ||
+ FT_NEW_ARRAY( components, num_components ) )
+ goto Exit;
+
+ count = num_components;
+
+ if ( FT_FRAME_ENTER( 4L * num_components ) )
+ goto Fail_Memory;
+
+ for ( comp = components; count > 0; count--, comp++ )
+ {
+ comp->glyph_code = FT_GET_USHORT();
+ comp->x_offset = FT_GET_CHAR();
+ comp->y_offset = FT_GET_CHAR();
+ }
+
+ FT_FRAME_EXIT();
+
+ /* Now recursively load each element glyph */
+ count = num_components;
+ comp = components;
+ for ( ; count > 0; count--, comp++ )
+ {
+ TT_SBit_Range elem_range;
+ TT_SBit_MetricsRec elem_metrics;
+ FT_ULong elem_offset;
+
+
+ /* find the range for this element */
+ error = find_sbit_range( comp->glyph_code,
+ strike,
+ &elem_range,
+ &elem_offset );
+ if ( error )
+ goto Fail_Memory;
+
+ /* now load the element, recursively */
+ error = Load_SBit_Image( strike,
+ elem_range,
+ ebdt_pos,
+ elem_offset,
+ slot,
+ x_offset + comp->x_offset,
+ y_offset + comp->y_offset,
+ stream,
+ &elem_metrics,
+ depth + 1 );
+ if ( error )
+ goto Fail_Memory;
+ }
+
+ Fail_Memory:
+ FT_FREE( components );
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_sbit_image */
+ /* */
+ /* <Description> */
+ /* Loads a given glyph sbit image from the font resource. This also */
+ /* returns its metrics. */
+ /* */
+ /* <Input> */
+ /* face :: The target face object. */
+ /* */
+ /* strike_index :: The current strike index. */
+ /* */
+ /* glyph_index :: The current glyph index. */
+ /* */
+ /* load_flags :: The glyph load flags (the code checks for the flag */
+ /* FT_LOAD_CROP_BITMAP). */
+ /* */
+ /* stream :: The input stream. */
+ /* */
+ /* <Output> */
+ /* map :: The target pixmap. */
+ /* */
+ /* metrics :: A big sbit metrics structure for the glyph image. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. Returns an error if no */
+ /* glyph sbit exists for the index. */
+ /* */
+ /* <Note> */
+ /* The `map.buffer' field is always freed before the glyph is loaded. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_sbit_image( TT_Face face,
+ FT_ULong strike_index,
+ FT_UInt glyph_index,
+ FT_UInt load_flags,
+ FT_Stream stream,
+ FT_Bitmap *map,
+ TT_SBit_MetricsRec *metrics )
+ {
+ FT_Error error;
+ FT_ULong ebdt_pos, glyph_offset;
+
+ TT_SBit_Strike strike;
+ TT_SBit_Range range;
+
+
+ /* Check whether there is a glyph sbit for the current index */
+ error = tt_find_sbit_image( face, glyph_index, strike_index,
+ &range, &strike, &glyph_offset );
+ if ( error )
+ goto Exit;
+
+ /* now, find the location of the `EBDT' table in */
+ /* the font file */
+ error = face->goto_table( face, TTAG_EBDT, stream, 0 );
+ if ( error )
+ error = face->goto_table( face, TTAG_bdat, stream, 0 );
+ if ( error )
+ goto Exit;
+
+ ebdt_pos = FT_STREAM_POS();
+
+ error = Load_SBit_Image( strike, range, ebdt_pos, glyph_offset,
+ face->root.glyph, 0, 0, stream, metrics, 0 );
+ if ( error )
+ goto Exit;
+
+ /* setup vertical metrics if needed */
+ if ( strike->flags & 1 )
+ {
+ /* in case of a horizontal strike only */
+ FT_Int advance;
+
+
+ advance = strike->hori.ascender - strike->hori.descender;
+
+ /* some heuristic values */
+
+ metrics->vertBearingX = (FT_Char)(-metrics->width / 2 );
+ metrics->vertBearingY = (FT_Char)( ( advance - metrics->height ) / 2 );
+ metrics->vertAdvance = (FT_Char)( advance * 12 / 10 );
+ }
+
+ /* Crop the bitmap now, unless specified otherwise */
+ if ( load_flags & FT_LOAD_CROP_BITMAP )
+ crop_bitmap( map, metrics );
+
+ Exit:
+ return error;
+ }
+
+#endif /* !FT_CONFIG_OPTION_OLD_INTERNALS */
+
+
+/* END */
diff --git a/src/sfnt/ttsbit.h b/src/sfnt/ttsbit.h
new file mode 100644
index 0000000..c6067c0
--- /dev/null
+++ b/src/sfnt/ttsbit.h
@@ -0,0 +1,79 @@
+/***************************************************************************/
+/* */
+/* ttsbit.h */
+/* */
+/* TrueType and OpenType embedded bitmap support (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTSBIT_H__
+#define __TTSBIT_H__
+
+
+#include <ft2build.h>
+#include "ttload.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_eblc( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ tt_face_free_eblc( TT_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_set_sbit_strike( TT_Face face,
+ FT_Size_Request req,
+ FT_ULong* astrike_index );
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_strike_metrics( TT_Face face,
+ FT_ULong strike_index,
+ FT_Size_Metrics* metrics );
+
+#if defined FT_CONFIG_OPTION_OLD_INTERNALS
+ FT_LOCAL( FT_Error )
+ tt_find_sbit_image( TT_Face face,
+ FT_UInt glyph_index,
+ FT_ULong strike_index,
+ TT_SBit_Range *arange,
+ TT_SBit_Strike *astrike,
+ FT_ULong *aglyph_offset );
+
+ FT_LOCAL( FT_Error )
+ tt_load_sbit_metrics( FT_Stream stream,
+ TT_SBit_Range range,
+ TT_SBit_Metrics metrics );
+
+#endif /* FT_CONFIG_OPTION_OLD_INTERNALS */
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_sbit_image( TT_Face face,
+ FT_ULong strike_index,
+ FT_UInt glyph_index,
+ FT_UInt load_flags,
+ FT_Stream stream,
+ FT_Bitmap *map,
+ TT_SBit_MetricsRec *metrics );
+
+
+FT_END_HEADER
+
+#endif /* __TTSBIT_H__ */
+
+
+/* END */
diff --git a/src/sfnt/ttsbit0.c b/src/sfnt/ttsbit0.c
new file mode 100644
index 0000000..f8adc64
--- /dev/null
+++ b/src/sfnt/ttsbit0.c
@@ -0,0 +1,996 @@
+/***************************************************************************/
+/* */
+/* ttsbit0.c */
+/* */
+/* TrueType and OpenType embedded bitmap support (body). */
+/* This is a heap-optimized version. */
+/* */
+/* Copyright 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+/* This file is included by ttsbit.c */
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttsbit.h"
+
+#include "sferrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttsbit
+
+
+ static const FT_Frame_Field tt_sbit_line_metrics_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_LineMetricsRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_CHAR( ascender ),
+ FT_FRAME_CHAR( descender ),
+ FT_FRAME_BYTE( max_width ),
+
+ FT_FRAME_CHAR( caret_slope_numerator ),
+ FT_FRAME_CHAR( caret_slope_denominator ),
+ FT_FRAME_CHAR( caret_offset ),
+
+ FT_FRAME_CHAR( min_origin_SB ),
+ FT_FRAME_CHAR( min_advance_SB ),
+ FT_FRAME_CHAR( max_before_BL ),
+ FT_FRAME_CHAR( min_after_BL ),
+ FT_FRAME_CHAR( pads[0] ),
+ FT_FRAME_CHAR( pads[1] ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field tt_strike_start_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE TT_SBit_StrikeRec
+
+ /* no FT_FRAME_START */
+ FT_FRAME_ULONG( ranges_offset ),
+ FT_FRAME_SKIP_LONG,
+ FT_FRAME_ULONG( num_ranges ),
+ FT_FRAME_ULONG( color_ref ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field tt_strike_end_fields[] =
+ {
+ /* no FT_FRAME_START */
+ FT_FRAME_USHORT( start_glyph ),
+ FT_FRAME_USHORT( end_glyph ),
+ FT_FRAME_BYTE ( x_ppem ),
+ FT_FRAME_BYTE ( y_ppem ),
+ FT_FRAME_BYTE ( bit_depth ),
+ FT_FRAME_CHAR ( flags ),
+ FT_FRAME_END
+ };
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_eblc( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_Fixed version;
+ FT_ULong num_strikes, table_size;
+ FT_Byte* p;
+ FT_Byte* p_limit;
+ FT_UInt count;
+
+
+ face->sbit_num_strikes = 0;
+
+ /* this table is optional */
+ error = face->goto_table( face, TTAG_EBLC, stream, &table_size );
+ if ( error )
+ error = face->goto_table( face, TTAG_bloc, stream, &table_size );
+ if ( error )
+ goto Exit;
+
+ if ( table_size < 8 )
+ {
+ FT_ERROR(( "%s: table too short!\n", "tt_face_load_sbit_strikes" ));
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( FT_FRAME_EXTRACT( table_size, face->sbit_table ) )
+ goto Exit;
+
+ face->sbit_table_size = table_size;
+
+ p = face->sbit_table;
+ p_limit = p + table_size;
+
+ version = FT_NEXT_ULONG( p );
+ num_strikes = FT_NEXT_ULONG( p );
+
+ if ( version != 0x00020000UL || num_strikes >= 0x10000UL )
+ {
+ FT_ERROR(( "%s: invalid table version!\n",
+ "tt_face_load_sbit_strikes" ));
+ error = SFNT_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /*
+ * Count the number of strikes available in the table. We are a bit
+ * paranoid there and don't trust the data.
+ */
+ count = (FT_UInt)num_strikes;
+ if ( 8 + 48UL * count > table_size )
+ count = (FT_UInt)( ( p_limit - p ) / 48 );
+
+ face->sbit_num_strikes = count;
+
+ FT_TRACE3(( "sbit_num_strikes: %u\n", count ));
+ Exit:
+ return error;
+
+ Fail:
+ FT_FRAME_RELEASE( face->sbit_table );
+ face->sbit_table_size = 0;
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_eblc( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+
+
+ FT_FRAME_RELEASE( face->sbit_table );
+ face->sbit_table_size = 0;
+ face->sbit_num_strikes = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_set_sbit_strike( TT_Face face,
+ FT_Size_Request req,
+ FT_ULong* astrike_index )
+ {
+ return FT_Match_Size( (FT_Face)face, req, 0, astrike_index );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_strike_metrics( TT_Face face,
+ FT_ULong strike_index,
+ FT_Size_Metrics* metrics )
+ {
+ FT_Byte* strike;
+
+
+ if ( strike_index >= (FT_ULong)face->sbit_num_strikes )
+ return SFNT_Err_Invalid_Argument;
+
+ strike = face->sbit_table + 8 + strike_index * 48;
+
+ metrics->x_ppem = (FT_UShort)strike[44];
+ metrics->y_ppem = (FT_UShort)strike[45];
+
+ metrics->ascender = (FT_Char)strike[16] << 6; /* hori.ascender */
+ metrics->descender = (FT_Char)strike[17] << 6; /* hori.descender */
+ metrics->height = metrics->ascender - metrics->descender;
+
+ /* XXX: Is this correct? */
+ metrics->max_advance = ( (FT_Char)strike[22] + /* min_origin_SB */
+ strike[18] + /* max_width */
+ (FT_Char)strike[23] /* min_advance_SB */
+ ) << 6;
+
+ return SFNT_Err_Ok;
+ }
+
+
+ typedef struct
+ {
+ TT_Face face;
+ FT_Stream stream;
+ FT_Bitmap* bitmap;
+ TT_SBit_Metrics metrics;
+ FT_Bool metrics_loaded;
+ FT_Bool bitmap_allocated;
+ FT_Byte bit_depth;
+
+ FT_ULong ebdt_start;
+ FT_ULong ebdt_size;
+
+ FT_ULong strike_index_array;
+ FT_ULong strike_index_count;
+ FT_Byte* eblc_base;
+ FT_Byte* eblc_limit;
+
+ } TT_SBitDecoderRec, *TT_SBitDecoder;
+
+
+ static FT_Error
+ tt_sbit_decoder_init( TT_SBitDecoder decoder,
+ TT_Face face,
+ FT_ULong strike_index,
+ TT_SBit_MetricsRec* metrics )
+ {
+ FT_Error error;
+ FT_Stream stream = face->root.stream;
+ FT_ULong ebdt_size;
+
+
+ error = face->goto_table( face, TTAG_EBDT, stream, &ebdt_size );
+ if ( error )
+ error = face->goto_table( face, TTAG_bdat, stream, &ebdt_size );
+ if ( error )
+ goto Exit;
+
+ decoder->face = face;
+ decoder->stream = stream;
+ decoder->bitmap = &face->root.glyph->bitmap;
+ decoder->metrics = metrics;
+
+ decoder->metrics_loaded = 0;
+ decoder->bitmap_allocated = 0;
+
+ decoder->ebdt_start = FT_STREAM_POS();
+ decoder->ebdt_size = ebdt_size;
+
+ decoder->eblc_base = face->sbit_table;
+ decoder->eblc_limit = face->sbit_table + face->sbit_table_size;
+
+ /* now find the strike corresponding to the index */
+ {
+ FT_Byte* p;
+
+
+ if ( 8 + 48 * strike_index + 3 * 4 + 34 + 1 > face->sbit_table_size )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ p = decoder->eblc_base + 8 + 48 * strike_index;
+
+ decoder->strike_index_array = FT_NEXT_ULONG( p );
+ p += 4;
+ decoder->strike_index_count = FT_NEXT_ULONG( p );
+ p += 34;
+ decoder->bit_depth = *p;
+
+ if ( decoder->strike_index_array > face->sbit_table_size ||
+ decoder->strike_index_array + 8 * decoder->strike_index_count >
+ face->sbit_table_size )
+ error = SFNT_Err_Invalid_File_Format;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ tt_sbit_decoder_done( TT_SBitDecoder decoder )
+ {
+ FT_UNUSED( decoder );
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_alloc_bitmap( TT_SBitDecoder decoder )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_UInt width, height;
+ FT_Bitmap* map = decoder->bitmap;
+ FT_Long size;
+
+
+ if ( !decoder->metrics_loaded )
+ {
+ error = SFNT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ width = decoder->metrics->width;
+ height = decoder->metrics->height;
+
+ map->width = (int)width;
+ map->rows = (int)height;
+
+ switch ( decoder->bit_depth )
+ {
+ case 1:
+ map->pixel_mode = FT_PIXEL_MODE_MONO;
+ map->pitch = ( map->width + 7 ) >> 3;
+ break;
+
+ case 2:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY2;
+ map->pitch = ( map->width + 3 ) >> 2;
+ break;
+
+ case 4:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY4;
+ map->pitch = ( map->width + 1 ) >> 1;
+ break;
+
+ case 8:
+ map->pixel_mode = FT_PIXEL_MODE_GRAY;
+ map->pitch = map->width;
+ break;
+
+ default:
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ size = map->rows * map->pitch;
+
+ /* check that there is no empty image */
+ if ( size == 0 )
+ goto Exit; /* exit successfully! */
+
+ error = ft_glyphslot_alloc_bitmap( decoder->face->root.glyph, size );
+ if ( error )
+ goto Exit;
+
+ decoder->bitmap_allocated = 1;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_load_metrics( TT_SBitDecoder decoder,
+ FT_Byte* *pp,
+ FT_Byte* limit,
+ FT_Bool big )
+ {
+ FT_Byte* p = *pp;
+ TT_SBit_Metrics metrics = decoder->metrics;
+
+
+ if ( p + 5 > limit )
+ goto Fail;
+
+ if ( !decoder->metrics_loaded )
+ {
+ metrics->height = p[0];
+ metrics->width = p[1];
+ metrics->horiBearingX = (FT_Char)p[2];
+ metrics->horiBearingY = (FT_Char)p[3];
+ metrics->horiAdvance = p[4];
+ }
+
+ p += 5;
+ if ( big )
+ {
+ if ( p + 3 > limit )
+ goto Fail;
+
+ if ( !decoder->metrics_loaded )
+ {
+ metrics->vertBearingX = (FT_Char)p[0];
+ metrics->vertBearingY = (FT_Char)p[1];
+ metrics->vertAdvance = p[2];
+ }
+
+ p += 3;
+ }
+
+ decoder->metrics_loaded = 1;
+ *pp = p;
+ return 0;
+
+ Fail:
+ return SFNT_Err_Invalid_Argument;
+ }
+
+
+ /* forward declaration */
+ static FT_Error
+ tt_sbit_decoder_load_image( TT_SBitDecoder decoder,
+ FT_UInt glyph_index,
+ FT_Int x_pos,
+ FT_Int y_pos );
+
+ typedef FT_Error (*TT_SBitDecoder_LoadFunc)( TT_SBitDecoder decoder,
+ FT_Byte* p,
+ FT_Byte* plimit,
+ FT_Int x_pos,
+ FT_Int y_pos );
+
+
+ static FT_Error
+ tt_sbit_decoder_load_byte_aligned( TT_SBitDecoder decoder,
+ FT_Byte* p,
+ FT_Byte* limit,
+ FT_Int x_pos,
+ FT_Int y_pos )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_Byte* line;
+ FT_Int bit_height, bit_width, pitch, width, height, h;
+ FT_Bitmap* bitmap;
+
+
+ if ( !decoder->bitmap_allocated )
+ {
+ error = tt_sbit_decoder_alloc_bitmap( decoder );
+ if ( error )
+ goto Exit;
+ }
+
+ /* check that we can write the glyph into the bitmap */
+ bitmap = decoder->bitmap;
+ bit_width = bitmap->width;
+ bit_height = bitmap->rows;
+ pitch = bitmap->pitch;
+ line = bitmap->buffer;
+
+ width = decoder->metrics->width;
+ height = decoder->metrics->height;
+
+ if ( x_pos < 0 || x_pos + width > bit_width ||
+ y_pos < 0 || y_pos + height > bit_height )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( p + ( ( width + 7 ) >> 3 ) * height > limit )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* now do the blit */
+ line += y_pos * pitch + ( x_pos >> 3 );
+ x_pos &= 7;
+
+ if ( x_pos == 0 ) /* the easy one */
+ {
+ for ( h = height; h > 0; h--, line += pitch )
+ {
+ FT_Byte* write = line;
+ FT_Int w;
+
+
+ for ( w = width; w >= 8; w -= 8 )
+ {
+ write[0] = (FT_Byte)( write[0] | *p++ );
+ write += 1;
+ }
+
+ if ( w > 0 )
+ write[0] = (FT_Byte)( write[0] | ( *p++ & ( 0xFF00U >> w ) ) );
+ }
+ }
+ else /* x_pos > 0 */
+ {
+ for ( h = height; h > 0; h--, line += pitch )
+ {
+ FT_Byte* write = line;
+ FT_Int w;
+ FT_UInt wval = 0;
+
+
+ for ( w = width; w >= 8; w -= 8 )
+ {
+ wval = (FT_UInt)( wval | *p++ );
+ write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) );
+ write += 1;
+ wval <<= 8;
+ }
+
+ if ( w > 0 )
+ wval = (FT_UInt)( wval | ( *p++ & ( 0xFF00U >> w ) ) );
+
+ /* all bits read and there are ( x_pos + w ) bits to be written */
+
+ write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) );
+
+ if ( x_pos + w > 8 )
+ {
+ write++;
+ wval <<= 8;
+ write[0] = (FT_Byte)( write[0] | ( wval >> x_pos ) );
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_load_bit_aligned( TT_SBitDecoder decoder,
+ FT_Byte* p,
+ FT_Byte* limit,
+ FT_Int x_pos,
+ FT_Int y_pos )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_Byte* line;
+ FT_Int bit_height, bit_width, pitch, width, height, h, nbits;
+ FT_Bitmap* bitmap;
+ FT_UShort rval;
+
+
+ if ( !decoder->bitmap_allocated )
+ {
+ error = tt_sbit_decoder_alloc_bitmap( decoder );
+ if ( error )
+ goto Exit;
+ }
+
+ /* check that we can write the glyph into the bitmap */
+ bitmap = decoder->bitmap;
+ bit_width = bitmap->width;
+ bit_height = bitmap->rows;
+ pitch = bitmap->pitch;
+ line = bitmap->buffer;
+
+ width = decoder->metrics->width;
+ height = decoder->metrics->height;
+
+ if ( x_pos < 0 || x_pos + width > bit_width ||
+ y_pos < 0 || y_pos + height > bit_height )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( p + ( ( width * height + 7 ) >> 3 ) > limit )
+ {
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* now do the blit */
+ line += y_pos * pitch + ( x_pos >> 3 );
+ x_pos &= 7;
+
+ /* the higher byte of `rval' is used as a buffer */
+ rval = 0;
+ nbits = 0;
+
+ for ( h = height; h > 0; h--, line += pitch )
+ {
+ FT_Byte* write = line;
+ FT_Int w = width;
+
+
+ if ( x_pos )
+ {
+ w = ( width < 8 - x_pos ) ? width : 8 - x_pos;
+
+ if ( nbits < w )
+ {
+ rval |= *p++;
+ nbits += 8 - w;
+ }
+ else
+ {
+ rval >>= 8;
+ nbits -= w;
+ }
+
+ *write++ |= ( ( rval >> nbits ) & 0xFF ) & ~( 0xFF << w );
+ rval <<= 8;
+
+ w = width - w;
+ }
+
+ for ( ; w >= 8; w -= 8 )
+ {
+ rval |= *p++;
+ *write++ |= ( rval >> nbits ) & 0xFF;
+
+ rval <<= 8;
+ }
+
+ if ( w > 0 )
+ {
+ if ( nbits < w )
+ {
+ rval |= *p++;
+ *write |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w );
+ nbits += 8 - w;
+
+ rval <<= 8;
+ }
+ else
+ {
+ *write |= ( ( rval >> nbits ) & 0xFF ) & ( 0xFF00U >> w );
+ nbits -= w;
+ }
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_load_compound( TT_SBitDecoder decoder,
+ FT_Byte* p,
+ FT_Byte* limit,
+ FT_Int x_pos,
+ FT_Int y_pos )
+ {
+ FT_Error error = SFNT_Err_Ok;
+ FT_UInt num_components, nn;
+
+
+ if ( p + 2 > limit )
+ goto Fail;
+
+ num_components = FT_NEXT_USHORT( p );
+ if ( p + 4 * num_components > limit )
+ goto Fail;
+
+ for ( nn = 0; nn < num_components; nn++ )
+ {
+ FT_UInt gindex = FT_NEXT_USHORT( p );
+ FT_Byte dx = FT_NEXT_BYTE( p );
+ FT_Byte dy = FT_NEXT_BYTE( p );
+
+
+ /* NB: a recursive call */
+ error = tt_sbit_decoder_load_image( decoder, gindex,
+ x_pos + dx, y_pos + dy );
+ if ( error )
+ break;
+ }
+
+ Exit:
+ return error;
+
+ Fail:
+ error = SFNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_load_bitmap( TT_SBitDecoder decoder,
+ FT_UInt glyph_format,
+ FT_ULong glyph_start,
+ FT_ULong glyph_size,
+ FT_Int x_pos,
+ FT_Int y_pos )
+ {
+ FT_Error error;
+ FT_Stream stream = decoder->stream;
+ FT_Byte* p;
+ FT_Byte* p_limit;
+ FT_Byte* data;
+
+
+ /* seek into the EBDT table now */
+ if ( glyph_start + glyph_size > decoder->ebdt_size )
+ {
+ error = SFNT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( FT_STREAM_SEEK( decoder->ebdt_start + glyph_start ) ||
+ FT_FRAME_EXTRACT( glyph_size, data ) )
+ goto Exit;
+
+ p = data;
+ p_limit = p + glyph_size;
+
+ /* read the data, depending on the glyph format */
+ switch ( glyph_format )
+ {
+ case 1:
+ case 2:
+ case 8:
+ error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 0 );
+ break;
+
+ case 6:
+ case 7:
+ case 9:
+ error = tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 );
+ break;
+
+ default:
+ error = SFNT_Err_Ok;
+ }
+
+ if ( error )
+ goto Fail;
+
+ {
+ TT_SBitDecoder_LoadFunc loader;
+
+
+ switch ( glyph_format )
+ {
+ case 1:
+ case 6:
+ loader = tt_sbit_decoder_load_byte_aligned;
+ break;
+
+ case 2:
+ case 5:
+ case 7:
+ loader = tt_sbit_decoder_load_bit_aligned;
+ break;
+
+ case 8:
+ if ( p + 1 > p_limit )
+ goto Fail;
+
+ p += 1; /* skip padding */
+ /* fall-through */
+
+ case 9:
+ loader = tt_sbit_decoder_load_compound;
+ break;
+
+ default:
+ goto Fail;
+ }
+
+ error = loader( decoder, p, p_limit, x_pos, y_pos );
+ }
+
+ Fail:
+ FT_FRAME_RELEASE( data );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ tt_sbit_decoder_load_image( TT_SBitDecoder decoder,
+ FT_UInt glyph_index,
+ FT_Int x_pos,
+ FT_Int y_pos )
+ {
+ /*
+ * First, we find the correct strike range that applies to this
+ * glyph index.
+ */
+
+ FT_Byte* p = decoder->eblc_base + decoder->strike_index_array;
+ FT_Byte* p_limit = decoder->eblc_limit;
+ FT_ULong num_ranges = decoder->strike_index_count;
+ FT_UInt start, end, index_format, image_format;
+ FT_ULong image_start = 0, image_end = 0, image_offset;
+
+
+ for ( ; num_ranges > 0; num_ranges-- )
+ {
+ start = FT_NEXT_USHORT( p );
+ end = FT_NEXT_USHORT( p );
+
+ if ( glyph_index >= start && glyph_index <= end )
+ goto FoundRange;
+
+ p += 4; /* ignore index offset */
+ }
+ goto NoBitmap;
+
+ FoundRange:
+ image_offset = FT_NEXT_ULONG( p );
+
+ /* overflow check */
+ if ( decoder->eblc_base + decoder->strike_index_array + image_offset <
+ decoder->eblc_base )
+ goto Failure;
+
+ p = decoder->eblc_base + decoder->strike_index_array + image_offset;
+ if ( p + 8 > p_limit )
+ goto NoBitmap;
+
+ /* now find the glyph's location and extend within the ebdt table */
+ index_format = FT_NEXT_USHORT( p );
+ image_format = FT_NEXT_USHORT( p );
+ image_offset = FT_NEXT_ULONG ( p );
+
+ switch ( index_format )
+ {
+ case 1: /* 4-byte offsets relative to `image_offset' */
+ {
+ p += 4 * ( glyph_index - start );
+ if ( p + 8 > p_limit )
+ goto NoBitmap;
+
+ image_start = FT_NEXT_ULONG( p );
+ image_end = FT_NEXT_ULONG( p );
+
+ if ( image_start == image_end ) /* missing glyph */
+ goto NoBitmap;
+ }
+ break;
+
+ case 2: /* big metrics, constant image size */
+ {
+ FT_ULong image_size;
+
+
+ if ( p + 12 > p_limit )
+ goto NoBitmap;
+
+ image_size = FT_NEXT_ULONG( p );
+
+ if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) )
+ goto NoBitmap;
+
+ image_start = image_size * ( glyph_index - start );
+ image_end = image_start + image_size;
+ }
+ break;
+
+ case 3: /* 2-byte offsets relative to 'image_offset' */
+ {
+ p += 2 * ( glyph_index - start );
+ if ( p + 4 > p_limit )
+ goto NoBitmap;
+
+ image_start = FT_NEXT_USHORT( p );
+ image_end = FT_NEXT_USHORT( p );
+
+ if ( image_start == image_end ) /* missing glyph */
+ goto NoBitmap;
+ }
+ break;
+
+ case 4: /* sparse glyph array with (glyph,offset) pairs */
+ {
+ FT_ULong mm, num_glyphs;
+
+
+ if ( p + 4 > p_limit )
+ goto NoBitmap;
+
+ num_glyphs = FT_NEXT_ULONG( p );
+
+ /* overflow check */
+ if ( p + ( num_glyphs + 1 ) * 4 < p )
+ goto Failure;
+
+ if ( p + ( num_glyphs + 1 ) * 4 > p_limit )
+ goto NoBitmap;
+
+ for ( mm = 0; mm < num_glyphs; mm++ )
+ {
+ FT_UInt gindex = FT_NEXT_USHORT( p );
+
+
+ if ( gindex == glyph_index )
+ {
+ image_start = FT_NEXT_USHORT( p );
+ p += 2;
+ image_end = FT_PEEK_USHORT( p );
+ break;
+ }
+ p += 2;
+ }
+
+ if ( mm >= num_glyphs )
+ goto NoBitmap;
+ }
+ break;
+
+ case 5: /* constant metrics with sparse glyph codes */
+ {
+ FT_ULong image_size, mm, num_glyphs;
+
+
+ if ( p + 16 > p_limit )
+ goto NoBitmap;
+
+ image_size = FT_NEXT_ULONG( p );
+
+ if ( tt_sbit_decoder_load_metrics( decoder, &p, p_limit, 1 ) )
+ goto NoBitmap;
+
+ num_glyphs = FT_NEXT_ULONG( p );
+
+ /* overflow check */
+ if ( p + 2 * num_glyphs < p )
+ goto Failure;
+
+ if ( p + 2 * num_glyphs > p_limit )
+ goto NoBitmap;
+
+ for ( mm = 0; mm < num_glyphs; mm++ )
+ {
+ FT_UInt gindex = FT_NEXT_USHORT( p );
+
+
+ if ( gindex == glyph_index )
+ break;
+ }
+
+ if ( mm >= num_glyphs )
+ goto NoBitmap;
+
+ image_start = image_size * mm;
+ image_end = image_start + image_size;
+ }
+ break;
+
+ default:
+ goto NoBitmap;
+ }
+
+ if ( image_start > image_end )
+ goto NoBitmap;
+
+ image_end -= image_start;
+ image_start = image_offset + image_start;
+
+ return tt_sbit_decoder_load_bitmap( decoder,
+ image_format,
+ image_start,
+ image_end,
+ x_pos,
+ y_pos );
+
+ Failure:
+ return SFNT_Err_Invalid_Table;
+
+ NoBitmap:
+ return SFNT_Err_Invalid_Argument;
+ }
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_sbit_image( TT_Face face,
+ FT_ULong strike_index,
+ FT_UInt glyph_index,
+ FT_UInt load_flags,
+ FT_Stream stream,
+ FT_Bitmap *map,
+ TT_SBit_MetricsRec *metrics )
+ {
+ TT_SBitDecoderRec decoder[1];
+ FT_Error error;
+
+ FT_UNUSED( load_flags );
+ FT_UNUSED( stream );
+ FT_UNUSED( map );
+
+
+ error = tt_sbit_decoder_init( decoder, face, strike_index, metrics );
+ if ( !error )
+ {
+ error = tt_sbit_decoder_load_image( decoder, glyph_index, 0, 0 );
+ tt_sbit_decoder_done( decoder );
+ }
+
+ return error;
+ }
+
+/* EOF */
diff --git a/src/smooth/Jamfile b/src/smooth/Jamfile
new file mode 100644
index 0000000..8a792df
--- /dev/null
+++ b/src/smooth/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/smooth Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) smooth ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = ftgrays ftsmooth ;
+ }
+ else
+ {
+ _sources = smooth ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/smooth Jamfile
diff --git a/src/smooth/ftgrays.c b/src/smooth/ftgrays.c
new file mode 100644
index 0000000..5a4a544
--- /dev/null
+++ b/src/smooth/ftgrays.c
@@ -0,0 +1,1983 @@
+/***************************************************************************/
+/* */
+/* ftgrays.c */
+/* */
+/* A new `perfect' anti-aliasing renderer (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This file can be compiled without the rest of the FreeType engine, by */
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */
+ /* put the files `ftgrays.h' and `ftimage.h' into the current */
+ /* compilation directory. Typically, you could do something like */
+ /* */
+ /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
+ /* */
+ /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
+ /* same directory */
+ /* */
+ /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
+ /* */
+ /* cc -c -D_STANDALONE_ ftgrays.c */
+ /* */
+ /* The renderer can be initialized with a call to */
+ /* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
+ /* with a call to `ft_gray_raster.raster_render'. */
+ /* */
+ /* See the comments and documentation in the file `ftimage.h' for more */
+ /* details on how the raster works. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* This is a new anti-aliasing scan-converter for FreeType 2. The */
+ /* algorithm used here is _very_ different from the one in the standard */
+ /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
+ /* coverage of the outline on each pixel cell. */
+ /* */
+ /* It is based on ideas that I initially found in Raph Levien's */
+ /* excellent LibArt graphics library (see http://www.levien.com/libart */
+ /* for more information, though the web pages do not tell anything */
+ /* about the renderer; you'll have to dive into the source code to */
+ /* understand how it works). */
+ /* */
+ /* Note, however, that this is a _very_ different implementation */
+ /* compared to Raph's. Coverage information is stored in a very */
+ /* different way, and I don't use sorted vector paths. Also, it doesn't */
+ /* use floating point values. */
+ /* */
+ /* This renderer has the following advantages: */
+ /* */
+ /* - It doesn't need an intermediate bitmap. Instead, one can supply a */
+ /* callback function that will be called by the renderer to draw gray */
+ /* spans on any target surface. You can thus do direct composition on */
+ /* any kind of bitmap, provided that you give the renderer the right */
+ /* callback. */
+ /* */
+ /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
+ /* each pixel cell. */
+ /* */
+ /* - It performs a single pass on the outline (the `standard' FT2 */
+ /* renderer makes two passes). */
+ /* */
+ /* - It can easily be modified to render to _any_ number of gray levels */
+ /* cheaply. */
+ /* */
+ /* - For small (< 20) pixel sizes, it is faster than the standard */
+ /* renderer. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_smooth
+
+
+
+
+#ifdef _STANDALONE_
+
+#include <string.h> /* for ft_memcpy() */
+#include <setjmp.h>
+#include <limits.h>
+#define FT_UINT_MAX UINT_MAX
+
+#define ft_memset memset
+
+#define ft_setjmp setjmp
+#define ft_longjmp longjmp
+#define ft_jmp_buf jmp_buf
+
+
+#define ErrRaster_Invalid_Mode -2
+#define ErrRaster_Invalid_Outline -1
+#define ErrRaster_Invalid_Argument -3
+#define ErrRaster_Memory_Overflow -4
+
+#define FT_BEGIN_HEADER
+#define FT_END_HEADER
+
+#include "ftimage.h"
+#include "ftgrays.h"
+
+ /* This macro is used to indicate that a function parameter is unused. */
+ /* Its purpose is simply to reduce compiler warnings. Note also that */
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */
+ /* ANSI compilers (e.g. LCC). */
+#define FT_UNUSED( x ) (x) = (x)
+
+ /* Disable the tracing mechanism for simplicity -- developers can */
+ /* activate it easily by redefining these two macros. */
+#ifndef FT_ERROR
+#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#ifndef FT_TRACE
+#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
+#endif
+
+#else /* !_STANDALONE_ */
+
+#include <ft2build.h>
+#include "ftgrays.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_OUTLINE_H
+
+#include "ftsmerrs.h"
+
+#define ErrRaster_Invalid_Mode Smooth_Err_Cannot_Render_Glyph
+#define ErrRaster_Invalid_Outline Smooth_Err_Invalid_Outline
+#define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory
+#define ErrRaster_Invalid_Argument Smooth_Err_Bad_Argument
+
+#endif /* !_STANDALONE_ */
+
+
+#ifndef FT_MEM_SET
+#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
+#endif
+
+#ifndef FT_MEM_ZERO
+#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
+#endif
+
+ /* define this to dump debugging information */
+#define xxxDEBUG_GRAYS
+
+
+ /* as usual, for the speed hungry :-) */
+
+#ifndef FT_STATIC_RASTER
+
+
+#define RAS_ARG PWorker worker
+#define RAS_ARG_ PWorker worker,
+
+#define RAS_VAR worker
+#define RAS_VAR_ worker,
+
+#define ras (*worker)
+
+
+#else /* FT_STATIC_RASTER */
+
+
+#define RAS_ARG /* empty */
+#define RAS_ARG_ /* empty */
+#define RAS_VAR /* empty */
+#define RAS_VAR_ /* empty */
+
+ static TWorker ras;
+
+
+#endif /* FT_STATIC_RASTER */
+
+
+ /* must be at least 6 bits! */
+#define PIXEL_BITS 8
+
+#define ONE_PIXEL ( 1L << PIXEL_BITS )
+#define PIXEL_MASK ( -1L << PIXEL_BITS )
+#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )
+#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS )
+#define FLOOR( x ) ( (x) & -ONE_PIXEL )
+#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
+#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
+
+#if PIXEL_BITS >= 6
+#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
+#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
+#else
+#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
+#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* TYPE DEFINITIONS */
+ /* */
+
+ /* don't change the following types to FT_Int or FT_Pos, since we might */
+ /* need to define them to "float" or "double" when experimenting with */
+ /* new algorithms */
+
+ typedef int TCoord; /* integer scanline/pixel coordinate */
+ typedef long TPos; /* sub-pixel coordinate */
+
+ /* determine the type used to store cell areas. This normally takes at */
+ /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */
+ /* `long' instead of `int', otherwise bad things happen */
+
+#if PIXEL_BITS <= 7
+
+ typedef int TArea;
+
+#else /* PIXEL_BITS >= 8 */
+
+ /* approximately determine the size of integers using an ANSI-C header */
+#if FT_UINT_MAX == 0xFFFFU
+ typedef long TArea;
+#else
+ typedef int TArea;
+#endif
+
+#endif /* PIXEL_BITS >= 8 */
+
+
+ /* maximal number of gray spans in a call to the span callback */
+#define FT_MAX_GRAY_SPANS 32
+
+
+ typedef struct TCell_* PCell;
+
+ typedef struct TCell_
+ {
+ int x;
+ int cover;
+ TArea area;
+ PCell next;
+
+ } TCell;
+
+
+ typedef struct TWorker_
+ {
+ TCoord ex, ey;
+ TPos min_ex, max_ex;
+ TPos min_ey, max_ey;
+ TPos count_ex, count_ey;
+
+ TArea area;
+ int cover;
+ int invalid;
+
+ PCell cells;
+ int max_cells;
+ int num_cells;
+
+ TCoord cx, cy;
+ TPos x, y;
+
+ TPos last_ey;
+
+ FT_Vector bez_stack[32 * 3 + 1];
+ int lev_stack[32];
+
+ FT_Outline outline;
+ FT_Bitmap target;
+ FT_BBox clip_box;
+
+ FT_Span gray_spans[FT_MAX_GRAY_SPANS];
+ int num_gray_spans;
+
+ FT_Raster_Span_Func render_span;
+ void* render_span_data;
+ int span_y;
+
+ int band_size;
+ int band_shoot;
+ int conic_level;
+ int cubic_level;
+
+ ft_jmp_buf jump_buffer;
+
+ void* buffer;
+ long buffer_size;
+
+ PCell* ycells;
+ int ycount;
+
+ } TWorker, *PWorker;
+
+
+ typedef struct TRaster_
+ {
+ void* buffer;
+ long buffer_size;
+ int band_size;
+ void* memory;
+ PWorker worker;
+
+ } TRaster, *PRaster;
+
+
+
+ /*************************************************************************/
+ /* */
+ /* Initialize the cells table. */
+ /* */
+ static void
+ gray_init_cells( RAS_ARG_ void* buffer,
+ long byte_size )
+ {
+ ras.buffer = buffer;
+ ras.buffer_size = byte_size;
+
+ ras.ycells = (PCell*) buffer;
+ ras.cells = NULL;
+ ras.max_cells = 0;
+ ras.num_cells = 0;
+ ras.area = 0;
+ ras.cover = 0;
+ ras.invalid = 1;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Compute the outline bounding box. */
+ /* */
+ static void
+ gray_compute_cbox( RAS_ARG )
+ {
+ FT_Outline* outline = &ras.outline;
+ FT_Vector* vec = outline->points;
+ FT_Vector* limit = vec + outline->n_points;
+
+
+ if ( outline->n_points <= 0 )
+ {
+ ras.min_ex = ras.max_ex = 0;
+ ras.min_ey = ras.max_ey = 0;
+ return;
+ }
+
+ ras.min_ex = ras.max_ex = vec->x;
+ ras.min_ey = ras.max_ey = vec->y;
+
+ vec++;
+
+ for ( ; vec < limit; vec++ )
+ {
+ TPos x = vec->x;
+ TPos y = vec->y;
+
+
+ if ( x < ras.min_ex ) ras.min_ex = x;
+ if ( x > ras.max_ex ) ras.max_ex = x;
+ if ( y < ras.min_ey ) ras.min_ey = y;
+ if ( y > ras.max_ey ) ras.max_ey = y;
+ }
+
+ /* truncate the bounding box to integer pixels */
+ ras.min_ex = ras.min_ex >> 6;
+ ras.min_ey = ras.min_ey >> 6;
+ ras.max_ex = ( ras.max_ex + 63 ) >> 6;
+ ras.max_ey = ( ras.max_ey + 63 ) >> 6;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Record the current cell in the table. */
+ /* */
+ static PCell
+ gray_find_cell( RAS_ARG )
+ {
+ PCell *pcell, cell;
+ int x = ras.ex;
+
+
+ if ( x > ras.max_ex )
+ x = ras.max_ex;
+
+ pcell = &ras.ycells[ras.ey];
+ for (;;)
+ {
+ cell = *pcell;
+ if ( cell == NULL || cell->x > x )
+ break;
+
+ if ( cell->x == x )
+ goto Exit;
+
+ pcell = &cell->next;
+ }
+
+ if ( ras.num_cells >= ras.max_cells )
+ ft_longjmp( ras.jump_buffer, 1 );
+
+ cell = ras.cells + ras.num_cells++;
+ cell->x = x;
+ cell->area = 0;
+ cell->cover = 0;
+
+ cell->next = *pcell;
+ *pcell = cell;
+
+ Exit:
+ return cell;
+ }
+
+
+ static void
+ gray_record_cell( RAS_ARG )
+ {
+ if ( !ras.invalid && ( ras.area | ras.cover ) )
+ {
+ PCell cell = gray_find_cell( RAS_VAR );
+
+
+ cell->area += ras.area;
+ cell->cover += ras.cover;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Set the current cell to a new position. */
+ /* */
+ static void
+ gray_set_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ /* Move the cell pointer to a new position. We set the `invalid' */
+ /* flag to indicate that the cell isn't part of those we're interested */
+ /* in during the render phase. This means that: */
+ /* */
+ /* . the new vertical position must be within min_ey..max_ey-1. */
+ /* . the new horizontal position must be strictly less than max_ex */
+ /* */
+ /* Note that if a cell is to the left of the clipping region, it is */
+ /* actually set to the (min_ex-1) horizontal position. */
+
+ /* All cells that are on the left of the clipping region go to the */
+ /* min_ex - 1 horizontal position. */
+ ey -= ras.min_ey;
+
+ if ( ex > ras.max_ex )
+ ex = ras.max_ex;
+
+ ex -= ras.min_ex;
+ if ( ex < 0 )
+ ex = -1;
+
+ /* are we moving to a different cell ? */
+ if ( ex != ras.ex || ey != ras.ey )
+ {
+ /* record the current one if it is valid */
+ if ( !ras.invalid )
+ gray_record_cell( RAS_VAR );
+
+ ras.area = 0;
+ ras.cover = 0;
+ }
+
+ ras.ex = ex;
+ ras.ey = ey;
+ ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||
+ ex >= ras.count_ex );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Start a new contour at a given cell. */
+ /* */
+ static void
+ gray_start_cell( RAS_ARG_ TCoord ex,
+ TCoord ey )
+ {
+ if ( ex > ras.max_ex )
+ ex = (TCoord)( ras.max_ex );
+
+ if ( ex < ras.min_ex )
+ ex = (TCoord)( ras.min_ex - 1 );
+
+ ras.area = 0;
+ ras.cover = 0;
+ ras.ex = ex - ras.min_ex;
+ ras.ey = ey - ras.min_ey;
+ ras.last_ey = SUBPIXELS( ey );
+ ras.invalid = 0;
+
+ gray_set_cell( RAS_VAR_ ex, ey );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a scanline as one or more cells. */
+ /* */
+ static void
+ gray_render_scanline( RAS_ARG_ TCoord ey,
+ TPos x1,
+ TCoord y1,
+ TPos x2,
+ TCoord y2 )
+ {
+ TCoord ex1, ex2, fx1, fx2, delta;
+ long p, first, dx;
+ int incr, lift, mod, rem;
+
+
+ dx = x2 - x1;
+
+ ex1 = TRUNC( x1 );
+ ex2 = TRUNC( x2 );
+ fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
+ fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
+
+ /* trivial case. Happens often */
+ if ( y1 == y2 )
+ {
+ gray_set_cell( RAS_VAR_ ex2, ey );
+ return;
+ }
+
+ /* everything is located in a single cell. That is easy! */
+ /* */
+ if ( ex1 == ex2 )
+ {
+ delta = y2 - y1;
+ ras.area += (TArea)( fx1 + fx2 ) * delta;
+ ras.cover += delta;
+ return;
+ }
+
+ /* ok, we'll have to render a run of adjacent cells on the same */
+ /* scanline... */
+ /* */
+ p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dx < 0 )
+ {
+ p = fx1 * ( y2 - y1 );
+ first = 0;
+ incr = -1;
+ dx = -dx;
+ }
+
+ delta = (TCoord)( p / dx );
+ mod = (TCoord)( p % dx );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dx;
+ }
+
+ ras.area += (TArea)( fx1 + first ) * delta;
+ ras.cover += delta;
+
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ y1 += delta;
+
+ if ( ex1 != ex2 )
+ {
+ p = ONE_PIXEL * ( y2 - y1 + delta );
+ lift = (TCoord)( p / dx );
+ rem = (TCoord)( p % dx );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (TCoord)dx;
+ }
+
+ mod -= (int)dx;
+
+ while ( ex1 != ex2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (TCoord)dx;
+ delta++;
+ }
+
+ ras.area += (TArea)ONE_PIXEL * delta;
+ ras.cover += delta;
+ y1 += delta;
+ ex1 += incr;
+ gray_set_cell( RAS_VAR_ ex1, ey );
+ }
+ }
+
+ delta = y2 - y1;
+ ras.area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;
+ ras.cover += delta;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Render a given line as a series of scanlines. */
+ /* */
+ static void
+ gray_render_line( RAS_ARG_ TPos to_x,
+ TPos to_y )
+ {
+ TCoord ey1, ey2, fy1, fy2;
+ TPos dx, dy, x, x2;
+ long p, first;
+ int delta, rem, mod, lift, incr;
+
+
+ ey1 = TRUNC( ras.last_ey );
+ ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
+ fy1 = (TCoord)( ras.y - ras.last_ey );
+ fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
+
+ dx = to_x - ras.x;
+ dy = to_y - ras.y;
+
+ /* XXX: we should do something about the trivial case where dx == 0, */
+ /* as it happens very often! */
+
+ /* perform vertical clipping */
+ {
+ TCoord min, max;
+
+
+ min = ey1;
+ max = ey2;
+ if ( ey1 > ey2 )
+ {
+ min = ey2;
+ max = ey1;
+ }
+ if ( min >= ras.max_ey || max < ras.min_ey )
+ goto End;
+ }
+
+ /* everything is on a single scanline */
+ if ( ey1 == ey2 )
+ {
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
+ goto End;
+ }
+
+ /* vertical line - avoid calling gray_render_scanline */
+ incr = 1;
+
+ if ( dx == 0 )
+ {
+ TCoord ex = TRUNC( ras.x );
+ TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );
+ TPos area;
+
+
+ first = ONE_PIXEL;
+ if ( dy < 0 )
+ {
+ first = 0;
+ incr = -1;
+ }
+
+ delta = (int)( first - fy1 );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+
+ delta = (int)( first + first - ONE_PIXEL );
+ area = (TArea)two_fx * delta;
+ while ( ey1 != ey2 )
+ {
+ ras.area += area;
+ ras.cover += delta;
+ ey1 += incr;
+
+ gray_set_cell( &ras, ex, ey1 );
+ }
+
+ delta = (int)( fy2 - ONE_PIXEL + first );
+ ras.area += (TArea)two_fx * delta;
+ ras.cover += delta;
+
+ goto End;
+ }
+
+ /* ok, we have to render several scanlines */
+ p = ( ONE_PIXEL - fy1 ) * dx;
+ first = ONE_PIXEL;
+ incr = 1;
+
+ if ( dy < 0 )
+ {
+ p = fy1 * dx;
+ first = 0;
+ incr = -1;
+ dy = -dy;
+ }
+
+ delta = (int)( p / dy );
+ mod = (int)( p % dy );
+ if ( mod < 0 )
+ {
+ delta--;
+ mod += (TCoord)dy;
+ }
+
+ x = ras.x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+
+ if ( ey1 != ey2 )
+ {
+ p = ONE_PIXEL * dx;
+ lift = (int)( p / dy );
+ rem = (int)( p % dy );
+ if ( rem < 0 )
+ {
+ lift--;
+ rem += (int)dy;
+ }
+ mod -= (int)dy;
+
+ while ( ey1 != ey2 )
+ {
+ delta = lift;
+ mod += rem;
+ if ( mod >= 0 )
+ {
+ mod -= (int)dy;
+ delta++;
+ }
+
+ x2 = x + delta;
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), x2,
+ (TCoord)first );
+ x = x2;
+
+ ey1 += incr;
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
+ }
+ }
+
+ gray_render_scanline( RAS_VAR_ ey1, x,
+ (TCoord)( ONE_PIXEL - first ), to_x,
+ fy2 );
+
+ End:
+ ras.x = to_x;
+ ras.y = to_y;
+ ras.last_ey = SUBPIXELS( ey2 );
+ }
+
+
+ static void
+ gray_split_conic( FT_Vector* base )
+ {
+ TPos a, b;
+
+
+ base[4].x = base[2].x;
+ b = base[1].x;
+ a = base[3].x = ( base[2].x + b ) / 2;
+ b = base[1].x = ( base[0].x + b ) / 2;
+ base[2].x = ( a + b ) / 2;
+
+ base[4].y = base[2].y;
+ b = base[1].y;
+ a = base[3].y = ( base[2].y + b ) / 2;
+ b = base[1].y = ( base[0].y + b ) / 2;
+ base[2].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_conic( RAS_ARG_ const FT_Vector* control,
+ const FT_Vector* to )
+ {
+ TPos dx, dy;
+ int top, level;
+ int* levels;
+ FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+
+ level = 1;
+ dx = dx / ras.conic_level;
+ while ( dx > 0 )
+ {
+ dx >>= 2;
+ level++;
+ }
+
+ /* a shortcut to speed things up */
+ if ( level <= 1 )
+ {
+ /* we compute the mid-point directly in order to avoid */
+ /* calling gray_split_conic() */
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
+ mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ return;
+ }
+
+ arc = ras.bez_stack;
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control->x );
+ arc[1].y = UPSCALE( control->y );
+ arc[2].x = ras.x;
+ arc[2].y = ras.y;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
+ goto Draw;
+
+ gray_split_conic( arc );
+ arc += 2;
+ top++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
+ mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+
+ top--;
+ arc -= 2;
+ }
+ }
+
+ return;
+ }
+
+
+ static void
+ gray_split_cubic( FT_Vector* base )
+ {
+ TPos a, b, c, d;
+
+
+ base[6].x = base[3].x;
+ c = base[1].x;
+ d = base[2].x;
+ base[1].x = a = ( base[0].x + c ) / 2;
+ base[5].x = b = ( base[3].x + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].x = a = ( a + c ) / 2;
+ base[4].x = b = ( b + c ) / 2;
+ base[3].x = ( a + b ) / 2;
+
+ base[6].y = base[3].y;
+ c = base[1].y;
+ d = base[2].y;
+ base[1].y = a = ( base[0].y + c ) / 2;
+ base[5].y = b = ( base[3].y + d ) / 2;
+ c = ( c + d ) / 2;
+ base[2].y = a = ( a + c ) / 2;
+ base[4].y = b = ( b + c ) / 2;
+ base[3].y = ( a + b ) / 2;
+ }
+
+
+ static void
+ gray_render_cubic( RAS_ARG_ const FT_Vector* control1,
+ const FT_Vector* control2,
+ const FT_Vector* to )
+ {
+ TPos dx, dy, da, db;
+ int top, level;
+ int* levels;
+ FT_Vector* arc;
+
+
+ dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ da = dx;
+
+ dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
+ if ( dx < 0 )
+ dx = -dx;
+ dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );
+ if ( dy < 0 )
+ dy = -dy;
+ if ( dx < dy )
+ dx = dy;
+ db = dx;
+
+ level = 1;
+ da = da / ras.cubic_level;
+ db = db / ras.conic_level;
+ while ( da > 0 || db > 0 )
+ {
+ da >>= 2;
+ db >>= 3;
+ level++;
+ }
+
+ if ( level <= 1 )
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = UPSCALE( to->x );
+ to_y = UPSCALE( to->y );
+ mid_x = ( ras.x + to_x +
+ 3 * UPSCALE( control1->x + control2->x ) ) / 8;
+ mid_y = ( ras.y + to_y +
+ 3 * UPSCALE( control1->y + control2->y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ return;
+ }
+
+ arc = ras.bez_stack;
+ arc[0].x = UPSCALE( to->x );
+ arc[0].y = UPSCALE( to->y );
+ arc[1].x = UPSCALE( control2->x );
+ arc[1].y = UPSCALE( control2->y );
+ arc[2].x = UPSCALE( control1->x );
+ arc[2].y = UPSCALE( control1->y );
+ arc[3].x = ras.x;
+ arc[3].y = ras.y;
+
+ levels = ras.lev_stack;
+ top = 0;
+ levels[0] = level;
+
+ while ( top >= 0 )
+ {
+ level = levels[top];
+ if ( level > 1 )
+ {
+ /* check that the arc crosses the current band */
+ TPos min, max, y;
+
+
+ min = max = arc[0].y;
+ y = arc[1].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[2].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ y = arc[3].y;
+ if ( y < min ) min = y;
+ if ( y > max ) max = y;
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
+ goto Draw;
+ gray_split_cubic( arc );
+ arc += 3;
+ top ++;
+ levels[top] = levels[top - 1] = level - 1;
+ continue;
+ }
+
+ Draw:
+ {
+ TPos to_x, to_y, mid_x, mid_y;
+
+
+ to_x = arc[0].x;
+ to_y = arc[0].y;
+ mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
+ mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
+
+ gray_render_line( RAS_VAR_ mid_x, mid_y );
+ gray_render_line( RAS_VAR_ to_x, to_y );
+ top --;
+ arc -= 3;
+ }
+ }
+
+ return;
+ }
+
+
+
+ static int
+ gray_move_to( const FT_Vector* to,
+ PWorker worker )
+ {
+ TPos x, y;
+
+
+ /* record current cell, if any */
+ gray_record_cell( worker );
+
+ /* start to a new position */
+ x = UPSCALE( to->x );
+ y = UPSCALE( to->y );
+
+ gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );
+
+ worker->x = x;
+ worker->y = y;
+ return 0;
+ }
+
+
+ static int
+ gray_line_to( const FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_line( worker, UPSCALE( to->x ), UPSCALE( to->y ) );
+ return 0;
+ }
+
+
+ static int
+ gray_conic_to( const FT_Vector* control,
+ const FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_conic( worker, control, to );
+ return 0;
+ }
+
+
+ static int
+ gray_cubic_to( const FT_Vector* control1,
+ const FT_Vector* control2,
+ const FT_Vector* to,
+ PWorker worker )
+ {
+ gray_render_cubic( worker, control1, control2, to );
+ return 0;
+ }
+
+
+ static void
+ gray_render_span( int y,
+ int count,
+ const FT_Span* spans,
+ PWorker worker )
+ {
+ unsigned char* p;
+ FT_Bitmap* map = &worker->target;
+
+
+ /* first of all, compute the scanline offset */
+ p = (unsigned char*)map->buffer - y * map->pitch;
+ if ( map->pitch >= 0 )
+ p += ( map->rows - 1 ) * map->pitch;
+
+ for ( ; count > 0; count--, spans++ )
+ {
+ unsigned char coverage = spans->coverage;
+
+
+ if ( coverage )
+ {
+ /* For small-spans it is faster to do it by ourselves than
+ * calling `memset'. This is mainly due to the cost of the
+ * function call.
+ */
+ if ( spans->len >= 8 )
+ FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );
+ else
+ {
+ unsigned char* q = p + spans->x;
+
+
+ switch ( spans->len )
+ {
+ case 7: *q++ = (unsigned char)coverage;
+ case 6: *q++ = (unsigned char)coverage;
+ case 5: *q++ = (unsigned char)coverage;
+ case 4: *q++ = (unsigned char)coverage;
+ case 3: *q++ = (unsigned char)coverage;
+ case 2: *q++ = (unsigned char)coverage;
+ case 1: *q = (unsigned char)coverage;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ }
+
+
+ static void
+ gray_hline( RAS_ARG_ TCoord x,
+ TCoord y,
+ TPos area,
+ int acount )
+ {
+ FT_Span* span;
+ int count;
+ int coverage;
+
+
+ /* compute the coverage line's coverage, depending on the */
+ /* outline fill rule */
+ /* */
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
+ /* */
+ coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
+ /* use range 0..256 */
+ if ( coverage < 0 )
+ coverage = -coverage;
+
+ if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL )
+ {
+ coverage &= 511;
+
+ if ( coverage > 256 )
+ coverage = 512 - coverage;
+ else if ( coverage == 256 )
+ coverage = 255;
+ }
+ else
+ {
+ /* normal non-zero winding rule */
+ if ( coverage >= 256 )
+ coverage = 255;
+ }
+
+ y += (TCoord)ras.min_ey;
+ x += (TCoord)ras.min_ex;
+
+ /* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
+ if ( x >= 32768 )
+ x = 32767;
+
+ if ( coverage )
+ {
+ /* see whether we can add this span to the current list */
+ count = ras.num_gray_spans;
+ span = ras.gray_spans + count - 1;
+ if ( count > 0 &&
+ ras.span_y == y &&
+ (int)span->x + span->len == (int)x &&
+ span->coverage == coverage )
+ {
+ span->len = (unsigned short)( span->len + acount );
+ return;
+ }
+
+ if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )
+ {
+ if ( ras.render_span && count > 0 )
+ ras.render_span( ras.span_y, count, ras.gray_spans,
+ ras.render_span_data );
+ /* ras.render_span( span->y, ras.gray_spans, count ); */
+
+#ifdef DEBUG_GRAYS
+
+ if ( ras.span_y >= 0 )
+ {
+ int n;
+
+
+ fprintf( stderr, "y=%3d ", ras.span_y );
+ span = ras.gray_spans;
+ for ( n = 0; n < count; n++, span++ )
+ fprintf( stderr, "[%d..%d]:%02x ",
+ span->x, span->x + span->len - 1, span->coverage );
+ fprintf( stderr, "\n" );
+ }
+
+#endif /* DEBUG_GRAYS */
+
+ ras.num_gray_spans = 0;
+ ras.span_y = y;
+
+ count = 0;
+ span = ras.gray_spans;
+ }
+ else
+ span++;
+
+ /* add a gray span to the current list */
+ span->x = (short)x;
+ span->len = (unsigned short)acount;
+ span->coverage = (unsigned char)coverage;
+
+ ras.num_gray_spans++;
+ }
+ }
+
+
+#ifdef DEBUG_GRAYS
+
+ /* to be called while in the debugger */
+ gray_dump_cells( RAS_ARG )
+ {
+ int yindex;
+
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell;
+
+
+ printf( "%3d:", yindex );
+
+ for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
+ printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );
+ printf( "\n" );
+ }
+ }
+
+#endif /* DEBUG_GRAYS */
+
+
+ static void
+ gray_sweep( RAS_ARG_ const FT_Bitmap* target )
+ {
+ int yindex;
+
+ FT_UNUSED( target );
+
+
+ if ( ras.num_cells == 0 )
+ return;
+
+ ras.num_gray_spans = 0;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ {
+ PCell cell = ras.ycells[yindex];
+ TCoord cover = 0;
+ TCoord x = 0;
+
+
+ for ( ; cell != NULL; cell = cell->next )
+ {
+ TArea area;
+
+
+ if ( cell->x > x && cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ cell->x - x );
+
+ cover += cell->cover;
+ area = cover * ( ONE_PIXEL * 2 ) - cell->area;
+
+ if ( area != 0 && cell->x >= 0 )
+ gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
+
+ x = cell->x + 1;
+ }
+
+ if ( cover != 0 )
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
+ ras.count_ex - x );
+ }
+
+ if ( ras.render_span && ras.num_gray_spans > 0 )
+ ras.render_span( ras.span_y, ras.num_gray_spans,
+ ras.gray_spans, ras.render_span_data );
+ }
+
+
+#ifdef _STANDALONE_
+
+ /*************************************************************************/
+ /* */
+ /* The following function should only compile in stand_alone mode, */
+ /* i.e., when building this component without the rest of FreeType. */
+ /* */
+ /*************************************************************************/
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* FT_Outline_Decompose */
+ /* */
+ /* <Description> */
+ /* Walks over an outline's structure to decompose it into individual */
+ /* segments and Bezier arcs. This function is also able to emit */
+ /* `move to' and `close to' operations to indicate the start and end */
+ /* of new contours in the outline. */
+ /* */
+ /* <Input> */
+ /* outline :: A pointer to the source target. */
+ /* */
+ /* func_interface :: A table of `emitters', i.e,. function pointers */
+ /* called during decomposition to indicate path */
+ /* operations. */
+ /* */
+ /* user :: A typeless pointer which is passed to each */
+ /* emitter during the decomposition. It can be */
+ /* used to store the state during the */
+ /* decomposition. */
+ /* */
+ /* <Return> */
+ /* Error code. 0 means success. */
+ /* */
+ static
+ int FT_Outline_Decompose( const FT_Outline* outline,
+ const FT_Outline_Funcs* func_interface,
+ void* user )
+ {
+#undef SCALED
+#if 0
+#define SCALED( x ) ( ( (x) << shift ) - delta )
+#else
+#define SCALED( x ) (x)
+#endif
+
+ FT_Vector v_last;
+ FT_Vector v_control;
+ FT_Vector v_start;
+
+ FT_Vector* point;
+ FT_Vector* limit;
+ char* tags;
+
+ int n; /* index of contour in outline */
+ int first; /* index of first point in contour */
+ int error;
+ char tag; /* current point's state */
+
+#if 0
+ int shift = func_interface->shift;
+ TPos delta = func_interface->delta;
+#endif
+
+
+ first = 0;
+
+ for ( n = 0; n < outline->n_contours; n++ )
+ {
+ int last; /* index of last point in contour */
+
+
+ last = outline->contours[n];
+ limit = outline->points + last;
+
+ v_start = outline->points[first];
+ v_last = outline->points[last];
+
+ v_start.x = SCALED( v_start.x );
+ v_start.y = SCALED( v_start.y );
+
+ v_last.x = SCALED( v_last.x );
+ v_last.y = SCALED( v_last.y );
+
+ v_control = v_start;
+
+ point = outline->points + first;
+ tags = outline->tags + first;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ /* A contour cannot start with a cubic control point! */
+ if ( tag == FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ /* check first point to determine origin */
+ if ( tag == FT_CURVE_TAG_CONIC )
+ {
+ /* first point is conic control. Yes, this happens. */
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
+ {
+ /* start at last point if it is on the curve */
+ v_start = v_last;
+ limit--;
+ }
+ else
+ {
+ /* if both first and last points are conic, */
+ /* start at their middle and record its position */
+ /* for closure */
+ v_start.x = ( v_start.x + v_last.x ) / 2;
+ v_start.y = ( v_start.y + v_last.y ) / 2;
+
+ v_last = v_start;
+ }
+ point--;
+ tags--;
+ }
+
+ error = func_interface->move_to( &v_start, user );
+ if ( error )
+ goto Exit;
+
+ while ( point < limit )
+ {
+ point++;
+ tags++;
+
+ tag = FT_CURVE_TAG( tags[0] );
+ switch ( tag )
+ {
+ case FT_CURVE_TAG_ON: /* emit a single line_to */
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->line_to( &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */
+ {
+ v_control.x = SCALED( point->x );
+ v_control.y = SCALED( point->y );
+
+ Do_Conic:
+ if ( point < limit )
+ {
+ FT_Vector vec;
+ FT_Vector v_middle;
+
+
+ point++;
+ tags++;
+ tag = FT_CURVE_TAG( tags[0] );
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ if ( tag == FT_CURVE_TAG_ON )
+ {
+ error = func_interface->conic_to( &v_control, &vec,
+ user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ if ( tag != FT_CURVE_TAG_CONIC )
+ goto Invalid_Outline;
+
+ v_middle.x = ( v_control.x + vec.x ) / 2;
+ v_middle.y = ( v_control.y + vec.y ) / 2;
+
+ error = func_interface->conic_to( &v_control, &v_middle,
+ user );
+ if ( error )
+ goto Exit;
+
+ v_control = vec;
+ goto Do_Conic;
+ }
+
+ error = func_interface->conic_to( &v_control, &v_start,
+ user );
+ goto Close;
+ }
+
+ default: /* FT_CURVE_TAG_CUBIC */
+ {
+ FT_Vector vec1, vec2;
+
+
+ if ( point + 1 > limit ||
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
+ goto Invalid_Outline;
+
+ point += 2;
+ tags += 2;
+
+ vec1.x = SCALED( point[-2].x );
+ vec1.y = SCALED( point[-2].y );
+
+ vec2.x = SCALED( point[-1].x );
+ vec2.y = SCALED( point[-1].y );
+
+ if ( point <= limit )
+ {
+ FT_Vector vec;
+
+
+ vec.x = SCALED( point->x );
+ vec.y = SCALED( point->y );
+
+ error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
+ if ( error )
+ goto Exit;
+ continue;
+ }
+
+ error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
+ goto Close;
+ }
+ }
+ }
+
+ /* close the contour with a line segment */
+ error = func_interface->line_to( &v_start, user );
+
+ Close:
+ if ( error )
+ goto Exit;
+
+ first = last + 1;
+ }
+
+ return 0;
+
+ Exit:
+ return error;
+
+ Invalid_Outline:
+ return ErrRaster_Invalid_Outline;
+ }
+
+#endif /* _STANDALONE_ */
+
+
+ typedef struct TBand_
+ {
+ TPos min, max;
+
+ } TBand;
+
+
+ static int
+ gray_convert_glyph_inner( RAS_ARG )
+ {
+ static
+ const FT_Outline_Funcs func_interface =
+ {
+ (FT_Outline_MoveTo_Func) gray_move_to,
+ (FT_Outline_LineTo_Func) gray_line_to,
+ (FT_Outline_ConicTo_Func)gray_conic_to,
+ (FT_Outline_CubicTo_Func)gray_cubic_to,
+ 0,
+ 0
+ };
+
+ volatile int error = 0;
+
+ if ( ft_setjmp( ras.jump_buffer ) == 0 )
+ {
+ error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras );
+ gray_record_cell( RAS_VAR );
+ }
+ else
+ {
+ error = ErrRaster_Memory_Overflow;
+ }
+
+ return error;
+ }
+
+
+ static int
+ gray_convert_glyph( RAS_ARG )
+ {
+ TBand bands[40];
+ TBand* volatile band;
+ int volatile n, num_bands;
+ TPos volatile min, max, max_y;
+ FT_BBox* clip;
+
+
+ /* Set up state in the raster object */
+ gray_compute_cbox( RAS_VAR );
+
+ /* clip to target bitmap, exit if nothing to do */
+ clip = &ras.clip_box;
+
+ if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
+ ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
+ return 0;
+
+ if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
+ if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
+
+ if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
+ if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
+
+ ras.count_ex = ras.max_ex - ras.min_ex;
+ ras.count_ey = ras.max_ey - ras.min_ey;
+
+ /* simple heuristic used to speed-up the bezier decomposition -- see */
+ /* the code in gray_render_conic() and gray_render_cubic() for more */
+ /* details */
+ ras.conic_level = 32;
+ ras.cubic_level = 16;
+
+ {
+ int level = 0;
+
+
+ if ( ras.count_ex > 24 || ras.count_ey > 24 )
+ level++;
+ if ( ras.count_ex > 120 || ras.count_ey > 120 )
+ level++;
+
+ ras.conic_level <<= level;
+ ras.cubic_level <<= level;
+ }
+
+ /* setup vertical bands */
+ num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
+ if ( num_bands == 0 ) num_bands = 1;
+ if ( num_bands >= 39 ) num_bands = 39;
+
+ ras.band_shoot = 0;
+
+ min = ras.min_ey;
+ max_y = ras.max_ey;
+
+ for ( n = 0; n < num_bands; n++, min = max )
+ {
+ max = min + ras.band_size;
+ if ( n == num_bands - 1 || max > max_y )
+ max = max_y;
+
+ bands[0].min = min;
+ bands[0].max = max;
+ band = bands;
+
+ while ( band >= bands )
+ {
+ TPos bottom, top, middle;
+ int error;
+
+ {
+ PCell cells_max;
+ int yindex;
+ long cell_start, cell_end, cell_mod;
+
+
+ ras.ycells = (PCell*)ras.buffer;
+ ras.ycount = band->max - band->min;
+
+ cell_start = sizeof ( PCell ) * ras.ycount;
+ cell_mod = cell_start % sizeof ( TCell );
+ if ( cell_mod > 0 )
+ cell_start += sizeof ( TCell ) - cell_mod;
+
+ cell_end = ras.buffer_size;
+ cell_end -= cell_end % sizeof( TCell );
+
+ cells_max = (PCell)( (char*)ras.buffer + cell_end );
+ ras.cells = (PCell)( (char*)ras.buffer + cell_start );
+ if ( ras.cells >= cells_max )
+ goto ReduceBands;
+
+ ras.max_cells = cells_max - ras.cells;
+ if ( ras.max_cells < 2 )
+ goto ReduceBands;
+
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )
+ ras.ycells[yindex] = NULL;
+ }
+
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.min_ey = band->min;
+ ras.max_ey = band->max;
+ ras.count_ey = band->max - band->min;
+
+ error = gray_convert_glyph_inner( RAS_VAR );
+
+ if ( !error )
+ {
+ gray_sweep( RAS_VAR_ &ras.target );
+ band--;
+ continue;
+ }
+ else if ( error != ErrRaster_Memory_Overflow )
+ return 1;
+
+ ReduceBands:
+ /* render pool overflow; we will reduce the render band by half */
+ bottom = band->min;
+ top = band->max;
+ middle = bottom + ( ( top - bottom ) >> 1 );
+
+ /* This is too complex for a single scanline; there must */
+ /* be some problems. */
+ if ( middle == bottom )
+ {
+#ifdef DEBUG_GRAYS
+ fprintf( stderr, "Rotten glyph!\n" );
+#endif
+ return 1;
+ }
+
+ if ( bottom-top >= ras.band_size )
+ ras.band_shoot++;
+
+ band[1].min = bottom;
+ band[1].max = middle;
+ band[0].min = middle;
+ band[0].max = top;
+ band++;
+ }
+ }
+
+ if ( ras.band_shoot > 8 && ras.band_size > 16 )
+ ras.band_size = ras.band_size / 2;
+
+ return 0;
+ }
+
+
+ static int
+ gray_raster_render( PRaster raster,
+ const FT_Raster_Params* params )
+ {
+ const FT_Outline* outline = (const FT_Outline*)params->source;
+ const FT_Bitmap* target_map = params->target;
+ PWorker worker;
+
+
+ if ( !raster || !raster->buffer || !raster->buffer_size )
+ return ErrRaster_Invalid_Argument;
+
+ /* return immediately if the outline is empty */
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )
+ return 0;
+
+ if ( !outline || !outline->contours || !outline->points )
+ return ErrRaster_Invalid_Outline;
+
+ if ( outline->n_points !=
+ outline->contours[outline->n_contours - 1] + 1 )
+ return ErrRaster_Invalid_Outline;
+
+ worker = raster->worker;
+
+ /* if direct mode is not set, we must have a target bitmap */
+ if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ if ( !target_map )
+ return ErrRaster_Invalid_Argument;
+
+ /* nothing to do */
+ if ( !target_map->width || !target_map->rows )
+ return 0;
+
+ if ( !target_map->buffer )
+ return ErrRaster_Invalid_Argument;
+ }
+
+ /* this version does not support monochrome rendering */
+ if ( !( params->flags & FT_RASTER_FLAG_AA ) )
+ return ErrRaster_Invalid_Mode;
+
+ /* compute clipping box */
+ if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )
+ {
+ /* compute clip box from target pixmap */
+ ras.clip_box.xMin = 0;
+ ras.clip_box.yMin = 0;
+ ras.clip_box.xMax = target_map->width;
+ ras.clip_box.yMax = target_map->rows;
+ }
+ else if ( params->flags & FT_RASTER_FLAG_CLIP )
+ {
+ ras.clip_box = params->clip_box;
+ }
+ else
+ {
+ ras.clip_box.xMin = -32768L;
+ ras.clip_box.yMin = -32768L;
+ ras.clip_box.xMax = 32767L;
+ ras.clip_box.yMax = 32767L;
+ }
+
+ gray_init_cells( worker, raster->buffer, raster->buffer_size );
+
+ ras.outline = *outline;
+ ras.num_cells = 0;
+ ras.invalid = 1;
+ ras.band_size = raster->band_size;
+ ras.num_gray_spans = 0;
+
+ if ( target_map )
+ ras.target = *target_map;
+
+ ras.render_span = (FT_Raster_Span_Func)gray_render_span;
+ ras.render_span_data = &ras;
+
+ if ( params->flags & FT_RASTER_FLAG_DIRECT )
+ {
+ ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
+ ras.render_span_data = params->user;
+ }
+
+ return gray_convert_glyph( worker );
+ }
+
+
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
+ /**** a static object. *****/
+
+#ifdef _STANDALONE_
+
+ static int
+ gray_raster_new( void* memory,
+ FT_Raster* araster )
+ {
+ static TRaster the_raster;
+
+ FT_UNUSED( memory );
+
+
+ *araster = (FT_Raster)&the_raster;
+ FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
+
+ return 0;
+ }
+
+
+ static void
+ gray_raster_done( FT_Raster raster )
+ {
+ /* nothing */
+ FT_UNUSED( raster );
+ }
+
+#else /* _STANDALONE_ */
+
+ static int
+ gray_raster_new( FT_Memory memory,
+ FT_Raster* araster )
+ {
+ FT_Error error;
+ PRaster raster;
+
+
+ *araster = 0;
+ if ( !FT_ALLOC( raster, sizeof ( TRaster ) ) )
+ {
+ raster->memory = memory;
+ *araster = (FT_Raster)raster;
+ }
+
+ return error;
+ }
+
+
+ static void
+ gray_raster_done( FT_Raster raster )
+ {
+ FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;
+
+
+ FT_FREE( raster );
+ }
+
+#endif /* _STANDALONE_ */
+
+
+ static void
+ gray_raster_reset( FT_Raster raster,
+ char* pool_base,
+ long pool_size )
+ {
+ PRaster rast = (PRaster)raster;
+
+
+ if ( raster )
+ {
+ if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 )
+ {
+ PWorker worker = (PWorker)pool_base;
+
+
+ rast->worker = worker;
+ rast->buffer = pool_base +
+ ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &
+ ~( sizeof ( TCell ) - 1 ) );
+ rast->buffer_size = (long)( ( pool_base + pool_size ) -
+ (char*)rast->buffer ) &
+ ~( sizeof ( TCell ) - 1 );
+ rast->band_size = (int)( rast->buffer_size /
+ ( sizeof ( TCell ) * 8 ) );
+ }
+ else
+ {
+ rast->buffer = NULL;
+ rast->buffer_size = 0;
+ rast->worker = NULL;
+ }
+ }
+ }
+
+
+ const FT_Raster_Funcs ft_grays_raster =
+ {
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Raster_New_Func) gray_raster_new,
+ (FT_Raster_Reset_Func) gray_raster_reset,
+ (FT_Raster_Set_Mode_Func)0,
+ (FT_Raster_Render_Func) gray_raster_render,
+ (FT_Raster_Done_Func) gray_raster_done
+ };
+
+
+/* END */
diff --git a/src/smooth/ftgrays.h b/src/smooth/ftgrays.h
new file mode 100644
index 0000000..2d40954
--- /dev/null
+++ b/src/smooth/ftgrays.h
@@ -0,0 +1,57 @@
+/***************************************************************************/
+/* */
+/* ftgrays.h */
+/* */
+/* FreeType smooth renderer declaration */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTGRAYS_H__
+#define __FTGRAYS_H__
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+#ifdef _STANDALONE_
+#include "ftimage.h"
+#else
+#include <ft2build.h>
+#include FT_IMAGE_H
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* To make ftgrays.h independent from configuration files we check */
+ /* whether FT_EXPORT_VAR has been defined already. */
+ /* */
+ /* On some systems and compilers (Win32 mostly), an extra keyword is */
+ /* necessary to compile the library as a DLL. */
+ /* */
+#ifndef FT_EXPORT_VAR
+#define FT_EXPORT_VAR( x ) extern x
+#endif
+
+ FT_EXPORT_VAR( const FT_Raster_Funcs ) ft_grays_raster;
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* __FTGRAYS_H__ */
+
+
+/* END */
diff --git a/src/smooth/ftsmerrs.h b/src/smooth/ftsmerrs.h
new file mode 100644
index 0000000..0c2a2ec
--- /dev/null
+++ b/src/smooth/ftsmerrs.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* ftsmerrs.h */
+/* */
+/* smooth renderer error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the smooth renderer error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __FTSMERRS_H__
+#define __FTSMERRS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX Smooth_Err_
+#define FT_ERR_BASE FT_Mod_Err_Smooth
+
+#include FT_ERRORS_H
+
+#endif /* __FTSMERRS_H__ */
+
+
+/* END */
diff --git a/src/smooth/ftsmooth.c b/src/smooth/ftsmooth.c
new file mode 100644
index 0000000..85d04eb
--- /dev/null
+++ b/src/smooth/ftsmooth.c
@@ -0,0 +1,467 @@
+/***************************************************************************/
+/* */
+/* ftsmooth.c */
+/* */
+/* Anti-aliasing renderer interface (body). */
+/* */
+/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_OUTLINE_H
+#include "ftsmooth.h"
+#include "ftgrays.h"
+
+#include "ftsmerrs.h"
+
+
+ /* initialize renderer -- init its raster */
+ static FT_Error
+ ft_smooth_init( FT_Renderer render )
+ {
+ FT_Library library = FT_MODULE_LIBRARY( render );
+
+
+ render->clazz->raster_class->raster_reset( render->raster,
+ library->raster_pool,
+ library->raster_pool_size );
+
+ return 0;
+ }
+
+
+ /* sets render-specific mode */
+ static FT_Error
+ ft_smooth_set_mode( FT_Renderer render,
+ FT_ULong mode_tag,
+ FT_Pointer data )
+ {
+ /* we simply pass it to the raster */
+ return render->clazz->raster_class->raster_set_mode( render->raster,
+ mode_tag,
+ data );
+ }
+
+ /* transform a given glyph image */
+ static FT_Error
+ ft_smooth_transform( FT_Renderer render,
+ FT_GlyphSlot slot,
+ const FT_Matrix* matrix,
+ const FT_Vector* delta )
+ {
+ FT_Error error = Smooth_Err_Ok;
+
+
+ if ( slot->format != render->glyph_format )
+ {
+ error = Smooth_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( matrix )
+ FT_Outline_Transform( &slot->outline, matrix );
+
+ if ( delta )
+ FT_Outline_Translate( &slot->outline, delta->x, delta->y );
+
+ Exit:
+ return error;
+ }
+
+
+ /* return the glyph's control box */
+ static void
+ ft_smooth_get_cbox( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_BBox* cbox )
+ {
+ FT_MEM_ZERO( cbox, sizeof ( *cbox ) );
+
+ if ( slot->format == render->glyph_format )
+ FT_Outline_Get_CBox( &slot->outline, cbox );
+ }
+
+
+ /* convert a slot's glyph image into a bitmap */
+ static FT_Error
+ ft_smooth_render_generic( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin,
+ FT_Render_Mode required_mode )
+ {
+ FT_Error error;
+ FT_Outline* outline = NULL;
+ FT_BBox cbox;
+ FT_UInt width, height, height_org, width_org, pitch;
+ FT_Bitmap* bitmap;
+ FT_Memory memory;
+ FT_Int hmul = mode == FT_RENDER_MODE_LCD;
+ FT_Int vmul = mode == FT_RENDER_MODE_LCD_V;
+ FT_Pos x_shift, y_shift, x_left, y_top;
+
+ FT_Raster_Params params;
+
+
+ /* check glyph image format */
+ if ( slot->format != render->glyph_format )
+ {
+ error = Smooth_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* check mode */
+ if ( mode != required_mode )
+ return Smooth_Err_Cannot_Render_Glyph;
+
+ outline = &slot->outline;
+
+ /* translate the outline to the new origin if needed */
+ if ( origin )
+ FT_Outline_Translate( outline, origin->x, origin->y );
+
+ /* compute the control box, and grid fit it */
+ FT_Outline_Get_CBox( outline, &cbox );
+
+ cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
+ cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
+ cbox.xMax = FT_PIX_CEIL( cbox.xMax );
+ cbox.yMax = FT_PIX_CEIL( cbox.yMax );
+
+ width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
+ height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
+ bitmap = &slot->bitmap;
+ memory = render->root.memory;
+
+ width_org = width;
+ height_org = height;
+
+ /* release old bitmap buffer */
+ if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
+ {
+ FT_FREE( bitmap->buffer );
+ slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
+ }
+
+ /* allocate new one, depends on pixel format */
+ pitch = width;
+ if ( hmul )
+ {
+ width = width * 3;
+ pitch = FT_PAD_CEIL( width, 4 );
+ }
+
+ if ( vmul )
+ height *= 3;
+
+ x_shift = (FT_Int) cbox.xMin;
+ y_shift = (FT_Int) cbox.yMin;
+ x_left = (FT_Int)( cbox.xMin >> 6 );
+ y_top = (FT_Int)( cbox.yMax >> 6 );
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+ if ( slot->library->lcd_filter_func )
+ {
+ FT_Int extra = slot->library->lcd_extra;
+
+
+ if ( hmul )
+ {
+ x_shift -= 64 * ( extra >> 1 );
+ width += 3 * extra;
+ pitch = FT_PAD_CEIL( width, 4 );
+ x_left -= extra >> 1;
+ }
+
+ if ( vmul )
+ {
+ y_shift -= 64 * ( extra >> 1 );
+ height += 3 * extra;
+ y_top += extra >> 1;
+ }
+ }
+
+#endif
+
+ bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
+ bitmap->num_grays = 256;
+ bitmap->width = width;
+ bitmap->rows = height;
+ bitmap->pitch = pitch;
+
+ /* translate outline to render it into the bitmap */
+ FT_Outline_Translate( outline, -x_shift, -y_shift );
+
+ if ( FT_ALLOC( bitmap->buffer, (FT_ULong)pitch * height ) )
+ goto Exit;
+
+ slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
+
+ /* set up parameters */
+ params.target = bitmap;
+ params.source = outline;
+ params.flags = FT_RASTER_FLAG_AA;
+
+#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
+
+ /* implode outline if needed */
+ {
+ FT_Vector* points = outline->points;
+ FT_Vector* points_end = points + outline->n_points;
+ FT_Vector* vec;
+
+
+ if ( hmul )
+ for ( vec = points; vec < points_end; vec++ )
+ vec->x *= 3;
+
+ if ( vmul )
+ for ( vec = points; vec < points_end; vec++ )
+ vec->y *= 3;
+ }
+
+ /* render outline into the bitmap */
+ error = render->raster_render( render->raster, &params );
+
+ /* deflate outline if needed */
+ {
+ FT_Vector* points = outline->points;
+ FT_Vector* points_end = points + outline->n_points;
+ FT_Vector* vec;
+
+
+ if ( hmul )
+ for ( vec = points; vec < points_end; vec++ )
+ vec->x /= 3;
+
+ if ( vmul )
+ for ( vec = points; vec < points_end; vec++ )
+ vec->y /= 3;
+ }
+
+ if ( slot->library->lcd_filter_func )
+ slot->library->lcd_filter_func( bitmap, mode, slot->library );
+
+#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+ /* render outline into bitmap */
+ error = render->raster_render( render->raster, &params );
+
+ /* expand it horizontally */
+ if ( hmul )
+ {
+ FT_Byte* line = bitmap->buffer;
+ FT_UInt hh;
+
+
+ for ( hh = height_org; hh > 0; hh--, line += pitch )
+ {
+ FT_UInt xx;
+ FT_Byte* end = line + width;
+
+
+ for ( xx = width_org; xx > 0; xx-- )
+ {
+ FT_UInt pixel = line[xx-1];
+
+
+ end[-3] = (FT_Byte)pixel;
+ end[-2] = (FT_Byte)pixel;
+ end[-1] = (FT_Byte)pixel;
+ end -= 3;
+ }
+ }
+ }
+
+ /* expand it vertically */
+ if ( vmul )
+ {
+ FT_Byte* read = bitmap->buffer + ( height - height_org ) * pitch;
+ FT_Byte* write = bitmap->buffer;
+ FT_UInt hh;
+
+
+ for ( hh = height_org; hh > 0; hh-- )
+ {
+ memcpy( write, read, pitch );
+ write += pitch;
+
+ memcpy( write, read, pitch );
+ write += pitch;
+
+ memcpy( write, read, pitch );
+ write += pitch;
+ read += pitch;
+ }
+ }
+
+#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
+
+ FT_Outline_Translate( outline, x_shift, y_shift );
+
+ if ( error )
+ goto Exit;
+
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+ slot->bitmap_left = x_left;
+ slot->bitmap_top = y_top;
+
+ Exit:
+ if ( outline && origin )
+ FT_Outline_Translate( outline, -origin->x, -origin->y );
+
+ return error;
+ }
+
+
+ /* convert a slot's glyph image into a bitmap */
+ static FT_Error
+ ft_smooth_render( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ if ( mode == FT_RENDER_MODE_LIGHT )
+ mode = FT_RENDER_MODE_NORMAL;
+
+ return ft_smooth_render_generic( render, slot, mode, origin,
+ FT_RENDER_MODE_NORMAL );
+ }
+
+
+ /* convert a slot's glyph image into a horizontal LCD bitmap */
+ static FT_Error
+ ft_smooth_render_lcd( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ FT_Error error;
+
+ error = ft_smooth_render_generic( render, slot, mode, origin,
+ FT_RENDER_MODE_LCD );
+ if ( !error )
+ slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD;
+
+ return error;
+ }
+
+
+ /* convert a slot's glyph image into a vertical LCD bitmap */
+ static FT_Error
+ ft_smooth_render_lcd_v( FT_Renderer render,
+ FT_GlyphSlot slot,
+ FT_Render_Mode mode,
+ const FT_Vector* origin )
+ {
+ FT_Error error;
+
+ error = ft_smooth_render_generic( render, slot, mode, origin,
+ FT_RENDER_MODE_LCD_V );
+ if ( !error )
+ slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD_V;
+
+ return error;
+ }
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Renderer_Class ft_smooth_renderer_class =
+ {
+ {
+ FT_MODULE_RENDERER,
+ sizeof( FT_RendererRec ),
+
+ "smooth",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module specific interface */
+
+ (FT_Module_Constructor)ft_smooth_init,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ },
+
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Renderer_RenderFunc) ft_smooth_render,
+ (FT_Renderer_TransformFunc)ft_smooth_transform,
+ (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
+ (FT_Renderer_SetModeFunc) ft_smooth_set_mode,
+
+ (FT_Raster_Funcs*) &ft_grays_raster
+ };
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Renderer_Class ft_smooth_lcd_renderer_class =
+ {
+ {
+ FT_MODULE_RENDERER,
+ sizeof( FT_RendererRec ),
+
+ "smooth-lcd",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module specific interface */
+
+ (FT_Module_Constructor)ft_smooth_init,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ },
+
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Renderer_RenderFunc) ft_smooth_render_lcd,
+ (FT_Renderer_TransformFunc)ft_smooth_transform,
+ (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
+ (FT_Renderer_SetModeFunc) ft_smooth_set_mode,
+
+ (FT_Raster_Funcs*) &ft_grays_raster
+ };
+
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Renderer_Class ft_smooth_lcdv_renderer_class =
+ {
+ {
+ FT_MODULE_RENDERER,
+ sizeof( FT_RendererRec ),
+
+ "smooth-lcdv",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* module specific interface */
+
+ (FT_Module_Constructor)ft_smooth_init,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) 0
+ },
+
+ FT_GLYPH_FORMAT_OUTLINE,
+
+ (FT_Renderer_RenderFunc) ft_smooth_render_lcd_v,
+ (FT_Renderer_TransformFunc)ft_smooth_transform,
+ (FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
+ (FT_Renderer_SetModeFunc) ft_smooth_set_mode,
+
+ (FT_Raster_Funcs*) &ft_grays_raster
+ };
+
+
+/* END */
diff --git a/src/smooth/ftsmooth.h b/src/smooth/ftsmooth.h
new file mode 100644
index 0000000..62cced4
--- /dev/null
+++ b/src/smooth/ftsmooth.h
@@ -0,0 +1,49 @@
+/***************************************************************************/
+/* */
+/* ftsmooth.h */
+/* */
+/* Anti-aliasing renderer interface (specification). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __FTSMOOTH_H__
+#define __FTSMOOTH_H__
+
+
+#include <ft2build.h>
+#include FT_RENDER_H
+
+
+FT_BEGIN_HEADER
+
+
+#ifndef FT_CONFIG_OPTION_NO_STD_RASTER
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_std_renderer_class;
+#endif
+
+#ifndef FT_CONFIG_OPTION_NO_SMOOTH_RASTER
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_smooth_renderer_class;
+
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_smooth_lcd_renderer_class;
+
+ FT_EXPORT_VAR( const FT_Renderer_Class ) ft_smooth_lcd_v_renderer_class;
+#endif
+
+
+
+FT_END_HEADER
+
+#endif /* __FTSMOOTH_H__ */
+
+
+/* END */
diff --git a/src/smooth/module.mk b/src/smooth/module.mk
new file mode 100644
index 0000000..05ad4ba
--- /dev/null
+++ b/src/smooth/module.mk
@@ -0,0 +1,27 @@
+#
+# FreeType 2 smooth renderer module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += SMOOTH_RENDERER
+
+define SMOOTH_RENDERER
+$(OPEN_DRIVER)ft_smooth_renderer_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)smooth $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer$(ECHO_DRIVER_DONE)
+$(OPEN_DRIVER)ft_smooth_lcd_renderer_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)smooth $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer for LCDs$(ECHO_DRIVER_DONE)
+$(OPEN_DRIVER)ft_smooth_lcdv_renderer_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)smooth $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer for vertical LCDs$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/smooth/rules.mk b/src/smooth/rules.mk
new file mode 100644
index 0000000..4f27f01
--- /dev/null
+++ b/src/smooth/rules.mk
@@ -0,0 +1,69 @@
+#
+# FreeType 2 smooth renderer module build rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# smooth driver directory
+#
+SMOOTH_DIR := $(SRC_DIR)/smooth
+
+# compilation flags for the driver
+#
+SMOOTH_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(SMOOTH_DIR))
+
+
+# smooth driver sources (i.e., C files)
+#
+SMOOTH_DRV_SRC := $(SMOOTH_DIR)/ftgrays.c \
+ $(SMOOTH_DIR)/ftsmooth.c
+
+
+# smooth driver headers
+#
+SMOOTH_DRV_H := $(SMOOTH_DRV_SRC:%c=%h) \
+ $(SMOOTH_DIR)/ftsmerrs.h
+
+
+# smooth driver object(s)
+#
+# SMOOTH_DRV_OBJ_M is used during `multi' builds.
+# SMOOTH_DRV_OBJ_S is used during `single' builds.
+#
+SMOOTH_DRV_OBJ_M := $(SMOOTH_DRV_SRC:$(SMOOTH_DIR)/%.c=$(OBJ_DIR)/%.$O)
+SMOOTH_DRV_OBJ_S := $(OBJ_DIR)/smooth.$O
+
+# smooth driver source file for single build
+#
+SMOOTH_DRV_SRC_S := $(SMOOTH_DIR)/smooth.c
+
+
+# smooth driver - single object
+#
+$(SMOOTH_DRV_OBJ_S): $(SMOOTH_DRV_SRC_S) $(SMOOTH_DRV_SRC) \
+ $(FREETYPE_H) $(SMOOTH_DRV_H)
+ $(SMOOTH_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(SMOOTH_DRV_SRC_S))
+
+
+# smooth driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(SMOOTH_DIR)/%.c $(FREETYPE_H) $(SMOOTH_DRV_H)
+ $(SMOOTH_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(SMOOTH_DRV_OBJ_S)
+DRV_OBJS_M += $(SMOOTH_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/smooth/smooth.c b/src/smooth/smooth.c
new file mode 100644
index 0000000..ff6be3e
--- /dev/null
+++ b/src/smooth/smooth.c
@@ -0,0 +1,26 @@
+/***************************************************************************/
+/* */
+/* smooth.c */
+/* */
+/* FreeType anti-aliasing rasterer module component (body only). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "ftgrays.c"
+#include "ftsmooth.c"
+
+
+/* END */
diff --git a/src/tools/Jamfile b/src/tools/Jamfile
new file mode 100644
index 0000000..475161e
--- /dev/null
+++ b/src/tools/Jamfile
@@ -0,0 +1,5 @@
+# Jamfile for src/tools
+#
+SubDir FT2_TOP src tools ;
+
+Main apinames : apinames.c ;
diff --git a/src/tools/apinames.c b/src/tools/apinames.c
new file mode 100644
index 0000000..f08919b
--- /dev/null
+++ b/src/tools/apinames.c
@@ -0,0 +1,443 @@
+/*
+ * This little program is used to parse the FreeType headers and
+ * find the declaration of all public APIs. This is easy, because
+ * they all look like the following:
+ *
+ * FT_EXPORT( return_type )
+ * function_name( function arguments );
+ *
+ * You must pass the list of header files as arguments. Wildcards are
+ * accepted if you are using GCC for compilation (and probably by
+ * other compilers too).
+ *
+ * Author: David Turner, 2005, 2006
+ *
+ * This code is explicitly placed into the public domain.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define PROGRAM_NAME "apinames"
+#define PROGRAM_VERSION "0.1"
+
+#define LINEBUFF_SIZE 1024
+
+typedef enum
+{
+ OUTPUT_LIST = 0, /* output the list of names, one per line */
+ OUTPUT_WINDOWS_DEF, /* output a Windows .DEF file for Visual C++ or Mingw */
+ OUTPUT_BORLAND_DEF, /* output a Windows .DEF file for Borland C++ */
+ OUTPUT_WATCOM_LBC /* output a Watcom Linker Command File */
+
+} OutputFormat;
+
+
+static void
+panic( const char* message )
+{
+ fprintf( stderr, "PANIC: %s\n", message );
+ exit(2);
+}
+
+
+typedef struct
+{
+ char* name;
+ unsigned int hash;
+
+} NameRec, *Name;
+
+static Name the_names;
+static int num_names;
+static int max_names;
+
+static void
+names_add( const char* name,
+ const char* end )
+{
+ int nn, len, h;
+ Name nm;
+
+ if ( end <= name )
+ return;
+
+ /* compute hash value */
+ len = (int)(end - name);
+ h = 0;
+ for ( nn = 0; nn < len; nn++ )
+ h = h*33 + name[nn];
+
+ /* check for an pre-existing name */
+ for ( nn = 0; nn < num_names; nn++ )
+ {
+ nm = the_names + nn;
+
+ if ( (int)nm->hash == h &&
+ memcmp( name, nm->name, len ) == 0 &&
+ nm->name[len] == 0 )
+ return;
+ }
+
+ /* add new name */
+ if ( num_names >= max_names )
+ {
+ max_names += (max_names >> 1) + 4;
+ the_names = (NameRec*)realloc( the_names, sizeof(the_names[0])*max_names );
+ if ( the_names == NULL )
+ panic( "not enough memory" );
+ }
+ nm = &the_names[num_names++];
+
+ nm->hash = h;
+ nm->name = (char*)malloc( len+1 );
+ if ( nm->name == NULL )
+ panic( "not enough memory" );
+
+ memcpy( nm->name, name, len );
+ nm->name[len] = 0;
+}
+
+
+static int
+name_compare( const void* name1,
+ const void* name2 )
+{
+ Name n1 = (Name)name1;
+ Name n2 = (Name)name2;
+
+ return strcmp( n1->name, n2->name );
+}
+
+static void
+names_sort( void )
+{
+ qsort( the_names, (size_t)num_names, sizeof(the_names[0]), name_compare );
+}
+
+
+static void
+names_dump( FILE* out,
+ OutputFormat format,
+ const char* dll_name )
+{
+ int nn;
+
+ switch ( format )
+ {
+ case OUTPUT_WINDOWS_DEF:
+ if ( dll_name )
+ fprintf( out, "LIBRARY %s\n", dll_name );
+
+ fprintf( out, "DESCRIPTION FreeType 2 DLL\n" );
+ fprintf( out, "EXPORTS\n" );
+ for ( nn = 0; nn < num_names; nn++ )
+ fprintf( out, " %s\n", the_names[nn].name );
+ break;
+
+ case OUTPUT_BORLAND_DEF:
+ if ( dll_name )
+ fprintf( out, "LIBRARY %s\n", dll_name );
+
+ fprintf( out, "DESCRIPTION FreeType 2 DLL\n" );
+ fprintf( out, "EXPORTS\n" );
+ for ( nn = 0; nn < num_names; nn++ )
+ fprintf( out, " _%s\n", the_names[nn].name );
+ break;
+
+ case OUTPUT_WATCOM_LBC:
+ {
+ /* we must omit the .dll suffix from the library name */
+ char temp[512];
+ char* dot;
+
+ if ( dll_name == NULL )
+ {
+ fprintf( stderr,
+ "you must provide a DLL name with the -d option !!\n" );
+ exit(4);
+ }
+
+ dot = strchr( dll_name, '.' );
+ if ( dot != NULL )
+ {
+ int len = (dot - dll_name);
+ if ( len > (int)(sizeof(temp)-1) )
+ len = sizeof(temp)-1;
+
+ memcpy( temp, dll_name, len );
+ temp[len] = 0;
+
+ dll_name = (const char*)temp;
+ }
+
+ for ( nn = 0; nn < num_names; nn++ )
+ fprintf( out, "++_%s.%s.%s\n", the_names[nn].name, dll_name,
+ the_names[nn].name );
+ }
+ break;
+
+ default: /* LIST */
+ for ( nn = 0; nn < num_names; nn++ )
+ fprintf( out, "%s\n", the_names[nn].name );
+ }
+}
+
+
+
+
+/* states of the line parser */
+
+typedef enum
+{
+ STATE_START = 0, /* waiting for FT_EXPORT keyword and return type */
+ STATE_TYPE /* type was read, waiting for function name */
+
+} State;
+
+static int
+read_header_file( FILE* file, int verbose )
+{
+ static char buff[ LINEBUFF_SIZE+1 ];
+ State state = STATE_START;
+
+ while ( !feof( file ) )
+ {
+ char* p;
+
+ if ( !fgets( buff, LINEBUFF_SIZE, file ) )
+ break;
+
+ p = buff;
+
+ while ( *p && (*p == ' ' || *p == '\\') ) /* skip leading whitespace */
+ p++;
+
+ if ( *p == '\n' || *p == '\r' ) /* skip empty lines */
+ continue;
+
+ switch ( state )
+ {
+ case STATE_START:
+ {
+ if ( memcmp( p, "FT_EXPORT(", 10 ) != 0 )
+ break;
+
+ p += 10;
+ for (;;)
+ {
+ if ( *p == 0 || *p == '\n' || *p == '\r' )
+ goto NextLine;
+
+ if ( *p == ')' )
+ {
+ p++;
+ break;
+ }
+
+ p++;
+ }
+
+ state = STATE_TYPE;
+
+ /* sometimes, the name is just after the FT_EXPORT(...), so
+ * skip whitespace, and fall-through if we find an alphanumeric
+ * character
+ */
+ while ( *p == ' ' || *p == '\t' )
+ p++;
+
+ if ( !isalpha(*p) )
+ break;
+ }
+ /* fall-through */
+
+ case STATE_TYPE:
+ {
+ char* name = p;
+
+ while ( isalnum(*p) || *p == '_' )
+ p++;
+
+ if ( p > name )
+ {
+ if ( verbose )
+ fprintf( stderr, ">>> %.*s\n", p-name, name );
+
+ names_add( name, p );
+ }
+
+ state = STATE_START;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ NextLine:
+ ;
+ }
+
+ return 0;
+}
+
+
+static void
+usage( void )
+{
+ static const char* const format =
+ "%s %s: extract FreeType API names from header files\n\n"
+ "this program is used to extract the list of public FreeType API\n"
+ "functions. It receives the list of header files as argument and\n"
+ "generates a sorted list of unique identifiers\n\n"
+
+ "usage: %s header1 [options] [header2 ...]\n\n"
+
+ "options: - : parse the content of stdin, ignore arguments\n"
+ " -v : verbose mode, output sent to standard error\n"
+ " -oFILE : write output to FILE instead of standard output\n"
+ " -dNAME : indicate DLL file name, 'freetype.dll' by default\n"
+ " -w : output .DEF file for Visual C++ and Mingw\n"
+ " -wB : output .DEF file for Borland C++\n"
+ " -wW : output Watcom Linker Response File\n"
+ "\n";
+
+ fprintf( stderr,
+ format,
+ PROGRAM_NAME,
+ PROGRAM_VERSION,
+ PROGRAM_NAME
+ );
+ exit(1);
+}
+
+
+int main( int argc, const char* const* argv )
+{
+ int from_stdin = 0;
+ int verbose = 0;
+ OutputFormat format = OUTPUT_LIST; /* the default */
+ FILE* out = stdout;
+ const char* library_name = NULL;
+
+ if ( argc < 2 )
+ usage();
+
+ /* '-' used as a single argument means read source file from stdin */
+ while ( argc > 1 && argv[1][0] == '-' )
+ {
+ const char* arg = argv[1];
+
+ switch ( arg[1] )
+ {
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'o':
+ if ( arg[2] == 0 )
+ {
+ if ( argc < 2 )
+ usage();
+
+ arg = argv[2];
+ argv++;
+ argc--;
+ }
+ else
+ arg += 2;
+
+ out = fopen( arg, "wt" );
+ if ( out == NULL )
+ {
+ fprintf( stderr, "could not open '%s' for writing\n", argv[2] );
+ exit(3);
+ }
+ break;
+
+ case 'd':
+ if ( arg[2] == 0 )
+ {
+ if ( argc < 2 )
+ usage();
+
+ arg = argv[2];
+ argv++;
+ argc--;
+ }
+ else
+ arg += 2;
+
+ library_name = arg;
+ break;
+
+ case 'w':
+ format = OUTPUT_WINDOWS_DEF;
+ switch ( arg[2] )
+ {
+ case 'B':
+ format = OUTPUT_BORLAND_DEF;
+ break;
+
+ case 'W':
+ format = OUTPUT_WATCOM_LBC;
+ break;
+
+ case 0:
+ break;
+
+ default:
+ usage();
+ }
+ break;
+
+ case 0:
+ from_stdin = 1;
+ break;
+
+ default:
+ usage();
+ }
+
+ argc--;
+ argv++;
+ }
+
+ if ( from_stdin )
+ {
+ read_header_file( stdin, verbose );
+ }
+ else
+ {
+ for ( --argc, argv++; argc > 0; argc--, argv++ )
+ {
+ FILE* file = fopen( argv[0], "rb" );
+
+ if ( file == NULL )
+ fprintf( stderr, "unable to open '%s'\n", argv[0] );
+ else
+ {
+ if ( verbose )
+ fprintf( stderr, "opening '%s'\n", argv[0] );
+
+ read_header_file( file, verbose );
+ fclose( file );
+ }
+ }
+ }
+
+ if ( num_names == 0 )
+ panic( "could not find exported functions !!\n" );
+
+ names_sort();
+ names_dump( out, format, library_name );
+
+ if ( out != stdout )
+ fclose( out );
+
+ return 0;
+}
diff --git a/src/tools/cordic.py b/src/tools/cordic.py
new file mode 100644
index 0000000..3f80c5f
--- /dev/null
+++ b/src/tools/cordic.py
@@ -0,0 +1,79 @@
+# compute arctangent table for CORDIC computations in fttrigon.c
+import sys, math
+
+#units = 64*65536.0 # don't change !!
+units = 256
+scale = units/math.pi
+shrink = 1.0
+comma = ""
+
+def calc_val( x ):
+ global units, shrink
+ angle = math.atan(x)
+ shrink = shrink * math.cos(angle)
+ return angle/math.pi * units
+
+def print_val( n, x ):
+ global comma
+
+ lo = int(x)
+ hi = lo + 1
+ alo = math.atan(lo)
+ ahi = math.atan(hi)
+ ax = math.atan(2.0**n)
+
+ errlo = abs( alo - ax )
+ errhi = abs( ahi - ax )
+
+ if ( errlo < errhi ):
+ hi = lo
+
+ sys.stdout.write( comma + repr( int(hi) ) )
+ comma = ", "
+
+
+print ""
+print "table of arctan( 1/2^n ) for PI = " + repr(units/65536.0) + " units"
+
+# compute range of "i"
+r = [-1]
+r = r + range(32)
+
+for n in r:
+
+ if n >= 0:
+ x = 1.0/(2.0**n) # tangent value
+ else:
+ x = 2.0**(-n)
+
+ angle = math.atan(x) # arctangent
+ angle2 = angle*scale # arctangent in FT_Angle units
+
+ # determine which integer value for angle gives the best tangent
+ lo = int(angle2)
+ hi = lo + 1
+ tlo = math.tan(lo/scale)
+ thi = math.tan(hi/scale)
+
+ errlo = abs( tlo - x )
+ errhi = abs( thi - x )
+
+ angle2 = hi
+ if errlo < errhi:
+ angle2 = lo
+
+ if angle2 <= 0:
+ break
+
+ sys.stdout.write( comma + repr( int(angle2) ) )
+ comma = ", "
+
+ shrink = shrink * math.cos( angle2/scale)
+
+
+print
+print "shrink factor = " + repr( shrink )
+print "shrink factor 2 = " + repr( shrink * (2.0**32) )
+print "expansion factor = " + repr(1/shrink)
+print ""
+
diff --git a/src/tools/docmaker/content.py b/src/tools/docmaker/content.py
new file mode 100644
index 0000000..b14c52e
--- /dev/null
+++ b/src/tools/docmaker/content.py
@@ -0,0 +1,588 @@
+# Content (c) 2002, 2004, 2006, 2007 David Turner <david@freetype.org>
+#
+# This file contains routines used to parse the content of documentation
+# comment blocks and build more structured objects out of them.
+#
+
+from sources import *
+from utils import *
+import string, re
+
+
+# this regular expression is used to detect code sequences. these
+# are simply code fragments embedded in '{' and '}' like in:
+#
+# {
+# x = y + z;
+# if ( zookoo == 2 )
+# {
+# foobar();
+# }
+# }
+#
+# note that indentation of the starting and ending accolades must be
+# exactly the same. the code sequence can contain accolades at greater
+# indentation
+#
+re_code_start = re.compile( r"(\s*){\s*$" )
+re_code_end = re.compile( r"(\s*)}\s*$" )
+
+
+# this regular expression is used to isolate identifiers from
+# other text
+#
+re_identifier = re.compile( r'(\w*)' )
+
+
+#############################################################################
+#
+# The DocCode class is used to store source code lines.
+#
+# 'self.lines' contains a set of source code lines that will be dumped as
+# HTML in a <PRE> tag.
+#
+# The object is filled line by line by the parser; it strips the leading
+# "margin" space from each input line before storing it in 'self.lines'.
+#
+class DocCode:
+
+ def __init__( self, margin, lines ):
+ self.lines = []
+ self.words = None
+
+ # remove margin spaces
+ for l in lines:
+ if string.strip( l[:margin] ) == "":
+ l = l[margin:]
+ self.lines.append( l )
+
+ def dump( self, prefix = "", width=60 ):
+ lines = self.dump_lines( 0, width )
+ for l in lines:
+ print prefix + l
+
+ def dump_lines( self, margin=0, width=60 ):
+ result = []
+ for l in self.lines:
+ result.append( " "*margin + l )
+ return result
+
+
+
+#############################################################################
+#
+# The DocPara class is used to store "normal" text paragraph.
+#
+# 'self.words' contains the list of words that make up the paragraph
+#
+class DocPara:
+
+ def __init__( self, lines ):
+ self.lines = None
+ self.words = []
+ for l in lines:
+ l = string.strip(l)
+ self.words.extend( string.split( l ) )
+
+ def dump( self, prefix = "", width = 60 ):
+ lines = self.dump_lines( 0, width )
+ for l in lines:
+ print prefix + l
+
+ def dump_lines( self, margin=0, width = 60 ):
+ cur = "" # current line
+ col = 0 # current width
+ result = []
+
+ for word in self.words:
+ ln = len(word)
+ if col > 0:
+ ln = ln+1
+
+ if col + ln > width:
+ result.append( " "*margin + cur )
+ cur = word
+ col = len(word)
+ else:
+ if col > 0:
+ cur = cur + " "
+ cur = cur + word
+ col = col + ln
+
+ if col > 0:
+ result.append( " "*margin + cur )
+
+ return result
+
+
+
+
+#############################################################################
+#
+# The DocField class is used to store a list containing either DocPara or
+# DocCode objects. Each DocField also has an optional "name" which is used
+# when the object corresponds to a field or value definition
+#
+class DocField:
+
+ def __init__( self, name, lines ):
+
+ self.name = name # can be None for normal paragraphs/sources
+ self.items = [] # list of items
+
+ mode_none = 0 # start parsing mode
+ mode_code = 1 # parsing code sequences
+ mode_para = 3 # parsing normal paragraph
+
+ margin = -1 # current code sequence indentation
+ cur_lines = []
+
+ # now analyze the markup lines to see if they contain paragraphs,
+ # code sequences or fields definitions
+ #
+ start = 0
+ mode = mode_none
+ for l in lines:
+
+ # are we parsing a code sequence ?
+ if mode == mode_code:
+
+ m = re_code_end.match( l )
+ if m and len(m.group(1)) <= margin:
+ # that's it, we finised the code sequence
+ code = DocCode( 0, cur_lines )
+ self.items.append( code )
+ margin = -1
+ cur_lines = []
+ mode = mode_none
+ else:
+ # nope, continue the code sequence
+ cur_lines.append( l[margin:] )
+ else:
+ # start of code sequence ?
+ m = re_code_start.match( l )
+ if m:
+ # save current lines
+ if cur_lines:
+ para = DocPara( cur_lines )
+ self.items.append( para )
+ cur_lines = []
+
+ # switch to code extraction mode
+ margin = len(m.group(1))
+ mode = mode_code
+
+ else:
+ if not string.split( l ) and cur_lines:
+ # if the line is empty, we end the current paragraph,
+ # if any
+ para = DocPara( cur_lines )
+ self.items.append( para )
+ cur_lines = []
+ else:
+ # otherwise, simply add the line to the current
+ # paragraph
+ cur_lines.append( l )
+
+ if mode == mode_code:
+ # unexpected end of code sequence
+ code = DocCode( margin, cur_lines )
+ self.items.append( code )
+
+ elif cur_lines:
+ para = DocPara( cur_lines )
+ self.items.append( para )
+
+ def dump( self, prefix = "" ):
+ if self.field:
+ print prefix + self.field + " ::"
+ prefix = prefix + "----"
+
+ first = 1
+ for p in self.items:
+ if not first:
+ print ""
+ p.dump( prefix )
+ first = 0
+
+ def dump_lines( self, margin=0, width=60 ):
+ result = []
+ nl = None
+ for p in self.items:
+ if nl:
+ result.append( "" )
+
+ result.extend( p.dump_lines( margin, width ) )
+ nl = 1
+
+ return result
+
+# this regular expression is used to detect field definitions
+#
+re_field = re.compile( r"\s*(\w*|\w(\w|\.)*\w)\s*::" )
+
+
+
+class DocMarkup:
+
+ def __init__( self, tag, lines ):
+ self.tag = string.lower(tag)
+ self.fields = []
+
+ cur_lines = []
+ field = None
+ mode = 0
+
+ for l in lines:
+ m = re_field.match( l )
+ if m:
+ # we detected the start of a new field definition
+
+ # first, save the current one
+ if cur_lines:
+ f = DocField( field, cur_lines )
+ self.fields.append( f )
+ cur_lines = []
+ field = None
+
+ field = m.group(1) # record field name
+ ln = len(m.group(0))
+ l = " "*ln + l[ln:]
+ cur_lines = [ l ]
+ else:
+ cur_lines.append( l )
+
+ if field or cur_lines:
+ f = DocField( field, cur_lines )
+ self.fields.append( f )
+
+ def get_name( self ):
+ try:
+ return self.fields[0].items[0].words[0]
+
+ except:
+ return None
+
+ def get_start( self ):
+ try:
+ result = ""
+ for word in self.fields[0].items[0].words:
+ result = result + " " + word
+ return result[1:]
+
+ except:
+ return "ERROR"
+
+ def dump( self, margin ):
+ print " "*margin + "<" + self.tag + ">"
+ for f in self.fields:
+ f.dump( " " )
+ print " "*margin + "</" + self.tag + ">"
+
+
+
+
+class DocChapter:
+
+ def __init__( self, block ):
+ self.block = block
+ self.sections = []
+ if block:
+ self.name = block.name
+ self.title = block.get_markup_words( "title" )
+ self.order = block.get_markup_words( "sections" )
+ else:
+ self.name = "Other"
+ self.title = string.split( "Miscellaneous" )
+ self.order = []
+
+
+
+class DocSection:
+
+ def __init__( self, name = "Other" ):
+ self.name = name
+ self.blocks = {}
+ self.block_names = [] # ordered block names in section
+ self.defs = []
+ self.abstract = ""
+ self.description = ""
+ self.order = []
+ self.title = "ERROR"
+ self.chapter = None
+
+ def add_def( self, block ):
+ self.defs.append( block )
+
+ def add_block( self, block ):
+ self.block_names.append( block.name )
+ self.blocks[ block.name ] = block
+
+ def process( self ):
+ # lookup one block that contains a valid section description
+ for block in self.defs:
+ title = block.get_markup_text( "title" )
+ if title:
+ self.title = title
+ self.abstract = block.get_markup_words( "abstract" )
+ self.description = block.get_markup_items( "description" )
+ self.order = block.get_markup_words( "order" )
+ return
+
+ def reorder( self ):
+
+ self.block_names = sort_order_list( self.block_names, self.order )
+
+
+class ContentProcessor:
+
+ def __init__( self ):
+ """initialize a block content processor"""
+ self.reset()
+
+ self.sections = {} # dictionary of documentation sections
+ self.section = None # current documentation section
+
+ self.chapters = [] # list of chapters
+
+ def set_section( self, section_name ):
+ """set current section during parsing"""
+ if not self.sections.has_key( section_name ):
+ section = DocSection( section_name )
+ self.sections[ section_name ] = section
+ self.section = section
+ else:
+ self.section = self.sections[ section_name ]
+
+ def add_chapter( self, block ):
+ chapter = DocChapter( block )
+ self.chapters.append( chapter )
+
+
+ def reset( self ):
+ """reset the content processor for a new block"""
+ self.markups = []
+ self.markup = None
+ self.markup_lines = []
+
+ def add_markup( self ):
+ """add a new markup section"""
+ if self.markup and self.markup_lines:
+
+ # get rid of last line of markup if it's empty
+ marks = self.markup_lines
+ if len(marks) > 0 and not string.strip(marks[-1]):
+ self.markup_lines = marks[:-1]
+
+ m = DocMarkup( self.markup, self.markup_lines )
+
+ self.markups.append( m )
+
+ self.markup = None
+ self.markup_lines = []
+
+
+ def process_content( self, content ):
+ """process a block content and return a list of DocMarkup objects
+ corresponding to it"""
+ markup = None
+ markup_lines = []
+ first = 1
+
+ for line in content:
+ found = None
+ for t in re_markup_tags:
+ m = t.match( line )
+ if m:
+ found = string.lower(m.group(1))
+ prefix = len(m.group(0))
+ line = " "*prefix + line[prefix:] # remove markup from line
+ break
+
+ # is it the start of a new markup section ?
+ if found:
+ first = 0
+ self.add_markup() # add current markup content
+ self.markup = found
+ if len(string.strip( line )) > 0:
+ self.markup_lines.append( line )
+ elif first == 0:
+ self.markup_lines.append( line )
+
+ self.add_markup()
+
+ return self.markups
+
+
+ def parse_sources( self, source_processor ):
+ blocks = source_processor.blocks
+ count = len(blocks)
+ for n in range(count):
+
+ source = blocks[n]
+ if source.content:
+ # this is a documentation comment, we need to catch
+ # all following normal blocks in the "follow" list
+ #
+ follow = []
+ m = n+1
+ while m < count and not blocks[m].content:
+ follow.append( blocks[m] )
+ m = m+1
+
+ doc_block = DocBlock( source, follow, self )
+
+
+ def finish( self ):
+
+ # process all sections to extract their abstract, description
+ # and ordered list of items
+ #
+ for sec in self.sections.values():
+ sec.process()
+
+ # process chapters to check that all sections are correctly
+ # listed there
+ for chap in self.chapters:
+ for sec in chap.order:
+ if self.sections.has_key(sec):
+ section = self.sections[ sec ]
+ section.chapter = chap
+ section.reorder()
+ chap.sections.append( section )
+ else:
+ sys.stderr.write( "WARNING: chapter '" +
+ chap.name + "' in " + chap.block.location() + \
+ " lists unknown section '" + sec + "'\n" )
+
+ # check that all sections are in a chapter
+ #
+ others = []
+ for sec in self.sections.values():
+ if not sec.chapter:
+ others.append(sec)
+
+ # create a new special chapter for all remaining sections
+ # when necessary
+ #
+ if others:
+ chap = DocChapter( None )
+ chap.sections = others
+ self.chapters.append( chap )
+
+
+
+class DocBlock:
+
+ def __init__( self, source, follow, processor ):
+
+ processor.reset()
+
+ self.source = source
+ self.code = []
+ self.type = "ERRTYPE"
+ self.name = "ERRNAME"
+ self.section = processor.section
+ self.markups = processor.process_content( source.content )
+
+ # compute block type from first markup tag
+ try:
+ self.type = self.markups[0].tag
+ except:
+ pass
+
+
+ # compute block name from first markup paragraph
+ try:
+ markup = self.markups[0]
+ para = markup.fields[0].items[0]
+ name = para.words[0]
+ m = re_identifier.match( name )
+ if m:
+ name = m.group(1)
+ self.name = name
+ except:
+ pass
+
+ # detect new section starts
+ if self.type == "section":
+ processor.set_section( self.name )
+ processor.section.add_def( self )
+
+ # detect new chapter
+ elif self.type == "chapter":
+ processor.add_chapter( self )
+
+ else:
+ processor.section.add_block( self )
+
+ # now, compute the source lines relevant to this documentation
+ # block. We keep normal comments in for obvious reasons (??)
+ source = []
+ for b in follow:
+ if b.format:
+ break
+ for l in b.lines:
+ # we use "/* */" as a separator
+ if re_source_sep.match( l ):
+ break
+ source.append( l )
+
+ # now strip the leading and trailing empty lines from the sources
+ start = 0
+ end = len( source )-1
+
+ while start < end and not string.strip( source[start] ):
+ start = start + 1
+
+ while start < end and not string.strip( source[end] ):
+ end = end - 1
+
+ source = source[start:end+1]
+
+ self.code = source
+
+
+ def location( self ):
+ return self.source.location()
+
+
+
+ def get_markup( self, tag_name ):
+ """return the DocMarkup corresponding to a given tag in a block"""
+ for m in self.markups:
+ if m.tag == string.lower(tag_name):
+ return m
+ return None
+
+
+ def get_markup_name( self, tag_name ):
+ """return the name of a given primary markup in a block"""
+ try:
+ m = self.get_markup( tag_name )
+ return m.get_name()
+ except:
+ return None
+
+
+ def get_markup_words( self, tag_name ):
+ try:
+ m = self.get_markup( tag_name )
+ return m.fields[0].items[0].words
+ except:
+ return []
+
+
+ def get_markup_text( self, tag_name ):
+ result = self.get_markup_words( tag_name )
+ return string.join( result )
+
+
+ def get_markup_items( self, tag_name ):
+ try:
+ m = self.get_markup( tag_name )
+ return m.fields[0].items
+ except:
+ return None
+
+# eof
diff --git a/src/tools/docmaker/docbeauty.py b/src/tools/docmaker/docbeauty.py
new file mode 100644
index 0000000..55c4329
--- /dev/null
+++ b/src/tools/docmaker/docbeauty.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+#
+# DocBeauty (c) 2003, 2004 David Turner <david@freetype.org>
+#
+# This program is used to beautify the documentation comments used
+# in the FreeType 2 public headers.
+#
+
+from sources import *
+from content import *
+from utils import *
+
+import utils
+
+import sys, os, time, string, getopt
+
+content_processor = ContentProcessor()
+
+
+def beautify_block( block ):
+ if block.content:
+ content_processor.reset()
+
+ markups = content_processor.process_content( block.content )
+ text = []
+ first = 1
+
+ for markup in markups:
+ text.extend( markup.beautify( first ) )
+ first = 0
+
+ # now beautify the documentation "borders" themselves
+ lines = [ " /*************************************************************************" ]
+ for l in text:
+ lines.append( " *" + l )
+ lines.append( " */" )
+
+ block.lines = lines
+
+
+def usage():
+ print "\nDocBeauty 0.1 Usage information\n"
+ print " docbeauty [options] file1 [ file2 ... ]\n"
+ print "using the following options:\n"
+ print " -h : print this page"
+ print " -b : backup original files with the 'orig' extension"
+ print ""
+ print " --backup : same as -b"
+
+
+def main( argv ):
+ """main program loop"""
+
+ global output_dir
+
+ try:
+ opts, args = getopt.getopt( sys.argv[1:],
+ "hb",
+ [ "help", "backup" ] )
+
+ except getopt.GetoptError:
+ usage()
+ sys.exit( 2 )
+
+ if args == []:
+ usage()
+ sys.exit( 1 )
+
+ # process options
+ #
+ output_dir = None
+ do_backup = None
+
+ for opt in opts:
+ if opt[0] in ( "-h", "--help" ):
+ usage()
+ sys.exit( 0 )
+
+ if opt[0] in ( "-b", "--backup" ):
+ do_backup = 1
+
+ # create context and processor
+ source_processor = SourceProcessor()
+
+ # retrieve the list of files to process
+ file_list = make_file_list( args )
+ for filename in file_list:
+ source_processor.parse_file( filename )
+ for block in source_processor.blocks:
+ beautify_block( block )
+ new_name = filename + ".new"
+ ok = None
+ try:
+ file = open( new_name, "wt" )
+ for block in source_processor.blocks:
+ for line in block.lines:
+ file.write( line )
+ file.write( "\n" )
+ file.close()
+ except:
+ ok = 0
+
+# if called from the command line
+#
+if __name__ == '__main__':
+ main( sys.argv )
+
+
+# eof
diff --git a/src/tools/docmaker/docmaker.py b/src/tools/docmaker/docmaker.py
new file mode 100644
index 0000000..d34b6e8
--- /dev/null
+++ b/src/tools/docmaker/docmaker.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+#
+# DocMaker (c) 2002, 2004 David Turner <david@freetype.org>
+#
+# This program is a re-write of the original DocMaker took used
+# to generate the API Reference of the FreeType font engine
+# by converting in-source comments into structured HTML.
+#
+# This new version is capable of outputting XML data, as well
+# as accepts more liberal formatting options.
+#
+# It also uses regular expression matching and substitution
+# to speed things significantly.
+#
+
+from sources import *
+from content import *
+from utils import *
+from formatter import *
+from tohtml import *
+
+import utils
+
+import sys, os, time, string, glob, getopt
+
+
+def usage():
+ print "\nDocMaker Usage information\n"
+ print " docmaker [options] file1 [ file2 ... ]\n"
+ print "using the following options:\n"
+ print " -h : print this page"
+ print " -t : set project title, as in '-t \"My Project\"'"
+ print " -o : set output directory, as in '-o mydir'"
+ print " -p : set documentation prefix, as in '-p ft2'"
+ print ""
+ print " --title : same as -t, as in '--title=\"My Project\"'"
+ print " --output : same as -o, as in '--output=mydir'"
+ print " --prefix : same as -p, as in '--prefix=ft2'"
+
+
+def main( argv ):
+ """main program loop"""
+
+ global output_dir
+
+ try:
+ opts, args = getopt.getopt( sys.argv[1:],
+ "ht:o:p:",
+ [ "help", "title=", "output=", "prefix=" ] )
+
+ except getopt.GetoptError:
+ usage()
+ sys.exit( 2 )
+
+ if args == []:
+ usage()
+ sys.exit( 1 )
+
+ # process options
+ #
+ project_title = "Project"
+ project_prefix = None
+ output_dir = None
+
+ for opt in opts:
+ if opt[0] in ( "-h", "--help" ):
+ usage()
+ sys.exit( 0 )
+
+ if opt[0] in ( "-t", "--title" ):
+ project_title = opt[1]
+
+ if opt[0] in ( "-o", "--output" ):
+ utils.output_dir = opt[1]
+
+ if opt[0] in ( "-p", "--prefix" ):
+ project_prefix = opt[1]
+
+ check_output( )
+
+ # create context and processor
+ source_processor = SourceProcessor()
+ content_processor = ContentProcessor()
+
+ # retrieve the list of files to process
+ file_list = make_file_list( args )
+ for filename in file_list:
+ source_processor.parse_file( filename )
+ content_processor.parse_sources( source_processor )
+
+ # process sections
+ content_processor.finish()
+
+ formatter = HtmlFormatter( content_processor, project_title, project_prefix )
+
+ formatter.toc_dump()
+ formatter.index_dump()
+ formatter.section_dump_all()
+
+
+# if called from the command line
+#
+if __name__ == '__main__':
+ main( sys.argv )
+
+
+# eof
diff --git a/src/tools/docmaker/formatter.py b/src/tools/docmaker/formatter.py
new file mode 100644
index 0000000..363410e
--- /dev/null
+++ b/src/tools/docmaker/formatter.py
@@ -0,0 +1,204 @@
+# Formatter (c) 2002, 2004, 2007 David Turner <david@freetype.org>
+#
+
+from sources import *
+from content import *
+from utils import *
+
+# This is the base Formatter class. Its purpose is to convert
+# a content processor's data into specific documents (i.e., table of
+# contents, global index, and individual API reference indices).
+#
+# You need to sub-class it to output anything sensible. For example,
+# the file tohtml.py contains the definition of the HtmlFormatter sub-class
+# used to output -- you guessed it -- HTML.
+#
+
+class Formatter:
+
+ def __init__( self, processor ):
+
+ self.processor = processor
+ self.identifiers = {}
+ self.chapters = processor.chapters
+ self.sections = processor.sections.values()
+ self.block_index = []
+
+ # store all blocks in a dictionary
+ self.blocks = []
+ for section in self.sections:
+ for block in section.blocks.values():
+ self.add_identifier( block.name, block )
+
+ # add enumeration values to the index, since this is useful
+ for markup in block.markups:
+ if markup.tag == 'values':
+ for field in markup.fields:
+ self.add_identifier( field.name, block )
+
+
+ self.block_index = self.identifiers.keys()
+ self.block_index.sort( index_sort )
+
+
+ def add_identifier( self, name, block ):
+ if self.identifiers.has_key( name ):
+ # duplicate name !!
+ sys.stderr.write( \
+ "WARNING: duplicate definition for '" + name + "' in " + \
+ block.location() + ", previous definition in " + \
+ self.identifiers[ name ].location() + "\n" )
+ else:
+ self.identifiers[name] = block
+
+
+ #
+ # Formatting the table of contents
+ #
+
+ def toc_enter( self ):
+ pass
+
+ def toc_chapter_enter( self, chapter ):
+ pass
+
+ def toc_section_enter( self, section ):
+ pass
+
+ def toc_section_exit( self, section ):
+ pass
+
+ def toc_chapter_exit( self, chapter ):
+ pass
+
+ def toc_index( self, index_filename ):
+ pass
+
+ def toc_exit( self ):
+ pass
+
+ def toc_dump( self, toc_filename = None, index_filename = None ):
+
+ output = None
+ if toc_filename:
+ output = open_output( toc_filename )
+
+ self.toc_enter()
+
+ for chap in self.processor.chapters:
+
+ self.toc_chapter_enter( chap )
+
+ for section in chap.sections:
+ self.toc_section_enter( section )
+ self.toc_section_exit( section )
+
+ self.toc_chapter_exit ( chap )
+
+ self.toc_index( index_filename )
+
+ self.toc_exit()
+
+ if output:
+ close_output( output )
+
+ #
+ # Formatting the index
+ #
+
+ def index_enter( self ):
+ pass
+
+ def index_name_enter( self, name ):
+ pass
+
+ def index_name_exit( self, name ):
+ pass
+
+ def index_exit( self ):
+ pass
+
+ def index_dump( self, index_filename = None ):
+
+ output = None
+ if index_filename:
+ output = open_output( index_filename )
+
+ self.index_enter()
+
+ for name in self.block_index:
+ self.index_name_enter( name )
+ self.index_name_exit ( name )
+
+ self.index_exit()
+
+ if output:
+ close_output( output )
+
+ #
+ # Formatting a section
+ #
+ def section_enter( self, section ):
+ pass
+
+ def block_enter( self, block ):
+ pass
+
+ def markup_enter( self, markup, block = None ):
+ pass
+
+ def field_enter( self, field, markup = None, block = None ):
+ pass
+
+ def field_exit( self, field, markup = None, block = None ):
+ pass
+
+ def markup_exit( self, markup, block = None ):
+ pass
+
+ def block_exit( self, block ):
+ pass
+
+ def section_exit( self, section ):
+ pass
+
+
+ def section_dump( self, section, section_filename = None ):
+
+ output = None
+ if section_filename:
+ output = open_output( section_filename )
+
+ self.section_enter( section )
+
+ for name in section.block_names:
+ block = self.identifiers[ name ]
+ self.block_enter( block )
+
+ for markup in block.markups[1:]: # always ignore first markup !!
+ self.markup_enter( markup, block )
+
+ for field in markup.fields:
+ self.field_enter( field, markup, block )
+
+ self.field_exit ( field, markup, block )
+
+ self.markup_exit( markup, block )
+
+ self.block_exit( block )
+
+ self.section_exit ( section )
+
+ if output:
+ close_output( output )
+
+
+ def section_dump_all( self ):
+ for section in self.sections:
+ self.section_dump( section )
+
+ #
+ # Formatting a block
+ #
+
+# eof
diff --git a/src/tools/docmaker/sources.py b/src/tools/docmaker/sources.py
new file mode 100644
index 0000000..09ff7f9
--- /dev/null
+++ b/src/tools/docmaker/sources.py
@@ -0,0 +1,358 @@
+# Sources (c) 2002, 2003, 2004, 2006, 2007
+# David Turner <david@freetype.org>
+#
+#
+# this file contains definitions of classes needed to decompose
+# C sources files into a series of multi-line "blocks". There are
+# two kinds of blocks:
+#
+# - normal blocks, which contain source code or ordinary comments
+#
+# - documentation blocks, which have restricted formatting, and
+# whose text always start with a documentation markup tag like
+# "<Function>", "<Type>", etc..
+#
+# the routines used to process the content of documentation blocks
+# are not contained here, but in "content.py"
+#
+# the classes and methods found here only deal with text parsing
+# and basic documentation block extraction
+#
+import fileinput, re, sys, os, string
+
+
+
+
+
+
+################################################################
+##
+## BLOCK FORMAT PATTERN
+##
+## A simple class containing compiled regular expressions used
+## to detect potential documentation format block comments within
+## C source code
+##
+## note that the 'column' pattern must contain a group that will
+## be used to "unbox" the content of documentation comment blocks
+##
+class SourceBlockFormat:
+
+ def __init__( self, id, start, column, end ):
+ """create a block pattern, used to recognize special documentation blocks"""
+
+ self.id = id
+ self.start = re.compile( start, re.VERBOSE )
+ self.column = re.compile( column, re.VERBOSE )
+ self.end = re.compile( end, re.VERBOSE )
+
+
+
+#
+# format 1 documentation comment blocks look like the following:
+#
+# /************************************/
+# /* */
+# /* */
+# /* */
+# /************************************/
+#
+# we define a few regular expressions here to detect them
+#
+
+start = r'''
+ \s* # any number of whitespace
+ /\*{2,}/ # followed by '/' and at least two asterisks then '/'
+ \s*$ # eventually followed by whitespace
+'''
+
+column = r'''
+ \s* # any number of whitespace
+ /\*{1} # followed by '/' and precisely one asterisk
+ ([^*].*) # followed by anything (group 1)
+ \*{1}/ # followed by one asterisk and a '/'
+ \s*$ # eventually followed by whitespace
+'''
+
+re_source_block_format1 = SourceBlockFormat( 1, start, column, start )
+
+#
+# format 2 documentation comment blocks look like the following:
+#
+# /************************************ (at least 2 asterisks)
+# *
+# *
+# *
+# *
+# **/ (1 or more asterisks at the end)
+#
+# we define a few regular expressions here to detect them
+#
+start = r'''
+ \s* # any number of whitespace
+ /\*{2,} # followed by '/' and at least two asterisks
+ \s*$ # eventually followed by whitespace
+'''
+
+column = r'''
+ \s* # any number of whitespace
+ \*{1}(?!/) # followed by precisely one asterisk not followed by `/'
+ (.*) # then anything (group1)
+'''
+
+end = r'''
+ \s* # any number of whitespace
+ \*+/ # followed by at least one asterisk, then '/'
+'''
+
+re_source_block_format2 = SourceBlockFormat( 2, start, column, end )
+
+#
+# the list of supported documentation block formats, we could add new ones
+# relatively easily
+#
+re_source_block_formats = [ re_source_block_format1, re_source_block_format2 ]
+
+
+#
+# the following regular expressions corresponds to markup tags
+# within the documentation comment blocks. they're equivalent
+# despite their different syntax
+#
+# notice how each markup tag _must_ begin a new line
+#
+re_markup_tag1 = re.compile( r'''\s*<(\w*)>''' ) # <xxxx> format
+re_markup_tag2 = re.compile( r'''\s*@(\w*):''' ) # @xxxx: format
+
+#
+# the list of supported markup tags, we could add new ones relatively
+# easily
+#
+re_markup_tags = [ re_markup_tag1, re_markup_tag2 ]
+
+#
+# used to detect a cross-reference, after markup tags have been stripped
+#
+re_crossref = re.compile( r'@(\w*)(.*)' )
+
+#
+# used to detect italic and bold styles in paragraph text
+#
+re_italic = re.compile( r"_(\w(\w|')*)_(.*)" ) # _italic_
+re_bold = re.compile( r"\*(\w(\w|')*)\*(.*)" ) # *bold*
+
+#
+# used to detect the end of commented source lines
+#
+re_source_sep = re.compile( r'\s*/\*\s*\*/' )
+
+#
+# used to perform cross-reference within source output
+#
+re_source_crossref = re.compile( r'(\W*)(\w*)' )
+
+#
+# a list of reserved source keywords
+#
+re_source_keywords = re.compile( '''\\b ( typedef |
+ struct |
+ enum |
+ union |
+ const |
+ char |
+ int |
+ short |
+ long |
+ void |
+ signed |
+ unsigned |
+ \#include |
+ \#define |
+ \#undef |
+ \#if |
+ \#ifdef |
+ \#ifndef |
+ \#else |
+ \#endif ) \\b''', re.VERBOSE )
+
+################################################################
+##
+## SOURCE BLOCK CLASS
+##
+## A SourceProcessor is in charge or reading a C source file
+## and decomposing it into a series of different "SourceBlocks".
+## each one of these blocks can be made of the following data:
+##
+## - A documentation comment block that starts with "/**" and
+## whose exact format will be discussed later
+##
+## - normal sources lines, include comments
+##
+## the important fields in a text block are the following ones:
+##
+## self.lines : a list of text lines for the corresponding block
+##
+## self.content : for documentation comment blocks only, this is the
+## block content that has been "unboxed" from its
+## decoration. This is None for all other blocks
+## (i.e. sources or ordinary comments with no starting
+## markup tag)
+##
+class SourceBlock:
+ def __init__( self, processor, filename, lineno, lines ):
+ self.processor = processor
+ self.filename = filename
+ self.lineno = lineno
+ self.lines = lines[:]
+ self.format = processor.format
+ self.content = []
+
+ if self.format == None:
+ return
+
+ words = []
+
+ # extract comment lines
+ lines = []
+
+ for line0 in self.lines:
+ m = self.format.column.match( line0 )
+ if m:
+ lines.append( m.group(1) )
+
+ # now, look for a markup tag
+ for l in lines:
+ l = string.strip(l)
+ if len(l) > 0:
+ for tag in re_markup_tags:
+ if tag.match( l ):
+ self.content = lines
+ return
+
+ def location( self ):
+ return "(" + self.filename + ":" + repr(self.lineno) + ")"
+
+
+ # debugging only - not used in normal operations
+ def dump( self ):
+
+ if self.content:
+ print "{{{content start---"
+ for l in self.content:
+ print l
+ print "---content end}}}"
+ return
+
+ fmt = ""
+ if self.format:
+ fmt = repr(self.format.id) + " "
+
+ for line in self.lines:
+ print line
+
+
+################################################################
+##
+## SOURCE PROCESSOR CLASS
+##
+## The SourceProcessor is in charge or reading a C source file
+## and decomposing it into a series of different "SourceBlock"
+## objects.
+##
+## each one of these blocks can be made of the following data:
+##
+## - A documentation comment block that starts with "/**" and
+## whose exact format will be discussed later
+##
+## - normal sources lines, include comments
+##
+##
+class SourceProcessor:
+
+ def __init__( self ):
+ """initialize a source processor"""
+ self.blocks = []
+ self.filename = None
+ self.format = None
+ self.lines = []
+
+ def reset( self ):
+ """reset a block processor, clean all its blocks"""
+ self.blocks = []
+ self.format = None
+
+
+ def parse_file( self, filename ):
+ """parse a C source file, and adds its blocks to the processor's list"""
+
+ self.reset()
+
+ self.filename = filename
+
+ fileinput.close()
+ self.format = None
+ self.lineno = 0
+ self.lines = []
+
+ for line in fileinput.input( filename ):
+
+ # strip trailing newlines, important on Windows machines !!
+ if line[-1] == '\012':
+ line = line[0:-1]
+
+ if self.format == None:
+ self.process_normal_line( line )
+
+ else:
+ if self.format.end.match( line ):
+ # that's a normal block end, add it to lines and
+ # create a new block
+ self.lines.append( line )
+ self.add_block_lines()
+
+ elif self.format.column.match( line ):
+ # that's a normal column line, add it to 'lines'
+ self.lines.append( line )
+
+ else:
+ # humm.. this is an unexpected block end,
+ # create a new block, but don't process the line
+ self.add_block_lines()
+
+ # we need to process the line again
+ self.process_normal_line( line )
+
+ # record the last lines
+ self.add_block_lines()
+
+
+
+ def process_normal_line( self, line ):
+ """process a normal line and check if it's the start of a new block"""
+ for f in re_source_block_formats:
+ if f.start.match( line ):
+ self.add_block_lines()
+ self.format = f
+ self.lineno = fileinput.filelineno()
+
+ self.lines.append( line )
+
+
+
+ def add_block_lines( self ):
+ """add the current accumulated lines, and create a new block"""
+ if self.lines != []:
+ block = SourceBlock( self, self.filename, self.lineno, self.lines )
+
+ self.blocks.append( block )
+ self.format = None
+ self.lines = []
+
+
+ # debugging only, not used in normal operations
+ def dump( self ):
+ """print all blocks in a processor"""
+ for b in self.blocks:
+ b.dump()
+
+# eof
diff --git a/src/tools/docmaker/tohtml.py b/src/tools/docmaker/tohtml.py
new file mode 100644
index 0000000..04dfba3
--- /dev/null
+++ b/src/tools/docmaker/tohtml.py
@@ -0,0 +1,538 @@
+# ToHTML (c) 2002, 2003, 2005, 2006, 2007
+# David Turner <david@freetype.org>
+
+from sources import *
+from content import *
+from formatter import *
+
+import time
+
+# The following defines the HTML header used by all generated pages.
+#
+html_header_1 = """\
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+"http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<title>"""
+
+html_header_2= """ API Reference</title>
+<style type="text/css">
+ body { font-family: Verdana, Geneva, Arial, Helvetica, serif;
+ color: #000000;
+ background: #FFFFFF; }
+
+ p { text-align: justify; }
+ h1 { text-align: center; }
+ li { text-align: justify; }
+ td { padding: 0 0.5em 0 0.5em }
+
+ a:link { color: #0000EF; }
+ a:visited { color: #51188E; }
+ a:hover { color: #FF0000; }
+
+ span.keyword { font-family: monospace;
+ text-align: left;
+ white-space: pre;
+ color: darkblue; }
+
+ pre.colored { color: blue; }
+
+ ul.empty { list-style-type: none; }
+</style>
+</head>
+<body>
+<center><h1>"""
+
+html_header_3=""" API Reference</h1></center>
+"""
+
+
+
+# The HTML footer used by all generated pages.
+#
+html_footer = """\
+</body>
+</html>"""
+
+# The header and footer used for each section.
+#
+section_title_header = "<center><h1>"
+section_title_footer = "</h1></center>"
+
+# The header and footer used for code segments.
+#
+code_header = '<pre class="colored">'
+code_footer = '</pre>'
+
+# Paragraph header and footer.
+#
+para_header = "<p>"
+para_footer = "</p>"
+
+# Block header and footer.
+#
+block_header = '<table align=center width="75%"><tr><td>'
+block_footer_start = """\
+</td></tr></table>
+<hr width="75%">
+<table align=center width="75%"><tr><td><font size=-2>[<a href="
+"""
+block_footer_middle = """\
+">Index</a>]</font></td>
+<td width="100%"></td>
+<td><font size=-2>[<a href="
+"""
+block_footer_end = """\
+">TOC</a>]</font></td></tr></table>
+"""
+
+# Description header/footer.
+#
+description_header = '<table align=center width="87%"><tr><td>'
+description_footer = "</td></tr></table><br>"
+
+# Marker header/inter/footer combination.
+#
+marker_header = '<table align=center width="87%" cellpadding=5><tr bgcolor="#EEEEFF"><td><em><b>'
+marker_inter = "</b></em></td></tr><tr><td>"
+marker_footer = "</td></tr></table>"
+
+# Source code extracts header/footer.
+#
+source_header = '<table align=center width="87%"><tr bgcolor="#D6E8FF"><td><pre>\n'
+source_footer = "\n</pre></table><br>"
+
+# Chapter header/inter/footer.
+#
+chapter_header = '<br><table align=center width="75%"><tr><td><h2>'
+chapter_inter = '</h2><ul class="empty"><li>'
+chapter_footer = '</li></ul></td></tr></table>'
+
+# Index footer.
+index_footer_start = """\
+<hr>
+<table><tr><td width="100%"></td>
+<td><font size=-2>[<a href="
+"""
+index_footer_end = """\
+">TOC</a>]</font></td></tr></table>
+"""
+
+
+# source language keyword coloration/styling
+#
+keyword_prefix = '<span class="keyword">'
+keyword_suffix = '</span>'
+
+section_synopsis_header = '<h2>Synopsis</h2>'
+section_synopsis_footer = ''
+
+# Translate a single line of source to HTML. This will convert
+# a "<" into "&lt.", ">" into "&gt.", etc.
+#
+def html_quote( line ):
+ result = string.replace( line, "&", "&amp;" )
+ result = string.replace( result, "<", "&lt;" )
+ result = string.replace( result, ">", "&gt;" )
+ return result
+
+
+# same as 'html_quote', but ignores left and right brackets
+#
+def html_quote0( line ):
+ return string.replace( line, "&", "&amp;" )
+
+
+def dump_html_code( lines, prefix = "" ):
+ # clean the last empty lines
+ #
+ l = len( self.lines )
+ while l > 0 and string.strip( self.lines[l - 1] ) == "":
+ l = l - 1
+
+ # The code footer should be directly appended to the last code
+ # line to avoid an additional blank line.
+ #
+ print prefix + code_header,
+ for line in self.lines[0 : l+1]:
+ print '\n' + prefix + html_quote(line),
+ print prefix + code_footer,
+
+
+
+class HtmlFormatter(Formatter):
+
+ def __init__( self, processor, project_title, file_prefix ):
+
+ Formatter.__init__( self, processor )
+
+ global html_header_1, html_header_2, html_header_3, html_footer
+
+ if file_prefix:
+ file_prefix = file_prefix + "-"
+ else:
+ file_prefix = ""
+
+ self.project_title = project_title
+ self.file_prefix = file_prefix
+ self.html_header = html_header_1 + project_title + html_header_2 + \
+ project_title + html_header_3
+
+ self.html_footer = "<center><font size=""-2"">generated on " + \
+ time.asctime( time.localtime( time.time() ) ) + \
+ "</font></center>" + html_footer
+
+ self.columns = 3
+
+ def make_section_url( self, section ):
+ return self.file_prefix + section.name + ".html"
+
+
+ def make_block_url( self, block ):
+ return self.make_section_url( block.section ) + "#" + block.name
+
+
+ def make_html_words( self, words ):
+ """ convert a series of simple words into some HTML text """
+ line = ""
+ if words:
+ line = html_quote( words[0] )
+ for w in words[1:]:
+ line = line + " " + html_quote( w )
+
+ return line
+
+
+ def make_html_word( self, word ):
+ """analyze a simple word to detect cross-references and styling"""
+ # look for cross-references
+ #
+ m = re_crossref.match( word )
+ if m:
+ try:
+ name = m.group(1)
+ rest = m.group(2)
+ block = self.identifiers[name]
+ url = self.make_block_url( block )
+ return '<a href="' + url + '">' + name + '</a>' + rest
+ except:
+ # we detected a cross-reference to an unknown item
+ sys.stderr.write( \
+ "WARNING: undefined cross reference '" + name + "'.\n" )
+ return '?' + name + '?' + rest
+
+ # look for italics and bolds
+ m = re_italic.match( word )
+ if m:
+ name = m.group(1)
+ rest = m.group(3)
+ return '<i>' + name + '</i>' + rest
+
+ m = re_bold.match( word )
+ if m:
+ name = m.group(1)
+ rest = m.group(3)
+ return '<b>' + name + '</b>' + rest
+
+ return html_quote(word)
+
+
+ def make_html_para( self, words ):
+ """ convert a paragraph's words into tagged HTML text, handle xrefs """
+ line = ""
+ if words:
+ line = self.make_html_word( words[0] )
+ for word in words[1:]:
+ line = line + " " + self.make_html_word( word )
+ # convert `...' quotations into real left and right single quotes
+ line = re.sub( r"(^|\W)`(.*?)'(\W|$)",
+ r'\1&lsquo;\2&rsquo;\3',
+ line )
+
+ return para_header + line + para_footer
+
+
+ def make_html_code( self, lines ):
+ """ convert a code sequence to HTML """
+ line = code_header + '\n'
+ for l in lines:
+ line = line + html_quote( l ) + '\n'
+
+ return line + code_footer
+
+
+ def make_html_items( self, items ):
+ """ convert a field's content into some valid HTML """
+ lines = []
+ for item in items:
+ if item.lines:
+ lines.append( self.make_html_code( item.lines ) )
+ else:
+ lines.append( self.make_html_para( item.words ) )
+
+ return string.join( lines, '\n' )
+
+
+ def print_html_items( self, items ):
+ print self.make_html_items( items )
+
+
+ def print_html_field( self, field ):
+ if field.name:
+ print "<table><tr valign=top><td><p><b>"+field.name+"</b></p></td><td>"
+
+ print self.make_html_items( field.items )
+
+ if field.name:
+ print "</td></tr></table>"
+
+
+ def html_source_quote( self, line, block_name = None ):
+ result = ""
+ while line:
+ m = re_source_crossref.match( line )
+ if m:
+ name = m.group(2)
+ prefix = html_quote( m.group(1) )
+ length = len( m.group(0) )
+
+ if name == block_name:
+ # this is the current block name, if any
+ result = result + prefix + '<b>' + name + '</b>'
+
+ elif re_source_keywords.match(name):
+ # this is a C keyword
+ result = result + prefix + keyword_prefix + name + keyword_suffix
+
+ elif self.identifiers.has_key(name):
+ # this is a known identifier
+ block = self.identifiers[name]
+ result = result + prefix + '<a href="' + \
+ self.make_block_url(block) + '">' + name + '</a>'
+ else:
+ result = result + html_quote(line[:length])
+
+ line = line[length:]
+ else:
+ result = result + html_quote(line)
+ line = []
+
+ return result
+
+
+ def print_html_field_list( self, fields ):
+ print "<table cellpadding=3 border=0>"
+ for field in fields:
+ if len(field.name) > 22:
+ print "<tr valign=top><td colspan=0><b>"+field.name+"</b></td></tr>"
+ print "<tr valign=top><td></td><td>"
+ else:
+ print "<tr valign=top><td><b>" + field.name + "</b></td><td>"
+
+ self.print_html_items( field.items )
+ print "</td></tr>"
+ print "</table>"
+
+
+ def print_html_markup( self, markup ):
+ table_fields = []
+ for field in markup.fields:
+ if field.name:
+ # we begin a new series of field or value definitions, we
+ # will record them in the 'table_fields' list before outputting
+ # all of them as a single table
+ #
+ table_fields.append( field )
+
+ else:
+ if table_fields:
+ self.print_html_field_list( table_fields )
+ table_fields = []
+
+ self.print_html_items( field.items )
+
+ if table_fields:
+ self.print_html_field_list( table_fields )
+
+ #
+ # Formatting the index
+ #
+
+ def index_enter( self ):
+ print self.html_header
+ self.index_items = {}
+
+ def index_name_enter( self, name ):
+ block = self.identifiers[name]
+ url = self.make_block_url( block )
+ self.index_items[name] = url
+
+ def index_exit( self ):
+
+ # block_index already contains the sorted list of index names
+ count = len( self.block_index )
+ rows = (count + self.columns - 1) / self.columns
+
+ print "<table align=center border=0 cellpadding=0 cellspacing=0>"
+ for r in range(rows):
+ line = "<tr>"
+ for c in range(self.columns):
+ i = r + c*rows
+ if i < count:
+ bname = self.block_index[r + c * rows]
+ url = self.index_items[bname]
+ line = line + '<td><a href="' + url + '">' + bname + '</a></td>'
+ else:
+ line = line + '<td></td>'
+ line = line + "</tr>"
+ print line
+
+ print "</table>"
+
+ print index_footer_start + \
+ self.file_prefix + "toc.html" + \
+ index_footer_end
+
+ self.index_items = {}
+
+ def index_dump( self, index_filename = None ):
+
+ if index_filename == None:
+ index_filename = self.file_prefix + "index.html"
+
+ Formatter.index_dump( self, index_filename )
+
+ #
+ # Formatting the table of content
+ #
+ def toc_enter( self ):
+ print self.html_header
+ print "<center><h1>Table of Contents</h1></center>"
+
+ def toc_chapter_enter( self, chapter ):
+ print chapter_header + string.join(chapter.title) + chapter_inter
+ print "<table cellpadding=5>"
+
+ def toc_section_enter( self, section ):
+ print "<tr valign=top><td>"
+ print '<a href="' + self.make_section_url( section ) + '">' + \
+ section.title + '</a></td><td>'
+
+ print self.make_html_para( section.abstract )
+
+ def toc_section_exit( self, section ):
+ print "</td></tr>"
+
+ def toc_chapter_exit( self, chapter ):
+ print "</table>"
+ print chapter_footer
+
+ def toc_index( self, index_filename ):
+ print chapter_header + '<a href="' + index_filename + '">Global Index</a>' + chapter_inter + chapter_footer
+
+ def toc_exit( self ):
+ print self.html_footer
+
+ def toc_dump( self, toc_filename = None, index_filename = None ):
+ if toc_filename == None:
+ toc_filename = self.file_prefix + "toc.html"
+
+ if index_filename == None:
+ index_filename = self.file_prefix + "index.html"
+
+ Formatter.toc_dump( self, toc_filename, index_filename )
+
+ #
+ # Formatting sections
+ #
+ def section_enter( self, section ):
+ print self.html_header
+
+ print section_title_header
+ print section.title
+ print section_title_footer
+
+ maxwidth = 0
+ for b in section.blocks.values():
+ if len( b.name ) > maxwidth:
+ maxwidth = len( b.name )
+
+ width = 70 # XXX magic number
+ if maxwidth <> 0:
+ # print section synopsis
+ print section_synopsis_header
+ print "<table align=center cellspacing=5 cellpadding=0 border=0>"
+
+ columns = width / maxwidth
+ if columns < 1:
+ columns = 1
+
+ count = len( section.block_names )
+ rows = ( count + columns - 1 ) / columns
+
+ for r in range( rows ):
+ line = "<tr>"
+ for c in range( columns ):
+ i = r + c * rows
+ line = line + '<td></td><td>'
+ if i < count:
+ name = section.block_names[i]
+ line = line + '<a href="#' + name + '">' + name + '</a>'
+
+ line = line + '</td>'
+ line = line + "</tr>"
+ print line
+
+ print "</table><br><br>"
+ print section_synopsis_footer
+
+ print description_header
+ print self.make_html_items( section.description )
+ print description_footer
+
+ def block_enter( self, block ):
+ print block_header
+
+ # place html anchor if needed
+ if block.name:
+ print '<h4><a name="' + block.name + '">' + block.name + '</a></h4>'
+
+ # dump the block C source lines now
+ if block.code:
+ print source_header
+ for l in block.code:
+ print self.html_source_quote( l, block.name )
+ print source_footer
+
+
+ def markup_enter( self, markup, block ):
+ if markup.tag == "description":
+ print description_header
+ else:
+ print marker_header + markup.tag + marker_inter
+
+ self.print_html_markup( markup )
+
+ def markup_exit( self, markup, block ):
+ if markup.tag == "description":
+ print description_footer
+ else:
+ print marker_footer
+
+ def block_exit( self, block ):
+ print block_footer_start + self.file_prefix + "index.html" + \
+ block_footer_middle + self.file_prefix + "toc.html" + \
+ block_footer_end
+
+
+ def section_exit( self, section ):
+ print html_footer
+
+
+ def section_dump_all( self ):
+ for section in self.sections:
+ self.section_dump( section, self.file_prefix + section.name + '.html' )
+
+# eof
diff --git a/src/tools/docmaker/utils.py b/src/tools/docmaker/utils.py
new file mode 100644
index 0000000..e751c56
--- /dev/null
+++ b/src/tools/docmaker/utils.py
@@ -0,0 +1,132 @@
+# Utils (c) 2002, 2004, 2007 David Turner <david@freetype.org>
+#
+
+import string, sys, os, glob
+
+# current output directory
+#
+output_dir = None
+
+
+# This function is used to sort the index. It is a simple lexicographical
+# sort, except that it places capital letters before lowercase ones.
+#
+def index_sort( s1, s2 ):
+ if not s1:
+ return -1
+
+ if not s2:
+ return 1
+
+ l1 = len( s1 )
+ l2 = len( s2 )
+ m1 = string.lower( s1 )
+ m2 = string.lower( s2 )
+
+ for i in range( l1 ):
+ if i >= l2 or m1[i] > m2[i]:
+ return 1
+
+ if m1[i] < m2[i]:
+ return -1
+
+ if s1[i] < s2[i]:
+ return -1
+
+ if s1[i] > s2[i]:
+ return 1
+
+ if l2 > l1:
+ return -1
+
+ return 0
+
+# Sort input_list, placing the elements of order_list in front.
+#
+def sort_order_list( input_list, order_list ):
+ new_list = order_list[:]
+ for id in input_list:
+ if not id in order_list:
+ new_list.append( id )
+ return new_list
+
+
+
+# Open the standard output to a given project documentation file. Use
+# "output_dir" to determine the filename location if necessary and save the
+# old stdout in a tuple that is returned by this function.
+#
+def open_output( filename ):
+ global output_dir
+
+ if output_dir and output_dir != "":
+ filename = output_dir + os.sep + filename
+
+ old_stdout = sys.stdout
+ new_file = open( filename, "w" )
+ sys.stdout = new_file
+
+ return ( new_file, old_stdout )
+
+
+# Close the output that was returned by "close_output".
+#
+def close_output( output ):
+ output[0].close()
+ sys.stdout = output[1]
+
+
+# Check output directory.
+#
+def check_output( ):
+ global output_dir
+ if output_dir:
+ if output_dir != "":
+ if not os.path.isdir( output_dir ):
+ sys.stderr.write( "argument" + " '" + output_dir + "' " +
+ "is not a valid directory" )
+ sys.exit( 2 )
+ else:
+ output_dir = None
+
+def file_exists( pathname ):
+ """checks that a given file exists"""
+ result = 1
+ try:
+ file = open( pathname, "r" )
+ file.close()
+ except:
+ result = None
+ sys.stderr.write( pathname + " couldn't be accessed\n" )
+
+ return result
+
+
+def make_file_list( args = None ):
+ """builds a list of input files from command-line arguments"""
+
+ file_list = []
+ # sys.stderr.write( repr( sys.argv[1 :] ) + '\n' )
+
+ if not args:
+ args = sys.argv[1 :]
+
+ for pathname in args:
+ if string.find( pathname, '*' ) >= 0:
+ newpath = glob.glob( pathname )
+ newpath.sort() # sort files -- this is important because
+ # of the order of files
+ else:
+ newpath = [pathname]
+
+ file_list.extend( newpath )
+
+ if len( file_list ) == 0:
+ file_list = None
+ else:
+ # now filter the file list to remove non-existing ones
+ file_list = filter( file_exists, file_list )
+
+ return file_list
+
+# eof
diff --git a/src/tools/ftrandom/Makefile b/src/tools/ftrandom/Makefile
new file mode 100644
index 0000000..2e61929
--- /dev/null
+++ b/src/tools/ftrandom/Makefile
@@ -0,0 +1,35 @@
+# TOP_DIR and OBJ_DIR should be set by the user to the right directories,
+# if necessary.
+
+TOP_DIR ?= ../../..
+OBJ_DIR ?= $(TOP_DIR)/objs
+
+
+# The setup below is for gcc on a Unix-like platform.
+
+SRC_DIR = $(TOP_DIR)/src/tools/ftrandom
+
+CC = gcc
+WFLAGS = -Wmissing-prototypes \
+ -Wunused \
+ -Wimplicit \
+ -Wreturn-type \
+ -Wparentheses \
+ -pedantic \
+ -Wformat \
+ -Wchar-subscripts \
+ -Wsequence-point
+CFLAGS = $(WFLAGS) \
+ -g \
+ -I $(TOP_DIR)/include
+LIBS = -lm \
+ -L $(OBJ_DIR) \
+ -lfreetype \
+ -lz
+
+all: $(OBJ_DIR)/ftrandom
+
+$(OBJ_DIR)/ftrandom: $(SRC_DIR)/ftrandom.c $(OBJ_DIR)/libfreetype.a
+ $(CC) -o $(OBJ_DIR)/ftrandom $(CFLAGS) $(SRC_DIR)/ftrandom.c $(LIBS)
+
+# EOF
diff --git a/src/tools/ftrandom/README b/src/tools/ftrandom/README
new file mode 100644
index 0000000..c093f15
--- /dev/null
+++ b/src/tools/ftrandom/README
@@ -0,0 +1,48 @@
+ftrandom
+--------
+
+This program expects a set of directories containing good fonts, and a set
+of extensions of fonts to be tested. It will randomly pick a font, copy it,
+introduce and error and then test it.
+
+The FreeType tests are quite basic:
+
+ For each erroneous font it
+ forks off a new tester;
+ initializes the library;
+ opens each font in the file;
+ loads each glyph;
+ (optionally reviewing the contours of the glyph)
+ (optionally rasterizing)
+ closes the face.
+
+If the tester exits with a signal, or takes longer than 20 seconds then
+ftrandom saves the erroneous font and continues. If the tester exits
+normally or with an error, then the superstructure removes the test font and
+continues.
+
+Arguments are:
+
+ --all Test every font in the directory(ies) no matter
+ what its extension (some CID-keyed fonts have no
+ extension).
+ --check-outlines Call FT_Outline_Decompose on each glyph.
+ --dir <dir> Append <dir> to the list of directories to search
+ for good fonts.
+ --error-count <cnt> Introduce <cnt> single-byte errors into the
+ erroneous fonts.
+ --error-fraction <frac> Multiply the file size of the font by <frac> and
+ introduce that many errors into the erroneous
+ font file.
+ --ext <ext> Add <ext> to the set of font types tested. Known
+ extensions are `ttf', `otf', `ttc', `cid', `pfb',
+ `pfa', `bdf', `pcf', `pfr', `fon', `otb', and
+ `cff'.
+ --help Print out this list of options.
+ --nohints Specify FT_LOAD_NO_HINTING when loading glyphs.
+ --rasterize Call FT_Render_Glyph as well as loading it.
+ --result <dir> This is the directory in which test files are
+ placed.
+ --test <file> Run a single test on a pre-generated testcase.
+ Done in the current process so it can be debugged
+ more easily.
diff --git a/src/tools/ftrandom/ftrandom.c b/src/tools/ftrandom/ftrandom.c
new file mode 100644
index 0000000..fcff27b
--- /dev/null
+++ b/src/tools/ftrandom/ftrandom.c
@@ -0,0 +1,659 @@
+/* Copyright (C) 2005 by George Williams */
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+
+ * The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* modified by Werner Lemberg <wl@gnu.org> */
+/* This file is now part of the FreeType library */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <math.h>
+#include <signal.h>
+#include <time.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+
+#define true 1
+#define false 0
+#define forever for (;;)
+
+
+ static int check_outlines = false;
+ static int nohints = false;
+ static int rasterize = false;
+ static char* results_dir = "results";
+
+#define GOOD_FONTS_DIR "/home/wl/freetype-testfonts"
+
+ static char* default_dir_list[] =
+ {
+ GOOD_FONTS_DIR,
+ NULL
+ };
+
+ static char* default_ext_list[] =
+ {
+ "ttf",
+ "otf",
+ "ttc",
+ "cid",
+ "pfb",
+ "pfa",
+ "bdf",
+ "pcf",
+ "pfr",
+ "fon",
+ "otb",
+ "cff",
+ NULL
+ };
+
+ static int error_count = 1;
+ static int error_fraction = 0;
+
+ static FT_F26Dot6 font_size = 12 * 64;
+
+ static struct fontlist
+ {
+ char* name;
+ int len;
+ unsigned int isbinary: 1;
+ unsigned int isascii: 1;
+ unsigned int ishex: 1;
+
+ } *fontlist;
+
+ static int fcnt;
+
+
+ static int
+ FT_MoveTo( const FT_Vector *to,
+ void *user )
+ {
+ return 0;
+ }
+
+
+ static int
+ FT_LineTo( const FT_Vector *to,
+ void *user )
+ {
+ return 0;
+ }
+
+
+ static int
+ FT_ConicTo( const FT_Vector *_cp,
+ const FT_Vector *to,
+ void *user )
+ {
+ return 0;
+ }
+
+
+ static int
+ FT_CubicTo( const FT_Vector *cp1,
+ const FT_Vector *cp2,
+ const FT_Vector *to,
+ void *user )
+ {
+ return 0;
+ }
+
+
+ static FT_Outline_Funcs outlinefuncs =
+ {
+ FT_MoveTo,
+ FT_LineTo,
+ FT_ConicTo,
+ FT_CubicTo,
+ 0, 0 /* No shift, no delta */
+ };
+
+
+ static void
+ TestFace( FT_Face face )
+ {
+ int gid;
+ int load_flags = FT_LOAD_DEFAULT;
+
+
+ if ( check_outlines &&
+ ( face->face_flags & FT_FACE_FLAG_SCALABLE ) )
+ load_flags = FT_LOAD_NO_BITMAP;
+
+ if ( nohints )
+ load_flags |= FT_LOAD_NO_HINTING;
+
+ FT_Set_Char_Size( face, 0, font_size, 72, 72 );
+
+ for ( gid = 0; gid < face->num_glyphs; ++gid )
+ {
+ if ( check_outlines &&
+ ( face->face_flags & FT_FACE_FLAG_SCALABLE ) )
+ {
+ if ( !FT_Load_Glyph( face, gid, load_flags ) )
+ FT_Outline_Decompose( &face->glyph->outline, &outlinefuncs, NULL );
+ }
+ else
+ FT_Load_Glyph( face, gid, load_flags );
+
+ if ( rasterize )
+ FT_Render_Glyph( face->glyph, ft_render_mode_normal );
+ }
+
+ FT_Done_Face( face );
+ }
+
+
+ static void
+ ExecuteTest( char* testfont )
+ {
+ FT_Library context;
+ FT_Face face;
+ int i, num;
+
+
+ if ( FT_Init_FreeType( &context ) )
+ {
+ fprintf( stderr, "Can't initialize FreeType.\n" );
+ exit( 1 );
+ }
+
+ if ( FT_New_Face( context, testfont, 0, &face ) )
+ {
+ /* The font is erroneous, so if this fails that's ok. */
+ exit( 0 );
+ }
+
+ if ( face->num_faces == 1 )
+ TestFace( face );
+ else
+ {
+ num = face->num_faces;
+ FT_Done_Face( face );
+
+ for ( i = 0; i < num; ++i )
+ {
+ if ( !FT_New_Face( context, testfont, i, &face ) )
+ TestFace( face );
+ }
+ }
+
+ exit( 0 );
+ }
+
+
+ static int
+ extmatch( char* filename,
+ char** extensions )
+ {
+ int i;
+ char* pt;
+
+
+ if ( extensions == NULL )
+ return true;
+
+ pt = strrchr( filename, '.' );
+ if ( pt == NULL )
+ return false;
+ if ( pt < strrchr( filename, '/' ) )
+ return false;
+
+ for ( i = 0; extensions[i] != NULL; ++i )
+ if ( strcasecmp( pt + 1, extensions[i] ) == 0 ||
+ strcasecmp( pt, extensions[i] ) == 0 )
+ return true;
+
+ return false;
+ }
+
+
+ static void
+ figurefiletype( struct fontlist* item )
+ {
+ FILE* foo;
+
+
+ item->isbinary = item->isascii = item->ishex = false;
+
+ foo = fopen( item->name, "rb" );
+ if ( foo != NULL )
+ {
+ /* Try to guess the file type from the first few characters... */
+ int ch1 = getc( foo );
+ int ch2 = getc( foo );
+ int ch3 = getc( foo );
+ int ch4 = getc( foo );
+
+
+ fclose( foo );
+
+ if ( ( ch1 == 0 && ch2 == 1 && ch3 == 0 && ch4 == 0 ) ||
+ ( ch1 == 'O' && ch2 == 'T' && ch3 == 'T' && ch4 == 'O' ) ||
+ ( ch1 == 't' && ch2 == 'r' && ch3 == 'u' && ch4 == 'e' ) ||
+ ( ch1 == 't' && ch2 == 't' && ch3 == 'c' && ch4 == 'f' ) )
+ {
+ /* ttf, otf, ttc files */
+ item->isbinary = true;
+ }
+ else if ( ch1 == 0x80 && ch2 == '\01' )
+ {
+ /* PFB header */
+ item->isbinary = true;
+ }
+ else if ( ch1 == '%' && ch2 == '!' )
+ {
+ /* Random PostScript */
+ if ( strstr( item->name, ".pfa" ) != NULL ||
+ strstr( item->name, ".PFA" ) != NULL )
+ item->ishex = true;
+ else
+ item->isascii = true;
+ }
+ else if ( ch1 == 1 && ch2 == 0 && ch3 == 4 )
+ {
+ /* Bare CFF */
+ item->isbinary = true;
+ }
+ else if ( ch1 == 'S' && ch2 == 'T' && ch3 == 'A' && ch4 == 'R' )
+ {
+ /* BDF */
+ item->ishex = true;
+ }
+ else if ( ch1 == 'P' && ch2 == 'F' && ch3 == 'R' && ch4 == '0' )
+ {
+ /* PFR */
+ item->isbinary = true;
+ }
+ else if ( ( ch1 == '\1' && ch2 == 'f' && ch3 == 'c' && ch4 == 'p' ) ||
+ ( ch1 == 'M' && ch2 == 'Z' ) )
+ {
+ /* Windows FON */
+ item->isbinary = true;
+ }
+ else
+ {
+ fprintf( stderr,
+ "Can't recognize file type of `%s', assuming binary\n",
+ item->name );
+ item->isbinary = true;
+ }
+ }
+ else
+ {
+ fprintf( stderr, "Can't open `%s' for typing the file.\n",
+ item->name );
+ item->isbinary = true;
+ }
+ }
+
+
+ static void
+ FindFonts( char** fontdirs,
+ char** extensions )
+ {
+ DIR* examples;
+ struct dirent* ent;
+
+ int i, max;
+ char buffer[1025];
+ struct stat statb;
+
+
+ max = 0;
+ fcnt = 0;
+
+ for ( i = 0; fontdirs[i] != NULL; ++i )
+ {
+ examples = opendir( fontdirs[i] );
+ if ( examples == NULL )
+ {
+ fprintf( stderr,
+ "Can't open example font directory `%s'\n",
+ fontdirs[i] );
+ exit( 1 );
+ }
+
+ while ( ( ent = readdir( examples ) ) != NULL )
+ {
+ snprintf( buffer, sizeof ( buffer ),
+ "%s/%s", fontdirs[i], ent->d_name );
+ if ( stat( buffer, &statb ) == -1 || S_ISDIR( statb.st_mode ) )
+ continue;
+ if ( extensions == NULL || extmatch( buffer, extensions ) )
+ {
+ if ( fcnt >= max )
+ {
+ max += 100;
+ fontlist = realloc( fontlist, max * sizeof ( struct fontlist ) );
+ if ( fontlist == NULL )
+ {
+ fprintf( stderr, "Can't allocate memory\n" );
+ exit( 1 );
+ }
+ }
+
+ fontlist[fcnt].name = strdup( buffer );
+ fontlist[fcnt].len = statb.st_size;
+
+ figurefiletype( &fontlist[fcnt] );
+ ++fcnt;
+ }
+ }
+
+ closedir( examples );
+ }
+
+ if ( fcnt == 0 )
+ {
+ fprintf( stderr, "Can't find matching font files.\n" );
+ exit( 1 );
+ }
+
+ fontlist[fcnt].name = NULL;
+ }
+
+
+ static int
+ getErrorCnt( struct fontlist* item )
+ {
+ if ( error_count == 0 && error_fraction == 0 )
+ return 0;
+
+ return error_count + ceil( error_fraction * item->len );
+ }
+
+
+ static int
+ getRandom( int low,
+ int high )
+ {
+ if ( low - high < 0x10000L )
+ return low + ( ( random() >> 8 ) % ( high + 1 - low ) );
+
+ return low + ( random() % ( high + 1 - low ) );
+ }
+
+
+ static int
+ copyfont( struct fontlist* item,
+ char* newfont )
+ {
+ static char buffer[8096];
+ FILE *good, *new;
+ int len;
+ int i, err_cnt;
+
+
+ good = fopen( item->name, "r" );
+ if ( good == NULL )
+ {
+ fprintf( stderr, "Can't open `%s'\n", item->name );
+ return false;
+ }
+
+ new = fopen( newfont, "w+" );
+ if ( new == NULL )
+ {
+ fprintf( stderr, "Can't create temporary output file `%s'\n",
+ newfont );
+ exit( 1 );
+ }
+
+ while ( ( len = fread( buffer, 1, sizeof ( buffer ), good ) ) > 0 )
+ fwrite( buffer, 1, len, new );
+
+ fclose( good );
+
+ err_cnt = getErrorCnt( item );
+ for ( i = 0; i < err_cnt; ++i )
+ {
+ fseek( new, getRandom( 0, item->len - 1 ), SEEK_SET );
+
+ if ( item->isbinary )
+ putc( getRandom( 0, 0xff ), new );
+ else if ( item->isascii )
+ putc( getRandom( 0x20, 0x7e ), new );
+ else
+ {
+ int hex = getRandom( 0, 15 );
+
+
+ if ( hex < 10 )
+ hex += '0';
+ else
+ hex += 'A' - 10;
+
+ putc( hex, new );
+ }
+ }
+
+ if ( ferror( new ) )
+ {
+ fclose( new );
+ unlink( newfont );
+ return false;
+ }
+
+ fclose( new );
+
+ return true;
+ }
+
+
+ static int child_pid;
+
+ static void
+ abort_test( int sig )
+ {
+ /* If a time-out happens, then kill the child */
+ kill( child_pid, SIGFPE );
+ write( 2, "Timeout... ", 11 );
+ }
+
+
+ static void
+ do_test( void )
+ {
+ int i = getRandom( 0, fcnt - 1 );
+ static int test_num = 0;
+ char buffer[1024];
+
+
+ sprintf( buffer, "%s/test%d", results_dir, test_num++ );
+
+ if ( copyfont ( &fontlist[i], buffer ) )
+ {
+ signal( SIGALRM, abort_test );
+ /* Anything that takes more than 20 seconds */
+ /* to parse and/or rasterize is an error. */
+ alarm( 20 );
+ if ( ( child_pid = fork() ) == 0 )
+ ExecuteTest( buffer );
+ else if ( child_pid != -1 )
+ {
+ int status;
+
+
+ waitpid( child_pid, &status, 0 );
+ alarm( 0 );
+ if ( WIFSIGNALED ( status ) )
+ printf( "Error found in file `%s'\n", buffer );
+ else
+ unlink( buffer );
+ }
+ else
+ {
+ fprintf( stderr, "Can't fork test case.\n" );
+ exit( 1 );
+ }
+ alarm( 0 );
+ }
+ }
+
+
+ static void
+ usage( FILE* out,
+ char* name )
+ {
+ fprintf( out, "%s [options] -- Generate random erroneous fonts\n"
+ " and attempt to parse them with FreeType.\n\n", name );
+
+ fprintf( out, " --all All non-directory files are assumed to be fonts.\n" );
+ fprintf( out, " --check-outlines Make sure we can parse the outlines of each glyph.\n" );
+ fprintf( out, " --dir <path> Append <path> to list of font search directories.\n" );
+ fprintf( out, " --error-count <cnt> Introduce <cnt> single byte errors into each font.\n" );
+ fprintf( out, " --error-fraction <frac> Introduce <frac>*filesize single byte errors\n"
+ " into each font.\n" );
+ fprintf( out, " --ext <ext> Add <ext> to list of extensions indicating fonts.\n" );
+ fprintf( out, " --help Print this.\n" );
+ fprintf( out, " --nohints Turn off hinting.\n" );
+ fprintf( out, " --rasterize Attempt to rasterize each glyph.\n" );
+ fprintf( out, " --results <dir> Directory in which to place the test fonts.\n" );
+ fprintf( out, " --size <float> Use the given font size for the tests.\n" );
+ fprintf( out, " --test <file> Run a single test on an already existing file.\n" );
+ }
+
+
+ int
+ main( int argc,
+ char** argv )
+ {
+ char **dirs, **exts;
+ char *pt, *end;
+ int dcnt = 0, ecnt = 0, rset = false, allexts = false;
+ int i;
+ time_t now;
+ char* testfile = NULL;
+
+
+ dirs = calloc( argc + 1, sizeof ( char ** ) );
+ exts = calloc( argc + 1, sizeof ( char ** ) );
+
+ for ( i = 1; i < argc; ++i )
+ {
+ pt = argv[i];
+ if ( pt[0] == '-' && pt[1] == '-' )
+ ++pt;
+
+ if ( strcmp( pt, "-all" ) == 0 )
+ allexts = true;
+ else if ( strcmp( pt, "-check-outlines" ) == 0 )
+ check_outlines = true;
+ else if ( strcmp( pt, "-dir" ) == 0 )
+ dirs[dcnt++] = argv[++i];
+ else if ( strcmp( pt, "-error-count" ) == 0 )
+ {
+ if ( !rset )
+ error_fraction = 0;
+ rset = true;
+ error_count = strtol( argv[++i], &end, 10 );
+ if ( *end != '\0' )
+ {
+ fprintf( stderr, "Bad value for error-count: %s\n", argv[i] );
+ exit( 1 );
+ }
+ }
+ else if ( strcmp( pt, "-error-fraction" ) == 0 )
+ {
+ if ( !rset )
+ error_count = 0;
+ rset = true;
+ error_fraction = strtod( argv[++i], &end );
+ if ( *end != '\0' )
+ {
+ fprintf( stderr, "Bad value for error-fraction: %s\n", argv[i] );
+ exit( 1 );
+ }
+ }
+ else if ( strcmp( pt, "-ext" ) == 0 )
+ exts[ecnt++] = argv[++i];
+ else if ( strcmp( pt, "-help" ) == 0 )
+ {
+ usage( stdout, argv[0] );
+ exit( 0 );
+ }
+ else if ( strcmp( pt, "-nohints" ) == 0 )
+ nohints = true;
+ else if ( strcmp( pt, "-rasterize" ) == 0 )
+ rasterize = true;
+ else if ( strcmp( pt, "-results" ) == 0 )
+ results_dir = argv[++i];
+ else if ( strcmp( pt, "-size" ) == 0 )
+ {
+ font_size = (FT_F26Dot6)( strtod( argv[++i], &end ) * 64 );
+ if ( *end != '\0' || font_size < 64 )
+ {
+ fprintf( stderr, "Bad value for size: %s\n", argv[i] );
+ exit( 1 );
+ }
+ }
+ else if ( strcmp( pt, "-test" ) == 0 )
+ testfile = argv[++i];
+ else
+ {
+ usage( stderr, argv[0] );
+ exit( 1 );
+ }
+ }
+
+ if ( allexts )
+ exts = NULL;
+ else if ( ecnt == 0 )
+ exts = default_ext_list;
+
+ if ( dcnt == 0 )
+ dirs = default_dir_list;
+
+ if ( testfile != NULL )
+ ExecuteTest( testfile ); /* This should never return */
+
+ time( &now );
+ srandom( now );
+
+ FindFonts( dirs, exts );
+ mkdir( results_dir, 0755 );
+
+ forever
+ do_test();
+
+ return 0;
+ }
+
+
+/* EOF */
diff --git a/src/tools/glnames.py b/src/tools/glnames.py
new file mode 100644
index 0000000..9a6da38
--- /dev/null
+++ b/src/tools/glnames.py
@@ -0,0 +1,5282 @@
+#!/usr/bin/env python
+#
+
+#
+# FreeType 2 glyph name builder
+#
+
+
+# Copyright 1996-2000, 2003, 2005, 2007 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+"""\
+
+usage: %s <output-file>
+
+ This python script generates the glyph names tables defined in the
+ PSNames module.
+
+ Its single argument is the name of the header file to be created.
+"""
+
+
+import sys, string, struct, re, os.path
+
+
+# This table lists the glyphs according to the Macintosh specification.
+# It is used by the TrueType Postscript names table.
+#
+# See
+#
+# http://fonts.apple.com/TTRefMan/RM06/Chap6post.html
+#
+# for the official list.
+#
+mac_standard_names = \
+[
+ # 0
+ ".notdef", ".null", "nonmarkingreturn", "space", "exclam",
+ "quotedbl", "numbersign", "dollar", "percent", "ampersand",
+
+ # 10
+ "quotesingle", "parenleft", "parenright", "asterisk", "plus",
+ "comma", "hyphen", "period", "slash", "zero",
+
+ # 20
+ "one", "two", "three", "four", "five",
+ "six", "seven", "eight", "nine", "colon",
+
+ # 30
+ "semicolon", "less", "equal", "greater", "question",
+ "at", "A", "B", "C", "D",
+
+ # 40
+ "E", "F", "G", "H", "I",
+ "J", "K", "L", "M", "N",
+
+ # 50
+ "O", "P", "Q", "R", "S",
+ "T", "U", "V", "W", "X",
+
+ # 60
+ "Y", "Z", "bracketleft", "backslash", "bracketright",
+ "asciicircum", "underscore", "grave", "a", "b",
+
+ # 70
+ "c", "d", "e", "f", "g",
+ "h", "i", "j", "k", "l",
+
+ # 80
+ "m", "n", "o", "p", "q",
+ "r", "s", "t", "u", "v",
+
+ # 90
+ "w", "x", "y", "z", "braceleft",
+ "bar", "braceright", "asciitilde", "Adieresis", "Aring",
+
+ # 100
+ "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
+ "aacute", "agrave", "acircumflex", "adieresis", "atilde",
+
+ # 110
+ "aring", "ccedilla", "eacute", "egrave", "ecircumflex",
+ "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
+
+ # 120
+ "ntilde", "oacute", "ograve", "ocircumflex", "odieresis",
+ "otilde", "uacute", "ugrave", "ucircumflex", "udieresis",
+
+ # 130
+ "dagger", "degree", "cent", "sterling", "section",
+ "bullet", "paragraph", "germandbls", "registered", "copyright",
+
+ # 140
+ "trademark", "acute", "dieresis", "notequal", "AE",
+ "Oslash", "infinity", "plusminus", "lessequal", "greaterequal",
+
+ # 150
+ "yen", "mu", "partialdiff", "summation", "product",
+ "pi", "integral", "ordfeminine", "ordmasculine", "Omega",
+
+ # 160
+ "ae", "oslash", "questiondown", "exclamdown", "logicalnot",
+ "radical", "florin", "approxequal", "Delta", "guillemotleft",
+
+ # 170
+ "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
+ "Otilde", "OE", "oe", "endash", "emdash",
+
+ # 180
+ "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",
+ "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
+
+ # 190
+ "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl",
+ "periodcentered", "quotesinglbase", "quotedblbase", "perthousand",
+ "Acircumflex",
+
+ # 200
+ "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",
+ "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
+
+ # 210
+ "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave",
+ "dotlessi", "circumflex", "tilde", "macron", "breve",
+
+ # 220
+ "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek",
+ "caron", "Lslash", "lslash", "Scaron", "scaron",
+
+ # 230
+ "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
+ "Yacute", "yacute", "Thorn", "thorn", "minus",
+
+ # 240
+ "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
+ "onequarter", "threequarters", "franc", "Gbreve", "gbreve",
+
+ # 250
+ "Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute",
+ "Ccaron", "ccaron", "dcroat"
+]
+
+
+# The list of standard `SID' glyph names. For the official list,
+# see Annex A of document at
+#
+# http://partners.adobe.com/asn/developer/pdfs/tn/5176.CFF.pdf.
+#
+sid_standard_names = \
+[
+ # 0
+ ".notdef", "space", "exclam", "quotedbl", "numbersign",
+ "dollar", "percent", "ampersand", "quoteright", "parenleft",
+
+ # 10
+ "parenright", "asterisk", "plus", "comma", "hyphen",
+ "period", "slash", "zero", "one", "two",
+
+ # 20
+ "three", "four", "five", "six", "seven",
+ "eight", "nine", "colon", "semicolon", "less",
+
+ # 30
+ "equal", "greater", "question", "at", "A",
+ "B", "C", "D", "E", "F",
+
+ # 40
+ "G", "H", "I", "J", "K",
+ "L", "M", "N", "O", "P",
+
+ # 50
+ "Q", "R", "S", "T", "U",
+ "V", "W", "X", "Y", "Z",
+
+ # 60
+ "bracketleft", "backslash", "bracketright", "asciicircum", "underscore",
+ "quoteleft", "a", "b", "c", "d",
+
+ # 70
+ "e", "f", "g", "h", "i",
+ "j", "k", "l", "m", "n",
+
+ # 80
+ "o", "p", "q", "r", "s",
+ "t", "u", "v", "w", "x",
+
+ # 90
+ "y", "z", "braceleft", "bar", "braceright",
+ "asciitilde", "exclamdown", "cent", "sterling", "fraction",
+
+ # 100
+ "yen", "florin", "section", "currency", "quotesingle",
+ "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi",
+
+ # 110
+ "fl", "endash", "dagger", "daggerdbl", "periodcentered",
+ "paragraph", "bullet", "quotesinglbase", "quotedblbase", "quotedblright",
+
+ # 120
+ "guillemotright", "ellipsis", "perthousand", "questiondown", "grave",
+ "acute", "circumflex", "tilde", "macron", "breve",
+
+ # 130
+ "dotaccent", "dieresis", "ring", "cedilla", "hungarumlaut",
+ "ogonek", "caron", "emdash", "AE", "ordfeminine",
+
+ # 140
+ "Lslash", "Oslash", "OE", "ordmasculine", "ae",
+ "dotlessi", "lslash", "oslash", "oe", "germandbls",
+
+ # 150
+ "onesuperior", "logicalnot", "mu", "trademark", "Eth",
+ "onehalf", "plusminus", "Thorn", "onequarter", "divide",
+
+ # 160
+ "brokenbar", "degree", "thorn", "threequarters", "twosuperior",
+ "registered", "minus", "eth", "multiply", "threesuperior",
+
+ # 170
+ "copyright", "Aacute", "Acircumflex", "Adieresis", "Agrave",
+ "Aring", "Atilde", "Ccedilla", "Eacute", "Ecircumflex",
+
+ # 180
+ "Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis",
+ "Igrave", "Ntilde", "Oacute", "Ocircumflex", "Odieresis",
+
+ # 190
+ "Ograve", "Otilde", "Scaron", "Uacute", "Ucircumflex",
+ "Udieresis", "Ugrave", "Yacute", "Ydieresis", "Zcaron",
+
+ # 200
+ "aacute", "acircumflex", "adieresis", "agrave", "aring",
+ "atilde", "ccedilla", "eacute", "ecircumflex", "edieresis",
+
+ # 210
+ "egrave", "iacute", "icircumflex", "idieresis", "igrave",
+ "ntilde", "oacute", "ocircumflex", "odieresis", "ograve",
+
+ # 220
+ "otilde", "scaron", "uacute", "ucircumflex", "udieresis",
+ "ugrave", "yacute", "ydieresis", "zcaron", "exclamsmall",
+
+ # 230
+ "Hungarumlautsmall", "dollaroldstyle", "dollarsuperior", "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior", "parenrightsuperior", "twodotenleader",
+ "onedotenleader", "zerooldstyle",
+
+ # 240
+ "oneoldstyle", "twooldstyle", "threeoldstyle", "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle", "sevenoldstyle", "eightoldstyle", "nineoldstyle",
+ "commasuperior",
+
+ # 250
+ "threequartersemdash", "periodsuperior", "questionsmall", "asuperior",
+ "bsuperior",
+ "centsuperior", "dsuperior", "esuperior", "isuperior", "lsuperior",
+
+ # 260
+ "msuperior", "nsuperior", "osuperior", "rsuperior", "ssuperior",
+ "tsuperior", "ff", "ffi", "ffl", "parenleftinferior",
+
+ # 270
+ "parenrightinferior", "Circumflexsmall", "hyphensuperior", "Gravesmall",
+ "Asmall",
+ "Bsmall", "Csmall", "Dsmall", "Esmall", "Fsmall",
+
+ # 280
+ "Gsmall", "Hsmall", "Ismall", "Jsmall", "Ksmall",
+ "Lsmall", "Msmall", "Nsmall", "Osmall", "Psmall",
+
+ # 290
+ "Qsmall", "Rsmall", "Ssmall", "Tsmall", "Usmall",
+ "Vsmall", "Wsmall", "Xsmall", "Ysmall", "Zsmall",
+
+ # 300
+ "colonmonetary", "onefitted", "rupiah", "Tildesmall", "exclamdownsmall",
+ "centoldstyle", "Lslashsmall", "Scaronsmall", "Zcaronsmall",
+ "Dieresissmall",
+
+ # 310
+ "Brevesmall", "Caronsmall", "Dotaccentsmall", "Macronsmall", "figuredash",
+ "hypheninferior", "Ogoneksmall", "Ringsmall", "Cedillasmall",
+ "questiondownsmall",
+
+ # 320
+ "oneeighth", "threeeighths", "fiveeighths", "seveneighths", "onethird",
+ "twothirds", "zerosuperior", "foursuperior", "fivesuperior",
+ "sixsuperior",
+
+ # 330
+ "sevensuperior", "eightsuperior", "ninesuperior", "zeroinferior",
+ "oneinferior",
+ "twoinferior", "threeinferior", "fourinferior", "fiveinferior",
+ "sixinferior",
+
+ # 340
+ "seveninferior", "eightinferior", "nineinferior", "centinferior",
+ "dollarinferior",
+ "periodinferior", "commainferior", "Agravesmall", "Aacutesmall",
+ "Acircumflexsmall",
+
+ # 350
+ "Atildesmall", "Adieresissmall", "Aringsmall", "AEsmall", "Ccedillasmall",
+ "Egravesmall", "Eacutesmall", "Ecircumflexsmall", "Edieresissmall",
+ "Igravesmall",
+
+ # 360
+ "Iacutesmall", "Icircumflexsmall", "Idieresissmall", "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall", "Oacutesmall", "Ocircumflexsmall", "Otildesmall",
+ "Odieresissmall",
+
+ # 370
+ "OEsmall", "Oslashsmall", "Ugravesmall", "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall", "Yacutesmall", "Thornsmall", "Ydieresissmall",
+ "001.000",
+
+ # 380
+ "001.001", "001.002", "001.003", "Black", "Bold",
+ "Book", "Light", "Medium", "Regular", "Roman",
+
+ # 390
+ "Semibold"
+]
+
+
+# This table maps character codes of the Adobe Standard Type 1
+# encoding to glyph indices in the sid_standard_names table.
+#
+t1_standard_encoding = \
+[
+ 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, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 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, 0,
+ 0, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 0, 111, 112, 113,
+ 114, 0, 115, 116, 117, 118, 119, 120, 121, 122,
+ 0, 123, 0, 124, 125, 126, 127, 128, 129, 130,
+
+ 131, 0, 132, 133, 0, 134, 135, 136, 137, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 138, 0, 139, 0, 0,
+ 0, 0, 140, 141, 142, 143, 0, 0, 0, 0,
+ 0, 144, 0, 0, 0, 145, 0, 0, 146, 147,
+
+ 148, 149, 0, 0, 0, 0
+]
+
+
+# This table maps character codes of the Adobe Expert Type 1
+# encoding to glyph indices in the sid_standard_names table.
+#
+t1_expert_encoding = \
+[
+ 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, 1, 229, 230, 0, 231, 232, 233, 234,
+ 235, 236, 237, 238, 13, 14, 15, 99, 239, 240,
+
+ 241, 242, 243, 244, 245, 246, 247, 248, 27, 28,
+ 249, 250, 251, 252, 0, 253, 254, 255, 256, 257,
+ 0, 0, 0, 258, 0, 0, 259, 260, 261, 262,
+ 0, 0, 263, 264, 265, 0, 266, 109, 110, 267,
+ 268, 269, 0, 270, 271, 272, 273, 274, 275, 276,
+
+ 277, 278, 279, 280, 281, 282, 283, 284, 285, 286,
+ 287, 288, 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 302, 303, 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, 0,
+ 0, 304, 305, 306, 0, 0, 307, 308, 309, 310,
+ 311, 0, 312, 0, 0, 313, 0, 0, 314, 315,
+ 0, 0, 316, 317, 318, 0, 0, 0, 158, 155,
+ 163, 319, 320, 321, 322, 323, 324, 325, 0, 0,
+
+ 326, 150, 164, 169, 327, 328, 329, 330, 331, 332,
+ 333, 334, 335, 336, 337, 338, 339, 340, 341, 342,
+ 343, 344, 345, 346, 347, 348, 349, 350, 351, 352,
+ 353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
+ 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
+
+ 373, 374, 375, 376, 377, 378
+]
+
+
+# This data has been taken literally from the file `glyphlist.txt',
+# version 2.0, 22 Sept 2002. It is available from
+#
+# http://partners.adobe.com/asn/developer/typeforum/unicodegn.html
+# http://partners.adobe.com/public/developer/en/opentype/glyphlist.txt
+#
+adobe_glyph_list = """\
+A;0041
+AE;00C6
+AEacute;01FC
+AEmacron;01E2
+AEsmall;F7E6
+Aacute;00C1
+Aacutesmall;F7E1
+Abreve;0102
+Abreveacute;1EAE
+Abrevecyrillic;04D0
+Abrevedotbelow;1EB6
+Abrevegrave;1EB0
+Abrevehookabove;1EB2
+Abrevetilde;1EB4
+Acaron;01CD
+Acircle;24B6
+Acircumflex;00C2
+Acircumflexacute;1EA4
+Acircumflexdotbelow;1EAC
+Acircumflexgrave;1EA6
+Acircumflexhookabove;1EA8
+Acircumflexsmall;F7E2
+Acircumflextilde;1EAA
+Acute;F6C9
+Acutesmall;F7B4
+Acyrillic;0410
+Adblgrave;0200
+Adieresis;00C4
+Adieresiscyrillic;04D2
+Adieresismacron;01DE
+Adieresissmall;F7E4
+Adotbelow;1EA0
+Adotmacron;01E0
+Agrave;00C0
+Agravesmall;F7E0
+Ahookabove;1EA2
+Aiecyrillic;04D4
+Ainvertedbreve;0202
+Alpha;0391
+Alphatonos;0386
+Amacron;0100
+Amonospace;FF21
+Aogonek;0104
+Aring;00C5
+Aringacute;01FA
+Aringbelow;1E00
+Aringsmall;F7E5
+Asmall;F761
+Atilde;00C3
+Atildesmall;F7E3
+Aybarmenian;0531
+B;0042
+Bcircle;24B7
+Bdotaccent;1E02
+Bdotbelow;1E04
+Becyrillic;0411
+Benarmenian;0532
+Beta;0392
+Bhook;0181
+Blinebelow;1E06
+Bmonospace;FF22
+Brevesmall;F6F4
+Bsmall;F762
+Btopbar;0182
+C;0043
+Caarmenian;053E
+Cacute;0106
+Caron;F6CA
+Caronsmall;F6F5
+Ccaron;010C
+Ccedilla;00C7
+Ccedillaacute;1E08
+Ccedillasmall;F7E7
+Ccircle;24B8
+Ccircumflex;0108
+Cdot;010A
+Cdotaccent;010A
+Cedillasmall;F7B8
+Chaarmenian;0549
+Cheabkhasiancyrillic;04BC
+Checyrillic;0427
+Chedescenderabkhasiancyrillic;04BE
+Chedescendercyrillic;04B6
+Chedieresiscyrillic;04F4
+Cheharmenian;0543
+Chekhakassiancyrillic;04CB
+Cheverticalstrokecyrillic;04B8
+Chi;03A7
+Chook;0187
+Circumflexsmall;F6F6
+Cmonospace;FF23
+Coarmenian;0551
+Csmall;F763
+D;0044
+DZ;01F1
+DZcaron;01C4
+Daarmenian;0534
+Dafrican;0189
+Dcaron;010E
+Dcedilla;1E10
+Dcircle;24B9
+Dcircumflexbelow;1E12
+Dcroat;0110
+Ddotaccent;1E0A
+Ddotbelow;1E0C
+Decyrillic;0414
+Deicoptic;03EE
+Delta;2206
+Deltagreek;0394
+Dhook;018A
+Dieresis;F6CB
+DieresisAcute;F6CC
+DieresisGrave;F6CD
+Dieresissmall;F7A8
+Digammagreek;03DC
+Djecyrillic;0402
+Dlinebelow;1E0E
+Dmonospace;FF24
+Dotaccentsmall;F6F7
+Dslash;0110
+Dsmall;F764
+Dtopbar;018B
+Dz;01F2
+Dzcaron;01C5
+Dzeabkhasiancyrillic;04E0
+Dzecyrillic;0405
+Dzhecyrillic;040F
+E;0045
+Eacute;00C9
+Eacutesmall;F7E9
+Ebreve;0114
+Ecaron;011A
+Ecedillabreve;1E1C
+Echarmenian;0535
+Ecircle;24BA
+Ecircumflex;00CA
+Ecircumflexacute;1EBE
+Ecircumflexbelow;1E18
+Ecircumflexdotbelow;1EC6
+Ecircumflexgrave;1EC0
+Ecircumflexhookabove;1EC2
+Ecircumflexsmall;F7EA
+Ecircumflextilde;1EC4
+Ecyrillic;0404
+Edblgrave;0204
+Edieresis;00CB
+Edieresissmall;F7EB
+Edot;0116
+Edotaccent;0116
+Edotbelow;1EB8
+Efcyrillic;0424
+Egrave;00C8
+Egravesmall;F7E8
+Eharmenian;0537
+Ehookabove;1EBA
+Eightroman;2167
+Einvertedbreve;0206
+Eiotifiedcyrillic;0464
+Elcyrillic;041B
+Elevenroman;216A
+Emacron;0112
+Emacronacute;1E16
+Emacrongrave;1E14
+Emcyrillic;041C
+Emonospace;FF25
+Encyrillic;041D
+Endescendercyrillic;04A2
+Eng;014A
+Enghecyrillic;04A4
+Enhookcyrillic;04C7
+Eogonek;0118
+Eopen;0190
+Epsilon;0395
+Epsilontonos;0388
+Ercyrillic;0420
+Ereversed;018E
+Ereversedcyrillic;042D
+Escyrillic;0421
+Esdescendercyrillic;04AA
+Esh;01A9
+Esmall;F765
+Eta;0397
+Etarmenian;0538
+Etatonos;0389
+Eth;00D0
+Ethsmall;F7F0
+Etilde;1EBC
+Etildebelow;1E1A
+Euro;20AC
+Ezh;01B7
+Ezhcaron;01EE
+Ezhreversed;01B8
+F;0046
+Fcircle;24BB
+Fdotaccent;1E1E
+Feharmenian;0556
+Feicoptic;03E4
+Fhook;0191
+Fitacyrillic;0472
+Fiveroman;2164
+Fmonospace;FF26
+Fourroman;2163
+Fsmall;F766
+G;0047
+GBsquare;3387
+Gacute;01F4
+Gamma;0393
+Gammaafrican;0194
+Gangiacoptic;03EA
+Gbreve;011E
+Gcaron;01E6
+Gcedilla;0122
+Gcircle;24BC
+Gcircumflex;011C
+Gcommaaccent;0122
+Gdot;0120
+Gdotaccent;0120
+Gecyrillic;0413
+Ghadarmenian;0542
+Ghemiddlehookcyrillic;0494
+Ghestrokecyrillic;0492
+Gheupturncyrillic;0490
+Ghook;0193
+Gimarmenian;0533
+Gjecyrillic;0403
+Gmacron;1E20
+Gmonospace;FF27
+Grave;F6CE
+Gravesmall;F760
+Gsmall;F767
+Gsmallhook;029B
+Gstroke;01E4
+H;0048
+H18533;25CF
+H18543;25AA
+H18551;25AB
+H22073;25A1
+HPsquare;33CB
+Haabkhasiancyrillic;04A8
+Hadescendercyrillic;04B2
+Hardsigncyrillic;042A
+Hbar;0126
+Hbrevebelow;1E2A
+Hcedilla;1E28
+Hcircle;24BD
+Hcircumflex;0124
+Hdieresis;1E26
+Hdotaccent;1E22
+Hdotbelow;1E24
+Hmonospace;FF28
+Hoarmenian;0540
+Horicoptic;03E8
+Hsmall;F768
+Hungarumlaut;F6CF
+Hungarumlautsmall;F6F8
+Hzsquare;3390
+I;0049
+IAcyrillic;042F
+IJ;0132
+IUcyrillic;042E
+Iacute;00CD
+Iacutesmall;F7ED
+Ibreve;012C
+Icaron;01CF
+Icircle;24BE
+Icircumflex;00CE
+Icircumflexsmall;F7EE
+Icyrillic;0406
+Idblgrave;0208
+Idieresis;00CF
+Idieresisacute;1E2E
+Idieresiscyrillic;04E4
+Idieresissmall;F7EF
+Idot;0130
+Idotaccent;0130
+Idotbelow;1ECA
+Iebrevecyrillic;04D6
+Iecyrillic;0415
+Ifraktur;2111
+Igrave;00CC
+Igravesmall;F7EC
+Ihookabove;1EC8
+Iicyrillic;0418
+Iinvertedbreve;020A
+Iishortcyrillic;0419
+Imacron;012A
+Imacroncyrillic;04E2
+Imonospace;FF29
+Iniarmenian;053B
+Iocyrillic;0401
+Iogonek;012E
+Iota;0399
+Iotaafrican;0196
+Iotadieresis;03AA
+Iotatonos;038A
+Ismall;F769
+Istroke;0197
+Itilde;0128
+Itildebelow;1E2C
+Izhitsacyrillic;0474
+Izhitsadblgravecyrillic;0476
+J;004A
+Jaarmenian;0541
+Jcircle;24BF
+Jcircumflex;0134
+Jecyrillic;0408
+Jheharmenian;054B
+Jmonospace;FF2A
+Jsmall;F76A
+K;004B
+KBsquare;3385
+KKsquare;33CD
+Kabashkircyrillic;04A0
+Kacute;1E30
+Kacyrillic;041A
+Kadescendercyrillic;049A
+Kahookcyrillic;04C3
+Kappa;039A
+Kastrokecyrillic;049E
+Kaverticalstrokecyrillic;049C
+Kcaron;01E8
+Kcedilla;0136
+Kcircle;24C0
+Kcommaaccent;0136
+Kdotbelow;1E32
+Keharmenian;0554
+Kenarmenian;053F
+Khacyrillic;0425
+Kheicoptic;03E6
+Khook;0198
+Kjecyrillic;040C
+Klinebelow;1E34
+Kmonospace;FF2B
+Koppacyrillic;0480
+Koppagreek;03DE
+Ksicyrillic;046E
+Ksmall;F76B
+L;004C
+LJ;01C7
+LL;F6BF
+Lacute;0139
+Lambda;039B
+Lcaron;013D
+Lcedilla;013B
+Lcircle;24C1
+Lcircumflexbelow;1E3C
+Lcommaaccent;013B
+Ldot;013F
+Ldotaccent;013F
+Ldotbelow;1E36
+Ldotbelowmacron;1E38
+Liwnarmenian;053C
+Lj;01C8
+Ljecyrillic;0409
+Llinebelow;1E3A
+Lmonospace;FF2C
+Lslash;0141
+Lslashsmall;F6F9
+Lsmall;F76C
+M;004D
+MBsquare;3386
+Macron;F6D0
+Macronsmall;F7AF
+Macute;1E3E
+Mcircle;24C2
+Mdotaccent;1E40
+Mdotbelow;1E42
+Menarmenian;0544
+Mmonospace;FF2D
+Msmall;F76D
+Mturned;019C
+Mu;039C
+N;004E
+NJ;01CA
+Nacute;0143
+Ncaron;0147
+Ncedilla;0145
+Ncircle;24C3
+Ncircumflexbelow;1E4A
+Ncommaaccent;0145
+Ndotaccent;1E44
+Ndotbelow;1E46
+Nhookleft;019D
+Nineroman;2168
+Nj;01CB
+Njecyrillic;040A
+Nlinebelow;1E48
+Nmonospace;FF2E
+Nowarmenian;0546
+Nsmall;F76E
+Ntilde;00D1
+Ntildesmall;F7F1
+Nu;039D
+O;004F
+OE;0152
+OEsmall;F6FA
+Oacute;00D3
+Oacutesmall;F7F3
+Obarredcyrillic;04E8
+Obarreddieresiscyrillic;04EA
+Obreve;014E
+Ocaron;01D1
+Ocenteredtilde;019F
+Ocircle;24C4
+Ocircumflex;00D4
+Ocircumflexacute;1ED0
+Ocircumflexdotbelow;1ED8
+Ocircumflexgrave;1ED2
+Ocircumflexhookabove;1ED4
+Ocircumflexsmall;F7F4
+Ocircumflextilde;1ED6
+Ocyrillic;041E
+Odblacute;0150
+Odblgrave;020C
+Odieresis;00D6
+Odieresiscyrillic;04E6
+Odieresissmall;F7F6
+Odotbelow;1ECC
+Ogoneksmall;F6FB
+Ograve;00D2
+Ogravesmall;F7F2
+Oharmenian;0555
+Ohm;2126
+Ohookabove;1ECE
+Ohorn;01A0
+Ohornacute;1EDA
+Ohorndotbelow;1EE2
+Ohorngrave;1EDC
+Ohornhookabove;1EDE
+Ohorntilde;1EE0
+Ohungarumlaut;0150
+Oi;01A2
+Oinvertedbreve;020E
+Omacron;014C
+Omacronacute;1E52
+Omacrongrave;1E50
+Omega;2126
+Omegacyrillic;0460
+Omegagreek;03A9
+Omegaroundcyrillic;047A
+Omegatitlocyrillic;047C
+Omegatonos;038F
+Omicron;039F
+Omicrontonos;038C
+Omonospace;FF2F
+Oneroman;2160
+Oogonek;01EA
+Oogonekmacron;01EC
+Oopen;0186
+Oslash;00D8
+Oslashacute;01FE
+Oslashsmall;F7F8
+Osmall;F76F
+Ostrokeacute;01FE
+Otcyrillic;047E
+Otilde;00D5
+Otildeacute;1E4C
+Otildedieresis;1E4E
+Otildesmall;F7F5
+P;0050
+Pacute;1E54
+Pcircle;24C5
+Pdotaccent;1E56
+Pecyrillic;041F
+Peharmenian;054A
+Pemiddlehookcyrillic;04A6
+Phi;03A6
+Phook;01A4
+Pi;03A0
+Piwrarmenian;0553
+Pmonospace;FF30
+Psi;03A8
+Psicyrillic;0470
+Psmall;F770
+Q;0051
+Qcircle;24C6
+Qmonospace;FF31
+Qsmall;F771
+R;0052
+Raarmenian;054C
+Racute;0154
+Rcaron;0158
+Rcedilla;0156
+Rcircle;24C7
+Rcommaaccent;0156
+Rdblgrave;0210
+Rdotaccent;1E58
+Rdotbelow;1E5A
+Rdotbelowmacron;1E5C
+Reharmenian;0550
+Rfraktur;211C
+Rho;03A1
+Ringsmall;F6FC
+Rinvertedbreve;0212
+Rlinebelow;1E5E
+Rmonospace;FF32
+Rsmall;F772
+Rsmallinverted;0281
+Rsmallinvertedsuperior;02B6
+S;0053
+SF010000;250C
+SF020000;2514
+SF030000;2510
+SF040000;2518
+SF050000;253C
+SF060000;252C
+SF070000;2534
+SF080000;251C
+SF090000;2524
+SF100000;2500
+SF110000;2502
+SF190000;2561
+SF200000;2562
+SF210000;2556
+SF220000;2555
+SF230000;2563
+SF240000;2551
+SF250000;2557
+SF260000;255D
+SF270000;255C
+SF280000;255B
+SF360000;255E
+SF370000;255F
+SF380000;255A
+SF390000;2554
+SF400000;2569
+SF410000;2566
+SF420000;2560
+SF430000;2550
+SF440000;256C
+SF450000;2567
+SF460000;2568
+SF470000;2564
+SF480000;2565
+SF490000;2559
+SF500000;2558
+SF510000;2552
+SF520000;2553
+SF530000;256B
+SF540000;256A
+Sacute;015A
+Sacutedotaccent;1E64
+Sampigreek;03E0
+Scaron;0160
+Scarondotaccent;1E66
+Scaronsmall;F6FD
+Scedilla;015E
+Schwa;018F
+Schwacyrillic;04D8
+Schwadieresiscyrillic;04DA
+Scircle;24C8
+Scircumflex;015C
+Scommaaccent;0218
+Sdotaccent;1E60
+Sdotbelow;1E62
+Sdotbelowdotaccent;1E68
+Seharmenian;054D
+Sevenroman;2166
+Shaarmenian;0547
+Shacyrillic;0428
+Shchacyrillic;0429
+Sheicoptic;03E2
+Shhacyrillic;04BA
+Shimacoptic;03EC
+Sigma;03A3
+Sixroman;2165
+Smonospace;FF33
+Softsigncyrillic;042C
+Ssmall;F773
+Stigmagreek;03DA
+T;0054
+Tau;03A4
+Tbar;0166
+Tcaron;0164
+Tcedilla;0162
+Tcircle;24C9
+Tcircumflexbelow;1E70
+Tcommaaccent;0162
+Tdotaccent;1E6A
+Tdotbelow;1E6C
+Tecyrillic;0422
+Tedescendercyrillic;04AC
+Tenroman;2169
+Tetsecyrillic;04B4
+Theta;0398
+Thook;01AC
+Thorn;00DE
+Thornsmall;F7FE
+Threeroman;2162
+Tildesmall;F6FE
+Tiwnarmenian;054F
+Tlinebelow;1E6E
+Tmonospace;FF34
+Toarmenian;0539
+Tonefive;01BC
+Tonesix;0184
+Tonetwo;01A7
+Tretroflexhook;01AE
+Tsecyrillic;0426
+Tshecyrillic;040B
+Tsmall;F774
+Twelveroman;216B
+Tworoman;2161
+U;0055
+Uacute;00DA
+Uacutesmall;F7FA
+Ubreve;016C
+Ucaron;01D3
+Ucircle;24CA
+Ucircumflex;00DB
+Ucircumflexbelow;1E76
+Ucircumflexsmall;F7FB
+Ucyrillic;0423
+Udblacute;0170
+Udblgrave;0214
+Udieresis;00DC
+Udieresisacute;01D7
+Udieresisbelow;1E72
+Udieresiscaron;01D9
+Udieresiscyrillic;04F0
+Udieresisgrave;01DB
+Udieresismacron;01D5
+Udieresissmall;F7FC
+Udotbelow;1EE4
+Ugrave;00D9
+Ugravesmall;F7F9
+Uhookabove;1EE6
+Uhorn;01AF
+Uhornacute;1EE8
+Uhorndotbelow;1EF0
+Uhorngrave;1EEA
+Uhornhookabove;1EEC
+Uhorntilde;1EEE
+Uhungarumlaut;0170
+Uhungarumlautcyrillic;04F2
+Uinvertedbreve;0216
+Ukcyrillic;0478
+Umacron;016A
+Umacroncyrillic;04EE
+Umacrondieresis;1E7A
+Umonospace;FF35
+Uogonek;0172
+Upsilon;03A5
+Upsilon1;03D2
+Upsilonacutehooksymbolgreek;03D3
+Upsilonafrican;01B1
+Upsilondieresis;03AB
+Upsilondieresishooksymbolgreek;03D4
+Upsilonhooksymbol;03D2
+Upsilontonos;038E
+Uring;016E
+Ushortcyrillic;040E
+Usmall;F775
+Ustraightcyrillic;04AE
+Ustraightstrokecyrillic;04B0
+Utilde;0168
+Utildeacute;1E78
+Utildebelow;1E74
+V;0056
+Vcircle;24CB
+Vdotbelow;1E7E
+Vecyrillic;0412
+Vewarmenian;054E
+Vhook;01B2
+Vmonospace;FF36
+Voarmenian;0548
+Vsmall;F776
+Vtilde;1E7C
+W;0057
+Wacute;1E82
+Wcircle;24CC
+Wcircumflex;0174
+Wdieresis;1E84
+Wdotaccent;1E86
+Wdotbelow;1E88
+Wgrave;1E80
+Wmonospace;FF37
+Wsmall;F777
+X;0058
+Xcircle;24CD
+Xdieresis;1E8C
+Xdotaccent;1E8A
+Xeharmenian;053D
+Xi;039E
+Xmonospace;FF38
+Xsmall;F778
+Y;0059
+Yacute;00DD
+Yacutesmall;F7FD
+Yatcyrillic;0462
+Ycircle;24CE
+Ycircumflex;0176
+Ydieresis;0178
+Ydieresissmall;F7FF
+Ydotaccent;1E8E
+Ydotbelow;1EF4
+Yericyrillic;042B
+Yerudieresiscyrillic;04F8
+Ygrave;1EF2
+Yhook;01B3
+Yhookabove;1EF6
+Yiarmenian;0545
+Yicyrillic;0407
+Yiwnarmenian;0552
+Ymonospace;FF39
+Ysmall;F779
+Ytilde;1EF8
+Yusbigcyrillic;046A
+Yusbigiotifiedcyrillic;046C
+Yuslittlecyrillic;0466
+Yuslittleiotifiedcyrillic;0468
+Z;005A
+Zaarmenian;0536
+Zacute;0179
+Zcaron;017D
+Zcaronsmall;F6FF
+Zcircle;24CF
+Zcircumflex;1E90
+Zdot;017B
+Zdotaccent;017B
+Zdotbelow;1E92
+Zecyrillic;0417
+Zedescendercyrillic;0498
+Zedieresiscyrillic;04DE
+Zeta;0396
+Zhearmenian;053A
+Zhebrevecyrillic;04C1
+Zhecyrillic;0416
+Zhedescendercyrillic;0496
+Zhedieresiscyrillic;04DC
+Zlinebelow;1E94
+Zmonospace;FF3A
+Zsmall;F77A
+Zstroke;01B5
+a;0061
+aabengali;0986
+aacute;00E1
+aadeva;0906
+aagujarati;0A86
+aagurmukhi;0A06
+aamatragurmukhi;0A3E
+aarusquare;3303
+aavowelsignbengali;09BE
+aavowelsigndeva;093E
+aavowelsigngujarati;0ABE
+abbreviationmarkarmenian;055F
+abbreviationsigndeva;0970
+abengali;0985
+abopomofo;311A
+abreve;0103
+abreveacute;1EAF
+abrevecyrillic;04D1
+abrevedotbelow;1EB7
+abrevegrave;1EB1
+abrevehookabove;1EB3
+abrevetilde;1EB5
+acaron;01CE
+acircle;24D0
+acircumflex;00E2
+acircumflexacute;1EA5
+acircumflexdotbelow;1EAD
+acircumflexgrave;1EA7
+acircumflexhookabove;1EA9
+acircumflextilde;1EAB
+acute;00B4
+acutebelowcmb;0317
+acutecmb;0301
+acutecomb;0301
+acutedeva;0954
+acutelowmod;02CF
+acutetonecmb;0341
+acyrillic;0430
+adblgrave;0201
+addakgurmukhi;0A71
+adeva;0905
+adieresis;00E4
+adieresiscyrillic;04D3
+adieresismacron;01DF
+adotbelow;1EA1
+adotmacron;01E1
+ae;00E6
+aeacute;01FD
+aekorean;3150
+aemacron;01E3
+afii00208;2015
+afii08941;20A4
+afii10017;0410
+afii10018;0411
+afii10019;0412
+afii10020;0413
+afii10021;0414
+afii10022;0415
+afii10023;0401
+afii10024;0416
+afii10025;0417
+afii10026;0418
+afii10027;0419
+afii10028;041A
+afii10029;041B
+afii10030;041C
+afii10031;041D
+afii10032;041E
+afii10033;041F
+afii10034;0420
+afii10035;0421
+afii10036;0422
+afii10037;0423
+afii10038;0424
+afii10039;0425
+afii10040;0426
+afii10041;0427
+afii10042;0428
+afii10043;0429
+afii10044;042A
+afii10045;042B
+afii10046;042C
+afii10047;042D
+afii10048;042E
+afii10049;042F
+afii10050;0490
+afii10051;0402
+afii10052;0403
+afii10053;0404
+afii10054;0405
+afii10055;0406
+afii10056;0407
+afii10057;0408
+afii10058;0409
+afii10059;040A
+afii10060;040B
+afii10061;040C
+afii10062;040E
+afii10063;F6C4
+afii10064;F6C5
+afii10065;0430
+afii10066;0431
+afii10067;0432
+afii10068;0433
+afii10069;0434
+afii10070;0435
+afii10071;0451
+afii10072;0436
+afii10073;0437
+afii10074;0438
+afii10075;0439
+afii10076;043A
+afii10077;043B
+afii10078;043C
+afii10079;043D
+afii10080;043E
+afii10081;043F
+afii10082;0440
+afii10083;0441
+afii10084;0442
+afii10085;0443
+afii10086;0444
+afii10087;0445
+afii10088;0446
+afii10089;0447
+afii10090;0448
+afii10091;0449
+afii10092;044A
+afii10093;044B
+afii10094;044C
+afii10095;044D
+afii10096;044E
+afii10097;044F
+afii10098;0491
+afii10099;0452
+afii10100;0453
+afii10101;0454
+afii10102;0455
+afii10103;0456
+afii10104;0457
+afii10105;0458
+afii10106;0459
+afii10107;045A
+afii10108;045B
+afii10109;045C
+afii10110;045E
+afii10145;040F
+afii10146;0462
+afii10147;0472
+afii10148;0474
+afii10192;F6C6
+afii10193;045F
+afii10194;0463
+afii10195;0473
+afii10196;0475
+afii10831;F6C7
+afii10832;F6C8
+afii10846;04D9
+afii299;200E
+afii300;200F
+afii301;200D
+afii57381;066A
+afii57388;060C
+afii57392;0660
+afii57393;0661
+afii57394;0662
+afii57395;0663
+afii57396;0664
+afii57397;0665
+afii57398;0666
+afii57399;0667
+afii57400;0668
+afii57401;0669
+afii57403;061B
+afii57407;061F
+afii57409;0621
+afii57410;0622
+afii57411;0623
+afii57412;0624
+afii57413;0625
+afii57414;0626
+afii57415;0627
+afii57416;0628
+afii57417;0629
+afii57418;062A
+afii57419;062B
+afii57420;062C
+afii57421;062D
+afii57422;062E
+afii57423;062F
+afii57424;0630
+afii57425;0631
+afii57426;0632
+afii57427;0633
+afii57428;0634
+afii57429;0635
+afii57430;0636
+afii57431;0637
+afii57432;0638
+afii57433;0639
+afii57434;063A
+afii57440;0640
+afii57441;0641
+afii57442;0642
+afii57443;0643
+afii57444;0644
+afii57445;0645
+afii57446;0646
+afii57448;0648
+afii57449;0649
+afii57450;064A
+afii57451;064B
+afii57452;064C
+afii57453;064D
+afii57454;064E
+afii57455;064F
+afii57456;0650
+afii57457;0651
+afii57458;0652
+afii57470;0647
+afii57505;06A4
+afii57506;067E
+afii57507;0686
+afii57508;0698
+afii57509;06AF
+afii57511;0679
+afii57512;0688
+afii57513;0691
+afii57514;06BA
+afii57519;06D2
+afii57534;06D5
+afii57636;20AA
+afii57645;05BE
+afii57658;05C3
+afii57664;05D0
+afii57665;05D1
+afii57666;05D2
+afii57667;05D3
+afii57668;05D4
+afii57669;05D5
+afii57670;05D6
+afii57671;05D7
+afii57672;05D8
+afii57673;05D9
+afii57674;05DA
+afii57675;05DB
+afii57676;05DC
+afii57677;05DD
+afii57678;05DE
+afii57679;05DF
+afii57680;05E0
+afii57681;05E1
+afii57682;05E2
+afii57683;05E3
+afii57684;05E4
+afii57685;05E5
+afii57686;05E6
+afii57687;05E7
+afii57688;05E8
+afii57689;05E9
+afii57690;05EA
+afii57694;FB2A
+afii57695;FB2B
+afii57700;FB4B
+afii57705;FB1F
+afii57716;05F0
+afii57717;05F1
+afii57718;05F2
+afii57723;FB35
+afii57793;05B4
+afii57794;05B5
+afii57795;05B6
+afii57796;05BB
+afii57797;05B8
+afii57798;05B7
+afii57799;05B0
+afii57800;05B2
+afii57801;05B1
+afii57802;05B3
+afii57803;05C2
+afii57804;05C1
+afii57806;05B9
+afii57807;05BC
+afii57839;05BD
+afii57841;05BF
+afii57842;05C0
+afii57929;02BC
+afii61248;2105
+afii61289;2113
+afii61352;2116
+afii61573;202C
+afii61574;202D
+afii61575;202E
+afii61664;200C
+afii63167;066D
+afii64937;02BD
+agrave;00E0
+agujarati;0A85
+agurmukhi;0A05
+ahiragana;3042
+ahookabove;1EA3
+aibengali;0990
+aibopomofo;311E
+aideva;0910
+aiecyrillic;04D5
+aigujarati;0A90
+aigurmukhi;0A10
+aimatragurmukhi;0A48
+ainarabic;0639
+ainfinalarabic;FECA
+aininitialarabic;FECB
+ainmedialarabic;FECC
+ainvertedbreve;0203
+aivowelsignbengali;09C8
+aivowelsigndeva;0948
+aivowelsigngujarati;0AC8
+akatakana;30A2
+akatakanahalfwidth;FF71
+akorean;314F
+alef;05D0
+alefarabic;0627
+alefdageshhebrew;FB30
+aleffinalarabic;FE8E
+alefhamzaabovearabic;0623
+alefhamzaabovefinalarabic;FE84
+alefhamzabelowarabic;0625
+alefhamzabelowfinalarabic;FE88
+alefhebrew;05D0
+aleflamedhebrew;FB4F
+alefmaddaabovearabic;0622
+alefmaddaabovefinalarabic;FE82
+alefmaksuraarabic;0649
+alefmaksurafinalarabic;FEF0
+alefmaksurainitialarabic;FEF3
+alefmaksuramedialarabic;FEF4
+alefpatahhebrew;FB2E
+alefqamatshebrew;FB2F
+aleph;2135
+allequal;224C
+alpha;03B1
+alphatonos;03AC
+amacron;0101
+amonospace;FF41
+ampersand;0026
+ampersandmonospace;FF06
+ampersandsmall;F726
+amsquare;33C2
+anbopomofo;3122
+angbopomofo;3124
+angkhankhuthai;0E5A
+angle;2220
+anglebracketleft;3008
+anglebracketleftvertical;FE3F
+anglebracketright;3009
+anglebracketrightvertical;FE40
+angleleft;2329
+angleright;232A
+angstrom;212B
+anoteleia;0387
+anudattadeva;0952
+anusvarabengali;0982
+anusvaradeva;0902
+anusvaragujarati;0A82
+aogonek;0105
+apaatosquare;3300
+aparen;249C
+apostrophearmenian;055A
+apostrophemod;02BC
+apple;F8FF
+approaches;2250
+approxequal;2248
+approxequalorimage;2252
+approximatelyequal;2245
+araeaekorean;318E
+araeakorean;318D
+arc;2312
+arighthalfring;1E9A
+aring;00E5
+aringacute;01FB
+aringbelow;1E01
+arrowboth;2194
+arrowdashdown;21E3
+arrowdashleft;21E0
+arrowdashright;21E2
+arrowdashup;21E1
+arrowdblboth;21D4
+arrowdbldown;21D3
+arrowdblleft;21D0
+arrowdblright;21D2
+arrowdblup;21D1
+arrowdown;2193
+arrowdownleft;2199
+arrowdownright;2198
+arrowdownwhite;21E9
+arrowheaddownmod;02C5
+arrowheadleftmod;02C2
+arrowheadrightmod;02C3
+arrowheadupmod;02C4
+arrowhorizex;F8E7
+arrowleft;2190
+arrowleftdbl;21D0
+arrowleftdblstroke;21CD
+arrowleftoverright;21C6
+arrowleftwhite;21E6
+arrowright;2192
+arrowrightdblstroke;21CF
+arrowrightheavy;279E
+arrowrightoverleft;21C4
+arrowrightwhite;21E8
+arrowtableft;21E4
+arrowtabright;21E5
+arrowup;2191
+arrowupdn;2195
+arrowupdnbse;21A8
+arrowupdownbase;21A8
+arrowupleft;2196
+arrowupleftofdown;21C5
+arrowupright;2197
+arrowupwhite;21E7
+arrowvertex;F8E6
+asciicircum;005E
+asciicircummonospace;FF3E
+asciitilde;007E
+asciitildemonospace;FF5E
+ascript;0251
+ascriptturned;0252
+asmallhiragana;3041
+asmallkatakana;30A1
+asmallkatakanahalfwidth;FF67
+asterisk;002A
+asteriskaltonearabic;066D
+asteriskarabic;066D
+asteriskmath;2217
+asteriskmonospace;FF0A
+asterisksmall;FE61
+asterism;2042
+asuperior;F6E9
+asymptoticallyequal;2243
+at;0040
+atilde;00E3
+atmonospace;FF20
+atsmall;FE6B
+aturned;0250
+aubengali;0994
+aubopomofo;3120
+audeva;0914
+augujarati;0A94
+augurmukhi;0A14
+aulengthmarkbengali;09D7
+aumatragurmukhi;0A4C
+auvowelsignbengali;09CC
+auvowelsigndeva;094C
+auvowelsigngujarati;0ACC
+avagrahadeva;093D
+aybarmenian;0561
+ayin;05E2
+ayinaltonehebrew;FB20
+ayinhebrew;05E2
+b;0062
+babengali;09AC
+backslash;005C
+backslashmonospace;FF3C
+badeva;092C
+bagujarati;0AAC
+bagurmukhi;0A2C
+bahiragana;3070
+bahtthai;0E3F
+bakatakana;30D0
+bar;007C
+barmonospace;FF5C
+bbopomofo;3105
+bcircle;24D1
+bdotaccent;1E03
+bdotbelow;1E05
+beamedsixteenthnotes;266C
+because;2235
+becyrillic;0431
+beharabic;0628
+behfinalarabic;FE90
+behinitialarabic;FE91
+behiragana;3079
+behmedialarabic;FE92
+behmeeminitialarabic;FC9F
+behmeemisolatedarabic;FC08
+behnoonfinalarabic;FC6D
+bekatakana;30D9
+benarmenian;0562
+bet;05D1
+beta;03B2
+betasymbolgreek;03D0
+betdagesh;FB31
+betdageshhebrew;FB31
+bethebrew;05D1
+betrafehebrew;FB4C
+bhabengali;09AD
+bhadeva;092D
+bhagujarati;0AAD
+bhagurmukhi;0A2D
+bhook;0253
+bihiragana;3073
+bikatakana;30D3
+bilabialclick;0298
+bindigurmukhi;0A02
+birusquare;3331
+blackcircle;25CF
+blackdiamond;25C6
+blackdownpointingtriangle;25BC
+blackleftpointingpointer;25C4
+blackleftpointingtriangle;25C0
+blacklenticularbracketleft;3010
+blacklenticularbracketleftvertical;FE3B
+blacklenticularbracketright;3011
+blacklenticularbracketrightvertical;FE3C
+blacklowerlefttriangle;25E3
+blacklowerrighttriangle;25E2
+blackrectangle;25AC
+blackrightpointingpointer;25BA
+blackrightpointingtriangle;25B6
+blacksmallsquare;25AA
+blacksmilingface;263B
+blacksquare;25A0
+blackstar;2605
+blackupperlefttriangle;25E4
+blackupperrighttriangle;25E5
+blackuppointingsmalltriangle;25B4
+blackuppointingtriangle;25B2
+blank;2423
+blinebelow;1E07
+block;2588
+bmonospace;FF42
+bobaimaithai;0E1A
+bohiragana;307C
+bokatakana;30DC
+bparen;249D
+bqsquare;33C3
+braceex;F8F4
+braceleft;007B
+braceleftbt;F8F3
+braceleftmid;F8F2
+braceleftmonospace;FF5B
+braceleftsmall;FE5B
+bracelefttp;F8F1
+braceleftvertical;FE37
+braceright;007D
+bracerightbt;F8FE
+bracerightmid;F8FD
+bracerightmonospace;FF5D
+bracerightsmall;FE5C
+bracerighttp;F8FC
+bracerightvertical;FE38
+bracketleft;005B
+bracketleftbt;F8F0
+bracketleftex;F8EF
+bracketleftmonospace;FF3B
+bracketlefttp;F8EE
+bracketright;005D
+bracketrightbt;F8FB
+bracketrightex;F8FA
+bracketrightmonospace;FF3D
+bracketrighttp;F8F9
+breve;02D8
+brevebelowcmb;032E
+brevecmb;0306
+breveinvertedbelowcmb;032F
+breveinvertedcmb;0311
+breveinverteddoublecmb;0361
+bridgebelowcmb;032A
+bridgeinvertedbelowcmb;033A
+brokenbar;00A6
+bstroke;0180
+bsuperior;F6EA
+btopbar;0183
+buhiragana;3076
+bukatakana;30D6
+bullet;2022
+bulletinverse;25D8
+bulletoperator;2219
+bullseye;25CE
+c;0063
+caarmenian;056E
+cabengali;099A
+cacute;0107
+cadeva;091A
+cagujarati;0A9A
+cagurmukhi;0A1A
+calsquare;3388
+candrabindubengali;0981
+candrabinducmb;0310
+candrabindudeva;0901
+candrabindugujarati;0A81
+capslock;21EA
+careof;2105
+caron;02C7
+caronbelowcmb;032C
+caroncmb;030C
+carriagereturn;21B5
+cbopomofo;3118
+ccaron;010D
+ccedilla;00E7
+ccedillaacute;1E09
+ccircle;24D2
+ccircumflex;0109
+ccurl;0255
+cdot;010B
+cdotaccent;010B
+cdsquare;33C5
+cedilla;00B8
+cedillacmb;0327
+cent;00A2
+centigrade;2103
+centinferior;F6DF
+centmonospace;FFE0
+centoldstyle;F7A2
+centsuperior;F6E0
+chaarmenian;0579
+chabengali;099B
+chadeva;091B
+chagujarati;0A9B
+chagurmukhi;0A1B
+chbopomofo;3114
+cheabkhasiancyrillic;04BD
+checkmark;2713
+checyrillic;0447
+chedescenderabkhasiancyrillic;04BF
+chedescendercyrillic;04B7
+chedieresiscyrillic;04F5
+cheharmenian;0573
+chekhakassiancyrillic;04CC
+cheverticalstrokecyrillic;04B9
+chi;03C7
+chieuchacirclekorean;3277
+chieuchaparenkorean;3217
+chieuchcirclekorean;3269
+chieuchkorean;314A
+chieuchparenkorean;3209
+chochangthai;0E0A
+chochanthai;0E08
+chochingthai;0E09
+chochoethai;0E0C
+chook;0188
+cieucacirclekorean;3276
+cieucaparenkorean;3216
+cieuccirclekorean;3268
+cieuckorean;3148
+cieucparenkorean;3208
+cieucuparenkorean;321C
+circle;25CB
+circlemultiply;2297
+circleot;2299
+circleplus;2295
+circlepostalmark;3036
+circlewithlefthalfblack;25D0
+circlewithrighthalfblack;25D1
+circumflex;02C6
+circumflexbelowcmb;032D
+circumflexcmb;0302
+clear;2327
+clickalveolar;01C2
+clickdental;01C0
+clicklateral;01C1
+clickretroflex;01C3
+club;2663
+clubsuitblack;2663
+clubsuitwhite;2667
+cmcubedsquare;33A4
+cmonospace;FF43
+cmsquaredsquare;33A0
+coarmenian;0581
+colon;003A
+colonmonetary;20A1
+colonmonospace;FF1A
+colonsign;20A1
+colonsmall;FE55
+colontriangularhalfmod;02D1
+colontriangularmod;02D0
+comma;002C
+commaabovecmb;0313
+commaaboverightcmb;0315
+commaaccent;F6C3
+commaarabic;060C
+commaarmenian;055D
+commainferior;F6E1
+commamonospace;FF0C
+commareversedabovecmb;0314
+commareversedmod;02BD
+commasmall;FE50
+commasuperior;F6E2
+commaturnedabovecmb;0312
+commaturnedmod;02BB
+compass;263C
+congruent;2245
+contourintegral;222E
+control;2303
+controlACK;0006
+controlBEL;0007
+controlBS;0008
+controlCAN;0018
+controlCR;000D
+controlDC1;0011
+controlDC2;0012
+controlDC3;0013
+controlDC4;0014
+controlDEL;007F
+controlDLE;0010
+controlEM;0019
+controlENQ;0005
+controlEOT;0004
+controlESC;001B
+controlETB;0017
+controlETX;0003
+controlFF;000C
+controlFS;001C
+controlGS;001D
+controlHT;0009
+controlLF;000A
+controlNAK;0015
+controlRS;001E
+controlSI;000F
+controlSO;000E
+controlSOT;0002
+controlSTX;0001
+controlSUB;001A
+controlSYN;0016
+controlUS;001F
+controlVT;000B
+copyright;00A9
+copyrightsans;F8E9
+copyrightserif;F6D9
+cornerbracketleft;300C
+cornerbracketlefthalfwidth;FF62
+cornerbracketleftvertical;FE41
+cornerbracketright;300D
+cornerbracketrighthalfwidth;FF63
+cornerbracketrightvertical;FE42
+corporationsquare;337F
+cosquare;33C7
+coverkgsquare;33C6
+cparen;249E
+cruzeiro;20A2
+cstretched;0297
+curlyand;22CF
+curlyor;22CE
+currency;00A4
+cyrBreve;F6D1
+cyrFlex;F6D2
+cyrbreve;F6D4
+cyrflex;F6D5
+d;0064
+daarmenian;0564
+dabengali;09A6
+dadarabic;0636
+dadeva;0926
+dadfinalarabic;FEBE
+dadinitialarabic;FEBF
+dadmedialarabic;FEC0
+dagesh;05BC
+dageshhebrew;05BC
+dagger;2020
+daggerdbl;2021
+dagujarati;0AA6
+dagurmukhi;0A26
+dahiragana;3060
+dakatakana;30C0
+dalarabic;062F
+dalet;05D3
+daletdagesh;FB33
+daletdageshhebrew;FB33
+dalethatafpatah;05D3 05B2
+dalethatafpatahhebrew;05D3 05B2
+dalethatafsegol;05D3 05B1
+dalethatafsegolhebrew;05D3 05B1
+dalethebrew;05D3
+dalethiriq;05D3 05B4
+dalethiriqhebrew;05D3 05B4
+daletholam;05D3 05B9
+daletholamhebrew;05D3 05B9
+daletpatah;05D3 05B7
+daletpatahhebrew;05D3 05B7
+daletqamats;05D3 05B8
+daletqamatshebrew;05D3 05B8
+daletqubuts;05D3 05BB
+daletqubutshebrew;05D3 05BB
+daletsegol;05D3 05B6
+daletsegolhebrew;05D3 05B6
+daletsheva;05D3 05B0
+daletshevahebrew;05D3 05B0
+dalettsere;05D3 05B5
+dalettserehebrew;05D3 05B5
+dalfinalarabic;FEAA
+dammaarabic;064F
+dammalowarabic;064F
+dammatanaltonearabic;064C
+dammatanarabic;064C
+danda;0964
+dargahebrew;05A7
+dargalefthebrew;05A7
+dasiapneumatacyrilliccmb;0485
+dblGrave;F6D3
+dblanglebracketleft;300A
+dblanglebracketleftvertical;FE3D
+dblanglebracketright;300B
+dblanglebracketrightvertical;FE3E
+dblarchinvertedbelowcmb;032B
+dblarrowleft;21D4
+dblarrowright;21D2
+dbldanda;0965
+dblgrave;F6D6
+dblgravecmb;030F
+dblintegral;222C
+dbllowline;2017
+dbllowlinecmb;0333
+dbloverlinecmb;033F
+dblprimemod;02BA
+dblverticalbar;2016
+dblverticallineabovecmb;030E
+dbopomofo;3109
+dbsquare;33C8
+dcaron;010F
+dcedilla;1E11
+dcircle;24D3
+dcircumflexbelow;1E13
+dcroat;0111
+ddabengali;09A1
+ddadeva;0921
+ddagujarati;0AA1
+ddagurmukhi;0A21
+ddalarabic;0688
+ddalfinalarabic;FB89
+dddhadeva;095C
+ddhabengali;09A2
+ddhadeva;0922
+ddhagujarati;0AA2
+ddhagurmukhi;0A22
+ddotaccent;1E0B
+ddotbelow;1E0D
+decimalseparatorarabic;066B
+decimalseparatorpersian;066B
+decyrillic;0434
+degree;00B0
+dehihebrew;05AD
+dehiragana;3067
+deicoptic;03EF
+dekatakana;30C7
+deleteleft;232B
+deleteright;2326
+delta;03B4
+deltaturned;018D
+denominatorminusonenumeratorbengali;09F8
+dezh;02A4
+dhabengali;09A7
+dhadeva;0927
+dhagujarati;0AA7
+dhagurmukhi;0A27
+dhook;0257
+dialytikatonos;0385
+dialytikatonoscmb;0344
+diamond;2666
+diamondsuitwhite;2662
+dieresis;00A8
+dieresisacute;F6D7
+dieresisbelowcmb;0324
+dieresiscmb;0308
+dieresisgrave;F6D8
+dieresistonos;0385
+dihiragana;3062
+dikatakana;30C2
+dittomark;3003
+divide;00F7
+divides;2223
+divisionslash;2215
+djecyrillic;0452
+dkshade;2593
+dlinebelow;1E0F
+dlsquare;3397
+dmacron;0111
+dmonospace;FF44
+dnblock;2584
+dochadathai;0E0E
+dodekthai;0E14
+dohiragana;3069
+dokatakana;30C9
+dollar;0024
+dollarinferior;F6E3
+dollarmonospace;FF04
+dollaroldstyle;F724
+dollarsmall;FE69
+dollarsuperior;F6E4
+dong;20AB
+dorusquare;3326
+dotaccent;02D9
+dotaccentcmb;0307
+dotbelowcmb;0323
+dotbelowcomb;0323
+dotkatakana;30FB
+dotlessi;0131
+dotlessj;F6BE
+dotlessjstrokehook;0284
+dotmath;22C5
+dottedcircle;25CC
+doubleyodpatah;FB1F
+doubleyodpatahhebrew;FB1F
+downtackbelowcmb;031E
+downtackmod;02D5
+dparen;249F
+dsuperior;F6EB
+dtail;0256
+dtopbar;018C
+duhiragana;3065
+dukatakana;30C5
+dz;01F3
+dzaltone;02A3
+dzcaron;01C6
+dzcurl;02A5
+dzeabkhasiancyrillic;04E1
+dzecyrillic;0455
+dzhecyrillic;045F
+e;0065
+eacute;00E9
+earth;2641
+ebengali;098F
+ebopomofo;311C
+ebreve;0115
+ecandradeva;090D
+ecandragujarati;0A8D
+ecandravowelsigndeva;0945
+ecandravowelsigngujarati;0AC5
+ecaron;011B
+ecedillabreve;1E1D
+echarmenian;0565
+echyiwnarmenian;0587
+ecircle;24D4
+ecircumflex;00EA
+ecircumflexacute;1EBF
+ecircumflexbelow;1E19
+ecircumflexdotbelow;1EC7
+ecircumflexgrave;1EC1
+ecircumflexhookabove;1EC3
+ecircumflextilde;1EC5
+ecyrillic;0454
+edblgrave;0205
+edeva;090F
+edieresis;00EB
+edot;0117
+edotaccent;0117
+edotbelow;1EB9
+eegurmukhi;0A0F
+eematragurmukhi;0A47
+efcyrillic;0444
+egrave;00E8
+egujarati;0A8F
+eharmenian;0567
+ehbopomofo;311D
+ehiragana;3048
+ehookabove;1EBB
+eibopomofo;311F
+eight;0038
+eightarabic;0668
+eightbengali;09EE
+eightcircle;2467
+eightcircleinversesansserif;2791
+eightdeva;096E
+eighteencircle;2471
+eighteenparen;2485
+eighteenperiod;2499
+eightgujarati;0AEE
+eightgurmukhi;0A6E
+eighthackarabic;0668
+eighthangzhou;3028
+eighthnotebeamed;266B
+eightideographicparen;3227
+eightinferior;2088
+eightmonospace;FF18
+eightoldstyle;F738
+eightparen;247B
+eightperiod;248F
+eightpersian;06F8
+eightroman;2177
+eightsuperior;2078
+eightthai;0E58
+einvertedbreve;0207
+eiotifiedcyrillic;0465
+ekatakana;30A8
+ekatakanahalfwidth;FF74
+ekonkargurmukhi;0A74
+ekorean;3154
+elcyrillic;043B
+element;2208
+elevencircle;246A
+elevenparen;247E
+elevenperiod;2492
+elevenroman;217A
+ellipsis;2026
+ellipsisvertical;22EE
+emacron;0113
+emacronacute;1E17
+emacrongrave;1E15
+emcyrillic;043C
+emdash;2014
+emdashvertical;FE31
+emonospace;FF45
+emphasismarkarmenian;055B
+emptyset;2205
+enbopomofo;3123
+encyrillic;043D
+endash;2013
+endashvertical;FE32
+endescendercyrillic;04A3
+eng;014B
+engbopomofo;3125
+enghecyrillic;04A5
+enhookcyrillic;04C8
+enspace;2002
+eogonek;0119
+eokorean;3153
+eopen;025B
+eopenclosed;029A
+eopenreversed;025C
+eopenreversedclosed;025E
+eopenreversedhook;025D
+eparen;24A0
+epsilon;03B5
+epsilontonos;03AD
+equal;003D
+equalmonospace;FF1D
+equalsmall;FE66
+equalsuperior;207C
+equivalence;2261
+erbopomofo;3126
+ercyrillic;0440
+ereversed;0258
+ereversedcyrillic;044D
+escyrillic;0441
+esdescendercyrillic;04AB
+esh;0283
+eshcurl;0286
+eshortdeva;090E
+eshortvowelsigndeva;0946
+eshreversedloop;01AA
+eshsquatreversed;0285
+esmallhiragana;3047
+esmallkatakana;30A7
+esmallkatakanahalfwidth;FF6A
+estimated;212E
+esuperior;F6EC
+eta;03B7
+etarmenian;0568
+etatonos;03AE
+eth;00F0
+etilde;1EBD
+etildebelow;1E1B
+etnahtafoukhhebrew;0591
+etnahtafoukhlefthebrew;0591
+etnahtahebrew;0591
+etnahtalefthebrew;0591
+eturned;01DD
+eukorean;3161
+euro;20AC
+evowelsignbengali;09C7
+evowelsigndeva;0947
+evowelsigngujarati;0AC7
+exclam;0021
+exclamarmenian;055C
+exclamdbl;203C
+exclamdown;00A1
+exclamdownsmall;F7A1
+exclammonospace;FF01
+exclamsmall;F721
+existential;2203
+ezh;0292
+ezhcaron;01EF
+ezhcurl;0293
+ezhreversed;01B9
+ezhtail;01BA
+f;0066
+fadeva;095E
+fagurmukhi;0A5E
+fahrenheit;2109
+fathaarabic;064E
+fathalowarabic;064E
+fathatanarabic;064B
+fbopomofo;3108
+fcircle;24D5
+fdotaccent;1E1F
+feharabic;0641
+feharmenian;0586
+fehfinalarabic;FED2
+fehinitialarabic;FED3
+fehmedialarabic;FED4
+feicoptic;03E5
+female;2640
+ff;FB00
+ffi;FB03
+ffl;FB04
+fi;FB01
+fifteencircle;246E
+fifteenparen;2482
+fifteenperiod;2496
+figuredash;2012
+filledbox;25A0
+filledrect;25AC
+finalkaf;05DA
+finalkafdagesh;FB3A
+finalkafdageshhebrew;FB3A
+finalkafhebrew;05DA
+finalkafqamats;05DA 05B8
+finalkafqamatshebrew;05DA 05B8
+finalkafsheva;05DA 05B0
+finalkafshevahebrew;05DA 05B0
+finalmem;05DD
+finalmemhebrew;05DD
+finalnun;05DF
+finalnunhebrew;05DF
+finalpe;05E3
+finalpehebrew;05E3
+finaltsadi;05E5
+finaltsadihebrew;05E5
+firsttonechinese;02C9
+fisheye;25C9
+fitacyrillic;0473
+five;0035
+fivearabic;0665
+fivebengali;09EB
+fivecircle;2464
+fivecircleinversesansserif;278E
+fivedeva;096B
+fiveeighths;215D
+fivegujarati;0AEB
+fivegurmukhi;0A6B
+fivehackarabic;0665
+fivehangzhou;3025
+fiveideographicparen;3224
+fiveinferior;2085
+fivemonospace;FF15
+fiveoldstyle;F735
+fiveparen;2478
+fiveperiod;248C
+fivepersian;06F5
+fiveroman;2174
+fivesuperior;2075
+fivethai;0E55
+fl;FB02
+florin;0192
+fmonospace;FF46
+fmsquare;3399
+fofanthai;0E1F
+fofathai;0E1D
+fongmanthai;0E4F
+forall;2200
+four;0034
+fourarabic;0664
+fourbengali;09EA
+fourcircle;2463
+fourcircleinversesansserif;278D
+fourdeva;096A
+fourgujarati;0AEA
+fourgurmukhi;0A6A
+fourhackarabic;0664
+fourhangzhou;3024
+fourideographicparen;3223
+fourinferior;2084
+fourmonospace;FF14
+fournumeratorbengali;09F7
+fouroldstyle;F734
+fourparen;2477
+fourperiod;248B
+fourpersian;06F4
+fourroman;2173
+foursuperior;2074
+fourteencircle;246D
+fourteenparen;2481
+fourteenperiod;2495
+fourthai;0E54
+fourthtonechinese;02CB
+fparen;24A1
+fraction;2044
+franc;20A3
+g;0067
+gabengali;0997
+gacute;01F5
+gadeva;0917
+gafarabic;06AF
+gaffinalarabic;FB93
+gafinitialarabic;FB94
+gafmedialarabic;FB95
+gagujarati;0A97
+gagurmukhi;0A17
+gahiragana;304C
+gakatakana;30AC
+gamma;03B3
+gammalatinsmall;0263
+gammasuperior;02E0
+gangiacoptic;03EB
+gbopomofo;310D
+gbreve;011F
+gcaron;01E7
+gcedilla;0123
+gcircle;24D6
+gcircumflex;011D
+gcommaaccent;0123
+gdot;0121
+gdotaccent;0121
+gecyrillic;0433
+gehiragana;3052
+gekatakana;30B2
+geometricallyequal;2251
+gereshaccenthebrew;059C
+gereshhebrew;05F3
+gereshmuqdamhebrew;059D
+germandbls;00DF
+gershayimaccenthebrew;059E
+gershayimhebrew;05F4
+getamark;3013
+ghabengali;0998
+ghadarmenian;0572
+ghadeva;0918
+ghagujarati;0A98
+ghagurmukhi;0A18
+ghainarabic;063A
+ghainfinalarabic;FECE
+ghaininitialarabic;FECF
+ghainmedialarabic;FED0
+ghemiddlehookcyrillic;0495
+ghestrokecyrillic;0493
+gheupturncyrillic;0491
+ghhadeva;095A
+ghhagurmukhi;0A5A
+ghook;0260
+ghzsquare;3393
+gihiragana;304E
+gikatakana;30AE
+gimarmenian;0563
+gimel;05D2
+gimeldagesh;FB32
+gimeldageshhebrew;FB32
+gimelhebrew;05D2
+gjecyrillic;0453
+glottalinvertedstroke;01BE
+glottalstop;0294
+glottalstopinverted;0296
+glottalstopmod;02C0
+glottalstopreversed;0295
+glottalstopreversedmod;02C1
+glottalstopreversedsuperior;02E4
+glottalstopstroke;02A1
+glottalstopstrokereversed;02A2
+gmacron;1E21
+gmonospace;FF47
+gohiragana;3054
+gokatakana;30B4
+gparen;24A2
+gpasquare;33AC
+gradient;2207
+grave;0060
+gravebelowcmb;0316
+gravecmb;0300
+gravecomb;0300
+gravedeva;0953
+gravelowmod;02CE
+gravemonospace;FF40
+gravetonecmb;0340
+greater;003E
+greaterequal;2265
+greaterequalorless;22DB
+greatermonospace;FF1E
+greaterorequivalent;2273
+greaterorless;2277
+greateroverequal;2267
+greatersmall;FE65
+gscript;0261
+gstroke;01E5
+guhiragana;3050
+guillemotleft;00AB
+guillemotright;00BB
+guilsinglleft;2039
+guilsinglright;203A
+gukatakana;30B0
+guramusquare;3318
+gysquare;33C9
+h;0068
+haabkhasiancyrillic;04A9
+haaltonearabic;06C1
+habengali;09B9
+hadescendercyrillic;04B3
+hadeva;0939
+hagujarati;0AB9
+hagurmukhi;0A39
+haharabic;062D
+hahfinalarabic;FEA2
+hahinitialarabic;FEA3
+hahiragana;306F
+hahmedialarabic;FEA4
+haitusquare;332A
+hakatakana;30CF
+hakatakanahalfwidth;FF8A
+halantgurmukhi;0A4D
+hamzaarabic;0621
+hamzadammaarabic;0621 064F
+hamzadammatanarabic;0621 064C
+hamzafathaarabic;0621 064E
+hamzafathatanarabic;0621 064B
+hamzalowarabic;0621
+hamzalowkasraarabic;0621 0650
+hamzalowkasratanarabic;0621 064D
+hamzasukunarabic;0621 0652
+hangulfiller;3164
+hardsigncyrillic;044A
+harpoonleftbarbup;21BC
+harpoonrightbarbup;21C0
+hasquare;33CA
+hatafpatah;05B2
+hatafpatah16;05B2
+hatafpatah23;05B2
+hatafpatah2f;05B2
+hatafpatahhebrew;05B2
+hatafpatahnarrowhebrew;05B2
+hatafpatahquarterhebrew;05B2
+hatafpatahwidehebrew;05B2
+hatafqamats;05B3
+hatafqamats1b;05B3
+hatafqamats28;05B3
+hatafqamats34;05B3
+hatafqamatshebrew;05B3
+hatafqamatsnarrowhebrew;05B3
+hatafqamatsquarterhebrew;05B3
+hatafqamatswidehebrew;05B3
+hatafsegol;05B1
+hatafsegol17;05B1
+hatafsegol24;05B1
+hatafsegol30;05B1
+hatafsegolhebrew;05B1
+hatafsegolnarrowhebrew;05B1
+hatafsegolquarterhebrew;05B1
+hatafsegolwidehebrew;05B1
+hbar;0127
+hbopomofo;310F
+hbrevebelow;1E2B
+hcedilla;1E29
+hcircle;24D7
+hcircumflex;0125
+hdieresis;1E27
+hdotaccent;1E23
+hdotbelow;1E25
+he;05D4
+heart;2665
+heartsuitblack;2665
+heartsuitwhite;2661
+hedagesh;FB34
+hedageshhebrew;FB34
+hehaltonearabic;06C1
+heharabic;0647
+hehebrew;05D4
+hehfinalaltonearabic;FBA7
+hehfinalalttwoarabic;FEEA
+hehfinalarabic;FEEA
+hehhamzaabovefinalarabic;FBA5
+hehhamzaaboveisolatedarabic;FBA4
+hehinitialaltonearabic;FBA8
+hehinitialarabic;FEEB
+hehiragana;3078
+hehmedialaltonearabic;FBA9
+hehmedialarabic;FEEC
+heiseierasquare;337B
+hekatakana;30D8
+hekatakanahalfwidth;FF8D
+hekutaarusquare;3336
+henghook;0267
+herutusquare;3339
+het;05D7
+hethebrew;05D7
+hhook;0266
+hhooksuperior;02B1
+hieuhacirclekorean;327B
+hieuhaparenkorean;321B
+hieuhcirclekorean;326D
+hieuhkorean;314E
+hieuhparenkorean;320D
+hihiragana;3072
+hikatakana;30D2
+hikatakanahalfwidth;FF8B
+hiriq;05B4
+hiriq14;05B4
+hiriq21;05B4
+hiriq2d;05B4
+hiriqhebrew;05B4
+hiriqnarrowhebrew;05B4
+hiriqquarterhebrew;05B4
+hiriqwidehebrew;05B4
+hlinebelow;1E96
+hmonospace;FF48
+hoarmenian;0570
+hohipthai;0E2B
+hohiragana;307B
+hokatakana;30DB
+hokatakanahalfwidth;FF8E
+holam;05B9
+holam19;05B9
+holam26;05B9
+holam32;05B9
+holamhebrew;05B9
+holamnarrowhebrew;05B9
+holamquarterhebrew;05B9
+holamwidehebrew;05B9
+honokhukthai;0E2E
+hookabovecomb;0309
+hookcmb;0309
+hookpalatalizedbelowcmb;0321
+hookretroflexbelowcmb;0322
+hoonsquare;3342
+horicoptic;03E9
+horizontalbar;2015
+horncmb;031B
+hotsprings;2668
+house;2302
+hparen;24A3
+hsuperior;02B0
+hturned;0265
+huhiragana;3075
+huiitosquare;3333
+hukatakana;30D5
+hukatakanahalfwidth;FF8C
+hungarumlaut;02DD
+hungarumlautcmb;030B
+hv;0195
+hyphen;002D
+hypheninferior;F6E5
+hyphenmonospace;FF0D
+hyphensmall;FE63
+hyphensuperior;F6E6
+hyphentwo;2010
+i;0069
+iacute;00ED
+iacyrillic;044F
+ibengali;0987
+ibopomofo;3127
+ibreve;012D
+icaron;01D0
+icircle;24D8
+icircumflex;00EE
+icyrillic;0456
+idblgrave;0209
+ideographearthcircle;328F
+ideographfirecircle;328B
+ideographicallianceparen;323F
+ideographiccallparen;323A
+ideographiccentrecircle;32A5
+ideographicclose;3006
+ideographiccomma;3001
+ideographiccommaleft;FF64
+ideographiccongratulationparen;3237
+ideographiccorrectcircle;32A3
+ideographicearthparen;322F
+ideographicenterpriseparen;323D
+ideographicexcellentcircle;329D
+ideographicfestivalparen;3240
+ideographicfinancialcircle;3296
+ideographicfinancialparen;3236
+ideographicfireparen;322B
+ideographichaveparen;3232
+ideographichighcircle;32A4
+ideographiciterationmark;3005
+ideographiclaborcircle;3298
+ideographiclaborparen;3238
+ideographicleftcircle;32A7
+ideographiclowcircle;32A6
+ideographicmedicinecircle;32A9
+ideographicmetalparen;322E
+ideographicmoonparen;322A
+ideographicnameparen;3234
+ideographicperiod;3002
+ideographicprintcircle;329E
+ideographicreachparen;3243
+ideographicrepresentparen;3239
+ideographicresourceparen;323E
+ideographicrightcircle;32A8
+ideographicsecretcircle;3299
+ideographicselfparen;3242
+ideographicsocietyparen;3233
+ideographicspace;3000
+ideographicspecialparen;3235
+ideographicstockparen;3231
+ideographicstudyparen;323B
+ideographicsunparen;3230
+ideographicsuperviseparen;323C
+ideographicwaterparen;322C
+ideographicwoodparen;322D
+ideographiczero;3007
+ideographmetalcircle;328E
+ideographmooncircle;328A
+ideographnamecircle;3294
+ideographsuncircle;3290
+ideographwatercircle;328C
+ideographwoodcircle;328D
+ideva;0907
+idieresis;00EF
+idieresisacute;1E2F
+idieresiscyrillic;04E5
+idotbelow;1ECB
+iebrevecyrillic;04D7
+iecyrillic;0435
+ieungacirclekorean;3275
+ieungaparenkorean;3215
+ieungcirclekorean;3267
+ieungkorean;3147
+ieungparenkorean;3207
+igrave;00EC
+igujarati;0A87
+igurmukhi;0A07
+ihiragana;3044
+ihookabove;1EC9
+iibengali;0988
+iicyrillic;0438
+iideva;0908
+iigujarati;0A88
+iigurmukhi;0A08
+iimatragurmukhi;0A40
+iinvertedbreve;020B
+iishortcyrillic;0439
+iivowelsignbengali;09C0
+iivowelsigndeva;0940
+iivowelsigngujarati;0AC0
+ij;0133
+ikatakana;30A4
+ikatakanahalfwidth;FF72
+ikorean;3163
+ilde;02DC
+iluyhebrew;05AC
+imacron;012B
+imacroncyrillic;04E3
+imageorapproximatelyequal;2253
+imatragurmukhi;0A3F
+imonospace;FF49
+increment;2206
+infinity;221E
+iniarmenian;056B
+integral;222B
+integralbottom;2321
+integralbt;2321
+integralex;F8F5
+integraltop;2320
+integraltp;2320
+intersection;2229
+intisquare;3305
+invbullet;25D8
+invcircle;25D9
+invsmileface;263B
+iocyrillic;0451
+iogonek;012F
+iota;03B9
+iotadieresis;03CA
+iotadieresistonos;0390
+iotalatin;0269
+iotatonos;03AF
+iparen;24A4
+irigurmukhi;0A72
+ismallhiragana;3043
+ismallkatakana;30A3
+ismallkatakanahalfwidth;FF68
+issharbengali;09FA
+istroke;0268
+isuperior;F6ED
+iterationhiragana;309D
+iterationkatakana;30FD
+itilde;0129
+itildebelow;1E2D
+iubopomofo;3129
+iucyrillic;044E
+ivowelsignbengali;09BF
+ivowelsigndeva;093F
+ivowelsigngujarati;0ABF
+izhitsacyrillic;0475
+izhitsadblgravecyrillic;0477
+j;006A
+jaarmenian;0571
+jabengali;099C
+jadeva;091C
+jagujarati;0A9C
+jagurmukhi;0A1C
+jbopomofo;3110
+jcaron;01F0
+jcircle;24D9
+jcircumflex;0135
+jcrossedtail;029D
+jdotlessstroke;025F
+jecyrillic;0458
+jeemarabic;062C
+jeemfinalarabic;FE9E
+jeeminitialarabic;FE9F
+jeemmedialarabic;FEA0
+jeharabic;0698
+jehfinalarabic;FB8B
+jhabengali;099D
+jhadeva;091D
+jhagujarati;0A9D
+jhagurmukhi;0A1D
+jheharmenian;057B
+jis;3004
+jmonospace;FF4A
+jparen;24A5
+jsuperior;02B2
+k;006B
+kabashkircyrillic;04A1
+kabengali;0995
+kacute;1E31
+kacyrillic;043A
+kadescendercyrillic;049B
+kadeva;0915
+kaf;05DB
+kafarabic;0643
+kafdagesh;FB3B
+kafdageshhebrew;FB3B
+kaffinalarabic;FEDA
+kafhebrew;05DB
+kafinitialarabic;FEDB
+kafmedialarabic;FEDC
+kafrafehebrew;FB4D
+kagujarati;0A95
+kagurmukhi;0A15
+kahiragana;304B
+kahookcyrillic;04C4
+kakatakana;30AB
+kakatakanahalfwidth;FF76
+kappa;03BA
+kappasymbolgreek;03F0
+kapyeounmieumkorean;3171
+kapyeounphieuphkorean;3184
+kapyeounpieupkorean;3178
+kapyeounssangpieupkorean;3179
+karoriisquare;330D
+kashidaautoarabic;0640
+kashidaautonosidebearingarabic;0640
+kasmallkatakana;30F5
+kasquare;3384
+kasraarabic;0650
+kasratanarabic;064D
+kastrokecyrillic;049F
+katahiraprolongmarkhalfwidth;FF70
+kaverticalstrokecyrillic;049D
+kbopomofo;310E
+kcalsquare;3389
+kcaron;01E9
+kcedilla;0137
+kcircle;24DA
+kcommaaccent;0137
+kdotbelow;1E33
+keharmenian;0584
+kehiragana;3051
+kekatakana;30B1
+kekatakanahalfwidth;FF79
+kenarmenian;056F
+kesmallkatakana;30F6
+kgreenlandic;0138
+khabengali;0996
+khacyrillic;0445
+khadeva;0916
+khagujarati;0A96
+khagurmukhi;0A16
+khaharabic;062E
+khahfinalarabic;FEA6
+khahinitialarabic;FEA7
+khahmedialarabic;FEA8
+kheicoptic;03E7
+khhadeva;0959
+khhagurmukhi;0A59
+khieukhacirclekorean;3278
+khieukhaparenkorean;3218
+khieukhcirclekorean;326A
+khieukhkorean;314B
+khieukhparenkorean;320A
+khokhaithai;0E02
+khokhonthai;0E05
+khokhuatthai;0E03
+khokhwaithai;0E04
+khomutthai;0E5B
+khook;0199
+khorakhangthai;0E06
+khzsquare;3391
+kihiragana;304D
+kikatakana;30AD
+kikatakanahalfwidth;FF77
+kiroguramusquare;3315
+kiromeetorusquare;3316
+kirosquare;3314
+kiyeokacirclekorean;326E
+kiyeokaparenkorean;320E
+kiyeokcirclekorean;3260
+kiyeokkorean;3131
+kiyeokparenkorean;3200
+kiyeoksioskorean;3133
+kjecyrillic;045C
+klinebelow;1E35
+klsquare;3398
+kmcubedsquare;33A6
+kmonospace;FF4B
+kmsquaredsquare;33A2
+kohiragana;3053
+kohmsquare;33C0
+kokaithai;0E01
+kokatakana;30B3
+kokatakanahalfwidth;FF7A
+kooposquare;331E
+koppacyrillic;0481
+koreanstandardsymbol;327F
+koroniscmb;0343
+kparen;24A6
+kpasquare;33AA
+ksicyrillic;046F
+ktsquare;33CF
+kturned;029E
+kuhiragana;304F
+kukatakana;30AF
+kukatakanahalfwidth;FF78
+kvsquare;33B8
+kwsquare;33BE
+l;006C
+labengali;09B2
+lacute;013A
+ladeva;0932
+lagujarati;0AB2
+lagurmukhi;0A32
+lakkhangyaothai;0E45
+lamaleffinalarabic;FEFC
+lamalefhamzaabovefinalarabic;FEF8
+lamalefhamzaaboveisolatedarabic;FEF7
+lamalefhamzabelowfinalarabic;FEFA
+lamalefhamzabelowisolatedarabic;FEF9
+lamalefisolatedarabic;FEFB
+lamalefmaddaabovefinalarabic;FEF6
+lamalefmaddaaboveisolatedarabic;FEF5
+lamarabic;0644
+lambda;03BB
+lambdastroke;019B
+lamed;05DC
+lameddagesh;FB3C
+lameddageshhebrew;FB3C
+lamedhebrew;05DC
+lamedholam;05DC 05B9
+lamedholamdagesh;05DC 05B9 05BC
+lamedholamdageshhebrew;05DC 05B9 05BC
+lamedholamhebrew;05DC 05B9
+lamfinalarabic;FEDE
+lamhahinitialarabic;FCCA
+laminitialarabic;FEDF
+lamjeeminitialarabic;FCC9
+lamkhahinitialarabic;FCCB
+lamlamhehisolatedarabic;FDF2
+lammedialarabic;FEE0
+lammeemhahinitialarabic;FD88
+lammeeminitialarabic;FCCC
+lammeemjeeminitialarabic;FEDF FEE4 FEA0
+lammeemkhahinitialarabic;FEDF FEE4 FEA8
+largecircle;25EF
+lbar;019A
+lbelt;026C
+lbopomofo;310C
+lcaron;013E
+lcedilla;013C
+lcircle;24DB
+lcircumflexbelow;1E3D
+lcommaaccent;013C
+ldot;0140
+ldotaccent;0140
+ldotbelow;1E37
+ldotbelowmacron;1E39
+leftangleabovecmb;031A
+lefttackbelowcmb;0318
+less;003C
+lessequal;2264
+lessequalorgreater;22DA
+lessmonospace;FF1C
+lessorequivalent;2272
+lessorgreater;2276
+lessoverequal;2266
+lesssmall;FE64
+lezh;026E
+lfblock;258C
+lhookretroflex;026D
+lira;20A4
+liwnarmenian;056C
+lj;01C9
+ljecyrillic;0459
+ll;F6C0
+lladeva;0933
+llagujarati;0AB3
+llinebelow;1E3B
+llladeva;0934
+llvocalicbengali;09E1
+llvocalicdeva;0961
+llvocalicvowelsignbengali;09E3
+llvocalicvowelsigndeva;0963
+lmiddletilde;026B
+lmonospace;FF4C
+lmsquare;33D0
+lochulathai;0E2C
+logicaland;2227
+logicalnot;00AC
+logicalnotreversed;2310
+logicalor;2228
+lolingthai;0E25
+longs;017F
+lowlinecenterline;FE4E
+lowlinecmb;0332
+lowlinedashed;FE4D
+lozenge;25CA
+lparen;24A7
+lslash;0142
+lsquare;2113
+lsuperior;F6EE
+ltshade;2591
+luthai;0E26
+lvocalicbengali;098C
+lvocalicdeva;090C
+lvocalicvowelsignbengali;09E2
+lvocalicvowelsigndeva;0962
+lxsquare;33D3
+m;006D
+mabengali;09AE
+macron;00AF
+macronbelowcmb;0331
+macroncmb;0304
+macronlowmod;02CD
+macronmonospace;FFE3
+macute;1E3F
+madeva;092E
+magujarati;0AAE
+magurmukhi;0A2E
+mahapakhhebrew;05A4
+mahapakhlefthebrew;05A4
+mahiragana;307E
+maichattawalowleftthai;F895
+maichattawalowrightthai;F894
+maichattawathai;0E4B
+maichattawaupperleftthai;F893
+maieklowleftthai;F88C
+maieklowrightthai;F88B
+maiekthai;0E48
+maiekupperleftthai;F88A
+maihanakatleftthai;F884
+maihanakatthai;0E31
+maitaikhuleftthai;F889
+maitaikhuthai;0E47
+maitholowleftthai;F88F
+maitholowrightthai;F88E
+maithothai;0E49
+maithoupperleftthai;F88D
+maitrilowleftthai;F892
+maitrilowrightthai;F891
+maitrithai;0E4A
+maitriupperleftthai;F890
+maiyamokthai;0E46
+makatakana;30DE
+makatakanahalfwidth;FF8F
+male;2642
+mansyonsquare;3347
+maqafhebrew;05BE
+mars;2642
+masoracirclehebrew;05AF
+masquare;3383
+mbopomofo;3107
+mbsquare;33D4
+mcircle;24DC
+mcubedsquare;33A5
+mdotaccent;1E41
+mdotbelow;1E43
+meemarabic;0645
+meemfinalarabic;FEE2
+meeminitialarabic;FEE3
+meemmedialarabic;FEE4
+meemmeeminitialarabic;FCD1
+meemmeemisolatedarabic;FC48
+meetorusquare;334D
+mehiragana;3081
+meizierasquare;337E
+mekatakana;30E1
+mekatakanahalfwidth;FF92
+mem;05DE
+memdagesh;FB3E
+memdageshhebrew;FB3E
+memhebrew;05DE
+menarmenian;0574
+merkhahebrew;05A5
+merkhakefulahebrew;05A6
+merkhakefulalefthebrew;05A6
+merkhalefthebrew;05A5
+mhook;0271
+mhzsquare;3392
+middledotkatakanahalfwidth;FF65
+middot;00B7
+mieumacirclekorean;3272
+mieumaparenkorean;3212
+mieumcirclekorean;3264
+mieumkorean;3141
+mieumpansioskorean;3170
+mieumparenkorean;3204
+mieumpieupkorean;316E
+mieumsioskorean;316F
+mihiragana;307F
+mikatakana;30DF
+mikatakanahalfwidth;FF90
+minus;2212
+minusbelowcmb;0320
+minuscircle;2296
+minusmod;02D7
+minusplus;2213
+minute;2032
+miribaarusquare;334A
+mirisquare;3349
+mlonglegturned;0270
+mlsquare;3396
+mmcubedsquare;33A3
+mmonospace;FF4D
+mmsquaredsquare;339F
+mohiragana;3082
+mohmsquare;33C1
+mokatakana;30E2
+mokatakanahalfwidth;FF93
+molsquare;33D6
+momathai;0E21
+moverssquare;33A7
+moverssquaredsquare;33A8
+mparen;24A8
+mpasquare;33AB
+mssquare;33B3
+msuperior;F6EF
+mturned;026F
+mu;00B5
+mu1;00B5
+muasquare;3382
+muchgreater;226B
+muchless;226A
+mufsquare;338C
+mugreek;03BC
+mugsquare;338D
+muhiragana;3080
+mukatakana;30E0
+mukatakanahalfwidth;FF91
+mulsquare;3395
+multiply;00D7
+mumsquare;339B
+munahhebrew;05A3
+munahlefthebrew;05A3
+musicalnote;266A
+musicalnotedbl;266B
+musicflatsign;266D
+musicsharpsign;266F
+mussquare;33B2
+muvsquare;33B6
+muwsquare;33BC
+mvmegasquare;33B9
+mvsquare;33B7
+mwmegasquare;33BF
+mwsquare;33BD
+n;006E
+nabengali;09A8
+nabla;2207
+nacute;0144
+nadeva;0928
+nagujarati;0AA8
+nagurmukhi;0A28
+nahiragana;306A
+nakatakana;30CA
+nakatakanahalfwidth;FF85
+napostrophe;0149
+nasquare;3381
+nbopomofo;310B
+nbspace;00A0
+ncaron;0148
+ncedilla;0146
+ncircle;24DD
+ncircumflexbelow;1E4B
+ncommaaccent;0146
+ndotaccent;1E45
+ndotbelow;1E47
+nehiragana;306D
+nekatakana;30CD
+nekatakanahalfwidth;FF88
+newsheqelsign;20AA
+nfsquare;338B
+ngabengali;0999
+ngadeva;0919
+ngagujarati;0A99
+ngagurmukhi;0A19
+ngonguthai;0E07
+nhiragana;3093
+nhookleft;0272
+nhookretroflex;0273
+nieunacirclekorean;326F
+nieunaparenkorean;320F
+nieuncieuckorean;3135
+nieuncirclekorean;3261
+nieunhieuhkorean;3136
+nieunkorean;3134
+nieunpansioskorean;3168
+nieunparenkorean;3201
+nieunsioskorean;3167
+nieuntikeutkorean;3166
+nihiragana;306B
+nikatakana;30CB
+nikatakanahalfwidth;FF86
+nikhahitleftthai;F899
+nikhahitthai;0E4D
+nine;0039
+ninearabic;0669
+ninebengali;09EF
+ninecircle;2468
+ninecircleinversesansserif;2792
+ninedeva;096F
+ninegujarati;0AEF
+ninegurmukhi;0A6F
+ninehackarabic;0669
+ninehangzhou;3029
+nineideographicparen;3228
+nineinferior;2089
+ninemonospace;FF19
+nineoldstyle;F739
+nineparen;247C
+nineperiod;2490
+ninepersian;06F9
+nineroman;2178
+ninesuperior;2079
+nineteencircle;2472
+nineteenparen;2486
+nineteenperiod;249A
+ninethai;0E59
+nj;01CC
+njecyrillic;045A
+nkatakana;30F3
+nkatakanahalfwidth;FF9D
+nlegrightlong;019E
+nlinebelow;1E49
+nmonospace;FF4E
+nmsquare;339A
+nnabengali;09A3
+nnadeva;0923
+nnagujarati;0AA3
+nnagurmukhi;0A23
+nnnadeva;0929
+nohiragana;306E
+nokatakana;30CE
+nokatakanahalfwidth;FF89
+nonbreakingspace;00A0
+nonenthai;0E13
+nonuthai;0E19
+noonarabic;0646
+noonfinalarabic;FEE6
+noonghunnaarabic;06BA
+noonghunnafinalarabic;FB9F
+noonhehinitialarabic;FEE7 FEEC
+nooninitialarabic;FEE7
+noonjeeminitialarabic;FCD2
+noonjeemisolatedarabic;FC4B
+noonmedialarabic;FEE8
+noonmeeminitialarabic;FCD5
+noonmeemisolatedarabic;FC4E
+noonnoonfinalarabic;FC8D
+notcontains;220C
+notelement;2209
+notelementof;2209
+notequal;2260
+notgreater;226F
+notgreaternorequal;2271
+notgreaternorless;2279
+notidentical;2262
+notless;226E
+notlessnorequal;2270
+notparallel;2226
+notprecedes;2280
+notsubset;2284
+notsucceeds;2281
+notsuperset;2285
+nowarmenian;0576
+nparen;24A9
+nssquare;33B1
+nsuperior;207F
+ntilde;00F1
+nu;03BD
+nuhiragana;306C
+nukatakana;30CC
+nukatakanahalfwidth;FF87
+nuktabengali;09BC
+nuktadeva;093C
+nuktagujarati;0ABC
+nuktagurmukhi;0A3C
+numbersign;0023
+numbersignmonospace;FF03
+numbersignsmall;FE5F
+numeralsigngreek;0374
+numeralsignlowergreek;0375
+numero;2116
+nun;05E0
+nundagesh;FB40
+nundageshhebrew;FB40
+nunhebrew;05E0
+nvsquare;33B5
+nwsquare;33BB
+nyabengali;099E
+nyadeva;091E
+nyagujarati;0A9E
+nyagurmukhi;0A1E
+o;006F
+oacute;00F3
+oangthai;0E2D
+obarred;0275
+obarredcyrillic;04E9
+obarreddieresiscyrillic;04EB
+obengali;0993
+obopomofo;311B
+obreve;014F
+ocandradeva;0911
+ocandragujarati;0A91
+ocandravowelsigndeva;0949
+ocandravowelsigngujarati;0AC9
+ocaron;01D2
+ocircle;24DE
+ocircumflex;00F4
+ocircumflexacute;1ED1
+ocircumflexdotbelow;1ED9
+ocircumflexgrave;1ED3
+ocircumflexhookabove;1ED5
+ocircumflextilde;1ED7
+ocyrillic;043E
+odblacute;0151
+odblgrave;020D
+odeva;0913
+odieresis;00F6
+odieresiscyrillic;04E7
+odotbelow;1ECD
+oe;0153
+oekorean;315A
+ogonek;02DB
+ogonekcmb;0328
+ograve;00F2
+ogujarati;0A93
+oharmenian;0585
+ohiragana;304A
+ohookabove;1ECF
+ohorn;01A1
+ohornacute;1EDB
+ohorndotbelow;1EE3
+ohorngrave;1EDD
+ohornhookabove;1EDF
+ohorntilde;1EE1
+ohungarumlaut;0151
+oi;01A3
+oinvertedbreve;020F
+okatakana;30AA
+okatakanahalfwidth;FF75
+okorean;3157
+olehebrew;05AB
+omacron;014D
+omacronacute;1E53
+omacrongrave;1E51
+omdeva;0950
+omega;03C9
+omega1;03D6
+omegacyrillic;0461
+omegalatinclosed;0277
+omegaroundcyrillic;047B
+omegatitlocyrillic;047D
+omegatonos;03CE
+omgujarati;0AD0
+omicron;03BF
+omicrontonos;03CC
+omonospace;FF4F
+one;0031
+onearabic;0661
+onebengali;09E7
+onecircle;2460
+onecircleinversesansserif;278A
+onedeva;0967
+onedotenleader;2024
+oneeighth;215B
+onefitted;F6DC
+onegujarati;0AE7
+onegurmukhi;0A67
+onehackarabic;0661
+onehalf;00BD
+onehangzhou;3021
+oneideographicparen;3220
+oneinferior;2081
+onemonospace;FF11
+onenumeratorbengali;09F4
+oneoldstyle;F731
+oneparen;2474
+oneperiod;2488
+onepersian;06F1
+onequarter;00BC
+oneroman;2170
+onesuperior;00B9
+onethai;0E51
+onethird;2153
+oogonek;01EB
+oogonekmacron;01ED
+oogurmukhi;0A13
+oomatragurmukhi;0A4B
+oopen;0254
+oparen;24AA
+openbullet;25E6
+option;2325
+ordfeminine;00AA
+ordmasculine;00BA
+orthogonal;221F
+oshortdeva;0912
+oshortvowelsigndeva;094A
+oslash;00F8
+oslashacute;01FF
+osmallhiragana;3049
+osmallkatakana;30A9
+osmallkatakanahalfwidth;FF6B
+ostrokeacute;01FF
+osuperior;F6F0
+otcyrillic;047F
+otilde;00F5
+otildeacute;1E4D
+otildedieresis;1E4F
+oubopomofo;3121
+overline;203E
+overlinecenterline;FE4A
+overlinecmb;0305
+overlinedashed;FE49
+overlinedblwavy;FE4C
+overlinewavy;FE4B
+overscore;00AF
+ovowelsignbengali;09CB
+ovowelsigndeva;094B
+ovowelsigngujarati;0ACB
+p;0070
+paampssquare;3380
+paasentosquare;332B
+pabengali;09AA
+pacute;1E55
+padeva;092A
+pagedown;21DF
+pageup;21DE
+pagujarati;0AAA
+pagurmukhi;0A2A
+pahiragana;3071
+paiyannoithai;0E2F
+pakatakana;30D1
+palatalizationcyrilliccmb;0484
+palochkacyrillic;04C0
+pansioskorean;317F
+paragraph;00B6
+parallel;2225
+parenleft;0028
+parenleftaltonearabic;FD3E
+parenleftbt;F8ED
+parenleftex;F8EC
+parenleftinferior;208D
+parenleftmonospace;FF08
+parenleftsmall;FE59
+parenleftsuperior;207D
+parenlefttp;F8EB
+parenleftvertical;FE35
+parenright;0029
+parenrightaltonearabic;FD3F
+parenrightbt;F8F8
+parenrightex;F8F7
+parenrightinferior;208E
+parenrightmonospace;FF09
+parenrightsmall;FE5A
+parenrightsuperior;207E
+parenrighttp;F8F6
+parenrightvertical;FE36
+partialdiff;2202
+paseqhebrew;05C0
+pashtahebrew;0599
+pasquare;33A9
+patah;05B7
+patah11;05B7
+patah1d;05B7
+patah2a;05B7
+patahhebrew;05B7
+patahnarrowhebrew;05B7
+patahquarterhebrew;05B7
+patahwidehebrew;05B7
+pazerhebrew;05A1
+pbopomofo;3106
+pcircle;24DF
+pdotaccent;1E57
+pe;05E4
+pecyrillic;043F
+pedagesh;FB44
+pedageshhebrew;FB44
+peezisquare;333B
+pefinaldageshhebrew;FB43
+peharabic;067E
+peharmenian;057A
+pehebrew;05E4
+pehfinalarabic;FB57
+pehinitialarabic;FB58
+pehiragana;307A
+pehmedialarabic;FB59
+pekatakana;30DA
+pemiddlehookcyrillic;04A7
+perafehebrew;FB4E
+percent;0025
+percentarabic;066A
+percentmonospace;FF05
+percentsmall;FE6A
+period;002E
+periodarmenian;0589
+periodcentered;00B7
+periodhalfwidth;FF61
+periodinferior;F6E7
+periodmonospace;FF0E
+periodsmall;FE52
+periodsuperior;F6E8
+perispomenigreekcmb;0342
+perpendicular;22A5
+perthousand;2030
+peseta;20A7
+pfsquare;338A
+phabengali;09AB
+phadeva;092B
+phagujarati;0AAB
+phagurmukhi;0A2B
+phi;03C6
+phi1;03D5
+phieuphacirclekorean;327A
+phieuphaparenkorean;321A
+phieuphcirclekorean;326C
+phieuphkorean;314D
+phieuphparenkorean;320C
+philatin;0278
+phinthuthai;0E3A
+phisymbolgreek;03D5
+phook;01A5
+phophanthai;0E1E
+phophungthai;0E1C
+phosamphaothai;0E20
+pi;03C0
+pieupacirclekorean;3273
+pieupaparenkorean;3213
+pieupcieuckorean;3176
+pieupcirclekorean;3265
+pieupkiyeokkorean;3172
+pieupkorean;3142
+pieupparenkorean;3205
+pieupsioskiyeokkorean;3174
+pieupsioskorean;3144
+pieupsiostikeutkorean;3175
+pieupthieuthkorean;3177
+pieuptikeutkorean;3173
+pihiragana;3074
+pikatakana;30D4
+pisymbolgreek;03D6
+piwrarmenian;0583
+plus;002B
+plusbelowcmb;031F
+pluscircle;2295
+plusminus;00B1
+plusmod;02D6
+plusmonospace;FF0B
+plussmall;FE62
+plussuperior;207A
+pmonospace;FF50
+pmsquare;33D8
+pohiragana;307D
+pointingindexdownwhite;261F
+pointingindexleftwhite;261C
+pointingindexrightwhite;261E
+pointingindexupwhite;261D
+pokatakana;30DD
+poplathai;0E1B
+postalmark;3012
+postalmarkface;3020
+pparen;24AB
+precedes;227A
+prescription;211E
+primemod;02B9
+primereversed;2035
+product;220F
+projective;2305
+prolongedkana;30FC
+propellor;2318
+propersubset;2282
+propersuperset;2283
+proportion;2237
+proportional;221D
+psi;03C8
+psicyrillic;0471
+psilipneumatacyrilliccmb;0486
+pssquare;33B0
+puhiragana;3077
+pukatakana;30D7
+pvsquare;33B4
+pwsquare;33BA
+q;0071
+qadeva;0958
+qadmahebrew;05A8
+qafarabic;0642
+qaffinalarabic;FED6
+qafinitialarabic;FED7
+qafmedialarabic;FED8
+qamats;05B8
+qamats10;05B8
+qamats1a;05B8
+qamats1c;05B8
+qamats27;05B8
+qamats29;05B8
+qamats33;05B8
+qamatsde;05B8
+qamatshebrew;05B8
+qamatsnarrowhebrew;05B8
+qamatsqatanhebrew;05B8
+qamatsqatannarrowhebrew;05B8
+qamatsqatanquarterhebrew;05B8
+qamatsqatanwidehebrew;05B8
+qamatsquarterhebrew;05B8
+qamatswidehebrew;05B8
+qarneyparahebrew;059F
+qbopomofo;3111
+qcircle;24E0
+qhook;02A0
+qmonospace;FF51
+qof;05E7
+qofdagesh;FB47
+qofdageshhebrew;FB47
+qofhatafpatah;05E7 05B2
+qofhatafpatahhebrew;05E7 05B2
+qofhatafsegol;05E7 05B1
+qofhatafsegolhebrew;05E7 05B1
+qofhebrew;05E7
+qofhiriq;05E7 05B4
+qofhiriqhebrew;05E7 05B4
+qofholam;05E7 05B9
+qofholamhebrew;05E7 05B9
+qofpatah;05E7 05B7
+qofpatahhebrew;05E7 05B7
+qofqamats;05E7 05B8
+qofqamatshebrew;05E7 05B8
+qofqubuts;05E7 05BB
+qofqubutshebrew;05E7 05BB
+qofsegol;05E7 05B6
+qofsegolhebrew;05E7 05B6
+qofsheva;05E7 05B0
+qofshevahebrew;05E7 05B0
+qoftsere;05E7 05B5
+qoftserehebrew;05E7 05B5
+qparen;24AC
+quarternote;2669
+qubuts;05BB
+qubuts18;05BB
+qubuts25;05BB
+qubuts31;05BB
+qubutshebrew;05BB
+qubutsnarrowhebrew;05BB
+qubutsquarterhebrew;05BB
+qubutswidehebrew;05BB
+question;003F
+questionarabic;061F
+questionarmenian;055E
+questiondown;00BF
+questiondownsmall;F7BF
+questiongreek;037E
+questionmonospace;FF1F
+questionsmall;F73F
+quotedbl;0022
+quotedblbase;201E
+quotedblleft;201C
+quotedblmonospace;FF02
+quotedblprime;301E
+quotedblprimereversed;301D
+quotedblright;201D
+quoteleft;2018
+quoteleftreversed;201B
+quotereversed;201B
+quoteright;2019
+quoterightn;0149
+quotesinglbase;201A
+quotesingle;0027
+quotesinglemonospace;FF07
+r;0072
+raarmenian;057C
+rabengali;09B0
+racute;0155
+radeva;0930
+radical;221A
+radicalex;F8E5
+radoverssquare;33AE
+radoverssquaredsquare;33AF
+radsquare;33AD
+rafe;05BF
+rafehebrew;05BF
+ragujarati;0AB0
+ragurmukhi;0A30
+rahiragana;3089
+rakatakana;30E9
+rakatakanahalfwidth;FF97
+ralowerdiagonalbengali;09F1
+ramiddlediagonalbengali;09F0
+ramshorn;0264
+ratio;2236
+rbopomofo;3116
+rcaron;0159
+rcedilla;0157
+rcircle;24E1
+rcommaaccent;0157
+rdblgrave;0211
+rdotaccent;1E59
+rdotbelow;1E5B
+rdotbelowmacron;1E5D
+referencemark;203B
+reflexsubset;2286
+reflexsuperset;2287
+registered;00AE
+registersans;F8E8
+registerserif;F6DA
+reharabic;0631
+reharmenian;0580
+rehfinalarabic;FEAE
+rehiragana;308C
+rehyehaleflamarabic;0631 FEF3 FE8E 0644
+rekatakana;30EC
+rekatakanahalfwidth;FF9A
+resh;05E8
+reshdageshhebrew;FB48
+reshhatafpatah;05E8 05B2
+reshhatafpatahhebrew;05E8 05B2
+reshhatafsegol;05E8 05B1
+reshhatafsegolhebrew;05E8 05B1
+reshhebrew;05E8
+reshhiriq;05E8 05B4
+reshhiriqhebrew;05E8 05B4
+reshholam;05E8 05B9
+reshholamhebrew;05E8 05B9
+reshpatah;05E8 05B7
+reshpatahhebrew;05E8 05B7
+reshqamats;05E8 05B8
+reshqamatshebrew;05E8 05B8
+reshqubuts;05E8 05BB
+reshqubutshebrew;05E8 05BB
+reshsegol;05E8 05B6
+reshsegolhebrew;05E8 05B6
+reshsheva;05E8 05B0
+reshshevahebrew;05E8 05B0
+reshtsere;05E8 05B5
+reshtserehebrew;05E8 05B5
+reversedtilde;223D
+reviahebrew;0597
+reviamugrashhebrew;0597
+revlogicalnot;2310
+rfishhook;027E
+rfishhookreversed;027F
+rhabengali;09DD
+rhadeva;095D
+rho;03C1
+rhook;027D
+rhookturned;027B
+rhookturnedsuperior;02B5
+rhosymbolgreek;03F1
+rhotichookmod;02DE
+rieulacirclekorean;3271
+rieulaparenkorean;3211
+rieulcirclekorean;3263
+rieulhieuhkorean;3140
+rieulkiyeokkorean;313A
+rieulkiyeoksioskorean;3169
+rieulkorean;3139
+rieulmieumkorean;313B
+rieulpansioskorean;316C
+rieulparenkorean;3203
+rieulphieuphkorean;313F
+rieulpieupkorean;313C
+rieulpieupsioskorean;316B
+rieulsioskorean;313D
+rieulthieuthkorean;313E
+rieultikeutkorean;316A
+rieulyeorinhieuhkorean;316D
+rightangle;221F
+righttackbelowcmb;0319
+righttriangle;22BF
+rihiragana;308A
+rikatakana;30EA
+rikatakanahalfwidth;FF98
+ring;02DA
+ringbelowcmb;0325
+ringcmb;030A
+ringhalfleft;02BF
+ringhalfleftarmenian;0559
+ringhalfleftbelowcmb;031C
+ringhalfleftcentered;02D3
+ringhalfright;02BE
+ringhalfrightbelowcmb;0339
+ringhalfrightcentered;02D2
+rinvertedbreve;0213
+rittorusquare;3351
+rlinebelow;1E5F
+rlongleg;027C
+rlonglegturned;027A
+rmonospace;FF52
+rohiragana;308D
+rokatakana;30ED
+rokatakanahalfwidth;FF9B
+roruathai;0E23
+rparen;24AD
+rrabengali;09DC
+rradeva;0931
+rragurmukhi;0A5C
+rreharabic;0691
+rrehfinalarabic;FB8D
+rrvocalicbengali;09E0
+rrvocalicdeva;0960
+rrvocalicgujarati;0AE0
+rrvocalicvowelsignbengali;09C4
+rrvocalicvowelsigndeva;0944
+rrvocalicvowelsigngujarati;0AC4
+rsuperior;F6F1
+rtblock;2590
+rturned;0279
+rturnedsuperior;02B4
+ruhiragana;308B
+rukatakana;30EB
+rukatakanahalfwidth;FF99
+rupeemarkbengali;09F2
+rupeesignbengali;09F3
+rupiah;F6DD
+ruthai;0E24
+rvocalicbengali;098B
+rvocalicdeva;090B
+rvocalicgujarati;0A8B
+rvocalicvowelsignbengali;09C3
+rvocalicvowelsigndeva;0943
+rvocalicvowelsigngujarati;0AC3
+s;0073
+sabengali;09B8
+sacute;015B
+sacutedotaccent;1E65
+sadarabic;0635
+sadeva;0938
+sadfinalarabic;FEBA
+sadinitialarabic;FEBB
+sadmedialarabic;FEBC
+sagujarati;0AB8
+sagurmukhi;0A38
+sahiragana;3055
+sakatakana;30B5
+sakatakanahalfwidth;FF7B
+sallallahoualayhewasallamarabic;FDFA
+samekh;05E1
+samekhdagesh;FB41
+samekhdageshhebrew;FB41
+samekhhebrew;05E1
+saraaathai;0E32
+saraaethai;0E41
+saraaimaimalaithai;0E44
+saraaimaimuanthai;0E43
+saraamthai;0E33
+saraathai;0E30
+saraethai;0E40
+saraiileftthai;F886
+saraiithai;0E35
+saraileftthai;F885
+saraithai;0E34
+saraothai;0E42
+saraueeleftthai;F888
+saraueethai;0E37
+saraueleftthai;F887
+sarauethai;0E36
+sarauthai;0E38
+sarauuthai;0E39
+sbopomofo;3119
+scaron;0161
+scarondotaccent;1E67
+scedilla;015F
+schwa;0259
+schwacyrillic;04D9
+schwadieresiscyrillic;04DB
+schwahook;025A
+scircle;24E2
+scircumflex;015D
+scommaaccent;0219
+sdotaccent;1E61
+sdotbelow;1E63
+sdotbelowdotaccent;1E69
+seagullbelowcmb;033C
+second;2033
+secondtonechinese;02CA
+section;00A7
+seenarabic;0633
+seenfinalarabic;FEB2
+seeninitialarabic;FEB3
+seenmedialarabic;FEB4
+segol;05B6
+segol13;05B6
+segol1f;05B6
+segol2c;05B6
+segolhebrew;05B6
+segolnarrowhebrew;05B6
+segolquarterhebrew;05B6
+segoltahebrew;0592
+segolwidehebrew;05B6
+seharmenian;057D
+sehiragana;305B
+sekatakana;30BB
+sekatakanahalfwidth;FF7E
+semicolon;003B
+semicolonarabic;061B
+semicolonmonospace;FF1B
+semicolonsmall;FE54
+semivoicedmarkkana;309C
+semivoicedmarkkanahalfwidth;FF9F
+sentisquare;3322
+sentosquare;3323
+seven;0037
+sevenarabic;0667
+sevenbengali;09ED
+sevencircle;2466
+sevencircleinversesansserif;2790
+sevendeva;096D
+seveneighths;215E
+sevengujarati;0AED
+sevengurmukhi;0A6D
+sevenhackarabic;0667
+sevenhangzhou;3027
+sevenideographicparen;3226
+seveninferior;2087
+sevenmonospace;FF17
+sevenoldstyle;F737
+sevenparen;247A
+sevenperiod;248E
+sevenpersian;06F7
+sevenroman;2176
+sevensuperior;2077
+seventeencircle;2470
+seventeenparen;2484
+seventeenperiod;2498
+seventhai;0E57
+sfthyphen;00AD
+shaarmenian;0577
+shabengali;09B6
+shacyrillic;0448
+shaddaarabic;0651
+shaddadammaarabic;FC61
+shaddadammatanarabic;FC5E
+shaddafathaarabic;FC60
+shaddafathatanarabic;0651 064B
+shaddakasraarabic;FC62
+shaddakasratanarabic;FC5F
+shade;2592
+shadedark;2593
+shadelight;2591
+shademedium;2592
+shadeva;0936
+shagujarati;0AB6
+shagurmukhi;0A36
+shalshelethebrew;0593
+shbopomofo;3115
+shchacyrillic;0449
+sheenarabic;0634
+sheenfinalarabic;FEB6
+sheeninitialarabic;FEB7
+sheenmedialarabic;FEB8
+sheicoptic;03E3
+sheqel;20AA
+sheqelhebrew;20AA
+sheva;05B0
+sheva115;05B0
+sheva15;05B0
+sheva22;05B0
+sheva2e;05B0
+shevahebrew;05B0
+shevanarrowhebrew;05B0
+shevaquarterhebrew;05B0
+shevawidehebrew;05B0
+shhacyrillic;04BB
+shimacoptic;03ED
+shin;05E9
+shindagesh;FB49
+shindageshhebrew;FB49
+shindageshshindot;FB2C
+shindageshshindothebrew;FB2C
+shindageshsindot;FB2D
+shindageshsindothebrew;FB2D
+shindothebrew;05C1
+shinhebrew;05E9
+shinshindot;FB2A
+shinshindothebrew;FB2A
+shinsindot;FB2B
+shinsindothebrew;FB2B
+shook;0282
+sigma;03C3
+sigma1;03C2
+sigmafinal;03C2
+sigmalunatesymbolgreek;03F2
+sihiragana;3057
+sikatakana;30B7
+sikatakanahalfwidth;FF7C
+siluqhebrew;05BD
+siluqlefthebrew;05BD
+similar;223C
+sindothebrew;05C2
+siosacirclekorean;3274
+siosaparenkorean;3214
+sioscieuckorean;317E
+sioscirclekorean;3266
+sioskiyeokkorean;317A
+sioskorean;3145
+siosnieunkorean;317B
+siosparenkorean;3206
+siospieupkorean;317D
+siostikeutkorean;317C
+six;0036
+sixarabic;0666
+sixbengali;09EC
+sixcircle;2465
+sixcircleinversesansserif;278F
+sixdeva;096C
+sixgujarati;0AEC
+sixgurmukhi;0A6C
+sixhackarabic;0666
+sixhangzhou;3026
+sixideographicparen;3225
+sixinferior;2086
+sixmonospace;FF16
+sixoldstyle;F736
+sixparen;2479
+sixperiod;248D
+sixpersian;06F6
+sixroman;2175
+sixsuperior;2076
+sixteencircle;246F
+sixteencurrencydenominatorbengali;09F9
+sixteenparen;2483
+sixteenperiod;2497
+sixthai;0E56
+slash;002F
+slashmonospace;FF0F
+slong;017F
+slongdotaccent;1E9B
+smileface;263A
+smonospace;FF53
+sofpasuqhebrew;05C3
+softhyphen;00AD
+softsigncyrillic;044C
+sohiragana;305D
+sokatakana;30BD
+sokatakanahalfwidth;FF7F
+soliduslongoverlaycmb;0338
+solidusshortoverlaycmb;0337
+sorusithai;0E29
+sosalathai;0E28
+sosothai;0E0B
+sosuathai;0E2A
+space;0020
+spacehackarabic;0020
+spade;2660
+spadesuitblack;2660
+spadesuitwhite;2664
+sparen;24AE
+squarebelowcmb;033B
+squarecc;33C4
+squarecm;339D
+squarediagonalcrosshatchfill;25A9
+squarehorizontalfill;25A4
+squarekg;338F
+squarekm;339E
+squarekmcapital;33CE
+squareln;33D1
+squarelog;33D2
+squaremg;338E
+squaremil;33D5
+squaremm;339C
+squaremsquared;33A1
+squareorthogonalcrosshatchfill;25A6
+squareupperlefttolowerrightfill;25A7
+squareupperrighttolowerleftfill;25A8
+squareverticalfill;25A5
+squarewhitewithsmallblack;25A3
+srsquare;33DB
+ssabengali;09B7
+ssadeva;0937
+ssagujarati;0AB7
+ssangcieuckorean;3149
+ssanghieuhkorean;3185
+ssangieungkorean;3180
+ssangkiyeokkorean;3132
+ssangnieunkorean;3165
+ssangpieupkorean;3143
+ssangsioskorean;3146
+ssangtikeutkorean;3138
+ssuperior;F6F2
+sterling;00A3
+sterlingmonospace;FFE1
+strokelongoverlaycmb;0336
+strokeshortoverlaycmb;0335
+subset;2282
+subsetnotequal;228A
+subsetorequal;2286
+succeeds;227B
+suchthat;220B
+suhiragana;3059
+sukatakana;30B9
+sukatakanahalfwidth;FF7D
+sukunarabic;0652
+summation;2211
+sun;263C
+superset;2283
+supersetnotequal;228B
+supersetorequal;2287
+svsquare;33DC
+syouwaerasquare;337C
+t;0074
+tabengali;09A4
+tackdown;22A4
+tackleft;22A3
+tadeva;0924
+tagujarati;0AA4
+tagurmukhi;0A24
+taharabic;0637
+tahfinalarabic;FEC2
+tahinitialarabic;FEC3
+tahiragana;305F
+tahmedialarabic;FEC4
+taisyouerasquare;337D
+takatakana;30BF
+takatakanahalfwidth;FF80
+tatweelarabic;0640
+tau;03C4
+tav;05EA
+tavdages;FB4A
+tavdagesh;FB4A
+tavdageshhebrew;FB4A
+tavhebrew;05EA
+tbar;0167
+tbopomofo;310A
+tcaron;0165
+tccurl;02A8
+tcedilla;0163
+tcheharabic;0686
+tchehfinalarabic;FB7B
+tchehinitialarabic;FB7C
+tchehmedialarabic;FB7D
+tchehmeeminitialarabic;FB7C FEE4
+tcircle;24E3
+tcircumflexbelow;1E71
+tcommaaccent;0163
+tdieresis;1E97
+tdotaccent;1E6B
+tdotbelow;1E6D
+tecyrillic;0442
+tedescendercyrillic;04AD
+teharabic;062A
+tehfinalarabic;FE96
+tehhahinitialarabic;FCA2
+tehhahisolatedarabic;FC0C
+tehinitialarabic;FE97
+tehiragana;3066
+tehjeeminitialarabic;FCA1
+tehjeemisolatedarabic;FC0B
+tehmarbutaarabic;0629
+tehmarbutafinalarabic;FE94
+tehmedialarabic;FE98
+tehmeeminitialarabic;FCA4
+tehmeemisolatedarabic;FC0E
+tehnoonfinalarabic;FC73
+tekatakana;30C6
+tekatakanahalfwidth;FF83
+telephone;2121
+telephoneblack;260E
+telishagedolahebrew;05A0
+telishaqetanahebrew;05A9
+tencircle;2469
+tenideographicparen;3229
+tenparen;247D
+tenperiod;2491
+tenroman;2179
+tesh;02A7
+tet;05D8
+tetdagesh;FB38
+tetdageshhebrew;FB38
+tethebrew;05D8
+tetsecyrillic;04B5
+tevirhebrew;059B
+tevirlefthebrew;059B
+thabengali;09A5
+thadeva;0925
+thagujarati;0AA5
+thagurmukhi;0A25
+thalarabic;0630
+thalfinalarabic;FEAC
+thanthakhatlowleftthai;F898
+thanthakhatlowrightthai;F897
+thanthakhatthai;0E4C
+thanthakhatupperleftthai;F896
+theharabic;062B
+thehfinalarabic;FE9A
+thehinitialarabic;FE9B
+thehmedialarabic;FE9C
+thereexists;2203
+therefore;2234
+theta;03B8
+theta1;03D1
+thetasymbolgreek;03D1
+thieuthacirclekorean;3279
+thieuthaparenkorean;3219
+thieuthcirclekorean;326B
+thieuthkorean;314C
+thieuthparenkorean;320B
+thirteencircle;246C
+thirteenparen;2480
+thirteenperiod;2494
+thonangmonthothai;0E11
+thook;01AD
+thophuthaothai;0E12
+thorn;00FE
+thothahanthai;0E17
+thothanthai;0E10
+thothongthai;0E18
+thothungthai;0E16
+thousandcyrillic;0482
+thousandsseparatorarabic;066C
+thousandsseparatorpersian;066C
+three;0033
+threearabic;0663
+threebengali;09E9
+threecircle;2462
+threecircleinversesansserif;278C
+threedeva;0969
+threeeighths;215C
+threegujarati;0AE9
+threegurmukhi;0A69
+threehackarabic;0663
+threehangzhou;3023
+threeideographicparen;3222
+threeinferior;2083
+threemonospace;FF13
+threenumeratorbengali;09F6
+threeoldstyle;F733
+threeparen;2476
+threeperiod;248A
+threepersian;06F3
+threequarters;00BE
+threequartersemdash;F6DE
+threeroman;2172
+threesuperior;00B3
+threethai;0E53
+thzsquare;3394
+tihiragana;3061
+tikatakana;30C1
+tikatakanahalfwidth;FF81
+tikeutacirclekorean;3270
+tikeutaparenkorean;3210
+tikeutcirclekorean;3262
+tikeutkorean;3137
+tikeutparenkorean;3202
+tilde;02DC
+tildebelowcmb;0330
+tildecmb;0303
+tildecomb;0303
+tildedoublecmb;0360
+tildeoperator;223C
+tildeoverlaycmb;0334
+tildeverticalcmb;033E
+timescircle;2297
+tipehahebrew;0596
+tipehalefthebrew;0596
+tippigurmukhi;0A70
+titlocyrilliccmb;0483
+tiwnarmenian;057F
+tlinebelow;1E6F
+tmonospace;FF54
+toarmenian;0569
+tohiragana;3068
+tokatakana;30C8
+tokatakanahalfwidth;FF84
+tonebarextrahighmod;02E5
+tonebarextralowmod;02E9
+tonebarhighmod;02E6
+tonebarlowmod;02E8
+tonebarmidmod;02E7
+tonefive;01BD
+tonesix;0185
+tonetwo;01A8
+tonos;0384
+tonsquare;3327
+topatakthai;0E0F
+tortoiseshellbracketleft;3014
+tortoiseshellbracketleftsmall;FE5D
+tortoiseshellbracketleftvertical;FE39
+tortoiseshellbracketright;3015
+tortoiseshellbracketrightsmall;FE5E
+tortoiseshellbracketrightvertical;FE3A
+totaothai;0E15
+tpalatalhook;01AB
+tparen;24AF
+trademark;2122
+trademarksans;F8EA
+trademarkserif;F6DB
+tretroflexhook;0288
+triagdn;25BC
+triaglf;25C4
+triagrt;25BA
+triagup;25B2
+ts;02A6
+tsadi;05E6
+tsadidagesh;FB46
+tsadidageshhebrew;FB46
+tsadihebrew;05E6
+tsecyrillic;0446
+tsere;05B5
+tsere12;05B5
+tsere1e;05B5
+tsere2b;05B5
+tserehebrew;05B5
+tserenarrowhebrew;05B5
+tserequarterhebrew;05B5
+tserewidehebrew;05B5
+tshecyrillic;045B
+tsuperior;F6F3
+ttabengali;099F
+ttadeva;091F
+ttagujarati;0A9F
+ttagurmukhi;0A1F
+tteharabic;0679
+ttehfinalarabic;FB67
+ttehinitialarabic;FB68
+ttehmedialarabic;FB69
+tthabengali;09A0
+tthadeva;0920
+tthagujarati;0AA0
+tthagurmukhi;0A20
+tturned;0287
+tuhiragana;3064
+tukatakana;30C4
+tukatakanahalfwidth;FF82
+tusmallhiragana;3063
+tusmallkatakana;30C3
+tusmallkatakanahalfwidth;FF6F
+twelvecircle;246B
+twelveparen;247F
+twelveperiod;2493
+twelveroman;217B
+twentycircle;2473
+twentyhangzhou;5344
+twentyparen;2487
+twentyperiod;249B
+two;0032
+twoarabic;0662
+twobengali;09E8
+twocircle;2461
+twocircleinversesansserif;278B
+twodeva;0968
+twodotenleader;2025
+twodotleader;2025
+twodotleadervertical;FE30
+twogujarati;0AE8
+twogurmukhi;0A68
+twohackarabic;0662
+twohangzhou;3022
+twoideographicparen;3221
+twoinferior;2082
+twomonospace;FF12
+twonumeratorbengali;09F5
+twooldstyle;F732
+twoparen;2475
+twoperiod;2489
+twopersian;06F2
+tworoman;2171
+twostroke;01BB
+twosuperior;00B2
+twothai;0E52
+twothirds;2154
+u;0075
+uacute;00FA
+ubar;0289
+ubengali;0989
+ubopomofo;3128
+ubreve;016D
+ucaron;01D4
+ucircle;24E4
+ucircumflex;00FB
+ucircumflexbelow;1E77
+ucyrillic;0443
+udattadeva;0951
+udblacute;0171
+udblgrave;0215
+udeva;0909
+udieresis;00FC
+udieresisacute;01D8
+udieresisbelow;1E73
+udieresiscaron;01DA
+udieresiscyrillic;04F1
+udieresisgrave;01DC
+udieresismacron;01D6
+udotbelow;1EE5
+ugrave;00F9
+ugujarati;0A89
+ugurmukhi;0A09
+uhiragana;3046
+uhookabove;1EE7
+uhorn;01B0
+uhornacute;1EE9
+uhorndotbelow;1EF1
+uhorngrave;1EEB
+uhornhookabove;1EED
+uhorntilde;1EEF
+uhungarumlaut;0171
+uhungarumlautcyrillic;04F3
+uinvertedbreve;0217
+ukatakana;30A6
+ukatakanahalfwidth;FF73
+ukcyrillic;0479
+ukorean;315C
+umacron;016B
+umacroncyrillic;04EF
+umacrondieresis;1E7B
+umatragurmukhi;0A41
+umonospace;FF55
+underscore;005F
+underscoredbl;2017
+underscoremonospace;FF3F
+underscorevertical;FE33
+underscorewavy;FE4F
+union;222A
+universal;2200
+uogonek;0173
+uparen;24B0
+upblock;2580
+upperdothebrew;05C4
+upsilon;03C5
+upsilondieresis;03CB
+upsilondieresistonos;03B0
+upsilonlatin;028A
+upsilontonos;03CD
+uptackbelowcmb;031D
+uptackmod;02D4
+uragurmukhi;0A73
+uring;016F
+ushortcyrillic;045E
+usmallhiragana;3045
+usmallkatakana;30A5
+usmallkatakanahalfwidth;FF69
+ustraightcyrillic;04AF
+ustraightstrokecyrillic;04B1
+utilde;0169
+utildeacute;1E79
+utildebelow;1E75
+uubengali;098A
+uudeva;090A
+uugujarati;0A8A
+uugurmukhi;0A0A
+uumatragurmukhi;0A42
+uuvowelsignbengali;09C2
+uuvowelsigndeva;0942
+uuvowelsigngujarati;0AC2
+uvowelsignbengali;09C1
+uvowelsigndeva;0941
+uvowelsigngujarati;0AC1
+v;0076
+vadeva;0935
+vagujarati;0AB5
+vagurmukhi;0A35
+vakatakana;30F7
+vav;05D5
+vavdagesh;FB35
+vavdagesh65;FB35
+vavdageshhebrew;FB35
+vavhebrew;05D5
+vavholam;FB4B
+vavholamhebrew;FB4B
+vavvavhebrew;05F0
+vavyodhebrew;05F1
+vcircle;24E5
+vdotbelow;1E7F
+vecyrillic;0432
+veharabic;06A4
+vehfinalarabic;FB6B
+vehinitialarabic;FB6C
+vehmedialarabic;FB6D
+vekatakana;30F9
+venus;2640
+verticalbar;007C
+verticallineabovecmb;030D
+verticallinebelowcmb;0329
+verticallinelowmod;02CC
+verticallinemod;02C8
+vewarmenian;057E
+vhook;028B
+vikatakana;30F8
+viramabengali;09CD
+viramadeva;094D
+viramagujarati;0ACD
+visargabengali;0983
+visargadeva;0903
+visargagujarati;0A83
+vmonospace;FF56
+voarmenian;0578
+voicediterationhiragana;309E
+voicediterationkatakana;30FE
+voicedmarkkana;309B
+voicedmarkkanahalfwidth;FF9E
+vokatakana;30FA
+vparen;24B1
+vtilde;1E7D
+vturned;028C
+vuhiragana;3094
+vukatakana;30F4
+w;0077
+wacute;1E83
+waekorean;3159
+wahiragana;308F
+wakatakana;30EF
+wakatakanahalfwidth;FF9C
+wakorean;3158
+wasmallhiragana;308E
+wasmallkatakana;30EE
+wattosquare;3357
+wavedash;301C
+wavyunderscorevertical;FE34
+wawarabic;0648
+wawfinalarabic;FEEE
+wawhamzaabovearabic;0624
+wawhamzaabovefinalarabic;FE86
+wbsquare;33DD
+wcircle;24E6
+wcircumflex;0175
+wdieresis;1E85
+wdotaccent;1E87
+wdotbelow;1E89
+wehiragana;3091
+weierstrass;2118
+wekatakana;30F1
+wekorean;315E
+weokorean;315D
+wgrave;1E81
+whitebullet;25E6
+whitecircle;25CB
+whitecircleinverse;25D9
+whitecornerbracketleft;300E
+whitecornerbracketleftvertical;FE43
+whitecornerbracketright;300F
+whitecornerbracketrightvertical;FE44
+whitediamond;25C7
+whitediamondcontainingblacksmalldiamond;25C8
+whitedownpointingsmalltriangle;25BF
+whitedownpointingtriangle;25BD
+whiteleftpointingsmalltriangle;25C3
+whiteleftpointingtriangle;25C1
+whitelenticularbracketleft;3016
+whitelenticularbracketright;3017
+whiterightpointingsmalltriangle;25B9
+whiterightpointingtriangle;25B7
+whitesmallsquare;25AB
+whitesmilingface;263A
+whitesquare;25A1
+whitestar;2606
+whitetelephone;260F
+whitetortoiseshellbracketleft;3018
+whitetortoiseshellbracketright;3019
+whiteuppointingsmalltriangle;25B5
+whiteuppointingtriangle;25B3
+wihiragana;3090
+wikatakana;30F0
+wikorean;315F
+wmonospace;FF57
+wohiragana;3092
+wokatakana;30F2
+wokatakanahalfwidth;FF66
+won;20A9
+wonmonospace;FFE6
+wowaenthai;0E27
+wparen;24B2
+wring;1E98
+wsuperior;02B7
+wturned;028D
+wynn;01BF
+x;0078
+xabovecmb;033D
+xbopomofo;3112
+xcircle;24E7
+xdieresis;1E8D
+xdotaccent;1E8B
+xeharmenian;056D
+xi;03BE
+xmonospace;FF58
+xparen;24B3
+xsuperior;02E3
+y;0079
+yaadosquare;334E
+yabengali;09AF
+yacute;00FD
+yadeva;092F
+yaekorean;3152
+yagujarati;0AAF
+yagurmukhi;0A2F
+yahiragana;3084
+yakatakana;30E4
+yakatakanahalfwidth;FF94
+yakorean;3151
+yamakkanthai;0E4E
+yasmallhiragana;3083
+yasmallkatakana;30E3
+yasmallkatakanahalfwidth;FF6C
+yatcyrillic;0463
+ycircle;24E8
+ycircumflex;0177
+ydieresis;00FF
+ydotaccent;1E8F
+ydotbelow;1EF5
+yeharabic;064A
+yehbarreearabic;06D2
+yehbarreefinalarabic;FBAF
+yehfinalarabic;FEF2
+yehhamzaabovearabic;0626
+yehhamzaabovefinalarabic;FE8A
+yehhamzaaboveinitialarabic;FE8B
+yehhamzaabovemedialarabic;FE8C
+yehinitialarabic;FEF3
+yehmedialarabic;FEF4
+yehmeeminitialarabic;FCDD
+yehmeemisolatedarabic;FC58
+yehnoonfinalarabic;FC94
+yehthreedotsbelowarabic;06D1
+yekorean;3156
+yen;00A5
+yenmonospace;FFE5
+yeokorean;3155
+yeorinhieuhkorean;3186
+yerahbenyomohebrew;05AA
+yerahbenyomolefthebrew;05AA
+yericyrillic;044B
+yerudieresiscyrillic;04F9
+yesieungkorean;3181
+yesieungpansioskorean;3183
+yesieungsioskorean;3182
+yetivhebrew;059A
+ygrave;1EF3
+yhook;01B4
+yhookabove;1EF7
+yiarmenian;0575
+yicyrillic;0457
+yikorean;3162
+yinyang;262F
+yiwnarmenian;0582
+ymonospace;FF59
+yod;05D9
+yoddagesh;FB39
+yoddageshhebrew;FB39
+yodhebrew;05D9
+yodyodhebrew;05F2
+yodyodpatahhebrew;FB1F
+yohiragana;3088
+yoikorean;3189
+yokatakana;30E8
+yokatakanahalfwidth;FF96
+yokorean;315B
+yosmallhiragana;3087
+yosmallkatakana;30E7
+yosmallkatakanahalfwidth;FF6E
+yotgreek;03F3
+yoyaekorean;3188
+yoyakorean;3187
+yoyakthai;0E22
+yoyingthai;0E0D
+yparen;24B4
+ypogegrammeni;037A
+ypogegrammenigreekcmb;0345
+yr;01A6
+yring;1E99
+ysuperior;02B8
+ytilde;1EF9
+yturned;028E
+yuhiragana;3086
+yuikorean;318C
+yukatakana;30E6
+yukatakanahalfwidth;FF95
+yukorean;3160
+yusbigcyrillic;046B
+yusbigiotifiedcyrillic;046D
+yuslittlecyrillic;0467
+yuslittleiotifiedcyrillic;0469
+yusmallhiragana;3085
+yusmallkatakana;30E5
+yusmallkatakanahalfwidth;FF6D
+yuyekorean;318B
+yuyeokorean;318A
+yyabengali;09DF
+yyadeva;095F
+z;007A
+zaarmenian;0566
+zacute;017A
+zadeva;095B
+zagurmukhi;0A5B
+zaharabic;0638
+zahfinalarabic;FEC6
+zahinitialarabic;FEC7
+zahiragana;3056
+zahmedialarabic;FEC8
+zainarabic;0632
+zainfinalarabic;FEB0
+zakatakana;30B6
+zaqefgadolhebrew;0595
+zaqefqatanhebrew;0594
+zarqahebrew;0598
+zayin;05D6
+zayindagesh;FB36
+zayindageshhebrew;FB36
+zayinhebrew;05D6
+zbopomofo;3117
+zcaron;017E
+zcircle;24E9
+zcircumflex;1E91
+zcurl;0291
+zdot;017C
+zdotaccent;017C
+zdotbelow;1E93
+zecyrillic;0437
+zedescendercyrillic;0499
+zedieresiscyrillic;04DF
+zehiragana;305C
+zekatakana;30BC
+zero;0030
+zeroarabic;0660
+zerobengali;09E6
+zerodeva;0966
+zerogujarati;0AE6
+zerogurmukhi;0A66
+zerohackarabic;0660
+zeroinferior;2080
+zeromonospace;FF10
+zerooldstyle;F730
+zeropersian;06F0
+zerosuperior;2070
+zerothai;0E50
+zerowidthjoiner;FEFF
+zerowidthnonjoiner;200C
+zerowidthspace;200B
+zeta;03B6
+zhbopomofo;3113
+zhearmenian;056A
+zhebrevecyrillic;04C2
+zhecyrillic;0436
+zhedescendercyrillic;0497
+zhedieresiscyrillic;04DD
+zihiragana;3058
+zikatakana;30B8
+zinorhebrew;05AE
+zlinebelow;1E95
+zmonospace;FF5A
+zohiragana;305E
+zokatakana;30BE
+zparen;24B5
+zretroflexhook;0290
+zstroke;01B6
+zuhiragana;305A
+zukatakana;30BA
+"""
+
+
+# string table management
+#
+class StringTable:
+ def __init__( self, name_list, master_table_name ):
+ self.names = name_list
+ self.master_table = master_table_name
+ self.indices = {}
+ index = 0
+
+ for name in name_list:
+ self.indices[name] = index
+ index += len( name ) + 1
+
+ self.total = index
+
+ def dump( self, file ):
+ write = file.write
+ write( " static const char " + self.master_table +
+ "[" + repr( self.total ) + "] =\n" )
+ write( " {\n" )
+
+ line = ""
+ for name in self.names:
+ line += " '"
+ line += string.join( ( re.findall( ".", name ) ), "','" )
+ line += "', 0,\n"
+
+ write( line + " };\n\n\n" )
+
+ def dump_sublist( self, file, table_name, macro_name, sublist ):
+ write = file.write
+ write( "#define " + macro_name + " " + repr( len( sublist ) ) + "\n\n" )
+
+ write( " /* Values are offsets into the `" +
+ self.master_table + "' table */\n\n" )
+ write( " static const short " + table_name +
+ "[" + macro_name + "] =\n" )
+ write( " {\n" )
+
+ line = " "
+ comma = ""
+ col = 0
+
+ for name in sublist:
+ line += comma
+ line += "%4d" % self.indices[name]
+ col += 1
+ comma = ","
+ if col == 14:
+ col = 0
+ comma = ",\n "
+
+ write( line + "\n };\n\n\n" )
+
+
+# We now store the Adobe Glyph List in compressed form. The list is put
+# into a data structure called `trie' (because it has a tree-like
+# appearance). Consider, for example, that you want to store the
+# following name mapping:
+#
+# A => 1
+# Aacute => 6
+# Abalon => 2
+# Abstract => 4
+#
+# It is possible to store the entries as follows.
+#
+# A => 1
+# |
+# +-acute => 6
+# |
+# +-b
+# |
+# +-alon => 2
+# |
+# +-stract => 4
+#
+# We see that each node in the trie has:
+#
+# - one or more `letters'
+# - an optional value
+# - zero or more child nodes
+#
+# The first step is to call
+#
+# root = StringNode( "", 0 )
+# for word in map.values():
+# root.add( word, map[word] )
+#
+# which creates a large trie where each node has only one children.
+#
+# Executing
+#
+# root = root.optimize()
+#
+# optimizes the trie by merging the letters of successive nodes whenever
+# possible.
+#
+# Each node of the trie is stored as follows.
+#
+# - First the node's letter, according to the following scheme. We
+# use the fact that in the AGL no name contains character codes > 127.
+#
+# name bitsize description
+# ----------------------------------------------------------------
+# notlast 1 Set to 1 if this is not the last letter
+# in the word.
+# ascii 7 The letter's ASCII value.
+#
+# - The letter is followed by a children count and the value of the
+# current key (if any). Again we can do some optimization because all
+# AGL entries are from the BMP; this means that 16 bits are sufficient
+# to store its Unicode values. Additionally, no node has more than
+# 127 children.
+#
+# name bitsize description
+# -----------------------------------------
+# hasvalue 1 Set to 1 if a 16-bit Unicode value follows.
+# num_children 7 Number of children. Can be 0 only if
+# `hasvalue' is set to 1.
+# value 16 Optional Unicode value.
+#
+# - A node is finished by a list of 16bit absolute offsets to the
+# children, which must be sorted in increasing order of their first
+# letter.
+#
+# For simplicity, all 16bit quantities are stored in big-endian order.
+#
+# The root node has first letter = 0, and no value.
+#
+class StringNode:
+ def __init__( self, letter, value ):
+ self.letter = letter
+ self.value = value
+ self.children = {}
+
+ def __cmp__( self, other ):
+ return ord( self.letter[0] ) - ord( other.letter[0] )
+
+ def add( self, word, value ):
+ if len( word ) == 0:
+ self.value = value
+ return
+
+ letter = word[0]
+ word = word[1:]
+
+ if self.children.has_key( letter ):
+ child = self.children[letter]
+ else:
+ child = StringNode( letter, 0 )
+ self.children[letter] = child
+
+ child.add( word, value )
+
+ def optimize( self ):
+ # optimize all children first
+ children = self.children.values()
+ self.children = {}
+
+ for child in children:
+ self.children[child.letter[0]] = child.optimize()
+
+ # don't optimize if there's a value,
+ # if we don't have any child or if we
+ # have more than one child
+ if ( self.value != 0 ) or ( not children ) or len( children ) > 1:
+ return self
+
+ child = children[0]
+
+ self.letter += child.letter
+ self.value = child.value
+ self.children = child.children
+
+ return self
+
+ def dump_debug( self, write, margin ):
+ # this is used during debugging
+ line = margin + "+-"
+ if len( self.letter ) == 0:
+ line += "<NOLETTER>"
+ else:
+ line += self.letter
+
+ if self.value:
+ line += " => " + repr( self.value )
+
+ write( line + "\n" )
+
+ if self.children:
+ margin += "| "
+ for child in self.children.values():
+ child.dump_debug( write, margin )
+
+ def locate( self, index ):
+ self.index = index
+ if len( self.letter ) > 0:
+ index += len( self.letter ) + 1
+ else:
+ index += 2
+
+ if self.value != 0:
+ index += 2
+
+ children = self.children.values()
+ children.sort()
+
+ index += 2 * len( children )
+ for child in children:
+ index = child.locate( index )
+
+ return index
+
+ def store( self, storage ):
+ # write the letters
+ l = len( self.letter )
+ if l == 0:
+ storage += struct.pack( "B", 0 )
+ else:
+ for n in range( l ):
+ val = ord( self.letter[n] )
+ if n < l - 1:
+ val += 128
+ storage += struct.pack( "B", val )
+
+ # write the count
+ children = self.children.values()
+ children.sort()
+
+ count = len( children )
+
+ if self.value != 0:
+ storage += struct.pack( "!BH", count + 128, self.value )
+ else:
+ storage += struct.pack( "B", count )
+
+ for child in children:
+ storage += struct.pack( "!H", child.index )
+
+ for child in children:
+ storage = child.store( storage )
+
+ return storage
+
+
+def adobe_glyph_values():
+ """return the list of glyph names and their unicode values"""
+
+ lines = string.split( adobe_glyph_list, '\n' )
+ glyphs = []
+ values = []
+
+ for line in lines:
+ if line:
+ fields = string.split( line, ';' )
+# print fields[1] + ' - ' + fields[0]
+ subfields = string.split( fields[1], ' ' )
+ if len( subfields ) == 1:
+ glyphs.append( fields[0] )
+ values.append( fields[1] )
+
+ return glyphs, values
+
+
+def filter_glyph_names( alist, filter ):
+ """filter `alist' by taking _out_ all glyph names that are in `filter'"""
+
+ count = 0
+ extras = []
+
+ for name in alist:
+ try:
+ filtered_index = filter.index( name )
+ except:
+ extras.append( name )
+
+ return extras
+
+
+def dump_encoding( file, encoding_name, encoding_list ):
+ """dump a given encoding"""
+
+ write = file.write
+ write( " /* the following are indices into the SID name table */\n" )
+ write( " static const unsigned short " + encoding_name +
+ "[" + repr( len( encoding_list ) ) + "] =\n" )
+ write( " {\n" )
+
+ line = " "
+ comma = ""
+ col = 0
+ for value in encoding_list:
+ line += comma
+ line += "%3d" % value
+ comma = ","
+ col += 1
+ if col == 16:
+ col = 0
+ comma = ",\n "
+
+ write( line + "\n };\n\n\n" )
+
+
+def dump_array( the_array, write, array_name ):
+ """dumps a given encoding"""
+
+ write( " static const unsigned char " + array_name +
+ "[" + repr( len( the_array ) ) + "] =\n" )
+ write( " {\n" )
+
+ line = ""
+ comma = " "
+ col = 0
+
+ for value in the_array:
+ line += comma
+ line += "%3d" % ord( value )
+ comma = ","
+ col += 1
+
+ if col == 16:
+ col = 0
+ comma = ",\n "
+
+ if len( line ) > 1024:
+ write( line )
+ line = ""
+
+ write( line + "\n };\n\n\n" )
+
+
+def main():
+ """main program body"""
+
+ if len( sys.argv ) != 2:
+ print __doc__ % sys.argv[0]
+ sys.exit( 1 )
+
+ file = open( sys.argv[1], "w\n" )
+ write = file.write
+
+ count_sid = len( sid_standard_names )
+
+ # `mac_extras' contains the list of glyph names in the Macintosh standard
+ # encoding which are not in the SID Standard Names.
+ #
+ mac_extras = filter_glyph_names( mac_standard_names, sid_standard_names )
+
+ # `base_list' contains the names of our final glyph names table.
+ # It consists of the `mac_extras' glyph names, followed by the SID
+ # standard names.
+ #
+ mac_extras_count = len( mac_extras )
+ base_list = mac_extras + sid_standard_names
+
+ write( "/***************************************************************************/\n" )
+ write( "/* */\n" )
+
+ write( "/* %-71s*/\n" % os.path.basename( sys.argv[1] ) )
+
+ write( "/* */\n" )
+ write( "/* PostScript glyph names. */\n" )
+ write( "/* */\n" )
+ write( "/* Copyright 2005 by */\n" )
+ write( "/* David Turner, Robert Wilhelm, and Werner Lemberg. */\n" )
+ write( "/* */\n" )
+ write( "/* This file is part of the FreeType project, and may only be used, */\n" )
+ write( "/* modified, and distributed under the terms of the FreeType project */\n" )
+ write( "/* license, LICENSE.TXT. By continuing to use, modify, or distribute */\n" )
+ write( "/* this file you indicate that you have read the license and */\n" )
+ write( "/* understand and accept it fully. */\n" )
+ write( "/* */\n" )
+ write( "/***************************************************************************/\n" )
+ write( "\n" )
+ write( "\n" )
+ write( " /* This file has been generated automatically -- do not edit! */\n" )
+ write( "\n" )
+ write( "\n" )
+
+ # dump final glyph list (mac extras + sid standard names)
+ #
+ st = StringTable( base_list, "ft_standard_glyph_names" )
+
+ st.dump( file )
+ st.dump_sublist( file, "ft_mac_names",
+ "FT_NUM_MAC_NAMES", mac_standard_names )
+ st.dump_sublist( file, "ft_sid_names",
+ "FT_NUM_SID_NAMES", sid_standard_names )
+
+ dump_encoding( file, "t1_standard_encoding", t1_standard_encoding )
+ dump_encoding( file, "t1_expert_encoding", t1_expert_encoding )
+
+ # dump the AGL in its compressed form
+ #
+ agl_glyphs, agl_values = adobe_glyph_values()
+ dict = StringNode( "", 0 )
+
+ for g in range( len( agl_glyphs ) ):
+ dict.add( agl_glyphs[g], eval( "0x" + agl_values[g] ) )
+
+ dict = dict.optimize()
+ dict_len = dict.locate( 0 )
+ dict_array = dict.store( "" )
+
+ write( """\
+ /*
+ * This table is a compressed version of the Adobe Glyph List (AGL),
+ * optimized for efficient searching. It has been generated by the
+ * `glnames.py' python script located in the `src/tools' directory.
+ *
+ * The lookup function to get the Unicode value for a given string
+ * is defined below the table.
+ */
+""" )
+
+ dump_array( dict_array, write, "ft_adobe_glyph_list" )
+
+ # write the lookup routine now
+ #
+ write( """\
+ /*
+ * This function searches the compressed table efficiently.
+ */
+ static unsigned long
+ ft_get_adobe_glyph_index( const char* name,
+ const char* limit )
+ {
+ int c = 0;
+ int count, min, max;
+ const unsigned char* p = ft_adobe_glyph_list;
+
+
+ if ( name == 0 || name >= limit )
+ goto NotFound;
+
+ c = *name++;
+ count = p[1];
+ p += 2;
+
+ min = 0;
+ max = count;
+
+ while ( min < max )
+ {
+ int mid = ( min + max ) >> 1;
+ const unsigned char* q = p + mid * 2;
+ int c2;
+
+
+ q = ft_adobe_glyph_list + ( ( (int)q[0] << 8 ) | q[1] );
+
+ c2 = q[0] & 127;
+ if ( c2 == c )
+ {
+ p = q;
+ goto Found;
+ }
+ if ( c2 < c )
+ min = mid + 1;
+ else
+ max = mid;
+ }
+ goto NotFound;
+
+ Found:
+ for (;;)
+ {
+ /* assert (*p & 127) == c */
+
+ if ( name >= limit )
+ {
+ if ( (p[0] & 128) == 0 &&
+ (p[1] & 128) != 0 )
+ return (unsigned long)( ( (int)p[2] << 8 ) | p[3] );
+
+ goto NotFound;
+ }
+ c = *name++;
+ if ( p[0] & 128 )
+ {
+ p++;
+ if ( c != (p[0] & 127) )
+ goto NotFound;
+
+ continue;
+ }
+
+ p++;
+ count = p[0] & 127;
+ if ( p[0] & 128 )
+ p += 2;
+
+ p++;
+
+ for ( ; count > 0; count--, p += 2 )
+ {
+ int offset = ( (int)p[0] << 8 ) | p[1];
+ const unsigned char* q = ft_adobe_glyph_list + offset;
+
+ if ( c == ( q[0] & 127 ) )
+ {
+ p = q;
+ goto NextIter;
+ }
+ }
+ goto NotFound;
+
+ NextIter:
+ ;
+ }
+
+ NotFound:
+ return 0;
+ }
+
+""" )
+
+ if 0: # generate unit test, or don't
+ #
+ # now write the unit test to check that everything works OK
+ #
+ write( "#ifdef TEST\n\n" )
+
+ write( "static const char* const the_names[] = {\n" )
+ for name in agl_glyphs:
+ write( ' "' + name + '",\n' )
+ write( " 0\n};\n" )
+
+ write( "static const unsigned long the_values[] = {\n" )
+ for val in agl_values:
+ write( ' 0x' + val + ',\n' )
+ write( " 0\n};\n" )
+
+ write( """
+#include <stdlib.h>
+#include <stdio.h>
+
+ int
+ main( void )
+ {
+ int result = 0;
+ const char* const* names = the_names;
+ const unsigned long* values = the_values;
+
+
+ for ( ; *names; names++, values++ )
+ {
+ const char* name = *names;
+ unsigned long reference = *values;
+ unsigned long value;
+
+
+ value = ft_get_adobe_glyph_index( name, name + strlen( name ) );
+ if ( value != reference )
+ {
+ result = 1;
+ fprintf( stderr, "name '%s' => %04x instead of %04x\\n",
+ name, value, reference );
+ }
+ }
+
+ return result;
+ }
+""" )
+
+ write( "#endif /* TEST */\n" )
+
+ write("\n/* END */\n")
+
+
+# Now run the main routine
+#
+main()
+
+
+# END
diff --git a/src/tools/test_afm.c b/src/tools/test_afm.c
new file mode 100644
index 0000000..d53cb33
--- /dev/null
+++ b/src/tools/test_afm.c
@@ -0,0 +1,157 @@
+/*
+ * gcc -DFT2_BUILD_LIBRARY -I../../include -o test_afm test_afm.c \
+ * -L../../objs/.libs -lfreetype -lz -static
+ */
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+ void dump_fontinfo( AFM_FontInfo fi )
+ {
+ FT_Int i;
+
+
+ printf( "This AFM is for %sCID font.\n\n",
+ ( fi->IsCIDFont ) ? "" : "non-" );
+
+ printf( "FontBBox: %.2f %.2f %.2f %.2f\n", fi->FontBBox.xMin / 65536.,
+ fi->FontBBox.yMin / 65536.,
+ fi->FontBBox.xMax / 65536.,
+ fi->FontBBox.yMax / 65536. );
+ printf( "Ascender: %.2f\n", fi->Ascender / 65536. );
+ printf( "Descender: %.2f\n\n", fi->Descender / 65536. );
+
+ if ( fi->NumTrackKern )
+ printf( "There are %d sets of track kernings:\n",
+ fi->NumTrackKern );
+ else
+ printf( "There is no track kerning.\n" );
+
+ for ( i = 0; i < fi->NumTrackKern; i++ )
+ {
+ AFM_TrackKern tk = fi->TrackKerns + i;
+
+
+ printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree,
+ tk->min_ptsize / 65536.,
+ tk->min_kern / 65536.,
+ tk->max_ptsize / 65536.,
+ tk->max_kern / 65536. );
+ }
+
+ printf( "\n" );
+
+ if ( fi->NumKernPair )
+ printf( "There are %d kerning pairs:\n",
+ fi->NumKernPair );
+ else
+ printf( "There is no kerning pair.\n" );
+
+ for ( i = 0; i < fi->NumKernPair; i++ )
+ {
+ AFM_KernPair kp = fi->KernPairs + i;
+
+
+ printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1,
+ kp->index2,
+ kp->x,
+ kp->y );
+ }
+
+ }
+
+ int
+ dummy_get_index( const char* name,
+ FT_UInt len,
+ void* user_data )
+ {
+ if ( len )
+ return name[0];
+ else
+ return 0;
+ }
+
+ FT_Error
+ parse_afm( FT_Library library,
+ FT_Stream stream,
+ AFM_FontInfo fi )
+ {
+ PSAux_Service psaux;
+ AFM_ParserRec parser;
+ FT_Error error = FT_Err_Ok;
+
+
+ psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" );
+ if ( !psaux || !psaux->afm_parser_funcs )
+ return -1;
+
+ error = FT_Stream_EnterFrame( stream, stream->size );
+ if ( error )
+ return error;
+
+ error = psaux->afm_parser_funcs->init( &parser,
+ library->memory,
+ stream->cursor,
+ stream->limit );
+ if ( error )
+ return error;
+
+ parser.FontInfo = fi;
+ parser.get_index = dummy_get_index;
+
+ error = psaux->afm_parser_funcs->parse( &parser );
+
+ psaux->afm_parser_funcs->done( &parser );
+
+ return error;
+ }
+
+
+ int main( int argc,
+ char** argv )
+ {
+ FT_Library library;
+ FT_StreamRec stream;
+ FT_Error error = FT_Err_Ok;
+ AFM_FontInfoRec fi;
+
+
+ if ( argc < 2 )
+ return FT_Err_Invalid_Argument;
+
+ error = FT_Init_FreeType( &library );
+ if ( error )
+ return error;
+
+ FT_ZERO( &stream );
+ error = FT_Stream_Open( &stream, argv[1] );
+ if ( error )
+ goto Exit;
+ stream.memory = library->memory;
+
+ FT_ZERO( &fi );
+ error = parse_afm( library, &stream, &fi );
+
+ if ( !error )
+ {
+ FT_Memory memory = library->memory;
+
+
+ dump_fontinfo( &fi );
+
+ if ( fi.KernPairs )
+ FT_FREE( fi.KernPairs );
+ if ( fi.TrackKerns )
+ FT_FREE( fi.TrackKerns );
+ }
+ else
+ printf( "parse error\n" );
+
+ FT_Stream_Close( &stream );
+
+ Exit:
+ FT_Done_FreeType( library );
+
+ return error;
+ }
diff --git a/src/tools/test_bbox.c b/src/tools/test_bbox.c
new file mode 100644
index 0000000..e085c5b
--- /dev/null
+++ b/src/tools/test_bbox.c
@@ -0,0 +1,160 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_BBOX_H
+
+
+#include <time.h> /* for clock() */
+
+/* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */
+/* to get the HZ macro which is the equivalent. */
+#if defined(__sun__) && !defined(SVR4) && !defined(__SVR4)
+#include <sys/param.h>
+#define CLOCKS_PER_SEC HZ
+#endif
+
+ static long
+ get_time( void )
+ {
+ return clock() * 10000L / CLOCKS_PER_SEC;
+ }
+
+
+
+
+ /* test bbox computations */
+
+#define XSCALE 65536
+#define XX(x) ((FT_Pos)(x*XSCALE))
+#define XVEC(x,y) { XX(x), XX(y) }
+#define XVAL(x) ((x)/(1.0*XSCALE))
+
+ /* dummy outline #1 */
+ static FT_Vector dummy_vec_1[4] =
+ {
+#if 1
+ XVEC( 408.9111, 535.3164 ),
+ XVEC( 455.8887, 634.396 ),
+ XVEC( -37.8765, 786.2207 ),
+ XVEC( 164.6074, 535.3164 )
+#else
+ { (FT_Int32)0x0198E93DL , (FT_Int32)0x021750FFL }, /* 408.9111, 535.3164 */
+ { (FT_Int32)0x01C7E312L , (FT_Int32)0x027A6560L }, /* 455.8887, 634.3960 */
+ { (FT_Int32)0xFFDA1F9EL , (FT_Int32)0x0312387FL }, /* -37.8765, 786.2207 */
+ { (FT_Int32)0x00A49B7EL , (FT_Int32)0x021750FFL } /* 164.6074, 535.3164 */
+#endif
+ };
+
+ static char dummy_tag_1[4] =
+ {
+ FT_CURVE_TAG_ON,
+ FT_CURVE_TAG_CUBIC,
+ FT_CURVE_TAG_CUBIC,
+ FT_CURVE_TAG_ON
+ };
+
+ static short dummy_contour_1[1] =
+ {
+ 3
+ };
+
+ static FT_Outline dummy_outline_1 =
+ {
+ 1,
+ 4,
+ dummy_vec_1,
+ dummy_tag_1,
+ dummy_contour_1,
+ 0
+ };
+
+
+ /* dummy outline #2 */
+ static FT_Vector dummy_vec_2[4] =
+ {
+ XVEC( 100.0, 100.0 ),
+ XVEC( 100.0, 200.0 ),
+ XVEC( 200.0, 200.0 ),
+ XVEC( 200.0, 133.0 )
+ };
+
+ static FT_Outline dummy_outline_2 =
+ {
+ 1,
+ 4,
+ dummy_vec_2,
+ dummy_tag_1,
+ dummy_contour_1,
+ 0
+ };
+
+
+ static void
+ dump_outline( FT_Outline* outline )
+ {
+ FT_BBox bbox;
+
+ /* compute and display cbox */
+ FT_Outline_Get_CBox( outline, &bbox );
+ printf( "cbox = [%.2f %.2f %.2f %.2f]\n",
+ XVAL( bbox.xMin ),
+ XVAL( bbox.yMin ),
+ XVAL( bbox.xMax ),
+ XVAL( bbox.yMax ) );
+
+ /* compute and display bbox */
+ FT_Outline_Get_BBox( outline, &bbox );
+ printf( "bbox = [%.2f %.2f %.2f %.2f]\n",
+ XVAL( bbox.xMin ),
+ XVAL( bbox.yMin ),
+ XVAL( bbox.xMax ),
+ XVAL( bbox.yMax ) );
+ }
+
+
+
+ static void
+ profile_outline( FT_Outline* outline,
+ long repeat )
+ {
+ FT_BBox bbox;
+ long count;
+ long time0;
+
+ time0 = get_time();
+ for ( count = repeat; count > 0; count-- )
+ FT_Outline_Get_CBox( outline, &bbox );
+
+ time0 = get_time() - time0;
+ printf( "time = %5.2f cbox = [%.2f %.2f %.2f %.2f]\n",
+ ((double)time0/10000.0),
+ XVAL( bbox.xMin ),
+ XVAL( bbox.yMin ),
+ XVAL( bbox.xMax ),
+ XVAL( bbox.yMax ) );
+
+
+ time0 = get_time();
+ for ( count = repeat; count > 0; count-- )
+ FT_Outline_Get_BBox( outline, &bbox );
+
+ time0 = get_time() - time0;
+ printf( "time = %5.2f bbox = [%.2f %.2f %.2f %.2f]\n",
+ ((double)time0/10000.0),
+ XVAL( bbox.xMin ),
+ XVAL( bbox.yMin ),
+ XVAL( bbox.xMax ),
+ XVAL( bbox.yMax ) );
+ }
+
+#define REPEAT 100000L
+
+ int main( int argc, char** argv )
+ {
+ printf( "outline #1\n" );
+ profile_outline( &dummy_outline_1, REPEAT );
+
+ printf( "outline #2\n" );
+ profile_outline( &dummy_outline_2, REPEAT );
+ return 0;
+ }
+
diff --git a/src/tools/test_trig.c b/src/tools/test_trig.c
new file mode 100644
index 0000000..8c8a544
--- /dev/null
+++ b/src/tools/test_trig.c
@@ -0,0 +1,236 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRIGONOMETRY_H
+
+#include <math.h>
+#include <stdio.h>
+
+#define PI 3.14159265358979323846
+#define SPI (PI/FT_ANGLE_PI)
+
+/* the precision in 16.16 fixed float points of the checks. Expect */
+/* between 2 and 5 noise LSB bits during operations, due to */
+/* rounding errors.. */
+#define THRESHOLD 64
+
+ static error = 0;
+
+ static void
+ test_cos( void )
+ {
+ FT_Fixed f1, f2;
+ double d1, d2;
+ int i;
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ f1 = FT_Cos(i);
+ d1 = f1/65536.0;
+ d2 = cos( i*SPI );
+ f2 = (FT_Fixed)(d2*65536.0);
+
+ if ( abs( f2-f1 ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Cos[%3d] = %.7f cos[%3d] = %.7f\n",
+ (i >> 16), f1/65536.0, (i >> 16), d2 );
+ }
+ }
+ }
+
+
+
+ static void
+ test_sin( void )
+ {
+ FT_Fixed f1, f2;
+ double d1, d2;
+ int i;
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ f1 = FT_Sin(i);
+ d1 = f1/65536.0;
+ d2 = sin( i*SPI );
+ f2 = (FT_Fixed)(d2*65536.0);
+
+ if ( abs( f2-f1 ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Sin[%3d] = %.7f sin[%3d] = %.7f\n",
+ (i >> 16), f1/65536.0, (i >> 16), d2 );
+ }
+ }
+ }
+
+
+ static void
+ test_tan( void )
+ {
+ FT_Fixed f1, f2;
+ double d1, d2;
+ int i;
+
+ for ( i = 0; i < FT_ANGLE_PI2-0x2000000; i += 0x10000 )
+ {
+ f1 = FT_Tan(i);
+ d1 = f1/65536.0;
+ d2 = tan( i*SPI );
+ f2 = (FT_Fixed)(d2*65536.0);
+
+ if ( abs( f2-f1 ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Tan[%3d] = %.7f tan[%3d] = %.7f\n",
+ (i >> 16), f1/65536.0, (i >> 16), d2 );
+ }
+ }
+ }
+
+
+ static void
+ test_atan2( void )
+ {
+ FT_Fixed c2, s2;
+ double l, a, c1, s1;
+ int i, j;
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ l = 5.0;
+ a = i*SPI;
+
+ c1 = l * cos(a);
+ s1 = l * sin(a);
+
+ c2 = (FT_Fixed)(c1*65536.0);
+ s2 = (FT_Fixed)(s1*65536.0);
+
+ j = FT_Atan2( c2, s2 );
+ if ( j < 0 )
+ j += FT_ANGLE_2PI;
+
+ if ( abs( i - j ) > 1 )
+ {
+ printf( "FT_Atan2( %.7f, %.7f ) = %.5f, atan = %.5f\n",
+ c2/65536.0, s2/65536.0, j/65536.0, i/65536.0 );
+ }
+ }
+ }
+
+ static void
+ test_unit( void )
+ {
+ FT_Vector v;
+ double a, c1, s1;
+ FT_Fixed c2, s2;
+ int i;
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ FT_Vector_Unit( &v, i );
+ a = ( i*SPI );
+ c1 = cos(a);
+ s1 = sin(a);
+ c2 = (FT_Fixed)(c1*65536.0);
+ s2 = (FT_Fixed)(s1*65536.0);
+
+ if ( abs( v.x-c2 ) > THRESHOLD ||
+ abs( v.y-s2 ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Vector_Unit[%3d] = ( %.7f, %.7f ) vec = ( %.7f, %.7f )\n",
+ (i >> 16),
+ v.x/65536.0, v.y/65536.0,
+ c1, s1 );
+ }
+ }
+ }
+
+
+ static void
+ test_length( void )
+ {
+ FT_Vector v;
+ FT_Fixed l, l2;
+ int i;
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ l = (FT_Fixed)(500.0*65536.0);
+ v.x = (FT_Fixed)( l * cos( i*SPI ) );
+ v.y = (FT_Fixed)( l * sin( i*SPI ) );
+ l2 = FT_Vector_Length( &v );
+
+ if ( abs( l2-l ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Length( %.7f, %.7f ) = %.5f, length = %.5f\n",
+ v.x/65536.0, v.y/65536.0, l2/65536.0, l/65536.0 );
+ }
+ }
+ }
+
+
+ static void
+ test_rotate( void )
+ {
+ FT_Fixed c2, s2, c4, s4;
+ FT_Vector v;
+ double l, ra, a, c1, s1, cra, sra, c3, s3;
+ int i, j, rotate;
+
+ for ( rotate = 0; rotate < FT_ANGLE_2PI; rotate += 0x10000 )
+ {
+ ra = rotate*SPI;
+ cra = cos( ra );
+ sra = sin( ra );
+
+ for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
+ {
+ l = 500.0;
+ a = i*SPI;
+
+ c1 = l * cos(a);
+ s1 = l * sin(a);
+
+ v.x = c2 = (FT_Fixed)(c1*65536.0);
+ v.y = s2 = (FT_Fixed)(s1*65536.0);
+
+ FT_Vector_Rotate( &v, rotate );
+
+ c3 = c1 * cra - s1 * sra;
+ s3 = c1 * sra + s1 * cra;
+
+ c4 = (FT_Fixed)(c3*65536.0);
+ s4 = (FT_Fixed)(s3*65536.0);
+
+ if ( abs( c4 - v.x ) > THRESHOLD ||
+ abs( s4 - v.y ) > THRESHOLD )
+ {
+ error = 1;
+ printf( "FT_Rotate( (%.7f,%.7f), %.5f ) = ( %.7f, %.7f ), rot = ( %.7f, %.7f )\n",
+ c1, s1, ra,
+ c2/65536.0, s2/65536.0,
+ c4/65536.0, s4/65536.0 );
+ }
+ }
+ }
+ }
+
+
+ int main( void )
+ {
+ test_cos();
+ test_sin();
+ test_tan();
+ test_atan2();
+ test_unit();
+ test_length();
+ test_rotate();
+
+ if (!error)
+ printf( "trigonometry test ok !\n" );
+
+ return !error;
+ }
diff --git a/src/truetype/Jamfile b/src/truetype/Jamfile
new file mode 100644
index 0000000..a166909
--- /dev/null
+++ b/src/truetype/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/truetype Jamfile
+#
+# Copyright 2001, 2004 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) truetype ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = ttdriver ttobjs ttpload ttgload ttinterp ttgxvar ;
+ }
+ else
+ {
+ _sources = truetype ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/truetype Jamfile
diff --git a/src/truetype/module.mk b/src/truetype/module.mk
new file mode 100644
index 0000000..3b05afc
--- /dev/null
+++ b/src/truetype/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 TrueType module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += TRUETYPE_DRIVER
+
+define TRUETYPE_DRIVER
+$(OPEN_DRIVER)tt_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)truetype $(ECHO_DRIVER_DESC)Windows/Mac font files with extension *.ttf or *.ttc$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/truetype/rules.mk b/src/truetype/rules.mk
new file mode 100644
index 0000000..7468426
--- /dev/null
+++ b/src/truetype/rules.mk
@@ -0,0 +1,72 @@
+#
+# FreeType 2 TrueType driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003, 2004 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# TrueType driver directory
+#
+TT_DIR := $(SRC_DIR)/truetype
+
+
+# compilation flags for the driver
+#
+TT_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(TT_DIR))
+
+
+# TrueType driver sources (i.e., C files)
+#
+TT_DRV_SRC := $(TT_DIR)/ttobjs.c \
+ $(TT_DIR)/ttpload.c \
+ $(TT_DIR)/ttgload.c \
+ $(TT_DIR)/ttinterp.c \
+ $(TT_DIR)/ttgxvar.c \
+ $(TT_DIR)/ttdriver.c
+
+# TrueType driver headers
+#
+TT_DRV_H := $(TT_DRV_SRC:%.c=%.h) \
+ $(TT_DIR)/tterrors.h
+
+
+# TrueType driver object(s)
+#
+# TT_DRV_OBJ_M is used during `multi' builds
+# TT_DRV_OBJ_S is used during `single' builds
+#
+TT_DRV_OBJ_M := $(TT_DRV_SRC:$(TT_DIR)/%.c=$(OBJ_DIR)/%.$O)
+TT_DRV_OBJ_S := $(OBJ_DIR)/truetype.$O
+
+# TrueType driver source file for single build
+#
+TT_DRV_SRC_S := $(TT_DIR)/truetype.c
+
+
+# TrueType driver - single object
+#
+$(TT_DRV_OBJ_S): $(TT_DRV_SRC_S) $(TT_DRV_SRC) $(FREETYPE_H) $(TT_DRV_H)
+ $(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(TT_DRV_SRC_S))
+
+
+# driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(TT_DIR)/%.c $(FREETYPE_H) $(TT_DRV_H)
+ $(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(TT_DRV_OBJ_S)
+DRV_OBJS_M += $(TT_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/truetype/truetype.c b/src/truetype/truetype.c
new file mode 100644
index 0000000..b36473a
--- /dev/null
+++ b/src/truetype/truetype.c
@@ -0,0 +1,36 @@
+/***************************************************************************/
+/* */
+/* truetype.c */
+/* */
+/* FreeType TrueType driver component (body only). */
+/* */
+/* Copyright 1996-2001, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "ttdriver.c" /* driver interface */
+#include "ttpload.c" /* tables loader */
+#include "ttgload.c" /* glyph loader */
+#include "ttobjs.c" /* object manager */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#include "ttinterp.c"
+#endif
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.c" /* gx distortable font */
+#endif
+
+
+/* END */
diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
new file mode 100644
index 0000000..c2cf452
--- /dev/null
+++ b/src/truetype/ttdriver.c
@@ -0,0 +1,418 @@
+/***************************************************************************/
+/* */
+/* ttdriver.c */
+/* */
+/* TrueType font driver implementation (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H
+#include FT_TRUETYPE_IDS_H
+#include FT_SERVICE_XFREE86_NAME_H
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include FT_MULTIPLE_MASTERS_H
+#include FT_SERVICE_MULTIPLE_MASTERS_H
+#endif
+
+#include FT_SERVICE_TRUETYPE_ENGINE_H
+#include FT_SERVICE_TRUETYPE_GLYF_H
+
+#include "ttdriver.h"
+#include "ttgload.h"
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttdriver
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** F A C E S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#undef PAIR_TAG
+#define PAIR_TAG( left, right ) ( ( (FT_ULong)left << 16 ) | \
+ (FT_ULong)right )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_get_kerning */
+ /* */
+ /* <Description> */
+ /* A driver method used to return the kerning vector between two */
+ /* glyphs of the same face. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* left_glyph :: The index of the left glyph in the kern pair. */
+ /* */
+ /* right_glyph :: The index of the right glyph in the kern pair. */
+ /* */
+ /* <Output> */
+ /* kerning :: The kerning vector. This is in font units for */
+ /* scalable formats, and in pixels for fixed-sizes */
+ /* formats. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only horizontal layouts (left-to-right & right-to-left) are */
+ /* supported by this function. Other layouts, or more sophisticated */
+ /* kernings, are out of scope of this method (the basic driver */
+ /* interface is meant to be simple). */
+ /* */
+ /* They can be implemented by format-specific interfaces. */
+ /* */
+ static FT_Error
+ tt_get_kerning( FT_Face ttface, /* TT_Face */
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_Vector* kerning )
+ {
+ TT_Face face = (TT_Face)ttface;
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+
+ kerning->x = 0;
+ kerning->y = 0;
+
+ if ( sfnt )
+ kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
+
+ return 0;
+ }
+
+
+#undef PAIR_TAG
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** S I Z E S ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ static FT_Error
+ tt_size_select( FT_Size size,
+ FT_ULong strike_index )
+ {
+ TT_Face ttface = (TT_Face)size->face;
+ TT_Size ttsize = (TT_Size)size;
+ FT_Error error = TT_Err_Ok;
+
+
+ ttsize->strike_index = strike_index;
+
+ if ( FT_IS_SCALABLE( size->face ) )
+ {
+ /* use the scaled metrics, even when tt_size_reset fails */
+ FT_Select_Metrics( size->face, strike_index );
+
+ tt_size_reset( ttsize );
+ }
+ else
+ {
+ SFNT_Service sfnt = (SFNT_Service) ttface->sfnt;
+ FT_Size_Metrics* metrics = &size->metrics;
+
+
+ error = sfnt->load_strike_metrics( ttface, strike_index, metrics );
+ if ( error )
+ ttsize->strike_index = 0xFFFFFFFFUL;
+ }
+
+ return error;
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+ static FT_Error
+ tt_size_request( FT_Size size,
+ FT_Size_Request req )
+ {
+ TT_Size ttsize = (TT_Size)size;
+ FT_Error error = TT_Err_Ok;
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ if ( FT_HAS_FIXED_SIZES( size->face ) )
+ {
+ TT_Face ttface = (TT_Face)size->face;
+ SFNT_Service sfnt = (SFNT_Service) ttface->sfnt;
+ FT_ULong strike_index;
+
+
+ error = sfnt->set_sbit_strike( ttface, req, &strike_index );
+
+ if ( error )
+ ttsize->strike_index = 0xFFFFFFFFUL;
+ else
+ return tt_size_select( size, strike_index );
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ FT_Request_Metrics( size->face, req );
+
+ if ( FT_IS_SCALABLE( size->face ) )
+ error = tt_size_reset( ttsize );
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Load_Glyph */
+ /* */
+ /* <Description> */
+ /* A driver method used to load a glyph within a given glyph slot. */
+ /* */
+ /* <Input> */
+ /* slot :: A handle to the target slot object where the glyph */
+ /* will be loaded. */
+ /* */
+ /* size :: A handle to the source face size at which the glyph */
+ /* must be scaled, loaded, etc. */
+ /* */
+ /* glyph_index :: The index of the glyph in the font file. */
+ /* */
+ /* load_flags :: A flag indicating what to load for this glyph. The */
+ /* FTLOAD_??? constants can be used to control the */
+ /* glyph loading process (e.g., whether the outline */
+ /* should be scaled, whether to load bitmaps or not, */
+ /* whether to hint the outline, etc). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Load_Glyph( FT_GlyphSlot ttslot, /* TT_GlyphSlot */
+ FT_Size ttsize, /* TT_Size */
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ TT_GlyphSlot slot = (TT_GlyphSlot)ttslot;
+ TT_Size size = (TT_Size)ttsize;
+ FT_Face face = ttslot->face;
+ FT_Error error;
+
+
+ if ( !slot )
+ return TT_Err_Invalid_Slot_Handle;
+
+ if ( !size )
+ return TT_Err_Invalid_Size_Handle;
+
+ if ( !face || glyph_index >= (FT_UInt)face->num_glyphs )
+ return TT_Err_Invalid_Argument;
+
+ if ( load_flags & ( FT_LOAD_NO_RECURSE | FT_LOAD_NO_SCALE ) )
+ {
+ load_flags |= FT_LOAD_NO_HINTING |
+ FT_LOAD_NO_BITMAP |
+ FT_LOAD_NO_SCALE;
+ }
+
+ /* now load the glyph outline if necessary */
+ error = TT_Load_Glyph( size, slot, glyph_index, load_flags );
+
+ /* force drop-out mode to 2 - irrelevant now */
+ /* slot->outline.dropout_mode = 2; */
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /**** ****/
+ /**** ****/
+ /**** D R I V E R I N T E R F A C E ****/
+ /**** ****/
+ /**** ****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ static const FT_Service_MultiMastersRec tt_service_gx_multi_masters =
+ {
+ (FT_Get_MM_Func) NULL,
+ (FT_Set_MM_Design_Func) NULL,
+ (FT_Set_MM_Blend_Func) TT_Set_MM_Blend,
+ (FT_Get_MM_Var_Func) TT_Get_MM_Var,
+ (FT_Set_Var_Design_Func)TT_Set_Var_Design
+ };
+#endif
+
+ static const FT_Service_TrueTypeEngineRec tt_service_truetype_engine =
+ {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ FT_TRUETYPE_ENGINE_TYPE_UNPATENTED
+#else
+ FT_TRUETYPE_ENGINE_TYPE_PATENTED
+#endif
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+ FT_TRUETYPE_ENGINE_TYPE_NONE
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+ };
+
+ static const FT_Service_TTGlyfRec tt_service_truetype_glyf =
+ {
+ (TT_Glyf_GetLocationFunc)tt_face_get_location
+ };
+
+ static const FT_ServiceDescRec tt_services[] =
+ {
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_TRUETYPE },
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ { FT_SERVICE_ID_MULTI_MASTERS, &tt_service_gx_multi_masters },
+#endif
+ { FT_SERVICE_ID_TRUETYPE_ENGINE, &tt_service_truetype_engine },
+ { FT_SERVICE_ID_TT_GLYF, &tt_service_truetype_glyf },
+ { NULL, NULL }
+ };
+
+
+ FT_CALLBACK_DEF( FT_Module_Interface )
+ tt_get_interface( FT_Module driver, /* TT_Driver */
+ const char* tt_interface )
+ {
+ FT_Module_Interface result;
+ FT_Module sfntd;
+ SFNT_Service sfnt;
+
+
+ result = ft_service_list_lookup( tt_services, tt_interface );
+ if ( result != NULL )
+ return result;
+
+ /* only return the default interface from the SFNT module */
+ sfntd = FT_Get_Module( driver->library, "sfnt" );
+ if ( sfntd )
+ {
+ sfnt = (SFNT_Service)( sfntd->clazz->module_interface );
+ if ( sfnt )
+ return sfnt->get_interface( driver, tt_interface );
+ }
+
+ return 0;
+ }
+
+
+ /* The FT_DriverInterface structure is defined in ftdriver.h. */
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec tt_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE |
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ FT_MODULE_DRIVER_HAS_HINTER,
+#else
+ 0,
+#endif
+
+ sizeof ( TT_DriverRec ),
+
+ "truetype", /* driver name */
+ 0x10000L, /* driver version == 1.0 */
+ 0x20000L, /* driver requires FreeType 2.0 or above */
+
+ (void*)0, /* driver specific interface */
+
+ tt_driver_init,
+ tt_driver_done,
+ tt_get_interface,
+ },
+
+ sizeof ( TT_FaceRec ),
+ sizeof ( TT_SizeRec ),
+ sizeof ( FT_GlyphSlotRec ),
+
+ tt_face_init,
+ tt_face_done,
+ tt_size_init,
+ tt_size_done,
+ tt_slot_init,
+ 0, /* FT_Slot_DoneFunc */
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ Load_Glyph,
+
+ tt_get_kerning,
+ 0, /* FT_Face_AttachFunc */
+ 0, /* FT_Face_GetAdvancesFunc */
+
+ tt_size_request,
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+ tt_size_select
+#else
+ 0 /* FT_Size_SelectFunc */
+#endif
+ };
+
+
+/* END */
diff --git a/src/truetype/ttdriver.h b/src/truetype/ttdriver.h
new file mode 100644
index 0000000..f6f26e4
--- /dev/null
+++ b/src/truetype/ttdriver.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* ttdriver.h */
+/* */
+/* High-level TrueType driver interface (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTDRIVER_H__
+#define __TTDRIVER_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) tt_driver_class;
+
+
+FT_END_HEADER
+
+#endif /* __TTDRIVER_H__ */
+
+
+/* END */
diff --git a/src/truetype/tterrors.h b/src/truetype/tterrors.h
new file mode 100644
index 0000000..d317c70
--- /dev/null
+++ b/src/truetype/tterrors.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* tterrors.h */
+/* */
+/* TrueType error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the TrueType error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __TTERRORS_H__
+#define __TTERRORS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX TT_Err_
+#define FT_ERR_BASE FT_Mod_Err_TrueType
+
+#include FT_ERRORS_H
+
+#endif /* __TTERRORS_H__ */
+
+/* END */
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
new file mode 100644
index 0000000..ae476a4
--- /dev/null
+++ b/src/truetype/ttgload.c
@@ -0,0 +1,1957 @@
+/***************************************************************************/
+/* */
+/* ttgload.c */
+/* */
+/* TrueType Glyph Loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_OUTLINE_H
+
+#include "ttgload.h"
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttgload
+
+
+ /*************************************************************************/
+ /* */
+ /* Composite font flags. */
+ /* */
+#define ARGS_ARE_WORDS 0x0001
+#define ARGS_ARE_XY_VALUES 0x0002
+#define ROUND_XY_TO_GRID 0x0004
+#define WE_HAVE_A_SCALE 0x0008
+/* reserved 0x0010 */
+#define MORE_COMPONENTS 0x0020
+#define WE_HAVE_AN_XY_SCALE 0x0040
+#define WE_HAVE_A_2X2 0x0080
+#define WE_HAVE_INSTR 0x0100
+#define USE_MY_METRICS 0x0200
+#define OVERLAP_COMPOUND 0x0400
+#define SCALED_COMPONENT_OFFSET 0x0800
+#define UNSCALED_COMPONENT_OFFSET 0x1000
+
+
+ /*************************************************************************/
+ /* */
+ /* Returns the horizontal metrics in font units for a given glyph. If */
+ /* `check' is true, take care of monospaced fonts by returning the */
+ /* advance width maximum. */
+ /* */
+ static void
+ Get_HMetrics( TT_Face face,
+ FT_UInt idx,
+ FT_Bool check,
+ FT_Short* lsb,
+ FT_UShort* aw )
+ {
+ ( (SFNT_Service)face->sfnt )->get_metrics( face, 0, idx, lsb, aw );
+
+ if ( check && face->postscript.isFixedPitch )
+ *aw = face->horizontal.advance_Width_Max;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Returns the vertical metrics in font units for a given glyph. */
+ /* Greg Hitchcock from Microsoft told us that if there were no `vmtx' */
+ /* table, typoAscender/Descender from the `OS/2' table would be used */
+ /* instead, and if there were no `OS/2' table, use ascender/descender */
+ /* from the `hhea' table. But that is not what Microsoft's rasterizer */
+ /* apparently does: It uses the ppem value as the advance height, and */
+ /* sets the top side bearing to be zero. */
+ /* */
+ /* The monospace `check' is probably not meaningful here, but we leave */
+ /* it in for a consistent interface. */
+ /* */
+ static void
+ Get_VMetrics( TT_Face face,
+ FT_UInt idx,
+ FT_Bool check,
+ FT_Short* tsb,
+ FT_UShort* ah )
+ {
+ FT_UNUSED( check );
+
+ if ( face->vertical_info )
+ ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, idx, tsb, ah );
+
+#if 1 /* Empirically determined, at variance with what MS said */
+
+ else
+ {
+ *tsb = 0;
+ *ah = face->root.units_per_EM;
+ }
+
+#else /* This is what MS said to do. It isn't what they do, however. */
+
+ else if ( face->os2.version != 0xFFFFU )
+ {
+ *tsb = face->os2.sTypoAscender;
+ *ah = face->os2.sTypoAscender - face->os2.sTypoDescender;
+ }
+ else
+ {
+ *tsb = face->horizontal.Ascender;
+ *ah = face->horizontal.Ascender - face->horizontal.Descender;
+ }
+
+#endif
+
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Translates an array of coordinates. */
+ /* */
+ static void
+ translate_array( FT_UInt n,
+ FT_Vector* coords,
+ FT_Pos delta_x,
+ FT_Pos delta_y )
+ {
+ FT_UInt k;
+
+
+ if ( delta_x )
+ for ( k = 0; k < n; k++ )
+ coords[k].x += delta_x;
+
+ if ( delta_y )
+ for ( k = 0; k < n; k++ )
+ coords[k].y += delta_y;
+ }
+
+
+#undef IS_HINTED
+#define IS_HINTED( flags ) ( ( flags & FT_LOAD_NO_HINTING ) == 0 )
+
+
+ /*************************************************************************/
+ /* */
+ /* The following functions are used by default with TrueType fonts. */
+ /* However, they can be replaced by alternatives if we need to support */
+ /* TrueType-compressed formats (like MicroType) in the future. */
+ /* */
+ /*************************************************************************/
+
+ FT_CALLBACK_DEF( FT_Error )
+ TT_Access_Glyph_Frame( TT_Loader loader,
+ FT_UInt glyph_index,
+ FT_ULong offset,
+ FT_UInt byte_count )
+ {
+ FT_Error error;
+ FT_Stream stream = loader->stream;
+
+ /* for non-debug mode */
+ FT_UNUSED( glyph_index );
+
+
+ FT_TRACE5(( "Glyph %ld\n", glyph_index ));
+
+ /* the following line sets the `error' variable through macros! */
+ if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( byte_count ) )
+ return error;
+
+ loader->cursor = stream->cursor;
+ loader->limit = stream->limit;
+
+ return TT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ TT_Forget_Glyph_Frame( TT_Loader loader )
+ {
+ FT_Stream stream = loader->stream;
+
+
+ FT_FRAME_EXIT();
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ TT_Load_Glyph_Header( TT_Loader loader )
+ {
+ FT_Byte* p = loader->cursor;
+ FT_Byte* limit = loader->limit;
+
+
+ if ( p + 10 > limit )
+ return TT_Err_Invalid_Outline;
+
+ loader->n_contours = FT_NEXT_SHORT( p );
+
+ loader->bbox.xMin = FT_NEXT_SHORT( p );
+ loader->bbox.yMin = FT_NEXT_SHORT( p );
+ loader->bbox.xMax = FT_NEXT_SHORT( p );
+ loader->bbox.yMax = FT_NEXT_SHORT( p );
+
+ FT_TRACE5(( " # of contours: %d\n", loader->n_contours ));
+ FT_TRACE5(( " xMin: %4d xMax: %4d\n", loader->bbox.xMin,
+ loader->bbox.xMax ));
+ FT_TRACE5(( " yMin: %4d yMax: %4d\n", loader->bbox.yMin,
+ loader->bbox.yMax ));
+ loader->cursor = p;
+
+ return TT_Err_Ok;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ TT_Load_Simple_Glyph( TT_Loader load )
+ {
+ FT_Error error;
+ FT_Byte* p = load->cursor;
+ FT_Byte* limit = load->limit;
+ FT_GlyphLoader gloader = load->gloader;
+ FT_Int n_contours = load->n_contours;
+ FT_Outline* outline;
+ TT_Face face = (TT_Face)load->face;
+ FT_UShort n_ins;
+ FT_Int n_points;
+
+ FT_Byte *flag, *flag_limit;
+ FT_Byte c, count;
+ FT_Vector *vec, *vec_limit;
+ FT_Pos x;
+ FT_Short *cont, *cont_limit, prev_cont;
+ FT_Int xy_size = 0;
+
+
+ /* check that we can add the contours to the glyph */
+ error = FT_GLYPHLOADER_CHECK_POINTS( gloader, 0, n_contours );
+ if ( error )
+ goto Fail;
+
+ /* reading the contours' endpoints & number of points */
+ cont = gloader->current.outline.contours;
+ cont_limit = cont + n_contours;
+
+ /* check space for contours array + instructions count */
+ if ( n_contours >= 0xFFF || p + ( n_contours + 1 ) * 2 > limit )
+ goto Invalid_Outline;
+
+ cont[0] = prev_cont = FT_NEXT_USHORT( p );
+ for ( cont++; cont < cont_limit; cont++ )
+ {
+ cont[0] = FT_NEXT_USHORT( p );
+ if ( cont[0] <= prev_cont )
+ {
+ /* unordered contours: this is invalid */
+ error = FT_Err_Invalid_Table;
+ goto Fail;
+ }
+ prev_cont = cont[0];
+ }
+
+ n_points = 0;
+ if ( n_contours > 0 )
+ {
+ n_points = cont[-1] + 1;
+ if ( n_points < 0 )
+ goto Invalid_Outline;
+ }
+
+ /* note that we will add four phantom points later */
+ error = FT_GLYPHLOADER_CHECK_POINTS( gloader, n_points + 4, 0 );
+ if ( error )
+ goto Fail;
+
+ /* we'd better check the contours table right now */
+ outline = &gloader->current.outline;
+
+ for ( cont = outline->contours + 1; cont < cont_limit; cont++ )
+ if ( cont[-1] >= cont[0] )
+ goto Invalid_Outline;
+
+ /* reading the bytecode instructions */
+ load->glyph->control_len = 0;
+ load->glyph->control_data = 0;
+
+ if ( p + 2 > limit )
+ goto Invalid_Outline;
+
+ n_ins = FT_NEXT_USHORT( p );
+
+ FT_TRACE5(( " Instructions size: %u\n", n_ins ));
+
+ if ( n_ins > face->max_profile.maxSizeOfInstructions )
+ {
+ FT_TRACE0(( "TT_Load_Simple_Glyph: Too many instructions (%d)\n",
+ n_ins ));
+ error = TT_Err_Too_Many_Hints;
+ goto Fail;
+ }
+
+ if ( ( limit - p ) < n_ins )
+ {
+ FT_TRACE0(( "TT_Load_Simple_Glyph: Instruction count mismatch!\n" ));
+ error = TT_Err_Too_Many_Hints;
+ goto Fail;
+ }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ if ( IS_HINTED( load->load_flags ) )
+ {
+ load->glyph->control_len = n_ins;
+ load->glyph->control_data = load->exec->glyphIns;
+
+ FT_MEM_COPY( load->exec->glyphIns, p, (FT_Long)n_ins );
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+ p += n_ins;
+
+ /* reading the point tags */
+ flag = (FT_Byte*)outline->tags;
+ flag_limit = flag + n_points;
+
+ FT_ASSERT( flag != NULL );
+
+ while ( flag < flag_limit )
+ {
+ if ( p + 1 > limit )
+ goto Invalid_Outline;
+
+ *flag++ = c = FT_NEXT_BYTE( p );
+ if ( c & 8 )
+ {
+ if ( p + 1 > limit )
+ goto Invalid_Outline;
+
+ count = FT_NEXT_BYTE( p );
+ if ( flag + (FT_Int)count > flag_limit )
+ goto Invalid_Outline;
+
+ for ( ; count > 0; count-- )
+ *flag++ = c;
+ }
+ }
+
+ /* reading the X coordinates */
+
+ vec = outline->points;
+ vec_limit = vec + n_points;
+ flag = (FT_Byte*)outline->tags;
+ x = 0;
+
+ if ( p + xy_size > limit )
+ goto Invalid_Outline;
+
+ for ( ; vec < vec_limit; vec++, flag++ )
+ {
+ FT_Pos y = 0;
+ FT_Byte f = *flag;
+
+
+ if ( f & 2 )
+ {
+ if ( p + 1 > limit )
+ goto Invalid_Outline;
+
+ y = (FT_Pos)FT_NEXT_BYTE( p );
+ if ( ( f & 16 ) == 0 )
+ y = -y;
+ }
+ else if ( ( f & 16 ) == 0 )
+ {
+ if ( p + 2 > limit )
+ goto Invalid_Outline;
+
+ y = (FT_Pos)FT_NEXT_SHORT( p );
+ }
+
+ x += y;
+ vec->x = x;
+ *flag = f & ~( 2 | 16 );
+ }
+
+ /* reading the Y coordinates */
+
+ vec = gloader->current.outline.points;
+ vec_limit = vec + n_points;
+ flag = (FT_Byte*)outline->tags;
+ x = 0;
+
+ for ( ; vec < vec_limit; vec++, flag++ )
+ {
+ FT_Pos y = 0;
+ FT_Byte f = *flag;
+
+
+ if ( f & 4 )
+ {
+ if ( p + 1 > limit )
+ goto Invalid_Outline;
+
+ y = (FT_Pos)FT_NEXT_BYTE( p );
+ if ( ( f & 32 ) == 0 )
+ y = -y;
+ }
+ else if ( ( f & 32 ) == 0 )
+ {
+ if ( p + 2 > limit )
+ goto Invalid_Outline;
+
+ y = (FT_Pos)FT_NEXT_SHORT( p );
+ }
+
+ x += y;
+ vec->y = x;
+ *flag = f & FT_CURVE_TAG_ON;
+ }
+
+ outline->n_points = (FT_UShort)n_points;
+ outline->n_contours = (FT_Short) n_contours;
+
+ load->cursor = p;
+
+ Fail:
+ return error;
+
+ Invalid_Outline:
+ error = TT_Err_Invalid_Outline;
+ goto Fail;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ TT_Load_Composite_Glyph( TT_Loader loader )
+ {
+ FT_Error error;
+ FT_Byte* p = loader->cursor;
+ FT_Byte* limit = loader->limit;
+ FT_GlyphLoader gloader = loader->gloader;
+ FT_SubGlyph subglyph;
+ FT_UInt num_subglyphs;
+
+
+ num_subglyphs = 0;
+
+ do
+ {
+ FT_Fixed xx, xy, yy, yx;
+ FT_UInt count;
+
+
+ /* check that we can load a new subglyph */
+ error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs + 1 );
+ if ( error )
+ goto Fail;
+
+ /* check space */
+ if ( p + 4 > limit )
+ goto Invalid_Composite;
+
+ subglyph = gloader->current.subglyphs + num_subglyphs;
+
+ subglyph->arg1 = subglyph->arg2 = 0;
+
+ subglyph->flags = FT_NEXT_USHORT( p );
+ subglyph->index = FT_NEXT_USHORT( p );
+
+ /* check space */
+ count = 2;
+ if ( subglyph->flags & ARGS_ARE_WORDS )
+ count += 2;
+ if ( subglyph->flags & WE_HAVE_A_SCALE )
+ count += 2;
+ else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+ count += 4;
+ else if ( subglyph->flags & WE_HAVE_A_2X2 )
+ count += 8;
+
+ if ( p + count > limit )
+ goto Invalid_Composite;
+
+ /* read arguments */
+ if ( subglyph->flags & ARGS_ARE_WORDS )
+ {
+ subglyph->arg1 = FT_NEXT_SHORT( p );
+ subglyph->arg2 = FT_NEXT_SHORT( p );
+ }
+ else
+ {
+ subglyph->arg1 = FT_NEXT_CHAR( p );
+ subglyph->arg2 = FT_NEXT_CHAR( p );
+ }
+
+ /* read transform */
+ xx = yy = 0x10000L;
+ xy = yx = 0;
+
+ if ( subglyph->flags & WE_HAVE_A_SCALE )
+ {
+ xx = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ yy = xx;
+ }
+ else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+ {
+ xx = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ yy = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ }
+ else if ( subglyph->flags & WE_HAVE_A_2X2 )
+ {
+ xx = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ yx = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ xy = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ yy = (FT_Fixed)FT_NEXT_SHORT( p ) << 2;
+ }
+
+ subglyph->transform.xx = xx;
+ subglyph->transform.xy = xy;
+ subglyph->transform.yx = yx;
+ subglyph->transform.yy = yy;
+
+ num_subglyphs++;
+
+ } while ( subglyph->flags & MORE_COMPONENTS );
+
+ gloader->current.num_subglyphs = num_subglyphs;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ {
+ FT_Stream stream = loader->stream;
+
+
+ /* we must undo the FT_FRAME_ENTER in order to point to the */
+ /* composite instructions, if we find some. */
+ /* we will process them later... */
+ /* */
+ loader->ins_pos = (FT_ULong)( FT_STREAM_POS() +
+ p - limit );
+ }
+
+#endif
+
+ loader->cursor = p;
+
+ Fail:
+ return error;
+
+ Invalid_Composite:
+ error = TT_Err_Invalid_Composite;
+ goto Fail;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ TT_Init_Glyph_Loading( TT_Face face )
+ {
+ face->access_glyph_frame = TT_Access_Glyph_Frame;
+ face->read_glyph_header = TT_Load_Glyph_Header;
+ face->read_simple_glyph = TT_Load_Simple_Glyph;
+ face->read_composite_glyph = TT_Load_Composite_Glyph;
+ face->forget_glyph_frame = TT_Forget_Glyph_Frame;
+ }
+
+
+ static void
+ tt_prepare_zone( TT_GlyphZone zone,
+ FT_GlyphLoad load,
+ FT_UInt start_point,
+ FT_UInt start_contour )
+ {
+ zone->n_points = (FT_UShort)( load->outline.n_points - start_point );
+ zone->n_contours = (FT_Short) ( load->outline.n_contours -
+ start_contour );
+ zone->org = load->extra_points + start_point;
+ zone->cur = load->outline.points + start_point;
+ zone->orus = load->extra_points2 + start_point;
+ zone->tags = (FT_Byte*)load->outline.tags + start_point;
+ zone->contours = (FT_UShort*)load->outline.contours + start_contour;
+ zone->first_point = (FT_UShort)start_point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Hint_Glyph */
+ /* */
+ /* <Description> */
+ /* Hint the glyph using the zone prepared by the caller. Note that */
+ /* the zone is supposed to include four phantom points. */
+ /* */
+ static FT_Error
+ TT_Hint_Glyph( TT_Loader loader,
+ FT_Bool is_composite )
+ {
+ TT_GlyphZone zone = &loader->zone;
+ FT_Pos origin;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ FT_UInt n_ins;
+#else
+ FT_UNUSED( is_composite );
+#endif
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ n_ins = loader->glyph->control_len;
+#endif
+
+ origin = zone->cur[zone->n_points - 4].x;
+ origin = FT_PIX_ROUND( origin ) - origin;
+ if ( origin )
+ translate_array( zone->n_points, zone->cur, origin, 0 );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ /* save original point position in org */
+ if ( n_ins > 0 )
+ FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points );
+#endif
+
+ /* round pp2 and pp4 */
+ zone->cur[zone->n_points - 3].x =
+ FT_PIX_ROUND( zone->cur[zone->n_points - 3].x );
+ zone->cur[zone->n_points - 1].y =
+ FT_PIX_ROUND( zone->cur[zone->n_points - 1].y );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ if ( n_ins > 0 )
+ {
+ FT_Bool debug;
+ FT_Error error;
+
+
+ error = TT_Set_CodeRange( loader->exec, tt_coderange_glyph,
+ loader->exec->glyphIns, n_ins );
+ if ( error )
+ return error;
+
+ loader->exec->is_composite = is_composite;
+ loader->exec->pts = *zone;
+
+ debug = FT_BOOL( !( loader->load_flags & FT_LOAD_NO_SCALE ) &&
+ ((TT_Size)loader->size)->debug );
+
+ error = TT_Run_Context( loader->exec, debug );
+ if ( error && loader->exec->pedantic_hinting )
+ return error;
+ }
+
+#endif
+
+ /* save glyph phantom points */
+ if ( !loader->preserve_pps )
+ {
+ loader->pp1 = zone->cur[zone->n_points - 4];
+ loader->pp2 = zone->cur[zone->n_points - 3];
+ loader->pp3 = zone->cur[zone->n_points - 2];
+ loader->pp4 = zone->cur[zone->n_points - 1];
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Process_Simple_Glyph */
+ /* */
+ /* <Description> */
+ /* Once a simple glyph has been loaded, it needs to be processed. */
+ /* Usually, this means scaling and hinting through bytecode */
+ /* interpretation. */
+ /* */
+ static FT_Error
+ TT_Process_Simple_Glyph( TT_Loader loader )
+ {
+ FT_GlyphLoader gloader = loader->gloader;
+ FT_Error error = TT_Err_Ok;
+ FT_Outline* outline;
+ FT_UInt n_points;
+
+
+ outline = &gloader->current.outline;
+ n_points = outline->n_points;
+
+ /* set phantom points */
+
+ outline->points[n_points ] = loader->pp1;
+ outline->points[n_points + 1] = loader->pp2;
+ outline->points[n_points + 2] = loader->pp3;
+ outline->points[n_points + 3] = loader->pp4;
+
+ outline->tags[n_points ] = 0;
+ outline->tags[n_points + 1] = 0;
+ outline->tags[n_points + 2] = 0;
+ outline->tags[n_points + 3] = 0;
+
+ n_points += 4;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ if ( ((TT_Face)loader->face)->doblend )
+ {
+ /* Deltas apply to the unscaled data. */
+ FT_Vector* deltas;
+ FT_Memory memory = loader->face->memory;
+ FT_UInt i;
+
+
+ error = TT_Vary_Get_Glyph_Deltas( (TT_Face)(loader->face),
+ loader->glyph_index,
+ &deltas,
+ n_points );
+ if ( error )
+ return error;
+
+ for ( i = 0; i < n_points; ++i )
+ {
+ outline->points[i].x += deltas[i].x;
+ outline->points[i].y += deltas[i].y;
+ }
+
+ FT_FREE( deltas );
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+ if ( IS_HINTED( loader->load_flags ) )
+ {
+ tt_prepare_zone( &loader->zone, &gloader->current, 0, 0 );
+
+ FT_ARRAY_COPY( loader->zone.orus, loader->zone.cur,
+ loader->zone.n_points + 4 );
+ }
+
+ /* scale the glyph */
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ FT_Vector* vec = outline->points;
+ FT_Vector* limit = outline->points + n_points;
+ FT_Fixed x_scale = ((TT_Size)loader->size)->metrics.x_scale;
+ FT_Fixed y_scale = ((TT_Size)loader->size)->metrics.y_scale;
+
+
+ for ( ; vec < limit; vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, x_scale );
+ vec->y = FT_MulFix( vec->y, y_scale );
+ }
+
+ loader->pp1 = outline->points[n_points - 4];
+ loader->pp2 = outline->points[n_points - 3];
+ loader->pp3 = outline->points[n_points - 2];
+ loader->pp4 = outline->points[n_points - 1];
+ }
+
+ if ( IS_HINTED( loader->load_flags ) )
+ {
+ loader->zone.n_points += 4;
+
+ error = TT_Hint_Glyph( loader, 0 );
+ }
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Process_Composite_Component */
+ /* */
+ /* <Description> */
+ /* Once a composite component has been loaded, it needs to be */
+ /* processed. Usually, this means transforming and translating. */
+ /* */
+ static FT_Error
+ TT_Process_Composite_Component( TT_Loader loader,
+ FT_SubGlyph subglyph,
+ FT_UInt start_point,
+ FT_UInt num_base_points )
+ {
+ FT_GlyphLoader gloader = loader->gloader;
+ FT_Vector* base_vec = gloader->base.outline.points;
+ FT_UInt num_points = gloader->base.outline.n_points;
+ FT_Bool have_scale;
+ FT_Pos x, y;
+
+
+ have_scale = FT_BOOL( subglyph->flags & ( WE_HAVE_A_SCALE |
+ WE_HAVE_AN_XY_SCALE |
+ WE_HAVE_A_2X2 ) );
+
+ /* perform the transform required for this subglyph */
+ if ( have_scale )
+ {
+ FT_UInt i;
+
+
+ for ( i = num_base_points; i < num_points; i++ )
+ FT_Vector_Transform( base_vec + i, &subglyph->transform );
+ }
+
+ /* get offset */
+ if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) )
+ {
+ FT_UInt k = subglyph->arg1;
+ FT_UInt l = subglyph->arg2;
+ FT_Vector* p1;
+ FT_Vector* p2;
+
+
+ /* match l-th point of the newly loaded component to the k-th point */
+ /* of the previously loaded components. */
+
+ /* change to the point numbers used by our outline */
+ k += start_point;
+ l += num_base_points;
+ if ( k >= num_base_points ||
+ l >= num_points )
+ return TT_Err_Invalid_Composite;
+
+ p1 = gloader->base.outline.points + k;
+ p2 = gloader->base.outline.points + l;
+
+ x = p1->x - p2->x;
+ y = p1->y - p2->y;
+ }
+ else
+ {
+ x = subglyph->arg1;
+ y = subglyph->arg2;
+
+ if ( !x && !y )
+ return TT_Err_Ok;
+
+ /* Use a default value dependent on */
+ /* TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED. This is useful for old TT */
+ /* fonts which don't set the xxx_COMPONENT_OFFSET bit. */
+
+ if ( have_scale &&
+#ifdef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
+ !( subglyph->flags & UNSCALED_COMPONENT_OFFSET ) )
+#else
+ ( subglyph->flags & SCALED_COMPONENT_OFFSET ) )
+#endif
+ {
+
+#if 0
+
+ /*************************************************************************/
+ /* */
+ /* This algorithm is what Apple documents. But it doesn't work. */
+ /* */
+ int a = subglyph->transform.xx > 0 ? subglyph->transform.xx
+ : -subglyph->transform.xx;
+ int b = subglyph->transform.yx > 0 ? subglyph->transform.yx
+ : -subglyph->transform.yx;
+ int c = subglyph->transform.xy > 0 ? subglyph->transform.xy
+ : -subglyph->transform.xy;
+ int d = subglyph->transform.yy > 0 ? subglyph->transform.yy
+ : -subglyph->transform.yy;
+ int m = a > b ? a : b;
+ int n = c > d ? c : d;
+
+
+ if ( a - b <= 33 && a - b >= -33 )
+ m *= 2;
+ if ( c - d <= 33 && c - d >= -33 )
+ n *= 2;
+ x = FT_MulFix( x, m );
+ y = FT_MulFix( y, n );
+
+#else /* 0 */
+
+ /*************************************************************************/
+ /* */
+ /* This algorithm is a guess and works much better than the above. */
+ /* */
+ FT_Fixed mac_xscale = FT_SqrtFixed(
+ FT_MulFix( subglyph->transform.xx,
+ subglyph->transform.xx ) +
+ FT_MulFix( subglyph->transform.xy,
+ subglyph->transform.xy ) );
+ FT_Fixed mac_yscale = FT_SqrtFixed(
+ FT_MulFix( subglyph->transform.yy,
+ subglyph->transform.yy ) +
+ FT_MulFix( subglyph->transform.yx,
+ subglyph->transform.yx ) );
+
+
+ x = FT_MulFix( x, mac_xscale );
+ y = FT_MulFix( y, mac_yscale );
+
+#endif /* 0 */
+
+ }
+
+ if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
+ {
+ FT_Fixed x_scale = ((TT_Size)loader->size)->metrics.x_scale;
+ FT_Fixed y_scale = ((TT_Size)loader->size)->metrics.y_scale;
+
+
+ x = FT_MulFix( x, x_scale );
+ y = FT_MulFix( y, y_scale );
+
+ if ( subglyph->flags & ROUND_XY_TO_GRID )
+ {
+ x = FT_PIX_ROUND( x );
+ y = FT_PIX_ROUND( y );
+ }
+ }
+ }
+
+ if ( x || y )
+ translate_array( num_points - num_base_points,
+ base_vec + num_base_points,
+ x, y );
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Process_Composite_Glyph */
+ /* */
+ /* <Description> */
+ /* This is slightly different from TT_Process_Simple_Glyph, in that */
+ /* its sole purpose is to hint the glyph. Thus this function is */
+ /* only available when bytecode interpreter is enabled. */
+ /* */
+ static FT_Error
+ TT_Process_Composite_Glyph( TT_Loader loader,
+ FT_UInt start_point,
+ FT_UInt start_contour )
+ {
+ FT_Error error;
+ FT_Outline* outline;
+ FT_UInt i;
+
+
+ outline = &loader->gloader->base.outline;
+
+ /* make room for phantom points */
+ error = FT_GLYPHLOADER_CHECK_POINTS( loader->gloader,
+ outline->n_points + 4,
+ 0 );
+ if ( error )
+ return error;
+
+ outline->points[outline->n_points ] = loader->pp1;
+ outline->points[outline->n_points + 1] = loader->pp2;
+ outline->points[outline->n_points + 2] = loader->pp3;
+ outline->points[outline->n_points + 3] = loader->pp4;
+
+ outline->tags[outline->n_points ] = 0;
+ outline->tags[outline->n_points + 1] = 0;
+ outline->tags[outline->n_points + 2] = 0;
+ outline->tags[outline->n_points + 3] = 0;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ {
+ FT_Stream stream = loader->stream;
+ FT_UShort n_ins;
+
+
+ /* TT_Load_Composite_Glyph only gives us the offset of instructions */
+ /* so we read them here */
+ if ( FT_STREAM_SEEK( loader->ins_pos ) ||
+ FT_READ_USHORT( n_ins ) )
+ return error;
+
+ FT_TRACE5(( " Instructions size = %d\n", n_ins ));
+
+ /* check it */
+ if ( n_ins > ((TT_Face)loader->face)->max_profile.maxSizeOfInstructions )
+ {
+ FT_TRACE0(( "TT_Process_Composite_Glyph: Too many instructions (%d)\n",
+ n_ins ));
+
+ return TT_Err_Too_Many_Hints;
+ }
+ else if ( n_ins == 0 )
+ return TT_Err_Ok;
+
+ if ( FT_STREAM_READ( loader->exec->glyphIns, n_ins ) )
+ return error;
+
+ loader->glyph->control_data = loader->exec->glyphIns;
+ loader->glyph->control_len = n_ins;
+ }
+
+#endif
+
+ tt_prepare_zone( &loader->zone, &loader->gloader->base,
+ start_point, start_contour );
+
+ /* Some points are likely touched during execution of */
+ /* instructions on components. So let's untouch them. */
+ for ( i = start_point; i < loader->zone.n_points; i++ )
+ loader->zone.tags[i] &= ~( FT_CURVE_TAG_TOUCH_X |
+ FT_CURVE_TAG_TOUCH_Y );
+
+ loader->zone.n_points += 4;
+
+ return TT_Hint_Glyph( loader, 1 );
+ }
+
+
+ /* Calculate the four phantom points. */
+ /* The first two stand for horizontal origin and advance. */
+ /* The last two stand for vertical origin and advance. */
+#define TT_LOADER_SET_PP( loader ) \
+ do { \
+ (loader)->pp1.x = (loader)->bbox.xMin - (loader)->left_bearing; \
+ (loader)->pp1.y = 0; \
+ (loader)->pp2.x = (loader)->pp1.x + (loader)->advance; \
+ (loader)->pp2.y = 0; \
+ (loader)->pp3.x = 0; \
+ (loader)->pp3.y = (loader)->top_bearing + (loader)->bbox.yMax; \
+ (loader)->pp4.x = 0; \
+ (loader)->pp4.y = (loader)->pp3.y - (loader)->vadvance; \
+ } while ( 0 )
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* load_truetype_glyph */
+ /* */
+ /* <Description> */
+ /* Loads a given truetype glyph. Handles composites and uses a */
+ /* TT_Loader object. */
+ /* */
+ static FT_Error
+ load_truetype_glyph( TT_Loader loader,
+ FT_UInt glyph_index,
+ FT_UInt recurse_count )
+ {
+ FT_Error error;
+ FT_Fixed x_scale, y_scale;
+ FT_ULong offset;
+ TT_Face face = (TT_Face)loader->face;
+ FT_GlyphLoader gloader = loader->gloader;
+ FT_Bool opened_frame = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ FT_Vector* deltas = NULL;
+#endif
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ FT_StreamRec inc_stream;
+ FT_Data glyph_data;
+ FT_Bool glyph_data_loaded = 0;
+#endif
+
+
+ if ( recurse_count > face->max_profile.maxComponentDepth )
+ {
+ error = TT_Err_Invalid_Composite;
+ goto Exit;
+ }
+
+ /* check glyph index */
+ if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
+ {
+ error = TT_Err_Invalid_Glyph_Index;
+ goto Exit;
+ }
+
+ loader->glyph_index = glyph_index;
+
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ x_scale = ((TT_Size)loader->size)->metrics.x_scale;
+ y_scale = ((TT_Size)loader->size)->metrics.y_scale;
+ }
+ else
+ {
+ x_scale = 0x10000L;
+ y_scale = 0x10000L;
+ }
+
+ /* get metrics, horizontal and vertical */
+ {
+ FT_Short left_bearing = 0, top_bearing = 0;
+ FT_UShort advance_width = 0, advance_height = 0;
+
+
+ Get_HMetrics( face, glyph_index,
+ (FT_Bool)!( loader->load_flags &
+ FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ),
+ &left_bearing,
+ &advance_width );
+ Get_VMetrics( face, glyph_index,
+ (FT_Bool)!( loader->load_flags &
+ FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ),
+ &top_bearing,
+ &advance_height );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* If this is an incrementally loaded font see if there are */
+ /* overriding metrics for this glyph. */
+ if ( face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->get_glyph_metrics )
+ {
+ FT_Incremental_MetricsRec metrics;
+
+
+ metrics.bearing_x = left_bearing;
+ metrics.bearing_y = 0;
+ metrics.advance = advance_width;
+ error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+ face->root.internal->incremental_interface->object,
+ glyph_index, FALSE, &metrics );
+ if ( error )
+ goto Exit;
+ left_bearing = (FT_Short)metrics.bearing_x;
+ advance_width = (FT_UShort)metrics.advance;
+
+#if 0
+
+ /* GWW: Do I do the same for vertical metrics? */
+ metrics.bearing_x = 0;
+ metrics.bearing_y = top_bearing;
+ metrics.advance = advance_height;
+ error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+ face->root.internal->incremental_interface->object,
+ glyph_index, TRUE, &metrics );
+ if ( error )
+ goto Exit;
+ top_bearing = (FT_Short)metrics.bearing_y;
+ advance_height = (FT_UShort)metrics.advance;
+
+#endif /* 0 */
+
+ }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ loader->left_bearing = left_bearing;
+ loader->advance = advance_width;
+ loader->top_bearing = top_bearing;
+ loader->vadvance = advance_height;
+
+ if ( !loader->linear_def )
+ {
+ loader->linear_def = 1;
+ loader->linear = advance_width;
+ }
+ }
+
+ /* Set `offset' to the start of the glyph relative to the start of */
+ /* the `glyf' table, and `byte_len' to the length of the glyph in */
+ /* bytes. */
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* If we are loading glyph data via the incremental interface, set */
+ /* the loader stream to a memory stream reading the data returned */
+ /* by the interface. */
+ if ( face->root.internal->incremental_interface )
+ {
+ error = face->root.internal->incremental_interface->funcs->get_glyph_data(
+ face->root.internal->incremental_interface->object,
+ glyph_index, &glyph_data );
+ if ( error )
+ goto Exit;
+
+ glyph_data_loaded = 1;
+ offset = 0;
+ loader->byte_len = glyph_data.length;
+
+ FT_MEM_ZERO( &inc_stream, sizeof ( inc_stream ) );
+ FT_Stream_OpenMemory( &inc_stream,
+ glyph_data.pointer, glyph_data.length );
+
+ loader->stream = &inc_stream;
+ }
+ else
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ offset = tt_face_get_location( face, glyph_index,
+ (FT_UInt*)&loader->byte_len );
+
+ if ( loader->byte_len == 0 )
+ {
+ /* as described by Frederic Loyer, these are spaces or */
+ /* the unknown glyph. */
+ loader->bbox.xMin = 0;
+ loader->bbox.xMax = 0;
+ loader->bbox.yMin = 0;
+ loader->bbox.yMax = 0;
+
+ TT_LOADER_SET_PP( loader );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ if ( ((TT_Face)(loader->face))->doblend )
+ {
+ /* this must be done before scaling */
+ FT_Memory memory = loader->face->memory;
+
+
+ error = TT_Vary_Get_Glyph_Deltas( (TT_Face)(loader->face),
+ glyph_index, &deltas, 4 );
+ if ( error )
+ goto Exit;
+
+ loader->pp1.x += deltas[0].x; loader->pp1.y += deltas[0].y;
+ loader->pp2.x += deltas[1].x; loader->pp2.y += deltas[1].y;
+ loader->pp3.x += deltas[2].x; loader->pp3.y += deltas[2].y;
+ loader->pp4.x += deltas[3].x; loader->pp4.y += deltas[3].y;
+
+ FT_FREE( deltas );
+ }
+
+#endif
+
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
+ loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
+ loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
+ loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
+ }
+
+ error = TT_Err_Ok;
+ goto Exit;
+ }
+
+ error = face->access_glyph_frame( loader, glyph_index,
+ loader->glyf_offset + offset,
+ loader->byte_len );
+ if ( error )
+ goto Exit;
+
+ opened_frame = 1;
+
+ /* read first glyph header */
+ error = face->read_glyph_header( loader );
+ if ( error )
+ goto Exit;
+
+ TT_LOADER_SET_PP( loader );
+
+ /***********************************************************************/
+ /***********************************************************************/
+ /***********************************************************************/
+
+ /* if it is a simple glyph, load it */
+
+ if ( loader->n_contours >= 0 )
+ {
+ error = face->read_simple_glyph( loader );
+ if ( error )
+ goto Exit;
+
+ /* all data have been read */
+ face->forget_glyph_frame( loader );
+ opened_frame = 0;
+
+ error = TT_Process_Simple_Glyph( loader );
+ if ( error )
+ goto Exit;
+
+ FT_GlyphLoader_Add( gloader );
+ }
+
+ /***********************************************************************/
+ /***********************************************************************/
+ /***********************************************************************/
+
+ /* otherwise, load a composite! */
+ else if ( loader->n_contours == -1 )
+ {
+ FT_UInt start_point;
+ FT_UInt start_contour;
+ FT_ULong ins_pos; /* position of composite instructions, if any */
+
+
+ start_point = gloader->base.outline.n_points;
+ start_contour = gloader->base.outline.n_contours;
+
+ /* for each subglyph, read composite header */
+ error = face->read_composite_glyph( loader );
+ if ( error )
+ goto Exit;
+
+ /* store the offset of instructions */
+ ins_pos = loader->ins_pos;
+
+ /* all data we need are read */
+ face->forget_glyph_frame( loader );
+ opened_frame = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+ if ( face->doblend )
+ {
+ FT_Int i, limit;
+ FT_SubGlyph subglyph;
+ FT_Memory memory = face->root.memory;
+
+
+ /* this provides additional offsets */
+ /* for each component's translation */
+
+ if ( ( error = TT_Vary_Get_Glyph_Deltas(
+ face,
+ glyph_index,
+ &deltas,
+ gloader->current.num_subglyphs + 4 )) != 0 )
+ goto Exit;
+
+ subglyph = gloader->current.subglyphs + gloader->base.num_subglyphs;
+ limit = gloader->current.num_subglyphs;
+
+ for ( i = 0; i < limit; ++i, ++subglyph )
+ {
+ if ( subglyph->flags & ARGS_ARE_XY_VALUES )
+ {
+ subglyph->arg1 += deltas[i].x;
+ subglyph->arg2 += deltas[i].y;
+ }
+ }
+
+ loader->pp1.x += deltas[i + 0].x; loader->pp1.y += deltas[i + 0].y;
+ loader->pp2.x += deltas[i + 1].x; loader->pp2.y += deltas[i + 1].y;
+ loader->pp3.x += deltas[i + 2].x; loader->pp3.y += deltas[i + 2].y;
+ loader->pp4.x += deltas[i + 3].x; loader->pp4.y += deltas[i + 3].y;
+
+ FT_FREE( deltas );
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
+ loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
+ loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
+ loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
+ }
+
+ /* if the flag FT_LOAD_NO_RECURSE is set, we return the subglyph */
+ /* `as is' in the glyph slot (the client application will be */
+ /* responsible for interpreting these data)... */
+ if ( loader->load_flags & FT_LOAD_NO_RECURSE )
+ {
+ FT_GlyphLoader_Add( gloader );
+ loader->glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+ goto Exit;
+ }
+
+ /*********************************************************************/
+ /*********************************************************************/
+ /*********************************************************************/
+
+ {
+ FT_UInt n, num_base_points;
+ FT_SubGlyph subglyph = 0;
+
+ FT_UInt num_points = start_point;
+ FT_UInt num_subglyphs = gloader->current.num_subglyphs;
+ FT_UInt num_base_subgs = gloader->base.num_subglyphs;
+
+ FT_Stream old_stream = loader->stream;
+
+ TT_GraphicsState saved_GS;
+
+
+ if ( loader->exec )
+ saved_GS = loader->exec->GS;
+
+ FT_GlyphLoader_Add( gloader );
+
+ /* read each subglyph independently */
+ for ( n = 0; n < num_subglyphs; n++ )
+ {
+ FT_Vector pp[4];
+
+
+ /* reinitialize graphics state */
+ if ( loader->exec )
+ loader->exec->GS = saved_GS;
+
+ /* Each time we call load_truetype_glyph in this loop, the */
+ /* value of `gloader.base.subglyphs' can change due to table */
+ /* reallocations. We thus need to recompute the subglyph */
+ /* pointer on each iteration. */
+ subglyph = gloader->base.subglyphs + num_base_subgs + n;
+
+ pp[0] = loader->pp1;
+ pp[1] = loader->pp2;
+ pp[2] = loader->pp3;
+ pp[3] = loader->pp4;
+
+ num_base_points = gloader->base.outline.n_points;
+
+ error = load_truetype_glyph( loader, subglyph->index,
+ recurse_count + 1 );
+ if ( error )
+ goto Exit;
+
+ /* restore subglyph pointer */
+ subglyph = gloader->base.subglyphs + num_base_subgs + n;
+
+ if ( !( subglyph->flags & USE_MY_METRICS ) )
+ {
+ loader->pp1 = pp[0];
+ loader->pp2 = pp[1];
+ loader->pp3 = pp[2];
+ loader->pp4 = pp[3];
+ }
+
+ num_points = gloader->base.outline.n_points;
+
+ if ( num_points == num_base_points )
+ continue;
+
+ /* gloader->base.outline consists of three parts: */
+ /* 0 -(1)-> start_point -(2)-> num_base_points -(3)-> n_points. */
+ /* */
+ /* (1): exists from the beginning */
+ /* (2): components that have been loaded so far */
+ /* (3): the newly loaded component */
+ TT_Process_Composite_Component( loader, subglyph, start_point,
+ num_base_points );
+ }
+
+ loader->stream = old_stream;
+
+ /* process the glyph */
+ loader->ins_pos = ins_pos;
+ if ( IS_HINTED( loader->load_flags ) &&
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ subglyph->flags & WE_HAVE_INSTR &&
+
+#endif
+
+ num_points > start_point )
+ TT_Process_Composite_Glyph( loader, start_point, start_contour );
+
+ }
+ }
+ else
+ {
+ /* invalid composite count (negative but not -1) */
+ error = TT_Err_Invalid_Outline;
+ goto Exit;
+ }
+
+ /***********************************************************************/
+ /***********************************************************************/
+ /***********************************************************************/
+
+ Exit:
+
+ if ( opened_frame )
+ face->forget_glyph_frame( loader );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ if ( glyph_data_loaded )
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,
+ &glyph_data );
+
+#endif
+
+ return error;
+ }
+
+
+ static FT_Error
+ compute_glyph_metrics( TT_Loader loader,
+ FT_UInt glyph_index )
+ {
+ FT_BBox bbox;
+ TT_Face face = (TT_Face)loader->face;
+ FT_Fixed y_scale;
+ TT_GlyphSlot glyph = loader->glyph;
+ TT_Size size = (TT_Size)loader->size;
+
+
+ y_scale = 0x10000L;
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ y_scale = size->root.metrics.y_scale;
+
+ if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE )
+ FT_Outline_Get_CBox( &glyph->outline, &bbox );
+ else
+ bbox = loader->bbox;
+
+ /* get the device-independent horizontal advance; it is scaled later */
+ /* by the base layer. */
+ {
+ FT_Pos advance = loader->linear;
+
+
+ /* the flag FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH was introduced to */
+ /* correctly support DynaLab fonts, which have an incorrect */
+ /* `advance_Width_Max' field! It is used, to my knowledge, */
+ /* exclusively in the X-TrueType font server. */
+ /* */
+ if ( face->postscript.isFixedPitch &&
+ ( loader->load_flags & FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH ) == 0 )
+ advance = face->horizontal.advance_Width_Max;
+
+ /* we need to return the advance in font units in linearHoriAdvance, */
+ /* it will be scaled later by the base layer. */
+ glyph->linearHoriAdvance = advance;
+ }
+
+ glyph->metrics.horiBearingX = bbox.xMin;
+ glyph->metrics.horiBearingY = bbox.yMax;
+ glyph->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
+
+ /* Now take care of vertical metrics. In the case where there is */
+ /* no vertical information within the font (relatively common), make */
+ /* up some metrics by `hand'... */
+
+ {
+ FT_Pos top; /* scaled vertical top side bearing */
+ FT_Pos advance; /* scaled vertical advance height */
+
+
+ /* Get the unscaled top bearing and advance height. */
+ if ( face->vertical_info &&
+ face->vertical.number_Of_VMetrics > 0 )
+ {
+ top = (FT_Short)FT_DivFix( loader->pp3.y - bbox.yMax,
+ y_scale );
+
+ if ( loader->pp3.y <= loader->pp4.y )
+ advance = 0;
+ else
+ advance = (FT_UShort)FT_DivFix( loader->pp3.y - loader->pp4.y,
+ y_scale );
+ }
+ else
+ {
+ FT_Pos height;
+
+
+ /* XXX Compute top side bearing and advance height in */
+ /* Get_VMetrics instead of here. */
+
+ /* NOTE: The OS/2 values are the only `portable' ones, */
+ /* which is why we use them, if there is an OS/2 */
+ /* table in the font. Otherwise, we use the */
+ /* values defined in the horizontal header. */
+
+ height = (FT_Short)FT_DivFix( bbox.yMax - bbox.yMin,
+ y_scale );
+ if ( face->os2.version != 0xFFFFU )
+ advance = (FT_Pos)( face->os2.sTypoAscender -
+ face->os2.sTypoDescender );
+ else
+ advance = (FT_Pos)( face->horizontal.Ascender -
+ face->horizontal.Descender );
+
+ top = ( advance - height ) / 2;
+ }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ {
+ FT_Incremental_InterfaceRec* incr;
+ FT_Incremental_MetricsRec metrics;
+ FT_Error error;
+
+
+ incr = face->root.internal->incremental_interface;
+
+ /* If this is an incrementally loaded font see if there are */
+ /* overriding metrics for this glyph. */
+ if ( incr && incr->funcs->get_glyph_metrics )
+ {
+ metrics.bearing_x = 0;
+ metrics.bearing_y = top;
+ metrics.advance = advance;
+
+ error = incr->funcs->get_glyph_metrics( incr->object,
+ glyph_index,
+ TRUE,
+ &metrics );
+ if ( error )
+ return error;
+
+ top = metrics.bearing_y;
+ advance = metrics.advance;
+ }
+ }
+
+ /* GWW: Do vertical metrics get loaded incrementally too? */
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ glyph->linearVertAdvance = advance;
+
+ /* scale the metrics */
+ if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
+ {
+ top = FT_MulFix( top, y_scale );
+ advance = FT_MulFix( advance, y_scale );
+ }
+
+ /* XXX: for now, we have no better algorithm for the lsb, but it */
+ /* should work fine. */
+ /* */
+ glyph->metrics.vertBearingX = ( bbox.xMin - bbox.xMax ) / 2;
+ glyph->metrics.vertBearingY = top;
+ glyph->metrics.vertAdvance = advance;
+ }
+
+ /* adjust advance width to the value contained in the hdmx table */
+ if ( !face->postscript.isFixedPitch &&
+ IS_HINTED( loader->load_flags ) )
+ {
+ FT_Byte* widthp;
+
+
+ widthp = tt_face_get_device_metrics( face,
+ size->root.metrics.x_ppem,
+ glyph_index );
+
+ if ( widthp )
+ glyph->metrics.horiAdvance = *widthp << 6;
+ }
+
+ /* set glyph dimensions */
+ glyph->metrics.width = bbox.xMax - bbox.xMin;
+ glyph->metrics.height = bbox.yMax - bbox.yMin;
+
+ return 0;
+ }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ static FT_Error
+ load_sbit_image( TT_Size size,
+ TT_GlyphSlot glyph,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ TT_Face face;
+ SFNT_Service sfnt;
+ FT_Stream stream;
+ FT_Error error;
+ TT_SBit_MetricsRec metrics;
+
+
+ face = (TT_Face)glyph->face;
+ sfnt = (SFNT_Service)face->sfnt;
+ stream = face->root.stream;
+
+ error = sfnt->load_sbit_image( face,
+ size->strike_index,
+ glyph_index,
+ (FT_Int)load_flags,
+ stream,
+ &glyph->bitmap,
+ &metrics );
+ if ( !error )
+ {
+ glyph->outline.n_points = 0;
+ glyph->outline.n_contours = 0;
+
+ glyph->metrics.width = (FT_Pos)metrics.width << 6;
+ glyph->metrics.height = (FT_Pos)metrics.height << 6;
+
+ glyph->metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6;
+ glyph->metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6;
+ glyph->metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6;
+
+ glyph->metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6;
+ glyph->metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6;
+ glyph->metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6;
+
+ glyph->format = FT_GLYPH_FORMAT_BITMAP;
+ if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+ {
+ glyph->bitmap_left = metrics.vertBearingX;
+ glyph->bitmap_top = metrics.vertBearingY;
+ }
+ else
+ {
+ glyph->bitmap_left = metrics.horiBearingX;
+ glyph->bitmap_top = metrics.horiBearingY;
+ }
+ }
+
+ return error;
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+ static FT_Error
+ tt_loader_init( TT_Loader loader,
+ TT_Size size,
+ TT_GlyphSlot glyph,
+ FT_Int32 load_flags )
+ {
+ TT_Face face;
+ FT_Stream stream;
+
+
+ face = (TT_Face)glyph->face;
+ stream = face->root.stream;
+
+ FT_MEM_ZERO( loader, sizeof ( TT_LoaderRec ) );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ /* load execution context */
+ if ( IS_HINTED( load_flags ) )
+ {
+ TT_ExecContext exec;
+ FT_Bool grayscale;
+
+
+ if ( !size->cvt_ready )
+ {
+ FT_Error error = tt_size_ready_bytecode( size );
+ if ( error )
+ return error;
+ }
+
+ /* query new execution context */
+ exec = size->debug ? size->context
+ : ( (TT_Driver)FT_FACE_DRIVER( face ) )->context;
+ if ( !exec )
+ return TT_Err_Could_Not_Find_Context;
+
+ grayscale =
+ FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) != FT_RENDER_MODE_MONO );
+
+ TT_Load_Context( exec, face, size );
+
+ /* a change from mono to grayscale rendering (and vice versa) */
+ /* requires a re-execution of the CVT program */
+ if ( grayscale != exec->grayscale )
+ {
+ FT_UInt i;
+
+
+ exec->grayscale = grayscale;
+
+ for ( i = 0; i < size->cvt_size; i++ )
+ size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale );
+ tt_size_run_prep( size );
+ }
+
+ /* see whether the cvt program has disabled hinting */
+ if ( exec->GS.instruct_control & 1 )
+ load_flags |= FT_LOAD_NO_HINTING;
+
+ /* load default graphics state -- if needed */
+ if ( exec->GS.instruct_control & 2 )
+ exec->GS = tt_default_graphics_state;
+
+ exec->pedantic_hinting = FT_BOOL( load_flags & FT_LOAD_PEDANTIC );
+ loader->exec = exec;
+ loader->instructions = exec->glyphIns;
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+ /* seek to the beginning of the glyph table -- for Type 42 fonts */
+ /* the table might be accessed from a Postscript stream or something */
+ /* else... */
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ if ( face->root.internal->incremental_interface )
+ loader->glyf_offset = 0;
+ else
+
+#endif
+
+ {
+ FT_Error error = face->goto_table( face, TTAG_glyf, stream, 0 );
+
+
+ if ( error )
+ {
+ FT_ERROR(( "TT_Load_Glyph: could not access glyph table\n" ));
+ return error;
+ }
+ loader->glyf_offset = FT_STREAM_POS();
+ }
+
+ /* get face's glyph loader */
+ {
+ FT_GlyphLoader gloader = glyph->internal->loader;
+
+
+ FT_GlyphLoader_Rewind( gloader );
+ loader->gloader = gloader;
+ }
+
+ loader->load_flags = load_flags;
+
+ loader->face = (FT_Face)face;
+ loader->size = (FT_Size)size;
+ loader->glyph = (FT_GlyphSlot)glyph;
+ loader->stream = stream;
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Load_Glyph */
+ /* */
+ /* <Description> */
+ /* A function used to load a single glyph within a given glyph slot, */
+ /* for a given size. */
+ /* */
+ /* <Input> */
+ /* glyph :: A handle to a target slot object where the glyph */
+ /* will be loaded. */
+ /* */
+ /* size :: A handle to the source face size at which the glyph */
+ /* must be scaled/loaded. */
+ /* */
+ /* glyph_index :: The index of the glyph in the font file. */
+ /* */
+ /* load_flags :: A flag indicating what to load for this glyph. The */
+ /* FT_LOAD_XXX constants can be used to control the */
+ /* glyph loading process (e.g., whether the outline */
+ /* should be scaled, whether to load bitmaps or not, */
+ /* whether to hint the outline, etc). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Load_Glyph( TT_Size size,
+ TT_GlyphSlot glyph,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ TT_Face face;
+ FT_Error error;
+ TT_LoaderRec loader;
+
+
+ face = (TT_Face)glyph->face;
+ error = TT_Err_Ok;
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+ /* try to load embedded bitmap if any */
+ /* */
+ /* XXX: The convention should be emphasized in */
+ /* the documents because it can be confusing. */
+ if ( size->strike_index != 0xFFFFFFFFUL &&
+ ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
+ {
+ error = load_sbit_image( size, glyph, glyph_index, load_flags );
+ if ( !error )
+ return TT_Err_Ok;
+ }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+ /* if FT_LOAD_NO_SCALE is not set, `ttmetrics' must be valid */
+ if ( !( load_flags & FT_LOAD_NO_SCALE ) && !size->ttmetrics.valid )
+ return TT_Err_Invalid_Size_Handle;
+
+ if ( load_flags & FT_LOAD_SBITS_ONLY )
+ return TT_Err_Invalid_Argument;
+
+ error = tt_loader_init( &loader, size, glyph, load_flags );
+ if ( error )
+ return error;
+
+ glyph->format = FT_GLYPH_FORMAT_OUTLINE;
+ glyph->num_subglyphs = 0;
+ glyph->outline.flags = 0;
+
+ /* main loading loop */
+ error = load_truetype_glyph( &loader, glyph_index, 0 );
+ if ( !error )
+ {
+ if ( glyph->format == FT_GLYPH_FORMAT_COMPOSITE )
+ {
+ glyph->num_subglyphs = loader.gloader->base.num_subglyphs;
+ glyph->subglyphs = loader.gloader->base.subglyphs;
+ }
+ else
+ {
+ glyph->outline = loader.gloader->base.outline;
+ glyph->outline.flags &= ~FT_OUTLINE_SINGLE_PASS;
+
+ /* In case bit 1 of the `flags' field in the `head' table isn't */
+ /* set, translate array so that (0,0) is the glyph's origin. */
+ if ( ( face->header.Flags & 2 ) == 0 && loader.pp1.x )
+ FT_Outline_Translate( &glyph->outline, -loader.pp1.x, 0 );
+ }
+
+ compute_glyph_metrics( &loader, glyph_index );
+ }
+
+ /* Set the `high precision' bit flag. */
+ /* This is _critical_ to get correct output for monochrome */
+ /* TrueType glyphs at all sizes using the bytecode interpreter. */
+ /* */
+ if ( !( load_flags & FT_LOAD_NO_SCALE ) &&
+ size->root.metrics.y_ppem < 24 )
+ glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/truetype/ttgload.h b/src/truetype/ttgload.h
new file mode 100644
index 0000000..b261e97
--- /dev/null
+++ b/src/truetype/ttgload.h
@@ -0,0 +1,49 @@
+/***************************************************************************/
+/* */
+/* ttgload.h */
+/* */
+/* TrueType Glyph Loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTGLOAD_H__
+#define __TTGLOAD_H__
+
+
+#include <ft2build.h>
+#include "ttobjs.h"
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#include "ttinterp.h"
+#endif
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( void )
+ TT_Init_Glyph_Loading( TT_Face face );
+
+ FT_LOCAL( FT_Error )
+ TT_Load_Glyph( TT_Size size,
+ TT_GlyphSlot glyph,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+
+FT_END_HEADER
+
+#endif /* __TTGLOAD_H__ */
+
+
+/* END */
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
new file mode 100644
index 0000000..0dc2c4f
--- /dev/null
+++ b/src/truetype/ttgxvar.c
@@ -0,0 +1,1536 @@
+/***************************************************************************/
+/* */
+/* ttgxvar.c */
+/* */
+/* TrueType GX Font Variation loader */
+/* */
+/* Copyright 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+/***************************************************************************/
+/* */
+/* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at */
+/* */
+/* http://developer.apple.com/fonts/TTRefMan/RM06/Chap6[fgca]var.html */
+/* */
+/* The documentation for `fvar' is inconsistent. At one point it says */
+/* that `countSizePairs' should be 3, at another point 2. It should be 2. */
+/* */
+/* The documentation for `gvar' is not intelligible; `cvar' refers you to */
+/* `gvar' and is thus also incomprehensible. */
+/* */
+/* The documentation for `avar' appears correct, but Apple has no fonts */
+/* with an `avar' table, so it is hard to test. */
+/* */
+/* Many thanks to John Jenkins (at Apple) in figuring this out. */
+/* */
+/* */
+/* Apple's `kern' table has some references to tuple indices, but as there */
+/* is no indication where these indices are defined, nor how to */
+/* interpolate the kerning values (different tuples have different */
+/* classes) this issue is ignored. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_SFNT_H
+#include FT_TRUETYPE_IDS_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_MULTIPLE_MASTERS_H
+
+#include "ttdriver.h"
+#include "ttpload.h"
+#include "ttgxvar.h"
+
+#include "tterrors.h"
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+
+#define FT_Stream_FTell( stream ) \
+ ( (stream)->cursor - (stream)->base )
+#define FT_Stream_SeekSet( stream, off ) \
+ ( (stream)->cursor = (stream)->base+(off) )
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttgxvar
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** Internal Routines *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It */
+ /* indicates that there is a delta for every point without needing to */
+ /* enumerate all of them. */
+ /* */
+#define ALL_POINTS (FT_UShort*)( -1 )
+
+
+ enum
+ {
+ GX_PT_POINTS_ARE_WORDS = 0x80,
+ GX_PT_POINT_RUN_COUNT_MASK = 0x7F
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_readpackedpoints */
+ /* */
+ /* <Description> */
+ /* Read a set of points to which the following deltas will apply. */
+ /* Points are packed with a run length encoding. */
+ /* */
+ /* <Input> */
+ /* stream :: The data stream. */
+ /* */
+ /* <Output> */
+ /* point_cnt :: The number of points read. A zero value means that */
+ /* all points in the glyph will be affected, without */
+ /* enumerating them individually. */
+ /* */
+ /* <Return> */
+ /* An array of FT_UShort containing the affected points or the */
+ /* special value ALL_POINTS. */
+ /* */
+ static FT_UShort*
+ ft_var_readpackedpoints( FT_Stream stream,
+ FT_UInt *point_cnt )
+ {
+ FT_UShort *points;
+ FT_Int n;
+ FT_Int runcnt;
+ FT_Int i;
+ FT_Int j;
+ FT_Int first;
+ FT_Memory memory = stream->memory;
+ FT_Error error = TT_Err_Ok;
+
+ FT_UNUSED( error );
+
+
+ *point_cnt = n = FT_GET_BYTE();
+ if ( n == 0 )
+ return ALL_POINTS;
+
+ if ( n & GX_PT_POINTS_ARE_WORDS )
+ n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
+
+ if ( FT_NEW_ARRAY( points, n ) )
+ return NULL;
+
+ i = 0;
+ while ( i < n )
+ {
+ runcnt = FT_GET_BYTE();
+ if ( runcnt & GX_PT_POINTS_ARE_WORDS )
+ {
+ runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
+ first = points[i++] = FT_GET_USHORT();
+
+ /* first point not included in runcount */
+ for ( j = 0; j < runcnt; ++j )
+ points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
+ }
+ else
+ {
+ first = points[i++] = FT_GET_BYTE();
+
+ for ( j = 0; j < runcnt; ++j )
+ points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
+ }
+ }
+
+ return points;
+ }
+
+
+ enum
+ {
+ GX_DT_DELTAS_ARE_ZERO = 0x80,
+ GX_DT_DELTAS_ARE_WORDS = 0x40,
+ GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
+ };
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_readpackeddeltas */
+ /* */
+ /* <Description> */
+ /* Read a set of deltas. These are packed slightly differently than */
+ /* points. In particular there is no overall count. */
+ /* */
+ /* <Input> */
+ /* stream :: The data stream. */
+ /* */
+ /* delta_cnt :: The number of to be read. */
+ /* */
+ /* <Return> */
+ /* An array of FT_Short containing the deltas for the affected */
+ /* points. (This only gets the deltas for one dimension. It will */
+ /* generally be called twice, once for x, once for y. When used in */
+ /* cvt table, it will only be called once.) */
+ /* */
+ static FT_Short*
+ ft_var_readpackeddeltas( FT_Stream stream,
+ FT_Int delta_cnt )
+ {
+ FT_Short *deltas;
+ FT_Int runcnt;
+ FT_Int i;
+ FT_Int j;
+ FT_Memory memory = stream->memory;
+ FT_Error error = TT_Err_Ok;
+
+ FT_UNUSED( error );
+
+
+ if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
+ return NULL;
+
+ i = 0;
+ while ( i < delta_cnt )
+ {
+ runcnt = FT_GET_BYTE();
+ if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
+ {
+ /* runcnt zeroes get added */
+ for ( j = 0;
+ j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
+ ++j )
+ deltas[i++] = 0;
+ }
+ else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
+ {
+ /* runcnt shorts from the stack */
+ for ( j = 0;
+ j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
+ ++j )
+ deltas[i++] = FT_GET_SHORT();
+ }
+ else
+ {
+ /* runcnt signed bytes from the stack */
+ for ( j = 0;
+ j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
+ ++j )
+ deltas[i++] = FT_GET_CHAR();
+ }
+
+ if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
+ {
+ /* Bad format */
+ FT_FREE( deltas );
+ return NULL;
+ }
+ }
+
+ return deltas;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_load_avar */
+ /* */
+ /* <Description> */
+ /* Parse the `avar' table if present. It need not be, so we return */
+ /* nothing. */
+ /* */
+ /* <InOut> */
+ /* face :: The font face. */
+ /* */
+ static void
+ ft_var_load_avar( TT_Face face )
+ {
+ FT_Stream stream = FT_FACE_STREAM(face);
+ FT_Memory memory = stream->memory;
+ GX_Blend blend = face->blend;
+ GX_AVarSegment segment;
+ FT_Error error = TT_Err_Ok;
+ FT_ULong version;
+ FT_Long axisCount;
+ FT_Int i, j;
+ FT_ULong table_len;
+
+ FT_UNUSED( error );
+
+
+ blend->avar_checked = TRUE;
+ if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
+ return;
+
+ if ( FT_FRAME_ENTER( table_len ) )
+ return;
+
+ version = FT_GET_LONG();
+ axisCount = FT_GET_LONG();
+
+ if ( version != 0x00010000L ||
+ axisCount != (FT_Long)blend->mmvar->num_axis )
+ goto Exit;
+
+ if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
+ goto Exit;
+
+ segment = &blend->avar_segment[0];
+ for ( i = 0; i < axisCount; ++i, ++segment )
+ {
+ segment->pairCount = FT_GET_USHORT();
+ if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
+ {
+ /* Failure. Free everything we have done so far. We must do */
+ /* it right now since loading the `avar' table is optional. */
+
+ for ( j = i - 1; j >= 0; --j )
+ FT_FREE( blend->avar_segment[j].correspondence );
+
+ FT_FREE( blend->avar_segment );
+ blend->avar_segment = NULL;
+ goto Exit;
+ }
+
+ for ( j = 0; j < segment->pairCount; ++j )
+ {
+ segment->correspondence[j].fromCoord =
+ FT_GET_SHORT() << 2; /* convert to Fixed */
+ segment->correspondence[j].toCoord =
+ FT_GET_SHORT()<<2; /* convert to Fixed */
+ }
+ }
+
+ Exit:
+ FT_FRAME_EXIT();
+ }
+
+
+ typedef struct GX_GVar_Head_ {
+ FT_Long version;
+ FT_UShort axisCount;
+ FT_UShort globalCoordCount;
+ FT_ULong offsetToCoord;
+ FT_UShort glyphCount;
+ FT_UShort flags;
+ FT_ULong offsetToData;
+
+ } GX_GVar_Head;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_load_gvar */
+ /* */
+ /* <Description> */
+ /* Parses the `gvar' table if present. If `fvar' is there, `gvar' */
+ /* had better be there too. */
+ /* */
+ /* <InOut> */
+ /* face :: The font face. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ ft_var_load_gvar( TT_Face face )
+ {
+ FT_Stream stream = FT_FACE_STREAM(face);
+ FT_Memory memory = stream->memory;
+ GX_Blend blend = face->blend;
+ FT_Error error;
+ FT_UInt i, j;
+ FT_ULong table_len;
+ FT_ULong gvar_start;
+ FT_ULong offsetToData;
+ GX_GVar_Head gvar_head;
+
+ static const FT_Frame_Field gvar_fields[] =
+ {
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE GX_GVar_Head
+
+ FT_FRAME_START( 20 ),
+ FT_FRAME_LONG ( version ),
+ FT_FRAME_USHORT( axisCount ),
+ FT_FRAME_USHORT( globalCoordCount ),
+ FT_FRAME_ULONG ( offsetToCoord ),
+ FT_FRAME_USHORT( glyphCount ),
+ FT_FRAME_USHORT( flags ),
+ FT_FRAME_ULONG ( offsetToData ),
+ FT_FRAME_END
+ };
+
+ if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
+ goto Exit;
+
+ gvar_start = FT_STREAM_POS( );
+ if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
+ goto Exit;
+
+ blend->tuplecount = gvar_head.globalCoordCount;
+ blend->gv_glyphcnt = gvar_head.glyphCount;
+ offsetToData = gvar_start + gvar_head.offsetToData;
+
+ if ( gvar_head.version != (FT_Long)0x00010000L ||
+ gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
+ {
+ error = TT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
+ goto Exit;
+
+ if ( gvar_head.flags & 1 )
+ {
+ /* long offsets (one more offset than glyphs, to mark size of last) */
+ if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
+ goto Exit;
+
+ for ( i = 0; i <= blend->gv_glyphcnt; ++i )
+ blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
+
+ FT_FRAME_EXIT();
+ }
+ else
+ {
+ /* short offsets (one more offset than glyphs, to mark size of last) */
+ if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
+ goto Exit;
+
+ for ( i = 0; i <= blend->gv_glyphcnt; ++i )
+ blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
+ /* XXX: Undocumented: `*2'! */
+
+ FT_FRAME_EXIT();
+ }
+
+ if ( blend->tuplecount != 0 )
+ {
+ if ( FT_NEW_ARRAY( blend->tuplecoords,
+ gvar_head.axisCount * blend->tuplecount ) )
+ goto Exit;
+
+ if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
+ FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
+ goto Exit;
+
+ for ( i = 0; i < blend->tuplecount; ++i )
+ for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
+ blend->tuplecoords[i * gvar_head.axisCount + j] =
+ FT_GET_SHORT() << 2; /* convert to FT_Fixed */
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* ft_var_apply_tuple */
+ /* */
+ /* <Description> */
+ /* Figure out whether a given tuple (design) applies to the current */
+ /* blend, and if so, what is the scaling factor. */
+ /* */
+ /* <Input> */
+ /* blend :: The current blend of the font. */
+ /* */
+ /* tupleIndex :: A flag saying whether this is an intermediate */
+ /* tuple or not. */
+ /* */
+ /* tuple_coords :: The coordinates of the tuple in normalized axis */
+ /* units. */
+ /* */
+ /* im_start_coords :: The initial coordinates where this tuple starts */
+ /* to apply (for intermediate coordinates). */
+ /* */
+ /* im_end_coords :: The final coordinates after which this tuple no */
+ /* longer applies (for intermediate coordinates). */
+ /* */
+ /* <Return> */
+ /* An FT_Fixed value containing the scaling factor. */
+ /* */
+ static FT_Fixed
+ ft_var_apply_tuple( GX_Blend blend,
+ FT_UShort tupleIndex,
+ FT_Fixed* tuple_coords,
+ FT_Fixed* im_start_coords,
+ FT_Fixed* im_end_coords )
+ {
+ FT_UInt i;
+ FT_Fixed apply;
+ FT_Fixed temp;
+
+
+ apply = 0x10000L;
+ for ( i = 0; i < blend->num_axis; ++i )
+ {
+ if ( tuple_coords[i] == 0 )
+ /* It's not clear why (for intermediate tuples) we don't need */
+ /* to check against start/end -- the documentation says we don't. */
+ /* Similarly, it's unclear why we don't need to scale along the */
+ /* axis. */
+ continue;
+
+ else if ( blend->normalizedcoords[i] == 0 ||
+ ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
+ ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
+ {
+ apply = 0;
+ break;
+ }
+
+ else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
+ /* not an intermediate tuple */
+ apply = FT_MulDiv( apply,
+ blend->normalizedcoords[i] > 0
+ ? blend->normalizedcoords[i]
+ : -blend->normalizedcoords[i],
+ 0x10000L );
+
+ else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
+ blend->normalizedcoords[i] >= im_end_coords[i] )
+ {
+ apply = 0;
+ break;
+ }
+
+ else if ( blend->normalizedcoords[i] < tuple_coords[i] )
+ {
+ temp = FT_MulDiv( blend->normalizedcoords[i] - im_start_coords[i],
+ 0x10000L,
+ tuple_coords[i] - im_start_coords[i]);
+ apply = FT_MulDiv( apply, temp, 0x10000L );
+ }
+
+ else
+ {
+ temp = FT_MulDiv( im_end_coords[i] - blend->normalizedcoords[i],
+ 0x10000L,
+ im_end_coords[i] - tuple_coords[i] );
+ apply = FT_MulDiv( apply, temp, 0x10000L );
+ }
+ }
+
+ return apply;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ typedef struct GX_FVar_Head_ {
+ FT_Long version;
+ FT_UShort offsetToData;
+ FT_UShort countSizePairs;
+ FT_UShort axisCount;
+ FT_UShort axisSize;
+ FT_UShort instanceCount;
+ FT_UShort instanceSize;
+
+ } GX_FVar_Head;
+
+
+ typedef struct fvar_axis {
+ FT_ULong axisTag;
+ FT_ULong minValue;
+ FT_ULong defaultValue;
+ FT_ULong maxValue;
+ FT_UShort flags;
+ FT_UShort nameID;
+
+ } GX_FVar_Axis;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Get_MM_Var */
+ /* */
+ /* <Description> */
+ /* Check that the font's `fvar' table is valid, parse it, and return */
+ /* those data. */
+ /* */
+ /* <InOut> */
+ /* face :: The font face. */
+ /* TT_Get_MM_Var initializes the blend structure. */
+ /* */
+ /* <Output> */
+ /* master :: The `fvar' data (must be freed by caller). */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Get_MM_Var( TT_Face face,
+ FT_MM_Var* *master )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = face->root.memory;
+ FT_ULong table_len;
+ FT_Error error = TT_Err_Ok;
+ FT_ULong fvar_start;
+ FT_Int i, j;
+ FT_MM_Var* mmvar;
+ FT_Fixed* next_coords;
+ FT_String* next_name;
+ FT_Var_Axis* a;
+ FT_Var_Named_Style* ns;
+ GX_FVar_Head fvar_head;
+
+ static const FT_Frame_Field fvar_fields[] =
+ {
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE GX_FVar_Head
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_LONG ( version ),
+ FT_FRAME_USHORT( offsetToData ),
+ FT_FRAME_USHORT( countSizePairs ),
+ FT_FRAME_USHORT( axisCount ),
+ FT_FRAME_USHORT( axisSize ),
+ FT_FRAME_USHORT( instanceCount ),
+ FT_FRAME_USHORT( instanceSize ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field fvaraxis_fields[] =
+ {
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE GX_FVar_Axis
+
+ FT_FRAME_START( 20 ),
+ FT_FRAME_ULONG ( axisTag ),
+ FT_FRAME_ULONG ( minValue ),
+ FT_FRAME_ULONG ( defaultValue ),
+ FT_FRAME_ULONG ( maxValue ),
+ FT_FRAME_USHORT( flags ),
+ FT_FRAME_USHORT( nameID ),
+ FT_FRAME_END
+ };
+
+
+ if ( face->blend == NULL )
+ {
+ /* both `fvar' and `gvar' must be present */
+ if ( (error = face->goto_table( face, TTAG_gvar,
+ stream, &table_len )) != 0 )
+ goto Exit;
+
+ if ( (error = face->goto_table( face, TTAG_fvar,
+ stream, &table_len )) != 0 )
+ goto Exit;
+
+ fvar_start = FT_STREAM_POS( );
+
+ if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
+ goto Exit;
+
+ if ( fvar_head.version != (FT_Long)0x00010000L ||
+ fvar_head.countSizePairs != 2 ||
+ fvar_head.axisSize != 20 ||
+ fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount ||
+ fvar_head.offsetToData + fvar_head.axisCount * 20U +
+ fvar_head.instanceCount * fvar_head.instanceSize > table_len )
+ {
+ error = TT_Err_Invalid_Table;
+ goto Exit;
+ }
+
+ if ( FT_NEW( face->blend ) )
+ goto Exit;
+
+ /* XXX: TODO - check for overflows */
+ face->blend->mmvar_len =
+ sizeof ( FT_MM_Var ) +
+ fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
+ fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
+ fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
+ 5 * fvar_head.axisCount;
+
+ if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
+ goto Exit;
+ face->blend->mmvar = mmvar;
+
+ mmvar->num_axis =
+ fvar_head.axisCount;
+ mmvar->num_designs =
+ (FT_UInt)-1; /* meaningless in this context; each glyph */
+ /* may have a different number of designs */
+ /* (or tuples, as called by Apple) */
+ mmvar->num_namedstyles =
+ fvar_head.instanceCount;
+ mmvar->axis =
+ (FT_Var_Axis*)&(mmvar[1]);
+ mmvar->namedstyle =
+ (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
+
+ next_coords =
+ (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
+ for ( i = 0; i < fvar_head.instanceCount; ++i )
+ {
+ mmvar->namedstyle[i].coords = next_coords;
+ next_coords += fvar_head.axisCount;
+ }
+
+ next_name = (FT_String*)next_coords;
+ for ( i = 0; i < fvar_head.axisCount; ++i )
+ {
+ mmvar->axis[i].name = next_name;
+ next_name += 5;
+ }
+
+ if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
+ goto Exit;
+
+ a = mmvar->axis;
+ for ( i = 0; i < fvar_head.axisCount; ++i )
+ {
+ GX_FVar_Axis axis_rec;
+
+
+ if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
+ goto Exit;
+ a->tag = axis_rec.axisTag;
+ a->minimum = axis_rec.minValue; /* A Fixed */
+ a->def = axis_rec.defaultValue; /* A Fixed */
+ a->maximum = axis_rec.maxValue; /* A Fixed */
+ a->strid = axis_rec.nameID;
+
+ a->name[0] = (FT_String)( a->tag >> 24 );
+ a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
+ a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF );
+ a->name[3] = (FT_String)( ( a->tag ) & 0xFF );
+ a->name[4] = 0;
+
+ ++a;
+ }
+
+ ns = mmvar->namedstyle;
+ for ( i = 0; i < fvar_head.instanceCount; ++i )
+ {
+ if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
+ goto Exit;
+
+ ns->strid = FT_GET_USHORT();
+ (void) /* flags = */ FT_GET_USHORT();
+
+ for ( j = 0; j < fvar_head.axisCount; ++j )
+ ns->coords[j] = FT_GET_ULONG(); /* A Fixed */
+
+ FT_FRAME_EXIT();
+ }
+ }
+
+ if ( master != NULL )
+ {
+ FT_UInt n;
+
+
+ if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
+ goto Exit;
+ FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
+
+ mmvar->axis =
+ (FT_Var_Axis*)&(mmvar[1]);
+ mmvar->namedstyle =
+ (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
+ next_coords =
+ (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
+
+ for ( n = 0; n < mmvar->num_namedstyles; ++n )
+ {
+ mmvar->namedstyle[n].coords = next_coords;
+ next_coords += mmvar->num_axis;
+ }
+
+ a = mmvar->axis;
+ next_name = (FT_String*)next_coords;
+ for ( n = 0; n < mmvar->num_axis; ++n )
+ {
+ a->name = next_name;
+
+ /* standard PostScript names for some standard apple tags */
+ if ( a->tag == TTAG_wght )
+ a->name = (char *)"Weight";
+ else if ( a->tag == TTAG_wdth )
+ a->name = (char *)"Width";
+ else if ( a->tag == TTAG_opsz )
+ a->name = (char *)"OpticalSize";
+ else if ( a->tag == TTAG_slnt )
+ a->name = (char *)"Slant";
+
+ next_name += 5;
+ ++a;
+ }
+
+ *master = mmvar;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Set_MM_Blend */
+ /* */
+ /* <Description> */
+ /* Set the blend (normalized) coordinates for this instance of the */
+ /* font. Check that the `gvar' table is reasonable and does some */
+ /* initial preparation. */
+ /* */
+ /* <InOut> */
+ /* face :: The font. */
+ /* Initialize the blend structure with `gvar' data. */
+ /* */
+ /* <Input> */
+ /* num_coords :: Must be the axis count of the font. */
+ /* */
+ /* coords :: An array of num_coords, each between [-1,1]. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Set_MM_Blend( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error = TT_Err_Ok;
+ GX_Blend blend;
+ FT_MM_Var* mmvar;
+ FT_UInt i;
+ FT_Memory memory = face->root.memory;
+
+ enum
+ {
+ mcvt_retain,
+ mcvt_modify,
+ mcvt_load
+
+ } manageCvt;
+
+
+ face->doblend = FALSE;
+
+ if ( face->blend == NULL )
+ {
+ if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
+ goto Exit;
+ }
+
+ blend = face->blend;
+ mmvar = blend->mmvar;
+
+ if ( num_coords != mmvar->num_axis )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ for ( i = 0; i < num_coords; ++i )
+ if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( blend->glyphoffsets == NULL )
+ if ( (error = ft_var_load_gvar( face )) != 0 )
+ goto Exit;
+
+ if ( blend->normalizedcoords == NULL )
+ {
+ if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
+ goto Exit;
+
+ manageCvt = mcvt_modify;
+
+ /* If we have not set the blend coordinates before this, then the */
+ /* cvt table will still be what we read from the `cvt ' table and */
+ /* we don't need to reload it. We may need to change it though... */
+ }
+ else
+ {
+ for ( i = 0;
+ i < num_coords && blend->normalizedcoords[i] == coords[i];
+ ++i );
+ if ( i == num_coords )
+ manageCvt = mcvt_retain;
+ else
+ manageCvt = mcvt_load;
+
+ /* If we don't change the blend coords then we don't need to do */
+ /* anything to the cvt table. It will be correct. Otherwise we */
+ /* no longer have the original cvt (it was modified when we set */
+ /* the blend last time), so we must reload and then modify it. */
+ }
+
+ blend->num_axis = num_coords;
+ FT_MEM_COPY( blend->normalizedcoords,
+ coords,
+ num_coords * sizeof ( FT_Fixed ) );
+
+ face->doblend = TRUE;
+
+ if ( face->cvt != NULL )
+ {
+ switch ( manageCvt )
+ {
+ case mcvt_load:
+ /* The cvt table has been loaded already; every time we change the */
+ /* blend we may need to reload and remodify the cvt table. */
+ FT_FREE( face->cvt );
+ face->cvt = NULL;
+
+ tt_face_load_cvt( face, face->root.stream );
+ break;
+
+ case mcvt_modify:
+ /* The original cvt table is in memory. All we need to do is */
+ /* apply the `cvar' table (if any). */
+ tt_face_vary_cvt( face, face->root.stream );
+ break;
+
+ case mcvt_retain:
+ /* The cvt table is correct for this set of coordinates. */
+ break;
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Set_Var_Design */
+ /* */
+ /* <Description> */
+ /* Set the coordinates for the instance, measured in the user */
+ /* coordinate system. Parse the `avar' table (if present) to convert */
+ /* from user to normalized coordinates. */
+ /* */
+ /* <InOut> */
+ /* face :: The font face. */
+ /* Initialize the blend struct with `gvar' data. */
+ /* */
+ /* <Input> */
+ /* num_coords :: This must be the axis count of the font. */
+ /* */
+ /* coords :: A coordinate array with `num_coords' elements. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Set_Var_Design( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Error error = TT_Err_Ok;
+ FT_Fixed* normalized = NULL;
+ GX_Blend blend;
+ FT_MM_Var* mmvar;
+ FT_UInt i, j;
+ FT_Var_Axis* a;
+ GX_AVarSegment av;
+ FT_Memory memory = face->root.memory;
+
+
+ if ( face->blend == NULL )
+ {
+ if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
+ goto Exit;
+ }
+
+ blend = face->blend;
+ mmvar = blend->mmvar;
+
+ if ( num_coords != mmvar->num_axis )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* Axis normalization is a two stage process. First we normalize */
+ /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
+ /* Then, if there's an `avar' table, we renormalize this range. */
+
+ if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
+ goto Exit;
+
+ a = mmvar->axis;
+ for ( i = 0; i < mmvar->num_axis; ++i, ++a )
+ {
+ if ( coords[i] > a->maximum || coords[i] < a->minimum )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( coords[i] < a->def )
+ {
+ normalized[i] = -FT_MulDiv( coords[i] - a->def,
+ 0x10000L,
+ a->minimum - a->def );
+ }
+ else if ( a->maximum == a->def )
+ normalized[i] = 0;
+ else
+ {
+ normalized[i] = FT_MulDiv( coords[i] - a->def,
+ 0x10000L,
+ a->maximum - a->def );
+ }
+ }
+
+ if ( !blend->avar_checked )
+ ft_var_load_avar( face );
+
+ if ( blend->avar_segment != NULL )
+ {
+ av = blend->avar_segment;
+ for ( i = 0; i < mmvar->num_axis; ++i, ++av )
+ {
+ for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
+ if ( normalized[i] < av->correspondence[j].fromCoord )
+ {
+ normalized[i] =
+ FT_MulDiv(
+ FT_MulDiv(
+ normalized[i] - av->correspondence[j - 1].fromCoord,
+ 0x10000L,
+ av->correspondence[j].fromCoord -
+ av->correspondence[j - 1].fromCoord ),
+ av->correspondence[j].toCoord -
+ av->correspondence[j - 1].toCoord,
+ 0x10000L ) +
+ av->correspondence[j - 1].toCoord;
+ break;
+ }
+ }
+ }
+
+ error = TT_Set_MM_Blend( face, num_coords, normalized );
+
+ Exit:
+ FT_FREE( normalized );
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** GX VAR PARSING ROUTINES *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_vary_cvt */
+ /* */
+ /* <Description> */
+ /* Modify the loaded cvt table according to the `cvar' table and the */
+ /* font's blend. */
+ /* */
+ /* <InOut> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* <Input> */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* Most errors are ignored. It is perfectly valid not to have a */
+ /* `cvar' table even if there is a `gvar' and `fvar' table. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_vary_cvt( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_ULong table_start;
+ FT_ULong table_len;
+ FT_UInt tupleCount;
+ FT_ULong offsetToData;
+ FT_ULong here;
+ FT_UInt i, j;
+ FT_Fixed* tuple_coords = NULL;
+ FT_Fixed* im_start_coords = NULL;
+ FT_Fixed* im_end_coords = NULL;
+ GX_Blend blend = face->blend;
+ FT_UInt point_count;
+ FT_UShort* localpoints;
+ FT_Short* deltas;
+
+
+ FT_TRACE2(( "CVAR " ));
+
+ if ( blend == NULL )
+ {
+ FT_TRACE2(( "no blend specified!\n" ));
+
+ error = TT_Err_Ok;
+ goto Exit;
+ }
+
+ if ( face->cvt == NULL )
+ {
+ FT_TRACE2(( "no `cvt ' table!\n" ));
+
+ error = TT_Err_Ok;
+ goto Exit;
+ }
+
+ error = face->goto_table( face, TTAG_cvar, stream, &table_len );
+ if ( error )
+ {
+ FT_TRACE2(( "is missing!\n" ));
+
+ error = TT_Err_Ok;
+ goto Exit;
+ }
+
+ if ( FT_FRAME_ENTER( table_len ) )
+ {
+ error = TT_Err_Ok;
+ goto Exit;
+ }
+
+ table_start = FT_Stream_FTell( stream );
+ if ( FT_GET_LONG() != 0x00010000L )
+ {
+ FT_TRACE2(( "bad table version!\n" ));
+
+ error = TT_Err_Ok;
+ goto FExit;
+ }
+
+ if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
+ FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
+ FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
+ goto FExit;
+
+ tupleCount = FT_GET_USHORT();
+ offsetToData = table_start + FT_GET_USHORT();
+
+ /* The documentation implies there are flags packed into the */
+ /* tuplecount, but John Jenkins says that shared points don't apply */
+ /* to `cvar', and no other flags are defined. */
+
+ for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
+ {
+ FT_UInt tupleDataSize;
+ FT_UInt tupleIndex;
+ FT_Fixed apply;
+
+
+ tupleDataSize = FT_GET_USHORT();
+ tupleIndex = FT_GET_USHORT();
+
+ /* There is no provision here for a global tuple coordinate section, */
+ /* so John says. There are no tuple indices, just embedded tuples. */
+
+ if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
+ {
+ for ( j = 0; j < blend->num_axis; ++j )
+ tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */
+ /* short frac to fixed */
+ }
+ else
+ {
+ /* skip this tuple; it makes no sense */
+
+ if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
+ for ( j = 0; j < 2 * blend->num_axis; ++j )
+ (void)FT_GET_SHORT();
+
+ offsetToData += tupleDataSize;
+ continue;
+ }
+
+ if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
+ {
+ for ( j = 0; j < blend->num_axis; ++j )
+ im_start_coords[j] = FT_GET_SHORT() << 2;
+ for ( j = 0; j < blend->num_axis; ++j )
+ im_end_coords[j] = FT_GET_SHORT() << 2;
+ }
+
+ apply = ft_var_apply_tuple( blend,
+ (FT_UShort)tupleIndex,
+ tuple_coords,
+ im_start_coords,
+ im_end_coords );
+ if ( /* tuple isn't active for our blend */
+ apply == 0 ||
+ /* global points not allowed, */
+ /* if they aren't local, makes no sense */
+ !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
+ {
+ offsetToData += tupleDataSize;
+ continue;
+ }
+
+ here = FT_Stream_FTell( stream );
+
+ FT_Stream_SeekSet( stream, offsetToData );
+
+ localpoints = ft_var_readpackedpoints( stream, &point_count );
+ deltas = ft_var_readpackeddeltas( stream,
+ point_count == 0 ? face->cvt_size
+ : point_count );
+ if ( localpoints == NULL || deltas == NULL )
+ /* failure, ignore it */;
+
+ else if ( localpoints == ALL_POINTS )
+ {
+ /* this means that there are deltas for every entry in cvt */
+ for ( j = 0; j < face->cvt_size; ++j )
+ face->cvt[j] = (FT_Short)( face->cvt[j] +
+ FT_MulFix( deltas[j], apply ) );
+ }
+
+ else
+ {
+ for ( j = 0; j < point_count; ++j )
+ {
+ int pindex = localpoints[j];
+
+ face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
+ FT_MulFix( deltas[j], apply ) );
+ }
+ }
+
+ if ( localpoints != ALL_POINTS )
+ FT_FREE( localpoints );
+ FT_FREE( deltas );
+
+ offsetToData += tupleDataSize;
+
+ FT_Stream_SeekSet( stream, here );
+ }
+
+ FExit:
+ FT_FRAME_EXIT();
+
+ Exit:
+ FT_FREE( tuple_coords );
+ FT_FREE( im_start_coords );
+ FT_FREE( im_end_coords );
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Vary_Get_Glyph_Deltas */
+ /* */
+ /* <Description> */
+ /* Load the appropriate deltas for the current glyph. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* glyph_index :: The index of the glyph being modified. */
+ /* */
+ /* n_points :: The number of the points in the glyph, including */
+ /* phantom points. */
+ /* */
+ /* <Output> */
+ /* deltas :: The array of points to change. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Vary_Get_Glyph_Deltas( TT_Face face,
+ FT_UInt glyph_index,
+ FT_Vector* *deltas,
+ FT_UInt n_points )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = stream->memory;
+ GX_Blend blend = face->blend;
+ FT_Vector* delta_xy;
+
+ FT_Error error;
+ FT_ULong glyph_start;
+ FT_UInt tupleCount;
+ FT_ULong offsetToData;
+ FT_ULong here;
+ FT_UInt i, j;
+ FT_Fixed* tuple_coords = NULL;
+ FT_Fixed* im_start_coords = NULL;
+ FT_Fixed* im_end_coords = NULL;
+ FT_UInt point_count, spoint_count = 0;
+ FT_UShort* sharedpoints = NULL;
+ FT_UShort* localpoints = NULL;
+ FT_UShort* points;
+ FT_Short *deltas_x, *deltas_y;
+
+
+ if ( !face->doblend || blend == NULL )
+ return TT_Err_Invalid_Argument;
+
+ /* to be freed by the caller */
+ if ( FT_NEW_ARRAY( delta_xy, n_points ) )
+ goto Exit;
+ *deltas = delta_xy;
+
+ if ( glyph_index >= blend->gv_glyphcnt ||
+ blend->glyphoffsets[glyph_index] ==
+ blend->glyphoffsets[glyph_index + 1] )
+ return TT_Err_Ok; /* no variation data for this glyph */
+
+ if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
+ FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
+ blend->glyphoffsets[glyph_index] ) )
+ goto Fail1;
+
+ glyph_start = FT_Stream_FTell( stream );
+
+ /* each set of glyph variation data is formatted similarly to `cvar' */
+ /* (except we get shared points and global tuples) */
+
+ if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
+ FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
+ FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
+ goto Fail2;
+
+ tupleCount = FT_GET_USHORT();
+ offsetToData = glyph_start + FT_GET_USHORT();
+
+ if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
+ {
+ here = FT_Stream_FTell( stream );
+
+ FT_Stream_SeekSet( stream, offsetToData );
+
+ sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+ offsetToData = FT_Stream_FTell( stream );
+
+ FT_Stream_SeekSet( stream, here );
+ }
+
+ for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
+ {
+ FT_UInt tupleDataSize;
+ FT_UInt tupleIndex;
+ FT_Fixed apply;
+
+
+ tupleDataSize = FT_GET_USHORT();
+ tupleIndex = FT_GET_USHORT();
+
+ if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
+ {
+ for ( j = 0; j < blend->num_axis; ++j )
+ tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from */
+ /* short frac to fixed */
+ }
+ else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
+ {
+ error = TT_Err_Invalid_Table;
+ goto Fail3;
+ }
+ else
+ {
+ FT_MEM_COPY(
+ tuple_coords,
+ &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
+ blend->num_axis * sizeof ( FT_Fixed ) );
+ }
+
+ if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
+ {
+ for ( j = 0; j < blend->num_axis; ++j )
+ im_start_coords[j] = FT_GET_SHORT() << 2;
+ for ( j = 0; j < blend->num_axis; ++j )
+ im_end_coords[j] = FT_GET_SHORT() << 2;
+ }
+
+ apply = ft_var_apply_tuple( blend,
+ (FT_UShort)tupleIndex,
+ tuple_coords,
+ im_start_coords,
+ im_end_coords );
+
+ if ( apply == 0 ) /* tuple isn't active for our blend */
+ {
+ offsetToData += tupleDataSize;
+ continue;
+ }
+
+ here = FT_Stream_FTell( stream );
+
+ if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
+ {
+ FT_Stream_SeekSet( stream, offsetToData );
+
+ localpoints = ft_var_readpackedpoints( stream, &point_count );
+ points = localpoints;
+ }
+ else
+ {
+ points = sharedpoints;
+ point_count = spoint_count;
+ }
+
+ deltas_x = ft_var_readpackeddeltas( stream,
+ point_count == 0 ? n_points
+ : point_count );
+ deltas_y = ft_var_readpackeddeltas( stream,
+ point_count == 0 ? n_points
+ : point_count );
+
+ if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
+ ; /* failure, ignore it */
+
+ else if ( points == ALL_POINTS )
+ {
+ /* this means that there are deltas for every point in the glyph */
+ for ( j = 0; j < n_points; ++j )
+ {
+ delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
+ delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
+ }
+ }
+
+ else
+ {
+ for ( j = 0; j < point_count; ++j )
+ {
+ delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
+ delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
+ }
+ }
+
+ if ( localpoints != ALL_POINTS )
+ FT_FREE( localpoints );
+ FT_FREE( deltas_x );
+ FT_FREE( deltas_y );
+
+ offsetToData += tupleDataSize;
+
+ FT_Stream_SeekSet( stream, here );
+ }
+
+ Fail3:
+ FT_FREE( tuple_coords );
+ FT_FREE( im_start_coords );
+ FT_FREE( im_end_coords );
+
+ Fail2:
+ FT_FRAME_EXIT();
+
+ Fail1:
+ if ( error )
+ {
+ FT_FREE( delta_xy );
+ *deltas = NULL;
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_done_blend */
+ /* */
+ /* <Description> */
+ /* Frees the blend internal data structure. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_done_blend( FT_Memory memory,
+ GX_Blend blend )
+ {
+ if ( blend != NULL )
+ {
+ FT_UInt i;
+
+
+ FT_FREE( blend->normalizedcoords );
+ FT_FREE( blend->mmvar );
+
+ if ( blend->avar_segment != NULL )
+ {
+ for ( i = 0; i < blend->num_axis; ++i )
+ FT_FREE( blend->avar_segment[i].correspondence );
+ FT_FREE( blend->avar_segment );
+ }
+
+ FT_FREE( blend->tuplecoords );
+ FT_FREE( blend->glyphoffsets );
+ FT_FREE( blend );
+ }
+ }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+/* END */
diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h
new file mode 100644
index 0000000..706cb4d
--- /dev/null
+++ b/src/truetype/ttgxvar.h
@@ -0,0 +1,182 @@
+/***************************************************************************/
+/* */
+/* ttgxvar.h */
+/* */
+/* TrueType GX Font Variation loader (specification) */
+/* */
+/* Copyright 2004 by */
+/* David Turner, Robert Wilhelm, Werner Lemberg and George Williams. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTGXVAR_H__
+#define __TTGXVAR_H__
+
+
+#include <ft2build.h>
+#include "ttobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* GX_AVarCorrespondenceRec */
+ /* */
+ /* <Description> */
+ /* A data structure representing `shortFracCorrespondence' in `avar' */
+ /* table according to the specifications from Apple. */
+ /* */
+ typedef struct GX_AVarCorrespondenceRec_
+ {
+ FT_Fixed fromCoord;
+ FT_Fixed toCoord;
+
+ } GX_AVarCorrespondenceRec_, *GX_AVarCorrespondence;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* GX_AVarRec */
+ /* */
+ /* <Description> */
+ /* Data from the segment field of `avar' table. */
+ /* There is one of these for each axis. */
+ /* */
+ typedef struct GX_AVarSegmentRec_
+ {
+ FT_UShort pairCount;
+ GX_AVarCorrespondence correspondence; /* array with pairCount entries */
+
+ } GX_AVarSegmentRec, *GX_AVarSegment;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* GX_BlendRec */
+ /* */
+ /* <Description> */
+ /* Data for interpolating a font from a distortable font specified */
+ /* by the GX *var tables ([fgca]var). */
+ /* */
+ /* <Fields> */
+ /* num_axis :: The number of axes along which interpolation */
+ /* may happen */
+ /* */
+ /* normalizedcoords :: A normalized value (between [-1,1]) indicating */
+ /* the contribution along each axis to the final */
+ /* interpolated font. */
+ /* */
+ typedef struct GX_BlendRec_
+ {
+ FT_UInt num_axis;
+ FT_Fixed* normalizedcoords;
+
+ FT_MM_Var* mmvar;
+ FT_Int mmvar_len;
+
+ FT_Bool avar_checked;
+ GX_AVarSegment avar_segment;
+
+ FT_UInt tuplecount; /* shared tuples in `gvar' */
+ FT_Fixed* tuplecoords; /* tuplecoords[tuplecount][num_axis] */
+
+ FT_UInt gv_glyphcnt;
+ FT_ULong* glyphoffsets;
+
+ } GX_BlendRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* <enum> */
+ /* GX_TupleCountFlags */
+ /* */
+ /* <Description> */
+ /* Flags used within the `TupleCount' field of the `gvar' table. */
+ /* */
+ typedef enum GX_TupleCountFlags_
+ {
+ GX_TC_TUPLES_SHARE_POINT_NUMBERS = 0x8000,
+ GX_TC_RESERVED_TUPLE_FLAGS = 0x7000,
+ GX_TC_TUPLE_COUNT_MASK = 0x0FFF
+
+ } GX_TupleCountFlags;
+
+
+ /*************************************************************************/
+ /* */
+ /* <enum> */
+ /* GX_TupleIndexFlags */
+ /* */
+ /* <Description> */
+ /* Flags used within the `TupleIndex' field of the `gvar' and `cvar' */
+ /* tables. */
+ /* */
+ typedef enum GX_TupleIndexFlags_
+ {
+ GX_TI_EMBEDDED_TUPLE_COORD = 0x8000,
+ GX_TI_INTERMEDIATE_TUPLE = 0x4000,
+ GX_TI_PRIVATE_POINT_NUMBERS = 0x2000,
+ GX_TI_RESERVED_TUPLE_FLAG = 0x1000,
+ GX_TI_TUPLE_INDEX_MASK = 0x0FFF
+
+ } GX_TupleIndexFlags;
+
+
+#define TTAG_wght FT_MAKE_TAG( 'w', 'g', 'h', 't' )
+#define TTAG_wdth FT_MAKE_TAG( 'w', 'd', 't', 'h' )
+#define TTAG_opsz FT_MAKE_TAG( 'o', 'p', 's', 'z' )
+#define TTAG_slnt FT_MAKE_TAG( 's', 'l', 'n', 't' )
+
+
+ FT_LOCAL( FT_Error )
+ TT_Set_MM_Blend( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
+
+ FT_LOCAL( FT_Error )
+ TT_Set_Var_Design( TT_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
+
+ FT_LOCAL( FT_Error )
+ TT_Get_MM_Var( TT_Face face,
+ FT_MM_Var* *master );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_vary_cvt( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ TT_Vary_Get_Glyph_Deltas( TT_Face face,
+ FT_UInt glyph_index,
+ FT_Vector* *deltas,
+ FT_UInt n_points );
+
+
+ FT_LOCAL( void )
+ tt_done_blend( FT_Memory memory,
+ GX_Blend blend );
+
+
+FT_END_HEADER
+
+
+#endif /* __TTGXVAR_H__ */
+
+
+/* END */
diff --git a/src/truetype/ttinterp.c b/src/truetype/ttinterp.c
new file mode 100644
index 0000000..85c8529
--- /dev/null
+++ b/src/truetype/ttinterp.c
@@ -0,0 +1,7809 @@
+/***************************************************************************/
+/* */
+/* ttinterp.c */
+/* */
+/* TrueType bytecode interpreter (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_TRIGONOMETRY_H
+#include FT_SYSTEM_H
+
+#include "ttinterp.h"
+
+#include "tterrors.h"
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+
+#define TT_MULFIX FT_MulFix
+#define TT_MULDIV FT_MulDiv
+#define TT_MULDIV_NO_ROUND FT_MulDiv_No_Round
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttinterp
+
+ /*************************************************************************/
+ /* */
+ /* In order to detect infinite loops in the code, we set up a counter */
+ /* within the run loop. A single stroke of interpretation is now */
+ /* limited to a maximal number of opcodes defined below. */
+ /* */
+#define MAX_RUNNABLE_OPCODES 1000000L
+
+
+ /*************************************************************************/
+ /* */
+ /* There are two kinds of implementations: */
+ /* */
+ /* a. static implementation */
+ /* */
+ /* The current execution context is a static variable, which fields */
+ /* are accessed directly by the interpreter during execution. The */
+ /* context is named `cur'. */
+ /* */
+ /* This version is non-reentrant, of course. */
+ /* */
+ /* b. indirect implementation */
+ /* */
+ /* The current execution context is passed to _each_ function as its */
+ /* first argument, and each field is thus accessed indirectly. */
+ /* */
+ /* This version is fully re-entrant. */
+ /* */
+ /* The idea is that an indirect implementation may be slower to execute */
+ /* on low-end processors that are used in some systems (like 386s or */
+ /* even 486s). */
+ /* */
+ /* As a consequence, the indirect implementation is now the default, as */
+ /* its performance costs can be considered negligible in our context. */
+ /* Note, however, that we kept the same source with macros because: */
+ /* */
+ /* - The code is kept very close in design to the Pascal code used for */
+ /* development. */
+ /* */
+ /* - It's much more readable that way! */
+ /* */
+ /* - It's still open to experimentation and tuning. */
+ /* */
+ /*************************************************************************/
+
+
+#ifndef TT_CONFIG_OPTION_STATIC_INTERPRETER /* indirect implementation */
+
+#define CUR (*exc) /* see ttobjs.h */
+
+ /*************************************************************************/
+ /* */
+ /* This macro is used whenever `exec' is unused in a function, to avoid */
+ /* stupid warnings from pedantic compilers. */
+ /* */
+#define FT_UNUSED_EXEC FT_UNUSED( exc )
+
+#else /* static implementation */
+
+#define CUR cur
+
+#define FT_UNUSED_EXEC int __dummy = __dummy
+
+ static
+ TT_ExecContextRec cur; /* static exec. context variable */
+
+ /* apparently, we have a _lot_ of direct indexing when accessing */
+ /* the static `cur', which makes the code bigger (due to all the */
+ /* four bytes addresses). */
+
+#endif /* TT_CONFIG_OPTION_STATIC_INTERPRETER */
+
+
+ /*************************************************************************/
+ /* */
+ /* The instruction argument stack. */
+ /* */
+#define INS_ARG EXEC_OP_ FT_Long* args /* see ttobjs.h for EXEC_OP_ */
+
+
+ /*************************************************************************/
+ /* */
+ /* This macro is used whenever `args' is unused in a function, to avoid */
+ /* stupid warnings from pedantic compilers. */
+ /* */
+#define FT_UNUSED_ARG FT_UNUSED_EXEC; FT_UNUSED( args )
+
+
+ /*************************************************************************/
+ /* */
+ /* The following macros hide the use of EXEC_ARG and EXEC_ARG_ to */
+ /* increase readability of the code. */
+ /* */
+ /*************************************************************************/
+
+
+#define SKIP_Code() \
+ SkipCode( EXEC_ARG )
+
+#define GET_ShortIns() \
+ GetShortIns( EXEC_ARG )
+
+#define NORMalize( x, y, v ) \
+ Normalize( EXEC_ARG_ x, y, v )
+
+#define SET_SuperRound( scale, flags ) \
+ SetSuperRound( EXEC_ARG_ scale, flags )
+
+#define ROUND_None( d, c ) \
+ Round_None( EXEC_ARG_ d, c )
+
+#define INS_Goto_CodeRange( range, ip ) \
+ Ins_Goto_CodeRange( EXEC_ARG_ range, ip )
+
+#define CUR_Func_move( z, p, d ) \
+ CUR.func_move( EXEC_ARG_ z, p, d )
+
+#define CUR_Func_move_orig( z, p, d ) \
+ CUR.func_move_orig( EXEC_ARG_ z, p, d )
+
+#define CUR_Func_round( d, c ) \
+ CUR.func_round( EXEC_ARG_ d, c )
+
+#define CUR_Func_read_cvt( index ) \
+ CUR.func_read_cvt( EXEC_ARG_ index )
+
+#define CUR_Func_write_cvt( index, val ) \
+ CUR.func_write_cvt( EXEC_ARG_ index, val )
+
+#define CUR_Func_move_cvt( index, val ) \
+ CUR.func_move_cvt( EXEC_ARG_ index, val )
+
+#define CURRENT_Ratio() \
+ Current_Ratio( EXEC_ARG )
+
+#define CURRENT_Ppem() \
+ Current_Ppem( EXEC_ARG )
+
+#define CUR_Ppem() \
+ Cur_PPEM( EXEC_ARG )
+
+#define INS_SxVTL( a, b, c, d ) \
+ Ins_SxVTL( EXEC_ARG_ a, b, c, d )
+
+#define COMPUTE_Funcs() \
+ Compute_Funcs( EXEC_ARG )
+
+#define COMPUTE_Round( a ) \
+ Compute_Round( EXEC_ARG_ a )
+
+#define COMPUTE_Point_Displacement( a, b, c, d ) \
+ Compute_Point_Displacement( EXEC_ARG_ a, b, c, d )
+
+#define MOVE_Zp2_Point( a, b, c, t ) \
+ Move_Zp2_Point( EXEC_ARG_ a, b, c, t )
+
+
+#define CUR_Func_project( v1, v2 ) \
+ CUR.func_project( EXEC_ARG_ (v1)->x - (v2)->x, (v1)->y - (v2)->y )
+
+#define CUR_Func_dualproj( v1, v2 ) \
+ CUR.func_dualproj( EXEC_ARG_ (v1)->x - (v2)->x, (v1)->y - (v2)->y )
+
+#define CUR_fast_project( v ) \
+ CUR.func_project( EXEC_ARG_ (v)->x, (v)->y )
+
+#define CUR_fast_dualproj( v ) \
+ CUR.func_dualproj( EXEC_ARG_ (v)->x, (v)->y )
+
+
+ /*************************************************************************/
+ /* */
+ /* Instruction dispatch function, as used by the interpreter. */
+ /* */
+ typedef void (*TInstruction_Function)( INS_ARG );
+
+
+ /*************************************************************************/
+ /* */
+ /* A simple bounds-checking macro. */
+ /* */
+#define BOUNDS( x, n ) ( (FT_UInt)(x) >= (FT_UInt)(n) )
+
+#undef SUCCESS
+#define SUCCESS 0
+
+#undef FAILURE
+#define FAILURE 1
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+#define GUESS_VECTOR( V ) \
+ if ( CUR.face->unpatented_hinting ) \
+ { \
+ CUR.GS.V.x = (FT_F2Dot14)( CUR.GS.both_x_axis ? 0x4000 : 0 ); \
+ CUR.GS.V.y = (FT_F2Dot14)( CUR.GS.both_x_axis ? 0 : 0x4000 ); \
+ }
+#else
+#define GUESS_VECTOR( V )
+#endif
+
+ /*************************************************************************/
+ /* */
+ /* CODERANGE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Goto_CodeRange */
+ /* */
+ /* <Description> */
+ /* Switches to a new code range (updates the code related elements in */
+ /* `exec', and `IP'). */
+ /* */
+ /* <Input> */
+ /* range :: The new execution code range. */
+ /* */
+ /* IP :: The new IP in the new code range. */
+ /* */
+ /* <InOut> */
+ /* exec :: The target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Goto_CodeRange( TT_ExecContext exec,
+ FT_Int range,
+ FT_Long IP )
+ {
+ TT_CodeRange* coderange;
+
+
+ FT_ASSERT( range >= 1 && range <= 3 );
+
+ coderange = &exec->codeRangeTable[range - 1];
+
+ FT_ASSERT( coderange->base != NULL );
+
+ /* NOTE: Because the last instruction of a program may be a CALL */
+ /* which will return to the first byte *after* the code */
+ /* range, we test for IP <= Size instead of IP < Size. */
+ /* */
+ FT_ASSERT( (FT_ULong)IP <= coderange->size );
+
+ exec->code = coderange->base;
+ exec->codeSize = coderange->size;
+ exec->IP = IP;
+ exec->curRange = range;
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Set_CodeRange */
+ /* */
+ /* <Description> */
+ /* Sets a code range. */
+ /* */
+ /* <Input> */
+ /* range :: The code range index. */
+ /* */
+ /* base :: The new code base. */
+ /* */
+ /* length :: The range size in bytes. */
+ /* */
+ /* <InOut> */
+ /* exec :: The target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Set_CodeRange( TT_ExecContext exec,
+ FT_Int range,
+ void* base,
+ FT_Long length )
+ {
+ FT_ASSERT( range >= 1 && range <= 3 );
+
+ exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
+ exec->codeRangeTable[range - 1].size = length;
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Clear_CodeRange */
+ /* */
+ /* <Description> */
+ /* Clears a code range. */
+ /* */
+ /* <Input> */
+ /* range :: The code range index. */
+ /* */
+ /* <InOut> */
+ /* exec :: The target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Does not set the Error variable. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Clear_CodeRange( TT_ExecContext exec,
+ FT_Int range )
+ {
+ FT_ASSERT( range >= 1 && range <= 3 );
+
+ exec->codeRangeTable[range - 1].base = NULL;
+ exec->codeRangeTable[range - 1].size = 0;
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* EXECUTION CONTEXT ROUTINES */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Done_Context */
+ /* */
+ /* <Description> */
+ /* Destroys a given context. */
+ /* */
+ /* <Input> */
+ /* exec :: A handle to the target execution context. */
+ /* */
+ /* memory :: A handle to the parent memory object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only the glyph loader and debugger should call this function. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Done_Context( TT_ExecContext exec )
+ {
+ FT_Memory memory = exec->memory;
+
+
+ /* points zone */
+ exec->maxPoints = 0;
+ exec->maxContours = 0;
+
+ /* free stack */
+ FT_FREE( exec->stack );
+ exec->stackSize = 0;
+
+ /* free call stack */
+ FT_FREE( exec->callStack );
+ exec->callSize = 0;
+ exec->callTop = 0;
+
+ /* free glyph code range */
+ FT_FREE( exec->glyphIns );
+ exec->glyphSize = 0;
+
+ exec->size = NULL;
+ exec->face = NULL;
+
+ FT_FREE( exec );
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Init_Context */
+ /* */
+ /* <Description> */
+ /* Initializes a context object. */
+ /* */
+ /* <Input> */
+ /* memory :: A handle to the parent memory object. */
+ /* */
+ /* <InOut> */
+ /* exec :: A handle to the target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Init_Context( TT_ExecContext exec,
+ FT_Memory memory )
+ {
+ FT_Error error;
+
+
+ FT_TRACE1(( "Init_Context: new object at 0x%08p\n", exec ));
+
+ exec->memory = memory;
+ exec->callSize = 32;
+
+ if ( FT_NEW_ARRAY( exec->callStack, exec->callSize ) )
+ goto Fail_Memory;
+
+ /* all values in the context are set to 0 already, but this is */
+ /* here as a remainder */
+ exec->maxPoints = 0;
+ exec->maxContours = 0;
+
+ exec->stackSize = 0;
+ exec->glyphSize = 0;
+
+ exec->stack = NULL;
+ exec->glyphIns = NULL;
+
+ exec->face = NULL;
+ exec->size = NULL;
+
+ return TT_Err_Ok;
+
+ Fail_Memory:
+ FT_ERROR(( "Init_Context: not enough memory for 0x%08lx\n",
+ (FT_Long)exec ));
+ TT_Done_Context( exec );
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Update_Max */
+ /* */
+ /* <Description> */
+ /* Checks the size of a buffer and reallocates it if necessary. */
+ /* */
+ /* <Input> */
+ /* memory :: A handle to the parent memory object. */
+ /* */
+ /* multiplier :: The size in bytes of each element in the buffer. */
+ /* */
+ /* new_max :: The new capacity (size) of the buffer. */
+ /* */
+ /* <InOut> */
+ /* size :: The address of the buffer's current size expressed */
+ /* in elements. */
+ /* */
+ /* buff :: The address of the buffer base pointer. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static FT_Error
+ Update_Max( FT_Memory memory,
+ FT_ULong* size,
+ FT_Long multiplier,
+ void* _pbuff,
+ FT_ULong new_max )
+ {
+ FT_Error error;
+ void** pbuff = (void**)_pbuff;
+
+
+ if ( *size < new_max )
+ {
+ if ( FT_REALLOC( *pbuff, *size * multiplier, new_max * multiplier ) )
+ return error;
+ *size = new_max;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Load_Context */
+ /* */
+ /* <Description> */
+ /* Prepare an execution context for glyph hinting. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* size :: A handle to the source size object. */
+ /* */
+ /* <InOut> */
+ /* exec :: A handle to the target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only the glyph loader and debugger should call this function. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Load_Context( TT_ExecContext exec,
+ TT_Face face,
+ TT_Size size )
+ {
+ FT_Int i;
+ FT_ULong tmp;
+ TT_MaxProfile* maxp;
+ FT_Error error;
+
+
+ exec->face = face;
+ maxp = &face->max_profile;
+ exec->size = size;
+
+ if ( size )
+ {
+ exec->numFDefs = size->num_function_defs;
+ exec->maxFDefs = size->max_function_defs;
+ exec->numIDefs = size->num_instruction_defs;
+ exec->maxIDefs = size->max_instruction_defs;
+ exec->FDefs = size->function_defs;
+ exec->IDefs = size->instruction_defs;
+ exec->tt_metrics = size->ttmetrics;
+ exec->metrics = size->metrics;
+
+ exec->maxFunc = size->max_func;
+ exec->maxIns = size->max_ins;
+
+ for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
+ exec->codeRangeTable[i] = size->codeRangeTable[i];
+
+ /* set graphics state */
+ exec->GS = size->GS;
+
+ exec->cvtSize = size->cvt_size;
+ exec->cvt = size->cvt;
+
+ exec->storeSize = size->storage_size;
+ exec->storage = size->storage;
+
+ exec->twilight = size->twilight;
+ }
+
+ /* XXX: We reserve a little more elements on the stack to deal safely */
+ /* with broken fonts like arialbs, courbs, timesbs, etc. */
+ tmp = exec->stackSize;
+ error = Update_Max( exec->memory,
+ &tmp,
+ sizeof ( FT_F26Dot6 ),
+ (void*)&exec->stack,
+ maxp->maxStackElements + 32 );
+ exec->stackSize = (FT_UInt)tmp;
+ if ( error )
+ return error;
+
+ tmp = exec->glyphSize;
+ error = Update_Max( exec->memory,
+ &tmp,
+ sizeof ( FT_Byte ),
+ (void*)&exec->glyphIns,
+ maxp->maxSizeOfInstructions );
+ exec->glyphSize = (FT_UShort)tmp;
+ if ( error )
+ return error;
+
+ exec->pts.n_points = 0;
+ exec->pts.n_contours = 0;
+
+ exec->zp1 = exec->pts;
+ exec->zp2 = exec->pts;
+ exec->zp0 = exec->pts;
+
+ exec->instruction_trap = FALSE;
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Save_Context */
+ /* */
+ /* <Description> */
+ /* Saves the code ranges in a `size' object. */
+ /* */
+ /* <Input> */
+ /* exec :: A handle to the source execution context. */
+ /* */
+ /* <InOut> */
+ /* size :: A handle to the target size object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only the glyph loader and debugger should call this function. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Save_Context( TT_ExecContext exec,
+ TT_Size size )
+ {
+ FT_Int i;
+
+
+ /* XXXX: Will probably disappear soon with all the code range */
+ /* management, which is now rather obsolete. */
+ /* */
+ size->num_function_defs = exec->numFDefs;
+ size->num_instruction_defs = exec->numIDefs;
+
+ size->max_func = exec->maxFunc;
+ size->max_ins = exec->maxIns;
+
+ for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
+ size->codeRangeTable[i] = exec->codeRangeTable[i];
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_Run_Context */
+ /* */
+ /* <Description> */
+ /* Executes one or more instructions in the execution context. */
+ /* */
+ /* <Input> */
+ /* debug :: A Boolean flag. If set, the function sets some internal */
+ /* variables and returns immediately, otherwise TT_RunIns() */
+ /* is called. */
+ /* */
+ /* This is commented out currently. */
+ /* */
+ /* <Input> */
+ /* exec :: A handle to the target execution context. */
+ /* */
+ /* <Return> */
+ /* TrueTyoe error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only the glyph loader and debugger should call this function. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ TT_Run_Context( TT_ExecContext exec,
+ FT_Bool debug )
+ {
+ FT_Error error;
+
+
+ if ( ( error = TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 ) )
+ != TT_Err_Ok )
+ return error;
+
+ exec->zp0 = exec->pts;
+ exec->zp1 = exec->pts;
+ exec->zp2 = exec->pts;
+
+ exec->GS.gep0 = 1;
+ exec->GS.gep1 = 1;
+ exec->GS.gep2 = 1;
+
+ exec->GS.projVector.x = 0x4000;
+ exec->GS.projVector.y = 0x0000;
+
+ exec->GS.freeVector = exec->GS.projVector;
+ exec->GS.dualVector = exec->GS.projVector;
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ exec->GS.both_x_axis = TRUE;
+#endif
+
+ exec->GS.round_state = 1;
+ exec->GS.loop = 1;
+
+ /* some glyphs leave something on the stack. so we clean it */
+ /* before a new execution. */
+ exec->top = 0;
+ exec->callTop = 0;
+
+#if 1
+ FT_UNUSED( debug );
+
+ return exec->face->interpreter( exec );
+#else
+ if ( !debug )
+ return TT_RunIns( exec );
+ else
+ return TT_Err_Ok;
+#endif
+ }
+
+
+ const TT_GraphicsState tt_default_graphics_state =
+ {
+ 0, 0, 0,
+ { 0x4000, 0 },
+ { 0x4000, 0 },
+ { 0x4000, 0 },
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ TRUE,
+#endif
+
+ 1, 64, 1,
+ TRUE, 68, 0, 0, 9, 3,
+ 0, FALSE, 2, 1, 1, 1
+ };
+
+
+ /* documentation is in ttinterp.h */
+
+ FT_EXPORT_DEF( TT_ExecContext )
+ TT_New_Context( TT_Driver driver )
+ {
+ TT_ExecContext exec;
+ FT_Memory memory;
+
+
+ memory = driver->root.root.memory;
+ exec = driver->context;
+
+ if ( !driver->context )
+ {
+ FT_Error error;
+
+
+ /* allocate object */
+ if ( FT_NEW( exec ) )
+ goto Exit;
+
+ /* initialize it */
+ error = Init_Context( exec, memory );
+ if ( error )
+ goto Fail;
+
+ /* store it into the driver */
+ driver->context = exec;
+ }
+
+ Exit:
+ return driver->context;
+
+ Fail:
+ FT_FREE( exec );
+
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Before an opcode is executed, the interpreter verifies that there are */
+ /* enough arguments on the stack, with the help of the `Pop_Push_Count' */
+ /* table. */
+ /* */
+ /* For each opcode, the first column gives the number of arguments that */
+ /* are popped from the stack; the second one gives the number of those */
+ /* that are pushed in result. */
+ /* */
+ /* Opcodes which have a varying number of parameters in the data stream */
+ /* (NPUSHB, NPUSHW) are handled specially; they have a negative value in */
+ /* the `opcode_length' table, and the value in `Pop_Push_Count' is set */
+ /* to zero. */
+ /* */
+ /*************************************************************************/
+
+
+#undef PACK
+#define PACK( x, y ) ( ( x << 4 ) | y )
+
+
+ static
+ const FT_Byte Pop_Push_Count[256] =
+ {
+ /* opcodes are gathered in groups of 16 */
+ /* please keep the spaces as they are */
+
+ /* SVTCA y */ PACK( 0, 0 ),
+ /* SVTCA x */ PACK( 0, 0 ),
+ /* SPvTCA y */ PACK( 0, 0 ),
+ /* SPvTCA x */ PACK( 0, 0 ),
+ /* SFvTCA y */ PACK( 0, 0 ),
+ /* SFvTCA x */ PACK( 0, 0 ),
+ /* SPvTL // */ PACK( 2, 0 ),
+ /* SPvTL + */ PACK( 2, 0 ),
+ /* SFvTL // */ PACK( 2, 0 ),
+ /* SFvTL + */ PACK( 2, 0 ),
+ /* SPvFS */ PACK( 2, 0 ),
+ /* SFvFS */ PACK( 2, 0 ),
+ /* GPV */ PACK( 0, 2 ),
+ /* GFV */ PACK( 0, 2 ),
+ /* SFvTPv */ PACK( 0, 0 ),
+ /* ISECT */ PACK( 5, 0 ),
+
+ /* SRP0 */ PACK( 1, 0 ),
+ /* SRP1 */ PACK( 1, 0 ),
+ /* SRP2 */ PACK( 1, 0 ),
+ /* SZP0 */ PACK( 1, 0 ),
+ /* SZP1 */ PACK( 1, 0 ),
+ /* SZP2 */ PACK( 1, 0 ),
+ /* SZPS */ PACK( 1, 0 ),
+ /* SLOOP */ PACK( 1, 0 ),
+ /* RTG */ PACK( 0, 0 ),
+ /* RTHG */ PACK( 0, 0 ),
+ /* SMD */ PACK( 1, 0 ),
+ /* ELSE */ PACK( 0, 0 ),
+ /* JMPR */ PACK( 1, 0 ),
+ /* SCvTCi */ PACK( 1, 0 ),
+ /* SSwCi */ PACK( 1, 0 ),
+ /* SSW */ PACK( 1, 0 ),
+
+ /* DUP */ PACK( 1, 2 ),
+ /* POP */ PACK( 1, 0 ),
+ /* CLEAR */ PACK( 0, 0 ),
+ /* SWAP */ PACK( 2, 2 ),
+ /* DEPTH */ PACK( 0, 1 ),
+ /* CINDEX */ PACK( 1, 1 ),
+ /* MINDEX */ PACK( 1, 0 ),
+ /* AlignPTS */ PACK( 2, 0 ),
+ /* INS_$28 */ PACK( 0, 0 ),
+ /* UTP */ PACK( 1, 0 ),
+ /* LOOPCALL */ PACK( 2, 0 ),
+ /* CALL */ PACK( 1, 0 ),
+ /* FDEF */ PACK( 1, 0 ),
+ /* ENDF */ PACK( 0, 0 ),
+ /* MDAP[0] */ PACK( 1, 0 ),
+ /* MDAP[1] */ PACK( 1, 0 ),
+
+ /* IUP[0] */ PACK( 0, 0 ),
+ /* IUP[1] */ PACK( 0, 0 ),
+ /* SHP[0] */ PACK( 0, 0 ),
+ /* SHP[1] */ PACK( 0, 0 ),
+ /* SHC[0] */ PACK( 1, 0 ),
+ /* SHC[1] */ PACK( 1, 0 ),
+ /* SHZ[0] */ PACK( 1, 0 ),
+ /* SHZ[1] */ PACK( 1, 0 ),
+ /* SHPIX */ PACK( 1, 0 ),
+ /* IP */ PACK( 0, 0 ),
+ /* MSIRP[0] */ PACK( 2, 0 ),
+ /* MSIRP[1] */ PACK( 2, 0 ),
+ /* AlignRP */ PACK( 0, 0 ),
+ /* RTDG */ PACK( 0, 0 ),
+ /* MIAP[0] */ PACK( 2, 0 ),
+ /* MIAP[1] */ PACK( 2, 0 ),
+
+ /* NPushB */ PACK( 0, 0 ),
+ /* NPushW */ PACK( 0, 0 ),
+ /* WS */ PACK( 2, 0 ),
+ /* RS */ PACK( 1, 1 ),
+ /* WCvtP */ PACK( 2, 0 ),
+ /* RCvt */ PACK( 1, 1 ),
+ /* GC[0] */ PACK( 1, 1 ),
+ /* GC[1] */ PACK( 1, 1 ),
+ /* SCFS */ PACK( 2, 0 ),
+ /* MD[0] */ PACK( 2, 1 ),
+ /* MD[1] */ PACK( 2, 1 ),
+ /* MPPEM */ PACK( 0, 1 ),
+ /* MPS */ PACK( 0, 1 ),
+ /* FlipON */ PACK( 0, 0 ),
+ /* FlipOFF */ PACK( 0, 0 ),
+ /* DEBUG */ PACK( 1, 0 ),
+
+ /* LT */ PACK( 2, 1 ),
+ /* LTEQ */ PACK( 2, 1 ),
+ /* GT */ PACK( 2, 1 ),
+ /* GTEQ */ PACK( 2, 1 ),
+ /* EQ */ PACK( 2, 1 ),
+ /* NEQ */ PACK( 2, 1 ),
+ /* ODD */ PACK( 1, 1 ),
+ /* EVEN */ PACK( 1, 1 ),
+ /* IF */ PACK( 1, 0 ),
+ /* EIF */ PACK( 0, 0 ),
+ /* AND */ PACK( 2, 1 ),
+ /* OR */ PACK( 2, 1 ),
+ /* NOT */ PACK( 1, 1 ),
+ /* DeltaP1 */ PACK( 1, 0 ),
+ /* SDB */ PACK( 1, 0 ),
+ /* SDS */ PACK( 1, 0 ),
+
+ /* ADD */ PACK( 2, 1 ),
+ /* SUB */ PACK( 2, 1 ),
+ /* DIV */ PACK( 2, 1 ),
+ /* MUL */ PACK( 2, 1 ),
+ /* ABS */ PACK( 1, 1 ),
+ /* NEG */ PACK( 1, 1 ),
+ /* FLOOR */ PACK( 1, 1 ),
+ /* CEILING */ PACK( 1, 1 ),
+ /* ROUND[0] */ PACK( 1, 1 ),
+ /* ROUND[1] */ PACK( 1, 1 ),
+ /* ROUND[2] */ PACK( 1, 1 ),
+ /* ROUND[3] */ PACK( 1, 1 ),
+ /* NROUND[0] */ PACK( 1, 1 ),
+ /* NROUND[1] */ PACK( 1, 1 ),
+ /* NROUND[2] */ PACK( 1, 1 ),
+ /* NROUND[3] */ PACK( 1, 1 ),
+
+ /* WCvtF */ PACK( 2, 0 ),
+ /* DeltaP2 */ PACK( 1, 0 ),
+ /* DeltaP3 */ PACK( 1, 0 ),
+ /* DeltaCn[0] */ PACK( 1, 0 ),
+ /* DeltaCn[1] */ PACK( 1, 0 ),
+ /* DeltaCn[2] */ PACK( 1, 0 ),
+ /* SROUND */ PACK( 1, 0 ),
+ /* S45Round */ PACK( 1, 0 ),
+ /* JROT */ PACK( 2, 0 ),
+ /* JROF */ PACK( 2, 0 ),
+ /* ROFF */ PACK( 0, 0 ),
+ /* INS_$7B */ PACK( 0, 0 ),
+ /* RUTG */ PACK( 0, 0 ),
+ /* RDTG */ PACK( 0, 0 ),
+ /* SANGW */ PACK( 1, 0 ),
+ /* AA */ PACK( 1, 0 ),
+
+ /* FlipPT */ PACK( 0, 0 ),
+ /* FlipRgON */ PACK( 2, 0 ),
+ /* FlipRgOFF */ PACK( 2, 0 ),
+ /* INS_$83 */ PACK( 0, 0 ),
+ /* INS_$84 */ PACK( 0, 0 ),
+ /* ScanCTRL */ PACK( 1, 0 ),
+ /* SDVPTL[0] */ PACK( 2, 0 ),
+ /* SDVPTL[1] */ PACK( 2, 0 ),
+ /* GetINFO */ PACK( 1, 1 ),
+ /* IDEF */ PACK( 1, 0 ),
+ /* ROLL */ PACK( 3, 3 ),
+ /* MAX */ PACK( 2, 1 ),
+ /* MIN */ PACK( 2, 1 ),
+ /* ScanTYPE */ PACK( 1, 0 ),
+ /* InstCTRL */ PACK( 2, 0 ),
+ /* INS_$8F */ PACK( 0, 0 ),
+
+ /* INS_$90 */ PACK( 0, 0 ),
+ /* INS_$91 */ PACK( 0, 0 ),
+ /* INS_$92 */ PACK( 0, 0 ),
+ /* INS_$93 */ PACK( 0, 0 ),
+ /* INS_$94 */ PACK( 0, 0 ),
+ /* INS_$95 */ PACK( 0, 0 ),
+ /* INS_$96 */ PACK( 0, 0 ),
+ /* INS_$97 */ PACK( 0, 0 ),
+ /* INS_$98 */ PACK( 0, 0 ),
+ /* INS_$99 */ PACK( 0, 0 ),
+ /* INS_$9A */ PACK( 0, 0 ),
+ /* INS_$9B */ PACK( 0, 0 ),
+ /* INS_$9C */ PACK( 0, 0 ),
+ /* INS_$9D */ PACK( 0, 0 ),
+ /* INS_$9E */ PACK( 0, 0 ),
+ /* INS_$9F */ PACK( 0, 0 ),
+
+ /* INS_$A0 */ PACK( 0, 0 ),
+ /* INS_$A1 */ PACK( 0, 0 ),
+ /* INS_$A2 */ PACK( 0, 0 ),
+ /* INS_$A3 */ PACK( 0, 0 ),
+ /* INS_$A4 */ PACK( 0, 0 ),
+ /* INS_$A5 */ PACK( 0, 0 ),
+ /* INS_$A6 */ PACK( 0, 0 ),
+ /* INS_$A7 */ PACK( 0, 0 ),
+ /* INS_$A8 */ PACK( 0, 0 ),
+ /* INS_$A9 */ PACK( 0, 0 ),
+ /* INS_$AA */ PACK( 0, 0 ),
+ /* INS_$AB */ PACK( 0, 0 ),
+ /* INS_$AC */ PACK( 0, 0 ),
+ /* INS_$AD */ PACK( 0, 0 ),
+ /* INS_$AE */ PACK( 0, 0 ),
+ /* INS_$AF */ PACK( 0, 0 ),
+
+ /* PushB[0] */ PACK( 0, 1 ),
+ /* PushB[1] */ PACK( 0, 2 ),
+ /* PushB[2] */ PACK( 0, 3 ),
+ /* PushB[3] */ PACK( 0, 4 ),
+ /* PushB[4] */ PACK( 0, 5 ),
+ /* PushB[5] */ PACK( 0, 6 ),
+ /* PushB[6] */ PACK( 0, 7 ),
+ /* PushB[7] */ PACK( 0, 8 ),
+ /* PushW[0] */ PACK( 0, 1 ),
+ /* PushW[1] */ PACK( 0, 2 ),
+ /* PushW[2] */ PACK( 0, 3 ),
+ /* PushW[3] */ PACK( 0, 4 ),
+ /* PushW[4] */ PACK( 0, 5 ),
+ /* PushW[5] */ PACK( 0, 6 ),
+ /* PushW[6] */ PACK( 0, 7 ),
+ /* PushW[7] */ PACK( 0, 8 ),
+
+ /* MDRP[00] */ PACK( 1, 0 ),
+ /* MDRP[01] */ PACK( 1, 0 ),
+ /* MDRP[02] */ PACK( 1, 0 ),
+ /* MDRP[03] */ PACK( 1, 0 ),
+ /* MDRP[04] */ PACK( 1, 0 ),
+ /* MDRP[05] */ PACK( 1, 0 ),
+ /* MDRP[06] */ PACK( 1, 0 ),
+ /* MDRP[07] */ PACK( 1, 0 ),
+ /* MDRP[08] */ PACK( 1, 0 ),
+ /* MDRP[09] */ PACK( 1, 0 ),
+ /* MDRP[10] */ PACK( 1, 0 ),
+ /* MDRP[11] */ PACK( 1, 0 ),
+ /* MDRP[12] */ PACK( 1, 0 ),
+ /* MDRP[13] */ PACK( 1, 0 ),
+ /* MDRP[14] */ PACK( 1, 0 ),
+ /* MDRP[15] */ PACK( 1, 0 ),
+
+ /* MDRP[16] */ PACK( 1, 0 ),
+ /* MDRP[17] */ PACK( 1, 0 ),
+ /* MDRP[18] */ PACK( 1, 0 ),
+ /* MDRP[19] */ PACK( 1, 0 ),
+ /* MDRP[20] */ PACK( 1, 0 ),
+ /* MDRP[21] */ PACK( 1, 0 ),
+ /* MDRP[22] */ PACK( 1, 0 ),
+ /* MDRP[23] */ PACK( 1, 0 ),
+ /* MDRP[24] */ PACK( 1, 0 ),
+ /* MDRP[25] */ PACK( 1, 0 ),
+ /* MDRP[26] */ PACK( 1, 0 ),
+ /* MDRP[27] */ PACK( 1, 0 ),
+ /* MDRP[28] */ PACK( 1, 0 ),
+ /* MDRP[29] */ PACK( 1, 0 ),
+ /* MDRP[30] */ PACK( 1, 0 ),
+ /* MDRP[31] */ PACK( 1, 0 ),
+
+ /* MIRP[00] */ PACK( 2, 0 ),
+ /* MIRP[01] */ PACK( 2, 0 ),
+ /* MIRP[02] */ PACK( 2, 0 ),
+ /* MIRP[03] */ PACK( 2, 0 ),
+ /* MIRP[04] */ PACK( 2, 0 ),
+ /* MIRP[05] */ PACK( 2, 0 ),
+ /* MIRP[06] */ PACK( 2, 0 ),
+ /* MIRP[07] */ PACK( 2, 0 ),
+ /* MIRP[08] */ PACK( 2, 0 ),
+ /* MIRP[09] */ PACK( 2, 0 ),
+ /* MIRP[10] */ PACK( 2, 0 ),
+ /* MIRP[11] */ PACK( 2, 0 ),
+ /* MIRP[12] */ PACK( 2, 0 ),
+ /* MIRP[13] */ PACK( 2, 0 ),
+ /* MIRP[14] */ PACK( 2, 0 ),
+ /* MIRP[15] */ PACK( 2, 0 ),
+
+ /* MIRP[16] */ PACK( 2, 0 ),
+ /* MIRP[17] */ PACK( 2, 0 ),
+ /* MIRP[18] */ PACK( 2, 0 ),
+ /* MIRP[19] */ PACK( 2, 0 ),
+ /* MIRP[20] */ PACK( 2, 0 ),
+ /* MIRP[21] */ PACK( 2, 0 ),
+ /* MIRP[22] */ PACK( 2, 0 ),
+ /* MIRP[23] */ PACK( 2, 0 ),
+ /* MIRP[24] */ PACK( 2, 0 ),
+ /* MIRP[25] */ PACK( 2, 0 ),
+ /* MIRP[26] */ PACK( 2, 0 ),
+ /* MIRP[27] */ PACK( 2, 0 ),
+ /* MIRP[28] */ PACK( 2, 0 ),
+ /* MIRP[29] */ PACK( 2, 0 ),
+ /* MIRP[30] */ PACK( 2, 0 ),
+ /* MIRP[31] */ PACK( 2, 0 )
+ };
+
+
+ static
+ const FT_Char opcode_length[256] =
+ {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ -1,-2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9, 3, 5, 7, 9, 11,13,15,17,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+#undef PACK
+
+#if 1
+
+ static FT_Int32
+ TT_MulFix14( FT_Int32 a,
+ FT_Int b )
+ {
+ FT_Int32 sign;
+ FT_UInt32 ah, al, mid, lo, hi;
+
+
+ sign = a ^ b;
+
+ if ( a < 0 )
+ a = -a;
+ if ( b < 0 )
+ b = -b;
+
+ ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU );
+ al = (FT_UInt32)( a & 0xFFFFU );
+
+ lo = al * b;
+ mid = ah * b;
+ hi = mid >> 16;
+ mid = ( mid << 16 ) + ( 1 << 13 ); /* rounding */
+ lo += mid;
+ if ( lo < mid )
+ hi += 1;
+
+ mid = ( lo >> 14 ) | ( hi << 18 );
+
+ return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
+ }
+
+#else
+
+ /* compute (a*b)/2^14 with maximal accuracy and rounding */
+ static FT_Int32
+ TT_MulFix14( FT_Int32 a,
+ FT_Int b )
+ {
+ FT_Int32 m, s, hi;
+ FT_UInt32 l, lo;
+
+
+ /* compute ax*bx as 64-bit value */
+ l = (FT_UInt32)( ( a & 0xFFFFU ) * b );
+ m = ( a >> 16 ) * b;
+
+ lo = l + (FT_UInt32)( m << 16 );
+ hi = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo < l );
+
+ /* divide the result by 2^14 with rounding */
+ s = hi >> 31;
+ l = lo + (FT_UInt32)s;
+ hi += s + ( l < lo );
+ lo = l;
+
+ l = lo + 0x2000U;
+ hi += l < lo;
+
+ return ( hi << 18 ) | ( l >> 14 );
+ }
+#endif
+
+
+ /* compute (ax*bx+ay*by)/2^14 with maximal accuracy and rounding */
+ static FT_Int32
+ TT_DotFix14( FT_Int32 ax,
+ FT_Int32 ay,
+ FT_Int bx,
+ FT_Int by )
+ {
+ FT_Int32 m, s, hi1, hi2, hi;
+ FT_UInt32 l, lo1, lo2, lo;
+
+
+ /* compute ax*bx as 64-bit value */
+ l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx );
+ m = ( ax >> 16 ) * bx;
+
+ lo1 = l + (FT_UInt32)( m << 16 );
+ hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l );
+
+ /* compute ay*by as 64-bit value */
+ l = (FT_UInt32)( ( ay & 0xFFFFU ) * by );
+ m = ( ay >> 16 ) * by;
+
+ lo2 = l + (FT_UInt32)( m << 16 );
+ hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l );
+
+ /* add them */
+ lo = lo1 + lo2;
+ hi = hi1 + hi2 + ( lo < lo1 );
+
+ /* divide the result by 2^14 with rounding */
+ s = hi >> 31;
+ l = lo + (FT_UInt32)s;
+ hi += s + ( l < lo );
+ lo = l;
+
+ l = lo + 0x2000U;
+ hi += ( l < lo );
+
+ return ( hi << 18 ) | ( l >> 14 );
+ }
+
+
+ /* return length of given vector */
+
+#if 0
+
+ static FT_Int32
+ TT_VecLen( FT_Int32 x,
+ FT_Int32 y )
+ {
+ FT_Int32 m, hi1, hi2, hi;
+ FT_UInt32 l, lo1, lo2, lo;
+
+
+ /* compute x*x as 64-bit value */
+ lo = (FT_UInt32)( x & 0xFFFFU );
+ hi = x >> 16;
+
+ l = lo * lo;
+ m = hi * lo;
+ hi = hi * hi;
+
+ lo1 = l + (FT_UInt32)( m << 17 );
+ hi1 = hi + ( m >> 15 ) + ( lo1 < l );
+
+ /* compute y*y as 64-bit value */
+ lo = (FT_UInt32)( y & 0xFFFFU );
+ hi = y >> 16;
+
+ l = lo * lo;
+ m = hi * lo;
+ hi = hi * hi;
+
+ lo2 = l + (FT_UInt32)( m << 17 );
+ hi2 = hi + ( m >> 15 ) + ( lo2 < l );
+
+ /* add them to get 'x*x+y*y' as 64-bit value */
+ lo = lo1 + lo2;
+ hi = hi1 + hi2 + ( lo < lo1 );
+
+ /* compute the square root of this value */
+ {
+ FT_UInt32 root, rem, test_div;
+ FT_Int count;
+
+
+ root = 0;
+
+ {
+ rem = 0;
+ count = 32;
+ do
+ {
+ rem = ( rem << 2 ) | ( (FT_UInt32)hi >> 30 );
+ hi = ( hi << 2 ) | ( lo >> 30 );
+ lo <<= 2;
+ root <<= 1;
+ test_div = ( root << 1 ) + 1;
+
+ if ( rem >= test_div )
+ {
+ rem -= test_div;
+ root += 1;
+ }
+ } while ( --count );
+ }
+
+ return (FT_Int32)root;
+ }
+ }
+
+#else
+
+ /* this version uses FT_Vector_Length which computes the same value */
+ /* much, much faster.. */
+ /* */
+ static FT_F26Dot6
+ TT_VecLen( FT_F26Dot6 X,
+ FT_F26Dot6 Y )
+ {
+ FT_Vector v;
+
+
+ v.x = X;
+ v.y = Y;
+
+ return FT_Vector_Length( &v );
+ }
+
+#endif
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Current_Ratio */
+ /* */
+ /* <Description> */
+ /* Returns the current aspect ratio scaling factor depending on the */
+ /* projection vector's state and device resolutions. */
+ /* */
+ /* <Return> */
+ /* The aspect ratio in 16.16 format, always <= 1.0 . */
+ /* */
+ static FT_Long
+ Current_Ratio( EXEC_OP )
+ {
+ if ( !CUR.tt_metrics.ratio )
+ {
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ if ( CUR.face->unpatented_hinting )
+ {
+ if ( CUR.GS.both_x_axis )
+ CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio;
+ else
+ CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio;
+ }
+ else
+#endif
+ {
+ if ( CUR.GS.projVector.y == 0 )
+ CUR.tt_metrics.ratio = CUR.tt_metrics.x_ratio;
+
+ else if ( CUR.GS.projVector.x == 0 )
+ CUR.tt_metrics.ratio = CUR.tt_metrics.y_ratio;
+
+ else
+ {
+ FT_Long x, y;
+
+
+ x = TT_MULDIV( CUR.GS.projVector.x,
+ CUR.tt_metrics.x_ratio, 0x4000 );
+ y = TT_MULDIV( CUR.GS.projVector.y,
+ CUR.tt_metrics.y_ratio, 0x4000 );
+ CUR.tt_metrics.ratio = TT_VecLen( x, y );
+ }
+ }
+ }
+ return CUR.tt_metrics.ratio;
+ }
+
+
+ static FT_Long
+ Current_Ppem( EXEC_OP )
+ {
+ return TT_MULFIX( CUR.tt_metrics.ppem, CURRENT_Ratio() );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Functions related to the control value table (CVT). */
+ /* */
+ /*************************************************************************/
+
+
+ FT_CALLBACK_DEF( FT_F26Dot6 )
+ Read_CVT( EXEC_OP_ FT_ULong idx )
+ {
+ return CUR.cvt[idx];
+ }
+
+
+ FT_CALLBACK_DEF( FT_F26Dot6 )
+ Read_CVT_Stretched( EXEC_OP_ FT_ULong idx )
+ {
+ return TT_MULFIX( CUR.cvt[idx], CURRENT_Ratio() );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ Write_CVT( EXEC_OP_ FT_ULong idx,
+ FT_F26Dot6 value )
+ {
+ CUR.cvt[idx] = value;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ Write_CVT_Stretched( EXEC_OP_ FT_ULong idx,
+ FT_F26Dot6 value )
+ {
+ CUR.cvt[idx] = FT_DivFix( value, CURRENT_Ratio() );
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ Move_CVT( EXEC_OP_ FT_ULong idx,
+ FT_F26Dot6 value )
+ {
+ CUR.cvt[idx] += value;
+ }
+
+
+ FT_CALLBACK_DEF( void )
+ Move_CVT_Stretched( EXEC_OP_ FT_ULong idx,
+ FT_F26Dot6 value )
+ {
+ CUR.cvt[idx] += FT_DivFix( value, CURRENT_Ratio() );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* GetShortIns */
+ /* */
+ /* <Description> */
+ /* Returns a short integer taken from the instruction stream at */
+ /* address IP. */
+ /* */
+ /* <Return> */
+ /* Short read at code[IP]. */
+ /* */
+ /* <Note> */
+ /* This one could become a macro. */
+ /* */
+ static FT_Short
+ GetShortIns( EXEC_OP )
+ {
+ /* Reading a byte stream so there is no endianess (DaveP) */
+ CUR.IP += 2;
+ return (FT_Short)( ( CUR.code[CUR.IP - 2] << 8 ) +
+ CUR.code[CUR.IP - 1] );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Ins_Goto_CodeRange */
+ /* */
+ /* <Description> */
+ /* Goes to a certain code range in the instruction stream. */
+ /* */
+ /* <Input> */
+ /* aRange :: The index of the code range. */
+ /* */
+ /* aIP :: The new IP address in the code range. */
+ /* */
+ /* <Return> */
+ /* SUCCESS or FAILURE. */
+ /* */
+ static FT_Bool
+ Ins_Goto_CodeRange( EXEC_OP_ FT_Int aRange,
+ FT_ULong aIP )
+ {
+ TT_CodeRange* range;
+
+
+ if ( aRange < 1 || aRange > 3 )
+ {
+ CUR.error = TT_Err_Bad_Argument;
+ return FAILURE;
+ }
+
+ range = &CUR.codeRangeTable[aRange - 1];
+
+ if ( range->base == NULL ) /* invalid coderange */
+ {
+ CUR.error = TT_Err_Invalid_CodeRange;
+ return FAILURE;
+ }
+
+ /* NOTE: Because the last instruction of a program may be a CALL */
+ /* which will return to the first byte *after* the code */
+ /* range, we test for AIP <= Size, instead of AIP < Size. */
+
+ if ( aIP > range->size )
+ {
+ CUR.error = TT_Err_Code_Overflow;
+ return FAILURE;
+ }
+
+ CUR.code = range->base;
+ CUR.codeSize = range->size;
+ CUR.IP = aIP;
+ CUR.curRange = aRange;
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Direct_Move */
+ /* */
+ /* <Description> */
+ /* Moves a point by a given distance along the freedom vector. The */
+ /* point will be `touched'. */
+ /* */
+ /* <Input> */
+ /* point :: The index of the point to move. */
+ /* */
+ /* distance :: The distance to apply. */
+ /* */
+ /* <InOut> */
+ /* zone :: The affected glyph zone. */
+ /* */
+ static void
+ Direct_Move( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_F26Dot6 v;
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ FT_ASSERT( !CUR.face->unpatented_hinting );
+#endif
+
+ v = CUR.GS.freeVector.x;
+
+ if ( v != 0 )
+ {
+ zone->cur[point].x += TT_MULDIV( distance,
+ v * 0x10000L,
+ CUR.F_dot_P );
+
+ zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
+ }
+
+ v = CUR.GS.freeVector.y;
+
+ if ( v != 0 )
+ {
+ zone->cur[point].y += TT_MULDIV( distance,
+ v * 0x10000L,
+ CUR.F_dot_P );
+
+ zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Direct_Move_Orig */
+ /* */
+ /* <Description> */
+ /* Moves the *original* position of a point by a given distance along */
+ /* the freedom vector. Obviously, the point will not be `touched'. */
+ /* */
+ /* <Input> */
+ /* point :: The index of the point to move. */
+ /* */
+ /* distance :: The distance to apply. */
+ /* */
+ /* <InOut> */
+ /* zone :: The affected glyph zone. */
+ /* */
+ static void
+ Direct_Move_Orig( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_F26Dot6 v;
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ FT_ASSERT( !CUR.face->unpatented_hinting );
+#endif
+
+ v = CUR.GS.freeVector.x;
+
+ if ( v != 0 )
+ zone->org[point].x += TT_MULDIV( distance,
+ v * 0x10000L,
+ CUR.F_dot_P );
+
+ v = CUR.GS.freeVector.y;
+
+ if ( v != 0 )
+ zone->org[point].y += TT_MULDIV( distance,
+ v * 0x10000L,
+ CUR.F_dot_P );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Special versions of Direct_Move() */
+ /* */
+ /* The following versions are used whenever both vectors are both */
+ /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */
+ /* */
+ /*************************************************************************/
+
+
+ static void
+ Direct_Move_X( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_UNUSED_EXEC;
+
+ zone->cur[point].x += distance;
+ zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
+ }
+
+
+ static void
+ Direct_Move_Y( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_UNUSED_EXEC;
+
+ zone->cur[point].y += distance;
+ zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Special versions of Direct_Move_Orig() */
+ /* */
+ /* The following versions are used whenever both vectors are both */
+ /* along one of the coordinate unit vectors, i.e. in 90% of the cases. */
+ /* */
+ /*************************************************************************/
+
+
+ static void
+ Direct_Move_Orig_X( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_UNUSED_EXEC;
+
+ zone->org[point].x += distance;
+ }
+
+
+ static void
+ Direct_Move_Orig_Y( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance )
+ {
+ FT_UNUSED_EXEC;
+
+ zone->org[point].y += distance;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_None */
+ /* */
+ /* <Description> */
+ /* Does not round, but adds engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance (not) to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* The compensated distance. */
+ /* */
+ /* <Note> */
+ /* The TrueType specification says very few about the relationship */
+ /* between rounding and engine compensation. However, it seems from */
+ /* the description of super round that we should add the compensation */
+ /* before rounding. */
+ /* */
+ static FT_F26Dot6
+ Round_None( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = distance + compensation;
+ if ( distance && val < 0 )
+ val = 0;
+ }
+ else {
+ val = distance - compensation;
+ if ( val > 0 )
+ val = 0;
+ }
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_To_Grid */
+ /* */
+ /* <Description> */
+ /* Rounds value to grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ static FT_F26Dot6
+ Round_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = distance + compensation + 32;
+ if ( distance && val > 0 )
+ val &= ~63;
+ else
+ val = 0;
+ }
+ else
+ {
+ val = -FT_PIX_ROUND( compensation - distance );
+ if ( val > 0 )
+ val = 0;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_To_Half_Grid */
+ /* */
+ /* <Description> */
+ /* Rounds value to half grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ static FT_F26Dot6
+ Round_To_Half_Grid( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = FT_PIX_FLOOR( distance + compensation ) + 32;
+ if ( distance && val < 0 )
+ val = 0;
+ }
+ else
+ {
+ val = -( FT_PIX_FLOOR( compensation - distance ) + 32 );
+ if ( val > 0 )
+ val = 0;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_Down_To_Grid */
+ /* */
+ /* <Description> */
+ /* Rounds value down to grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ static FT_F26Dot6
+ Round_Down_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = distance + compensation;
+ if ( distance && val > 0 )
+ val &= ~63;
+ else
+ val = 0;
+ }
+ else
+ {
+ val = -( ( compensation - distance ) & -64 );
+ if ( val > 0 )
+ val = 0;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_Up_To_Grid */
+ /* */
+ /* <Description> */
+ /* Rounds value up to grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ static FT_F26Dot6
+ Round_Up_To_Grid( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = distance + compensation + 63;
+ if ( distance && val > 0 )
+ val &= ~63;
+ else
+ val = 0;
+ }
+ else
+ {
+ val = - FT_PIX_CEIL( compensation - distance );
+ if ( val > 0 )
+ val = 0;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_To_Double_Grid */
+ /* */
+ /* <Description> */
+ /* Rounds value to double grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ static FT_F26Dot6
+ Round_To_Double_Grid( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( distance >= 0 )
+ {
+ val = distance + compensation + 16;
+ if ( distance && val > 0 )
+ val &= ~31;
+ else
+ val = 0;
+ }
+ else
+ {
+ val = -FT_PAD_ROUND( compensation - distance, 32 );
+ if ( val > 0 )
+ val = 0;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_Super */
+ /* */
+ /* <Description> */
+ /* Super-rounds value to grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ /* <Note> */
+ /* The TrueType specification says very few about the relationship */
+ /* between rounding and engine compensation. However, it seems from */
+ /* the description of super round that we should add the compensation */
+ /* before rounding. */
+ /* */
+ static FT_F26Dot6
+ Round_Super( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+
+ if ( distance >= 0 )
+ {
+ val = ( distance - CUR.phase + CUR.threshold + compensation ) &
+ -CUR.period;
+ if ( distance && val < 0 )
+ val = 0;
+ val += CUR.phase;
+ }
+ else
+ {
+ val = -( ( CUR.threshold - CUR.phase - distance + compensation ) &
+ -CUR.period );
+ if ( val > 0 )
+ val = 0;
+ val -= CUR.phase;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Round_Super_45 */
+ /* */
+ /* <Description> */
+ /* Super-rounds value to grid after adding engine compensation. */
+ /* */
+ /* <Input> */
+ /* distance :: The distance to round. */
+ /* */
+ /* compensation :: The engine compensation. */
+ /* */
+ /* <Return> */
+ /* Rounded distance. */
+ /* */
+ /* <Note> */
+ /* There is a separate function for Round_Super_45() as we may need */
+ /* greater precision. */
+ /* */
+ static FT_F26Dot6
+ Round_Super_45( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation )
+ {
+ FT_F26Dot6 val;
+
+
+ if ( distance >= 0 )
+ {
+ val = ( ( distance - CUR.phase + CUR.threshold + compensation ) /
+ CUR.period ) * CUR.period;
+ if ( distance && val < 0 )
+ val = 0;
+ val += CUR.phase;
+ }
+ else
+ {
+ val = -( ( ( CUR.threshold - CUR.phase - distance + compensation ) /
+ CUR.period ) * CUR.period );
+ if ( val > 0 )
+ val = 0;
+ val -= CUR.phase;
+ }
+
+ return val;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Compute_Round */
+ /* */
+ /* <Description> */
+ /* Sets the rounding mode. */
+ /* */
+ /* <Input> */
+ /* round_mode :: The rounding mode to be used. */
+ /* */
+ static void
+ Compute_Round( EXEC_OP_ FT_Byte round_mode )
+ {
+ switch ( round_mode )
+ {
+ case TT_Round_Off:
+ CUR.func_round = (TT_Round_Func)Round_None;
+ break;
+
+ case TT_Round_To_Grid:
+ CUR.func_round = (TT_Round_Func)Round_To_Grid;
+ break;
+
+ case TT_Round_Up_To_Grid:
+ CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;
+ break;
+
+ case TT_Round_Down_To_Grid:
+ CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;
+ break;
+
+ case TT_Round_To_Half_Grid:
+ CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;
+ break;
+
+ case TT_Round_To_Double_Grid:
+ CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;
+ break;
+
+ case TT_Round_Super:
+ CUR.func_round = (TT_Round_Func)Round_Super;
+ break;
+
+ case TT_Round_Super_45:
+ CUR.func_round = (TT_Round_Func)Round_Super_45;
+ break;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* SetSuperRound */
+ /* */
+ /* <Description> */
+ /* Sets Super Round parameters. */
+ /* */
+ /* <Input> */
+ /* GridPeriod :: Grid period */
+ /* selector :: SROUND opcode */
+ /* */
+ static void
+ SetSuperRound( EXEC_OP_ FT_F26Dot6 GridPeriod,
+ FT_Long selector )
+ {
+ switch ( (FT_Int)( selector & 0xC0 ) )
+ {
+ case 0:
+ CUR.period = GridPeriod / 2;
+ break;
+
+ case 0x40:
+ CUR.period = GridPeriod;
+ break;
+
+ case 0x80:
+ CUR.period = GridPeriod * 2;
+ break;
+
+ /* This opcode is reserved, but... */
+
+ case 0xC0:
+ CUR.period = GridPeriod;
+ break;
+ }
+
+ switch ( (FT_Int)( selector & 0x30 ) )
+ {
+ case 0:
+ CUR.phase = 0;
+ break;
+
+ case 0x10:
+ CUR.phase = CUR.period / 4;
+ break;
+
+ case 0x20:
+ CUR.phase = CUR.period / 2;
+ break;
+
+ case 0x30:
+ CUR.phase = CUR.period * 3 / 4;
+ break;
+ }
+
+ if ( ( selector & 0x0F ) == 0 )
+ CUR.threshold = CUR.period - 1;
+ else
+ CUR.threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * CUR.period / 8;
+
+ CUR.period /= 256;
+ CUR.phase /= 256;
+ CUR.threshold /= 256;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Project */
+ /* */
+ /* <Description> */
+ /* Computes the projection of vector given by (v2-v1) along the */
+ /* current projection vector. */
+ /* */
+ /* <Input> */
+ /* v1 :: First input vector. */
+ /* v2 :: Second input vector. */
+ /* */
+ /* <Return> */
+ /* The distance in F26dot6 format. */
+ /* */
+ static FT_F26Dot6
+ Project( EXEC_OP_ FT_Pos dx,
+ FT_Pos dy )
+ {
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ FT_ASSERT( !CUR.face->unpatented_hinting );
+#endif
+
+ return TT_DotFix14( dx, dy,
+ CUR.GS.projVector.x,
+ CUR.GS.projVector.y );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Dual_Project */
+ /* */
+ /* <Description> */
+ /* Computes the projection of the vector given by (v2-v1) along the */
+ /* current dual vector. */
+ /* */
+ /* <Input> */
+ /* v1 :: First input vector. */
+ /* v2 :: Second input vector. */
+ /* */
+ /* <Return> */
+ /* The distance in F26dot6 format. */
+ /* */
+ static FT_F26Dot6
+ Dual_Project( EXEC_OP_ FT_Pos dx,
+ FT_Pos dy )
+ {
+ return TT_DotFix14( dx, dy,
+ CUR.GS.dualVector.x,
+ CUR.GS.dualVector.y );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Project_x */
+ /* */
+ /* <Description> */
+ /* Computes the projection of the vector given by (v2-v1) along the */
+ /* horizontal axis. */
+ /* */
+ /* <Input> */
+ /* v1 :: First input vector. */
+ /* v2 :: Second input vector. */
+ /* */
+ /* <Return> */
+ /* The distance in F26dot6 format. */
+ /* */
+ static FT_F26Dot6
+ Project_x( EXEC_OP_ FT_Pos dx,
+ FT_Pos dy )
+ {
+ FT_UNUSED_EXEC;
+ FT_UNUSED( dy );
+
+ return dx;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Project_y */
+ /* */
+ /* <Description> */
+ /* Computes the projection of the vector given by (v2-v1) along the */
+ /* vertical axis. */
+ /* */
+ /* <Input> */
+ /* v1 :: First input vector. */
+ /* v2 :: Second input vector. */
+ /* */
+ /* <Return> */
+ /* The distance in F26dot6 format. */
+ /* */
+ static FT_F26Dot6
+ Project_y( EXEC_OP_ FT_Pos dx,
+ FT_Pos dy )
+ {
+ FT_UNUSED_EXEC;
+ FT_UNUSED( dx );
+
+ return dy;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Compute_Funcs */
+ /* */
+ /* <Description> */
+ /* Computes the projection and movement function pointers according */
+ /* to the current graphics state. */
+ /* */
+ static void
+ Compute_Funcs( EXEC_OP )
+ {
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ if ( CUR.face->unpatented_hinting )
+ {
+ /* If both vectors point rightwards along the x axis, set */
+ /* `both-x-axis' true, otherwise set it false. The x values only */
+ /* need be tested because the vector has been normalised to a unit */
+ /* vector of length 0x4000 = unity. */
+ CUR.GS.both_x_axis = (FT_Bool)( CUR.GS.projVector.x == 0x4000 &&
+ CUR.GS.freeVector.x == 0x4000 );
+
+ /* Throw away projection and freedom vector information */
+ /* because the patents don't allow them to be stored. */
+ /* The relevant US Patents are 5155805 and 5325479. */
+ CUR.GS.projVector.x = 0;
+ CUR.GS.projVector.y = 0;
+ CUR.GS.freeVector.x = 0;
+ CUR.GS.freeVector.y = 0;
+
+ if ( CUR.GS.both_x_axis )
+ {
+ CUR.func_project = Project_x;
+ CUR.func_move = Direct_Move_X;
+ CUR.func_move_orig = Direct_Move_Orig_X;
+ }
+ else
+ {
+ CUR.func_project = Project_y;
+ CUR.func_move = Direct_Move_Y;
+ CUR.func_move_orig = Direct_Move_Orig_Y;
+ }
+
+ if ( CUR.GS.dualVector.x == 0x4000 )
+ CUR.func_dualproj = Project_x;
+ else
+ {
+ if ( CUR.GS.dualVector.y == 0x4000 )
+ CUR.func_dualproj = Project_y;
+ else
+ CUR.func_dualproj = Dual_Project;
+ }
+
+ /* Force recalculation of cached aspect ratio */
+ CUR.tt_metrics.ratio = 0;
+
+ return;
+ }
+#endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING */
+
+ if ( CUR.GS.freeVector.x == 0x4000 )
+ CUR.F_dot_P = CUR.GS.projVector.x * 0x10000L;
+ else
+ {
+ if ( CUR.GS.freeVector.y == 0x4000 )
+ CUR.F_dot_P = CUR.GS.projVector.y * 0x10000L;
+ else
+ CUR.F_dot_P = (FT_Long)CUR.GS.projVector.x * CUR.GS.freeVector.x * 4 +
+ (FT_Long)CUR.GS.projVector.y * CUR.GS.freeVector.y * 4;
+ }
+
+ if ( CUR.GS.projVector.x == 0x4000 )
+ CUR.func_project = (TT_Project_Func)Project_x;
+ else
+ {
+ if ( CUR.GS.projVector.y == 0x4000 )
+ CUR.func_project = (TT_Project_Func)Project_y;
+ else
+ CUR.func_project = (TT_Project_Func)Project;
+ }
+
+ if ( CUR.GS.dualVector.x == 0x4000 )
+ CUR.func_dualproj = (TT_Project_Func)Project_x;
+ else
+ {
+ if ( CUR.GS.dualVector.y == 0x4000 )
+ CUR.func_dualproj = (TT_Project_Func)Project_y;
+ else
+ CUR.func_dualproj = (TT_Project_Func)Dual_Project;
+ }
+
+ CUR.func_move = (TT_Move_Func)Direct_Move;
+ CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig;
+
+ if ( CUR.F_dot_P == 0x40000000L )
+ {
+ if ( CUR.GS.freeVector.x == 0x4000 )
+ {
+ CUR.func_move = (TT_Move_Func)Direct_Move_X;
+ CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig_X;
+ }
+ else
+ {
+ if ( CUR.GS.freeVector.y == 0x4000 )
+ {
+ CUR.func_move = (TT_Move_Func)Direct_Move_Y;
+ CUR.func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
+ }
+ }
+ }
+
+ /* at small sizes, F_dot_P can become too small, resulting */
+ /* in overflows and `spikes' in a number of glyphs like `w'. */
+
+ if ( FT_ABS( CUR.F_dot_P ) < 0x4000000L )
+ CUR.F_dot_P = 0x40000000L;
+
+ /* Disable cached aspect ratio */
+ CUR.tt_metrics.ratio = 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Normalize */
+ /* */
+ /* <Description> */
+ /* Norms a vector. */
+ /* */
+ /* <Input> */
+ /* Vx :: The horizontal input vector coordinate. */
+ /* Vy :: The vertical input vector coordinate. */
+ /* */
+ /* <Output> */
+ /* R :: The normed unit vector. */
+ /* */
+ /* <Return> */
+ /* Returns FAILURE if a vector parameter is zero. */
+ /* */
+ /* <Note> */
+ /* In case Vx and Vy are both zero, Normalize() returns SUCCESS, and */
+ /* R is undefined. */
+ /* */
+
+
+ static FT_Bool
+ Normalize( EXEC_OP_ FT_F26Dot6 Vx,
+ FT_F26Dot6 Vy,
+ FT_UnitVector* R )
+ {
+ FT_F26Dot6 W;
+ FT_Bool S1, S2;
+
+ FT_UNUSED_EXEC;
+
+
+ if ( FT_ABS( Vx ) < 0x10000L && FT_ABS( Vy ) < 0x10000L )
+ {
+ Vx *= 0x100;
+ Vy *= 0x100;
+
+ W = TT_VecLen( Vx, Vy );
+
+ if ( W == 0 )
+ {
+ /* XXX: UNDOCUMENTED! It seems that it is possible to try */
+ /* to normalize the vector (0,0). Return immediately. */
+ return SUCCESS;
+ }
+
+ R->x = (FT_F2Dot14)FT_MulDiv( Vx, 0x4000L, W );
+ R->y = (FT_F2Dot14)FT_MulDiv( Vy, 0x4000L, W );
+
+ return SUCCESS;
+ }
+
+ W = TT_VecLen( Vx, Vy );
+
+ Vx = FT_MulDiv( Vx, 0x4000L, W );
+ Vy = FT_MulDiv( Vy, 0x4000L, W );
+
+ W = Vx * Vx + Vy * Vy;
+
+ /* Now, we want that Sqrt( W ) = 0x4000 */
+ /* Or 0x10000000 <= W < 0x10004000 */
+
+ if ( Vx < 0 )
+ {
+ Vx = -Vx;
+ S1 = TRUE;
+ }
+ else
+ S1 = FALSE;
+
+ if ( Vy < 0 )
+ {
+ Vy = -Vy;
+ S2 = TRUE;
+ }
+ else
+ S2 = FALSE;
+
+ while ( W < 0x10000000L )
+ {
+ /* We need to increase W by a minimal amount */
+ if ( Vx < Vy )
+ Vx++;
+ else
+ Vy++;
+
+ W = Vx * Vx + Vy * Vy;
+ }
+
+ while ( W >= 0x10004000L )
+ {
+ /* We need to decrease W by a minimal amount */
+ if ( Vx < Vy )
+ Vx--;
+ else
+ Vy--;
+
+ W = Vx * Vx + Vy * Vy;
+ }
+
+ /* Note that in various cases, we can only */
+ /* compute a Sqrt(W) of 0x3FFF, eg. Vx = Vy */
+
+ if ( S1 )
+ Vx = -Vx;
+
+ if ( S2 )
+ Vy = -Vy;
+
+ R->x = (FT_F2Dot14)Vx; /* Type conversion */
+ R->y = (FT_F2Dot14)Vy; /* Type conversion */
+
+ return SUCCESS;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Here we start with the implementation of the various opcodes. */
+ /* */
+ /*************************************************************************/
+
+
+ static FT_Bool
+ Ins_SxVTL( EXEC_OP_ FT_UShort aIdx1,
+ FT_UShort aIdx2,
+ FT_Int aOpc,
+ FT_UnitVector* Vec )
+ {
+ FT_Long A, B, C;
+ FT_Vector* p1;
+ FT_Vector* p2;
+
+
+ if ( BOUNDS( aIdx1, CUR.zp2.n_points ) ||
+ BOUNDS( aIdx2, CUR.zp1.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return FAILURE;
+ }
+
+ p1 = CUR.zp1.cur + aIdx2;
+ p2 = CUR.zp2.cur + aIdx1;
+
+ A = p1->x - p2->x;
+ B = p1->y - p2->y;
+
+ if ( ( aOpc & 1 ) != 0 )
+ {
+ C = B; /* counter clockwise rotation */
+ B = A;
+ A = -C;
+ }
+
+ NORMalize( A, B, Vec );
+
+ return SUCCESS;
+ }
+
+
+ /* When not using the big switch statements, the interpreter uses a */
+ /* call table defined later below in this source. Each opcode must */
+ /* thus have a corresponding function, even trivial ones. */
+ /* */
+ /* They are all defined there. */
+
+#define DO_SVTCA \
+ { \
+ FT_Short A, B; \
+ \
+ \
+ A = (FT_Short)( CUR.opcode & 1 ) << 14; \
+ B = A ^ (FT_Short)0x4000; \
+ \
+ CUR.GS.freeVector.x = A; \
+ CUR.GS.projVector.x = A; \
+ CUR.GS.dualVector.x = A; \
+ \
+ CUR.GS.freeVector.y = B; \
+ CUR.GS.projVector.y = B; \
+ CUR.GS.dualVector.y = B; \
+ \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SPVTCA \
+ { \
+ FT_Short A, B; \
+ \
+ \
+ A = (FT_Short)( CUR.opcode & 1 ) << 14; \
+ B = A ^ (FT_Short)0x4000; \
+ \
+ CUR.GS.projVector.x = A; \
+ CUR.GS.dualVector.x = A; \
+ \
+ CUR.GS.projVector.y = B; \
+ CUR.GS.dualVector.y = B; \
+ \
+ GUESS_VECTOR( freeVector ); \
+ \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SFVTCA \
+ { \
+ FT_Short A, B; \
+ \
+ \
+ A = (FT_Short)( CUR.opcode & 1 ) << 14; \
+ B = A ^ (FT_Short)0x4000; \
+ \
+ CUR.GS.freeVector.x = A; \
+ CUR.GS.freeVector.y = B; \
+ \
+ GUESS_VECTOR( projVector ); \
+ \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SPVTL \
+ if ( INS_SxVTL( (FT_UShort)args[1], \
+ (FT_UShort)args[0], \
+ CUR.opcode, \
+ &CUR.GS.projVector ) == SUCCESS ) \
+ { \
+ CUR.GS.dualVector = CUR.GS.projVector; \
+ GUESS_VECTOR( freeVector ); \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SFVTL \
+ if ( INS_SxVTL( (FT_UShort)args[1], \
+ (FT_UShort)args[0], \
+ CUR.opcode, \
+ &CUR.GS.freeVector ) == SUCCESS ) \
+ { \
+ GUESS_VECTOR( projVector ); \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SFVTPV \
+ GUESS_VECTOR( projVector ); \
+ CUR.GS.freeVector = CUR.GS.projVector; \
+ COMPUTE_Funcs();
+
+
+#define DO_SPVFS \
+ { \
+ FT_Short S; \
+ FT_Long X, Y; \
+ \
+ \
+ /* Only use low 16bits, then sign extend */ \
+ S = (FT_Short)args[1]; \
+ Y = (FT_Long)S; \
+ S = (FT_Short)args[0]; \
+ X = (FT_Long)S; \
+ \
+ NORMalize( X, Y, &CUR.GS.projVector ); \
+ \
+ CUR.GS.dualVector = CUR.GS.projVector; \
+ GUESS_VECTOR( freeVector ); \
+ COMPUTE_Funcs(); \
+ }
+
+
+#define DO_SFVFS \
+ { \
+ FT_Short S; \
+ FT_Long X, Y; \
+ \
+ \
+ /* Only use low 16bits, then sign extend */ \
+ S = (FT_Short)args[1]; \
+ Y = (FT_Long)S; \
+ S = (FT_Short)args[0]; \
+ X = S; \
+ \
+ NORMalize( X, Y, &CUR.GS.freeVector ); \
+ GUESS_VECTOR( projVector ); \
+ COMPUTE_Funcs(); \
+ }
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+#define DO_GPV \
+ if ( CUR.face->unpatented_hinting ) \
+ { \
+ args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \
+ args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \
+ } \
+ else \
+ { \
+ args[0] = CUR.GS.projVector.x; \
+ args[1] = CUR.GS.projVector.y; \
+ }
+#else
+#define DO_GPV \
+ args[0] = CUR.GS.projVector.x; \
+ args[1] = CUR.GS.projVector.y;
+#endif
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+#define DO_GFV \
+ if ( CUR.face->unpatented_hinting ) \
+ { \
+ args[0] = CUR.GS.both_x_axis ? 0x4000 : 0; \
+ args[1] = CUR.GS.both_x_axis ? 0 : 0x4000; \
+ } \
+ else \
+ { \
+ args[0] = CUR.GS.freeVector.x; \
+ args[1] = CUR.GS.freeVector.y; \
+ }
+#else
+#define DO_GFV \
+ args[0] = CUR.GS.freeVector.x; \
+ args[1] = CUR.GS.freeVector.y;
+#endif
+
+
+#define DO_SRP0 \
+ CUR.GS.rp0 = (FT_UShort)args[0];
+
+
+#define DO_SRP1 \
+ CUR.GS.rp1 = (FT_UShort)args[0];
+
+
+#define DO_SRP2 \
+ CUR.GS.rp2 = (FT_UShort)args[0];
+
+
+#define DO_RTHG \
+ CUR.GS.round_state = TT_Round_To_Half_Grid; \
+ CUR.func_round = (TT_Round_Func)Round_To_Half_Grid;
+
+
+#define DO_RTG \
+ CUR.GS.round_state = TT_Round_To_Grid; \
+ CUR.func_round = (TT_Round_Func)Round_To_Grid;
+
+
+#define DO_RTDG \
+ CUR.GS.round_state = TT_Round_To_Double_Grid; \
+ CUR.func_round = (TT_Round_Func)Round_To_Double_Grid;
+
+
+#define DO_RUTG \
+ CUR.GS.round_state = TT_Round_Up_To_Grid; \
+ CUR.func_round = (TT_Round_Func)Round_Up_To_Grid;
+
+
+#define DO_RDTG \
+ CUR.GS.round_state = TT_Round_Down_To_Grid; \
+ CUR.func_round = (TT_Round_Func)Round_Down_To_Grid;
+
+
+#define DO_ROFF \
+ CUR.GS.round_state = TT_Round_Off; \
+ CUR.func_round = (TT_Round_Func)Round_None;
+
+
+#define DO_SROUND \
+ SET_SuperRound( 0x4000, args[0] ); \
+ CUR.GS.round_state = TT_Round_Super; \
+ CUR.func_round = (TT_Round_Func)Round_Super;
+
+
+#define DO_S45ROUND \
+ SET_SuperRound( 0x2D41, args[0] ); \
+ CUR.GS.round_state = TT_Round_Super_45; \
+ CUR.func_round = (TT_Round_Func)Round_Super_45;
+
+
+#define DO_SLOOP \
+ if ( args[0] < 0 ) \
+ CUR.error = TT_Err_Bad_Argument; \
+ else \
+ CUR.GS.loop = args[0];
+
+
+#define DO_SMD \
+ CUR.GS.minimum_distance = args[0];
+
+
+#define DO_SCVTCI \
+ CUR.GS.control_value_cutin = (FT_F26Dot6)args[0];
+
+
+#define DO_SSWCI \
+ CUR.GS.single_width_cutin = (FT_F26Dot6)args[0];
+
+
+ /* XXX: UNDOCUMENTED! or bug in the Windows engine? */
+ /* */
+ /* It seems that the value that is read here is */
+ /* expressed in 16.16 format rather than in font */
+ /* units. */
+ /* */
+#define DO_SSW \
+ CUR.GS.single_width_value = (FT_F26Dot6)( args[0] >> 10 );
+
+
+#define DO_FLIPON \
+ CUR.GS.auto_flip = TRUE;
+
+
+#define DO_FLIPOFF \
+ CUR.GS.auto_flip = FALSE;
+
+
+#define DO_SDB \
+ CUR.GS.delta_base = (FT_Short)args[0];
+
+
+#define DO_SDS \
+ CUR.GS.delta_shift = (FT_Short)args[0];
+
+
+#define DO_MD /* nothing */
+
+
+#define DO_MPPEM \
+ args[0] = CURRENT_Ppem();
+
+
+ /* Note: The pointSize should be irrelevant in a given font program; */
+ /* we thus decide to return only the ppem. */
+#if 0
+
+#define DO_MPS \
+ args[0] = CUR.metrics.pointSize;
+
+#else
+
+#define DO_MPS \
+ args[0] = CURRENT_Ppem();
+
+#endif /* 0 */
+
+
+#define DO_DUP \
+ args[1] = args[0];
+
+
+#define DO_CLEAR \
+ CUR.new_top = 0;
+
+
+#define DO_SWAP \
+ { \
+ FT_Long L; \
+ \
+ \
+ L = args[0]; \
+ args[0] = args[1]; \
+ args[1] = L; \
+ }
+
+
+#define DO_DEPTH \
+ args[0] = CUR.top;
+
+
+#define DO_CINDEX \
+ { \
+ FT_Long L; \
+ \
+ \
+ L = args[0]; \
+ \
+ if ( L <= 0 || L > CUR.args ) \
+ CUR.error = TT_Err_Invalid_Reference; \
+ else \
+ args[0] = CUR.stack[CUR.args - L]; \
+ }
+
+
+#define DO_JROT \
+ if ( args[1] != 0 ) \
+ { \
+ CUR.IP += args[0]; \
+ CUR.step_ins = FALSE; \
+ }
+
+
+#define DO_JMPR \
+ CUR.IP += args[0]; \
+ CUR.step_ins = FALSE;
+
+
+#define DO_JROF \
+ if ( args[1] == 0 ) \
+ { \
+ CUR.IP += args[0]; \
+ CUR.step_ins = FALSE; \
+ }
+
+
+#define DO_LT \
+ args[0] = ( args[0] < args[1] );
+
+
+#define DO_LTEQ \
+ args[0] = ( args[0] <= args[1] );
+
+
+#define DO_GT \
+ args[0] = ( args[0] > args[1] );
+
+
+#define DO_GTEQ \
+ args[0] = ( args[0] >= args[1] );
+
+
+#define DO_EQ \
+ args[0] = ( args[0] == args[1] );
+
+
+#define DO_NEQ \
+ args[0] = ( args[0] != args[1] );
+
+
+#define DO_ODD \
+ args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 64 );
+
+
+#define DO_EVEN \
+ args[0] = ( ( CUR_Func_round( args[0], 0 ) & 127 ) == 0 );
+
+
+#define DO_AND \
+ args[0] = ( args[0] && args[1] );
+
+
+#define DO_OR \
+ args[0] = ( args[0] || args[1] );
+
+
+#define DO_NOT \
+ args[0] = !args[0];
+
+
+#define DO_ADD \
+ args[0] += args[1];
+
+
+#define DO_SUB \
+ args[0] -= args[1];
+
+
+#define DO_DIV \
+ if ( args[1] == 0 ) \
+ CUR.error = TT_Err_Divide_By_Zero; \
+ else \
+ args[0] = TT_MULDIV_NO_ROUND( args[0], 64L, args[1] );
+
+
+#define DO_MUL \
+ args[0] = TT_MULDIV( args[0], args[1], 64L );
+
+
+#define DO_ABS \
+ args[0] = FT_ABS( args[0] );
+
+
+#define DO_NEG \
+ args[0] = -args[0];
+
+
+#define DO_FLOOR \
+ args[0] = FT_PIX_FLOOR( args[0] );
+
+
+#define DO_CEILING \
+ args[0] = FT_PIX_CEIL( args[0] );
+
+
+#define DO_RS \
+ { \
+ FT_ULong I = (FT_ULong)args[0]; \
+ \
+ \
+ if ( BOUNDS( I, CUR.storeSize ) ) \
+ { \
+ if ( CUR.pedantic_hinting ) \
+ { \
+ ARRAY_BOUND_ERROR; \
+ } \
+ else \
+ args[0] = 0; \
+ } \
+ else \
+ args[0] = CUR.storage[I]; \
+ }
+
+
+#define DO_WS \
+ { \
+ FT_ULong I = (FT_ULong)args[0]; \
+ \
+ \
+ if ( BOUNDS( I, CUR.storeSize ) ) \
+ { \
+ if ( CUR.pedantic_hinting ) \
+ { \
+ ARRAY_BOUND_ERROR; \
+ } \
+ } \
+ else \
+ CUR.storage[I] = args[1]; \
+ }
+
+
+#define DO_RCVT \
+ { \
+ FT_ULong I = (FT_ULong)args[0]; \
+ \
+ \
+ if ( BOUNDS( I, CUR.cvtSize ) ) \
+ { \
+ if ( CUR.pedantic_hinting ) \
+ { \
+ ARRAY_BOUND_ERROR; \
+ } \
+ else \
+ args[0] = 0; \
+ } \
+ else \
+ args[0] = CUR_Func_read_cvt( I ); \
+ }
+
+
+#define DO_WCVTP \
+ { \
+ FT_ULong I = (FT_ULong)args[0]; \
+ \
+ \
+ if ( BOUNDS( I, CUR.cvtSize ) ) \
+ { \
+ if ( CUR.pedantic_hinting ) \
+ { \
+ ARRAY_BOUND_ERROR; \
+ } \
+ } \
+ else \
+ CUR_Func_write_cvt( I, args[1] ); \
+ }
+
+
+#define DO_WCVTF \
+ { \
+ FT_ULong I = (FT_ULong)args[0]; \
+ \
+ \
+ if ( BOUNDS( I, CUR.cvtSize ) ) \
+ { \
+ if ( CUR.pedantic_hinting ) \
+ { \
+ ARRAY_BOUND_ERROR; \
+ } \
+ } \
+ else \
+ CUR.cvt[I] = TT_MULFIX( args[1], CUR.tt_metrics.scale ); \
+ }
+
+
+#define DO_DEBUG \
+ CUR.error = TT_Err_Debug_OpCode;
+
+
+#define DO_ROUND \
+ args[0] = CUR_Func_round( \
+ args[0], \
+ CUR.tt_metrics.compensations[CUR.opcode - 0x68] );
+
+
+#define DO_NROUND \
+ args[0] = ROUND_None( args[0], \
+ CUR.tt_metrics.compensations[CUR.opcode - 0x6C] );
+
+
+#define DO_MAX \
+ if ( args[1] > args[0] ) \
+ args[0] = args[1];
+
+
+#define DO_MIN \
+ if ( args[1] < args[0] ) \
+ args[0] = args[1];
+
+
+#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
+
+
+#undef ARRAY_BOUND_ERROR
+#define ARRAY_BOUND_ERROR \
+ { \
+ CUR.error = TT_Err_Invalid_Reference; \
+ return; \
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SVTCA[a]: Set (F and P) Vectors to Coordinate Axis */
+ /* Opcode range: 0x00-0x01 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_SVTCA( INS_ARG )
+ {
+ DO_SVTCA
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SPVTCA[a]: Set PVector to Coordinate Axis */
+ /* Opcode range: 0x02-0x03 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_SPVTCA( INS_ARG )
+ {
+ DO_SPVTCA
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SFVTCA[a]: Set FVector to Coordinate Axis */
+ /* Opcode range: 0x04-0x05 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_SFVTCA( INS_ARG )
+ {
+ DO_SFVTCA
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SPVTL[a]: Set PVector To Line */
+ /* Opcode range: 0x06-0x07 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_SPVTL( INS_ARG )
+ {
+ DO_SPVTL
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SFVTL[a]: Set FVector To Line */
+ /* Opcode range: 0x08-0x09 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_SFVTL( INS_ARG )
+ {
+ DO_SFVTL
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SFVTPV[]: Set FVector To PVector */
+ /* Opcode range: 0x0E */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_SFVTPV( INS_ARG )
+ {
+ DO_SFVTPV
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SPVFS[]: Set PVector From Stack */
+ /* Opcode range: 0x0A */
+ /* Stack: f2.14 f2.14 --> */
+ /* */
+ static void
+ Ins_SPVFS( INS_ARG )
+ {
+ DO_SPVFS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SFVFS[]: Set FVector From Stack */
+ /* Opcode range: 0x0B */
+ /* Stack: f2.14 f2.14 --> */
+ /* */
+ static void
+ Ins_SFVFS( INS_ARG )
+ {
+ DO_SFVFS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* GPV[]: Get Projection Vector */
+ /* Opcode range: 0x0C */
+ /* Stack: ef2.14 --> ef2.14 */
+ /* */
+ static void
+ Ins_GPV( INS_ARG )
+ {
+ DO_GPV
+ }
+
+
+ /*************************************************************************/
+ /* GFV[]: Get Freedom Vector */
+ /* Opcode range: 0x0D */
+ /* Stack: ef2.14 --> ef2.14 */
+ /* */
+ static void
+ Ins_GFV( INS_ARG )
+ {
+ DO_GFV
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SRP0[]: Set Reference Point 0 */
+ /* Opcode range: 0x10 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SRP0( INS_ARG )
+ {
+ DO_SRP0
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SRP1[]: Set Reference Point 1 */
+ /* Opcode range: 0x11 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SRP1( INS_ARG )
+ {
+ DO_SRP1
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SRP2[]: Set Reference Point 2 */
+ /* Opcode range: 0x12 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SRP2( INS_ARG )
+ {
+ DO_SRP2
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RTHG[]: Round To Half Grid */
+ /* Opcode range: 0x19 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_RTHG( INS_ARG )
+ {
+ DO_RTHG
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RTG[]: Round To Grid */
+ /* Opcode range: 0x18 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_RTG( INS_ARG )
+ {
+ DO_RTG
+ }
+
+
+ /*************************************************************************/
+ /* RTDG[]: Round To Double Grid */
+ /* Opcode range: 0x3D */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_RTDG( INS_ARG )
+ {
+ DO_RTDG
+ }
+
+
+ /*************************************************************************/
+ /* RUTG[]: Round Up To Grid */
+ /* Opcode range: 0x7C */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_RUTG( INS_ARG )
+ {
+ DO_RUTG
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RDTG[]: Round Down To Grid */
+ /* Opcode range: 0x7D */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_RDTG( INS_ARG )
+ {
+ DO_RDTG
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ROFF[]: Round OFF */
+ /* Opcode range: 0x7A */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_ROFF( INS_ARG )
+ {
+ DO_ROFF
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SROUND[]: Super ROUND */
+ /* Opcode range: 0x76 */
+ /* Stack: Eint8 --> */
+ /* */
+ static void
+ Ins_SROUND( INS_ARG )
+ {
+ DO_SROUND
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* S45ROUND[]: Super ROUND 45 degrees */
+ /* Opcode range: 0x77 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_S45ROUND( INS_ARG )
+ {
+ DO_S45ROUND
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SLOOP[]: Set LOOP variable */
+ /* Opcode range: 0x17 */
+ /* Stack: int32? --> */
+ /* */
+ static void
+ Ins_SLOOP( INS_ARG )
+ {
+ DO_SLOOP
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SMD[]: Set Minimum Distance */
+ /* Opcode range: 0x1A */
+ /* Stack: f26.6 --> */
+ /* */
+ static void
+ Ins_SMD( INS_ARG )
+ {
+ DO_SMD
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SCVTCI[]: Set Control Value Table Cut In */
+ /* Opcode range: 0x1D */
+ /* Stack: f26.6 --> */
+ /* */
+ static void
+ Ins_SCVTCI( INS_ARG )
+ {
+ DO_SCVTCI
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SSWCI[]: Set Single Width Cut In */
+ /* Opcode range: 0x1E */
+ /* Stack: f26.6 --> */
+ /* */
+ static void
+ Ins_SSWCI( INS_ARG )
+ {
+ DO_SSWCI
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SSW[]: Set Single Width */
+ /* Opcode range: 0x1F */
+ /* Stack: int32? --> */
+ /* */
+ static void
+ Ins_SSW( INS_ARG )
+ {
+ DO_SSW
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FLIPON[]: Set auto-FLIP to ON */
+ /* Opcode range: 0x4D */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_FLIPON( INS_ARG )
+ {
+ DO_FLIPON
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FLIPOFF[]: Set auto-FLIP to OFF */
+ /* Opcode range: 0x4E */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_FLIPOFF( INS_ARG )
+ {
+ DO_FLIPOFF
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SANGW[]: Set ANGle Weight */
+ /* Opcode range: 0x7E */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SANGW( INS_ARG )
+ {
+ /* instruction not supported anymore */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SDB[]: Set Delta Base */
+ /* Opcode range: 0x5E */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SDB( INS_ARG )
+ {
+ DO_SDB
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SDS[]: Set Delta Shift */
+ /* Opcode range: 0x5F */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SDS( INS_ARG )
+ {
+ DO_SDS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MPPEM[]: Measure Pixel Per EM */
+ /* Opcode range: 0x4B */
+ /* Stack: --> Euint16 */
+ /* */
+ static void
+ Ins_MPPEM( INS_ARG )
+ {
+ DO_MPPEM
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MPS[]: Measure Point Size */
+ /* Opcode range: 0x4C */
+ /* Stack: --> Euint16 */
+ /* */
+ static void
+ Ins_MPS( INS_ARG )
+ {
+ DO_MPS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DUP[]: DUPlicate the top stack's element */
+ /* Opcode range: 0x20 */
+ /* Stack: StkElt --> StkElt StkElt */
+ /* */
+ static void
+ Ins_DUP( INS_ARG )
+ {
+ DO_DUP
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* POP[]: POP the stack's top element */
+ /* Opcode range: 0x21 */
+ /* Stack: StkElt --> */
+ /* */
+ static void
+ Ins_POP( INS_ARG )
+ {
+ /* nothing to do */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* CLEAR[]: CLEAR the entire stack */
+ /* Opcode range: 0x22 */
+ /* Stack: StkElt... --> */
+ /* */
+ static void
+ Ins_CLEAR( INS_ARG )
+ {
+ DO_CLEAR
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SWAP[]: SWAP the stack's top two elements */
+ /* Opcode range: 0x23 */
+ /* Stack: 2 * StkElt --> 2 * StkElt */
+ /* */
+ static void
+ Ins_SWAP( INS_ARG )
+ {
+ DO_SWAP
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DEPTH[]: return the stack DEPTH */
+ /* Opcode range: 0x24 */
+ /* Stack: --> uint32 */
+ /* */
+ static void
+ Ins_DEPTH( INS_ARG )
+ {
+ DO_DEPTH
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* CINDEX[]: Copy INDEXed element */
+ /* Opcode range: 0x25 */
+ /* Stack: int32 --> StkElt */
+ /* */
+ static void
+ Ins_CINDEX( INS_ARG )
+ {
+ DO_CINDEX
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* EIF[]: End IF */
+ /* Opcode range: 0x59 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_EIF( INS_ARG )
+ {
+ /* nothing to do */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* JROT[]: Jump Relative On True */
+ /* Opcode range: 0x78 */
+ /* Stack: StkElt int32 --> */
+ /* */
+ static void
+ Ins_JROT( INS_ARG )
+ {
+ DO_JROT
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* JMPR[]: JuMP Relative */
+ /* Opcode range: 0x1C */
+ /* Stack: int32 --> */
+ /* */
+ static void
+ Ins_JMPR( INS_ARG )
+ {
+ DO_JMPR
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* JROF[]: Jump Relative On False */
+ /* Opcode range: 0x79 */
+ /* Stack: StkElt int32 --> */
+ /* */
+ static void
+ Ins_JROF( INS_ARG )
+ {
+ DO_JROF
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* LT[]: Less Than */
+ /* Opcode range: 0x50 */
+ /* Stack: int32? int32? --> bool */
+ /* */
+ static void
+ Ins_LT( INS_ARG )
+ {
+ DO_LT
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* LTEQ[]: Less Than or EQual */
+ /* Opcode range: 0x51 */
+ /* Stack: int32? int32? --> bool */
+ /* */
+ static void
+ Ins_LTEQ( INS_ARG )
+ {
+ DO_LTEQ
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* GT[]: Greater Than */
+ /* Opcode range: 0x52 */
+ /* Stack: int32? int32? --> bool */
+ /* */
+ static void
+ Ins_GT( INS_ARG )
+ {
+ DO_GT
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* GTEQ[]: Greater Than or EQual */
+ /* Opcode range: 0x53 */
+ /* Stack: int32? int32? --> bool */
+ /* */
+ static void
+ Ins_GTEQ( INS_ARG )
+ {
+ DO_GTEQ
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* EQ[]: EQual */
+ /* Opcode range: 0x54 */
+ /* Stack: StkElt StkElt --> bool */
+ /* */
+ static void
+ Ins_EQ( INS_ARG )
+ {
+ DO_EQ
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* NEQ[]: Not EQual */
+ /* Opcode range: 0x55 */
+ /* Stack: StkElt StkElt --> bool */
+ /* */
+ static void
+ Ins_NEQ( INS_ARG )
+ {
+ DO_NEQ
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ODD[]: Is ODD */
+ /* Opcode range: 0x56 */
+ /* Stack: f26.6 --> bool */
+ /* */
+ static void
+ Ins_ODD( INS_ARG )
+ {
+ DO_ODD
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* EVEN[]: Is EVEN */
+ /* Opcode range: 0x57 */
+ /* Stack: f26.6 --> bool */
+ /* */
+ static void
+ Ins_EVEN( INS_ARG )
+ {
+ DO_EVEN
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* AND[]: logical AND */
+ /* Opcode range: 0x5A */
+ /* Stack: uint32 uint32 --> uint32 */
+ /* */
+ static void
+ Ins_AND( INS_ARG )
+ {
+ DO_AND
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* OR[]: logical OR */
+ /* Opcode range: 0x5B */
+ /* Stack: uint32 uint32 --> uint32 */
+ /* */
+ static void
+ Ins_OR( INS_ARG )
+ {
+ DO_OR
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* NOT[]: logical NOT */
+ /* Opcode range: 0x5C */
+ /* Stack: StkElt --> uint32 */
+ /* */
+ static void
+ Ins_NOT( INS_ARG )
+ {
+ DO_NOT
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ADD[]: ADD */
+ /* Opcode range: 0x60 */
+ /* Stack: f26.6 f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_ADD( INS_ARG )
+ {
+ DO_ADD
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SUB[]: SUBtract */
+ /* Opcode range: 0x61 */
+ /* Stack: f26.6 f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_SUB( INS_ARG )
+ {
+ DO_SUB
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DIV[]: DIVide */
+ /* Opcode range: 0x62 */
+ /* Stack: f26.6 f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_DIV( INS_ARG )
+ {
+ DO_DIV
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MUL[]: MULtiply */
+ /* Opcode range: 0x63 */
+ /* Stack: f26.6 f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_MUL( INS_ARG )
+ {
+ DO_MUL
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ABS[]: ABSolute value */
+ /* Opcode range: 0x64 */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_ABS( INS_ARG )
+ {
+ DO_ABS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* NEG[]: NEGate */
+ /* Opcode range: 0x65 */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_NEG( INS_ARG )
+ {
+ DO_NEG
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FLOOR[]: FLOOR */
+ /* Opcode range: 0x66 */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_FLOOR( INS_ARG )
+ {
+ DO_FLOOR
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* CEILING[]: CEILING */
+ /* Opcode range: 0x67 */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_CEILING( INS_ARG )
+ {
+ DO_CEILING
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RS[]: Read Store */
+ /* Opcode range: 0x43 */
+ /* Stack: uint32 --> uint32 */
+ /* */
+ static void
+ Ins_RS( INS_ARG )
+ {
+ DO_RS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* WS[]: Write Store */
+ /* Opcode range: 0x42 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_WS( INS_ARG )
+ {
+ DO_WS
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* WCVTP[]: Write CVT in Pixel units */
+ /* Opcode range: 0x44 */
+ /* Stack: f26.6 uint32 --> */
+ /* */
+ static void
+ Ins_WCVTP( INS_ARG )
+ {
+ DO_WCVTP
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* WCVTF[]: Write CVT in Funits */
+ /* Opcode range: 0x70 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_WCVTF( INS_ARG )
+ {
+ DO_WCVTF
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* RCVT[]: Read CVT */
+ /* Opcode range: 0x45 */
+ /* Stack: uint32 --> f26.6 */
+ /* */
+ static void
+ Ins_RCVT( INS_ARG )
+ {
+ DO_RCVT
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* AA[]: Adjust Angle */
+ /* Opcode range: 0x7F */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_AA( INS_ARG )
+ {
+ /* intentionally no longer supported */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DEBUG[]: DEBUG. Unsupported. */
+ /* Opcode range: 0x4F */
+ /* Stack: uint32 --> */
+ /* */
+ /* Note: The original instruction pops a value from the stack. */
+ /* */
+ static void
+ Ins_DEBUG( INS_ARG )
+ {
+ DO_DEBUG
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ROUND[ab]: ROUND value */
+ /* Opcode range: 0x68-0x6B */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_ROUND( INS_ARG )
+ {
+ DO_ROUND
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* NROUND[ab]: No ROUNDing of value */
+ /* Opcode range: 0x6C-0x6F */
+ /* Stack: f26.6 --> f26.6 */
+ /* */
+ static void
+ Ins_NROUND( INS_ARG )
+ {
+ DO_NROUND
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MAX[]: MAXimum */
+ /* Opcode range: 0x68 */
+ /* Stack: int32? int32? --> int32 */
+ /* */
+ static void
+ Ins_MAX( INS_ARG )
+ {
+ DO_MAX
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MIN[]: MINimum */
+ /* Opcode range: 0x69 */
+ /* Stack: int32? int32? --> int32 */
+ /* */
+ static void
+ Ins_MIN( INS_ARG )
+ {
+ DO_MIN
+ }
+
+
+#endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */
+
+
+ /*************************************************************************/
+ /* */
+ /* The following functions are called as is within the switch statement. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* MINDEX[]: Move INDEXed element */
+ /* Opcode range: 0x26 */
+ /* Stack: int32? --> StkElt */
+ /* */
+ static void
+ Ins_MINDEX( INS_ARG )
+ {
+ FT_Long L, K;
+
+
+ L = args[0];
+
+ if ( L <= 0 || L > CUR.args )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ K = CUR.stack[CUR.args - L];
+
+ FT_ARRAY_MOVE( &CUR.stack[CUR.args - L ],
+ &CUR.stack[CUR.args - L + 1],
+ ( L - 1 ) );
+
+ CUR.stack[CUR.args - 1] = K;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ROLL[]: ROLL top three elements */
+ /* Opcode range: 0x8A */
+ /* Stack: 3 * StkElt --> 3 * StkElt */
+ /* */
+ static void
+ Ins_ROLL( INS_ARG )
+ {
+ FT_Long A, B, C;
+
+ FT_UNUSED_EXEC;
+
+
+ A = args[2];
+ B = args[1];
+ C = args[0];
+
+ args[2] = C;
+ args[1] = A;
+ args[0] = B;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MANAGING THE FLOW OF CONTROL */
+ /* */
+ /* Instructions appear in the specification's order. */
+ /* */
+ /*************************************************************************/
+
+
+ static FT_Bool
+ SkipCode( EXEC_OP )
+ {
+ CUR.IP += CUR.length;
+
+ if ( CUR.IP < CUR.codeSize )
+ {
+ CUR.opcode = CUR.code[CUR.IP];
+
+ CUR.length = opcode_length[CUR.opcode];
+ if ( CUR.length < 0 )
+ {
+ if ( CUR.IP + 1 > CUR.codeSize )
+ goto Fail_Overflow;
+ CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1];
+ }
+
+ if ( CUR.IP + CUR.length <= CUR.codeSize )
+ return SUCCESS;
+ }
+
+ Fail_Overflow:
+ CUR.error = TT_Err_Code_Overflow;
+ return FAILURE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* IF[]: IF test */
+ /* Opcode range: 0x58 */
+ /* Stack: StkElt --> */
+ /* */
+ static void
+ Ins_IF( INS_ARG )
+ {
+ FT_Int nIfs;
+ FT_Bool Out;
+
+
+ if ( args[0] != 0 )
+ return;
+
+ nIfs = 1;
+ Out = 0;
+
+ do
+ {
+ if ( SKIP_Code() == FAILURE )
+ return;
+
+ switch ( CUR.opcode )
+ {
+ case 0x58: /* IF */
+ nIfs++;
+ break;
+
+ case 0x1B: /* ELSE */
+ Out = FT_BOOL( nIfs == 1 );
+ break;
+
+ case 0x59: /* EIF */
+ nIfs--;
+ Out = FT_BOOL( nIfs == 0 );
+ break;
+ }
+ } while ( Out == 0 );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ELSE[]: ELSE */
+ /* Opcode range: 0x1B */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_ELSE( INS_ARG )
+ {
+ FT_Int nIfs;
+
+ FT_UNUSED_ARG;
+
+
+ nIfs = 1;
+
+ do
+ {
+ if ( SKIP_Code() == FAILURE )
+ return;
+
+ switch ( CUR.opcode )
+ {
+ case 0x58: /* IF */
+ nIfs++;
+ break;
+
+ case 0x59: /* EIF */
+ nIfs--;
+ break;
+ }
+ } while ( nIfs != 0 );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DEFINING AND USING FUNCTIONS AND INSTRUCTIONS */
+ /* */
+ /* Instructions appear in the specification's order. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* FDEF[]: Function DEFinition */
+ /* Opcode range: 0x2C */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_FDEF( INS_ARG )
+ {
+ FT_ULong n;
+ TT_DefRecord* rec;
+ TT_DefRecord* limit;
+
+
+ /* some font programs are broken enough to redefine functions! */
+ /* We will then parse the current table. */
+
+ rec = CUR.FDefs;
+ limit = rec + CUR.numFDefs;
+ n = args[0];
+
+ for ( ; rec < limit; rec++ )
+ {
+ if ( rec->opc == n )
+ break;
+ }
+
+ if ( rec == limit )
+ {
+ /* check that there is enough room for new functions */
+ if ( CUR.numFDefs >= CUR.maxFDefs )
+ {
+ CUR.error = TT_Err_Too_Many_Function_Defs;
+ return;
+ }
+ CUR.numFDefs++;
+ }
+
+ rec->range = CUR.curRange;
+ rec->opc = n;
+ rec->start = CUR.IP + 1;
+ rec->active = TRUE;
+
+ if ( n > CUR.maxFunc )
+ CUR.maxFunc = n;
+
+ /* Now skip the whole function definition. */
+ /* We don't allow nested IDEFS & FDEFs. */
+
+ while ( SKIP_Code() == SUCCESS )
+ {
+ switch ( CUR.opcode )
+ {
+ case 0x89: /* IDEF */
+ case 0x2C: /* FDEF */
+ CUR.error = TT_Err_Nested_DEFS;
+ return;
+
+ case 0x2D: /* ENDF */
+ return;
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ENDF[]: END Function definition */
+ /* Opcode range: 0x2D */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_ENDF( INS_ARG )
+ {
+ TT_CallRec* pRec;
+
+ FT_UNUSED_ARG;
+
+
+ if ( CUR.callTop <= 0 ) /* We encountered an ENDF without a call */
+ {
+ CUR.error = TT_Err_ENDF_In_Exec_Stream;
+ return;
+ }
+
+ CUR.callTop--;
+
+ pRec = &CUR.callStack[CUR.callTop];
+
+ pRec->Cur_Count--;
+
+ CUR.step_ins = FALSE;
+
+ if ( pRec->Cur_Count > 0 )
+ {
+ CUR.callTop++;
+ CUR.IP = pRec->Cur_Restart;
+ }
+ else
+ /* Loop through the current function */
+ INS_Goto_CodeRange( pRec->Caller_Range,
+ pRec->Caller_IP );
+
+ /* Exit the current call frame. */
+
+ /* NOTE: If the last instruction of a program is a */
+ /* CALL or LOOPCALL, the return address is */
+ /* always out of the code range. This is a */
+ /* valid address, and it is why we do not test */
+ /* the result of Ins_Goto_CodeRange() here! */
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* CALL[]: CALL function */
+ /* Opcode range: 0x2B */
+ /* Stack: uint32? --> */
+ /* */
+ static void
+ Ins_CALL( INS_ARG )
+ {
+ FT_ULong F;
+ TT_CallRec* pCrec;
+ TT_DefRecord* def;
+
+
+ /* first of all, check the index */
+
+ F = args[0];
+ if ( BOUNDS( F, CUR.maxFunc + 1 ) )
+ goto Fail;
+
+ /* Except for some old Apple fonts, all functions in a TrueType */
+ /* font are defined in increasing order, starting from 0. This */
+ /* means that we normally have */
+ /* */
+ /* CUR.maxFunc+1 == CUR.numFDefs */
+ /* CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc */
+ /* */
+ /* If this isn't true, we need to look up the function table. */
+
+ def = CUR.FDefs + F;
+ if ( CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F )
+ {
+ /* look up the FDefs table */
+ TT_DefRecord* limit;
+
+
+ def = CUR.FDefs;
+ limit = def + CUR.numFDefs;
+
+ while ( def < limit && def->opc != F )
+ def++;
+
+ if ( def == limit )
+ goto Fail;
+ }
+
+ /* check that the function is active */
+ if ( !def->active )
+ goto Fail;
+
+ /* check the call stack */
+ if ( CUR.callTop >= CUR.callSize )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ pCrec = CUR.callStack + CUR.callTop;
+
+ pCrec->Caller_Range = CUR.curRange;
+ pCrec->Caller_IP = CUR.IP + 1;
+ pCrec->Cur_Count = 1;
+ pCrec->Cur_Restart = def->start;
+
+ CUR.callTop++;
+
+ INS_Goto_CodeRange( def->range,
+ def->start );
+
+ CUR.step_ins = FALSE;
+ return;
+
+ Fail:
+ CUR.error = TT_Err_Invalid_Reference;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* LOOPCALL[]: LOOP and CALL function */
+ /* Opcode range: 0x2A */
+ /* Stack: uint32? Eint16? --> */
+ /* */
+ static void
+ Ins_LOOPCALL( INS_ARG )
+ {
+ FT_ULong F;
+ TT_CallRec* pCrec;
+ TT_DefRecord* def;
+
+
+ /* first of all, check the index */
+ F = args[1];
+ if ( BOUNDS( F, CUR.maxFunc + 1 ) )
+ goto Fail;
+
+ /* Except for some old Apple fonts, all functions in a TrueType */
+ /* font are defined in increasing order, starting from 0. This */
+ /* means that we normally have */
+ /* */
+ /* CUR.maxFunc+1 == CUR.numFDefs */
+ /* CUR.FDefs[n].opc == n for n in 0..CUR.maxFunc */
+ /* */
+ /* If this isn't true, we need to look up the function table. */
+
+ def = CUR.FDefs + F;
+ if ( CUR.maxFunc + 1 != CUR.numFDefs || def->opc != F )
+ {
+ /* look up the FDefs table */
+ TT_DefRecord* limit;
+
+
+ def = CUR.FDefs;
+ limit = def + CUR.numFDefs;
+
+ while ( def < limit && def->opc != F )
+ def++;
+
+ if ( def == limit )
+ goto Fail;
+ }
+
+ /* check that the function is active */
+ if ( !def->active )
+ goto Fail;
+
+ /* check stack */
+ if ( CUR.callTop >= CUR.callSize )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ if ( args[0] > 0 )
+ {
+ pCrec = CUR.callStack + CUR.callTop;
+
+ pCrec->Caller_Range = CUR.curRange;
+ pCrec->Caller_IP = CUR.IP + 1;
+ pCrec->Cur_Count = (FT_Int)args[0];
+ pCrec->Cur_Restart = def->start;
+
+ CUR.callTop++;
+
+ INS_Goto_CodeRange( def->range, def->start );
+
+ CUR.step_ins = FALSE;
+ }
+ return;
+
+ Fail:
+ CUR.error = TT_Err_Invalid_Reference;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* IDEF[]: Instruction DEFinition */
+ /* Opcode range: 0x89 */
+ /* Stack: Eint8 --> */
+ /* */
+ static void
+ Ins_IDEF( INS_ARG )
+ {
+ TT_DefRecord* def;
+ TT_DefRecord* limit;
+
+
+ /* First of all, look for the same function in our table */
+
+ def = CUR.IDefs;
+ limit = def + CUR.numIDefs;
+
+ for ( ; def < limit; def++ )
+ if ( def->opc == (FT_ULong)args[0] )
+ break;
+
+ if ( def == limit )
+ {
+ /* check that there is enough room for a new instruction */
+ if ( CUR.numIDefs >= CUR.maxIDefs )
+ {
+ CUR.error = TT_Err_Too_Many_Instruction_Defs;
+ return;
+ }
+ CUR.numIDefs++;
+ }
+
+ def->opc = args[0];
+ def->start = CUR.IP+1;
+ def->range = CUR.curRange;
+ def->active = TRUE;
+
+ if ( (FT_ULong)args[0] > CUR.maxIns )
+ CUR.maxIns = args[0];
+
+ /* Now skip the whole function definition. */
+ /* We don't allow nested IDEFs & FDEFs. */
+
+ while ( SKIP_Code() == SUCCESS )
+ {
+ switch ( CUR.opcode )
+ {
+ case 0x89: /* IDEF */
+ case 0x2C: /* FDEF */
+ CUR.error = TT_Err_Nested_DEFS;
+ return;
+ case 0x2D: /* ENDF */
+ return;
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* PUSHING DATA ONTO THE INTERPRETER STACK */
+ /* */
+ /* Instructions appear in the specification's order. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* NPUSHB[]: PUSH N Bytes */
+ /* Opcode range: 0x40 */
+ /* Stack: --> uint32... */
+ /* */
+ static void
+ Ins_NPUSHB( INS_ARG )
+ {
+ FT_UShort L, K;
+
+
+ L = (FT_UShort)CUR.code[CUR.IP + 1];
+
+ if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ for ( K = 1; K <= L; K++ )
+ args[K - 1] = CUR.code[CUR.IP + K + 1];
+
+ CUR.new_top += L;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* NPUSHW[]: PUSH N Words */
+ /* Opcode range: 0x41 */
+ /* Stack: --> int32... */
+ /* */
+ static void
+ Ins_NPUSHW( INS_ARG )
+ {
+ FT_UShort L, K;
+
+
+ L = (FT_UShort)CUR.code[CUR.IP + 1];
+
+ if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ CUR.IP += 2;
+
+ for ( K = 0; K < L; K++ )
+ args[K] = GET_ShortIns();
+
+ CUR.step_ins = FALSE;
+ CUR.new_top += L;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* PUSHB[abc]: PUSH Bytes */
+ /* Opcode range: 0xB0-0xB7 */
+ /* Stack: --> uint32... */
+ /* */
+ static void
+ Ins_PUSHB( INS_ARG )
+ {
+ FT_UShort L, K;
+
+
+ L = (FT_UShort)( CUR.opcode - 0xB0 + 1 );
+
+ if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ for ( K = 1; K <= L; K++ )
+ args[K - 1] = CUR.code[CUR.IP + K];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* PUSHW[abc]: PUSH Words */
+ /* Opcode range: 0xB8-0xBF */
+ /* Stack: --> int32... */
+ /* */
+ static void
+ Ins_PUSHW( INS_ARG )
+ {
+ FT_UShort L, K;
+
+
+ L = (FT_UShort)( CUR.opcode - 0xB8 + 1 );
+
+ if ( BOUNDS( L, CUR.stackSize + 1 - CUR.top ) )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ CUR.IP++;
+
+ for ( K = 0; K < L; K++ )
+ args[K] = GET_ShortIns();
+
+ CUR.step_ins = FALSE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MANAGING THE GRAPHICS STATE */
+ /* */
+ /* Instructions appear in the specs' order. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* GC[a]: Get Coordinate projected onto */
+ /* Opcode range: 0x46-0x47 */
+ /* Stack: uint32 --> f26.6 */
+ /* */
+ /* BULLSHIT: Measures from the original glyph must be taken along the */
+ /* dual projection vector! */
+ /* */
+ static void
+ Ins_GC( INS_ARG )
+ {
+ FT_ULong L;
+ FT_F26Dot6 R;
+
+
+ L = (FT_ULong)args[0];
+
+ if ( BOUNDS( L, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ else
+ R = 0;
+ }
+ else
+ {
+ if ( CUR.opcode & 1 )
+ R = CUR_fast_dualproj( &CUR.zp2.org[L] );
+ else
+ R = CUR_fast_project( &CUR.zp2.cur[L] );
+ }
+
+ args[0] = R;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SCFS[]: Set Coordinate From Stack */
+ /* Opcode range: 0x48 */
+ /* Stack: f26.6 uint32 --> */
+ /* */
+ /* Formula: */
+ /* */
+ /* OA := OA + ( value - OA.p )/( f.p ) * f */
+ /* */
+ static void
+ Ins_SCFS( INS_ARG )
+ {
+ FT_Long K;
+ FT_UShort L;
+
+
+ L = (FT_UShort)args[0];
+
+ if ( BOUNDS( L, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ K = CUR_fast_project( &CUR.zp2.cur[L] );
+
+ CUR_Func_move( &CUR.zp2, L, args[1] - K );
+
+ /* not part of the specs, but here for safety */
+
+ if ( CUR.GS.gep2 == 0 )
+ CUR.zp2.org[L] = CUR.zp2.cur[L];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MD[a]: Measure Distance */
+ /* Opcode range: 0x49-0x4A */
+ /* Stack: uint32 uint32 --> f26.6 */
+ /* */
+ /* BULLSHIT: Measure taken in the original glyph must be along the dual */
+ /* projection vector. */
+ /* */
+ /* Second BULLSHIT: Flag attributes are inverted! */
+ /* 0 => measure distance in original outline */
+ /* 1 => measure distance in grid-fitted outline */
+ /* */
+ /* Third one: `zp0 - zp1', and not `zp2 - zp1! */
+ /* */
+ static void
+ Ins_MD( INS_ARG )
+ {
+ FT_UShort K, L;
+ FT_F26Dot6 D;
+
+
+ K = (FT_UShort)args[1];
+ L = (FT_UShort)args[0];
+
+ if( BOUNDS( L, CUR.zp0.n_points ) ||
+ BOUNDS( K, CUR.zp1.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ D = 0;
+ }
+ else
+ {
+ if ( CUR.opcode & 1 )
+ D = CUR_Func_project( CUR.zp0.cur + L, CUR.zp1.cur + K );
+ else
+ D = CUR_Func_dualproj( CUR.zp0.org + L, CUR.zp1.org + K );
+ }
+
+ args[0] = D;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SDPVTL[a]: Set Dual PVector to Line */
+ /* Opcode range: 0x86-0x87 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_SDPVTL( INS_ARG )
+ {
+ FT_Long A, B, C;
+ FT_UShort p1, p2; /* was FT_Int in pas type ERROR */
+
+
+ p1 = (FT_UShort)args[1];
+ p2 = (FT_UShort)args[0];
+
+ if ( BOUNDS( p2, CUR.zp1.n_points ) ||
+ BOUNDS( p1, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ {
+ FT_Vector* v1 = CUR.zp1.org + p2;
+ FT_Vector* v2 = CUR.zp2.org + p1;
+
+
+ A = v1->x - v2->x;
+ B = v1->y - v2->y;
+ }
+
+ if ( ( CUR.opcode & 1 ) != 0 )
+ {
+ C = B; /* counter clockwise rotation */
+ B = A;
+ A = -C;
+ }
+
+ NORMalize( A, B, &CUR.GS.dualVector );
+
+ {
+ FT_Vector* v1 = CUR.zp1.cur + p2;
+ FT_Vector* v2 = CUR.zp2.cur + p1;
+
+
+ A = v1->x - v2->x;
+ B = v1->y - v2->y;
+ }
+
+ if ( ( CUR.opcode & 1 ) != 0 )
+ {
+ C = B; /* counter clockwise rotation */
+ B = A;
+ A = -C;
+ }
+
+ NORMalize( A, B, &CUR.GS.projVector );
+
+ GUESS_VECTOR( freeVector );
+
+ COMPUTE_Funcs();
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SZP0[]: Set Zone Pointer 0 */
+ /* Opcode range: 0x13 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SZP0( INS_ARG )
+ {
+ switch ( (FT_Int)args[0] )
+ {
+ case 0:
+ CUR.zp0 = CUR.twilight;
+ break;
+
+ case 1:
+ CUR.zp0 = CUR.pts;
+ break;
+
+ default:
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ CUR.GS.gep0 = (FT_UShort)args[0];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SZP1[]: Set Zone Pointer 1 */
+ /* Opcode range: 0x14 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SZP1( INS_ARG )
+ {
+ switch ( (FT_Int)args[0] )
+ {
+ case 0:
+ CUR.zp1 = CUR.twilight;
+ break;
+
+ case 1:
+ CUR.zp1 = CUR.pts;
+ break;
+
+ default:
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ CUR.GS.gep1 = (FT_UShort)args[0];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SZP2[]: Set Zone Pointer 2 */
+ /* Opcode range: 0x15 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SZP2( INS_ARG )
+ {
+ switch ( (FT_Int)args[0] )
+ {
+ case 0:
+ CUR.zp2 = CUR.twilight;
+ break;
+
+ case 1:
+ CUR.zp2 = CUR.pts;
+ break;
+
+ default:
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ CUR.GS.gep2 = (FT_UShort)args[0];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SZPS[]: Set Zone PointerS */
+ /* Opcode range: 0x16 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SZPS( INS_ARG )
+ {
+ switch ( (FT_Int)args[0] )
+ {
+ case 0:
+ CUR.zp0 = CUR.twilight;
+ break;
+
+ case 1:
+ CUR.zp0 = CUR.pts;
+ break;
+
+ default:
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ CUR.zp1 = CUR.zp0;
+ CUR.zp2 = CUR.zp0;
+
+ CUR.GS.gep0 = (FT_UShort)args[0];
+ CUR.GS.gep1 = (FT_UShort)args[0];
+ CUR.GS.gep2 = (FT_UShort)args[0];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* INSTCTRL[]: INSTruction ConTRoL */
+ /* Opcode range: 0x8e */
+ /* Stack: int32 int32 --> */
+ /* */
+ static void
+ Ins_INSTCTRL( INS_ARG )
+ {
+ FT_Long K, L;
+
+
+ K = args[1];
+ L = args[0];
+
+ if ( K < 1 || K > 2 )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( L != 0 )
+ L = K;
+
+ CUR.GS.instruct_control = FT_BOOL(
+ ( (FT_Byte)CUR.GS.instruct_control & ~(FT_Byte)K ) | (FT_Byte)L );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SCANCTRL[]: SCAN ConTRoL */
+ /* Opcode range: 0x85 */
+ /* Stack: uint32? --> */
+ /* */
+ static void
+ Ins_SCANCTRL( INS_ARG )
+ {
+ FT_Int A;
+
+
+ /* Get Threshold */
+ A = (FT_Int)( args[0] & 0xFF );
+
+ if ( A == 0xFF )
+ {
+ CUR.GS.scan_control = TRUE;
+ return;
+ }
+ else if ( A == 0 )
+ {
+ CUR.GS.scan_control = FALSE;
+ return;
+ }
+
+ A *= 64;
+
+#if 0
+ if ( ( args[0] & 0x100 ) != 0 && CUR.metrics.pointSize <= A )
+ CUR.GS.scan_control = TRUE;
+#endif
+
+ if ( ( args[0] & 0x200 ) != 0 && CUR.tt_metrics.rotated )
+ CUR.GS.scan_control = TRUE;
+
+ if ( ( args[0] & 0x400 ) != 0 && CUR.tt_metrics.stretched )
+ CUR.GS.scan_control = TRUE;
+
+#if 0
+ if ( ( args[0] & 0x800 ) != 0 && CUR.metrics.pointSize > A )
+ CUR.GS.scan_control = FALSE;
+#endif
+
+ if ( ( args[0] & 0x1000 ) != 0 && CUR.tt_metrics.rotated )
+ CUR.GS.scan_control = FALSE;
+
+ if ( ( args[0] & 0x2000 ) != 0 && CUR.tt_metrics.stretched )
+ CUR.GS.scan_control = FALSE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SCANTYPE[]: SCAN TYPE */
+ /* Opcode range: 0x8D */
+ /* Stack: uint32? --> */
+ /* */
+ static void
+ Ins_SCANTYPE( INS_ARG )
+ {
+ /* for compatibility with future enhancements, */
+ /* we must ignore new modes */
+
+ if ( args[0] >= 0 && args[0] <= 5 )
+ {
+ if ( args[0] == 3 )
+ args[0] = 2;
+
+ CUR.GS.scan_type = (FT_Int)args[0];
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MANAGING OUTLINES */
+ /* */
+ /* Instructions appear in the specification's order. */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* FLIPPT[]: FLIP PoinT */
+ /* Opcode range: 0x80 */
+ /* Stack: uint32... --> */
+ /* */
+ static void
+ Ins_FLIPPT( INS_ARG )
+ {
+ FT_UShort point;
+
+ FT_UNUSED_ARG;
+
+
+ if ( CUR.top < CUR.GS.loop )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ return;
+ }
+
+ while ( CUR.GS.loop > 0 )
+ {
+ CUR.args--;
+
+ point = (FT_UShort)CUR.stack[CUR.args];
+
+ if ( BOUNDS( point, CUR.pts.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ }
+ else
+ CUR.pts.tags[point] ^= FT_CURVE_TAG_ON;
+
+ CUR.GS.loop--;
+ }
+
+ CUR.GS.loop = 1;
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FLIPRGON[]: FLIP RanGe ON */
+ /* Opcode range: 0x81 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_FLIPRGON( INS_ARG )
+ {
+ FT_UShort I, K, L;
+
+
+ K = (FT_UShort)args[1];
+ L = (FT_UShort)args[0];
+
+ if ( BOUNDS( K, CUR.pts.n_points ) ||
+ BOUNDS( L, CUR.pts.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ for ( I = L; I <= K; I++ )
+ CUR.pts.tags[I] |= FT_CURVE_TAG_ON;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FLIPRGOFF: FLIP RanGe OFF */
+ /* Opcode range: 0x82 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_FLIPRGOFF( INS_ARG )
+ {
+ FT_UShort I, K, L;
+
+
+ K = (FT_UShort)args[1];
+ L = (FT_UShort)args[0];
+
+ if ( BOUNDS( K, CUR.pts.n_points ) ||
+ BOUNDS( L, CUR.pts.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ for ( I = L; I <= K; I++ )
+ CUR.pts.tags[I] &= ~FT_CURVE_TAG_ON;
+ }
+
+
+ static FT_Bool
+ Compute_Point_Displacement( EXEC_OP_ FT_F26Dot6* x,
+ FT_F26Dot6* y,
+ TT_GlyphZone zone,
+ FT_UShort* refp )
+ {
+ TT_GlyphZoneRec zp;
+ FT_UShort p;
+ FT_F26Dot6 d;
+
+
+ if ( CUR.opcode & 1 )
+ {
+ zp = CUR.zp0;
+ p = CUR.GS.rp1;
+ }
+ else
+ {
+ zp = CUR.zp1;
+ p = CUR.GS.rp2;
+ }
+
+ if ( BOUNDS( p, zp.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ *refp = 0;
+ return FAILURE;
+ }
+
+ *zone = zp;
+ *refp = p;
+
+ d = CUR_Func_project( zp.cur + p, zp.org + p );
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ if ( CUR.face->unpatented_hinting )
+ {
+ if ( CUR.GS.both_x_axis )
+ {
+ *x = d;
+ *y = 0;
+ }
+ else
+ {
+ *x = 0;
+ *y = d;
+ }
+ }
+ else
+#endif
+ {
+ *x = TT_MULDIV( d,
+ (FT_Long)CUR.GS.freeVector.x * 0x10000L,
+ CUR.F_dot_P );
+ *y = TT_MULDIV( d,
+ (FT_Long)CUR.GS.freeVector.y * 0x10000L,
+ CUR.F_dot_P );
+ }
+
+ return SUCCESS;
+ }
+
+
+ static void
+ Move_Zp2_Point( EXEC_OP_ FT_UShort point,
+ FT_F26Dot6 dx,
+ FT_F26Dot6 dy,
+ FT_Bool touch )
+ {
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ if ( CUR.face->unpatented_hinting )
+ {
+ if ( CUR.GS.both_x_axis )
+ {
+ CUR.zp2.cur[point].x += dx;
+ if ( touch )
+ CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
+ }
+ else
+ {
+ CUR.zp2.cur[point].y += dy;
+ if ( touch )
+ CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+ }
+ return;
+ }
+#endif
+
+ if ( CUR.GS.freeVector.x != 0 )
+ {
+ CUR.zp2.cur[point].x += dx;
+ if ( touch )
+ CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
+ }
+
+ if ( CUR.GS.freeVector.y != 0 )
+ {
+ CUR.zp2.cur[point].y += dy;
+ if ( touch )
+ CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SHP[a]: SHift Point by the last point */
+ /* Opcode range: 0x32-0x33 */
+ /* Stack: uint32... --> */
+ /* */
+ static void
+ Ins_SHP( INS_ARG )
+ {
+ TT_GlyphZoneRec zp;
+ FT_UShort refp;
+
+ FT_F26Dot6 dx,
+ dy;
+ FT_UShort point;
+
+ FT_UNUSED_ARG;
+
+
+ if ( CUR.top < CUR.GS.loop )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
+ return;
+
+ while ( CUR.GS.loop > 0 )
+ {
+ CUR.args--;
+ point = (FT_UShort)CUR.stack[CUR.args];
+
+ if ( BOUNDS( point, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ }
+ else
+ /* XXX: UNDOCUMENTED! SHP touches the points */
+ MOVE_Zp2_Point( point, dx, dy, TRUE );
+
+ CUR.GS.loop--;
+ }
+
+ CUR.GS.loop = 1;
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SHC[a]: SHift Contour */
+ /* Opcode range: 0x34-35 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SHC( INS_ARG )
+ {
+ TT_GlyphZoneRec zp;
+ FT_UShort refp;
+ FT_F26Dot6 dx,
+ dy;
+
+ FT_Short contour;
+ FT_UShort first_point, last_point, i;
+
+
+ contour = (FT_UShort)args[0];
+
+ if ( BOUNDS( contour, CUR.pts.n_contours ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
+ return;
+
+ if ( contour == 0 )
+ first_point = 0;
+ else
+ first_point = (FT_UShort)( CUR.pts.contours[contour - 1] + 1 -
+ CUR.pts.first_point );
+
+ last_point = (FT_UShort)( CUR.pts.contours[contour] -
+ CUR.pts.first_point );
+
+ /* XXX: this is probably wrong... at least it prevents memory */
+ /* corruption when zp2 is the twilight zone */
+ if ( last_point > CUR.zp2.n_points )
+ {
+ if ( CUR.zp2.n_points > 0 )
+ last_point = (FT_UShort)(CUR.zp2.n_points - 1);
+ else
+ last_point = 0;
+ }
+
+ /* XXX: UNDOCUMENTED! SHC touches the points */
+ for ( i = first_point; i <= last_point; i++ )
+ {
+ if ( zp.cur != CUR.zp2.cur || refp != i )
+ MOVE_Zp2_Point( i, dx, dy, TRUE );
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SHZ[a]: SHift Zone */
+ /* Opcode range: 0x36-37 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_SHZ( INS_ARG )
+ {
+ TT_GlyphZoneRec zp;
+ FT_UShort refp;
+ FT_F26Dot6 dx,
+ dy;
+
+ FT_UShort last_point, i;
+
+
+ if ( BOUNDS( args[0], 2 ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( COMPUTE_Point_Displacement( &dx, &dy, &zp, &refp ) )
+ return;
+
+ /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points. */
+ /* Twilight zone has no contours, so use `n_points'. */
+ /* Normal zone's `n_points' includes phantoms, so must */
+ /* use end of last contour. */
+ if ( CUR.GS.gep2 == 0 && CUR.zp2.n_points > 0 )
+ last_point = (FT_UShort)( CUR.zp2.n_points - 1 );
+ else if ( CUR.GS.gep2 == 1 && CUR.zp2.n_contours > 0 )
+ last_point = (FT_UShort)( CUR.zp2.contours[CUR.zp2.n_contours - 1] );
+ else
+ last_point = 0;
+
+ /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
+ for ( i = 0; i <= last_point; i++ )
+ {
+ if ( zp.cur != CUR.zp2.cur || refp != i )
+ MOVE_Zp2_Point( i, dx, dy, FALSE );
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SHPIX[]: SHift points by a PIXel amount */
+ /* Opcode range: 0x38 */
+ /* Stack: f26.6 uint32... --> */
+ /* */
+ static void
+ Ins_SHPIX( INS_ARG )
+ {
+ FT_F26Dot6 dx, dy;
+ FT_UShort point;
+
+
+ if ( CUR.top < CUR.GS.loop + 1 )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ if ( CUR.face->unpatented_hinting )
+ {
+ if ( CUR.GS.both_x_axis )
+ {
+ dx = TT_MulFix14( args[0], 0x4000 );
+ dy = 0;
+ }
+ else
+ {
+ dx = 0;
+ dy = TT_MulFix14( args[0], 0x4000 );
+ }
+ }
+ else
+#endif
+ {
+ dx = TT_MulFix14( args[0], CUR.GS.freeVector.x );
+ dy = TT_MulFix14( args[0], CUR.GS.freeVector.y );
+ }
+
+ while ( CUR.GS.loop > 0 )
+ {
+ CUR.args--;
+
+ point = (FT_UShort)CUR.stack[CUR.args];
+
+ if ( BOUNDS( point, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ }
+ else
+ MOVE_Zp2_Point( point, dx, dy, TRUE );
+
+ CUR.GS.loop--;
+ }
+
+ CUR.GS.loop = 1;
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MSIRP[a]: Move Stack Indirect Relative Position */
+ /* Opcode range: 0x3A-0x3B */
+ /* Stack: f26.6 uint32 --> */
+ /* */
+ static void
+ Ins_MSIRP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_F26Dot6 distance;
+
+
+ point = (FT_UShort)args[0];
+
+ if ( BOUNDS( point, CUR.zp1.n_points ) ||
+ BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ /* XXX: UNDOCUMENTED! behaviour */
+ if ( CUR.GS.gep1 == 0 ) /* if the point that is to be moved */
+ /* is in twilight zone */
+ {
+ CUR.zp1.org[point] = CUR.zp0.org[CUR.GS.rp0];
+ CUR_Func_move_orig( &CUR.zp1, point, args[1] );
+ CUR.zp1.cur[point] = CUR.zp1.org[point];
+ }
+
+ distance = CUR_Func_project( CUR.zp1.cur + point,
+ CUR.zp0.cur + CUR.GS.rp0 );
+
+ CUR_Func_move( &CUR.zp1, point, args[1] - distance );
+
+ CUR.GS.rp1 = CUR.GS.rp0;
+ CUR.GS.rp2 = point;
+
+ if ( ( CUR.opcode & 1 ) != 0 )
+ CUR.GS.rp0 = point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MDAP[a]: Move Direct Absolute Point */
+ /* Opcode range: 0x2E-0x2F */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_MDAP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_F26Dot6 cur_dist,
+ distance;
+
+
+ point = (FT_UShort)args[0];
+
+ if ( BOUNDS( point, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ /* XXX: Is there some undocumented feature while in the */
+ /* twilight zone? ? */
+ if ( ( CUR.opcode & 1 ) != 0 )
+ {
+ cur_dist = CUR_fast_project( &CUR.zp0.cur[point] );
+ distance = CUR_Func_round( cur_dist,
+ CUR.tt_metrics.compensations[0] ) - cur_dist;
+ }
+ else
+ distance = 0;
+
+ CUR_Func_move( &CUR.zp0, point, distance );
+
+ CUR.GS.rp0 = point;
+ CUR.GS.rp1 = point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MIAP[a]: Move Indirect Absolute Point */
+ /* Opcode range: 0x3E-0x3F */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_MIAP( INS_ARG )
+ {
+ FT_ULong cvtEntry;
+ FT_UShort point;
+ FT_F26Dot6 distance,
+ org_dist;
+
+
+ cvtEntry = (FT_ULong)args[1];
+ point = (FT_UShort)args[0];
+
+ if ( BOUNDS( point, CUR.zp0.n_points ) ||
+ BOUNDS( cvtEntry, CUR.cvtSize ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ /* XXX: UNDOCUMENTED! */
+ /* */
+ /* The behaviour of an MIAP instruction is quite */
+ /* different when used in the twilight zone. */
+ /* */
+ /* First, no control value cut-in test is performed */
+ /* as it would fail anyway. Second, the original */
+ /* point, i.e. (org_x,org_y) of zp0.point, is set */
+ /* to the absolute, unrounded distance found in */
+ /* the CVT. */
+ /* */
+ /* This is used in the CVT programs of the Microsoft */
+ /* fonts Arial, Times, etc., in order to re-adjust */
+ /* some key font heights. It allows the use of the */
+ /* IP instruction in the twilight zone, which */
+ /* otherwise would be `illegal' according to the */
+ /* specification. */
+ /* */
+ /* We implement it with a special sequence for the */
+ /* twilight zone. This is a bad hack, but it seems */
+ /* to work. */
+
+ distance = CUR_Func_read_cvt( cvtEntry );
+
+ if ( CUR.GS.gep0 == 0 ) /* If in twilight zone */
+ {
+ CUR.zp0.org[point].x = TT_MulFix14( distance, CUR.GS.freeVector.x );
+ CUR.zp0.org[point].y = TT_MulFix14( distance, CUR.GS.freeVector.y ),
+ CUR.zp0.cur[point] = CUR.zp0.org[point];
+ }
+
+ org_dist = CUR_fast_project( &CUR.zp0.cur[point] );
+
+ if ( ( CUR.opcode & 1 ) != 0 ) /* rounding and control cutin flag */
+ {
+ if ( FT_ABS( distance - org_dist ) > CUR.GS.control_value_cutin )
+ distance = org_dist;
+
+ distance = CUR_Func_round( distance, CUR.tt_metrics.compensations[0] );
+ }
+
+ CUR_Func_move( &CUR.zp0, point, distance - org_dist );
+
+ CUR.GS.rp0 = point;
+ CUR.GS.rp1 = point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MDRP[abcde]: Move Direct Relative Point */
+ /* Opcode range: 0xC0-0xDF */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_MDRP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_F26Dot6 org_dist, distance;
+
+
+ point = (FT_UShort)args[0];
+
+ if ( BOUNDS( point, CUR.zp1.n_points ) ||
+ BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ /* XXX: Is there some undocumented feature while in the */
+ /* twilight zone? */
+
+ /* XXX: UNDOCUMENTED: twilight zone special case */
+
+ if ( CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 )
+ {
+ FT_Vector* vec1 = &CUR.zp1.org[point];
+ FT_Vector* vec2 = &CUR.zp0.org[CUR.GS.rp0];
+
+
+ org_dist = CUR_Func_dualproj( vec1, vec2 );
+ }
+ else
+ {
+ FT_Vector* vec1 = &CUR.zp1.orus[point];
+ FT_Vector* vec2 = &CUR.zp0.orus[CUR.GS.rp0];
+
+
+ if ( CUR.metrics.x_scale == CUR.metrics.y_scale )
+ {
+ /* this should be faster */
+ org_dist = CUR_Func_dualproj( vec1, vec2 );
+ org_dist = TT_MULFIX( org_dist, CUR.metrics.x_scale );
+ }
+ else
+ {
+ FT_Vector vec;
+
+
+ vec.x = TT_MULFIX( vec1->x - vec2->x, CUR.metrics.x_scale );
+ vec.y = TT_MULFIX( vec1->y - vec2->y, CUR.metrics.y_scale );
+
+ org_dist = CUR_fast_dualproj( &vec );
+ }
+ }
+
+ /* single width cut-in test */
+
+ if ( FT_ABS( org_dist - CUR.GS.single_width_value ) <
+ CUR.GS.single_width_cutin )
+ {
+ if ( org_dist >= 0 )
+ org_dist = CUR.GS.single_width_value;
+ else
+ org_dist = -CUR.GS.single_width_value;
+ }
+
+ /* round flag */
+
+ if ( ( CUR.opcode & 4 ) != 0 )
+ distance = CUR_Func_round(
+ org_dist,
+ CUR.tt_metrics.compensations[CUR.opcode & 3] );
+ else
+ distance = ROUND_None(
+ org_dist,
+ CUR.tt_metrics.compensations[CUR.opcode & 3] );
+
+ /* minimum distance flag */
+
+ if ( ( CUR.opcode & 8 ) != 0 )
+ {
+ if ( org_dist >= 0 )
+ {
+ if ( distance < CUR.GS.minimum_distance )
+ distance = CUR.GS.minimum_distance;
+ }
+ else
+ {
+ if ( distance > -CUR.GS.minimum_distance )
+ distance = -CUR.GS.minimum_distance;
+ }
+ }
+
+ /* now move the point */
+
+ org_dist = CUR_Func_project( CUR.zp1.cur + point,
+ CUR.zp0.cur + CUR.GS.rp0 );
+
+ CUR_Func_move( &CUR.zp1, point, distance - org_dist );
+
+ CUR.GS.rp1 = CUR.GS.rp0;
+ CUR.GS.rp2 = point;
+
+ if ( ( CUR.opcode & 16 ) != 0 )
+ CUR.GS.rp0 = point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MIRP[abcde]: Move Indirect Relative Point */
+ /* Opcode range: 0xE0-0xFF */
+ /* Stack: int32? uint32 --> */
+ /* */
+ static void
+ Ins_MIRP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_ULong cvtEntry;
+
+ FT_F26Dot6 cvt_dist,
+ distance,
+ cur_dist,
+ org_dist;
+
+
+ point = (FT_UShort)args[0];
+ cvtEntry = (FT_ULong)( args[1] + 1 );
+
+ /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
+
+ if ( BOUNDS( point, CUR.zp1.n_points ) ||
+ BOUNDS( cvtEntry, CUR.cvtSize + 1 ) ||
+ BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( !cvtEntry )
+ cvt_dist = 0;
+ else
+ cvt_dist = CUR_Func_read_cvt( cvtEntry - 1 );
+
+ /* single width test */
+
+ if ( FT_ABS( cvt_dist - CUR.GS.single_width_value ) <
+ CUR.GS.single_width_cutin )
+ {
+ if ( cvt_dist >= 0 )
+ cvt_dist = CUR.GS.single_width_value;
+ else
+ cvt_dist = -CUR.GS.single_width_value;
+ }
+
+ /* XXX: UNDOCUMENTED! -- twilight zone */
+
+ if ( CUR.GS.gep1 == 0 )
+ {
+ CUR.zp1.org[point].x = CUR.zp0.org[CUR.GS.rp0].x +
+ TT_MulFix14( cvt_dist, CUR.GS.freeVector.x );
+
+ CUR.zp1.org[point].y = CUR.zp0.org[CUR.GS.rp0].y +
+ TT_MulFix14( cvt_dist, CUR.GS.freeVector.y );
+
+ CUR.zp1.cur[point] = CUR.zp0.cur[point];
+ }
+
+ org_dist = CUR_Func_dualproj( &CUR.zp1.org[point],
+ &CUR.zp0.org[CUR.GS.rp0] );
+ cur_dist = CUR_Func_project ( &CUR.zp1.cur[point],
+ &CUR.zp0.cur[CUR.GS.rp0] );
+
+ /* auto-flip test */
+
+ if ( CUR.GS.auto_flip )
+ {
+ if ( ( org_dist ^ cvt_dist ) < 0 )
+ cvt_dist = -cvt_dist;
+ }
+
+ /* control value cutin and round */
+
+ if ( ( CUR.opcode & 4 ) != 0 )
+ {
+ /* XXX: UNDOCUMENTED! Only perform cut-in test when both points */
+ /* refer to the same zone. */
+
+ if ( CUR.GS.gep0 == CUR.GS.gep1 )
+ if ( FT_ABS( cvt_dist - org_dist ) >= CUR.GS.control_value_cutin )
+ cvt_dist = org_dist;
+
+ distance = CUR_Func_round(
+ cvt_dist,
+ CUR.tt_metrics.compensations[CUR.opcode & 3] );
+ }
+ else
+ distance = ROUND_None(
+ cvt_dist,
+ CUR.tt_metrics.compensations[CUR.opcode & 3] );
+
+ /* minimum distance test */
+
+ if ( ( CUR.opcode & 8 ) != 0 )
+ {
+ if ( org_dist >= 0 )
+ {
+ if ( distance < CUR.GS.minimum_distance )
+ distance = CUR.GS.minimum_distance;
+ }
+ else
+ {
+ if ( distance > -CUR.GS.minimum_distance )
+ distance = -CUR.GS.minimum_distance;
+ }
+ }
+
+ CUR_Func_move( &CUR.zp1, point, distance - cur_dist );
+
+ CUR.GS.rp1 = CUR.GS.rp0;
+
+ if ( ( CUR.opcode & 16 ) != 0 )
+ CUR.GS.rp0 = point;
+
+ /* XXX: UNDOCUMENTED! */
+ CUR.GS.rp2 = point;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ALIGNRP[]: ALIGN Relative Point */
+ /* Opcode range: 0x3C */
+ /* Stack: uint32 uint32... --> */
+ /* */
+ static void
+ Ins_ALIGNRP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_F26Dot6 distance;
+
+ FT_UNUSED_ARG;
+
+
+ if ( CUR.top < CUR.GS.loop ||
+ BOUNDS( CUR.GS.rp0, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ while ( CUR.GS.loop > 0 )
+ {
+ CUR.args--;
+
+ point = (FT_UShort)CUR.stack[CUR.args];
+
+ if ( BOUNDS( point, CUR.zp1.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ }
+ else
+ {
+ distance = CUR_Func_project( CUR.zp1.cur + point,
+ CUR.zp0.cur + CUR.GS.rp0 );
+
+ CUR_Func_move( &CUR.zp1, point, -distance );
+ }
+
+ CUR.GS.loop--;
+ }
+
+ CUR.GS.loop = 1;
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ISECT[]: moves point to InterSECTion */
+ /* Opcode range: 0x0F */
+ /* Stack: 5 * uint32 --> */
+ /* */
+ static void
+ Ins_ISECT( INS_ARG )
+ {
+ FT_UShort point,
+ a0, a1,
+ b0, b1;
+
+ FT_F26Dot6 discriminant;
+
+ FT_F26Dot6 dx, dy,
+ dax, day,
+ dbx, dby;
+
+ FT_F26Dot6 val;
+
+ FT_Vector R;
+
+
+ point = (FT_UShort)args[0];
+
+ a0 = (FT_UShort)args[1];
+ a1 = (FT_UShort)args[2];
+ b0 = (FT_UShort)args[3];
+ b1 = (FT_UShort)args[4];
+
+ if ( BOUNDS( b0, CUR.zp0.n_points ) ||
+ BOUNDS( b1, CUR.zp0.n_points ) ||
+ BOUNDS( a0, CUR.zp1.n_points ) ||
+ BOUNDS( a1, CUR.zp1.n_points ) ||
+ BOUNDS( point, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ dbx = CUR.zp0.cur[b1].x - CUR.zp0.cur[b0].x;
+ dby = CUR.zp0.cur[b1].y - CUR.zp0.cur[b0].y;
+
+ dax = CUR.zp1.cur[a1].x - CUR.zp1.cur[a0].x;
+ day = CUR.zp1.cur[a1].y - CUR.zp1.cur[a0].y;
+
+ dx = CUR.zp0.cur[b0].x - CUR.zp1.cur[a0].x;
+ dy = CUR.zp0.cur[b0].y - CUR.zp1.cur[a0].y;
+
+ CUR.zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH;
+
+ discriminant = TT_MULDIV( dax, -dby, 0x40 ) +
+ TT_MULDIV( day, dbx, 0x40 );
+
+ if ( FT_ABS( discriminant ) >= 0x40 )
+ {
+ val = TT_MULDIV( dx, -dby, 0x40 ) + TT_MULDIV( dy, dbx, 0x40 );
+
+ R.x = TT_MULDIV( val, dax, discriminant );
+ R.y = TT_MULDIV( val, day, discriminant );
+
+ CUR.zp2.cur[point].x = CUR.zp1.cur[a0].x + R.x;
+ CUR.zp2.cur[point].y = CUR.zp1.cur[a0].y + R.y;
+ }
+ else
+ {
+ /* else, take the middle of the middles of A and B */
+
+ CUR.zp2.cur[point].x = ( CUR.zp1.cur[a0].x +
+ CUR.zp1.cur[a1].x +
+ CUR.zp0.cur[b0].x +
+ CUR.zp0.cur[b1].x ) / 4;
+ CUR.zp2.cur[point].y = ( CUR.zp1.cur[a0].y +
+ CUR.zp1.cur[a1].y +
+ CUR.zp0.cur[b0].y +
+ CUR.zp0.cur[b1].y ) / 4;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* ALIGNPTS[]: ALIGN PoinTS */
+ /* Opcode range: 0x27 */
+ /* Stack: uint32 uint32 --> */
+ /* */
+ static void
+ Ins_ALIGNPTS( INS_ARG )
+ {
+ FT_UShort p1, p2;
+ FT_F26Dot6 distance;
+
+
+ p1 = (FT_UShort)args[0];
+ p2 = (FT_UShort)args[1];
+
+ if ( BOUNDS( args[0], CUR.zp1.n_points ) ||
+ BOUNDS( args[1], CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ distance = CUR_Func_project( CUR.zp0.cur + p2,
+ CUR.zp1.cur + p1 ) / 2;
+
+ CUR_Func_move( &CUR.zp1, p1, distance );
+ CUR_Func_move( &CUR.zp0, p2, -distance );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* IP[]: Interpolate Point */
+ /* Opcode range: 0x39 */
+ /* Stack: uint32... --> */
+ /* */
+
+ /* SOMETIMES, DUMBER CODE IS BETTER CODE */
+
+ static void
+ Ins_IP( INS_ARG )
+ {
+ FT_F26Dot6 old_range, cur_range;
+ FT_Vector* orus_base;
+ FT_Vector* cur_base;
+ FT_Int twilight;
+
+ FT_UNUSED_ARG;
+
+
+ if ( CUR.top < CUR.GS.loop )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ /*
+ * We need to deal in a special way with the twilight zone.
+ * Otherwise, by definition, the value of CUR.twilight.orus[n] is (0,0),
+ * for every n.
+ */
+ twilight = CUR.GS.gep0 == 0 || CUR.GS.gep1 == 0 || CUR.GS.gep2 == 0;
+
+ if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ if ( twilight )
+ orus_base = &CUR.zp0.org[CUR.GS.rp1];
+ else
+ orus_base = &CUR.zp0.orus[CUR.GS.rp1];
+
+ cur_base = &CUR.zp0.cur[CUR.GS.rp1];
+
+ /* XXX: There are some glyphs in some braindead but popular */
+ /* fonts out there (e.g. [aeu]grave in monotype.ttf) */
+ /* calling IP[] with bad values of rp[12]. */
+ /* Do something sane when this odd thing happens. */
+ if ( BOUNDS( CUR.GS.rp1, CUR.zp0.n_points ) ||
+ BOUNDS( CUR.GS.rp2, CUR.zp1.n_points ) )
+ {
+ old_range = 0;
+ cur_range = 0;
+ }
+ else
+ {
+ if ( twilight )
+ old_range = CUR_Func_dualproj( &CUR.zp1.org[CUR.GS.rp2],
+ orus_base );
+ else
+ old_range = CUR_Func_dualproj( &CUR.zp1.orus[CUR.GS.rp2],
+ orus_base );
+
+ cur_range = CUR_Func_project ( &CUR.zp1.cur[CUR.GS.rp2], cur_base );
+ }
+
+ for ( ; CUR.GS.loop > 0; --CUR.GS.loop )
+ {
+ FT_UInt point = (FT_UInt)CUR.stack[--CUR.args];
+ FT_F26Dot6 org_dist, cur_dist, new_dist;
+
+
+ /* check point bounds */
+ if ( BOUNDS( point, CUR.zp2.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ continue;
+ }
+
+ if ( twilight )
+ org_dist = CUR_Func_dualproj( &CUR.zp2.org[point], orus_base );
+ else
+ org_dist = CUR_Func_dualproj( &CUR.zp2.orus[point], orus_base );
+
+ cur_dist = CUR_Func_project ( &CUR.zp2.cur[point], cur_base );
+ new_dist = ( old_range != 0 )
+ ? TT_MULDIV( org_dist, cur_range, old_range )
+ : cur_dist;
+
+ CUR_Func_move( &CUR.zp2, (FT_UShort)point, new_dist - cur_dist );
+ }
+ CUR.GS.loop = 1;
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* UTP[a]: UnTouch Point */
+ /* Opcode range: 0x29 */
+ /* Stack: uint32 --> */
+ /* */
+ static void
+ Ins_UTP( INS_ARG )
+ {
+ FT_UShort point;
+ FT_Byte mask;
+
+
+ point = (FT_UShort)args[0];
+
+ if ( BOUNDS( point, CUR.zp0.n_points ) )
+ {
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+
+ mask = 0xFF;
+
+ if ( CUR.GS.freeVector.x != 0 )
+ mask &= ~FT_CURVE_TAG_TOUCH_X;
+
+ if ( CUR.GS.freeVector.y != 0 )
+ mask &= ~FT_CURVE_TAG_TOUCH_Y;
+
+ CUR.zp0.tags[point] &= mask;
+ }
+
+
+ /* Local variables for Ins_IUP: */
+ typedef struct
+ {
+ FT_Vector* orgs; /* original and current coordinate */
+ FT_Vector* curs; /* arrays */
+ FT_Vector* orus;
+ FT_UInt max_points;
+
+ } IUP_WorkerRec, *IUP_Worker;
+
+
+ static void
+ _iup_worker_shift( IUP_Worker worker,
+ FT_UInt p1,
+ FT_UInt p2,
+ FT_UInt p )
+ {
+ FT_UInt i;
+ FT_F26Dot6 dx;
+
+
+ dx = worker->curs[p].x - worker->orgs[p].x;
+ if ( dx != 0 )
+ {
+ for ( i = p1; i < p; i++ )
+ worker->curs[i].x += dx;
+
+ for ( i = p + 1; i <= p2; i++ )
+ worker->curs[i].x += dx;
+ }
+ }
+
+
+ static void
+ _iup_worker_interpolate( IUP_Worker worker,
+ FT_UInt p1,
+ FT_UInt p2,
+ FT_UInt ref1,
+ FT_UInt ref2 )
+ {
+ FT_UInt i;
+ FT_F26Dot6 orus1, orus2, org1, org2, delta1, delta2;
+
+
+ if ( p1 > p2 )
+ return;
+
+ if ( BOUNDS( ref1, worker->max_points ) ||
+ BOUNDS( ref2, worker->max_points ) )
+ return;
+
+ orus1 = worker->orus[ref1].x;
+ orus2 = worker->orus[ref2].x;
+
+ if ( orus1 > orus2 )
+ {
+ FT_F26Dot6 tmp_o;
+ FT_UInt tmp_r;
+
+
+ tmp_o = orus1;
+ orus1 = orus2;
+ orus2 = tmp_o;
+
+ tmp_r = ref1;
+ ref1 = ref2;
+ ref2 = tmp_r;
+ }
+
+ org1 = worker->orgs[ref1].x;
+ org2 = worker->orgs[ref2].x;
+ delta1 = worker->curs[ref1].x - org1;
+ delta2 = worker->curs[ref2].x - org2;
+
+ if ( orus1 == orus2 )
+ {
+ /* simple shift of untouched points */
+ for ( i = p1; i <= p2; i++ )
+ {
+ FT_F26Dot6 x = worker->orgs[i].x;
+
+
+ if ( x <= org1 )
+ x += delta1;
+ else
+ x += delta2;
+
+ worker->curs[i].x = x;
+ }
+ }
+ else
+ {
+ FT_Fixed scale = 0;
+ FT_Bool scale_valid = 0;
+
+
+ /* interpolation */
+ for ( i = p1; i <= p2; i++ )
+ {
+ FT_F26Dot6 x = worker->orgs[i].x;
+
+
+ if ( x <= org1 )
+ x += delta1;
+
+ else if ( x >= org2 )
+ x += delta2;
+
+ else
+ {
+ if ( !scale_valid )
+ {
+ scale_valid = 1;
+ scale = TT_MULDIV( org2 + delta2 - ( org1 + delta1 ),
+ 0x10000, orus2 - orus1 );
+ }
+
+ x = ( org1 + delta1 ) +
+ TT_MULFIX( worker->orus[i].x - orus1, scale );
+ }
+ worker->curs[i].x = x;
+ }
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* IUP[a]: Interpolate Untouched Points */
+ /* Opcode range: 0x30-0x31 */
+ /* Stack: --> */
+ /* */
+ static void
+ Ins_IUP( INS_ARG )
+ {
+ IUP_WorkerRec V;
+ FT_Byte mask;
+
+ FT_UInt first_point; /* first point of contour */
+ FT_UInt end_point; /* end point (last+1) of contour */
+
+ FT_UInt first_touched; /* first touched point in contour */
+ FT_UInt cur_touched; /* current touched point in contour */
+
+ FT_UInt point; /* current point */
+ FT_Short contour; /* current contour */
+
+ FT_UNUSED_ARG;
+
+
+ /* ignore empty outlines */
+ if ( CUR.pts.n_contours == 0 )
+ return;
+
+ if ( CUR.opcode & 1 )
+ {
+ mask = FT_CURVE_TAG_TOUCH_X;
+ V.orgs = CUR.pts.org;
+ V.curs = CUR.pts.cur;
+ V.orus = CUR.pts.orus;
+ }
+ else
+ {
+ mask = FT_CURVE_TAG_TOUCH_Y;
+ V.orgs = (FT_Vector*)( (FT_Pos*)CUR.pts.org + 1 );
+ V.curs = (FT_Vector*)( (FT_Pos*)CUR.pts.cur + 1 );
+ V.orus = (FT_Vector*)( (FT_Pos*)CUR.pts.orus + 1 );
+ }
+ V.max_points = CUR.pts.n_points;
+
+ contour = 0;
+ point = 0;
+
+ do
+ {
+ end_point = CUR.pts.contours[contour] - CUR.pts.first_point;
+ first_point = point;
+
+ while ( point <= end_point && ( CUR.pts.tags[point] & mask ) == 0 )
+ point++;
+
+ if ( point <= end_point )
+ {
+ first_touched = point;
+ cur_touched = point;
+
+ point++;
+
+ while ( point <= end_point )
+ {
+ if ( ( CUR.pts.tags[point] & mask ) != 0 )
+ {
+ if ( point > 0 )
+ _iup_worker_interpolate( &V,
+ cur_touched + 1,
+ point - 1,
+ cur_touched,
+ point );
+ cur_touched = point;
+ }
+
+ point++;
+ }
+
+ if ( cur_touched == first_touched )
+ _iup_worker_shift( &V, first_point, end_point, cur_touched );
+ else
+ {
+ _iup_worker_interpolate( &V,
+ (FT_UShort)( cur_touched + 1 ),
+ end_point,
+ cur_touched,
+ first_touched );
+
+ if ( first_touched > 0 )
+ _iup_worker_interpolate( &V,
+ first_point,
+ first_touched - 1,
+ cur_touched,
+ first_touched );
+ }
+ }
+ contour++;
+ } while ( contour < CUR.pts.n_contours );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DELTAPn[]: DELTA exceptions P1, P2, P3 */
+ /* Opcode range: 0x5D,0x71,0x72 */
+ /* Stack: uint32 (2 * uint32)... --> */
+ /* */
+ static void
+ Ins_DELTAP( INS_ARG )
+ {
+ FT_ULong k, nump;
+ FT_UShort A;
+ FT_ULong C;
+ FT_Long B;
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ /* Delta hinting is covered by US Patent 5159668. */
+ if ( CUR.face->unpatented_hinting )
+ {
+ FT_Long n = args[0] * 2;
+
+
+ if ( CUR.args < n )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ return;
+ }
+
+ CUR.args -= n;
+ CUR.new_top = CUR.args;
+ return;
+ }
+#endif
+
+ nump = (FT_ULong)args[0]; /* some points theoretically may occur more
+ than once, thus UShort isn't enough */
+
+ for ( k = 1; k <= nump; k++ )
+ {
+ if ( CUR.args < 2 )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ return;
+ }
+
+ CUR.args -= 2;
+
+ A = (FT_UShort)CUR.stack[CUR.args + 1];
+ B = CUR.stack[CUR.args];
+
+ /* XXX: Because some popular fonts contain some invalid DeltaP */
+ /* instructions, we simply ignore them when the stacked */
+ /* point reference is off limit, rather than returning an */
+ /* error. As a delta instruction doesn't change a glyph */
+ /* in great ways, this shouldn't be a problem. */
+
+ if ( !BOUNDS( A, CUR.zp0.n_points ) )
+ {
+ C = ( (FT_ULong)B & 0xF0 ) >> 4;
+
+ switch ( CUR.opcode )
+ {
+ case 0x5D:
+ break;
+
+ case 0x71:
+ C += 16;
+ break;
+
+ case 0x72:
+ C += 32;
+ break;
+ }
+
+ C += CUR.GS.delta_base;
+
+ if ( CURRENT_Ppem() == (FT_Long)C )
+ {
+ B = ( (FT_ULong)B & 0xF ) - 8;
+ if ( B >= 0 )
+ B++;
+ B = B * 64 / ( 1L << CUR.GS.delta_shift );
+
+ CUR_Func_move( &CUR.zp0, A, B );
+ }
+ }
+ else
+ if ( CUR.pedantic_hinting )
+ CUR.error = TT_Err_Invalid_Reference;
+ }
+
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* DELTACn[]: DELTA exceptions C1, C2, C3 */
+ /* Opcode range: 0x73,0x74,0x75 */
+ /* Stack: uint32 (2 * uint32)... --> */
+ /* */
+ static void
+ Ins_DELTAC( INS_ARG )
+ {
+ FT_ULong nump, k;
+ FT_ULong A, C;
+ FT_Long B;
+
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ /* Delta hinting is covered by US Patent 5159668. */
+ if ( CUR.face->unpatented_hinting )
+ {
+ FT_Long n = args[0] * 2;
+
+
+ if ( CUR.args < n )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ return;
+ }
+
+ CUR.args -= n;
+ CUR.new_top = CUR.args;
+ return;
+ }
+#endif
+
+ nump = (FT_ULong)args[0];
+
+ for ( k = 1; k <= nump; k++ )
+ {
+ if ( CUR.args < 2 )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ return;
+ }
+
+ CUR.args -= 2;
+
+ A = (FT_ULong)CUR.stack[CUR.args + 1];
+ B = CUR.stack[CUR.args];
+
+ if ( BOUNDS( A, CUR.cvtSize ) )
+ {
+ if ( CUR.pedantic_hinting )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ return;
+ }
+ }
+ else
+ {
+ C = ( (FT_ULong)B & 0xF0 ) >> 4;
+
+ switch ( CUR.opcode )
+ {
+ case 0x73:
+ break;
+
+ case 0x74:
+ C += 16;
+ break;
+
+ case 0x75:
+ C += 32;
+ break;
+ }
+
+ C += CUR.GS.delta_base;
+
+ if ( CURRENT_Ppem() == (FT_Long)C )
+ {
+ B = ( (FT_ULong)B & 0xF ) - 8;
+ if ( B >= 0 )
+ B++;
+ B = B * 64 / ( 1L << CUR.GS.delta_shift );
+
+ CUR_Func_move_cvt( A, B );
+ }
+ }
+ }
+
+ CUR.new_top = CUR.args;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* MISC. INSTRUCTIONS */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* GETINFO[]: GET INFOrmation */
+ /* Opcode range: 0x88 */
+ /* Stack: uint32 --> uint32 */
+ /* */
+ static void
+ Ins_GETINFO( INS_ARG )
+ {
+ FT_Long K;
+
+
+ K = 0;
+
+ /* We return MS rasterizer version 1.7 for the font scaler. */
+ if ( ( args[0] & 1 ) != 0 )
+ K = 35;
+
+ /* Has the glyph been rotated? */
+ if ( ( args[0] & 2 ) != 0 && CUR.tt_metrics.rotated )
+ K |= 0x80;
+
+ /* Has the glyph been stretched? */
+ if ( ( args[0] & 4 ) != 0 && CUR.tt_metrics.stretched )
+ K |= 1 << 8;
+
+ /* Are we hinting for grayscale? */
+ if ( ( args[0] & 32 ) != 0 && CUR.grayscale )
+ K |= 1 << 12;
+
+ args[0] = K;
+ }
+
+
+ static void
+ Ins_UNKNOWN( INS_ARG )
+ {
+ TT_DefRecord* def = CUR.IDefs;
+ TT_DefRecord* limit = def + CUR.numIDefs;
+
+ FT_UNUSED_ARG;
+
+
+ for ( ; def < limit; def++ )
+ {
+ if ( (FT_Byte)def->opc == CUR.opcode && def->active )
+ {
+ TT_CallRec* call;
+
+
+ if ( CUR.callTop >= CUR.callSize )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ return;
+ }
+
+ call = CUR.callStack + CUR.callTop++;
+
+ call->Caller_Range = CUR.curRange;
+ call->Caller_IP = CUR.IP+1;
+ call->Cur_Count = 1;
+ call->Cur_Restart = def->start;
+
+ INS_Goto_CodeRange( def->range, def->start );
+
+ CUR.step_ins = FALSE;
+ return;
+ }
+ }
+
+ CUR.error = TT_Err_Invalid_Opcode;
+ }
+
+
+#ifndef TT_CONFIG_OPTION_INTERPRETER_SWITCH
+
+
+ static
+ TInstruction_Function Instruct_Dispatch[256] =
+ {
+ /* Opcodes are gathered in groups of 16. */
+ /* Please keep the spaces as they are. */
+
+ /* SVTCA y */ Ins_SVTCA,
+ /* SVTCA x */ Ins_SVTCA,
+ /* SPvTCA y */ Ins_SPVTCA,
+ /* SPvTCA x */ Ins_SPVTCA,
+ /* SFvTCA y */ Ins_SFVTCA,
+ /* SFvTCA x */ Ins_SFVTCA,
+ /* SPvTL // */ Ins_SPVTL,
+ /* SPvTL + */ Ins_SPVTL,
+ /* SFvTL // */ Ins_SFVTL,
+ /* SFvTL + */ Ins_SFVTL,
+ /* SPvFS */ Ins_SPVFS,
+ /* SFvFS */ Ins_SFVFS,
+ /* GPV */ Ins_GPV,
+ /* GFV */ Ins_GFV,
+ /* SFvTPv */ Ins_SFVTPV,
+ /* ISECT */ Ins_ISECT,
+
+ /* SRP0 */ Ins_SRP0,
+ /* SRP1 */ Ins_SRP1,
+ /* SRP2 */ Ins_SRP2,
+ /* SZP0 */ Ins_SZP0,
+ /* SZP1 */ Ins_SZP1,
+ /* SZP2 */ Ins_SZP2,
+ /* SZPS */ Ins_SZPS,
+ /* SLOOP */ Ins_SLOOP,
+ /* RTG */ Ins_RTG,
+ /* RTHG */ Ins_RTHG,
+ /* SMD */ Ins_SMD,
+ /* ELSE */ Ins_ELSE,
+ /* JMPR */ Ins_JMPR,
+ /* SCvTCi */ Ins_SCVTCI,
+ /* SSwCi */ Ins_SSWCI,
+ /* SSW */ Ins_SSW,
+
+ /* DUP */ Ins_DUP,
+ /* POP */ Ins_POP,
+ /* CLEAR */ Ins_CLEAR,
+ /* SWAP */ Ins_SWAP,
+ /* DEPTH */ Ins_DEPTH,
+ /* CINDEX */ Ins_CINDEX,
+ /* MINDEX */ Ins_MINDEX,
+ /* AlignPTS */ Ins_ALIGNPTS,
+ /* INS_0x28 */ Ins_UNKNOWN,
+ /* UTP */ Ins_UTP,
+ /* LOOPCALL */ Ins_LOOPCALL,
+ /* CALL */ Ins_CALL,
+ /* FDEF */ Ins_FDEF,
+ /* ENDF */ Ins_ENDF,
+ /* MDAP[0] */ Ins_MDAP,
+ /* MDAP[1] */ Ins_MDAP,
+
+ /* IUP[0] */ Ins_IUP,
+ /* IUP[1] */ Ins_IUP,
+ /* SHP[0] */ Ins_SHP,
+ /* SHP[1] */ Ins_SHP,
+ /* SHC[0] */ Ins_SHC,
+ /* SHC[1] */ Ins_SHC,
+ /* SHZ[0] */ Ins_SHZ,
+ /* SHZ[1] */ Ins_SHZ,
+ /* SHPIX */ Ins_SHPIX,
+ /* IP */ Ins_IP,
+ /* MSIRP[0] */ Ins_MSIRP,
+ /* MSIRP[1] */ Ins_MSIRP,
+ /* AlignRP */ Ins_ALIGNRP,
+ /* RTDG */ Ins_RTDG,
+ /* MIAP[0] */ Ins_MIAP,
+ /* MIAP[1] */ Ins_MIAP,
+
+ /* NPushB */ Ins_NPUSHB,
+ /* NPushW */ Ins_NPUSHW,
+ /* WS */ Ins_WS,
+ /* RS */ Ins_RS,
+ /* WCvtP */ Ins_WCVTP,
+ /* RCvt */ Ins_RCVT,
+ /* GC[0] */ Ins_GC,
+ /* GC[1] */ Ins_GC,
+ /* SCFS */ Ins_SCFS,
+ /* MD[0] */ Ins_MD,
+ /* MD[1] */ Ins_MD,
+ /* MPPEM */ Ins_MPPEM,
+ /* MPS */ Ins_MPS,
+ /* FlipON */ Ins_FLIPON,
+ /* FlipOFF */ Ins_FLIPOFF,
+ /* DEBUG */ Ins_DEBUG,
+
+ /* LT */ Ins_LT,
+ /* LTEQ */ Ins_LTEQ,
+ /* GT */ Ins_GT,
+ /* GTEQ */ Ins_GTEQ,
+ /* EQ */ Ins_EQ,
+ /* NEQ */ Ins_NEQ,
+ /* ODD */ Ins_ODD,
+ /* EVEN */ Ins_EVEN,
+ /* IF */ Ins_IF,
+ /* EIF */ Ins_EIF,
+ /* AND */ Ins_AND,
+ /* OR */ Ins_OR,
+ /* NOT */ Ins_NOT,
+ /* DeltaP1 */ Ins_DELTAP,
+ /* SDB */ Ins_SDB,
+ /* SDS */ Ins_SDS,
+
+ /* ADD */ Ins_ADD,
+ /* SUB */ Ins_SUB,
+ /* DIV */ Ins_DIV,
+ /* MUL */ Ins_MUL,
+ /* ABS */ Ins_ABS,
+ /* NEG */ Ins_NEG,
+ /* FLOOR */ Ins_FLOOR,
+ /* CEILING */ Ins_CEILING,
+ /* ROUND[0] */ Ins_ROUND,
+ /* ROUND[1] */ Ins_ROUND,
+ /* ROUND[2] */ Ins_ROUND,
+ /* ROUND[3] */ Ins_ROUND,
+ /* NROUND[0] */ Ins_NROUND,
+ /* NROUND[1] */ Ins_NROUND,
+ /* NROUND[2] */ Ins_NROUND,
+ /* NROUND[3] */ Ins_NROUND,
+
+ /* WCvtF */ Ins_WCVTF,
+ /* DeltaP2 */ Ins_DELTAP,
+ /* DeltaP3 */ Ins_DELTAP,
+ /* DeltaCn[0] */ Ins_DELTAC,
+ /* DeltaCn[1] */ Ins_DELTAC,
+ /* DeltaCn[2] */ Ins_DELTAC,
+ /* SROUND */ Ins_SROUND,
+ /* S45Round */ Ins_S45ROUND,
+ /* JROT */ Ins_JROT,
+ /* JROF */ Ins_JROF,
+ /* ROFF */ Ins_ROFF,
+ /* INS_0x7B */ Ins_UNKNOWN,
+ /* RUTG */ Ins_RUTG,
+ /* RDTG */ Ins_RDTG,
+ /* SANGW */ Ins_SANGW,
+ /* AA */ Ins_AA,
+
+ /* FlipPT */ Ins_FLIPPT,
+ /* FlipRgON */ Ins_FLIPRGON,
+ /* FlipRgOFF */ Ins_FLIPRGOFF,
+ /* INS_0x83 */ Ins_UNKNOWN,
+ /* INS_0x84 */ Ins_UNKNOWN,
+ /* ScanCTRL */ Ins_SCANCTRL,
+ /* SDPVTL[0] */ Ins_SDPVTL,
+ /* SDPVTL[1] */ Ins_SDPVTL,
+ /* GetINFO */ Ins_GETINFO,
+ /* IDEF */ Ins_IDEF,
+ /* ROLL */ Ins_ROLL,
+ /* MAX */ Ins_MAX,
+ /* MIN */ Ins_MIN,
+ /* ScanTYPE */ Ins_SCANTYPE,
+ /* InstCTRL */ Ins_INSTCTRL,
+ /* INS_0x8F */ Ins_UNKNOWN,
+
+ /* INS_0x90 */ Ins_UNKNOWN,
+ /* INS_0x91 */ Ins_UNKNOWN,
+ /* INS_0x92 */ Ins_UNKNOWN,
+ /* INS_0x93 */ Ins_UNKNOWN,
+ /* INS_0x94 */ Ins_UNKNOWN,
+ /* INS_0x95 */ Ins_UNKNOWN,
+ /* INS_0x96 */ Ins_UNKNOWN,
+ /* INS_0x97 */ Ins_UNKNOWN,
+ /* INS_0x98 */ Ins_UNKNOWN,
+ /* INS_0x99 */ Ins_UNKNOWN,
+ /* INS_0x9A */ Ins_UNKNOWN,
+ /* INS_0x9B */ Ins_UNKNOWN,
+ /* INS_0x9C */ Ins_UNKNOWN,
+ /* INS_0x9D */ Ins_UNKNOWN,
+ /* INS_0x9E */ Ins_UNKNOWN,
+ /* INS_0x9F */ Ins_UNKNOWN,
+
+ /* INS_0xA0 */ Ins_UNKNOWN,
+ /* INS_0xA1 */ Ins_UNKNOWN,
+ /* INS_0xA2 */ Ins_UNKNOWN,
+ /* INS_0xA3 */ Ins_UNKNOWN,
+ /* INS_0xA4 */ Ins_UNKNOWN,
+ /* INS_0xA5 */ Ins_UNKNOWN,
+ /* INS_0xA6 */ Ins_UNKNOWN,
+ /* INS_0xA7 */ Ins_UNKNOWN,
+ /* INS_0xA8 */ Ins_UNKNOWN,
+ /* INS_0xA9 */ Ins_UNKNOWN,
+ /* INS_0xAA */ Ins_UNKNOWN,
+ /* INS_0xAB */ Ins_UNKNOWN,
+ /* INS_0xAC */ Ins_UNKNOWN,
+ /* INS_0xAD */ Ins_UNKNOWN,
+ /* INS_0xAE */ Ins_UNKNOWN,
+ /* INS_0xAF */ Ins_UNKNOWN,
+
+ /* PushB[0] */ Ins_PUSHB,
+ /* PushB[1] */ Ins_PUSHB,
+ /* PushB[2] */ Ins_PUSHB,
+ /* PushB[3] */ Ins_PUSHB,
+ /* PushB[4] */ Ins_PUSHB,
+ /* PushB[5] */ Ins_PUSHB,
+ /* PushB[6] */ Ins_PUSHB,
+ /* PushB[7] */ Ins_PUSHB,
+ /* PushW[0] */ Ins_PUSHW,
+ /* PushW[1] */ Ins_PUSHW,
+ /* PushW[2] */ Ins_PUSHW,
+ /* PushW[3] */ Ins_PUSHW,
+ /* PushW[4] */ Ins_PUSHW,
+ /* PushW[5] */ Ins_PUSHW,
+ /* PushW[6] */ Ins_PUSHW,
+ /* PushW[7] */ Ins_PUSHW,
+
+ /* MDRP[00] */ Ins_MDRP,
+ /* MDRP[01] */ Ins_MDRP,
+ /* MDRP[02] */ Ins_MDRP,
+ /* MDRP[03] */ Ins_MDRP,
+ /* MDRP[04] */ Ins_MDRP,
+ /* MDRP[05] */ Ins_MDRP,
+ /* MDRP[06] */ Ins_MDRP,
+ /* MDRP[07] */ Ins_MDRP,
+ /* MDRP[08] */ Ins_MDRP,
+ /* MDRP[09] */ Ins_MDRP,
+ /* MDRP[10] */ Ins_MDRP,
+ /* MDRP[11] */ Ins_MDRP,
+ /* MDRP[12] */ Ins_MDRP,
+ /* MDRP[13] */ Ins_MDRP,
+ /* MDRP[14] */ Ins_MDRP,
+ /* MDRP[15] */ Ins_MDRP,
+
+ /* MDRP[16] */ Ins_MDRP,
+ /* MDRP[17] */ Ins_MDRP,
+ /* MDRP[18] */ Ins_MDRP,
+ /* MDRP[19] */ Ins_MDRP,
+ /* MDRP[20] */ Ins_MDRP,
+ /* MDRP[21] */ Ins_MDRP,
+ /* MDRP[22] */ Ins_MDRP,
+ /* MDRP[23] */ Ins_MDRP,
+ /* MDRP[24] */ Ins_MDRP,
+ /* MDRP[25] */ Ins_MDRP,
+ /* MDRP[26] */ Ins_MDRP,
+ /* MDRP[27] */ Ins_MDRP,
+ /* MDRP[28] */ Ins_MDRP,
+ /* MDRP[29] */ Ins_MDRP,
+ /* MDRP[30] */ Ins_MDRP,
+ /* MDRP[31] */ Ins_MDRP,
+
+ /* MIRP[00] */ Ins_MIRP,
+ /* MIRP[01] */ Ins_MIRP,
+ /* MIRP[02] */ Ins_MIRP,
+ /* MIRP[03] */ Ins_MIRP,
+ /* MIRP[04] */ Ins_MIRP,
+ /* MIRP[05] */ Ins_MIRP,
+ /* MIRP[06] */ Ins_MIRP,
+ /* MIRP[07] */ Ins_MIRP,
+ /* MIRP[08] */ Ins_MIRP,
+ /* MIRP[09] */ Ins_MIRP,
+ /* MIRP[10] */ Ins_MIRP,
+ /* MIRP[11] */ Ins_MIRP,
+ /* MIRP[12] */ Ins_MIRP,
+ /* MIRP[13] */ Ins_MIRP,
+ /* MIRP[14] */ Ins_MIRP,
+ /* MIRP[15] */ Ins_MIRP,
+
+ /* MIRP[16] */ Ins_MIRP,
+ /* MIRP[17] */ Ins_MIRP,
+ /* MIRP[18] */ Ins_MIRP,
+ /* MIRP[19] */ Ins_MIRP,
+ /* MIRP[20] */ Ins_MIRP,
+ /* MIRP[21] */ Ins_MIRP,
+ /* MIRP[22] */ Ins_MIRP,
+ /* MIRP[23] */ Ins_MIRP,
+ /* MIRP[24] */ Ins_MIRP,
+ /* MIRP[25] */ Ins_MIRP,
+ /* MIRP[26] */ Ins_MIRP,
+ /* MIRP[27] */ Ins_MIRP,
+ /* MIRP[28] */ Ins_MIRP,
+ /* MIRP[29] */ Ins_MIRP,
+ /* MIRP[30] */ Ins_MIRP,
+ /* MIRP[31] */ Ins_MIRP
+ };
+
+
+#endif /* !TT_CONFIG_OPTION_INTERPRETER_SWITCH */
+
+
+ /*************************************************************************/
+ /* */
+ /* RUN */
+ /* */
+ /* This function executes a run of opcodes. It will exit in the */
+ /* following cases: */
+ /* */
+ /* - Errors (in which case it returns FALSE). */
+ /* */
+ /* - Reaching the end of the main code range (returns TRUE). */
+ /* Reaching the end of a code range within a function call is an */
+ /* error. */
+ /* */
+ /* - After executing one single opcode, if the flag `Instruction_Trap' */
+ /* is set to TRUE (returns TRUE). */
+ /* */
+ /* On exit with TRUE, test IP < CodeSize to know whether it comes from */
+ /* an instruction trap or a normal termination. */
+ /* */
+ /* */
+ /* Note: The documented DEBUG opcode pops a value from the stack. This */
+ /* behaviour is unsupported; here a DEBUG opcode is always an */
+ /* error. */
+ /* */
+ /* */
+ /* THIS IS THE INTERPRETER'S MAIN LOOP. */
+ /* */
+ /* Instructions appear in the specification's order. */
+ /* */
+ /*************************************************************************/
+
+
+ /* documentation is in ttinterp.h */
+
+ FT_EXPORT_DEF( FT_Error )
+ TT_RunIns( TT_ExecContext exc )
+ {
+ FT_Long ins_counter = 0; /* executed instructions counter */
+
+
+#ifdef TT_CONFIG_OPTION_STATIC_RASTER
+ cur = *exc;
+#endif
+
+ /* set CVT functions */
+ CUR.tt_metrics.ratio = 0;
+ if ( CUR.metrics.x_ppem != CUR.metrics.y_ppem )
+ {
+ /* non-square pixels, use the stretched routines */
+ CUR.func_read_cvt = Read_CVT_Stretched;
+ CUR.func_write_cvt = Write_CVT_Stretched;
+ CUR.func_move_cvt = Move_CVT_Stretched;
+ }
+ else
+ {
+ /* square pixels, use normal routines */
+ CUR.func_read_cvt = Read_CVT;
+ CUR.func_write_cvt = Write_CVT;
+ CUR.func_move_cvt = Move_CVT;
+ }
+
+ COMPUTE_Funcs();
+ COMPUTE_Round( (FT_Byte)exc->GS.round_state );
+
+ do
+ {
+ CUR.opcode = CUR.code[CUR.IP];
+
+ if ( ( CUR.length = opcode_length[CUR.opcode] ) < 0 )
+ {
+ if ( CUR.IP + 1 > CUR.codeSize )
+ goto LErrorCodeOverflow_;
+
+ CUR.length = 2 - CUR.length * CUR.code[CUR.IP + 1];
+ }
+
+ if ( CUR.IP + CUR.length > CUR.codeSize )
+ goto LErrorCodeOverflow_;
+
+ /* First, let's check for empty stack and overflow */
+ CUR.args = CUR.top - ( Pop_Push_Count[CUR.opcode] >> 4 );
+
+ /* `args' is the top of the stack once arguments have been popped. */
+ /* One can also interpret it as the index of the last argument. */
+ if ( CUR.args < 0 )
+ {
+ CUR.error = TT_Err_Too_Few_Arguments;
+ goto LErrorLabel_;
+ }
+
+ CUR.new_top = CUR.args + ( Pop_Push_Count[CUR.opcode] & 15 );
+
+ /* `new_top' is the new top of the stack, after the instruction's */
+ /* execution. `top' will be set to `new_top' after the `switch' */
+ /* statement. */
+ if ( CUR.new_top > CUR.stackSize )
+ {
+ CUR.error = TT_Err_Stack_Overflow;
+ goto LErrorLabel_;
+ }
+
+ CUR.step_ins = TRUE;
+ CUR.error = TT_Err_Ok;
+
+#ifdef TT_CONFIG_OPTION_INTERPRETER_SWITCH
+
+ {
+ FT_Long* args = CUR.stack + CUR.args;
+ FT_Byte opcode = CUR.opcode;
+
+
+#undef ARRAY_BOUND_ERROR
+#define ARRAY_BOUND_ERROR goto Set_Invalid_Ref
+
+
+ switch ( opcode )
+ {
+ case 0x00: /* SVTCA y */
+ case 0x01: /* SVTCA x */
+ case 0x02: /* SPvTCA y */
+ case 0x03: /* SPvTCA x */
+ case 0x04: /* SFvTCA y */
+ case 0x05: /* SFvTCA x */
+ {
+ FT_Short AA, BB;
+
+
+ AA = (FT_Short)( ( opcode & 1 ) << 14 );
+ BB = (FT_Short)( AA ^ 0x4000 );
+
+ if ( opcode < 4 )
+ {
+ CUR.GS.projVector.x = AA;
+ CUR.GS.projVector.y = BB;
+
+ CUR.GS.dualVector.x = AA;
+ CUR.GS.dualVector.y = BB;
+ }
+ else
+ {
+ GUESS_VECTOR( projVector );
+ }
+
+ if ( ( opcode & 2 ) == 0 )
+ {
+ CUR.GS.freeVector.x = AA;
+ CUR.GS.freeVector.y = BB;
+ }
+ else
+ {
+ GUESS_VECTOR( freeVector );
+ }
+
+ COMPUTE_Funcs();
+ }
+ break;
+
+ case 0x06: /* SPvTL // */
+ case 0x07: /* SPvTL + */
+ DO_SPVTL
+ break;
+
+ case 0x08: /* SFvTL // */
+ case 0x09: /* SFvTL + */
+ DO_SFVTL
+ break;
+
+ case 0x0A: /* SPvFS */
+ DO_SPVFS
+ break;
+
+ case 0x0B: /* SFvFS */
+ DO_SFVFS
+ break;
+
+ case 0x0C: /* GPV */
+ DO_GPV
+ break;
+
+ case 0x0D: /* GFV */
+ DO_GFV
+ break;
+
+ case 0x0E: /* SFvTPv */
+ DO_SFVTPV
+ break;
+
+ case 0x0F: /* ISECT */
+ Ins_ISECT( EXEC_ARG_ args );
+ break;
+
+ case 0x10: /* SRP0 */
+ DO_SRP0
+ break;
+
+ case 0x11: /* SRP1 */
+ DO_SRP1
+ break;
+
+ case 0x12: /* SRP2 */
+ DO_SRP2
+ break;
+
+ case 0x13: /* SZP0 */
+ Ins_SZP0( EXEC_ARG_ args );
+ break;
+
+ case 0x14: /* SZP1 */
+ Ins_SZP1( EXEC_ARG_ args );
+ break;
+
+ case 0x15: /* SZP2 */
+ Ins_SZP2( EXEC_ARG_ args );
+ break;
+
+ case 0x16: /* SZPS */
+ Ins_SZPS( EXEC_ARG_ args );
+ break;
+
+ case 0x17: /* SLOOP */
+ DO_SLOOP
+ break;
+
+ case 0x18: /* RTG */
+ DO_RTG
+ break;
+
+ case 0x19: /* RTHG */
+ DO_RTHG
+ break;
+
+ case 0x1A: /* SMD */
+ DO_SMD
+ break;
+
+ case 0x1B: /* ELSE */
+ Ins_ELSE( EXEC_ARG_ args );
+ break;
+
+ case 0x1C: /* JMPR */
+ DO_JMPR
+ break;
+
+ case 0x1D: /* SCVTCI */
+ DO_SCVTCI
+ break;
+
+ case 0x1E: /* SSWCI */
+ DO_SSWCI
+ break;
+
+ case 0x1F: /* SSW */
+ DO_SSW
+ break;
+
+ case 0x20: /* DUP */
+ DO_DUP
+ break;
+
+ case 0x21: /* POP */
+ /* nothing :-) */
+ break;
+
+ case 0x22: /* CLEAR */
+ DO_CLEAR
+ break;
+
+ case 0x23: /* SWAP */
+ DO_SWAP
+ break;
+
+ case 0x24: /* DEPTH */
+ DO_DEPTH
+ break;
+
+ case 0x25: /* CINDEX */
+ DO_CINDEX
+ break;
+
+ case 0x26: /* MINDEX */
+ Ins_MINDEX( EXEC_ARG_ args );
+ break;
+
+ case 0x27: /* ALIGNPTS */
+ Ins_ALIGNPTS( EXEC_ARG_ args );
+ break;
+
+ case 0x28: /* ???? */
+ Ins_UNKNOWN( EXEC_ARG_ args );
+ break;
+
+ case 0x29: /* UTP */
+ Ins_UTP( EXEC_ARG_ args );
+ break;
+
+ case 0x2A: /* LOOPCALL */
+ Ins_LOOPCALL( EXEC_ARG_ args );
+ break;
+
+ case 0x2B: /* CALL */
+ Ins_CALL( EXEC_ARG_ args );
+ break;
+
+ case 0x2C: /* FDEF */
+ Ins_FDEF( EXEC_ARG_ args );
+ break;
+
+ case 0x2D: /* ENDF */
+ Ins_ENDF( EXEC_ARG_ args );
+ break;
+
+ case 0x2E: /* MDAP */
+ case 0x2F: /* MDAP */
+ Ins_MDAP( EXEC_ARG_ args );
+ break;
+
+
+ case 0x30: /* IUP */
+ case 0x31: /* IUP */
+ Ins_IUP( EXEC_ARG_ args );
+ break;
+
+ case 0x32: /* SHP */
+ case 0x33: /* SHP */
+ Ins_SHP( EXEC_ARG_ args );
+ break;
+
+ case 0x34: /* SHC */
+ case 0x35: /* SHC */
+ Ins_SHC( EXEC_ARG_ args );
+ break;
+
+ case 0x36: /* SHZ */
+ case 0x37: /* SHZ */
+ Ins_SHZ( EXEC_ARG_ args );
+ break;
+
+ case 0x38: /* SHPIX */
+ Ins_SHPIX( EXEC_ARG_ args );
+ break;
+
+ case 0x39: /* IP */
+ Ins_IP( EXEC_ARG_ args );
+ break;
+
+ case 0x3A: /* MSIRP */
+ case 0x3B: /* MSIRP */
+ Ins_MSIRP( EXEC_ARG_ args );
+ break;
+
+ case 0x3C: /* AlignRP */
+ Ins_ALIGNRP( EXEC_ARG_ args );
+ break;
+
+ case 0x3D: /* RTDG */
+ DO_RTDG
+ break;
+
+ case 0x3E: /* MIAP */
+ case 0x3F: /* MIAP */
+ Ins_MIAP( EXEC_ARG_ args );
+ break;
+
+ case 0x40: /* NPUSHB */
+ Ins_NPUSHB( EXEC_ARG_ args );
+ break;
+
+ case 0x41: /* NPUSHW */
+ Ins_NPUSHW( EXEC_ARG_ args );
+ break;
+
+ case 0x42: /* WS */
+ DO_WS
+ break;
+
+ Set_Invalid_Ref:
+ CUR.error = TT_Err_Invalid_Reference;
+ break;
+
+ case 0x43: /* RS */
+ DO_RS
+ break;
+
+ case 0x44: /* WCVTP */
+ DO_WCVTP
+ break;
+
+ case 0x45: /* RCVT */
+ DO_RCVT
+ break;
+
+ case 0x46: /* GC */
+ case 0x47: /* GC */
+ Ins_GC( EXEC_ARG_ args );
+ break;
+
+ case 0x48: /* SCFS */
+ Ins_SCFS( EXEC_ARG_ args );
+ break;
+
+ case 0x49: /* MD */
+ case 0x4A: /* MD */
+ Ins_MD( EXEC_ARG_ args );
+ break;
+
+ case 0x4B: /* MPPEM */
+ DO_MPPEM
+ break;
+
+ case 0x4C: /* MPS */
+ DO_MPS
+ break;
+
+ case 0x4D: /* FLIPON */
+ DO_FLIPON
+ break;
+
+ case 0x4E: /* FLIPOFF */
+ DO_FLIPOFF
+ break;
+
+ case 0x4F: /* DEBUG */
+ DO_DEBUG
+ break;
+
+ case 0x50: /* LT */
+ DO_LT
+ break;
+
+ case 0x51: /* LTEQ */
+ DO_LTEQ
+ break;
+
+ case 0x52: /* GT */
+ DO_GT
+ break;
+
+ case 0x53: /* GTEQ */
+ DO_GTEQ
+ break;
+
+ case 0x54: /* EQ */
+ DO_EQ
+ break;
+
+ case 0x55: /* NEQ */
+ DO_NEQ
+ break;
+
+ case 0x56: /* ODD */
+ DO_ODD
+ break;
+
+ case 0x57: /* EVEN */
+ DO_EVEN
+ break;
+
+ case 0x58: /* IF */
+ Ins_IF( EXEC_ARG_ args );
+ break;
+
+ case 0x59: /* EIF */
+ /* do nothing */
+ break;
+
+ case 0x5A: /* AND */
+ DO_AND
+ break;
+
+ case 0x5B: /* OR */
+ DO_OR
+ break;
+
+ case 0x5C: /* NOT */
+ DO_NOT
+ break;
+
+ case 0x5D: /* DELTAP1 */
+ Ins_DELTAP( EXEC_ARG_ args );
+ break;
+
+ case 0x5E: /* SDB */
+ DO_SDB
+ break;
+
+ case 0x5F: /* SDS */
+ DO_SDS
+ break;
+
+ case 0x60: /* ADD */
+ DO_ADD
+ break;
+
+ case 0x61: /* SUB */
+ DO_SUB
+ break;
+
+ case 0x62: /* DIV */
+ DO_DIV
+ break;
+
+ case 0x63: /* MUL */
+ DO_MUL
+ break;
+
+ case 0x64: /* ABS */
+ DO_ABS
+ break;
+
+ case 0x65: /* NEG */
+ DO_NEG
+ break;
+
+ case 0x66: /* FLOOR */
+ DO_FLOOR
+ break;
+
+ case 0x67: /* CEILING */
+ DO_CEILING
+ break;
+
+ case 0x68: /* ROUND */
+ case 0x69: /* ROUND */
+ case 0x6A: /* ROUND */
+ case 0x6B: /* ROUND */
+ DO_ROUND
+ break;
+
+ case 0x6C: /* NROUND */
+ case 0x6D: /* NROUND */
+ case 0x6E: /* NRRUND */
+ case 0x6F: /* NROUND */
+ DO_NROUND
+ break;
+
+ case 0x70: /* WCVTF */
+ DO_WCVTF
+ break;
+
+ case 0x71: /* DELTAP2 */
+ case 0x72: /* DELTAP3 */
+ Ins_DELTAP( EXEC_ARG_ args );
+ break;
+
+ case 0x73: /* DELTAC0 */
+ case 0x74: /* DELTAC1 */
+ case 0x75: /* DELTAC2 */
+ Ins_DELTAC( EXEC_ARG_ args );
+ break;
+
+ case 0x76: /* SROUND */
+ DO_SROUND
+ break;
+
+ case 0x77: /* S45Round */
+ DO_S45ROUND
+ break;
+
+ case 0x78: /* JROT */
+ DO_JROT
+ break;
+
+ case 0x79: /* JROF */
+ DO_JROF
+ break;
+
+ case 0x7A: /* ROFF */
+ DO_ROFF
+ break;
+
+ case 0x7B: /* ???? */
+ Ins_UNKNOWN( EXEC_ARG_ args );
+ break;
+
+ case 0x7C: /* RUTG */
+ DO_RUTG
+ break;
+
+ case 0x7D: /* RDTG */
+ DO_RDTG
+ break;
+
+ case 0x7E: /* SANGW */
+ case 0x7F: /* AA */
+ /* nothing - obsolete */
+ break;
+
+ case 0x80: /* FLIPPT */
+ Ins_FLIPPT( EXEC_ARG_ args );
+ break;
+
+ case 0x81: /* FLIPRGON */
+ Ins_FLIPRGON( EXEC_ARG_ args );
+ break;
+
+ case 0x82: /* FLIPRGOFF */
+ Ins_FLIPRGOFF( EXEC_ARG_ args );
+ break;
+
+ case 0x83: /* UNKNOWN */
+ case 0x84: /* UNKNOWN */
+ Ins_UNKNOWN( EXEC_ARG_ args );
+ break;
+
+ case 0x85: /* SCANCTRL */
+ Ins_SCANCTRL( EXEC_ARG_ args );
+ break;
+
+ case 0x86: /* SDPVTL */
+ case 0x87: /* SDPVTL */
+ Ins_SDPVTL( EXEC_ARG_ args );
+ break;
+
+ case 0x88: /* GETINFO */
+ Ins_GETINFO( EXEC_ARG_ args );
+ break;
+
+ case 0x89: /* IDEF */
+ Ins_IDEF( EXEC_ARG_ args );
+ break;
+
+ case 0x8A: /* ROLL */
+ Ins_ROLL( EXEC_ARG_ args );
+ break;
+
+ case 0x8B: /* MAX */
+ DO_MAX
+ break;
+
+ case 0x8C: /* MIN */
+ DO_MIN
+ break;
+
+ case 0x8D: /* SCANTYPE */
+ Ins_SCANTYPE( EXEC_ARG_ args );
+ break;
+
+ case 0x8E: /* INSTCTRL */
+ Ins_INSTCTRL( EXEC_ARG_ args );
+ break;
+
+ case 0x8F:
+ Ins_UNKNOWN( EXEC_ARG_ args );
+ break;
+
+ default:
+ if ( opcode >= 0xE0 )
+ Ins_MIRP( EXEC_ARG_ args );
+ else if ( opcode >= 0xC0 )
+ Ins_MDRP( EXEC_ARG_ args );
+ else if ( opcode >= 0xB8 )
+ Ins_PUSHW( EXEC_ARG_ args );
+ else if ( opcode >= 0xB0 )
+ Ins_PUSHB( EXEC_ARG_ args );
+ else
+ Ins_UNKNOWN( EXEC_ARG_ args );
+ }
+
+ }
+
+#else
+
+ Instruct_Dispatch[CUR.opcode]( EXEC_ARG_ &CUR.stack[CUR.args] );
+
+#endif /* TT_CONFIG_OPTION_INTERPRETER_SWITCH */
+
+ if ( CUR.error != TT_Err_Ok )
+ {
+ switch ( CUR.error )
+ {
+ case TT_Err_Invalid_Opcode: /* looking for redefined instructions */
+ {
+ TT_DefRecord* def = CUR.IDefs;
+ TT_DefRecord* limit = def + CUR.numIDefs;
+
+
+ for ( ; def < limit; def++ )
+ {
+ if ( def->active && CUR.opcode == (FT_Byte)def->opc )
+ {
+ TT_CallRec* callrec;
+
+
+ if ( CUR.callTop >= CUR.callSize )
+ {
+ CUR.error = TT_Err_Invalid_Reference;
+ goto LErrorLabel_;
+ }
+
+ callrec = &CUR.callStack[CUR.callTop];
+
+ callrec->Caller_Range = CUR.curRange;
+ callrec->Caller_IP = CUR.IP + 1;
+ callrec->Cur_Count = 1;
+ callrec->Cur_Restart = def->start;
+
+ if ( INS_Goto_CodeRange( def->range, def->start ) == FAILURE )
+ goto LErrorLabel_;
+
+ goto LSuiteLabel_;
+ }
+ }
+ }
+
+ CUR.error = TT_Err_Invalid_Opcode;
+ goto LErrorLabel_;
+
+#if 0
+ break; /* Unreachable code warning suppression. */
+ /* Leave to remind in case a later change the editor */
+ /* to consider break; */
+#endif
+
+ default:
+ goto LErrorLabel_;
+
+#if 0
+ break;
+#endif
+ }
+ }
+
+ CUR.top = CUR.new_top;
+
+ if ( CUR.step_ins )
+ CUR.IP += CUR.length;
+
+ /* increment instruction counter and check if we didn't */
+ /* run this program for too long (e.g. infinite loops). */
+ if ( ++ins_counter > MAX_RUNNABLE_OPCODES )
+ return TT_Err_Execution_Too_Long;
+
+ LSuiteLabel_:
+ if ( CUR.IP >= CUR.codeSize )
+ {
+ if ( CUR.callTop > 0 )
+ {
+ CUR.error = TT_Err_Code_Overflow;
+ goto LErrorLabel_;
+ }
+ else
+ goto LNo_Error_;
+ }
+ } while ( !CUR.instruction_trap );
+
+ LNo_Error_:
+
+#ifdef TT_CONFIG_OPTION_STATIC_RASTER
+ *exc = cur;
+#endif
+
+ return TT_Err_Ok;
+
+ LErrorCodeOverflow_:
+ CUR.error = TT_Err_Code_Overflow;
+
+ LErrorLabel_:
+
+#ifdef TT_CONFIG_OPTION_STATIC_RASTER
+ *exc = cur;
+#endif
+
+ return CUR.error;
+ }
+
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+/* END */
diff --git a/src/truetype/ttinterp.h b/src/truetype/ttinterp.h
new file mode 100644
index 0000000..07a8972
--- /dev/null
+++ b/src/truetype/ttinterp.h
@@ -0,0 +1,311 @@
+/***************************************************************************/
+/* */
+/* ttinterp.h */
+/* */
+/* TrueType bytecode interpreter (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTINTERP_H__
+#define __TTINTERP_H__
+
+#include <ft2build.h>
+#include "ttobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+#ifndef TT_CONFIG_OPTION_STATIC_INTERPRETER /* indirect implementation */
+
+#define EXEC_OP_ TT_ExecContext exc,
+#define EXEC_OP TT_ExecContext exc
+#define EXEC_ARG_ exc,
+#define EXEC_ARG exc
+
+#else /* static implementation */
+
+#define EXEC_OP_ /* void */
+#define EXEC_OP /* void */
+#define EXEC_ARG_ /* void */
+#define EXEC_ARG /* void */
+
+#endif /* TT_CONFIG_OPTION_STATIC_INTERPRETER */
+
+
+ /*************************************************************************/
+ /* */
+ /* Rounding mode constants. */
+ /* */
+#define TT_Round_Off 5
+#define TT_Round_To_Half_Grid 0
+#define TT_Round_To_Grid 1
+#define TT_Round_To_Double_Grid 2
+#define TT_Round_Up_To_Grid 4
+#define TT_Round_Down_To_Grid 3
+#define TT_Round_Super 6
+#define TT_Round_Super_45 7
+
+
+ /*************************************************************************/
+ /* */
+ /* Function types used by the interpreter, depending on various modes */
+ /* (e.g. the rounding mode, whether to render a vertical or horizontal */
+ /* line etc). */
+ /* */
+ /*************************************************************************/
+
+ /* Rounding function */
+ typedef FT_F26Dot6
+ (*TT_Round_Func)( EXEC_OP_ FT_F26Dot6 distance,
+ FT_F26Dot6 compensation );
+
+ /* Point displacement along the freedom vector routine */
+ typedef void
+ (*TT_Move_Func)( EXEC_OP_ TT_GlyphZone zone,
+ FT_UShort point,
+ FT_F26Dot6 distance );
+
+ /* Distance projection along one of the projection vectors */
+ typedef FT_F26Dot6
+ (*TT_Project_Func)( EXEC_OP_ FT_Pos dx,
+ FT_Pos dy );
+
+ /* reading a cvt value. Take care of non-square pixels if necessary */
+ typedef FT_F26Dot6
+ (*TT_Get_CVT_Func)( EXEC_OP_ FT_ULong idx );
+
+ /* setting or moving a cvt value. Take care of non-square pixels */
+ /* if necessary */
+ typedef void
+ (*TT_Set_CVT_Func)( EXEC_OP_ FT_ULong idx,
+ FT_F26Dot6 value );
+
+
+ /*************************************************************************/
+ /* */
+ /* This structure defines a call record, used to manage function calls. */
+ /* */
+ typedef struct TT_CallRec_
+ {
+ FT_Int Caller_Range;
+ FT_Long Caller_IP;
+ FT_Long Cur_Count;
+ FT_Long Cur_Restart;
+
+ } TT_CallRec, *TT_CallStack;
+
+
+ /*************************************************************************/
+ /* */
+ /* The main structure for the interpreter which collects all necessary */
+ /* variables and states. */
+ /* */
+ typedef struct TT_ExecContextRec_
+ {
+ TT_Face face;
+ TT_Size size;
+ FT_Memory memory;
+
+ /* instructions state */
+
+ FT_Error error; /* last execution error */
+
+ FT_Long top; /* top of exec. stack */
+
+ FT_UInt stackSize; /* size of exec. stack */
+ FT_Long* stack; /* current exec. stack */
+
+ FT_Long args;
+ FT_UInt new_top; /* new top after exec. */
+
+ TT_GlyphZoneRec zp0, /* zone records */
+ zp1,
+ zp2,
+ pts,
+ twilight;
+
+ FT_Size_Metrics metrics;
+ TT_Size_Metrics tt_metrics; /* size metrics */
+
+ TT_GraphicsState GS; /* current graphics state */
+
+ FT_Int curRange; /* current code range number */
+ FT_Byte* code; /* current code range */
+ FT_Long IP; /* current instruction pointer */
+ FT_Long codeSize; /* size of current range */
+
+ FT_Byte opcode; /* current opcode */
+ FT_Int length; /* length of current opcode */
+
+ FT_Bool step_ins; /* true if the interpreter must */
+ /* increment IP after ins. exec */
+ FT_Long cvtSize;
+ FT_Long* cvt;
+
+ FT_UInt glyphSize; /* glyph instructions buffer size */
+ FT_Byte* glyphIns; /* glyph instructions buffer */
+
+ FT_UInt numFDefs; /* number of function defs */
+ FT_UInt maxFDefs; /* maximum number of function defs */
+ TT_DefArray FDefs; /* table of FDefs entries */
+
+ FT_UInt numIDefs; /* number of instruction defs */
+ FT_UInt maxIDefs; /* maximum number of ins defs */
+ TT_DefArray IDefs; /* table of IDefs entries */
+
+ FT_UInt maxFunc; /* maximum function index */
+ FT_UInt maxIns; /* maximum instruction index */
+
+ FT_Int callTop, /* top of call stack during execution */
+ callSize; /* size of call stack */
+ TT_CallStack callStack; /* call stack */
+
+ FT_UShort maxPoints; /* capacity of this context's `pts' */
+ FT_Short maxContours; /* record, expressed in points and */
+ /* contours. */
+
+ TT_CodeRangeTable codeRangeTable; /* table of valid code ranges */
+ /* useful for the debugger */
+
+ FT_UShort storeSize; /* size of current storage */
+ FT_Long* storage; /* storage area */
+
+ FT_F26Dot6 period; /* values used for the */
+ FT_F26Dot6 phase; /* `SuperRounding' */
+ FT_F26Dot6 threshold;
+
+#if 0
+ /* this seems to be unused */
+ FT_Int cur_ppem; /* ppem along the current proj vector */
+#endif
+
+ FT_Bool instruction_trap; /* If `True', the interpreter will */
+ /* exit after each instruction */
+
+ TT_GraphicsState default_GS; /* graphics state resulting from */
+ /* the prep program */
+ FT_Bool is_composite; /* true if the glyph is composite */
+ FT_Bool pedantic_hinting; /* true if pedantic interpretation */
+
+ /* latest interpreter additions */
+
+ FT_Long F_dot_P; /* dot product of freedom and projection */
+ /* vectors */
+ TT_Round_Func func_round; /* current rounding function */
+
+ TT_Project_Func func_project, /* current projection function */
+ func_dualproj, /* current dual proj. function */
+ func_freeProj; /* current freedom proj. func */
+
+ TT_Move_Func func_move; /* current point move function */
+ TT_Move_Func func_move_orig; /* move original position function */
+
+ TT_Get_CVT_Func func_read_cvt; /* read a cvt entry */
+ TT_Set_CVT_Func func_write_cvt; /* write a cvt entry (in pixels) */
+ TT_Set_CVT_Func func_move_cvt; /* incr a cvt entry (in pixels) */
+
+ FT_Bool grayscale; /* are we hinting for grayscale? */
+
+ } TT_ExecContextRec;
+
+
+ extern const TT_GraphicsState tt_default_graphics_state;
+
+
+ FT_LOCAL( FT_Error )
+ TT_Goto_CodeRange( TT_ExecContext exec,
+ FT_Int range,
+ FT_Long IP );
+
+ FT_LOCAL( FT_Error )
+ TT_Set_CodeRange( TT_ExecContext exec,
+ FT_Int range,
+ void* base,
+ FT_Long length );
+
+ FT_LOCAL( FT_Error )
+ TT_Clear_CodeRange( TT_ExecContext exec,
+ FT_Int range );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_New_Context */
+ /* */
+ /* <Description> */
+ /* Queries the face context for a given font. Note that there is */
+ /* now a _single_ execution context in the TrueType driver which is */
+ /* shared among faces. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* <Return> */
+ /* A handle to the execution context. Initialized for `face'. */
+ /* */
+ /* <Note> */
+ /* Only the glyph loader and debugger should call this function. */
+ /* */
+ FT_EXPORT( TT_ExecContext )
+ TT_New_Context( TT_Driver driver );
+
+ FT_LOCAL( FT_Error )
+ TT_Done_Context( TT_ExecContext exec );
+
+ FT_LOCAL( FT_Error )
+ TT_Load_Context( TT_ExecContext exec,
+ TT_Face face,
+ TT_Size size );
+
+ FT_LOCAL( FT_Error )
+ TT_Save_Context( TT_ExecContext exec,
+ TT_Size ins );
+
+ FT_LOCAL( FT_Error )
+ TT_Run_Context( TT_ExecContext exec,
+ FT_Bool debug );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* TT_RunIns */
+ /* */
+ /* <Description> */
+ /* Executes one or more instruction in the execution context. This */
+ /* is the main function of the TrueType opcode interpreter. */
+ /* */
+ /* <Input> */
+ /* exec :: A handle to the target execution context. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only the object manager and debugger should call this function. */
+ /* */
+ /* This function is publicly exported because it is directly */
+ /* invoked by the TrueType debugger. */
+ /* */
+ FT_EXPORT( FT_Error )
+ TT_RunIns( TT_ExecContext exec );
+
+
+FT_END_HEADER
+
+#endif /* __TTINTERP_H__ */
+
+
+/* END */
diff --git a/src/truetype/ttobjs.c b/src/truetype/ttobjs.c
new file mode 100644
index 0000000..0294a1b
--- /dev/null
+++ b/src/truetype/ttobjs.c
@@ -0,0 +1,937 @@
+/***************************************************************************/
+/* */
+/* ttobjs.c */
+/* */
+/* Objects manager (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_IDS_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_INTERNAL_SFNT_H
+
+#include "ttgload.h"
+#include "ttpload.h"
+
+#include "tterrors.h"
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#include "ttinterp.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+#include FT_TRUETYPE_UNPATENTED_H
+#endif
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttobjs
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ /*************************************************************************/
+ /* */
+ /* GLYPH ZONE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_glyphzone_done */
+ /* */
+ /* <Description> */
+ /* Deallocate a glyph zone. */
+ /* */
+ /* <Input> */
+ /* zone :: A pointer to the target glyph zone. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_glyphzone_done( TT_GlyphZone zone )
+ {
+ FT_Memory memory = zone->memory;
+
+
+ if ( memory )
+ {
+ FT_FREE( zone->contours );
+ FT_FREE( zone->tags );
+ FT_FREE( zone->cur );
+ FT_FREE( zone->org );
+ FT_FREE( zone->orus );
+
+ zone->max_points = zone->n_points = 0;
+ zone->max_contours = zone->n_contours = 0;
+ zone->memory = NULL;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_glyphzone_new */
+ /* */
+ /* <Description> */
+ /* Allocate a new glyph zone. */
+ /* */
+ /* <Input> */
+ /* memory :: A handle to the current memory object. */
+ /* */
+ /* maxPoints :: The capacity of glyph zone in points. */
+ /* */
+ /* maxContours :: The capacity of glyph zone in contours. */
+ /* */
+ /* <Output> */
+ /* zone :: A pointer to the target glyph zone record. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_glyphzone_new( FT_Memory memory,
+ FT_UShort maxPoints,
+ FT_Short maxContours,
+ TT_GlyphZone zone )
+ {
+ FT_Error error;
+
+
+ FT_MEM_ZERO( zone, sizeof ( *zone ) );
+ zone->memory = memory;
+
+ if ( FT_NEW_ARRAY( zone->org, maxPoints ) ||
+ FT_NEW_ARRAY( zone->cur, maxPoints ) ||
+ FT_NEW_ARRAY( zone->orus, maxPoints ) ||
+ FT_NEW_ARRAY( zone->tags, maxPoints ) ||
+ FT_NEW_ARRAY( zone->contours, maxContours ) )
+ {
+ tt_glyphzone_done( zone );
+ }
+ else
+ {
+ zone->max_points = maxPoints;
+ zone->max_contours = maxContours;
+ }
+
+ return error;
+ }
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_init */
+ /* */
+ /* <Description> */
+ /* Initialize a given TrueType face object. */
+ /* */
+ /* <Input> */
+ /* stream :: The source font stream. */
+ /* */
+ /* face_index :: The index of the font face in the resource. */
+ /* */
+ /* num_params :: Number of additional generic parameters. Ignored. */
+ /* */
+ /* params :: Additional generic parameters. Ignored. */
+ /* */
+ /* <InOut> */
+ /* face :: The newly built face object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_init( FT_Stream stream,
+ FT_Face ttface, /* TT_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Library library;
+ SFNT_Service sfnt;
+ TT_Face face = (TT_Face)ttface;
+
+
+ library = face->root.driver->root.library;
+ sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
+ if ( !sfnt )
+ goto Bad_Format;
+
+ /* create input stream from resource */
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ /* check that we have a valid TrueType file */
+ error = sfnt->init_face( stream, face, face_index, num_params, params );
+ if ( error )
+ goto Exit;
+
+ /* We must also be able to accept Mac/GX fonts, as well as OT ones. */
+ /* The 0x00020000 tag is completely undocumented; some fonts from */
+ /* Arphic made for Chinese Windows 3.1 have this. */
+ if ( face->format_tag != 0x00010000L && /* MS fonts */
+ face->format_tag != 0x00020000L && /* CJK fonts for Win 3.1 */
+ face->format_tag != TTAG_true ) /* Mac fonts */
+ {
+ FT_TRACE2(( "[not a valid TTF font]\n" ));
+ goto Bad_Format;
+ }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ face->root.face_flags |= FT_FACE_FLAG_HINTER;
+#endif
+
+ /* If we are performing a simple font format check, exit immediately. */
+ if ( face_index < 0 )
+ return TT_Err_Ok;
+
+ /* Load font directory */
+ error = sfnt->load_face( stream, face, face_index, num_params, params );
+ if ( error )
+ goto Exit;
+
+ error = tt_face_load_hdmx( face, stream );
+ if ( error )
+ goto Exit;
+
+ if ( face->root.face_flags & FT_FACE_FLAG_SCALABLE )
+ {
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ if ( !face->root.internal->incremental_interface )
+ error = tt_face_load_loca( face, stream );
+ if ( !error )
+ error = tt_face_load_cvt( face, stream ) ||
+ tt_face_load_fpgm( face, stream ) ||
+ tt_face_load_prep( face, stream );
+
+#else
+
+ if ( !error )
+ error = tt_face_load_loca( face, stream ) ||
+ tt_face_load_cvt( face, stream ) ||
+ tt_face_load_fpgm( face, stream ) ||
+ tt_face_load_prep( face, stream );
+
+#endif
+
+ }
+
+#if defined( TT_CONFIG_OPTION_UNPATENTED_HINTING ) && \
+ !defined( TT_CONFIG_OPTION_BYTECODE_INTERPRETER )
+
+ {
+ FT_Bool unpatented_hinting;
+ int i;
+
+
+ /* Determine whether unpatented hinting is to be used for this face. */
+ unpatented_hinting = FT_BOOL
+ ( library->debug_hooks[FT_DEBUG_HOOK_UNPATENTED_HINTING] != NULL );
+
+ for ( i = 0; i < num_params && !face->unpatented_hinting; i++ )
+ if ( params[i].tag == FT_PARAM_TAG_UNPATENTED_HINTING )
+ unpatented_hinting = TRUE;
+
+ /* Compare the face with a list of well-known `tricky' fonts. */
+ /* This list shall be expanded as we find more of them. */
+ if ( !unpatented_hinting )
+ {
+ static const char* const trick_names[] =
+ {
+ "DFKaiSho-SB", /* dfkaisb.ttf */
+ "DFKai-SB", /* kaiu.ttf */
+ "HuaTianSongTi?", /* htst3.ttf */
+ "MingLiU", /* mingliu.ttf & mingliu.ttc */
+ "PMingLiU", /* mingliu.ttc */
+ "MingLi43", /* mingli.ttf */
+ NULL
+ };
+ int nn;
+
+
+ /* Note that we only check the face name at the moment; it might */
+ /* be worth to do more checks for a few special cases. */
+ for ( nn = 0; trick_names[nn] != NULL; nn++ )
+ {
+ if ( ttface->family_name &&
+ ft_strstr( ttface->family_name, trick_names[nn] ) )
+ {
+ unpatented_hinting = 1;
+ break;
+ }
+ }
+ }
+
+ ttface->internal->ignore_unpatented_hinter =
+ FT_BOOL( !unpatented_hinting );
+ }
+
+#endif /* TT_CONFIG_OPTION_UNPATENTED_HINTING &&
+ !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
+
+ /* initialize standard glyph loading routines */
+ TT_Init_Glyph_Loading( face );
+
+ Exit:
+ return error;
+
+ Bad_Format:
+ error = TT_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_done */
+ /* */
+ /* <Description> */
+ /* Finalize a given face object. */
+ /* */
+ /* <Input> */
+ /* face :: A pointer to the face object to destroy. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_face_done( FT_Face ttface ) /* TT_Face */
+ {
+ TT_Face face = (TT_Face)ttface;
+ FT_Memory memory = face->root.memory;
+ FT_Stream stream = face->root.stream;
+
+ SFNT_Service sfnt = (SFNT_Service)face->sfnt;
+
+
+ /* for `extended TrueType formats' (i.e. compressed versions) */
+ if ( face->extra.finalizer )
+ face->extra.finalizer( face->extra.data );
+
+ if ( sfnt )
+ sfnt->done_face( face );
+
+ /* freeing the locations table */
+ tt_face_done_loca( face );
+
+ tt_face_free_hdmx( face );
+
+ /* freeing the CVT */
+ FT_FREE( face->cvt );
+ face->cvt_size = 0;
+
+ /* freeing the programs */
+ FT_FRAME_RELEASE( face->font_program );
+ FT_FRAME_RELEASE( face->cvt_program );
+ face->font_program_size = 0;
+ face->cvt_program_size = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ tt_done_blend( memory, face->blend );
+ face->blend = NULL;
+#endif
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SIZE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_size_run_fpgm */
+ /* */
+ /* <Description> */
+ /* Run the font program. */
+ /* */
+ /* <Input> */
+ /* size :: A handle to the size object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_run_fpgm( TT_Size size )
+ {
+ TT_Face face = (TT_Face)size->root.face;
+ TT_ExecContext exec;
+ FT_Error error;
+
+
+ /* debugging instances have their own context */
+ if ( size->debug )
+ exec = size->context;
+ else
+ exec = ( (TT_Driver)FT_FACE_DRIVER( face ) )->context;
+
+ if ( !exec )
+ return TT_Err_Could_Not_Find_Context;
+
+ TT_Load_Context( exec, face, size );
+
+ exec->callTop = 0;
+ exec->top = 0;
+
+ exec->period = 64;
+ exec->phase = 0;
+ exec->threshold = 0;
+
+ exec->instruction_trap = FALSE;
+ exec->F_dot_P = 0x10000L;
+
+ {
+ FT_Size_Metrics* metrics = &exec->metrics;
+ TT_Size_Metrics* tt_metrics = &exec->tt_metrics;
+
+
+ metrics->x_ppem = 0;
+ metrics->y_ppem = 0;
+ metrics->x_scale = 0;
+ metrics->y_scale = 0;
+
+ tt_metrics->ppem = 0;
+ tt_metrics->scale = 0;
+ tt_metrics->ratio = 0x10000L;
+ }
+
+ /* allow font program execution */
+ TT_Set_CodeRange( exec,
+ tt_coderange_font,
+ face->font_program,
+ face->font_program_size );
+
+ /* disable CVT and glyph programs coderange */
+ TT_Clear_CodeRange( exec, tt_coderange_cvt );
+ TT_Clear_CodeRange( exec, tt_coderange_glyph );
+
+ if ( face->font_program_size > 0 )
+ {
+ error = TT_Goto_CodeRange( exec, tt_coderange_font, 0 );
+
+ if ( !error )
+ error = face->interpreter( exec );
+ }
+ else
+ error = TT_Err_Ok;
+
+ if ( !error )
+ TT_Save_Context( exec, size );
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_size_run_prep */
+ /* */
+ /* <Description> */
+ /* Run the control value program. */
+ /* */
+ /* <Input> */
+ /* size :: A handle to the size object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_run_prep( TT_Size size )
+ {
+ TT_Face face = (TT_Face)size->root.face;
+ TT_ExecContext exec;
+ FT_Error error;
+
+
+ /* debugging instances have their own context */
+ if ( size->debug )
+ exec = size->context;
+ else
+ exec = ( (TT_Driver)FT_FACE_DRIVER( face ) )->context;
+
+ if ( !exec )
+ return TT_Err_Could_Not_Find_Context;
+
+ TT_Load_Context( exec, face, size );
+
+ exec->callTop = 0;
+ exec->top = 0;
+
+ exec->instruction_trap = FALSE;
+
+ TT_Set_CodeRange( exec,
+ tt_coderange_cvt,
+ face->cvt_program,
+ face->cvt_program_size );
+
+ TT_Clear_CodeRange( exec, tt_coderange_glyph );
+
+ if ( face->cvt_program_size > 0 )
+ {
+ error = TT_Goto_CodeRange( exec, tt_coderange_cvt, 0 );
+
+ if ( !error && !size->debug )
+ error = face->interpreter( exec );
+ }
+ else
+ error = TT_Err_Ok;
+
+ /* save as default graphics state */
+ size->GS = exec->GS;
+
+ TT_Save_Context( exec, size );
+
+ return error;
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ static void
+ tt_size_done_bytecode( FT_Size ftsize )
+ {
+ TT_Size size = (TT_Size)ftsize;
+ TT_Face face = (TT_Face)ftsize->face;
+ FT_Memory memory = face->root.memory;
+
+
+ if ( size->debug )
+ {
+ /* the debug context must be deleted by the debugger itself */
+ size->context = NULL;
+ size->debug = FALSE;
+ }
+
+ FT_FREE( size->cvt );
+ size->cvt_size = 0;
+
+ /* free storage area */
+ FT_FREE( size->storage );
+ size->storage_size = 0;
+
+ /* twilight zone */
+ tt_glyphzone_done( &size->twilight );
+
+ FT_FREE( size->function_defs );
+ FT_FREE( size->instruction_defs );
+
+ size->num_function_defs = 0;
+ size->max_function_defs = 0;
+ size->num_instruction_defs = 0;
+ size->max_instruction_defs = 0;
+
+ size->max_func = 0;
+ size->max_ins = 0;
+
+ size->bytecode_ready = 0;
+ size->cvt_ready = 0;
+ }
+
+
+ /* Initialize bytecode-related fields in the size object. */
+ /* We do this only if bytecode interpretation is really needed. */
+ static FT_Error
+ tt_size_init_bytecode( FT_Size ftsize )
+ {
+ FT_Error error;
+ TT_Size size = (TT_Size)ftsize;
+ TT_Face face = (TT_Face)ftsize->face;
+ FT_Memory memory = face->root.memory;
+ FT_Int i;
+
+ FT_UShort n_twilight;
+ TT_MaxProfile* maxp = &face->max_profile;
+
+
+ size->bytecode_ready = 1;
+ size->cvt_ready = 0;
+
+ size->max_function_defs = maxp->maxFunctionDefs;
+ size->max_instruction_defs = maxp->maxInstructionDefs;
+
+ size->num_function_defs = 0;
+ size->num_instruction_defs = 0;
+
+ size->max_func = 0;
+ size->max_ins = 0;
+
+ size->cvt_size = face->cvt_size;
+ size->storage_size = maxp->maxStorage;
+
+ /* Set default metrics */
+ {
+ FT_Size_Metrics* metrics = &size->metrics;
+ TT_Size_Metrics* metrics2 = &size->ttmetrics;
+
+ metrics->x_ppem = 0;
+ metrics->y_ppem = 0;
+
+ metrics2->rotated = FALSE;
+ metrics2->stretched = FALSE;
+
+ /* set default compensation (all 0) */
+ for ( i = 0; i < 4; i++ )
+ metrics2->compensations[i] = 0;
+ }
+
+ /* allocate function defs, instruction defs, cvt, and storage area */
+ if ( FT_NEW_ARRAY( size->function_defs, size->max_function_defs ) ||
+ FT_NEW_ARRAY( size->instruction_defs, size->max_instruction_defs ) ||
+ FT_NEW_ARRAY( size->cvt, size->cvt_size ) ||
+ FT_NEW_ARRAY( size->storage, size->storage_size ) )
+ goto Exit;
+
+ /* reserve twilight zone */
+ n_twilight = maxp->maxTwilightPoints;
+
+ /* there are 4 phantom points (do we need this?) */
+ n_twilight += 4;
+
+ error = tt_glyphzone_new( memory, n_twilight, 0, &size->twilight );
+ if ( error )
+ goto Exit;
+
+ size->twilight.n_points = n_twilight;
+
+ size->GS = tt_default_graphics_state;
+
+ /* set `face->interpreter' according to the debug hook present */
+ {
+ FT_Library library = face->root.driver->root.library;
+
+
+ face->interpreter = (TT_Interpreter)
+ library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE];
+ if ( !face->interpreter )
+ face->interpreter = (TT_Interpreter)TT_RunIns;
+ }
+
+ /* Fine, now run the font program! */
+ error = tt_size_run_fpgm( size );
+
+ Exit:
+ if ( error )
+ tt_size_done_bytecode( ftsize );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_ready_bytecode( TT_Size size )
+ {
+ FT_Error error = TT_Err_Ok;
+
+
+ if ( !size->bytecode_ready )
+ {
+ error = tt_size_init_bytecode( (FT_Size)size );
+ if ( error )
+ goto Exit;
+ }
+
+ /* rescale CVT when needed */
+ if ( !size->cvt_ready )
+ {
+ FT_UInt i;
+ TT_Face face = (TT_Face) size->root.face;
+
+
+ /* Scale the cvt values to the new ppem. */
+ /* We use by default the y ppem to scale the CVT. */
+ for ( i = 0; i < size->cvt_size; i++ )
+ size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale );
+
+ /* all twilight points are originally zero */
+ for ( i = 0; i < (FT_UInt)size->twilight.n_points; i++ )
+ {
+ size->twilight.org[i].x = 0;
+ size->twilight.org[i].y = 0;
+ size->twilight.cur[i].x = 0;
+ size->twilight.cur[i].y = 0;
+ }
+
+ /* clear storage area */
+ for ( i = 0; i < (FT_UInt)size->storage_size; i++ )
+ size->storage[i] = 0;
+
+ size->GS = tt_default_graphics_state;
+
+ error = tt_size_run_prep( size );
+ if ( !error )
+ size->cvt_ready = 1;
+ }
+ Exit:
+ return error;
+ }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_size_init */
+ /* */
+ /* <Description> */
+ /* Initialize a new TrueType size object. */
+ /* */
+ /* <InOut> */
+ /* size :: A handle to the size object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_init( FT_Size ttsize ) /* TT_Size */
+ {
+ TT_Size size = (TT_Size)ttsize;
+ FT_Error error = TT_Err_Ok;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ size->bytecode_ready = 0;
+ size->cvt_ready = 0;
+#endif
+
+ size->ttmetrics.valid = FALSE;
+ size->strike_index = 0xFFFFFFFFUL;
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_size_done */
+ /* */
+ /* <Description> */
+ /* The TrueType size object finalizer. */
+ /* */
+ /* <Input> */
+ /* size :: A handle to the target size object. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_size_done( FT_Size ttsize ) /* TT_Size */
+ {
+ TT_Size size = (TT_Size)ttsize;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ if ( size->bytecode_ready )
+ tt_size_done_bytecode( ttsize );
+#endif
+
+ size->ttmetrics.valid = FALSE;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_size_reset */
+ /* */
+ /* <Description> */
+ /* Reset a TrueType size when resolutions and character dimensions */
+ /* have been changed. */
+ /* */
+ /* <Input> */
+ /* size :: A handle to the target size object. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_size_reset( TT_Size size )
+ {
+ TT_Face face;
+ FT_Error error = TT_Err_Ok;
+ FT_Size_Metrics* metrics;
+
+
+ size->ttmetrics.valid = FALSE;
+
+ face = (TT_Face)size->root.face;
+
+ metrics = &size->metrics;
+
+ /* copy the result from base layer */
+ *metrics = size->root.metrics;
+
+ if ( metrics->x_ppem < 1 || metrics->y_ppem < 1 )
+ return TT_Err_Invalid_PPem;
+
+ /* This bit flag, if set, indicates that the ppems must be */
+ /* rounded to integers. Nearly all TrueType fonts have this bit */
+ /* set, as hinting won't work really well otherwise. */
+ /* */
+ if ( face->header.Flags & 8 )
+ {
+ metrics->x_scale = FT_DivFix( metrics->x_ppem << 6,
+ face->root.units_per_EM );
+ metrics->y_scale = FT_DivFix( metrics->y_ppem << 6,
+ face->root.units_per_EM );
+
+ metrics->ascender =
+ FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) );
+ metrics->descender =
+ FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) );
+ metrics->height =
+ FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) );
+ metrics->max_advance =
+ FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width,
+ metrics->x_scale ) );
+ }
+
+ /* compute new transformation */
+ if ( metrics->x_ppem >= metrics->y_ppem )
+ {
+ size->ttmetrics.scale = metrics->x_scale;
+ size->ttmetrics.ppem = metrics->x_ppem;
+ size->ttmetrics.x_ratio = 0x10000L;
+ size->ttmetrics.y_ratio = FT_MulDiv( metrics->y_ppem,
+ 0x10000L,
+ metrics->x_ppem );
+ }
+ else
+ {
+ size->ttmetrics.scale = metrics->y_scale;
+ size->ttmetrics.ppem = metrics->y_ppem;
+ size->ttmetrics.x_ratio = FT_MulDiv( metrics->x_ppem,
+ 0x10000L,
+ metrics->y_ppem );
+ size->ttmetrics.y_ratio = 0x10000L;
+ }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ size->cvt_ready = 0;
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+ if ( !error )
+ size->ttmetrics.valid = TRUE;
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_driver_init */
+ /* */
+ /* <Description> */
+ /* Initialize a given TrueType driver object. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target driver object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_driver_init( FT_Module ttdriver ) /* TT_Driver */
+ {
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ TT_Driver driver = (TT_Driver)ttdriver;
+
+
+ if ( !TT_New_Context( driver ) )
+ return TT_Err_Could_Not_Find_Context;
+
+#else
+
+ FT_UNUSED( ttdriver );
+
+#endif
+
+ return TT_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_driver_done */
+ /* */
+ /* <Description> */
+ /* Finalize a given TrueType driver. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target TrueType driver. */
+ /* */
+ FT_LOCAL_DEF( void )
+ tt_driver_done( FT_Module ttdriver ) /* TT_Driver */
+ {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ TT_Driver driver = (TT_Driver)ttdriver;
+
+
+ /* destroy the execution context */
+ if ( driver->context )
+ {
+ TT_Done_Context( driver->context );
+ driver->context = NULL;
+ }
+#else
+ FT_UNUSED( ttdriver );
+#endif
+
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_slot_init */
+ /* */
+ /* <Description> */
+ /* Initialize a new slot object. */
+ /* */
+ /* <InOut> */
+ /* slot :: A handle to the slot object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_slot_init( FT_GlyphSlot slot )
+ {
+ return FT_GlyphLoader_CreateExtra( slot->internal->loader );
+ }
+
+
+/* END */
diff --git a/src/truetype/ttobjs.h b/src/truetype/ttobjs.h
new file mode 100644
index 0000000..6971013
--- /dev/null
+++ b/src/truetype/ttobjs.h
@@ -0,0 +1,459 @@
+/***************************************************************************/
+/* */
+/* ttobjs.h */
+/* */
+/* Objects manager (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTOBJS_H__
+#define __TTOBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* TT_Driver */
+ /* */
+ /* <Description> */
+ /* A handle to a TrueType driver object. */
+ /* */
+ typedef struct TT_DriverRec_* TT_Driver;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* TT_Instance */
+ /* */
+ /* <Description> */
+ /* A handle to a TrueType size object. */
+ /* */
+ typedef struct TT_SizeRec_* TT_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* TT_GlyphSlot */
+ /* */
+ /* <Description> */
+ /* A handle to a TrueType glyph slot object. */
+ /* */
+ /* <Note> */
+ /* This is a direct typedef of FT_GlyphSlot, as there is nothing */
+ /* specific about the TrueType glyph slot. */
+ /* */
+ typedef FT_GlyphSlot TT_GlyphSlot;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* TT_GraphicsState */
+ /* */
+ /* <Description> */
+ /* The TrueType graphics state used during bytecode interpretation. */
+ /* */
+ typedef struct TT_GraphicsState_
+ {
+ FT_UShort rp0;
+ FT_UShort rp1;
+ FT_UShort rp2;
+
+ FT_UnitVector dualVector;
+ FT_UnitVector projVector;
+ FT_UnitVector freeVector;
+
+#ifdef TT_CONFIG_OPTION_UNPATENTED_HINTING
+ FT_Bool both_x_axis;
+#endif
+
+ FT_Long loop;
+ FT_F26Dot6 minimum_distance;
+ FT_Int round_state;
+
+ FT_Bool auto_flip;
+ FT_F26Dot6 control_value_cutin;
+ FT_F26Dot6 single_width_cutin;
+ FT_F26Dot6 single_width_value;
+ FT_Short delta_base;
+ FT_Short delta_shift;
+
+ FT_Byte instruct_control;
+ FT_Bool scan_control;
+ FT_Int scan_type;
+
+ FT_UShort gep0;
+ FT_UShort gep1;
+ FT_UShort gep2;
+
+ } TT_GraphicsState;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_LOCAL( void )
+ tt_glyphzone_done( TT_GlyphZone zone );
+
+ FT_LOCAL( FT_Error )
+ tt_glyphzone_new( FT_Memory memory,
+ FT_UShort maxPoints,
+ FT_Short maxContours,
+ TT_GlyphZone zone );
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+
+ /*************************************************************************/
+ /* */
+ /* EXECUTION SUBTABLES */
+ /* */
+ /* These sub-tables relate to instruction execution. */
+ /* */
+ /*************************************************************************/
+
+
+#define TT_MAX_CODE_RANGES 3
+
+
+ /*************************************************************************/
+ /* */
+ /* There can only be 3 active code ranges at once: */
+ /* - the Font Program */
+ /* - the CVT Program */
+ /* - a glyph's instructions set */
+ /* */
+ typedef enum TT_CodeRange_Tag_
+ {
+ tt_coderange_none = 0,
+ tt_coderange_font,
+ tt_coderange_cvt,
+ tt_coderange_glyph
+
+ } TT_CodeRange_Tag;
+
+
+ typedef struct TT_CodeRange_
+ {
+ FT_Byte* base;
+ FT_ULong size;
+
+ } TT_CodeRange;
+
+ typedef TT_CodeRange TT_CodeRangeTable[TT_MAX_CODE_RANGES];
+
+
+ /*************************************************************************/
+ /* */
+ /* Defines a function/instruction definition record. */
+ /* */
+ typedef struct TT_DefRecord_
+ {
+ FT_Int range; /* in which code range is it located? */
+ FT_Long start; /* where does it start? */
+ FT_UInt opc; /* function #, or instruction code */
+ FT_Bool active; /* is it active? */
+
+ } TT_DefRecord, *TT_DefArray;
+
+
+ /*************************************************************************/
+ /* */
+ /* Subglyph transformation record. */
+ /* */
+ typedef struct TT_Transform_
+ {
+ FT_Fixed xx, xy; /* transformation matrix coefficients */
+ FT_Fixed yx, yy;
+ FT_F26Dot6 ox, oy; /* offsets */
+
+ } TT_Transform;
+
+
+ /*************************************************************************/
+ /* */
+ /* Subglyph loading record. Used to load composite components. */
+ /* */
+ typedef struct TT_SubglyphRec_
+ {
+ FT_Long index; /* subglyph index; initialized with -1 */
+ FT_Bool is_scaled; /* is the subglyph scaled? */
+ FT_Bool is_hinted; /* should it be hinted? */
+ FT_Bool preserve_pps; /* preserve phantom points? */
+
+ FT_Long file_offset;
+
+ FT_BBox bbox;
+ FT_Pos left_bearing;
+ FT_Pos advance;
+
+ TT_GlyphZoneRec zone;
+
+ FT_Long arg1; /* first argument */
+ FT_Long arg2; /* second argument */
+
+ FT_UShort element_flag; /* current load element flag */
+
+ TT_Transform transform; /* transformation matrix */
+
+ FT_Vector pp1, pp2; /* phantom points (horizontal) */
+ FT_Vector pp3, pp4; /* phantom points (vertical) */
+
+ } TT_SubGlyphRec, *TT_SubGlyph_Stack;
+
+
+ /*************************************************************************/
+ /* */
+ /* A note regarding non-squared pixels: */
+ /* */
+ /* (This text will probably go into some docs at some time; for now, it */
+ /* is kept here to explain some definitions in the TIns_Metrics */
+ /* record). */
+ /* */
+ /* The CVT is a one-dimensional array containing values that control */
+ /* certain important characteristics in a font, like the height of all */
+ /* capitals, all lowercase letter, default spacing or stem width/height. */
+ /* */
+ /* These values are found in FUnits in the font file, and must be scaled */
+ /* to pixel coordinates before being used by the CVT and glyph programs. */
+ /* Unfortunately, when using distinct x and y resolutions (or distinct x */
+ /* and y pointsizes), there are two possible scalings. */
+ /* */
+ /* A first try was to implement a `lazy' scheme where all values were */
+ /* scaled when first used. However, while some values are always used */
+ /* in the same direction, some others are used under many different */
+ /* circumstances and orientations. */
+ /* */
+ /* I have found a simpler way to do the same, and it even seems to work */
+ /* in most of the cases: */
+ /* */
+ /* - All CVT values are scaled to the maximum ppem size. */
+ /* */
+ /* - When performing a read or write in the CVT, a ratio factor is used */
+ /* to perform adequate scaling. Example: */
+ /* */
+ /* x_ppem = 14 */
+ /* y_ppem = 10 */
+ /* */
+ /* We choose ppem = x_ppem = 14 as the CVT scaling size. All cvt */
+ /* entries are scaled to it. */
+ /* */
+ /* x_ratio = 1.0 */
+ /* y_ratio = y_ppem/ppem (< 1.0) */
+ /* */
+ /* We compute the current ratio like: */
+ /* */
+ /* - If projVector is horizontal, */
+ /* ratio = x_ratio = 1.0 */
+ /* */
+ /* - if projVector is vertical, */
+ /* ratio = y_ratio */
+ /* */
+ /* - else, */
+ /* ratio = sqrt( (proj.x * x_ratio) ^ 2 + (proj.y * y_ratio) ^ 2 ) */
+ /* */
+ /* Reading a cvt value returns */
+ /* ratio * cvt[index] */
+ /* */
+ /* Writing a cvt value in pixels: */
+ /* cvt[index] / ratio */
+ /* */
+ /* The current ppem is simply */
+ /* ratio * ppem */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* Metrics used by the TrueType size and context objects. */
+ /* */
+ typedef struct TT_Size_Metrics_
+ {
+ /* for non-square pixels */
+ FT_Long x_ratio;
+ FT_Long y_ratio;
+
+ FT_UShort ppem; /* maximum ppem size */
+ FT_Long ratio; /* current ratio */
+ FT_Fixed scale;
+
+ FT_F26Dot6 compensations[4]; /* device-specific compensations */
+
+ FT_Bool valid;
+
+ FT_Bool rotated; /* `is the glyph rotated?'-flag */
+ FT_Bool stretched; /* `is the glyph stretched?'-flag */
+
+ } TT_Size_Metrics;
+
+
+ /*************************************************************************/
+ /* */
+ /* TrueType size class. */
+ /* */
+ typedef struct TT_SizeRec_
+ {
+ FT_SizeRec root;
+
+ /* we have our own copy of metrics so that we can modify */
+ /* it without affecting auto-hinting (when used) */
+ FT_Size_Metrics metrics;
+
+ TT_Size_Metrics ttmetrics;
+
+ FT_ULong strike_index; /* 0xFFFFFFFF to indicate invalid */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_UInt num_function_defs; /* number of function definitions */
+ FT_UInt max_function_defs;
+ TT_DefArray function_defs; /* table of function definitions */
+
+ FT_UInt num_instruction_defs; /* number of ins. definitions */
+ FT_UInt max_instruction_defs;
+ TT_DefArray instruction_defs; /* table of ins. definitions */
+
+ FT_UInt max_func;
+ FT_UInt max_ins;
+
+ TT_CodeRangeTable codeRangeTable;
+
+ TT_GraphicsState GS;
+
+ FT_ULong cvt_size; /* the scaled control value table */
+ FT_Long* cvt;
+
+ FT_UShort storage_size; /* The storage area is now part of */
+ FT_Long* storage; /* the instance */
+
+ TT_GlyphZoneRec twilight; /* The instance's twilight zone */
+
+ /* debugging variables */
+
+ /* When using the debugger, we must keep the */
+ /* execution context tied to the instance */
+ /* object rather than asking it on demand. */
+
+ FT_Bool debug;
+ TT_ExecContext context;
+
+ FT_Bool bytecode_ready;
+ FT_Bool cvt_ready;
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+ } TT_SizeRec;
+
+
+ /*************************************************************************/
+ /* */
+ /* TrueType driver class. */
+ /* */
+ typedef struct TT_DriverRec_
+ {
+ FT_DriverRec root;
+ TT_ExecContext context; /* execution context */
+ TT_GlyphZoneRec zone; /* glyph loader points zone */
+
+ void* extension_component;
+
+ } TT_DriverRec;
+
+
+ /* Note: All of the functions below (except tt_size_reset()) are used */
+ /* as function pointers in a FT_Driver_ClassRec. Therefore their */
+ /* parameters are of types FT_Face, FT_Size, etc., rather than TT_Face, */
+ /* TT_Size, etc., so that the compiler can confirm that the types and */
+ /* number of parameters are correct. In all cases the FT_xxx types are */
+ /* cast to their TT_xxx counterparts inside the functions since FreeType */
+ /* will always use the TT driver to create them. */
+
+
+ /*************************************************************************/
+ /* */
+ /* Face functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_face_init( FT_Stream stream,
+ FT_Face ttface, /* TT_Face */
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ tt_face_done( FT_Face ttface ); /* TT_Face */
+
+
+ /*************************************************************************/
+ /* */
+ /* Size functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_size_init( FT_Size ttsize ); /* TT_Size */
+
+ FT_LOCAL( void )
+ tt_size_done( FT_Size ttsize ); /* TT_Size */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_LOCAL( FT_Error )
+ tt_size_run_fpgm( TT_Size size );
+
+ FT_LOCAL( FT_Error )
+ tt_size_run_prep( TT_Size size );
+
+ FT_LOCAL( FT_Error )
+ tt_size_ready_bytecode( TT_Size size );
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+ FT_LOCAL( FT_Error )
+ tt_size_reset( TT_Size size );
+
+
+ /*************************************************************************/
+ /* */
+ /* Driver functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_driver_init( FT_Module ttdriver ); /* TT_Driver */
+
+ FT_LOCAL( void )
+ tt_driver_done( FT_Module ttdriver ); /* TT_Driver */
+
+
+ /*************************************************************************/
+ /* */
+ /* Slot functions */
+ /* */
+ FT_LOCAL( FT_Error )
+ tt_slot_init( FT_GlyphSlot slot );
+
+
+FT_END_HEADER
+
+#endif /* __TTOBJS_H__ */
+
+
+/* END */
diff --git a/src/truetype/ttpload.c b/src/truetype/ttpload.c
new file mode 100644
index 0000000..9d3381b
--- /dev/null
+++ b/src/truetype/ttpload.c
@@ -0,0 +1,523 @@
+/***************************************************************************/
+/* */
+/* ttpload.c */
+/* */
+/* TrueType-specific tables loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_ttpload
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_loca */
+ /* */
+ /* <Description> */
+ /* Load the locations table. */
+ /* */
+ /* <InOut> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* <Input> */
+ /* stream :: The input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_loca( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_ULong table_len;
+
+
+ /* we need the size of the `glyf' table for malformed `loca' tables */
+ error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len );
+ if ( error )
+ goto Exit;
+
+ FT_TRACE2(( "Locations " ));
+ error = face->goto_table( face, TTAG_loca, stream, &table_len );
+ if ( error )
+ {
+ error = TT_Err_Locations_Missing;
+ goto Exit;
+ }
+
+ if ( face->header.Index_To_Loc_Format != 0 )
+ {
+ if ( table_len >= 0x40000L )
+ {
+ FT_TRACE2(( "table too large!\n" ));
+ error = TT_Err_Invalid_Table;
+ goto Exit;
+ }
+ face->num_locations = (FT_UInt)( table_len >> 2 );
+ }
+ else
+ {
+ if ( table_len >= 0x20000L )
+ {
+ FT_TRACE2(( "table too large!\n" ));
+ error = TT_Err_Invalid_Table;
+ goto Exit;
+ }
+ face->num_locations = (FT_UInt)( table_len >> 1 );
+ }
+
+ /*
+ * Extract the frame. We don't need to decompress it since
+ * we are able to parse it directly.
+ */
+ if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) )
+ goto Exit;
+
+ FT_TRACE2(( "loaded\n" ));
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_ULong )
+ tt_face_get_location( TT_Face face,
+ FT_UInt gindex,
+ FT_UInt *asize )
+ {
+ FT_ULong pos1, pos2;
+ FT_Byte* p;
+ FT_Byte* p_limit;
+
+
+ pos1 = pos2 = 0;
+
+ if ( gindex < face->num_locations )
+ {
+ if ( face->header.Index_To_Loc_Format != 0 )
+ {
+ p = face->glyph_locations + gindex * 4;
+ p_limit = face->glyph_locations + face->num_locations * 4;
+
+ pos1 = FT_NEXT_ULONG( p );
+ pos2 = pos1;
+
+ if ( p + 4 <= p_limit )
+ pos2 = FT_NEXT_ULONG( p );
+ }
+ else
+ {
+ p = face->glyph_locations + gindex * 2;
+ p_limit = face->glyph_locations + face->num_locations * 2;
+
+ pos1 = FT_NEXT_USHORT( p );
+ pos2 = pos1;
+
+ if ( p + 2 <= p_limit )
+ pos2 = FT_NEXT_USHORT( p );
+
+ pos1 <<= 1;
+ pos2 <<= 1;
+ }
+ }
+
+ /* It isn't mentioned explicitly that the `loca' table must be */
+ /* ordered, but implicitly it refers to the length of an entry */
+ /* as the difference between the current and the next position. */
+ /* Anyway, there do exist (malformed) fonts which don't obey */
+ /* this rule, so we are only able to provide an upper bound for */
+ /* the size. */
+ if ( pos2 >= pos1 )
+ *asize = (FT_UInt)( pos2 - pos1 );
+ else
+ *asize = (FT_UInt)( face->glyf_len - pos1 );
+
+ return pos1;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_done_loca( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+
+
+ FT_FRAME_RELEASE( face->glyph_locations );
+ face->num_locations = 0;
+ }
+
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_cvt */
+ /* */
+ /* <Description> */
+ /* Load the control value table into a face object. */
+ /* */
+ /* <InOut> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* <Input> */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_cvt( TT_Face face,
+ FT_Stream stream )
+ {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_ULong table_len;
+
+
+ FT_TRACE2(( "CVT " ));
+
+ error = face->goto_table( face, TTAG_cvt, stream, &table_len );
+ if ( error )
+ {
+ FT_TRACE2(( "is missing!\n" ));
+
+ face->cvt_size = 0;
+ face->cvt = NULL;
+ error = TT_Err_Ok;
+
+ goto Exit;
+ }
+
+ face->cvt_size = table_len / 2;
+
+ if ( FT_NEW_ARRAY( face->cvt, face->cvt_size ) )
+ goto Exit;
+
+ if ( FT_FRAME_ENTER( face->cvt_size * 2L ) )
+ goto Exit;
+
+ {
+ FT_Short* cur = face->cvt;
+ FT_Short* limit = cur + face->cvt_size;
+
+
+ for ( ; cur < limit; cur++ )
+ *cur = FT_GET_SHORT();
+ }
+
+ FT_FRAME_EXIT();
+ FT_TRACE2(( "loaded\n" ));
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+ if ( face->doblend )
+ error = tt_face_vary_cvt( face, stream );
+#endif
+
+ Exit:
+ return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+
+ return TT_Err_Ok;
+
+#endif
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_fpgm */
+ /* */
+ /* <Description> */
+ /* Load the font program. */
+ /* */
+ /* <InOut> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* <Input> */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_fpgm( TT_Face face,
+ FT_Stream stream )
+ {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_Error error;
+ FT_ULong table_len;
+
+
+ FT_TRACE2(( "Font program " ));
+
+ /* The font program is optional */
+ error = face->goto_table( face, TTAG_fpgm, stream, &table_len );
+ if ( error )
+ {
+ face->font_program = NULL;
+ face->font_program_size = 0;
+ error = TT_Err_Ok;
+
+ FT_TRACE2(( "is missing!\n" ));
+ }
+ else
+ {
+ face->font_program_size = table_len;
+ if ( FT_FRAME_EXTRACT( table_len, face->font_program ) )
+ goto Exit;
+
+ FT_TRACE2(( "loaded, %12d bytes\n", face->font_program_size ));
+ }
+
+ Exit:
+ return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+
+ return TT_Err_Ok;
+
+#endif
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_prep */
+ /* */
+ /* <Description> */
+ /* Load the cvt program. */
+ /* */
+ /* <InOut> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* <Input> */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_prep( TT_Face face,
+ FT_Stream stream )
+ {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+ FT_Error error;
+ FT_ULong table_len;
+
+
+ FT_TRACE2(( "Prep program " ));
+
+ error = face->goto_table( face, TTAG_prep, stream, &table_len );
+ if ( error )
+ {
+ face->cvt_program = NULL;
+ face->cvt_program_size = 0;
+ error = TT_Err_Ok;
+
+ FT_TRACE2(( "is missing!\n" ));
+ }
+ else
+ {
+ face->cvt_program_size = table_len;
+ if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) )
+ goto Exit;
+
+ FT_TRACE2(( "loaded, %12d bytes\n", face->cvt_program_size ));
+ }
+
+ Exit:
+ return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+ FT_UNUSED( face );
+ FT_UNUSED( stream );
+
+ return TT_Err_Ok;
+
+#endif
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* tt_face_load_hdmx */
+ /* */
+ /* <Description> */
+ /* Load the `hdmx' table into the face object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target face object. */
+ /* */
+ /* stream :: A handle to the input stream. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+
+ FT_LOCAL_DEF( FT_Error )
+ tt_face_load_hdmx( TT_Face face,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UInt version, nn, num_records;
+ FT_ULong table_size, record_size;
+ FT_Byte* p;
+ FT_Byte* limit;
+
+
+ /* this table is optional */
+ error = face->goto_table( face, TTAG_hdmx, stream, &table_size );
+ if ( error || table_size < 8 )
+ return TT_Err_Ok;
+
+ if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) )
+ goto Exit;
+
+ p = face->hdmx_table;
+ limit = p + table_size;
+
+ version = FT_NEXT_USHORT( p );
+ num_records = FT_NEXT_USHORT( p );
+ record_size = FT_NEXT_ULONG( p );
+
+ /* The maximum number of bytes in an hdmx device record is the */
+ /* maximum number of glyphs + 2; this is 0xFFFF + 2; this is */
+ /* the reason why `record_size' is a long (which we read as */
+ /* unsigned long for convenience). In practice, two bytes */
+ /* sufficient to hold the size value. */
+ /* */
+ /* There are at least two fonts, HANNOM-A and HANNOM-B version */
+ /* 2.0 (2005), which get this wrong: The upper two bytes of */
+ /* the size value are set to 0xFF instead of 0x00. We catch */
+ /* and fix this. */
+
+ if ( record_size >= 0xFFFF0000UL )
+ record_size &= 0xFFFFU;
+
+ /* The limit for `num_records' is a heuristic value. */
+
+ if ( version != 0 || num_records > 255 || record_size > 0x10001L )
+ {
+ error = TT_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) )
+ goto Fail;
+
+ for ( nn = 0; nn < num_records; nn++ )
+ {
+ if ( p + record_size > limit )
+ break;
+
+ face->hdmx_record_sizes[nn] = p[0];
+ p += record_size;
+ }
+
+ face->hdmx_record_count = nn;
+ face->hdmx_table_size = table_size;
+ face->hdmx_record_size = record_size;
+
+ Exit:
+ return error;
+
+ Fail:
+ FT_FRAME_RELEASE( face->hdmx_table );
+ face->hdmx_table_size = 0;
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ tt_face_free_hdmx( TT_Face face )
+ {
+ FT_Stream stream = face->root.stream;
+ FT_Memory memory = stream->memory;
+
+
+ FT_FREE( face->hdmx_record_sizes );
+ FT_FRAME_RELEASE( face->hdmx_table );
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Return the advance width table for a given pixel size if it is found */
+ /* in the font's `hdmx' table (if any). */
+ /* */
+ FT_LOCAL_DEF( FT_Byte* )
+ tt_face_get_device_metrics( TT_Face face,
+ FT_UInt ppem,
+ FT_UInt gindex )
+ {
+ FT_UInt nn;
+ FT_Byte* result = NULL;
+ FT_ULong record_size = face->hdmx_record_size;
+ FT_Byte* record = face->hdmx_table + 8;
+
+
+ for ( nn = 0; nn < face->hdmx_record_count; nn++ )
+ if ( face->hdmx_record_sizes[nn] == ppem )
+ {
+ gindex += 2;
+ if ( gindex < record_size )
+ result = record + nn * record_size + gindex;
+ break;
+ }
+
+ return result;
+ }
+
+
+/* END */
diff --git a/src/truetype/ttpload.h b/src/truetype/ttpload.h
new file mode 100644
index 0000000..f61ac07
--- /dev/null
+++ b/src/truetype/ttpload.h
@@ -0,0 +1,75 @@
+/***************************************************************************/
+/* */
+/* ttpload.h */
+/* */
+/* TrueType-specific tables loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __TTPLOAD_H__
+#define __TTPLOAD_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_loca( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( FT_ULong )
+ tt_face_get_location( TT_Face face,
+ FT_UInt gindex,
+ FT_UInt *asize );
+
+ FT_LOCAL( void )
+ tt_face_done_loca( TT_Face face );
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_cvt( TT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_fpgm( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_prep( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( FT_Error )
+ tt_face_load_hdmx( TT_Face face,
+ FT_Stream stream );
+
+
+ FT_LOCAL( void )
+ tt_face_free_hdmx( TT_Face face );
+
+
+ FT_LOCAL( FT_Byte* )
+ tt_face_get_device_metrics( TT_Face face,
+ FT_UInt ppem,
+ FT_UInt gindex );
+
+FT_END_HEADER
+
+#endif /* __TTPLOAD_H__ */
+
+
+/* END */
diff --git a/src/type1/Jamfile b/src/type1/Jamfile
new file mode 100644
index 0000000..8e366ba
--- /dev/null
+++ b/src/type1/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/type1 Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) type1 ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = t1afm t1driver t1objs t1load t1gload t1parse ;
+ }
+ else
+ {
+ _sources = type1 ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/type1 Jamfile
diff --git a/src/type1/module.mk b/src/type1/module.mk
new file mode 100644
index 0000000..baf98c0
--- /dev/null
+++ b/src/type1/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 Type1 module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += TYPE1_DRIVER
+
+define TYPE1_DRIVER
+$(OPEN_DRIVER)t1_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)type1 $(ECHO_DRIVER_DESC)Postscript font files with extension *.pfa or *.pfb$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/type1/rules.mk b/src/type1/rules.mk
new file mode 100644
index 0000000..15087b0
--- /dev/null
+++ b/src/type1/rules.mk
@@ -0,0 +1,73 @@
+#
+# FreeType 2 Type1 driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# Type1 driver directory
+#
+T1_DIR := $(SRC_DIR)/type1
+
+
+# compilation flags for the driver
+#
+T1_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(T1_DIR))
+
+
+# Type1 driver sources (i.e., C files)
+#
+T1_DRV_SRC := $(T1_DIR)/t1parse.c \
+ $(T1_DIR)/t1load.c \
+ $(T1_DIR)/t1driver.c \
+ $(T1_DIR)/t1afm.c \
+ $(T1_DIR)/t1gload.c \
+ $(T1_DIR)/t1objs.c
+
+# Type1 driver headers
+#
+T1_DRV_H := $(T1_DRV_SRC:%.c=%.h) \
+ $(T1_DIR)/t1tokens.h \
+ $(T1_DIR)/t1errors.h
+
+
+# Type1 driver object(s)
+#
+# T1_DRV_OBJ_M is used during `multi' builds
+# T1_DRV_OBJ_S is used during `single' builds
+#
+T1_DRV_OBJ_M := $(T1_DRV_SRC:$(T1_DIR)/%.c=$(OBJ_DIR)/%.$O)
+T1_DRV_OBJ_S := $(OBJ_DIR)/type1.$O
+
+# Type1 driver source file for single build
+#
+T1_DRV_SRC_S := $(T1_DIR)/type1.c
+
+
+# Type1 driver - single object
+#
+$(T1_DRV_OBJ_S): $(T1_DRV_SRC_S) $(T1_DRV_SRC) $(FREETYPE_H) $(T1_DRV_H)
+ $(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(T1_DRV_SRC_S))
+
+
+# Type1 driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(T1_DIR)/%.c $(FREETYPE_H) $(T1_DRV_H)
+ $(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(T1_DRV_OBJ_S)
+DRV_OBJS_M += $(T1_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/type1/t1afm.c b/src/type1/t1afm.c
new file mode 100644
index 0000000..b81a8df
--- /dev/null
+++ b/src/type1/t1afm.c
@@ -0,0 +1,385 @@
+/***************************************************************************/
+/* */
+/* t1afm.c */
+/* */
+/* AFM support for Type 1 fonts (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "t1afm.h"
+#include "t1errors.h"
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1afm
+
+
+ FT_LOCAL_DEF( void )
+ T1_Done_Metrics( FT_Memory memory,
+ AFM_FontInfo fi )
+ {
+ FT_FREE( fi->KernPairs );
+ fi->NumKernPair = 0;
+
+ FT_FREE( fi->TrackKerns );
+ fi->NumTrackKern = 0;
+
+ FT_FREE( fi );
+ }
+
+
+ /* read a glyph name and return the equivalent glyph index */
+ static FT_Int
+ t1_get_index( const char* name,
+ FT_UInt len,
+ void* user_data )
+ {
+ T1_Font type1 = (T1_Font)user_data;
+ FT_Int n;
+
+
+ for ( n = 0; n < type1->num_glyphs; n++ )
+ {
+ char* gname = (char*)type1->glyph_names[n];
+
+
+ if ( gname && gname[0] == name[0] &&
+ ft_strlen( gname ) == len &&
+ ft_strncmp( gname, name, len ) == 0 )
+ return n;
+ }
+
+ return 0;
+ }
+
+
+#undef KERN_INDEX
+#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) )
+
+
+ /* compare two kerning pairs */
+ FT_CALLBACK_DEF( int )
+ compare_kern_pairs( const void* a,
+ const void* b )
+ {
+ AFM_KernPair pair1 = (AFM_KernPair)a;
+ AFM_KernPair pair2 = (AFM_KernPair)b;
+
+ FT_ULong index1 = KERN_INDEX( pair1->index1, pair1->index2 );
+ FT_ULong index2 = KERN_INDEX( pair2->index1, pair2->index2 );
+
+
+ return (int)( index1 - index2 );
+ }
+
+
+ /* parse a PFM file -- for now, only read the kerning pairs */
+ static FT_Error
+ T1_Read_PFM( FT_Face t1_face,
+ FT_Stream stream,
+ AFM_FontInfo fi )
+ {
+ FT_Error error = T1_Err_Ok;
+ FT_Memory memory = stream->memory;
+ FT_Byte* start;
+ FT_Byte* limit;
+ FT_Byte* p;
+ AFM_KernPair kp;
+ FT_Int width_table_length;
+ FT_CharMap oldcharmap;
+ FT_CharMap charmap;
+ FT_Int n;
+
+
+ start = (FT_Byte*)stream->cursor;
+ limit = (FT_Byte*)stream->limit;
+ p = start;
+
+ /* Figure out how long the width table is. */
+ /* This info is a little-endian short at offset 99. */
+ p = start + 99;
+ if ( p + 2 > limit )
+ {
+ error = T1_Err_Unknown_File_Format;
+ goto Exit;
+ }
+ width_table_length = FT_PEEK_USHORT_LE( p );
+
+ p += 18 + width_table_length;
+ if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
+ /* extension table is probably optional */
+ goto Exit;
+
+ /* Kerning offset is 14 bytes from start of extensions table. */
+ p += 14;
+ p = start + FT_PEEK_ULONG_LE( p );
+
+ if ( p == start )
+ /* zero offset means no table */
+ goto Exit;
+
+ if ( p + 2 > limit )
+ {
+ error = T1_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ fi->NumKernPair = FT_PEEK_USHORT_LE( p );
+ p += 2;
+ if ( p + 4 * fi->NumKernPair > limit )
+ {
+ error = T1_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* Actually, kerning pairs are simply optional! */
+ if ( fi->NumKernPair == 0 )
+ goto Exit;
+
+ /* allocate the pairs */
+ if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
+ goto Exit;
+
+ /* now, read each kern pair */
+ kp = fi->KernPairs;
+ limit = p + 4 * fi->NumKernPair;
+
+ /* PFM kerning data are stored by encoding rather than glyph index, */
+ /* so find the PostScript charmap of this font and install it */
+ /* temporarily. If we find no PostScript charmap, then just use */
+ /* the default and hope it is the right one. */
+ oldcharmap = t1_face->charmap;
+ charmap = NULL;
+
+ for ( n = 0; n < t1_face->num_charmaps; n++ )
+ {
+ charmap = t1_face->charmaps[n];
+ /* check against PostScript pseudo platform */
+ if ( charmap->platform_id == 7 )
+ {
+ error = FT_Set_Charmap( t1_face, charmap );
+ if ( error )
+ goto Exit;
+ break;
+ }
+ }
+
+ /* Kerning info is stored as: */
+ /* */
+ /* encoding of first glyph (1 byte) */
+ /* encoding of second glyph (1 byte) */
+ /* offset (little-endian short) */
+ for ( ; p < limit ; p += 4 )
+ {
+ kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
+ kp->index2 = FT_Get_Char_Index( t1_face, p[1] );
+
+ kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);
+ kp->y = 0;
+
+ kp++;
+ }
+
+ if ( oldcharmap != NULL )
+ error = FT_Set_Charmap( t1_face, oldcharmap );
+ if ( error )
+ goto Exit;
+
+ /* now, sort the kern pairs according to their glyph indices */
+ ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
+ compare_kern_pairs );
+
+ Exit:
+ if ( error )
+ {
+ FT_FREE( fi->KernPairs );
+ fi->NumKernPair = 0;
+ }
+
+ return error;
+ }
+
+
+ /* parse a metrics file -- either AFM or PFM depending on what */
+ /* it turns out to be */
+ FT_LOCAL_DEF( FT_Error )
+ T1_Read_Metrics( FT_Face t1_face,
+ FT_Stream stream )
+ {
+ PSAux_Service psaux;
+ FT_Memory memory = stream->memory;
+ AFM_ParserRec parser;
+ AFM_FontInfo fi;
+ FT_Error error = T1_Err_Unknown_File_Format;
+ T1_Font t1_font = &( (T1_Face)t1_face )->type1;
+
+
+ if ( FT_NEW( fi ) ||
+ FT_FRAME_ENTER( stream->size ) )
+ goto Exit;
+
+ fi->FontBBox = t1_font->font_bbox;
+ fi->Ascender = t1_font->font_bbox.yMax;
+ fi->Descender = t1_font->font_bbox.yMin;
+
+ psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux;
+ if ( psaux && psaux->afm_parser_funcs )
+ {
+ error = psaux->afm_parser_funcs->init( &parser,
+ stream->memory,
+ stream->cursor,
+ stream->limit );
+
+ if ( !error )
+ {
+ parser.FontInfo = fi;
+ parser.get_index = t1_get_index;
+ parser.user_data = t1_font;
+
+ error = psaux->afm_parser_funcs->parse( &parser );
+ psaux->afm_parser_funcs->done( &parser );
+ }
+ }
+
+ if ( error == T1_Err_Unknown_File_Format )
+ {
+ FT_Byte* start = stream->cursor;
+
+
+ /* MS Windows allows versions up to 0x3FF without complaining */
+ if ( stream->size > 6 &&
+ start[1] < 4 &&
+ FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
+ error = T1_Read_PFM( t1_face, stream, fi );
+ }
+
+ if ( !error )
+ {
+ t1_font->font_bbox = fi->FontBBox;
+
+ t1_face->bbox.xMin = fi->FontBBox.xMin >> 16;
+ t1_face->bbox.yMin = fi->FontBBox.yMin >> 16;
+ t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFFU ) >> 16;
+ t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFFU ) >> 16;
+
+ t1_face->ascender = (FT_Short)( ( fi->Ascender + 0x8000U ) >> 16 );
+ t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000U ) >> 16 );
+
+ if ( fi->NumKernPair )
+ {
+ t1_face->face_flags |= FT_FACE_FLAG_KERNING;
+ ( (T1_Face)t1_face )->afm_data = fi;
+ fi = NULL;
+ }
+ }
+
+ FT_FRAME_EXIT();
+
+ Exit:
+ if ( fi != NULL )
+ T1_Done_Metrics( memory, fi );
+
+ return error;
+ }
+
+
+ /* find the kerning for a given glyph pair */
+ FT_LOCAL_DEF( void )
+ T1_Get_Kerning( AFM_FontInfo fi,
+ FT_UInt glyph1,
+ FT_UInt glyph2,
+ FT_Vector* kerning )
+ {
+ AFM_KernPair min, mid, max;
+ FT_ULong idx = KERN_INDEX( glyph1, glyph2 );
+
+
+ /* simple binary search */
+ min = fi->KernPairs;
+ max = min + fi->NumKernPair - 1;
+
+ while ( min <= max )
+ {
+ FT_ULong midi;
+
+
+ mid = min + ( max - min ) / 2;
+ midi = KERN_INDEX( mid->index1, mid->index2 );
+
+ if ( midi == idx )
+ {
+ kerning->x = mid->x;
+ kerning->y = mid->y;
+
+ return;
+ }
+
+ if ( midi < idx )
+ min = mid + 1;
+ else
+ max = mid - 1;
+ }
+
+ kerning->x = 0;
+ kerning->y = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Get_Track_Kerning( FT_Face face,
+ FT_Fixed ptsize,
+ FT_Int degree,
+ FT_Fixed* kerning )
+ {
+ AFM_FontInfo fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;
+ FT_Int i;
+
+
+ if ( !fi )
+ return T1_Err_Invalid_Argument;
+
+ for ( i = 0; i < fi->NumTrackKern; i++ )
+ {
+ AFM_TrackKern tk = fi->TrackKerns + i;
+
+
+ if ( tk->degree != degree )
+ continue;
+
+ if ( ptsize < tk->min_ptsize )
+ *kerning = tk->min_kern;
+ else if ( ptsize > tk->max_ptsize )
+ *kerning = tk->max_kern;
+ else
+ {
+ *kerning = FT_MulDiv( ptsize - tk->min_ptsize,
+ tk->max_kern - tk->min_kern,
+ tk->max_ptsize - tk->min_ptsize ) +
+ tk->min_kern;
+ }
+ }
+
+ return T1_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/type1/t1afm.h b/src/type1/t1afm.h
new file mode 100644
index 0000000..8eb1764
--- /dev/null
+++ b/src/type1/t1afm.h
@@ -0,0 +1,54 @@
+/***************************************************************************/
+/* */
+/* t1afm.h */
+/* */
+/* AFM support for Type 1 fonts (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1AFM_H__
+#define __T1AFM_H__
+
+#include <ft2build.h>
+#include "t1objs.h"
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ T1_Read_Metrics( FT_Face face,
+ FT_Stream stream );
+
+ FT_LOCAL( void )
+ T1_Done_Metrics( FT_Memory memory,
+ AFM_FontInfo fi );
+
+ FT_LOCAL( void )
+ T1_Get_Kerning( AFM_FontInfo fi,
+ FT_UInt glyph1,
+ FT_UInt glyph2,
+ FT_Vector* kerning );
+
+ FT_LOCAL( FT_Error )
+ T1_Get_Track_Kerning( FT_Face face,
+ FT_Fixed ptsize,
+ FT_Int degree,
+ FT_Fixed* kerning );
+
+FT_END_HEADER
+
+#endif /* __T1AFM_H__ */
+
+
+/* END */
diff --git a/src/type1/t1driver.c b/src/type1/t1driver.c
new file mode 100644
index 0000000..3ca21dc
--- /dev/null
+++ b/src/type1/t1driver.c
@@ -0,0 +1,313 @@
+/***************************************************************************/
+/* */
+/* t1driver.c */
+/* */
+/* Type 1 driver interface (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "t1driver.h"
+#include "t1gload.h"
+#include "t1load.h"
+
+#include "t1errors.h"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "t1afm.h"
+#endif
+
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+
+#include FT_SERVICE_MULTIPLE_MASTERS_H
+#include FT_SERVICE_GLYPH_DICT_H
+#include FT_SERVICE_XFREE86_NAME_H
+#include FT_SERVICE_POSTSCRIPT_NAME_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_SERVICE_POSTSCRIPT_INFO_H
+#include FT_SERVICE_KERNING_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1driver
+
+ /*
+ * GLYPH DICT SERVICE
+ *
+ */
+
+ static FT_Error
+ t1_get_glyph_name( T1_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max );
+
+ return T1_Err_Ok;
+ }
+
+
+ static FT_UInt
+ t1_get_name_index( T1_Face face,
+ FT_String* glyph_name )
+ {
+ FT_Int i;
+ FT_String* gname;
+
+
+ for ( i = 0; i < face->type1.num_glyphs; i++ )
+ {
+ gname = face->type1.glyph_names[i];
+
+ if ( !ft_strcmp( glyph_name, gname ) )
+ return (FT_UInt)i;
+ }
+
+ return 0;
+ }
+
+ static const FT_Service_GlyphDictRec t1_service_glyph_dict =
+ {
+ (FT_GlyphDict_GetNameFunc) t1_get_glyph_name,
+ (FT_GlyphDict_NameIndexFunc)t1_get_name_index
+ };
+
+
+ /*
+ * POSTSCRIPT NAME SERVICE
+ *
+ */
+
+ static const char*
+ t1_get_ps_name( T1_Face face )
+ {
+ return (const char*) face->type1.font_name;
+ }
+
+ static const FT_Service_PsFontNameRec t1_service_ps_name =
+ {
+ (FT_PsName_GetFunc)t1_get_ps_name
+ };
+
+
+ /*
+ * MULTIPLE MASTERS SERVICE
+ *
+ */
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+ static const FT_Service_MultiMastersRec t1_service_multi_masters =
+ {
+ (FT_Get_MM_Func) T1_Get_Multi_Master,
+ (FT_Set_MM_Design_Func) T1_Set_MM_Design,
+ (FT_Set_MM_Blend_Func) T1_Set_MM_Blend,
+ (FT_Get_MM_Var_Func) T1_Get_MM_Var,
+ (FT_Set_Var_Design_Func)T1_Set_Var_Design
+ };
+#endif
+
+
+ /*
+ * POSTSCRIPT INFO SERVICE
+ *
+ */
+
+ static FT_Error
+ t1_ps_get_font_info( FT_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ *afont_info = ((T1_Face)face)->type1.font_info;
+ return 0;
+ }
+
+
+ static FT_Int
+ t1_ps_has_glyph_names( FT_Face face )
+ {
+ FT_UNUSED( face );
+ return 1;
+ }
+
+
+ static FT_Error
+ t1_ps_get_font_private( FT_Face face,
+ PS_PrivateRec* afont_private )
+ {
+ *afont_private = ((T1_Face)face)->type1.private_dict;
+ return 0;
+ }
+
+
+ static const FT_Service_PsInfoRec t1_service_ps_info =
+ {
+ (PS_GetFontInfoFunc) t1_ps_get_font_info,
+ (PS_HasGlyphNamesFunc) t1_ps_has_glyph_names,
+ (PS_GetFontPrivateFunc)t1_ps_get_font_private,
+ };
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ static const FT_Service_KerningRec t1_service_kerning =
+ {
+ T1_Get_Track_Kerning,
+ };
+#endif
+
+ /*
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec t1_services[] =
+ {
+ { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &t1_service_ps_name },
+ { FT_SERVICE_ID_GLYPH_DICT, &t1_service_glyph_dict },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_TYPE_1 },
+ { FT_SERVICE_ID_POSTSCRIPT_INFO, &t1_service_ps_info },
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ { FT_SERVICE_ID_KERNING, &t1_service_kerning },
+#endif
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+ { FT_SERVICE_ID_MULTI_MASTERS, &t1_service_multi_masters },
+#endif
+ { NULL, NULL }
+ };
+
+
+ static FT_Module_Interface
+ Get_Interface( FT_Driver driver,
+ const FT_String* t1_interface )
+ {
+ FT_UNUSED( driver );
+
+ return ft_service_list_lookup( t1_services, t1_interface );
+ }
+
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Get_Kerning */
+ /* */
+ /* <Description> */
+ /* A driver method used to return the kerning vector between two */
+ /* glyphs of the same face. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the source face object. */
+ /* */
+ /* left_glyph :: The index of the left glyph in the kern pair. */
+ /* */
+ /* right_glyph :: The index of the right glyph in the kern pair. */
+ /* */
+ /* <Output> */
+ /* kerning :: The kerning vector. This is in font units for */
+ /* scalable formats, and in pixels for fixed-sizes */
+ /* formats. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* Only horizontal layouts (left-to-right & right-to-left) are */
+ /* supported by this function. Other layouts, or more sophisticated */
+ /* kernings are out of scope of this method (the basic driver */
+ /* interface is meant to be simple). */
+ /* */
+ /* They can be implemented by format-specific interfaces. */
+ /* */
+ static FT_Error
+ Get_Kerning( T1_Face face,
+ FT_UInt left_glyph,
+ FT_UInt right_glyph,
+ FT_Vector* kerning )
+ {
+ kerning->x = 0;
+ kerning->y = 0;
+
+ if ( face->afm_data )
+ T1_Get_Kerning( (AFM_FontInfo)face->afm_data,
+ left_glyph,
+ right_glyph,
+ kerning );
+
+ return T1_Err_Ok;
+ }
+
+
+#endif /* T1_CONFIG_OPTION_NO_AFM */
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec t1_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE |
+ FT_MODULE_DRIVER_HAS_HINTER,
+
+ sizeof( FT_DriverRec ),
+
+ "type1",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* format interface */
+
+ (FT_Module_Constructor)T1_Driver_Init,
+ (FT_Module_Destructor) T1_Driver_Done,
+ (FT_Module_Requester) Get_Interface,
+ },
+
+ sizeof( T1_FaceRec ),
+ sizeof( T1_SizeRec ),
+ sizeof( T1_GlyphSlotRec ),
+
+ (FT_Face_InitFunc) T1_Face_Init,
+ (FT_Face_DoneFunc) T1_Face_Done,
+ (FT_Size_InitFunc) T1_Size_Init,
+ (FT_Size_DoneFunc) T1_Size_Done,
+ (FT_Slot_InitFunc) T1_GlyphSlot_Init,
+ (FT_Slot_DoneFunc) T1_GlyphSlot_Done,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ (FT_Slot_LoadFunc) T1_Load_Glyph,
+
+#ifdef T1_CONFIG_OPTION_NO_AFM
+ (FT_Face_GetKerningFunc) 0,
+ (FT_Face_AttachFunc) 0,
+#else
+ (FT_Face_GetKerningFunc) Get_Kerning,
+ (FT_Face_AttachFunc) T1_Read_Metrics,
+#endif
+ (FT_Face_GetAdvancesFunc) 0,
+ (FT_Size_RequestFunc) T1_Size_Request,
+ (FT_Size_SelectFunc) 0
+ };
+
+
+/* END */
diff --git a/src/type1/t1driver.h b/src/type1/t1driver.h
new file mode 100644
index 0000000..ad42944
--- /dev/null
+++ b/src/type1/t1driver.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* t1driver.h */
+/* */
+/* High-level Type 1 driver interface (specification). */
+/* */
+/* Copyright 1996-2001, 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1DRIVER_H__
+#define __T1DRIVER_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) t1_driver_class;
+
+
+FT_END_HEADER
+
+#endif /* __T1DRIVER_H__ */
+
+
+/* END */
diff --git a/src/type1/t1errors.h b/src/type1/t1errors.h
new file mode 100644
index 0000000..81221c3
--- /dev/null
+++ b/src/type1/t1errors.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* t1errors.h */
+/* */
+/* Type 1 error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the Type 1 error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __T1ERRORS_H__
+#define __T1ERRORS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX T1_Err_
+#define FT_ERR_BASE FT_Mod_Err_Type1
+
+#include FT_ERRORS_H
+
+#endif /* __T1ERRORS_H__ */
+
+
+/* END */
diff --git a/src/type1/t1gload.c b/src/type1/t1gload.c
new file mode 100644
index 0000000..e08a428
--- /dev/null
+++ b/src/type1/t1gload.c
@@ -0,0 +1,422 @@
+/***************************************************************************/
+/* */
+/* t1gload.c */
+/* */
+/* Type 1 Glyph Loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include "t1gload.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_OUTLINE_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+#include "t1errors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1gload
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /********** *********/
+ /********** COMPUTE THE MAXIMUM ADVANCE WIDTH *********/
+ /********** *********/
+ /********** The following code is in charge of computing *********/
+ /********** the maximum advance width of the font. It *********/
+ /********** quickly processes each glyph charstring to *********/
+ /********** extract the value from either a `sbw' or `seac' *********/
+ /********** operator. *********/
+ /********** *********/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Parse_Glyph_And_Get_Char_String( T1_Decoder decoder,
+ FT_UInt glyph_index,
+ FT_Data* char_string )
+ {
+ T1_Face face = (T1_Face)decoder->builder.face;
+ T1_Font type1 = &face->type1;
+ FT_Error error = T1_Err_Ok;
+
+
+ decoder->font_matrix = type1->font_matrix;
+ decoder->font_offset = type1->font_offset;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* For incremental fonts get the character data using the */
+ /* callback function. */
+ if ( face->root.internal->incremental_interface )
+ error = face->root.internal->incremental_interface->funcs->get_glyph_data(
+ face->root.internal->incremental_interface->object,
+ glyph_index, char_string );
+ else
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ /* For ordinary fonts get the character data stored in the face record. */
+ {
+ char_string->pointer = type1->charstrings[glyph_index];
+ char_string->length = (FT_Int)type1->charstrings_len[glyph_index];
+ }
+
+ if ( !error )
+ error = decoder->funcs.parse_charstrings(
+ decoder, (FT_Byte*)char_string->pointer,
+ char_string->length );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ /* Incremental fonts can optionally override the metrics. */
+ if ( !error && face->root.internal->incremental_interface &&
+ face->root.internal->incremental_interface->funcs->get_glyph_metrics )
+ {
+ FT_Incremental_MetricsRec metrics;
+
+
+ metrics.bearing_x = decoder->builder.left_bearing.x;
+ metrics.bearing_y = decoder->builder.left_bearing.y;
+ metrics.advance = decoder->builder.advance.x;
+ error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+ face->root.internal->incremental_interface->object,
+ glyph_index, FALSE, &metrics );
+ decoder->builder.left_bearing.x = metrics.bearing_x;
+ decoder->builder.left_bearing.y = metrics.bearing_y;
+ decoder->builder.advance.x = metrics.advance;
+ decoder->builder.advance.y = 0;
+ }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ return error;
+ }
+
+
+ FT_CALLBACK_DEF( FT_Error )
+ T1_Parse_Glyph( T1_Decoder decoder,
+ FT_UInt glyph_index )
+ {
+ FT_Data glyph_data;
+ FT_Error error = T1_Parse_Glyph_And_Get_Char_String(
+ decoder, glyph_index, &glyph_data );
+
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+ if ( !error )
+ {
+ T1_Face face = (T1_Face)decoder->builder.face;
+
+
+ if ( face->root.internal->incremental_interface )
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,
+ &glyph_data );
+ }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Compute_Max_Advance( T1_Face face,
+ FT_Pos* max_advance )
+ {
+ FT_Error error;
+ T1_DecoderRec decoder;
+ FT_Int glyph_index;
+ T1_Font type1 = &face->type1;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );
+
+ *max_advance = 0;
+
+ /* initialize load decoder */
+ error = psaux->t1_decoder_funcs->init( &decoder,
+ (FT_Face)face,
+ 0, /* size */
+ 0, /* glyph slot */
+ (FT_Byte**)type1->glyph_names,
+ face->blend,
+ 0,
+ FT_RENDER_MODE_NORMAL,
+ T1_Parse_Glyph );
+ if ( error )
+ return error;
+
+ decoder.builder.metrics_only = 1;
+ decoder.builder.load_points = 0;
+
+ decoder.num_subrs = type1->num_subrs;
+ decoder.subrs = type1->subrs;
+ decoder.subrs_len = type1->subrs_len;
+
+ decoder.buildchar = face->buildchar;
+ decoder.len_buildchar = face->len_buildchar;
+
+ *max_advance = 0;
+
+ /* for each glyph, parse the glyph charstring and extract */
+ /* the advance width */
+ for ( glyph_index = 0; glyph_index < type1->num_glyphs; glyph_index++ )
+ {
+ /* now get load the unscaled outline */
+ error = T1_Parse_Glyph( &decoder, glyph_index );
+ if ( glyph_index == 0 || decoder.builder.advance.x > *max_advance )
+ *max_advance = decoder.builder.advance.x;
+
+ /* ignore the error if one occurred - skip to next glyph */
+ }
+
+ psaux->t1_decoder_funcs->done( &decoder );
+
+ return T1_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Load_Glyph( T1_GlyphSlot glyph,
+ T1_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ T1_DecoderRec decoder;
+ T1_Face face = (T1_Face)glyph->root.face;
+ FT_Bool hinting;
+ T1_Font type1 = &face->type1;
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+ const T1_Decoder_Funcs decoder_funcs = psaux->t1_decoder_funcs;
+
+ FT_Matrix font_matrix;
+ FT_Vector font_offset;
+ FT_Data glyph_data;
+ FT_Bool must_finish_decoder = FALSE;
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ FT_Bool glyph_data_loaded = 0;
+#endif
+
+
+ if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
+ {
+ error = T1_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );
+
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
+
+ glyph->x_scale = size->root.metrics.x_scale;
+ glyph->y_scale = size->root.metrics.y_scale;
+
+ glyph->root.outline.n_points = 0;
+ glyph->root.outline.n_contours = 0;
+
+ hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 &&
+ ( load_flags & FT_LOAD_NO_HINTING ) == 0 );
+
+ glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;
+
+ error = decoder_funcs->init( &decoder,
+ (FT_Face)face,
+ (FT_Size)size,
+ (FT_GlyphSlot)glyph,
+ (FT_Byte**)type1->glyph_names,
+ face->blend,
+ FT_BOOL( hinting ),
+ FT_LOAD_TARGET_MODE( load_flags ),
+ T1_Parse_Glyph );
+ if ( error )
+ goto Exit;
+
+ must_finish_decoder = TRUE;
+
+ decoder.builder.no_recurse = FT_BOOL(
+ ( load_flags & FT_LOAD_NO_RECURSE ) != 0 );
+
+ decoder.num_subrs = type1->num_subrs;
+ decoder.subrs = type1->subrs;
+ decoder.subrs_len = type1->subrs_len;
+
+ decoder.buildchar = face->buildchar;
+ decoder.len_buildchar = face->len_buildchar;
+
+ /* now load the unscaled outline */
+ error = T1_Parse_Glyph_And_Get_Char_String( &decoder, glyph_index,
+ &glyph_data );
+ if ( error )
+ goto Exit;
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ glyph_data_loaded = 1;
+#endif
+
+ font_matrix = decoder.font_matrix;
+ font_offset = decoder.font_offset;
+
+ /* save new glyph tables */
+ decoder_funcs->done( &decoder );
+
+ must_finish_decoder = FALSE;
+
+ /* now, set the metrics -- this is rather simple, as */
+ /* the left side bearing is the xMin, and the top side */
+ /* bearing the yMax */
+ if ( !error )
+ {
+ glyph->root.outline.flags &= FT_OUTLINE_OWNER;
+ glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL;
+
+ /* for composite glyphs, return only left side bearing and */
+ /* advance width */
+ if ( load_flags & FT_LOAD_NO_RECURSE )
+ {
+ FT_Slot_Internal internal = glyph->root.internal;
+
+
+ glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x;
+ glyph->root.metrics.horiAdvance = decoder.builder.advance.x;
+ internal->glyph_matrix = font_matrix;
+ internal->glyph_delta = font_offset;
+ internal->glyph_transformed = 1;
+ }
+ else
+ {
+ FT_BBox cbox;
+ FT_Glyph_Metrics* metrics = &glyph->root.metrics;
+ FT_Vector advance;
+
+
+ /* copy the _unscaled_ advance width */
+ metrics->horiAdvance = decoder.builder.advance.x;
+ glyph->root.linearHoriAdvance = decoder.builder.advance.x;
+ glyph->root.internal->glyph_transformed = 0;
+
+ /* make up vertical ones */
+ metrics->vertAdvance = ( face->type1.font_bbox.yMax -
+ face->type1.font_bbox.yMin ) >> 16;
+ glyph->root.linearVertAdvance = metrics->vertAdvance;
+
+ glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;
+
+ if ( size && size->root.metrics.y_ppem < 24 )
+ glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+#if 1
+ /* apply the font matrix, if any */
+ if ( font_matrix.xx != 0x10000L || font_matrix.yy != font_matrix.xx ||
+ font_matrix.xy != 0 || font_matrix.yx != 0 )
+ FT_Outline_Transform( &glyph->root.outline, &font_matrix );
+
+ if ( font_offset.x || font_offset.y )
+ FT_Outline_Translate( &glyph->root.outline,
+ font_offset.x,
+ font_offset.y );
+
+ advance.x = metrics->horiAdvance;
+ advance.y = 0;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->horiAdvance = advance.x + font_offset.x;
+ advance.x = 0;
+ advance.y = metrics->vertAdvance;
+ FT_Vector_Transform( &advance, &font_matrix );
+ metrics->vertAdvance = advance.y + font_offset.y;
+#endif
+
+ if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
+ {
+ /* scale the outline and the metrics */
+ FT_Int n;
+ FT_Outline* cur = decoder.builder.base;
+ FT_Vector* vec = cur->points;
+ FT_Fixed x_scale = glyph->x_scale;
+ FT_Fixed y_scale = glyph->y_scale;
+
+
+ /* First of all, scale the points, if we are not hinting */
+ if ( !hinting || ! decoder.builder.hints_funcs )
+ for ( n = cur->n_points; n > 0; n--, vec++ )
+ {
+ vec->x = FT_MulFix( vec->x, x_scale );
+ vec->y = FT_MulFix( vec->y, y_scale );
+ }
+
+ /* Then scale the metrics */
+ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
+ metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
+ }
+
+ /* compute the other metrics */
+ FT_Outline_Get_CBox( &glyph->root.outline, &cbox );
+
+ metrics->width = cbox.xMax - cbox.xMin;
+ metrics->height = cbox.yMax - cbox.yMin;
+
+ metrics->horiBearingX = cbox.xMin;
+ metrics->horiBearingY = cbox.yMax;
+
+ /* make up vertical ones */
+ ft_synthesize_vertical_metrics( metrics,
+ metrics->vertAdvance );
+ }
+
+ /* Set control data to the glyph charstrings. Note that this is */
+ /* _not_ zero-terminated. */
+ glyph->root.control_data = (FT_Byte*)glyph_data.pointer;
+ glyph->root.control_len = glyph_data.length;
+ }
+
+
+ Exit:
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( glyph_data_loaded && face->root.internal->incremental_interface )
+ {
+ face->root.internal->incremental_interface->funcs->free_glyph_data(
+ face->root.internal->incremental_interface->object,
+ &glyph_data );
+
+ /* Set the control data to null - it is no longer available if */
+ /* loaded incrementally. */
+ glyph->root.control_data = 0;
+ glyph->root.control_len = 0;
+ }
+#endif
+
+ if ( must_finish_decoder )
+ decoder_funcs->done( &decoder );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/type1/t1gload.h b/src/type1/t1gload.h
new file mode 100644
index 0000000..de87896
--- /dev/null
+++ b/src/type1/t1gload.h
@@ -0,0 +1,46 @@
+/***************************************************************************/
+/* */
+/* t1gload.h */
+/* */
+/* Type 1 Glyph Loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1GLOAD_H__
+#define __T1GLOAD_H__
+
+
+#include <ft2build.h>
+#include "t1objs.h"
+
+
+FT_BEGIN_HEADER
+
+
+ FT_LOCAL( FT_Error )
+ T1_Compute_Max_Advance( T1_Face face,
+ FT_Pos* max_advance );
+
+ FT_LOCAL( FT_Error )
+ T1_Load_Glyph( T1_GlyphSlot glyph,
+ T1_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+
+FT_END_HEADER
+
+#endif /* __T1GLOAD_H__ */
+
+
+/* END */
diff --git a/src/type1/t1load.c b/src/type1/t1load.c
new file mode 100644
index 0000000..55177ee
--- /dev/null
+++ b/src/type1/t1load.c
@@ -0,0 +1,2221 @@
+/***************************************************************************/
+/* */
+/* t1load.c */
+/* */
+/* Type 1 font loader (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This is the new and improved Type 1 data loader for FreeType 2. The */
+ /* old loader has several problems: it is slow, complex, difficult to */
+ /* maintain, and contains incredible hacks to make it accept some */
+ /* ill-formed Type 1 fonts without hiccup-ing. Moreover, about 5% of */
+ /* the Type 1 fonts on my machine still aren't loaded correctly by it. */
+ /* */
+ /* This version is much simpler, much faster and also easier to read and */
+ /* maintain by a great order of magnitude. The idea behind it is to */
+ /* _not_ try to read the Type 1 token stream with a state machine (i.e. */
+ /* a Postscript-like interpreter) but rather to perform simple pattern */
+ /* matching. */
+ /* */
+ /* Indeed, nearly all data definitions follow a simple pattern like */
+ /* */
+ /* ... /Field <data> ... */
+ /* */
+ /* where <data> can be a number, a boolean, a string, or an array of */
+ /* numbers. There are a few exceptions, namely the encoding, font name, */
+ /* charstrings, and subrs; they are handled with a special pattern */
+ /* matching routine. */
+ /* */
+ /* All other common cases are handled very simply. The matching rules */
+ /* are defined in the file `t1tokens.h' through the use of several */
+ /* macros calls PARSE_XXX. This file is included twice here; the first */
+ /* time to generate parsing callback functions, the second time to */
+ /* generate a table of keywords (with pointers to the associated */
+ /* callback functions). */
+ /* */
+ /* The function `parse_dict' simply scans *linearly* a given dictionary */
+ /* (either the top-level or private one) and calls the appropriate */
+ /* callback when it encounters an immediate keyword. */
+ /* */
+ /* This is by far the fastest way one can find to parse and read all */
+ /* data. */
+ /* */
+ /* This led to tremendous code size reduction. Note that later, the */
+ /* glyph loader will also be _greatly_ simplified, and the automatic */
+ /* hinter will replace the clumsy `t1hinter'. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_CONFIG_CONFIG_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+#include "t1load.h"
+#include "t1errors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1load
+
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** MULTIPLE MASTERS SUPPORT *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ t1_allocate_blend( T1_Face face,
+ FT_UInt num_designs,
+ FT_UInt num_axis )
+ {
+ PS_Blend blend;
+ FT_Memory memory = face->root.memory;
+ FT_Error error = T1_Err_Ok;
+
+
+ blend = face->blend;
+ if ( !blend )
+ {
+ if ( FT_NEW( blend ) )
+ goto Exit;
+
+ blend->num_default_design_vector = 0;
+
+ face->blend = blend;
+ }
+
+ /* allocate design data if needed */
+ if ( num_designs > 0 )
+ {
+ if ( blend->num_designs == 0 )
+ {
+ FT_UInt nn;
+
+
+ /* allocate the blend `private' and `font_info' dictionaries */
+ if ( FT_NEW_ARRAY( blend->font_infos[1], num_designs ) ||
+ FT_NEW_ARRAY( blend->privates[1], num_designs ) ||
+ FT_NEW_ARRAY( blend->bboxes[1], num_designs ) ||
+ FT_NEW_ARRAY( blend->weight_vector, num_designs * 2 ) )
+ goto Exit;
+
+ blend->default_weight_vector = blend->weight_vector + num_designs;
+
+ blend->font_infos[0] = &face->type1.font_info;
+ blend->privates [0] = &face->type1.private_dict;
+ blend->bboxes [0] = &face->type1.font_bbox;
+
+ for ( nn = 2; nn <= num_designs; nn++ )
+ {
+ blend->privates[nn] = blend->privates [nn - 1] + 1;
+ blend->font_infos[nn] = blend->font_infos[nn - 1] + 1;
+ blend->bboxes[nn] = blend->bboxes [nn - 1] + 1;
+ }
+
+ blend->num_designs = num_designs;
+ }
+ else if ( blend->num_designs != num_designs )
+ goto Fail;
+ }
+
+ /* allocate axis data if needed */
+ if ( num_axis > 0 )
+ {
+ if ( blend->num_axis != 0 && blend->num_axis != num_axis )
+ goto Fail;
+
+ blend->num_axis = num_axis;
+ }
+
+ /* allocate the blend design pos table if needed */
+ num_designs = blend->num_designs;
+ num_axis = blend->num_axis;
+ if ( num_designs && num_axis && blend->design_pos[0] == 0 )
+ {
+ FT_UInt n;
+
+
+ if ( FT_NEW_ARRAY( blend->design_pos[0], num_designs * num_axis ) )
+ goto Exit;
+
+ for ( n = 1; n < num_designs; n++ )
+ blend->design_pos[n] = blend->design_pos[0] + num_axis * n;
+ }
+
+ Exit:
+ return error;
+
+ Fail:
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Get_Multi_Master( T1_Face face,
+ FT_Multi_Master* master )
+ {
+ PS_Blend blend = face->blend;
+ FT_UInt n;
+ FT_Error error;
+
+
+ error = T1_Err_Invalid_Argument;
+
+ if ( blend )
+ {
+ master->num_axis = blend->num_axis;
+ master->num_designs = blend->num_designs;
+
+ for ( n = 0; n < blend->num_axis; n++ )
+ {
+ FT_MM_Axis* axis = master->axis + n;
+ PS_DesignMap map = blend->design_map + n;
+
+
+ axis->name = blend->axis_names[n];
+ axis->minimum = map->design_points[0];
+ axis->maximum = map->design_points[map->num_points - 1];
+ }
+
+ error = T1_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+#define FT_INT_TO_FIXED( a ) ( (a) << 16 )
+#define FT_FIXED_TO_INT( a ) ( FT_RoundFix( a ) >> 16 )
+
+
+ /*************************************************************************/
+ /* */
+ /* Given a normalized (blend) coordinate, figure out the design */
+ /* coordinate appropriate for that value. */
+ /* */
+ FT_LOCAL_DEF( FT_Fixed )
+ mm_axis_unmap( PS_DesignMap axismap,
+ FT_Fixed ncv )
+ {
+ int j;
+
+
+ if ( ncv <= axismap->blend_points[0] )
+ return axismap->design_points[0];
+
+ for ( j = 1; j < axismap->num_points; ++j )
+ {
+ if ( ncv <= axismap->blend_points[j] )
+ {
+ FT_Fixed t = FT_MulDiv( ncv - axismap->blend_points[j - 1],
+ 0x10000L,
+ axismap->blend_points[j] -
+ axismap->blend_points[j - 1] );
+
+
+ return axismap->design_points[j - 1] +
+ FT_MulDiv( t,
+ axismap->design_points[j] -
+ axismap->design_points[j - 1],
+ 1L );
+ }
+ }
+
+ return axismap->design_points[axismap->num_points - 1];
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Given a vector of weights, one for each design, figure out the */
+ /* normalized axis coordinates which gave rise to those weights. */
+ /* */
+ FT_LOCAL_DEF( void )
+ mm_weights_unmap( FT_Fixed* weights,
+ FT_Fixed* axiscoords,
+ FT_UInt axis_count )
+ {
+ FT_ASSERT( axis_count <= T1_MAX_MM_AXIS );
+
+ if ( axis_count == 1 )
+ axiscoords[0] = weights[1];
+
+ else if ( axis_count == 2 )
+ {
+ axiscoords[0] = weights[3] + weights[1];
+ axiscoords[1] = weights[3] + weights[2];
+ }
+
+ else if ( axis_count == 3 )
+ {
+ axiscoords[0] = weights[7] + weights[5] + weights[3] + weights[1];
+ axiscoords[1] = weights[7] + weights[6] + weights[3] + weights[2];
+ axiscoords[2] = weights[7] + weights[6] + weights[5] + weights[4];
+ }
+
+ else
+ {
+ axiscoords[0] = weights[15] + weights[13] + weights[11] + weights[9] +
+ weights[7] + weights[5] + weights[3] + weights[1];
+ axiscoords[1] = weights[15] + weights[14] + weights[11] + weights[10] +
+ weights[7] + weights[6] + weights[3] + weights[2];
+ axiscoords[2] = weights[15] + weights[14] + weights[13] + weights[12] +
+ weights[7] + weights[6] + weights[5] + weights[4];
+ axiscoords[3] = weights[15] + weights[14] + weights[13] + weights[12] +
+ weights[11] + weights[10] + weights[9] + weights[8];
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Just a wrapper around T1_Get_Multi_Master to support the different */
+ /* arguments needed by the GX var distortable fonts. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ T1_Get_MM_Var( T1_Face face,
+ FT_MM_Var* *master )
+ {
+ FT_Memory memory = face->root.memory;
+ FT_MM_Var *mmvar;
+ FT_Multi_Master mmaster;
+ FT_Error error;
+ FT_UInt i;
+ FT_Fixed axiscoords[T1_MAX_MM_AXIS];
+ PS_Blend blend = face->blend;
+
+
+ error = T1_Get_Multi_Master( face, &mmaster );
+ if ( error )
+ goto Exit;
+ if ( FT_ALLOC( mmvar,
+ sizeof ( FT_MM_Var ) +
+ mmaster.num_axis * sizeof ( FT_Var_Axis ) ) )
+ goto Exit;
+
+ mmvar->num_axis = mmaster.num_axis;
+ mmvar->num_designs = mmaster.num_designs;
+ mmvar->num_namedstyles = (FT_UInt)-1; /* Does not apply */
+ mmvar->axis = (FT_Var_Axis*)&mmvar[1];
+ /* Point to axes after MM_Var struct */
+ mmvar->namedstyle = NULL;
+
+ for ( i = 0 ; i < mmaster.num_axis; ++i )
+ {
+ mmvar->axis[i].name = mmaster.axis[i].name;
+ mmvar->axis[i].minimum = FT_INT_TO_FIXED( mmaster.axis[i].minimum);
+ mmvar->axis[i].maximum = FT_INT_TO_FIXED( mmaster.axis[i].maximum);
+ mmvar->axis[i].def = ( mmvar->axis[i].minimum +
+ mmvar->axis[i].maximum ) / 2;
+ /* Does not apply. But this value is in range */
+ mmvar->axis[i].strid = 0xFFFFFFFFUL; /* Does not apply */
+ mmvar->axis[i].tag = 0xFFFFFFFFUL; /* Does not apply */
+
+ if ( ft_strcmp( mmvar->axis[i].name, "Weight" ) == 0 )
+ mmvar->axis[i].tag = FT_MAKE_TAG( 'w', 'g', 'h', 't' );
+ else if ( ft_strcmp( mmvar->axis[i].name, "Width" ) == 0 )
+ mmvar->axis[i].tag = FT_MAKE_TAG( 'w', 'd', 't', 'h' );
+ else if ( ft_strcmp( mmvar->axis[i].name, "OpticalSize" ) == 0 )
+ mmvar->axis[i].tag = FT_MAKE_TAG( 'o', 'p', 's', 'z' );
+ }
+
+ if ( blend->num_designs == 1U << blend->num_axis )
+ {
+ mm_weights_unmap( blend->default_weight_vector,
+ axiscoords,
+ blend->num_axis );
+
+ for ( i = 0; i < mmaster.num_axis; ++i )
+ mmvar->axis[i].def =
+ FT_INT_TO_FIXED( mm_axis_unmap( &blend->design_map[i],
+ axiscoords[i] ) );
+ }
+
+ *master = mmvar;
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Set_MM_Blend( T1_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ PS_Blend blend = face->blend;
+ FT_Error error;
+ FT_UInt n, m;
+
+
+ error = T1_Err_Invalid_Argument;
+
+ if ( blend && blend->num_axis == num_coords )
+ {
+ /* recompute the weight vector from the blend coordinates */
+ error = T1_Err_Ok;
+
+ for ( n = 0; n < blend->num_designs; n++ )
+ {
+ FT_Fixed result = 0x10000L; /* 1.0 fixed */
+
+
+ for ( m = 0; m < blend->num_axis; m++ )
+ {
+ FT_Fixed factor;
+
+
+ /* get current blend axis position */
+ factor = coords[m];
+ if ( factor < 0 ) factor = 0;
+ if ( factor > 0x10000L ) factor = 0x10000L;
+
+ if ( ( n & ( 1 << m ) ) == 0 )
+ factor = 0x10000L - factor;
+
+ result = FT_MulFix( result, factor );
+ }
+ blend->weight_vector[n] = result;
+ }
+
+ error = T1_Err_Ok;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Set_MM_Design( T1_Face face,
+ FT_UInt num_coords,
+ FT_Long* coords )
+ {
+ PS_Blend blend = face->blend;
+ FT_Error error;
+ FT_UInt n, p;
+
+
+ error = T1_Err_Invalid_Argument;
+ if ( blend && blend->num_axis == num_coords )
+ {
+ /* compute the blend coordinates through the blend design map */
+ FT_Fixed final_blends[T1_MAX_MM_DESIGNS];
+
+
+ for ( n = 0; n < blend->num_axis; n++ )
+ {
+ FT_Long design = coords[n];
+ FT_Fixed the_blend;
+ PS_DesignMap map = blend->design_map + n;
+ FT_Long* designs = map->design_points;
+ FT_Fixed* blends = map->blend_points;
+ FT_Int before = -1, after = -1;
+
+
+ for ( p = 0; p < (FT_UInt)map->num_points; p++ )
+ {
+ FT_Long p_design = designs[p];
+
+
+ /* exact match? */
+ if ( design == p_design )
+ {
+ the_blend = blends[p];
+ goto Found;
+ }
+
+ if ( design < p_design )
+ {
+ after = p;
+ break;
+ }
+
+ before = p;
+ }
+
+ /* now interpolate if necessary */
+ if ( before < 0 )
+ the_blend = blends[0];
+
+ else if ( after < 0 )
+ the_blend = blends[map->num_points - 1];
+
+ else
+ the_blend = FT_MulDiv( design - designs[before],
+ blends [after] - blends [before],
+ designs[after] - designs[before] );
+
+ Found:
+ final_blends[n] = the_blend;
+ }
+
+ error = T1_Set_MM_Blend( face, num_coords, final_blends );
+ }
+
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Just a wrapper around T1_Set_MM_Design to support the different */
+ /* arguments needed by the GX var distortable fonts. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ T1_Set_Var_Design( T1_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords )
+ {
+ FT_Long lcoords[4]; /* maximum axis count is 4 */
+ FT_UInt i;
+ FT_Error error;
+
+
+ error = T1_Err_Invalid_Argument;
+ if ( num_coords <= 4 && num_coords > 0 )
+ {
+ for ( i = 0; i < num_coords; ++i )
+ lcoords[i] = FT_FIXED_TO_INT( coords[i] );
+ error = T1_Set_MM_Design( face, num_coords, lcoords );
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T1_Done_Blend( T1_Face face )
+ {
+ FT_Memory memory = face->root.memory;
+ PS_Blend blend = face->blend;
+
+
+ if ( blend )
+ {
+ FT_UInt num_designs = blend->num_designs;
+ FT_UInt num_axis = blend->num_axis;
+ FT_UInt n;
+
+
+ /* release design pos table */
+ FT_FREE( blend->design_pos[0] );
+ for ( n = 1; n < num_designs; n++ )
+ blend->design_pos[n] = 0;
+
+ /* release blend `private' and `font info' dictionaries */
+ FT_FREE( blend->privates[1] );
+ FT_FREE( blend->font_infos[1] );
+ FT_FREE( blend->bboxes[1] );
+
+ for ( n = 0; n < num_designs; n++ )
+ {
+ blend->privates [n] = 0;
+ blend->font_infos[n] = 0;
+ blend->bboxes [n] = 0;
+ }
+
+ /* release weight vectors */
+ FT_FREE( blend->weight_vector );
+ blend->default_weight_vector = 0;
+
+ /* release axis names */
+ for ( n = 0; n < num_axis; n++ )
+ FT_FREE( blend->axis_names[n] );
+
+ /* release design map */
+ for ( n = 0; n < num_axis; n++ )
+ {
+ PS_DesignMap dmap = blend->design_map + n;
+
+
+ FT_FREE( dmap->design_points );
+ dmap->num_points = 0;
+ }
+
+ FT_FREE( face->blend );
+ }
+ }
+
+
+ static void
+ parse_blend_axis_types( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_TokenRec axis_tokens[T1_MAX_MM_AXIS];
+ FT_Int n, num_axis;
+ FT_Error error = T1_Err_Ok;
+ PS_Blend blend;
+ FT_Memory memory;
+
+
+ /* take an array of objects */
+ T1_ToTokenArray( &loader->parser, axis_tokens,
+ T1_MAX_MM_AXIS, &num_axis );
+ if ( num_axis < 0 )
+ {
+ error = T1_Err_Ignore;
+ goto Exit;
+ }
+ if ( num_axis == 0 || num_axis > T1_MAX_MM_AXIS )
+ {
+ FT_ERROR(( "parse_blend_axis_types: incorrect number of axes: %d\n",
+ num_axis ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* allocate blend if necessary */
+ error = t1_allocate_blend( face, 0, (FT_UInt)num_axis );
+ if ( error )
+ goto Exit;
+
+ blend = face->blend;
+ memory = face->root.memory;
+
+ /* each token is an immediate containing the name of the axis */
+ for ( n = 0; n < num_axis; n++ )
+ {
+ T1_Token token = axis_tokens + n;
+ FT_Byte* name;
+ FT_PtrDist len;
+
+
+ /* skip first slash, if any */
+ if ( token->start[0] == '/' )
+ token->start++;
+
+ len = token->limit - token->start;
+ if ( len == 0 )
+ {
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( FT_ALLOC( blend->axis_names[n], len + 1 ) )
+ goto Exit;
+
+ name = (FT_Byte*)blend->axis_names[n];
+ FT_MEM_COPY( name, token->start, len );
+ name[len] = 0;
+ }
+
+ Exit:
+ loader->parser.root.error = error;
+ }
+
+
+ static void
+ parse_blend_design_positions( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS];
+ FT_Int num_designs;
+ FT_Int num_axis;
+ T1_Parser parser = &loader->parser;
+
+ FT_Error error = T1_Err_Ok;
+ PS_Blend blend;
+
+
+ /* get the array of design tokens -- compute number of designs */
+ T1_ToTokenArray( parser, design_tokens,
+ T1_MAX_MM_DESIGNS, &num_designs );
+ if ( num_designs < 0 )
+ {
+ error = T1_Err_Ignore;
+ goto Exit;
+ }
+ if ( num_designs == 0 || num_designs > T1_MAX_MM_DESIGNS )
+ {
+ FT_ERROR(( "parse_blend_design_positions:" ));
+ FT_ERROR(( " incorrect number of designs: %d\n",
+ num_designs ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ {
+ FT_Byte* old_cursor = parser->root.cursor;
+ FT_Byte* old_limit = parser->root.limit;
+ FT_Int n;
+
+
+ blend = face->blend;
+ num_axis = 0; /* make compiler happy */
+
+ for ( n = 0; n < num_designs; n++ )
+ {
+ T1_TokenRec axis_tokens[T1_MAX_MM_DESIGNS];
+ T1_Token token;
+ FT_Int axis, n_axis;
+
+
+ /* read axis/coordinates tokens */
+ token = design_tokens + n;
+ parser->root.cursor = token->start;
+ parser->root.limit = token->limit;
+ T1_ToTokenArray( parser, axis_tokens, T1_MAX_MM_AXIS, &n_axis );
+
+ if ( n == 0 )
+ {
+ num_axis = n_axis;
+ error = t1_allocate_blend( face, num_designs, num_axis );
+ if ( error )
+ goto Exit;
+ blend = face->blend;
+ }
+ else if ( n_axis != num_axis )
+ {
+ FT_ERROR(( "parse_blend_design_positions: incorrect table\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* now read each axis token into the design position */
+ for ( axis = 0; axis < n_axis; axis++ )
+ {
+ T1_Token token2 = axis_tokens + axis;
+
+
+ parser->root.cursor = token2->start;
+ parser->root.limit = token2->limit;
+ blend->design_pos[n][axis] = T1_ToFixed( parser, 0 );
+ }
+ }
+
+ loader->parser.root.cursor = old_cursor;
+ loader->parser.root.limit = old_limit;
+ }
+
+ Exit:
+ loader->parser.root.error = error;
+ }
+
+
+ static void
+ parse_blend_design_map( T1_Face face,
+ T1_Loader loader )
+ {
+ FT_Error error = T1_Err_Ok;
+ T1_Parser parser = &loader->parser;
+ PS_Blend blend;
+ T1_TokenRec axis_tokens[T1_MAX_MM_AXIS];
+ FT_Int n, num_axis;
+ FT_Byte* old_cursor;
+ FT_Byte* old_limit;
+ FT_Memory memory = face->root.memory;
+
+
+ T1_ToTokenArray( parser, axis_tokens,
+ T1_MAX_MM_AXIS, &num_axis );
+ if ( num_axis < 0 )
+ {
+ error = T1_Err_Ignore;
+ goto Exit;
+ }
+ if ( num_axis == 0 || num_axis > T1_MAX_MM_AXIS )
+ {
+ FT_ERROR(( "parse_blend_design_map: incorrect number of axes: %d\n",
+ num_axis ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ old_cursor = parser->root.cursor;
+ old_limit = parser->root.limit;
+
+ error = t1_allocate_blend( face, 0, num_axis );
+ if ( error )
+ goto Exit;
+ blend = face->blend;
+
+ /* now read each axis design map */
+ for ( n = 0; n < num_axis; n++ )
+ {
+ PS_DesignMap map = blend->design_map + n;
+ T1_Token axis_token;
+ T1_TokenRec point_tokens[T1_MAX_MM_MAP_POINTS];
+ FT_Int p, num_points;
+
+
+ axis_token = axis_tokens + n;
+
+ parser->root.cursor = axis_token->start;
+ parser->root.limit = axis_token->limit;
+ T1_ToTokenArray( parser, point_tokens,
+ T1_MAX_MM_MAP_POINTS, &num_points );
+
+ if ( num_points <= 0 || num_points > T1_MAX_MM_MAP_POINTS )
+ {
+ FT_ERROR(( "parse_blend_design_map: incorrect table\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* allocate design map data */
+ if ( FT_NEW_ARRAY( map->design_points, num_points * 2 ) )
+ goto Exit;
+ map->blend_points = map->design_points + num_points;
+ map->num_points = (FT_Byte)num_points;
+
+ for ( p = 0; p < num_points; p++ )
+ {
+ T1_Token point_token;
+
+
+ point_token = point_tokens + p;
+
+ /* don't include delimiting brackets */
+ parser->root.cursor = point_token->start + 1;
+ parser->root.limit = point_token->limit - 1;
+
+ map->design_points[p] = T1_ToInt( parser );
+ map->blend_points [p] = T1_ToFixed( parser, 0 );
+ }
+ }
+
+ parser->root.cursor = old_cursor;
+ parser->root.limit = old_limit;
+
+ Exit:
+ parser->root.error = error;
+ }
+
+
+ static void
+ parse_weight_vector( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_TokenRec design_tokens[T1_MAX_MM_DESIGNS];
+ FT_Int num_designs;
+ FT_Error error = T1_Err_Ok;
+ T1_Parser parser = &loader->parser;
+ PS_Blend blend = face->blend;
+ T1_Token token;
+ FT_Int n;
+ FT_Byte* old_cursor;
+ FT_Byte* old_limit;
+
+
+ T1_ToTokenArray( parser, design_tokens,
+ T1_MAX_MM_DESIGNS, &num_designs );
+ if ( num_designs < 0 )
+ {
+ error = T1_Err_Ignore;
+ goto Exit;
+ }
+ if ( num_designs == 0 || num_designs > T1_MAX_MM_DESIGNS )
+ {
+ FT_ERROR(( "parse_weight_vector:" ));
+ FT_ERROR(( " incorrect number of designs: %d\n",
+ num_designs ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( !blend || !blend->num_designs )
+ {
+ error = t1_allocate_blend( face, num_designs, 0 );
+ if ( error )
+ goto Exit;
+ blend = face->blend;
+ }
+ else if ( blend->num_designs != (FT_UInt)num_designs )
+ {
+ FT_ERROR(( "parse_weight_vector:"
+ " /BlendDesignPosition and /WeightVector have\n" ));
+ FT_ERROR(( " "
+ " different number of elements!\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ old_cursor = parser->root.cursor;
+ old_limit = parser->root.limit;
+
+ for ( n = 0; n < num_designs; n++ )
+ {
+ token = design_tokens + n;
+ parser->root.cursor = token->start;
+ parser->root.limit = token->limit;
+
+ blend->default_weight_vector[n] =
+ blend->weight_vector[n] = T1_ToFixed( parser, 0 );
+ }
+
+ parser->root.cursor = old_cursor;
+ parser->root.limit = old_limit;
+
+ Exit:
+ parser->root.error = error;
+ }
+
+
+ /* e.g., /BuildCharArray [0 0 0 0 0 0 0 0] def */
+ /* we're only interested in the number of array elements */
+ static void
+ parse_buildchar( T1_Face face,
+ T1_Loader loader )
+ {
+ face->len_buildchar = T1_ToFixedArray( &loader->parser, 0, NULL, 0 );
+
+ return;
+ }
+
+
+#endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */
+
+
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** TYPE 1 SYMBOL PARSING *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+
+ static FT_Error
+ t1_load_keyword( T1_Face face,
+ T1_Loader loader,
+ const T1_Field field )
+ {
+ FT_Error error;
+ void* dummy_object;
+ void** objects;
+ FT_UInt max_objects;
+ PS_Blend blend = face->blend;
+
+
+ /* if the keyword has a dedicated callback, call it */
+ if ( field->type == T1_FIELD_TYPE_CALLBACK )
+ {
+ field->reader( (FT_Face)face, loader );
+ error = loader->parser.root.error;
+ goto Exit;
+ }
+
+ /* now, the keyword is either a simple field, or a table of fields; */
+ /* we are now going to take care of it */
+ switch ( field->location )
+ {
+ case T1_FIELD_LOCATION_FONT_INFO:
+ dummy_object = &face->type1.font_info;
+ objects = &dummy_object;
+ max_objects = 0;
+
+ if ( blend )
+ {
+ objects = (void**)blend->font_infos;
+ max_objects = blend->num_designs;
+ }
+ break;
+
+ case T1_FIELD_LOCATION_PRIVATE:
+ dummy_object = &face->type1.private_dict;
+ objects = &dummy_object;
+ max_objects = 0;
+
+ if ( blend )
+ {
+ objects = (void**)blend->privates;
+ max_objects = blend->num_designs;
+ }
+ break;
+
+ case T1_FIELD_LOCATION_BBOX:
+ dummy_object = &face->type1.font_bbox;
+ objects = &dummy_object;
+ max_objects = 0;
+
+ if ( blend )
+ {
+ objects = (void**)blend->bboxes;
+ max_objects = blend->num_designs;
+ }
+ break;
+
+ case T1_FIELD_LOCATION_LOADER:
+ dummy_object = loader;
+ objects = &dummy_object;
+ max_objects = 0;
+ break;
+
+ case T1_FIELD_LOCATION_FACE:
+ dummy_object = face;
+ objects = &dummy_object;
+ max_objects = 0;
+ break;
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+ case T1_FIELD_LOCATION_BLEND:
+ dummy_object = face->blend;
+ objects = &dummy_object;
+ max_objects = 0;
+ break;
+#endif
+
+ default:
+ dummy_object = &face->type1;
+ objects = &dummy_object;
+ max_objects = 0;
+ }
+
+ if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
+ field->type == T1_FIELD_TYPE_FIXED_ARRAY )
+ error = T1_Load_Field_Table( &loader->parser, field,
+ objects, max_objects, 0 );
+ else
+ error = T1_Load_Field( &loader->parser, field,
+ objects, max_objects, 0 );
+
+ Exit:
+ return error;
+ }
+
+
+ static void
+ parse_private( T1_Face face,
+ T1_Loader loader )
+ {
+ FT_UNUSED( face );
+
+ loader->keywords_encountered |= T1_PRIVATE;
+ }
+
+
+ static int
+ read_binary_data( T1_Parser parser,
+ FT_Long* size,
+ FT_Byte** base )
+ {
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+
+
+ /* the binary data has one of the following formats */
+ /* */
+ /* `size' [white*] RD white ....... ND */
+ /* `size' [white*] -| white ....... |- */
+ /* */
+
+ T1_Skip_Spaces( parser );
+
+ cur = parser->root.cursor;
+
+ if ( cur < limit && ft_isdigit( *cur ) )
+ {
+ *size = T1_ToInt( parser );
+
+ T1_Skip_PS_Token( parser ); /* `RD' or `-|' or something else */
+
+ /* there is only one whitespace char after the */
+ /* `RD' or `-|' token */
+ *base = parser->root.cursor + 1;
+
+ parser->root.cursor += *size + 1;
+ return !parser->root.error;
+ }
+
+ FT_ERROR(( "read_binary_data: invalid size field\n" ));
+ parser->root.error = T1_Err_Invalid_File_Format;
+ return 0;
+ }
+
+
+ /* We now define the routines to handle the `/Encoding', `/Subrs', */
+ /* and `/CharStrings' dictionaries. */
+
+ static void
+ parse_font_matrix( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_Parser parser = &loader->parser;
+ FT_Matrix* matrix = &face->type1.font_matrix;
+ FT_Vector* offset = &face->type1.font_offset;
+ FT_Face root = (FT_Face)&face->root;
+ FT_Fixed temp[6];
+ FT_Fixed temp_scale;
+ FT_Int result;
+
+
+ result = T1_ToFixedArray( parser, 6, temp, 3 );
+
+ if ( result < 0 )
+ {
+ parser->root.error = T1_Err_Invalid_File_Format;
+ return;
+ }
+
+ temp_scale = FT_ABS( temp[3] );
+
+ if ( temp_scale == 0 )
+ {
+ FT_ERROR(( "parse_font_matrix: invalid font matrix\n" ));
+ parser->root.error = T1_Err_Invalid_File_Format;
+ return;
+ }
+
+ /* Set Units per EM based on FontMatrix values. We set the value to */
+ /* 1000 / temp_scale, because temp_scale was already multiplied by */
+ /* 1000 (in t1_tofixed, from psobjs.c). */
+
+ root->units_per_EM = (FT_UShort)( FT_DivFix( 1000 * 0x10000L,
+ temp_scale ) >> 16 );
+
+ /* we need to scale the values by 1.0/temp_scale */
+ if ( temp_scale != 0x10000L )
+ {
+ temp[0] = FT_DivFix( temp[0], temp_scale );
+ temp[1] = FT_DivFix( temp[1], temp_scale );
+ temp[2] = FT_DivFix( temp[2], temp_scale );
+ temp[4] = FT_DivFix( temp[4], temp_scale );
+ temp[5] = FT_DivFix( temp[5], temp_scale );
+ temp[3] = 0x10000L;
+ }
+
+ matrix->xx = temp[0];
+ matrix->yx = temp[1];
+ matrix->xy = temp[2];
+ matrix->yy = temp[3];
+
+ /* note that the offsets must be expressed in integer font units */
+ offset->x = temp[4] >> 16;
+ offset->y = temp[5] >> 16;
+ }
+
+
+ static void
+ parse_encoding( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_Parser parser = &loader->parser;
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ T1_Skip_Spaces( parser );
+ cur = parser->root.cursor;
+ if ( cur >= limit )
+ {
+ FT_ERROR(( "parse_encoding: out of bounds!\n" ));
+ parser->root.error = T1_Err_Invalid_File_Format;
+ return;
+ }
+
+ /* if we have a number or `[', the encoding is an array, */
+ /* and we must load it now */
+ if ( ft_isdigit( *cur ) || *cur == '[' )
+ {
+ T1_Encoding encode = &face->type1.encoding;
+ FT_Int count, n;
+ PS_Table char_table = &loader->encoding_table;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error;
+ FT_Bool only_immediates = 0;
+
+
+ /* read the number of entries in the encoding; should be 256 */
+ if ( *cur == '[' )
+ {
+ count = 256;
+ only_immediates = 1;
+ parser->root.cursor++;
+ }
+ else
+ count = (FT_Int)T1_ToInt( parser );
+
+ T1_Skip_Spaces( parser );
+ if ( parser->root.cursor >= limit )
+ return;
+
+ /* we use a T1_Table to store our charnames */
+ loader->num_chars = encode->num_chars = count;
+ if ( FT_NEW_ARRAY( encode->char_index, count ) ||
+ FT_NEW_ARRAY( encode->char_name, count ) ||
+ FT_SET_ERROR( psaux->ps_table_funcs->init(
+ char_table, count, memory ) ) )
+ {
+ parser->root.error = error;
+ return;
+ }
+
+ /* We need to `zero' out encoding_table.elements */
+ for ( n = 0; n < count; n++ )
+ {
+ char* notdef = (char *)".notdef";
+
+
+ T1_Add_Table( char_table, n, notdef, 8 );
+ }
+
+ /* Now we need to read records of the form */
+ /* */
+ /* ... charcode /charname ... */
+ /* */
+ /* for each entry in our table. */
+ /* */
+ /* We simply look for a number followed by an immediate */
+ /* name. Note that this ignores correctly the sequence */
+ /* that is often seen in type1 fonts: */
+ /* */
+ /* 0 1 255 { 1 index exch /.notdef put } for dup */
+ /* */
+ /* used to clean the encoding array before anything else. */
+ /* */
+ /* Alternatively, if the array is directly given as */
+ /* */
+ /* /Encoding [ ... ] */
+ /* */
+ /* we only read immediates. */
+
+ n = 0;
+ T1_Skip_Spaces( parser );
+
+ while ( parser->root.cursor < limit )
+ {
+ cur = parser->root.cursor;
+
+ /* we stop when we encounter a `def' or `]' */
+ if ( *cur == 'd' && cur + 3 < limit )
+ {
+ if ( cur[1] == 'e' &&
+ cur[2] == 'f' &&
+ IS_PS_DELIM( cur[3] ) )
+ {
+ FT_TRACE6(( "encoding end\n" ));
+ cur += 3;
+ break;
+ }
+ }
+ if ( *cur == ']' )
+ {
+ FT_TRACE6(( "encoding end\n" ));
+ cur++;
+ break;
+ }
+
+ /* check whether we've found an entry */
+ if ( ft_isdigit( *cur ) || only_immediates )
+ {
+ FT_Int charcode;
+
+
+ if ( only_immediates )
+ charcode = n;
+ else
+ {
+ charcode = (FT_Int)T1_ToInt( parser );
+ T1_Skip_Spaces( parser );
+ }
+
+ cur = parser->root.cursor;
+
+ if ( *cur == '/' && cur + 2 < limit && n < count )
+ {
+ FT_PtrDist len;
+
+
+ cur++;
+
+ parser->root.cursor = cur;
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+
+ len = parser->root.cursor - cur;
+
+ parser->root.error = T1_Add_Table( char_table, charcode,
+ cur, len + 1 );
+ if ( parser->root.error )
+ return;
+ char_table->elements[charcode][len] = '\0';
+
+ n++;
+ }
+ }
+ else
+ {
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+ }
+
+ T1_Skip_Spaces( parser );
+ }
+
+ face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
+ parser->root.cursor = cur;
+ }
+
+ /* Otherwise, we should have either `StandardEncoding', */
+ /* `ExpertEncoding', or `ISOLatin1Encoding' */
+ else
+ {
+ if ( cur + 17 < limit &&
+ ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
+
+ else if ( cur + 15 < limit &&
+ ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
+
+ else if ( cur + 18 < limit &&
+ ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
+
+ else
+ parser->root.error = T1_Err_Ignore;
+ }
+ }
+
+
+ static void
+ parse_subrs( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_Parser parser = &loader->parser;
+ PS_Table table = &loader->subrs;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error;
+ FT_Int n, num_subrs;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ T1_Skip_Spaces( parser );
+
+ /* test for empty array */
+ if ( parser->root.cursor < parser->root.limit &&
+ *parser->root.cursor == '[' )
+ {
+ T1_Skip_PS_Token( parser );
+ T1_Skip_Spaces ( parser );
+ if ( parser->root.cursor >= parser->root.limit ||
+ *parser->root.cursor != ']' )
+ parser->root.error = T1_Err_Invalid_File_Format;
+ return;
+ }
+
+ num_subrs = (FT_Int)T1_ToInt( parser );
+
+ /* position the parser right before the `dup' of the first subr */
+ T1_Skip_PS_Token( parser ); /* `array' */
+ if ( parser->root.error )
+ return;
+ T1_Skip_Spaces( parser );
+
+ /* initialize subrs array -- with synthetic fonts it is possible */
+ /* we get here twice */
+ if ( !loader->num_subrs )
+ {
+ error = psaux->ps_table_funcs->init( table, num_subrs, memory );
+ if ( error )
+ goto Fail;
+ }
+
+ /* the format is simple: */
+ /* */
+ /* `index' + binary data */
+ /* */
+ for ( n = 0; n < num_subrs; n++ )
+ {
+ FT_Long idx, size;
+ FT_Byte* base;
+
+
+ /* If the next token isn't `dup', we are also done. This */
+ /* happens when there are `holes' in the Subrs array. */
+ if ( ft_strncmp( (char*)parser->root.cursor, "dup", 3 ) != 0 )
+ break;
+
+ T1_Skip_PS_Token( parser ); /* `dup' */
+
+ idx = T1_ToInt( parser );
+
+ if ( !read_binary_data( parser, &size, &base ) )
+ return;
+
+ /* The binary string is followed by one token, e.g. `NP' */
+ /* (bound to `noaccess put') or by two separate tokens: */
+ /* `noaccess' & `put'. We position the parser right */
+ /* before the next `dup', if any. */
+ T1_Skip_PS_Token( parser ); /* `NP' or `|' or `noaccess' */
+ if ( parser->root.error )
+ return;
+ T1_Skip_Spaces ( parser );
+
+ if ( ft_strncmp( (char*)parser->root.cursor, "put", 3 ) == 0 )
+ {
+ T1_Skip_PS_Token( parser ); /* skip `put' */
+ T1_Skip_Spaces ( parser );
+ }
+
+ /* with synthetic fonts it is possible we get here twice */
+ if ( loader->num_subrs )
+ continue;
+
+ /* some fonts use a value of -1 for lenIV to indicate that */
+ /* the charstrings are unencoded */
+ /* */
+ /* thanks to Tom Kacvinsky for pointing this out */
+ /* */
+ if ( face->type1.private_dict.lenIV >= 0 )
+ {
+ FT_Byte* temp;
+
+
+ if ( size <= face->type1.private_dict.lenIV )
+ {
+ error = T1_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /* t1_decrypt() shouldn't write to base -- make temporary copy */
+ if ( FT_ALLOC( temp, size ) )
+ goto Fail;
+ FT_MEM_COPY( temp, base, size );
+ psaux->t1_decrypt( temp, size, 4330 );
+ size -= face->type1.private_dict.lenIV;
+ error = T1_Add_Table( table, (FT_Int)idx,
+ temp + face->type1.private_dict.lenIV, size );
+ FT_FREE( temp );
+ }
+ else
+ error = T1_Add_Table( table, (FT_Int)idx, base, size );
+ if ( error )
+ goto Fail;
+ }
+
+ if ( !loader->num_subrs )
+ loader->num_subrs = num_subrs;
+
+ return;
+
+ Fail:
+ parser->root.error = error;
+ }
+
+
+#define TABLE_EXTEND 5
+
+
+ static void
+ parse_charstrings( T1_Face face,
+ T1_Loader loader )
+ {
+ T1_Parser parser = &loader->parser;
+ PS_Table code_table = &loader->charstrings;
+ PS_Table name_table = &loader->glyph_names;
+ PS_Table swap_table = &loader->swap_table;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+ FT_Int n, num_glyphs;
+ FT_UInt notdef_index = 0;
+ FT_Byte notdef_found = 0;
+
+
+ num_glyphs = (FT_Int)T1_ToInt( parser );
+ /* some fonts like Optima-Oblique not only define the /CharStrings */
+ /* array but access it also */
+ if ( num_glyphs == 0 || parser->root.error )
+ return;
+
+ /* initialize tables, leaving space for addition of .notdef, */
+ /* if necessary, and a few other glyphs to handle buggy */
+ /* fonts which have more glyphs than specified. */
+
+ /* for some non-standard fonts like `Optima' which provides */
+ /* different outlines depending on the resolution it is */
+ /* possible to get here twice */
+ if ( !loader->num_glyphs )
+ {
+ error = psaux->ps_table_funcs->init(
+ code_table, num_glyphs + 1 + TABLE_EXTEND, memory );
+ if ( error )
+ goto Fail;
+
+ error = psaux->ps_table_funcs->init(
+ name_table, num_glyphs + 1 + TABLE_EXTEND, memory );
+ if ( error )
+ goto Fail;
+
+ /* Initialize table for swapping index notdef_index and */
+ /* index 0 names and codes (if necessary). */
+
+ error = psaux->ps_table_funcs->init( swap_table, 4, memory );
+ if ( error )
+ goto Fail;
+ }
+
+ n = 0;
+
+ for (;;)
+ {
+ FT_Long size;
+ FT_Byte* base;
+
+
+ /* the format is simple: */
+ /* `/glyphname' + binary data */
+
+ T1_Skip_Spaces( parser );
+
+ cur = parser->root.cursor;
+ if ( cur >= limit )
+ break;
+
+ /* we stop when we find a `def' or `end' keyword */
+ if ( cur + 3 < limit && IS_PS_DELIM( cur[3] ) )
+ {
+ if ( cur[0] == 'd' &&
+ cur[1] == 'e' &&
+ cur[2] == 'f' )
+ {
+ /* There are fonts which have this: */
+ /* */
+ /* /CharStrings 118 dict def */
+ /* Private begin */
+ /* CharStrings begin */
+ /* ... */
+ /* */
+ /* To catch this we ignore `def' if */
+ /* no charstring has actually been */
+ /* seen. */
+ if ( n )
+ break;
+ }
+
+ if ( cur[0] == 'e' &&
+ cur[1] == 'n' &&
+ cur[2] == 'd' )
+ break;
+ }
+
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+
+ if ( *cur == '/' )
+ {
+ FT_PtrDist len;
+
+
+ if ( cur + 1 >= limit )
+ {
+ error = T1_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ cur++; /* skip `/' */
+ len = parser->root.cursor - cur;
+
+ if ( !read_binary_data( parser, &size, &base ) )
+ return;
+
+ /* for some non-standard fonts like `Optima' which provides */
+ /* different outlines depending on the resolution it is */
+ /* possible to get here twice */
+ if ( loader->num_glyphs )
+ continue;
+
+ error = T1_Add_Table( name_table, n, cur, len + 1 );
+ if ( error )
+ goto Fail;
+
+ /* add a trailing zero to the name table */
+ name_table->elements[n][len] = '\0';
+
+ /* record index of /.notdef */
+ if ( *cur == '.' &&
+ ft_strcmp( ".notdef",
+ (const char*)(name_table->elements[n]) ) == 0 )
+ {
+ notdef_index = n;
+ notdef_found = 1;
+ }
+
+ if ( face->type1.private_dict.lenIV >= 0 &&
+ n < num_glyphs + TABLE_EXTEND )
+ {
+ FT_Byte* temp;
+
+
+ if ( size <= face->type1.private_dict.lenIV )
+ {
+ error = T1_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /* t1_decrypt() shouldn't write to base -- make temporary copy */
+ if ( FT_ALLOC( temp, size ) )
+ goto Fail;
+ FT_MEM_COPY( temp, base, size );
+ psaux->t1_decrypt( temp, size, 4330 );
+ size -= face->type1.private_dict.lenIV;
+ error = T1_Add_Table( code_table, n,
+ temp + face->type1.private_dict.lenIV, size );
+ FT_FREE( temp );
+ }
+ else
+ error = T1_Add_Table( code_table, n, base, size );
+ if ( error )
+ goto Fail;
+
+ n++;
+ }
+ }
+
+ if ( loader->num_glyphs )
+ return;
+ else
+ loader->num_glyphs = n;
+
+ /* if /.notdef is found but does not occupy index 0, do our magic. */
+ if ( ft_strcmp( (const char*)".notdef",
+ (const char*)name_table->elements[0] ) &&
+ notdef_found )
+ {
+ /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */
+ /* name and code entries to swap_table. Then place notdef_index */
+ /* name and code entries into swap_table. Then swap name and code */
+ /* entries at indices notdef_index and 0 using values stored in */
+ /* swap_table. */
+
+ /* Index 0 name */
+ error = T1_Add_Table( swap_table, 0,
+ name_table->elements[0],
+ name_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ /* Index 0 code */
+ error = T1_Add_Table( swap_table, 1,
+ code_table->elements[0],
+ code_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ /* Index notdef_index name */
+ error = T1_Add_Table( swap_table, 2,
+ name_table->elements[notdef_index],
+ name_table->lengths [notdef_index] );
+ if ( error )
+ goto Fail;
+
+ /* Index notdef_index code */
+ error = T1_Add_Table( swap_table, 3,
+ code_table->elements[notdef_index],
+ code_table->lengths [notdef_index] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, notdef_index,
+ swap_table->elements[0],
+ swap_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, notdef_index,
+ swap_table->elements[1],
+ swap_table->lengths [1] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, 0,
+ swap_table->elements[2],
+ swap_table->lengths [2] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, 0,
+ swap_table->elements[3],
+ swap_table->lengths [3] );
+ if ( error )
+ goto Fail;
+
+ }
+ else if ( !notdef_found )
+ {
+ /* notdef_index is already 0, or /.notdef is undefined in */
+ /* charstrings dictionary. Worry about /.notdef undefined. */
+ /* We take index 0 and add it to the end of the table(s) */
+ /* and add our own /.notdef glyph to index 0. */
+
+ /* 0 333 hsbw endchar */
+ FT_Byte notdef_glyph[] = {0x8B, 0xF7, 0xE1, 0x0D, 0x0E};
+ char* notdef_name = (char *)".notdef";
+
+
+ error = T1_Add_Table( swap_table, 0,
+ name_table->elements[0],
+ name_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( swap_table, 1,
+ code_table->elements[0],
+ code_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, 0, notdef_name, 8 );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, 0, notdef_glyph, 5 );
+
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, n,
+ swap_table->elements[0],
+ swap_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, n,
+ swap_table->elements[1],
+ swap_table->lengths [1] );
+ if ( error )
+ goto Fail;
+
+ /* we added a glyph. */
+ loader->num_glyphs = n + 1;
+ }
+
+ return;
+
+ Fail:
+ parser->root.error = error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* Define the token field static variables. This is a set of */
+ /* T1_FieldRec variables. */
+ /* */
+ /*************************************************************************/
+
+
+ static
+ const T1_FieldRec t1_keywords[] =
+ {
+
+#include "t1tokens.h"
+
+ /* now add the special functions... */
+ T1_FIELD_CALLBACK( "FontMatrix", parse_font_matrix,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "Encoding", parse_encoding,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "Subrs", parse_subrs,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_CALLBACK( "CharStrings", parse_charstrings,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_CALLBACK( "Private", parse_private,
+ T1_FIELD_DICT_FONTDICT )
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+ T1_FIELD_CALLBACK( "BlendDesignPositions", parse_blend_design_positions,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "BlendDesignMap", parse_blend_design_map,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "BlendAxisTypes", parse_blend_axis_types,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "WeightVector", parse_weight_vector,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_CALLBACK( "BuildCharArray", parse_buildchar,
+ T1_FIELD_DICT_PRIVATE )
+#endif
+
+ { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
+ };
+
+
+#define T1_FIELD_COUNT \
+ ( sizeof ( t1_keywords ) / sizeof ( t1_keywords[0] ) )
+
+
+ static FT_Error
+ parse_dict( T1_Face face,
+ T1_Loader loader,
+ FT_Byte* base,
+ FT_Long size )
+ {
+ T1_Parser parser = &loader->parser;
+ FT_Byte *limit, *start_binary = NULL;
+ FT_Bool have_integer = 0;
+
+
+ parser->root.cursor = base;
+ parser->root.limit = base + size;
+ parser->root.error = T1_Err_Ok;
+
+ limit = parser->root.limit;
+
+ T1_Skip_Spaces( parser );
+
+ while ( parser->root.cursor < limit )
+ {
+ FT_Byte* cur;
+
+
+ cur = parser->root.cursor;
+
+ /* look for `eexec' */
+ if ( IS_PS_TOKEN( cur, limit, "eexec" ) )
+ break;
+
+ /* look for `closefile' which ends the eexec section */
+ else if ( IS_PS_TOKEN( cur, limit, "closefile" ) )
+ break;
+
+ /* in a synthetic font the base font starts after a */
+ /* `FontDictionary' token that is placed after a Private dict */
+ else if ( IS_PS_TOKEN( cur, limit, "FontDirectory" ) )
+ {
+ if ( loader->keywords_encountered & T1_PRIVATE )
+ loader->keywords_encountered |=
+ T1_FONTDIR_AFTER_PRIVATE;
+ parser->root.cursor += 13;
+ }
+
+ /* check whether we have an integer */
+ else if ( ft_isdigit( *cur ) )
+ {
+ start_binary = cur;
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+ have_integer = 1;
+ }
+
+ /* in valid Type 1 fonts we don't see `RD' or `-|' directly */
+ /* since those tokens are handled by parse_subrs and */
+ /* parse_charstrings */
+ else if ( *cur == 'R' && cur + 6 < limit && *(cur + 1) == 'D' &&
+ have_integer )
+ {
+ FT_Long s;
+ FT_Byte* b;
+
+
+ parser->root.cursor = start_binary;
+ if ( !read_binary_data( parser, &s, &b ) )
+ return T1_Err_Invalid_File_Format;
+ have_integer = 0;
+ }
+
+ else if ( *cur == '-' && cur + 6 < limit && *(cur + 1) == '|' &&
+ have_integer )
+ {
+ FT_Long s;
+ FT_Byte* b;
+
+
+ parser->root.cursor = start_binary;
+ if ( !read_binary_data( parser, &s, &b ) )
+ return T1_Err_Invalid_File_Format;
+ have_integer = 0;
+ }
+
+ /* look for immediates */
+ else if ( *cur == '/' && cur + 2 < limit )
+ {
+ FT_PtrDist len;
+
+
+ cur++;
+
+ parser->root.cursor = cur;
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+
+ len = parser->root.cursor - cur;
+
+ if ( len > 0 && len < 22 && parser->root.cursor < limit )
+ {
+ /* now compare the immediate name to the keyword table */
+ T1_Field keyword = (T1_Field)t1_keywords;
+
+
+ for (;;)
+ {
+ FT_Byte* name;
+
+
+ name = (FT_Byte*)keyword->ident;
+ if ( !name )
+ break;
+
+ if ( cur[0] == name[0] &&
+ len == (FT_PtrDist)ft_strlen( (const char *)name ) &&
+ ft_memcmp( cur, name, len ) == 0 )
+ {
+ /* We found it -- run the parsing callback! */
+ /* We record every instance of every field */
+ /* (until we reach the base font of a */
+ /* synthetic font) to deal adequately with */
+ /* multiple master fonts; this is also */
+ /* necessary because later PostScript */
+ /* definitions override earlier ones. */
+
+ /* Once we encounter `FontDirectory' after */
+ /* `/Private', we know that this is a synthetic */
+ /* font; except for `/CharStrings' we are not */
+ /* interested in anything that follows this */
+ /* `FontDirectory'. */
+
+ /* MM fonts have more than one /Private token at */
+ /* the top level; let's hope that all the junk */
+ /* that follows the first /Private token is not */
+ /* interesting to us. */
+
+ /* According to Adobe Tech Note #5175 (CID-Keyed */
+ /* Font Installation for ATM Software) a `begin' */
+ /* must be followed by exactly one `end', and */
+ /* `begin' -- `end' pairs must be accurately */
+ /* paired. We could use this to distinguish */
+ /* between the global Private and the Private */
+ /* dict that is a member of the Blend dict. */
+
+ const FT_UInt dict =
+ ( loader->keywords_encountered & T1_PRIVATE )
+ ? T1_FIELD_DICT_PRIVATE
+ : T1_FIELD_DICT_FONTDICT;
+
+ if ( !( dict & keyword->dict ) )
+ {
+ FT_TRACE1(( "parse_dict: found %s but ignoring it "
+ "since it is in the wrong dictionary\n",
+ keyword->ident ));
+ break;
+ }
+
+ if ( !( loader->keywords_encountered &
+ T1_FONTDIR_AFTER_PRIVATE ) ||
+ ft_strcmp( (const char*)name, "CharStrings" ) == 0 )
+ {
+ parser->root.error = t1_load_keyword( face,
+ loader,
+ keyword );
+ if ( parser->root.error != T1_Err_Ok )
+ {
+ if ( FT_ERROR_BASE( parser->root.error ) == FT_Err_Ignore )
+ parser->root.error = T1_Err_Ok;
+ else
+ return parser->root.error;
+ }
+ }
+ break;
+ }
+
+ keyword++;
+ }
+ }
+
+ have_integer = 0;
+ }
+ else
+ {
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+ have_integer = 0;
+ }
+
+ T1_Skip_Spaces( parser );
+ }
+
+ Exit:
+ return parser->root.error;
+ }
+
+
+ static void
+ t1_init_loader( T1_Loader loader,
+ T1_Face face )
+ {
+ FT_UNUSED( face );
+
+ FT_MEM_ZERO( loader, sizeof ( *loader ) );
+ loader->num_glyphs = 0;
+ loader->num_chars = 0;
+
+ /* initialize the tables -- simply set their `init' field to 0 */
+ loader->encoding_table.init = 0;
+ loader->charstrings.init = 0;
+ loader->glyph_names.init = 0;
+ loader->subrs.init = 0;
+ loader->swap_table.init = 0;
+ loader->fontdata = 0;
+ loader->keywords_encountered = 0;
+ }
+
+
+ static void
+ t1_done_loader( T1_Loader loader )
+ {
+ T1_Parser parser = &loader->parser;
+
+
+ /* finalize tables */
+ T1_Release_Table( &loader->encoding_table );
+ T1_Release_Table( &loader->charstrings );
+ T1_Release_Table( &loader->glyph_names );
+ T1_Release_Table( &loader->swap_table );
+ T1_Release_Table( &loader->subrs );
+
+ /* finalize parser */
+ T1_Finalize_Parser( parser );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Open_Face( T1_Face face )
+ {
+ T1_LoaderRec loader;
+ T1_Parser parser;
+ T1_Font type1 = &face->type1;
+ PS_Private priv = &type1->private_dict;
+ FT_Error error;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ t1_init_loader( &loader, face );
+
+ /* default values */
+ face->ndv_idx = -1;
+ face->cdv_idx = -1;
+ face->len_buildchar = 0;
+
+ priv->blue_shift = 7;
+ priv->blue_fuzz = 1;
+ priv->lenIV = 4;
+ priv->expansion_factor = (FT_Fixed)( 0.06 * 0x10000L );
+ priv->blue_scale = (FT_Fixed)( 0.039625 * 0x10000L * 1000 );
+
+ parser = &loader.parser;
+ error = T1_New_Parser( parser,
+ face->root.stream,
+ face->root.memory,
+ psaux );
+ if ( error )
+ goto Exit;
+
+ error = parse_dict( face, &loader,
+ parser->base_dict, parser->base_len );
+ if ( error )
+ goto Exit;
+
+ error = T1_Get_Private_Dict( parser, psaux );
+ if ( error )
+ goto Exit;
+
+ error = parse_dict( face, &loader,
+ parser->private_dict, parser->private_len );
+ if ( error )
+ goto Exit;
+
+ /* ensure even-ness of `num_blue_values' */
+ priv->num_blue_values &= ~1;
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+
+ if ( face->blend &&
+ face->blend->num_default_design_vector != 0 &&
+ face->blend->num_default_design_vector != face->blend->num_axis )
+ {
+ /* we don't use it currently so just warn, reset, and ignore */
+ FT_ERROR(( "T1_Open_Face(): /DesignVector contains %u entries "
+ "while there are %u axes.\n",
+ face->blend->num_default_design_vector,
+ face->blend->num_axis ));
+
+ face->blend->num_default_design_vector = 0;
+ }
+
+ /* the following can happen for MM instances; we then treat the */
+ /* font as a normal PS font */
+ if ( face->blend &&
+ ( !face->blend->num_designs || !face->blend->num_axis ) )
+ T1_Done_Blend( face );
+
+ /* another safety check */
+ if ( face->blend )
+ {
+ FT_UInt i;
+
+
+ for ( i = 0; i < face->blend->num_axis; i++ )
+ if ( !face->blend->design_map[i].num_points )
+ {
+ T1_Done_Blend( face );
+ break;
+ }
+ }
+
+ if ( face->blend )
+ {
+ if ( face->len_buildchar > 0 )
+ {
+ FT_Memory memory = face->root.memory;
+
+
+ if ( FT_NEW_ARRAY( face->buildchar, face->len_buildchar ) )
+ {
+ FT_ERROR(( "T1_Open_Face: cannot allocate BuildCharArray\n" ));
+ face->len_buildchar = 0;
+ goto Exit;
+ }
+ }
+ }
+
+#endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */
+
+ /* now, propagate the subrs, charstrings, and glyphnames tables */
+ /* to the Type1 data */
+ type1->num_glyphs = loader.num_glyphs;
+
+ if ( loader.subrs.init )
+ {
+ loader.subrs.init = 0;
+ type1->num_subrs = loader.num_subrs;
+ type1->subrs_block = loader.subrs.block;
+ type1->subrs = loader.subrs.elements;
+ type1->subrs_len = loader.subrs.lengths;
+ }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+ if ( !face->root.internal->incremental_interface )
+#endif
+ if ( !loader.charstrings.init )
+ {
+ FT_ERROR(( "T1_Open_Face: no `/CharStrings' array in face!\n" ));
+ error = T1_Err_Invalid_File_Format;
+ }
+
+ loader.charstrings.init = 0;
+ type1->charstrings_block = loader.charstrings.block;
+ type1->charstrings = loader.charstrings.elements;
+ type1->charstrings_len = loader.charstrings.lengths;
+
+ /* we copy the glyph names `block' and `elements' fields; */
+ /* the `lengths' field must be released later */
+ type1->glyph_names_block = loader.glyph_names.block;
+ type1->glyph_names = (FT_String**)loader.glyph_names.elements;
+ loader.glyph_names.block = 0;
+ loader.glyph_names.elements = 0;
+
+ /* we must now build type1.encoding when we have a custom array */
+ if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY )
+ {
+ FT_Int charcode, idx, min_char, max_char;
+ FT_Byte* char_name;
+ FT_Byte* glyph_name;
+
+
+ /* OK, we do the following: for each element in the encoding */
+ /* table, look up the index of the glyph having the same name */
+ /* the index is then stored in type1.encoding.char_index, and */
+ /* a the name to type1.encoding.char_name */
+
+ min_char = +32000;
+ max_char = -32000;
+
+ charcode = 0;
+ for ( ; charcode < loader.encoding_table.max_elems; charcode++ )
+ {
+ type1->encoding.char_index[charcode] = 0;
+ type1->encoding.char_name [charcode] = (char *)".notdef";
+
+ char_name = loader.encoding_table.elements[charcode];
+ if ( char_name )
+ for ( idx = 0; idx < type1->num_glyphs; idx++ )
+ {
+ glyph_name = (FT_Byte*)type1->glyph_names[idx];
+ if ( ft_strcmp( (const char*)char_name,
+ (const char*)glyph_name ) == 0 )
+ {
+ type1->encoding.char_index[charcode] = (FT_UShort)idx;
+ type1->encoding.char_name [charcode] = (char*)glyph_name;
+
+ /* Change min/max encoded char only if glyph name is */
+ /* not /.notdef */
+ if ( ft_strcmp( (const char*)".notdef",
+ (const char*)glyph_name ) != 0 )
+ {
+ if ( charcode < min_char )
+ min_char = charcode;
+ if ( charcode > max_char )
+ max_char = charcode;
+ }
+ break;
+ }
+ }
+ }
+
+ /*
+ * Yes, this happens: Certain PDF-embedded fonts have only a
+ * `.notdef' glyph defined!
+ */
+
+ if ( min_char > max_char )
+ {
+ min_char = 0;
+ max_char = loader.encoding_table.max_elems;
+ }
+
+ type1->encoding.code_first = min_char;
+ type1->encoding.code_last = max_char;
+ type1->encoding.num_chars = loader.num_chars;
+ }
+
+ Exit:
+ t1_done_loader( &loader );
+ return error;
+ }
+
+
+/* END */
diff --git a/src/type1/t1load.h b/src/type1/t1load.h
new file mode 100644
index 0000000..546fc33
--- /dev/null
+++ b/src/type1/t1load.h
@@ -0,0 +1,102 @@
+/***************************************************************************/
+/* */
+/* t1load.h */
+/* */
+/* Type 1 font loader (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1LOAD_H__
+#define __T1LOAD_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_MULTIPLE_MASTERS_H
+
+#include "t1parse.h"
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct T1_Loader_
+ {
+ T1_ParserRec parser; /* parser used to read the stream */
+
+ FT_Int num_chars; /* number of characters in encoding */
+ PS_TableRec encoding_table; /* PS_Table used to store the */
+ /* encoding character names */
+
+ FT_Int num_glyphs;
+ PS_TableRec glyph_names;
+ PS_TableRec charstrings;
+ PS_TableRec swap_table; /* For moving .notdef glyph to index 0. */
+
+ FT_Int num_subrs;
+ PS_TableRec subrs;
+ FT_Bool fontdata;
+
+ FT_UInt keywords_encountered; /* T1_LOADER_ENCOUNTERED_XXX */
+
+ } T1_LoaderRec, *T1_Loader;
+
+
+ /* treatment of some keywords differs depending on whether */
+ /* they precede or follow certain other keywords */
+
+#define T1_PRIVATE ( 1 << 0 )
+#define T1_FONTDIR_AFTER_PRIVATE ( 1 << 1 )
+
+
+ FT_LOCAL( FT_Error )
+ T1_Open_Face( T1_Face face );
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+
+ FT_LOCAL( FT_Error )
+ T1_Get_Multi_Master( T1_Face face,
+ FT_Multi_Master* master );
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Get_MM_Var( T1_Face face,
+ FT_MM_Var* *master );
+
+ FT_LOCAL( FT_Error )
+ T1_Set_MM_Blend( T1_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
+
+ FT_LOCAL( FT_Error )
+ T1_Set_MM_Design( T1_Face face,
+ FT_UInt num_coords,
+ FT_Long* coords );
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Set_Var_Design( T1_Face face,
+ FT_UInt num_coords,
+ FT_Fixed* coords );
+
+ FT_LOCAL( void )
+ T1_Done_Blend( T1_Face face );
+
+#endif /* !T1_CONFIG_OPTION_NO_MM_SUPPORT */
+
+
+FT_END_HEADER
+
+#endif /* __T1LOAD_H__ */
+
+
+/* END */
diff --git a/src/type1/t1objs.c b/src/type1/t1objs.c
new file mode 100644
index 0000000..3d08336
--- /dev/null
+++ b/src/type1/t1objs.c
@@ -0,0 +1,566 @@
+/***************************************************************************/
+/* */
+/* t1objs.c */
+/* */
+/* Type 1 objects manager (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_IDS_H
+
+#include "t1gload.h"
+#include "t1load.h"
+
+#include "t1errors.h"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "t1afm.h"
+#endif
+
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1objs
+
+
+ /*************************************************************************/
+ /* */
+ /* SIZE FUNCTIONS */
+ /* */
+ /* note that we store the global hints in the size's "internal" root */
+ /* field */
+ /* */
+ /*************************************************************************/
+
+
+ static PSH_Globals_Funcs
+ T1_Size_Get_Globals_Funcs( T1_Size size )
+ {
+ T1_Face face = (T1_Face)size->root.face;
+ PSHinter_Service pshinter = (PSHinter_Service)face->pshinter;
+ FT_Module module;
+
+
+ module = FT_Get_Module( size->root.face->driver->root.library,
+ "pshinter" );
+ return ( module && pshinter && pshinter->get_globals_funcs )
+ ? pshinter->get_globals_funcs( module )
+ : 0 ;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T1_Size_Done( T1_Size size )
+ {
+ if ( size->root.internal )
+ {
+ PSH_Globals_Funcs funcs;
+
+
+ funcs = T1_Size_Get_Globals_Funcs( size );
+ if ( funcs )
+ funcs->destroy( (PSH_Globals)size->root.internal );
+
+ size->root.internal = 0;
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Size_Init( T1_Size size )
+ {
+ FT_Error error = 0;
+ PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size );
+
+
+ if ( funcs )
+ {
+ PSH_Globals globals;
+ T1_Face face = (T1_Face)size->root.face;
+
+
+ error = funcs->create( size->root.face->memory,
+ &face->type1.private_dict, &globals );
+ if ( !error )
+ size->root.internal = (FT_Size_Internal)(void*)globals;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Size_Request( T1_Size size,
+ FT_Size_Request req )
+ {
+ PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size );
+
+
+ FT_Request_Metrics( size->root.face, req );
+
+ if ( funcs )
+ funcs->set_scale( (PSH_Globals)size->root.internal,
+ size->root.metrics.x_scale,
+ size->root.metrics.y_scale,
+ 0, 0 );
+
+ return T1_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* SLOT FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+ FT_LOCAL_DEF( void )
+ T1_GlyphSlot_Done( T1_GlyphSlot slot )
+ {
+ slot->root.internal->glyph_hints = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_GlyphSlot_Init( T1_GlyphSlot slot )
+ {
+ T1_Face face;
+ PSHinter_Service pshinter;
+
+
+ face = (T1_Face)slot->root.face;
+ pshinter = (PSHinter_Service)face->pshinter;
+
+ if ( pshinter )
+ {
+ FT_Module module;
+
+
+ module = FT_Get_Module( slot->root.face->driver->root.library, "pshinter" );
+ if (module)
+ {
+ T1_Hints_Funcs funcs;
+
+ funcs = pshinter->get_t1_funcs( module );
+ slot->root.internal->glyph_hints = (void*)funcs;
+ }
+ }
+ return 0;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* FACE FUNCTIONS */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* T1_Face_Done */
+ /* */
+ /* <Description> */
+ /* The face object destructor. */
+ /* */
+ /* <Input> */
+ /* face :: A typeless pointer to the face object to destroy. */
+ /* */
+ FT_LOCAL_DEF( void )
+ T1_Face_Done( T1_Face face )
+ {
+ if ( face )
+ {
+ FT_Memory memory = face->root.memory;
+ T1_Font type1 = &face->type1;
+
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+ /* release multiple masters information */
+ FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );
+
+ if ( face->buildchar )
+ {
+ FT_FREE( face->buildchar );
+
+ face->buildchar = NULL;
+ face->len_buildchar = 0;
+ }
+
+ T1_Done_Blend( face );
+ face->blend = 0;
+#endif
+
+ /* release font info strings */
+ {
+ PS_FontInfo info = &type1->font_info;
+
+
+ FT_FREE( info->version );
+ FT_FREE( info->notice );
+ FT_FREE( info->full_name );
+ FT_FREE( info->family_name );
+ FT_FREE( info->weight );
+ }
+
+ /* release top dictionary */
+ FT_FREE( type1->charstrings_len );
+ FT_FREE( type1->charstrings );
+ FT_FREE( type1->glyph_names );
+
+ FT_FREE( type1->subrs );
+ FT_FREE( type1->subrs_len );
+
+ FT_FREE( type1->subrs_block );
+ FT_FREE( type1->charstrings_block );
+ FT_FREE( type1->glyph_names_block );
+
+ FT_FREE( type1->encoding.char_index );
+ FT_FREE( type1->encoding.char_name );
+ FT_FREE( type1->font_name );
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+ /* release afm data if present */
+ if ( face->afm_data )
+ T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );
+#endif
+
+ /* release unicode map, if any */
+#if 0
+ FT_FREE( face->unicode_map_rec.maps );
+ face->unicode_map_rec.num_maps = 0;
+ face->unicode_map = NULL;
+#endif
+
+ face->root.family_name = 0;
+ face->root.style_name = 0;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* T1_Face_Init */
+ /* */
+ /* <Description> */
+ /* The face object constructor. */
+ /* */
+ /* <Input> */
+ /* stream :: input stream where to load font data. */
+ /* */
+ /* face_index :: The index of the font face in the resource. */
+ /* */
+ /* num_params :: Number of additional generic parameters. Ignored. */
+ /* */
+ /* params :: Additional generic parameters. Ignored. */
+ /* */
+ /* <InOut> */
+ /* face :: The face record to build. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ T1_Face_Init( FT_Stream stream,
+ T1_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Service_PsCMaps psnames;
+ PSAux_Service psaux;
+ T1_Font type1 = &face->type1;
+ PS_FontInfo info = &type1->font_info;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+ FT_UNUSED( face_index );
+ FT_UNUSED( stream );
+
+
+ face->root.num_faces = 1;
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ face->psnames = psnames;
+
+ face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
+ "psaux" );
+ psaux = (PSAux_Service)face->psaux;
+
+ face->pshinter = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
+ "pshinter" );
+
+ /* open the tokenizer; this will also check the font format */
+ error = T1_Open_Face( face );
+ if ( error )
+ goto Exit;
+
+ /* if we just wanted to check the format, leave successfully now */
+ if ( face_index < 0 )
+ goto Exit;
+
+ /* check the face index */
+ if ( face_index != 0 )
+ {
+ FT_ERROR(( "T1_Face_Init: invalid face index\n" ));
+ error = T1_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* now load the font program into the face object */
+
+ /* initialize the face object fields */
+
+ /* set up root face fields */
+ {
+ FT_Face root = (FT_Face)&face->root;
+
+
+ root->num_glyphs = type1->num_glyphs;
+ root->face_index = face_index;
+
+ root->face_flags = FT_FACE_FLAG_SCALABLE |
+ FT_FACE_FLAG_HORIZONTAL |
+ FT_FACE_FLAG_GLYPH_NAMES |
+ FT_FACE_FLAG_HINTER;
+
+ if ( info->is_fixed_pitch )
+ root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ if ( face->blend )
+ root->face_flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
+
+ /* XXX: TODO -- add kerning with .afm support */
+
+ /* get style name -- be careful, some broken fonts only */
+ /* have a `/FontName' dictionary entry! */
+ root->family_name = info->family_name;
+ /* assume "Regular" style if we don't know better */
+ root->style_name = (char *)"Regular";
+ if ( root->family_name )
+ {
+ char* full = info->full_name;
+ char* family = root->family_name;
+
+
+ if ( full )
+ {
+ while ( *full )
+ {
+ if ( *full == *family )
+ {
+ family++;
+ full++;
+ }
+ else
+ {
+ if ( *full == ' ' || *full == '-' )
+ full++;
+ else if ( *family == ' ' || *family == '-' )
+ family++;
+ else
+ {
+ if ( !*family )
+ root->style_name = full;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* do we have a `/FontName'? */
+ if ( type1->font_name )
+ root->family_name = type1->font_name;
+ }
+
+ /* compute style flags */
+ root->style_flags = 0;
+ if ( info->italic_angle )
+ root->style_flags |= FT_STYLE_FLAG_ITALIC;
+ if ( info->weight )
+ {
+ if ( !ft_strcmp( info->weight, "Bold" ) ||
+ !ft_strcmp( info->weight, "Black" ) )
+ root->style_flags |= FT_STYLE_FLAG_BOLD;
+ }
+
+ /* no embedded bitmap support */
+ root->num_fixed_sizes = 0;
+ root->available_sizes = 0;
+
+ root->bbox.xMin = type1->font_bbox.xMin >> 16;
+ root->bbox.yMin = type1->font_bbox.yMin >> 16;
+ root->bbox.xMax = ( type1->font_bbox.xMax + 0xFFFFU ) >> 16;
+ root->bbox.yMax = ( type1->font_bbox.yMax + 0xFFFFU ) >> 16;
+
+ /* Set units_per_EM if we didn't set it in parse_font_matrix. */
+ if ( !root->units_per_EM )
+ root->units_per_EM = 1000;
+
+ root->ascender = (FT_Short)( root->bbox.yMax );
+ root->descender = (FT_Short)( root->bbox.yMin );
+
+ root->height = (FT_Short)( ( root->units_per_EM * 12 ) / 10 );
+ if ( root->height < root->ascender - root->descender )
+ root->height = (FT_Short)( root->ascender - root->descender );
+
+ /* now compute the maximum advance width */
+ root->max_advance_width =
+ (FT_Short)( root->bbox.xMax );
+ {
+ FT_Pos max_advance;
+
+
+ error = T1_Compute_Max_Advance( face, &max_advance );
+
+ /* in case of error, keep the standard width */
+ if ( !error )
+ root->max_advance_width = (FT_Short)max_advance;
+ else
+ error = 0; /* clear error */
+ }
+
+ root->max_advance_height = root->height;
+
+ root->underline_position = (FT_Short)info->underline_position;
+ root->underline_thickness = (FT_Short)info->underline_thickness;
+ }
+
+ {
+ FT_Face root = &face->root;
+
+
+ if ( psnames && psaux )
+ {
+ FT_CharMapRec charmap;
+ T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes;
+ FT_CMap_Class clazz;
+
+
+ charmap.face = root;
+
+ /* first of all, try to synthetize a Unicode charmap */
+ charmap.platform_id = 3;
+ charmap.encoding_id = 1;
+ charmap.encoding = FT_ENCODING_UNICODE;
+
+ FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL );
+
+ /* now, generate an Adobe Standard encoding when appropriate */
+ charmap.platform_id = 7;
+ clazz = NULL;
+
+ switch ( type1->encoding_type )
+ {
+ case T1_ENCODING_TYPE_STANDARD:
+ charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
+ charmap.encoding_id = TT_ADOBE_ID_STANDARD;
+ clazz = cmap_classes->standard;
+ break;
+
+ case T1_ENCODING_TYPE_EXPERT:
+ charmap.encoding = FT_ENCODING_ADOBE_EXPERT;
+ charmap.encoding_id = TT_ADOBE_ID_EXPERT;
+ clazz = cmap_classes->expert;
+ break;
+
+ case T1_ENCODING_TYPE_ARRAY:
+ charmap.encoding = FT_ENCODING_ADOBE_CUSTOM;
+ charmap.encoding_id = TT_ADOBE_ID_CUSTOM;
+ clazz = cmap_classes->custom;
+ break;
+
+ case T1_ENCODING_TYPE_ISOLATIN1:
+ charmap.encoding = FT_ENCODING_ADOBE_LATIN_1;
+ charmap.encoding_id = TT_ADOBE_ID_LATIN_1;
+ clazz = cmap_classes->unicode;
+ break;
+
+ default:
+ ;
+ }
+
+ if ( clazz )
+ FT_CMap_New( clazz, NULL, &charmap, NULL );
+
+#if 0
+ /* Select default charmap */
+ if (root->num_charmaps)
+ root->charmap = root->charmaps[0];
+#endif
+ }
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* T1_Driver_Init */
+ /* */
+ /* <Description> */
+ /* Initializes a given Type 1 driver object. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target driver object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ T1_Driver_Init( T1_Driver driver )
+ {
+ FT_UNUSED( driver );
+
+ return T1_Err_Ok;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* T1_Driver_Done */
+ /* */
+ /* <Description> */
+ /* Finalizes a given Type 1 driver. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target Type 1 driver. */
+ /* */
+ FT_LOCAL_DEF( void )
+ T1_Driver_Done( T1_Driver driver )
+ {
+ FT_UNUSED( driver );
+ }
+
+
+/* END */
diff --git a/src/type1/t1objs.h b/src/type1/t1objs.h
new file mode 100644
index 0000000..e5e9029
--- /dev/null
+++ b/src/type1/t1objs.h
@@ -0,0 +1,171 @@
+/***************************************************************************/
+/* */
+/* t1objs.h */
+/* */
+/* Type 1 objects manager (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1OBJS_H__
+#define __T1OBJS_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_OBJECTS_H
+#include FT_CONFIG_CONFIG_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+ /* The following structures must be defined by the hinter */
+ typedef struct T1_Size_Hints_ T1_Size_Hints;
+ typedef struct T1_Glyph_Hints_ T1_Glyph_Hints;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_Driver */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 driver object. */
+ /* */
+ typedef struct T1_DriverRec_ *T1_Driver;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_Size */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 size object. */
+ /* */
+ typedef struct T1_SizeRec_* T1_Size;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_GlyphSlot */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 glyph slot object. */
+ /* */
+ typedef struct T1_GlyphSlotRec_* T1_GlyphSlot;
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_CharMap */
+ /* */
+ /* <Description> */
+ /* A handle to a Type 1 character mapping object. */
+ /* */
+ /* <Note> */
+ /* The Type 1 format doesn't use a charmap but an encoding table. */
+ /* The driver is responsible for making up charmap objects */
+ /* corresponding to these tables. */
+ /* */
+ typedef struct T1_CharMapRec_* T1_CharMap;
+
+
+ /*************************************************************************/
+ /* */
+ /* HERE BEGINS THE TYPE1 SPECIFIC STUFF */
+ /* */
+ /*************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_SizeRec */
+ /* */
+ /* <Description> */
+ /* Type 1 size record. */
+ /* */
+ typedef struct T1_SizeRec_
+ {
+ FT_SizeRec root;
+
+ } T1_SizeRec;
+
+
+ FT_LOCAL( void )
+ T1_Size_Done( T1_Size size );
+
+ FT_LOCAL( FT_Error )
+ T1_Size_Request( T1_Size size,
+ FT_Size_Request req );
+
+ FT_LOCAL( FT_Error )
+ T1_Size_Init( T1_Size size );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Type> */
+ /* T1_GlyphSlotRec */
+ /* */
+ /* <Description> */
+ /* Type 1 glyph slot record. */
+ /* */
+ typedef struct T1_GlyphSlotRec_
+ {
+ FT_GlyphSlotRec root;
+
+ FT_Bool hint;
+ FT_Bool scaled;
+
+ FT_Int max_points;
+ FT_Int max_contours;
+
+ FT_Fixed x_scale;
+ FT_Fixed y_scale;
+
+ } T1_GlyphSlotRec;
+
+
+ FT_LOCAL( FT_Error )
+ T1_Face_Init( FT_Stream stream,
+ T1_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+ FT_LOCAL( void )
+ T1_Face_Done( T1_Face face );
+
+ FT_LOCAL( FT_Error )
+ T1_GlyphSlot_Init( T1_GlyphSlot slot );
+
+ FT_LOCAL( void )
+ T1_GlyphSlot_Done( T1_GlyphSlot slot );
+
+ FT_LOCAL( FT_Error )
+ T1_Driver_Init( T1_Driver driver );
+
+ FT_LOCAL( void )
+ T1_Driver_Done( T1_Driver driver );
+
+
+FT_END_HEADER
+
+#endif /* __T1OBJS_H__ */
+
+
+/* END */
diff --git a/src/type1/t1parse.c b/src/type1/t1parse.c
new file mode 100644
index 0000000..1b252c7
--- /dev/null
+++ b/src/type1/t1parse.c
@@ -0,0 +1,479 @@
+/***************************************************************************/
+/* */
+/* t1parse.c */
+/* */
+/* Type 1 parser (body). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2005 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* The Type 1 parser is in charge of the following: */
+ /* */
+ /* - provide an implementation of a growing sequence of objects called */
+ /* a `T1_Table' (used to build various tables needed by the loader). */
+ /* */
+ /* - opening .pfb and .pfa files to extract their top-level and private */
+ /* dictionaries. */
+ /* */
+ /* - read numbers, arrays & strings from any dictionary. */
+ /* */
+ /* See `t1load.c' to see how data is loaded from the font file. */
+ /* */
+ /*************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+#include "t1parse.h"
+
+#include "t1errors.h"
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t1parse
+
+
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+ /***** *****/
+ /***** INPUT STREAM PARSER *****/
+ /***** *****/
+ /*************************************************************************/
+ /*************************************************************************/
+ /*************************************************************************/
+
+
+ static FT_Error
+ read_pfb_tag( FT_Stream stream,
+ FT_UShort *atag,
+ FT_Long *asize )
+ {
+ FT_Error error;
+ FT_UShort tag;
+ FT_Long size;
+
+
+ *atag = 0;
+ *asize = 0;
+
+ if ( !FT_READ_USHORT( tag ) )
+ {
+ if ( tag == 0x8001U || tag == 0x8002U )
+ {
+ if ( !FT_READ_LONG_LE( size ) )
+ *asize = size;
+ }
+
+ *atag = tag;
+ }
+
+ return error;
+ }
+
+
+ static FT_Error
+ check_type1_format( FT_Stream stream,
+ const char* header_string,
+ size_t header_length )
+ {
+ FT_Error error;
+ FT_UShort tag;
+ FT_Long size;
+
+
+ if ( FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ error = read_pfb_tag( stream, &tag, &size );
+ if ( error )
+ goto Exit;
+
+ if ( tag != 0x8001U && FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ if ( !FT_FRAME_ENTER( header_length ) )
+ {
+ error = 0;
+
+ if ( ft_memcmp( stream->cursor, header_string, header_length ) != 0 )
+ error = T1_Err_Unknown_File_Format;
+
+ FT_FRAME_EXIT();
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_New_Parser( T1_Parser parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux )
+ {
+ FT_Error error;
+ FT_UShort tag;
+ FT_Long size;
+
+
+ psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );
+
+ parser->stream = stream;
+ parser->base_len = 0;
+ parser->base_dict = 0;
+ parser->private_len = 0;
+ parser->private_dict = 0;
+ parser->in_pfb = 0;
+ parser->in_memory = 0;
+ parser->single_block = 0;
+
+ /* check the header format */
+ error = check_type1_format( stream, "%!PS-AdobeFont", 14 );
+ if ( error )
+ {
+ if ( error != T1_Err_Unknown_File_Format )
+ goto Exit;
+
+ error = check_type1_format( stream, "%!FontType", 10 );
+ if ( error )
+ {
+ FT_TRACE2(( "[not a Type1 font]\n" ));
+ goto Exit;
+ }
+ }
+
+ /******************************************************************/
+ /* */
+ /* Here a short summary of what is going on: */
+ /* */
+ /* When creating a new Type 1 parser, we try to locate and load */
+ /* the base dictionary if this is possible (i.e. for PFB */
+ /* files). Otherwise, we load the whole font into memory. */
+ /* */
+ /* When `loading' the base dictionary, we only setup pointers */
+ /* in the case of a memory-based stream. Otherwise, we */
+ /* allocate and load the base dictionary in it. */
+ /* */
+ /* parser->in_pfb is set if we are in a binary (".pfb") font. */
+ /* parser->in_memory is set if we have a memory stream. */
+ /* */
+
+ /* try to compute the size of the base dictionary; */
+ /* look for a Postscript binary file tag, i.e 0x8001 */
+ if ( FT_STREAM_SEEK( 0L ) )
+ goto Exit;
+
+ error = read_pfb_tag( stream, &tag, &size );
+ if ( error )
+ goto Exit;
+
+ if ( tag != 0x8001U )
+ {
+ /* assume that this is a PFA file for now; an error will */
+ /* be produced later when more things are checked */
+ if ( FT_STREAM_SEEK( 0L ) )
+ goto Exit;
+ size = stream->size;
+ }
+ else
+ parser->in_pfb = 1;
+
+ /* now, try to load `size' bytes of the `base' dictionary we */
+ /* found previously */
+
+ /* if it is a memory-based resource, set up pointers */
+ if ( !stream->read )
+ {
+ parser->base_dict = (FT_Byte*)stream->base + stream->pos;
+ parser->base_len = size;
+ parser->in_memory = 1;
+
+ /* check that the `size' field is valid */
+ if ( FT_STREAM_SKIP( size ) )
+ goto Exit;
+ }
+ else
+ {
+ /* read segment in memory - this is clumsy, but so does the format */
+ if ( FT_ALLOC( parser->base_dict, size ) ||
+ FT_STREAM_READ( parser->base_dict, size ) )
+ goto Exit;
+ parser->base_len = size;
+ }
+
+ parser->root.base = parser->base_dict;
+ parser->root.cursor = parser->base_dict;
+ parser->root.limit = parser->root.cursor + parser->base_len;
+
+ Exit:
+ if ( error && !parser->in_memory )
+ FT_FREE( parser->base_dict );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T1_Finalize_Parser( T1_Parser parser )
+ {
+ FT_Memory memory = parser->root.memory;
+
+
+ /* always free the private dictionary */
+ FT_FREE( parser->private_dict );
+
+ /* free the base dictionary only when we have a disk stream */
+ if ( !parser->in_memory )
+ FT_FREE( parser->base_dict );
+
+ parser->root.funcs.done( &parser->root );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T1_Get_Private_Dict( T1_Parser parser,
+ PSAux_Service psaux )
+ {
+ FT_Stream stream = parser->stream;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error = T1_Err_Ok;
+ FT_Long size;
+
+
+ if ( parser->in_pfb )
+ {
+ /* in the case of the PFB format, the private dictionary can be */
+ /* made of several segments. We thus first read the number of */
+ /* segments to compute the total size of the private dictionary */
+ /* then re-read them into memory. */
+ FT_Long start_pos = FT_STREAM_POS();
+ FT_UShort tag;
+
+
+ parser->private_len = 0;
+ for (;;)
+ {
+ error = read_pfb_tag( stream, &tag, &size );
+ if ( error )
+ goto Fail;
+
+ if ( tag != 0x8002U )
+ break;
+
+ parser->private_len += size;
+
+ if ( FT_STREAM_SKIP( size ) )
+ goto Fail;
+ }
+
+ /* Check that we have a private dictionary there */
+ /* and allocate private dictionary buffer */
+ if ( parser->private_len == 0 )
+ {
+ FT_ERROR(( "T1_Get_Private_Dict:" ));
+ FT_ERROR(( " invalid private dictionary section\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ if ( FT_STREAM_SEEK( start_pos ) ||
+ FT_ALLOC( parser->private_dict, parser->private_len ) )
+ goto Fail;
+
+ parser->private_len = 0;
+ for (;;)
+ {
+ error = read_pfb_tag( stream, &tag, &size );
+ if ( error || tag != 0x8002U )
+ {
+ error = T1_Err_Ok;
+ break;
+ }
+
+ if ( FT_STREAM_READ( parser->private_dict + parser->private_len,
+ size ) )
+ goto Fail;
+
+ parser->private_len += size;
+ }
+ }
+ else
+ {
+ /* We have already `loaded' the whole PFA font file into memory; */
+ /* if this is a memory resource, allocate a new block to hold */
+ /* the private dict. Otherwise, simply overwrite into the base */
+ /* dictionary block in the heap. */
+
+ /* first of all, look at the `eexec' keyword */
+ FT_Byte* cur = parser->base_dict;
+ FT_Byte* limit = cur + parser->base_len;
+ FT_Byte c;
+
+
+ Again:
+ for (;;)
+ {
+ c = cur[0];
+ if ( c == 'e' && cur + 9 < limit ) /* 9 = 5 letters for `eexec' + */
+ /* newline + 4 chars */
+ {
+ if ( cur[1] == 'e' &&
+ cur[2] == 'x' &&
+ cur[3] == 'e' &&
+ cur[4] == 'c' )
+ break;
+ }
+ cur++;
+ if ( cur >= limit )
+ {
+ FT_ERROR(( "T1_Get_Private_Dict:" ));
+ FT_ERROR(( " could not find `eexec' keyword\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+ }
+
+ /* check whether `eexec' was real -- it could be in a comment */
+ /* or string (as e.g. in u003043t.gsf from ghostscript) */
+
+ parser->root.cursor = parser->base_dict;
+ parser->root.limit = cur + 9;
+
+ cur = parser->root.cursor;
+ limit = parser->root.limit;
+
+ while ( cur < limit )
+ {
+ if ( *cur == 'e' && ft_strncmp( (char*)cur, "eexec", 5 ) == 0 )
+ goto Found;
+
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ break;
+ T1_Skip_Spaces ( parser );
+ cur = parser->root.cursor;
+ }
+
+ /* we haven't found the correct `eexec'; go back and continue */
+ /* searching */
+
+ cur = limit;
+ limit = parser->base_dict + parser->base_len;
+ goto Again;
+
+ /* now determine where to write the _encrypted_ binary private */
+ /* dictionary. We overwrite the base dictionary for disk-based */
+ /* resources and allocate a new block otherwise */
+
+ Found:
+ parser->root.limit = parser->base_dict + parser->base_len;
+
+ T1_Skip_PS_Token( parser );
+ cur = parser->root.cursor;
+ if ( *cur == '\r' )
+ {
+ cur++;
+ if ( *cur == '\n' )
+ cur++;
+ }
+ else if ( *cur == '\n' )
+ cur++;
+ else
+ {
+ FT_ERROR(( "T1_Get_Private_Dict:" ));
+ FT_ERROR(( " `eexec' not properly terminated\n" ));
+ error = T1_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ size = (FT_Long)( parser->base_len - ( cur - parser->base_dict ) );
+
+ if ( parser->in_memory )
+ {
+ /* note that we allocate one more byte to put a terminating `0' */
+ if ( FT_ALLOC( parser->private_dict, size + 1 ) )
+ goto Fail;
+ parser->private_len = size;
+ }
+ else
+ {
+ parser->single_block = 1;
+ parser->private_dict = parser->base_dict;
+ parser->private_len = size;
+ parser->base_dict = 0;
+ parser->base_len = 0;
+ }
+
+ /* now determine whether the private dictionary is encoded in binary */
+ /* or hexadecimal ASCII format -- decode it accordingly */
+
+ /* we need to access the next 4 bytes (after the final \r following */
+ /* the `eexec' keyword); if they all are hexadecimal digits, then */
+ /* we have a case of ASCII storage */
+
+ if ( ft_isxdigit( cur[0] ) && ft_isxdigit( cur[1] ) &&
+ ft_isxdigit( cur[2] ) && ft_isxdigit( cur[3] ) )
+ {
+ /* ASCII hexadecimal encoding */
+ FT_Long len;
+
+
+ parser->root.cursor = cur;
+ (void)psaux->ps_parser_funcs->to_bytes( &parser->root,
+ parser->private_dict,
+ parser->private_len,
+ &len,
+ 0 );
+ parser->private_len = len;
+
+ /* put a safeguard */
+ parser->private_dict[len] = '\0';
+ }
+ else
+ /* binary encoding -- copy the private dict */
+ FT_MEM_MOVE( parser->private_dict, cur, size );
+ }
+
+ /* we now decrypt the encoded binary private dictionary */
+ psaux->t1_decrypt( parser->private_dict, parser->private_len, 55665U );
+
+ /* replace the four random bytes at the beginning with whitespace */
+ parser->private_dict[0] = ' ';
+ parser->private_dict[1] = ' ';
+ parser->private_dict[2] = ' ';
+ parser->private_dict[3] = ' ';
+
+ parser->root.base = parser->private_dict;
+ parser->root.cursor = parser->private_dict;
+ parser->root.limit = parser->root.cursor + parser->private_len;
+
+ Fail:
+ Exit:
+ return error;
+ }
+
+
+/* END */
diff --git a/src/type1/t1parse.h b/src/type1/t1parse.h
new file mode 100644
index 0000000..6fa4ca6
--- /dev/null
+++ b/src/type1/t1parse.h
@@ -0,0 +1,135 @@
+/***************************************************************************/
+/* */
+/* t1parse.h */
+/* */
+/* Type 1 parser (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T1PARSE_H__
+#define __T1PARSE_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_TYPE1_TYPES_H
+#include FT_INTERNAL_STREAM_H
+
+
+FT_BEGIN_HEADER
+
+
+ /*************************************************************************/
+ /* */
+ /* <Struct> */
+ /* T1_ParserRec */
+ /* */
+ /* <Description> */
+ /* A PS_ParserRec is an object used to parse a Type 1 fonts very */
+ /* quickly. */
+ /* */
+ /* <Fields> */
+ /* root :: The root parser. */
+ /* */
+ /* stream :: The current input stream. */
+ /* */
+ /* base_dict :: A pointer to the top-level dictionary. */
+ /* */
+ /* base_len :: The length in bytes of the top dictionary. */
+ /* */
+ /* private_dict :: A pointer to the private dictionary. */
+ /* */
+ /* private_len :: The length in bytes of the private dictionary. */
+ /* */
+ /* in_pfb :: A boolean. Indicates that we are handling a PFB */
+ /* file. */
+ /* */
+ /* in_memory :: A boolean. Indicates a memory-based stream. */
+ /* */
+ /* single_block :: A boolean. Indicates that the private dictionary */
+ /* is stored in lieu of the base dictionary. */
+ /* */
+ typedef struct T1_ParserRec_
+ {
+ PS_ParserRec root;
+ FT_Stream stream;
+
+ FT_Byte* base_dict;
+ FT_Long base_len;
+
+ FT_Byte* private_dict;
+ FT_Long private_len;
+
+ FT_Bool in_pfb;
+ FT_Bool in_memory;
+ FT_Bool single_block;
+
+ } T1_ParserRec, *T1_Parser;
+
+
+#define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l )
+#define T1_Done_Table( p ) \
+ do \
+ { \
+ if ( (p)->funcs.done ) \
+ (p)->funcs.done( p ); \
+ } while ( 0 )
+#define T1_Release_Table( p ) \
+ do \
+ { \
+ if ( (p)->funcs.release ) \
+ (p)->funcs.release( p ); \
+ } while ( 0 )
+
+
+#define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root )
+#define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root )
+
+#define T1_ToInt( p ) (p)->root.funcs.to_int( &(p)->root )
+#define T1_ToFixed( p, t ) (p)->root.funcs.to_fixed( &(p)->root, t )
+
+#define T1_ToCoordArray( p, m, c ) \
+ (p)->root.funcs.to_coord_array( &(p)->root, m, c )
+#define T1_ToFixedArray( p, m, f, t ) \
+ (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t )
+#define T1_ToToken( p, t ) \
+ (p)->root.funcs.to_token( &(p)->root, t )
+#define T1_ToTokenArray( p, t, m, c ) \
+ (p)->root.funcs.to_token_array( &(p)->root, t, m, c )
+
+#define T1_Load_Field( p, f, o, m, pf ) \
+ (p)->root.funcs.load_field( &(p)->root, f, o, m, pf )
+
+#define T1_Load_Field_Table( p, f, o, m, pf ) \
+ (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf )
+
+
+ FT_LOCAL( FT_Error )
+ T1_New_Parser( T1_Parser parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux );
+
+ FT_LOCAL( FT_Error )
+ T1_Get_Private_Dict( T1_Parser parser,
+ PSAux_Service psaux );
+
+ FT_LOCAL( void )
+ T1_Finalize_Parser( T1_Parser parser );
+
+
+FT_END_HEADER
+
+#endif /* __T1PARSE_H__ */
+
+
+/* END */
diff --git a/src/type1/t1tokens.h b/src/type1/t1tokens.h
new file mode 100644
index 0000000..788c811
--- /dev/null
+++ b/src/type1/t1tokens.h
@@ -0,0 +1,134 @@
+/***************************************************************************/
+/* */
+/* t1tokens.h */
+/* */
+/* Type 1 tokenizer (specification). */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PS_FontInfoRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_INFO
+
+ T1_FIELD_STRING( "version", version,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_STRING( "Notice", notice,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_STRING( "FullName", full_name,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_STRING( "FamilyName", family_name,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_STRING( "Weight", weight,
+ T1_FIELD_DICT_FONTDICT )
+
+ /* we use pointers to detect modifications made by synthetic fonts */
+ T1_FIELD_NUM ( "ItalicAngle", italic_angle,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_NUM ( "UnderlinePosition", underline_position,
+ T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_NUM ( "UnderlineThickness", underline_thickness,
+ T1_FIELD_DICT_FONTDICT )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PS_PrivateRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_PRIVATE
+
+ T1_FIELD_NUM ( "UniqueID", unique_id,
+ T1_FIELD_DICT_FONTDICT | T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM ( "lenIV", lenIV,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM ( "LanguageGroup", language_group,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM ( "password", password,
+ T1_FIELD_DICT_PRIVATE )
+
+ T1_FIELD_FIXED_1000( "BlueScale", blue_scale,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM ( "BlueShift", blue_shift,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM ( "BlueFuzz", blue_fuzz,
+ T1_FIELD_DICT_PRIVATE )
+
+ T1_FIELD_NUM_TABLE ( "BlueValues", blue_values, 14,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE ( "OtherBlues", other_blues, 10,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE ( "FamilyBlues", family_blues, 14,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE ( "FamilyOtherBlues", family_other_blues, 10,
+ T1_FIELD_DICT_PRIVATE )
+
+ T1_FIELD_NUM_TABLE2( "StdHW", standard_width, 1,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE2( "StdVW", standard_height, 1,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE2( "MinFeature", min_feature, 2,
+ T1_FIELD_DICT_PRIVATE )
+
+ T1_FIELD_NUM_TABLE ( "StemSnapH", snap_widths, 12,
+ T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM_TABLE ( "StemSnapV", snap_heights, 12,
+ T1_FIELD_DICT_PRIVATE )
+
+ T1_FIELD_FIXED ( "ExpansionFactor", expansion_factor,
+ T1_FIELD_DICT_PRIVATE )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE T1_FontRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_DICT
+
+ T1_FIELD_KEY ( "FontName", font_name, T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_NUM ( "PaintType", paint_type, T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_NUM ( "FontType", font_type, T1_FIELD_DICT_FONTDICT )
+ T1_FIELD_FIXED( "StrokeWidth", stroke_width, T1_FIELD_DICT_FONTDICT )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE FT_BBox
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_BBOX
+
+ T1_FIELD_BBOX( "FontBBox", xMin, T1_FIELD_DICT_FONTDICT )
+
+
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE T1_FaceRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FACE
+
+ T1_FIELD_NUM( "NDV", ndv_idx, T1_FIELD_DICT_PRIVATE )
+ T1_FIELD_NUM( "CDV", cdv_idx, T1_FIELD_DICT_PRIVATE )
+
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE PS_BlendRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_BLEND
+
+ T1_FIELD_NUM_TABLE( "DesignVector", default_design_vector,
+ T1_MAX_MM_DESIGNS, T1_FIELD_DICT_FONTDICT )
+
+
+#endif /* T1_CONFIG_OPTION_NO_MM_SUPPORT */
+
+
+/* END */
diff --git a/src/type1/type1.c b/src/type1/type1.c
new file mode 100644
index 0000000..ccc12be
--- /dev/null
+++ b/src/type1/type1.c
@@ -0,0 +1,33 @@
+/***************************************************************************/
+/* */
+/* type1.c */
+/* */
+/* FreeType Type 1 driver component (body only). */
+/* */
+/* Copyright 1996-2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "t1parse.c"
+#include "t1load.c"
+#include "t1objs.c"
+#include "t1driver.c"
+#include "t1gload.c"
+
+#ifndef T1_CONFIG_OPTION_NO_AFM
+#include "t1afm.c"
+#endif
+
+
+/* END */
diff --git a/src/type42/Jamfile b/src/type42/Jamfile
new file mode 100644
index 0000000..00371d5
--- /dev/null
+++ b/src/type42/Jamfile
@@ -0,0 +1,29 @@
+# FreeType 2 src/type42 Jamfile
+#
+# Copyright 2002 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) type42 ;
+
+{
+ local _sources ;
+
+ if $(FT2_MULTI)
+ {
+ _sources = t42objs t42parse t42drivr ;
+ }
+ else
+ {
+ _sources = type42 ;
+ }
+
+ Library $(FT2_LIB) : $(_sources).c ;
+}
+
+# end of src/type42 Jamfile
diff --git a/src/type42/module.mk b/src/type42/module.mk
new file mode 100644
index 0000000..8bd40a5
--- /dev/null
+++ b/src/type42/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 Type42 module definition
+#
+
+
+# Copyright 2002, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += TYPE42_DRIVER
+
+define TYPE42_DRIVER
+$(OPEN_DRIVER)t42_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)type42 $(ECHO_DRIVER_DESC)Type 42 font files with no known extension$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/type42/rules.mk b/src/type42/rules.mk
new file mode 100644
index 0000000..5563061
--- /dev/null
+++ b/src/type42/rules.mk
@@ -0,0 +1,69 @@
+#
+# FreeType 2 Type42 driver configuration rules
+#
+
+
+# Copyright 2002, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# Type42 driver directory
+#
+T42_DIR := $(SRC_DIR)/type42
+
+
+# compilation flags for the driver
+#
+T42_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(T42_DIR))
+
+
+# Type42 driver source
+#
+T42_DRV_SRC := $(T42_DIR)/t42objs.c \
+ $(T42_DIR)/t42parse.c \
+ $(T42_DIR)/t42drivr.c
+
+# Type42 driver headers
+#
+T42_DRV_H := $(T42_DRV_SRC:%.c=%.h) \
+ $(T42_DIR)/t42error.h
+
+
+# Type42 driver object(s)
+#
+# T42_DRV_OBJ_M is used during `multi' builds
+# T42_DRV_OBJ_S is used during `single' builds
+#
+T42_DRV_OBJ_M := $(T42_DRV_SRC:$(T42_DIR)/%.c=$(OBJ_DIR)/%.$O)
+T42_DRV_OBJ_S := $(OBJ_DIR)/type42.$O
+
+# Type42 driver source file for single build
+#
+T42_DRV_SRC_S := $(T42_DIR)/type42.c
+
+
+# Type42 driver - single object
+#
+$(T42_DRV_OBJ_S): $(T42_DRV_SRC_S) $(T42_DRV_SRC) $(FREETYPE_H) $(T42_DRV_H)
+ $(T42_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(T42_DRV_SRC_S))
+
+
+# Type42 driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(T42_DIR)/%.c $(FREETYPE_H) $(T42_DRV_H)
+ $(T42_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(T42_DRV_OBJ_S)
+DRV_OBJS_M += $(T42_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/type42/t42drivr.c b/src/type42/t42drivr.c
new file mode 100644
index 0000000..a6e4cf4
--- /dev/null
+++ b/src/type42/t42drivr.c
@@ -0,0 +1,232 @@
+/***************************************************************************/
+/* */
+/* t42drivr.c */
+/* */
+/* High-level Type 42 driver interface (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2006, 2007 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This driver implements Type42 fonts as described in the */
+ /* Technical Note #5012 from Adobe, with these limitations: */
+ /* */
+ /* 1) CID Fonts are not currently supported. */
+ /* 2) Incremental fonts making use of the GlyphDirectory keyword */
+ /* will be loaded, but the rendering will be using the TrueType */
+ /* tables. */
+ /* 3) As for Type1 fonts, CDevProc is not supported. */
+ /* 4) The Metrics dictionary is not supported. */
+ /* 5) AFM metrics are not supported. */
+ /* */
+ /* In other words, this driver supports Type42 fonts derived from */
+ /* TrueType fonts in a non-CID manner, as done by usual conversion */
+ /* programs. */
+ /* */
+ /*************************************************************************/
+
+
+#include "t42drivr.h"
+#include "t42objs.h"
+#include "t42error.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include FT_SERVICE_XFREE86_NAME_H
+#include FT_SERVICE_GLYPH_DICT_H
+#include FT_SERVICE_POSTSCRIPT_NAME_H
+#include FT_SERVICE_POSTSCRIPT_INFO_H
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t42
+
+
+ /*
+ *
+ * GLYPH DICT SERVICE
+ *
+ */
+
+ static FT_Error
+ t42_get_glyph_name( T42_Face face,
+ FT_UInt glyph_index,
+ FT_Pointer buffer,
+ FT_UInt buffer_max )
+ {
+ FT_STRCPYN( buffer, face->type1.glyph_names[glyph_index], buffer_max );
+
+ return T42_Err_Ok;
+ }
+
+
+ static FT_UInt
+ t42_get_name_index( T42_Face face,
+ FT_String* glyph_name )
+ {
+ FT_Int i;
+ FT_String* gname;
+
+
+ for ( i = 0; i < face->type1.num_glyphs; i++ )
+ {
+ gname = face->type1.glyph_names[i];
+
+ if ( glyph_name[0] == gname[0] && !ft_strcmp( glyph_name, gname ) )
+ return (FT_UInt)ft_atol( (const char *)face->type1.charstrings[i] );
+ }
+
+ return 0;
+ }
+
+
+ static const FT_Service_GlyphDictRec t42_service_glyph_dict =
+ {
+ (FT_GlyphDict_GetNameFunc) t42_get_glyph_name,
+ (FT_GlyphDict_NameIndexFunc)t42_get_name_index
+ };
+
+
+ /*
+ *
+ * POSTSCRIPT NAME SERVICE
+ *
+ */
+
+ static const char*
+ t42_get_ps_font_name( T42_Face face )
+ {
+ return (const char*)face->type1.font_name;
+ }
+
+
+ static const FT_Service_PsFontNameRec t42_service_ps_font_name =
+ {
+ (FT_PsName_GetFunc)t42_get_ps_font_name
+ };
+
+
+ /*
+ *
+ * POSTSCRIPT INFO SERVICE
+ *
+ */
+
+ static FT_Error
+ t42_ps_get_font_info( FT_Face face,
+ PS_FontInfoRec* afont_info )
+ {
+ *afont_info = ((T42_Face)face)->type1.font_info;
+ return T42_Err_Ok;
+ }
+
+
+ static FT_Int
+ t42_ps_has_glyph_names( FT_Face face )
+ {
+ FT_UNUSED( face );
+ return 1;
+ }
+
+
+ static FT_Error
+ t42_ps_get_font_private( FT_Face face,
+ PS_PrivateRec* afont_private )
+ {
+ *afont_private = ((T42_Face)face)->type1.private_dict;
+ return T42_Err_Ok;
+ }
+
+
+ static const FT_Service_PsInfoRec t42_service_ps_info =
+ {
+ (PS_GetFontInfoFunc) t42_ps_get_font_info,
+ (PS_HasGlyphNamesFunc) t42_ps_has_glyph_names,
+ (PS_GetFontPrivateFunc)t42_ps_get_font_private
+ };
+
+
+ /*
+ *
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec t42_services[] =
+ {
+ { FT_SERVICE_ID_GLYPH_DICT, &t42_service_glyph_dict },
+ { FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &t42_service_ps_font_name },
+ { FT_SERVICE_ID_POSTSCRIPT_INFO, &t42_service_ps_info },
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_TYPE_42 },
+ { NULL, NULL }
+ };
+
+
+ static FT_Module_Interface
+ T42_Get_Interface( FT_Driver driver,
+ const FT_String* t42_interface )
+ {
+ FT_UNUSED( driver );
+
+ return ft_service_list_lookup( t42_services, t42_interface );
+ }
+
+
+ const FT_Driver_ClassRec t42_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_SCALABLE |
+#ifdef TT_USE_BYTECODE_INTERPRETER
+ FT_MODULE_DRIVER_HAS_HINTER,
+#else
+ 0,
+#endif
+
+ sizeof ( T42_DriverRec ),
+
+ "type42",
+ 0x10000L,
+ 0x20000L,
+
+ 0, /* format interface */
+
+ (FT_Module_Constructor)T42_Driver_Init,
+ (FT_Module_Destructor) T42_Driver_Done,
+ (FT_Module_Requester) T42_Get_Interface,
+ },
+
+ sizeof ( T42_FaceRec ),
+ sizeof ( T42_SizeRec ),
+ sizeof ( T42_GlyphSlotRec ),
+
+ (FT_Face_InitFunc) T42_Face_Init,
+ (FT_Face_DoneFunc) T42_Face_Done,
+ (FT_Size_InitFunc) T42_Size_Init,
+ (FT_Size_DoneFunc) T42_Size_Done,
+ (FT_Slot_InitFunc) T42_GlyphSlot_Init,
+ (FT_Slot_DoneFunc) T42_GlyphSlot_Done,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ (FT_Slot_LoadFunc) T42_GlyphSlot_Load,
+
+ (FT_Face_GetKerningFunc) 0,
+ (FT_Face_AttachFunc) 0,
+
+ (FT_Face_GetAdvancesFunc) 0,
+ (FT_Size_RequestFunc) T42_Size_Request,
+ (FT_Size_SelectFunc) T42_Size_Select
+ };
+
+
+/* END */
diff --git a/src/type42/t42drivr.h b/src/type42/t42drivr.h
new file mode 100644
index 0000000..98b7410
--- /dev/null
+++ b/src/type42/t42drivr.h
@@ -0,0 +1,38 @@
+/***************************************************************************/
+/* */
+/* t42drivr.h */
+/* */
+/* High-level Type 42 driver interface (specification). */
+/* */
+/* Copyright 2002 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T42DRIVR_H__
+#define __T42DRIVR_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) t42_driver_class;
+
+
+FT_END_HEADER
+
+
+#endif /* __T42DRIVR_H__ */
+
+
+/* END */
diff --git a/src/type42/t42error.h b/src/type42/t42error.h
new file mode 100644
index 0000000..b230910
--- /dev/null
+++ b/src/type42/t42error.h
@@ -0,0 +1,40 @@
+/***************************************************************************/
+/* */
+/* t42error.h */
+/* */
+/* Type 42 error codes (specification only). */
+/* */
+/* Copyright 2002, 2003 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the Type 42 error enumeration constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __T42ERROR_H__
+#define __T42ERROR_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX T42_Err_
+#define FT_ERR_BASE FT_Mod_Err_Type42
+
+#include FT_ERRORS_H
+
+#endif /* __T42ERROR_H__ */
+
+
+/* END */
diff --git a/src/type42/t42objs.c b/src/type42/t42objs.c
new file mode 100644
index 0000000..db04fde
--- /dev/null
+++ b/src/type42/t42objs.c
@@ -0,0 +1,647 @@
+/***************************************************************************/
+/* */
+/* t42objs.c */
+/* */
+/* Type 42 objects manager (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006, 2007 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "t42objs.h"
+#include "t42parse.h"
+#include "t42error.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_LIST_H
+
+
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t42
+
+
+ static FT_Error
+ T42_Open_Face( T42_Face face )
+ {
+ T42_LoaderRec loader;
+ T42_Parser parser;
+ T1_Font type1 = &face->type1;
+ FT_Memory memory = face->root.memory;
+ FT_Error error;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ t42_loader_init( &loader, face );
+
+ parser = &loader.parser;
+
+ if ( FT_ALLOC( face->ttf_data, 12 ) )
+ goto Exit;
+
+ error = t42_parser_init( parser,
+ face->root.stream,
+ memory,
+ psaux);
+ if ( error )
+ goto Exit;
+
+ error = t42_parse_dict( face, &loader,
+ parser->base_dict, parser->base_len );
+ if ( error )
+ goto Exit;
+
+ if ( type1->font_type != 42 )
+ {
+ error = T42_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* now, propagate the charstrings and glyphnames tables */
+ /* to the Type1 data */
+ type1->num_glyphs = loader.num_glyphs;
+
+ if ( !loader.charstrings.init )
+ {
+ FT_ERROR(( "T42_Open_Face: no charstrings array in face!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ }
+
+ loader.charstrings.init = 0;
+ type1->charstrings_block = loader.charstrings.block;
+ type1->charstrings = loader.charstrings.elements;
+ type1->charstrings_len = loader.charstrings.lengths;
+
+ /* we copy the glyph names `block' and `elements' fields; */
+ /* the `lengths' field must be released later */
+ type1->glyph_names_block = loader.glyph_names.block;
+ type1->glyph_names = (FT_String**)loader.glyph_names.elements;
+ loader.glyph_names.block = 0;
+ loader.glyph_names.elements = 0;
+
+ /* we must now build type1.encoding when we have a custom array */
+ if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY )
+ {
+ FT_Int charcode, idx, min_char, max_char;
+ FT_Byte* char_name;
+ FT_Byte* glyph_name;
+
+
+ /* OK, we do the following: for each element in the encoding */
+ /* table, look up the index of the glyph having the same name */
+ /* as defined in the CharStrings array. */
+ /* The index is then stored in type1.encoding.char_index, and */
+ /* the name in type1.encoding.char_name */
+
+ min_char = +32000;
+ max_char = -32000;
+
+ charcode = 0;
+ for ( ; charcode < loader.encoding_table.max_elems; charcode++ )
+ {
+ type1->encoding.char_index[charcode] = 0;
+ type1->encoding.char_name [charcode] = (char *)".notdef";
+
+ char_name = loader.encoding_table.elements[charcode];
+ if ( char_name )
+ for ( idx = 0; idx < type1->num_glyphs; idx++ )
+ {
+ glyph_name = (FT_Byte*)type1->glyph_names[idx];
+ if ( ft_strcmp( (const char*)char_name,
+ (const char*)glyph_name ) == 0 )
+ {
+ type1->encoding.char_index[charcode] = (FT_UShort)idx;
+ type1->encoding.char_name [charcode] = (char*)glyph_name;
+
+ /* Change min/max encoded char only if glyph name is */
+ /* not /.notdef */
+ if ( ft_strcmp( (const char*)".notdef",
+ (const char*)glyph_name ) != 0 )
+ {
+ if ( charcode < min_char )
+ min_char = charcode;
+ if ( charcode > max_char )
+ max_char = charcode;
+ }
+ break;
+ }
+ }
+ }
+ type1->encoding.code_first = min_char;
+ type1->encoding.code_last = max_char;
+ type1->encoding.num_chars = loader.num_chars;
+ }
+
+ Exit:
+ t42_loader_done( &loader );
+ return error;
+ }
+
+
+ /***************** Driver Functions *************/
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_Face_Init( FT_Stream stream,
+ T42_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Service_PsCMaps psnames;
+ PSAux_Service psaux;
+ FT_Face root = (FT_Face)&face->root;
+ T1_Font type1 = &face->type1;
+ PS_FontInfo info = &type1->font_info;
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+ FT_UNUSED( face_index );
+ FT_UNUSED( stream );
+
+
+ face->ttf_face = NULL;
+ face->root.num_faces = 1;
+
+ FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
+ face->psnames = psnames;
+
+ face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
+ "psaux" );
+ psaux = (PSAux_Service)face->psaux;
+
+ /* open the tokenizer, this will also check the font format */
+ error = T42_Open_Face( face );
+ if ( error )
+ goto Exit;
+
+ /* if we just wanted to check the format, leave successfully now */
+ if ( face_index < 0 )
+ goto Exit;
+
+ /* check the face index */
+ if ( face_index != 0 )
+ {
+ FT_ERROR(( "T42_Face_Init: invalid face index\n" ));
+ error = T42_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ /* Now load the font program into the face object */
+
+ /* Init the face object fields */
+ /* Now set up root face fields */
+
+ root->num_glyphs = type1->num_glyphs;
+ root->num_charmaps = 0;
+ root->face_index = face_index;
+
+ root->face_flags = FT_FACE_FLAG_SCALABLE |
+ FT_FACE_FLAG_HORIZONTAL |
+ FT_FACE_FLAG_GLYPH_NAMES;
+
+ if ( info->is_fixed_pitch )
+ root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ /* We only set this flag if we have the patented bytecode interpreter. */
+ /* There are no known `tricky' Type42 fonts that could be loaded with */
+ /* the unpatented interpreter. */
+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+ root->face_flags |= FT_FACE_FLAG_HINTER;
+#endif
+
+ /* XXX: TODO -- add kerning with .afm support */
+
+ /* get style name -- be careful, some broken fonts only */
+ /* have a `/FontName' dictionary entry! */
+ root->family_name = info->family_name;
+ /* assume "Regular" style if we don't know better */
+ root->style_name = (char *)"Regular";
+ if ( root->family_name )
+ {
+ char* full = info->full_name;
+ char* family = root->family_name;
+
+
+ if ( full )
+ {
+ while ( *full )
+ {
+ if ( *full == *family )
+ {
+ family++;
+ full++;
+ }
+ else
+ {
+ if ( *full == ' ' || *full == '-' )
+ full++;
+ else if ( *family == ' ' || *family == '-' )
+ family++;
+ else
+ {
+ if ( !*family )
+ root->style_name = full;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* do we have a `/FontName'? */
+ if ( type1->font_name )
+ root->family_name = type1->font_name;
+ }
+
+ /* no embedded bitmap support */
+ root->num_fixed_sizes = 0;
+ root->available_sizes = 0;
+
+ /* Load the TTF font embedded in the T42 font */
+ {
+ FT_Open_Args args;
+
+
+ args.flags = FT_OPEN_MEMORY;
+ args.memory_base = face->ttf_data;
+ args.memory_size = face->ttf_size;
+
+ if ( num_params )
+ {
+ args.flags |= FT_OPEN_PARAMS;
+ args.num_params = num_params;
+ args.params = params;
+ }
+
+ error = FT_Open_Face( FT_FACE_LIBRARY( face ),
+ &args, 0, &face->ttf_face );
+ }
+
+ if ( error )
+ goto Exit;
+
+ FT_Done_Size( face->ttf_face->size );
+
+ /* Ignore info in FontInfo dictionary and use the info from the */
+ /* loaded TTF font. The PostScript interpreter also ignores it. */
+ root->bbox = face->ttf_face->bbox;
+ root->units_per_EM = face->ttf_face->units_per_EM;
+
+ root->ascender = face->ttf_face->ascender;
+ root->descender = face->ttf_face->descender;
+ root->height = face->ttf_face->height;
+
+ root->max_advance_width = face->ttf_face->max_advance_width;
+ root->max_advance_height = face->ttf_face->max_advance_height;
+
+ root->underline_position = (FT_Short)info->underline_position;
+ root->underline_thickness = (FT_Short)info->underline_thickness;
+
+ /* compute style flags */
+ root->style_flags = 0;
+ if ( info->italic_angle )
+ root->style_flags |= FT_STYLE_FLAG_ITALIC;
+
+ if ( face->ttf_face->style_flags & FT_STYLE_FLAG_BOLD )
+ root->style_flags |= FT_STYLE_FLAG_BOLD;
+
+ if ( face->ttf_face->face_flags & FT_FACE_FLAG_VERTICAL )
+ root->face_flags |= FT_FACE_FLAG_VERTICAL;
+
+ {
+ if ( psnames && psaux )
+ {
+ FT_CharMapRec charmap;
+ T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes;
+ FT_CMap_Class clazz;
+
+
+ charmap.face = root;
+
+ /* first of all, try to synthetize a Unicode charmap */
+ charmap.platform_id = 3;
+ charmap.encoding_id = 1;
+ charmap.encoding = FT_ENCODING_UNICODE;
+
+ FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL );
+
+ /* now, generate an Adobe Standard encoding when appropriate */
+ charmap.platform_id = 7;
+ clazz = NULL;
+
+ switch ( type1->encoding_type )
+ {
+ case T1_ENCODING_TYPE_STANDARD:
+ charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
+ charmap.encoding_id = 0;
+ clazz = cmap_classes->standard;
+ break;
+
+ case T1_ENCODING_TYPE_EXPERT:
+ charmap.encoding = FT_ENCODING_ADOBE_EXPERT;
+ charmap.encoding_id = 1;
+ clazz = cmap_classes->expert;
+ break;
+
+ case T1_ENCODING_TYPE_ARRAY:
+ charmap.encoding = FT_ENCODING_ADOBE_CUSTOM;
+ charmap.encoding_id = 2;
+ clazz = cmap_classes->custom;
+ break;
+
+ case T1_ENCODING_TYPE_ISOLATIN1:
+ charmap.encoding = FT_ENCODING_ADOBE_LATIN_1;
+ charmap.encoding_id = 3;
+ clazz = cmap_classes->unicode;
+ break;
+
+ default:
+ ;
+ }
+
+ if ( clazz )
+ FT_CMap_New( clazz, NULL, &charmap, NULL );
+
+#if 0
+ /* Select default charmap */
+ if ( root->num_charmaps )
+ root->charmap = root->charmaps[0];
+#endif
+ }
+ }
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T42_Face_Done( T42_Face face )
+ {
+ T1_Font type1;
+ PS_FontInfo info;
+ FT_Memory memory;
+
+
+ if ( face )
+ {
+ type1 = &face->type1;
+ info = &type1->font_info;
+ memory = face->root.memory;
+
+ /* delete internal ttf face prior to freeing face->ttf_data */
+ if ( face->ttf_face )
+ FT_Done_Face( face->ttf_face );
+
+ /* release font info strings */
+ FT_FREE( info->version );
+ FT_FREE( info->notice );
+ FT_FREE( info->full_name );
+ FT_FREE( info->family_name );
+ FT_FREE( info->weight );
+
+ /* release top dictionary */
+ FT_FREE( type1->charstrings_len );
+ FT_FREE( type1->charstrings );
+ FT_FREE( type1->glyph_names );
+
+ FT_FREE( type1->charstrings_block );
+ FT_FREE( type1->glyph_names_block );
+
+ FT_FREE( type1->encoding.char_index );
+ FT_FREE( type1->encoding.char_name );
+ FT_FREE( type1->font_name );
+
+ FT_FREE( face->ttf_data );
+
+#if 0
+ /* release afm data if present */
+ if ( face->afm_data )
+ T1_Done_AFM( memory, (T1_AFM*)face->afm_data );
+#endif
+
+ /* release unicode map, if any */
+ FT_FREE( face->unicode_map.maps );
+ face->unicode_map.num_maps = 0;
+
+ face->root.family_name = 0;
+ face->root.style_name = 0;
+ }
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* T42_Driver_Init */
+ /* */
+ /* <Description> */
+ /* Initializes a given Type 42 driver object. */
+ /* */
+ /* <Input> */
+ /* driver :: A handle to the target driver object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ FT_LOCAL_DEF( FT_Error )
+ T42_Driver_Init( T42_Driver driver )
+ {
+ FT_Module ttmodule;
+
+
+ ttmodule = FT_Get_Module( FT_MODULE(driver)->library, "truetype" );
+ driver->ttclazz = (FT_Driver_Class)ttmodule->clazz;
+
+ return T42_Err_Ok;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T42_Driver_Done( T42_Driver driver )
+ {
+ FT_UNUSED( driver );
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_Size_Init( T42_Size size )
+ {
+ FT_Face face = size->root.face;
+ T42_Face t42face = (T42_Face)face;
+ FT_Size ttsize;
+ FT_Error error = T42_Err_Ok;
+
+
+ error = FT_New_Size( t42face->ttf_face, &ttsize );
+ size->ttsize = ttsize;
+
+ FT_Activate_Size( ttsize );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_Size_Request( T42_Size size,
+ FT_Size_Request req )
+ {
+ T42_Face face = (T42_Face)size->root.face;
+ FT_Error error;
+
+
+ FT_Activate_Size( size->ttsize );
+
+ error = FT_Request_Size( face->ttf_face, req );
+ if ( !error )
+ ( (FT_Size)size )->metrics = face->ttf_face->size->metrics;
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_Size_Select( T42_Size size,
+ FT_ULong strike_index )
+ {
+ T42_Face face = (T42_Face)size->root.face;
+ FT_Error error;
+
+
+ FT_Activate_Size( size->ttsize );
+
+ error = FT_Select_Size( face->ttf_face, strike_index );
+ if ( !error )
+ ( (FT_Size)size )->metrics = face->ttf_face->size->metrics;
+
+ return error;
+
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T42_Size_Done( T42_Size size )
+ {
+ FT_Face face = size->root.face;
+ T42_Face t42face = (T42_Face)face;
+ FT_ListNode node;
+
+
+ node = FT_List_Find( &t42face->ttf_face->sizes_list, size->ttsize );
+ if ( node )
+ {
+ FT_Done_Size( size->ttsize );
+ size->ttsize = NULL;
+ }
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_GlyphSlot_Init( T42_GlyphSlot slot )
+ {
+ FT_Face face = slot->root.face;
+ T42_Face t42face = (T42_Face)face;
+ FT_GlyphSlot ttslot;
+ FT_Error error = T42_Err_Ok;
+
+
+ if ( face->glyph == NULL )
+ {
+ /* First glyph slot for this face */
+ slot->ttslot = t42face->ttf_face->glyph;
+ }
+ else
+ {
+ error = FT_New_GlyphSlot( t42face->ttf_face, &ttslot );
+ slot->ttslot = ttslot;
+ }
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ T42_GlyphSlot_Done( T42_GlyphSlot slot )
+ {
+ FT_Done_GlyphSlot( slot->ttslot );
+ }
+
+
+ static void
+ t42_glyphslot_clear( FT_GlyphSlot slot )
+ {
+ /* free bitmap if needed */
+ ft_glyphslot_free_bitmap( slot );
+
+ /* clear all public fields in the glyph slot */
+ FT_ZERO( &slot->metrics );
+ FT_ZERO( &slot->outline );
+ FT_ZERO( &slot->bitmap );
+
+ slot->bitmap_left = 0;
+ slot->bitmap_top = 0;
+ slot->num_subglyphs = 0;
+ slot->subglyphs = 0;
+ slot->control_data = 0;
+ slot->control_len = 0;
+ slot->other = 0;
+ slot->format = FT_GLYPH_FORMAT_NONE;
+
+ slot->linearHoriAdvance = 0;
+ slot->linearVertAdvance = 0;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ T42_GlyphSlot_Load( FT_GlyphSlot glyph,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FT_Error error;
+ T42_GlyphSlot t42slot = (T42_GlyphSlot)glyph;
+ T42_Size t42size = (T42_Size)size;
+ FT_Driver_Class ttclazz = ((T42_Driver)glyph->face->driver)->ttclazz;
+
+
+ t42_glyphslot_clear( t42slot->ttslot );
+ error = ttclazz->load_glyph( t42slot->ttslot,
+ t42size->ttsize,
+ glyph_index,
+ load_flags | FT_LOAD_NO_BITMAP );
+
+ if ( !error )
+ {
+ glyph->metrics = t42slot->ttslot->metrics;
+
+ glyph->linearHoriAdvance = t42slot->ttslot->linearHoriAdvance;
+ glyph->linearVertAdvance = t42slot->ttslot->linearVertAdvance;
+
+ glyph->format = t42slot->ttslot->format;
+ glyph->outline = t42slot->ttslot->outline;
+
+ glyph->bitmap = t42slot->ttslot->bitmap;
+ glyph->bitmap_left = t42slot->ttslot->bitmap_left;
+ glyph->bitmap_top = t42slot->ttslot->bitmap_top;
+
+ glyph->num_subglyphs = t42slot->ttslot->num_subglyphs;
+ glyph->subglyphs = t42slot->ttslot->subglyphs;
+
+ glyph->control_data = t42slot->ttslot->control_data;
+ glyph->control_len = t42slot->ttslot->control_len;
+ }
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/type42/t42objs.h b/src/type42/t42objs.h
new file mode 100644
index 0000000..289dedc
--- /dev/null
+++ b/src/type42/t42objs.h
@@ -0,0 +1,124 @@
+/***************************************************************************/
+/* */
+/* t42objs.h */
+/* */
+/* Type 42 objects manager (specification). */
+/* */
+/* Copyright 2002, 2003, 2006, 2007 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T42OBJS_H__
+#define __T42OBJS_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+#include "t42types.h"
+#include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_DRIVER_H
+#include FT_SERVICE_POSTSCRIPT_CMAPS_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+
+FT_BEGIN_HEADER
+
+
+ /* Type42 size */
+ typedef struct T42_SizeRec_
+ {
+ FT_SizeRec root;
+ FT_Size ttsize;
+
+ } T42_SizeRec, *T42_Size;
+
+
+ /* Type42 slot */
+ typedef struct T42_GlyphSlotRec_
+ {
+ FT_GlyphSlotRec root;
+ FT_GlyphSlot ttslot;
+
+ } T42_GlyphSlotRec, *T42_GlyphSlot;
+
+
+ /* Type 42 driver */
+ typedef struct T42_DriverRec_
+ {
+ FT_DriverRec root;
+ FT_Driver_Class ttclazz;
+ void* extension_component;
+
+ } T42_DriverRec, *T42_Driver;
+
+
+ /* */
+
+
+ FT_LOCAL( FT_Error )
+ T42_Face_Init( FT_Stream stream,
+ T42_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params );
+
+
+ FT_LOCAL( void )
+ T42_Face_Done( T42_Face face );
+
+
+ FT_LOCAL( FT_Error )
+ T42_Size_Init( T42_Size size );
+
+
+ FT_LOCAL( FT_Error )
+ T42_Size_Request( T42_Size size,
+ FT_Size_Request req );
+
+
+ FT_LOCAL( FT_Error )
+ T42_Size_Select( T42_Size size,
+ FT_ULong strike_index );
+
+
+ FT_LOCAL( void )
+ T42_Size_Done( T42_Size size );
+
+
+ FT_LOCAL( FT_Error )
+ T42_GlyphSlot_Init( T42_GlyphSlot slot );
+
+
+ FT_LOCAL( FT_Error )
+ T42_GlyphSlot_Load( FT_GlyphSlot glyph,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags );
+
+ FT_LOCAL( void )
+ T42_GlyphSlot_Done( T42_GlyphSlot slot );
+
+
+ FT_LOCAL( FT_Error )
+ T42_Driver_Init( T42_Driver driver );
+
+ FT_LOCAL( void )
+ T42_Driver_Done( T42_Driver driver );
+
+ /* */
+
+FT_END_HEADER
+
+
+#endif /* __T42OBJS_H__ */
+
+
+/* END */
diff --git a/src/type42/t42parse.c b/src/type42/t42parse.c
new file mode 100644
index 0000000..7148379
--- /dev/null
+++ b/src/type42/t42parse.c
@@ -0,0 +1,1167 @@
+/***************************************************************************/
+/* */
+/* t42parse.c */
+/* */
+/* Type 42 font parser (body). */
+/* */
+/* Copyright 2002, 2003, 2004, 2005, 2006, 2007 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include "t42parse.h"
+#include "t42error.h"
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_LIST_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_t42
+
+
+ static void
+ t42_parse_font_matrix( T42_Face face,
+ T42_Loader loader );
+ static void
+ t42_parse_encoding( T42_Face face,
+ T42_Loader loader );
+
+ static void
+ t42_parse_charstrings( T42_Face face,
+ T42_Loader loader );
+
+ static void
+ t42_parse_sfnts( T42_Face face,
+ T42_Loader loader );
+
+
+ /* as Type42 fonts have no Private dict, */
+ /* we set the last argument of T1_FIELD_XXX to 0 */
+ static const
+ T1_FieldRec t42_keywords[] = {
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE T1_FontInfo
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_INFO
+
+ T1_FIELD_STRING( "version", version, 0 )
+ T1_FIELD_STRING( "Notice", notice, 0 )
+ T1_FIELD_STRING( "FullName", full_name, 0 )
+ T1_FIELD_STRING( "FamilyName", family_name, 0 )
+ T1_FIELD_STRING( "Weight", weight, 0 )
+ T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 )
+ T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 )
+ T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 )
+ T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 )
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE T1_FontRec
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_FONT_DICT
+
+ T1_FIELD_KEY ( "FontName", font_name, 0 )
+ T1_FIELD_NUM ( "PaintType", paint_type, 0 )
+ T1_FIELD_NUM ( "FontType", font_type, 0 )
+ T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 )
+
+#undef FT_STRUCTURE
+#define FT_STRUCTURE FT_BBox
+#undef T1CODE
+#define T1CODE T1_FIELD_LOCATION_BBOX
+
+ T1_FIELD_BBOX("FontBBox", xMin, 0 )
+
+ T1_FIELD_CALLBACK( "FontMatrix", t42_parse_font_matrix, 0 )
+ T1_FIELD_CALLBACK( "Encoding", t42_parse_encoding, 0 )
+ T1_FIELD_CALLBACK( "CharStrings", t42_parse_charstrings, 0 )
+ T1_FIELD_CALLBACK( "sfnts", t42_parse_sfnts, 0 )
+
+ { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 }
+ };
+
+
+#define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l )
+#define T1_Done_Table( p ) \
+ do \
+ { \
+ if ( (p)->funcs.done ) \
+ (p)->funcs.done( p ); \
+ } while ( 0 )
+#define T1_Release_Table( p ) \
+ do \
+ { \
+ if ( (p)->funcs.release ) \
+ (p)->funcs.release( p ); \
+ } while ( 0 )
+
+#define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root )
+#define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root )
+
+#define T1_ToInt( p ) \
+ (p)->root.funcs.to_int( &(p)->root )
+#define T1_ToBytes( p, b, m, n, d ) \
+ (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d )
+
+#define T1_ToFixedArray( p, m, f, t ) \
+ (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t )
+#define T1_ToToken( p, t ) \
+ (p)->root.funcs.to_token( &(p)->root, t )
+
+#define T1_Load_Field( p, f, o, m, pf ) \
+ (p)->root.funcs.load_field( &(p)->root, f, o, m, pf )
+#define T1_Load_Field_Table( p, f, o, m, pf ) \
+ (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf )
+
+
+ /********************* Parsing Functions ******************/
+
+ FT_LOCAL_DEF( FT_Error )
+ t42_parser_init( T42_Parser parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux )
+ {
+ FT_Error error = T42_Err_Ok;
+ FT_Long size;
+
+
+ psaux->ps_parser_funcs->init( &parser->root, 0, 0, memory );
+
+ parser->stream = stream;
+ parser->base_len = 0;
+ parser->base_dict = 0;
+ parser->in_memory = 0;
+
+ /*******************************************************************/
+ /* */
+ /* Here a short summary of what is going on: */
+ /* */
+ /* When creating a new Type 42 parser, we try to locate and load */
+ /* the base dictionary, loading the whole font into memory. */
+ /* */
+ /* When `loading' the base dictionary, we only set up pointers */
+ /* in the case of a memory-based stream. Otherwise, we allocate */
+ /* and load the base dictionary in it. */
+ /* */
+ /* parser->in_memory is set if we have a memory stream. */
+ /* */
+
+ if ( FT_STREAM_SEEK( 0L ) ||
+ FT_FRAME_ENTER( 17 ) )
+ goto Exit;
+
+ if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont", 17 ) != 0 )
+ {
+ FT_TRACE2(( "not a Type42 font\n" ));
+ error = T42_Err_Unknown_File_Format;
+ }
+
+ FT_FRAME_EXIT();
+
+ if ( error || FT_STREAM_SEEK( 0 ) )
+ goto Exit;
+
+ size = stream->size;
+
+ /* now, try to load `size' bytes of the `base' dictionary we */
+ /* found previously */
+
+ /* if it is a memory-based resource, set up pointers */
+ if ( !stream->read )
+ {
+ parser->base_dict = (FT_Byte*)stream->base + stream->pos;
+ parser->base_len = size;
+ parser->in_memory = 1;
+
+ /* check that the `size' field is valid */
+ if ( FT_STREAM_SKIP( size ) )
+ goto Exit;
+ }
+ else
+ {
+ /* read segment in memory */
+ if ( FT_ALLOC( parser->base_dict, size ) ||
+ FT_STREAM_READ( parser->base_dict, size ) )
+ goto Exit;
+
+ parser->base_len = size;
+ }
+
+ parser->root.base = parser->base_dict;
+ parser->root.cursor = parser->base_dict;
+ parser->root.limit = parser->root.cursor + parser->base_len;
+
+ Exit:
+ if ( error && !parser->in_memory )
+ FT_FREE( parser->base_dict );
+
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t42_parser_done( T42_Parser parser )
+ {
+ FT_Memory memory = parser->root.memory;
+
+
+ /* free the base dictionary only when we have a disk stream */
+ if ( !parser->in_memory )
+ FT_FREE( parser->base_dict );
+
+ parser->root.funcs.done( &parser->root );
+ }
+
+
+ static int
+ t42_is_space( FT_Byte c )
+ {
+ return ( c == ' ' || c == '\t' ||
+ c == '\r' || c == '\n' || c == '\f' ||
+ c == '\0' );
+ }
+
+
+ static void
+ t42_parse_font_matrix( T42_Face face,
+ T42_Loader loader )
+ {
+ T42_Parser parser = &loader->parser;
+ FT_Matrix* matrix = &face->type1.font_matrix;
+ FT_Vector* offset = &face->type1.font_offset;
+ FT_Face root = (FT_Face)&face->root;
+ FT_Fixed temp[6];
+ FT_Fixed temp_scale;
+
+
+ (void)T1_ToFixedArray( parser, 6, temp, 3 );
+
+ temp_scale = FT_ABS( temp[3] );
+
+ /* Set Units per EM based on FontMatrix values. We set the value to */
+ /* 1000 / temp_scale, because temp_scale was already multiplied by */
+ /* 1000 (in t1_tofixed, from psobjs.c). */
+
+ root->units_per_EM = (FT_UShort)( FT_DivFix( 1000 * 0x10000L,
+ temp_scale ) >> 16 );
+
+ /* we need to scale the values by 1.0/temp_scale */
+ if ( temp_scale != 0x10000L ) {
+ temp[0] = FT_DivFix( temp[0], temp_scale );
+ temp[1] = FT_DivFix( temp[1], temp_scale );
+ temp[2] = FT_DivFix( temp[2], temp_scale );
+ temp[4] = FT_DivFix( temp[4], temp_scale );
+ temp[5] = FT_DivFix( temp[5], temp_scale );
+ temp[3] = 0x10000L;
+ }
+
+ matrix->xx = temp[0];
+ matrix->yx = temp[1];
+ matrix->xy = temp[2];
+ matrix->yy = temp[3];
+
+ /* note that the offsets must be expressed in integer font units */
+ offset->x = temp[4] >> 16;
+ offset->y = temp[5] >> 16;
+ }
+
+
+ static void
+ t42_parse_encoding( T42_Face face,
+ T42_Loader loader )
+ {
+ T42_Parser parser = &loader->parser;
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+
+ T1_Skip_Spaces( parser );
+ cur = parser->root.cursor;
+ if ( cur >= limit )
+ {
+ FT_ERROR(( "t42_parse_encoding: out of bounds!\n" ));
+ parser->root.error = T42_Err_Invalid_File_Format;
+ return;
+ }
+
+ /* if we have a number or `[', the encoding is an array, */
+ /* and we must load it now */
+ if ( ft_isdigit( *cur ) || *cur == '[' )
+ {
+ T1_Encoding encode = &face->type1.encoding;
+ FT_UInt count, n;
+ PS_Table char_table = &loader->encoding_table;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error;
+ FT_Bool only_immediates = 0;
+
+
+ /* read the number of entries in the encoding; should be 256 */
+ if ( *cur == '[' )
+ {
+ count = 256;
+ only_immediates = 1;
+ parser->root.cursor++;
+ }
+ else
+ count = (FT_UInt)T1_ToInt( parser );
+
+ T1_Skip_Spaces( parser );
+ if ( parser->root.cursor >= limit )
+ return;
+
+ /* we use a T1_Table to store our charnames */
+ loader->num_chars = encode->num_chars = count;
+ if ( FT_NEW_ARRAY( encode->char_index, count ) ||
+ FT_NEW_ARRAY( encode->char_name, count ) ||
+ FT_SET_ERROR( psaux->ps_table_funcs->init(
+ char_table, count, memory ) ) )
+ {
+ parser->root.error = error;
+ return;
+ }
+
+ /* We need to `zero' out encoding_table.elements */
+ for ( n = 0; n < count; n++ )
+ {
+ char* notdef = (char *)".notdef";
+
+
+ T1_Add_Table( char_table, n, notdef, 8 );
+ }
+
+ /* Now we need to read records of the form */
+ /* */
+ /* ... charcode /charname ... */
+ /* */
+ /* for each entry in our table. */
+ /* */
+ /* We simply look for a number followed by an immediate */
+ /* name. Note that this ignores correctly the sequence */
+ /* that is often seen in type42 fonts: */
+ /* */
+ /* 0 1 255 { 1 index exch /.notdef put } for dup */
+ /* */
+ /* used to clean the encoding array before anything else. */
+ /* */
+ /* Alternatively, if the array is directly given as */
+ /* */
+ /* /Encoding [ ... ] */
+ /* */
+ /* we only read immediates. */
+
+ n = 0;
+ T1_Skip_Spaces( parser );
+
+ while ( parser->root.cursor < limit )
+ {
+ cur = parser->root.cursor;
+
+ /* we stop when we encounter `def' or `]' */
+ if ( *cur == 'd' && cur + 3 < limit )
+ {
+ if ( cur[1] == 'e' &&
+ cur[2] == 'f' &&
+ t42_is_space( cur[3] ) )
+ {
+ FT_TRACE6(( "encoding end\n" ));
+ cur += 3;
+ break;
+ }
+ }
+ if ( *cur == ']' )
+ {
+ FT_TRACE6(( "encoding end\n" ));
+ cur++;
+ break;
+ }
+
+ /* check whether we have found an entry */
+ if ( ft_isdigit( *cur ) || only_immediates )
+ {
+ FT_Int charcode;
+
+
+ if ( only_immediates )
+ charcode = n;
+ else
+ {
+ charcode = (FT_Int)T1_ToInt( parser );
+ T1_Skip_Spaces( parser );
+ }
+
+ cur = parser->root.cursor;
+
+ if ( *cur == '/' && cur + 2 < limit && n < count )
+ {
+ FT_PtrDist len;
+
+
+ cur++;
+
+ parser->root.cursor = cur;
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+
+ len = parser->root.cursor - cur;
+
+ parser->root.error = T1_Add_Table( char_table, charcode,
+ cur, len + 1 );
+ if ( parser->root.error )
+ return;
+ char_table->elements[charcode][len] = '\0';
+
+ n++;
+ }
+ }
+ else
+ {
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+ }
+
+ T1_Skip_Spaces( parser );
+ }
+
+ face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY;
+ parser->root.cursor = cur;
+ }
+
+ /* Otherwise, we should have either `StandardEncoding', */
+ /* `ExpertEncoding', or `ISOLatin1Encoding' */
+ else
+ {
+ if ( cur + 17 < limit &&
+ ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD;
+
+ else if ( cur + 15 < limit &&
+ ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT;
+
+ else if ( cur + 18 < limit &&
+ ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 )
+ face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1;
+
+ else
+ {
+ FT_ERROR(( "t42_parse_encoding: invalid token!\n" ));
+ parser->root.error = T42_Err_Invalid_File_Format;
+ }
+ }
+ }
+
+
+ typedef enum
+ {
+ BEFORE_START,
+ BEFORE_TABLE_DIR,
+ OTHER_TABLES
+
+ } T42_Load_Status;
+
+
+ static void
+ t42_parse_sfnts( T42_Face face,
+ T42_Loader loader )
+ {
+ T42_Parser parser = &loader->parser;
+ FT_Memory memory = parser->root.memory;
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+ FT_Error error;
+ FT_Int num_tables = 0;
+ FT_ULong count, ttf_size = 0;
+
+ FT_Long n, string_size, old_string_size, real_size;
+ FT_Byte* string_buf = NULL;
+ FT_Bool allocated = 0;
+
+ T42_Load_Status status;
+
+
+ /* The format is */
+ /* */
+ /* /sfnts [ <hexstring> <hexstring> ... ] def */
+ /* */
+ /* or */
+ /* */
+ /* /sfnts [ */
+ /* <num_bin_bytes> RD <binary data> */
+ /* <num_bin_bytes> RD <binary data> */
+ /* ... */
+ /* ] def */
+ /* */
+ /* with exactly one space after the `RD' token. */
+
+ T1_Skip_Spaces( parser );
+
+ if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' )
+ {
+ FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ T1_Skip_Spaces( parser );
+ status = BEFORE_START;
+ string_size = 0;
+ old_string_size = 0;
+ count = 0;
+
+ while ( parser->root.cursor < limit )
+ {
+ cur = parser->root.cursor;
+
+ if ( *cur == ']' )
+ {
+ parser->root.cursor++;
+ goto Exit;
+ }
+
+ else if ( *cur == '<' )
+ {
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+
+ /* don't include delimiters */
+ string_size = (FT_Long)( ( parser->root.cursor - cur - 2 + 1 ) / 2 );
+ if ( FT_REALLOC( string_buf, old_string_size, string_size ) )
+ goto Fail;
+
+ allocated = 1;
+
+ parser->root.cursor = cur;
+ (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 );
+ old_string_size = string_size;
+ string_size = real_size;
+ }
+
+ else if ( ft_isdigit( *cur ) )
+ {
+ if ( allocated )
+ {
+ FT_ERROR(( "t42_parse_sfnts: "
+ "can't handle mixed binary and hex strings!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ string_size = T1_ToInt( parser );
+
+ T1_Skip_PS_Token( parser ); /* `RD' */
+ if ( parser->root.error )
+ return;
+
+ string_buf = parser->root.cursor + 1; /* one space after `RD' */
+
+ parser->root.cursor += string_size + 1;
+ if ( parser->root.cursor >= limit )
+ {
+ FT_ERROR(( "t42_parse_sfnts: too many binary data!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+ }
+
+ if ( !string_buf )
+ {
+ FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /* A string can have a trailing zero byte for padding. Ignore it. */
+ if ( string_buf[string_size - 1] == 0 && ( string_size % 2 == 1 ) )
+ string_size--;
+
+ if ( !string_size )
+ {
+ FT_ERROR(( "t42_parse_sfnts: invalid string!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ for ( n = 0; n < string_size; n++ )
+ {
+ switch ( status )
+ {
+ case BEFORE_START:
+ /* load offset table, 12 bytes */
+ if ( count < 12 )
+ {
+ face->ttf_data[count++] = string_buf[n];
+ continue;
+ }
+ else
+ {
+ num_tables = 16 * face->ttf_data[4] + face->ttf_data[5];
+ status = BEFORE_TABLE_DIR;
+ ttf_size = 12 + 16 * num_tables;
+
+ if ( FT_REALLOC( face->ttf_data, 12, ttf_size ) )
+ goto Fail;
+ }
+ /* fall through */
+
+ case BEFORE_TABLE_DIR:
+ /* the offset table is read; read the table directory */
+ if ( count < ttf_size )
+ {
+ face->ttf_data[count++] = string_buf[n];
+ continue;
+ }
+ else
+ {
+ int i;
+ FT_ULong len;
+
+
+ for ( i = 0; i < num_tables; i++ )
+ {
+ FT_Byte* p = face->ttf_data + 12 + 16 * i + 12;
+
+
+ len = FT_PEEK_ULONG( p );
+
+ /* Pad to a 4-byte boundary length */
+ ttf_size += ( len + 3 ) & ~3;
+ }
+
+ status = OTHER_TABLES;
+ face->ttf_size = ttf_size;
+
+ /* there are no more than 256 tables, so no size check here */
+ if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables,
+ ttf_size + 1 ) )
+ goto Fail;
+ }
+ /* fall through */
+
+ case OTHER_TABLES:
+ /* all other tables are just copied */
+ if ( count >= ttf_size )
+ {
+ FT_ERROR(( "t42_parse_sfnts: too many binary data!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+ face->ttf_data[count++] = string_buf[n];
+ }
+ }
+
+ T1_Skip_Spaces( parser );
+ }
+
+ /* if control reaches this point, the format was not valid */
+ error = T42_Err_Invalid_File_Format;
+
+ Fail:
+ parser->root.error = error;
+
+ Exit:
+ if ( allocated )
+ FT_FREE( string_buf );
+ }
+
+
+ static void
+ t42_parse_charstrings( T42_Face face,
+ T42_Loader loader )
+ {
+ T42_Parser parser = &loader->parser;
+ PS_Table code_table = &loader->charstrings;
+ PS_Table name_table = &loader->glyph_names;
+ PS_Table swap_table = &loader->swap_table;
+ FT_Memory memory = parser->root.memory;
+ FT_Error error;
+
+ PSAux_Service psaux = (PSAux_Service)face->psaux;
+
+ FT_Byte* cur;
+ FT_Byte* limit = parser->root.limit;
+ FT_UInt n;
+ FT_UInt notdef_index = 0;
+ FT_Byte notdef_found = 0;
+
+
+ T1_Skip_Spaces( parser );
+
+ if ( parser->root.cursor >= limit )
+ {
+ FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ if ( ft_isdigit( *parser->root.cursor ) )
+ {
+ loader->num_glyphs = (FT_UInt)T1_ToInt( parser );
+ if ( parser->root.error )
+ return;
+ }
+ else if ( *parser->root.cursor == '<' )
+ {
+ /* We have `<< ... >>'. Count the number of `/' in the dictionary */
+ /* to get its size. */
+ FT_UInt count = 0;
+
+
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+ T1_Skip_Spaces( parser );
+ cur = parser->root.cursor;
+
+ while ( parser->root.cursor < limit )
+ {
+ if ( *parser->root.cursor == '/' )
+ count++;
+ else if ( *parser->root.cursor == '>' )
+ {
+ loader->num_glyphs = count;
+ parser->root.cursor = cur; /* rewind */
+ break;
+ }
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+ T1_Skip_Spaces( parser );
+ }
+ }
+ else
+ {
+ FT_ERROR(( "t42_parse_charstrings: invalid token!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ if ( parser->root.cursor >= limit )
+ {
+ FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /* initialize tables */
+
+ error = psaux->ps_table_funcs->init( code_table,
+ loader->num_glyphs,
+ memory );
+ if ( error )
+ goto Fail;
+
+ error = psaux->ps_table_funcs->init( name_table,
+ loader->num_glyphs,
+ memory );
+ if ( error )
+ goto Fail;
+
+ /* Initialize table for swapping index notdef_index and */
+ /* index 0 names and codes (if necessary). */
+
+ error = psaux->ps_table_funcs->init( swap_table, 4, memory );
+ if ( error )
+ goto Fail;
+
+ n = 0;
+
+ for (;;)
+ {
+ /* The format is simple: */
+ /* `/glyphname' + index [+ def] */
+
+ T1_Skip_Spaces( parser );
+
+ cur = parser->root.cursor;
+ if ( cur >= limit )
+ break;
+
+ /* We stop when we find an `end' keyword or '>' */
+ if ( *cur == 'e' &&
+ cur + 3 < limit &&
+ cur[1] == 'n' &&
+ cur[2] == 'd' &&
+ t42_is_space( cur[3] ) )
+ break;
+ if ( *cur == '>' )
+ break;
+
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ return;
+
+ if ( *cur == '/' )
+ {
+ FT_PtrDist len;
+
+
+ if ( cur + 1 >= limit )
+ {
+ FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ cur++; /* skip `/' */
+ len = parser->root.cursor - cur;
+
+ error = T1_Add_Table( name_table, n, cur, len + 1 );
+ if ( error )
+ goto Fail;
+
+ /* add a trailing zero to the name table */
+ name_table->elements[n][len] = '\0';
+
+ /* record index of /.notdef */
+ if ( *cur == '.' &&
+ ft_strcmp( ".notdef",
+ (const char*)(name_table->elements[n]) ) == 0 )
+ {
+ notdef_index = n;
+ notdef_found = 1;
+ }
+
+ T1_Skip_Spaces( parser );
+
+ cur = parser->root.cursor;
+
+ (void)T1_ToInt( parser );
+ if ( parser->root.cursor >= limit )
+ {
+ FT_ERROR(( "t42_parse_charstrings: out of bounds!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ len = parser->root.cursor - cur;
+
+ error = T1_Add_Table( code_table, n, cur, len + 1 );
+ if ( error )
+ goto Fail;
+
+ code_table->elements[n][len] = '\0';
+
+ n++;
+ if ( n >= loader->num_glyphs )
+ break;
+ }
+ }
+
+ loader->num_glyphs = n;
+
+ if ( !notdef_found )
+ {
+ FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph!\n" ));
+ error = T42_Err_Invalid_File_Format;
+ goto Fail;
+ }
+
+ /* if /.notdef does not occupy index 0, do our magic. */
+ if ( ft_strcmp( (const char*)".notdef",
+ (const char*)name_table->elements[0] ) )
+ {
+ /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */
+ /* name and code entries to swap_table. Then place notdef_index */
+ /* name and code entries into swap_table. Then swap name and code */
+ /* entries at indices notdef_index and 0 using values stored in */
+ /* swap_table. */
+
+ /* Index 0 name */
+ error = T1_Add_Table( swap_table, 0,
+ name_table->elements[0],
+ name_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ /* Index 0 code */
+ error = T1_Add_Table( swap_table, 1,
+ code_table->elements[0],
+ code_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ /* Index notdef_index name */
+ error = T1_Add_Table( swap_table, 2,
+ name_table->elements[notdef_index],
+ name_table->lengths [notdef_index] );
+ if ( error )
+ goto Fail;
+
+ /* Index notdef_index code */
+ error = T1_Add_Table( swap_table, 3,
+ code_table->elements[notdef_index],
+ code_table->lengths [notdef_index] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, notdef_index,
+ swap_table->elements[0],
+ swap_table->lengths [0] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, notdef_index,
+ swap_table->elements[1],
+ swap_table->lengths [1] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( name_table, 0,
+ swap_table->elements[2],
+ swap_table->lengths [2] );
+ if ( error )
+ goto Fail;
+
+ error = T1_Add_Table( code_table, 0,
+ swap_table->elements[3],
+ swap_table->lengths [3] );
+ if ( error )
+ goto Fail;
+
+ }
+
+ return;
+
+ Fail:
+ parser->root.error = error;
+ }
+
+
+ static FT_Error
+ t42_load_keyword( T42_Face face,
+ T42_Loader loader,
+ T1_Field field )
+ {
+ FT_Error error;
+ void* dummy_object;
+ void** objects;
+ FT_UInt max_objects = 0;
+
+
+ /* if the keyword has a dedicated callback, call it */
+ if ( field->type == T1_FIELD_TYPE_CALLBACK )
+ {
+ field->reader( (FT_Face)face, loader );
+ error = loader->parser.root.error;
+ goto Exit;
+ }
+
+ /* now the keyword is either a simple field or a table of fields; */
+ /* we are now going to take care of it */
+
+ switch ( field->location )
+ {
+ case T1_FIELD_LOCATION_FONT_INFO:
+ dummy_object = &face->type1.font_info;
+ break;
+
+ case T1_FIELD_LOCATION_BBOX:
+ dummy_object = &face->type1.font_bbox;
+ break;
+
+ default:
+ dummy_object = &face->type1;
+ }
+
+ objects = &dummy_object;
+
+ if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY ||
+ field->type == T1_FIELD_TYPE_FIXED_ARRAY )
+ error = T1_Load_Field_Table( &loader->parser, field,
+ objects, max_objects, 0 );
+ else
+ error = T1_Load_Field( &loader->parser, field,
+ objects, max_objects, 0 );
+
+ Exit:
+ return error;
+ }
+
+
+ FT_LOCAL_DEF( FT_Error )
+ t42_parse_dict( T42_Face face,
+ T42_Loader loader,
+ FT_Byte* base,
+ FT_Long size )
+ {
+ T42_Parser parser = &loader->parser;
+ FT_Byte* limit;
+ FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) /
+ sizeof ( t42_keywords[0] ) );
+
+
+ parser->root.cursor = base;
+ parser->root.limit = base + size;
+ parser->root.error = T42_Err_Ok;
+
+ limit = parser->root.limit;
+
+ T1_Skip_Spaces( parser );
+
+ while ( parser->root.cursor < limit )
+ {
+ FT_Byte* cur;
+
+
+ cur = parser->root.cursor;
+
+ /* look for `FontDirectory' which causes problems for some fonts */
+ if ( *cur == 'F' && cur + 25 < limit &&
+ ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 )
+ {
+ FT_Byte* cur2;
+
+
+ /* skip the `FontDirectory' keyword */
+ T1_Skip_PS_Token( parser );
+ T1_Skip_Spaces ( parser );
+ cur = cur2 = parser->root.cursor;
+
+ /* look up the `known' keyword */
+ while ( cur < limit )
+ {
+ if ( *cur == 'k' && cur + 5 < limit &&
+ ft_strncmp( (char*)cur, "known", 5 ) == 0 )
+ break;
+
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+ T1_Skip_Spaces ( parser );
+ cur = parser->root.cursor;
+ }
+
+ if ( cur < limit )
+ {
+ T1_TokenRec token;
+
+
+ /* skip the `known' keyword and the token following it */
+ T1_Skip_PS_Token( parser );
+ T1_ToToken( parser, &token );
+
+ /* if the last token was an array, skip it! */
+ if ( token.type == T1_TOKEN_TYPE_ARRAY )
+ cur2 = parser->root.cursor;
+ }
+ parser->root.cursor = cur2;
+ }
+
+ /* look for immediates */
+ else if ( *cur == '/' && cur + 2 < limit )
+ {
+ FT_PtrDist len;
+
+
+ cur++;
+
+ parser->root.cursor = cur;
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+
+ len = parser->root.cursor - cur;
+
+ if ( len > 0 && len < 22 && parser->root.cursor < limit )
+ {
+ int i;
+
+
+ /* now compare the immediate name to the keyword table */
+
+ /* loop through all known keywords */
+ for ( i = 0; i < n_keywords; i++ )
+ {
+ T1_Field keyword = (T1_Field)&t42_keywords[i];
+ FT_Byte *name = (FT_Byte*)keyword->ident;
+
+
+ if ( !name )
+ continue;
+
+ if ( cur[0] == name[0] &&
+ len == (FT_PtrDist)ft_strlen( (const char *)name ) &&
+ ft_memcmp( cur, name, len ) == 0 )
+ {
+ /* we found it -- run the parsing callback! */
+ parser->root.error = t42_load_keyword( face,
+ loader,
+ keyword );
+ if ( parser->root.error )
+ return parser->root.error;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ T1_Skip_PS_Token( parser );
+ if ( parser->root.error )
+ goto Exit;
+ }
+
+ T1_Skip_Spaces( parser );
+ }
+
+ Exit:
+ return parser->root.error;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t42_loader_init( T42_Loader loader,
+ T42_Face face )
+ {
+ FT_UNUSED( face );
+
+ FT_MEM_ZERO( loader, sizeof ( *loader ) );
+ loader->num_glyphs = 0;
+ loader->num_chars = 0;
+
+ /* initialize the tables -- simply set their `init' field to 0 */
+ loader->encoding_table.init = 0;
+ loader->charstrings.init = 0;
+ loader->glyph_names.init = 0;
+ }
+
+
+ FT_LOCAL_DEF( void )
+ t42_loader_done( T42_Loader loader )
+ {
+ T42_Parser parser = &loader->parser;
+
+
+ /* finalize tables */
+ T1_Release_Table( &loader->encoding_table );
+ T1_Release_Table( &loader->charstrings );
+ T1_Release_Table( &loader->glyph_names );
+ T1_Release_Table( &loader->swap_table );
+
+ /* finalize parser */
+ t42_parser_done( parser );
+ }
+
+
+/* END */
diff --git a/src/type42/t42parse.h b/src/type42/t42parse.h
new file mode 100644
index 0000000..f77ec4a
--- /dev/null
+++ b/src/type42/t42parse.h
@@ -0,0 +1,90 @@
+/***************************************************************************/
+/* */
+/* t42parse.h */
+/* */
+/* Type 42 font parser (specification). */
+/* */
+/* Copyright 2002, 2003 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T42PARSE_H__
+#define __T42PARSE_H__
+
+
+#include "t42objs.h"
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+ typedef struct T42_ParserRec_
+ {
+ PS_ParserRec root;
+ FT_Stream stream;
+
+ FT_Byte* base_dict;
+ FT_Long base_len;
+
+ FT_Bool in_memory;
+
+ } T42_ParserRec, *T42_Parser;
+
+
+ typedef struct T42_Loader_
+ {
+ T42_ParserRec parser; /* parser used to read the stream */
+
+ FT_UInt num_chars; /* number of characters in encoding */
+ PS_TableRec encoding_table; /* PS_Table used to store the */
+ /* encoding character names */
+
+ FT_UInt num_glyphs;
+ PS_TableRec glyph_names;
+ PS_TableRec charstrings;
+ PS_TableRec swap_table; /* For moving .notdef glyph to index 0. */
+
+ } T42_LoaderRec, *T42_Loader;
+
+
+ FT_LOCAL( FT_Error )
+ t42_parser_init( T42_Parser parser,
+ FT_Stream stream,
+ FT_Memory memory,
+ PSAux_Service psaux );
+
+ FT_LOCAL( void )
+ t42_parser_done( T42_Parser parser );
+
+
+ FT_LOCAL( FT_Error )
+ t42_parse_dict( T42_Face face,
+ T42_Loader loader,
+ FT_Byte* base,
+ FT_Long size );
+
+
+ FT_LOCAL( void )
+ t42_loader_init( T42_Loader loader,
+ T42_Face face );
+
+ FT_LOCAL( void )
+ t42_loader_done( T42_Loader loader );
+
+
+ /* */
+
+FT_END_HEADER
+
+
+#endif /* __T42PARSE_H__ */
+
+
+/* END */
diff --git a/src/type42/t42types.h b/src/type42/t42types.h
new file mode 100644
index 0000000..6626b04
--- /dev/null
+++ b/src/type42/t42types.h
@@ -0,0 +1,54 @@
+/***************************************************************************/
+/* */
+/* t42types.h */
+/* */
+/* Type 42 font data types (specification only). */
+/* */
+/* Copyright 2002, 2003, 2006 by Roberto Alameda. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __T42TYPES_H__
+#define __T42TYPES_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TYPE1_TABLES_H
+#include FT_INTERNAL_TYPE1_TYPES_H
+#include FT_INTERNAL_POSTSCRIPT_HINTS_H
+
+
+FT_BEGIN_HEADER
+
+
+ typedef struct T42_FaceRec_
+ {
+ FT_FaceRec root;
+ T1_FontRec type1;
+ const void* psnames;
+ const void* psaux;
+ const void* afm_data;
+ FT_Byte* ttf_data;
+ FT_ULong ttf_size;
+ FT_Face ttf_face;
+ FT_CharMapRec charmaprecs[2];
+ FT_CharMap charmaps[2];
+ PS_UnicodesRec unicode_map;
+
+ } T42_FaceRec, *T42_Face;
+
+
+FT_END_HEADER
+
+#endif /* __T1TYPES_H__ */
+
+
+/* END */
diff --git a/src/type42/type42.c b/src/type42/type42.c
new file mode 100644
index 0000000..d13df56
--- /dev/null
+++ b/src/type42/type42.c
@@ -0,0 +1,25 @@
+/***************************************************************************/
+/* */
+/* type42.c */
+/* */
+/* FreeType Type 42 driver component. */
+/* */
+/* Copyright 2002 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include <ft2build.h>
+#include "t42objs.c"
+#include "t42parse.c"
+#include "t42drivr.c"
+
+/* END */
diff --git a/src/winfonts/Jamfile b/src/winfonts/Jamfile
new file mode 100644
index 0000000..71cf567
--- /dev/null
+++ b/src/winfonts/Jamfile
@@ -0,0 +1,16 @@
+# FreeType 2 src/winfonts Jamfile
+#
+# Copyright 2001 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+SubDir FT2_TOP $(FT2_SRC_DIR) winfonts ;
+
+Library $(FT2_LIB) : winfnt.c ;
+
+# end of src/winfonts Jamfile
diff --git a/src/winfonts/fnterrs.h b/src/winfonts/fnterrs.h
new file mode 100644
index 0000000..ea80909
--- /dev/null
+++ b/src/winfonts/fnterrs.h
@@ -0,0 +1,41 @@
+/***************************************************************************/
+/* */
+/* fnterrs.h */
+/* */
+/* Win FNT/FON error codes (specification only). */
+/* */
+/* Copyright 2001 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+ /*************************************************************************/
+ /* */
+ /* This file is used to define the Windows FNT/FON error enumeration */
+ /* constants. */
+ /* */
+ /*************************************************************************/
+
+#ifndef __FNTERRS_H__
+#define __FNTERRS_H__
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#define FT_ERR_PREFIX FNT_Err_
+#define FT_ERR_BASE FT_Mod_Err_Winfonts
+
+#include FT_ERRORS_H
+
+#endif /* __FNTERRS_H__ */
+
+
+/* END */
diff --git a/src/winfonts/module.mk b/src/winfonts/module.mk
new file mode 100644
index 0000000..0ace3ae
--- /dev/null
+++ b/src/winfonts/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 Windows FNT/FON module definition
+#
+
+
+# Copyright 1996-2000, 2006 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += WINDOWS_DRIVER
+
+define WINDOWS_DRIVER
+$(OPEN_DRIVER)winfnt_driver_class$(CLOSE_DRIVER)
+$(ECHO_DRIVER)winfnt $(ECHO_DRIVER_DESC)Windows bitmap fonts with extension *.fnt or *.fon$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/winfonts/rules.mk b/src/winfonts/rules.mk
new file mode 100644
index 0000000..71a7df2
--- /dev/null
+++ b/src/winfonts/rules.mk
@@ -0,0 +1,65 @@
+#
+# FreeType 2 Windows FNT/FON driver configuration rules
+#
+
+
+# Copyright 1996-2000, 2001, 2003 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT. By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# Windows driver directory
+#
+FNT_DIR := $(SRC_DIR)/winfonts
+
+
+FNT_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(FNT_DIR))
+
+
+# Windows driver sources (i.e., C files)
+#
+FNT_DRV_SRC := $(FNT_DIR)/winfnt.c
+
+# Windows driver headers
+#
+FNT_DRV_H := $(FNT_DRV_SRC:%.c=%.h) \
+ $(FNT_DIR)/fnterrs.h
+
+
+# Windows driver object(s)
+#
+# FNT_DRV_OBJ_M is used during `multi' builds
+# FNT_DRV_OBJ_S is used during `single' builds
+#
+FNT_DRV_OBJ_M := $(FNT_DRV_SRC:$(FNT_DIR)/%.c=$(OBJ_DIR)/%.$O)
+FNT_DRV_OBJ_S := $(OBJ_DIR)/winfnt.$O
+
+# Windows driver source file for single build
+#
+FNT_DRV_SRC_S := $(FNT_DIR)/winfnt.c
+
+
+# Windows driver - single object
+#
+$(FNT_DRV_OBJ_S): $(FNT_DRV_SRC_S) $(FNT_DRV_SRC) $(FREETYPE_H) $(FNT_DRV_H)
+ $(FNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(FNT_DRV_SRC_S))
+
+
+# Windows driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(FNT_DIR)/%.c $(FREETYPE_H) $(FNT_DRV_H)
+ $(FNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(FNT_DRV_OBJ_S)
+DRV_OBJS_M += $(FNT_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/winfonts/winfnt.c b/src/winfonts/winfnt.c
new file mode 100644
index 0000000..4aa9744
--- /dev/null
+++ b/src/winfonts/winfnt.c
@@ -0,0 +1,1122 @@
+/***************************************************************************/
+/* */
+/* winfnt.c */
+/* */
+/* FreeType font driver for Windows FNT/FON files */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2006, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* Copyright 2003 Huw D M Davies for Codeweavers */
+/* Copyright 2007 Dmitry Timoshkov for Codeweavers */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_WINFONTS_H
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_OBJECTS_H
+
+#include "winfnt.h"
+#include "fnterrs.h"
+#include FT_SERVICE_WINFNT_H
+#include FT_SERVICE_XFREE86_NAME_H
+
+ /*************************************************************************/
+ /* */
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
+ /* messages during execution. */
+ /* */
+#undef FT_COMPONENT
+#define FT_COMPONENT trace_winfnt
+
+
+ static const FT_Frame_Field winmz_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinMZ_HeaderRec
+
+ FT_FRAME_START( 64 ),
+ FT_FRAME_USHORT_LE ( magic ),
+ FT_FRAME_SKIP_BYTES( 29 * 2 ),
+ FT_FRAME_ULONG_LE ( lfanew ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winne_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinNE_HeaderRec
+
+ FT_FRAME_START( 40 ),
+ FT_FRAME_USHORT_LE ( magic ),
+ FT_FRAME_SKIP_BYTES( 34 ),
+ FT_FRAME_USHORT_LE ( resource_tab_offset ),
+ FT_FRAME_USHORT_LE ( rname_tab_offset ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winpe32_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinPE32_HeaderRec
+
+ FT_FRAME_START( 248 ),
+ FT_FRAME_ULONG_LE ( magic ), /* PE00 */
+ FT_FRAME_USHORT_LE ( machine ), /* 0x014c - i386 */
+ FT_FRAME_USHORT_LE ( number_of_sections ),
+ FT_FRAME_SKIP_BYTES( 12 ),
+ FT_FRAME_USHORT_LE ( size_of_optional_header ),
+ FT_FRAME_SKIP_BYTES( 2 ),
+ FT_FRAME_USHORT_LE ( magic32 ), /* 0x10b */
+ FT_FRAME_SKIP_BYTES( 110 ),
+ FT_FRAME_ULONG_LE ( rsrc_virtual_address ),
+ FT_FRAME_ULONG_LE ( rsrc_size ),
+ FT_FRAME_SKIP_BYTES( 104 ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winpe32_section_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinPE32_SectionRec
+
+ FT_FRAME_START( 40 ),
+ FT_FRAME_BYTES ( name, 8 ),
+ FT_FRAME_SKIP_BYTES( 4 ),
+ FT_FRAME_ULONG_LE ( virtual_address ),
+ FT_FRAME_ULONG_LE ( size_of_raw_data ),
+ FT_FRAME_ULONG_LE ( pointer_to_raw_data ),
+ FT_FRAME_SKIP_BYTES( 16 ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winpe_rsrc_dir_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinPE_RsrcDirRec
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_ULONG_LE ( characteristics ),
+ FT_FRAME_ULONG_LE ( time_date_stamp ),
+ FT_FRAME_USHORT_LE( major_version ),
+ FT_FRAME_USHORT_LE( minor_version ),
+ FT_FRAME_USHORT_LE( number_of_named_entries ),
+ FT_FRAME_USHORT_LE( number_of_id_entries ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winpe_rsrc_dir_entry_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinPE_RsrcDirEntryRec
+
+ FT_FRAME_START( 8 ),
+ FT_FRAME_ULONG_LE( name ),
+ FT_FRAME_ULONG_LE( offset ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winpe_rsrc_data_entry_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE WinPE_RsrcDataEntryRec
+
+ FT_FRAME_START( 16 ),
+ FT_FRAME_ULONG_LE( offset_to_data ),
+ FT_FRAME_ULONG_LE( size ),
+ FT_FRAME_ULONG_LE( code_page ),
+ FT_FRAME_ULONG_LE( reserved ),
+ FT_FRAME_END
+ };
+
+ static const FT_Frame_Field winfnt_header_fields[] =
+ {
+#undef FT_STRUCTURE
+#define FT_STRUCTURE FT_WinFNT_HeaderRec
+
+ FT_FRAME_START( 148 ),
+ FT_FRAME_USHORT_LE( version ),
+ FT_FRAME_ULONG_LE ( file_size ),
+ FT_FRAME_BYTES ( copyright, 60 ),
+ FT_FRAME_USHORT_LE( file_type ),
+ FT_FRAME_USHORT_LE( nominal_point_size ),
+ FT_FRAME_USHORT_LE( vertical_resolution ),
+ FT_FRAME_USHORT_LE( horizontal_resolution ),
+ FT_FRAME_USHORT_LE( ascent ),
+ FT_FRAME_USHORT_LE( internal_leading ),
+ FT_FRAME_USHORT_LE( external_leading ),
+ FT_FRAME_BYTE ( italic ),
+ FT_FRAME_BYTE ( underline ),
+ FT_FRAME_BYTE ( strike_out ),
+ FT_FRAME_USHORT_LE( weight ),
+ FT_FRAME_BYTE ( charset ),
+ FT_FRAME_USHORT_LE( pixel_width ),
+ FT_FRAME_USHORT_LE( pixel_height ),
+ FT_FRAME_BYTE ( pitch_and_family ),
+ FT_FRAME_USHORT_LE( avg_width ),
+ FT_FRAME_USHORT_LE( max_width ),
+ FT_FRAME_BYTE ( first_char ),
+ FT_FRAME_BYTE ( last_char ),
+ FT_FRAME_BYTE ( default_char ),
+ FT_FRAME_BYTE ( break_char ),
+ FT_FRAME_USHORT_LE( bytes_per_row ),
+ FT_FRAME_ULONG_LE ( device_offset ),
+ FT_FRAME_ULONG_LE ( face_name_offset ),
+ FT_FRAME_ULONG_LE ( bits_pointer ),
+ FT_FRAME_ULONG_LE ( bits_offset ),
+ FT_FRAME_BYTE ( reserved ),
+ FT_FRAME_ULONG_LE ( flags ),
+ FT_FRAME_USHORT_LE( A_space ),
+ FT_FRAME_USHORT_LE( B_space ),
+ FT_FRAME_USHORT_LE( C_space ),
+ FT_FRAME_ULONG_LE ( color_table_offset ),
+ FT_FRAME_BYTES ( reserved1, 16 ),
+ FT_FRAME_END
+ };
+
+
+ static void
+ fnt_font_done( FNT_Face face )
+ {
+ FT_Memory memory = FT_FACE( face )->memory;
+ FT_Stream stream = FT_FACE( face )->stream;
+ FNT_Font font = face->font;
+
+
+ if ( !font )
+ return;
+
+ if ( font->fnt_frame )
+ FT_FRAME_RELEASE( font->fnt_frame );
+ FT_FREE( font->family_name );
+
+ FT_FREE( font );
+ face->font = 0;
+ }
+
+
+ static FT_Error
+ fnt_font_load( FNT_Font font,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_WinFNT_Header header = &font->header;
+ FT_Bool new_format;
+ FT_UInt size;
+
+
+ /* first of all, read the FNT header */
+ if ( FT_STREAM_SEEK( font->offset ) ||
+ FT_STREAM_READ_FIELDS( winfnt_header_fields, header ) )
+ goto Exit;
+
+ /* check header */
+ if ( header->version != 0x200 &&
+ header->version != 0x300 )
+ {
+ FT_TRACE2(( "[not a valid FNT file]\n" ));
+ error = FNT_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ new_format = FT_BOOL( font->header.version == 0x300 );
+ size = new_format ? 148 : 118;
+
+ if ( header->file_size < size )
+ {
+ FT_TRACE2(( "[not a valid FNT file]\n" ));
+ error = FNT_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* Version 2 doesn't have these fields */
+ if ( header->version == 0x200 )
+ {
+ header->flags = 0;
+ header->A_space = 0;
+ header->B_space = 0;
+ header->C_space = 0;
+
+ header->color_table_offset = 0;
+ }
+
+ if ( header->file_type & 1 )
+ {
+ FT_TRACE2(( "[can't handle vector FNT fonts]\n" ));
+ error = FNT_Err_Unknown_File_Format;
+ goto Exit;
+ }
+
+ /* this is a FNT file/table; extract its frame */
+ if ( FT_STREAM_SEEK( font->offset ) ||
+ FT_FRAME_EXTRACT( header->file_size, font->fnt_frame ) )
+ goto Exit;
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ fnt_face_get_dll_font( FNT_Face face,
+ FT_Int face_index )
+ {
+ FT_Error error;
+ FT_Stream stream = FT_FACE( face )->stream;
+ FT_Memory memory = FT_FACE( face )->memory;
+ WinMZ_HeaderRec mz_header;
+
+
+ face->font = 0;
+
+ /* does it begin with an MZ header? */
+ if ( FT_STREAM_SEEK( 0 ) ||
+ FT_STREAM_READ_FIELDS( winmz_header_fields, &mz_header ) )
+ goto Exit;
+
+ error = FNT_Err_Unknown_File_Format;
+ if ( mz_header.magic == WINFNT_MZ_MAGIC )
+ {
+ /* yes, now look for an NE header in the file */
+ WinNE_HeaderRec ne_header;
+
+
+ FT_TRACE2(( "MZ signature found\n" ));
+
+ if ( FT_STREAM_SEEK( mz_header.lfanew ) ||
+ FT_STREAM_READ_FIELDS( winne_header_fields, &ne_header ) )
+ goto Exit;
+
+ error = FNT_Err_Unknown_File_Format;
+ if ( ne_header.magic == WINFNT_NE_MAGIC )
+ {
+ /* good, now look into the resource table for each FNT resource */
+ FT_ULong res_offset = mz_header.lfanew +
+ ne_header.resource_tab_offset;
+ FT_UShort size_shift;
+ FT_UShort font_count = 0;
+ FT_ULong font_offset = 0;
+
+
+ FT_TRACE2(( "NE signature found\n" ));
+
+ if ( FT_STREAM_SEEK( res_offset ) ||
+ FT_FRAME_ENTER( ne_header.rname_tab_offset -
+ ne_header.resource_tab_offset ) )
+ goto Exit;
+
+ size_shift = FT_GET_USHORT_LE();
+
+ for (;;)
+ {
+ FT_UShort type_id, count;
+
+
+ type_id = FT_GET_USHORT_LE();
+ if ( !type_id )
+ break;
+
+ count = FT_GET_USHORT_LE();
+
+ if ( type_id == 0x8008U )
+ {
+ font_count = count;
+ font_offset = (FT_ULong)( FT_STREAM_POS() + 4 +
+ ( stream->cursor - stream->limit ) );
+ break;
+ }
+
+ stream->cursor += 4 + count * 12;
+ }
+
+ FT_FRAME_EXIT();
+
+ if ( !font_count || !font_offset )
+ {
+ FT_TRACE2(( "this file doesn't contain any FNT resources!\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* loading `winfnt_header_fields' needs at least 118 bytes; */
+ /* use this as a rough measure to check the expected font size */
+ if ( font_count * 118UL > stream->size )
+ {
+ FT_TRACE2(( "invalid number of faces\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ face->root.num_faces = font_count;
+
+ if ( face_index >= font_count )
+ {
+ error = FNT_Err_Bad_Argument;
+ goto Exit;
+ }
+
+ if ( FT_NEW( face->font ) )
+ goto Exit;
+
+ if ( FT_STREAM_SEEK( font_offset + face_index * 12 ) ||
+ FT_FRAME_ENTER( 12 ) )
+ goto Fail;
+
+ face->font->offset = (FT_ULong)FT_GET_USHORT_LE() << size_shift;
+ face->font->fnt_size = (FT_ULong)FT_GET_USHORT_LE() << size_shift;
+
+ stream->cursor += 8;
+
+ FT_FRAME_EXIT();
+
+ error = fnt_font_load( face->font, stream );
+ }
+ else if ( ne_header.magic == WINFNT_PE_MAGIC )
+ {
+ WinPE32_HeaderRec pe32_header;
+ WinPE32_SectionRec pe32_section;
+ WinPE_RsrcDirRec root_dir, name_dir, lang_dir;
+ WinPE_RsrcDirEntryRec dir_entry1, dir_entry2, dir_entry3;
+ WinPE_RsrcDataEntryRec data_entry;
+
+ FT_Long root_dir_offset, name_dir_offset, lang_dir_offset;
+ FT_UShort i, j, k;
+
+
+ FT_TRACE2(( "PE signature found\n" ));
+
+ if ( FT_STREAM_SEEK( mz_header.lfanew ) ||
+ FT_STREAM_READ_FIELDS( winpe32_header_fields, &pe32_header ) )
+ goto Exit;
+
+ FT_TRACE2(( "magic %04lx, machine %02x, number_of_sections %u, "
+ "size_of_optional_header %02x\n"
+ "magic32 %02x, rsrc_virtual_address %04lx, "
+ "rsrc_size %04lx\n",
+ pe32_header.magic, pe32_header.machine,
+ pe32_header.number_of_sections,
+ pe32_header.size_of_optional_header,
+ pe32_header.magic32, pe32_header.rsrc_virtual_address,
+ pe32_header.rsrc_size ));
+
+ if ( pe32_header.magic != WINFNT_PE_MAGIC /* check full signature */ ||
+ pe32_header.machine != 0x014c /* i386 */ ||
+ pe32_header.size_of_optional_header != 0xe0 /* FIXME */ ||
+ pe32_header.magic32 != 0x10b )
+ {
+ FT_TRACE2(( "this file has an invalid PE header\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ face->root.num_faces = 0;
+
+ for ( i = 0; i < pe32_header.number_of_sections; i++ )
+ {
+ if ( FT_STREAM_READ_FIELDS( winpe32_section_fields,
+ &pe32_section ) )
+ goto Exit;
+
+ FT_TRACE2(( "name %.8s, va %04lx, size %04lx, offset %04lx\n",
+ pe32_section.name, pe32_section.virtual_address,
+ pe32_section.size_of_raw_data,
+ pe32_section.pointer_to_raw_data ));
+
+ if ( pe32_header.rsrc_virtual_address ==
+ pe32_section.virtual_address )
+ goto Found_rsrc_section;
+ }
+
+ FT_TRACE2(( "this file doesn't contain any resources\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+
+ Found_rsrc_section:
+ FT_TRACE2(( "found resources section %.8s\n", pe32_section.name ));
+
+ if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &root_dir ) )
+ goto Exit;
+
+ root_dir_offset = pe32_section.pointer_to_raw_data;
+
+ for ( i = 0; i < root_dir.number_of_named_entries +
+ root_dir.number_of_id_entries; i++ )
+ {
+ if ( FT_STREAM_SEEK( root_dir_offset + 16 + i * 8 ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields,
+ &dir_entry1 ) )
+ goto Exit;
+
+ if ( !(dir_entry1.offset & 0x80000000UL ) /* DataIsDirectory */ )
+ {
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ dir_entry1.offset &= ~0x80000000UL;
+
+ name_dir_offset = pe32_section.pointer_to_raw_data +
+ dir_entry1.offset;
+
+ if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data +
+ dir_entry1.offset ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &name_dir ) )
+ goto Exit;
+
+ for ( j = 0; j < name_dir.number_of_named_entries +
+ name_dir.number_of_id_entries; j++ )
+ {
+ if ( FT_STREAM_SEEK( name_dir_offset + 16 + j * 8 ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields,
+ &dir_entry2 ) )
+ goto Exit;
+
+ if ( !(dir_entry2.offset & 0x80000000UL ) /* DataIsDirectory */ )
+ {
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ dir_entry2.offset &= ~0x80000000UL;
+
+ lang_dir_offset = pe32_section.pointer_to_raw_data +
+ dir_entry2.offset;
+
+ if ( FT_STREAM_SEEK( pe32_section.pointer_to_raw_data +
+ dir_entry2.offset ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_fields, &lang_dir ) )
+ goto Exit;
+
+ for ( k = 0; k < lang_dir.number_of_named_entries +
+ lang_dir.number_of_id_entries; k++ )
+ {
+ if ( FT_STREAM_SEEK( lang_dir_offset + 16 + k * 8 ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_dir_entry_fields,
+ &dir_entry3 ) )
+ goto Exit;
+
+ if ( dir_entry2.offset & 0x80000000UL /* DataIsDirectory */ )
+ {
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( dir_entry1.name == 8 /* RT_FONT */ )
+ {
+ if ( FT_STREAM_SEEK( root_dir_offset + dir_entry3.offset ) ||
+ FT_STREAM_READ_FIELDS( winpe_rsrc_data_entry_fields,
+ &data_entry ) )
+ goto Exit;
+
+ FT_TRACE2(( "found font #%lu, offset %04lx, "
+ "size %04lx, cp %lu\n",
+ dir_entry2.name,
+ pe32_section.pointer_to_raw_data +
+ data_entry.offset_to_data -
+ pe32_section.virtual_address,
+ data_entry.size, data_entry.code_page ));
+
+ if ( face_index == face->root.num_faces )
+ {
+ if ( FT_NEW( face->font ) )
+ goto Exit;
+
+ face->font->offset = pe32_section.pointer_to_raw_data +
+ data_entry.offset_to_data -
+ pe32_section.virtual_address;
+ face->font->fnt_size = data_entry.size;
+
+ error = fnt_font_load( face->font, stream );
+ if ( error )
+ {
+ FT_TRACE2(( "font #%lu load error %d\n",
+ dir_entry2.name, error ));
+ goto Fail;
+ }
+ else
+ FT_TRACE2(( "font #%lu successfully loaded\n",
+ dir_entry2.name ));
+ }
+
+ face->root.num_faces++;
+ }
+ }
+ }
+ }
+ }
+
+ if ( !face->root.num_faces )
+ {
+ FT_TRACE2(( "this file doesn't contain any RT_FONT resources\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ if ( face_index >= face->root.num_faces )
+ {
+ error = FNT_Err_Bad_Argument;
+ goto Exit;
+ }
+ }
+
+ Fail:
+ if ( error )
+ fnt_font_done( face );
+
+ Exit:
+ return error;
+ }
+
+
+ typedef struct FNT_CMapRec_
+ {
+ FT_CMapRec cmap;
+ FT_UInt32 first;
+ FT_UInt32 count;
+
+ } FNT_CMapRec, *FNT_CMap;
+
+
+ static FT_Error
+ fnt_cmap_init( FNT_CMap cmap )
+ {
+ FNT_Face face = (FNT_Face)FT_CMAP_FACE( cmap );
+ FNT_Font font = face->font;
+
+
+ cmap->first = (FT_UInt32) font->header.first_char;
+ cmap->count = (FT_UInt32)( font->header.last_char - cmap->first + 1 );
+
+ return 0;
+ }
+
+
+ static FT_UInt
+ fnt_cmap_char_index( FNT_CMap cmap,
+ FT_UInt32 char_code )
+ {
+ FT_UInt gindex = 0;
+
+
+ char_code -= cmap->first;
+ if ( char_code < cmap->count )
+ gindex = char_code + 1; /* we artificially increase the glyph index; */
+ /* FNT_Load_Glyph reverts to the right one */
+ return gindex;
+ }
+
+
+ static FT_UInt
+ fnt_cmap_char_next( FNT_CMap cmap,
+ FT_UInt32 *pchar_code )
+ {
+ FT_UInt gindex = 0;
+ FT_UInt32 result = 0;
+ FT_UInt32 char_code = *pchar_code + 1;
+
+
+ if ( char_code <= cmap->first )
+ {
+ result = cmap->first;
+ gindex = 1;
+ }
+ else
+ {
+ char_code -= cmap->first;
+ if ( char_code < cmap->count )
+ {
+ result = cmap->first + char_code;
+ gindex = char_code + 1;
+ }
+ }
+
+ *pchar_code = result;
+ return gindex;
+ }
+
+
+ static const FT_CMap_ClassRec fnt_cmap_class_rec =
+ {
+ sizeof ( FNT_CMapRec ),
+
+ (FT_CMap_InitFunc) fnt_cmap_init,
+ (FT_CMap_DoneFunc) NULL,
+ (FT_CMap_CharIndexFunc)fnt_cmap_char_index,
+ (FT_CMap_CharNextFunc) fnt_cmap_char_next
+ };
+
+ static FT_CMap_Class const fnt_cmap_class = &fnt_cmap_class_rec;
+
+
+ static void
+ FNT_Face_Done( FNT_Face face )
+ {
+ if ( face )
+ {
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+
+ fnt_font_done( face );
+
+ FT_FREE( face->root.available_sizes );
+ face->root.num_fixed_sizes = 0;
+ }
+ }
+
+
+ static FT_Error
+ FNT_Face_Init( FT_Stream stream,
+ FNT_Face face,
+ FT_Int face_index,
+ FT_Int num_params,
+ FT_Parameter* params )
+ {
+ FT_Error error;
+ FT_Memory memory = FT_FACE_MEMORY( face );
+
+ FT_UNUSED( num_params );
+ FT_UNUSED( params );
+
+
+ /* try to load font from a DLL */
+ error = fnt_face_get_dll_font( face, face_index );
+ if ( error == FNT_Err_Unknown_File_Format )
+ {
+ /* this didn't work; try to load a single FNT font */
+ FNT_Font font;
+
+
+ if ( face_index > 0 )
+ {
+ error = FNT_Err_Bad_Argument;
+ goto Exit;
+ }
+
+ if ( FT_NEW( face->font ) )
+ goto Exit;
+
+ face->root.num_faces = 1;
+
+ font = face->font;
+ font->offset = 0;
+ font->fnt_size = stream->size;
+
+ error = fnt_font_load( font, stream );
+ }
+
+ if ( error )
+ goto Fail;
+
+ /* we now need to fill the root FT_Face fields */
+ /* with relevant information */
+ {
+ FT_Face root = FT_FACE( face );
+ FNT_Font font = face->font;
+ FT_PtrDist family_size;
+
+
+ root->face_flags = FT_FACE_FLAG_FIXED_SIZES |
+ FT_FACE_FLAG_HORIZONTAL;
+
+ if ( font->header.avg_width == font->header.max_width )
+ root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
+
+ if ( font->header.italic )
+ root->style_flags |= FT_STYLE_FLAG_ITALIC;
+
+ if ( font->header.weight >= 800 )
+ root->style_flags |= FT_STYLE_FLAG_BOLD;
+
+ /* set up the `fixed_sizes' array */
+ if ( FT_NEW_ARRAY( root->available_sizes, 1 ) )
+ goto Fail;
+
+ root->num_fixed_sizes = 1;
+
+ {
+ FT_Bitmap_Size* bsize = root->available_sizes;
+ FT_UShort x_res, y_res;
+
+
+ bsize->width = font->header.avg_width;
+ bsize->height = (FT_Short)(
+ font->header.pixel_height + font->header.external_leading );
+ bsize->size = font->header.nominal_point_size << 6;
+
+ x_res = font->header.horizontal_resolution;
+ if ( !x_res )
+ x_res = 72;
+
+ y_res = font->header.vertical_resolution;
+ if ( !y_res )
+ y_res = 72;
+
+ bsize->y_ppem = FT_MulDiv( bsize->size, y_res, 72 );
+ bsize->y_ppem = FT_PIX_ROUND( bsize->y_ppem );
+
+ /*
+ * this reads:
+ *
+ * the nominal height is larger than the bbox's height
+ *
+ * => nominal_point_size contains incorrect value;
+ * use pixel_height as the nominal height
+ */
+ if ( bsize->y_ppem > font->header.pixel_height << 6 )
+ {
+ FT_TRACE2(( "use pixel_height as the nominal height\n" ));
+
+ bsize->y_ppem = font->header.pixel_height << 6;
+ bsize->size = FT_MulDiv( bsize->y_ppem, 72, y_res );
+ }
+
+ bsize->x_ppem = FT_MulDiv( bsize->size, x_res, 72 );
+ bsize->x_ppem = FT_PIX_ROUND( bsize->x_ppem );
+ }
+
+ {
+ FT_CharMapRec charmap;
+
+
+ charmap.encoding = FT_ENCODING_NONE;
+ charmap.platform_id = 0;
+ charmap.encoding_id = 0;
+ charmap.face = root;
+
+ if ( font->header.charset == FT_WinFNT_ID_MAC )
+ {
+ charmap.encoding = FT_ENCODING_APPLE_ROMAN;
+ charmap.platform_id = 1;
+/* charmap.encoding_id = 0; */
+ }
+
+ error = FT_CMap_New( fnt_cmap_class,
+ NULL,
+ &charmap,
+ NULL );
+ if ( error )
+ goto Fail;
+
+ /* Select default charmap */
+ if ( root->num_charmaps )
+ root->charmap = root->charmaps[0];
+ }
+
+ /* setup remaining flags */
+
+ /* reserve one slot for the .notdef glyph at index 0 */
+ root->num_glyphs = font->header.last_char -
+ font->header.first_char + 1 + 1;
+
+ if ( font->header.face_name_offset >= font->header.file_size )
+ {
+ FT_TRACE2(( "invalid family name offset!\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Fail;
+ }
+ family_size = font->header.file_size - font->header.face_name_offset;
+ /* Some broken fonts don't delimit the face name with a final */
+ /* NULL byte -- the frame is erroneously one byte too small. */
+ /* We thus allocate one more byte, setting it explicitly to */
+ /* zero. */
+ if ( FT_ALLOC( font->family_name, family_size + 1 ) )
+ goto Fail;
+
+ FT_MEM_COPY( font->family_name,
+ font->fnt_frame + font->header.face_name_offset,
+ family_size );
+
+ font->family_name[family_size] = '\0';
+
+ if ( FT_REALLOC( font->family_name,
+ family_size,
+ ft_strlen( font->family_name ) + 1 ) )
+ goto Fail;
+
+ root->family_name = font->family_name;
+ root->style_name = (char *)"Regular";
+
+ if ( root->style_flags & FT_STYLE_FLAG_BOLD )
+ {
+ if ( root->style_flags & FT_STYLE_FLAG_ITALIC )
+ root->style_name = (char *)"Bold Italic";
+ else
+ root->style_name = (char *)"Bold";
+ }
+ else if ( root->style_flags & FT_STYLE_FLAG_ITALIC )
+ root->style_name = (char *)"Italic";
+ }
+ goto Exit;
+
+ Fail:
+ FNT_Face_Done( face );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ FNT_Size_Select( FT_Size size )
+ {
+ FNT_Face face = (FNT_Face)size->face;
+ FT_WinFNT_Header header = &face->font->header;
+
+
+ FT_Select_Metrics( size->face, 0 );
+
+ size->metrics.ascender = header->ascent * 64;
+ size->metrics.descender = -( header->pixel_height -
+ header->ascent ) * 64;
+ size->metrics.max_advance = header->max_width * 64;
+
+ return FNT_Err_Ok;
+ }
+
+
+ static FT_Error
+ FNT_Size_Request( FT_Size size,
+ FT_Size_Request req )
+ {
+ FNT_Face face = (FNT_Face)size->face;
+ FT_WinFNT_Header header = &face->font->header;
+ FT_Bitmap_Size* bsize = size->face->available_sizes;
+ FT_Error error = FNT_Err_Invalid_Pixel_Size;
+ FT_Long height;
+
+
+ height = FT_REQUEST_HEIGHT( req );
+ height = ( height + 32 ) >> 6;
+
+ switch ( req->type )
+ {
+ case FT_SIZE_REQUEST_TYPE_NOMINAL:
+ if ( height == ( bsize->y_ppem + 32 ) >> 6 )
+ error = FNT_Err_Ok;
+ break;
+
+ case FT_SIZE_REQUEST_TYPE_REAL_DIM:
+ if ( height == header->pixel_height )
+ error = FNT_Err_Ok;
+ break;
+
+ default:
+ error = FNT_Err_Unimplemented_Feature;
+ break;
+ }
+
+ if ( error )
+ return error;
+ else
+ return FNT_Size_Select( size );
+ }
+
+
+ static FT_Error
+ FNT_Load_Glyph( FT_GlyphSlot slot,
+ FT_Size size,
+ FT_UInt glyph_index,
+ FT_Int32 load_flags )
+ {
+ FNT_Face face = (FNT_Face)FT_SIZE_FACE( size );
+ FNT_Font font = face->font;
+ FT_Error error = FNT_Err_Ok;
+ FT_Byte* p;
+ FT_Int len;
+ FT_Bitmap* bitmap = &slot->bitmap;
+ FT_ULong offset;
+ FT_Bool new_format;
+
+ FT_UNUSED( load_flags );
+
+
+ if ( !face || !font ||
+ glyph_index >= (FT_UInt)( FT_FACE( face )->num_glyphs ) )
+ {
+ error = FNT_Err_Invalid_Argument;
+ goto Exit;
+ }
+
+ if ( glyph_index > 0 )
+ glyph_index--; /* revert to real index */
+ else
+ glyph_index = font->header.default_char; /* the .notdef glyph */
+
+ new_format = FT_BOOL( font->header.version == 0x300 );
+ len = new_format ? 6 : 4;
+
+ /* jump to glyph entry */
+ p = font->fnt_frame + ( new_format ? 148 : 118 ) + len * glyph_index;
+
+ bitmap->width = FT_NEXT_SHORT_LE( p );
+
+ if ( new_format )
+ offset = FT_NEXT_ULONG_LE( p );
+ else
+ offset = FT_NEXT_USHORT_LE( p );
+
+ if ( offset >= font->header.file_size )
+ {
+ FT_TRACE2(( "invalid FNT offset!\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* jump to glyph data */
+ p = font->fnt_frame + /* font->header.bits_offset */ + offset;
+
+ /* allocate and build bitmap */
+ {
+ FT_Memory memory = FT_FACE_MEMORY( slot->face );
+ FT_Int pitch = ( bitmap->width + 7 ) >> 3;
+ FT_Byte* column;
+ FT_Byte* write;
+
+
+ bitmap->pitch = pitch;
+ bitmap->rows = font->header.pixel_height;
+ bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
+
+ if ( offset + pitch * bitmap->rows >= font->header.file_size )
+ {
+ FT_TRACE2(( "invalid bitmap width\n" ));
+ error = FNT_Err_Invalid_File_Format;
+ goto Exit;
+ }
+
+ /* note: since glyphs are stored in columns and not in rows we */
+ /* can't use ft_glyphslot_set_bitmap */
+ if ( FT_ALLOC_MULT( bitmap->buffer, pitch, bitmap->rows ) )
+ goto Exit;
+
+ column = (FT_Byte*)bitmap->buffer;
+
+ for ( ; pitch > 0; pitch--, column++ )
+ {
+ FT_Byte* limit = p + bitmap->rows;
+
+
+ for ( write = column; p < limit; p++, write += bitmap->pitch )
+ *write = *p;
+ }
+ }
+
+ slot->internal->flags = FT_GLYPH_OWN_BITMAP;
+ slot->bitmap_left = 0;
+ slot->bitmap_top = font->header.ascent;
+ slot->format = FT_GLYPH_FORMAT_BITMAP;
+
+ /* now set up metrics */
+ slot->metrics.width = bitmap->width << 6;
+ slot->metrics.height = bitmap->rows << 6;
+ slot->metrics.horiAdvance = bitmap->width << 6;
+ slot->metrics.horiBearingX = 0;
+ slot->metrics.horiBearingY = slot->bitmap_top << 6;
+
+ ft_synthesize_vertical_metrics( &slot->metrics,
+ bitmap->rows << 6 );
+
+ Exit:
+ return error;
+ }
+
+
+ static FT_Error
+ winfnt_get_header( FT_Face face,
+ FT_WinFNT_HeaderRec *aheader )
+ {
+ FNT_Font font = ((FNT_Face)face)->font;
+
+
+ *aheader = font->header;
+
+ return 0;
+ }
+
+
+ static const FT_Service_WinFntRec winfnt_service_rec =
+ {
+ winfnt_get_header
+ };
+
+ /*
+ * SERVICE LIST
+ *
+ */
+
+ static const FT_ServiceDescRec winfnt_services[] =
+ {
+ { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_WINFNT },
+ { FT_SERVICE_ID_WINFNT, &winfnt_service_rec },
+ { NULL, NULL }
+ };
+
+
+ static FT_Module_Interface
+ winfnt_get_service( FT_Driver driver,
+ const FT_String* service_id )
+ {
+ FT_UNUSED( driver );
+
+ return ft_service_list_lookup( winfnt_services, service_id );
+ }
+
+
+
+
+ FT_CALLBACK_TABLE_DEF
+ const FT_Driver_ClassRec winfnt_driver_class =
+ {
+ {
+ FT_MODULE_FONT_DRIVER |
+ FT_MODULE_DRIVER_NO_OUTLINES,
+ sizeof ( FT_DriverRec ),
+
+ "winfonts",
+ 0x10000L,
+ 0x20000L,
+
+ 0,
+
+ (FT_Module_Constructor)0,
+ (FT_Module_Destructor) 0,
+ (FT_Module_Requester) winfnt_get_service
+ },
+
+ sizeof( FNT_FaceRec ),
+ sizeof( FT_SizeRec ),
+ sizeof( FT_GlyphSlotRec ),
+
+ (FT_Face_InitFunc) FNT_Face_Init,
+ (FT_Face_DoneFunc) FNT_Face_Done,
+ (FT_Size_InitFunc) 0,
+ (FT_Size_DoneFunc) 0,
+ (FT_Slot_InitFunc) 0,
+ (FT_Slot_DoneFunc) 0,
+
+#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
+ ft_stub_set_char_sizes,
+ ft_stub_set_pixel_sizes,
+#endif
+ (FT_Slot_LoadFunc) FNT_Load_Glyph,
+
+ (FT_Face_GetKerningFunc) 0,
+ (FT_Face_AttachFunc) 0,
+ (FT_Face_GetAdvancesFunc) 0,
+
+ (FT_Size_RequestFunc) FNT_Size_Request,
+ (FT_Size_SelectFunc) FNT_Size_Select
+ };
+
+
+/* END */
diff --git a/src/winfonts/winfnt.h b/src/winfonts/winfnt.h
new file mode 100644
index 0000000..ca75c95
--- /dev/null
+++ b/src/winfonts/winfnt.h
@@ -0,0 +1,167 @@
+/***************************************************************************/
+/* */
+/* winfnt.h */
+/* */
+/* FreeType font driver for Windows FNT/FON files */
+/* */
+/* Copyright 1996-2001, 2002, 2003, 2004, 2007 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* Copyright 2007 Dmitry Timoshkov for Codeweavers */
+/* */
+/* This file is part of the FreeType project, and may only be used, */
+/* modified, and distributed under the terms of the FreeType project */
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
+/* this file you indicate that you have read the license and */
+/* understand and accept it fully. */
+/* */
+/***************************************************************************/
+
+
+#ifndef __WINFNT_H__
+#define __WINFNT_H__
+
+
+#include <ft2build.h>
+#include FT_WINFONTS_H
+#include FT_INTERNAL_DRIVER_H
+
+
+FT_BEGIN_HEADER
+
+ typedef struct WinMZ_HeaderRec_
+ {
+ FT_UShort magic;
+ /* skipped content */
+ FT_UShort lfanew;
+
+ } WinMZ_HeaderRec;
+
+
+ typedef struct WinNE_HeaderRec_
+ {
+ FT_UShort magic;
+ /* skipped content */
+ FT_UShort resource_tab_offset;
+ FT_UShort rname_tab_offset;
+
+ } WinNE_HeaderRec;
+
+
+ typedef struct WinPE32_HeaderRec_
+ {
+ FT_ULong magic;
+ FT_UShort machine;
+ FT_UShort number_of_sections;
+ /* skipped content */
+ FT_UShort size_of_optional_header;
+ /* skipped content */
+ FT_UShort magic32;
+ /* skipped content */
+ FT_ULong rsrc_virtual_address;
+ FT_ULong rsrc_size;
+ /* skipped content */
+
+ } WinPE32_HeaderRec;
+
+
+ typedef struct WinPE32_SectionRec_
+ {
+ FT_Byte name[8];
+ /* skipped content */
+ FT_ULong virtual_address;
+ FT_ULong size_of_raw_data;
+ FT_ULong pointer_to_raw_data;
+ /* skipped content */
+
+ } WinPE32_SectionRec;
+
+
+ typedef struct WinPE_RsrcDirRec_
+ {
+ FT_ULong characteristics;
+ FT_ULong time_date_stamp;
+ FT_UShort major_version;
+ FT_UShort minor_version;
+ FT_UShort number_of_named_entries;
+ FT_UShort number_of_id_entries;
+
+ } WinPE_RsrcDirRec;
+
+
+ typedef struct WinPE_RsrcDirEntryRec_
+ {
+ FT_ULong name;
+ FT_ULong offset;
+
+ } WinPE_RsrcDirEntryRec;
+
+
+ typedef struct WinPE_RsrcDataEntryRec_
+ {
+ FT_ULong offset_to_data;
+ FT_ULong size;
+ FT_ULong code_page;
+ FT_ULong reserved;
+
+ } WinPE_RsrcDataEntryRec;
+
+
+ typedef struct WinNameInfoRec_
+ {
+ FT_UShort offset;
+ FT_UShort length;
+ FT_UShort flags;
+ FT_UShort id;
+ FT_UShort handle;
+ FT_UShort usage;
+
+ } WinNameInfoRec;
+
+
+ typedef struct WinResourceInfoRec_
+ {
+ FT_UShort type_id;
+ FT_UShort count;
+
+ } WinResourceInfoRec;
+
+
+#define WINFNT_MZ_MAGIC 0x5A4D
+#define WINFNT_NE_MAGIC 0x454E
+#define WINFNT_PE_MAGIC 0x4550
+
+
+ typedef struct FNT_FontRec_
+ {
+ FT_ULong offset;
+
+ FT_WinFNT_HeaderRec header;
+
+ FT_Byte* fnt_frame;
+ FT_ULong fnt_size;
+ FT_String* family_name;
+
+ } FNT_FontRec, *FNT_Font;
+
+
+ typedef struct FNT_FaceRec_
+ {
+ FT_FaceRec root;
+ FNT_Font font;
+
+ FT_CharMap charmap_handle;
+ FT_CharMapRec charmap; /* a single charmap per face */
+
+ } FNT_FaceRec, *FNT_Face;
+
+
+ FT_EXPORT_VAR( const FT_Driver_ClassRec ) winfnt_driver_class;
+
+
+FT_END_HEADER
+
+
+#endif /* __WINFNT_H__ */
+
+
+/* END */