void fz_close(fz_stream *stm) { if (!stm) return; stm->refs --; if (stm->refs == 0) { if (stm->close) stm->close(stm->ctx, stm->state); if (stm->locked) fz_unlock(stm->ctx, FZ_LOCK_FILE); fz_free(stm->ctx, stm); } }
static void fz_keep_freetype(fz_context *ctx) { int fterr; int maj, min, pat; fz_font_context *fct = ctx->font; fz_lock(ctx, FZ_LOCK_FREETYPE); if (fct->ftlib) { fct->ftlib_refs++; fz_unlock(ctx, FZ_LOCK_FREETYPE); return; } fterr = FT_Init_FreeType(&fct->ftlib); if (fterr) { char *mess = ft_error_string(fterr); fz_unlock(ctx, FZ_LOCK_FREETYPE); fz_throw(ctx, "cannot init freetype: %s", mess); } FT_Library_Version(fct->ftlib, &maj, &min, &pat); if (maj == 2 && min == 1 && pat < 7) { fterr = FT_Done_FreeType(fct->ftlib); if (fterr) fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr)); fz_unlock(ctx, FZ_LOCK_FREETYPE); fz_throw(ctx, "freetype version too old: %d.%d.%d", maj, min, pat); } fct->ftlib_refs++; fz_unlock(ctx, FZ_LOCK_FREETYPE); }
void xps_measure_font_glyph(fz_context *ctx, xps_document *doc, fz_font *font, int gid, xps_glyph_metrics *mtx) { int mask = FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; FT_Face face = font->ft_face; FT_Fixed hadv = 0, vadv = 0; fz_lock(ctx, FZ_LOCK_FREETYPE); FT_Get_Advance(face, gid, mask, &hadv); FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv); fz_unlock(ctx, FZ_LOCK_FREETYPE); mtx->hadv = hadv / (float)face->units_per_EM; mtx->vadv = vadv / (float)face->units_per_EM; mtx->vorg = face->ascender / (float) face->units_per_EM; }
void fz_drop_glyph_cache_context(fz_context *ctx) { if (!ctx->glyph_cache) return; fz_lock(ctx, FZ_LOCK_GLYPHCACHE); ctx->glyph_cache->refs--; if (ctx->glyph_cache->refs == 0) { do_purge(ctx); fz_free(ctx, ctx->glyph_cache); ctx->glyph_cache = NULL; } fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); }
static void fz_drop_freetype(fz_context *ctx) { int fterr; fz_font_context *fct = ctx->font; fz_lock(ctx, FZ_LOCK_FREETYPE); if (--fct->ftlib_refs == 0) { fterr = FT_Done_FreeType(fct->ftlib); if (fterr) fz_warn(ctx, "freetype finalizing: %s", ft_error_string(fterr)); fct->ftlib = NULL; } fz_unlock(ctx, FZ_LOCK_FREETYPE); }
static void fz_drop_image_key(fz_context *ctx, void *key_) { fz_image_key *key = (fz_image_key *)key_; int drop; if (key == NULL) return; fz_lock(ctx, FZ_LOCK_ALLOC); drop = --key->refs; fz_unlock(ctx, FZ_LOCK_ALLOC); if (drop == 0) { fz_drop_image(ctx, key->image); fz_free(ctx, key); } }
static pdf_font_desc * pdf_load_simple_font(pdf_document *xref, pdf_obj *dict) { pdf_obj *descriptor; pdf_obj *encoding; pdf_obj *widths; unsigned short *etable = NULL; pdf_font_desc *fontdesc = NULL; char *subtype; FT_Face face; FT_CharMap cmap; int symbolic; int kind; char *basefont; char *estrings[256]; char ebuffer[256][32]; int i, k, n; int fterr; fz_context *ctx = xref->ctx; fz_var(fontdesc); fz_var(etable); basefont = pdf_to_name(pdf_dict_gets(dict, "BaseFont")); /* Load font file */ fz_try(ctx) { fontdesc = pdf_new_font_desc(ctx); descriptor = pdf_dict_gets(dict, "FontDescriptor"); if (descriptor) pdf_load_font_descriptor(fontdesc, xref, descriptor, NULL, basefont); else pdf_load_builtin_font(ctx, fontdesc, basefont); /* Some chinese documents mistakenly consider WinAnsiEncoding to be codepage 936 */ if (descriptor && pdf_is_string(pdf_dict_gets(descriptor, "FontName")) && !pdf_dict_gets(dict, "ToUnicode") && !strcmp(pdf_to_name(pdf_dict_gets(dict, "Encoding")), "WinAnsiEncoding") && pdf_to_int(pdf_dict_gets(descriptor, "Flags")) == 4) { char *cp936fonts[] = { "\xCB\xCE\xCC\xE5", "SimSun,Regular", "\xBA\xDA\xCC\xE5", "SimHei,Regular", "\xBF\xAC\xCC\xE5_GB2312", "SimKai,Regular", "\xB7\xC2\xCB\xCE_GB2312", "SimFang,Regular", "\xC1\xA5\xCA\xE9", "SimLi,Regular", NULL }; for (i = 0; cp936fonts[i]; i += 2) if (!strcmp(basefont, cp936fonts[i])) break; if (cp936fonts[i]) { fz_warn(ctx, "workaround for S22PDF lying about chinese font encodings"); pdf_drop_font(ctx, fontdesc); fontdesc = pdf_new_font_desc(ctx); pdf_load_font_descriptor(fontdesc, xref, descriptor, "Adobe-GB1", cp936fonts[i+1]); fontdesc->encoding = pdf_load_system_cmap(ctx, "GBK-EUC-H"); fontdesc->to_unicode = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2"); fontdesc->to_ttf_cmap = pdf_load_system_cmap(ctx, "Adobe-GB1-UCS2"); face = fontdesc->font->ft_face; kind = ft_kind(face); goto skip_encoding; } } face = fontdesc->font->ft_face; kind = ft_kind(face); /* Encoding */ symbolic = fontdesc->flags & 4; if (face->num_charmaps > 0) cmap = face->charmaps[0]; else cmap = NULL; for (i = 0; i < face->num_charmaps; i++) { FT_CharMap test = face->charmaps[i]; if (kind == TYPE1) { if (test->platform_id == 7) cmap = test; } if (kind == TRUETYPE) { if (test->platform_id == 1 && test->encoding_id == 0) cmap = test; if (test->platform_id == 3 && test->encoding_id == 1) cmap = test; if (symbolic && test->platform_id == 3 && test->encoding_id == 0) cmap = test; } } if (cmap) { fterr = FT_Set_Charmap(face, cmap); if (fterr) fz_warn(ctx, "freetype could not set cmap: %s", ft_error_string(fterr)); } else fz_warn(ctx, "freetype could not find any cmaps"); etable = fz_malloc_array(ctx, 256, sizeof(unsigned short)); fontdesc->size += 256 * sizeof(unsigned short); for (i = 0; i < 256; i++) { estrings[i] = NULL; etable[i] = 0; } encoding = pdf_dict_gets(dict, "Encoding"); if (encoding) { if (pdf_is_name(encoding)) pdf_load_encoding(estrings, pdf_to_name(encoding)); if (pdf_is_dict(encoding)) { pdf_obj *base, *diff, *item; base = pdf_dict_gets(encoding, "BaseEncoding"); if (pdf_is_name(base)) pdf_load_encoding(estrings, pdf_to_name(base)); else if (!fontdesc->is_embedded && !symbolic) pdf_load_encoding(estrings, "StandardEncoding"); diff = pdf_dict_gets(encoding, "Differences"); if (pdf_is_array(diff)) { n = pdf_array_len(diff); k = 0; for (i = 0; i < n; i++) { item = pdf_array_get(diff, i); if (pdf_is_int(item)) k = pdf_to_int(item); if (pdf_is_name(item) && k >= 0 && k < nelem(estrings)) estrings[k++] = pdf_to_name(item); } } } } /* start with the builtin encoding */ for (i = 0; i < 256; i++) etable[i] = ft_char_index(face, i); fz_lock(ctx, FZ_LOCK_FREETYPE); /* built-in and substitute fonts may be a different type than what the document expects */ subtype = pdf_to_name(pdf_dict_gets(dict, "Subtype")); if (!strcmp(subtype, "Type1")) kind = TYPE1; else if (!strcmp(subtype, "MMType1")) kind = TYPE1; else if (!strcmp(subtype, "TrueType")) kind = TRUETYPE; else if (!strcmp(subtype, "CIDFontType0")) kind = TYPE1; else if (!strcmp(subtype, "CIDFontType2")) kind = TRUETYPE; /* encode by glyph name where we can */ if (kind == TYPE1) { for (i = 0; i < 256; i++) { if (estrings[i]) { etable[i] = FT_Get_Name_Index(face, estrings[i]); if (etable[i] == 0) { int aglcode = pdf_lookup_agl(estrings[i]); const char **dupnames = pdf_lookup_agl_duplicates(aglcode); while (*dupnames) { etable[i] = FT_Get_Name_Index(face, (char*)*dupnames); if (etable[i]) break; dupnames++; } } } } } /* encode by glyph name where we can */ if (kind == TRUETYPE) { /* Unicode cmap */ if (!symbolic && face->charmap && face->charmap->platform_id == 3) { for (i = 0; i < 256; i++) { if (estrings[i]) { int aglcode = pdf_lookup_agl(estrings[i]); if (!aglcode) etable[i] = FT_Get_Name_Index(face, estrings[i]); else etable[i] = ft_char_index(face, aglcode); } } } /* MacRoman cmap */ else if (!symbolic && face->charmap && face->charmap->platform_id == 1) { for (i = 0; i < 256; i++) { if (estrings[i]) { k = lookup_mre_code(estrings[i]); if (k <= 0) etable[i] = FT_Get_Name_Index(face, estrings[i]); else etable[i] = ft_char_index(face, k); } } } /* Symbolic cmap */ else if (!face->charmap || face->charmap->encoding != FT_ENCODING_MS_SYMBOL) { for (i = 0; i < 256; i++) { if (estrings[i]) { etable[i] = FT_Get_Name_Index(face, estrings[i]); if (etable[i] == 0) etable[i] = ft_char_index(face, i); } } } } /* try to reverse the glyph names from the builtin encoding */ for (i = 0; i < 256; i++) { if (etable[i] && !estrings[i]) { if (FT_HAS_GLYPH_NAMES(face)) { fterr = FT_Get_Glyph_Name(face, etable[i], ebuffer[i], 32); if (fterr) fz_warn(ctx, "freetype get glyph name (gid %d): %s", etable[i], ft_error_string(fterr)); if (ebuffer[i][0]) estrings[i] = ebuffer[i]; } else { estrings[i] = (char*) pdf_win_ansi[i]; /* discard const */ } } } /* symbolic Type 1 fonts with an implicit encoding and non-standard glyph names */ if (kind == TYPE1 && symbolic) { for (i = 0; i < 256; i++) if (etable[i] && estrings[i] && !pdf_lookup_agl(estrings[i])) estrings[i] = (char*) pdf_standard[i]; } fz_unlock(ctx, FZ_LOCK_FREETYPE); fontdesc->encoding = pdf_new_identity_cmap(ctx, 0, 1); fontdesc->size += pdf_cmap_size(ctx, fontdesc->encoding); fontdesc->cid_to_gid_len = 256; fontdesc->cid_to_gid = etable; fz_try(ctx) { pdf_load_to_unicode(xref, fontdesc, estrings, NULL, pdf_dict_gets(dict, "ToUnicode")); } fz_catch(ctx) { fz_warn(ctx, "cannot load ToUnicode CMap"); } skip_encoding: /* Widths */ pdf_set_default_hmtx(ctx, fontdesc, fontdesc->missing_width); widths = pdf_dict_gets(dict, "Widths"); if (widths) { int first, last; first = pdf_to_int(pdf_dict_gets(dict, "FirstChar")); last = pdf_to_int(pdf_dict_gets(dict, "LastChar")); if (first < 0 || last > 255 || first > last) first = last = 0; for (i = 0; i < last - first + 1; i++) { int wid = pdf_to_int(pdf_array_get(widths, i)); pdf_add_hmtx(ctx, fontdesc, i + first, i + first, wid); } } else { fz_lock(ctx, FZ_LOCK_FREETYPE); fterr = FT_Set_Char_Size(face, 1000, 1000, 72, 72); if (fterr) fz_warn(ctx, "freetype set character size: %s", ft_error_string(fterr)); for (i = 0; i < 256; i++) { pdf_add_hmtx(ctx, fontdesc, i, i, ft_width(ctx, fontdesc, i)); } fz_unlock(ctx, FZ_LOCK_FREETYPE); } pdf_end_hmtx(ctx, fontdesc); } fz_catch(ctx) { if (fontdesc && etable != fontdesc->cid_to_gid) fz_free(ctx, etable); pdf_drop_font(ctx, fontdesc); fz_throw(ctx, "cannot load simple font (%d %d R)", pdf_to_num(dict), pdf_to_gen(dict)); } return fontdesc; }
fz_glyph * fz_render_glyph(fz_context *ctx, fz_font *font, int gid, fz_matrix *ctm, fz_colorspace *model, const fz_irect *scissor, int alpha) { fz_glyph_cache *cache; fz_glyph_key key; fz_matrix subpix_ctm; fz_irect subpix_scissor; float size; fz_glyph *val; int do_cache, locked, caching; fz_glyph_cache_entry *entry; unsigned hash; fz_var(locked); fz_var(caching); fz_var(val); memset(&key, 0, sizeof key); size = fz_subpixel_adjust(ctx, ctm, &subpix_ctm, &key.e, &key.f); if (size <= MAX_GLYPH_SIZE) { scissor = &fz_infinite_irect; do_cache = 1; } else { if (font->ft_face) return NULL; subpix_scissor.x0 = scissor->x0 - floorf(ctm->e); subpix_scissor.y0 = scissor->y0 - floorf(ctm->f); subpix_scissor.x1 = scissor->x1 - floorf(ctm->e); subpix_scissor.y1 = scissor->y1 - floorf(ctm->f); scissor = &subpix_scissor; do_cache = 0; } cache = ctx->glyph_cache; key.font = font; key.gid = gid; key.a = subpix_ctm.a * 65536; key.b = subpix_ctm.b * 65536; key.c = subpix_ctm.c * 65536; key.d = subpix_ctm.d * 65536; key.aa = fz_text_aa_level(ctx); hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN; fz_lock(ctx, FZ_LOCK_GLYPHCACHE); entry = cache->entry[hash]; while (entry) { if (memcmp(&entry->key, &key, sizeof(key)) == 0) { move_to_front(cache, entry); val = fz_keep_glyph(ctx, entry->val); fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); return val; } entry = entry->bucket_next; } locked = 1; caching = 0; val = NULL; fz_try(ctx) { if (font->ft_face) { val = fz_render_ft_glyph(ctx, font, gid, &subpix_ctm, key.aa); } else if (font->t3procs) { /* We drop the glyphcache here, and execute the t3 * glyph code. The danger here is that some other * thread will come along, and want the same glyph * too. If it does, we may both end up rendering * pixmaps. We cope with this later on, by ensuring * that only one gets inserted into the cache. If * we insert ours to find one already there, we * abandon ours, and use the one there already. */ fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); locked = 0; val = fz_render_t3_glyph(ctx, font, gid, &subpix_ctm, model, scissor); fz_lock(ctx, FZ_LOCK_GLYPHCACHE); locked = 1; } else { fz_warn(ctx, "assert: uninitialized font structure"); } if (val && do_cache) { if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) { /* If we throw an exception whilst caching, * just ignore the exception and carry on. */ caching = 1; if (!font->ft_face) { /* We had to unlock. Someone else might * have rendered in the meantime */ entry = cache->entry[hash]; while (entry) { if (memcmp(&entry->key, &key, sizeof(key)) == 0) { fz_drop_glyph(ctx, val); move_to_front(cache, entry); val = fz_keep_glyph(ctx, entry->val); goto unlock_and_return_val; } entry = entry->bucket_next; } } entry = fz_malloc_struct(ctx, fz_glyph_cache_entry); entry->key = key; entry->hash = hash; entry->bucket_next = cache->entry[hash]; if (entry->bucket_next) entry->bucket_next->bucket_prev = entry; cache->entry[hash] = entry; entry->val = fz_keep_glyph(ctx, val); fz_keep_font(ctx, key.font); entry->lru_next = cache->lru_head; if (entry->lru_next) entry->lru_next->lru_prev = entry; else cache->lru_tail = entry; cache->lru_head = entry; cache->total += fz_glyph_size(ctx, val); while (cache->total > MAX_CACHE_SIZE) { #ifndef NDEBUG cache->num_evictions++; cache->evicted += fz_glyph_size(ctx, cache->lru_tail->val); #endif drop_glyph_cache_entry(ctx, cache->lru_tail); } } } unlock_and_return_val: { } } fz_always(ctx) { if (locked) fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); } fz_catch(ctx) { if (caching) fz_warn(ctx, "cannot encache glyph; continuing"); else fz_rethrow(ctx); } return val; }
static void fz_text_extract(fz_context *ctx, fz_text_device *dev, fz_text *text, const fz_matrix *ctm, fz_text_style *style) { fz_font *font = text->font; FT_Face face = font->ft_face; fz_matrix tm = text->trm; fz_matrix trm; float adv; float ascender = 1; float descender = 0; int multi; int i, j, err; if (text->len == 0) return; if (dev->spans == NULL) dev->spans = new_span_soup(ctx); if (style->wmode == 0) { if (font->ft_face) { fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); ascender = (float)face->ascender / face->units_per_EM; descender = (float)face->descender / face->units_per_EM; fz_unlock(ctx, FZ_LOCK_FREETYPE); } else if (font->t3procs && !fz_is_empty_rect(&font->bbox)) { ascender = font->bbox.y1; descender = font->bbox.y0; } } else { ascender = font->bbox.x1; descender = font->bbox.x0; } style->ascender = ascender; style->descender = descender; tm.e = 0; tm.f = 0; fz_concat(&trm, &tm, ctm); for (i = 0; i < text->len; i++) { /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; fz_concat(&trm, &tm, ctm); /* Calculate bounding box and new pen position based on font metrics */ adv = fz_advance_glyph(ctx, font, text->items[i].gid); /* Check for one glyph to many char mapping */ for (j = i + 1; j < text->len; j++) if (text->items[j].gid >= 0) break; multi = j - i; if (multi == 1) { fz_add_text_char(ctx, dev, style, text->items[i].ucs, &trm, adv, text->wmode); } else { for (j = 0; j < multi; j++) { fz_add_text_char(ctx, dev, style, text->items[i + j].ucs, &trm, adv/multi, text->wmode); } i += j - 1; } dev->lastchar = text->items[i].ucs; } }
static void fz_text_extract_span(fz_context *ctx, fz_text_span **last, fz_text *text, fz_matrix ctm, fz_point *pen) { fz_font *font = text->font; FT_Face face = font->ft_face; fz_matrix tm = text->trm; fz_matrix trm; float size; float adv; fz_rect rect; fz_point dir, ndir; fz_point delta, ndelta; float dist, dot; float ascender = 1; float descender = 0; int multi; int i, err; if (text->len == 0) return; fz_lock(ctx, FZ_LOCK_FREETYPE); if (font->ft_face) { err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); ascender = (float)face->ascender / face->units_per_EM; descender = (float)face->descender / face->units_per_EM; } rect = fz_empty_rect; if (text->wmode == 0) { dir.x = 1; dir.y = 0; } else { dir.x = 0; dir.y = 1; } tm.e = 0; tm.f = 0; trm = fz_concat(tm, ctm); dir = fz_transform_vector(trm, dir); dist = sqrtf(dir.x * dir.x + dir.y * dir.y); ndir.x = dir.x / dist; ndir.y = dir.y / dist; size = fz_matrix_expansion(trm); multi = 1; for (i = 0; i < text->len; i++) { if (text->items[i].gid < 0) { fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); multi ++; fz_divide_text_chars(last, multi, fz_round_rect(rect)); continue; } multi = 1; /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; trm = fz_concat(tm, ctm); delta.x = pen->x - trm.e; delta.y = pen->y - trm.f; if (pen->x == -1 && pen->y == -1) delta.x = delta.y = 0; dist = sqrtf(delta.x * delta.x + delta.y * delta.y); /* Add space and newlines based on pen movement */ if (dist > 0) { ndelta.x = delta.x / dist; ndelta.y = delta.y / dist; dot = ndelta.x * ndir.x + ndelta.y * ndir.y; if (dist > size * LINE_DIST) { fz_add_text_newline(ctx, last, font, size, text->wmode); } else if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST) { if ((*last)->len > 0 && (*last)->text[(*last)->len - 1].c != ' ') { fz_rect spacerect; spacerect.x0 = -0.2f; spacerect.y0 = 0; spacerect.x1 = 0; spacerect.y1 = 1; spacerect = fz_transform_rect(trm, spacerect); fz_add_text_char(ctx, last, font, size, text->wmode, ' ', fz_round_rect(spacerect)); } } } /* Calculate bounding box and new pen position based on font metrics */ if (font->ft_face) { FT_Fixed ftadv = 0; int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; /* TODO: freetype returns broken vertical metrics */ /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */ FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); adv = ftadv / 65536.0f; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } else { adv = font->t3widths[text->items[i].gid]; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } rect = fz_transform_rect(trm, rect); pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; fz_add_text_char(ctx, last, font, size, text->wmode, text->items[i].ucs, fz_round_rect(rect)); } fz_unlock(ctx, FZ_LOCK_FREETYPE); }
void hb_unlock(fz_context *ctx) { set_context(NULL); fz_unlock(ctx, FZ_LOCK_FREETYPE); }
static void fz_text_extract(fz_context *ctx, fz_text_device *dev, fz_text *text, const fz_matrix *ctm, fz_text_style *style) { fz_point *pen = &dev->point; fz_font *font = text->font; FT_Face face = font->ft_face; fz_matrix tm = text->trm; fz_matrix trm; float size; float adv; fz_rect rect; fz_point dir, ndir; fz_point delta, ndelta; float dist, dot; float ascender = 1; float descender = 0; int multi; int i, j, err; if (text->len == 0) return; if (font->ft_face) { fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); ascender = (float)face->ascender / face->units_per_EM; descender = (float)face->descender / face->units_per_EM; fz_unlock(ctx, FZ_LOCK_FREETYPE); } else if (font->t3procs && !fz_is_empty_rect(&font->bbox)) { ascender = font->bbox.y1; descender = font->bbox.y0; } rect = fz_empty_rect; /* SumatraPDF: TODO: make this depend on the per-glyph displacement vector */ if (text->wmode == 0) { dir.x = 1; dir.y = 0; } else { dir.x = 0; dir.y = 1; } tm.e = 0; tm.f = 0; fz_concat(&trm, &tm, ctm); fz_transform_vector(&dir, &trm); dist = sqrtf(dir.x * dir.x + dir.y * dir.y); ndir.x = dir.x / dist; ndir.y = dir.y / dist; size = fz_matrix_expansion(&trm); for (i = 0; i < text->len; i++) { /* Calculate new pen location and delta */ tm.e = text->items[i].x; tm.f = text->items[i].y; fz_concat(&trm, &tm, ctm); delta.x = pen->x - trm.e; delta.y = pen->y - trm.f; if (pen->x == -1 && pen->y == -1) delta.x = delta.y = 0; dist = sqrtf(delta.x * delta.x + delta.y * delta.y); /* Add space and newlines based on pen movement */ if (dist > 0) { /* SumatraPDF: don't add spaces for large overlapping glyphs */ fz_text_span *last = &dev->cur_span; if (last->len == 0 && dev->cur_line.len > 0) last = &dev->cur_line.spans[dev->cur_line.len - 1]; ndelta.x = delta.x / dist; ndelta.y = delta.y / dist; dot = ndelta.x * ndir.x + ndelta.y * ndir.y; /* SumatraPDF: don't merge multiple lines into one */ if (dist > size * LINE_DIST && hypotf(delta.y * ndir.x, delta.x * ndir.y) > size * 0.5f) { fz_flush_text_line(ctx, dev, style); dev->lastchar = ' '; } else /* SumatraPDF: use 0.95f instead of 0.9995f for slightly better results */ if (fabsf(dot) > 0.95f && dist > size * SPACE_DIST && dist < size * SPACE_MAX_DIST) { if (dev->lastchar != ' ' && /* SumatraPDF: don't add spaces before spaces or for large overlapping glyphs */ text->items[i].ucs != ' ' && !fz_maps_into_rect(trm, last->text[last->len - 1].bbox)) { fz_rect spacerect; spacerect.x0 = -0.2f; spacerect.y0 = descender; spacerect.x1 = 0; spacerect.y1 = ascender; fz_transform_rect(&spacerect, &trm); fz_add_text_char(ctx, dev, style, ' ', spacerect); dev->lastchar = ' '; } } else if (dist > size * LINE_DIST) { fz_flush_text_line(ctx, dev, style); dev->lastchar = ' '; } } /* Calculate bounding box and new pen position based on font metrics */ if (font->ft_face) { FT_Fixed ftadv = 0; int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM; /* TODO: freetype returns broken vertical metrics */ /* if (text->wmode) mask |= FT_LOAD_VERTICAL_LAYOUT; */ fz_lock(ctx, FZ_LOCK_FREETYPE); err = FT_Set_Char_Size(font->ft_face, 64, 64, 72, 72); if (err) fz_warn(ctx, "freetype set character size: %s", ft_error_string(err)); FT_Get_Advance(font->ft_face, text->items[i].gid, mask, &ftadv); adv = ftadv / 65536.0f; fz_unlock(ctx, FZ_LOCK_FREETYPE); rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } /* SumatraPDF: TODO: this check might no longer be needed */ else if (text->items[i].gid < 256) { adv = font->t3widths[text->items[i].gid]; rect.x0 = 0; rect.y0 = descender; rect.x1 = adv; rect.y1 = ascender; } fz_transform_rect(&rect, &trm); /* cf. http://code.google.com/p/sumatrapdf/issues/detail?id=1839 */ if (font->ft_face) { fz_rect bbox; fz_bound_glyph(ctx, font, text->items[i].gid, &trm, &bbox); rect.y0 = fz_min(rect.y0, bbox.y0); rect.y1 = fz_max(rect.y1, bbox.y1); } pen->x = trm.e + dir.x * adv; pen->y = trm.f + dir.y * adv; /* Check for one glyph to many char mapping */ for (j = i + 1; j < text->len; j++) if (text->items[j].gid >= 0) break; multi = j - i; if (multi == 1) { fz_add_text_char(ctx, dev, style, text->items[i].ucs, rect); } else { for (j = 0; j < multi; j++) { fz_rect part = fz_split_bbox(rect, j, multi); fz_add_text_char(ctx, dev, style, text->items[i + j].ucs, part); } i += j - 1; } dev->lastchar = text->items[i].ucs; } }
/* Render a glyph and return a bitmap. If the glyph is too large to fit the cache we have two choices: 1) Return NULL so the caller can draw the glyph using an outline. Only supported for freetype fonts. 2) Render a clipped glyph by using the scissor rectangle. Only supported for type 3 fonts. This must not be inserted into the cache. */ fz_pixmap * fz_render_glyph(fz_context *ctx, fz_font *font, int gid, const fz_matrix *ctm, fz_colorspace *model, fz_irect scissor) { fz_glyph_cache *cache; fz_glyph_key key; fz_pixmap *val; float size = fz_matrix_expansion(ctm); int do_cache, locked, caching; fz_matrix local_ctm = *ctm; fz_glyph_cache_entry *entry; unsigned hash; fz_var(locked); fz_var(caching); if (size <= MAX_GLYPH_SIZE) { scissor = fz_infinite_irect; do_cache = 1; } else { /* SumatraPDF: don't break clipping by larger glyphs */ if (font->ft_face && size > 3000) return NULL; do_cache = 0; } cache = ctx->glyph_cache; memset(&key, 0, sizeof key); key.font = font; key.gid = gid; key.a = local_ctm.a * 65536; key.b = local_ctm.b * 65536; key.c = local_ctm.c * 65536; key.d = local_ctm.d * 65536; key.e = (local_ctm.e - floorf(local_ctm.e)) * 256; key.f = (local_ctm.f - floorf(local_ctm.f)) * 256; key.aa = fz_aa_level(ctx); local_ctm.e = floorf(local_ctm.e) + key.e / 256.0f; local_ctm.f = floorf(local_ctm.f) + key.f / 256.0f; fz_lock(ctx, FZ_LOCK_GLYPHCACHE); hash = do_hash((unsigned char *)&key, sizeof(key)) % GLYPH_HASH_LEN; entry = cache->entry[hash]; while (entry) { if (memcmp(&entry->key, &key, sizeof(key)) == 0) { move_to_front(cache, entry); val = fz_keep_pixmap(ctx, entry->val); fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); return val; } entry = entry->bucket_next; } locked = 1; caching = 0; fz_try(ctx) { if (font->ft_face) { val = fz_render_ft_glyph(ctx, font, gid, &local_ctm, key.aa); } else if (font->t3procs) { /* We drop the glyphcache here, and execute the t3 * glyph code. The danger here is that some other * thread will come along, and want the same glyph * too. If it does, we may both end up rendering * pixmaps. We cope with this later on, by ensuring * that only one gets inserted into the cache. If * we insert ours to find one already there, we * abandon ours, and use the one there already. */ fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); locked = 0; val = fz_render_t3_glyph(ctx, font, gid, &local_ctm, model, scissor); fz_lock(ctx, FZ_LOCK_GLYPHCACHE); locked = 1; } else { fz_warn(ctx, "assert: uninitialized font structure"); val = NULL; } if (val && do_cache) { if (val->w < MAX_GLYPH_SIZE && val->h < MAX_GLYPH_SIZE) { /* If we throw an exception whilst caching, * just ignore the exception and carry on. */ caching = 1; if (!font->ft_face) { /* We had to unlock. Someone else might * have rendered in the meantime */ entry = cache->entry[hash]; while (entry) { if (memcmp(&entry->key, &key, sizeof(key)) == 0) { fz_drop_pixmap(ctx, val); move_to_front(cache, entry); val = fz_keep_pixmap(ctx, entry->val); fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); return val; } entry = entry->bucket_next; } } entry = fz_malloc_struct(ctx, fz_glyph_cache_entry); entry->key = key; entry->hash = hash; entry->bucket_next = cache->entry[hash]; if (entry->bucket_next) entry->bucket_next->bucket_prev = entry; cache->entry[hash] = entry; entry->val = fz_keep_pixmap(ctx, val); fz_keep_font(ctx, key.font); entry->lru_next = cache->lru_head; if (entry->lru_next) entry->lru_next->lru_prev = entry; else cache->lru_tail = entry; cache->lru_head = entry; cache->total += val->w * val->h; while (cache->total > MAX_CACHE_SIZE) { drop_glyph_cache_entry(ctx, cache->lru_tail); } } } } fz_always(ctx) { if (locked) fz_unlock(ctx, FZ_LOCK_GLYPHCACHE); } fz_catch(ctx) { if (caching) fz_warn(ctx, "cannot encache glyph; continuing"); else fz_rethrow(ctx); } return val; }