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