summaryrefslogtreecommitdiffstats
path: root/libass/ass_font.c
diff options
context:
space:
mode:
Diffstat (limited to 'libass/ass_font.c')
-rw-r--r--libass/ass_font.c724
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;
+}