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