dwrite: Split each text range into runs by script.
This commit is contained in:
parent
61fc9ac92f
commit
894737ac55
|
@ -98,8 +98,16 @@ struct layout_range {
|
||||||
IDWriteFontCollection *collection;
|
IDWriteFontCollection *collection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct layout_run {
|
||||||
|
struct list entry;
|
||||||
|
DWRITE_GLYPH_RUN_DESCRIPTION descr;
|
||||||
|
DWRITE_SCRIPT_ANALYSIS sa;
|
||||||
|
};
|
||||||
|
|
||||||
struct dwrite_textlayout {
|
struct dwrite_textlayout {
|
||||||
IDWriteTextLayout2 IDWriteTextLayout2_iface;
|
IDWriteTextLayout2 IDWriteTextLayout2_iface;
|
||||||
|
IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface;
|
||||||
|
IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
|
||||||
LONG ref;
|
LONG ref;
|
||||||
|
|
||||||
WCHAR *str;
|
WCHAR *str;
|
||||||
|
@ -108,6 +116,8 @@ struct dwrite_textlayout {
|
||||||
FLOAT maxwidth;
|
FLOAT maxwidth;
|
||||||
FLOAT maxheight;
|
FLOAT maxheight;
|
||||||
struct list ranges;
|
struct list ranges;
|
||||||
|
struct list runs;
|
||||||
|
BOOL recompute;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dwrite_textformat {
|
struct dwrite_textformat {
|
||||||
|
@ -145,6 +155,16 @@ static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout2(IDWriteText
|
||||||
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout2_iface);
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout2_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface)
|
||||||
|
{
|
||||||
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
|
||||||
|
{
|
||||||
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource_iface);
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct dwrite_textformat *impl_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
|
static inline struct dwrite_textformat *impl_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
|
||||||
{
|
{
|
||||||
return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat1_iface);
|
return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat1_iface);
|
||||||
|
@ -165,6 +185,70 @@ static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypog
|
||||||
return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
|
return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct layout_run *alloc_layout_run(void)
|
||||||
|
{
|
||||||
|
struct layout_run *ret;
|
||||||
|
|
||||||
|
ret = heap_alloc(sizeof(*ret));
|
||||||
|
if (!ret) return NULL;
|
||||||
|
|
||||||
|
ret->descr.localeName = NULL;
|
||||||
|
ret->descr.string = NULL;
|
||||||
|
ret->descr.stringLength = 0;
|
||||||
|
ret->descr.clusterMap = NULL;
|
||||||
|
ret->descr.textPosition = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_layout_runs(struct dwrite_textlayout *layout)
|
||||||
|
{
|
||||||
|
struct layout_run *cur, *cur2;
|
||||||
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
|
||||||
|
list_remove(&cur->entry);
|
||||||
|
heap_free(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
|
{
|
||||||
|
IDWriteTextAnalyzer *analyzer;
|
||||||
|
struct layout_range *cur;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
free_layout_runs(layout);
|
||||||
|
|
||||||
|
hr = get_textanalyzer(&analyzer);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, entry) {
|
||||||
|
/* inline objects override actual text in a range */
|
||||||
|
if (cur->object)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &layout->IDWriteTextAnalysisSource_iface,
|
||||||
|
cur->range.startPosition, cur->range.length, &layout->IDWriteTextAnalysisSink_iface);
|
||||||
|
if (FAILED(hr))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDWriteTextAnalyzer_Release(analyzer);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT layout_compute(struct dwrite_textlayout *layout)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (!layout->recompute)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
hr = layout_compute_runs(layout);
|
||||||
|
layout->recompute = FALSE;
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
/* To be used in IDWriteTextLayout methods to validate and fix passed range */
|
/* To be used in IDWriteTextLayout methods to validate and fix passed range */
|
||||||
static inline BOOL validate_text_range(struct dwrite_textlayout *layout, DWRITE_TEXT_RANGE *r)
|
static inline BOOL validate_text_range(struct dwrite_textlayout *layout, DWRITE_TEXT_RANGE *r)
|
||||||
{
|
{
|
||||||
|
@ -499,6 +583,7 @@ done:
|
||||||
if (changed) {
|
if (changed) {
|
||||||
struct list *next, *i;
|
struct list *next, *i;
|
||||||
|
|
||||||
|
layout->recompute = TRUE;
|
||||||
i = list_head(ranges);
|
i = list_head(ranges);
|
||||||
while ((next = list_next(ranges, i))) {
|
while ((next = list_next(ranges, i))) {
|
||||||
struct layout_range *next_range = LIST_ENTRY(next, struct layout_range, entry);
|
struct layout_range *next_range = LIST_ENTRY(next, struct layout_range, entry);
|
||||||
|
@ -557,6 +642,7 @@ static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout2 *iface)
|
||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
free_layout_ranges_list(This);
|
free_layout_ranges_list(This);
|
||||||
|
free_layout_runs(This);
|
||||||
release_format_data(&This->format);
|
release_format_data(&This->format);
|
||||||
heap_free(This->str);
|
heap_free(This->str);
|
||||||
heap_free(This);
|
heap_free(This);
|
||||||
|
@ -1187,10 +1273,17 @@ static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout2 *if
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *iface,
|
static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *iface,
|
||||||
DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32* act_count)
|
DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
|
||||||
{
|
{
|
||||||
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
|
||||||
FIXME("(%p)->(%p %u %p): stub\n", This, metrics, max_count, act_count);
|
HRESULT hr;
|
||||||
|
|
||||||
|
FIXME("(%p)->(%p %u %p): stub\n", This, metrics, max_count, count);
|
||||||
|
|
||||||
|
hr = layout_compute(This);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1405,6 +1498,159 @@ static const IDWriteTextLayout2Vtbl dwritetextlayoutvtbl = {
|
||||||
dwritetextlayout2_GetFontFallback
|
dwritetextlayout2_GetFontFallback
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink *iface,
|
||||||
|
REFIID riid, void **obj)
|
||||||
|
{
|
||||||
|
if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown)) {
|
||||||
|
*obj = iface;
|
||||||
|
IDWriteTextAnalysisSink_AddRef(iface);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
*obj = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink *iface)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink *iface)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
|
||||||
|
UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
|
||||||
|
{
|
||||||
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink(iface);
|
||||||
|
struct layout_run *run;
|
||||||
|
|
||||||
|
TRACE("%u %u script=%d\n", position, length, sa->script);
|
||||||
|
|
||||||
|
run = alloc_layout_run();
|
||||||
|
if (!run)
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
run->descr.string = &layout->str[position];
|
||||||
|
run->descr.stringLength = length;
|
||||||
|
run->descr.textPosition = position;
|
||||||
|
run->sa = *sa;
|
||||||
|
list_add_head(&layout->runs, &run->entry);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
|
||||||
|
UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
|
||||||
|
{
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink *iface, UINT32 position,
|
||||||
|
UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
|
||||||
|
{
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
|
||||||
|
UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
|
||||||
|
{
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const IDWriteTextAnalysisSinkVtbl dwritetextlayoutsinkvtbl = {
|
||||||
|
dwritetextlayout_sink_QueryInterface,
|
||||||
|
dwritetextlayout_sink_AddRef,
|
||||||
|
dwritetextlayout_sink_Release,
|
||||||
|
dwritetextlayout_sink_SetScriptAnalysis,
|
||||||
|
dwritetextlayout_sink_SetLineBreakpoints,
|
||||||
|
dwritetextlayout_sink_SetBidiLevel,
|
||||||
|
dwritetextlayout_sink_SetNumberSubstitution
|
||||||
|
};
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource *iface,
|
||||||
|
REFIID riid, void **obj)
|
||||||
|
{
|
||||||
|
if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
|
||||||
|
IsEqualIID(riid, &IID_IUnknown))
|
||||||
|
{
|
||||||
|
*obj = iface;
|
||||||
|
IDWriteTextAnalysisSource_AddRef(iface);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
*obj = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource *iface)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource *iface)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
|
||||||
|
UINT32 position, WCHAR const** text, UINT32* text_len)
|
||||||
|
{
|
||||||
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface);
|
||||||
|
|
||||||
|
TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
|
||||||
|
|
||||||
|
if (position < layout->len) {
|
||||||
|
*text = &layout->str[position];
|
||||||
|
*text_len = layout->len - position;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*text = NULL;
|
||||||
|
*text_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
|
||||||
|
UINT32 position, WCHAR const** text, UINT32* text_len)
|
||||||
|
{
|
||||||
|
FIXME("%u %p %p: stub\n", position, text, text_len);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource *iface)
|
||||||
|
{
|
||||||
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface);
|
||||||
|
return IDWriteTextLayout2_GetReadingDirection(&layout->IDWriteTextLayout2_iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource *iface,
|
||||||
|
UINT32 position, UINT32* text_len, WCHAR const** locale)
|
||||||
|
{
|
||||||
|
FIXME("%u %p %p: stub\n", position, text_len, locale);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
|
||||||
|
UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
|
||||||
|
{
|
||||||
|
FIXME("%u %p %p: stub\n", position, text_len, substitution);
|
||||||
|
return E_NOTIMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const IDWriteTextAnalysisSourceVtbl dwritetextlayoutsourcevtbl = {
|
||||||
|
dwritetextlayout_source_QueryInterface,
|
||||||
|
dwritetextlayout_source_AddRef,
|
||||||
|
dwritetextlayout_source_Release,
|
||||||
|
dwritetextlayout_source_GetTextAtPosition,
|
||||||
|
dwritetextlayout_source_GetTextBeforePosition,
|
||||||
|
dwritetextlayout_source_GetParagraphReadingDirection,
|
||||||
|
dwritetextlayout_source_GetLocaleName,
|
||||||
|
dwritetextlayout_source_GetNumberSubstitution
|
||||||
|
};
|
||||||
|
|
||||||
static void layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
|
static void layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
|
||||||
{
|
{
|
||||||
struct dwrite_textformat *f;
|
struct dwrite_textformat *f;
|
||||||
|
@ -1467,13 +1713,17 @@ HRESULT create_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *forma
|
||||||
if (!This) return E_OUTOFMEMORY;
|
if (!This) return E_OUTOFMEMORY;
|
||||||
|
|
||||||
This->IDWriteTextLayout2_iface.lpVtbl = &dwritetextlayoutvtbl;
|
This->IDWriteTextLayout2_iface.lpVtbl = &dwritetextlayoutvtbl;
|
||||||
|
This->IDWriteTextAnalysisSink_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
|
||||||
|
This->IDWriteTextAnalysisSource_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
|
||||||
This->ref = 1;
|
This->ref = 1;
|
||||||
This->str = heap_strdupnW(str, len);
|
This->str = heap_strdupnW(str, len);
|
||||||
This->len = len;
|
This->len = len;
|
||||||
This->maxwidth = maxwidth;
|
This->maxwidth = maxwidth;
|
||||||
This->maxheight = maxheight;
|
This->maxheight = maxheight;
|
||||||
|
This->recompute = TRUE;
|
||||||
layout_format_from_textformat(This, format);
|
layout_format_from_textformat(This, format);
|
||||||
|
|
||||||
|
list_init(&This->runs);
|
||||||
list_init(&This->ranges);
|
list_init(&This->ranges);
|
||||||
range = alloc_layout_range(This, &r);
|
range = alloc_layout_range(This, &r);
|
||||||
if (!range) {
|
if (!range) {
|
||||||
|
|
|
@ -881,6 +881,49 @@ static void test_typography(void)
|
||||||
IDWriteTypography_Release(typography);
|
IDWriteTypography_Release(typography);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_GetClusterMetrics(void)
|
||||||
|
{
|
||||||
|
static const WCHAR strW[] = {'a','b','c','d',0};
|
||||||
|
IDWriteInlineObject *trimm;
|
||||||
|
IDWriteTextFormat *format;
|
||||||
|
IDWriteTextLayout *layout;
|
||||||
|
DWRITE_TEXT_RANGE range;
|
||||||
|
UINT32 count;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
|
||||||
|
DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
|
||||||
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
||||||
|
|
||||||
|
hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
|
||||||
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
|
||||||
|
todo_wine {
|
||||||
|
ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
|
||||||
|
ok(count == 4, "got %u\n", count);
|
||||||
|
}
|
||||||
|
hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm);
|
||||||
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
||||||
|
|
||||||
|
range.startPosition = 0;
|
||||||
|
range.length = 2;
|
||||||
|
hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range);
|
||||||
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
||||||
|
|
||||||
|
/* inline object takes a separate cluster, replaced codepoints number doesn't matter */
|
||||||
|
count = 0;
|
||||||
|
hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
|
||||||
|
todo_wine {
|
||||||
|
ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
|
||||||
|
ok(count == 3, "got %u\n", count);
|
||||||
|
}
|
||||||
|
IDWriteInlineObject_Release(trimm);
|
||||||
|
IDWriteTextLayout_Release(layout);
|
||||||
|
IDWriteTextFormat_Release(format);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(layout)
|
START_TEST(layout)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -905,6 +948,7 @@ START_TEST(layout)
|
||||||
test_SetInlineObject();
|
test_SetInlineObject();
|
||||||
test_draw_sequence();
|
test_draw_sequence();
|
||||||
test_typography();
|
test_typography();
|
||||||
|
test_GetClusterMetrics();
|
||||||
|
|
||||||
IDWriteFactory_Release(factory);
|
IDWriteFactory_Release(factory);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue