diff options
Diffstat (limited to 'libass/ass_font.c')
-rw-r--r-- | libass/ass_font.c | 724 |
1 files changed, 484 insertions, 240 deletions
diff --git a/libass/ass_font.c b/libass/ass_font.c index 7d79d2e..222199d 100644 --- a/libass/ass_font.c +++ b/libass/ass_font.c @@ -26,6 +26,8 @@ #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H #include FT_OUTLINE_H +#include FT_TRUETYPE_IDS_H +#include FT_TYPE1_TABLES_H #include <limits.h> #include "ass.h" @@ -35,30 +37,198 @@ #include "ass_utils.h" #include "ass_shaper.h" +#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 6 +// The lowercase name is still included (as a macro) but deprecated as of 2.6, so avoid using it if we can +#define FT_SFNT_OS2 ft_sfnt_os2 +#endif + +/** + * Get the mbcs codepoint from the output bytes of iconv/WideCharToMultiByte, + * by treating the bytes as a prefix-zero-byte-omitted big-endian integer. + */ +static inline uint32_t pack_mbcs_bytes(const char *bytes, size_t length) +{ + uint32_t ret = 0; + for (size_t i = 0; i < length; ++i) { + ret <<= 8; + ret |= (uint8_t) bytes[i]; + } + return ret; +} + +/** + * Convert a UCS-4 code unit to a packed uint32_t in given multibyte encoding. + * + * We don't exclude Cygwin for Windows since we use WideCharToMultiByte only, + * this shall not violate any Cygwin restrictions on Windows APIs. + */ +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +static uint32_t convert_unicode_to_mb(FT_Encoding encoding, uint32_t codepoint) +{ + // map freetype encoding to windows codepage + UINT codepage; + switch (encoding) { + case FT_ENCODING_MS_SJIS: + codepage = 932; + break; + case FT_ENCODING_MS_GB2312: + codepage = 936; + break; + case FT_ENCODING_MS_BIG5: + codepage = 950; + break; + case FT_ENCODING_MS_WANSUNG: + codepage = 949; + break; + case FT_ENCODING_MS_JOHAB: + codepage = 1361; + break; + default: + return 0; + } + + WCHAR input_buffer[2]; + size_t inbuf_size; + + // encode surrogate pair, assuming codepoint > 0 && codepoint <= 10FFFF + if (codepoint >= 0x10000) { + // surrogate pair required + inbuf_size = 2; + input_buffer[0] = 0xD7C0 + (codepoint >> 10); + input_buffer[1] = 0xDC00 + (codepoint & 0x3FF); + } else { + inbuf_size = 1; + input_buffer[0] = codepoint; + } + + // do convert + char output_buffer[2]; + BOOL conversion_fail; + int output_length = WideCharToMultiByte(codepage, WC_NO_BEST_FIT_CHARS, input_buffer, inbuf_size, + output_buffer, sizeof(output_buffer), NULL, &conversion_fail); + if (output_length == 0 || conversion_fail) + return 0; + + return pack_mbcs_bytes(output_buffer, output_length); +} +#elif defined(CONFIG_ICONV) + +#include <iconv.h> + +static uint32_t convert_unicode_to_mb(FT_Encoding encoding, uint32_t codepoint) +{ + typedef struct { const char *names[5]; } EncodingList; + + EncodingList encoding_list; + + // map freetype encoding to iconv encoding + switch (encoding) { + case FT_ENCODING_MS_SJIS: + encoding_list = (EncodingList) {{"CP932", "SHIFT_JIS", NULL}}; + break; + case FT_ENCODING_MS_GB2312: + encoding_list = (EncodingList) {{"CP936", "GBK", "GB18030", "GB2312", NULL}}; + break; + case FT_ENCODING_MS_BIG5: + encoding_list = (EncodingList) {{"CP950", "BIG5", NULL}}; + break; + case FT_ENCODING_MS_WANSUNG: + encoding_list = (EncodingList) {{"CP949", "EUC-KR", NULL}}; + break; + case FT_ENCODING_MS_JOHAB: + encoding_list = (EncodingList) {{"CP1361", "JOHAB", NULL}}; + break; + default: + return 0; + } + + // open iconv context + const char **encoding_str = encoding_list.names; + iconv_t cd = (iconv_t) -1; + while (*encoding_str) { + cd = iconv_open(*encoding_str, "UTF-32LE"); + if (cd != (iconv_t) -1) break; + ++encoding_str; + } + if (cd == (iconv_t) -1) + return 0; + + char input_buffer[4]; + char output_buffer[2]; // MS-flavour encodings only need 2 bytes + uint32_t result = codepoint; + + // convert input codepoint to little endian uint32_t bytearray, + // result becomes 0 after the loop finishes + for (int i = 0; i < 4; ++i) { + input_buffer[i] = result & 0xFF; + result >>= 8; + } + + // do actual convert, only reversible converts are valid, since we are converting unicode to something else + size_t inbuf_size = sizeof(input_buffer); + size_t outbuf_size = sizeof(output_buffer); + char *inbuf = input_buffer; + char *outbuf = output_buffer; + if (iconv(cd, &inbuf, &inbuf_size, &outbuf, &outbuf_size)) + goto clean; + + // now we have multibyte string in output_buffer + // assemble those bytes into uint32_t + size_t output_length = sizeof(output_buffer) - outbuf_size; + result = pack_mbcs_bytes(output_buffer, output_length); + +clean: + iconv_close(cd); + return result; +} +#else +static uint32_t convert_unicode_to_mb(FT_Encoding encoding, uint32_t codepoint) { + // just a stub + // we can't handle this cmap, fallback + return 0; +} +#endif + /** * Select a good charmap, prefer Microsoft Unicode charmaps. * Otherwise, let FreeType decide. */ -void charmap_magic(ASS_Library *library, FT_Face face) +void ass_charmap_magic(ASS_Library *library, FT_Face face) { int i; - int ms_cmap = -1; + int ms_cmap = -1, ms_unicode_cmap = -1; // Search for a Microsoft Unicode cmap for (i = 0; i < face->num_charmaps; ++i) { FT_CharMap cmap = face->charmaps[i]; unsigned pid = cmap->platform_id; unsigned eid = cmap->encoding_id; - if (pid == 3 /*microsoft */ - && (eid == 1 /*unicode bmp */ - || eid == 10 /*full unicode */ )) { - FT_Set_Charmap(face, cmap); - return; - } else if (pid == 3 && ms_cmap < 0) - ms_cmap = i; + if (pid == TT_PLATFORM_MICROSOFT) { + switch (eid) { + case TT_MS_ID_UCS_4: + // Full Unicode cmap: select this immediately + FT_Set_Charmap(face, cmap); + return; + case TT_MS_ID_UNICODE_CS: + // BMP-only Unicode cmap: select this + // if no fuller Unicode cmap exists + if (ms_unicode_cmap < 0) + ms_unicode_cmap = ms_cmap = i; + break; + default: + // Non-Unicode cmap: select this if no Unicode cmap exists + if (ms_cmap < 0) + ms_cmap = i; + } + } } - // Try the first Microsoft cmap if no Microsoft Unicode cmap was found + // Try the first Microsoft BMP cmap if no MS cmap had full Unicode, + // or the first MS cmap of any kind if none of them had Unicode at all if (ms_cmap >= 0) { FT_CharMap cmap = face->charmaps[ms_cmap]; FT_Set_Charmap(face, cmap); @@ -79,7 +249,7 @@ void charmap_magic(ASS_Library *library, FT_Face face) /** * Adjust char index if the charmap is weird - * (currently just MS Symbol) + * (currently all non-Unicode Microsoft cmap) */ uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol) @@ -87,12 +257,22 @@ uint32_t ass_font_index_magic(FT_Face face, uint32_t symbol) if (!face->charmap) return symbol; - switch (face->charmap->encoding) { - case FT_ENCODING_MS_SYMBOL: - return 0xF000 | symbol; - default: - return symbol; + if (face->charmap->platform_id == TT_PLATFORM_MICROSOFT) { + switch (face->charmap->encoding) { + case FT_ENCODING_MS_SYMBOL: + return 0xF000 | symbol; + case FT_ENCODING_MS_SJIS: + case FT_ENCODING_MS_GB2312: + case FT_ENCODING_MS_BIG5: + case FT_ENCODING_MS_WANSUNG: + case FT_ENCODING_MS_JOHAB: + return convert_unicode_to_mb(face->charmap->encoding, symbol); + default: + return symbol; + } } + + return symbol; } static void set_font_metrics(FT_Face face) @@ -100,7 +280,7 @@ static void set_font_metrics(FT_Face face) // Mimicking GDI's behavior for asc/desc/height. // These fields are (apparently) sometimes used for signed values, // despite being unsigned in the spec. - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); if (os2 && ((short)os2->usWinAscent + (short)os2->usWinDescent != 0)) { face->ascender = (short)os2->usWinAscent; face->descender = -(short)os2->usWinDescent; @@ -130,6 +310,54 @@ static void set_font_metrics(FT_Face face) } } +FT_Face ass_face_open(ASS_Library *lib, FT_Library ftlib, const char *path, + const char *postscript_name, int index) +{ + FT_Face face; + int error = FT_New_Face(ftlib, path, index, &face); + if (error) { + ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, index); + return NULL; + } + + if (index >= 0) { + return face; + } else { + // The font provider gave us a postscript name and is not sure + // about the face index.. so use the postscript name to find the + // correct face_index in the collection! + for (int i = 0; i < face->num_faces; i++) { + FT_Done_Face(face); + error = FT_New_Face(ftlib, path, i, &face); + if (error) { + ass_msg(lib, MSGL_WARN, "Error opening font: '%s', %d", path, i); + return NULL; + } + + // If there is only one face, don't bother checking the name. + // The font might not even *have* a valid PostScript name. + if (!i && face->num_faces == 1) + return face; + + // Otherwise, we really need a name to search for. + if (!postscript_name) { + FT_Done_Face(face); + return NULL; + } + + const char *face_psname = FT_Get_Postscript_Name(face); + if (face_psname != NULL && + strcmp(face_psname, postscript_name) == 0) + return face; + } + + FT_Done_Face(face); + ass_msg(lib, MSGL_WARN, "Failed to find font '%s' in file: '%s'", + postscript_name, path); + return NULL; + } +} + static unsigned long read_stream_font(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) @@ -147,6 +375,45 @@ close_stream_font(FT_Stream stream) free(stream); } +FT_Face ass_face_stream(ASS_Library *lib, FT_Library ftlib, const char *name, + const ASS_FontStream *stream, int index) +{ + ASS_FontStream *fs = calloc(1, sizeof(ASS_FontStream)); + if (!fs) + return NULL; + *fs = *stream; + + FT_Stream ftstream = calloc(1, sizeof(FT_StreamRec)); + if (!ftstream) { + free(fs); + return NULL; + } + ftstream->size = stream->func(stream->priv, NULL, 0, 0); + ftstream->read = read_stream_font; + ftstream->close = close_stream_font; + ftstream->descriptor.pointer = (void *)fs; + + FT_Open_Args args = { + .flags = FT_OPEN_STREAM, + .stream = ftstream, + }; + + FT_Face face; + int error = FT_Open_Face(ftlib, &args, index, &face); + if (error) { + if (name) { + ass_msg(lib, MSGL_WARN, + "Error opening memory font: '%s'", name); + } else { + ass_msg(lib, MSGL_WARN, + "Error opening memory font"); + } + return NULL; + } + + return face; +} + /** * \brief Select a face with the given charcode and add it to ASS_Font * \return index of the new face in font->faces, -1 if failed @@ -155,14 +422,15 @@ static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch) { char *path; char *postscript_name = NULL; - int i, index, uid, error; + int i, index, uid; ASS_FontStream stream = { NULL, NULL }; FT_Face face; + int ret = -1; if (font->n_faces == ASS_FONT_MAX_FACES) return -1; - path = ass_font_select(fontsel, font->library, font , &index, + path = ass_font_select(fontsel, font, &index, &postscript_name, &uid, &stream, ch); if (!path) @@ -177,64 +445,30 @@ static int add_face(ASS_FontSelector *fontsel, ASS_Font *font, uint32_t ch) } if (stream.func) { - FT_Open_Args args; - FT_Stream ftstream = calloc(1, sizeof(FT_StreamRec)); - ASS_FontStream *fs = calloc(1, sizeof(ASS_FontStream)); - - *fs = stream; - ftstream->size = stream.func(stream.priv, NULL, 0, 0); - ftstream->read = read_stream_font; - ftstream->close = close_stream_font; - ftstream->descriptor.pointer = (void *)fs; - - memset(&args, 0, sizeof(FT_Open_Args)); - args.flags = FT_OPEN_STREAM; - args.stream = ftstream; - - error = FT_Open_Face(font->ftlibrary, &args, index, &face); - - if (error) { - ass_msg(font->library, MSGL_WARN, - "Error opening memory font: '%s'", path); - return -1; - } - + face = ass_face_stream(font->library, font->ftlibrary, path, + &stream, index); } else { - error = FT_New_Face(font->ftlibrary, path, index, &face); - if (error) { - ass_msg(font->library, MSGL_WARN, - "Error opening font: '%s', %d", path, index); - return -1; - } - - if (postscript_name && index < 0 && face->num_faces > 0) { - // The font provider gave us a post_script name and is not sure - // about the face index.. so use the postscript name to find the - // correct face_index in the collection! - for (int i = 0; i < face->num_faces; i++) { - FT_Done_Face(face); - error = FT_New_Face(font->ftlibrary, path, i, &face); - if (error) { - ass_msg(font->library, MSGL_WARN, - "Error opening font: '%s', %d", path, i); - return -1; - } - - const char *face_psname = FT_Get_Postscript_Name(face); - if (face_psname != NULL && - strcmp(face_psname, postscript_name) == 0) - break; - } - } + face = ass_face_open(font->library, font->ftlibrary, path, + postscript_name, index); } - charmap_magic(font->library, face); + if (!face) + return -1; + + ass_charmap_magic(font->library, face); set_font_metrics(face); font->faces[font->n_faces] = face; - font->faces_uid[font->n_faces++] = uid; - ass_face_set_size(face, font->size); - return font->n_faces - 1; + font->faces_uid[font->n_faces] = uid; + if (!ass_create_hb_font(font, font->n_faces)) { + FT_Done_Face(face); + goto fail; + } + + ret = font->n_faces++; + +fail: + return ret; } /** @@ -259,15 +493,12 @@ size_t ass_font_construct(void *key, void *value, void *priv) font->library = render_priv->library; font->ftlibrary = render_priv->ftlibrary; - font->shaper_priv = NULL; font->n_faces = 0; font->desc.family = desc->family; font->desc.bold = desc->bold; font->desc.italic = desc->italic; font->desc.vertical = desc->vertical; - font->size = 0.; - int error = add_face(render_priv->fontselect, font, 0); if (error == -1) font->library = NULL; @@ -285,17 +516,10 @@ void ass_face_set_size(FT_Face face, double size) FT_Request_Size(face, &rq); } -/** - * \brief Set font size - **/ -void ass_font_set_size(ASS_Font *font, double size) +bool ass_face_is_postscript(FT_Face face) { - int i; - if (font->size != size) { - font->size = size; - for (i = 0; i < font->n_faces; ++i) - ass_face_set_size(font->faces[i], size); - } + PS_FontInfoRec postscript_info; + return !FT_Get_PS_Font_Info(face, &postscript_info); } /** @@ -303,16 +527,55 @@ void ass_font_set_size(ASS_Font *font, double size) **/ int ass_face_get_weight(FT_Face face) { -#if FREETYPE_MAJOR > 2 || (FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 6) TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); -#else - // This old name is still included (as a macro), but deprecated as of 2.6, so avoid using it if we can - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); -#endif - if (os2 && os2->version != 0xffff && os2->usWeightClass) - return os2->usWeightClass; - else + FT_UShort os2Weight = os2 ? os2->usWeightClass : 0; + switch (os2Weight) { + case 0: return 300 * !!(face->style_flags & FT_STYLE_FLAG_BOLD) + 400; + case 1: + return 100; + case 2: + return 200; + case 3: + return 300; + case 4: + return 350; + case 5: + return 400; + case 6: + return 600; + case 7: + return 700; + case 8: + return 800; + case 9: + return 900; + default: + return os2Weight; + } +} + +static FT_Long fsSelection_to_style_flags(uint16_t fsSelection) +{ + FT_Long ret = 0; + + if (fsSelection & 1) + ret |= FT_STYLE_FLAG_ITALIC; + if (fsSelection & (1 << 5)) + ret |= FT_STYLE_FLAG_BOLD; + + return ret; +} + +FT_Long ass_face_get_style_flags(FT_Face face) +{ + // If we have an OS/2 table, compute this ourselves, since FreeType + // will mix in some flags that GDI ignores. + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); + if (os2) + return fsSelection_to_style_flags(os2->fsSelection); + + return face->style_flags; } /** @@ -327,93 +590,6 @@ void ass_font_get_asc_desc(ASS_Font *font, int face_index, *desc = FT_MulFix(-face->descender, y_scale); } -static void add_line(FT_Outline *ol, int bear, int advance, int dir, int pos, int size) { - FT_Vector points[4] = { - {.x = bear, .y = pos + size}, - {.x = advance, .y = pos + size}, - {.x = advance, .y = pos - size}, - {.x = bear, .y = pos - size}, - }; - - if (dir == FT_ORIENTATION_TRUETYPE) { - int i; - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } else { - int i; - for (i = 3; i >= 0; i--) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; - } - } - - ol->contours[ol->n_contours++] = ol->n_points - 1; -} - -/* - * Strike a glyph with a horizontal line; it's possible to underline it - * and/or strike through it. For the line's position and size, truetype - * tables are consulted. Obviously this relies on the data in the tables - * being accurate. - * - */ -static int ass_strike_outline_glyph(FT_Face face, ASS_Font *font, - FT_Glyph glyph, int under, int through) -{ - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); - FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; - int advance, y_scale, i, dir; - - if (!under && !through) - return 0; - - // Grow outline - i = (under ? 4 : 0) + (through ? 4 : 0); - if (ol->n_points > SHRT_MAX - i) - return 0; - if (!ASS_REALLOC_ARRAY(ol->points, ol->n_points + i)) - return 0; - if (!ASS_REALLOC_ARRAY(ol->tags, ol->n_points + i)) - return 0; - i = !!under + !!through; - if (ol->n_contours > SHRT_MAX - i) - return 0; - if (!ASS_REALLOC_ARRAY(ol->contours, ol->n_contours + i)) - return 0; - - advance = d16_to_d6(glyph->advance.x); - y_scale = face->size->metrics.y_scale; - - // Reverse drawing direction for non-truetype fonts - dir = FT_Outline_Get_Orientation(ol); - - // Add points to the outline - if (under && ps) { - int pos = FT_MulFix(ps->underlinePosition, y_scale); - int size = FT_MulFix(ps->underlineThickness, y_scale / 2); - - if (pos > 0 || size <= 0) - return 1; - - add_line(ol, 0, advance, dir, pos, size); - } - - if (through && os2) { - int pos = FT_MulFix(os2->yStrikeoutPosition, y_scale); - int size = FT_MulFix(os2->yStrikeoutSize, y_scale / 2); - - if (pos < 0 || size <= 0) - return 1; - - add_line(ol, 0, advance, dir, pos, size); - } - - return 0; -} - /** * Slightly embold a glyph without touching its metrics */ @@ -431,6 +607,23 @@ static void ass_glyph_embolden(FT_GlyphSlot slot) } /** + * Slightly italicize a glyph + */ +static void ass_glyph_italicize(FT_Face face) +{ + FT_Matrix xfrm = { + .xx = 0x10000L, + .yx = 0x00000L, + .xy = ass_face_is_postscript(face) + ? 0x02d24L /* tan(10 deg) */ + : 0x05700L /* matches GDI; effectively tan(18.77 deg) */, + .yy = 0x10000L, + }; + + FT_Outline_Transform(&face->glyph->outline, &xfrm); +} + +/** * \brief Get glyph and face index * Finds a face that has the requested codepoint and returns both face * and glyph index. @@ -448,24 +641,16 @@ int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font, *face_index = 0; return 0; } - // Handle NBSP like a regular space when rendering the glyph - if (symbol == 0xa0) - symbol = ' '; if (font->n_faces == 0) { *face_index = 0; return 0; } - // try with the requested face - if (*face_index < font->n_faces) { - face = font->faces[*face_index]; - index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol)); - } - - // not found in requested face, try all others for (i = 0; i < font->n_faces && index == 0; ++i) { face = font->faces[i]; - index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol)); + index = ass_font_index_magic(face, symbol); + if (index) + index = FT_Get_Char_Index(face, index); if (index) *face_index = i; } @@ -474,25 +659,30 @@ int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font, int face_idx; ass_msg(font->library, MSGL_INFO, "Glyph 0x%X not found, selecting one more " - "font for (%s, %d, %d)", symbol, font->desc.family, + "font for (%.*s, %d, %d)", symbol, (int) font->desc.family.len, font->desc.family.str, font->desc.bold, font->desc.italic); face_idx = *face_index = add_face(fontsel, font, symbol); if (face_idx >= 0) { face = font->faces[face_idx]; - index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol)); + index = ass_font_index_magic(face, symbol); + if (index) + index = FT_Get_Char_Index(face, index); if (index == 0 && face->num_charmaps > 0) { int i; ass_msg(font->library, MSGL_WARN, "Glyph 0x%X not found, broken font? Trying all charmaps", symbol); for (i = 0; i < face->num_charmaps; i++) { FT_Set_Charmap(face, face->charmaps[i]); - if ((index = FT_Get_Char_Index(face, ass_font_index_magic(face, symbol))) != 0) break; + index = ass_font_index_magic(face, symbol); + if (index) + index = FT_Get_Char_Index(face, index); + if (index) break; } } if (index == 0) { ass_msg(font->library, MSGL_ERR, - "Glyph 0x%X not found in font for (%s, %d, %d)", - symbol, font->desc.family, font->desc.bold, + "Glyph 0x%X not found in font for (%.*s, %d, %d)", + symbol, (int) font->desc.family.len, font->desc.family.str, font->desc.bold, font->desc.italic); } } @@ -509,15 +699,10 @@ int ass_font_get_index(ASS_FontSelector *fontsel, ASS_Font *font, * \brief Get a glyph * \param ch character code **/ -FT_Glyph ass_font_get_glyph(ASS_Font *font, int face_index, int index, - ASS_Hinting hinting, int deco) +bool ass_font_get_glyph(ASS_Font *font, int face_index, int index, + ASS_Hinting hinting) { - int error; - FT_Glyph glyph; - FT_Face face = font->faces[face_index]; - int flags = 0; - - flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH + FT_Int32 flags = FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH | FT_LOAD_IGNORE_TRANSFORM; switch (hinting) { case ASS_HINTING_NONE: @@ -533,47 +718,21 @@ FT_Glyph ass_font_get_glyph(ASS_Font *font, int face_index, int index, break; } - error = FT_Load_Glyph(face, index, flags); + FT_Face face = font->faces[face_index]; + FT_Error error = FT_Load_Glyph(face, index, flags); if (error) { ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", index); - return 0; - } - if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && - (font->desc.italic > 55)) { - FT_GlyphSlot_Oblique(face->glyph); + return false; } - if (font->desc.bold > ass_face_get_weight(face) + 150) { + FT_Long style_flags = ass_face_get_style_flags(face); + if (!(style_flags & FT_STYLE_FLAG_ITALIC) && (font->desc.italic > 55)) + ass_glyph_italicize(face); + if (!(style_flags & FT_STYLE_FLAG_BOLD) && + font->desc.bold > ass_face_get_weight(face) + 150) ass_glyph_embolden(face->glyph); - } - error = FT_Get_Glyph(face->glyph, &glyph); - if (error) { - ass_msg(font->library, MSGL_WARN, "Error loading glyph, index %d", - index); - return 0; - } - - // Rotate glyph, if needed - if (deco & DECO_ROTATE) { - FT_Matrix m = { 0, double_to_d16(-1.0), double_to_d16(1.0), 0 }; - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - int desc = 0; - - if (os2) - desc = FT_MulFix(os2->sTypoDescender, face->size->metrics.y_scale); - - FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, 0, -desc); - FT_Outline_Transform(&((FT_OutlineGlyph) glyph)->outline, &m); - FT_Outline_Translate(&((FT_OutlineGlyph) glyph)->outline, - face->glyph->metrics.vertAdvance, desc); - glyph->advance.x = face->glyph->linearVertAdvance; - } - - ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, - deco & DECO_STRIKETHROUGH); - - return glyph; + return true; } /** @@ -582,11 +741,96 @@ FT_Glyph ass_font_get_glyph(ASS_Font *font, int face_index, int index, void ass_font_clear(ASS_Font *font) { int i; - if (font->shaper_priv) - ass_shaper_font_data_free(font->shaper_priv); for (i = 0; i < font->n_faces; ++i) { if (font->faces[i]) FT_Done_Face(font->faces[i]); + if (font->hb_fonts[i]) + hb_font_destroy(font->hb_fonts[i]); } free((char *) font->desc.family.str); } + +/** + * \brief Convert glyph into ASS_Outline according to decoration flags + **/ +bool ass_get_glyph_outline(ASS_Outline *outline, int32_t *advance, + FT_Face face, unsigned flags) +{ + int32_t y_scale = face->size->metrics.y_scale; + int32_t adv = face->glyph->advance.x; + if (flags & DECO_ROTATE) + adv = d16_to_d6(face->glyph->linearVertAdvance); + *advance = adv; + + int n_lines = 0; + int32_t line_y[2][2]; + if (adv > 0 && (flags & DECO_UNDERLINE)) { + TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); + if (ps && ps->underlinePosition <= 0 && ps->underlineThickness > 0) { + int64_t pos = ((int64_t) ps->underlinePosition * y_scale + 0x8000) >> 16; + int64_t size = ((int64_t) ps->underlineThickness * y_scale + 0x8000) >> 16; + pos = -pos - (size >> 1); + if (pos >= -OUTLINE_MAX && pos + size <= OUTLINE_MAX) { + line_y[n_lines][0] = pos; + line_y[n_lines][1] = pos + size; + n_lines++; + } + } + } + if (adv > 0 && (flags & DECO_STRIKETHROUGH)) { + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); + if (os2 && os2->yStrikeoutPosition >= 0 && os2->yStrikeoutSize > 0) { + int64_t pos = ((int64_t) os2->yStrikeoutPosition * y_scale + 0x8000) >> 16; + int64_t size = ((int64_t) os2->yStrikeoutSize * y_scale + 0x8000) >> 16; + pos = -pos - (size >> 1); + if (pos >= -OUTLINE_MAX && pos + size <= OUTLINE_MAX) { + line_y[n_lines][0] = pos; + line_y[n_lines][1] = pos + size; + n_lines++; + } + } + } + + assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); + FT_Outline *source = &face->glyph->outline; + if (!source->n_points && !n_lines) { + ass_outline_clear(outline); + return true; + } + + size_t max_points = 2 * source->n_points + 4 * n_lines; + size_t max_segments = source->n_points + 4 * n_lines; + if (!ass_outline_alloc(outline, max_points, max_segments)) + return false; + + if (!ass_outline_convert(outline, source)) + goto fail; + + if (flags & DECO_ROTATE) { + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); + int64_t desc = 0; + if (os2) { + desc = ((int64_t) os2->sTypoDescender * y_scale + 0x8000) >> 16; + if (llabs(desc) > 2 * OUTLINE_MAX) + goto fail; + } + int64_t dv = face->glyph->metrics.vertAdvance + desc; + if (llabs(dv) > 2 * OUTLINE_MAX) + goto fail; + ASS_Vector offs = { dv, -desc }; + if (!ass_outline_rotate_90(outline, offs)) + goto fail; + } + + if (!n_lines) + return true; + FT_Orientation dir = FT_Outline_Get_Orientation(source); + int iy = (dir == FT_ORIENTATION_TRUETYPE ? 0 : 1); + for (int i = 0; i < n_lines; i++) + ass_outline_add_rect(outline, 0, line_y[i][iy], adv, line_y[i][iy ^ 1]); + return true; + +fail: + ass_outline_free(outline); + return false; +} |