| /* |
| * Text format and layout |
| * |
| * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #define COBJMACROS |
| |
| #include <assert.h> |
| #include <stdarg.h> |
| #include <math.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "dwrite_private.h" |
| #include "scripts.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(dwrite); |
| |
| struct dwrite_textformat_data { |
| WCHAR *family_name; |
| UINT32 family_len; |
| WCHAR *locale; |
| UINT32 locale_len; |
| |
| DWRITE_FONT_WEIGHT weight; |
| DWRITE_FONT_STYLE style; |
| DWRITE_FONT_STRETCH stretch; |
| |
| DWRITE_PARAGRAPH_ALIGNMENT paralign; |
| DWRITE_READING_DIRECTION readingdir; |
| DWRITE_WORD_WRAPPING wrapping; |
| BOOL last_line_wrapping; |
| DWRITE_TEXT_ALIGNMENT textalignment; |
| DWRITE_FLOW_DIRECTION flow; |
| DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation; |
| DWRITE_OPTICAL_ALIGNMENT optical_alignment; |
| DWRITE_LINE_SPACING spacing; |
| |
| FLOAT fontsize; |
| |
| DWRITE_TRIMMING trimming; |
| IDWriteInlineObject *trimmingsign; |
| |
| IDWriteFontCollection *collection; |
| IDWriteFontFallback *fallback; |
| }; |
| |
| enum layout_range_attr_kind { |
| LAYOUT_RANGE_ATTR_WEIGHT, |
| LAYOUT_RANGE_ATTR_STYLE, |
| LAYOUT_RANGE_ATTR_STRETCH, |
| LAYOUT_RANGE_ATTR_FONTSIZE, |
| LAYOUT_RANGE_ATTR_EFFECT, |
| LAYOUT_RANGE_ATTR_INLINE, |
| LAYOUT_RANGE_ATTR_UNDERLINE, |
| LAYOUT_RANGE_ATTR_STRIKETHROUGH, |
| LAYOUT_RANGE_ATTR_PAIR_KERNING, |
| LAYOUT_RANGE_ATTR_FONTCOLL, |
| LAYOUT_RANGE_ATTR_LOCALE, |
| LAYOUT_RANGE_ATTR_FONTFAMILY, |
| LAYOUT_RANGE_ATTR_SPACING, |
| LAYOUT_RANGE_ATTR_TYPOGRAPHY |
| }; |
| |
| struct layout_range_attr_value { |
| DWRITE_TEXT_RANGE range; |
| union { |
| DWRITE_FONT_WEIGHT weight; |
| DWRITE_FONT_STYLE style; |
| DWRITE_FONT_STRETCH stretch; |
| FLOAT fontsize; |
| IDWriteInlineObject *object; |
| IUnknown *effect; |
| BOOL underline; |
| BOOL strikethrough; |
| BOOL pair_kerning; |
| IDWriteFontCollection *collection; |
| const WCHAR *locale; |
| const WCHAR *fontfamily; |
| struct { |
| FLOAT leading; |
| FLOAT trailing; |
| FLOAT min_advance; |
| } spacing; |
| IDWriteTypography *typography; |
| } u; |
| }; |
| |
| enum layout_range_kind { |
| LAYOUT_RANGE_REGULAR, |
| LAYOUT_RANGE_UNDERLINE, |
| LAYOUT_RANGE_STRIKETHROUGH, |
| LAYOUT_RANGE_EFFECT, |
| LAYOUT_RANGE_SPACING, |
| LAYOUT_RANGE_TYPOGRAPHY |
| }; |
| |
| struct layout_range_header { |
| struct list entry; |
| enum layout_range_kind kind; |
| DWRITE_TEXT_RANGE range; |
| }; |
| |
| struct layout_range { |
| struct layout_range_header h; |
| DWRITE_FONT_WEIGHT weight; |
| DWRITE_FONT_STYLE style; |
| FLOAT fontsize; |
| DWRITE_FONT_STRETCH stretch; |
| IDWriteInlineObject *object; |
| BOOL pair_kerning; |
| IDWriteFontCollection *collection; |
| WCHAR locale[LOCALE_NAME_MAX_LENGTH]; |
| WCHAR *fontfamily; |
| }; |
| |
| struct layout_range_bool { |
| struct layout_range_header h; |
| BOOL value; |
| }; |
| |
| struct layout_range_iface { |
| struct layout_range_header h; |
| IUnknown *iface; |
| }; |
| |
| struct layout_range_spacing { |
| struct layout_range_header h; |
| FLOAT leading; |
| FLOAT trailing; |
| FLOAT min_advance; |
| }; |
| |
| enum layout_run_kind { |
| LAYOUT_RUN_REGULAR, |
| LAYOUT_RUN_INLINE |
| }; |
| |
| struct inline_object_run { |
| IDWriteInlineObject *object; |
| UINT16 length; |
| }; |
| |
| struct regular_layout_run { |
| DWRITE_GLYPH_RUN_DESCRIPTION descr; |
| DWRITE_GLYPH_RUN run; |
| DWRITE_SCRIPT_ANALYSIS sa; |
| UINT16 *glyphs; |
| UINT16 *clustermap; |
| FLOAT *advances; |
| DWRITE_GLYPH_OFFSET *offsets; |
| UINT32 glyphcount; /* actual glyph count after shaping, not necessarily the same as reported to Draw() */ |
| }; |
| |
| struct layout_run { |
| struct list entry; |
| enum layout_run_kind kind; |
| union { |
| struct inline_object_run object; |
| struct regular_layout_run regular; |
| } u; |
| FLOAT baseline; |
| FLOAT height; |
| UINT32 start_position; /* run text position in range [0, layout-text-length) */ |
| }; |
| |
| struct layout_effective_run { |
| struct list entry; |
| const struct layout_run *run; /* nominal run this one is based on */ |
| UINT32 start; /* relative text position, 0 means first text position of a nominal run */ |
| UINT32 length; /* length in codepoints that this run covers */ |
| UINT32 glyphcount; /* total glyph count in this run */ |
| IUnknown *effect; /* original reference is kept only at range level */ |
| D2D1_POINT_2F origin; /* baseline origin */ |
| FLOAT align_dx; /* adjustment from text alignment */ |
| FLOAT width; /* run width */ |
| UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */ |
| UINT32 line; /* 0-based line index in line metrics array */ |
| BOOL underlined; /* set if this run is underlined */ |
| D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */ |
| }; |
| |
| struct layout_effective_inline { |
| struct list entry; |
| IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */ |
| IUnknown *effect; /* original reference is kept only at range level */ |
| FLOAT baseline; |
| D2D1_POINT_2F origin; /* left top corner */ |
| FLOAT align_dx; /* adjustment from text alignment */ |
| FLOAT width; /* object width as it's reported it */ |
| BOOL is_sideways; /* vertical flow direction flag passed to Draw */ |
| BOOL is_rtl; /* bidi flag passed to Draw */ |
| UINT32 line; /* 0-based line index in line metrics array */ |
| }; |
| |
| struct layout_underline { |
| struct list entry; |
| const struct layout_effective_run *run; |
| DWRITE_UNDERLINE u; |
| }; |
| |
| struct layout_strikethrough { |
| struct list entry; |
| const struct layout_effective_run *run; |
| DWRITE_STRIKETHROUGH s; |
| }; |
| |
| struct layout_cluster { |
| const struct layout_run *run; /* link to nominal run this cluster belongs to */ |
| UINT32 position; /* relative to run, first cluster has 0 position */ |
| }; |
| |
| struct layout_line { |
| FLOAT height; /* height based on content */ |
| FLOAT baseline; /* baseline based on content */ |
| }; |
| |
| enum layout_recompute_mask { |
| RECOMPUTE_CLUSTERS = 1 << 0, |
| RECOMPUTE_MINIMAL_WIDTH = 1 << 1, |
| RECOMPUTE_LINES = 1 << 2, |
| RECOMPUTE_OVERHANGS = 1 << 3, |
| RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS, |
| RECOMPUTE_EVERYTHING = 0xffff |
| }; |
| |
| struct dwrite_textlayout { |
| IDWriteTextLayout3 IDWriteTextLayout3_iface; |
| IDWriteTextFormat1 IDWriteTextFormat1_iface; |
| IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface; |
| IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface; |
| LONG ref; |
| |
| IDWriteFactory5 *factory; |
| |
| WCHAR *str; |
| UINT32 len; |
| struct dwrite_textformat_data format; |
| struct list strike_ranges; |
| struct list underline_ranges; |
| struct list typographies; |
| struct list effects; |
| struct list spacing; |
| struct list ranges; |
| struct list runs; |
| /* lists ready to use by Draw() */ |
| struct list eruns; |
| struct list inlineobjects; |
| struct list underlines; |
| struct list strikethrough; |
| USHORT recompute; |
| |
| DWRITE_LINE_BREAKPOINT *nominal_breakpoints; |
| DWRITE_LINE_BREAKPOINT *actual_breakpoints; |
| |
| struct layout_cluster *clusters; |
| DWRITE_CLUSTER_METRICS *clustermetrics; |
| UINT32 cluster_count; |
| FLOAT minwidth; |
| |
| struct layout_line *lines; |
| DWRITE_LINE_METRICS1 *linemetrics; |
| UINT32 line_alloc; |
| |
| DWRITE_TEXT_METRICS1 metrics; |
| DWRITE_OVERHANG_METRICS overhangs; |
| |
| DWRITE_MEASURING_MODE measuringmode; |
| |
| /* gdi-compatible layout specifics */ |
| FLOAT ppdip; |
| DWRITE_MATRIX transform; |
| }; |
| |
| struct dwrite_textformat { |
| IDWriteTextFormat2 IDWriteTextFormat2_iface; |
| LONG ref; |
| struct dwrite_textformat_data format; |
| }; |
| |
| struct dwrite_trimmingsign { |
| IDWriteInlineObject IDWriteInlineObject_iface; |
| LONG ref; |
| |
| IDWriteTextLayout *layout; |
| }; |
| |
| struct dwrite_typography { |
| IDWriteTypography IDWriteTypography_iface; |
| LONG ref; |
| |
| DWRITE_FONT_FEATURE *features; |
| UINT32 allocated; |
| UINT32 count; |
| }; |
| |
| static const IDWriteTextFormat2Vtbl dwritetextformatvtbl; |
| |
| static void release_format_data(struct dwrite_textformat_data *data) |
| { |
| if (data->collection) IDWriteFontCollection_Release(data->collection); |
| if (data->fallback) IDWriteFontFallback_Release(data->fallback); |
| if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign); |
| heap_free(data->family_name); |
| heap_free(data->locale); |
| } |
| |
| static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout3(IDWriteTextLayout3 *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout3_iface); |
| } |
| |
| static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface); |
| } |
| |
| static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface); |
| } |
| |
| static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface); |
| } |
| |
| static inline struct dwrite_textformat *impl_from_IDWriteTextFormat2(IDWriteTextFormat2 *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface); |
| } |
| |
| static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*); |
| |
| static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface); |
| } |
| |
| static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface) |
| { |
| return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface); |
| } |
| |
| static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr) |
| { |
| return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength); |
| } |
| |
| static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout) |
| { |
| return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL; |
| } |
| |
| static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment, |
| BOOL *changed) |
| { |
| if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED) |
| return E_INVALIDARG; |
| if (changed) *changed = format->textalignment != alignment; |
| format->textalignment = alignment; |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format, |
| DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed) |
| { |
| if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER) |
| return E_INVALIDARG; |
| if (changed) *changed = format->paralign != alignment; |
| format->paralign = alignment; |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format, |
| DWRITE_READING_DIRECTION direction, BOOL *changed) |
| { |
| if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP) |
| return E_INVALIDARG; |
| if (changed) *changed = format->readingdir != direction; |
| format->readingdir = direction; |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format, |
| DWRITE_WORD_WRAPPING wrapping, BOOL *changed) |
| { |
| if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER) |
| return E_INVALIDARG; |
| if (changed) *changed = format->wrapping != wrapping; |
| format->wrapping = wrapping; |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format, |
| DWRITE_FLOW_DIRECTION direction, BOOL *changed) |
| { |
| if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT) |
| return E_INVALIDARG; |
| if (changed) *changed = format->flow != direction; |
| format->flow = direction; |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format, |
| DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed) |
| { |
| if (changed) |
| *changed = FALSE; |
| |
| if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD) |
| return E_INVALIDARG; |
| |
| if (changed) { |
| *changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming)); |
| if (format->trimmingsign != trimming_sign) |
| *changed = TRUE; |
| } |
| |
| format->trimming = *trimming; |
| if (format->trimmingsign) |
| IDWriteInlineObject_Release(format->trimmingsign); |
| format->trimmingsign = trimming_sign; |
| if (format->trimmingsign) |
| IDWriteInlineObject_AddRef(format->trimmingsign); |
| return S_OK; |
| } |
| |
| static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format, |
| DWRITE_LINE_SPACING const *spacing, BOOL *changed) |
| { |
| if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f || |
| (UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL) |
| return E_INVALIDARG; |
| |
| if (changed) |
| *changed = memcmp(spacing, &format->spacing, sizeof(*spacing)); |
| |
| format->spacing = *spacing; |
| return S_OK; |
| } |
| |
| static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback) |
| { |
| *fallback = format->fallback; |
| if (*fallback) |
| IDWriteFontFallback_AddRef(*fallback); |
| return S_OK; |
| } |
| |
| static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback) |
| { |
| if (format->fallback) |
| IDWriteFontFallback_Release(format->fallback); |
| format->fallback = fallback; |
| if (fallback) |
| IDWriteFontFallback_AddRef(fallback); |
| return S_OK; |
| } |
| |
| static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format, |
| DWRITE_OPTICAL_ALIGNMENT alignment) |
| { |
| if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS) |
| return E_INVALIDARG; |
| format->optical_alignment = alignment; |
| return S_OK; |
| } |
| |
| static BOOL is_run_rtl(const struct layout_effective_run *run) |
| { |
| return run->run->u.regular.run.bidiLevel & 1; |
| } |
| |
| static struct layout_run *alloc_layout_run(enum layout_run_kind kind, UINT32 start_position) |
| { |
| struct layout_run *ret; |
| |
| ret = heap_alloc(sizeof(*ret)); |
| if (!ret) return NULL; |
| |
| memset(ret, 0, sizeof(*ret)); |
| ret->kind = kind; |
| if (kind == LAYOUT_RUN_REGULAR) { |
| ret->u.regular.sa.script = Script_Unknown; |
| ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT; |
| } |
| ret->start_position = start_position; |
| |
| 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); |
| if (cur->kind == LAYOUT_RUN_REGULAR) { |
| if (cur->u.regular.run.fontFace) |
| IDWriteFontFace_Release(cur->u.regular.run.fontFace); |
| heap_free(cur->u.regular.glyphs); |
| heap_free(cur->u.regular.clustermap); |
| heap_free(cur->u.regular.advances); |
| heap_free(cur->u.regular.offsets); |
| } |
| heap_free(cur); |
| } |
| } |
| |
| static void free_layout_eruns(struct dwrite_textlayout *layout) |
| { |
| struct layout_effective_inline *in, *in2; |
| struct layout_effective_run *cur, *cur2; |
| struct layout_strikethrough *s, *s2; |
| struct layout_underline *u, *u2; |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) { |
| list_remove(&cur->entry); |
| heap_free(cur->clustermap); |
| heap_free(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) { |
| list_remove(&in->entry); |
| heap_free(in); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) { |
| list_remove(&u->entry); |
| heap_free(u); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) { |
| list_remove(&s->entry); |
| heap_free(s); |
| } |
| } |
| |
| /* Used to resolve break condition by forcing stronger condition over weaker. */ |
| static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak) |
| { |
| switch (existingbreak) { |
| case DWRITE_BREAK_CONDITION_NEUTRAL: |
| return newbreak; |
| case DWRITE_BREAK_CONDITION_CAN_BREAK: |
| return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak; |
| /* let's keep stronger conditions as is */ |
| case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK: |
| case DWRITE_BREAK_CONDITION_MUST_BREAK: |
| break; |
| default: |
| ERR("unknown break condition %d\n", existingbreak); |
| } |
| |
| return existingbreak; |
| } |
| |
| /* This helper should be used to get effective range length, in other words it returns number of text |
| positions from range starting point to the end of the range, limited by layout text length */ |
| static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range) |
| { |
| if (range->h.range.startPosition + range->h.range.length <= layout->len) |
| return range->h.range.length; |
| return layout->len - range->h.range.startPosition; |
| } |
| |
| /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */ |
| static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur) |
| { |
| DWRITE_BREAK_CONDITION before, after; |
| UINT32 i, length; |
| HRESULT hr; |
| |
| /* ignore returned conditions if failed */ |
| hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after); |
| if (FAILED(hr)) |
| after = before = DWRITE_BREAK_CONDITION_NEUTRAL; |
| |
| if (!layout->actual_breakpoints) { |
| layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len); |
| if (!layout->actual_breakpoints) |
| return E_OUTOFMEMORY; |
| memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len); |
| } |
| |
| length = get_clipped_range_length(layout, cur); |
| for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) { |
| /* for first codepoint check if there's anything before it and update accordingly */ |
| if (i == cur->h.range.startPosition) { |
| if (i > 0) |
| layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter = |
| override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before); |
| else |
| layout->actual_breakpoints[i].breakConditionBefore = before; |
| layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; |
| } |
| /* similar check for last codepoint */ |
| else if (i == cur->h.range.startPosition + length - 1) { |
| if (i == layout->len - 1) |
| layout->actual_breakpoints[i].breakConditionAfter = after; |
| else |
| layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore = |
| override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after); |
| layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; |
| } |
| /* for all positions within a range disable breaks */ |
| else { |
| layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; |
| layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; |
| } |
| |
| layout->actual_breakpoints[i].isWhitespace = 0; |
| layout->actual_breakpoints[i].isSoftHyphen = 0; |
| } |
| |
| return S_OK; |
| } |
| |
| static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos); |
| |
| static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| if (layout->actual_breakpoints) |
| return layout->actual_breakpoints[pos]; |
| return layout->nominal_breakpoints[pos]; |
| } |
| |
| static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run, |
| UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics) |
| { |
| UINT8 breakcondition; |
| UINT32 position; |
| UINT16 j; |
| |
| /* For clusters made of control chars we report zero glyphs, and we need zero cluster |
| width as well; advances are already computed at this point and are not necessary zero. */ |
| metrics->width = 0.0f; |
| if (run->run.glyphCount) { |
| for (j = start_glyph; j < stop_glyph; j++) |
| metrics->width += run->run.glyphAdvances[j]; |
| } |
| metrics->length = length; |
| |
| position = run->descr.textPosition + stop_position; |
| if (stop_glyph == run->glyphcount) |
| breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter; |
| else { |
| breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore; |
| if (stop_position) position -= 1; |
| } |
| |
| metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK || |
| breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK; |
| if (metrics->length == 1) { |
| DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position); |
| metrics->isWhitespace = bp.isWhitespace; |
| metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]); |
| metrics->isSoftHyphen = bp.isSoftHyphen; |
| } |
| else { |
| metrics->isWhitespace = 0; |
| metrics->isNewline = 0; |
| metrics->isSoftHyphen = 0; |
| } |
| metrics->isRightToLeft = run->run.bidiLevel & 1; |
| metrics->padding = 0; |
| } |
| |
| /* |
| |
| All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'. |
| On return 'cluster' is updated to point to next metrics struct to be filled in on next call. |
| Note that there's no need to reallocate anything at this point as we allocate one cluster per |
| codepoint initially. |
| |
| */ |
| static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster) |
| { |
| DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster]; |
| struct layout_cluster *c = &layout->clusters[*cluster]; |
| const struct regular_layout_run *run = &r->u.regular; |
| UINT32 i, start = 0; |
| |
| assert(r->kind == LAYOUT_RUN_REGULAR); |
| |
| for (i = 0; i < run->descr.stringLength; i++) { |
| BOOL end = i == run->descr.stringLength - 1; |
| |
| if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) { |
| init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i, |
| i - start, metrics); |
| c->position = start; |
| c->run = r; |
| |
| *cluster += 1; |
| metrics++; |
| c++; |
| start = i; |
| } |
| |
| if (end) { |
| init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i, |
| i - start + 1, metrics); |
| c->position = start; |
| c->run = r; |
| |
| *cluster += 1; |
| return; |
| } |
| } |
| } |
| |
| #define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm) |
| |
| static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize, |
| DWRITE_FONT_METRICS *fontmetrics) |
| { |
| if (is_layout_gdi_compatible(layout)) { |
| HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics); |
| if (FAILED(hr)) |
| WARN("failed to get compat metrics, 0x%08x\n", hr); |
| } |
| else |
| IDWriteFontFace_GetMetrics(fontface, fontmetrics); |
| } |
| |
| static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height) |
| { |
| *baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics); |
| *height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics); |
| } |
| |
| static HRESULT layout_itemize(struct dwrite_textlayout *layout) |
| { |
| IDWriteTextAnalyzer *analyzer; |
| struct layout_range *range; |
| struct layout_run *r; |
| HRESULT hr = S_OK; |
| |
| analyzer = get_text_analyzer(); |
| |
| LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) { |
| /* We don't care about ranges that don't contain any text. */ |
| if (range->h.range.startPosition >= layout->len) |
| break; |
| |
| /* Inline objects override actual text in range. */ |
| if (range->object) { |
| hr = layout_update_breakpoints_range(layout, range); |
| if (FAILED(hr)) |
| return hr; |
| |
| r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition); |
| if (!r) |
| return E_OUTOFMEMORY; |
| |
| r->u.object.object = range->object; |
| r->u.object.length = get_clipped_range_length(layout, range); |
| list_add_tail(&layout->runs, &r->entry); |
| continue; |
| } |
| |
| /* Initial splitting by script. */ |
| hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, |
| range->h.range.startPosition, get_clipped_range_length(layout, range), |
| (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); |
| if (FAILED(hr)) |
| break; |
| |
| /* Splitting further by bidi levels. */ |
| hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, |
| range->h.range.startPosition, get_clipped_range_length(layout, range), |
| (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface); |
| if (FAILED(hr)) |
| break; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout) |
| { |
| IDWriteFontCollection *sys_collection; |
| IDWriteFontFallback *fallback = NULL; |
| struct layout_range *range; |
| struct layout_run *r; |
| HRESULT hr; |
| |
| if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE, |
| (IDWriteFontCollection1 **)&sys_collection, FALSE))) { |
| WARN("Failed to get system collection, hr %#x.\n", hr); |
| return hr; |
| } |
| |
| if (layout->format.fallback) { |
| fallback = layout->format.fallback; |
| IDWriteFontFallback_AddRef(fallback); |
| } |
| else { |
| if (FAILED(hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback))) { |
| WARN("Failed to get system fallback, hr %#x.\n", hr); |
| goto fatal; |
| } |
| } |
| |
| LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) { |
| struct regular_layout_run *run = &r->u.regular; |
| IDWriteFont *font; |
| UINT32 length; |
| |
| if (r->kind == LAYOUT_RUN_INLINE) |
| continue; |
| |
| range = get_layout_range_by_pos(layout, run->descr.textPosition); |
| |
| if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) { |
| IDWriteFontCollection *collection; |
| |
| collection = range->collection ? range->collection : sys_collection; |
| |
| if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style, |
| range->stretch, &font))) { |
| WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n", |
| debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection); |
| break; |
| } |
| |
| hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace); |
| IDWriteFont_Release(font); |
| if (FAILED(hr)) { |
| WARN("Failed to create font face, hr %#x.\n", hr); |
| break; |
| } |
| |
| run->run.fontEmSize = range->fontsize; |
| continue; |
| } |
| |
| length = run->descr.stringLength; |
| |
| while (length) { |
| UINT32 mapped_length; |
| FLOAT scale; |
| |
| run = &r->u.regular; |
| |
| hr = IDWriteFontFallback_MapCharacters(fallback, |
| (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, |
| run->descr.textPosition, |
| run->descr.stringLength, |
| range->collection, |
| range->fontfamily, |
| range->weight, |
| range->style, |
| range->stretch, |
| &mapped_length, |
| &font, |
| &scale); |
| if (FAILED(hr)) { |
| WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr), |
| debugstr_w(range->fontfamily), range->collection, hr); |
| goto fatal; |
| } |
| |
| hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace); |
| IDWriteFont_Release(font); |
| if (FAILED(hr)) { |
| WARN("Failed to create font face, hr %#x.\n", hr); |
| goto fatal; |
| } |
| |
| run->run.fontEmSize = range->fontsize * scale; |
| |
| if (mapped_length < length) { |
| struct regular_layout_run *nextrun; |
| struct layout_run *nextr; |
| |
| /* keep mapped part for current run, add another run for the rest */ |
| nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0); |
| if (!nextr) { |
| hr = E_OUTOFMEMORY; |
| goto fatal; |
| } |
| |
| *nextr = *r; |
| nextr->start_position = run->descr.textPosition + mapped_length; |
| nextrun = &nextr->u.regular; |
| nextrun->descr.textPosition = nextr->start_position; |
| nextrun->descr.stringLength = run->descr.stringLength - mapped_length; |
| nextrun->descr.string = &layout->str[nextrun->descr.textPosition]; |
| run->descr.stringLength = mapped_length; |
| list_add_after(&r->entry, &nextr->entry); |
| r = nextr; |
| } |
| |
| length -= mapped_length; |
| } |
| } |
| |
| fatal: |
| IDWriteFontCollection_Release(sys_collection); |
| if (fallback) |
| IDWriteFontFallback_Release(fallback); |
| |
| return hr; |
| } |
| |
| static HRESULT layout_shape_run(struct dwrite_textlayout *layout, struct regular_layout_run *run) |
| { |
| DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props; |
| DWRITE_SHAPING_TEXT_PROPERTIES *text_props; |
| IDWriteTextAnalyzer *analyzer; |
| struct layout_range *range; |
| UINT32 max_count; |
| HRESULT hr; |
| |
| range = get_layout_range_by_pos(layout, run->descr.textPosition); |
| run->descr.localeName = range->locale; |
| run->clustermap = heap_alloc(run->descr.stringLength * sizeof(*run->clustermap)); |
| |
| max_count = 3 * run->descr.stringLength / 2 + 16; |
| run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs)); |
| if (!run->clustermap || !run->glyphs) |
| return E_OUTOFMEMORY; |
| |
| text_props = heap_alloc(run->descr.stringLength * sizeof(*text_props)); |
| glyph_props = heap_alloc(max_count * sizeof(*glyph_props)); |
| if (!text_props || !glyph_props) { |
| heap_free(text_props); |
| heap_free(glyph_props); |
| return E_OUTOFMEMORY; |
| } |
| |
| analyzer = get_text_analyzer(); |
| |
| for (;;) { |
| hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength, run->run.fontFace, |
| run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL /* FIXME */, NULL, |
| NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props, &run->glyphcount); |
| if (hr == E_NOT_SUFFICIENT_BUFFER) { |
| heap_free(run->glyphs); |
| heap_free(glyph_props); |
| |
| max_count = run->glyphcount; |
| |
| run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs)); |
| glyph_props = heap_alloc(max_count * sizeof(*glyph_props)); |
| if (!run->glyphs || !glyph_props) { |
| hr = E_OUTOFMEMORY; |
| break; |
| } |
| |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (FAILED(hr)) { |
| heap_free(text_props); |
| heap_free(glyph_props); |
| WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr); |
| return hr; |
| } |
| |
| run->run.glyphIndices = run->glyphs; |
| run->descr.clusterMap = run->clustermap; |
| |
| run->advances = heap_alloc(run->glyphcount * sizeof(*run->advances)); |
| run->offsets = heap_alloc(run->glyphcount * sizeof(*run->offsets)); |
| if (!run->advances || !run->offsets) |
| return E_OUTOFMEMORY; |
| |
| /* Get advances and offsets. */ |
| if (is_layout_gdi_compatible(layout)) |
| hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, |
| text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, |
| run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform, |
| layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1, |
| &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets); |
| else |
| hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props, |
| run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace, |
| run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, |
| NULL, NULL, 0, run->advances, run->offsets); |
| |
| heap_free(text_props); |
| heap_free(glyph_props); |
| if (FAILED(hr)) { |
| memset(run->advances, 0, run->glyphcount * sizeof(*run->advances)); |
| memset(run->offsets, 0, run->glyphcount * sizeof(*run->offsets)); |
| WARN("%s: failed to get glyph placement info, hr %#x.\n", debugstr_rundescr(&run->descr), hr); |
| } |
| |
| run->run.glyphAdvances = run->advances; |
| run->run.glyphOffsets = run->offsets; |
| |
| /* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them, |
| with valid cluster map and potentially with non-zero advances; layout code exposes those as zero |
| width clusters. */ |
| if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) |
| run->run.glyphCount = 0; |
| else |
| run->run.glyphCount = run->glyphcount; |
| |
| return S_OK; |
| } |
| |
| static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) |
| { |
| struct layout_run *r; |
| UINT32 cluster = 0; |
| HRESULT hr; |
| |
| free_layout_eruns(layout); |
| free_layout_runs(layout); |
| |
| /* Cluster data arrays are allocated once, assuming one text position per cluster. */ |
| if (!layout->clustermetrics && layout->len) { |
| layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics)); |
| layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters)); |
| if (!layout->clustermetrics || !layout->clusters) { |
| heap_free(layout->clustermetrics); |
| heap_free(layout->clusters); |
| return E_OUTOFMEMORY; |
| } |
| } |
| layout->cluster_count = 0; |
| |
| if (FAILED(hr = layout_itemize(layout))) { |
| WARN("Itemization failed, hr %#x.\n", hr); |
| return hr; |
| } |
| |
| if (FAILED(hr = layout_resolve_fonts(layout))) { |
| WARN("Failed to resolve layout fonts, hr %#x.\n", hr); |
| return hr; |
| } |
| |
| /* fill run info */ |
| LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) { |
| struct regular_layout_run *run = &r->u.regular; |
| DWRITE_FONT_METRICS fontmetrics = { 0 }; |
| |
| /* we need to do very little in case of inline objects */ |
| if (r->kind == LAYOUT_RUN_INLINE) { |
| DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster]; |
| struct layout_cluster *c = &layout->clusters[cluster]; |
| DWRITE_INLINE_OBJECT_METRICS inlinemetrics; |
| |
| metrics->width = 0.0f; |
| metrics->length = r->u.object.length; |
| metrics->canWrapLineAfter = 0; |
| metrics->isWhitespace = 0; |
| metrics->isNewline = 0; |
| metrics->isSoftHyphen = 0; |
| metrics->isRightToLeft = 0; |
| metrics->padding = 0; |
| c->run = r; |
| c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */ |
| cluster++; |
| |
| /* it's not fatal if GetMetrics() fails, all returned metrics are ignored */ |
| hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics); |
| if (FAILED(hr)) { |
| memset(&inlinemetrics, 0, sizeof(inlinemetrics)); |
| hr = S_OK; |
| } |
| metrics->width = inlinemetrics.width; |
| r->baseline = inlinemetrics.baseline; |
| r->height = inlinemetrics.height; |
| |
| /* FIXME: use resolved breakpoints in this case too */ |
| |
| continue; |
| } |
| |
| if (FAILED(hr = layout_shape_run(layout, run))) |
| WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr); |
| |
| /* baseline derived from font metrics */ |
| layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics); |
| layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height); |
| |
| layout_set_cluster_metrics(layout, r, &cluster); |
| } |
| |
| if (hr == S_OK) { |
| layout->cluster_count = cluster; |
| if (cluster) |
| layout->clustermetrics[cluster-1].canWrapLineAfter = 1; |
| } |
| |
| return hr; |
| } |
| |
| static HRESULT layout_compute(struct dwrite_textlayout *layout) |
| { |
| HRESULT hr; |
| |
| if (!(layout->recompute & RECOMPUTE_CLUSTERS)) |
| return S_OK; |
| |
| /* nominal breakpoints are evaluated only once, because string never changes */ |
| if (!layout->nominal_breakpoints) { |
| IDWriteTextAnalyzer *analyzer; |
| |
| layout->nominal_breakpoints = heap_alloc(layout->len * sizeof(*layout->nominal_breakpoints)); |
| if (!layout->nominal_breakpoints) |
| return E_OUTOFMEMORY; |
| |
| analyzer = get_text_analyzer(); |
| |
| if (FAILED(hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, |
| (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface, |
| 0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface))) |
| WARN("Line breakpoints analysis failed, hr %#x.\n", hr); |
| } |
| |
| heap_free(layout->actual_breakpoints); |
| layout->actual_breakpoints = NULL; |
| |
| hr = layout_compute_runs(layout); |
| |
| if (TRACE_ON(dwrite)) { |
| struct layout_run *cur; |
| |
| LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) { |
| if (cur->kind == LAYOUT_RUN_INLINE) |
| TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length); |
| else |
| TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition + |
| cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel); |
| } |
| } |
| |
| layout->recompute &= ~RECOMPUTE_CLUSTERS; |
| return hr; |
| } |
| |
| static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end) |
| { |
| FLOAT width = 0.0f; |
| for (; start < end; start++) |
| width += layout->clustermetrics[start].width; |
| return width; |
| } |
| |
| static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos) |
| { |
| struct layout_range_header *cur; |
| |
| LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) { |
| DWRITE_TEXT_RANGE *r = &cur->range; |
| if (r->startPosition <= pos && pos < r->startPosition + r->length) |
| return cur; |
| } |
| |
| return NULL; |
| } |
| |
| static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos); |
| return ((struct layout_range_iface*)h)->iface; |
| } |
| |
| static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun) |
| { |
| return erun->run->u.regular.run.bidiLevel & 1; |
| } |
| |
| /* A set of parameters that additionally splits resulting runs. It happens after shaping and all text processing, |
| no glyph changes are possible. It's understandable for drawing effects, because DrawGlyphRun() reports them as |
| one of the arguments, but it also happens for decorations, so every effective run has uniform |
| underline/strikethough/effect tuple. */ |
| struct layout_final_splitting_params { |
| BOOL strikethrough; |
| BOOL underline; |
| IUnknown *effect; |
| }; |
| |
| static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos); |
| return ((struct layout_range_bool*)h)->value; |
| } |
| |
| static inline BOOL layout_get_underline_from_pos(struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| struct layout_range_header *h = get_layout_range_header_by_pos(&layout->underline_ranges, pos); |
| return ((struct layout_range_bool*)h)->value; |
| } |
| |
| static void layout_splitting_params_from_pos(struct dwrite_textlayout *layout, UINT32 pos, |
| struct layout_final_splitting_params *params) |
| { |
| params->strikethrough = layout_get_strikethrough_from_pos(layout, pos); |
| params->underline = layout_get_underline_from_pos(layout, pos); |
| params->effect = layout_get_effect_from_pos(layout, pos); |
| } |
| |
| static BOOL is_same_splitting_params(const struct layout_final_splitting_params *left, |
| const struct layout_final_splitting_params *right) |
| { |
| return left->strikethrough == right->strikethrough && |
| left->underline == right->underline && |
| left->effect == right->effect; |
| } |
| |
| static void layout_get_erun_font_metrics(struct dwrite_textlayout *layout, struct layout_effective_run *erun, |
| DWRITE_FONT_METRICS *metrics) |
| { |
| memset(metrics, 0, sizeof(*metrics)); |
| if (is_layout_gdi_compatible(layout)) { |
| HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics( |
| erun->run->u.regular.run.fontFace, |
| erun->run->u.regular.run.fontEmSize, |
| layout->ppdip, |
| &layout->transform, |
| metrics); |
| if (FAILED(hr)) |
| WARN("failed to get font metrics, 0x%08x\n", hr); |
| } |
| else |
| IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics); |
| } |
| |
| /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index, |
| 'cluster_count' indicates how many clusters to add, including first one. */ |
| static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster, |
| UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| UINT32 i, start, length, last_cluster; |
| struct layout_effective_run *run; |
| |
| if (r->kind == LAYOUT_RUN_INLINE) { |
| struct layout_effective_inline *inlineobject; |
| |
| inlineobject = heap_alloc(sizeof(*inlineobject)); |
| if (!inlineobject) |
| return E_OUTOFMEMORY; |
| |
| inlineobject->object = r->u.object.object; |
| inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); |
| inlineobject->origin.x = is_rtl ? origin_x - inlineobject->width : origin_x; |
| inlineobject->origin.y = 0.0f; /* set after line is built */ |
| inlineobject->align_dx = 0.0f; |
| inlineobject->baseline = r->baseline; |
| |
| /* It's not clear how these two are set, possibly directionality |
| is derived from surrounding text (replaced text could have |
| different ranges which differ in reading direction). */ |
| inlineobject->is_sideways = FALSE; |
| inlineobject->is_rtl = FALSE; |
| inlineobject->line = line; |
| |
| /* effect assigned from start position and on is used for inline objects */ |
| inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position + |
| layout->clusters[first_cluster].run->start_position); |
| |
| list_add_tail(&layout->inlineobjects, &inlineobject->entry); |
| return S_OK; |
| } |
| |
| run = heap_alloc(sizeof(*run)); |
| if (!run) |
| return E_OUTOFMEMORY; |
| |
| /* No need to iterate for that, use simple fact that: |
| <last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */ |
| last_cluster = first_cluster + cluster_count - 1; |
| length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position + |
| layout->clustermetrics[last_cluster].length; |
| |
| run->clustermap = heap_alloc(sizeof(UINT16)*length); |
| if (!run->clustermap) { |
| heap_free(run); |
| return E_OUTOFMEMORY; |
| } |
| |
| run->run = r; |
| run->start = start = layout->clusters[first_cluster].position; |
| run->length = length; |
| run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); |
| memset(&run->bbox, 0, sizeof(run->bbox)); |
| |
| /* Check if run direction matches paragraph direction, if it doesn't adjust by |
| run width */ |
| if (layout_is_erun_rtl(run) ^ is_rtl) |
| run->origin.x = is_rtl ? origin_x - run->width : origin_x + run->width; |
| else |
| run->origin.x = origin_x; |
| |
| run->origin.y = 0.0f; /* set after line is built */ |
| run->align_dx = 0.0f; |
| run->line = line; |
| |
| if (r->u.regular.run.glyphCount) { |
| /* Trim leading and trailing clusters. */ |
| run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start]; |
| if (start + length < r->u.regular.descr.stringLength) |
| run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length]; |
| } |
| else |
| run->glyphcount = 0; |
| |
| /* cluster map needs to be shifted */ |
| for (i = 0; i < length; i++) |
| run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start]; |
| |
| run->effect = params->effect; |
| run->underlined = params->underline; |
| list_add_tail(&layout->eruns, &run->entry); |
| |
| /* Strikethrough style is guaranteed to be consistent within effective run, |
| its width equals to run width, thickness and offset are derived from |
| font metrics, rest of the values are from layout or run itself */ |
| if (params->strikethrough) { |
| struct layout_strikethrough *s; |
| DWRITE_FONT_METRICS metrics; |
| |
| s = heap_alloc(sizeof(*s)); |
| if (!s) |
| return E_OUTOFMEMORY; |
| |
| layout_get_erun_font_metrics(layout, run, &metrics); |
| s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count); |
| s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics); |
| /* Negative offset moves it above baseline as Y coordinate grows downward. */ |
| s->s.offset = -SCALE_FONT_METRIC(metrics.strikethroughPosition, r->u.regular.run.fontEmSize, &metrics); |
| s->s.readingDirection = layout->format.readingdir; |
| s->s.flowDirection = layout->format.flow; |
| s->s.localeName = r->u.regular.descr.localeName; |
| s->s.measuringMode = layout->measuringmode; |
| s->run = run; |
| |
| list_add_tail(&layout->strikethrough, &s->entry); |
| } |
| |
| return S_OK; |
| } |
| |
| static void layout_apply_line_spacing(struct dwrite_textlayout *layout, UINT32 line) |
| { |
| switch (layout->format.spacing.method) |
| { |
| case DWRITE_LINE_SPACING_METHOD_DEFAULT: |
| layout->linemetrics[line].height = layout->lines[line].height; |
| layout->linemetrics[line].baseline = layout->lines[line].baseline; |
| break; |
| case DWRITE_LINE_SPACING_METHOD_UNIFORM: |
| layout->linemetrics[line].height = layout->format.spacing.height; |
| layout->linemetrics[line].baseline = layout->format.spacing.baseline; |
| break; |
| case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL: |
| layout->linemetrics[line].height = layout->lines[line].height * layout->format.spacing.height; |
| layout->linemetrics[line].baseline = layout->lines[line].baseline * layout->format.spacing.baseline; |
| break; |
| default: |
| ERR("Unknown spacing method %u\n", layout->format.spacing.method); |
| } |
| } |
| |
| static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics) |
| { |
| UINT32 i = layout->metrics.lineCount; |
| |
| if (!layout->line_alloc) { |
| layout->line_alloc = 5; |
| layout->linemetrics = heap_alloc(layout->line_alloc * sizeof(*layout->linemetrics)); |
| layout->lines = heap_alloc(layout->line_alloc * sizeof(*layout->lines)); |
| if (!layout->linemetrics || !layout->lines) { |
| heap_free(layout->linemetrics); |
| heap_free(layout->lines); |
| layout->linemetrics = NULL; |
| layout->lines = NULL; |
| return E_OUTOFMEMORY; |
| } |
| } |
| |
| if (layout->metrics.lineCount == layout->line_alloc) { |
| DWRITE_LINE_METRICS1 *metrics; |
| struct layout_line *lines; |
| |
| if ((metrics = heap_realloc(layout->linemetrics, layout->line_alloc * 2 * sizeof(*layout->linemetrics)))) |
| layout->linemetrics = metrics; |
| if ((lines = heap_realloc(layout->lines, layout->line_alloc * 2 * sizeof(*layout->lines)))) |
| layout->lines = lines; |
| |
| if (!metrics || !lines) |
| return E_OUTOFMEMORY; |
| |
| layout->line_alloc *= 2; |
| } |
| |
| layout->linemetrics[i] = *metrics; |
| layout->lines[i].height = metrics->height; |
| layout->lines[i].baseline = metrics->baseline; |
| |
| if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT) |
| layout_apply_line_spacing(layout, i); |
| |
| layout->metrics.lineCount++; |
| return S_OK; |
| } |
| |
| static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout, |
| const struct layout_effective_run *cur) |
| { |
| struct list *e; |
| |
| if (!cur) |
| e = list_head(&layout->eruns); |
| else |
| e = list_next(&layout->eruns, &cur->entry); |
| if (!e) |
| return NULL; |
| return LIST_ENTRY(e, struct layout_effective_run, entry); |
| } |
| |
| static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout, |
| const struct layout_effective_run *cur) |
| { |
| struct list *e; |
| |
| if (!cur) |
| e = list_tail(&layout->eruns); |
| else |
| e = list_prev(&layout->eruns, &cur->entry); |
| if (!e) |
| return NULL; |
| return LIST_ENTRY(e, struct layout_effective_run, entry); |
| } |
| |
| static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout, |
| const struct layout_effective_inline *cur) |
| { |
| struct list *e; |
| |
| if (!cur) |
| e = list_head(&layout->inlineobjects); |
| else |
| e = list_next(&layout->inlineobjects, &cur->entry); |
| if (!e) |
| return NULL; |
| return LIST_ENTRY(e, struct layout_effective_inline, entry); |
| } |
| |
| static FLOAT layout_get_line_width(struct dwrite_textlayout *layout, |
| struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line) |
| { |
| FLOAT width = 0.0f; |
| |
| while (erun && erun->line == line) { |
| width += erun->width; |
| erun = layout_get_next_erun(layout, erun); |
| if (!erun) |
| break; |
| } |
| |
| while (inrun && inrun->line == line) { |
| width += inrun->width; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| if (!inrun) |
| break; |
| } |
| |
| return width; |
| } |
| |
| static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det) |
| { |
| *det = m->m11 * m->m22 - m->m12 * m->m21; |
| /* on certain conditions we can skip transform */ |
| return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f); |
| } |
| |
| static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, FLOAT ppdip, |
| const DWRITE_MATRIX *m, FLOAT det) |
| { |
| if (!skiptransform) { |
| D2D1_POINT_2F vec2; |
| |
| /* apply transform */ |
| vec->x *= ppdip; |
| vec->y *= ppdip; |
| |
| vec2.x = m->m11 * vec->x + m->m21 * vec->y + m->dx; |
| vec2.y = m->m12 * vec->x + m->m22 * vec->y + m->dy; |
| |
| /* snap */ |
| vec2.x = floorf(vec2.x + 0.5f); |
| vec2.y = floorf(vec2.y + 0.5f); |
| |
| /* apply inverted transform, we don't care about X component at this point */ |
| vec->x = (m->m22 * vec2.x - m->m21 * vec2.y + m->m21 * m->dy - m->m22 * m->dx) / det; |
| vec->x /= ppdip; |
| |
| vec->y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det; |
| vec->y /= ppdip; |
| } |
| else { |
| vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip; |
| vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip; |
| } |
| } |
| |
| static void layout_apply_leading_alignment(struct dwrite_textlayout *layout) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| struct layout_effective_inline *inrun; |
| struct layout_effective_run *erun; |
| |
| erun = layout_get_next_erun(layout, NULL); |
| inrun = layout_get_next_inline_run(layout, NULL); |
| |
| while (erun) { |
| erun->align_dx = 0.0f; |
| erun = layout_get_next_erun(layout, erun); |
| } |
| |
| while (inrun) { |
| inrun->align_dx = 0.0f; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| } |
| |
| layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f; |
| } |
| |
| static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| struct layout_effective_inline *inrun; |
| struct layout_effective_run *erun; |
| UINT32 line; |
| |
| erun = layout_get_next_erun(layout, NULL); |
| inrun = layout_get_next_inline_run(layout, NULL); |
| |
| for (line = 0; line < layout->metrics.lineCount; line++) { |
| FLOAT width = layout_get_line_width(layout, erun, inrun, line); |
| FLOAT shift = layout->metrics.layoutWidth - width; |
| |
| if (is_rtl) |
| shift *= -1.0f; |
| |
| while (erun && erun->line == line) { |
| erun->align_dx = shift; |
| erun = layout_get_next_erun(layout, erun); |
| } |
| |
| while (inrun && inrun->line == line) { |
| inrun->align_dx = shift; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| } |
| } |
| |
| layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width; |
| } |
| |
| static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform, |
| FLOAT width, FLOAT det) |
| { |
| if (is_layout_gdi_compatible(layout)) { |
| D2D1_POINT_2F vec = { layout->metrics.layoutWidth - width, 0.0f}; |
| layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det); |
| return floorf(vec.x / 2.0f); |
| } |
| else |
| return (layout->metrics.layoutWidth - width) / 2.0f; |
| } |
| |
| static void layout_apply_centered_alignment(struct dwrite_textlayout *layout) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| struct layout_effective_inline *inrun; |
| struct layout_effective_run *erun; |
| BOOL skiptransform; |
| UINT32 line; |
| FLOAT det; |
| |
| erun = layout_get_next_erun(layout, NULL); |
| inrun = layout_get_next_inline_run(layout, NULL); |
| |
| skiptransform = should_skip_transform(&layout->transform, &det); |
| |
| for (line = 0; line < layout->metrics.lineCount; line++) { |
| FLOAT width = layout_get_line_width(layout, erun, inrun, line); |
| FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det); |
| |
| if (is_rtl) |
| shift *= -1.0f; |
| |
| while (erun && erun->line == line) { |
| erun->align_dx = shift; |
| erun = layout_get_next_erun(layout, erun); |
| } |
| |
| while (inrun && inrun->line == line) { |
| inrun->align_dx = shift; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| } |
| } |
| |
| layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f; |
| } |
| |
| static void layout_apply_text_alignment(struct dwrite_textlayout *layout) |
| { |
| switch (layout->format.textalignment) |
| { |
| case DWRITE_TEXT_ALIGNMENT_LEADING: |
| layout_apply_leading_alignment(layout); |
| break; |
| case DWRITE_TEXT_ALIGNMENT_TRAILING: |
| layout_apply_trailing_alignment(layout); |
| break; |
| case DWRITE_TEXT_ALIGNMENT_CENTER: |
| layout_apply_centered_alignment(layout); |
| break; |
| case DWRITE_TEXT_ALIGNMENT_JUSTIFIED: |
| FIXME("alignment %d not implemented\n", layout->format.textalignment); |
| break; |
| default: |
| ; |
| } |
| } |
| |
| static void layout_apply_par_alignment(struct dwrite_textlayout *layout) |
| { |
| struct layout_effective_inline *inrun; |
| struct layout_effective_run *erun; |
| FLOAT origin_y = 0.0f; |
| UINT32 line; |
| |
| /* alignment mode defines origin, after that all run origins are updated |
| the same way */ |
| |
| switch (layout->format.paralign) |
| { |
| case DWRITE_PARAGRAPH_ALIGNMENT_NEAR: |
| origin_y = 0.0f; |
| break; |
| case DWRITE_PARAGRAPH_ALIGNMENT_FAR: |
| origin_y = layout->metrics.layoutHeight - layout->metrics.height; |
| break; |
| case DWRITE_PARAGRAPH_ALIGNMENT_CENTER: |
| origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f; |
| break; |
| default: |
| ; |
| } |
| |
| layout->metrics.top = origin_y; |
| |
| erun = layout_get_next_erun(layout, NULL); |
| inrun = layout_get_next_inline_run(layout, NULL); |
| for (line = 0; line < layout->metrics.lineCount; line++) { |
| FLOAT pos_y = origin_y + layout->linemetrics[line].baseline; |
| |
| while (erun && erun->line == line) { |
| erun->origin.y = pos_y; |
| erun = layout_get_next_erun(layout, erun); |
| } |
| |
| while (inrun && inrun->line == line) { |
| inrun->origin.y = pos_y - inrun->baseline; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| } |
| |
| origin_y += layout->linemetrics[line].height; |
| } |
| } |
| |
| struct layout_underline_splitting_params { |
| const WCHAR *locale; /* points to range data, no additional allocation */ |
| IUnknown *effect; /* does not hold another reference */ |
| }; |
| |
| static void init_u_splitting_params_from_erun(struct layout_effective_run *erun, |
| struct layout_underline_splitting_params *params) |
| { |
| params->locale = erun->run->u.regular.descr.localeName; |
| params->effect = erun->effect; |
| } |
| |
| static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left, |
| struct layout_underline_splitting_params *right) |
| { |
| return left->effect == right->effect && !strcmpiW(left->locale, right->locale); |
| } |
| |
| static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first, |
| struct layout_effective_run *last) |
| { |
| FLOAT thickness, offset, runheight; |
| struct layout_effective_run *cur; |
| DWRITE_FONT_METRICS metrics; |
| |
| if (first == layout_get_prev_erun(layout, last)) { |
| layout_get_erun_font_metrics(layout, first, &metrics); |
| thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics); |
| offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics); |
| runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics); |
| } |
| else { |
| FLOAT width = 0.0f; |
| |
| /* Single underline is added for consecutive underlined runs. In this case underline parameters are |
| calculated as weighted average, where run width acts as a weight. */ |
| thickness = offset = runheight = 0.0f; |
| cur = first; |
| do { |
| layout_get_erun_font_metrics(layout, cur, &metrics); |
| |
| thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width; |
| offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width; |
| runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight); |
| width += cur->width; |
| |
| cur = layout_get_next_erun(layout, cur); |
| } while (cur != last); |
| |
| thickness /= width; |
| offset /= width; |
| } |
| |
| cur = first; |
| do { |
| struct layout_underline_splitting_params params, prev_params; |
| struct layout_effective_run *next, *w; |
| struct layout_underline *u; |
| |
| init_u_splitting_params_from_erun(cur, &prev_params); |
| while ((next = layout_get_next_erun(layout, cur)) != last) { |
| init_u_splitting_params_from_erun(next, ¶ms); |
| if (!is_same_u_splitting(&prev_params, ¶ms)) |
| break; |
| cur = next; |
| } |
| |
| u = heap_alloc(sizeof(*u)); |
| if (!u) |
| return E_OUTOFMEMORY; |
| |
| w = cur; |
| u->u.width = 0.0f; |
| while (w != next) { |
| u->u.width += w->width; |
| w = layout_get_next_erun(layout, w); |
| } |
| |
| u->u.thickness = thickness; |
| /* Font metrics convention is to have it negative when below baseline, for rendering |
| however Y grows from baseline down for horizontal baseline. */ |
| u->u.offset = -offset; |
| u->u.runHeight = runheight; |
| u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : |
| DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; |
| u->u.flowDirection = layout->format.flow; |
| u->u.localeName = cur->run->u.regular.descr.localeName; |
| u->u.measuringMode = layout->measuringmode; |
| u->run = cur; |
| list_add_tail(&layout->underlines, &u->entry); |
| |
| cur = next; |
| } while (cur != last); |
| |
| return S_OK; |
| } |
| |
| /* Adds zero width line, metrics are derived from font at specified text position. */ |
| static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| DWRITE_LINE_METRICS1 metrics = { 0 }; |
| DWRITE_FONT_METRICS fontmetrics; |
| struct layout_range *range; |
| IDWriteFontFace *fontface; |
| IDWriteFont *font; |
| HRESULT hr; |
| |
| range = get_layout_range_by_pos(layout, pos); |
| hr = create_matching_font(range->collection, |
| range->fontfamily, |
| range->weight, |
| range->style, |
| range->stretch, |
| &font); |
| if (FAILED(hr)) |
| return hr; |
| hr = IDWriteFont_CreateFontFace(font, &fontface); |
| IDWriteFont_Release(font); |
| if (FAILED(hr)) |
| return hr; |
| |
| layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics); |
| layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height); |
| IDWriteFontFace_Release(fontface); |
| |
| return layout_set_line_metrics(layout, &metrics); |
| } |
| |
| static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster, |
| UINT32 *textpos) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| struct layout_final_splitting_params params, prev_params; |
| DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 }; |
| UINT32 line = layout->metrics.lineCount, i; |
| DWRITE_LINE_METRICS1 metrics = { 0 }; |
| UINT32 index, start, pos = *textpos; |
| FLOAT descent, trailingspacewidth; |
| BOOL append_trimming_run = FALSE; |
| const struct layout_run *run; |
| FLOAT width, origin_x; |
| HRESULT hr; |
| |
| /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */ |
| for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) { |
| DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index]; |
| struct layout_cluster *lc = &layout->clusters[index]; |
| WCHAR ch; |
| |
| /* This also filters out clusters added from inline objects, those are never |
| treated as a white space. */ |
| if (!cluster->isWhitespace) |
| break; |
| |
| /* Every isNewline cluster is also isWhitespace, but not every |
| newline character cluster has isNewline set, so go back to original string. */ |
| ch = lc->run->u.regular.descr.string[lc->position]; |
| if (cluster->length == 1 && lb_is_newline_char(ch)) |
| metrics.newlineLength += cluster->length; |
| |
| metrics.trailingWhitespaceLength += cluster->length; |
| trailingspacewidth += cluster->width; |
| } |
| |
| /* Line metrics length includes trailing whitespace length too */ |
| for (i = first_cluster; i <= last_cluster; i++) |
| metrics.length += layout->clustermetrics[i].length; |
| |
| /* Ignore trailing whitespaces */ |
| while (last_cluster > first_cluster) { |
| if (!layout->clustermetrics[last_cluster].isWhitespace) |
| break; |
| |
| last_cluster--; |
| } |
| |
| /* Does not include trailing space width */ |
| width = get_cluster_range_width(layout, first_cluster, last_cluster + 1); |
| |
| /* Append trimming run if necessary */ |
| if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL && |
| layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) { |
| FLOAT trimmed_width = width; |
| |
| hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics); |
| if (SUCCEEDED(hr)) { |
| while (last_cluster > first_cluster) { |
| if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth) |
| break; |
| if (layout->format.trimming.granularity == DWRITE_TRIMMING_GRANULARITY_CHARACTER) |
| trimmed_width -= layout->clustermetrics[last_cluster--].width; |
| else { |
| while (last_cluster > first_cluster) { |
| trimmed_width -= layout->clustermetrics[last_cluster].width; |
| if (layout->clustermetrics[last_cluster--].canWrapLineAfter) |
| break; |
| } |
| } |
| } |
| append_trimming_run = TRUE; |
| } |
| else |
| WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr); |
| |
| width = trimmed_width + sign_metrics.width; |
| } |
| |
| layout_splitting_params_from_pos(layout, pos, ¶ms); |
| prev_params = params; |
| run = layout->clusters[first_cluster].run; |
| |
| /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */ |
| origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f; |
| for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) { |
| layout_splitting_params_from_pos(layout, pos, ¶ms); |
| |
| if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, ¶ms)) { |
| hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params); |
| if (FAILED(hr)) |
| return; |
| |
| origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : |
| get_cluster_range_width(layout, start, i); |
| run = layout->clusters[i].run; |
| start = i; |
| } |
| |
| prev_params = params; |
| pos += layout->clustermetrics[i].length; |
| } |
| |
| /* Final run from what's left from cluster range */ |
| hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params); |
| if (FAILED(hr)) |
| return; |
| |
| if (append_trimming_run) { |
| struct layout_effective_inline *trimming_sign; |
| |
| trimming_sign = heap_alloc(sizeof(*trimming_sign)); |
| if (!trimming_sign) |
| return; |
| |
| trimming_sign->object = layout->format.trimmingsign; |
| trimming_sign->width = sign_metrics.width; |
| origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i); |
| trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x; |
| trimming_sign->origin.y = 0.0f; /* set after line is built */ |
| trimming_sign->align_dx = 0.0f; |
| trimming_sign->baseline = sign_metrics.baseline; |
| |
| trimming_sign->is_sideways = FALSE; |
| trimming_sign->is_rtl = FALSE; |
| trimming_sign->line = line; |
| |
| trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position + |
| layout->clusters[i].run->start_position); |
| |
| list_add_tail(&layout->inlineobjects, &trimming_sign->entry); |
| } |
| |
| /* Look for max baseline and descent for this line */ |
| for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) { |
| const struct layout_run *cur = layout->clusters[index].run; |
| FLOAT cur_descent = cur->height - cur->baseline; |
| |
| if (cur->baseline > metrics.baseline) |
| metrics.baseline = cur->baseline; |
| if (cur_descent > descent) |
| descent = cur_descent; |
| } |
| |
| layout->metrics.width = max(width, layout->metrics.width); |
| layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth, |
| layout->metrics.widthIncludingTrailingWhitespace); |
| |
| metrics.height = descent + metrics.baseline; |
| metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth; |
| layout_set_line_metrics(layout, &metrics); |
| |
| *textpos += metrics.length; |
| } |
| |
| static void layout_set_line_positions(struct dwrite_textlayout *layout) |
| { |
| struct layout_effective_inline *inrun; |
| struct layout_effective_run *erun; |
| FLOAT origin_y; |
| UINT32 line; |
| |
| /* Now all line info is here, update effective runs positions in flow direction */ |
| erun = layout_get_next_erun(layout, NULL); |
| inrun = layout_get_next_inline_run(layout, NULL); |
| |
| for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) { |
| FLOAT pos_y = origin_y + layout->linemetrics[line].baseline; |
| |
| /* For all runs on this line */ |
| while (erun && erun->line == line) { |
| erun->origin.y = pos_y; |
| erun = layout_get_next_erun(layout, erun); |
| } |
| |
| /* Same for inline runs */ |
| while (inrun && inrun->line == line) { |
| inrun->origin.y = pos_y - inrun->baseline; |
| inrun = layout_get_next_inline_run(layout, inrun); |
| } |
| |
| origin_y += layout->linemetrics[line].height; |
| } |
| |
| layout->metrics.height = origin_y; |
| |
| /* Initial paragraph alignment is always near */ |
| if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR) |
| layout_apply_par_alignment(layout); |
| } |
| |
| static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster) |
| { |
| if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER) |
| return TRUE; |
| |
| return layout->clustermetrics[cluster].canWrapLineAfter; |
| } |
| |
| static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout) |
| { |
| BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| struct layout_effective_run *erun, *first_underlined; |
| UINT32 i, start, textpos, last_breaking_point; |
| DWRITE_LINE_METRICS1 metrics; |
| FLOAT width; |
| UINT32 line; |
| HRESULT hr; |
| |
| if (!(layout->recompute & RECOMPUTE_LINES)) |
| return S_OK; |
| |
| free_layout_eruns(layout); |
| |
| hr = layout_compute(layout); |
| if (FAILED(hr)) |
| return hr; |
| |
| layout->metrics.lineCount = 0; |
| memset(&metrics, 0, sizeof(metrics)); |
| |
| layout->metrics.height = 0.0f; |
| layout->metrics.width = 0.0f; |
| layout->metrics.widthIncludingTrailingWhitespace = 0.0f; |
| |
| last_breaking_point = ~0u; |
| |
| for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) { |
| BOOL overflow = FALSE; |
| |
| while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) { |
| /* Check for overflow */ |
| overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) && |
| (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP)); |
| if (overflow) |
| break; |
| |
| if (layout_can_wrap_after(layout, i)) |
| last_breaking_point = i; |
| width += layout->clustermetrics[i].width; |
| i++; |
| } |
| i = min(i, layout->cluster_count - 1); |
| |
| /* Ignore if overflown on whitespace */ |
| if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) { |
| /* Use most recently found breaking point */ |
| if (last_breaking_point != ~0u) { |
| i = last_breaking_point; |
| last_breaking_point = ~0u; |
| } |
| else { |
| /* Otherwise proceed forward to next newline or breaking point */ |
| for (; i < layout->cluster_count; i++) |
| if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline) |
| break; |
| } |
| } |
| i = min(i, layout->cluster_count - 1); |
| |
| layout_add_line(layout, start, i, &textpos); |
| start = i + 1; |
| width = 0.0f; |
| } |
| |
| /* Add dummy line if: |
| - there's no text, metrics come from first range in this case; |
| - last ended with a mandatory break, metrics come from last text position. |
| */ |
| if (layout->len == 0) |
| hr = layout_set_dummy_line_metrics(layout, 0); |
| else if (layout->clustermetrics[layout->cluster_count - 1].isNewline) |
| hr = layout_set_dummy_line_metrics(layout, layout->len - 1); |
| if (FAILED(hr)) |
| return hr; |
| |
| layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f; |
| layout->metrics.top = 0.0f; |
| layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */ |
| |
| /* Add explicit underlined runs */ |
| erun = layout_get_next_erun(layout, NULL); |
| first_underlined = erun && erun->underlined ? erun : NULL; |
| for (line = 0; line < layout->metrics.lineCount; line++) { |
| while (erun && erun->line == line) { |
| erun = layout_get_next_erun(layout, erun); |
| |
| if (first_underlined && (!erun || !erun->underlined)) { |
| layout_add_underline(layout, first_underlined, erun); |
| first_underlined = NULL; |
| } |
| else if (!first_underlined && erun && erun->underlined) |
| first_underlined = erun; |
| } |
| } |
| |
| /* Position runs in flow direction */ |
| layout_set_line_positions(layout); |
| |
| /* Initial alignment is always leading */ |
| if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING) |
| layout_apply_text_alignment(layout); |
| |
| layout->recompute &= ~RECOMPUTE_LINES; |
| return hr; |
| } |
| |
| static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr, |
| struct layout_range_attr_value *value) |
| { |
| struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h; |
| struct layout_range_iface const *range_iface = (struct layout_range_iface*)h; |
| struct layout_range_bool const *range_bool = (struct layout_range_bool*)h; |
| struct layout_range const *range = (struct layout_range*)h; |
| |
| switch (attr) { |
| case LAYOUT_RANGE_ATTR_WEIGHT: |
| return range->weight == value->u.weight; |
| case LAYOUT_RANGE_ATTR_STYLE: |
| return range->style == value->u.style; |
| case LAYOUT_RANGE_ATTR_STRETCH: |
| return range->stretch == value->u.stretch; |
| case LAYOUT_RANGE_ATTR_FONTSIZE: |
| return range->fontsize == value->u.fontsize; |
| case LAYOUT_RANGE_ATTR_INLINE: |
| return range->object == value->u.object; |
| case LAYOUT_RANGE_ATTR_EFFECT: |
| return range_iface->iface == value->u.effect; |
| case LAYOUT_RANGE_ATTR_UNDERLINE: |
| return range_bool->value == value->u.underline; |
| case LAYOUT_RANGE_ATTR_STRIKETHROUGH: |
| return range_bool->value == value->u.strikethrough; |
| case LAYOUT_RANGE_ATTR_PAIR_KERNING: |
| return range->pair_kerning == value->u.pair_kerning; |
| case LAYOUT_RANGE_ATTR_FONTCOLL: |
| return range->collection == value->u.collection; |
| case LAYOUT_RANGE_ATTR_LOCALE: |
| return strcmpiW(range->locale, value->u.locale) == 0; |
| case LAYOUT_RANGE_ATTR_FONTFAMILY: |
| return strcmpW(range->fontfamily, value->u.fontfamily) == 0; |
| case LAYOUT_RANGE_ATTR_SPACING: |
| return range_spacing->leading == value->u.spacing.leading && |
| range_spacing->trailing == value->u.spacing.trailing && |
| range_spacing->min_advance == value->u.spacing.min_advance; |
| case LAYOUT_RANGE_ATTR_TYPOGRAPHY: |
| return range_iface->iface == (IUnknown*)value->u.typography; |
| default: |
| ; |
| } |
| |
| return FALSE; |
| } |
| |
| static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright) |
| { |
| switch (hleft->kind) |
| { |
| case LAYOUT_RANGE_REGULAR: |
| { |
| struct layout_range const *left = (struct layout_range const*)hleft; |
| struct layout_range const *right = (struct layout_range const*)hright; |
| return left->weight == right->weight && |
| left->style == right->style && |
| left->stretch == right->stretch && |
| left->fontsize == right->fontsize && |
| left->object == right->object && |
| left->pair_kerning == right->pair_kerning && |
| left->collection == right->collection && |
| !strcmpiW(left->locale, right->locale) && |
| !strcmpW(left->fontfamily, right->fontfamily); |
| } |
| case LAYOUT_RANGE_UNDERLINE: |
| case LAYOUT_RANGE_STRIKETHROUGH: |
| { |
| struct layout_range_bool const *left = (struct layout_range_bool const*)hleft; |
| struct layout_range_bool const *right = (struct layout_range_bool const*)hright; |
| return left->value == right->value; |
| } |
| case LAYOUT_RANGE_EFFECT: |
| case LAYOUT_RANGE_TYPOGRAPHY: |
| { |
| struct layout_range_iface const *left = (struct layout_range_iface const*)hleft; |
| struct layout_range_iface const *right = (struct layout_range_iface const*)hright; |
| return left->iface == right->iface; |
| } |
| case LAYOUT_RANGE_SPACING: |
| { |
| struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft; |
| struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright; |
| return left->leading == right->leading && |
| left->trailing == right->trailing && |
| left->min_advance == right->min_advance; |
| } |
| default: |
| FIXME("unknown range kind %d\n", hleft->kind); |
| return FALSE; |
| } |
| } |
| |
| static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right) |
| { |
| return left->startPosition == right->startPosition && left->length == right->length; |
| } |
| |
| /* Allocates range and inits it with default values from text format. */ |
| static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r, |
| enum layout_range_kind kind) |
| { |
| struct layout_range_header *h; |
| |
| switch (kind) |
| { |
| case LAYOUT_RANGE_REGULAR: |
| { |
| struct layout_range *range; |
| |
| range = heap_alloc(sizeof(*range)); |
| if (!range) return NULL; |
| |
| range->weight = layout->format.weight; |
| range->style = layout->format.style; |
| range->stretch = layout->format.stretch; |
| range->fontsize = layout->format.fontsize; |
| range->object = NULL; |
| range->pair_kerning = FALSE; |
| |
| range->fontfamily = heap_strdupW(layout->format.family_name); |
| if (!range->fontfamily) { |
| heap_free(range); |
| return NULL; |
| } |
| |
| range->collection = layout->format.collection; |
| if (range->collection) |
| IDWriteFontCollection_AddRef(range->collection); |
| strcpyW(range->locale, layout->format.locale); |
| |
| h = &range->h; |
| break; |
| } |
| case LAYOUT_RANGE_UNDERLINE: |
| case LAYOUT_RANGE_STRIKETHROUGH: |
| { |
| struct layout_range_bool *range; |
| |
| range = heap_alloc(sizeof(*range)); |
| if (!range) return NULL; |
| |
| range->value = FALSE; |
| h = &range->h; |
| break; |
| } |
| case LAYOUT_RANGE_EFFECT: |
| case LAYOUT_RANGE_TYPOGRAPHY: |
| { |
| struct layout_range_iface *range; |
| |
| range = heap_alloc(sizeof(*range)); |
| if (!range) return NULL; |
| |
| range->iface = NULL; |
| h = &range->h; |
| break; |
| } |
| case LAYOUT_RANGE_SPACING: |
| { |
| struct layout_range_spacing *range; |
| |
| range = heap_alloc(sizeof(*range)); |
| if (!range) return NULL; |
| |
| range->leading = 0.0f; |
| range->trailing = 0.0f; |
| range->min_advance = 0.0f; |
| h = &range->h; |
| break; |
| } |
| default: |
| FIXME("unknown range kind %d\n", kind); |
| return NULL; |
| } |
| |
| h->kind = kind; |
| h->range = *r; |
| return h; |
| } |
| |
| static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r) |
| { |
| struct layout_range_header *ret; |
| |
| switch (h->kind) |
| { |
| case LAYOUT_RANGE_REGULAR: |
| { |
| struct layout_range *from = (struct layout_range*)h; |
| |
| struct layout_range *range = heap_alloc(sizeof(*range)); |
| if (!range) return NULL; |
| |
| *range = *from; |
| range->fontfamily = heap_strdupW(from->fontfamily); |
| if (!range->fontfamily) { |
| heap_free(range); |
| return NULL; |
| } |
| |
| /* update refcounts */ |
| if (range->object) |
| IDWriteInlineObject_AddRef(range->object); |
| if (range->collection) |
| IDWriteFontCollection_AddRef(range->collection); |
| ret = &range->h; |
| break; |
| } |
| case LAYOUT_RANGE_UNDERLINE: |
| case LAYOUT_RANGE_STRIKETHROUGH: |
| { |
| struct layout_range_bool *strike = heap_alloc(sizeof(*strike)); |
| if (!strike) return NULL; |
| |
| *strike = *(struct layout_range_bool*)h; |
| ret = &strike->h; |
| break; |
| } |
| case LAYOUT_RANGE_EFFECT: |
| case LAYOUT_RANGE_TYPOGRAPHY: |
| { |
| struct layout_range_iface *effect = heap_alloc(sizeof(*effect)); |
| if (!effect) return NULL; |
| |
| *effect = *(struct layout_range_iface*)h; |
| if (effect->iface) |
| IUnknown_AddRef(effect->iface); |
| ret = &effect->h; |
| break; |
| } |
| case LAYOUT_RANGE_SPACING: |
| { |
| struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing)); |
| if (!spacing) return NULL; |
| |
| *spacing = *(struct layout_range_spacing*)h; |
| ret = &spacing->h; |
| break; |
| } |
| default: |
| FIXME("unknown range kind %d\n", h->kind); |
| return NULL; |
| } |
| |
| ret->range = *r; |
| return ret; |
| } |
| |
| static void free_layout_range(struct layout_range_header *h) |
| { |
| if (!h) |
| return; |
| |
| switch (h->kind) |
| { |
| case LAYOUT_RANGE_REGULAR: |
| { |
| struct layout_range *range = (struct layout_range*)h; |
| |
| if (range->object) |
| IDWriteInlineObject_Release(range->object); |
| if (range->collection) |
| IDWriteFontCollection_Release(range->collection); |
| heap_free(range->fontfamily); |
| break; |
| } |
| case LAYOUT_RANGE_EFFECT: |
| case LAYOUT_RANGE_TYPOGRAPHY: |
| { |
| struct layout_range_iface *range = (struct layout_range_iface*)h; |
| if (range->iface) |
| IUnknown_Release(range->iface); |
| break; |
| } |
| default: |
| ; |
| } |
| |
| heap_free(h); |
| } |
| |
| static void free_layout_ranges_list(struct dwrite_textlayout *layout) |
| { |
| struct layout_range_header *cur, *cur2; |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| |
| LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) { |
| list_remove(&cur->entry); |
| free_layout_range(cur); |
| } |
| } |
| |
| static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range) |
| { |
| struct layout_range_header *cur; |
| |
| LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) { |
| |
| if (cur->range.startPosition > range->startPosition) |
| return NULL; |
| |
| if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) && |
| (range->startPosition < cur->range.startPosition + cur->range.length)) |
| return NULL; |
| if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length) |
| return cur; |
| } |
| |
| return NULL; |
| } |
| |
| static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos) |
| { |
| struct layout_range *cur; |
| |
| LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) { |
| DWRITE_TEXT_RANGE *r = &cur->h.range; |
| if (r->startPosition <= pos && pos < r->startPosition + r->length) |
| return cur; |
| } |
| |
| return NULL; |
| } |
| |
| static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value) |
| { |
| if (*dest == value) return FALSE; |
| |
| if (*dest) |
| IUnknown_Release(*dest); |
| *dest = value; |
| if (*dest) |
| IUnknown_AddRef(*dest); |
| |
| return TRUE; |
| } |
| |
| static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value) |
| { |
| struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h; |
| struct layout_range_iface *dest_iface = (struct layout_range_iface*)h; |
| struct layout_range_bool *dest_bool = (struct layout_range_bool*)h; |
| struct layout_range *dest = (struct layout_range*)h; |
| |
| BOOL changed = FALSE; |
| |
| switch (attr) { |
| case LAYOUT_RANGE_ATTR_WEIGHT: |
| changed = dest->weight != value->u.weight; |
| dest->weight = value->u.weight; |
| break; |
| case LAYOUT_RANGE_ATTR_STYLE: |
| changed = dest->style != value->u.style; |
| dest->style = value->u.style; |
| break; |
| case LAYOUT_RANGE_ATTR_STRETCH: |
| changed = dest->stretch != value->u.stretch; |
| dest->stretch = value->u.stretch; |
| break; |
| case LAYOUT_RANGE_ATTR_FONTSIZE: |
| changed = dest->fontsize != value->u.fontsize; |
| dest->fontsize = value->u.fontsize; |
| break; |
| case LAYOUT_RANGE_ATTR_INLINE: |
| changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object); |
| break; |
| case LAYOUT_RANGE_ATTR_EFFECT: |
| changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.effect); |
| break; |
| case LAYOUT_RANGE_ATTR_UNDERLINE: |
| changed = dest_bool->value != value->u.underline; |
| dest_bool->value = value->u.underline; |
| break; |
| case LAYOUT_RANGE_ATTR_STRIKETHROUGH: |
| changed = dest_bool->value != value->u.strikethrough; |
| dest_bool->value = value->u.strikethrough; |
| break; |
| case LAYOUT_RANGE_ATTR_PAIR_KERNING: |
| changed = dest->pair_kerning != value->u.pair_kerning; |
| dest->pair_kerning = value->u.pair_kerning; |
| break; |
| case LAYOUT_RANGE_ATTR_FONTCOLL: |
| changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection); |
| break; |
| case LAYOUT_RANGE_ATTR_LOCALE: |
| changed = strcmpiW(dest->locale, value->u.locale) != 0; |
| if (changed) { |
| strcpyW(dest->locale, value->u.locale); |
| strlwrW(dest->locale); |
| } |
| break; |
| case LAYOUT_RANGE_ATTR_FONTFAMILY: |
| changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0; |
| if (changed) { |
| heap_free(dest->fontfamily); |
| dest->fontfamily = heap_strdupW(value->u.fontfamily); |
| } |
| break; |
| case LAYOUT_RANGE_ATTR_SPACING: |
| changed = dest_spacing->leading != value->u.spacing.leading || |
| dest_spacing->trailing != value->u.spacing.trailing || |
| dest_spacing->min_advance != value->u.spacing.min_advance; |
| dest_spacing->leading = value->u.spacing.leading; |
| dest_spacing->trailing = value->u.spacing.trailing; |
| dest_spacing->min_advance = value->u.spacing.min_advance; |
| break; |
| case LAYOUT_RANGE_ATTR_TYPOGRAPHY: |
| changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.typography); |
| break; |
| default: |
| ; |
| } |
| |
| return changed; |
| } |
| |
| static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner) |
| { |
| return (inner->startPosition >= outer->startPosition) && |
| (inner->startPosition + inner->length <= outer->startPosition + outer->length); |
| } |
| |
| static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r) |
| { |
| if (r) *r = h->range; |
| return S_OK; |
| } |
| |
| /* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */ |
| static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value) |
| { |
| struct layout_range_header *cur, *right, *left, *outer; |
| BOOL changed = FALSE; |
| struct list *ranges; |
| DWRITE_TEXT_RANGE r; |
| |
| /* ignore zero length ranges */ |
| if (value->range.length == 0) |
| return S_OK; |
| |
| /* select from ranges lists */ |
| switch (attr) |
| { |
| case LAYOUT_RANGE_ATTR_WEIGHT: |
| case LAYOUT_RANGE_ATTR_STYLE: |
| case LAYOUT_RANGE_ATTR_STRETCH: |
| case LAYOUT_RANGE_ATTR_FONTSIZE: |
| case LAYOUT_RANGE_ATTR_INLINE: |
| case LAYOUT_RANGE_ATTR_PAIR_KERNING: |
| case LAYOUT_RANGE_ATTR_FONTCOLL: |
| case LAYOUT_RANGE_ATTR_LOCALE: |
| case LAYOUT_RANGE_ATTR_FONTFAMILY: |
| ranges = &layout->ranges; |
| break; |
| case LAYOUT_RANGE_ATTR_UNDERLINE: |
| ranges = &layout->underline_ranges; |
| break; |
| case LAYOUT_RANGE_ATTR_STRIKETHROUGH: |
| ranges = &layout->strike_ranges; |
| break; |
| case LAYOUT_RANGE_ATTR_EFFECT: |
| ranges = &layout->effects; |
| break; |
| case LAYOUT_RANGE_ATTR_SPACING: |
| ranges = &layout->spacing; |
| break; |
| case LAYOUT_RANGE_ATTR_TYPOGRAPHY: |
| ranges = &layout->typographies; |
| break; |
| default: |
| FIXME("unknown attr kind %d\n", attr); |
| return E_FAIL; |
| } |
| |
| /* If new range is completely within existing range, split existing range in two */ |
| if ((outer = find_outer_range(ranges, &value->range))) { |
| |
| /* no need to add same range */ |
| if (is_same_layout_attrvalue(outer, attr, value)) |
| return S_OK; |
| |
| /* for matching range bounds just replace data */ |
| if (is_same_text_range(&outer->range, &value->range)) { |
| changed = set_layout_range_attrval(outer, attr, value); |
| goto done; |
| } |
| |
| /* add new range to the left */ |
| if (value->range.startPosition == outer->range.startPosition) { |
| left = alloc_layout_range_from(outer, &value->range); |
| if (!left) return E_OUTOFMEMORY; |
| |
| changed = set_layout_range_attrval(left, attr, value); |
| list_add_before(&outer->entry, &left->entry); |
| outer->range.startPosition += value->range.length; |
| outer->range.length -= value->range.length; |
| goto done; |
| } |
| |
| /* add new range to the right */ |
| if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) { |
| right = alloc_layout_range_from(outer, &value->range); |
| if (!right) return E_OUTOFMEMORY; |
| |
| changed = set_layout_range_attrval(right, attr, value); |
| list_add_after(&outer->entry, &right->entry); |
| outer->range.length -= value->range.length; |
| goto done; |
| } |
| |
| r.startPosition = value->range.startPosition + value->range.length; |
| r.length = outer->range.length + outer->range.startPosition - r.startPosition; |
| |
| /* right part */ |
| right = alloc_layout_range_from(outer, &r); |
| /* new range in the middle */ |
| cur = alloc_layout_range_from(outer, &value->range); |
| if (!right || !cur) { |
| free_layout_range(right); |
| free_layout_range(cur); |
| return E_OUTOFMEMORY; |
| } |
| |
| /* reuse container range as a left part */ |
| outer->range.length = value->range.startPosition - outer->range.startPosition; |
| |
| /* new part */ |
| set_layout_range_attrval(cur, attr, value); |
| |
| list_add_after(&outer->entry, &cur->entry); |
| list_add_after(&cur->entry, &right->entry); |
| |
| layout->recompute = RECOMPUTE_EVERYTHING; |
| return S_OK; |
| } |
| |
| /* Now it's only possible that given range contains some existing ranges, fully or partially. |
| Update all of them. */ |
| left = get_layout_range_header_by_pos(ranges, value->range.startPosition); |
| if (left->range.startPosition == value->range.startPosition) |
| changed = set_layout_range_attrval(left, attr, value); |
| else /* need to split */ { |
| r.startPosition = value->range.startPosition; |
| r.length = left->range.length - value->range.startPosition + left->range.startPosition; |
| left->range.length -= r.length; |
| cur = alloc_layout_range_from(left, &r); |
| changed = set_layout_range_attrval(cur, attr, value); |
| list_add_after(&left->entry, &cur->entry); |
| } |
| cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry); |
| |
| /* for all existing ranges covered by new one update value */ |
| while (cur && is_in_layout_range(&value->range, &cur->range)) { |
| changed |= set_layout_range_attrval(cur, attr, value); |
| cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry); |
| } |
| |
| /* it's possible rightmost range intersects */ |
| if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) { |
| r.startPosition = cur->range.startPosition; |
| r.length = value->range.startPosition + value->range.length - cur->range.startPosition; |
| left = alloc_layout_range_from(cur, &r); |
| changed |= set_layout_range_attrval(left, attr, value); |
| cur->range.startPosition += left->range.length; |
| cur->range.length -= left->range.length; |
| list_add_before(&cur->entry, &left->entry); |
| } |
| |
| done: |
| if (changed) { |
| struct list *next, *i; |
| |
| layout->recompute = RECOMPUTE_EVERYTHING; |
| i = list_head(ranges); |
| while ((next = list_next(ranges, i))) { |
| struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry); |
| |
| cur = LIST_ENTRY(i, struct layout_range_header, entry); |
| if (is_same_layout_attributes(cur, next_range)) { |
| /* remove similar range */ |
| cur->range.length += next_range->range.length; |
| list_remove(next); |
| free_layout_range(next_range); |
| } |
| else |
| i = list_next(ranges, i); |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind) |
| { |
| const WCHAR *str; |
| |
| switch (kind) { |
| case LAYOUT_RANGE_ATTR_LOCALE: |
| str = range->locale; |
| break; |
| case LAYOUT_RANGE_ATTR_FONTFAMILY: |
| str = range->fontfamily; |
| break; |
| default: |
| str = NULL; |
| } |
| |
| return str; |
| } |
| |
| static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position, |
| UINT32 *length, DWRITE_TEXT_RANGE *r) |
| { |
| struct layout_range *range; |
| const WCHAR *str; |
| |
| range = get_layout_range_by_pos(layout, position); |
| if (!range) { |
| *length = 0; |
| return S_OK; |
| } |
| |
| str = get_string_attribute_ptr(range, kind); |
| *length = strlenW(str); |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position, |
| WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r) |
| { |
| struct layout_range *range; |
| const WCHAR *str; |
| |
| if (length == 0) |
| return E_INVALIDARG; |
| |
| ret[0] = 0; |
| range = get_layout_range_by_pos(layout, position); |
| if (!range) |
| return E_INVALIDARG; |
| |
| str = get_string_attribute_ptr(range, kind); |
| if (length < strlenW(str) + 1) |
| return E_NOT_SUFFICIENT_BUFFER; |
| |
| strcpyW(ret, str); |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout3 *iface, REFIID riid, void **obj) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); |
| |
| *obj = NULL; |
| |
| if (IsEqualIID(riid, &IID_IDWriteTextLayout3) || |
| IsEqualIID(riid, &IID_IDWriteTextLayout2) || |
| IsEqualIID(riid, &IID_IDWriteTextLayout1) || |
| IsEqualIID(riid, &IID_IDWriteTextLayout) || |
| IsEqualIID(riid, &IID_IUnknown)) |
| { |
| *obj = iface; |
| } |
| else if (IsEqualIID(riid, &IID_IDWriteTextFormat1) || |
| IsEqualIID(riid, &IID_IDWriteTextFormat)) |
| *obj = &This->IDWriteTextFormat1_iface; |
| |
| if (*obj) { |
| IDWriteTextLayout3_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| TRACE("(%p)->(%d)\n", This, ref); |
| return ref; |
| } |
| |
| static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p)->(%d)\n", This, ref); |
| |
| if (!ref) { |
| IDWriteFactory5_Release(This->factory); |
| free_layout_ranges_list(This); |
| free_layout_eruns(This); |
| free_layout_runs(This); |
| release_format_data(&This->format); |
| heap_free(This->nominal_breakpoints); |
| heap_free(This->actual_breakpoints); |
| heap_free(This->clustermetrics); |
| heap_free(This->clusters); |
| heap_free(This->linemetrics); |
| heap_free(This->lines); |
| heap_free(This->str); |
| heap_free(This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%d)\n", This, direction); |
| return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%.2f)\n", This, tabstop); |
| return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming, |
| IDWriteInlineObject *trimming_sign) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign); |
| return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing, |
| FLOAT line_spacing, FLOAT baseline) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline); |
| return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline); |
| } |
| |
| static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options, |
| IDWriteInlineObject **trimming_sign) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method, |
| FLOAT *spacing, FLOAT *baseline) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat*)&This->IDWriteTextFormat1_iface, method, spacing, baseline); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection); |
| } |
| |
| static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size); |
| } |
| |
| static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| BOOL changed; |
| |
| TRACE("(%p)->(%.2f)\n", This, maxWidth); |
| |
| if (maxWidth < 0.0f) |
| return E_INVALIDARG; |
| |
| changed = This->metrics.layoutWidth != maxWidth; |
| This->metrics.layoutWidth = maxWidth; |
| |
| if (changed) |
| This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| BOOL changed; |
| |
| TRACE("(%p)->(%.2f)\n", This, maxHeight); |
| |
| if (maxHeight < 0.0f) |
| return E_INVALIDARG; |
| |
| changed = This->metrics.layoutHeight != maxHeight; |
| This->metrics.layoutHeight = maxHeight; |
| |
| if (changed) |
| This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.collection = collection; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range)); |
| |
| if (!name) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.fontfamily = name; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range)); |
| |
| if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.weight = weight; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range)); |
| |
| if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.style = style; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range)); |
| |
| if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.stretch = stretch; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range)); |
| |
| if (size <= 0.0f) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.fontsize = size; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.underline = underline; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.strikethrough = strikethrough; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.effect = effect; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.object = object; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.typography = typography; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range)); |
| |
| if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.locale = locale; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value); |
| } |
| |
| static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)\n", This); |
| return This->metrics.layoutWidth; |
| } |
| |
| static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)\n", This); |
| return This->metrics.layoutHeight; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position, |
| IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, collection, r); |
| |
| if (position >= This->len) |
| return S_OK; |
| |
| range = get_layout_range_by_pos(This, position); |
| *collection = range->collection; |
| if (*collection) |
| IDWriteFontCollection_AddRef(*collection); |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface, |
| UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%d %p %p)\n", This, position, length, r); |
| return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface, |
| UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r); |
| return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface, |
| UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, weight, r); |
| |
| if (position >= This->len) |
| return S_OK; |
| |
| range = get_layout_range_by_pos(This, position); |
| *weight = range->weight; |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface, |
| UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, style, r); |
| |
| range = get_layout_range_by_pos(This, position); |
| *style = range->style; |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface, |
| UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r); |
| |
| range = get_layout_range_by_pos(This, position); |
| *stretch = range->stretch; |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface, |
| UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, size, r); |
| |
| range = get_layout_range_by_pos(This, position); |
| *size = range->fontsize; |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface, |
| UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_bool *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, underline, r); |
| |
| range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position); |
| *underline = range->value; |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface, |
| UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_bool *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r); |
| |
| range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position); |
| *strikethrough = range->value; |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface, |
| UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_iface *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, effect, r); |
| |
| range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position); |
| *effect = range->iface; |
| if (*effect) |
| IUnknown_AddRef(*effect); |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface, |
| UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, object, r); |
| |
| if (position >= This->len) |
| return S_OK; |
| |
| range = get_layout_range_by_pos(This, position); |
| *object = range->object; |
| if (*object) |
| IDWriteInlineObject_AddRef(*object); |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface, |
| UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_iface *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, typography, r); |
| |
| range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position); |
| *typography = (IDWriteTypography*)range->iface; |
| if (*typography) |
| IDWriteTypography_AddRef(*typography); |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface, |
| UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%u %p %p)\n", This, position, length, r); |
| return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface, |
| UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r); |
| return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r); |
| } |
| |
| static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det, |
| const DWRITE_MATRIX *m) |
| { |
| D2D1_POINT_2F vec, vec2; |
| |
| if (!skiptransform) { |
| /* apply transform */ |
| vec.x = 0.0f; |
| vec.y = coord * ppdip; |
| |
| vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx; |
| vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy; |
| |
| /* snap */ |
| vec2.x = floorf(vec2.x + 0.5f); |
| vec2.y = floorf(vec2.y + 0.5f); |
| |
| /* apply inverted transform, we don't care about X component at this point */ |
| vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det; |
| vec.y /= ppdip; |
| } |
| else |
| vec.y = floorf(coord * ppdip + 0.5f) / ppdip; |
| |
| return vec.y; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface, |
| void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| BOOL disabled = FALSE, skiptransform = FALSE; |
| struct layout_effective_inline *inlineobject; |
| struct layout_effective_run *run; |
| struct layout_strikethrough *s; |
| struct layout_underline *u; |
| FLOAT det = 0.0f, ppdip = 0.0f; |
| DWRITE_MATRIX m = { 0 }; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y); |
| |
| hr = layout_compute_effective_runs(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (!disabled) { |
| hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip); |
| if (FAILED(hr)) |
| return hr; |
| |
| hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m); |
| if (FAILED(hr)) |
| return hr; |
| |
| /* it's only allowed to have a diagonal/antidiagonal transform matrix */ |
| if (ppdip <= 0.0f || |
| (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) || |
| (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f))) |
| disabled = TRUE; |
| else |
| skiptransform = should_skip_transform(&m, &det); |
| } |
| |
| #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m)) |
| /* 1. Regular runs */ |
| LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) { |
| const struct regular_layout_run *regular = &run->run->u.regular; |
| UINT32 start_glyph = regular->clustermap[run->start]; |
| DWRITE_GLYPH_RUN_DESCRIPTION descr; |
| DWRITE_GLYPH_RUN glyph_run; |
| |
| /* Everything but cluster map will be reused from nominal run, as we only need |
| to adjust some pointers. Cluster map however is rebuilt when effective run is added, |
| it can't be reused because it has to start with 0 index for each reported run. */ |
| glyph_run = regular->run; |
| glyph_run.glyphCount = run->glyphcount; |
| |
| /* fixup glyph data arrays */ |
| glyph_run.glyphIndices += start_glyph; |
| glyph_run.glyphAdvances += start_glyph; |
| glyph_run.glyphOffsets += start_glyph; |
| |
| /* description */ |
| descr = regular->descr; |
| descr.stringLength = run->length; |
| descr.string += run->start; |
| descr.clusterMap = run->clustermap; |
| descr.textPosition += run->start; |
| |
| /* return value is ignored */ |
| IDWriteTextRenderer_DrawGlyphRun(renderer, |
| context, |
| run->origin.x + run->align_dx + origin_x, |
| SNAP_COORD(run->origin.y + origin_y), |
| This->measuringmode, |
| &glyph_run, |
| &descr, |
| run->effect); |
| } |
| |
| /* 2. Inline objects */ |
| LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) { |
| IDWriteTextRenderer_DrawInlineObject(renderer, |
| context, |
| inlineobject->origin.x + inlineobject->align_dx + origin_x, |
| SNAP_COORD(inlineobject->origin.y + origin_y), |
| inlineobject->object, |
| inlineobject->is_sideways, |
| inlineobject->is_rtl, |
| inlineobject->effect); |
| } |
| |
| /* 3. Underlines */ |
| LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) { |
| IDWriteTextRenderer_DrawUnderline(renderer, |
| context, |
| /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */ |
| (is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x, |
| SNAP_COORD(u->run->origin.y + origin_y), |
| &u->u, |
| u->run->effect); |
| } |
| |
| /* 4. Strikethrough */ |
| LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) { |
| IDWriteTextRenderer_DrawStrikethrough(renderer, |
| context, |
| s->run->origin.x + s->run->align_dx + origin_x, |
| SNAP_COORD(s->run->origin.y + origin_y), |
| &s->s, |
| s->run->effect); |
| } |
| #undef SNAP_COORD |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface, |
| DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count); |
| |
| hr = layout_compute_effective_runs(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (metrics) { |
| UINT32 i, c = min(max_count, This->metrics.lineCount); |
| for (i = 0; i < c; i++) |
| memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics)); |
| } |
| |
| *count = This->metrics.lineCount; |
| return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| DWRITE_TEXT_METRICS1 metrics1; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, metrics); |
| |
| hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1); |
| if (hr == S_OK) |
| memcpy(metrics, &metrics1, sizeof(*metrics)); |
| |
| return hr; |
| } |
| |
| static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D1_RECT_F *ret) |
| { |
| #define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em) |
| ret->left = SCALE(bbox->left); |
| ret->right = SCALE(bbox->right); |
| ret->top = SCALE(bbox->top); |
| ret->bottom = SCALE(bbox->bottom); |
| #undef SCALE |
| } |
| |
| static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y) |
| { |
| rect->left += x; |
| rect->right += x; |
| rect->top += y; |
| rect->bottom += y; |
| } |
| |
| static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect) |
| { |
| return ((rect->left >= rect->right) || (rect->top >= rect->bottom)); |
| } |
| |
| static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src) |
| { |
| if (d2d_rect_is_empty(dst)) { |
| if (d2d_rect_is_empty(src)) { |
| dst->left = dst->right = dst->top = dst->bottom = 0.0f; |
| return; |
| } |
| else |
| *dst = *src; |
| } |
| else { |
| if (!d2d_rect_is_empty(src)) { |
| dst->left = min(dst->left, src->left); |
| dst->right = max(dst->right, src->right); |
| dst->top = min(dst->top, src->top); |
| dst->bottom = max(dst->bottom, src->bottom); |
| } |
| } |
| } |
| |
| static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox) |
| { |
| const struct regular_layout_run *regular = &run->run->u.regular; |
| UINT32 start_glyph = regular->clustermap[run->start]; |
| const DWRITE_GLYPH_RUN *glyph_run = ®ular->run; |
| DWRITE_FONT_METRICS font_metrics; |
| D2D1_POINT_2F origin = { 0 }; |
| UINT32 i; |
| |
| if (run->bbox.top == run->bbox.bottom) { |
| IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics); |
| |
| for (i = 0; i < run->glyphcount; i++) { |
| D2D1_RECT_F glyph_bbox; |
| RECT design_bbox; |
| |
| freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm, |
| glyph_run->glyphIndices[i + start_glyph], &design_bbox); |
| |
| scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox); |
| d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset, |
| origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset); |
| d2d_rect_union(&run->bbox, &glyph_bbox); |
| |
| /* FIXME: take care of vertical/rtl */ |
| origin.x += glyph_run->glyphAdvances[i + start_glyph]; |
| } |
| } |
| |
| *bbox = run->bbox; |
| d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y); |
| } |
| |
| static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run, |
| D2D1_RECT_F *bbox) |
| { |
| DWRITE_OVERHANG_METRICS overhang_metrics = { 0 }; |
| DWRITE_INLINE_OBJECT_METRICS metrics = { 0 }; |
| HRESULT hr; |
| |
| if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) { |
| WARN("Failed to get inline object metrics, hr %#x.\n", hr); |
| memset(bbox, 0, sizeof(*bbox)); |
| return; |
| } |
| |
| bbox->left = run->origin.x + run->align_dx; |
| bbox->right = bbox->left + metrics.width; |
| bbox->top = run->origin.y; |
| bbox->bottom = bbox->top + metrics.height; |
| |
| IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics); |
| |
| bbox->left -= overhang_metrics.left; |
| bbox->right += overhang_metrics.right; |
| bbox->top -= overhang_metrics.top; |
| bbox->bottom += overhang_metrics.bottom; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface, |
| DWRITE_OVERHANG_METRICS *overhangs) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_effective_inline *inline_run; |
| struct layout_effective_run *run; |
| D2D1_RECT_F bbox = { 0 }; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, overhangs); |
| |
| memset(overhangs, 0, sizeof(*overhangs)); |
| |
| if (!(This->recompute & RECOMPUTE_OVERHANGS)) { |
| *overhangs = This->overhangs; |
| return S_OK; |
| } |
| |
| hr = layout_compute_effective_runs(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) { |
| D2D1_RECT_F run_bbox; |
| |
| layout_get_erun_bbox(This, run, &run_bbox); |
| d2d_rect_union(&bbox, &run_bbox); |
| } |
| |
| LIST_FOR_EACH_ENTRY(inline_run, &This->inlineobjects, struct layout_effective_inline, entry) { |
| D2D1_RECT_F object_bbox; |
| |
| layout_get_inlineobj_bbox(This, inline_run, &object_bbox); |
| d2d_rect_union(&bbox, &object_bbox); |
| } |
| |
| /* Deltas from layout box. */ |
| This->overhangs.left = -bbox.left; |
| This->overhangs.top = -bbox.top; |
| This->overhangs.right = bbox.right - This->metrics.layoutWidth; |
| This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight; |
| This->recompute &= ~RECOMPUTE_OVERHANGS; |
| |
| *overhangs = This->overhangs; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface, |
| DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count); |
| |
| hr = layout_compute(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (metrics) |
| memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count)); |
| |
| *count = This->cluster_count; |
| return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| UINT32 start; |
| FLOAT width; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, min_width); |
| |
| if (!min_width) |
| return E_INVALIDARG; |
| |
| if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH)) |
| goto width_done; |
| |
| *min_width = 0.0f; |
| hr = layout_compute(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| /* Find widest word without emergency breaking between clusters, trailing whitespaces |
| preceding breaking point do not contribute to word width. */ |
| for (start = 0; start < This->cluster_count;) { |
| UINT32 end = start, j, next; |
| |
| /* Last cluster always could be wrapped after. */ |
| while (!This->clustermetrics[end].canWrapLineAfter) |
| end++; |
| /* make is so current cluster range that we can wrap after is [start,end) */ |
| end++; |
| |
| next = end; |
| |
| /* Ignore trailing whitespace clusters, in case of single space range will |
| be reduced to empty range, or [start,start+1). */ |
| while (end > start && This->clustermetrics[end-1].isWhitespace) |
| end--; |
| |
| /* check if cluster range exceeds last minimal width */ |
| width = 0.0f; |
| for (j = start; j < end; j++) |
| width += This->clustermetrics[j].width; |
| |
| start = next; |
| |
| if (width > This->minwidth) |
| This->minwidth = width; |
| } |
| This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH; |
| |
| width_done: |
| *min_width = This->minwidth; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface, |
| FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface, |
| UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface, |
| UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY, |
| DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics, |
| max_metricscount, actual_metricscount); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled, |
| DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range)); |
| |
| value.range = range; |
| value.u.pair_kerning = !!is_pairkerning_enabled; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled, |
| DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range *range; |
| |
| TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r); |
| |
| if (position >= This->len) |
| return S_OK; |
| |
| range = get_layout_range_by_pos(This, position); |
| *is_pairkerning_enabled = range->pair_kerning; |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing, |
| FLOAT min_advance, DWRITE_TEXT_RANGE range) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_attr_value value; |
| |
| TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range)); |
| |
| if (min_advance < 0.0f) |
| return E_INVALIDARG; |
| |
| value.range = range; |
| value.u.spacing.leading = leading; |
| value.u.spacing.trailing = trailing; |
| value.u.spacing.min_advance = min_advance; |
| return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading, |
| FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| struct layout_range_spacing *range; |
| |
| TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r); |
| |
| range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position); |
| *leading = range->leading; |
| *trailing = range->trailing; |
| *min_advance = range->min_advance; |
| |
| return return_range(&range->h, r); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, metrics); |
| |
| hr = layout_compute_effective_runs(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| *metrics = This->metrics; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| |
| TRACE("(%p)->(%d)\n", This, orientation); |
| |
| if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED) |
| return E_INVALIDARG; |
| |
| This->format.vertical_orientation = orientation; |
| return S_OK; |
| } |
| |
| static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)\n", This); |
| return This->format.vertical_orientation; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled); |
| return IDWriteTextFormat1_SetLastLineWrapping(&This->IDWriteTextFormat1_iface, lastline_wrapping_enabled); |
| } |
| |
| static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)\n", This); |
| return IDWriteTextFormat1_GetLastLineWrapping(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%d)\n", This, alignment); |
| return IDWriteTextFormat1_SetOpticalAlignment(&This->IDWriteTextFormat1_iface, alignment); |
| } |
| |
| static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)\n", This); |
| return IDWriteTextFormat1_GetOpticalAlignment(&This->IDWriteTextFormat1_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return set_fontfallback_for_format(&This->format, fallback); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return get_fontfallback_from_format(&This->format, fallback); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| |
| TRACE("(%p)\n", This); |
| |
| This->recompute = RECOMPUTE_EVERYTHING; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, spacing); |
| |
| hr = format_set_linespacing(&This->format, spacing, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) { |
| if (!(This->recompute & RECOMPUTE_LINES)) { |
| UINT32 line; |
| |
| for (line = 0; line < This->metrics.lineCount; line++) |
| layout_apply_line_spacing(This, line); |
| |
| layout_set_line_positions(This); |
| } |
| |
| This->recompute |= RECOMPUTE_OVERHANGS; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| |
| TRACE("(%p)->(%p)\n", This, spacing); |
| |
| *spacing = This->format.spacing; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics, |
| UINT32 max_count, UINT32 *count) |
| { |
| struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface); |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count); |
| |
| hr = layout_compute_effective_runs(This); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (metrics) |
| memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount)); |
| |
| *count = This->metrics.lineCount; |
| return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER; |
| } |
| |
| static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = { |
| dwritetextlayout_QueryInterface, |
| dwritetextlayout_AddRef, |
| dwritetextlayout_Release, |
| dwritetextlayout_SetTextAlignment, |
| dwritetextlayout_SetParagraphAlignment, |
| dwritetextlayout_SetWordWrapping, |
| dwritetextlayout_SetReadingDirection, |
| dwritetextlayout_SetFlowDirection, |
| dwritetextlayout_SetIncrementalTabStop, |
| dwritetextlayout_SetTrimming, |
| dwritetextlayout_SetLineSpacing, |
| dwritetextlayout_GetTextAlignment, |
| dwritetextlayout_GetParagraphAlignment, |
| dwritetextlayout_GetWordWrapping, |
| dwritetextlayout_GetReadingDirection, |
| dwritetextlayout_GetFlowDirection, |
| dwritetextlayout_GetIncrementalTabStop, |
| dwritetextlayout_GetTrimming, |
| dwritetextlayout_GetLineSpacing, |
| dwritetextlayout_GetFontCollection, |
| dwritetextlayout_GetFontFamilyNameLength, |
| dwritetextlayout_GetFontFamilyName, |
| dwritetextlayout_GetFontWeight, |
| dwritetextlayout_GetFontStyle, |
| dwritetextlayout_GetFontStretch, |
| dwritetextlayout_GetFontSize, |
| dwritetextlayout_GetLocaleNameLength, |
| dwritetextlayout_GetLocaleName, |
| dwritetextlayout_SetMaxWidth, |
| dwritetextlayout_SetMaxHeight, |
| dwritetextlayout_SetFontCollection, |
| dwritetextlayout_SetFontFamilyName, |
| dwritetextlayout_SetFontWeight, |
| dwritetextlayout_SetFontStyle, |
| dwritetextlayout_SetFontStretch, |
| dwritetextlayout_SetFontSize, |
| dwritetextlayout_SetUnderline, |
| dwritetextlayout_SetStrikethrough, |
| dwritetextlayout_SetDrawingEffect, |
| dwritetextlayout_SetInlineObject, |
| dwritetextlayout_SetTypography, |
| dwritetextlayout_SetLocaleName, |
| dwritetextlayout_GetMaxWidth, |
| dwritetextlayout_GetMaxHeight, |
| dwritetextlayout_layout_GetFontCollection, |
| dwritetextlayout_layout_GetFontFamilyNameLength, |
| dwritetextlayout_layout_GetFontFamilyName, |
| dwritetextlayout_layout_GetFontWeight, |
| dwritetextlayout_layout_GetFontStyle, |
| dwritetextlayout_layout_GetFontStretch, |
| dwritetextlayout_layout_GetFontSize, |
| dwritetextlayout_GetUnderline, |
| dwritetextlayout_GetStrikethrough, |
| dwritetextlayout_GetDrawingEffect, |
| dwritetextlayout_GetInlineObject, |
| dwritetextlayout_GetTypography, |
| dwritetextlayout_layout_GetLocaleNameLength, |
| dwritetextlayout_layout_GetLocaleName, |
| dwritetextlayout_Draw, |
| dwritetextlayout_GetLineMetrics, |
| dwritetextlayout_GetMetrics, |
| dwritetextlayout_GetOverhangMetrics, |
| dwritetextlayout_GetClusterMetrics, |
| dwritetextlayout_DetermineMinWidth, |
| dwritetextlayout_HitTestPoint, |
| dwritetextlayout_HitTestTextPosition, |
| dwritetextlayout_HitTestTextRange, |
| dwritetextlayout1_SetPairKerning, |
| dwritetextlayout1_GetPairKerning, |
| dwritetextlayout1_SetCharacterSpacing, |
| dwritetextlayout1_GetCharacterSpacing, |
| dwritetextlayout2_GetMetrics, |
| dwritetextlayout2_SetVerticalGlyphOrientation, |
| dwritetextlayout2_GetVerticalGlyphOrientation, |
| dwritetextlayout2_SetLastLineWrapping, |
| dwritetextlayout2_GetLastLineWrapping, |
| dwritetextlayout2_SetOpticalAlignment, |
| dwritetextlayout2_GetOpticalAlignment, |
| dwritetextlayout2_SetFontFallback, |
| dwritetextlayout2_GetFontFallback, |
| dwritetextlayout3_InvalidateLayout, |
| dwritetextlayout3_SetLineSpacing, |
| dwritetextlayout3_GetLineSpacing, |
| dwritetextlayout3_GetLineMetrics |
| }; |
| |
| static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); |
| return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj); |
| } |
| |
| static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface); |
| } |
| |
| static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%d)\n", This, alignment); |
| |
| hr = format_set_textalignment(&This->format, alignment, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) { |
| /* if layout is not ready there's nothing to align */ |
| if (!(This->recompute & RECOMPUTE_LINES)) |
| layout_apply_text_alignment(This); |
| This->recompute |= RECOMPUTE_OVERHANGS; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%d)\n", This, alignment); |
| |
| hr = format_set_paralignment(&This->format, alignment, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) { |
| /* if layout is not ready there's nothing to align */ |
| if (!(This->recompute & RECOMPUTE_LINES)) |
| layout_apply_par_alignment(This); |
| This->recompute |= RECOMPUTE_OVERHANGS; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%d)\n", This, wrapping); |
| |
| hr = format_set_wordwrapping(&This->format, wrapping, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) |
| This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%d)\n", This, direction); |
| |
| hr = format_set_readingdirection(&This->format, direction, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) |
| This->recompute = RECOMPUTE_EVERYTHING; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%d)\n", This, direction); |
| |
| hr = format_set_flowdirection(&This->format, direction, &changed); |
| if (FAILED(hr)) |
| return hr; |
| |
| if (changed) |
| This->recompute = RECOMPUTE_EVERYTHING; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| FIXME("(%p)->(%f): stub\n", This, tabstop); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming, |
| IDWriteInlineObject *trimming_sign) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| BOOL changed; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign); |
| |
| hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed); |
| |
| if (changed) |
| This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method, |
| FLOAT height, FLOAT baseline) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| DWRITE_LINE_SPACING spacing; |
| |
| TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline); |
| |
| spacing = This->format.spacing; |
| spacing.method = method; |
| spacing.height = height; |
| spacing.baseline = baseline; |
| return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing); |
| } |
| |
| static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.textalignment; |
| } |
| |
| static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.paralign; |
| } |
| |
| static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.wrapping; |
| } |
| |
| static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.readingdir; |
| } |
| |
| static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.flow; |
| } |
| |
| static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| FIXME("(%p): stub\n", This); |
| return 0.0f; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options, |
| IDWriteInlineObject **trimming_sign) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%p %p)\n", This, options, trimming_sign); |
| |
| *options = This->format.trimming; |
| *trimming_sign = This->format.trimmingsign; |
| if (*trimming_sign) |
| IDWriteInlineObject_AddRef(*trimming_sign); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method, |
| FLOAT *spacing, FLOAT *baseline) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline); |
| |
| *method = This->format.spacing.method; |
| *spacing = This->format.spacing.height; |
| *baseline = This->format.spacing.baseline; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%p)\n", This, collection); |
| |
| *collection = This->format.collection; |
| if (*collection) |
| IDWriteFontCollection_AddRef(*collection); |
| return S_OK; |
| } |
| |
| static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.family_len; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%p %u)\n", This, name, size); |
| |
| if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER; |
| strcpyW(name, This->format.family_name); |
| return S_OK; |
| } |
| |
| static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.weight; |
| } |
| |
| static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.style; |
| } |
| |
| static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.stretch; |
| } |
| |
| static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.fontsize; |
| } |
| |
| static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.locale_len; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%p %u)\n", This, name, size); |
| |
| if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER; |
| strcpyW(name, This->format.locale); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| FIXME("(%p)->(%d): stub\n", This, orientation); |
| return E_NOTIMPL; |
| } |
| |
| static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| FIXME("(%p): stub\n", This); |
| return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| |
| TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled); |
| |
| This->format.last_line_wrapping = !!lastline_wrapping_enabled; |
| return S_OK; |
| } |
| |
| static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.last_line_wrapping; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)->(%d)\n", This, alignment); |
| return format_set_optical_alignment(&This->format, alignment); |
| } |
| |
| static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)\n", This); |
| return This->format.optical_alignment; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback); |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback) |
| { |
| struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback); |
| } |
| |
| static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = { |
| dwritetextformat_layout_QueryInterface, |
| dwritetextformat_layout_AddRef, |
| dwritetextformat_layout_Release, |
| dwritetextformat_layout_SetTextAlignment, |
| dwritetextformat_layout_SetParagraphAlignment, |
| dwritetextformat_layout_SetWordWrapping, |
| dwritetextformat_layout_SetReadingDirection, |
| dwritetextformat_layout_SetFlowDirection, |
| dwritetextformat_layout_SetIncrementalTabStop, |
| dwritetextformat_layout_SetTrimming, |
| dwritetextformat_layout_SetLineSpacing, |
| dwritetextformat_layout_GetTextAlignment, |
| dwritetextformat_layout_GetParagraphAlignment, |
| dwritetextformat_layout_GetWordWrapping, |
| dwritetextformat_layout_GetReadingDirection, |
| dwritetextformat_layout_GetFlowDirection, |
| dwritetextformat_layout_GetIncrementalTabStop, |
| dwritetextformat_layout_GetTrimming, |
| dwritetextformat_layout_GetLineSpacing, |
| dwritetextformat_layout_GetFontCollection, |
| dwritetextformat_layout_GetFontFamilyNameLength, |
| dwritetextformat_layout_GetFontFamilyName, |
| dwritetextformat_layout_GetFontWeight, |
| dwritetextformat_layout_GetFontStyle, |
| dwritetextformat_layout_GetFontStretch, |
| dwritetextformat_layout_GetFontSize, |
| dwritetextformat_layout_GetLocaleNameLength, |
| dwritetextformat_layout_GetLocaleName, |
| dwritetextformat1_layout_SetVerticalGlyphOrientation, |
| dwritetextformat1_layout_GetVerticalGlyphOrientation, |
| dwritetextformat1_layout_SetLastLineWrapping, |
| dwritetextformat1_layout_GetLastLineWrapping, |
| dwritetextformat1_layout_SetOpticalAlignment, |
| dwritetextformat1_layout_GetOpticalAlignment, |
| dwritetextformat1_layout_SetFontFallback, |
| dwritetextformat1_layout_GetFontFallback, |
| }; |
| |
| static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface, |
| REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) || |
| IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || |
| IsEqualIID(riid, &IID_IUnknown)) |
| { |
| *obj = iface; |
| IDWriteTextAnalysisSink1_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); |
| return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface); |
| } |
| |
| static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); |
| return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface, |
| UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); |
| struct layout_run *run; |
| |
| TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script)); |
| |
| run = alloc_layout_run(LAYOUT_RUN_REGULAR, position); |
| if (!run) |
| return E_OUTOFMEMORY; |
| |
| run->u.regular.descr.string = &layout->str[position]; |
| run->u.regular.descr.stringLength = length; |
| run->u.regular.descr.textPosition = position; |
| run->u.regular.sa = *sa; |
| list_add_tail(&layout->runs, &run->entry); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface, |
| UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); |
| |
| if (position + length > layout->len) |
| return E_FAIL; |
| |
| memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT)); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position, |
| UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface); |
| struct layout_run *cur_run; |
| |
| TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel); |
| |
| LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) { |
| struct regular_layout_run *cur = &cur_run->u.regular; |
| struct layout_run *run; |
| |
| if (cur_run->kind == LAYOUT_RUN_INLINE) |
| continue; |
| |
| /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */ |
| if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength) |
| continue; |
| |
| /* full hit - just set run level */ |
| if (cur->descr.textPosition == position && cur->descr.stringLength == length) { |
| cur->run.bidiLevel = resolvedLevel; |
| break; |
| } |
| |
| /* current run is fully covered, move to next one */ |
| if (cur->descr.textPosition == position && cur->descr.stringLength < length) { |
| cur->run.bidiLevel = resolvedLevel; |
| position += cur->descr.stringLength; |
| length -= cur->descr.stringLength; |
| continue; |
| } |
| |
| /* all fully covered runs are processed at this point, reuse existing run for remaining |
| reported bidi range and add another run for the rest of original one */ |
| |
| run = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length); |
| if (!run) |
| return E_OUTOFMEMORY; |
| |
| *run = *cur_run; |
| run->u.regular.descr.textPosition = position + length; |
| run->u.regular.descr.stringLength = cur->descr.stringLength - length; |
| run->u.regular.descr.string = &layout->str[position + length]; |
| |
| /* reduce existing run */ |
| cur->run.bidiLevel = resolvedLevel; |
| cur->descr.stringLength = length; |
| |
| list_add_after(&cur_run->entry, &run->entry); |
| break; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface, |
| UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface, |
| UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level, |
| BOOL is_sideways, BOOL is_rtl) |
| { |
| return E_NOTIMPL; |
| } |
| |
| static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = { |
| dwritetextlayout_sink_QueryInterface, |
| dwritetextlayout_sink_AddRef, |
| dwritetextlayout_sink_Release, |
| dwritetextlayout_sink_SetScriptAnalysis, |
| dwritetextlayout_sink_SetLineBreakpoints, |
| dwritetextlayout_sink_SetBidiLevel, |
| dwritetextlayout_sink_SetNumberSubstitution, |
| dwritetextlayout_sink_SetGlyphOrientation |
| }; |
| |
| static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface, |
| REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) || |
| IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || |
| IsEqualIID(riid, &IID_IUnknown)) |
| { |
| *obj = iface; |
| IDWriteTextAnalysisSource1_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface); |
| return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface); |
| } |
| |
| static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface); |
| return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface, |
| UINT32 position, WCHAR const** text, UINT32* text_len) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(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(IDWriteTextAnalysisSource1 *iface, |
| UINT32 position, WCHAR const** text, UINT32* text_len) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface); |
| |
| TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len); |
| |
| if (position > 0 && position < layout->len) { |
| *text = layout->str; |
| *text_len = position; |
| } |
| else { |
| *text = NULL; |
| *text_len = 0; |
| } |
| |
| return S_OK; |
| } |
| |
| static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface); |
| return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface); |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface, |
| UINT32 position, UINT32* text_len, WCHAR const** locale) |
| { |
| struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface); |
| struct layout_range *range = get_layout_range_by_pos(layout, position); |
| |
| if (position < layout->len) { |
| struct layout_range *next; |
| |
| *locale = range->locale; |
| *text_len = range->h.range.length - position; |
| |
| next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry); |
| while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) { |
| *text_len += next->h.range.length; |
| next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry); |
| } |
| |
| *text_len = min(*text_len, layout->len - position); |
| } |
| else { |
| *locale = NULL; |
| *text_len = 0; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface, |
| UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution) |
| { |
| FIXME("%u %p %p: stub\n", position, text_len, substitution); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface, |
| UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level) |
| { |
| FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level); |
| return E_NOTIMPL; |
| } |
| |
| static const IDWriteTextAnalysisSource1Vtbl 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, |
| dwritetextlayout_source_GetVerticalGlyphOrientation |
| }; |
| |
| static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format) |
| { |
| struct dwrite_textformat *textformat; |
| IDWriteTextFormat1 *format1; |
| UINT32 len; |
| HRESULT hr; |
| |
| if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) { |
| layout->format = textformat->format; |
| |
| layout->format.locale = heap_strdupW(textformat->format.locale); |
| layout->format.family_name = heap_strdupW(textformat->format.family_name); |
| if (!layout->format.locale || !layout->format.family_name) |
| { |
| heap_free(layout->format.locale); |
| heap_free(layout->format.family_name); |
| return E_OUTOFMEMORY; |
| } |
| |
| if (layout->format.trimmingsign) |
| IDWriteInlineObject_AddRef(layout->format.trimmingsign); |
| if (layout->format.collection) |
| IDWriteFontCollection_AddRef(layout->format.collection); |
| if (layout->format.fallback) |
| IDWriteFontFallback_AddRef(layout->format.fallback); |
| |
| return S_OK; |
| } |
| |
| layout->format.weight = IDWriteTextFormat_GetFontWeight(format); |
| layout->format.style = IDWriteTextFormat_GetFontStyle(format); |
| layout->format.stretch = IDWriteTextFormat_GetFontStretch(format); |
| layout->format.fontsize= IDWriteTextFormat_GetFontSize(format); |
| layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format); |
| layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format); |
| layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format); |
| layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format); |
| layout->format.flow = IDWriteTextFormat_GetFlowDirection(format); |
| layout->format.fallback = NULL; |
| layout->format.spacing.leadingBefore = 0.0f; |
| layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT; |
| hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method, |
| &layout->format.spacing.height, &layout->format.spacing.baseline); |
| if (FAILED(hr)) |
| return hr; |
| |
| hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign); |
| if (FAILED(hr)) |
| return hr; |
| |
| /* locale name and length */ |
| len = IDWriteTextFormat_GetLocaleNameLength(format); |
| layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR)); |
| if (!layout->format.locale) |
| return E_OUTOFMEMORY; |
| |
| hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1); |
| if (FAILED(hr)) |
| return hr; |
| layout->format.locale_len = len; |
| |
| /* font family name and length */ |
| len = IDWriteTextFormat_GetFontFamilyNameLength(format); |
| layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR)); |
| if (!layout->format.family_name) |
| return E_OUTOFMEMORY; |
| |
| hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1); |
| if (FAILED(hr)) |
| return hr; |
| layout->format.family_len = len; |
| |
| hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1); |
| if (hr == S_OK) { |
| IDWriteTextFormat2 *format2; |
| |
| layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1); |
| layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1); |
| IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback); |
| |
| if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) { |
| IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing); |
| IDWriteTextFormat2_Release(format2); |
| } |
| |
| IDWriteTextFormat1_Release(format1); |
| } |
| else { |
| layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT; |
| layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE; |
| } |
| |
| return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection); |
| } |
| |
| static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout) |
| { |
| struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography; |
| static const DWRITE_TEXT_RANGE r = { 0, ~0u }; |
| HRESULT hr; |
| |
| layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl; |
| layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl; |
| layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl; |
| layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl; |
| layout->ref = 1; |
| layout->len = desc->length; |
| layout->recompute = RECOMPUTE_EVERYTHING; |
| layout->nominal_breakpoints = NULL; |
| layout->actual_breakpoints = NULL; |
| layout->cluster_count = 0; |
| layout->clustermetrics = NULL; |
| layout->clusters = NULL; |
| layout->linemetrics = NULL; |
| layout->lines = NULL; |
| layout->line_alloc = 0; |
| layout->minwidth = 0.0f; |
| list_init(&layout->eruns); |
| list_init(&layout->inlineobjects); |
| list_init(&layout->underlines); |
| list_init(&layout->strikethrough); |
| list_init(&layout->runs); |
| list_init(&layout->ranges); |
| list_init(&layout->strike_ranges); |
| list_init(&layout->underline_ranges); |
| list_init(&layout->effects); |
| list_init(&layout->spacing); |
| list_init(&layout->typographies); |
| memset(&layout->format, 0, sizeof(layout->format)); |
| memset(&layout->metrics, 0, sizeof(layout->metrics)); |
| layout->metrics.layoutWidth = desc->max_width; |
| layout->metrics.layoutHeight = desc->max_height; |
| layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL; |
| |
| layout->ppdip = 0.0f; |
| memset(&layout->transform, 0, sizeof(layout->transform)); |
| |
| layout->str = heap_strdupnW(desc->string, desc->length); |
| if (desc->length && !layout->str) { |
| hr = E_OUTOFMEMORY; |
| goto fail; |
| } |
| |
| hr = layout_format_from_textformat(layout, desc->format); |
| if (FAILED(hr)) |
| goto fail; |
| |
| range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR); |
| strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH); |
| underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE); |
| effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT); |
| spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING); |
| typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY); |
| if (!range || !strike || !effect || !spacing || !typography || !underline) { |
| free_layout_range(range); |
| free_layout_range(strike); |
| free_layout_range(underline); |
| free_layout_range(effect); |
| free_layout_range(spacing); |
| free_layout_range(typography); |
| hr = E_OUTOFMEMORY; |
| goto fail; |
| } |
| |
| if (desc->is_gdi_compatible) |
| layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; |
| else |
| layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL; |
| layout->ppdip = desc->ppdip; |
| layout->transform = desc->transform ? *desc->transform : identity; |
| |
| layout->factory = desc->factory; |
| IDWriteFactory5_AddRef(layout->factory); |
| list_add_head(&layout->ranges, &range->entry); |
| list_add_head(&layout->strike_ranges, &strike->entry); |
| list_add_head(&layout->underline_ranges, &underline->entry); |
| list_add_head(&layout->effects, &effect->entry); |
| list_add_head(&layout->spacing, &spacing->entry); |
| list_add_head(&layout->typographies, &typography->entry); |
| return S_OK; |
| |
| fail: |
| IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface); |
| return hr; |
| } |
| |
| HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret) |
| { |
| struct dwrite_textlayout *layout; |
| HRESULT hr; |
| |
| *ret = NULL; |
| |
| if (!desc->format || !desc->string) |
| return E_INVALIDARG; |
| |
| layout = heap_alloc(sizeof(struct dwrite_textlayout)); |
| if (!layout) return E_OUTOFMEMORY; |
| |
| hr = init_textlayout(desc, layout); |
| if (hr == S_OK) |
| *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); |
| |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) { |
| *obj = iface; |
| IDWriteInlineObject_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| TRACE("(%p)->(%d)\n", This, ref); |
| return ref; |
| } |
| |
| static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p)->(%d)\n", This, ref); |
| |
| if (!ref) { |
| IDWriteTextLayout_Release(This->layout); |
| heap_free(This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer, |
| FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| DWRITE_LINE_METRICS line; |
| UINT32 line_count; |
| |
| TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY, |
| is_sideways, is_rtl, effect); |
| |
| IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count); |
| return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline); |
| } |
| |
| static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| DWRITE_TEXT_METRICS metrics; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, ret); |
| |
| hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics); |
| if (FAILED(hr)) { |
| memset(ret, 0, sizeof(*ret)); |
| return hr; |
| } |
| |
| ret->width = metrics.width; |
| ret->height = 0.0f; |
| ret->baseline = 0.0f; |
| ret->supportsSideways = FALSE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| TRACE("(%p)->(%p)\n", This, overhangs); |
| return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs); |
| } |
| |
| static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before, |
| DWRITE_BREAK_CONDITION *after) |
| { |
| struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface); |
| |
| TRACE("(%p)->(%p %p)\n", This, before, after); |
| |
| *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL; |
| return S_OK; |
| } |
| |
| static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = { |
| dwritetrimmingsign_QueryInterface, |
| dwritetrimmingsign_AddRef, |
| dwritetrimmingsign_Release, |
| dwritetrimmingsign_Draw, |
| dwritetrimmingsign_GetMetrics, |
| dwritetrimmingsign_GetOverhangMetrics, |
| dwritetrimmingsign_GetBreakConditions |
| }; |
| |
| static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction) |
| { |
| return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) || |
| (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); |
| } |
| |
| static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction) |
| { |
| return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) || |
| (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP); |
| } |
| |
| static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction) |
| { |
| return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) || |
| (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT); |
| } |
| |
| static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction) |
| { |
| return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) || |
| (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP); |
| } |
| |
| HRESULT create_trimmingsign(IDWriteFactory5 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign) |
| { |
| static const WCHAR ellipsisW = 0x2026; |
| struct dwrite_trimmingsign *This; |
| DWRITE_READING_DIRECTION reading; |
| DWRITE_FLOW_DIRECTION flow; |
| HRESULT hr; |
| |
| *sign = NULL; |
| |
| /* Validate reading/flow direction here, layout creation won't complain about |
| invalid combinations. */ |
| reading = IDWriteTextFormat_GetReadingDirection(format); |
| flow = IDWriteTextFormat_GetFlowDirection(format); |
| |
| if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) || |
| (is_reading_direction_vert(reading) && is_flow_direction_vert(flow))) |
| return DWRITE_E_FLOWDIRECTIONCONFLICTS; |
| |
| This = heap_alloc(sizeof(*This)); |
| if (!This) |
| return E_OUTOFMEMORY; |
| |
| This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl; |
| This->ref = 1; |
| |
| hr = IDWriteFactory5_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout); |
| if (FAILED(hr)) { |
| heap_free(This); |
| return hr; |
| } |
| |
| IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP); |
| IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR); |
| IDWriteTextLayout_SetTextAlignment(This->layout, DWRITE_TEXT_ALIGNMENT_LEADING); |
| |
| *sign = &This->IDWriteInlineObject_iface; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); |
| |
| if (IsEqualIID(riid, &IID_IDWriteTextFormat2) || |
| IsEqualIID(riid, &IID_IDWriteTextFormat1) || |
| IsEqualIID(riid, &IID_IDWriteTextFormat) || |
| IsEqualIID(riid, &IID_IUnknown)) |
| { |
| *obj = iface; |
| IDWriteTextFormat2_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| *obj = NULL; |
| |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| TRACE("(%p)->(%d)\n", This, ref); |
| return ref; |
| } |
| |
| static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| ULONG ref = InterlockedDecrement(&This->ref); |
| |
| TRACE("(%p)->(%d)\n", This, ref); |
| |
| if (!ref) |
| { |
| release_format_data(&This->format); |
| heap_free(This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, alignment); |
| return format_set_textalignment(&This->format, alignment, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, alignment); |
| return format_set_paralignment(&This->format, alignment, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, wrapping); |
| return format_set_wordwrapping(&This->format, wrapping, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, direction); |
| return format_set_readingdirection(&This->format, direction, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, direction); |
| return format_set_flowdirection(&This->format, direction, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| FIXME("(%p)->(%f): stub\n", This, tabstop); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming, |
| IDWriteInlineObject *trimming_sign) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign); |
| return format_set_trimming(&This->format, trimming, trimming_sign, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method, |
| FLOAT height, FLOAT baseline) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| DWRITE_LINE_SPACING spacing; |
| |
| TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline); |
| |
| spacing = This->format.spacing; |
| spacing.method = method; |
| spacing.height = height; |
| spacing.baseline = baseline; |
| |
| return format_set_linespacing(&This->format, &spacing, NULL); |
| } |
| |
| static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.textalignment; |
| } |
| |
| static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.paralign; |
| } |
| |
| static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.wrapping; |
| } |
| |
| static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.readingdir; |
| } |
| |
| static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.flow; |
| } |
| |
| static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| FIXME("(%p): stub\n", This); |
| return 0.0f; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options, |
| IDWriteInlineObject **trimming_sign) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p %p)\n", This, options, trimming_sign); |
| |
| *options = This->format.trimming; |
| if ((*trimming_sign = This->format.trimmingsign)) |
| IDWriteInlineObject_AddRef(*trimming_sign); |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method, |
| FLOAT *spacing, FLOAT *baseline) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline); |
| |
| *method = This->format.spacing.method; |
| *spacing = This->format.spacing.height; |
| *baseline = This->format.spacing.baseline; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%p)\n", This, collection); |
| |
| *collection = This->format.collection; |
| IDWriteFontCollection_AddRef(*collection); |
| |
| return S_OK; |
| } |
| |
| static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.family_len; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%p %u)\n", This, name, size); |
| |
| if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER; |
| strcpyW(name, This->format.family_name); |
| return S_OK; |
| } |
| |
| static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.weight; |
| } |
| |
| static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.style; |
| } |
| |
| static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.stretch; |
| } |
| |
| static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.fontsize; |
| } |
| |
| static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.locale_len; |
| } |
| |
| static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%p %u)\n", This, name, size); |
| |
| if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER; |
| strcpyW(name, This->format.locale); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%d)\n", This, orientation); |
| |
| if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED) |
| return E_INVALIDARG; |
| |
| This->format.vertical_orientation = orientation; |
| return S_OK; |
| } |
| |
| static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.vertical_orientation; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled); |
| |
| This->format.last_line_wrapping = !!lastline_wrapping_enabled; |
| return S_OK; |
| } |
| |
| static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.last_line_wrapping; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%d)\n", This, alignment); |
| return format_set_optical_alignment(&This->format, alignment); |
| } |
| |
| static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)\n", This); |
| return This->format.optical_alignment; |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return set_fontfallback_for_format(&This->format, fallback); |
| } |
| |
| static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p)\n", This, fallback); |
| return get_fontfallback_from_format(&This->format, fallback); |
| } |
| |
| static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| TRACE("(%p)->(%p)\n", This, spacing); |
| return format_set_linespacing(&This->format, spacing, NULL); |
| } |
| |
| static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing) |
| { |
| struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface); |
| |
| TRACE("(%p)->(%p)\n", This, spacing); |
| |
| *spacing = This->format.spacing; |
| return S_OK; |
| } |
| |
| static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = { |
| dwritetextformat_QueryInterface, |
| dwritetextformat_AddRef, |
| dwritetextformat_Release, |
| dwritetextformat_SetTextAlignment, |
| dwritetextformat_SetParagraphAlignment, |
| dwritetextformat_SetWordWrapping, |
| dwritetextformat_SetReadingDirection, |
| dwritetextformat_SetFlowDirection, |
| dwritetextformat_SetIncrementalTabStop, |
| dwritetextformat_SetTrimming, |
| dwritetextformat_SetLineSpacing, |
| dwritetextformat_GetTextAlignment, |
| dwritetextformat_GetParagraphAlignment, |
| dwritetextformat_GetWordWrapping, |
| dwritetextformat_GetReadingDirection, |
| dwritetextformat_GetFlowDirection, |
| dwritetextformat_GetIncrementalTabStop, |
| dwritetextformat_GetTrimming, |
| dwritetextformat_GetLineSpacing, |
| dwritetextformat_GetFontCollection, |
| dwritetextformat_GetFontFamilyNameLength, |
| dwritetextformat_GetFontFamilyName, |
| dwritetextformat_GetFontWeight, |
| dwritetextformat_GetFontStyle, |
| dwritetextformat_GetFontStretch, |
| dwritetextformat_GetFontSize, |
| dwritetextformat_GetLocaleNameLength, |
| dwritetextformat_GetLocaleName, |
| dwritetextformat1_SetVerticalGlyphOrientation, |
| dwritetextformat1_GetVerticalGlyphOrientation, |
| dwritetextformat1_SetLastLineWrapping, |
| dwritetextformat1_GetLastLineWrapping, |
| dwritetextformat1_SetOpticalAlignment, |
| dwritetextformat1_GetOpticalAlignment, |
| dwritetextformat1_SetFontFallback, |
| dwritetextformat1_GetFontFallback, |
| dwritetextformat2_SetLineSpacing, |
| dwritetextformat2_GetLineSpacing |
| }; |
| |
| static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface) |
| { |
| return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ? |
| CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL; |
| } |
| |
| HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, |
| DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format) |
| { |
| struct dwrite_textformat *This; |
| |
| *format = NULL; |
| |
| if (size <= 0.0f) |
| return E_INVALIDARG; |
| |
| if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) || |
| ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) || |
| ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)) |
| return E_INVALIDARG; |
| |
| This = heap_alloc(sizeof(struct dwrite_textformat)); |
| if (!This) return E_OUTOFMEMORY; |
| |
| This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl; |
| This->ref = 1; |
| This->format.family_name = heap_strdupW(family_name); |
| This->format.family_len = strlenW(family_name); |
| This->format.locale = heap_strdupW(locale); |
| This->format.locale_len = strlenW(locale); |
| /* force locale name to lower case, layout will inherit this modified value */ |
| strlwrW(This->format.locale); |
| This->format.weight = weight; |
| This->format.style = style; |
| This->format.fontsize = size; |
| This->format.stretch = stretch; |
| This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING; |
| This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE; |
| This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR; |
| This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP; |
| This->format.last_line_wrapping = TRUE; |
| This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT; |
| This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM; |
| This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT; |
| This->format.spacing.height = 0.0f; |
| This->format.spacing.baseline = 0.0f; |
| This->format.spacing.leadingBefore = 0.0f; |
| This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT; |
| This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT; |
| This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE; |
| This->format.trimming.delimiter = 0; |
| This->format.trimming.delimiterCount = 0; |
| This->format.trimmingsign = NULL; |
| This->format.collection = collection; |
| This->format.fallback = NULL; |
| IDWriteFontCollection_AddRef(collection); |
| |
| *format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface; |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| |
| TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj); |
| |
| if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) { |
| *obj = iface; |
| IDWriteTypography_AddRef(iface); |
| return S_OK; |
| } |
| |
| WARN("%s not implemented.\n", debugstr_guid(riid)); |
| |
| *obj = NULL; |
| |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| ULONG ref = InterlockedIncrement(&typography->ref); |
| TRACE("(%p)->(%d)\n", typography, ref); |
| return ref; |
| } |
| |
| static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| ULONG ref = InterlockedDecrement(&typography->ref); |
| |
| TRACE("(%p)->(%d)\n", typography, ref); |
| |
| if (!ref) { |
| heap_free(typography->features); |
| heap_free(typography); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| |
| TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter); |
| |
| if (typography->count == typography->allocated) { |
| DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE)); |
| if (!ptr) |
| return E_OUTOFMEMORY; |
| |
| typography->features = ptr; |
| typography->allocated *= 2; |
| } |
| |
| typography->features[typography->count++] = feature; |
| return S_OK; |
| } |
| |
| static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| TRACE("(%p)\n", typography); |
| return typography->count; |
| } |
| |
| static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature) |
| { |
| struct dwrite_typography *typography = impl_from_IDWriteTypography(iface); |
| |
| TRACE("(%p)->(%u %p)\n", typography, index, feature); |
| |
| if (index >= typography->count) |
| return E_INVALIDARG; |
| |
| *feature = typography->features[index]; |
| return S_OK; |
| } |
| |
| static const IDWriteTypographyVtbl dwritetypographyvtbl = { |
| dwritetypography_QueryInterface, |
| dwritetypography_AddRef, |
| dwritetypography_Release, |
| dwritetypography_AddFontFeature, |
| dwritetypography_GetFontFeatureCount, |
| dwritetypography_GetFontFeature |
| }; |
| |
| HRESULT create_typography(IDWriteTypography **ret) |
| { |
| struct dwrite_typography *typography; |
| |
| *ret = NULL; |
| |
| typography = heap_alloc(sizeof(*typography)); |
| if (!typography) |
| return E_OUTOFMEMORY; |
| |
| typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl; |
| typography->ref = 1; |
| typography->allocated = 2; |
| typography->count = 0; |
| |
| typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE)); |
| if (!typography->features) { |
| heap_free(typography); |
| return E_OUTOFMEMORY; |
| } |
| |
| *ret = &typography->IDWriteTypography_iface; |
| return S_OK; |
| } |