1 /****************************************************************************\
2  Part of the XeTeX typesetting system
3  Copyright (c) 1994-2008 by SIL International
4  Copyright (c) 2009 by Jonathan Kew
5 
6  SIL Author(s): Jonathan Kew
7 
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
23 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 
27 Except as contained in this notice, the name of the copyright holders
28 shall not be used in advertising or otherwise to promote the sale,
29 use or other dealings in this Software without prior written
30 authorization from the copyright holders.
31 \****************************************************************************/
32 
33 /*
34  *   file name:  XeTeXFontInst.cpp
35  *
36  *   created on: 2005-10-22
37  *   created by: Jonathan Kew
38  *
39  *     originally based on PortableFontInstance.cpp from ICU
40  */
41 
42 #include <w2c/config.h>
43 
44 #include "XeTeXFontInst.h"
45 #include "XeTeXLayoutInterface.h"
46 #include "XeTeX_ext.h"
47 
48 #include <string.h>
49 #include FT_GLYPH_H
50 #include FT_ADVANCES_H
51 
52 FT_Library gFreeTypeLibrary = 0;
53 
54 static hb_font_funcs_t* hbFontFuncs = NULL;
55 
XeTeXFontInst(const char * pathname,int index,float pointSize,int & status)56 XeTeXFontInst::XeTeXFontInst(const char* pathname, int index, float pointSize, int &status)
57     : m_pointSize(pointSize)
58     , m_unitsPerEM(0)
59     , m_ascent(0)
60     , m_descent(0)
61     , m_capHeight(0)
62     , m_xHeight(0)
63     , m_italicAngle(0)
64     , m_vertical(false)
65     , m_filename(NULL)
66     , m_index(0)
67     , m_ftFace(0)
68     , m_hbFont(NULL)
69     , m_math(NULL)
70 {
71     if (pathname != NULL)
72         initialize(pathname, index, status);
73 }
74 
~XeTeXFontInst()75 XeTeXFontInst::~XeTeXFontInst()
76 {
77     if (m_ftFace != 0) {
78         FT_Done_Face(m_ftFace);
79         m_ftFace = 0;
80     }
81     hb_font_destroy(m_hbFont);
82     delete[] m_filename;
83     free((void*) m_math);
84 }
85 
86 /* HarfBuzz font functions */
87 
88 static hb_bool_t
_get_glyph(hb_font_t *,void * font_data,hb_codepoint_t ch,hb_codepoint_t vs,hb_codepoint_t * gid,void *)89 _get_glyph(hb_font_t*, void *font_data, hb_codepoint_t ch, hb_codepoint_t vs, hb_codepoint_t *gid, void*)
90 {
91     FT_Face face = (FT_Face) font_data;
92     *gid = 0;
93 
94     if (vs)
95         *gid = FT_Face_GetCharVariantIndex (face, ch, vs);
96 
97     if (*gid == 0)
98         *gid = FT_Get_Char_Index (face, ch);
99 
100     return *gid != 0;
101 }
102 
103 static FT_Fixed
_get_glyph_advance(FT_Face face,FT_UInt gid,bool vertical)104 _get_glyph_advance(FT_Face face, FT_UInt gid, bool vertical)
105 {
106     FT_Error error;
107     FT_Fixed advance;
108     int flags = FT_LOAD_NO_SCALE;
109 
110     if (vertical)
111         flags |= FT_LOAD_VERTICAL_LAYOUT;
112 
113     error = FT_Get_Advance(face, gid, flags, &advance);
114     if (error)
115         advance = 0;
116     else
117         advance = advance;
118 
119     /* FreeType's vertical metrics grows downward */
120     if (vertical)
121         advance = -advance;
122 
123     return advance;
124 }
125 
126 static hb_position_t
_get_glyph_h_advance(hb_font_t *,void * font_data,hb_codepoint_t gid,void *)127 _get_glyph_h_advance(hb_font_t*, void *font_data, hb_codepoint_t gid, void*)
128 {
129     return _get_glyph_advance((FT_Face) font_data, gid, false);
130 }
131 
132 static hb_position_t
_get_glyph_v_advance(hb_font_t *,void * font_data,hb_codepoint_t gid,void *)133 _get_glyph_v_advance(hb_font_t*, void *font_data, hb_codepoint_t gid, void*)
134 {
135     return _get_glyph_advance((FT_Face) font_data, gid, true);
136 }
137 
138 static hb_bool_t
_get_glyph_h_origin(hb_font_t *,void * font_data,hb_codepoint_t gid,hb_position_t * x,hb_position_t * y,void *)139 _get_glyph_h_origin(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_position_t *x, hb_position_t *y, void*)
140 {
141     // horizontal origin is (0, 0)
142     return true;
143 }
144 
145 static hb_bool_t
_get_glyph_v_origin(hb_font_t *,void * font_data,hb_codepoint_t gid,hb_position_t * x,hb_position_t * y,void *)146 _get_glyph_v_origin(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_position_t *x, hb_position_t *y, void*)
147 {
148     // vertical origin is (0, 0) for now
149     return true;
150 
151     // TODO
152     // Keep the code below for reference, for now we want to keep vertical
153     // origin at (0, 0) for compatibility with pre-0.9999.
154     // Reconsider this (e.g. using BASE table) when we get around overhauling
155     // the text directionality model and implementing real vertical typesetting.
156 
157     FT_Face face = (FT_Face) font_data;
158     FT_Error error;
159 
160     error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE);
161     if (!error) {
162         *x = face->glyph->metrics.horiBearingX -   face->glyph->metrics.vertBearingX;
163         *y = face->glyph->metrics.horiBearingY - (-face->glyph->metrics.vertBearingY);
164     }
165 
166     return !error;
167 }
168 
169 static hb_position_t
_get_glyph_h_kerning(hb_font_t *,void * font_data,hb_codepoint_t gid1,hb_codepoint_t gid2,void *)170 _get_glyph_h_kerning(hb_font_t*, void *font_data, hb_codepoint_t gid1, hb_codepoint_t gid2, void*)
171 {
172     FT_Face face = (FT_Face) font_data;
173     FT_Error error;
174     FT_Vector kerning;
175     hb_position_t ret;
176 
177     error = FT_Get_Kerning (face, gid1, gid2, FT_KERNING_UNFITTED, &kerning);
178     if (error)
179         ret = 0;
180     else
181         ret = kerning.x;
182     return ret;
183 }
184 
185 static hb_position_t
_get_glyph_v_kerning(hb_font_t *,void * font_data,hb_codepoint_t gid1,hb_codepoint_t gid2,void *)186 _get_glyph_v_kerning(hb_font_t*, void *font_data, hb_codepoint_t gid1, hb_codepoint_t gid2, void*)
187 {
188     /* FreeType does not support vertical kerning */
189     return 0;
190 }
191 
192 static hb_bool_t
_get_glyph_extents(hb_font_t *,void * font_data,hb_codepoint_t gid,hb_glyph_extents_t * extents,void *)193 _get_glyph_extents(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_glyph_extents_t *extents, void*)
194 {
195     FT_Face face = (FT_Face) font_data;
196     FT_Error error;
197 
198     error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE);
199     if (!error) {
200         extents->x_bearing = face->glyph->metrics.horiBearingX;
201         extents->y_bearing = face->glyph->metrics.horiBearingY;
202         extents->width  =  face->glyph->metrics.width;
203         extents->height = -face->glyph->metrics.height;
204     }
205 
206     return !error;
207 }
208 
209 static hb_bool_t
_get_glyph_contour_point(hb_font_t *,void * font_data,hb_codepoint_t gid,unsigned int point_index,hb_position_t * x,hb_position_t * y,void *)210 _get_glyph_contour_point(hb_font_t*, void *font_data, hb_codepoint_t gid, unsigned int point_index, hb_position_t *x, hb_position_t *y, void*)
211 {
212     FT_Face face = (FT_Face) font_data;
213     FT_Error error;
214     bool ret = false;
215 
216     error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE);
217     if (!error) {
218         if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
219             if (point_index < (unsigned int) face->glyph->outline.n_points) {
220                 *x = face->glyph->outline.points[point_index].x;
221                 *y = face->glyph->outline.points[point_index].y;
222                 ret = true;
223             }
224         }
225     }
226 
227     return ret;
228 }
229 
230 static hb_bool_t
_get_glyph_name(hb_font_t *,void * font_data,hb_codepoint_t gid,char * name,unsigned int size,void *)231 _get_glyph_name(hb_font_t *, void *font_data, hb_codepoint_t gid, char *name, unsigned int size, void *)
232 {
233     FT_Face face = (FT_Face) font_data;
234     bool ret = false;
235 
236     ret = !FT_Get_Glyph_Name (face, gid, name, size);
237     if (ret && (size && !*name))
238         ret = false;
239 
240     return ret;
241 }
242 
243 static hb_font_funcs_t *
_get_font_funcs(void)244 _get_font_funcs(void)
245 {
246     static hb_font_funcs_t* funcs = hb_font_funcs_create();
247 
248     hb_font_funcs_set_glyph_func                (funcs, _get_glyph, NULL, NULL);
249     hb_font_funcs_set_glyph_h_advance_func      (funcs, _get_glyph_h_advance, NULL, NULL);
250     hb_font_funcs_set_glyph_v_advance_func      (funcs, _get_glyph_v_advance, NULL, NULL);
251     hb_font_funcs_set_glyph_h_origin_func       (funcs, _get_glyph_h_origin, NULL, NULL);
252     hb_font_funcs_set_glyph_v_origin_func       (funcs, _get_glyph_v_origin, NULL, NULL);
253     hb_font_funcs_set_glyph_h_kerning_func      (funcs, _get_glyph_h_kerning, NULL, NULL);
254     hb_font_funcs_set_glyph_v_kerning_func      (funcs, _get_glyph_v_kerning, NULL, NULL);
255     hb_font_funcs_set_glyph_extents_func        (funcs, _get_glyph_extents, NULL, NULL);
256     hb_font_funcs_set_glyph_contour_point_func  (funcs, _get_glyph_contour_point, NULL, NULL);
257     hb_font_funcs_set_glyph_name_func           (funcs, _get_glyph_name, NULL, NULL);
258 
259     return funcs;
260 }
261 
262 static hb_blob_t *
_get_table(hb_face_t *,hb_tag_t tag,void * user_data)263 _get_table(hb_face_t *, hb_tag_t tag, void *user_data)
264 {
265     FT_Face face = (FT_Face) user_data;
266     FT_ULong length = 0;
267     FT_Byte *table;
268     FT_Error error;
269     hb_blob_t* blob = NULL;
270 
271     error = FT_Load_Sfnt_Table(face, tag, 0, NULL, &length);
272     if (!error) {
273         table = (FT_Byte *) xmalloc(length * sizeof(char));
274         if (table != NULL) {
275             error = FT_Load_Sfnt_Table(face, tag, 0, (FT_Byte*)table, &length);
276             if (!error) {
277                 blob = hb_blob_create((const char*) table, length, HB_MEMORY_MODE_WRITABLE, table, free);
278             } else {
279                 free(table);
280             }
281         }
282     }
283 
284     return blob;
285 }
286 
287 void
initialize(const char * pathname,int index,int & status)288 XeTeXFontInst::initialize(const char* pathname, int index, int &status)
289 {
290     TT_Postscript *postTable;
291     TT_OS2* os2Table;
292     FT_Error error;
293     hb_face_t *hbFace;
294 
295     if (!gFreeTypeLibrary) {
296         error = FT_Init_FreeType(&gFreeTypeLibrary);
297         if (error) {
298             fprintf(stderr, "FreeType initialization failed! (%d)\n", error);
299             exit(1);
300         }
301     }
302 
303     error = FT_New_Face(gFreeTypeLibrary, (char*)pathname, index, &m_ftFace);
304     if (error) {
305         status = 1;
306         return;
307     }
308 
309     if (!FT_IS_SCALABLE(m_ftFace)) {
310         status = 1;
311         return;
312     }
313 
314     /* for non-sfnt-packaged fonts (presumably Type 1), see if there is an AFM file we can attach */
315     if (index == 0 && !FT_IS_SFNT(m_ftFace)) {
316         char* afm = new char[strlen((const char*)pathname) + 5]; // room to append ".afm"
317         strcpy(afm, (const char*)pathname);
318         char* p = strrchr(afm, '.');
319         if (p == NULL || strlen(p) != 4 || tolower(*(p+1)) != 'p' || tolower(*(p+2)) != 'f')
320             strcat(afm, ".afm");   // append .afm if the extension didn't seem to be .pf[ab]
321         else
322             strcpy(p, ".afm");     // else replace extension with .afm
323         FT_Attach_File(m_ftFace, afm); // ignore error code; AFM might not exist
324         delete[] afm;
325     }
326 
327     m_filename = xstrdup(pathname);
328     m_index = index;
329     m_unitsPerEM = m_ftFace->units_per_EM;
330     m_ascent = unitsToPoints(m_ftFace->ascender);
331     m_descent = unitsToPoints(m_ftFace->descender);
332 
333     postTable = (TT_Postscript *) getFontTable(ft_sfnt_post);
334     if (postTable != NULL) {
335         m_italicAngle = Fix2D(postTable->italicAngle);
336     }
337 
338     os2Table = (TT_OS2*) getFontTable(ft_sfnt_os2);
339     if (os2Table) {
340         m_capHeight = unitsToPoints(os2Table->sCapHeight);
341         m_xHeight = unitsToPoints(os2Table->sxHeight);
342     }
343 
344     // Set up HarfBuzz font
345     hbFace = hb_face_create_for_tables(_get_table, m_ftFace, NULL);
346     hb_face_set_index(hbFace, index);
347     hb_face_set_upem(hbFace, m_unitsPerEM);
348     m_hbFont = hb_font_create(hbFace);
349     hb_face_destroy(hbFace);
350 
351     if (hbFontFuncs == NULL)
352         hbFontFuncs = _get_font_funcs();
353 
354     hb_font_set_funcs(m_hbFont, hbFontFuncs, m_ftFace, NULL);
355     hb_font_set_scale(m_hbFont, m_unitsPerEM, m_unitsPerEM);
356     // We don’t want device tables adjustments
357     hb_font_set_ppem(m_hbFont, 0, 0);
358 
359     return;
360 }
361 
362 void
setLayoutDirVertical(bool vertical)363 XeTeXFontInst::setLayoutDirVertical(bool vertical)
364 {
365     m_vertical = vertical;
366 }
367 
368 const void *
getFontTable(OTTag tag) const369 XeTeXFontInst::getFontTable(OTTag tag) const
370 {
371     FT_ULong tmpLength = 0;
372     FT_Error error = FT_Load_Sfnt_Table(m_ftFace, tag, 0, NULL, &tmpLength);
373     if (error)
374         return NULL;
375 
376     void* table = xmalloc(tmpLength * sizeof(char));
377     if (table != NULL) {
378         error = FT_Load_Sfnt_Table(m_ftFace, tag, 0, (FT_Byte*)table, &tmpLength);
379         if (error) {
380             free((void *) table);
381             return NULL;
382         }
383     }
384 
385     return table;
386 }
387 
388 const char *
getMathTable()389 XeTeXFontInst::getMathTable()
390 {
391     if (m_math == NULL)
392         m_math = (const char*) getFontTable(MATH_TAG);
393     return m_math;
394 }
395 
396 const void *
getFontTable(FT_Sfnt_Tag tag) const397 XeTeXFontInst::getFontTable(FT_Sfnt_Tag tag) const
398 {
399     return FT_Get_Sfnt_Table(m_ftFace, tag);
400 }
401 
402 void
getGlyphBounds(GlyphID gid,GlyphBBox * bbox)403 XeTeXFontInst::getGlyphBounds(GlyphID gid, GlyphBBox* bbox)
404 {
405     bbox->xMin = bbox->yMin = bbox->xMax = bbox->yMax = 0.0;
406 
407     FT_Error error = FT_Load_Glyph(m_ftFace, gid, FT_LOAD_NO_SCALE);
408     if (error)
409         return;
410 
411     FT_Glyph glyph;
412     error = FT_Get_Glyph(m_ftFace->glyph, &glyph);
413     if (error == 0) {
414         FT_BBox ft_bbox;
415         FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_UNSCALED, &ft_bbox);
416         bbox->xMin = unitsToPoints(ft_bbox.xMin);
417         bbox->yMin = unitsToPoints(ft_bbox.yMin);
418         bbox->xMax = unitsToPoints(ft_bbox.xMax);
419         bbox->yMax = unitsToPoints(ft_bbox.yMax);
420         FT_Done_Glyph(glyph);
421     }
422 }
423 
424 GlyphID
mapCharToGlyph(UChar32 ch) const425 XeTeXFontInst::mapCharToGlyph(UChar32 ch) const
426 {
427     return FT_Get_Char_Index(m_ftFace, ch);
428 }
429 
430 uint16_t
getNumGlyphs() const431 XeTeXFontInst::getNumGlyphs() const
432 {
433     return m_ftFace->num_glyphs;
434 }
435 
436 float
getGlyphWidth(GlyphID gid)437 XeTeXFontInst::getGlyphWidth(GlyphID gid)
438 {
439     return unitsToPoints(_get_glyph_advance(m_ftFace, gid, false));
440 }
441 
442 void
getGlyphHeightDepth(GlyphID gid,float * ht,float * dp)443 XeTeXFontInst::getGlyphHeightDepth(GlyphID gid, float* ht, float* dp)
444 {
445     GlyphBBox bbox;
446     getGlyphBounds(gid, &bbox);
447 
448     if (ht)
449         *ht = bbox.yMax;
450     if (dp)
451         *dp = -bbox.yMin;
452 }
453 
454 void
getGlyphSidebearings(GlyphID gid,float * lsb,float * rsb)455 XeTeXFontInst::getGlyphSidebearings(GlyphID gid, float* lsb, float* rsb)
456 {
457     float width = getGlyphWidth(gid);
458 
459     GlyphBBox bbox;
460     getGlyphBounds(gid, &bbox);
461 
462     if (lsb)
463         *lsb = bbox.xMin;
464     if (rsb)
465         *rsb = width - bbox.xMax;
466 }
467 
468 float
getGlyphItalCorr(GlyphID gid)469 XeTeXFontInst::getGlyphItalCorr(GlyphID gid)
470 {
471     float rval = 0.0;
472 
473     float width = getGlyphWidth(gid);
474 
475     GlyphBBox bbox;
476     getGlyphBounds(gid, &bbox);
477 
478     if (bbox.xMax > width)
479         rval = bbox.xMax - width;
480 
481     return rval;
482 }
483 
484 GlyphID
mapGlyphToIndex(const char * glyphName) const485 XeTeXFontInst::mapGlyphToIndex(const char* glyphName) const
486 {
487     return FT_Get_Name_Index(m_ftFace, const_cast<char*>(glyphName));
488 }
489 
490 const char*
getGlyphName(GlyphID gid,int & nameLen)491 XeTeXFontInst::getGlyphName(GlyphID gid, int& nameLen)
492 {
493     if (FT_HAS_GLYPH_NAMES(m_ftFace)) {
494         static char buffer[256];
495         FT_Get_Glyph_Name(m_ftFace, gid, buffer, 256);
496         nameLen = strlen(buffer);
497         return &buffer[0];
498     }
499     else {
500         nameLen = 0;
501         return NULL;
502     }
503 }
504 
505 UChar32
getFirstCharCode()506 XeTeXFontInst::getFirstCharCode()
507 {
508     FT_UInt gindex;
509     return FT_Get_First_Char(m_ftFace, &gindex);
510 }
511 
512 UChar32
getLastCharCode()513 XeTeXFontInst::getLastCharCode()
514 {
515     FT_UInt gindex;
516     UChar32 ch = FT_Get_First_Char(m_ftFace, &gindex);
517     UChar32 prev = ch;
518     while (gindex != 0) {
519         prev = ch;
520         ch = FT_Get_Next_Char(m_ftFace, ch, &gindex);
521     }
522     return prev;
523 }
524