Exemplo n.º 1
0
BOOL doScriptShape(struct scriptShapeParams *p)
{
	HRESULT hr;

	hr = ScriptShape(p->hdc, p->psc, p->pwcChars, p->cChars,
		p->cMaxGlyphs, p->psa,
		p->pwOutGlyphs, p->pwLogClust, p->psva,
		p->pcGlyphs);
	if (hr == S_OK)
		return TRUE;
	if (hr != E_OUTOFMEMORY)
		die("error calling ScriptShape()", hr);
	return FALSE;
}
Exemplo n.º 2
0
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;
}
Exemplo n.º 3
0
bool UniscribeController::shape(const UChar* str, int len, SCRIPT_ITEM item, const Font* fontData,
                                Vector<WORD>& glyphs, Vector<WORD>& clusters,
                                Vector<SCRIPT_VISATTR>& visualAttributes)
{
    HWndDC hdc;
    HFONT oldFont = 0;
    HRESULT shapeResult = E_PENDING;
    int glyphCount = 0;

    if (!fontData)
        return false;

    do {
        shapeResult = ScriptShape(hdc, fontData->scriptCache(), str, len, glyphs.size(), &item.a,
                                  glyphs.data(), clusters.data(), visualAttributes.data(), &glyphCount);
        if (shapeResult == 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 ScriptShape.
            ASSERT(!hdc);
            hdc.setHWnd(0);
            HFONT hfont = fontData->platformData().hfont();
            oldFont = (HFONT)SelectObject(hdc, hfont);
        } else if (shapeResult == E_OUTOFMEMORY) {
            // Need to resize our buffers.
            glyphs.resize(glyphs.size() * 2);
            visualAttributes.resize(glyphs.size());
        }
    } while (shapeResult == E_PENDING || shapeResult == E_OUTOFMEMORY);

    if (hdc)
        SelectObject(hdc, oldFont);

    if (FAILED(shapeResult))
        return false;

    glyphs.shrink(glyphCount);
    visualAttributes.shrink(glyphCount);

    return true;
}
    HRESULT Shape() {
        HRESULT rv;
        HDC shapeDC = nullptr;

        const PRUnichar *str = mAlternativeString ? mAlternativeString : mItemString;

        mScriptItem->a.fLogicalOrder = true; 
        SCRIPT_ANALYSIS sa = mScriptItem->a;

        while (true) {

            rv = ScriptShape(shapeDC, mShaper->ScriptCache(),
                             str, mItemLength,
                             mMaxGlyphs, &sa,
                             mGlyphs.Elements(), mClusters.Elements(),
                             mAttr.Elements(), &mNumGlyphs);

            if (rv == E_OUTOFMEMORY) {
                mMaxGlyphs *= 2;
                if (!mGlyphs.SetLength(mMaxGlyphs) ||
                    !mAttr.SetLength(mMaxGlyphs)) {
                    return E_OUTOFMEMORY;
                }
                continue;
            }

            // Uniscribe can't do shaping with some fonts, so it sets the 
            // fNoGlyphIndex flag in the SCRIPT_ANALYSIS structure to indicate
            // this.  This occurs with CFF fonts loaded with 
            // AddFontMemResourceEx but it's not clear what the other cases
            // are. We return an error so our caller can try fallback shaping.
            // see http://msdn.microsoft.com/en-us/library/ms776520(VS.85).aspx

            if (sa.fNoGlyphIndex) {
                return GDI_ERROR;
            }

            if (rv == E_PENDING) {
                if (shapeDC == mDC) {
                    // we already tried this once, something failed, give up
                    return E_PENDING;
                }

                SelectFont();

                shapeDC = mDC;
                continue;
            }

            // http://msdn.microsoft.com/en-us/library/dd368564(VS.85).aspx:
            // Uniscribe will return this if "the font corresponding to the
            // DC does not support the script required by the run...".
            // In this case, we'll set the script code to SCRIPT_UNDEFINED
            // and try again, so that we'll at least get glyphs even though
            // they won't necessarily have proper shaping.
            // (We probably shouldn't have selected this font at all,
            // but it's too late to fix that here.)
            if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
                sa.eScript = SCRIPT_UNDEFINED;
                NS_WARNING("Uniscribe says font does not support script needed");
                continue;
            }

            // Prior to Windows 7, Uniscribe didn't support Ideographic Variation
            // Selectors. Replace the UVS glyph manually.
            if (mIVS) {
                uint32_t lastChar = str[mItemLength - 1];
                if (NS_IS_LOW_SURROGATE(lastChar)
                    && NS_IS_HIGH_SURROGATE(str[mItemLength - 2])) {
                    lastChar = SURROGATE_TO_UCS4(str[mItemLength - 2], lastChar);
                }
                uint16_t glyphId = mShaper->GetFont()->GetUVSGlyph(lastChar, mIVS);
                if (glyphId) {
                    mGlyphs[mNumGlyphs - 1] = glyphId;
                }
            }

            return rv;
        }
    }
bool UniscribeHelper::shape(const UChar* input,
                            int itemLength,
                            int numGlyphs,
                            SCRIPT_ITEM& run,
                            Shaping& shaping)
{
    HFONT hfont = m_hfont;
    SCRIPT_CACHE* scriptCache = m_scriptCache;
    SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
    int ascent = m_ascent;
    HDC tempDC = 0;
    HGDIOBJ oldFont = 0;
    HRESULT hr;
    // When used to fill up glyph pages for simple scripts in non-BMP,
    // we don't want any font fallback in this class. The simple script
    // font path can take care of font fallback.
    bool lastFallbackTried = m_disableFontFallback;
    bool result;

    int generatedGlyphs = 0;

    // In case HFONT passed in ctor cannot render this run, we have to scan
    // other fonts from the beginning of the font list.
    resetFontIndex();

    // Compute shapes.
    while (true) {
        shaping.m_logs.resize(itemLength);
        shaping.m_glyphs.resize(numGlyphs);
        shaping.m_visualAttributes.resize(numGlyphs);

#ifdef PURIFY
        // http://code.google.com/p/chromium/issues/detail?id=5309
        // Purify isn't able to track the assignments that ScriptShape makes to
        // shaping.m_glyphs. Consequently, any bytes with value 0xCD that it
        // writes, will be considered un-initialized data.
        //
        // This hack avoid the false-positive UMRs by marking the buffer as
        // initialized.
        //
        // FIXME: A better solution would be to use Purify's API and mark only
        // the populated range as initialized:
        //
        //     PurifyMarkAsInitialized(
        //         &shaping.m_glyphs[0],
        //         sizeof(shaping.m_glyphs[0] * generatedGlyphs);

        ZeroMemory(&shaping.m_glyphs[0],
                   sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
#endif

        // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
        // here. Is that what we want? It will display control characters.
        hr = ScriptShape(tempDC, scriptCache, input, itemLength,
                         numGlyphs, &run.a,
                         &shaping.m_glyphs[0], &shaping.m_logs[0],
                         &shaping.m_visualAttributes[0], &generatedGlyphs);
        if (hr == E_PENDING) {
            // Allocate the DC.
            tempDC = GetDC(0);
            oldFont = SelectObject(tempDC, hfont);
            continue;
        } else if (hr == E_OUTOFMEMORY) {
            numGlyphs *= 2;
            continue;
        } else if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(&shaping.m_glyphs[0], generatedGlyphs, fontProperties)))
            break;

        // The current font can't render this run. clear DC and try
        // next font.
        if (tempDC) {
            SelectObject(tempDC, oldFont);
            ReleaseDC(0, tempDC);
            tempDC = 0;
        }

        if (!m_disableFontFallback &&
            nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
            // The primary font does not support this run. Try next font.
            // In case of web page rendering, they come from fonts specified in
            // CSS stylesheets.
            continue;
        } else if (!lastFallbackTried) {
            lastFallbackTried = true;

            // Generate a last fallback font based on the script of
            // a character to draw while inheriting size and styles
            // from the primary font
            if (!m_logfont.lfFaceName[0])
                setLogFontAndStyle(m_hfont, &m_logfont, &m_style);

            // TODO(jungshik): generic type should come from webkit for
            // UniscribeHelperTextRun (a derived class used in webkit).
            const UChar *family = getFallbackFamily(input, itemLength,
                FontDescription::StandardFamily, 0, 0);
            bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
                                              &ascent, &hfont, &scriptCache);

            if (!fontOk) {
                // If this GetDerivedFontData is called from the renderer it
                // might fail because the sandbox is preventing it from opening
                // the font files.  If we are running in the renderer,
                // TryToPreloadFont is overridden to ask the browser to preload
                // the font for us so we can access it.
                tryToPreloadFont(hfont);

                // Try again.
                fontOk = getDerivedFontData(family, m_style, &m_logfont,
                                             &ascent, &hfont, &scriptCache);
                ASSERT(fontOk);
            }

            // TODO(jungshik) : Currently GetDerivedHFont always returns a
            // a valid HFONT, but in the future, I may change it to return 0.
            ASSERT(hfont);

            // We don't need a font_properties for the last resort fallback font
            // because we don't have anything more to try and are forced to
            // accept empty glyph boxes. If we tried a series of fonts as
            // 'last-resort fallback', we'd need it, but currently, we don't.
            continue;
        } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
            run.a.eScript = SCRIPT_UNDEFINED;
            continue;
        } else if (FAILED(hr)) {
            // Error shaping.
            generatedGlyphs = 0;
            result = false;
            goto cleanup;
        }
    }

    // Sets Windows font data for this run to those corresponding to
    // a font supporting this run. we don't need to store font_properties
    // because it's not used elsewhere.
    shaping.m_hfont = hfont;
    shaping.m_scriptCache = scriptCache;

    // The ascent of a font for this run can be different from
    // that of the primary font so that we need to keep track of
    // the difference per run and take that into account when calling
    // ScriptTextOut in |draw|. Otherwise, different runs rendered by
    // different fonts would not be aligned vertically.
    shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
    result = true;

  cleanup:
    shaping.m_glyphs.resize(generatedGlyphs);
    shaping.m_visualAttributes.resize(generatedGlyphs);
    shaping.m_advance.resize(generatedGlyphs);
    shaping.m_offsets.resize(generatedGlyphs);
    if (tempDC) {
        SelectObject(tempDC, oldFont);
        ReleaseDC(0, tempDC);
    }
    // On failure, our logs don't mean anything, so zero those out.
    if (!result)
        shaping.m_logs.clear();

    return result;
}
Exemplo n.º 6
0
/*************************************************************
 *    BIDI_Reorder
 *
 *     Returns TRUE if reordering was required and done.
 */
BOOL BIDI_Reorder(
    HDC hDC,        /*[in] Display DC */
    LPCWSTR lpString,       /* [in] The string for which information is to be returned */
    INT uCount,     /* [in] Number of WCHARs in string. */
    DWORD dwFlags,  /* [in] GetCharacterPlacement compatible flags specifying how to process the string */
    DWORD dwWineGCP_Flags,       /* [in] Wine internal flags - Force paragraph direction */
    LPWSTR lpOutString, /* [out] Reordered string */
    INT uCountOut,  /* [in] Size of output buffer */
    UINT *lpOrder, /* [out] Logical -> Visual order map */
    WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
    INT *cGlyphs /* [out] number of glyphs generated */
)
{
    WORD *chartype;
    BYTE *levels;
    unsigned i, done, glyph_i;
    BOOL is_complex;

    int maxItems;
    int nItems;
    SCRIPT_CONTROL Control;
    SCRIPT_STATE State;
    SCRIPT_ITEM *pItems;
    HRESULT res;
    SCRIPT_CACHE psc = NULL;
    WORD *run_glyphs = NULL;
    WORD *pwLogClust = NULL;
    SCRIPT_VISATTR *psva = NULL;
    DWORD cMaxGlyphs = 0;
    BOOL  doGlyphs = TRUE;

    TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
          debugstr_wn(lpString, uCount), uCount, dwFlags,
          lpOutString, lpOrder);

    memset(&Control, 0, sizeof(Control));
    memset(&State, 0, sizeof(State));
    if (lpGlyphs)
        *lpGlyphs = NULL;

    if (!(dwFlags & GCP_REORDER))
    {
        FIXME("Asked to reorder without reorder flag set\n");
        return FALSE;
    }

    if (lpOutString && uCountOut < uCount)
    {
        FIXME("lpOutString too small\n");
        return FALSE;
    }

    chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
    if (!chartype)
    {
        WARN("Out of memory\n");
        return FALSE;
    }

    if (lpOutString)
        memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));

    is_complex = FALSE;
    for (i = 0; i < uCount && !is_complex; i++)
    {
        if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
                (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
                (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
            is_complex = TRUE;
    }

    /* Verify reordering will be required */
    if ((WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags&WINE_GCPW_DIR_MASK)) ||
            ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL))
        State.uBidiLevel = 1;
    else if (!is_complex)
    {
        done = 1;
        classify(lpString, chartype, uCount);
        for (i = 0; i < uCount; i++)
            switch (chartype[i])
            {
            case R:
            case AL:
            case RLE:
            case RLO:
                done = 0;
                break;
            }
        if (done)
        {
            HeapFree(GetProcessHeap(), 0, chartype);
            if (lpOrder)
            {
                for (i = 0; i < uCount; i++)
                    lpOrder[i] = i;
            }
            return TRUE;
        }
    }

    levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
    if (!levels)
    {
        WARN("Out of memory\n");
        HeapFree(GetProcessHeap(), 0, chartype);
        return FALSE;
    }

    maxItems = 5;
    pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
    if (!pItems)
    {
        WARN("Out of memory\n");
        HeapFree(GetProcessHeap(), 0, chartype);
        HeapFree(GetProcessHeap(), 0, levels);
        return FALSE;
    }

    if (lpGlyphs)
    {
        cMaxGlyphs = 1.5 * uCount + 16;
        run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
        if (!run_glyphs)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            return FALSE;
        }
        pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
        if (!pwLogClust)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            HeapFree(GetProcessHeap(), 0, run_glyphs);
            return FALSE;
        }
        psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * uCount);
        if (!psva)
        {
            WARN("Out of memory\n");
            HeapFree(GetProcessHeap(), 0, chartype);
            HeapFree(GetProcessHeap(), 0, levels);
            HeapFree(GetProcessHeap(), 0, pItems);
            HeapFree(GetProcessHeap(), 0, run_glyphs);
            HeapFree(GetProcessHeap(), 0, pwLogClust);
            return FALSE;
        }
    }

    done = 0;
    glyph_i = 0;
    while (done < uCount)
    {
        unsigned j;
        classify(lpString + done, chartype, uCount - done);
        /* limit text to first block */
        i = resolveParagraphs(chartype, uCount - done);
        for (j = 0; j < i; ++j)
            switch(chartype[j])
            {
            case B:
            case S:
            case WS:
            case ON:
                chartype[j] = N;
            default:
                continue;
            }

        if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)
            State.uBidiLevel = 1;
        else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR)
            State.uBidiLevel = 0;

        if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK)
        {
            for (j = 0; j < i; ++j)
                if (chartype[j] == L)
                {
                    State.uBidiLevel = 0;
                    break;
                }
                else if (chartype[j] == R || chartype[j] == AL)
                {
                    State.uBidiLevel = 1;
                    break;
                }
        }

        res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
        while (res == E_OUTOFMEMORY)
        {
            maxItems = maxItems * 2;
            pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(SCRIPT_ITEM) * maxItems);
            if (!pItems)
            {
                WARN("Out of memory\n");
                HeapFree(GetProcessHeap(), 0, chartype);
                HeapFree(GetProcessHeap(), 0, levels);
                return FALSE;
            }
            res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
        }

        if (lpOutString || lpOrder)
            for (j = 0; j < nItems; j++)
            {
                int k;
                for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
                    levels[k] = pItems[j].a.s.uBidiLevel;
            }

        if (lpOutString)
        {
            /* assign directional types again, but for WS, S this time */
            classify(lpString + done, chartype, i);

            BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
                      chartype, levels, i, 0);
        }

        if (lpOrder)
        {
            int k, lastgood;
            for (j = lastgood = 0; j < i; ++j)
                if (levels[j] != levels[lastgood])
                {
                    --j;
                    if (odd(levels[lastgood]))
                        for (k = j; k >= lastgood; --k)
                            lpOrder[done + k] = done + j - k;
                    else
                        for (k = lastgood; k <= j; ++k)
                            lpOrder[done + k] = done + k;
                    lastgood = ++j;
                }
            if (odd(levels[lastgood]))
                for (k = j - 1; k >= lastgood; --k)
                    lpOrder[done + k] = done + j - 1 - k;
            else
                for (k = lastgood; k < j; ++k)
                    lpOrder[done + k] = done + k;
        }

        if (lpGlyphs && doGlyphs)
        {
            int j;
            BYTE runOrder[maxItems];
            int visOrder[maxItems];
            SCRIPT_ITEM *curItem;

            for (j = 0; j < nItems; j++)
                runOrder[j] = pItems[j].a.s.uBidiLevel;

            ScriptLayout(nItems, runOrder, visOrder, NULL);

            for (j = 0; j < nItems; j++)
            {
                int k;
                int cChars,cOutGlyphs;
                curItem = &pItems[visOrder[j]];

                cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;

                res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
                while (res == E_OUTOFMEMORY)
                {
                    cMaxGlyphs *= 2;
                    run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(WORD) * cMaxGlyphs);
                    if (!run_glyphs)
                    {
                        WARN("Out of memory\n");
                        HeapFree(GetProcessHeap(), 0, chartype);
                        HeapFree(GetProcessHeap(), 0, levels);
                        HeapFree(GetProcessHeap(), 0, pItems);
                        HeapFree(GetProcessHeap(), 0, psva);
                        HeapFree(GetProcessHeap(), 0, pwLogClust);
                        HeapFree(GetProcessHeap(), 0, *lpGlyphs);
                        ScriptFreeCache(&psc);
                        *lpGlyphs = NULL;
                        return FALSE;
                    }
                    res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
                }
                if (res)
                {
                    if (res == USP_E_SCRIPT_NOT_IN_FONT)
                        TRACE("Unable to shape with currently selected font\n");
                    else
                        FIXME("Unable to shape string (%x)\n",res);
                    j = nItems;
                    doGlyphs = FALSE;
                    HeapFree(GetProcessHeap(), 0, *lpGlyphs);
                    *lpGlyphs = NULL;
                }
                else
                {
                    if (*lpGlyphs)
                        *lpGlyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(WORD) * (glyph_i + cOutGlyphs));
                    else
                        *lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WORD) * (glyph_i + cOutGlyphs));
                    for (k = 0; k < cOutGlyphs; k++)
                        (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
                    glyph_i += cOutGlyphs;
                }
            }
        }

        done += i;
    }
    if (cGlyphs)
        *cGlyphs = glyph_i;

    HeapFree(GetProcessHeap(), 0, chartype);
    HeapFree(GetProcessHeap(), 0, levels);
    HeapFree(GetProcessHeap(), 0, pItems);
    HeapFree(GetProcessHeap(), 0, run_glyphs);
    HeapFree(GetProcessHeap(), 0, pwLogClust);
    HeapFree(GetProcessHeap(), 0, psva);
    ScriptFreeCache(&psc);
    return TRUE;
}
Exemplo n.º 7
0
static bool MCTextLayoutShapeItem(MCTextLayoutState& self, SCRIPT_ANALYSIS p_analysis, const unichar_t *p_chars, uint32_t p_char_count, MCTextLayoutFont *p_font, bool* p_try)
{
	bool t_success;
	t_success = true;

	// We do everything in logical order, doing any swizzling of glyphs at the
	// end.
	p_analysis . fLogicalOrder = true;

	// We start off by using the given font, however if this causes an issue
	// with shaping we fall back to the primary font.
	MCTextLayoutFont *t_layout_font;
	t_layout_font = p_font;

	// If the initial font is nil, then we use the primary font and undefined
	// script.
	if (t_layout_font == nil)
	{
		p_analysis . eScript = SCRIPT_UNDEFINED;
		t_layout_font = self . primary_font;
	}

	// Allocate a big enough cluster array for the item
	WORD *t_clusters;
	uint32_t t_cluster_limit;
	t_clusters = nil;
	t_cluster_limit = 0;
	if (t_success)
		t_success = MCMemoryResizeArray(p_char_count, t_clusters, t_cluster_limit);

	// Loop until we succeed in generating some glyphs - note that this loop
	// is a little hairy. It loops until we don't get E_OUTOFMEMORY, but
	// additionally, may loop with USP_E_SCRIPT_NOT_IN_FONT, in which case
	// t_script_font will have changed, as might p_analysis.
	WORD *t_glyphs;
	SCRIPT_VISATTR *t_attrs;
	uint32_t t_glyph_limit, t_attr_limit, t_glyph_count;
	t_glyphs = nil;
	t_attrs = nil;
	t_glyph_limit = 0;
	t_attr_limit = 0;
	t_glyph_count = 0;
	while(t_success)
	{
		// Make sure we have enough room in the glyph buffer
		uint32_t t_new_glyph_limit;
		t_new_glyph_limit = (t_glyph_limit == 0 ? p_char_count + p_char_count / 2 + 16 : t_glyph_limit + p_char_count / 2);
		t_success = 
			MCMemoryResizeArray(t_new_glyph_limit, t_glyphs, t_glyph_limit) &&
			MCMemoryResizeArray(t_new_glyph_limit, t_attrs, t_attr_limit);

		// Now try to shape
		HRESULT t_result;
		if (t_success)
		{
			SelectObject(self . dc, t_layout_font -> handle);
			t_result = ScriptShape(self . dc, &t_layout_font -> cache, p_chars, p_char_count, t_glyph_limit, &p_analysis, t_glyphs, t_clusters, t_attrs, (int *)&t_glyph_count);
			if (t_result != S_OK && t_result != E_OUTOFMEMORY && t_result != USP_E_SCRIPT_NOT_IN_FONT)
				t_success = false;
		}

		// If the result was 'script not in font' then reprocess with the undefined
		// script with the primary font.
		if (t_success && t_result == USP_E_SCRIPT_NOT_IN_FONT)
		{
			// If we are just testing for shaping success, break on no font.
			if (p_try != nil)
				break;

			t_layout_font = self . primary_font;
			p_analysis . eScript = SCRIPT_UNDEFINED;
			continue;
		}

		// If the result is S_OK, we can break
		if (t_success && t_result == S_OK)
			break;
	}

	// Assuming nothing failed, we now have a sequence of glyphs to place (only
	// if we aren't just testing for shaping success).
	if (t_success && p_try == nil)
		t_success = MCTextLayoutPlaceItem(self, p_analysis, p_chars, p_char_count, t_clusters, t_glyphs, t_attrs, t_glyph_count, t_layout_font);
	
	// If we are testing for shaping success then scan the glyph buffer for
	// undefined glyphs.
	if (t_success && p_try != nil)
	{
		uint32_t t_undefined;
		t_undefined = 0;
		for(uint32_t i = 0; i < t_glyph_count; i++)
			if (t_glyphs[i] == t_layout_font -> default_glyph)
				t_undefined += 1;

		// We use the following heuristic - if the first glyph is undefined
		// then shaping failed, otherwise shaping only fails if *all* the
		// glyphs are undefined.
		*p_try = t_glyph_count != 0 && t_undefined != t_glyph_count && t_glyphs[0] != t_layout_font -> default_glyph;
	}

	// Free up the arrays we allocated - placing causes the contents to be
	// copied/processed into separate run buffers.
	MCMemoryDeleteArray(t_clusters);
	MCMemoryDeleteArray(t_glyphs);
	MCMemoryDeleteArray(t_attrs);

	return t_success;
}
Exemplo n.º 8
0
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;
}