HRESULT Place() { HRESULT rv; HDC placeDC = nullptr; if (!mOffsets.SetLength(mNumGlyphs) || !mAdvances.SetLength(mNumGlyphs)) { return E_OUTOFMEMORY; } SCRIPT_ANALYSIS sa = mScriptItem->a; while (true) { rv = ScriptPlace(placeDC, mShaper->ScriptCache(), mGlyphs.Elements(), mNumGlyphs, mAttr.Elements(), &sa, mAdvances.Elements(), mOffsets.Elements(), nullptr); if (rv == E_PENDING) { SelectFont(); placeDC = mDC; continue; } if (rv == USP_E_SCRIPT_NOT_IN_FONT) { sa.eScript = SCRIPT_UNDEFINED; continue; } break; } return rv; }
static HRESULT shape_run( ME_Context *c, ME_Run *run ) { HRESULT hr; HFONT old_font; int i; if (!run->glyphs) { run->max_glyphs = 1.5 * run->len + 16; /* This is suggested in the uniscribe documentation */ run->max_glyphs = (run->max_glyphs + 7) & ~7; /* Keep alignment simple */ get_run_glyph_buffers( run ); } if (run->max_clusters < run->len) { heap_free( run->clusters ); run->max_clusters = run->len * 2; run->clusters = heap_alloc( run->max_clusters * sizeof(WORD) ); } old_font = ME_SelectStyleFont( c, run->style ); while (1) { hr = ScriptShape( c->hDC, &run->style->script_cache, get_text( run, 0 ), run->len, run->max_glyphs, &run->script_analysis, run->glyphs, run->clusters, run->vis_attrs, &run->num_glyphs ); if (hr != E_OUTOFMEMORY) break; if (run->max_glyphs > 10 * run->len) break; /* something has clearly gone wrong */ run->max_glyphs *= 2; get_run_glyph_buffers( run ); } if (SUCCEEDED(hr)) hr = ScriptPlace( c->hDC, &run->style->script_cache, run->glyphs, run->num_glyphs, run->vis_attrs, &run->script_analysis, run->advances, run->offsets, NULL ); if (SUCCEEDED(hr)) { for (i = 0, run->nWidth = 0; i < run->num_glyphs; i++) run->nWidth += run->advances[i]; } ME_UnselectStyleFont( c, run->style, old_font ); return hr; }
bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const SimpleFontData* fontData, GlyphBuffer* glyphBuffer) { // Determine the string for this item. const UChar* str = cp + m_items[i].iCharPos; int len = m_items[i+1].iCharPos - m_items[i].iCharPos; SCRIPT_ITEM item = m_items[i]; // Set up buffers to hold the results of shaping the item. Vector<WORD> glyphs; Vector<WORD> clusters; Vector<SCRIPT_VISATTR> visualAttributes; clusters.resize(len); // Shape the item. // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs. // Apparently this is a good size to avoid having to make repeated calls to ScriptShape. glyphs.resize(1.5 * len + 16); visualAttributes.resize(glyphs.size()); if (!shape(str, len, item, fontData, glyphs, clusters, visualAttributes)) return true; // We now have a collection of glyphs. Vector<GOFFSET> offsets; Vector<int> advances; offsets.resize(glyphs.size()); advances.resize(glyphs.size()); int glyphCount = 0; HRESULT placeResult = ScriptPlace(0, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); if (placeResult == E_PENDING) { // The script cache isn't primed with enough info yet. We need to select our HFONT into // a DC and pass the DC in to ScriptPlace. HDC hdc = GetDC(0); HFONT hfont = fontData->platformData().hfont(); HFONT oldFont = (HFONT)SelectObject(hdc, hfont); placeResult = ScriptPlace(hdc, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); } if (FAILED(placeResult) || glyphs.isEmpty()) return true; // Convert all chars that should be treated as spaces to use the space glyph. // We also create a map that allows us to quickly go from space glyphs back to their corresponding characters. Vector<int> spaceCharacters(glyphs.size()); spaceCharacters.fill(-1); const float cLogicalScale = fontData->platformData().useGDI() ? 1.0f : 32.0f; unsigned logicalSpaceWidth = fontData->spaceWidth() * cLogicalScale; float spaceWidth = fontData->spaceWidth(); for (int k = 0; k < len; k++) { UChar ch = *(str + k); bool treatAsSpace = Font::treatAsSpace(ch); bool treatAsZeroWidthSpace = ch == zeroWidthSpace || Font::treatAsZeroWidthSpace(ch); if (treatAsSpace || treatAsZeroWidthSpace) { // Substitute in the space glyph at the appropriate place in the glyphs // array. glyphs[clusters[k]] = fontData->spaceGlyph(); advances[clusters[k]] = treatAsSpace ? logicalSpaceWidth : 0; if (treatAsSpace) spaceCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; } } // Populate our glyph buffer with this information. bool hasExtraSpacing = m_font.letterSpacing() || m_font.wordSpacing() || m_padding; float leftEdge = m_runWidthSoFar; for (unsigned k = 0; k < glyphs.size(); k++) { Glyph glyph = glyphs[k]; float advance = advances[k] / cLogicalScale; float offsetX = offsets[k].du / cLogicalScale; float offsetY = offsets[k].dv / cLogicalScale; // Match AppKit's rules for the integer vs. non-integer rendering modes. float roundedAdvance = roundf(advance); if (!m_font.isPrinterFont() && !fontData->isSystemFont()) { advance = roundedAdvance; offsetX = roundf(offsetX); offsetY = roundf(offsetY); } advance += fontData->syntheticBoldOffset(); if (hasExtraSpacing) { // If we're a glyph with an advance, go ahead and add in letter-spacing. // That way we weed out zero width lurkers. This behavior matches the fast text code path. if (advance && m_font.letterSpacing()) advance += m_font.letterSpacing(); // Handle justification and word-spacing. int characterIndex = spaceCharacters[k]; // characterIndex is left at the initial value of -1 for glyphs that do not map back to treated-as-space characters. if (characterIndex != -1) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { // Use leftover padding if not evenly divisible by number of spaces. if (m_padding < m_padPerSpace) { advance += m_padding; m_padding = 0; } else { m_padding -= m_padPerSpace; advance += m_padPerSpace; } } // Account for word-spacing. if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) advance += m_font.wordSpacing(); } } m_runWidthSoFar += advance; // FIXME: We need to take the GOFFSETS for combining glyphs and store them in the glyph buffer // as well, so that when the time comes to draw those glyphs, we can apply the appropriate // translation. if (glyphBuffer) { FloatSize size(offsetX, -offsetY); glyphBuffer->add(glyph, fontData, advance, &size); } FloatRect glyphBounds = fontData->boundsForGlyph(glyph); glyphBounds.move(m_glyphOrigin.x(), m_glyphOrigin.y()); m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); m_glyphOrigin.move(advance + offsetX, -offsetY); // Mutate the glyph array to contain our altered advances. if (m_computingOffsetPosition) advances[k] = advance; } while (m_computingOffsetPosition && m_offsetX >= leftEdge && m_offsetX < m_runWidthSoFar) { // The position is somewhere inside this run. int trailing = 0; ScriptXtoCP(m_offsetX - leftEdge, clusters.size(), glyphs.size(), clusters.data(), visualAttributes.data(), advances.data(), &item.a, &m_offsetPosition, &trailing); if (trailing && m_includePartialGlyphs && m_offsetPosition < len - 1) { m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; m_offsetX += m_run.rtl() ? -trailing : trailing; } else { m_computingOffsetPosition = false; m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; if (trailing && m_includePartialGlyphs) m_offsetPosition++; return false; } } return true; }
void UniscribeHelper::fillShapes() { m_shapes.resize(m_runs.size()); for (size_t i = 0; i < m_runs.size(); i++) { int startItem = m_runs[i].iCharPos; int itemLength = m_inputLength - startItem; if (i < m_runs.size() - 1) itemLength = m_runs[i + 1].iCharPos - startItem; int numGlyphs; if (itemLength < UNISCRIBE_HELPER_STACK_CHARS) { // We'll start our buffer sizes with the current stack space // available in our buffers if the current input fits. As long as // it doesn't expand past that we'll save a lot of time mallocing. numGlyphs = UNISCRIBE_HELPER_STACK_CHARS; } else { // When the input doesn't fit, give up with the stack since it will // almost surely not be enough room (unless the input actually // shrinks, which is unlikely) and just start with the length // recommended by the Uniscribe documentation as a "usually fits" // size. numGlyphs = itemLength * 3 / 2 + 16; } // Convert a string to a glyph string trying the primary font, fonts in // the fallback list and then script-specific last resort font. Shaping& shaping = m_shapes[i]; if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping)) continue; // At the moment, the only time m_disableFontFallback is set is // when we look up glyph indices for non-BMP code ranges. So, // we can skip the glyph placement. When that becomes not the case // any more, we have to add a new flag to control glyph placement. if (m_disableFontFallback) continue; // Compute placements. Note that offsets is documented incorrectly // and is actually an array. // DC that we lazily create if Uniscribe commands us to. // (this does not happen often because scriptCache is already // updated when calling ScriptShape). HDC tempDC = 0; HGDIOBJ oldFont = 0; HRESULT hr; while (true) { shaping.m_prePadding = 0; hr = ScriptPlace(tempDC, shaping.m_scriptCache, &shaping.m_glyphs[0], static_cast<int>(shaping.m_glyphs.size()), &shaping.m_visualAttributes[0], &m_runs[i].a, &shaping.m_advance[0], &shaping.m_offsets[0], &shaping.m_abc); if (hr != E_PENDING) break; // Allocate the DC and run the loop again. tempDC = GetDC(0); oldFont = SelectObject(tempDC, shaping.m_hfont); } if (FAILED(hr)) { // Some error we don't know how to handle. Nuke all of our data // since we can't deal with partially valid data later. m_runs.clear(); m_shapes.clear(); m_screenOrder.clear(); } if (tempDC) { SelectObject(tempDC, oldFont); ReleaseDC(0, tempDC); } } adjustSpaceAdvances(); if (m_letterSpacing != 0 || m_wordSpacing != 0) applySpacing(); }
void UniscribeHelper::fillShapes() { m_shapes.resize(m_runs.size()); for (size_t i = 0; i < m_runs.size(); i++) { int startItem = m_runs[i].iCharPos; int itemLength = m_inputLength - startItem; if (i < m_runs.size() - 1) itemLength = m_runs[i + 1].iCharPos - startItem; int numGlyphs; if (itemLength < cUniscribeHelperStackChars) { // We'll start our buffer sizes with the current stack space // available in our buffers if the current input fits. As long as // it doesn't expand past that we'll save a lot of time mallocing. numGlyphs = cUniscribeHelperStackChars; } else { // When the input doesn't fit, give up with the stack since it will // almost surely not be enough room (unless the input actually // shrinks, which is unlikely) and just start with the length // recommended by the Uniscribe documentation as a "usually fits" // size. numGlyphs = itemLength * 3 / 2 + 16; } // Convert a string to a glyph string trying the primary font, fonts in // the fallback list and then script-specific last resort font. Shaping& shaping = m_shapes[i]; if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], m_scriptTags[i], shaping)) continue; // At the moment, the only time m_disableFontFallback is set is // when we look up glyph indices for non-BMP code ranges. So, // we can skip the glyph placement. When that becomes not the case // any more, we have to add a new flag to control glyph placement. if (m_disableFontFallback) continue; // Compute placements. Note that offsets is documented incorrectly // and is actually an array. EnsureCachedDCCreated(); SelectObject(m_cachedDC, shaping.m_hfont); shaping.m_prePadding = 0; if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache, &shaping.m_glyphs[0], static_cast<int>(shaping.m_glyphs.size()), &shaping.m_visualAttributes[0], &m_runs[i].a, &shaping.m_advance[0], &shaping.m_offsets[0], &shaping.m_abc))) { // Some error we don't know how to handle. Nuke all of our data // since we can't deal with partially valid data later. m_runs.clear(); m_scriptTags.clear(); m_shapes.clear(); m_screenOrder.clear(); } } adjustSpaceAdvances(); if (m_letterSpacing != 0 || m_wordSpacing != 0) applySpacing(); }
bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const SimpleFontData* fontData, GlyphBuffer* glyphBuffer) { // Determine the string for this item. const UChar* str = cp + m_items[i].iCharPos; int len = m_items[i+1].iCharPos - m_items[i].iCharPos; SCRIPT_ITEM item = m_items[i]; // Set up buffers to hold the results of shaping the item. Vector<WORD> glyphs; Vector<WORD> clusters; Vector<SCRIPT_VISATTR> visualAttributes; clusters.resize(len); // Shape the item. // The recommended size for the glyph buffer is 1.5 * the character length + 16 in the uniscribe docs. // Apparently this is a good size to avoid having to make repeated calls to ScriptShape. glyphs.resize(1.5 * len + 16); visualAttributes.resize(glyphs.size()); if (!shape(str, len, item, fontData, glyphs, clusters, visualAttributes)) return true; // We now have a collection of glyphs. Vector<GOFFSET> offsets; Vector<int> advances; offsets.resize(glyphs.size()); advances.resize(glyphs.size()); int glyphCount = 0; HRESULT placeResult = ScriptPlace(0, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); if (placeResult == E_PENDING) { // The script cache isn't primed with enough info yet. We need to select our HFONT into // a DC and pass the DC in to ScriptPlace. HDC hdc = GetDC(0); HFONT hfont = fontData->platformData().hfont(); HFONT oldFont = (HFONT)SelectObject(hdc, hfont); placeResult = ScriptPlace(hdc, fontData->scriptCache(), glyphs.data(), glyphs.size(), visualAttributes.data(), &item.a, advances.data(), offsets.data(), 0); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); } if (FAILED(placeResult) || glyphs.isEmpty()) return true; // Convert all chars that should be treated as spaces to use the space glyph. // We also create a map that allows us to quickly go from space glyphs or rounding // hack glyphs back to their corresponding characters. Vector<int> spaceCharacters(glyphs.size()); spaceCharacters.fill(-1); Vector<int> roundingHackCharacters(glyphs.size()); roundingHackCharacters.fill(-1); Vector<int> roundingHackWordBoundaries(glyphs.size()); roundingHackWordBoundaries.fill(-1); const float cLogicalScale = fontData->platformData().useGDI() ? 1.0f : 32.0f; unsigned logicalSpaceWidth = fontData->spaceWidth() * cLogicalScale; float roundedSpaceWidth = roundf(fontData->spaceWidth()); for (int k = 0; k < len; k++) { UChar ch = *(str + k); if (Font::treatAsSpace(ch)) { // Substitute in the space glyph at the appropriate place in the glyphs // array. glyphs[clusters[k]] = fontData->spaceGlyph(); advances[clusters[k]] = logicalSpaceWidth; spaceCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; } if (Font::isRoundingHackCharacter(ch)) roundingHackCharacters[clusters[k]] = m_currentCharacter + k + item.iCharPos; int boundary = k + m_currentCharacter + item.iCharPos; if (boundary < m_run.length() && Font::isRoundingHackCharacter(*(str + k + 1))) roundingHackWordBoundaries[clusters[k]] = boundary; } // Populate our glyph buffer with this information. bool hasExtraSpacing = m_font.letterSpacing() || m_font.wordSpacing() || m_padding; float leftEdge = m_runWidthSoFar; for (unsigned k = 0; k < glyphs.size(); k++) { Glyph glyph = glyphs[k]; float advance = advances[k] / cLogicalScale; float offsetX = offsets[k].du / cLogicalScale; float offsetY = offsets[k].dv / cLogicalScale; // Match AppKit's rules for the integer vs. non-integer rendering modes. float roundedAdvance = roundf(advance); if (!m_font.isPrinterFont() && !fontData->isSystemFont()) { advance = roundedAdvance; offsetX = roundf(offsetX); offsetY = roundf(offsetY); } advance += fontData->syntheticBoldOffset(); // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all glyphs that // match the width of the space glyph have the same width as the space glyph. if (roundedAdvance == roundedSpaceWidth && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()) && m_run.applyWordRounding()) advance = fontData->adjustedSpaceWidth(); if (hasExtraSpacing) { // If we're a glyph with an advance, go ahead and add in letter-spacing. // That way we weed out zero width lurkers. This behavior matches the fast text code path. if (advance && m_font.letterSpacing()) advance += m_font.letterSpacing(); // Handle justification and word-spacing. if (glyph == fontData->spaceGlyph()) { // Account for padding. WebCore uses space padding to justify text. // We distribute the specified padding over the available spaces in the run. if (m_padding) { // Use leftover padding if not evenly divisible by number of spaces. if (m_padding < m_padPerSpace) { advance += m_padding; m_padding = 0; } else { advance += m_padPerSpace; m_padding -= m_padPerSpace; } } // Account for word-spacing. int characterIndex = spaceCharacters[k]; if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) advance += m_font.wordSpacing(); } } // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters // followed by a character defined by isRoundingHackCharacter()) are always an integer width. // We adjust the width of the last character of a "word" to ensure an integer width. // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so the following words will start on an integer boundary. int roundingHackIndex = roundingHackCharacters[k]; if (m_run.applyWordRounding() && roundingHackIndex != -1) advance = ceilf(advance); // Check to see if the next character is a "rounding hack character", if so, adjust the // width so that the total run width will be on an integer boundary. int position = m_currentCharacter + len; bool lastGlyph = (k == glyphs.size() - 1) && (m_run.rtl() ? i == 0 : i == m_items.size() - 2) && (position >= m_end); if ((m_run.applyWordRounding() && roundingHackWordBoundaries[k] != -1) || (m_run.applyRunRounding() && lastGlyph)) { float totalWidth = m_runWidthSoFar + advance; advance += ceilf(totalWidth) - totalWidth; } m_runWidthSoFar += advance; // FIXME: We need to take the GOFFSETS for combining glyphs and store them in the glyph buffer // as well, so that when the time comes to draw those glyphs, we can apply the appropriate // translation. if (glyphBuffer) { FloatSize size(offsetX, -offsetY); glyphBuffer->add(glyph, fontData, advance, &size); } // Mutate the glyph array to contain our altered advances. if (m_computingOffsetPosition) advances[k] = advance; } while (m_computingOffsetPosition && m_offsetX >= leftEdge && m_offsetX < m_runWidthSoFar) { // The position is somewhere inside this run. int trailing = 0; ScriptXtoCP(m_offsetX - leftEdge, clusters.size(), glyphs.size(), clusters.data(), visualAttributes.data(), advances.data(), &item.a, &m_offsetPosition, &trailing); if (trailing && m_includePartialGlyphs && m_offsetPosition < len - 1) { m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; m_offsetX += m_run.rtl() ? -trailing : trailing; } else { m_computingOffsetPosition = false; m_offsetPosition += m_currentCharacter + m_items[i].iCharPos; if (trailing && m_includePartialGlyphs) m_offsetPosition++; return false; } } return true; }
static bool MCTextLayoutPlaceItem(MCTextLayoutState& self, SCRIPT_ANALYSIS p_analysis, const unichar_t *p_chars, uint32_t p_char_count, const uint16_t *p_clusters, const uint16_t *p_glyphs, const SCRIPT_VISATTR *p_attrs, uint32_t p_glyph_count, MCTextLayoutFont *p_font) { bool t_success; t_success = true; MCTextLayoutRun *t_run; if (self . run_count != 0) t_run = &self . runs[self . run_count - 1]; else t_run = nil; // We need to append a new run if the font or embedding level has changed. if (t_run == nil || t_run -> font != p_font || t_run -> embedding_level != p_analysis . s . uBidiLevel) { if (self . run_count + 1 > self . run_limit) t_success = MCMemoryResizeArray(self . run_limit + 8, self . runs, self . run_limit); if (t_success) { t_run = &self . runs[self . run_count]; t_run -> font = p_font; t_run -> embedding_level = p_analysis . s . uBidiLevel; self . run_count += 1; } } // Allocate the cluster, glyph, goffset and advance arrays that we need. if (t_success) t_success = MCMemoryReallocate(t_run -> chars, (t_run -> char_count + p_char_count) * sizeof(unichar_t), t_run -> chars) && MCMemoryReallocate(t_run -> clusters, (t_run -> char_count + p_char_count) * sizeof(uint16_t), t_run -> clusters) && MCMemoryReallocate(t_run -> glyphs, (t_run -> glyph_count + p_glyph_count) * sizeof(uint16_t), t_run -> glyphs) && MCMemoryReallocate(t_run -> goffsets, (t_run -> glyph_count + p_glyph_count) * sizeof(GOFFSET), t_run -> goffsets) && MCMemoryReallocate(t_run -> advances, (t_run -> glyph_count + p_glyph_count) * sizeof(int), t_run -> advances); // Run the script placement method if (t_success) { SelectObject(self . dc, p_font -> handle); if (ScriptPlace( self . dc, &p_font -> cache, p_glyphs, p_glyph_count, p_attrs, &p_analysis, t_run -> advances + t_run -> glyph_count, t_run -> goffsets + t_run -> glyph_count, nil) != S_OK) t_success = false; } // Fill in the span structure if (t_success) { MCMemoryCopy(t_run -> chars + t_run -> char_count, p_chars, sizeof(unichar_t) * p_char_count); if ((p_analysis . s . uBidiLevel & 1) == 0) { MCMemoryCopy(t_run -> glyphs + t_run -> glyph_count, p_glyphs, sizeof(uint16_t) * p_glyph_count); MCMemoryCopy(t_run -> clusters + t_run -> char_count, p_clusters, sizeof(uint16_t) * p_char_count); } else { // In this case things are going Right to Left, so remap into visual order. for(uint32_t i = 0; i < p_char_count; i++) t_run -> clusters[i + t_run -> char_count] = p_glyph_count - p_clusters[i] - 1; for(uint32_t i = 0; i < p_glyph_count; i++) t_run -> glyphs[i + t_run -> glyph_count] = p_glyphs[p_glyph_count - i - 1]; for(uint32_t i = 0; i < p_glyph_count / 2; i++) { MCSwap(t_run -> advances[i + t_run -> glyph_count], t_run -> advances[t_run -> glyph_count + (p_glyph_count - i - 1)]); MCSwap(t_run -> goffsets[i + t_run -> glyph_count], t_run -> goffsets[t_run -> glyph_count + (p_glyph_count - i - 1)]); } } // As we might have elided a span, we need to shift all the cluster offsets up by the current // glyph count. for(uint32_t i = 0; i < p_char_count; i++) t_run -> clusters[i + t_run -> char_count] += t_run -> glyph_count; t_run -> char_count += p_char_count; t_run -> glyph_count += p_glyph_count; } return t_success; }
static gboolean itemize_shape_and_place (PangoFont *font, HDC hdc, wchar_t *wtext, int wlen, const PangoAnalysis *analysis, PangoGlyphString *glyphs) { int i; int item, nitems, item_step; int itemlen, glyphix, nglyphs; SCRIPT_CONTROL control; SCRIPT_STATE state; SCRIPT_ITEM items[100]; double scale = pango_win32_font_get_metrics_factor (font); HFONT hfont = _pango_win32_font_get_hfont (font); static GHashTable *script_cache_hash = NULL; if (!script_cache_hash) script_cache_hash = g_hash_table_new (g_int64_hash, g_int64_equal); memset (&control, 0, sizeof (control)); memset (&state, 0, sizeof (state)); control.uDefaultLanguage = make_langid (analysis->language); state.uBidiLevel = analysis->level; #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print (G_STRLOC ": ScriptItemize: uDefaultLanguage:%04x uBidiLevel:%d\n", control.uDefaultLanguage, state.uBidiLevel); #endif if (ScriptItemize (wtext, wlen, G_N_ELEMENTS (items), &control, NULL, items, &nitems)) { #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print ("ScriptItemize failed\n"); #endif return FALSE; } #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print ("%d items:\n", nitems); #endif if (analysis->level % 2) { item = nitems - 1; item_step = -1; } else { item = 0; item_step = 1; } for (i = 0; i < nitems; i++, item += item_step) { WORD iglyphs[1000]; WORD log_clusters[1000]; SCRIPT_VISATTR visattrs[1000]; int advances[1000]; GOFFSET offsets[1000]; ABC abc; gint32 script = items[item].a.eScript; int ng; int char_offset; SCRIPT_CACHE *script_cache; gint64 font_and_script_key; memset (advances, 0, sizeof (advances)); memset (offsets, 0, sizeof (offsets)); memset (&abc, 0, sizeof (abc)); /* Note that itemlen is number of wchar_t's i.e. surrogate pairs * count as two! */ itemlen = items[item+1].iCharPos - items[item].iCharPos; char_offset = items[item].iCharPos; #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print (" Item %d: iCharPos=%d eScript=%d (%s) %s%s%s%s%s%s%s wchar_t %d--%d (%d)\n", item, items[item].iCharPos, script, lang_name (scripts[script]->langid), scripts[script]->fComplex ? "complex" : "simple", items[item].a.fRTL ? " fRTL" : "", items[item].a.fLayoutRTL ? " fLayoutRTL" : "", items[item].a.fLinkBefore ? " fLinkBefore" : "", items[item].a.fLinkAfter ? " fLinkAfter" : "", items[item].a.fLogicalOrder ? " fLogicalOrder" : "", items[item].a.fNoGlyphIndex ? " fNoGlyphIndex" : "", items[item].iCharPos, items[item+1].iCharPos-1, itemlen); #endif /* Create a hash key based on hfont and script engine */ font_and_script_key = (((gint64) ((gint32) hfont)) << 32) | script; /* Get the script cache for this hfont and script */ script_cache = g_hash_table_lookup (script_cache_hash, &font_and_script_key); if (!script_cache) { gint64 *key_n; SCRIPT_CACHE *new_script_cache; key_n = g_new (gint64, 1); *key_n = font_and_script_key; new_script_cache = g_new0 (SCRIPT_CACHE, 1); script_cache = new_script_cache; /* Insert the new value */ g_hash_table_insert (script_cache_hash, key_n, new_script_cache); #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print (" New SCRIPT_CACHE for font %p and script %d\n", hfont, script); #endif } items[item].a.fRTL = analysis->level % 2; if (ScriptShape (hdc, script_cache, wtext + items[item].iCharPos, itemlen, G_N_ELEMENTS (iglyphs), &items[item].a, iglyphs, log_clusters, visattrs, &nglyphs)) { #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print ("pango-basic-win32: ScriptShape failed\n"); #endif return FALSE; } #ifdef BASIC_WIN32_DEBUGGING dump_glyphs_and_log_clusters (items[item].a.fRTL, itemlen, items[item].iCharPos, log_clusters, iglyphs, nglyphs); #endif ng = glyphs->num_glyphs; pango_glyph_string_set_size (glyphs, ng + nglyphs); set_up_pango_log_clusters (wtext + items[item].iCharPos, items[item].a.fRTL, itemlen, log_clusters, nglyphs, glyphs->log_clusters + ng, char_offset); if (ScriptPlace (hdc, script_cache, iglyphs, nglyphs, visattrs, &items[item].a, advances, offsets, &abc)) { #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) g_print ("pango-basic-win32: ScriptPlace failed\n"); #endif return FALSE; } for (glyphix = 0; glyphix < nglyphs; glyphix++) { if (iglyphs[glyphix] != 0) { glyphs->glyphs[ng+glyphix].glyph = iglyphs[glyphix]; glyphs->glyphs[ng+glyphix].geometry.width = floor (0.5 + scale * advances[glyphix]); glyphs->glyphs[ng+glyphix].geometry.x_offset = floor (0.5 + scale * offsets[glyphix].du); glyphs->glyphs[ng+glyphix].geometry.y_offset = floor (0.5 + scale * offsets[glyphix].dv); } else { PangoRectangle logical_rect; /* Should pass actual char that was not found to * PANGO_GET_UNKNOWN_GLYPH(), but a bit hard to * find out that at this point, so cheat and use 0. */ PangoGlyph unk = PANGO_GET_UNKNOWN_GLYPH (0); glyphs->glyphs[ng+glyphix].glyph = unk; pango_font_get_glyph_extents (font, unk, NULL, &logical_rect); glyphs->glyphs[ng+glyphix].geometry.width = logical_rect.width; glyphs->glyphs[ng+glyphix].geometry.x_offset = 0; glyphs->glyphs[ng+glyphix].geometry.y_offset = 0; } } } #ifdef BASIC_WIN32_DEBUGGING if (pango_win32_debug) { g_print (" Pango log_clusters (level:%d), char index:", analysis->level); for (glyphix = 0; glyphix < glyphs->num_glyphs; glyphix++) g_print ("%d ", glyphs->log_clusters[glyphix]); g_print ("\n"); } #endif return TRUE; }