Code

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