comparison libass/ass_font.c @ 31853:e64df5862cea

Import libass 0.9.10
author greg
date Fri, 06 Aug 2010 21:13:41 +0000
parents 48d020c5ceca
children ac6e48baa03d
comparison
equal deleted inserted replaced
31852:4ab8b78352ff 31853:e64df5862cea
34 #include "ass_bitmap.h" 34 #include "ass_bitmap.h"
35 #include "ass_cache.h" 35 #include "ass_cache.h"
36 #include "ass_fontconfig.h" 36 #include "ass_fontconfig.h"
37 #include "ass_utils.h" 37 #include "ass_utils.h"
38 38
39 /** 39 #define VERTICAL_LOWER_BOUND 0x02f1
40 * Select Microfost Unicode CharMap, if the font has one. 40
41 /**
42 * Select a good charmap, prefer Microsoft Unicode charmaps.
41 * Otherwise, let FreeType decide. 43 * Otherwise, let FreeType decide.
42 */ 44 */
43 static void charmap_magic(ASS_Library *library, FT_Face face) 45 static void charmap_magic(ASS_Library *library, FT_Face face)
44 { 46 {
45 int i; 47 int i;
48 int ms_cmap = -1;
49
50 // Search for a Microsoft Unicode cmap
46 for (i = 0; i < face->num_charmaps; ++i) { 51 for (i = 0; i < face->num_charmaps; ++i) {
47 FT_CharMap cmap = face->charmaps[i]; 52 FT_CharMap cmap = face->charmaps[i];
48 unsigned pid = cmap->platform_id; 53 unsigned pid = cmap->platform_id;
49 unsigned eid = cmap->encoding_id; 54 unsigned eid = cmap->encoding_id;
50 if (pid == 3 /*microsoft */ 55 if (pid == 3 /*microsoft */
51 && (eid == 1 /*unicode bmp */ 56 && (eid == 1 /*unicode bmp */
52 || eid == 10 /*full unicode */ )) { 57 || eid == 10 /*full unicode */ )) {
53 FT_Set_Charmap(face, cmap); 58 FT_Set_Charmap(face, cmap);
54 return; 59 return;
55 } 60 } else if (pid == 3 && ms_cmap < 0)
61 ms_cmap = i;
62 }
63
64 // Try the first Microsoft cmap if no Microsoft Unicode cmap was found
65 if (ms_cmap >= 0) {
66 FT_CharMap cmap = face->charmaps[ms_cmap];
67 FT_Set_Charmap(face, cmap);
68 return;
56 } 69 }
57 70
58 if (!face->charmap) { 71 if (!face->charmap) {
59 if (face->num_charmaps == 0) { 72 if (face->num_charmaps == 0) {
60 ass_msg(library, MSGL_WARN, "Font face with no charmaps"); 73 ass_msg(library, MSGL_WARN, "Font face with no charmaps");
63 ass_msg(library, MSGL_WARN, 76 ass_msg(library, MSGL_WARN,
64 "No charmap autodetected, trying the first one"); 77 "No charmap autodetected, trying the first one");
65 FT_Set_Charmap(face, face->charmaps[0]); 78 FT_Set_Charmap(face, face->charmaps[0]);
66 return; 79 return;
67 } 80 }
68 }
69
70 static void update_transform(ASS_Font *font)
71 {
72 int i;
73 FT_Matrix m;
74 m.xx = double_to_d16(font->scale_x);
75 m.yy = double_to_d16(font->scale_y);
76 m.xy = m.yx = 0;
77 for (i = 0; i < font->n_faces; ++i)
78 FT_Set_Transform(font->faces[i], &m, &font->v);
79 } 81 }
80 82
81 /** 83 /**
82 * \brief find a memory font by name 84 * \brief find a memory font by name
83 */ 85 */
137 if (mem_idx >= 0) { 139 if (mem_idx >= 0) {
138 error = 140 error =
139 FT_New_Memory_Face(font->ftlibrary, 141 FT_New_Memory_Face(font->ftlibrary,
140 (unsigned char *) font->library-> 142 (unsigned char *) font->library->
141 fontdata[mem_idx].data, 143 fontdata[mem_idx].data,
142 font->library->fontdata[mem_idx].size, 0, 144 font->library->fontdata[mem_idx].size, index,
143 &face); 145 &face);
144 if (error) { 146 if (error) {
145 ass_msg(font->library, MSGL_WARN, 147 ass_msg(font->library, MSGL_WARN,
146 "Error opening memory font: '%s'", path); 148 "Error opening memory font: '%s'", path);
147 free(path); 149 free(path);
158 } 160 }
159 charmap_magic(font->library, face); 161 charmap_magic(font->library, face);
160 buggy_font_workaround(face); 162 buggy_font_workaround(face);
161 163
162 font->faces[font->n_faces++] = face; 164 font->faces[font->n_faces++] = face;
163 update_transform(font);
164 face_set_size(face, font->size); 165 face_set_size(face, font->size);
165 free(path); 166 free(path);
166 return font->n_faces - 1; 167 return font->n_faces - 1;
167 } 168 }
168 169
186 font.n_faces = 0; 187 font.n_faces = 0;
187 font.desc.family = strdup(desc->family); 188 font.desc.family = strdup(desc->family);
188 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; 189 font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
189 font.desc.bold = desc->bold; 190 font.desc.bold = desc->bold;
190 font.desc.italic = desc->italic; 191 font.desc.italic = desc->italic;
192 font.desc.vertical = desc->vertical;
191 193
192 font.scale_x = font.scale_y = 1.; 194 font.scale_x = font.scale_y = 1.;
193 font.v.x = font.v.y = 0; 195 font.v.x = font.v.y = 0;
194 font.size = 0.; 196 font.size = 0.;
195 197
211 font->scale_y = scale_y; 213 font->scale_y = scale_y;
212 if (v) { 214 if (v) {
213 font->v.x = v->x; 215 font->v.x = v->x;
214 font->v.y = v->y; 216 font->v.y = v->y;
215 } 217 }
216 update_transform(font);
217 } 218 }
218 219
219 static void face_set_size(FT_Face face, double size) 220 static void face_set_size(FT_Face face, double size)
220 { 221 {
221 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); 222 TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
274 *desc = FT_MulFix(os2->usWinDescent, y_scale); 275 *desc = FT_MulFix(os2->usWinDescent, y_scale);
275 } else { 276 } else {
276 *asc = FT_MulFix(face->ascender, y_scale); 277 *asc = FT_MulFix(face->ascender, y_scale);
277 *desc = FT_MulFix(-face->descender, y_scale); 278 *desc = FT_MulFix(-face->descender, y_scale);
278 } 279 }
280 if (font->desc.vertical && ch >= VERTICAL_LOWER_BOUND) {
281 *asc = FT_MulFix(face->max_advance_width, y_scale);
282 }
279 return; 283 return;
280 } 284 }
281 } 285 }
282 286
283 *asc = *desc = 0; 287 *asc = *desc = 0;
412 int index = 0; 416 int index = 0;
413 int i; 417 int i;
414 FT_Glyph glyph; 418 FT_Glyph glyph;
415 FT_Face face = 0; 419 FT_Face face = 0;
416 int flags = 0; 420 int flags = 0;
421 int vertical = font->desc.vertical;
417 422
418 if (ch < 0x20) 423 if (ch < 0x20)
419 return 0; 424 return 0;
420 // Handle NBSP like a regular space when rendering the glyph 425 // Handle NBSP like a regular space when rendering the glyph
421 if (ch == 0xa0) 426 if (ch == 0xa0)
439 font->desc.bold, font->desc.italic); 444 font->desc.bold, font->desc.italic);
440 face_idx = add_face(fontconfig_priv, font, ch); 445 face_idx = add_face(fontconfig_priv, font, ch);
441 if (face_idx >= 0) { 446 if (face_idx >= 0) {
442 face = font->faces[face_idx]; 447 face = font->faces[face_idx];
443 index = FT_Get_Char_Index(face, ch); 448 index = FT_Get_Char_Index(face, ch);
449 if (index == 0 && face->num_charmaps > 0) {
450 ass_msg(font->library, MSGL_WARN,
451 "Glyph 0x%X not found, falling back to first charmap", ch);
452 FT_CharMap cur = face->charmap;
453 FT_Set_Charmap(face, face->charmaps[0]);
454 index = FT_Get_Char_Index(face, ch);
455 FT_Set_Charmap(face, cur);
456 }
444 if (index == 0) { 457 if (index == 0) {
445 ass_msg(font->library, MSGL_ERR, 458 ass_msg(font->library, MSGL_ERR,
446 "Glyph 0x%X not found in font for (%s, %d, %d)", 459 "Glyph 0x%X not found in font for (%s, %d, %d)",
447 ch, font->desc.family, font->desc.bold, 460 ch, font->desc.family, font->desc.bold,
448 font->desc.italic); 461 font->desc.italic);
449 } 462 }
450 } 463 }
451 } 464 }
452 #endif 465 #endif
453 466
467 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
468 | FT_LOAD_IGNORE_TRANSFORM;
454 switch (hinting) { 469 switch (hinting) {
455 case ASS_HINTING_NONE: 470 case ASS_HINTING_NONE:
456 flags = FT_LOAD_NO_HINTING; 471 flags |= FT_LOAD_NO_HINTING;
457 break; 472 break;
458 case ASS_HINTING_LIGHT: 473 case ASS_HINTING_LIGHT:
459 flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; 474 flags |= FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT;
460 break; 475 break;
461 case ASS_HINTING_NORMAL: 476 case ASS_HINTING_NORMAL:
462 flags = FT_LOAD_FORCE_AUTOHINT; 477 flags |= FT_LOAD_FORCE_AUTOHINT;
463 break; 478 break;
464 case ASS_HINTING_NATIVE: 479 case ASS_HINTING_NATIVE:
465 flags = 0;
466 break; 480 break;
467 } 481 }
468 482
469 error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags); 483 error = FT_Load_Glyph(face, index, flags);
470 if (error) { 484 if (error) {
471 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", 485 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
472 index); 486 index);
473 return 0; 487 return 0;
474 } 488 }
486 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", 500 ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d",
487 index); 501 index);
488 return 0; 502 return 0;
489 } 503 }
490 504
505 // Rotate glyph, if needed
506 if (vertical && ch >= VERTICAL_LOWER_BOUND) {
507 FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 };
508 FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m);
509 FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline,
510 face->glyph->metrics.vertAdvance,
511 0);
512 glyph->advance.x = face->glyph->linearVertAdvance;
513 }
514
515 // Apply scaling and shift
516 FT_Matrix scale = { double_to_d16(font->scale_x), 0, 0,
517 double_to_d16(font->scale_y) };
518 FT_Outline *outl = &((FT_OutlineGlyph) glyph)->outline;
519 FT_Outline_Transform(outl, &scale);
520 FT_Outline_Translate(outl, font->v.x, font->v.y);
521 glyph->advance.x *= font->scale_x;
522
491 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, 523 ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE,
492 deco & DECO_STRIKETHROUGH); 524 deco & DECO_STRIKETHROUGH);
493 525
494 return glyph; 526 return glyph;
495 } 527 }
499 **/ 531 **/
500 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2) 532 FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2)
501 { 533 {
502 FT_Vector v = { 0, 0 }; 534 FT_Vector v = { 0, 0 };
503 int i; 535 int i;
536
537 if (font->desc.vertical)
538 return v;
504 539
505 for (i = 0; i < font->n_faces; ++i) { 540 for (i = 0; i < font->n_faces; ++i) {
506 FT_Face face = font->faces[i]; 541 FT_Face face = font->faces[i];
507 int i1 = FT_Get_Char_Index(face, c1); 542 int i1 = FT_Get_Char_Index(face, c1);
508 int i2 = FT_Get_Char_Index(face, c2); 543 int i2 = FT_Get_Char_Index(face, c2);
528 FT_Done_Face(font->faces[i]); 563 FT_Done_Face(font->faces[i]);
529 if (font->desc.family) 564 if (font->desc.family)
530 free(font->desc.family); 565 free(font->desc.family);
531 free(font); 566 free(font);
532 } 567 }
568
569 /**
570 * \brief Calculate the cbox of a series of points
571 */
572 static void
573 get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end)
574 {
575 box->xMin = box->yMin = INT_MAX;
576 box->xMax = box->yMax = INT_MIN;
577 int i;
578
579 for (i = start; i <= end; i++) {
580 box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin;
581 box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax;
582 box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin;
583 box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax;
584 }
585 }
586
587 /**
588 * \brief Determine winding direction of a contour
589 * \return direction; 0 = clockwise
590 */
591 static int get_contour_direction(FT_Vector *points, int start, int end)
592 {
593 int i;
594 long long sum = 0;
595 int x = points[start].x;
596 int y = points[start].y;
597 for (i = start + 1; i <= end; i++) {
598 sum += x * (points[i].y - y) - y * (points[i].x - x);
599 x = points[i].x;
600 y = points[i].y;
601 }
602 sum += x * (points[start].y - y) - y * (points[start].x - x);
603 return sum > 0;
604 }
605
606 /**
607 * \brief Fix-up stroker result for huge borders by removing inside contours
608 * that would reverse in size
609 */
610 void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, int border_y)
611 {
612 int nc = glyph->outline.n_contours;
613 int begin, stop;
614 char modified = 0;
615 char *valid_cont = malloc(nc);
616 int start = 0;
617 int end = -1;
618 FT_BBox *boxes = malloc(nc * sizeof(FT_BBox));
619 int i, j;
620 int inside_direction;
621
622 inside_direction = FT_Outline_Get_Orientation(&glyph->outline) ==
623 FT_ORIENTATION_TRUETYPE;
624
625 // create a list of cboxes of the contours
626 for (i = 0; i < nc; i++) {
627 start = end + 1;
628 end = glyph->outline.contours[i];
629 get_contour_cbox(&boxes[i], glyph->outline.points, start, end);
630 }
631
632 // for each contour, check direction and whether it's "outside"
633 // or contained in another contour
634 end = -1;
635 for (i = 0; i < nc; i++) {
636 start = end + 1;
637 end = glyph->outline.contours[i];
638 int dir = get_contour_direction(glyph->outline.points, start, end);
639 valid_cont[i] = 1;
640 if (dir == inside_direction) {
641 for (j = 0; j < nc; j++) {
642 if (i == j)
643 continue;
644 if (boxes[i].xMin >= boxes[j].xMin &&
645 boxes[i].xMax <= boxes[j].xMax &&
646 boxes[i].yMin >= boxes[j].yMin &&
647 boxes[i].yMax <= boxes[j].yMax)
648 goto check_inside;
649 }
650 /* "inside" contour but we can't find anything it could be
651 * inside of - assume the font is buggy and it should be
652 * an "outside" contour, and reverse it */
653 for (j = 0; j < (end + 1 - start) / 2; j++) {
654 FT_Vector temp = glyph->outline.points[start + j];
655 char temp2 = glyph->outline.tags[start + j];
656 glyph->outline.points[start + j] = glyph->outline.points[end - j];
657 glyph->outline.points[end - j] = temp;
658 glyph->outline.tags[start + j] = glyph->outline.tags[end - j];
659 glyph->outline.tags[end - j] = temp2;
660 }
661 dir ^= 1;
662 }
663 check_inside:
664 if (dir == inside_direction) {
665 FT_BBox box;
666 get_contour_cbox(&box, glyph->outline.points, start, end);
667 int width = box.xMax - box.xMin;
668 int height = box.yMax - box.yMin;
669 if (width < border_x * 2 || height < border_y * 2) {
670 valid_cont[i] = 0;
671 modified = 1;
672 }
673 }
674 }
675
676 // zero-out contours that can be removed; much simpler than copying
677 if (modified) {
678 for (i = 0; i < nc; i++) {
679 if (valid_cont[i])
680 continue;
681 begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1;
682 stop = glyph->outline.contours[i];
683 for (j = begin; j <= stop; j++) {
684 glyph->outline.points[j].x = 0;
685 glyph->outline.points[j].y = 0;
686 glyph->outline.tags[j] = 0;
687 }
688 }
689 }
690
691 free(boxes);
692 free(valid_cont);
693 }
694