comparison libass/ass_font.c @ 34295:6e7f60f6f9d4

Update libass to 0.10 release. Patch by [subjunk gmail com], see bug #2008.
author reimar
date Sat, 03 Dec 2011 21:35:56 +0000
parents 88eebbbbd6a0
children c3aaaf17c721
comparison
equal deleted inserted replaced
34294:d30ea496ed2a 34295:6e7f60f6f9d4
28 #include <strings.h> 28 #include <strings.h>
29 29
30 #include "ass.h" 30 #include "ass.h"
31 #include "ass_library.h" 31 #include "ass_library.h"
32 #include "ass_font.h" 32 #include "ass_font.h"
33 #include "ass_bitmap.h"
34 #include "ass_cache.h"
35 #include "ass_fontconfig.h" 33 #include "ass_fontconfig.h"
36 #include "ass_utils.h" 34 #include "ass_utils.h"
37 35 #include "ass_shaper.h"
38 #define VERTICAL_LOWER_BOUND 0x02f1
39 36
40 /** 37 /**
41 * Select a good charmap, prefer Microsoft Unicode charmaps. 38 * Select a good charmap, prefer Microsoft Unicode charmaps.
42 * Otherwise, let FreeType decide. 39 * Otherwise, let FreeType decide.
43 */ 40 */
88 for (i = 0; i < library->num_fontdata; ++i) 85 for (i = 0; i < library->num_fontdata; ++i)
89 if (strcasecmp(name, library->fontdata[i].name) == 0) 86 if (strcasecmp(name, library->fontdata[i].name) == 0)
90 return i; 87 return i;
91 return -1; 88 return -1;
92 } 89 }
93
94 static void face_set_size(FT_Face face, double size);
95 90
96 static void buggy_font_workaround(FT_Face face) 91 static void buggy_font_workaround(FT_Face face)
97 { 92 {
98 // Some fonts have zero Ascender/Descender fields in 'hhea' table. 93 // Some fonts have zero Ascender/Descender fields in 'hhea' table.
99 // In this case, get the information from 'os2' table or, as 94 // In this case, get the information from 'os2' table or, as
159 } 154 }
160 charmap_magic(font->library, face); 155 charmap_magic(font->library, face);
161 buggy_font_workaround(face); 156 buggy_font_workaround(face);
162 157
163 font->faces[font->n_faces++] = face; 158 font->faces[font->n_faces++] = face;
164 face_set_size(face, font->size); 159 ass_face_set_size(face, font->size);
165 free(path); 160 free(path);
166 return font->n_faces - 1; 161 return font->n_faces - 1;
167 } 162 }
168 163
169 /** 164 /**
170 * \brief Create a new ASS_Font according to "desc" argument 165 * \brief Create a new ASS_Font according to "desc" argument
171 */ 166 */
172 ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, 167 ASS_Font *ass_font_new(Cache *font_cache, ASS_Library *library,
173 FT_Library ftlibrary, void *fc_priv, 168 FT_Library ftlibrary, void *fc_priv,
174 ASS_FontDesc *desc) 169 ASS_FontDesc *desc)
175 { 170 {
176 int error; 171 int error;
177 ASS_Font *fontp; 172 ASS_Font *fontp;
178 ASS_Font font; 173 ASS_Font font;
179 174
180 fontp = ass_font_cache_find((Hashmap *) font_cache, desc); 175 fontp = ass_cache_get(font_cache, desc);
181 if (fontp) 176 if (fontp)
182 return fontp; 177 return fontp;
183 178
184 font.library = library; 179 font.library = library;
185 font.ftlibrary = ftlibrary; 180 font.ftlibrary = ftlibrary;
181 font.shaper_priv = NULL;
186 font.n_faces = 0; 182 font.n_faces = 0;
187 font.desc.family = strdup(desc->family); 183 font.desc.family = strdup(desc->family);
188 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; 184 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
189 font.desc.bold = desc->bold; 185 font.desc.bold = desc->bold;
190 font.desc.italic = desc->italic; 186 font.desc.italic = desc->italic;
197 error = add_face(fc_priv, &font, 0); 193 error = add_face(fc_priv, &font, 0);
198 if (error == -1) { 194 if (error == -1) {
199 free(font.desc.family); 195 free(font.desc.family);
200 return 0; 196 return 0;
201 } else 197 } else
202 return ass_font_cache_add((Hashmap *) font_cache, &font); 198 return ass_cache_put(font_cache, &font.desc, &font);
203 } 199 }
204 200
205 /** 201 /**
206 * \brief Set font transformation matrix and shift vector 202 * \brief Set font transformation matrix and shift vector
207 **/ 203 **/
214 font->v.x = v->x; 210 font->v.x = v->x;
215 font->v.y = v->y; 211 font->v.y = v->y;
216 } 212 }
217 } 213 }
218 214
219 static void face_set_size(FT_Face face, double size) 215 void ass_face_set_size(FT_Face face, double size)
220 { 216 {
221 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); 217 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
222 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); 218 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
223 double mscale = 1.; 219 double mscale = 1.;
224 FT_Size_RequestRec rq; 220 FT_Size_RequestRec rq;
249 { 245 {
250 int i; 246 int i;
251 if (font->size != size) { 247 if (font->size != size) {
252 font->size = size; 248 font->size = size;
253 for (i = 0; i < font->n_faces; ++i) 249 for (i = 0; i < font->n_faces; ++i)
254 face_set_size(font->faces[i], size); 250 ass_face_set_size(font->faces[i], size);
255 } 251 }
256 } 252 }
257 253
258 /** 254 /**
259 * \brief Get maximal font ascender and descender. 255 * \brief Get maximal font ascender and descender.
274 *desc = FT_MulFix(os2->usWinDescent, y_scale); 270 *desc = FT_MulFix(os2->usWinDescent, y_scale);
275 } else { 271 } else {
276 *asc = FT_MulFix(face->ascender, y_scale); 272 *asc = FT_MulFix(face->ascender, y_scale);
277 *desc = FT_MulFix(-face->descender, y_scale); 273 *desc = FT_MulFix(-face->descender, y_scale);
278 } 274 }
279 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {
280 *asc = FT_MulFix(face->max_advance_width, y_scale);
281 }
282 return; 275 return;
283 } 276 }
284 } 277 }
285 278
286 *asc = *desc = 0; 279 *asc = *desc = 0;
386 } 379 }
387 380
388 return 0; 381 return 0;
389 } 382 }
390 383
384 void outline_copy(FT_Library lib, FT_Outline *source, FT_Outline **dest)
385 {
386 if (source == NULL) {
387 *dest = NULL;
388 return;
389 }
390 *dest = calloc(1, sizeof(**dest));
391
392 FT_Outline_New(lib, source->n_points, source->n_contours, *dest);
393 FT_Outline_Copy(source, *dest);
394 }
395
396 void outline_free(FT_Library lib, FT_Outline *outline)
397 {
398 if (outline)
399 FT_Outline_Done(lib, outline);
400 free(outline);
401 }
402
391 /** 403 /**
392 * Slightly embold a glyph without touching its metrics 404 * Slightly embold a glyph without touching its metrics
393 */ 405 */
394 static void ass_glyph_embolden(FT_GlyphSlot slot) 406 static void ass_glyph_embolden(FT_GlyphSlot slot)
395 { 407 {
403 415
404 FT_Outline_Embolden(&slot->outline, str); 416 FT_Outline_Embolden(&slot->outline, str);
405 } 417 }
406 418
407 /** 419 /**
408 * \brief Get a glyph 420 * \brief Get glyph and face index
409 * \param ch character code 421 * Finds a face that has the requested codepoint and returns both face
410 **/ 422 * and glyph index.
411 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, 423 */
412 uint32_t ch, ASS_Hinting hinting, int deco) 424 int ass_font_get_index(void *fcpriv, ASS_Font *font, uint32_t symbol,
413 { 425 int *face_index, int *glyph_index)
414 int error; 426 {
415 int index = 0; 427 int index = 0;
416 int i; 428 int i;
417 FT_Glyph glyph;
418 FT_Face face = 0; 429 FT_Face face = 0;
419 int flags = 0; 430
420 int vertical = font->desc.vertical; 431 *glyph_index = 0;
421 432
422 if (ch < 0x20) 433 if (symbol < 0x20) {
434 *face_index = 0;
423 return 0; 435 return 0;
436 }
424 // Handle NBSP like a regular space when rendering the glyph 437 // Handle NBSP like a regular space when rendering the glyph
425 if (ch == 0xa0) 438 if (symbol == 0xa0)
426 ch = ' '; 439 symbol = ' ';
427 if (font->n_faces == 0) 440 if (font->n_faces == 0) {
441 *face_index = 0;
428 return 0; 442 return 0;
429 443 }
430 for (i = 0; i < font->n_faces; ++i) { 444
445 // try with the requested face
446 if (*face_index < font->n_faces) {
447 face = font->faces[*face_index];
448 index = FT_Get_Char_Index(face, symbol);
449 }
450
451 // not found in requested face, try all others
452 for (i = 0; i < font->n_faces && index == 0; ++i) {
431 face = font->faces[i]; 453 face = font->faces[i];
432 index = FT_Get_Char_Index(face, ch); 454 index = FT_Get_Char_Index(face, symbol);
433 if (index) 455 if (index)
434 break; 456 *face_index = i;
435 } 457 }
436 458
437 #ifdef CONFIG_FONTCONFIG 459 #ifdef CONFIG_FONTCONFIG
438 if (index == 0) { 460 if (index == 0) {
439 int face_idx; 461 int face_idx;
440 ass_msg(font->library, MSGL_INFO, 462 ass_msg(font->library, MSGL_INFO,
441 "Glyph 0x%X not found, selecting one more " 463 "Glyph 0x%X not found, selecting one more "
442 "font for (%s, %d, %d)", ch, font->desc.family, 464 "font for (%s, %d, %d)", symbol, font->desc.family,
443 font->desc.bold, font->desc.italic); 465 font->desc.bold, font->desc.italic);
444 face_idx = add_face(fontconfig_priv, font, ch); 466 face_idx = *face_index = add_face(fcpriv, font, symbol);
445 if (face_idx >= 0) { 467 if (face_idx >= 0) {
446 face = font->faces[face_idx]; 468 face = font->faces[face_idx];
447 index = FT_Get_Char_Index(face, ch); 469 index = FT_Get_Char_Index(face, symbol);
448 if (index == 0 && face->num_charmaps > 0) { 470 if (index == 0 && face->num_charmaps > 0) {
449 int i; 471 int i;
450 ass_msg(font->library, MSGL_WARN, 472 ass_msg(font->library, MSGL_WARN,
451 "Glyph 0x%X not found, broken font? Trying all charmaps", ch); 473 "Glyph 0x%X not found, broken font? Trying all charmaps", symbol);
452 for (i = 0; i < face->num_charmaps; i++) { 474 for (i = 0; i < face->num_charmaps; i++) {
453 FT_Set_Charmap(face, face->charmaps[i]); 475 FT_Set_Charmap(face, face->charmaps[i]);
454 if ((index = FT_Get_Char_Index(face, ch)) != 0) break; 476 if ((index = FT_Get_Char_Index(face, symbol)) != 0) break;
455 } 477 }
456 } 478 }
457 if (index == 0) { 479 if (index == 0) {
458 ass_msg(font->library, MSGL_ERR, 480 ass_msg(font->library, MSGL_ERR,
459 "Glyph 0x%X not found in font for (%s, %d, %d)", 481 "Glyph 0x%X not found in font for (%s, %d, %d)",
460 ch, font->desc.family, font->desc.bold, 482 symbol, font->desc.family, font->desc.bold,
461 font->desc.italic); 483 font->desc.italic);
462 } 484 }
463 } 485 }
464 } 486 }
465 #endif 487 #endif
488 // FIXME: make sure we have a valid face_index. this is a HACK.
489 *face_index = FFMAX(*face_index, 0);
490 *glyph_index = index;
491
492 return 1;
493 }
494
495 /**
496 * \brief Get a glyph
497 * \param ch character code
498 **/
499 FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font,
500 uint32_t ch, int face_index, int index,
501 ASS_Hinting hinting, int deco)
502 {
503 int error;
504 FT_Glyph glyph;
505 FT_Face face = font->faces[face_index];
506 int flags = 0;
507 int vertical = font->desc.vertical;
466 508
467 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH 509 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
468 | FT_LOAD_IGNORE_TRANSFORM; 510 | FT_LOAD_IGNORE_TRANSFORM;
469 switch (hinting) { 511 switch (hinting) {
470 case ASS_HINTING_NONE: 512 case ASS_HINTING_NONE:
503 } 545 }
504 546
505 // Rotate glyph, if needed 547 // Rotate glyph, if needed
506 if (vertical && ch >= VERTICAL_LOWER_BOUND) { 548 if (vertical && ch >= VERTICAL_LOWER_BOUND) {
507 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; 549 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
550 TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
551 int desc = 0;
552
553 if (os2)
554 desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale);
555
556 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc);
508 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); 557 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
509 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 558 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
510 face->glyph->metrics.vertAdvance, 559 face->glyph->metrics.vertAdvance, desc);
511 0);
512 glyph->advance.x = face->glyph->linearVertAdvance; 560 glyph->advance.x = face->glyph->linearVertAdvance;
513 } 561 }
514 562
515 // Apply scaling and shift 563 // Apply scaling and shift
516 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0, 564 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
559 { 607 {
560 int i; 608 int i;
561 for (i = 0; i < font->n_faces; ++i) 609 for (i = 0; i < font->n_faces; ++i)
562 if (font->faces[i]) 610 if (font->faces[i])
563 FT_Done_Face(font->faces[i]); 611 FT_Done_Face(font->faces[i]);
612 if (font->shaper_priv)
613 ass_shaper_font_data_free(font->shaper_priv);
564 free(font->desc.family); 614 free(font->desc.family);
565 free(font); 615 free(font);
566 } 616 }
567 617
568 /** 618 /**
616 * 666 *
617 * \param outline FreeType outline, modified in-place 667 * \param outline FreeType outline, modified in-place
618 * \param border_x border size, x direction, d6 format 668 * \param border_x border size, x direction, d6 format
619 * \param border_x border size, y direction, d6 format 669 * \param border_x border size, y direction, d6 format
620 */ 670 */
621 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y) 671 void fix_freetype_stroker(FT_Outline *outline, int border_x, int border_y)
622 { 672 {
623 int nc = glyph->outline.n_contours; 673 int nc = outline->n_contours;
624 int begin, stop; 674 int begin, stop;
625 char modified = 0; 675 char modified = 0;
626 char *valid_cont = malloc(nc); 676 char *valid_cont = malloc(nc);
627 int start = 0; 677 int start = 0;
628 int end = -1; 678 int end = -1;
629 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox)); 679 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
630 int i, j; 680 int i, j;
631 int inside_direction; 681 int inside_direction;
632 682
633 inside_direction = FT_Outline_Get_Orientation(&glyph->outline) == 683 inside_direction = FT_Outline_Get_Orientation(outline) ==
634 FT_ORIENTATION_TRUETYPE; 684 FT_ORIENTATION_TRUETYPE;
635 685
636 // create a list of cboxes of the contours 686 // create a list of cboxes of the contours
637 for (i = 0; i < nc; i++) { 687 for (i = 0; i < nc; i++) {
638 start = end + 1; 688 start = end + 1;
639 end = glyph->outline.contours[i]; 689 end = outline->contours[i];
640 get_contour_cbox(&boxes[i], glyph->outline.points, start, end); 690 get_contour_cbox(&boxes[i], outline->points, start, end);
641 } 691 }
642 692
643 // for each contour, check direction and whether it's "outside" 693 // for each contour, check direction and whether it's "outside"
644 // or contained in another contour 694 // or contained in another contour
645 end = -1; 695 end = -1;
646 for (i = 0; i < nc; i++) { 696 for (i = 0; i < nc; i++) {
647 start = end + 1; 697 start = end + 1;
648 end = glyph->outline.contours[i]; 698 end = outline->contours[i];
649 int dir = get_contour_direction(glyph->outline.points, start, end); 699 int dir = get_contour_direction(outline->points, start, end);
650 valid_cont[i] = 1; 700 valid_cont[i] = 1;
651 if (dir == inside_direction) { 701 if (dir == inside_direction) {
652 for (j = 0; j < nc; j++) { 702 for (j = 0; j < nc; j++) {
653 if (i == j) 703 if (i == j)
654 continue; 704 continue;
660 } 710 }
661 /* "inside" contour but we can't find anything it could be 711 /* "inside" contour but we can't find anything it could be
662 * inside of - assume the font is buggy and it should be 712 * inside of - assume the font is buggy and it should be
663 * an "outside" contour, and reverse it */ 713 * an "outside" contour, and reverse it */
664 for (j = 0; j < (end + 1 - start) / 2; j++) { 714 for (j = 0; j < (end + 1 - start) / 2; j++) {
665 FT_Vector temp = glyph->outline.points[start + j]; 715 FT_Vector temp = outline->points[start + j];
666 char temp2 = glyph->outline.tags[start + j]; 716 char temp2 = outline->tags[start + j];
667 glyph->outline.points[start + j] = glyph->outline.points[end - j]; 717 outline->points[start + j] = outline->points[end - j];
668 glyph->outline.points[end - j] = temp; 718 outline->points[end - j] = temp;
669 glyph->outline.tags[start + j] = glyph->outline.tags[end - j]; 719 outline->tags[start + j] = outline->tags[end - j];
670 glyph->outline.tags[end - j] = temp2; 720 outline->tags[end - j] = temp2;
671 } 721 }
672 dir ^= 1; 722 dir ^= 1;
673 } 723 }
674 check_inside: 724 check_inside:
675 if (dir == inside_direction) { 725 if (dir == inside_direction) {
676 FT_BBox box; 726 FT_BBox box;
677 get_contour_cbox(&box, glyph->outline.points, start, end); 727 get_contour_cbox(&box, outline->points, start, end);
678 int width = box.xMax - box.xMin; 728 int width = box.xMax - box.xMin;
679 int height = box.yMax - box.yMin; 729 int height = box.yMax - box.yMin;
680 if (width < border_x * 2 || height < border_y * 2) { 730 if (width < border_x * 2 || height < border_y * 2) {
681 valid_cont[i] = 0; 731 valid_cont[i] = 0;
682 modified = 1; 732 modified = 1;
685 } 735 }
686 736
687 // if we need to modify the outline, rewrite it and skip 737 // if we need to modify the outline, rewrite it and skip
688 // the contours that we determined should be removed. 738 // the contours that we determined should be removed.
689 if (modified) { 739 if (modified) {
690 FT_Outline *outline = &glyph->outline;
691 int p = 0, c = 0; 740 int p = 0, c = 0;
692 for (i = 0; i < nc; i++) { 741 for (i = 0; i < nc; i++) {
693 if (!valid_cont[i]) 742 if (!valid_cont[i])
694 continue; 743 continue;
695 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; 744 begin = (i == 0) ? 0 : outline->contours[i - 1] + 1;
696 stop = glyph->outline.contours[i]; 745 stop = outline->contours[i];
697 for (j = begin; j <= stop; j++) { 746 for (j = begin; j <= stop; j++) {
698 outline->points[p].x = outline->points[j].x; 747 outline->points[p].x = outline->points[j].x;
699 outline->points[p].y = outline->points[j].y; 748 outline->points[p].y = outline->points[j].y;
700 outline->tags[p] = outline->tags[j]; 749 outline->tags[p] = outline->tags[j];
701 p++; 750 p++;