619 lines
13 KiB
C
619 lines
13 KiB
C
/*
|
|
* The font parser using the FreeType library version 2.
|
|
*
|
|
* see COPYRIGHT
|
|
*
|
|
*/
|
|
|
|
#ifdef USE_FREETYPE
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <freetype/freetype.h>
|
|
#include <freetype/ftglyph.h>
|
|
#include <freetype/ftsnames.h>
|
|
#include <freetype/ttnameid.h>
|
|
#include <freetype/ftoutln.h>
|
|
#include "pt1.h"
|
|
#include "global.h"
|
|
|
|
/* prototypes of call entries */
|
|
static void openfont(char *fname, char *arg);
|
|
static void closefont( void);
|
|
static int getnglyphs ( void);
|
|
static int glnames( GLYPH *glyph_list);
|
|
static void glmetrics( GLYPH *glyph_list);
|
|
static int glenc( GLYPH *glyph_list, int *encoding, int *unimap);
|
|
static void fnmetrics( struct font_metrics *fm);
|
|
static void glpath( int glyphno, GLYPH *glyph_list);
|
|
static void kerning( GLYPH *glyph_list);
|
|
|
|
/* globals */
|
|
|
|
/* front-end descriptor */
|
|
struct frontsw freetype_sw = {
|
|
/*name*/ "ft",
|
|
/*descr*/ "based on the FreeType library",
|
|
/*suffix*/ { "ttf", "otf", "pfa", "pfb" },
|
|
/*open*/ openfont,
|
|
/*close*/ closefont,
|
|
/*nglyphs*/ getnglyphs,
|
|
/*glnames*/ glnames,
|
|
/*glmetrics*/ glmetrics,
|
|
/*glenc*/ glenc,
|
|
/*fnmetrics*/ fnmetrics,
|
|
/*glpath*/ glpath,
|
|
/*kerning*/ kerning,
|
|
};
|
|
|
|
/* statics */
|
|
|
|
static FT_Library library;
|
|
static FT_Face face;
|
|
|
|
static int enc_type, enc_found;
|
|
|
|
/* SFNT functions do not seem to be included by default in FT2beta8 */
|
|
#define ENABLE_SFNT
|
|
|
|
/*
|
|
* Open font and prepare to return information to the main driver.
|
|
* May print error and warning messages.
|
|
* Exit on error.
|
|
*/
|
|
|
|
static void
|
|
openfont(
|
|
char *fname,
|
|
char *arg /* unused now */
|
|
)
|
|
{
|
|
FT_Error error;
|
|
|
|
if( FT_Init_FreeType( &library ) ) {
|
|
fprintf(stderr, "** FreeType initialization failed\n");
|
|
exit(1);
|
|
}
|
|
|
|
if( error = FT_New_Face( library, fname, 0, &face ) ) {
|
|
if ( error == FT_Err_Unknown_File_Format )
|
|
fprintf(stderr, "**** %s has format unknown to FreeType\n", fname);
|
|
else
|
|
fprintf(stderr, "**** Cannot access %s ****\n", fname);
|
|
exit(1);
|
|
}
|
|
|
|
if(FT_HAS_FIXED_SIZES(face)) {
|
|
WARNING_1 fprintf(stderr, "Font contains bitmaps\n");
|
|
}
|
|
if(FT_HAS_MULTIPLE_MASTERS(face)) {
|
|
WARNING_1 fprintf(stderr, "Font contains multiple masters, using default\n");
|
|
}
|
|
|
|
if(ISDBG(FT)) fprintf(stderr," %d units per EM\n", face->units_per_EM);
|
|
|
|
enc_found = 0;
|
|
}
|
|
|
|
/*
|
|
* Close font.
|
|
* Exit on error.
|
|
*/
|
|
|
|
static void
|
|
closefont(
|
|
void
|
|
)
|
|
{
|
|
if( FT_Done_Face(face) ) {
|
|
WARNING_1 fprintf(stderr, "Errors when closing the font file, ignored\n");
|
|
}
|
|
if( FT_Done_FreeType(library) ) {
|
|
WARNING_1 fprintf(stderr, "Errors when stopping FreeType, ignored\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the number of glyphs in font.
|
|
*/
|
|
|
|
static int
|
|
getnglyphs (
|
|
void
|
|
)
|
|
{
|
|
if(ISDBG(FT)) fprintf(stderr, "%d glyphs in font\n", face->num_glyphs);
|
|
return (int)face->num_glyphs;
|
|
}
|
|
|
|
/*
|
|
* Get the names of the glyphs.
|
|
* Returns 0 if the names were assigned, non-zero if the font
|
|
* provides no glyph names.
|
|
*/
|
|
|
|
static int
|
|
glnames(
|
|
GLYPH *glyph_list
|
|
)
|
|
{
|
|
#define MAX_NAMELEN 1024
|
|
unsigned char bf[1024];
|
|
int i;
|
|
|
|
if( ! FT_HAS_GLYPH_NAMES(face) ) {
|
|
WARNING_1 fprintf(stderr, "Font has no glyph names\n");
|
|
return 1;
|
|
}
|
|
|
|
for(i=0; i < face->num_glyphs; i++) {
|
|
if( FT_Get_Glyph_Name(face, i, bf, MAX_NAMELEN) || bf[0]==0 ) {
|
|
sprintf(bf, "_g_%d", i);
|
|
WARNING_2 fprintf(stderr,
|
|
"Glyph No. %d has no postscript name, becomes %s\n", i, bf);
|
|
}
|
|
glyph_list[i].name = strdup(bf);
|
|
if(ISDBG(FT)) fprintf(stderr, "%d has name %s\n", i, bf);
|
|
if (glyph_list[i].name == NULL) {
|
|
fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
|
|
exit(255);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get the metrics of the glyphs.
|
|
*/
|
|
|
|
static void
|
|
glmetrics(
|
|
GLYPH *glyph_list
|
|
)
|
|
{
|
|
GLYPH *g;
|
|
int i;
|
|
FT_Glyph_Metrics *met;
|
|
FT_BBox bbox;
|
|
FT_Glyph gly;
|
|
|
|
for(i=0; i < face->num_glyphs; i++) {
|
|
g = &(glyph_list[i]);
|
|
|
|
if( FT_Load_Glyph(face, i, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE) ) {
|
|
fprintf(stderr, "Can't load glyph %s, skipped\n", g->name);
|
|
continue;
|
|
}
|
|
|
|
met = &face->glyph->metrics;
|
|
|
|
if(FT_HAS_HORIZONTAL(face)) {
|
|
g->width = met->horiAdvance;
|
|
g->lsb = met->horiBearingX;
|
|
} else {
|
|
WARNING_2 fprintf(stderr, "Glyph %s has no horizontal metrics, guessed them\n", g->name);
|
|
g->width = met->width;
|
|
g->lsb = 0;
|
|
}
|
|
|
|
if( FT_Get_Glyph(face->glyph, &gly) ) {
|
|
fprintf(stderr, "Can't access glyph %s bbox, skipped\n", g->name);
|
|
continue;
|
|
}
|
|
|
|
FT_Glyph_Get_CBox(gly, ft_glyph_bbox_unscaled, &bbox);
|
|
g->xMin = bbox.xMin;
|
|
g->yMin = bbox.yMin;
|
|
g->xMax = bbox.xMax;
|
|
g->yMax = bbox.yMax;
|
|
|
|
g->ttf_pathlen = face->glyph->outline.n_points;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the original encoding of the font.
|
|
* Returns 1 for if the original encoding is Unicode, 2 if the
|
|
* original encoding is other 16-bit, 0 if 8-bit.
|
|
*/
|
|
|
|
static int
|
|
glenc(
|
|
GLYPH *glyph_list,
|
|
int *encoding,
|
|
int *unimap
|
|
)
|
|
{
|
|
int i, e;
|
|
unsigned code;
|
|
|
|
if(ISDBG(FT))
|
|
for(e=0; e < face->num_charmaps; e++) {
|
|
fprintf(stderr, "found encoding pid=%d eid=%d\n",
|
|
face->charmaps[e]->platform_id,
|
|
face->charmaps[e]->encoding_id);
|
|
}
|
|
|
|
if(enc_found)
|
|
goto populate_map;
|
|
|
|
enc_type = 0;
|
|
|
|
/* first check for an explicit PID/EID */
|
|
|
|
if(force_pid != -1) {
|
|
for(e=0; e < face->num_charmaps; e++) {
|
|
if(face->charmaps[e]->platform_id == force_pid
|
|
&& face->charmaps[e]->encoding_id == force_eid) {
|
|
WARNING_1 fprintf(stderr, "Found Encoding PID=%d/EID=%d\n",
|
|
force_pid, force_eid);
|
|
if( !face->charmaps || FT_Set_Charmap(face, face->charmaps[e]) ) {
|
|
fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
|
|
exit(1);
|
|
}
|
|
enc_type = 1;
|
|
goto populate_map;
|
|
}
|
|
}
|
|
fprintf(stderr, "*** TTF encoding table PID=%d/EID=%d not found\n",
|
|
force_pid, force_eid);
|
|
exit(1);
|
|
}
|
|
|
|
/* next check for a direct Adobe mapping */
|
|
|
|
if(!forcemap) {
|
|
for(e=0; e < face->num_charmaps; e++) {
|
|
if(face->charmaps[e]->encoding == ft_encoding_adobe_custom) {
|
|
WARNING_1 fputs("Found Adobe Custom Encoding\n", stderr);
|
|
if( FT_Set_Charmap(face, face->charmaps[e]) ) {
|
|
fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
|
|
exit(1);
|
|
}
|
|
goto populate_map;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(e=0; e < face->num_charmaps; e++) {
|
|
if(face->charmaps[e]->platform_id == 3) {
|
|
switch(face->charmaps[e]->encoding_id) {
|
|
case 0:
|
|
WARNING_1 fputs("Found Symbol Encoding\n", stderr);
|
|
break;
|
|
case 1:
|
|
WARNING_1 fputs("Found Unicode Encoding\n", stderr);
|
|
enc_type = 1;
|
|
break;
|
|
default:
|
|
WARNING_1 {
|
|
fprintf(stderr,
|
|
"****MS Encoding ID %d not supported****\n",
|
|
face->charmaps[e]->encoding_id);
|
|
fputs("Treating it like Symbol encoding\n", stderr);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(e >= face->num_charmaps) {
|
|
WARNING_1 fputs("No Microsoft encoding, using first encoding available\n", stderr);
|
|
e = 0;
|
|
}
|
|
|
|
if( FT_Set_Charmap(face, face->charmaps[e]) ) {
|
|
fprintf(stderr, "**** Cannot set charmap in FreeType ****\n");
|
|
exit(1);
|
|
}
|
|
|
|
populate_map:
|
|
enc_found = 1;
|
|
for(i=0; i<ENCTABSZ; i++) {
|
|
if(encoding[i] != -1)
|
|
continue;
|
|
if(enc_type == 1 || forcemap) {
|
|
code = unimap[i];
|
|
if(code == (unsigned) -1)
|
|
continue;
|
|
} else
|
|
code = i;
|
|
|
|
code = FT_Get_Char_Index(face, code);
|
|
if(0 && ISDBG(FT)) fprintf(stderr, "code of %3d is %3d\n", i, code);
|
|
if(code == 0)
|
|
continue; /* .notdef */
|
|
encoding[i] = code;
|
|
}
|
|
|
|
return enc_type;
|
|
}
|
|
|
|
/*
|
|
* Get the font metrics
|
|
*/
|
|
static void
|
|
fnmetrics(
|
|
struct font_metrics *fm
|
|
)
|
|
{
|
|
char *str;
|
|
static char *fieldstocheck[3];
|
|
#ifdef ENABLE_SFNT
|
|
FT_SfntName sn;
|
|
#endif /* ENABLE_SFNT */
|
|
int i, j, len;
|
|
|
|
fm->italic_angle = 0.0; /* FreeType hides the angle */
|
|
fm->underline_position = face->underline_position;
|
|
fm->underline_thickness = face->underline_thickness;
|
|
fm->is_fixed_pitch = FT_IS_FIXED_WIDTH(face);
|
|
|
|
fm->ascender = face->ascender;
|
|
fm->descender = face->descender;
|
|
|
|
fm->units_per_em = face->units_per_EM;
|
|
|
|
fm->bbox[0] = face->bbox.xMin;
|
|
fm->bbox[1] = face->bbox.yMin;
|
|
fm->bbox[2] = face->bbox.xMax;
|
|
fm->bbox[3] = face->bbox.yMax;
|
|
|
|
#ifdef ENABLE_SFNT
|
|
if( FT_Get_Sfnt_Name(face, TT_NAME_ID_COPYRIGHT, &sn) )
|
|
#endif /* ENABLE_SFNT */
|
|
fm->name_copyright = "";
|
|
#ifdef ENABLE_SFNT
|
|
else
|
|
fm->name_copyright = dupcnstring(sn.string, sn.string_len);
|
|
#endif /* ENABLE_SFNT */
|
|
|
|
fm->name_family = face->family_name;
|
|
|
|
fm->name_style = face->style_name;
|
|
if(fm->name_style == NULL)
|
|
fm->name_style = "";
|
|
|
|
#ifdef ENABLE_SFNT
|
|
if( FT_Get_Sfnt_Name(face, TT_NAME_ID_FULL_NAME, &sn) )
|
|
#endif /* ENABLE_SFNT */
|
|
{
|
|
int len;
|
|
|
|
len = strlen(fm->name_family) + strlen(fm->name_style) + 2;
|
|
if(( fm->name_full = malloc(len) )==NULL) {
|
|
fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
|
|
exit(255);
|
|
}
|
|
strcpy(fm->name_full, fm->name_family);
|
|
if(strlen(fm->name_style) != 0) {
|
|
strcat(fm->name_full, " ");
|
|
strcat(fm->name_full, fm->name_style);
|
|
}
|
|
}
|
|
#ifdef ENABLE_SFNT
|
|
else
|
|
fm->name_full = dupcnstring(sn.string, sn.string_len);
|
|
#endif /* ENABLE_SFNT */
|
|
|
|
#ifdef ENABLE_SFNT
|
|
if( FT_Get_Sfnt_Name(face, TT_NAME_ID_VERSION_STRING, &sn) )
|
|
#endif /* ENABLE_SFNT */
|
|
fm->name_version = "1.0";
|
|
#ifdef ENABLE_SFNT
|
|
else
|
|
fm->name_version = dupcnstring(sn.string, sn.string_len);
|
|
#endif /* ENABLE_SFNT */
|
|
|
|
#ifdef ENABLE_SFNT
|
|
if( FT_Get_Sfnt_Name(face, TT_NAME_ID_PS_NAME , &sn) ) {
|
|
#endif /* ENABLE_SFNT */
|
|
if(( fm->name_ps = strdup(fm->name_full) )==NULL) {
|
|
fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
|
|
exit(255);
|
|
}
|
|
#ifdef ENABLE_SFNT
|
|
} else
|
|
fm->name_ps = dupcnstring(sn.string, sn.string_len);
|
|
#endif /* ENABLE_SFNT */
|
|
for(i=0; fm->name_ps[i]!=0; i++)
|
|
if(fm->name_ps[i] == ' ')
|
|
fm->name_ps[i] = '_'; /* no spaces in the Postscript name *m
|
|
|
|
/* guess the boldness from the font names */
|
|
fm->force_bold=0;
|
|
|
|
fieldstocheck[0] = fm->name_style;
|
|
fieldstocheck[1] = fm->name_full;
|
|
fieldstocheck[2] = fm->name_ps;
|
|
|
|
for(i=0; !fm->force_bold && i<sizeof fieldstocheck /sizeof(fieldstocheck[0]); i++) {
|
|
str=fieldstocheck[i];
|
|
len = strlen(str);
|
|
for(j=0; j<len; j++) {
|
|
if( (str[j]=='B'
|
|
|| str[j]=='b'
|
|
&& ( j==0 || !isalpha(str[j-1]) )
|
|
)
|
|
&& !strncmp("old",&str[j+1],3)
|
|
&& (j+4 >= len || !islower(str[j+4]))
|
|
) {
|
|
fm->force_bold=1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Functions to decompose the outlines
|
|
*/
|
|
|
|
static GLYPH *curg;
|
|
static double lastx, lasty;
|
|
|
|
static int
|
|
outl_moveto(
|
|
FT_Vector *to,
|
|
void *unused
|
|
)
|
|
{
|
|
double tox, toy;
|
|
|
|
tox = fscale((double)to->x); toy = fscale((double)to->y);
|
|
|
|
/* FreeType does not do explicit closepath() */
|
|
if(curg->lastentry) {
|
|
g_closepath(curg);
|
|
}
|
|
fg_rmoveto(curg, tox, toy);
|
|
lastx = tox; lasty = toy;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
outl_lineto(
|
|
FT_Vector *to,
|
|
void *unused
|
|
)
|
|
{
|
|
double tox, toy;
|
|
|
|
tox = fscale((double)to->x); toy = fscale((double)to->y);
|
|
|
|
fg_rlineto(curg, tox, toy);
|
|
lastx = tox; lasty = toy;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
outl_conicto(
|
|
FT_Vector *control1,
|
|
FT_Vector *to,
|
|
void *unused
|
|
)
|
|
{
|
|
double c1x, c1y, tox, toy;
|
|
|
|
c1x = fscale((double)control1->x); c1y = fscale((double)control1->y);
|
|
tox = fscale((double)to->x); toy = fscale((double)to->y);
|
|
|
|
fg_rrcurveto(curg,
|
|
(lastx + 2.0 * c1x) / 3.0, (lasty + 2.0 * c1y) / 3.0,
|
|
(2.0 * c1x + tox) / 3.0, (2.0 * c1y + toy) / 3.0,
|
|
tox, toy );
|
|
lastx = tox; lasty = toy;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
outl_cubicto(
|
|
FT_Vector *control1,
|
|
FT_Vector *control2,
|
|
FT_Vector *to,
|
|
void *unused
|
|
)
|
|
{
|
|
double c1x, c1y, c2x, c2y, tox, toy;
|
|
|
|
c1x = fscale((double)control1->x); c1y = fscale((double)control1->y);
|
|
c2x = fscale((double)control2->x); c2y = fscale((double)control2->y);
|
|
tox = fscale((double)to->x); toy = fscale((double)to->y);
|
|
|
|
fg_rrcurveto(curg, c1x, c1y, c2x, c2y, tox, toy);
|
|
lastx = tox; lasty = toy;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static FT_Outline_Funcs ft_outl_funcs = {
|
|
outl_moveto,
|
|
outl_lineto,
|
|
outl_conicto,
|
|
outl_cubicto,
|
|
0,
|
|
0
|
|
};
|
|
|
|
/*
|
|
* Get the path of contrours for a glyph.
|
|
*/
|
|
|
|
static void
|
|
glpath(
|
|
int glyphno,
|
|
GLYPH *glyf_list
|
|
)
|
|
{
|
|
FT_Outline *ol;
|
|
|
|
curg = &glyf_list[glyphno];
|
|
|
|
if( FT_Load_Glyph(face, glyphno, FT_LOAD_NO_BITMAP|FT_LOAD_NO_SCALE|FT_LOAD_NO_HINTING)
|
|
|| face->glyph->format != ft_glyph_format_outline ) {
|
|
fprintf(stderr, "Can't load glyph %s, skipped\n", curg->name);
|
|
return;
|
|
}
|
|
|
|
ol = &face->glyph->outline;
|
|
lastx = 0.0; lasty = 0.0;
|
|
|
|
if( FT_Outline_Decompose(ol, &ft_outl_funcs, NULL) ) {
|
|
fprintf(stderr, "Can't decompose outline of glyph %s, skipped\n", curg->name);
|
|
return;
|
|
}
|
|
|
|
/* FreeType does not do explicit closepath() */
|
|
if(curg->lastentry) {
|
|
g_closepath(curg);
|
|
}
|
|
|
|
if(ol->flags & ft_outline_reverse_fill) {
|
|
assertpath(curg->entries, __FILE__, __LINE__, curg->name);
|
|
reversepaths(curg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the kerning data.
|
|
*/
|
|
|
|
static void
|
|
kerning(
|
|
GLYPH *glyph_list
|
|
)
|
|
{
|
|
int i, j, n;
|
|
int nglyphs = face->num_glyphs;
|
|
FT_Vector k;
|
|
GLYPH *gl;
|
|
|
|
if( nglyphs == 0 || !FT_HAS_KERNING(face) ) {
|
|
WARNING_1 fputs("No Kerning data\n", stderr);
|
|
return;
|
|
}
|
|
|
|
for(i=0; i<nglyphs; i++) {
|
|
if( (glyph_list[i].flags & GF_USED) ==0)
|
|
continue;
|
|
for(j=0; j<nglyphs; j++) {
|
|
if( (glyph_list[j].flags & GF_USED) ==0)
|
|
continue;
|
|
if( FT_Get_Kerning(face, i, j, ft_kerning_unscaled, &k) )
|
|
continue;
|
|
if( k.x == 0 )
|
|
continue;
|
|
|
|
addkernpair(i, j, k.x);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|