| /* | |
| * Glyph shaping support | |
| * | |
| * Copyright 2010 Aric Stewart for CodeWeavers | |
| * Copyright 2014 Nikolay Sivov for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #include "dwrite_private.h" | |
| struct scriptshaping_cache | |
| { | |
| IDWriteFontFace *fontface; | |
| }; | |
| HRESULT create_scriptshaping_cache(IDWriteFontFace *fontface, struct scriptshaping_cache **cache) | |
| { | |
| struct scriptshaping_cache *ret; | |
| ret = heap_alloc(sizeof(*ret)); | |
| if (!ret) | |
| return E_OUTOFMEMORY; | |
| ret->fontface = fontface; | |
| IDWriteFontFace_AddRef(fontface); | |
| *cache = ret; | |
| return S_OK; | |
| } | |
| void release_scriptshaping_cache(struct scriptshaping_cache *cache) | |
| { | |
| if (!cache) | |
| return; | |
| IDWriteFontFace_Release(cache->fontface); | |
| heap_free(cache); | |
| } | |
| static void shape_update_clusters_from_glyphprop(UINT32 glyphcount, UINT32 text_len, UINT16 *clustermap, DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props) | |
| { | |
| UINT32 i; | |
| for (i = 0; i < glyphcount; i++) { | |
| if (!glyph_props[i].isClusterStart) { | |
| UINT32 j; | |
| for (j = 0; j < text_len; j++) { | |
| if (clustermap[j] == i) { | |
| int k = j; | |
| while (k >= 0 && k < text_len && !glyph_props[clustermap[k]].isClusterStart) | |
| k--; | |
| if (k >= 0 && k < text_len && glyph_props[clustermap[k]].isClusterStart) | |
| clustermap[j] = clustermap[k]; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| static int compare_clustersearch(const void *a, const void* b) | |
| { | |
| UINT16 target = *(UINT16*)a; | |
| UINT16 index = *(UINT16*)b; | |
| int ret = 0; | |
| if (target > index) | |
| ret = 1; | |
| else if (target < index) | |
| ret = -1; | |
| return ret; | |
| } | |
| /* Maps given glyph position in glyph indices array to text index this glyph represents. | |
| Lowest possible index is returned. | |
| clustermap [I] Text index to index in glyph indices array map | |
| len [I] Clustermap size | |
| target [I] Index in glyph indices array to map | |
| */ | |
| static INT32 map_glyph_to_text_pos(const UINT16 *clustermap, UINT32 len, UINT16 target) | |
| { | |
| UINT16 *ptr; | |
| INT32 k; | |
| ptr = bsearch(&target, clustermap, len, sizeof(UINT16), compare_clustersearch); | |
| if (!ptr) | |
| return -1; | |
| /* get to the beginning */ | |
| for (k = (ptr - clustermap) - 1; k >= 0 && clustermap[k] == target; k--) | |
| ; | |
| k++; | |
| return k; | |
| } | |
| static HRESULT default_set_text_glyphs_props(struct scriptshaping_context *context, UINT16 *clustermap, UINT16 *glyph_indices, | |
| UINT32 glyphcount, DWRITE_SHAPING_TEXT_PROPERTIES *text_props, DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props) | |
| { | |
| UINT32 i; | |
| for (i = 0; i < glyphcount; i++) { | |
| UINT32 char_index[20]; | |
| UINT32 char_count = 0; | |
| INT32 k; | |
| k = map_glyph_to_text_pos(clustermap, context->length, i); | |
| if (k >= 0) { | |
| for (; k < context->length && clustermap[k] == i; k++) | |
| char_index[char_count++] = k; | |
| } | |
| if (char_count == 0) | |
| continue; | |
| if (char_count == 1 && isspaceW(context->text[char_index[0]])) { | |
| glyph_props[i].justification = SCRIPT_JUSTIFY_BLANK; | |
| text_props[char_index[0]].isShapedAlone = context->text[char_index[0]] == ' '; | |
| } | |
| else | |
| glyph_props[i].justification = SCRIPT_JUSTIFY_CHARACTER; | |
| } | |
| /* FIXME: update properties using GDEF table */ | |
| shape_update_clusters_from_glyphprop(glyphcount, context->length, clustermap, glyph_props); | |
| return S_OK; | |
| } | |
| static HRESULT latn_set_text_glyphs_props(struct scriptshaping_context *context, UINT16 *clustermap, UINT16 *glyph_indices, | |
| UINT32 glyphcount, DWRITE_SHAPING_TEXT_PROPERTIES *text_props, DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props) | |
| { | |
| HRESULT hr; | |
| UINT32 i; | |
| hr = default_set_text_glyphs_props(context, clustermap, glyph_indices, glyphcount, text_props, glyph_props); | |
| for (i = 0; i < glyphcount; i++) | |
| if (glyph_props[i].isZeroWidthSpace) | |
| glyph_props[i].justification = SCRIPT_JUSTIFY_NONE; | |
| return hr; | |
| } | |
| const struct scriptshaping_ops latn_shaping_ops = | |
| { | |
| NULL, | |
| latn_set_text_glyphs_props | |
| }; | |
| const struct scriptshaping_ops default_shaping_ops = | |
| { | |
| NULL, | |
| default_set_text_glyphs_props | |
| }; |