Mercurial > hgrepos > Python2 > PyMuPDF
diff mupdf-source/source/fitz/font.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mupdf-source/source/fitz/font.c Mon Sep 15 11:43:07 2025 +0200 @@ -0,0 +1,2421 @@ +// Copyright (C) 2004-2025 Artifex Software, Inc. +// +// This file is part of MuPDF. +// +// MuPDF is free software: you can redistribute it and/or modify it under the +// terms of the GNU Affero General Public License as published by the Free +// Software Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +// details. +// +// You should have received a copy of the GNU Affero General Public License +// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> +// +// Alternative licensing terms are available from the licensor. +// For commercial licensing, see <https://www.artifex.com/> or contact +// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, +// CA 94129, USA, for further information. + +#include "mupdf/fitz.h" +#include "mupdf/ucdn.h" + +#include "draw-imp.h" +#include "color-imp.h" +#include "glyph-imp.h" +#include "pixmap-imp.h" + +#include <ft2build.h> + +#include <assert.h> + +#include FT_FREETYPE_H +#include FT_ADVANCES_H +#include FT_MODULE_H +#include FT_STROKER_H +#include FT_SYSTEM_H +#include FT_TRUETYPE_TABLES_H +#include FT_TRUETYPE_TAGS_H + +#ifndef FT_SFNT_OS2 +#define FT_SFNT_OS2 ft_sfnt_os2 +#endif + +/* 20 degrees */ +#define SHEAR 0.36397f + +int ft_char_index(void *face, int cid) +{ + int gid = FT_Get_Char_Index(face, cid); + if (gid == 0) + gid = FT_Get_Char_Index(face, 0xf000 + cid); + + /* some chinese fonts only ship the similarly looking 0x2026 */ + if (gid == 0 && cid == 0x22ef) + gid = FT_Get_Char_Index(face, 0x2026); + + return gid; +} + +int ft_name_index(void *face, const char *name) +{ + int code = FT_Get_Name_Index(face, (char*)name); + if (code == 0) + { + int unicode = fz_unicode_from_glyph_name(name); + if (unicode) + { + const char **dupnames = fz_duplicate_glyph_names_from_unicode(unicode); + while (*dupnames) + { + code = FT_Get_Name_Index(face, (char*)*dupnames); + if (code) + break; + dupnames++; + } + if (code == 0) + { + char buf[12]; + sprintf(buf, "uni%04X", unicode); + code = FT_Get_Name_Index(face, buf); + } + } + } + return code; +} + +static void fz_drop_freetype(fz_context *ctx); + +static fz_font * +fz_new_font(fz_context *ctx, const char *name, int use_glyph_bbox, int glyph_count) +{ + fz_font *font; + + font = fz_malloc_struct(ctx, fz_font); + font->refs = 1; + + if (name) + fz_strlcpy(font->name, name, sizeof font->name); + else + fz_strlcpy(font->name, "(null)", sizeof font->name); + + font->ft_face = NULL; + font->flags.ft_substitute = 0; + font->flags.fake_bold = 0; + font->flags.fake_italic = 0; + font->flags.has_opentype = 0; + font->flags.embed = 0; + font->flags.never_embed = 0; + + font->t3matrix = fz_identity; + font->t3resources = NULL; + font->t3procs = NULL; + font->t3lists = NULL; + font->t3widths = NULL; + font->t3flags = NULL; + font->t3doc = NULL; + font->t3run = NULL; + + font->bbox.x0 = 0; + font->bbox.y0 = 0; + font->bbox.x1 = 1; + font->bbox.y1 = 1; + + font->glyph_count = glyph_count; + + font->bbox_table = NULL; + font->use_glyph_bbox = use_glyph_bbox; + + font->width_count = 0; + font->width_table = NULL; + + font->subfont = 0; + + return font; +} + +fz_font * +fz_keep_font(fz_context *ctx, fz_font *font) +{ + return fz_keep_imp(ctx, font, &font->refs); +} + +static void +free_resources(fz_context *ctx, fz_font *font) +{ + int i; + + if (font->t3resources) + { + font->t3freeres(ctx, font->t3doc, font->t3resources); + font->t3resources = NULL; + } + + if (font->t3procs) + { + for (i = 0; i < 256; i++) + fz_drop_buffer(ctx, font->t3procs[i]); + } + fz_free(ctx, font->t3procs); + font->t3procs = NULL; +} + +/* + Internal function to remove the + references to a document held by a Type3 font. This is + called during document destruction to ensure that Type3 + fonts clean up properly. + + Without this call being made, Type3 fonts can be left + holding pdf_obj references for the sake of interpretation + operations that will never come. These references + cannot be freed after the document, hence this function + forces them to be freed earlier in the process. + + font: The font to decouple. + + t3doc: The document to which the font may refer. +*/ +void fz_decouple_type3_font(fz_context *ctx, fz_font *font, void *t3doc) +{ + if (!font || !t3doc || font->t3doc == NULL) + return; + + if (font->t3doc != t3doc) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "can't decouple type3 font from a different doc"); + + font->t3doc = NULL; + free_resources(ctx, font); +} + +void +fz_drop_font(fz_context *ctx, fz_font *font) +{ + int fterr; + int i; + + if (!fz_drop_imp(ctx, font, &font->refs)) + return; + + free_resources(ctx, font); + if (font->t3lists) + for (i = 0; i < 256; i++) + fz_drop_display_list(ctx, font->t3lists[i]); + fz_free(ctx, font->t3procs); + fz_free(ctx, font->t3lists); + fz_free(ctx, font->t3widths); + fz_free(ctx, font->t3flags); + + if (font->ft_face) + { + fz_ft_lock(ctx); + fterr = FT_Done_Face((FT_Face)font->ft_face); + fz_ft_unlock(ctx); + if (fterr) + fz_warn(ctx, "FT_Done_Face(%s): %s", font->name, ft_error_string(fterr)); + fz_drop_freetype(ctx); + } + + for (i = 0; i < 256; ++i) + fz_free(ctx, font->encoding_cache[i]); + + fz_drop_buffer(ctx, font->buffer); + if (font->bbox_table) + { + int n = (font->glyph_count+255)/256; + for (i = 0; i < n; i++) + fz_free(ctx, font->bbox_table[i]); + fz_free(ctx, font->bbox_table); + } + fz_free(ctx, font->width_table); + if (font->advance_cache) + { + int n = (font->glyph_count+255)/256; + for (i = 0; i < n; i++) + fz_free(ctx, font->advance_cache[i]); + fz_free(ctx, font->advance_cache); + } + if (font->shaper_data.destroy && font->shaper_data.shaper_handle) + { + font->shaper_data.destroy(ctx, font->shaper_data.shaper_handle); + } + fz_free(ctx, font); +} + +void +fz_set_font_bbox(fz_context *ctx, fz_font *font, float xmin, float ymin, float xmax, float ymax) +{ + if (xmin >= xmax || ymin >= ymax) + { + /* Invalid bbox supplied. */ + if (font->t3procs) + { + /* For type3 fonts we use the union of all the glyphs' bboxes. */ + font->bbox = fz_empty_rect; + } + else + { + /* For other fonts it would be prohibitively slow to measure the true one, so make one up. */ + font->bbox = fz_unit_rect; + } + font->flags.invalid_bbox = 1; + } + else + { + font->bbox.x0 = xmin; + font->bbox.y0 = ymin; + font->bbox.x1 = xmax; + font->bbox.y1 = ymax; + } +} + +float fz_font_ascender(fz_context *ctx, fz_font *font) +{ + return font->ascender; +} + +float fz_font_descender(fz_context *ctx, fz_font *font) +{ + return font->descender; +} + +/* + * Freetype hooks + */ + +struct fz_font_context +{ + int ctx_refs; + FT_Library ftlib; + struct FT_MemoryRec_ ftmemory; + int ftlib_refs; + fz_load_system_font_fn *load_font; + fz_load_system_cjk_font_fn *load_cjk_font; + fz_load_system_fallback_font_fn *load_fallback_font; + + /* Cached fallback fonts */ + fz_font *base14[14]; + fz_font *cjk[4]; + struct { fz_font *serif, *sans; } fallback[256]; + fz_font *symbol1, *symbol2, *math, *music, *boxes; + fz_font *emoji; +}; + +#undef __FTERRORS_H__ +#define FT_ERRORDEF(e, v, s) { (e), (s) }, +#define FT_ERROR_START_LIST +#define FT_ERROR_END_LIST { 0, NULL } + +struct ft_error +{ + int err; + char *str; +}; + +static void *ft_alloc(FT_Memory memory, long size) +{ + fz_context *ctx = (fz_context *) memory->user; + return Memento_label(fz_malloc_no_throw(ctx, size), "ft_alloc"); +} + +static void ft_free(FT_Memory memory, void *block) +{ + fz_context *ctx = (fz_context *) memory->user; + fz_free(ctx, block); +} + +static void *ft_realloc(FT_Memory memory, long cur_size, long new_size, void *block) +{ + fz_context *ctx = (fz_context *) memory->user; + void *newblock = NULL; + if (new_size == 0) + { + fz_free(ctx, block); + return newblock; + } + if (block == NULL) + return ft_alloc(memory, new_size); + return fz_realloc_no_throw(ctx, block, new_size); +} + +void +fz_ft_lock(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_FREETYPE); + fz_lock(ctx, FZ_LOCK_ALLOC); + assert(ctx->font->ftmemory.user == NULL); + ctx->font->ftmemory.user = ctx; + fz_unlock(ctx, FZ_LOCK_ALLOC); +} + +void +fz_ft_unlock(fz_context *ctx) +{ + fz_lock(ctx, FZ_LOCK_ALLOC); + ctx->font->ftmemory.user = NULL; + fz_unlock(ctx, FZ_LOCK_ALLOC); + fz_unlock(ctx, FZ_LOCK_FREETYPE); +} + +int +fz_ft_lock_held(fz_context *ctx) +{ + /* If this thread has locked the freetype lock already, then + * the stored context will be this one. */ + return (ctx->font->ftmemory.user == ctx); +} + +void fz_new_font_context(fz_context *ctx) +{ + ctx->font = fz_malloc_struct(ctx, fz_font_context); + ctx->font->ctx_refs = 1; + ctx->font->ftlib = NULL; + ctx->font->ftlib_refs = 0; + ctx->font->load_font = NULL; + ctx->font->ftmemory.user = NULL; + ctx->font->ftmemory.alloc = ft_alloc; + ctx->font->ftmemory.free = ft_free; + ctx->font->ftmemory.realloc = ft_realloc; +} + +fz_font_context * +fz_keep_font_context(fz_context *ctx) +{ + if (!ctx) + return NULL; + return fz_keep_imp(ctx, ctx->font, &ctx->font->ctx_refs); +} + +void fz_drop_font_context(fz_context *ctx) +{ + if (!ctx) + return; + + if (fz_drop_imp(ctx, ctx->font, &ctx->font->ctx_refs)) + { + int i; + + for (i = 0; i < (int)nelem(ctx->font->base14); ++i) + fz_drop_font(ctx, ctx->font->base14[i]); + for (i = 0; i < (int)nelem(ctx->font->cjk); ++i) + fz_drop_font(ctx, ctx->font->cjk[i]); + for (i = 0; i < (int)nelem(ctx->font->fallback); ++i) + { + fz_drop_font(ctx, ctx->font->fallback[i].serif); + fz_drop_font(ctx, ctx->font->fallback[i].sans); + } + fz_drop_font(ctx, ctx->font->symbol1); + fz_drop_font(ctx, ctx->font->symbol2); + fz_drop_font(ctx, ctx->font->math); + fz_drop_font(ctx, ctx->font->music); + fz_drop_font(ctx, ctx->font->emoji); + fz_drop_font(ctx, ctx->font->boxes); + fz_free(ctx, ctx->font); + ctx->font = NULL; + } +} + +void fz_install_load_system_font_funcs(fz_context *ctx, + fz_load_system_font_fn *f, + fz_load_system_cjk_font_fn *f_cjk, + fz_load_system_fallback_font_fn *f_back) +{ + ctx->font->load_font = f; + ctx->font->load_cjk_font = f_cjk; + ctx->font->load_fallback_font = f_back; +} + +/* fz_load_*_font returns NULL if no font could be loaded (also on error) */ +fz_font *fz_load_system_font(fz_context *ctx, const char *name, int bold, int italic, int needs_exact_metrics) +{ + fz_font *font = NULL; + + if (ctx->font->load_font) + { + fz_try(ctx) + font = ctx->font->load_font(ctx, name, bold, italic, needs_exact_metrics); + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); + fz_report_error(ctx); + font = NULL; + } + } + + return font; +} + +fz_font *fz_load_system_cjk_font(fz_context *ctx, const char *name, int ros, int serif) +{ + fz_font *font = NULL; + + if (ctx->font->load_cjk_font) + { + fz_try(ctx) + font = ctx->font->load_cjk_font(ctx, name, ros, serif); + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); + fz_report_error(ctx); + font = NULL; + } + } + + return font; +} + +fz_font *fz_load_system_fallback_font(fz_context *ctx, int script, int language, int serif, int bold, int italic) +{ + fz_font *font = NULL; + + if (ctx->font->load_fallback_font) + { + fz_try(ctx) + font = ctx->font->load_fallback_font(ctx, script, language, serif, bold, italic); + fz_catch(ctx) + { + fz_rethrow_if(ctx, FZ_ERROR_TRYLATER); + fz_rethrow_if(ctx, FZ_ERROR_SYSTEM); + fz_report_error(ctx); + font = NULL; + } + } + + return font; +} + +fz_font *fz_load_fallback_font(fz_context *ctx, int script, int language, int serif, int bold, int italic) +{ + fz_font **fontp; + const unsigned char *data; + int ordering = FZ_ADOBE_JAPAN; + int index; + int subfont; + int size; + + if (script < 0 || script >= (int)nelem(ctx->font->fallback)) + return NULL; + + /* TODO: bold and italic */ + + index = script; + if (script == UCDN_SCRIPT_HAN) + { + switch (language) + { + case FZ_LANG_ja: index = UCDN_LAST_SCRIPT + 1; ordering = FZ_ADOBE_JAPAN; break; + case FZ_LANG_ko: index = UCDN_LAST_SCRIPT + 2; ordering = FZ_ADOBE_KOREA; break; + case FZ_LANG_zh_Hans: index = UCDN_LAST_SCRIPT + 3; ordering = FZ_ADOBE_GB; break; + case FZ_LANG_zh_Hant: index = UCDN_LAST_SCRIPT + 4; ordering = FZ_ADOBE_CNS; break; + } + } + if (script == UCDN_SCRIPT_ARABIC) + { + if (language == FZ_LANG_ur || language == FZ_LANG_urd) + index = UCDN_LAST_SCRIPT + 5; + } + + if (serif) + fontp = &ctx->font->fallback[index].serif; + else + fontp = &ctx->font->fallback[index].sans; + + if (!*fontp) + { + *fontp = fz_load_system_fallback_font(ctx, script, language, serif, bold, italic); + if (!*fontp) + { + data = fz_lookup_noto_font(ctx, script, language, &size, &subfont); + if (data) + { + *fontp = fz_new_font_from_memory(ctx, NULL, data, size, subfont, 0); + /* Noto fonts can be embedded. */ + fz_set_font_embedding(ctx, *fontp, 1); + } + } + } + + switch (script) + { + case UCDN_SCRIPT_HANGUL: script = UCDN_SCRIPT_HAN; ordering = FZ_ADOBE_KOREA; break; + case UCDN_SCRIPT_HIRAGANA: script = UCDN_SCRIPT_HAN; ordering = FZ_ADOBE_JAPAN; break; + case UCDN_SCRIPT_KATAKANA: script = UCDN_SCRIPT_HAN; ordering = FZ_ADOBE_JAPAN; break; + case UCDN_SCRIPT_BOPOMOFO: script = UCDN_SCRIPT_HAN; ordering = FZ_ADOBE_CNS; break; + } + if (*fontp && (script == UCDN_SCRIPT_HAN)) + { + (*fontp)->flags.cjk = 1; + (*fontp)->flags.cjk_lang = ordering; + } + + return *fontp; +} + +static fz_font *fz_load_fallback_math_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->math) + { + data = fz_lookup_noto_math_font(ctx, &size); + if (data) + ctx->font->math = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->math; +} + +static fz_font *fz_load_fallback_music_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->music) + { + data = fz_lookup_noto_music_font(ctx, &size); + if (data) + ctx->font->music = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->music; +} + +static fz_font *fz_load_fallback_symbol1_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->symbol1) + { + data = fz_lookup_noto_symbol1_font(ctx, &size); + if (data) + ctx->font->symbol1 = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->symbol1; +} + +static fz_font *fz_load_fallback_symbol2_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->symbol2) + { + data = fz_lookup_noto_symbol2_font(ctx, &size); + if (data) + ctx->font->symbol2 = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->symbol2; +} + +static fz_font *fz_load_fallback_emoji_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->emoji) + { + data = fz_lookup_noto_emoji_font(ctx, &size); + if (data) + ctx->font->emoji = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->emoji; +} + +static fz_font *fz_load_fallback_boxes_font(fz_context *ctx) +{ + const unsigned char *data; + int size; + if (!ctx->font->boxes) + { + data = fz_lookup_noto_boxes_font(ctx, &size); + if (data) + ctx->font->boxes = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + } + return ctx->font->boxes; +} + +static const struct ft_error ft_errors[] = +{ +#include FT_ERRORS_H +}; + +const char *ft_error_string(int err) +{ + const struct ft_error *e; + + for (e = ft_errors; e->str; e++) + if (e->err == err) + return e->str; + + return "Unknown error"; +} + +static void +fz_keep_freetype(fz_context *ctx) +{ + int fterr; + int maj, min, pat; + fz_font_context *fct = ctx->font; + + fz_ft_lock(ctx); + if (fct->ftlib) + { + fct->ftlib_refs++; + fz_ft_unlock(ctx); + return; + } + + fterr = FT_New_Library(&fct->ftmemory, &fct->ftlib); + if (fterr) + { + const char *mess = ft_error_string(fterr); + fz_ft_unlock(ctx); + fz_throw(ctx, FZ_ERROR_LIBRARY, "cannot init freetype: %s", mess); + } + + FT_Add_Default_Modules(fct->ftlib); + + FT_Library_Version(fct->ftlib, &maj, &min, &pat); + if (maj == 2 && min == 1 && pat < 7) + { + fterr = FT_Done_Library(fct->ftlib); + if (fterr) + fz_warn(ctx, "FT_Done_Library(): %s", ft_error_string(fterr)); + fz_ft_unlock(ctx); + fz_throw(ctx, FZ_ERROR_LIBRARY, "freetype version too old: %d.%d.%d", maj, min, pat); + } + + fct->ftlib_refs++; + fz_ft_unlock(ctx); +} + +static void +fz_drop_freetype(fz_context *ctx) +{ + int fterr; + fz_font_context *fct = ctx->font; + + fz_ft_lock(ctx); + if (--fct->ftlib_refs == 0) + { + fterr = FT_Done_Library(fct->ftlib); + if (fterr) + fz_warn(ctx, "FT_Done_Library(): %s", ft_error_string(fterr)); + fct->ftlib = NULL; + } + fz_ft_unlock(ctx); +} + +fz_font * +fz_new_font_from_buffer(fz_context *ctx, const char *name, fz_buffer *buffer, int index, int use_glyph_bbox) +{ + FT_Face face; + TT_OS2 *os2; + fz_font *font; + int fterr; + FT_ULong tag, size, i, n; + FT_UShort flags; + char namebuf[sizeof(font->name)]; + fz_ascdesc_source ascdesc_src = FZ_ASCDESC_FROM_FONT; + + fz_keep_freetype(ctx); + + fz_ft_lock(ctx); + fterr = FT_New_Memory_Face(ctx->font->ftlib, buffer->data, (FT_Long)buffer->len, index, &face); + fz_ft_unlock(ctx); + if (fterr) + { + fz_drop_freetype(ctx); + fz_throw(ctx, FZ_ERROR_LIBRARY, "FT_New_Memory_Face(%s): %s", name, ft_error_string(fterr)); + } + + if (!name) + { + if (!face->family_name) + { + name = face->style_name; + } + else if (!face->style_name) + { + name = face->family_name; + } + else if (strstr(face->style_name, face->family_name) == face->style_name) + { + name = face->style_name; + } + else + { + fz_strlcpy(namebuf, face->family_name, sizeof(namebuf)); + fz_strlcat(namebuf, " ", sizeof(namebuf)); + fz_strlcat(namebuf, face->style_name, sizeof(namebuf)); + name = namebuf; + } + } + + fz_try(ctx) + font = fz_new_font(ctx, name, use_glyph_bbox, face->num_glyphs); + fz_catch(ctx) + { + fz_ft_lock(ctx); + fterr = FT_Done_Face(face); + fz_ft_unlock(ctx); + if (fterr) + fz_warn(ctx, "FT_Done_Face(%s): %s", name, ft_error_string(fterr)); + fz_drop_freetype(ctx); + fz_rethrow(ctx); + } + + font->ft_face = face; + fz_set_font_bbox(ctx, font, + (float) face->bbox.xMin / face->units_per_EM, + (float) face->bbox.yMin / face->units_per_EM, + (float) face->bbox.xMax / face->units_per_EM, + (float) face->bbox.yMax / face->units_per_EM); + + if (face->ascender <= 0 || face->ascender > FZ_MAX_TRUSTWORTHY_ASCENT * face->units_per_EM) + font->ascender = 0.8f, ascdesc_src = FZ_ASCDESC_DEFAULT; + else + font->ascender = (float)face->ascender / face->units_per_EM; + + if (face->descender < FZ_MAX_TRUSTWORTHY_DESCENT * face->units_per_EM || face->descender > -FZ_MAX_TRUSTWORTHY_DESCENT * face->units_per_EM) + font->descender = -0.2f, ascdesc_src = FZ_ASCDESC_DEFAULT; + else + { + font->descender = (float)face->descender / face->units_per_EM; + if (font->descender > 0) + font->descender = -font->descender; + } + + font->ascdesc_src = ascdesc_src; + + font->subfont = index; + + font->flags.is_mono = !!(face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + font->flags.is_serif = 1; + font->flags.is_bold = !!(face->style_flags & FT_STYLE_FLAG_BOLD); + font->flags.is_italic = !!(face->style_flags & FT_STYLE_FLAG_ITALIC); + font->flags.embed = 1; + font->flags.never_embed = 0; + + if (FT_IS_SFNT(face)) + { + fz_ft_lock(ctx); + os2 = FT_Get_Sfnt_Table(face, FT_SFNT_OS2); + if (os2) + font->flags.is_serif = !(os2->sFamilyClass & 2048); /* Class 8 is sans-serif */ + + flags = FT_Get_FSType_Flags(face); + if (flags & (FT_FSTYPE_RESTRICTED_LICENSE_EMBEDDING | + FT_FSTYPE_BITMAP_EMBEDDING_ONLY)) + { + font->flags.never_embed = 1; + font->flags.embed = 0; + } + + FT_Sfnt_Table_Info(face, 0, NULL, &n); + for (i = 0; i < n; ++i) + { + FT_Sfnt_Table_Info(face, i, &tag, &size); + if (tag == TTAG_GDEF || tag == TTAG_GPOS || tag == TTAG_GSUB) + font->flags.has_opentype = 1; + } + fz_ft_unlock(ctx); + } + + if (name) + { + if (!font->flags.is_bold) + { + if (strstr(name, "Semibold")) font->flags.is_bold = 1; + if (strstr(name, "Bold")) font->flags.is_bold = 1; + } + if (!font->flags.is_italic) + { + if (strstr(name, "Italic")) font->flags.is_italic = 1; + if (strstr(name, "Oblique")) font->flags.is_italic = 1; + } + } + + font->buffer = fz_keep_buffer(ctx, buffer); + + return font; +} + +fz_font * +fz_new_font_from_memory(fz_context *ctx, const char *name, const unsigned char *data, int len, int index, int use_glyph_bbox) +{ + fz_buffer *buffer = fz_new_buffer_from_shared_data(ctx, data, len); + fz_font *font = NULL; + fz_try(ctx) + font = fz_new_font_from_buffer(ctx, name, buffer, index, use_glyph_bbox); + fz_always(ctx) + fz_drop_buffer(ctx, buffer); + fz_catch(ctx) + fz_rethrow(ctx); + return font; +} + +fz_font * +fz_new_font_from_file(fz_context *ctx, const char *name, const char *path, int index, int use_glyph_bbox) +{ + fz_buffer *buffer = fz_read_file(ctx, path); + fz_font *font = NULL; + fz_try(ctx) + font = fz_new_font_from_buffer(ctx, name, buffer, index, use_glyph_bbox); + fz_always(ctx) + fz_drop_buffer(ctx, buffer); + fz_catch(ctx) + fz_rethrow(ctx); + return font; +} + +void fz_set_font_embedding(fz_context *ctx, fz_font *font, int embed) +{ + if (!font) + return; + if (embed) + { + if (font->flags.never_embed) + fz_warn(ctx, "not allowed to embed font: %s", font->name); + else + font->flags.embed = 1; + } + else + { + font->flags.embed = 0; + } +} + +static int +find_base14_index(const char *name) +{ + if (!strcmp(name, "Courier")) return 0; + if (!strcmp(name, "Courier-Oblique")) return 1; + if (!strcmp(name, "Courier-Bold")) return 2; + if (!strcmp(name, "Courier-BoldOblique")) return 3; + if (!strcmp(name, "Helvetica")) return 4; + if (!strcmp(name, "Helvetica-Oblique")) return 5; + if (!strcmp(name, "Helvetica-Bold")) return 6; + if (!strcmp(name, "Helvetica-BoldOblique")) return 7; + if (!strcmp(name, "Times-Roman")) return 8; + if (!strcmp(name, "Times-Italic")) return 9; + if (!strcmp(name, "Times-Bold")) return 10; + if (!strcmp(name, "Times-BoldItalic")) return 11; + if (!strcmp(name, "Symbol")) return 12; + if (!strcmp(name, "ZapfDingbats")) return 13; + return -1; +} + +fz_font * +fz_new_base14_font(fz_context *ctx, const char *name) +{ + const unsigned char *data; + int size; + int x = find_base14_index(name); + if (x >= 0) + { + if (ctx->font->base14[x]) + return fz_keep_font(ctx, ctx->font->base14[x]); + data = fz_lookup_base14_font(ctx, name, &size); + if (data) + { + ctx->font->base14[x] = fz_new_font_from_memory(ctx, name, data, size, 0, 1); + ctx->font->base14[x]->flags.is_serif = (name[0] == 'T'); /* Times-Roman */ + /* Ideally we should not embed base14 fonts by default, but we have to + * allow it for now until we have written code in pdf-device to output + * base14s in a 'special' manner. */ + fz_set_font_embedding(ctx, ctx->font->base14[x], 1); + return fz_keep_font(ctx, ctx->font->base14[x]); + } + } + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot find builtin font with name '%s'", name); +} + +fz_font * +fz_new_cjk_font(fz_context *ctx, int ordering) +{ + const unsigned char *data; + int size, index; + fz_font *font; + if (ordering >= 0 && ordering < (int)nelem(ctx->font->cjk)) + { + if (ctx->font->cjk[ordering]) + return fz_keep_font(ctx, ctx->font->cjk[ordering]); + data = fz_lookup_cjk_font(ctx, ordering, &size, &index); + if (data) + font = fz_new_font_from_memory(ctx, NULL, data, size, index, 0); + else + font = fz_load_system_cjk_font(ctx, "SourceHanSerif", ordering, 1); + /* FIXME: Currently the builtin one at least will be set to embed. Is that right? */ + if (font) + { + font->flags.cjk = 1; + font->flags.cjk_lang = ordering; + ctx->font->cjk[ordering] = font; + return fz_keep_font(ctx, ctx->font->cjk[ordering]); + } + } + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot find builtin CJK font"); +} + +fz_font * +fz_new_builtin_font(fz_context *ctx, const char *name, int is_bold, int is_italic) +{ + const unsigned char *data; + int size; + fz_font *font; + data = fz_lookup_builtin_font(ctx, name, is_bold, is_italic, &size); + if (!data) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot find builtin font with name '%s'", name); + font = fz_new_font_from_memory(ctx, NULL, data, size, 0, 0); + + /* Don't embed builtin fonts. */ + fz_set_font_embedding(ctx, font, 0); + + return font; +} + +static fz_matrix * +fz_adjust_ft_glyph_width(fz_context *ctx, fz_font *font, int gid, fz_matrix *trm) +{ + /* Fudge the font matrix to stretch the glyph if we've substituted the font. */ + if (font->flags.ft_stretch && font->width_table /* && font->wmode == 0 */) + { + FT_Error fterr; + FT_Fixed adv = 0; + float subw; + float realw; + + fz_ft_lock(ctx); + fterr = FT_Get_Advance(font->ft_face, gid, FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM, &adv); + fz_ft_unlock(ctx); + if (fterr && fterr != FT_Err_Invalid_Argument) + fz_warn(ctx, "FT_Get_Advance(%s,%d): %s", font->name, gid, ft_error_string(fterr)); + + realw = adv * 1000.0f / ((FT_Face)font->ft_face)->units_per_EM; + if (gid < font->width_count) + subw = font->width_table[gid]; + else + subw = font->width_default; + + /* Sanity check scaling in case of broken metrics. */ + if (realw > 0 && subw > 0) + *trm = fz_pre_scale(*trm, subw / realw, 1); + } + + return trm; +} + +static fz_glyph * +glyph_from_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap) +{ + (void)Memento_label(bitmap->buffer, "ft_bitmap"); + if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) + return fz_new_glyph_from_1bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch); + else + return fz_new_glyph_from_8bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch); +} + +static fz_pixmap * +pixmap_from_ft_bitmap(fz_context *ctx, int left, int top, FT_Bitmap *bitmap) +{ + (void)Memento_label(bitmap->buffer, "ft_bitmap"); + if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) + return fz_new_pixmap_from_1bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch); + else + return fz_new_pixmap_from_8bpp_data(ctx, left, top - bitmap->rows, bitmap->width, bitmap->rows, bitmap->buffer + (bitmap->rows-1)*bitmap->pitch, -bitmap->pitch); +} + +/* Takes the freetype lock, and returns with it held */ +static FT_GlyphSlot +do_ft_render_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, int aa) +{ + FT_Face face = font->ft_face; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + + float strength = fz_matrix_expansion(trm) * 0.02f; + + fz_adjust_ft_glyph_width(ctx, font, gid, &trm); + + if (font->flags.fake_italic) + trm = fz_pre_shear(trm, SHEAR, 0); + + fz_ft_lock(ctx); + + if (aa == 0) + { + /* enable grid fitting for non-antialiased rendering */ + float scale = fz_matrix_expansion(trm); + m.xx = trm.a * 65536 / scale; + m.yx = trm.b * 65536 / scale; + m.xy = trm.c * 65536 / scale; + m.yy = trm.d * 65536 / scale; + v.x = 0; + v.y = 0; + + fterr = FT_Set_Char_Size(face, 64 * scale, 64 * scale, 72, 72); + if (fterr) + fz_warn(ctx, "FT_Set_Char_Size(%s,%d,72): %s", font->name, (int)(64*scale), ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_TARGET_MONO); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_TARGET_MONO): %s", font->name, gid, ft_error_string(fterr)); + goto retry_unhinted; + } + } + else + { +retry_unhinted: + /* + * Freetype mutilates complex glyphs if they are loaded with + * FT_Set_Char_Size 1.0. It rounds the coordinates before applying + * transformation. To get more precision in freetype, we shift part of + * the scale in the matrix into FT_Set_Char_Size instead. + */ + + /* Check for overflow; FreeType matrices use 16.16 fixed-point numbers */ + if (trm.a < -512 || trm.a > 512) return NULL; + if (trm.b < -512 || trm.b > 512) return NULL; + if (trm.c < -512 || trm.c > 512) return NULL; + if (trm.d < -512 || trm.d > 512) return NULL; + + m.xx = trm.a * 64; /* should be 65536 */ + m.yx = trm.b * 64; + m.xy = trm.c * 64; + m.yy = trm.d * 64; + v.x = trm.e * 64; + v.y = trm.f * 64; + + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + fz_warn(ctx, "FT_Set_Char_Size(%s,65536,72): %s", font->name, ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_NO_HINTING): %s", font->name, gid, ft_error_string(fterr)); + return NULL; + } + } + + if (font->flags.fake_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * 64); + FT_Outline_Translate(&face->glyph->outline, -strength * 32, -strength * 32); + } + + fterr = FT_Render_Glyph(face->glyph, aa > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO); + if (fterr) + { + if (aa > 0) + fz_warn(ctx, "FT_Render_Glyph(%s,%d,FT_RENDER_MODE_NORMAL): %s", font->name, gid, ft_error_string(fterr)); + else + fz_warn(ctx, "FT_Render_Glyph(%s,%d,FT_RENDER_MODE_MONO): %s", font->name, gid, ft_error_string(fterr)); + return NULL; + } + return face->glyph; +} + +fz_pixmap * +fz_render_ft_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, int aa) +{ + FT_GlyphSlot slot = do_ft_render_glyph(ctx, font, gid, trm, aa); + fz_pixmap *pixmap = NULL; + + if (slot == NULL) + { + fz_ft_unlock(ctx); + return NULL; + } + + fz_try(ctx) + { + pixmap = pixmap_from_ft_bitmap(ctx, slot->bitmap_left, slot->bitmap_top, &slot->bitmap); + } + fz_always(ctx) + { + fz_ft_unlock(ctx); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return pixmap; +} + +/* The glyph cache lock is always taken when this is called. */ +fz_glyph * +fz_render_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, int aa) +{ + FT_GlyphSlot slot = do_ft_render_glyph(ctx, font, gid, trm, aa); + fz_glyph *glyph = NULL; + + if (slot == NULL) + { + fz_ft_unlock(ctx); + return NULL; + } + + fz_try(ctx) + { + glyph = glyph_from_ft_bitmap(ctx, slot->bitmap_left, slot->bitmap_top, &slot->bitmap); + } + fz_always(ctx) + { + fz_ft_unlock(ctx); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return glyph; +} + +/* Takes the freetype lock, and returns with it held */ +static FT_Glyph +do_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, const fz_stroke_state *state, int aa) +{ + FT_Face face = font->ft_face; + float expansion = fz_matrix_expansion(ctm); + int linewidth = state->linewidth * expansion * 64 / 2; + FT_Matrix m; + FT_Vector v; + FT_Error fterr; + FT_Stroker stroker; + FT_Glyph glyph; + FT_Stroker_LineJoin line_join; + FT_Stroker_LineCap line_cap; + + fz_adjust_ft_glyph_width(ctx, font, gid, &trm); + + if (font->flags.fake_italic) + trm = fz_pre_shear(trm, SHEAR, 0); + + m.xx = trm.a * 64; /* should be 65536 */ + m.yx = trm.b * 64; + m.xy = trm.c * 64; + m.yy = trm.d * 64; + v.x = trm.e * 64; + v.y = trm.f * 64; + + fz_ft_lock(ctx); + fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ + if (fterr) + { + fz_warn(ctx, "FT_Set_Char_Size(%s,65536,72): %s", font->name, ft_error_string(fterr)); + return NULL; + } + + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_NO_HINTING): %s", font->name, gid, ft_error_string(fterr)); + return NULL; + } + + fterr = FT_Stroker_New(ctx->font->ftlib, &stroker); + if (fterr) + { + fz_warn(ctx, "FT_Stroker_New(): %s", ft_error_string(fterr)); + return NULL; + } + + line_join = + state->linejoin == FZ_LINEJOIN_MITER ? FT_STROKER_LINEJOIN_MITER_FIXED : + state->linejoin == FZ_LINEJOIN_ROUND ? FT_STROKER_LINEJOIN_ROUND : + state->linejoin == FZ_LINEJOIN_BEVEL ? FT_STROKER_LINEJOIN_BEVEL : + FT_STROKER_LINEJOIN_MITER_VARIABLE; + line_cap = + state->start_cap == FZ_LINECAP_BUTT ? FT_STROKER_LINECAP_BUTT : + state->start_cap == FZ_LINECAP_ROUND ? FT_STROKER_LINECAP_ROUND : + state->start_cap == FZ_LINECAP_SQUARE ? FT_STROKER_LINECAP_SQUARE : + state->start_cap == FZ_LINECAP_TRIANGLE ? FT_STROKER_LINECAP_BUTT : + FT_STROKER_LINECAP_BUTT; + + FT_Stroker_Set(stroker, linewidth, line_cap, line_join, state->miterlimit * 65536); + + fterr = FT_Get_Glyph(face->glyph, &glyph); + if (fterr) + { + fz_warn(ctx, "FT_Get_Glyph(): %s", ft_error_string(fterr)); + FT_Stroker_Done(stroker); + return NULL; + } + + fterr = FT_Glyph_Stroke(&glyph, stroker, 1); + if (fterr) + { + fz_warn(ctx, "FT_Glyph_Stroke(): %s", ft_error_string(fterr)); + FT_Done_Glyph(glyph); + FT_Stroker_Done(stroker); + return NULL; + } + + FT_Stroker_Done(stroker); + + fterr = FT_Glyph_To_Bitmap(&glyph, aa > 0 ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); + if (fterr) + { + fz_warn(ctx, "FT_Glyph_To_Bitmap(): %s", ft_error_string(fterr)); + FT_Done_Glyph(glyph); + return NULL; + } + return glyph; +} + +fz_glyph * +fz_render_ft_stroked_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, const fz_stroke_state *state, int aa) +{ + FT_Glyph glyph = do_render_ft_stroked_glyph(ctx, font, gid, trm, ctm, state, aa); + FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph; + fz_glyph *result = NULL; + + if (bitmap == NULL) + { + fz_ft_unlock(ctx); + return NULL; + } + + fz_try(ctx) + { + result = glyph_from_ft_bitmap(ctx, bitmap->left, bitmap->top, &bitmap->bitmap); + } + fz_always(ctx) + { + FT_Done_Glyph(glyph); + fz_ft_unlock(ctx); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + return result; +} + +static fz_rect * +get_gid_bbox(fz_context *ctx, fz_font *font, int gid) +{ + int i; + + if (gid < 0 || gid >= font->glyph_count || !font->use_glyph_bbox) + return NULL; + + if (font->bbox_table == NULL) { + i = (font->glyph_count + 255)/256; + font->bbox_table = Memento_label(fz_malloc_array(ctx, i, fz_rect *), "bbox_table(top)"); + memset(font->bbox_table, 0, sizeof(fz_rect *) * i); + } + + if (font->bbox_table[gid>>8] == NULL) { + font->bbox_table[gid>>8] = Memento_label(fz_malloc_array(ctx, 256, fz_rect), "bbox_table"); + for (i = 0; i < 256; i++) { + font->bbox_table[gid>>8][i] = fz_empty_rect; + } + } + + return &font->bbox_table[gid>>8][gid & 255]; +} + +static fz_rect * +fz_bound_ft_glyph(fz_context *ctx, fz_font *font, int gid) +{ + FT_Face face = font->ft_face; + FT_Error fterr; + FT_BBox cbox; + FT_Matrix m; + FT_Vector v; + fz_rect *bounds = get_gid_bbox(ctx, font, gid); + + // TODO: refactor loading into fz_load_ft_glyph + // TODO: cache results + + const int scale = face->units_per_EM; + const float recip = 1.0f / scale; + const float strength = 0.02f; + fz_matrix trm = fz_identity; + + fz_adjust_ft_glyph_width(ctx, font, gid, &trm); + + if (font->flags.fake_italic) + trm = fz_pre_shear(trm, SHEAR, 0); + + m.xx = trm.a * 65536; + m.yx = trm.b * 65536; + m.xy = trm.c * 65536; + m.yy = trm.d * 65536; + v.x = trm.e * 65536; + v.y = trm.f * 65536; + + fz_ft_lock(ctx); + /* Set the char size to scale=face->units_per_EM to effectively give + * us unscaled results. This avoids quantisation. We then apply the + * scale ourselves below. */ + fterr = FT_Set_Char_Size(face, scale, scale, 72, 72); + if (fterr) + fz_warn(ctx, "FT_Set_Char_Size(%s,%d,72): %s", font->name, scale, ft_error_string(fterr)); + FT_Set_Transform(face, &m, &v); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_NO_HINTING): %s", font->name, gid, ft_error_string(fterr)); + fz_ft_unlock(ctx); + bounds->x0 = bounds->x1 = trm.e; + bounds->y0 = bounds->y1 = trm.f; + return bounds; + } + + if (font->flags.fake_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * scale); + FT_Outline_Translate(&face->glyph->outline, -strength * 0.5f * scale, -strength * 0.5f * scale); + } + + FT_Outline_Get_CBox(&face->glyph->outline, &cbox); + fz_ft_unlock(ctx); + bounds->x0 = cbox.xMin * recip; + bounds->y0 = cbox.yMin * recip; + bounds->x1 = cbox.xMax * recip; + bounds->y1 = cbox.yMax * recip; + + if (fz_is_empty_rect(*bounds)) + { + bounds->x0 = bounds->x1 = trm.e; + bounds->y0 = bounds->y1 = trm.f; + } + + return bounds; +} + +/* Turn FT_Outline into a fz_path */ + +struct closure { + fz_context *ctx; + fz_path *path; + fz_matrix trm; +}; + +static int move_to(const FT_Vector *p, void *cc_) +{ + struct closure *cc = (struct closure *)cc_; + fz_context *ctx = cc->ctx; + fz_path *path = cc->path; + fz_point pt; + + pt = fz_transform_point_xy(p->x, p->y, cc->trm); + fz_moveto(ctx, path, pt.x, pt.y); + return 0; +} + +static int line_to(const FT_Vector *p, void *cc_) +{ + struct closure *cc = (struct closure *)cc_; + fz_context *ctx = cc->ctx; + fz_path *path = cc->path; + fz_point pt; + + pt = fz_transform_point_xy(p->x, p->y, cc->trm); + fz_lineto(ctx, path, pt.x, pt.y); + return 0; +} + +static int conic_to(const FT_Vector *c, const FT_Vector *p, void *cc_) +{ + struct closure *cc = (struct closure *)cc_; + fz_context *ctx = cc->ctx; + fz_path *path = cc->path; + fz_point ct, pt; + + ct = fz_transform_point_xy(c->x, c->y, cc->trm); + pt = fz_transform_point_xy(p->x, p->y, cc->trm); + + fz_quadto(ctx, path, ct.x, ct.y, pt.x, pt.y); + return 0; +} + +static int cubic_to(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *p, void *cc_) +{ + struct closure *cc = (struct closure *)cc_; + fz_context *ctx = cc->ctx; + fz_path *path = cc->path; + fz_point c1t, c2t, pt; + + c1t = fz_transform_point_xy(c1->x, c1->y, cc->trm); + c2t = fz_transform_point_xy(c2->x, c2->y, cc->trm); + pt = fz_transform_point_xy(p->x, p->y, cc->trm); + + fz_curveto(ctx, path, c1t.x, c1t.y, c2t.x, c2t.y, pt.x, pt.y); + return 0; +} + +static const FT_Outline_Funcs outline_funcs = { + move_to, line_to, conic_to, cubic_to, 0, 0 +}; + +fz_path * +fz_outline_ft_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm) +{ + struct closure cc; + FT_Face face = font->ft_face; + int fterr; + + const int scale = 65536; + const float recip = 1.0f / scale; + const float strength = 0.02f; + + fz_adjust_ft_glyph_width(ctx, font, gid, &trm); + + if (font->flags.fake_italic) + trm = fz_pre_shear(trm, SHEAR, 0); + + fz_ft_lock(ctx); + + fterr = FT_Set_Char_Size(face, scale, scale, 72, 72); + if (fterr) + fz_warn(ctx, "FT_Set_Char_Size(%s,%d,72): %s", font->name, scale, ft_error_string(fterr)); + + fterr = FT_Load_Glyph(face, gid, FT_LOAD_IGNORE_TRANSFORM); + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_IGNORE_TRANSFORM): %s", font->name, gid, ft_error_string(fterr)); + fterr = FT_Load_Glyph(face, gid, FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING); + } + if (fterr) + { + fz_warn(ctx, "FT_Load_Glyph(%s,%d,FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING): %s", font->name, gid, ft_error_string(fterr)); + fz_ft_unlock(ctx); + return NULL; + } + + if (font->flags.fake_bold) + { + FT_Outline_Embolden(&face->glyph->outline, strength * scale); + FT_Outline_Translate(&face->glyph->outline, -strength * 0.5f * scale, -strength * 0.5f * scale); + } + + cc.path = NULL; + fz_try(ctx) + { + cc.ctx = ctx; + cc.path = fz_new_path(ctx); + cc.trm = fz_concat(fz_scale(recip, recip), trm); + fz_moveto(ctx, cc.path, cc.trm.e, cc.trm.f); + FT_Outline_Decompose(&face->glyph->outline, &outline_funcs, &cc); + fz_closepath(ctx, cc.path); + } + fz_always(ctx) + { + fz_ft_unlock(ctx); + } + fz_catch(ctx) + { + fz_warn(ctx, "freetype cannot decompose outline"); + fz_drop_path(ctx, cc.path); + return NULL; + } + + return cc.path; +} + +/* + Type 3 fonts... + */ + +fz_font * +fz_new_type3_font(fz_context *ctx, const char *name, fz_matrix matrix) +{ + fz_font *font; + + font = fz_new_font(ctx, name, 1, 256); + fz_try(ctx) + { + font->t3procs = fz_calloc(ctx, 256, sizeof(fz_buffer*)); + font->t3lists = fz_calloc(ctx, 256, sizeof(fz_display_list*)); + font->t3widths = fz_calloc(ctx, 256, sizeof(float)); + font->t3flags = fz_calloc(ctx, 256, sizeof(unsigned short)); + } + fz_catch(ctx) + { + fz_drop_font(ctx, font); + fz_rethrow(ctx); + } + + font->t3matrix = matrix; + + return font; +} + +static void +fz_bound_t3_glyph(fz_context *ctx, fz_font *font, int gid) +{ + fz_display_list *list; + fz_device *dev; + fz_rect *r = get_gid_bbox(ctx, font, gid); + + list = font->t3lists[gid]; + if (!list) + { + *r = fz_empty_rect; + return; + } + + dev = fz_new_bbox_device(ctx, r); + fz_try(ctx) + { + fz_run_display_list(ctx, list, dev, font->t3matrix, fz_infinite_rect, NULL); + fz_close_device(ctx, dev); + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + + /* Update font bbox with glyph's computed bbox if the font bbox is invalid */ + if (font->flags.invalid_bbox) + font->bbox = fz_union_rect(font->bbox, *r); +} + +void +fz_prepare_t3_glyph(fz_context *ctx, fz_font *font, int gid) +{ + fz_device *dev; + fz_rect d1_rect; + + /* We've not already loaded this one! */ + assert(font->t3lists[gid] == NULL); + + font->t3lists[gid] = fz_new_display_list(ctx, font->bbox); + + dev = fz_new_list_device(ctx, font->t3lists[gid]); + dev->flags = FZ_DEVFLAG_FILLCOLOR_UNDEFINED | + FZ_DEVFLAG_STROKECOLOR_UNDEFINED | + FZ_DEVFLAG_STARTCAP_UNDEFINED | + FZ_DEVFLAG_DASHCAP_UNDEFINED | + FZ_DEVFLAG_ENDCAP_UNDEFINED | + FZ_DEVFLAG_LINEJOIN_UNDEFINED | + FZ_DEVFLAG_MITERLIMIT_UNDEFINED | + FZ_DEVFLAG_LINEWIDTH_UNDEFINED | + FZ_DEVFLAG_DASH_PATTERN_UNDEFINED; + + fz_try(ctx) + { + font->t3run(ctx, font->t3doc, font->t3resources, font->t3procs[gid], dev, fz_identity, NULL, NULL, NULL, NULL); + fz_close_device(ctx, dev); + font->t3flags[gid] = dev->flags; + d1_rect = dev->d1_rect; + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + } + fz_catch(ctx) + fz_rethrow(ctx); + if (fz_display_list_is_empty(ctx, font->t3lists[gid])) + { + fz_rect *r = get_gid_bbox(ctx, font, gid); + /* If empty, no need for a huge bbox, especially as the logic + * in the 'else if' can make it huge. */ + r->x0 = font->flags.invalid_bbox ? 0 : font->bbox.x0; + r->y0 = font->flags.invalid_bbox ? 0 : font->bbox.y0; + r->x1 = r->x0 + .00001f; + r->y1 = r->y0 + .00001f; + } + else if (font->t3flags[gid] & FZ_DEVFLAG_BBOX_DEFINED) + { + fz_rect *r = get_gid_bbox(ctx, font, gid); + *r = fz_transform_rect(d1_rect, font->t3matrix); + + if (font->flags.invalid_bbox || !fz_contains_rect(font->bbox, d1_rect)) + { + /* Either the font bbox is invalid, or the d1_rect returned is + * incompatible with it. Either way, don't trust the d1 rect + * and calculate it from the contents. */ + fz_bound_t3_glyph(ctx, font, gid); + } + } + else + { + /* No bbox has been defined for this glyph, so compute it. */ + fz_bound_t3_glyph(ctx, font, gid); + } +} + +void +fz_run_t3_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_device *dev) +{ + fz_display_list *list; + fz_matrix ctm; + + list = font->t3lists[gid]; + if (!list) + return; + + ctm = fz_concat(font->t3matrix, trm); + fz_run_display_list(ctx, list, dev, ctm, fz_infinite_rect, NULL); +} + +fz_pixmap * +fz_render_t3_glyph_pixmap(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_colorspace *model, const fz_irect *scissor, int aa) +{ + fz_display_list *list; + fz_rect bounds; + fz_irect bbox; + fz_device *dev = NULL; + fz_pixmap *glyph; + fz_pixmap *result = NULL; + + if (gid < 0 || gid > 255) + return NULL; + + list = font->t3lists[gid]; + if (!list) + return NULL; + + if (font->t3flags[gid] & FZ_DEVFLAG_MASK) + { + if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + fz_warn(ctx, "type3 glyph claims to be both masked and colored"); + model = NULL; + } + else if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + { + if (!model) + fz_warn(ctx, "colored type3 glyph wanted in masked context"); + } + else + { + fz_warn(ctx, "type3 glyph doesn't specify masked or colored"); + model = NULL; /* Treat as masked */ + } + + bounds = fz_expand_rect(fz_bound_glyph(ctx, font, gid, trm), 1); + bbox = fz_irect_from_rect(bounds); + bbox = fz_intersect_irect(bbox, *scissor); + + /* Glyphs must always have alpha */ + glyph = fz_new_pixmap_with_bbox(ctx, model, bbox, NULL/* FIXME */, 1); + + fz_var(dev); + fz_try(ctx) + { + fz_clear_pixmap(ctx, glyph); + dev = fz_new_draw_device_type3(ctx, fz_identity, glyph); + fz_run_t3_glyph(ctx, font, gid, trm, dev); + fz_close_device(ctx, dev); + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + } + fz_catch(ctx) + { + fz_drop_pixmap(ctx, glyph); + fz_rethrow(ctx); + } + + if (!model) + { + fz_try(ctx) + { + result = fz_alpha_from_gray(ctx, glyph); + } + fz_always(ctx) + { + fz_drop_pixmap(ctx, glyph); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } + } + else + result = glyph; + + return result; +} + +fz_glyph * +fz_render_t3_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm, fz_colorspace *model, const fz_irect *scissor, int aa) +{ + fz_pixmap *pixmap = fz_render_t3_glyph_pixmap(ctx, font, gid, trm, model, scissor, aa); + return fz_new_glyph_from_pixmap(ctx, pixmap); +} + +void +fz_render_t3_glyph_direct(fz_context *ctx, fz_device *dev, fz_font *font, int gid, fz_matrix trm, void *gstate, fz_default_colorspaces *def_cs, void *fill_gstate, void *stroke_gstate) +{ + fz_matrix ctm; + + if (gid < 0 || gid > 255) + return; + + if (font->t3flags[gid] & FZ_DEVFLAG_MASK) + { + if (font->t3flags[gid] & FZ_DEVFLAG_COLOR) + fz_warn(ctx, "type3 glyph claims to be both masked and colored"); + } + else if (!(font->t3flags[gid] & FZ_DEVFLAG_COLOR)) + { + fz_warn(ctx, "type3 glyph doesn't specify masked or colored"); + } + + ctm = fz_concat(font->t3matrix, trm); + font->t3run(ctx, font->t3doc, font->t3resources, font->t3procs[gid], dev, ctm, gstate, def_cs, fill_gstate, stroke_gstate); +} + +fz_rect +fz_bound_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix trm) +{ + fz_rect rect; + fz_rect *r = get_gid_bbox(ctx, font, gid); + if (r) + { + /* If the bbox is infinite or empty, distrust it */ + if (fz_is_infinite_rect(*r) || fz_is_empty_rect(*r)) + { + /* Get the real size from the glyph */ + if (font->ft_face) + fz_bound_ft_glyph(ctx, font, gid); + else if (font->t3lists) + fz_bound_t3_glyph(ctx, font, gid); + else + /* If we can't get a real size, fall back to the font + * bbox. */ + *r = font->bbox; + /* If the real size came back as empty, then store it as + * a very small rectangle to avoid us calling this same + * check every time. */ + if (fz_is_empty_rect(*r)) + { + r->x0 = 0; + r->y0 = 0; + r->x1 = 0.0000001f; + r->y1 = 0.0000001f; + } + } + rect = *r; + } + else + { + /* fall back to font bbox */ + rect = font->bbox; + } + return fz_transform_rect(rect, trm); +} + +fz_path * +fz_outline_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix ctm) +{ + if (!font->ft_face) + return NULL; + return fz_outline_ft_glyph(ctx, font, gid, ctm); +} + +int fz_glyph_cacheable(fz_context *ctx, fz_font *font, int gid) +{ + if (!font->t3procs || !font->t3flags || gid < 0 || gid >= font->glyph_count) + return 1; + return (font->t3flags[gid] & FZ_DEVFLAG_UNCACHEABLE) == 0; +} + +static float +fz_advance_ft_glyph_aux(fz_context *ctx, fz_font *font, int gid, int wmode, int locked) +{ + FT_Error fterr; + FT_Fixed adv = 0; + int mask; + + if (gid < 0) + { + fz_warn(ctx, "FT_Get_Advance(%s,%d): %s", font->name, gid, ft_error_string(FT_Err_Invalid_Argument)); + return font->width_default / 1000.0f; + } + + /* PDF and substitute font widths. */ + if (font->flags.ft_stretch) + { + if (font->width_table) + { + if (gid < font->width_count) + return font->width_table[gid] / 1000.0f; + return font->width_default / 1000.0f; + } + } + + mask = FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; + if (wmode) + mask |= FT_LOAD_VERTICAL_LAYOUT; + if (!locked) + fz_ft_lock(ctx); + fterr = FT_Get_Advance(font->ft_face, gid, mask, &adv); + if (!locked) + fz_ft_unlock(ctx); + if (fterr && fterr != FT_Err_Invalid_Argument) + { + fz_warn(ctx, "FT_Get_Advance(%s,%d): %s", font->name, gid, ft_error_string(fterr)); + if (font->width_table) + { + if (gid < font->width_count) + return font->width_table[gid] / 1000.0f; + return font->width_default / 1000.0f; + } + } + return (float) adv / ((FT_Face)font->ft_face)->units_per_EM; +} + +static float +fz_advance_ft_glyph(fz_context *ctx, fz_font *font, int gid, int wmode) +{ + return fz_advance_ft_glyph_aux(ctx, font, gid, wmode, 0); +} + +static float +fz_advance_t3_glyph(fz_context *ctx, fz_font *font, int gid) +{ + if (gid < 0 || gid > 255) + return 0; + return font->t3widths[gid]; +} + +void +fz_get_glyph_name(fz_context *ctx, fz_font *font, int glyph, char *buf, int size) +{ + FT_Face face = font->ft_face; + if (face) + { + if (FT_HAS_GLYPH_NAMES(face)) + { + int fterr; + fz_ft_lock(ctx); + fterr = FT_Get_Glyph_Name(face, glyph, buf, size); + fz_ft_unlock(ctx); + if (fterr) + fz_warn(ctx, "FT_Get_Glyph_Name(%s,%d): %s", font->name, glyph, ft_error_string(fterr)); + } + else + fz_snprintf(buf, size, "%d", glyph); + } + else + { + fz_snprintf(buf, size, "%d", glyph); + } +} + +float +fz_advance_glyph(fz_context *ctx, fz_font *font, int gid, int wmode) +{ + if (font->ft_face) + { + if (wmode) + return fz_advance_ft_glyph(ctx, font, gid, 1); + if (gid >= 0 && gid < font->glyph_count) + { + float f; + int block = gid>>8; + fz_ft_lock(ctx); + if (!font->advance_cache) + { + int n = (font->glyph_count+255)/256; + fz_try(ctx) + font->advance_cache = Memento_label(fz_malloc_array(ctx, n, float *), "font_advance_cache"); + fz_catch(ctx) + { + fz_ft_unlock(ctx); + fz_rethrow(ctx); + } + memset(font->advance_cache, 0, n * sizeof(float *)); + } + if (!font->advance_cache[block]) + { + int i, n; + fz_try(ctx) + font->advance_cache[block] = Memento_label(fz_malloc_array(ctx, 256, float), "font_advance_cache"); + fz_catch(ctx) + { + fz_ft_unlock(ctx); + fz_rethrow(ctx); + } + n = (block<<8)+256; + if (n > font->glyph_count) + n = font->glyph_count; + n -= (block<<8); + for (i = 0; i < n; ++i) + font->advance_cache[block][i] = fz_advance_ft_glyph_aux(ctx, font, (block<<8)+i, 0, 1); + } + f = font->advance_cache[block][gid & 255]; + fz_ft_unlock(ctx); + return f; + } + + return fz_advance_ft_glyph(ctx, font, gid, 0); + } + if (font->t3procs) + return fz_advance_t3_glyph(ctx, font, gid); + return 0; +} + +int +fz_encode_character(fz_context *ctx, fz_font *font, int ucs) +{ + if (font->ft_face) + { + int idx; + if (ucs >= 0 && ucs < 0x10000) + { + int pg = ucs >> 8; + int ix = ucs & 0xFF; + if (!font->encoding_cache[pg]) + { + int i; + font->encoding_cache[pg] = fz_malloc_array(ctx, 256, uint16_t); + fz_ft_lock(ctx); + for (i = 0; i < 256; ++i) + font->encoding_cache[pg][i] = FT_Get_Char_Index(font->ft_face, (pg << 8) + i); + fz_ft_unlock(ctx); + } + return font->encoding_cache[pg][ix]; + } + fz_ft_lock(ctx); + idx = FT_Get_Char_Index(font->ft_face, ucs); + fz_ft_unlock(ctx); + return idx; + } + return ucs; +} + +int +fz_encode_character_sc(fz_context *ctx, fz_font *font, int unicode) +{ + if (font->ft_face) + { + int cat = ucdn_get_general_category(unicode); + if (cat == UCDN_GENERAL_CATEGORY_LL || cat == UCDN_GENERAL_CATEGORY_LT) + { + int glyph; + const char *name; + char buf[20]; + + name = fz_glyph_name_from_unicode_sc(unicode); + if (name) + { + fz_ft_lock(ctx); + glyph = FT_Get_Name_Index(font->ft_face, (char*)name); + fz_ft_unlock(ctx); + if (glyph > 0) + return glyph; + } + + sprintf(buf, "uni%04X.sc", unicode); + fz_ft_lock(ctx); + glyph = FT_Get_Name_Index(font->ft_face, buf); + fz_ft_unlock(ctx); + if (glyph > 0) + return glyph; + } + } + return fz_encode_character(ctx, font, unicode); +} + +int +fz_encode_character_by_glyph_name(fz_context *ctx, fz_font *font, const char *glyphname) +{ + int glyph = 0; + if (font->ft_face) + { + fz_ft_lock(ctx); + glyph = ft_name_index(font->ft_face, glyphname); + if (glyph == 0) + glyph = ft_char_index(font->ft_face, fz_unicode_from_glyph_name(glyphname)); + fz_ft_unlock(ctx); + } + // TODO: type3 fonts (not needed for now) + return glyph; +} + +/* FIXME: This should take language too eventually, to allow for fonts where we can select different + * languages using opentype features. */ +int +fz_encode_character_with_fallback(fz_context *ctx, fz_font *user_font, int unicode, int script, int language, fz_font **out_font) +{ + fz_font *font; + int is_serif = user_font->flags.is_serif; + int is_italic = user_font->flags.is_italic | user_font->flags.fake_italic; + int is_bold = user_font->flags.is_bold | user_font->flags.fake_bold; + int gid; + + gid = fz_encode_character(ctx, user_font, unicode); + if (gid > 0) + return *out_font = user_font, gid; + + if (script == 0) + script = ucdn_get_script(unicode); + + /* Fix for ideographic/halfwidth/fullwidth punctuation forms. */ + if ((unicode >= 0x3000 && unicode <= 0x303F) || (unicode >= 0xFF00 && unicode <= 0xFFEF)) + { + if (script != UCDN_SCRIPT_HANGUL && + script != UCDN_SCRIPT_HIRAGANA && + script != UCDN_SCRIPT_KATAKANA && + script != UCDN_SCRIPT_BOPOMOFO) + script = UCDN_SCRIPT_HAN; + } + + font = fz_load_fallback_font(ctx, script, language, is_serif, is_bold, is_italic); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + +#ifndef TOFU_CJK_LANG + if (script == UCDN_SCRIPT_HAN) + { + font = fz_load_fallback_font(ctx, script, FZ_LANG_zh_Hant, is_serif, is_bold, is_italic); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + font = fz_load_fallback_font(ctx, script, FZ_LANG_ja, is_serif, is_bold, is_italic); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + font = fz_load_fallback_font(ctx, script, FZ_LANG_ko, is_serif, is_bold, is_italic); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + font = fz_load_fallback_font(ctx, script, FZ_LANG_zh_Hans, is_serif, is_bold, is_italic); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + } +#endif + + font = fz_load_fallback_math_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_load_fallback_music_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_load_fallback_symbol1_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_load_fallback_symbol2_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_load_fallback_emoji_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_load_fallback_boxes_font(ctx); + if (font) + { + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + font = fz_new_base14_font(ctx, "Symbol"); + if (font) + { + fz_drop_font(ctx, font); /* it's cached in the font context, return a borrowed pointer */ + gid = fz_encode_character(ctx, font, unicode); + if (gid > 0) + return *out_font = font, gid; + } + + return *out_font = user_font, 0; +} + +int fz_font_is_bold(fz_context *ctx, fz_font *font) +{ + return font ? font->flags.is_bold : 0; +} + +int fz_font_is_italic(fz_context *ctx, fz_font *font) +{ + return font ? font->flags.is_italic : 0; +} + +int fz_font_is_serif(fz_context *ctx, fz_font *font) +{ + return font ? font->flags.is_serif : 0; +} + +int fz_font_is_monospaced(fz_context *ctx, fz_font *font) +{ + return font ? font->flags.is_mono : 0; +} + +const char *fz_font_name(fz_context *ctx, fz_font *font) +{ + return font ? font->name : ""; +} + +fz_buffer **fz_font_t3_procs(fz_context *ctx, fz_font *font) +{ + return font ? font->t3procs : NULL; +} + +fz_rect fz_font_bbox(fz_context *ctx, fz_font *font) +{ + return font->bbox; +} + +void *fz_font_ft_face(fz_context *ctx, fz_font *font) +{ + return font ? font->ft_face : NULL; +} + +fz_font_flags_t *fz_font_flags(fz_font *font) +{ + return font ? &font->flags : NULL; +} + +fz_shaper_data_t *fz_font_shaper_data(fz_context *ctx, fz_font *font) +{ + return font ? &font->shaper_data : NULL; +} + +void fz_font_digest(fz_context *ctx, fz_font *font, unsigned char digest[16]) +{ + if (!font->buffer) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "no font file for digest"); + if (!font->has_digest) + { + fz_md5_buffer(ctx, font->buffer, font->digest); + font->has_digest = 1; + } + memcpy(digest, font->digest, 16); +} + +#define CHR(a,b,c,d) ((a<<24) | (b<<16) | (c<<8) | d) + +typedef struct +{ + uint32_t offset; + uint32_t length; +} ttc_block_details_t; + +/* The operation of the following is largely based on the operation of + * https://github.com/fontist/extract_ttc/blob/main/ext/stripttc/stripttc.c + * released under a BSD 3-clause license. + */ +fz_buffer * +fz_extract_ttf_from_ttc(fz_context *ctx, fz_font *font) +{ + fz_stream *stream; + uint32_t tmp; + int i, count; + fz_buffer *buf = NULL; + fz_output *out = NULL; + ttc_block_details_t *bd = NULL; + uint32_t start_pos; + uint32_t csumpos = 0; + + if (!font || !font->buffer) + fz_throw(ctx, FZ_ERROR_ARGUMENT, "missing input"); + + stream = fz_open_buffer(ctx, font->buffer); + + fz_var(buf); + fz_var(out); + fz_var(bd); + + fz_try(ctx) + { + /* Signature */ + if (fz_read_uint32(ctx, stream) != CHR('t','t','c','f')) + fz_throw(ctx, FZ_ERROR_FORMAT, "Not a ttc"); + + /* Version */ + tmp = fz_read_uint32(ctx, stream); + if (tmp != 0x10000 && tmp != 0x20000) + fz_throw(ctx, FZ_ERROR_FORMAT, "Unsupported TTC version"); + + /* How many subfonts are there? */ + tmp = fz_read_uint32(ctx, stream); + if ((uint32_t)font->subfont >= tmp || font->subfont < 0) + fz_throw(ctx, FZ_ERROR_FORMAT, "Bad subfont in TTC"); + + /* Read through the index table until we get the one for our subfont. */ + for (i = 0; i <= font->subfont; i++) + tmp = fz_read_uint32(ctx, stream); + + fz_seek(ctx, stream, tmp, SEEK_SET); + buf = fz_new_buffer(ctx, 1); + out = fz_new_output_with_buffer(ctx, buf); + + fz_write_uint32_be(ctx, out, fz_read_uint32(ctx, stream)); /* sfnt version */ + fz_write_uint16_be(ctx, out, count = fz_read_uint16(ctx, stream)); /* table count */ + fz_write_uint16_be(ctx, out, fz_read_uint16(ctx, stream)); /* bsearch header */ + fz_write_uint16_be(ctx, out, fz_read_uint16(ctx, stream)); + fz_write_uint16_be(ctx, out, fz_read_uint16(ctx, stream)); + + /* We are currently here... */ + start_pos = 4+2+2+2+2; + /* And after we've written the header, we will be here. */ + start_pos += count*4*4; + bd = fz_malloc_array(ctx, count, ttc_block_details_t); + for (i = 0; i < count; i++) + { + uint32_t tag; + + fz_write_uint32_be(ctx, out, tag = fz_read_uint32(ctx, stream)); + fz_write_uint32_be(ctx, out, fz_read_uint32(ctx, stream)); /* checksum */ + bd[i].offset = fz_read_uint32(ctx, stream); + fz_write_uint32_be(ctx, out, start_pos); + if (tag == CHR('h','e','a','d')) + csumpos = start_pos + 8; + fz_write_uint32_be(ctx, out, bd[i].length = fz_read_uint32(ctx, stream)); + start_pos += (bd[i].length + 3) & ~3; + } + + for (i = 0; i < count; i++) + { + uint32_t j; + + fz_seek(ctx, stream, bd[i].offset, SEEK_SET); + for (j = 0; j < bd[i].length; j++) + fz_write_byte(ctx, out, fz_read_byte(ctx, stream)); + if (bd[i].length & 1) + { + fz_write_byte(ctx, out, 0); + bd[i].length++; + } + if (bd[i].length & 2) + fz_write_uint16_be(ctx, out, 0); + } + + fz_close_output(ctx, out); + } + fz_always(ctx) + { + fz_free(ctx, bd); + fz_drop_output(ctx, out); + fz_drop_stream(ctx, stream); + } + fz_catch(ctx) + { + fz_drop_buffer(ctx, buf); + fz_rethrow(ctx); + } + + /* Now fixup the checksum */ + if (csumpos) + { + unsigned char *data; + uint32_t sum = 0; + uint32_t j; + size_t len = fz_buffer_storage(ctx, buf, &data); + + /* First off, blat the old checksum */ + memset(data+csumpos, 0, 4); + + /* Calculate the new sum. */ + for (j = 0; j < len; j += 4) + { + uint32_t v = (data[j]<<24) | (data[j+1]<<16) | (data[j+2]<<8) | (data[j+3]); + sum += v; + } + sum = 0xb1b0afba-sum; + + /* Insert it. */ + data[csumpos] = sum>>24; + data[csumpos+1] = sum>>16; + data[csumpos+2] = sum>>8; + data[csumpos+3] = sum; + } + + return buf; +} + +void fz_enumerate_font_cmap(fz_context *ctx, fz_font *font, fz_cmap_callback *cb, void *opaque) +{ + unsigned long ucs; + unsigned int gid; + + if (font == NULL || font->ft_face == NULL) + return; + + fz_ft_lock(ctx); + for (ucs = FT_Get_First_Char(font->ft_face, &gid); gid > 0; ucs = FT_Get_Next_Char(font->ft_face, ucs, &gid)) + { + fz_ft_unlock(ctx); + cb(ctx, opaque, ucs, gid); + fz_ft_lock(ctx); + } + fz_ft_unlock(ctx); +} + +void fz_calculate_font_ascender_descender(fz_context *ctx, fz_font *font) +{ + int i, n; + fz_rect bounds = fz_empty_rect; + fz_matrix trm = { 1, 0, 0, 1, 0, 0 }; + + if (font == NULL) + return; + + if (font->ascdesc_src == FZ_ASCDESC_FROM_BOUNDS) + return; + + n = font->glyph_count; + for (i = 0; i < n; i++) + { + bounds = fz_union_rect(bounds, fz_bound_glyph(ctx, font, i, trm)); + } + + if (bounds.y1 > font->ascender) + font->ascender = bounds.y1; + if (bounds.y0 < font->descender) + font->descender = bounds.y0; + font->ascdesc_src = FZ_ASCDESC_FROM_BOUNDS; +}
