Ejemplo n.º 1
0
static void layout_row( ME_DisplayItem *start, const ME_DisplayItem *end )
{
    ME_DisplayItem *p;
    int i, num_runs = 0;
    int buf[16 * 5]; /* 5 arrays - 4 of int & 1 of BYTE, alloc space for 5 of ints */
    int *vis_to_log = buf, *log_to_vis, *widths, *pos;
    BYTE *levels;
    BOOL found_black = FALSE;

    for (p = end->prev; p != start->prev; p = p->prev)
    {
        if (p->type == diRun)
        {
            if (!found_black) found_black = !(p->member.run.nFlags & (MERF_WHITESPACE | MERF_ENDPARA));
            if (found_black) num_runs++;
        }
    }

    TRACE("%d runs\n", num_runs);
    if (!num_runs) return;

    if (num_runs > sizeof(buf) / (sizeof(buf[0]) * 5))
        vis_to_log = heap_alloc( num_runs * sizeof(int) * 5 );

    log_to_vis = vis_to_log + num_runs;
    widths = vis_to_log + 2 * num_runs;
    pos = vis_to_log + 3 * num_runs;
    levels = (BYTE*)(vis_to_log + 4 * num_runs);

    for (i = 0, p = start; i < num_runs; p = p->next)
    {
        if (p->type == diRun)
        {
            levels[i] = p->member.run.script_analysis.s.uBidiLevel;
            widths[i] = p->member.run.nWidth;
            TRACE( "%d: level %d width %d\n", i, levels[i], widths[i] );
            i++;
        }
    }

    ScriptLayout( num_runs, levels, vis_to_log, log_to_vis );

    pos[0] = start->member.run.para->pt.x;
    for (i = 1; i < num_runs; i++)
        pos[i] = pos[i - 1] + widths[ vis_to_log[ i - 1 ] ];

    for (i = 0, p = start; i < num_runs; p = p->next)
    {
        if (p->type == diRun)
        {
            p->member.run.pt.x = pos[ log_to_vis[ i ] ];
            TRACE( "%d: x = %d\n", i, p->member.run.pt.x );
            i++;
        }
    }

    if (vis_to_log != buf) heap_free( vis_to_log );
}
Ejemplo n.º 2
0
/*------------------------------------------------------------------------
    Function: BidiLines

    Implements the Line-by-Line phases of the Unicode Bidi Algorithm

      Input:     Count of characters
                 Array of character directions

    Inp/Out: Input text
             Array of levels

------------------------------------------------------------------------*/
static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
                      BYTE * plevelLine, int cchPara, const BOOL * pbrk)
{
    int cchLine = 0;
    int done = 0;
    int *run;

    run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
    if (!run)
    {
        WARN("Out of memory\n");
        return;
    }

    do
    {
        /* break lines at LS */
        cchLine = resolveLines(pszLine, pbrk, cchPara);

        /* resolve whitespace */
        resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);

        if (pszOutLine)
        {
            int i;
            /* reorder each line in place */
            ScriptLayout(cchLine, plevelLine, NULL, run);
            for (i = 0; i < cchLine; i++)
                pszOutLine[done+run[i]] = pszLine[i];
        }

        pszLine += cchLine;
        plevelLine += cchLine;
        pbrk += pbrk ? cchLine : 0;
        pclsLine += cchLine;
        cchPara -= cchLine;
        done += cchLine;

    } while (cchPara);

    HeapFree(GetProcessHeap(), 0, run);
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
bool MCTextLayout(const unichar_t *p_chars, uint32_t p_char_count, MCFontStruct *p_font, MCTextLayoutCallback p_callback, void *p_context)
{
	bool t_success;
	t_success = true;

	// The state structure we use to record the list of runs and the dc we use
	// for processing.
	MCTextLayoutState self;
	MCMemoryClear(&self, sizeof(MCTextLayoutState));

	if (t_success)
	{
		self . dc = CreateCompatibleDC(nil);
		if (self . dc == nil)
			t_success = false;
	}

	// Fetch a layout font for the provided HFONT.
	if (t_success)
		t_success = MCTextLayoutFontFromHFONT(p_font -> fid, self . primary_font);

	// First thing we need to do is itemize the input string. The ScriptItemize
	// function splits up the chars into runs, each run being potentially
	// processed differently by Uniscribe.
	// Unfortunately, there is no way to predict for an arbitrary string how
	// many items might be generated, nor is the ScriptItemize function
	// incremental, thus we must loop with an every increasing buffer until
	// we have enough room.
	SCRIPT_ITEM *t_items;
	uint32_t t_item_limit, t_item_count;
	SCRIPT_STATE t_script_state;
	SCRIPT_CONTROL t_script_control;
	t_items = nil;
	t_item_limit = 0;
	t_item_count = 0;
	MCMemoryClear(&t_script_state, sizeof(SCRIPT_STATE));
	MCMemoryClear(&t_script_control, sizeof(SCRIPT_CONTROL));
	while(t_success)
	{
		// Increase the item array by 32 each time
		if (t_success)
			t_success = MCMemoryResizeArray(t_item_limit + 32, t_items, t_item_limit);
		
		// Attempt to itemize
		HRESULT t_result;
		if (t_success)
		{
			t_result = ScriptItemize(p_chars, p_char_count, t_item_limit, &t_script_control, &t_script_state, t_items, (int *)&t_item_count);
			if (t_result != S_OK && t_result != E_OUTOFMEMORY)
				t_success = false;
		}

		if (t_success && t_result == S_OK)
			break;
	}

	// Next we loop through the items one by one, processing them as we go, this
	// process is slightly recursive - LayoutItem may recurse to fill in any
	// 'holes' caused by glyphs not in the primary font.
	for(uint32_t i = 0; i < t_item_count && t_success; i++)
		t_success = MCTextLayoutStyleItem(
						self,
						t_items[i] . a,
						p_chars + t_items[i] . iCharPos,
						t_items[i + 1] . iCharPos - t_items[i] . iCharPos,
						self . primary_font);

	// At this point we should have an array of runs to render. First though we
	// need to compute the visual to logical mapping.
	uint8_t *t_levels;
	int *t_map;
	t_levels = nil;
	t_map = nil;
	if (t_success)
		t_success =
			MCMemoryNewArray(self . run_count, t_levels) &&
			MCMemoryNewArray(self . run_count, t_map);

	// Work out the run mapping, but only if we have runs to map!
	if (t_success && self . run_count > 0)
	{
		for(uint32_t i = 0; i < self . run_count; i++)
			t_levels[i] = self . runs[i] . embedding_level;

		if (ScriptLayout(self . run_count, t_levels, t_map, nil) != S_OK)
			t_success = false;
	}

	// Now we have the mapping we loop through the runs in the correct order
	// dispatching them to the callback.
	if (t_success && p_callback != nil)
	{
		double t_x;
		t_x = 0.0;
		for(uint32_t i = 0; i < self . run_count && t_success; i++)
		{
			MCTextLayoutRun *t_run;
			t_run = &self . runs[t_map[i]];

			// Allocate a temporary array for the glyph structures
			MCTextLayoutGlyph *t_glyphs;
			t_glyphs = nil;
			if (t_success)
				t_success = MCMemoryNewArray(t_run -> glyph_count, t_glyphs);

			if (t_success)
			{
				// Compute the position for each glyph, keeping a running
				// total of the advance width.
				for(uint32_t i = 0; i < t_run -> glyph_count; i++)
				{
					t_glyphs[i] . index = t_run -> glyphs[i];
					t_glyphs[i] . x = t_x + t_run -> goffsets[i] . du;
					t_glyphs[i] . y = t_run -> goffsets[i] . dv;
					t_x += t_run -> advances[i];
				}

				// Dispatch the span to the callback.
				MCTextLayoutSpan t_span;
				t_span . chars = t_run -> chars;
				t_span . clusters = t_run -> clusters;
				t_span . char_count = t_run -> char_count;
				t_span . glyphs = t_glyphs;
				t_span . glyph_count = t_run -> glyph_count;
				t_span . font = t_run -> font -> handle;
				t_success = p_callback(p_context, &t_span);
			}

			// Free the temporary array.
			MCMemoryDeleteArray(t_glyphs);
		}
	}

	// Free all the arrays and other resources that have been allocated.
	MCMemoryDeleteArray(t_map);
	MCMemoryDeleteArray(t_levels);

	for(uint32_t i = 0; i < self . run_count; i++)
	{
		MCMemoryDeallocate(self . runs[i] . chars);
		MCMemoryDeallocate(self . runs[i] . clusters);
		MCMemoryDeallocate(self . runs[i] . glyphs);
		MCMemoryDeallocate(self . runs[i] . advances);
		MCMemoryDeallocate(self . runs[i] . goffsets);
	}

	MCMemoryDeleteArray(self . runs);
	
	MCMemoryDeleteArray(t_items);

	if (self . dc != nil)
		DeleteDC(self . dc);

	return t_success;
}