Code

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