Rev 1892 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1892 | serge | 1 | /* |
2 | * Copyright © 2004 Keith Packard |
||
3 | * Copyright © 2008 Red Hat, Inc. |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it either under the terms of the GNU Lesser General Public |
||
7 | * License version 2.1 as published by the Free Software Foundation |
||
8 | * (the "LGPL") or, at your option, under the terms of the Mozilla |
||
9 | * Public License Version 1.1 (the "MPL"). If you do not alter this |
||
10 | * notice, a recipient may use your version of this file under either |
||
11 | * the MPL or the LGPL. |
||
12 | * |
||
13 | * You should have received a copy of the LGPL along with this library |
||
14 | * in the file COPYING-LGPL-2.1; if not, write to the Free Software |
||
15 | * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA |
||
16 | * You should have received a copy of the MPL along with this library |
||
17 | * in the file COPYING-MPL-1.1 |
||
18 | * |
||
19 | * The contents of this file are subject to the Mozilla Public License |
||
20 | * Version 1.1 (the "License"); you may not use this file except in |
||
21 | * compliance with the License. You may obtain a copy of the License at |
||
22 | * http://www.mozilla.org/MPL/ |
||
23 | * |
||
24 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY |
||
25 | * OF ANY KIND, either express or implied. See the LGPL or the MPL for |
||
26 | * the specific language governing rights and limitations. |
||
27 | * |
||
28 | * The Original Code is the cairo graphics library. |
||
29 | * |
||
30 | * The Initial Developer of the Original Code is Keith Packard |
||
31 | * |
||
32 | * Contributor(s): |
||
33 | * Keith Packard |
||
34 | * Behdad Esfahbod |
||
35 | */ |
||
36 | |||
37 | #include "cairoint.h" |
||
38 | #include "cairo-error-private.h" |
||
39 | |||
40 | #include |
||
41 | |||
42 | /* |
||
43 | * This file implements a user-font rendering the descendant of the Hershey |
||
44 | * font coded by Keith Packard for use in the Twin window system. |
||
45 | * The actual font data is in cairo-font-face-twin-data.c |
||
46 | * |
||
47 | * Ported to cairo user font and extended by Behdad Esfahbod. |
||
48 | */ |
||
49 | |||
50 | |||
51 | |||
52 | static cairo_user_data_key_t twin_properties_key; |
||
53 | |||
54 | |||
55 | /* |
||
56 | * Face properties |
||
57 | */ |
||
58 | |||
59 | /* We synthesize multiple faces from the twin data. Here is the parameters. */ |
||
60 | |||
61 | /* The following tables and matching code are copied from Pango */ |
||
62 | |||
63 | /* CSS weight */ |
||
64 | typedef enum { |
||
65 | TWIN_WEIGHT_THIN = 100, |
||
66 | TWIN_WEIGHT_ULTRALIGHT = 200, |
||
67 | TWIN_WEIGHT_LIGHT = 300, |
||
68 | TWIN_WEIGHT_BOOK = 380, |
||
69 | TWIN_WEIGHT_NORMAL = 400, |
||
70 | TWIN_WEIGHT_MEDIUM = 500, |
||
71 | TWIN_WEIGHT_SEMIBOLD = 600, |
||
72 | TWIN_WEIGHT_BOLD = 700, |
||
73 | TWIN_WEIGHT_ULTRABOLD = 800, |
||
74 | TWIN_WEIGHT_HEAVY = 900, |
||
75 | TWIN_WEIGHT_ULTRAHEAVY = 1000 |
||
76 | } twin_face_weight_t; |
||
77 | |||
78 | /* CSS stretch */ |
||
79 | typedef enum { |
||
80 | TWIN_STRETCH_ULTRA_CONDENSED, |
||
81 | TWIN_STRETCH_EXTRA_CONDENSED, |
||
82 | TWIN_STRETCH_CONDENSED, |
||
83 | TWIN_STRETCH_SEMI_CONDENSED, |
||
84 | TWIN_STRETCH_NORMAL, |
||
85 | TWIN_STRETCH_SEMI_EXPANDED, |
||
86 | TWIN_STRETCH_EXPANDED, |
||
87 | TWIN_STRETCH_EXTRA_EXPANDED, |
||
88 | TWIN_STRETCH_ULTRA_EXPANDED |
||
89 | } twin_face_stretch_t; |
||
90 | |||
91 | typedef struct |
||
92 | { |
||
93 | int value; |
||
94 | const char str[16]; |
||
95 | } FieldMap; |
||
96 | |||
97 | static const FieldMap slant_map[] = { |
||
98 | { CAIRO_FONT_SLANT_NORMAL, "" }, |
||
99 | { CAIRO_FONT_SLANT_NORMAL, "Roman" }, |
||
100 | { CAIRO_FONT_SLANT_OBLIQUE, "Oblique" }, |
||
101 | { CAIRO_FONT_SLANT_ITALIC, "Italic" } |
||
102 | }; |
||
103 | |||
104 | static const FieldMap smallcaps_map[] = { |
||
105 | { FALSE, "" }, |
||
106 | { TRUE, "Small-Caps" } |
||
107 | }; |
||
108 | |||
109 | static const FieldMap weight_map[] = { |
||
110 | { TWIN_WEIGHT_THIN, "Thin" }, |
||
111 | { TWIN_WEIGHT_ULTRALIGHT, "Ultra-Light" }, |
||
112 | { TWIN_WEIGHT_ULTRALIGHT, "Extra-Light" }, |
||
113 | { TWIN_WEIGHT_LIGHT, "Light" }, |
||
114 | { TWIN_WEIGHT_BOOK, "Book" }, |
||
115 | { TWIN_WEIGHT_NORMAL, "" }, |
||
116 | { TWIN_WEIGHT_NORMAL, "Regular" }, |
||
117 | { TWIN_WEIGHT_MEDIUM, "Medium" }, |
||
118 | { TWIN_WEIGHT_SEMIBOLD, "Semi-Bold" }, |
||
119 | { TWIN_WEIGHT_SEMIBOLD, "Demi-Bold" }, |
||
120 | { TWIN_WEIGHT_BOLD, "Bold" }, |
||
121 | { TWIN_WEIGHT_ULTRABOLD, "Ultra-Bold" }, |
||
122 | { TWIN_WEIGHT_ULTRABOLD, "Extra-Bold" }, |
||
123 | { TWIN_WEIGHT_HEAVY, "Heavy" }, |
||
124 | { TWIN_WEIGHT_HEAVY, "Black" }, |
||
125 | { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Heavy" }, |
||
126 | { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Heavy" }, |
||
127 | { TWIN_WEIGHT_ULTRAHEAVY, "Ultra-Black" }, |
||
128 | { TWIN_WEIGHT_ULTRAHEAVY, "Extra-Black" } |
||
129 | }; |
||
130 | |||
131 | static const FieldMap stretch_map[] = { |
||
132 | { TWIN_STRETCH_ULTRA_CONDENSED, "Ultra-Condensed" }, |
||
133 | { TWIN_STRETCH_EXTRA_CONDENSED, "Extra-Condensed" }, |
||
134 | { TWIN_STRETCH_CONDENSED, "Condensed" }, |
||
135 | { TWIN_STRETCH_SEMI_CONDENSED, "Semi-Condensed" }, |
||
136 | { TWIN_STRETCH_NORMAL, "" }, |
||
137 | { TWIN_STRETCH_SEMI_EXPANDED, "Semi-Expanded" }, |
||
138 | { TWIN_STRETCH_EXPANDED, "Expanded" }, |
||
139 | { TWIN_STRETCH_EXTRA_EXPANDED, "Extra-Expanded" }, |
||
140 | { TWIN_STRETCH_ULTRA_EXPANDED, "Ultra-Expanded" } |
||
141 | }; |
||
142 | |||
143 | static const FieldMap monospace_map[] = { |
||
144 | { FALSE, "" }, |
||
145 | { TRUE, "Mono" }, |
||
146 | { TRUE, "Monospace" } |
||
147 | }; |
||
148 | |||
149 | |||
150 | typedef struct _twin_face_properties { |
||
151 | cairo_font_slant_t slant; |
||
152 | twin_face_weight_t weight; |
||
153 | twin_face_stretch_t stretch; |
||
154 | |||
155 | /* lets have some fun */ |
||
156 | cairo_bool_t monospace; |
||
157 | cairo_bool_t smallcaps; |
||
158 | } twin_face_properties_t; |
||
159 | |||
160 | static cairo_bool_t |
||
161 | field_matches (const char *s1, |
||
162 | const char *s2, |
||
163 | int len) |
||
164 | { |
||
165 | int c1, c2; |
||
166 | |||
167 | while (len && *s1 && *s2) |
||
168 | { |
||
169 | #define TOLOWER(c) \ |
||
170 | (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) |
||
171 | |||
172 | c1 = TOLOWER (*s1); |
||
173 | c2 = TOLOWER (*s2); |
||
174 | if (c1 != c2) { |
||
175 | if (c1 == '-') { |
||
176 | s1++; |
||
177 | continue; |
||
178 | } |
||
179 | return FALSE; |
||
180 | } |
||
181 | s1++; s2++; |
||
182 | len--; |
||
183 | } |
||
184 | |||
185 | return len == 0 && *s1 == '\0'; |
||
186 | } |
||
187 | |||
188 | static cairo_bool_t |
||
189 | parse_int (const char *word, |
||
190 | size_t wordlen, |
||
191 | int *out) |
||
192 | { |
||
193 | char *end; |
||
194 | long val = strtol (word, &end, 10); |
||
195 | int i = val; |
||
196 | |||
197 | if (end != word && (end == word + wordlen) && val >= 0 && val == i) |
||
198 | { |
||
199 | if (out) |
||
200 | *out = i; |
||
201 | |||
202 | return TRUE; |
||
203 | } |
||
204 | |||
205 | return FALSE; |
||
206 | } |
||
207 | |||
208 | static cairo_bool_t |
||
209 | find_field (const char *what, |
||
210 | const FieldMap *map, |
||
211 | int n_elements, |
||
212 | const char *str, |
||
213 | int len, |
||
214 | int *val) |
||
215 | { |
||
216 | int i; |
||
217 | cairo_bool_t had_prefix = FALSE; |
||
218 | |||
219 | if (what) |
||
220 | { |
||
221 | i = strlen (what); |
||
222 | if (len > i && 0 == strncmp (what, str, i) && str[i] == '=') |
||
223 | { |
||
224 | str += i + 1; |
||
225 | len -= i + 1; |
||
226 | had_prefix = TRUE; |
||
227 | } |
||
228 | } |
||
229 | |||
230 | for (i=0; i |
||
231 | { |
||
232 | if (map[i].str[0] && field_matches (map[i].str, str, len)) |
||
233 | { |
||
234 | if (val) |
||
235 | *val = map[i].value; |
||
236 | return TRUE; |
||
237 | } |
||
238 | } |
||
239 | |||
240 | if (!what || had_prefix) |
||
241 | return parse_int (str, len, val); |
||
242 | |||
243 | return FALSE; |
||
244 | } |
||
245 | |||
246 | static void |
||
247 | parse_field (twin_face_properties_t *props, |
||
248 | const char *str, |
||
249 | int len) |
||
250 | { |
||
251 | if (field_matches ("Normal", str, len)) |
||
252 | return; |
||
253 | |||
254 | #define FIELD(NAME) \ |
||
255 | if (find_field (STRINGIFY (NAME), NAME##_map, ARRAY_LENGTH (NAME##_map), str, len, \ |
||
256 | (int *)(void *)&props->NAME)) \ |
||
257 | return; \ |
||
258 | |||
259 | FIELD (weight); |
||
260 | FIELD (slant); |
||
261 | FIELD (stretch); |
||
262 | FIELD (smallcaps); |
||
263 | FIELD (monospace); |
||
264 | |||
265 | #undef FIELD |
||
266 | } |
||
267 | |||
268 | static void |
||
269 | face_props_parse (twin_face_properties_t *props, |
||
270 | const char *s) |
||
271 | { |
||
272 | const char *start, *end; |
||
273 | |||
274 | for (start = end = s; *end; end++) { |
||
275 | if (*end != ' ' && *end != ':') |
||
276 | continue; |
||
277 | |||
278 | if (start < end) |
||
279 | parse_field (props, start, end - start); |
||
280 | start = end + 1; |
||
281 | } |
||
282 | if (start < end) |
||
283 | parse_field (props, start, end - start); |
||
284 | } |
||
285 | |||
3959 | Serge | 286 | static twin_face_properties_t * |
287 | twin_font_face_create_properties (cairo_font_face_t *twin_face) |
||
1892 | serge | 288 | { |
289 | twin_face_properties_t *props; |
||
290 | |||
291 | props = malloc (sizeof (twin_face_properties_t)); |
||
292 | if (unlikely (props == NULL)) |
||
3959 | Serge | 293 | return NULL; |
1892 | serge | 294 | |
295 | props->stretch = TWIN_STRETCH_NORMAL; |
||
296 | props->slant = CAIRO_FONT_SLANT_NORMAL; |
||
297 | props->weight = TWIN_WEIGHT_NORMAL; |
||
298 | props->monospace = FALSE; |
||
299 | props->smallcaps = FALSE; |
||
300 | |||
3959 | Serge | 301 | if (unlikely (cairo_font_face_set_user_data (twin_face, |
1892 | serge | 302 | &twin_properties_key, |
3959 | Serge | 303 | props, free))) { |
1892 | serge | 304 | free (props); |
3959 | Serge | 305 | return NULL; |
1892 | serge | 306 | } |
307 | |||
3959 | Serge | 308 | return props; |
1892 | serge | 309 | } |
310 | |||
311 | static cairo_status_t |
||
312 | twin_font_face_set_properties_from_toy (cairo_font_face_t *twin_face, |
||
313 | cairo_toy_font_face_t *toy_face) |
||
314 | { |
||
315 | twin_face_properties_t *props; |
||
316 | |||
3959 | Serge | 317 | props = twin_font_face_create_properties (twin_face); |
318 | if (unlikely (props == NULL)) |
||
319 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
1892 | serge | 320 | |
321 | props->slant = toy_face->slant; |
||
322 | props->weight = toy_face->weight == CAIRO_FONT_WEIGHT_NORMAL ? |
||
323 | TWIN_WEIGHT_NORMAL : TWIN_WEIGHT_BOLD; |
||
324 | face_props_parse (props, toy_face->family); |
||
325 | |||
326 | return CAIRO_STATUS_SUCCESS; |
||
327 | } |
||
328 | |||
329 | |||
330 | /* |
||
331 | * Scaled properties |
||
332 | */ |
||
333 | |||
334 | typedef struct _twin_scaled_properties { |
||
335 | twin_face_properties_t *face_props; |
||
336 | |||
337 | cairo_bool_t snap; /* hint outlines */ |
||
338 | |||
339 | double weight; /* unhinted pen width */ |
||
340 | double penx, peny; /* hinted pen width */ |
||
341 | double marginl, marginr; /* hinted side margins */ |
||
342 | |||
343 | double stretch; /* stretch factor */ |
||
344 | } twin_scaled_properties_t; |
||
345 | |||
346 | static void |
||
347 | compute_hinting_scale (cairo_t *cr, |
||
348 | double x, double y, |
||
349 | double *scale, double *inv) |
||
350 | { |
||
351 | cairo_user_to_device_distance (cr, &x, &y); |
||
352 | *scale = x == 0 ? y : y == 0 ? x :sqrt (x*x + y*y); |
||
353 | *inv = 1 / *scale; |
||
354 | } |
||
355 | |||
356 | static void |
||
357 | compute_hinting_scales (cairo_t *cr, |
||
358 | double *x_scale, double *x_scale_inv, |
||
359 | double *y_scale, double *y_scale_inv) |
||
360 | { |
||
361 | double x, y; |
||
362 | |||
363 | x = 1; y = 0; |
||
364 | compute_hinting_scale (cr, x, y, x_scale, x_scale_inv); |
||
365 | |||
366 | x = 0; y = 1; |
||
367 | compute_hinting_scale (cr, x, y, y_scale, y_scale_inv); |
||
368 | } |
||
369 | |||
370 | #define SNAPXI(p) (_cairo_round ((p) * x_scale) * x_scale_inv) |
||
371 | #define SNAPYI(p) (_cairo_round ((p) * y_scale) * y_scale_inv) |
||
372 | |||
373 | /* This controls the global font size */ |
||
374 | #define F(g) ((g) / 72.) |
||
375 | |||
376 | static void |
||
377 | twin_hint_pen_and_margins(cairo_t *cr, |
||
378 | double *penx, double *peny, |
||
379 | double *marginl, double *marginr) |
||
380 | { |
||
381 | double x_scale, x_scale_inv; |
||
382 | double y_scale, y_scale_inv; |
||
383 | double margin; |
||
384 | |||
385 | compute_hinting_scales (cr, |
||
386 | &x_scale, &x_scale_inv, |
||
387 | &y_scale, &y_scale_inv); |
||
388 | |||
389 | *penx = SNAPXI (*penx); |
||
390 | if (*penx < x_scale_inv) |
||
391 | *penx = x_scale_inv; |
||
392 | |||
393 | *peny = SNAPYI (*peny); |
||
394 | if (*peny < y_scale_inv) |
||
395 | *peny = y_scale_inv; |
||
396 | |||
397 | margin = *marginl + *marginr; |
||
398 | *marginl = SNAPXI (*marginl); |
||
399 | if (*marginl < x_scale_inv) |
||
400 | *marginl = x_scale_inv; |
||
401 | |||
402 | *marginr = margin - *marginl; |
||
403 | if (*marginr < 0) |
||
404 | *marginr = 0; |
||
405 | *marginr = SNAPXI (*marginr); |
||
406 | } |
||
407 | |||
408 | static cairo_status_t |
||
409 | twin_scaled_font_compute_properties (cairo_scaled_font_t *scaled_font, |
||
410 | cairo_t *cr) |
||
411 | { |
||
412 | cairo_status_t status; |
||
413 | twin_scaled_properties_t *props; |
||
414 | |||
415 | props = malloc (sizeof (twin_scaled_properties_t)); |
||
416 | if (unlikely (props == NULL)) |
||
417 | return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
||
418 | |||
419 | |||
420 | props->face_props = cairo_font_face_get_user_data (cairo_scaled_font_get_font_face (scaled_font), |
||
421 | &twin_properties_key); |
||
422 | |||
423 | props->snap = scaled_font->options.hint_style > CAIRO_HINT_STYLE_NONE; |
||
424 | |||
425 | /* weight */ |
||
426 | props->weight = props->face_props->weight * (F (4) / TWIN_WEIGHT_NORMAL); |
||
427 | |||
428 | /* pen & margins */ |
||
429 | props->penx = props->peny = props->weight; |
||
430 | props->marginl = props->marginr = F (4); |
||
431 | if (scaled_font->options.hint_style > CAIRO_HINT_STYLE_SLIGHT) |
||
432 | twin_hint_pen_and_margins(cr, |
||
433 | &props->penx, &props->peny, |
||
434 | &props->marginl, &props->marginr); |
||
435 | |||
436 | /* stretch */ |
||
437 | props->stretch = 1 + .1 * ((int) props->face_props->stretch - (int) TWIN_STRETCH_NORMAL); |
||
438 | |||
439 | |||
440 | /* Save it */ |
||
441 | status = cairo_scaled_font_set_user_data (scaled_font, |
||
442 | &twin_properties_key, |
||
443 | props, free); |
||
444 | if (unlikely (status)) |
||
445 | goto FREE_PROPS; |
||
446 | |||
447 | return CAIRO_STATUS_SUCCESS; |
||
448 | |||
449 | FREE_PROPS: |
||
450 | free (props); |
||
451 | return status; |
||
452 | } |
||
453 | |||
454 | |||
455 | /* |
||
456 | * User-font implementation |
||
457 | */ |
||
458 | |||
459 | static cairo_status_t |
||
460 | twin_scaled_font_init (cairo_scaled_font_t *scaled_font, |
||
461 | cairo_t *cr, |
||
462 | cairo_font_extents_t *metrics) |
||
463 | { |
||
464 | metrics->ascent = F (54); |
||
465 | metrics->descent = 1 - metrics->ascent; |
||
466 | |||
467 | return twin_scaled_font_compute_properties (scaled_font, cr); |
||
468 | } |
||
469 | |||
470 | #define TWIN_GLYPH_MAX_SNAP_X 4 |
||
471 | #define TWIN_GLYPH_MAX_SNAP_Y 7 |
||
472 | |||
473 | typedef struct { |
||
474 | int n_snap_x; |
||
475 | int8_t snap_x[TWIN_GLYPH_MAX_SNAP_X]; |
||
476 | double snapped_x[TWIN_GLYPH_MAX_SNAP_X]; |
||
477 | int n_snap_y; |
||
478 | int8_t snap_y[TWIN_GLYPH_MAX_SNAP_Y]; |
||
479 | double snapped_y[TWIN_GLYPH_MAX_SNAP_Y]; |
||
480 | } twin_snap_info_t; |
||
481 | |||
482 | #define twin_glyph_left(g) ((g)[0]) |
||
483 | #define twin_glyph_right(g) ((g)[1]) |
||
484 | #define twin_glyph_ascent(g) ((g)[2]) |
||
485 | #define twin_glyph_descent(g) ((g)[3]) |
||
486 | |||
487 | #define twin_glyph_n_snap_x(g) ((g)[4]) |
||
488 | #define twin_glyph_n_snap_y(g) ((g)[5]) |
||
489 | #define twin_glyph_snap_x(g) (&g[6]) |
||
490 | #define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) |
||
491 | #define twin_glyph_draw(g) (twin_glyph_snap_y(g) + twin_glyph_n_snap_y(g)) |
||
492 | |||
493 | static void |
||
494 | twin_compute_snap (cairo_t *cr, |
||
495 | twin_snap_info_t *info, |
||
496 | const signed char *b) |
||
497 | { |
||
498 | int s, n; |
||
499 | const signed char *snap; |
||
500 | double x_scale, x_scale_inv; |
||
501 | double y_scale, y_scale_inv; |
||
502 | |||
503 | compute_hinting_scales (cr, |
||
504 | &x_scale, &x_scale_inv, |
||
505 | &y_scale, &y_scale_inv); |
||
506 | |||
507 | snap = twin_glyph_snap_x (b); |
||
508 | n = twin_glyph_n_snap_x (b); |
||
509 | info->n_snap_x = n; |
||
510 | assert (n <= TWIN_GLYPH_MAX_SNAP_X); |
||
511 | for (s = 0; s < n; s++) { |
||
512 | info->snap_x[s] = snap[s]; |
||
513 | info->snapped_x[s] = SNAPXI (F (snap[s])); |
||
514 | } |
||
515 | |||
516 | snap = twin_glyph_snap_y (b); |
||
517 | n = twin_glyph_n_snap_y (b); |
||
518 | info->n_snap_y = n; |
||
519 | assert (n <= TWIN_GLYPH_MAX_SNAP_Y); |
||
520 | for (s = 0; s < n; s++) { |
||
521 | info->snap_y[s] = snap[s]; |
||
522 | info->snapped_y[s] = SNAPYI (F (snap[s])); |
||
523 | } |
||
524 | } |
||
525 | |||
526 | static double |
||
527 | twin_snap (int8_t v, int n, int8_t *snap, double *snapped) |
||
528 | { |
||
529 | int s; |
||
530 | |||
531 | if (!n) |
||
532 | return F(v); |
||
533 | |||
534 | if (snap[0] == v) |
||
535 | return snapped[0]; |
||
536 | |||
537 | for (s = 0; s < n - 1; s++) |
||
538 | { |
||
539 | if (snap[s+1] == v) |
||
540 | return snapped[s+1]; |
||
541 | |||
542 | if (snap[s] <= v && v <= snap[s+1]) |
||
543 | { |
||
544 | int before = snap[s]; |
||
545 | int after = snap[s+1]; |
||
546 | int dist = after - before; |
||
547 | double snap_before = snapped[s]; |
||
548 | double snap_after = snapped[s+1]; |
||
549 | double dist_before = v - before; |
||
550 | return snap_before + (snap_after - snap_before) * dist_before / dist; |
||
551 | } |
||
552 | } |
||
553 | return F(v); |
||
554 | } |
||
555 | |||
556 | #define SNAPX(p) twin_snap (p, info.n_snap_x, info.snap_x, info.snapped_x) |
||
557 | #define SNAPY(p) twin_snap (p, info.n_snap_y, info.snap_y, info.snapped_y) |
||
558 | |||
559 | static cairo_status_t |
||
560 | twin_scaled_font_render_glyph (cairo_scaled_font_t *scaled_font, |
||
561 | unsigned long glyph, |
||
562 | cairo_t *cr, |
||
563 | cairo_text_extents_t *metrics) |
||
564 | { |
||
565 | double x1, y1, x2, y2, x3, y3; |
||
566 | double marginl; |
||
567 | twin_scaled_properties_t *props; |
||
568 | twin_snap_info_t info; |
||
569 | const int8_t *b; |
||
570 | const int8_t *g; |
||
571 | int8_t w; |
||
572 | double gw; |
||
573 | |||
574 | props = cairo_scaled_font_get_user_data (scaled_font, &twin_properties_key); |
||
575 | |||
576 | /* Save glyph space, we need it when stroking */ |
||
577 | cairo_save (cr); |
||
578 | |||
579 | /* center the pen */ |
||
580 | cairo_translate (cr, props->penx * .5, -props->peny * .5); |
||
581 | |||
582 | /* small-caps */ |
||
583 | if (props->face_props->smallcaps && glyph >= 'a' && glyph <= 'z') { |
||
584 | glyph += 'A' - 'a'; |
||
585 | /* 28 and 42 are small and capital letter heights of the glyph data */ |
||
586 | cairo_scale (cr, 1, 28. / 42); |
||
587 | } |
||
588 | |||
589 | /* slant */ |
||
590 | if (props->face_props->slant != CAIRO_FONT_SLANT_NORMAL) { |
||
591 | cairo_matrix_t shear = { 1, 0, -.2, 1, 0, 0}; |
||
592 | cairo_transform (cr, &shear); |
||
593 | } |
||
594 | |||
595 | b = _cairo_twin_outlines + |
||
596 | _cairo_twin_charmap[unlikely (glyph >= ARRAY_LENGTH (_cairo_twin_charmap)) ? 0 : glyph]; |
||
597 | g = twin_glyph_draw(b); |
||
598 | w = twin_glyph_right(b); |
||
599 | gw = F(w); |
||
600 | |||
601 | marginl = props->marginl; |
||
602 | |||
603 | /* monospace */ |
||
604 | if (props->face_props->monospace) { |
||
605 | double monow = F(24); |
||
606 | double extra = props->penx + props->marginl + props->marginr; |
||
607 | cairo_scale (cr, (monow + extra) / (gw + extra), 1); |
||
608 | gw = monow; |
||
609 | |||
610 | /* resnap margin for new transform */ |
||
611 | { |
||
612 | double x, y, x_scale, x_scale_inv; |
||
613 | x = 1; y = 0; |
||
614 | compute_hinting_scale (cr, x, y, &x_scale, &x_scale_inv); |
||
615 | marginl = SNAPXI (marginl); |
||
616 | } |
||
617 | } |
||
618 | |||
619 | cairo_translate (cr, marginl, 0); |
||
620 | |||
621 | /* stretch */ |
||
622 | cairo_scale (cr, props->stretch, 1); |
||
623 | |||
624 | if (props->snap) |
||
625 | twin_compute_snap (cr, &info, b); |
||
626 | else |
||
627 | info.n_snap_x = info.n_snap_y = 0; |
||
628 | |||
629 | /* advance width */ |
||
630 | metrics->x_advance = gw * props->stretch + props->penx + props->marginl + props->marginr; |
||
631 | |||
632 | /* glyph shape */ |
||
633 | for (;;) { |
||
634 | switch (*g++) { |
||
635 | case 'M': |
||
636 | cairo_close_path (cr); |
||
637 | /* fall through */ |
||
638 | case 'm': |
||
639 | x1 = SNAPX(*g++); |
||
640 | y1 = SNAPY(*g++); |
||
641 | cairo_move_to (cr, x1, y1); |
||
642 | continue; |
||
643 | case 'L': |
||
644 | cairo_close_path (cr); |
||
645 | /* fall through */ |
||
646 | case 'l': |
||
647 | x1 = SNAPX(*g++); |
||
648 | y1 = SNAPY(*g++); |
||
649 | cairo_line_to (cr, x1, y1); |
||
650 | continue; |
||
651 | case 'C': |
||
652 | cairo_close_path (cr); |
||
653 | /* fall through */ |
||
654 | case 'c': |
||
655 | x1 = SNAPX(*g++); |
||
656 | y1 = SNAPY(*g++); |
||
657 | x2 = SNAPX(*g++); |
||
658 | y2 = SNAPY(*g++); |
||
659 | x3 = SNAPX(*g++); |
||
660 | y3 = SNAPY(*g++); |
||
661 | cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); |
||
662 | continue; |
||
663 | case 'E': |
||
664 | cairo_close_path (cr); |
||
665 | /* fall through */ |
||
666 | case 'e': |
||
667 | cairo_restore (cr); /* restore glyph space */ |
||
668 | cairo_set_tolerance (cr, 0.01); |
||
669 | cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); |
||
670 | cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); |
||
671 | cairo_set_line_width (cr, 1); |
||
672 | cairo_scale (cr, props->penx, props->peny); |
||
673 | cairo_stroke (cr); |
||
674 | break; |
||
675 | case 'X': |
||
676 | /* filler */ |
||
677 | continue; |
||
678 | } |
||
679 | break; |
||
680 | } |
||
681 | |||
682 | return CAIRO_STATUS_SUCCESS; |
||
683 | } |
||
684 | |||
685 | static cairo_status_t |
||
686 | twin_scaled_font_unicode_to_glyph (cairo_scaled_font_t *scaled_font, |
||
687 | unsigned long unicode, |
||
688 | unsigned long *glyph) |
||
689 | { |
||
690 | /* We use an identity charmap. Which means we could live |
||
691 | * with no unicode_to_glyph method too. But we define this |
||
692 | * to map all unknown chars to a single unknown glyph to |
||
693 | * reduce pressure on cache. */ |
||
694 | |||
695 | if (likely (unicode < ARRAY_LENGTH (_cairo_twin_charmap))) |
||
696 | *glyph = unicode; |
||
697 | else |
||
698 | *glyph = 0; |
||
699 | |||
700 | return CAIRO_STATUS_SUCCESS; |
||
701 | } |
||
702 | |||
703 | |||
704 | /* |
||
705 | * Face constructor |
||
706 | */ |
||
707 | |||
708 | static cairo_font_face_t * |
||
709 | _cairo_font_face_twin_create_internal (void) |
||
710 | { |
||
711 | cairo_font_face_t *twin_font_face; |
||
712 | |||
713 | twin_font_face = cairo_user_font_face_create (); |
||
714 | cairo_user_font_face_set_init_func (twin_font_face, twin_scaled_font_init); |
||
715 | cairo_user_font_face_set_render_glyph_func (twin_font_face, twin_scaled_font_render_glyph); |
||
716 | cairo_user_font_face_set_unicode_to_glyph_func (twin_font_face, twin_scaled_font_unicode_to_glyph); |
||
717 | |||
718 | return twin_font_face; |
||
719 | } |
||
720 | |||
721 | cairo_font_face_t * |
||
722 | _cairo_font_face_twin_create_fallback (void) |
||
723 | { |
||
724 | cairo_font_face_t *twin_font_face; |
||
725 | |||
726 | twin_font_face = _cairo_font_face_twin_create_internal (); |
||
3959 | Serge | 727 | if (! twin_font_face_create_properties (twin_font_face)) { |
1892 | serge | 728 | cairo_font_face_destroy (twin_font_face); |
729 | return (cairo_font_face_t *) &_cairo_font_face_nil; |
||
730 | } |
||
731 | |||
732 | return twin_font_face; |
||
733 | } |
||
734 | |||
735 | cairo_status_t |
||
736 | _cairo_font_face_twin_create_for_toy (cairo_toy_font_face_t *toy_face, |
||
737 | cairo_font_face_t **font_face) |
||
738 | { |
||
739 | cairo_status_t status; |
||
740 | cairo_font_face_t *twin_font_face; |
||
741 | |||
742 | twin_font_face = _cairo_font_face_twin_create_internal (); |
||
743 | status = twin_font_face_set_properties_from_toy (twin_font_face, toy_face); |
||
744 | if (status) { |
||
745 | cairo_font_face_destroy (twin_font_face); |
||
746 | return status; |
||
747 | } |
||
748 | |||
749 | *font_face = twin_font_face; |
||
750 | |||
751 | return CAIRO_STATUS_SUCCESS; |
||
752 | }>=>=>=>>>=>>=>>>>>>>=> |