be5eb86c893cc6d7a38d162f32122167bb3adca5
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;
125 }
127 static int ft2_line_to(FREETYPE_VECTOR *to, void *i_user)
128 {
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;
135 }
137 static int ft2_conic_to(FREETYPE_VECTOR *control, FREETYPE_VECTOR *to, void *i_user)
138 {
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;
147 }
149 static int ft2_cubic_to(FREETYPE_VECTOR *control1, FREETYPE_VECTOR *control2, FREETYPE_VECTOR *to, void *i_user)
150 {
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;
159 }
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)
178 {
179 //printf("font instance born\n");
180 }
182 font_instance::~font_instance(void)
183 {
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;
223 }
225 void font_instance::Ref(void)
226 {
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);
231 }
233 void font_instance::Unref(void)
234 {
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 }
244 }
246 unsigned int font_instance::Name(gchar *str, unsigned int size)
247 {
248 return Attribute("name", str, size);
249 }
251 unsigned int font_instance::Family(gchar *str, unsigned int size)
252 {
253 return Attribute("family", str, size);
254 }
256 unsigned int font_instance::PSName(gchar *str, unsigned int size)
257 {
258 return Attribute("psname", str, size);
259 }
261 unsigned int font_instance::Attribute(const gchar *key, gchar *str, unsigned int size)
262 {
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_ULTRALIGHT ) {
311 res=(char*)"200";
312 } else if ( v <= PANGO_WEIGHT_LIGHT ) {
313 res=(char*)"300";
314 } else if ( v <= PANGO_WEIGHT_NORMAL ) {
315 res=(char*)"normal";
316 } else if ( v <= PANGO_WEIGHT_BOLD ) {
317 res=(char*)"bold";
318 } else if ( v <= PANGO_WEIGHT_ULTRABOLD ) {
319 res=(char*)"800";
320 } else { // HEAVY
321 res=(char*)"900";
322 }
323 free_res=false;
324 } else if ( strcmp(key,"stretch") == 0 ) {
325 PangoStretch v=pango_font_description_get_stretch(descr);
326 if ( v <= PANGO_STRETCH_EXTRA_CONDENSED ) {
327 res=(char*)"extra-condensed";
328 } else if ( v <= PANGO_STRETCH_CONDENSED ) {
329 res=(char*)"condensed";
330 } else if ( v <= PANGO_STRETCH_SEMI_CONDENSED ) {
331 res=(char*)"semi-condensed";
332 } else if ( v <= PANGO_STRETCH_NORMAL ) {
333 res=(char*)"normal";
334 } else if ( v <= PANGO_STRETCH_SEMI_EXPANDED ) {
335 res=(char*)"semi-expanded";
336 } else if ( v <= PANGO_STRETCH_EXPANDED ) {
337 res=(char*)"expanded";
338 } else {
339 res=(char*)"extra-expanded";
340 }
341 free_res=false;
342 } else if ( strcmp(key,"variant") == 0 ) {
343 PangoVariant v=pango_font_description_get_variant(descr);
344 if ( v == PANGO_VARIANT_SMALL_CAPS ) {
345 res=(char*)"small-caps";
346 } else {
347 res=(char*)"normal";
348 }
349 free_res=false;
350 } else {
351 res = NULL;
352 free_res=false;
353 }
354 if ( res == NULL ) {
355 if ( size > 0 ) str[0]=0;
356 return 0;
357 }
359 if (res) {
360 unsigned int len=strlen(res);
361 unsigned int rlen=(size-1<len)?size-1:len;
362 if ( str ) {
363 if ( rlen > 0 ) memcpy(str,res,rlen);
364 if ( size > 0 ) str[rlen]=0;
365 }
366 if (free_res) free(res);
367 return len;
368 }
369 return 0;
370 }
372 void font_instance::InitTheFace()
373 {
374 #ifdef USE_PANGO_WIN32
375 if ( !theFace ) {
376 LOGFONT *lf=pango_win32_font_logfont(pFont);
377 g_assert(lf != NULL);
378 theFace=pango_win32_font_cache_load(daddy->pangoFontCache,lf);
379 g_free(lf);
380 }
381 XFORM identity = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
382 SetWorldTransform(daddy->hScreenDC, &identity);
383 SetGraphicsMode(daddy->hScreenDC, GM_COMPATIBLE);
384 SelectObject(daddy->hScreenDC,theFace);
385 #else
386 theFace=pango_ft2_font_get_face(pFont);
387 if ( theFace )
388 FT_Select_Charmap(theFace,ft_encoding_unicode) && FT_Select_Charmap(theFace,ft_encoding_symbol);
389 #endif
390 }
392 void font_instance::FreeTheFace()
393 {
394 #ifdef USE_PANGO_WIN32
395 SelectObject(daddy->hScreenDC,GetStockObject(SYSTEM_FONT));
396 pango_win32_font_cache_unload(daddy->pangoFontCache,theFace);
397 #endif
398 theFace=NULL;
399 }
401 void font_instance::InstallFace(PangoFont* iFace)
402 {
403 if ( !iFace )
404 return;
405 pFont=iFace;
407 InitTheFace();
409 if ( pFont && IsOutlineFont() == false ) {
410 FreeTheFace();
411 if ( pFont ) g_object_unref(pFont);
412 pFont=NULL;
413 }
414 }
416 bool font_instance::IsOutlineFont(void)
417 {
418 if ( pFont == NULL ) return false;
419 InitTheFace();
420 #ifdef USE_PANGO_WIN32
421 TEXTMETRIC tm;
422 return GetTextMetrics(daddy->hScreenDC,&tm) && tm.tmPitchAndFamily&(TMPF_TRUETYPE|TMPF_DEVICE);
423 #else
424 return FT_IS_SCALABLE(theFace);
425 #endif
426 }
428 int font_instance::MapUnicodeChar(gunichar c)
429 {
430 if ( pFont == NULL ) return 0;
431 #ifdef USE_PANGO_WIN32
432 return pango_win32_font_get_glyph_index(pFont,c);
433 #else
434 int res=0;
435 theFace=pango_ft2_font_get_face(pFont);
436 if ( c > 0xf0000 ) {
437 res=CLAMP(c,0xf0000,0x1fffff)-0xf0000;
438 } else {
439 res=FT_Get_Char_Index(theFace, c);
440 }
441 return res;
442 #endif
443 }
446 #ifdef USE_PANGO_WIN32
447 static inline Geom::Point pointfx_to_nrpoint(const POINTFX &p, double scale)
448 {
449 return Geom::Point(*(long*)&p.x / 65536.0 * scale,
450 *(long*)&p.y / 65536.0 * scale);
451 }
452 #endif
454 void font_instance::LoadGlyph(int glyph_id)
455 {
456 if ( pFont == NULL ) return;
457 InitTheFace();
458 #ifndef USE_PANGO_WIN32
459 if ( !FT_IS_SCALABLE(theFace) ) return; // bitmap font
460 #endif
462 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
463 if ( nbGlyph >= maxGlyph ) {
464 maxGlyph=2*nbGlyph+1;
465 glyphs=(font_glyph*)realloc(glyphs,maxGlyph*sizeof(font_glyph));
466 }
467 font_glyph n_g;
468 n_g.outline=NULL;
469 n_g.pathvector=NULL;
470 n_g.bbox[0]=n_g.bbox[1]=n_g.bbox[2]=n_g.bbox[3]=0;
471 bool doAdd=false;
473 #ifdef USE_PANGO_WIN32
475 #ifndef GGO_UNHINTED // For compatibility with old SDKs.
476 #define GGO_UNHINTED 0x0100
477 #endif
479 MAT2 identity = {{0,1},{0,0},{0,0},{0,1}};
480 OUTLINETEXTMETRIC otm;
481 GetOutlineTextMetrics(daddy->hScreenDC, sizeof(otm), &otm);
482 GLYPHMETRICS metrics;
483 DWORD bufferSize=GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, 0, NULL, &identity);
484 double scale=1.0/daddy->fontSize;
485 n_g.h_advance=metrics.gmCellIncX*scale;
486 n_g.v_advance=otm.otmTextMetrics.tmHeight*scale;
487 n_g.h_width=metrics.gmBlackBoxX*scale;
488 n_g.v_width=metrics.gmBlackBoxY*scale;
489 n_g.outline=NULL;
490 if ( bufferSize == GDI_ERROR) {
491 // shit happened
492 } else if ( bufferSize == 0) {
493 // character has no visual representation, but is valid (eg whitespace)
494 doAdd=true;
495 } else {
496 std::auto_ptr<char> buffer(new char[bufferSize]);
497 if ( GetGlyphOutline (daddy->hScreenDC, glyph_id, GGO_GLYPH_INDEX | GGO_NATIVE | GGO_UNHINTED, &metrics, bufferSize, buffer.get(), &identity) <= 0 ) {
498 // shit happened
499 } else {
500 // Platform SDK is rubbish, read KB87115 instead
501 n_g.outline=new Path;
502 DWORD polyOffset=0;
503 while ( polyOffset < bufferSize ) {
504 TTPOLYGONHEADER const *polyHeader=(TTPOLYGONHEADER const *)(buffer.get()+polyOffset);
505 if (polyOffset+polyHeader->cb > bufferSize) break;
507 if (polyHeader->dwType == TT_POLYGON_TYPE) {
508 n_g.outline->MoveTo(pointfx_to_nrpoint(polyHeader->pfxStart, scale));
509 DWORD curveOffset=polyOffset+sizeof(TTPOLYGONHEADER);
511 while ( curveOffset < polyOffset+polyHeader->cb ) {
512 TTPOLYCURVE const *polyCurve=(TTPOLYCURVE const *)(buffer.get()+curveOffset);
513 POINTFX const *p=polyCurve->apfx;
514 POINTFX const *endp=p+polyCurve->cpfx;
516 switch (polyCurve->wType) {
517 case TT_PRIM_LINE:
518 while ( p != endp )
519 n_g.outline->LineTo(pointfx_to_nrpoint(*p++, scale));
520 break;
522 case TT_PRIM_QSPLINE:
523 {
524 g_assert(polyCurve->cpfx >= 2);
525 endp -= 2;
526 Geom::Point this_mid=pointfx_to_nrpoint(p[0], scale);
527 while ( p != endp ) {
528 Geom::Point next_mid=pointfx_to_nrpoint(p[1], scale);
529 n_g.outline->BezierTo((next_mid+this_mid)/2);
530 n_g.outline->IntermBezierTo(this_mid);
531 n_g.outline->EndBezierTo();
532 ++p;
533 this_mid=next_mid;
534 }
535 n_g.outline->BezierTo(pointfx_to_nrpoint(p[1], scale));
536 n_g.outline->IntermBezierTo(this_mid);
537 n_g.outline->EndBezierTo();
538 break;
539 }
541 case 3: // TT_PRIM_CSPLINE
542 g_assert(polyCurve->cpfx % 3 == 0);
543 while ( p != endp ) {
544 n_g.outline->CubicTo(pointfx_to_nrpoint(p[2], scale), pointfx_to_nrpoint(p[0], scale), pointfx_to_nrpoint(p[1], scale));
545 p += 3;
546 }
547 break;
548 }
549 curveOffset += sizeof(TTPOLYCURVE)+sizeof(POINTFX)*(polyCurve->cpfx-1);
550 }
551 n_g.outline->Close();
552 }
553 polyOffset += polyHeader->cb;
554 }
555 doAdd=true;
556 }
557 }
558 #else
559 if (FT_Load_Glyph (theFace, glyph_id, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) {
560 // shit happened
561 } else {
562 if ( FT_HAS_HORIZONTAL(theFace) ) {
563 n_g.h_advance=((double)theFace->glyph->metrics.horiAdvance)/((double)theFace->units_per_EM);
564 n_g.h_width=((double)theFace->glyph->metrics.width)/((double)theFace->units_per_EM);
565 } else {
566 n_g.h_width=n_g.h_advance=((double)(theFace->bbox.xMax-theFace->bbox.xMin))/((double)theFace->units_per_EM);
567 }
568 if ( FT_HAS_VERTICAL(theFace) ) {
569 n_g.v_advance=((double)theFace->glyph->metrics.vertAdvance)/((double)theFace->units_per_EM);
570 n_g.v_width=((double)theFace->glyph->metrics.height)/((double)theFace->units_per_EM);
571 } else {
572 n_g.v_width=n_g.v_advance=((double)theFace->height)/((double)theFace->units_per_EM);
573 }
574 if ( theFace->glyph->format == ft_glyph_format_outline ) {
575 FT_Outline_Funcs ft2_outline_funcs = {
576 ft2_move_to,
577 ft2_line_to,
578 ft2_conic_to,
579 ft2_cubic_to,
580 0, 0
581 };
582 n_g.outline=new Path;
583 ft2_to_liv tData;
584 tData.theP=n_g.outline;
585 tData.scale=1.0/((double)theFace->units_per_EM);
586 tData.last=Geom::Point(0,0);
587 FT_Outline_Decompose (&theFace->glyph->outline, &ft2_outline_funcs, &tData);
588 }
589 doAdd=true;
590 }
591 #endif
593 if ( doAdd ) {
594 if ( n_g.outline ) {
595 n_g.outline->FastBBox(n_g.bbox[0],n_g.bbox[1],n_g.bbox[2],n_g.bbox[3]);
596 n_g.pathvector=n_g.outline->MakePathVector();
597 }
598 glyphs[nbGlyph]=n_g;
599 id_to_no[glyph_id]=nbGlyph;
600 nbGlyph++;
601 }
602 } else {
603 }
604 }
606 bool font_instance::FontMetrics(double &ascent,double &descent,double &leading)
607 {
608 if ( pFont == NULL ) return false;
609 InitTheFace();
610 if ( theFace == NULL ) return false;
611 #ifdef USE_PANGO_WIN32
612 OUTLINETEXTMETRIC otm;
613 if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) return false;
614 double scale=1.0/daddy->fontSize;
615 ascent=fabs(otm.otmAscent*scale);
616 descent=fabs(otm.otmDescent*scale);
617 leading=fabs(otm.otmLineGap*scale);
618 #else
619 if ( theFace->units_per_EM == 0 ) return false; // bitmap font
620 ascent=fabs(((double)theFace->ascender)/((double)theFace->units_per_EM));
621 descent=fabs(((double)theFace->descender)/((double)theFace->units_per_EM));
622 leading=fabs(((double)theFace->height)/((double)theFace->units_per_EM));
623 leading-=ascent+descent;
624 #endif
625 return true;
626 }
628 bool font_instance::FontSlope(double &run, double &rise)
629 {
630 run = 0.0;
631 rise = 1.0;
633 if ( pFont == NULL ) return false;
634 InitTheFace();
635 if ( theFace == NULL ) return false;
637 #ifdef USE_PANGO_WIN32
638 OUTLINETEXTMETRIC otm;
639 if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) return false;
640 run=otm.otmsCharSlopeRun;
641 rise=otm.otmsCharSlopeRise;
642 #else
643 if ( !FT_IS_SCALABLE(theFace) ) return false; // bitmap font
645 TT_HoriHeader *hhea = (TT_HoriHeader*)FT_Get_Sfnt_Table(theFace, ft_sfnt_hhea);
646 if (hhea == NULL) return false;
647 run = hhea->caret_Slope_Run;
648 rise = hhea->caret_Slope_Rise;
649 #endif
650 return true;
651 }
653 Geom::OptRect font_instance::BBox(int glyph_id)
654 {
655 int no=-1;
656 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
657 LoadGlyph(glyph_id);
658 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
659 // didn't load
660 } else {
661 no=id_to_no[glyph_id];
662 }
663 } else {
664 no=id_to_no[glyph_id];
665 }
666 if ( no < 0 ) {
667 return Geom::OptRect();
668 } else {
669 Geom::Point rmin(glyphs[no].bbox[0],glyphs[no].bbox[1]);
670 Geom::Point rmax(glyphs[no].bbox[2],glyphs[no].bbox[3]);
671 return Geom::Rect(rmin, rmax);
672 }
673 }
675 Path* font_instance::Outline(int glyph_id,Path* copyInto)
676 {
677 int no=-1;
678 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
679 LoadGlyph(glyph_id);
680 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
681 // didn't load
682 } else {
683 no=id_to_no[glyph_id];
684 }
685 } else {
686 no=id_to_no[glyph_id];
687 }
688 if ( no < 0 ) return NULL;
689 Path* src_o=glyphs[no].outline;
690 if ( copyInto ) {
691 copyInto->Reset();
692 copyInto->Copy(src_o);
693 return copyInto;
694 }
695 return src_o;
696 }
698 Geom::PathVector* font_instance::PathVector(int glyph_id)
699 {
700 int no = -1;
701 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
702 LoadGlyph(glyph_id);
703 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
704 // didn't load
705 } else {
706 no = id_to_no[glyph_id];
707 }
708 } else {
709 no = id_to_no[glyph_id];
710 }
711 if ( no < 0 ) return NULL;
712 return glyphs[no].pathvector;
713 }
715 double font_instance::Advance(int glyph_id,bool vertical)
716 {
717 int no=-1;
718 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
719 LoadGlyph(glyph_id);
720 if ( id_to_no.find(glyph_id) == id_to_no.end() ) {
721 // didn't load
722 } else {
723 no=id_to_no[glyph_id];
724 }
725 } else {
726 no=id_to_no[glyph_id];
727 }
728 if ( no >= 0 ) {
729 if ( vertical ) {
730 return glyphs[no].v_advance;
731 } else {
732 return glyphs[no].h_advance;
733 }
734 }
735 return 0;
736 }
739 raster_font* font_instance::RasterFont(const Geom::Matrix &trs, double stroke_width, bool vertical, JoinType stroke_join, ButtType stroke_cap, float /*miter_limit*/)
740 {
741 font_style nStyle;
742 nStyle.transform=trs;
743 nStyle.vertical=vertical;
744 nStyle.stroke_width=stroke_width;
745 nStyle.stroke_cap=stroke_cap;
746 nStyle.stroke_join=stroke_join;
747 nStyle.nbDash=0;
748 nStyle.dash_offset=0;
749 nStyle.dashes=NULL;
750 return RasterFont(nStyle);
751 }
753 raster_font* font_instance::RasterFont(const font_style &inStyle)
754 {
755 raster_font *res=NULL;
756 double *savDashes=NULL;
757 font_style nStyle=inStyle;
758 // for some evil reason font_style doesn't have a copy ctor, so the
759 // stuff that should be done there is done here instead (because the
760 // raster_font ctor copies nStyle).
761 if ( nStyle.stroke_width > 0 && nStyle.nbDash > 0 && nStyle.dashes ) {
762 savDashes=nStyle.dashes;
763 nStyle.dashes=(double*)malloc(nStyle.nbDash*sizeof(double));
764 memcpy(nStyle.dashes,savDashes,nStyle.nbDash*sizeof(double));
765 }
766 StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr);
767 if ( loadedStyles.find(nStyle) == loadedStyles.end() ) {
768 raster_font *nR = new raster_font(nStyle);
769 nR->Ref();
770 nR->daddy=this;
771 loadedStyles[nStyle]=nR;
772 res=nR;
773 if ( res ) Ref();
774 } else {
775 res=loadedStyles[nStyle];
776 res->Ref();
777 if ( nStyle.dashes ) free(nStyle.dashes); // since they're not taken by a new rasterfont
778 }
779 nStyle.dashes=savDashes;
780 return res;
781 }
783 void font_instance::RemoveRasterFont(raster_font* who)
784 {
785 if ( who ) {
786 StyleMap& loadedStyles = *static_cast<StyleMap*>(loadedPtr);
787 if ( loadedStyles.find(who->style) == loadedStyles.end() ) {
788 //g_print("RemoveRasterFont failed \n");
789 // not found
790 } else {
791 loadedStyles.erase(loadedStyles.find(who->style));
792 //g_print("RemoveRasterFont\n");
793 Unref();
794 }
795 }
796 }
800 /*
801 Local Variables:
802 mode:c++
803 c-file-style:"stroustrup"
804 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
805 indent-tabs-mode:nil
806 fill-column:99
807 End:
808 */
809 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :