Commit 7a0a4ce7 authored by Huw Davies's avatar Huw Davies Committed by Alexandre Julliard

riched20: Add uniscribe support.

parent 57d5b6fc
MODULE = riched20.dll
IMPORTLIB = riched20
IMPORTS = uuid ole32 oleaut32 imm32 user32 gdi32
IMPORTS = uuid usp10 ole32 oleaut32 imm32 user32 gdi32
EXTRADLLFLAGS = -Wl,--image-base,0x7ac00000
C_SRCS = \
......
......@@ -47,6 +47,7 @@
#include <richole.h>
#include "imm.h"
#include <textserv.h>
#include "usp10.h"
#include "wine/debug.h"
#include "wine/list.h"
......@@ -68,6 +69,7 @@ typedef struct tagME_Style
HFONT hFont; /* cached font for the style */
TEXTMETRICW tm; /* cached font metrics for the style */
int nRefs; /* reference count */
SCRIPT_CACHE script_cache;
} ME_Style;
typedef enum {
......@@ -134,6 +136,7 @@ typedef enum {
#define MEPF_CELL 0x04 /* The paragraph is nested in a cell */
#define MEPF_ROWSTART 0x08 /* Hidden empty paragraph at the start of the row */
#define MEPF_ROWEND 0x10 /* Visible empty paragraph at the end of the row */
#define MEPF_COMPLEX 0x20 /* Use uniscribe */
/******************************** structures *************************/
......@@ -150,6 +153,15 @@ typedef struct tagME_Run
int nAscent, nDescent; /* pixels above/below baseline */
POINT pt; /* relative to para's position */
REOBJECT *ole_obj; /* FIXME: should be a union with strText (at least) */
SCRIPT_ANALYSIS script_analysis;
int num_glyphs, max_glyphs;
WORD *glyphs;
SCRIPT_VISATTR *vis_attrs;
int *advances;
GOFFSET *offsets;
int max_clusters;
WORD *clusters;
} ME_Run;
typedef struct tagME_Border
......
......@@ -166,6 +166,8 @@ void ME_DestroyDisplayItem(ME_DisplayItem *item)
if (item->type==diRun)
{
if (item->member.run.ole_obj) ME_DeleteReObject(item->member.run.ole_obj);
heap_free( item->member.run.glyphs );
heap_free( item->member.run.clusters );
ME_ReleaseStyle(item->member.run.style);
}
FREE_OBJ(item);
......
......@@ -331,7 +331,12 @@ static void draw_text( ME_Context *c, ME_Run *run, int x, int y, BOOL selected,
old_text = SetTextColor( c->hDC, text_color );
if (selected) old_back = SetBkColor( c->hDC, back_color );
ExtTextOutW( c->hDC, x, y, selected ? ETO_OPAQUE : 0, sel_rect, text, run->len, NULL );
if (run->para->nFlags & MEPF_COMPLEX)
ScriptTextOut( c->hDC, &run->style->script_cache, x, y, selected ? ETO_OPAQUE : 0, sel_rect,
&run->script_analysis, NULL, 0, run->glyphs, run->num_glyphs, run->advances,
NULL, run->offsets );
else
ExtTextOutW( c->hDC, x, y, selected ? ETO_OPAQUE : 0, sel_rect, text, run->len, NULL );
if (selected) SetBkColor( c->hDC, old_back );
SetTextColor( c->hDC, old_text );
......
......@@ -295,6 +295,14 @@ ME_DisplayItem *ME_MakeRun(ME_Style *s, int nFlags)
item->member.run.nCharOfs = -1;
item->member.run.len = 0;
item->member.run.para = NULL;
item->member.run.num_glyphs = 0;
item->member.run.max_glyphs = 0;
item->member.run.glyphs = NULL;
item->member.run.vis_attrs = NULL;
item->member.run.advances = NULL;
item->member.run.offsets = NULL;
item->member.run.max_clusters = 0;
item->member.run.clusters = NULL;
ME_AddRefStyle(s);
return item;
}
......@@ -468,6 +476,18 @@ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BO
return 1;
}
if (run->para->nFlags & MEPF_COMPLEX)
{
int cp, trailing;
if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1;
ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis,
&cp, &trailing );
TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth,
run->script_analysis.fRTL, run->script_analysis.fLogicalOrder);
return closest ? cp + trailing : cp;
}
if (c->editor->cPasswordMask)
{
mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len );
......@@ -543,6 +563,14 @@ int ME_PointFromCharContext(ME_Context *c, ME_Run *pRun, int nOffset, BOOL visua
nOffset = 0;
}
if (pRun->para->nFlags & MEPF_COMPLEX)
{
int x;
ScriptCPtoX( nOffset, FALSE, pRun->len, pRun->num_glyphs, pRun->clusters,
pRun->vis_attrs, pRun->advances, &pRun->script_analysis, &x );
if (visual_order && pRun->script_analysis.fRTL) x = pRun->nWidth - x - 1;
return x;
}
if (c->editor->cPasswordMask)
{
mask_text = ME_MakeStringR(c->editor->cPasswordMask, pRun->len);
......@@ -592,8 +620,12 @@ SIZE ME_GetRunSizeCommon(ME_Context *c, const ME_Paragraph *para, ME_Run *run, i
* this is wasteful for MERF_NONTEXT runs, but that shouldn't matter
* in practice
*/
if (c->editor->cPasswordMask)
if (para->nFlags & MEPF_COMPLEX)
{
size.cx = run->nWidth;
}
else if (c->editor->cPasswordMask)
{
ME_String *szMasked = ME_MakeStringR(c->editor->cPasswordMask,nLen);
ME_GetTextExtent(c, szMasked->szData, nLen,run->style, &size);
......
......@@ -149,6 +149,7 @@ ME_Style *ME_MakeStyle(CHARFORMAT2W *style)
s->hFont = NULL;
memset(&s->tm, 0, sizeof(s->tm));
s->tm.tmAscent = -1;
s->script_cache = NULL;
all_refs++;
TRACE_(richedit_style)("ME_MakeStyle %p, total refs=%d\n", s, all_refs);
return s;
......@@ -435,6 +436,7 @@ static void ME_DestroyStyle(ME_Style *s) {
DeleteObject(s->hFont);
s->hFont = NULL;
}
ScriptFreeCache( &s->script_cache );
FREE_OBJ(s);
}
......
......@@ -32,6 +32,66 @@ WINE_DEFAULT_DEBUG_CHANNEL(richedit);
* - no tabs
*/
static BOOL get_run_glyph_buffers( ME_Run *run )
{
heap_free( run->glyphs );
run->glyphs = heap_alloc( run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int) + sizeof(GOFFSET)) );
if (!run->glyphs) return FALSE;
run->vis_attrs = (SCRIPT_VISATTR*)((char*)run->glyphs + run->max_glyphs * sizeof(WORD));
run->advances = (int*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR)));
run->offsets = (GOFFSET*)((char*)run->glyphs + run->max_glyphs * (sizeof(WORD) + sizeof(SCRIPT_VISATTR) + sizeof(int)));
return TRUE;
}
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;
}
/******************************************************************************
* calc_run_extent
*
......@@ -78,7 +138,10 @@ static ME_DisplayItem *split_run_extents(ME_WrapContext *wc, ME_DisplayItem *ite
ME_SplitRunSimple(editor, &cursor);
run2 = &cursor.pRun->member.run;
run2->script_analysis = run->script_analysis;
shape_run( wc->context, run );
shape_run( wc->context, run2 );
calc_run_extent(wc->context, para, wc->nRow ? wc->nLeftMargin : wc->nFirstMargin, run);
run2->pt.x = run->pt.x+run->nWidth;
......@@ -166,6 +229,65 @@ static void ME_BeginRow(ME_WrapContext *wc)
wc->pt.y++;
}
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 );
}
static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
{
ME_DisplayItem *p, *row;
......@@ -227,6 +349,9 @@ static void ME_InsertRowStart(ME_WrapContext *wc, const ME_DisplayItem *pEnd)
shift = max((wc->nAvailWidth-width)/2, 0);
if (align == PFA_RIGHT)
shift = max(wc->nAvailWidth-width, 0);
if (para->nFlags & MEPF_COMPLEX) layout_row( wc->pRowStart, pEnd );
row->member.row.pt.x = row->member.row.nLMargin + shift;
for (p = wc->pRowStart; p!=pEnd; p = p->next)
{
......@@ -613,6 +738,111 @@ static void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp) {
}
}
static HRESULT itemize_para( ME_Context *c, ME_DisplayItem *p )
{
ME_Paragraph *para = &p->member.para;
ME_Run *run;
ME_DisplayItem *di;
SCRIPT_ITEM buf[16], *items = buf;
int items_passed = sizeof( buf ) / sizeof( buf[0] ), num_items, cur_item;
SCRIPT_CONTROL control = { LANG_USER_DEFAULT, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, 0 };
SCRIPT_STATE state = { 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0, 0 };
HRESULT hr;
assert( p->type == diParagraph );
while (1)
{
hr = ScriptItemize( para->text->szData, para->text->nLen, items_passed, &control,
&state, items, &num_items );
if (hr != E_OUTOFMEMORY) break; /* may not be enough items if hr == E_OUTOFMEMORY */
if (items_passed > para->text->nLen + 1) break; /* something else has gone wrong */
items_passed *= 2;
if (items == buf)
items = heap_alloc( items_passed * sizeof( *items ) );
else
items = heap_realloc( items, items_passed * sizeof( *items ) );
if (!items) break;
}
if (FAILED( hr )) goto end;
if (TRACE_ON( richedit ))
{
TRACE( "got items:\n" );
for (cur_item = 0; cur_item < num_items; cur_item++)
{
TRACE( "\t%d - %d RTL %d bidi level %d\n", items[cur_item].iCharPos, items[cur_item+1].iCharPos - 1,
items[cur_item].a.fRTL, items[cur_item].a.s.uBidiLevel );
}
TRACE( "before splitting runs into ranges\n" );
for (di = p->next; di != p->member.para.next_para; di = di->next)
{
if (di->type != diRun) continue;
TRACE( "\t%d: %s\n", di->member.run.nCharOfs, debugstr_run( &di->member.run ) );
}
}
/* split runs into ranges at item boundaries */
for (di = p->next, cur_item = 0; di != p->member.para.next_para; di = di->next)
{
if (di->type != diRun) continue;
run = &di->member.run;
if (run->nCharOfs == items[cur_item+1].iCharPos) cur_item++;
items[cur_item].a.fLogicalOrder = TRUE;
run->script_analysis = items[cur_item].a;
if (run->nFlags & MERF_ENDPARA) break; /* don't split eop runs */
if (run->nCharOfs + run->len > items[cur_item+1].iCharPos)
{
ME_Cursor cursor = {p, di, items[cur_item+1].iCharPos - run->nCharOfs};
ME_SplitRunSimple( c->editor, &cursor );
}
}
if (TRACE_ON( richedit ))
{
TRACE( "after splitting into ranges\n" );
for (di = p->next; di != p->member.para.next_para; di = di->next)
{
if (di->type != diRun) continue;
TRACE( "\t%d: %s\n", di->member.run.nCharOfs, debugstr_run( &di->member.run ) );
}
}
para->nFlags |= MEPF_COMPLEX;
end:
if (items != buf) heap_free( items );
return hr;
}
static HRESULT shape_para( ME_Context *c, ME_DisplayItem *p )
{
ME_DisplayItem *di;
ME_Run *run;
HRESULT hr;
for (di = p->next; di != p->member.para.next_para; di = di->next)
{
if (di->type != diRun) continue;
run = &di->member.run;
hr = shape_run( c, run );
if (FAILED( hr ))
{
run->para->nFlags &= ~MEPF_COMPLEX;
return hr;
}
}
return hr;
}
static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
ME_DisplayItem *p;
ME_WrapContext wc;
......@@ -625,6 +855,15 @@ static void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
return;
}
ME_PrepareParagraphForWrapping(c, tp);
/* For now treating all non-password text as complex for better testing */
if (!c->editor->cPasswordMask /* &&
ScriptIsComplex( tp->member.para.text->szData, tp->member.para.text->nLen, SIC_COMPLEX ) == S_OK */)
{
if (SUCCEEDED( itemize_para( c, tp ) ))
shape_para( c, tp );
}
pFmt = tp->member.para.pFmt;
wc.context = c;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment