Code

Added missing Pango Weights.
[inkscape.git] / src / libnrtype / FontInstance.cpp
1 /*
2  *  FontInstance.cpp
3  *  testICU
4  *
5  *   Authors:
6  *     fred
7  *     bulia byak <buliabyak@users.sf.net>
8  *
9  */
11 #define PANGO_ENABLE_ENGINE
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <ft2build.h>
18 #include FT_OUTLINE_H
19 #include FT_BBOX_H
20 #include FT_TRUETYPE_TAGS_H
21 #include FT_TRUETYPE_TABLES_H
22 #include <pango/pangoft2.h>
23 #include <2geom/pathvector.h>
24 #include "libnr/nr-rect.h"
25 #include "libnrtype/font-glyph.h"
26 #include "libnrtype/font-instance.h"
27 #include "libnrtype/RasterFont.h"
28 #include "livarot/Path.h"
29 #include "util/unordered-containers.h"
32 struct font_style_hash : public std::unary_function<font_style, size_t> {
33     size_t operator()(font_style const &x) const;
34 };
36 struct font_style_equal : public std::binary_function<font_style, font_style, bool> {
37     bool operator()(font_style const &a, font_style const &b) const;
38 };
40 typedef INK_UNORDERED_MAP<font_style, raster_font*, font_style_hash, font_style_equal> StyleMap;
44 size_t  font_style_hash::operator()(const font_style &x) const {
45         int      h=0,n;
46         n=(int)floor(100*x.stroke_width);
47         h*=12186;
48         h+=n;
49         n=(x.vertical)?1:0;
50         h*=12186;
51         h+=n;
52         if ( x.stroke_width >= 0.01 ) {
53                 n=x.stroke_cap*10+x.stroke_join+(int)(x.stroke_miter_limit*100);
54                 h*=12186;
55                 h+=n;
56                 if ( x.nbDash > 0 ) {
57                         n=x.nbDash;
58                         h*=12186;
59                         h+=n;
60                         n=(int)floor(100*x.dash_offset);
61                         h*=12186;
62                         h+=n;
63                         for (int i=0;i<x.nbDash;i++) {
64                                 n=(int)floor(100*x.dashes[i]);
65                                 h*=12186;
66                                 h+=n;
67                         }
68                 }
69         }
70         return h;
71 }
73 bool  font_style_equal::operator()(const font_style &a,const font_style &b) const {
74     for (int i=0;i<6;i++) {
75         if ( (int)(100*a.transform[i]) != (int)(100*b.transform[i]) ) return false;
76     }
77         if ( a.vertical && b.vertical == false ) return false;
78         if ( a.vertical == false && b.vertical ) return false;
79         if ( a.stroke_width > 0.01 && b.stroke_width <= 0.01 ) return false;
80         if ( a.stroke_width <= 0.01 && b.stroke_width > 0.01 ) return false;
81         if ( a.stroke_width <= 0.01 && b.stroke_width <= 0.01 ) return true;
83         if ( a.stroke_cap != b.stroke_cap ) return false;
84         if ( a.stroke_join != b.stroke_join ) return false;
85     if ( (int)(a.stroke_miter_limit*100) != (int)(b.stroke_miter_limit*100) ) return false;
86         if ( a.nbDash != b.nbDash ) return false;
87         if ( a.nbDash <= 0 ) return true;
88         if ( (int)floor(100*a.dash_offset) != (int)floor(100*b.dash_offset) ) return false;
89         for (int i=0;i<a.nbDash;i++) {
90                 if ( (int)floor(100*a.dashes[i]) != (int)floor(100*b.dashes[i]) ) return false;
91         }
92         return true;
93 }
95 #ifndef USE_PANGO_WIN32
96 /*
97  * Outline extraction
98  */
99 typedef struct ft2_to_liv {
100         Path*        theP;
101         double       scale;
102         Geom::Point    last;
103 } ft2_to_liv;
105 // Note: Freetype 2.2.1 redefined function signatures for functions to be placed in an
106 // FT_Outline_Funcs structure.  This is needed to keep backwards compatibility with the
107 // 2.1.x series.
109 /* *** BEGIN #if HACK *** */
110 #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 2
111 typedef FT_Vector const FREETYPE_VECTOR;
112 #else
113 typedef FT_Vector FREETYPE_VECTOR;
114 #endif
116 // outline as returned by freetype -> livarot Path
117 // see nr-type-ft2.cpp for the freetype -> artBPath on which this code is based
118 static int ft2_move_to(FREETYPE_VECTOR *to, void * i_user) {
119         ft2_to_liv* user=(ft2_to_liv*)i_user;
120         Geom::Point   p(user->scale*to->x,user->scale*to->y);
121         //      printf("m  t=%f %f\n",p[0],p[1]);
122         user->theP->MoveTo(p);
123         user->last=p;
124         return 0;
127 static int ft2_line_to(FREETYPE_VECTOR *to, void *i_user)
129         ft2_to_liv* user=(ft2_to_liv*)i_user;
130         Geom::Point   p(user->scale*to->x,user->scale*to->y);
131         //      printf("l  t=%f %f\n",p[0],p[1]);
132         user->theP->LineTo(p);
133         user->last=p;
134         return 0;
137 static int ft2_conic_to(FREETYPE_VECTOR *control, FREETYPE_VECTOR *to, void *i_user)
139         ft2_to_liv* user=(ft2_to_liv*)i_user;
140         Geom::Point   p(user->scale*to->x,user->scale*to->y),c(user->scale*control->x,user->scale*control->y);
141         //      printf("b c=%f %f  t=%f %f\n",c[0],c[1],p[0],p[1]);
142         user->theP->BezierTo(p);
143         user->theP->IntermBezierTo(c);
144         user->theP->EndBezierTo();
145         user->last=p;
146         return 0;
149 static int ft2_cubic_to(FREETYPE_VECTOR *control1, FREETYPE_VECTOR *control2, FREETYPE_VECTOR *to, void *i_user)
151         ft2_to_liv* user=(ft2_to_liv*)i_user;
152         Geom::Point   p(user->scale*to->x,user->scale*to->y),
153         c1(user->scale*control1->x,user->scale*control1->y),
154         c2(user->scale*control2->x,user->scale*control2->y);
155         //      printf("c c1=%f %f  c2=%f %f   t=%f %f\n",c1[0],c1[1],c2[0],c2[1],p[0],p[1]);
156         user->theP->CubicTo(p,3*(c1-user->last),3*(p-c2));
157         user->last=p;
158         return 0;
160 #endif
162 /* *** END #if HACK *** */
164 /*
165  *
166  */
168 font_instance::font_instance(void) :
169     pFont(0),
170     descr(0),
171     refCount(0),
172     daddy(0),
173     nbGlyph(0),
174     maxGlyph(0),
175     glyphs(0),
176     loadedPtr(new StyleMap()),
177     theFace(0)
179     //printf("font instance born\n");
182 font_instance::~font_instance(void)
184     if ( loadedPtr ) {
185         StyleMap* tmp = static_cast<StyleMap*>(loadedPtr);
186         delete tmp;
187         loadedPtr = 0;
188     }
190     if ( daddy ) {
191         daddy->UnrefFace(this);
192         daddy = 0;
193     }
195     //printf("font instance death\n");
196     if ( pFont ) {
197         g_object_unref(pFont);
198         pFont = 0;
199     }
201     if ( descr ) {
202         pango_font_description_free(descr);
203         descr = 0;
204     }
206     //  if ( theFace ) FT_Done_Face(theFace); // owned by pFont. don't touch
207     theFace = 0;
209     for (int i=0;i<nbGlyph;i++) {
210         if ( glyphs[i].outline ) {
211             delete glyphs[i].outline;
212         }
213         if ( glyphs[i].pathvector ) {
214             delete glyphs[i].pathvector;
215         }
216     }
217     if ( glyphs ) {
218         free(glyphs);
219         glyphs = 0;
220     }
221     nbGlyph = 0;
222     maxGlyph = 0;
225 void font_instance::Ref(void)
227         refCount++;
228         //char *tc=pango_font_description_to_string(descr);
229         //printf("font %x %s ref'd %i\n",this,tc,refCount);
230         //free(tc);
233 void font_instance::Unref(void)
235         refCount--;
236         //char *tc=pango_font_description_to_string(descr);
237         //printf("font %x %s unref'd %i\n",this,tc,refCount);
238         //free(tc);
239         if ( refCount <= 0 ) {
240                 if ( daddy ) daddy->UnrefFace(this);
241                 daddy=NULL;
242                 delete this;
243         }
246 unsigned int font_instance::Name(gchar *str, unsigned int size)
248         return Attribute("name", str, size);
251 unsigned int font_instance::Family(gchar *str, unsigned int size)
253         return Attribute("family", str, size);
256 unsigned int font_instance::PSName(gchar *str, unsigned int size)
258         return Attribute("psname", str, size);
261 unsigned int font_instance::Attribute(const gchar *key, gchar *str, unsigned int size)
263         if ( descr == NULL ) {
264                 if ( size > 0 ) str[0]=0;
265                 return 0;
266         }
267         char*   res=NULL;
268         bool    free_res=false;
270         if ( strcmp(key,"name") == 0 ) {
271                 PangoFontDescription* td=pango_font_description_copy(descr);
272                 pango_font_description_unset_fields (td, PANGO_FONT_MASK_SIZE);
273                 res=pango_font_description_to_string (td);
274                 pango_font_description_free(td);
275                 free_res=true;
276         } else if ( strcmp(key,"psname") == 0 ) {
277 #ifndef USE_PANGO_WIN32
278          res = (char *) FT_Get_Postscript_Name (theFace); // that's the main method, seems to always work
279 #endif
280          free_res=false;
281          if (res == NULL) { // a very limited workaround, only bold, italic, and oblique will work
282              PangoStyle style=pango_font_description_get_style(descr);
283              bool i = (style == PANGO_STYLE_ITALIC);
284              bool o = (style == PANGO_STYLE_OBLIQUE);
285              PangoWeight weight=pango_font_description_get_weight(descr);
286              bool b = (weight >= PANGO_WEIGHT_BOLD);
288              res = g_strdup_printf ("%s%s%s%s",
289                                     pango_font_description_get_family(descr),
290                                     (b || i || o) ? "-" : "",
291                                     (b) ? "Bold" : "",
292                                     (i) ? "Italic" : ((o) ? "Oblique" : "")  );
293              free_res = true;
294          }
295         } else if ( strcmp(key,"family") == 0 ) {
296                 res=(char*)pango_font_description_get_family(descr);
297                 free_res=false;
298         } else if ( strcmp(key,"style") == 0 ) {
299                 PangoStyle v=pango_font_description_get_style(descr);
300                 if ( v == PANGO_STYLE_ITALIC ) {
301                         res=(char*)"italic";
302                 } else if ( v == PANGO_STYLE_OBLIQUE ) {
303                         res=(char*)"oblique";
304                 } else {
305                         res=(char*)"normal";
306                 }
307                 free_res=false;
308         } else if ( strcmp(key,"weight") == 0 ) {
309                 PangoWeight v=pango_font_description_get_weight(descr);
310                 if ( v <= PANGO_WEIGHT_THIN ) {
311                         res=(char*)"100";
312                 } else if ( v <= PANGO_WEIGHT_ULTRALIGHT ) {
313                         res=(char*)"200";
314                 } else if ( v <= PANGO_WEIGHT_LIGHT ) {
315                         res=(char*)"300";
316                 } else if ( v <= PANGO_WEIGHT_BOOK ) {
317                         res=(char*)"380";
318                 } else if ( v <= PANGO_WEIGHT_NORMAL ) {
319                         res=(char*)"normal";
320                 } else if ( v <= PANGO_WEIGHT_MEDIUM ) {
321                         res=(char*)"500";
322                 } else if ( v <= PANGO_WEIGHT_SEMIBOLD ) {
323                         res=(char*)"600";
324                 } else if ( v <= PANGO_WEIGHT_BOLD ) {
325                         res=(char*)"bold";
326                 } else if ( v <= PANGO_WEIGHT_ULTRABOLD ) {
327                         res=(char*)"800";
328                 } else { // HEAVY   NB: Pango defines ULTRAHEAVY = 1000 but not CSS2
329                         res=(char*)"900";
330                 }
331                 free_res=false;
332         } else if ( strcmp(key,"stretch") == 0 ) {
333                 PangoStretch v=pango_font_description_get_stretch(descr);
334                 if ( v <= PANGO_STRETCH_EXTRA_CONDENSED ) {
335                         res=(char*)"extra-condensed";
336                 } else if ( v <= PANGO_STRETCH_CONDENSED ) {
337                         res=(char*)"condensed";
338                 } else if ( v <= PANGO_STRETCH_SEMI_CONDENSED ) {
339                         res=(char*)"semi-condensed";
340                 } else if ( v <= PANGO_STRETCH_NORMAL ) {
341                         res=(char*)"normal";
342                 } else if ( v <= PANGO_STRETCH_SEMI_EXPANDED ) {
343                         res=(char*)"semi-expanded";
344                 } else if ( v <= PANGO_STRETCH_EXPANDED ) {
345                         res=(char*)"expanded";
346                 } else {
347                         res=(char*)"extra-expanded";
348                 }
349                 free_res=false;
350         } else if ( strcmp(key,"variant") == 0 ) {
351                 PangoVariant v=pango_font_description_get_variant(descr);
352                 if ( v == PANGO_VARIANT_SMALL_CAPS ) {
353                         res=(char*)"small-caps";
354                 } else {
355                         res=(char*)"normal";
356                 }
357                 free_res=false;
358         } else {
359                 res = NULL;
360                 free_res=false;
361         }
362         if ( res == NULL ) {
363                 if ( size > 0 ) str[0]=0;
364                 return 0;
365         }
367         if (res) {
368                 unsigned int len=strlen(res);
369                 unsigned int rlen=(size-1<len)?size-1:len;
370                 if ( str ) {
371                         if ( rlen > 0 ) memcpy(str,res,rlen);
372                         if ( size > 0 ) str[rlen]=0;
373                 }
374                 if (free_res) free(res);
375                 return len;
376         }
377         return 0;
380 void font_instance::InitTheFace()
382 #ifdef USE_PANGO_WIN32
383     if ( !theFace ) {
384         LOGFONT *lf=pango_win32_font_logfont(pFont);
385         g_assert(lf != NULL);
386         theFace=pango_win32_font_cache_load(daddy->pangoFontCache,lf);
387         g_free(lf);
388     }
389     XFORM identity = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
390     SetWorldTransform(daddy->hScreenDC, &identity);
391     SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE);
392     SelectObject(daddy->hScreenDC,theFace);
393 #else
394         theFace=pango_ft2_font_get_face(pFont);
395     if ( theFace )
396         FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol);
397 #endif
400 void font_instance::FreeTheFace()
402 #ifdef USE_PANGO_WIN32
403     SelectObject(daddy->hScreenDC,GetStockObject(SYSTEM_FONT));
404     pango_win32_font_cache_unload(daddy->pangoFontCache,theFace);
405 #endif
406     theFace=NULL;
409 void font_instance::InstallFace(PangoFont* iFace)
411         if ( !iFace )
412             return;
413         pFont=iFace;
415     InitTheFace();
417         if ( pFont && IsOutlineFont() == false ) {
418         FreeTheFace();
419                 if ( pFont ) g_object_unref(pFont);
420                 pFont=NULL;
421         }
424 bool    font_instance::IsOutlineFont(void)
426         if ( pFont == NULL ) return false;
427     InitTheFace();
428 #ifdef USE_PANGO_WIN32
429     TEXTMETRIC tm;
430     return GetTextMetrics(daddy->hScreenDC,&tm) && tm.tmPitchAndFamily&(TMPF_TRUETYPE|TMPF_DEVICE);
431 #else
432         return FT_IS_SCALABLE(theFace);
433 #endif
436 int font_instance::MapUnicodeChar(gunichar c)
438         if ( pFont == NULL ) return 0;
439 #ifdef USE_PANGO_WIN32
440     return pango_win32_font_get_glyph_index(pFont,c);
441 #else
442         int res=0;
443         theFace=pango_ft2_font_get_face(pFont);
444         if ( c > 0xf0000 ) {
445                 res=CLAMP(c,0xf0000,0x1fffff)-0xf0000;
446         } else {
447                 res=FT_Get_Char_Index(theFace, c);
448         }
449         return res;
450 #endif
454 #ifdef USE_PANGO_WIN32
455 static inline Geom::Point pointfx_to_nrpoint(const POINTFX &p, double scale)
457     return Geom::Point(*(long*)&p.x / 65536.0 * scale,
458                      *(long*)&p.y / 65536.0 * scale);
460 #endif
462 void font_instance::LoadGlyph(int glyph_id)
464         if ( pFont == NULL ) return;
465     InitTheFace();
466 #ifndef USE_PANGO_WIN32
467         if ( !FT_IS_SCALABLE(theFace) ) return; // bitmap font
468 #endif
470         if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
471                 if ( nbGlyph >= maxGlyph ) {
472                         maxGlyph=2*nbGlyph+1;
473                         glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph));
474                 }
475                 font_glyph  n_g;
476                 n_g.outline=NULL;
477         n_g.pathvector=NULL;
478                 n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0;
479                 bool   doAdd=false;
481 #ifdef USE_PANGO_WIN32
483 #ifndef GGO_UNHINTED         // For compatibility with old SDKs.
484 #define GGO_UNHINTED 0x0100
485 #endif
487         MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
488         OUTLINETEXTMETRIC otm;
489         GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm);
490         GLYPHMETRICS metrics;
491         DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
492         double scale=1.0/daddy->fontSize;
493         n_g.h_advance=metrics.gmCellIncX*scale;
494         n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
495         n_g.h_width=metrics.gmBlackBoxX*scale;
496         n_g.v_width=metrics.gmBlackBoxY*scale;
497         n_g.outline=NULL;
498         if ( bufferSize == GDI_ERROR) {
499             // shit happened
500         } else if ( bufferSize == 0) {
501             // character has no visual representation, but is valid (eg whitespace)
502             doAdd=true;
503         } else {
504             char *buffer = new char[bufferSize];
505             if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer, &identity) <= 0 ) {
506                 // shit happened
507             } else {
508                 // Platform SDK is rubbish, read KB87115 instead
509                 n_g.outline=new Path;
510                 DWORD polyOffset=0;
511                 while ( polyOffset < bufferSize ) {
512                     TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer+polyOffset);
513                     if (polyOffset+polyHeader->cb > bufferSize) break;
515                     if (polyHeader->dwType == TT_POLYGON_TYPE) {
516                         n_g.outline->MoveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale));
517                         DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER);
519                         while ( curveOffset < polyOffset+polyHeader->cb ) {
520                             TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer+curveOffset);
521                             POINTFX const *p=polyCurve->apfx;
522                             POINTFX const *endp=p+polyCurve->cpfx;
524                             switch (polyCurve->wType) {
525                                 case TT_PRIM_LINE:
526                                     while ( p != endp )
527                                         n_g.outline->LineTo(pointfx_to_nrpoint(*p++, scale));
528                                     break;
530                                 case TT_PRIM_QSPLINE:
531                                 {
532                                     g_assert(polyCurve->cpfx >= 2);
533                                     endp -= 2;
534                                     Geom::Point this_mid=pointfx_to_nrpoint(p[0], scale);
535                                     while ( p != endp ) {
536                                         Geom::Point next_mid=pointfx_to_nrpoint(p[1], scale);
537                                             n_g.outline->BezierTo((next_mid+this_mid)/2);
538                                             n_g.outline->IntermBezierTo(this_mid);
539                                             n_g.outline->EndBezierTo();
540                                         ++p;
541                                         this_mid=next_mid;
542                                     }
543                                         n_g.outline->BezierTo(pointfx_to_nrpoint(p[1], scale));
544                                         n_g.outline->IntermBezierTo(this_mid);
545                                         n_g.outline->EndBezierTo();
546                                     break;
547                                 }
549                                 case 3:  // TT_PRIM_CSPLINE
550                                     g_assert(polyCurve->cpfx % 3 == 0);
551                                     while ( p != endp ) {
552                                         n_g.outline->CubicTo(pointfx_to_nrpoint(p[2], scale), pointfx_to_nrpoint(p[0], scale), pointfx_to_nrpoint(p[1], scale));
553                                         p += 3;
554                                     }
555                                     break;
556                             }
557                             curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1);
558                         }
559                         n_g.outline->Close();
560                     }
561                     polyOffset += polyHeader->cb;
562                 }
563                 doAdd=true;
564             }
565             delete [] buffer;
566         }
567 #else
568                 if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) {
569                         // shit happened
570                 } else {
571                         if ( FT_HAS_HORIZONTAL(theFace) ) {
572                                 n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM);
573                                 n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM);
574                         } else {
575                                 n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM);
576                         }
577                         if ( FT_HAS_VERTICAL(theFace) ) {
578                                 n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM);
579                                 n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM);
580                         } else {
581                                 n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM);
582                         }
583                         if ( theFace->glyph->format == ft_glyph_format_outline ) {
584                                 FT_Outline_Funcs ft2_outline_funcs = {
585                                         ft2_move_to,
586                                         ft2_line_to,
587                                         ft2_conic_to,
588                                         ft2_cubic_to,
589                                         0, 0
590                                 };
591                                 n_g.outline=new Path;
592                                 ft2_to_liv   tData;
593                                 tData.theP=n_g.outline;
594                                 tData.scale=1.0/((double)theFace->units_per_EM);
595                                 tData.last=Geom::Point(0,0);
596                                 FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &tData);
597                         }
598                         doAdd=true;
599                 }
600 #endif
602                 if ( doAdd ) {
603                         if ( n_g.outline ) {
604                                 n_g.outline->FastBBox(n_g.bbox[0],n_g.bbox[1],n_g.bbox[2],n_g.bbox[3]);
605                 n_g.pathvector=n_g.outline->MakePathVector();
606                         }
607                         glyphs[nbGlyph]=n_g;
608                         id_to_no[glyph_id]=nbGlyph;
609                         nbGlyph++;
610                 }
611     } else {
612     }
615 bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
617         if ( pFont == NULL ) return false;
618     InitTheFace();
619         if ( theFace == NULL ) return false;
620 #ifdef USE_PANGO_WIN32
621     OUTLINETEXTMETRIC otm;
622     if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) return false;
623     double scale=1.0/daddy->fontSize;
624     ascent=fabs(otm.otmAscent*scale);
625     descent=fabs(otm.otmDescent*scale);
626     leading=fabs(otm.otmLineGap*scale);
627 #else
628         if ( theFace->units_per_EM == 0 ) return false; // bitmap font
629         ascent=fabs(((double)theFace->ascender)/((double)theFace->units_per_EM));
630         descent=fabs(((double)theFace->descender)/((double)theFace->units_per_EM));
631         leading=fabs(((double)theFace->height)/((double)theFace->units_per_EM));
632         leading-=ascent+descent;
633 #endif
634         return true;
637 bool font_instance::FontSlope(double &run, double &rise)
639     run = 0.0;
640     rise = 1.0;
642         if ( pFont == NULL ) return false;
643     InitTheFace();
644         if ( theFace == NULL ) return false;
646 #ifdef USE_PANGO_WIN32
647     OUTLINETEXTMETRIC otm;
648     if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) return false;
649     run=otm.otmsCharSlopeRun;
650     rise=otm.otmsCharSlopeRise;
651 #else
652         if ( !FT_IS_SCALABLE(theFace) ) return false; // bitmap font
654     TT_HoriHeader *hhea = (TT_HoriHeader*)FT_Get_Sfnt_Table(theFace, ft_sfnt_hhea);
655     if (hhea == NULL) return false;
656     run = hhea->caret_Slope_Run;
657     rise = hhea->caret_Slope_Rise;
658 #endif
659         return true;
662 Geom::OptRect font_instance::BBox(int glyph_id)
664         int no=-1;
665         if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
666                 LoadGlyph(glyph_id);
667                 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
668                         // didn't load
669                 } else {
670                         no=id_to_no[glyph_id];
671                 }
672         } else {
673                 no=id_to_no[glyph_id];
674         }
675         if ( no < 0 ) {
676             return Geom::OptRect();
677         } else {
678             Geom::Point rmin(glyphs[no].bbox[0],glyphs[no].bbox[1]);
679             Geom::Point rmax(glyphs[no].bbox[2],glyphs[no].bbox[3]);
680             return Geom::Rect(rmin, rmax);
681         }
684 Path* font_instance::Outline(int glyph_id,Path* copyInto)
686         int no=-1;
687         if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
688                 LoadGlyph(glyph_id);
689                 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
690                         // didn't load
691                 } else {
692                         no=id_to_no[glyph_id];
693                 }
694         } else {
695                 no=id_to_no[glyph_id];
696         }
697         if ( no < 0 ) return NULL;
698         Path*    src_o=glyphs[no].outline;
699         if ( copyInto ) {
700                 copyInto->Reset();
701                 copyInto->Copy(src_o);
702                 return copyInto;
703         }
704         return src_o;
707 Geom::PathVector* font_instance::PathVector(int glyph_id)
709     int no = -1;
710     if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
711         LoadGlyph(glyph_id);
712         if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
713             // didn't load
714         } else {
715             no = id_to_no[glyph_id];
716         }
717     } else {
718         no = id_to_no[glyph_id];
719     }
720     if ( no < 0 ) return NULL;
721     return glyphs[no].pathvector;
724 double font_instance::Advance(int glyph_id,bool vertical)
726         int no=-1;
727         if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
728                 LoadGlyph(glyph_id);
729                 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
730                         // didn't load
731                 } else {
732                         no=id_to_no[glyph_id];
733                 }
734         } else {
735                 no=id_to_no[glyph_id];
736         }
737         if ( no >= 0 ) {
738                 if ( vertical ) {
739                         return glyphs[no].v_advance;
740                 } else {
741                         return glyphs[no].h_advance;
742                 }
743         }
744         return 0;
748 raster_font* font_instance::RasterFont(const Geom::Matrix &trs, double stroke_width, bool vertical, JoinType stroke_join, ButtType stroke_cap, float /*miter_limit*/)
750         font_style  nStyle;
751         nStyle.transform=trs;
752         nStyle.vertical=vertical;
753         nStyle.stroke_width=stroke_width;
754         nStyle.stroke_cap=stroke_cap;
755         nStyle.stroke_join=stroke_join;
756         nStyle.nbDash=0;
757         nStyle.dash_offset=0;
758         nStyle.dashes=NULL;
759         return RasterFont(nStyle);
762 raster_font* font_instance::RasterFont(const font_style &inStyle)
764         raster_font  *res=NULL;
765         double *savDashes=NULL;
766         font_style nStyle=inStyle;
767     // for some evil reason font_style doesn't have a copy ctor, so the
768     // stuff that should be done there is done here instead (because the
769     // raster_font ctor copies nStyle).
770         if ( nStyle.stroke_width > 0 && nStyle.nbDash > 0 && nStyle.dashes ) {
771                 savDashes=nStyle.dashes;
772                 nStyle.dashes=(double*)malloc(nStyle.nbDash*sizeof(double));
773                 memcpy(nStyle.dashes,savDashes,nStyle.nbDash*sizeof(double));
774         }
775         StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr);
776         if ( loadedStyles.find(nStyle) == loadedStyles.end() ) {
777                 raster_font *nR = new raster_font(nStyle);
778                 nR->Ref();
779                 nR->daddy=this;
780                 loadedStyles[nStyle]=nR;
781                 res=nR;
782                 if ( res ) Ref();
783         } else {
784                 res=loadedStyles[nStyle];
785                 res->Ref();
786                 if ( nStyle.dashes ) free(nStyle.dashes); // since they're not taken by a new rasterfont
787         }
788         nStyle.dashes=savDashes;
789         return res;
792 void font_instance::RemoveRasterFont(raster_font* who)
794     if ( who ) {
795         StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr);
796         if ( loadedStyles.find(who->style) == loadedStyles.end() ) {
797             //g_print("RemoveRasterFont failed \n");
798             // not found
799         } else {
800             loadedStyles.erase(loadedStyles.find(who->style));
801             //g_print("RemoveRasterFont\n");
802             Unref();
803         }
804     }
809 /*
810  Local Variables:
811  mode:c++
812  c-file-style:"stroustrup"
813  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
814  indent-tabs-mode:nil
815  fill-column:99
816  End:
817  */
818 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :