dwrite: Don't use drawing effect for trimming signs.
[wine.git] / dlls / dwrite / layout.c
blobff09c78e138c4660cd7ed062f85c97014d003434
1 /*
2 * Text format and layout
4 * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <math.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "dwrite_private.h"
31 #include "scripts.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
35 struct dwrite_textformat_data {
36 WCHAR *family_name;
37 UINT32 family_len;
38 WCHAR *locale;
39 UINT32 locale_len;
41 DWRITE_FONT_WEIGHT weight;
42 DWRITE_FONT_STYLE style;
43 DWRITE_FONT_STRETCH stretch;
45 DWRITE_PARAGRAPH_ALIGNMENT paralign;
46 DWRITE_READING_DIRECTION readingdir;
47 DWRITE_WORD_WRAPPING wrapping;
48 BOOL last_line_wrapping;
49 DWRITE_TEXT_ALIGNMENT textalignment;
50 DWRITE_FLOW_DIRECTION flow;
51 DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
52 DWRITE_OPTICAL_ALIGNMENT optical_alignment;
53 DWRITE_LINE_SPACING spacing;
55 FLOAT fontsize;
57 DWRITE_TRIMMING trimming;
58 IDWriteInlineObject *trimmingsign;
60 IDWriteFontCollection *collection;
61 IDWriteFontFallback *fallback;
64 enum layout_range_attr_kind {
65 LAYOUT_RANGE_ATTR_WEIGHT,
66 LAYOUT_RANGE_ATTR_STYLE,
67 LAYOUT_RANGE_ATTR_STRETCH,
68 LAYOUT_RANGE_ATTR_FONTSIZE,
69 LAYOUT_RANGE_ATTR_EFFECT,
70 LAYOUT_RANGE_ATTR_INLINE,
71 LAYOUT_RANGE_ATTR_UNDERLINE,
72 LAYOUT_RANGE_ATTR_STRIKETHROUGH,
73 LAYOUT_RANGE_ATTR_PAIR_KERNING,
74 LAYOUT_RANGE_ATTR_FONTCOLL,
75 LAYOUT_RANGE_ATTR_LOCALE,
76 LAYOUT_RANGE_ATTR_FONTFAMILY,
77 LAYOUT_RANGE_ATTR_SPACING,
78 LAYOUT_RANGE_ATTR_TYPOGRAPHY
81 struct layout_range_attr_value {
82 DWRITE_TEXT_RANGE range;
83 union {
84 DWRITE_FONT_WEIGHT weight;
85 DWRITE_FONT_STYLE style;
86 DWRITE_FONT_STRETCH stretch;
87 FLOAT fontsize;
88 IDWriteInlineObject *object;
89 IUnknown *effect;
90 BOOL underline;
91 BOOL strikethrough;
92 BOOL pair_kerning;
93 IDWriteFontCollection *collection;
94 const WCHAR *locale;
95 const WCHAR *fontfamily;
96 struct {
97 FLOAT leading;
98 FLOAT trailing;
99 FLOAT min_advance;
100 } spacing;
101 IDWriteTypography *typography;
102 } u;
105 enum layout_range_kind {
106 LAYOUT_RANGE_REGULAR,
107 LAYOUT_RANGE_UNDERLINE,
108 LAYOUT_RANGE_STRIKETHROUGH,
109 LAYOUT_RANGE_EFFECT,
110 LAYOUT_RANGE_SPACING,
111 LAYOUT_RANGE_TYPOGRAPHY
114 struct layout_range_header {
115 struct list entry;
116 enum layout_range_kind kind;
117 DWRITE_TEXT_RANGE range;
120 struct layout_range {
121 struct layout_range_header h;
122 DWRITE_FONT_WEIGHT weight;
123 DWRITE_FONT_STYLE style;
124 FLOAT fontsize;
125 DWRITE_FONT_STRETCH stretch;
126 IDWriteInlineObject *object;
127 BOOL pair_kerning;
128 IDWriteFontCollection *collection;
129 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
130 WCHAR *fontfamily;
133 struct layout_range_bool {
134 struct layout_range_header h;
135 BOOL value;
138 struct layout_range_iface {
139 struct layout_range_header h;
140 IUnknown *iface;
143 struct layout_range_spacing {
144 struct layout_range_header h;
145 FLOAT leading;
146 FLOAT trailing;
147 FLOAT min_advance;
150 enum layout_run_kind {
151 LAYOUT_RUN_REGULAR,
152 LAYOUT_RUN_INLINE
155 struct inline_object_run {
156 IDWriteInlineObject *object;
157 UINT16 length;
160 struct regular_layout_run {
161 DWRITE_GLYPH_RUN_DESCRIPTION descr;
162 DWRITE_GLYPH_RUN run;
163 DWRITE_SCRIPT_ANALYSIS sa;
164 UINT16 *glyphs;
165 UINT16 *clustermap;
166 FLOAT *advances;
167 DWRITE_GLYPH_OFFSET *offsets;
168 UINT32 glyphcount; /* actual glyph count after shaping, not necessarily the same as reported to Draw() */
171 struct layout_run {
172 struct list entry;
173 enum layout_run_kind kind;
174 union {
175 struct inline_object_run object;
176 struct regular_layout_run regular;
177 } u;
178 FLOAT baseline;
179 FLOAT height;
180 UINT32 start_position; /* run text position in range [0, layout-text-length) */
183 struct layout_effective_run {
184 struct list entry;
185 const struct layout_run *run; /* nominal run this one is based on */
186 UINT32 start; /* relative text position, 0 means first text position of a nominal run */
187 UINT32 length; /* length in codepoints that this run covers */
188 UINT32 glyphcount; /* total glyph count in this run */
189 IUnknown *effect; /* original reference is kept only at range level */
190 D2D1_POINT_2F origin; /* baseline origin */
191 FLOAT align_dx; /* adjustment from text alignment */
192 FLOAT width; /* run width */
193 UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
194 UINT32 line; /* 0-based line index in line metrics array */
195 BOOL underlined; /* set if this run is underlined */
196 D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */
199 struct layout_effective_inline {
200 struct list entry;
201 IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */
202 IUnknown *effect; /* original reference is kept only at range level */
203 FLOAT baseline;
204 D2D1_POINT_2F origin; /* left top corner */
205 FLOAT align_dx; /* adjustment from text alignment */
206 FLOAT width; /* object width as it's reported it */
207 BOOL is_sideways; /* vertical flow direction flag passed to Draw */
208 BOOL is_rtl; /* bidi flag passed to Draw */
209 UINT32 line; /* 0-based line index in line metrics array */
212 struct layout_underline {
213 struct list entry;
214 const struct layout_effective_run *run;
215 DWRITE_UNDERLINE u;
218 struct layout_strikethrough {
219 struct list entry;
220 const struct layout_effective_run *run;
221 DWRITE_STRIKETHROUGH s;
224 struct layout_cluster {
225 const struct layout_run *run; /* link to nominal run this cluster belongs to */
226 UINT32 position; /* relative to run, first cluster has 0 position */
229 struct layout_line {
230 FLOAT height; /* height based on content */
231 FLOAT baseline; /* baseline based on content */
234 enum layout_recompute_mask {
235 RECOMPUTE_CLUSTERS = 1 << 0,
236 RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
237 RECOMPUTE_LINES = 1 << 2,
238 RECOMPUTE_OVERHANGS = 1 << 3,
239 RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS,
240 RECOMPUTE_EVERYTHING = 0xffff
243 struct dwrite_textlayout {
244 IDWriteTextLayout3 IDWriteTextLayout3_iface;
245 IDWriteTextFormat1 IDWriteTextFormat1_iface;
246 IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface;
247 IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface;
248 LONG ref;
250 IDWriteFactory5 *factory;
252 WCHAR *str;
253 UINT32 len;
254 struct dwrite_textformat_data format;
255 struct list strike_ranges;
256 struct list underline_ranges;
257 struct list typographies;
258 struct list effects;
259 struct list spacing;
260 struct list ranges;
261 struct list runs;
262 /* lists ready to use by Draw() */
263 struct list eruns;
264 struct list inlineobjects;
265 struct list underlines;
266 struct list strikethrough;
267 USHORT recompute;
269 DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
270 DWRITE_LINE_BREAKPOINT *actual_breakpoints;
272 struct layout_cluster *clusters;
273 DWRITE_CLUSTER_METRICS *clustermetrics;
274 UINT32 cluster_count;
275 FLOAT minwidth;
277 struct layout_line *lines;
278 DWRITE_LINE_METRICS1 *linemetrics;
279 UINT32 line_alloc;
281 DWRITE_TEXT_METRICS1 metrics;
282 DWRITE_OVERHANG_METRICS overhangs;
284 DWRITE_MEASURING_MODE measuringmode;
286 /* gdi-compatible layout specifics */
287 FLOAT ppdip;
288 DWRITE_MATRIX transform;
291 struct dwrite_textformat {
292 IDWriteTextFormat2 IDWriteTextFormat2_iface;
293 LONG ref;
294 struct dwrite_textformat_data format;
297 struct dwrite_trimmingsign {
298 IDWriteInlineObject IDWriteInlineObject_iface;
299 LONG ref;
301 IDWriteTextLayout *layout;
304 struct dwrite_typography {
305 IDWriteTypography IDWriteTypography_iface;
306 LONG ref;
308 DWRITE_FONT_FEATURE *features;
309 UINT32 allocated;
310 UINT32 count;
313 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl;
315 static void release_format_data(struct dwrite_textformat_data *data)
317 if (data->collection) IDWriteFontCollection_Release(data->collection);
318 if (data->fallback) IDWriteFontFallback_Release(data->fallback);
319 if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
320 heap_free(data->family_name);
321 heap_free(data->locale);
324 static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout3(IDWriteTextLayout3 *iface)
326 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout3_iface);
329 static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
331 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface);
334 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface)
336 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface);
339 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface)
341 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface);
344 static inline struct dwrite_textformat *impl_from_IDWriteTextFormat2(IDWriteTextFormat2 *iface)
346 return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface);
349 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*);
351 static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
353 return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
356 static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
358 return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
361 static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr)
363 return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength);
366 static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
368 return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
371 static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
372 BOOL *changed)
374 if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
375 return E_INVALIDARG;
376 if (changed) *changed = format->textalignment != alignment;
377 format->textalignment = alignment;
378 return S_OK;
381 static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
382 DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
384 if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
385 return E_INVALIDARG;
386 if (changed) *changed = format->paralign != alignment;
387 format->paralign = alignment;
388 return S_OK;
391 static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
392 DWRITE_READING_DIRECTION direction, BOOL *changed)
394 if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
395 return E_INVALIDARG;
396 if (changed) *changed = format->readingdir != direction;
397 format->readingdir = direction;
398 return S_OK;
401 static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
402 DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
404 if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
405 return E_INVALIDARG;
406 if (changed) *changed = format->wrapping != wrapping;
407 format->wrapping = wrapping;
408 return S_OK;
411 static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format,
412 DWRITE_FLOW_DIRECTION direction, BOOL *changed)
414 if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
415 return E_INVALIDARG;
416 if (changed) *changed = format->flow != direction;
417 format->flow = direction;
418 return S_OK;
421 static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format,
422 DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed)
424 if (changed)
425 *changed = FALSE;
427 if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD)
428 return E_INVALIDARG;
430 if (changed) {
431 *changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming));
432 if (format->trimmingsign != trimming_sign)
433 *changed = TRUE;
436 format->trimming = *trimming;
437 if (format->trimmingsign)
438 IDWriteInlineObject_Release(format->trimmingsign);
439 format->trimmingsign = trimming_sign;
440 if (format->trimmingsign)
441 IDWriteInlineObject_AddRef(format->trimmingsign);
442 return S_OK;
445 static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format,
446 DWRITE_LINE_SPACING const *spacing, BOOL *changed)
448 if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f ||
449 (UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL)
450 return E_INVALIDARG;
452 if (changed)
453 *changed = memcmp(spacing, &format->spacing, sizeof(*spacing));
455 format->spacing = *spacing;
456 return S_OK;
459 static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
461 *fallback = format->fallback;
462 if (*fallback)
463 IDWriteFontFallback_AddRef(*fallback);
464 return S_OK;
467 static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
469 if (format->fallback)
470 IDWriteFontFallback_Release(format->fallback);
471 format->fallback = fallback;
472 if (fallback)
473 IDWriteFontFallback_AddRef(fallback);
474 return S_OK;
477 static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format,
478 DWRITE_OPTICAL_ALIGNMENT alignment)
480 if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS)
481 return E_INVALIDARG;
482 format->optical_alignment = alignment;
483 return S_OK;
486 static BOOL is_run_rtl(const struct layout_effective_run *run)
488 return run->run->u.regular.run.bidiLevel & 1;
491 static struct layout_run *alloc_layout_run(enum layout_run_kind kind, UINT32 start_position)
493 struct layout_run *ret;
495 ret = heap_alloc(sizeof(*ret));
496 if (!ret) return NULL;
498 memset(ret, 0, sizeof(*ret));
499 ret->kind = kind;
500 if (kind == LAYOUT_RUN_REGULAR) {
501 ret->u.regular.sa.script = Script_Unknown;
502 ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
504 ret->start_position = start_position;
506 return ret;
509 static void free_layout_runs(struct dwrite_textlayout *layout)
511 struct layout_run *cur, *cur2;
512 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
513 list_remove(&cur->entry);
514 if (cur->kind == LAYOUT_RUN_REGULAR) {
515 if (cur->u.regular.run.fontFace)
516 IDWriteFontFace_Release(cur->u.regular.run.fontFace);
517 heap_free(cur->u.regular.glyphs);
518 heap_free(cur->u.regular.clustermap);
519 heap_free(cur->u.regular.advances);
520 heap_free(cur->u.regular.offsets);
522 heap_free(cur);
526 static void free_layout_eruns(struct dwrite_textlayout *layout)
528 struct layout_effective_inline *in, *in2;
529 struct layout_effective_run *cur, *cur2;
530 struct layout_strikethrough *s, *s2;
531 struct layout_underline *u, *u2;
533 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
534 list_remove(&cur->entry);
535 heap_free(cur->clustermap);
536 heap_free(cur);
539 LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
540 list_remove(&in->entry);
541 heap_free(in);
544 LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) {
545 list_remove(&u->entry);
546 heap_free(u);
549 LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
550 list_remove(&s->entry);
551 heap_free(s);
555 /* Used to resolve break condition by forcing stronger condition over weaker. */
556 static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
558 switch (existingbreak) {
559 case DWRITE_BREAK_CONDITION_NEUTRAL:
560 return newbreak;
561 case DWRITE_BREAK_CONDITION_CAN_BREAK:
562 return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
563 /* let's keep stronger conditions as is */
564 case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
565 case DWRITE_BREAK_CONDITION_MUST_BREAK:
566 break;
567 default:
568 ERR("unknown break condition %d\n", existingbreak);
571 return existingbreak;
574 /* This helper should be used to get effective range length, in other words it returns number of text
575 positions from range starting point to the end of the range, limited by layout text length */
576 static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
578 if (range->h.range.startPosition + range->h.range.length <= layout->len)
579 return range->h.range.length;
580 return layout->len - range->h.range.startPosition;
583 /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
584 static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
586 DWRITE_BREAK_CONDITION before, after;
587 UINT32 i, length;
588 HRESULT hr;
590 /* ignore returned conditions if failed */
591 hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
592 if (FAILED(hr))
593 after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
595 if (!layout->actual_breakpoints) {
596 layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
597 if (!layout->actual_breakpoints)
598 return E_OUTOFMEMORY;
599 memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
602 length = get_clipped_range_length(layout, cur);
603 for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
604 /* for first codepoint check if there's anything before it and update accordingly */
605 if (i == cur->h.range.startPosition) {
606 if (i > 0)
607 layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
608 override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
609 else
610 layout->actual_breakpoints[i].breakConditionBefore = before;
611 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
613 /* similar check for last codepoint */
614 else if (i == cur->h.range.startPosition + length - 1) {
615 if (i == layout->len - 1)
616 layout->actual_breakpoints[i].breakConditionAfter = after;
617 else
618 layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
619 override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
620 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
622 /* for all positions within a range disable breaks */
623 else {
624 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
625 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
628 layout->actual_breakpoints[i].isWhitespace = 0;
629 layout->actual_breakpoints[i].isSoftHyphen = 0;
632 return S_OK;
635 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
637 static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
639 if (layout->actual_breakpoints)
640 return layout->actual_breakpoints[pos];
641 return layout->nominal_breakpoints[pos];
644 static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
645 UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
647 UINT8 breakcondition;
648 UINT32 position;
649 UINT16 j;
651 /* For clusters made of control chars we report zero glyphs, and we need zero cluster
652 width as well; advances are already computed at this point and are not necessary zero. */
653 metrics->width = 0.0f;
654 if (run->run.glyphCount) {
655 for (j = start_glyph; j < stop_glyph; j++)
656 metrics->width += run->run.glyphAdvances[j];
658 metrics->length = length;
660 position = run->descr.textPosition + stop_position;
661 if (stop_glyph == run->glyphcount)
662 breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter;
663 else {
664 breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore;
665 if (stop_position) position -= 1;
668 metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
669 breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
670 if (metrics->length == 1) {
671 DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position);
672 metrics->isWhitespace = bp.isWhitespace;
673 metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]);
674 metrics->isSoftHyphen = bp.isSoftHyphen;
676 else {
677 metrics->isWhitespace = 0;
678 metrics->isNewline = 0;
679 metrics->isSoftHyphen = 0;
681 metrics->isRightToLeft = run->run.bidiLevel & 1;
682 metrics->padding = 0;
687 All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
688 On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
689 Note that there's no need to reallocate anything at this point as we allocate one cluster per
690 codepoint initially.
693 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
695 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
696 struct layout_cluster *c = &layout->clusters[*cluster];
697 const struct regular_layout_run *run = &r->u.regular;
698 UINT32 i, start = 0;
700 assert(r->kind == LAYOUT_RUN_REGULAR);
702 for (i = 0; i < run->descr.stringLength; i++) {
703 BOOL end = i == run->descr.stringLength - 1;
705 if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
706 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
707 i - start, metrics);
708 c->position = start;
709 c->run = r;
711 *cluster += 1;
712 metrics++;
713 c++;
714 start = i;
717 if (end) {
718 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
719 i - start + 1, metrics);
720 c->position = start;
721 c->run = r;
723 *cluster += 1;
724 return;
729 #define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm)
731 static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize,
732 DWRITE_FONT_METRICS *fontmetrics)
734 if (is_layout_gdi_compatible(layout)) {
735 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics);
736 if (FAILED(hr))
737 WARN("failed to get compat metrics, 0x%08x\n", hr);
739 else
740 IDWriteFontFace_GetMetrics(fontface, fontmetrics);
743 static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height)
745 *baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics);
746 *height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
749 static HRESULT layout_itemize(struct dwrite_textlayout *layout)
751 IDWriteTextAnalyzer *analyzer;
752 struct layout_range *range;
753 struct layout_run *r;
754 HRESULT hr = S_OK;
756 analyzer = get_text_analyzer();
758 LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
759 /* We don't care about ranges that don't contain any text. */
760 if (range->h.range.startPosition >= layout->len)
761 break;
763 /* Inline objects override actual text in range. */
764 if (range->object) {
765 hr = layout_update_breakpoints_range(layout, range);
766 if (FAILED(hr))
767 return hr;
769 r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition);
770 if (!r)
771 return E_OUTOFMEMORY;
773 r->u.object.object = range->object;
774 r->u.object.length = get_clipped_range_length(layout, range);
775 list_add_tail(&layout->runs, &r->entry);
776 continue;
779 /* Initial splitting by script. */
780 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
781 range->h.range.startPosition, get_clipped_range_length(layout, range),
782 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
783 if (FAILED(hr))
784 break;
786 /* Splitting further by bidi levels. */
787 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
788 range->h.range.startPosition, get_clipped_range_length(layout, range),
789 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
790 if (FAILED(hr))
791 break;
794 return hr;
797 static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout)
799 IDWriteFontCollection *sys_collection;
800 IDWriteFontFallback *fallback = NULL;
801 struct layout_range *range;
802 struct layout_run *r;
803 HRESULT hr;
805 if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE,
806 (IDWriteFontCollection1 **)&sys_collection, FALSE))) {
807 WARN("Failed to get system collection, hr %#x.\n", hr);
808 return hr;
811 if (layout->format.fallback) {
812 fallback = layout->format.fallback;
813 IDWriteFontFallback_AddRef(fallback);
815 else {
816 if (FAILED(hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback))) {
817 WARN("Failed to get system fallback, hr %#x.\n", hr);
818 goto fatal;
822 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
823 struct regular_layout_run *run = &r->u.regular;
824 IDWriteFont *font;
825 UINT32 length;
827 if (r->kind == LAYOUT_RUN_INLINE)
828 continue;
830 range = get_layout_range_by_pos(layout, run->descr.textPosition);
832 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
833 IDWriteFontCollection *collection;
835 collection = range->collection ? range->collection : sys_collection;
837 if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style,
838 range->stretch, &font))) {
839 WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n",
840 debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
841 break;
844 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
845 IDWriteFont_Release(font);
846 if (FAILED(hr)) {
847 WARN("Failed to create font face, hr %#x.\n", hr);
848 break;
851 run->run.fontEmSize = range->fontsize;
852 continue;
855 length = run->descr.stringLength;
857 while (length) {
858 UINT32 mapped_length;
859 FLOAT scale;
861 run = &r->u.regular;
863 hr = IDWriteFontFallback_MapCharacters(fallback,
864 (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
865 run->descr.textPosition,
866 run->descr.stringLength,
867 range->collection,
868 range->fontfamily,
869 range->weight,
870 range->style,
871 range->stretch,
872 &mapped_length,
873 &font,
874 &scale);
875 if (FAILED(hr)) {
876 WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr),
877 debugstr_w(range->fontfamily), range->collection, hr);
878 goto fatal;
881 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
882 IDWriteFont_Release(font);
883 if (FAILED(hr)) {
884 WARN("Failed to create font face, hr %#x.\n", hr);
885 goto fatal;
888 run->run.fontEmSize = range->fontsize * scale;
890 if (mapped_length < length) {
891 struct regular_layout_run *nextrun;
892 struct layout_run *nextr;
894 /* keep mapped part for current run, add another run for the rest */
895 nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0);
896 if (!nextr) {
897 hr = E_OUTOFMEMORY;
898 goto fatal;
901 *nextr = *r;
902 nextr->start_position = run->descr.textPosition + mapped_length;
903 nextrun = &nextr->u.regular;
904 nextrun->descr.textPosition = nextr->start_position;
905 nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
906 nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
907 run->descr.stringLength = mapped_length;
908 list_add_after(&r->entry, &nextr->entry);
909 r = nextr;
912 length -= mapped_length;
916 fatal:
917 IDWriteFontCollection_Release(sys_collection);
918 if (fallback)
919 IDWriteFontFallback_Release(fallback);
921 return hr;
924 static HRESULT layout_shape_run(struct dwrite_textlayout *layout, struct regular_layout_run *run)
926 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
927 DWRITE_SHAPING_TEXT_PROPERTIES *text_props;
928 IDWriteTextAnalyzer *analyzer;
929 struct layout_range *range;
930 UINT32 max_count;
931 HRESULT hr;
933 range = get_layout_range_by_pos(layout, run->descr.textPosition);
934 run->descr.localeName = range->locale;
935 run->clustermap = heap_alloc(run->descr.stringLength * sizeof(*run->clustermap));
937 max_count = 3 * run->descr.stringLength / 2 + 16;
938 run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
939 if (!run->clustermap || !run->glyphs)
940 return E_OUTOFMEMORY;
942 text_props = heap_alloc(run->descr.stringLength * sizeof(*text_props));
943 glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
944 if (!text_props || !glyph_props) {
945 heap_free(text_props);
946 heap_free(glyph_props);
947 return E_OUTOFMEMORY;
950 analyzer = get_text_analyzer();
952 for (;;) {
953 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength, run->run.fontFace,
954 run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL /* FIXME */, NULL,
955 NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props, &run->glyphcount);
956 if (hr == E_NOT_SUFFICIENT_BUFFER) {
957 heap_free(run->glyphs);
958 heap_free(glyph_props);
960 max_count = run->glyphcount;
962 run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
963 glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
964 if (!run->glyphs || !glyph_props) {
965 hr = E_OUTOFMEMORY;
966 break;
969 continue;
972 break;
975 if (FAILED(hr)) {
976 heap_free(text_props);
977 heap_free(glyph_props);
978 WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
979 return hr;
982 run->run.glyphIndices = run->glyphs;
983 run->descr.clusterMap = run->clustermap;
985 run->advances = heap_alloc(run->glyphcount * sizeof(*run->advances));
986 run->offsets = heap_alloc(run->glyphcount * sizeof(*run->offsets));
987 if (!run->advances || !run->offsets)
988 return E_OUTOFMEMORY;
990 /* Get advances and offsets. */
991 if (is_layout_gdi_compatible(layout))
992 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
993 text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
994 run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
995 layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1,
996 &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
997 else
998 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
999 run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
1000 run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
1001 NULL, NULL, 0, run->advances, run->offsets);
1003 heap_free(text_props);
1004 heap_free(glyph_props);
1005 if (FAILED(hr)) {
1006 memset(run->advances, 0, run->glyphcount * sizeof(*run->advances));
1007 memset(run->offsets, 0, run->glyphcount * sizeof(*run->offsets));
1008 WARN("%s: failed to get glyph placement info, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
1011 run->run.glyphAdvances = run->advances;
1012 run->run.glyphOffsets = run->offsets;
1014 /* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
1015 with valid cluster map and potentially with non-zero advances; layout code exposes those as zero
1016 width clusters. */
1017 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
1018 run->run.glyphCount = 0;
1019 else
1020 run->run.glyphCount = run->glyphcount;
1022 return S_OK;
1025 static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
1027 struct layout_run *r;
1028 UINT32 cluster = 0;
1029 HRESULT hr;
1031 free_layout_eruns(layout);
1032 free_layout_runs(layout);
1034 /* Cluster data arrays are allocated once, assuming one text position per cluster. */
1035 if (!layout->clustermetrics && layout->len) {
1036 layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics));
1037 layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters));
1038 if (!layout->clustermetrics || !layout->clusters) {
1039 heap_free(layout->clustermetrics);
1040 heap_free(layout->clusters);
1041 return E_OUTOFMEMORY;
1044 layout->cluster_count = 0;
1046 if (FAILED(hr = layout_itemize(layout))) {
1047 WARN("Itemization failed, hr %#x.\n", hr);
1048 return hr;
1051 if (FAILED(hr = layout_resolve_fonts(layout))) {
1052 WARN("Failed to resolve layout fonts, hr %#x.\n", hr);
1053 return hr;
1056 /* fill run info */
1057 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
1058 struct regular_layout_run *run = &r->u.regular;
1059 DWRITE_FONT_METRICS fontmetrics = { 0 };
1061 /* we need to do very little in case of inline objects */
1062 if (r->kind == LAYOUT_RUN_INLINE) {
1063 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster];
1064 struct layout_cluster *c = &layout->clusters[cluster];
1065 DWRITE_INLINE_OBJECT_METRICS inlinemetrics;
1067 metrics->width = 0.0f;
1068 metrics->length = r->u.object.length;
1069 metrics->canWrapLineAfter = 0;
1070 metrics->isWhitespace = 0;
1071 metrics->isNewline = 0;
1072 metrics->isSoftHyphen = 0;
1073 metrics->isRightToLeft = 0;
1074 metrics->padding = 0;
1075 c->run = r;
1076 c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */
1077 cluster++;
1079 /* it's not fatal if GetMetrics() fails, all returned metrics are ignored */
1080 hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics);
1081 if (FAILED(hr)) {
1082 memset(&inlinemetrics, 0, sizeof(inlinemetrics));
1083 hr = S_OK;
1085 metrics->width = inlinemetrics.width;
1086 r->baseline = inlinemetrics.baseline;
1087 r->height = inlinemetrics.height;
1089 /* FIXME: use resolved breakpoints in this case too */
1091 continue;
1094 if (FAILED(hr = layout_shape_run(layout, run)))
1095 WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
1097 /* baseline derived from font metrics */
1098 layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
1099 layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
1101 layout_set_cluster_metrics(layout, r, &cluster);
1104 if (hr == S_OK) {
1105 layout->cluster_count = cluster;
1106 if (cluster)
1107 layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
1110 return hr;
1113 static HRESULT layout_compute(struct dwrite_textlayout *layout)
1115 HRESULT hr;
1117 if (!(layout->recompute & RECOMPUTE_CLUSTERS))
1118 return S_OK;
1120 /* nominal breakpoints are evaluated only once, because string never changes */
1121 if (!layout->nominal_breakpoints) {
1122 IDWriteTextAnalyzer *analyzer;
1124 layout->nominal_breakpoints = heap_alloc(layout->len * sizeof(*layout->nominal_breakpoints));
1125 if (!layout->nominal_breakpoints)
1126 return E_OUTOFMEMORY;
1128 analyzer = get_text_analyzer();
1130 if (FAILED(hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer,
1131 (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
1132 0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface)))
1133 WARN("Line breakpoints analysis failed, hr %#x.\n", hr);
1136 heap_free(layout->actual_breakpoints);
1137 layout->actual_breakpoints = NULL;
1139 hr = layout_compute_runs(layout);
1141 if (TRACE_ON(dwrite)) {
1142 struct layout_run *cur;
1144 LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) {
1145 if (cur->kind == LAYOUT_RUN_INLINE)
1146 TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length);
1147 else
1148 TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition +
1149 cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel);
1153 layout->recompute &= ~RECOMPUTE_CLUSTERS;
1154 return hr;
1157 static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end)
1159 FLOAT width = 0.0f;
1160 for (; start < end; start++)
1161 width += layout->clustermetrics[start].width;
1162 return width;
1165 static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos)
1167 struct layout_range_header *cur;
1169 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
1170 DWRITE_TEXT_RANGE *r = &cur->range;
1171 if (r->startPosition <= pos && pos < r->startPosition + r->length)
1172 return cur;
1175 return NULL;
1178 static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1180 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos);
1181 return ((struct layout_range_iface*)h)->iface;
1184 static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun)
1186 return erun->run->u.regular.run.bidiLevel & 1;
1189 /* A set of parameters that additionally splits resulting runs. It happens after shaping and all text processing,
1190 no glyph changes are possible. It's understandable for drawing effects, because DrawGlyphRun() reports them as
1191 one of the arguments, but it also happens for decorations, so every effective run has uniform
1192 underline/strikethough/effect tuple. */
1193 struct layout_final_splitting_params {
1194 BOOL strikethrough;
1195 BOOL underline;
1196 IUnknown *effect;
1199 static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1201 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos);
1202 return ((struct layout_range_bool*)h)->value;
1205 static inline BOOL layout_get_underline_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1207 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->underline_ranges, pos);
1208 return ((struct layout_range_bool*)h)->value;
1211 static void layout_splitting_params_from_pos(struct dwrite_textlayout *layout, UINT32 pos,
1212 struct layout_final_splitting_params *params)
1214 params->strikethrough = layout_get_strikethrough_from_pos(layout, pos);
1215 params->underline = layout_get_underline_from_pos(layout, pos);
1216 params->effect = layout_get_effect_from_pos(layout, pos);
1219 static BOOL is_same_splitting_params(const struct layout_final_splitting_params *left,
1220 const struct layout_final_splitting_params *right)
1222 return left->strikethrough == right->strikethrough &&
1223 left->underline == right->underline &&
1224 left->effect == right->effect;
1227 static void layout_get_erun_font_metrics(struct dwrite_textlayout *layout, struct layout_effective_run *erun,
1228 DWRITE_FONT_METRICS *metrics)
1230 memset(metrics, 0, sizeof(*metrics));
1231 if (is_layout_gdi_compatible(layout)) {
1232 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
1233 erun->run->u.regular.run.fontFace,
1234 erun->run->u.regular.run.fontEmSize,
1235 layout->ppdip,
1236 &layout->transform,
1237 metrics);
1238 if (FAILED(hr))
1239 WARN("failed to get font metrics, 0x%08x\n", hr);
1241 else
1242 IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics);
1245 /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index,
1246 'cluster_count' indicates how many clusters to add, including first one. */
1247 static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster,
1248 UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params)
1250 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1251 UINT32 i, start, length, last_cluster;
1252 struct layout_effective_run *run;
1254 if (r->kind == LAYOUT_RUN_INLINE) {
1255 struct layout_effective_inline *inlineobject;
1257 inlineobject = heap_alloc(sizeof(*inlineobject));
1258 if (!inlineobject)
1259 return E_OUTOFMEMORY;
1261 inlineobject->object = r->u.object.object;
1262 inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1263 inlineobject->origin.x = is_rtl ? origin_x - inlineobject->width : origin_x;
1264 inlineobject->origin.y = 0.0f; /* set after line is built */
1265 inlineobject->align_dx = 0.0f;
1266 inlineobject->baseline = r->baseline;
1268 /* It's not clear how these two are set, possibly directionality
1269 is derived from surrounding text (replaced text could have
1270 different ranges which differ in reading direction). */
1271 inlineobject->is_sideways = FALSE;
1272 inlineobject->is_rtl = FALSE;
1273 inlineobject->line = line;
1275 /* effect assigned from start position and on is used for inline objects */
1276 inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position +
1277 layout->clusters[first_cluster].run->start_position);
1279 list_add_tail(&layout->inlineobjects, &inlineobject->entry);
1280 return S_OK;
1283 run = heap_alloc(sizeof(*run));
1284 if (!run)
1285 return E_OUTOFMEMORY;
1287 /* No need to iterate for that, use simple fact that:
1288 <last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */
1289 last_cluster = first_cluster + cluster_count - 1;
1290 length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
1291 layout->clustermetrics[last_cluster].length;
1293 run->clustermap = heap_alloc(sizeof(UINT16)*length);
1294 if (!run->clustermap) {
1295 heap_free(run);
1296 return E_OUTOFMEMORY;
1299 run->run = r;
1300 run->start = start = layout->clusters[first_cluster].position;
1301 run->length = length;
1302 run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1303 memset(&run->bbox, 0, sizeof(run->bbox));
1305 /* Check if run direction matches paragraph direction, if it doesn't adjust by
1306 run width */
1307 if (layout_is_erun_rtl(run) ^ is_rtl)
1308 run->origin.x = is_rtl ? origin_x - run->width : origin_x + run->width;
1309 else
1310 run->origin.x = origin_x;
1312 run->origin.y = 0.0f; /* set after line is built */
1313 run->align_dx = 0.0f;
1314 run->line = line;
1316 if (r->u.regular.run.glyphCount) {
1317 /* Trim leading and trailing clusters. */
1318 run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start];
1319 if (start + length < r->u.regular.descr.stringLength)
1320 run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length];
1322 else
1323 run->glyphcount = 0;
1325 /* cluster map needs to be shifted */
1326 for (i = 0; i < length; i++)
1327 run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start];
1329 run->effect = params->effect;
1330 run->underlined = params->underline;
1331 list_add_tail(&layout->eruns, &run->entry);
1333 /* Strikethrough style is guaranteed to be consistent within effective run,
1334 its width equals to run width, thickness and offset are derived from
1335 font metrics, rest of the values are from layout or run itself */
1336 if (params->strikethrough) {
1337 struct layout_strikethrough *s;
1338 DWRITE_FONT_METRICS metrics;
1340 s = heap_alloc(sizeof(*s));
1341 if (!s)
1342 return E_OUTOFMEMORY;
1344 layout_get_erun_font_metrics(layout, run, &metrics);
1345 s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1346 s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics);
1347 /* Negative offset moves it above baseline as Y coordinate grows downward. */
1348 s->s.offset = -SCALE_FONT_METRIC(metrics.strikethroughPosition, r->u.regular.run.fontEmSize, &metrics);
1349 s->s.readingDirection = layout->format.readingdir;
1350 s->s.flowDirection = layout->format.flow;
1351 s->s.localeName = r->u.regular.descr.localeName;
1352 s->s.measuringMode = layout->measuringmode;
1353 s->run = run;
1355 list_add_tail(&layout->strikethrough, &s->entry);
1358 return S_OK;
1361 static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics)
1363 UINT32 i = layout->metrics.lineCount;
1365 if (!layout->line_alloc) {
1366 layout->line_alloc = 5;
1367 layout->linemetrics = heap_alloc(layout->line_alloc * sizeof(*layout->linemetrics));
1368 layout->lines = heap_alloc(layout->line_alloc * sizeof(*layout->lines));
1369 if (!layout->linemetrics || !layout->lines) {
1370 heap_free(layout->linemetrics);
1371 heap_free(layout->lines);
1372 layout->linemetrics = NULL;
1373 layout->lines = NULL;
1374 return E_OUTOFMEMORY;
1378 if (layout->metrics.lineCount == layout->line_alloc) {
1379 DWRITE_LINE_METRICS1 *metrics;
1380 struct layout_line *lines;
1382 if ((metrics = heap_realloc(layout->linemetrics, layout->line_alloc * 2 * sizeof(*layout->linemetrics))))
1383 layout->linemetrics = metrics;
1384 if ((lines = heap_realloc(layout->lines, layout->line_alloc * 2 * sizeof(*layout->lines))))
1385 layout->lines = lines;
1387 if (!metrics || !lines)
1388 return E_OUTOFMEMORY;
1390 layout->line_alloc *= 2;
1393 layout->linemetrics[i] = *metrics;
1395 switch (layout->format.spacing.method)
1397 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
1398 if (layout->format.spacing.method == DWRITE_LINE_SPACING_METHOD_UNIFORM) {
1399 layout->linemetrics[i].height = layout->format.spacing.height;
1400 layout->linemetrics[i].baseline = layout->format.spacing.baseline;
1402 break;
1403 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
1404 if (layout->format.spacing.method == DWRITE_LINE_SPACING_METHOD_UNIFORM) {
1405 layout->linemetrics[i].height = layout->format.spacing.height * metrics->height;
1406 layout->linemetrics[i].baseline = layout->format.spacing.baseline * metrics->baseline;
1408 break;
1409 default:
1410 /* using content values */;
1413 layout->lines[i].height = metrics->height;
1414 layout->lines[i].baseline = metrics->baseline;
1416 layout->metrics.lineCount++;
1417 return S_OK;
1420 static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
1421 const struct layout_effective_run *cur)
1423 struct list *e;
1425 if (!cur)
1426 e = list_head(&layout->eruns);
1427 else
1428 e = list_next(&layout->eruns, &cur->entry);
1429 if (!e)
1430 return NULL;
1431 return LIST_ENTRY(e, struct layout_effective_run, entry);
1434 static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout,
1435 const struct layout_effective_run *cur)
1437 struct list *e;
1439 if (!cur)
1440 e = list_tail(&layout->eruns);
1441 else
1442 e = list_prev(&layout->eruns, &cur->entry);
1443 if (!e)
1444 return NULL;
1445 return LIST_ENTRY(e, struct layout_effective_run, entry);
1448 static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
1449 const struct layout_effective_inline *cur)
1451 struct list *e;
1453 if (!cur)
1454 e = list_head(&layout->inlineobjects);
1455 else
1456 e = list_next(&layout->inlineobjects, &cur->entry);
1457 if (!e)
1458 return NULL;
1459 return LIST_ENTRY(e, struct layout_effective_inline, entry);
1462 static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
1463 struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
1465 FLOAT width = 0.0f;
1467 while (erun && erun->line == line) {
1468 width += erun->width;
1469 erun = layout_get_next_erun(layout, erun);
1470 if (!erun)
1471 break;
1474 while (inrun && inrun->line == line) {
1475 width += inrun->width;
1476 inrun = layout_get_next_inline_run(layout, inrun);
1477 if (!inrun)
1478 break;
1481 return width;
1484 static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
1486 *det = m->m11 * m->m22 - m->m12 * m->m21;
1487 /* on certain conditions we can skip transform */
1488 return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
1491 static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, FLOAT ppdip,
1492 const DWRITE_MATRIX *m, FLOAT det)
1494 if (!skiptransform) {
1495 D2D1_POINT_2F vec2;
1497 /* apply transform */
1498 vec->x *= ppdip;
1499 vec->y *= ppdip;
1501 vec2.x = m->m11 * vec->x + m->m21 * vec->y + m->dx;
1502 vec2.y = m->m12 * vec->x + m->m22 * vec->y + m->dy;
1504 /* snap */
1505 vec2.x = floorf(vec2.x + 0.5f);
1506 vec2.y = floorf(vec2.y + 0.5f);
1508 /* apply inverted transform, we don't care about X component at this point */
1509 vec->x = (m->m22 * vec2.x - m->m21 * vec2.y + m->m21 * m->dy - m->m22 * m->dx) / det;
1510 vec->x /= ppdip;
1512 vec->y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
1513 vec->y /= ppdip;
1515 else {
1516 vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
1517 vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
1521 static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
1523 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1524 struct layout_effective_inline *inrun;
1525 struct layout_effective_run *erun;
1527 erun = layout_get_next_erun(layout, NULL);
1528 inrun = layout_get_next_inline_run(layout, NULL);
1530 while (erun) {
1531 erun->align_dx = 0.0f;
1532 erun = layout_get_next_erun(layout, erun);
1535 while (inrun) {
1536 inrun->align_dx = 0.0f;
1537 inrun = layout_get_next_inline_run(layout, inrun);
1540 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
1543 static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout)
1545 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1546 struct layout_effective_inline *inrun;
1547 struct layout_effective_run *erun;
1548 UINT32 line;
1550 erun = layout_get_next_erun(layout, NULL);
1551 inrun = layout_get_next_inline_run(layout, NULL);
1553 for (line = 0; line < layout->metrics.lineCount; line++) {
1554 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1555 FLOAT shift = layout->metrics.layoutWidth - width;
1557 if (is_rtl)
1558 shift *= -1.0f;
1560 while (erun && erun->line == line) {
1561 erun->align_dx = shift;
1562 erun = layout_get_next_erun(layout, erun);
1565 while (inrun && inrun->line == line) {
1566 inrun->align_dx = shift;
1567 inrun = layout_get_next_inline_run(layout, inrun);
1571 layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width;
1574 static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
1575 FLOAT width, FLOAT det)
1577 if (is_layout_gdi_compatible(layout)) {
1578 D2D1_POINT_2F vec = { layout->metrics.layoutWidth - width, 0.0f};
1579 layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
1580 return floorf(vec.x / 2.0f);
1582 else
1583 return (layout->metrics.layoutWidth - width) / 2.0f;
1586 static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
1588 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1589 struct layout_effective_inline *inrun;
1590 struct layout_effective_run *erun;
1591 BOOL skiptransform;
1592 UINT32 line;
1593 FLOAT det;
1595 erun = layout_get_next_erun(layout, NULL);
1596 inrun = layout_get_next_inline_run(layout, NULL);
1598 skiptransform = should_skip_transform(&layout->transform, &det);
1600 for (line = 0; line < layout->metrics.lineCount; line++) {
1601 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1602 FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
1604 if (is_rtl)
1605 shift *= -1.0f;
1607 while (erun && erun->line == line) {
1608 erun->align_dx = shift;
1609 erun = layout_get_next_erun(layout, erun);
1612 while (inrun && inrun->line == line) {
1613 inrun->align_dx = shift;
1614 inrun = layout_get_next_inline_run(layout, inrun);
1618 layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f;
1621 static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
1623 switch (layout->format.textalignment)
1625 case DWRITE_TEXT_ALIGNMENT_LEADING:
1626 layout_apply_leading_alignment(layout);
1627 break;
1628 case DWRITE_TEXT_ALIGNMENT_TRAILING:
1629 layout_apply_trailing_alignment(layout);
1630 break;
1631 case DWRITE_TEXT_ALIGNMENT_CENTER:
1632 layout_apply_centered_alignment(layout);
1633 break;
1634 case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
1635 FIXME("alignment %d not implemented\n", layout->format.textalignment);
1636 break;
1637 default:
1642 static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
1644 struct layout_effective_inline *inrun;
1645 struct layout_effective_run *erun;
1646 FLOAT origin_y = 0.0f;
1647 UINT32 line;
1649 /* alignment mode defines origin, after that all run origins are updated
1650 the same way */
1652 switch (layout->format.paralign)
1654 case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
1655 origin_y = 0.0f;
1656 break;
1657 case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
1658 origin_y = layout->metrics.layoutHeight - layout->metrics.height;
1659 break;
1660 case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
1661 origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f;
1662 break;
1663 default:
1667 layout->metrics.top = origin_y;
1669 erun = layout_get_next_erun(layout, NULL);
1670 inrun = layout_get_next_inline_run(layout, NULL);
1671 for (line = 0; line < layout->metrics.lineCount; line++) {
1672 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1674 while (erun && erun->line == line) {
1675 erun->origin.y = pos_y;
1676 erun = layout_get_next_erun(layout, erun);
1679 while (inrun && inrun->line == line) {
1680 inrun->origin.y = pos_y - inrun->baseline;
1681 inrun = layout_get_next_inline_run(layout, inrun);
1684 origin_y += layout->linemetrics[line].height;
1688 struct layout_underline_splitting_params {
1689 const WCHAR *locale; /* points to range data, no additional allocation */
1690 IUnknown *effect; /* does not hold another reference */
1693 static void init_u_splitting_params_from_erun(struct layout_effective_run *erun,
1694 struct layout_underline_splitting_params *params)
1696 params->locale = erun->run->u.regular.descr.localeName;
1697 params->effect = erun->effect;
1700 static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left,
1701 struct layout_underline_splitting_params *right)
1703 return left->effect == right->effect && !strcmpiW(left->locale, right->locale);
1706 static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first,
1707 struct layout_effective_run *last)
1709 FLOAT thickness, offset, runheight;
1710 struct layout_effective_run *cur;
1711 DWRITE_FONT_METRICS metrics;
1713 if (first == layout_get_prev_erun(layout, last)) {
1714 layout_get_erun_font_metrics(layout, first, &metrics);
1715 thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics);
1716 offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics);
1717 runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics);
1719 else {
1720 FLOAT width = 0.0f;
1722 /* Single underline is added for consecutive underlined runs. In this case underline parameters are
1723 calculated as weighted average, where run width acts as a weight. */
1724 thickness = offset = runheight = 0.0f;
1725 cur = first;
1726 do {
1727 layout_get_erun_font_metrics(layout, cur, &metrics);
1729 thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1730 offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1731 runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight);
1732 width += cur->width;
1734 cur = layout_get_next_erun(layout, cur);
1735 } while (cur != last);
1737 thickness /= width;
1738 offset /= width;
1741 cur = first;
1742 do {
1743 struct layout_underline_splitting_params params, prev_params;
1744 struct layout_effective_run *next, *w;
1745 struct layout_underline *u;
1747 init_u_splitting_params_from_erun(cur, &prev_params);
1748 while ((next = layout_get_next_erun(layout, cur)) != last) {
1749 init_u_splitting_params_from_erun(next, &params);
1750 if (!is_same_u_splitting(&prev_params, &params))
1751 break;
1752 cur = next;
1755 u = heap_alloc(sizeof(*u));
1756 if (!u)
1757 return E_OUTOFMEMORY;
1759 w = cur;
1760 u->u.width = 0.0f;
1761 while (w != next) {
1762 u->u.width += w->width;
1763 w = layout_get_next_erun(layout, w);
1766 u->u.thickness = thickness;
1767 /* Font metrics convention is to have it negative when below baseline, for rendering
1768 however Y grows from baseline down for horizontal baseline. */
1769 u->u.offset = -offset;
1770 u->u.runHeight = runheight;
1771 u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
1772 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
1773 u->u.flowDirection = layout->format.flow;
1774 u->u.localeName = cur->run->u.regular.descr.localeName;
1775 u->u.measuringMode = layout->measuringmode;
1776 u->run = cur;
1777 list_add_tail(&layout->underlines, &u->entry);
1779 cur = next;
1780 } while (cur != last);
1782 return S_OK;
1785 /* Adds zero width line, metrics are derived from font at specified text position. */
1786 static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
1788 DWRITE_LINE_METRICS1 metrics = { 0 };
1789 DWRITE_FONT_METRICS fontmetrics;
1790 struct layout_range *range;
1791 IDWriteFontFace *fontface;
1792 IDWriteFont *font;
1793 HRESULT hr;
1795 range = get_layout_range_by_pos(layout, pos);
1796 hr = create_matching_font(range->collection,
1797 range->fontfamily,
1798 range->weight,
1799 range->style,
1800 range->stretch,
1801 &font);
1802 if (FAILED(hr))
1803 return hr;
1804 hr = IDWriteFont_CreateFontFace(font, &fontface);
1805 IDWriteFont_Release(font);
1806 if (FAILED(hr))
1807 return hr;
1809 layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics);
1810 layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
1811 IDWriteFontFace_Release(fontface);
1813 return layout_set_line_metrics(layout, &metrics);
1816 static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
1817 UINT32 *textpos)
1819 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1820 struct layout_final_splitting_params params, prev_params;
1821 DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 };
1822 UINT32 line = layout->metrics.lineCount, i;
1823 DWRITE_LINE_METRICS1 metrics = { 0 };
1824 UINT32 index, start, pos = *textpos;
1825 FLOAT descent, trailingspacewidth;
1826 BOOL append_trimming_run = FALSE;
1827 const struct layout_run *run;
1828 FLOAT width, origin_x;
1829 HRESULT hr;
1831 /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
1832 for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
1833 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1834 struct layout_cluster *lc = &layout->clusters[index];
1835 WCHAR ch;
1837 /* This also filters out clusters added from inline objects, those are never
1838 treated as a white space. */
1839 if (!cluster->isWhitespace)
1840 break;
1842 /* Every isNewline cluster is also isWhitespace, but not every
1843 newline character cluster has isNewline set, so go back to original string. */
1844 ch = lc->run->u.regular.descr.string[lc->position];
1845 if (cluster->length == 1 && lb_is_newline_char(ch))
1846 metrics.newlineLength += cluster->length;
1848 metrics.trailingWhitespaceLength += cluster->length;
1849 trailingspacewidth += cluster->width;
1852 /* Line metrics length includes trailing whitespace length too */
1853 for (i = first_cluster; i <= last_cluster; i++)
1854 metrics.length += layout->clustermetrics[i].length;
1856 /* Ignore trailing whitespaces */
1857 while (last_cluster > first_cluster) {
1858 if (!layout->clustermetrics[last_cluster].isWhitespace)
1859 break;
1861 last_cluster--;
1864 /* Does not include trailing space width */
1865 width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
1867 /* Append trimming run if necessary */
1868 if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL &&
1869 layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) {
1870 FLOAT trimmed_width = width;
1872 hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics);
1873 if (SUCCEEDED(hr)) {
1874 while (last_cluster > first_cluster) {
1875 if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth)
1876 break;
1877 trimmed_width -= layout->clustermetrics[last_cluster--].width;
1879 append_trimming_run = TRUE;
1881 else
1882 WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr);
1884 width = trimmed_width + sign_metrics.width;
1887 layout_splitting_params_from_pos(layout, pos, &params);
1888 prev_params = params;
1889 run = layout->clusters[first_cluster].run;
1891 /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
1892 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
1893 for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
1894 layout_splitting_params_from_pos(layout, pos, &params);
1896 if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, &params)) {
1897 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1898 if (FAILED(hr))
1899 return;
1901 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
1902 get_cluster_range_width(layout, start, i);
1903 run = layout->clusters[i].run;
1904 start = i;
1907 prev_params = params;
1908 pos += layout->clustermetrics[i].length;
1911 /* Final run from what's left from cluster range */
1912 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1913 if (FAILED(hr))
1914 return;
1916 if (append_trimming_run) {
1917 struct layout_effective_inline *trimming_sign;
1919 trimming_sign = heap_alloc(sizeof(*trimming_sign));
1920 if (!trimming_sign)
1921 return;
1923 trimming_sign->object = layout->format.trimmingsign;
1924 trimming_sign->width = sign_metrics.width;
1925 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i);
1926 trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x;
1927 trimming_sign->origin.y = 0.0f; /* set after line is built */
1928 trimming_sign->align_dx = 0.0f;
1929 trimming_sign->baseline = sign_metrics.baseline;
1931 trimming_sign->is_sideways = FALSE;
1932 trimming_sign->is_rtl = FALSE;
1933 trimming_sign->line = line;
1935 trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position +
1936 layout->clusters[i].run->start_position);
1938 list_add_tail(&layout->inlineobjects, &trimming_sign->entry);
1941 /* Look for max baseline and descent for this line */
1942 for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) {
1943 const struct layout_run *cur = layout->clusters[index].run;
1944 FLOAT cur_descent = cur->height - cur->baseline;
1946 if (cur->baseline > metrics.baseline)
1947 metrics.baseline = cur->baseline;
1948 if (cur_descent > descent)
1949 descent = cur_descent;
1952 layout->metrics.width = max(width, layout->metrics.width);
1953 layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
1954 layout->metrics.widthIncludingTrailingWhitespace);
1956 metrics.height = descent + metrics.baseline;
1957 metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth;
1958 layout_set_line_metrics(layout, &metrics);
1960 *textpos += metrics.length;
1963 static void layout_set_line_positions(struct dwrite_textlayout *layout)
1965 struct layout_effective_inline *inrun;
1966 struct layout_effective_run *erun;
1967 FLOAT origin_y;
1968 UINT32 line;
1970 /* Now all line info is here, update effective runs positions in flow direction */
1971 erun = layout_get_next_erun(layout, NULL);
1972 inrun = layout_get_next_inline_run(layout, NULL);
1974 for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) {
1975 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1977 /* For all runs on this line */
1978 while (erun && erun->line == line) {
1979 erun->origin.y = pos_y;
1980 erun = layout_get_next_erun(layout, erun);
1983 /* Same for inline runs */
1984 while (inrun && inrun->line == line) {
1985 inrun->origin.y = pos_y - inrun->baseline;
1986 inrun = layout_get_next_inline_run(layout, inrun);
1989 origin_y += layout->linemetrics[line].height;
1992 layout->metrics.height = origin_y;
1994 /* Initial paragraph alignment is always near */
1995 if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
1996 layout_apply_par_alignment(layout);
1999 static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster)
2001 if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER)
2002 return TRUE;
2004 return layout->clustermetrics[cluster].canWrapLineAfter;
2007 static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
2009 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
2010 struct layout_effective_run *erun, *first_underlined;
2011 UINT32 i, start, textpos, last_breaking_point;
2012 DWRITE_LINE_METRICS1 metrics;
2013 FLOAT width;
2014 UINT32 line;
2015 HRESULT hr;
2017 if (!(layout->recompute & RECOMPUTE_LINES))
2018 return S_OK;
2020 free_layout_eruns(layout);
2022 hr = layout_compute(layout);
2023 if (FAILED(hr))
2024 return hr;
2026 layout->metrics.lineCount = 0;
2027 memset(&metrics, 0, sizeof(metrics));
2029 layout->metrics.height = 0.0f;
2030 layout->metrics.width = 0.0f;
2031 layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
2033 last_breaking_point = ~0u;
2035 for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
2036 BOOL overflow = FALSE;
2038 while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
2039 /* Check for overflow */
2040 overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
2041 (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
2042 if (overflow)
2043 break;
2045 if (layout_can_wrap_after(layout, i))
2046 last_breaking_point = i;
2047 width += layout->clustermetrics[i].width;
2048 i++;
2050 i = min(i, layout->cluster_count - 1);
2052 /* Ignore if overflown on whitespace */
2053 if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) {
2054 /* Use most recently found breaking point */
2055 if (last_breaking_point != ~0u) {
2056 i = last_breaking_point;
2057 last_breaking_point = ~0u;
2059 else {
2060 /* Otherwise proceed forward to next newline or breaking point */
2061 for (; i < layout->cluster_count; i++)
2062 if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline)
2063 break;
2066 i = min(i, layout->cluster_count - 1);
2068 layout_add_line(layout, start, i, &textpos);
2069 start = i + 1;
2070 width = 0.0f;
2073 /* Add dummy line if:
2074 - there's no text, metrics come from first range in this case;
2075 - last ended with a mandatory break, metrics come from last text position.
2077 if (layout->len == 0)
2078 hr = layout_set_dummy_line_metrics(layout, 0);
2079 else if (layout->clustermetrics[layout->cluster_count - 1].isNewline)
2080 hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
2081 if (FAILED(hr))
2082 return hr;
2084 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
2085 layout->metrics.top = 0.0f;
2086 layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
2088 /* Add explicit underlined runs */
2089 erun = layout_get_next_erun(layout, NULL);
2090 first_underlined = erun && erun->underlined ? erun : NULL;
2091 for (line = 0; line < layout->metrics.lineCount; line++) {
2092 while (erun && erun->line == line) {
2093 erun = layout_get_next_erun(layout, erun);
2095 if (first_underlined && (!erun || !erun->underlined)) {
2096 layout_add_underline(layout, first_underlined, erun);
2097 first_underlined = NULL;
2099 else if (!first_underlined && erun && erun->underlined)
2100 first_underlined = erun;
2104 /* Position runs in flow direction */
2105 layout_set_line_positions(layout);
2107 /* Initial alignment is always leading */
2108 if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
2109 layout_apply_text_alignment(layout);
2111 layout->recompute &= ~RECOMPUTE_LINES;
2112 return hr;
2115 static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
2116 struct layout_range_attr_value *value)
2118 struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
2119 struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
2120 struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
2121 struct layout_range const *range = (struct layout_range*)h;
2123 switch (attr) {
2124 case LAYOUT_RANGE_ATTR_WEIGHT:
2125 return range->weight == value->u.weight;
2126 case LAYOUT_RANGE_ATTR_STYLE:
2127 return range->style == value->u.style;
2128 case LAYOUT_RANGE_ATTR_STRETCH:
2129 return range->stretch == value->u.stretch;
2130 case LAYOUT_RANGE_ATTR_FONTSIZE:
2131 return range->fontsize == value->u.fontsize;
2132 case LAYOUT_RANGE_ATTR_INLINE:
2133 return range->object == value->u.object;
2134 case LAYOUT_RANGE_ATTR_EFFECT:
2135 return range_iface->iface == value->u.effect;
2136 case LAYOUT_RANGE_ATTR_UNDERLINE:
2137 return range_bool->value == value->u.underline;
2138 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2139 return range_bool->value == value->u.strikethrough;
2140 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2141 return range->pair_kerning == value->u.pair_kerning;
2142 case LAYOUT_RANGE_ATTR_FONTCOLL:
2143 return range->collection == value->u.collection;
2144 case LAYOUT_RANGE_ATTR_LOCALE:
2145 return strcmpiW(range->locale, value->u.locale) == 0;
2146 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2147 return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
2148 case LAYOUT_RANGE_ATTR_SPACING:
2149 return range_spacing->leading == value->u.spacing.leading &&
2150 range_spacing->trailing == value->u.spacing.trailing &&
2151 range_spacing->min_advance == value->u.spacing.min_advance;
2152 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2153 return range_iface->iface == (IUnknown*)value->u.typography;
2154 default:
2158 return FALSE;
2161 static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
2163 switch (hleft->kind)
2165 case LAYOUT_RANGE_REGULAR:
2167 struct layout_range const *left = (struct layout_range const*)hleft;
2168 struct layout_range const *right = (struct layout_range const*)hright;
2169 return left->weight == right->weight &&
2170 left->style == right->style &&
2171 left->stretch == right->stretch &&
2172 left->fontsize == right->fontsize &&
2173 left->object == right->object &&
2174 left->pair_kerning == right->pair_kerning &&
2175 left->collection == right->collection &&
2176 !strcmpiW(left->locale, right->locale) &&
2177 !strcmpW(left->fontfamily, right->fontfamily);
2179 case LAYOUT_RANGE_UNDERLINE:
2180 case LAYOUT_RANGE_STRIKETHROUGH:
2182 struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
2183 struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
2184 return left->value == right->value;
2186 case LAYOUT_RANGE_EFFECT:
2187 case LAYOUT_RANGE_TYPOGRAPHY:
2189 struct layout_range_iface const *left = (struct layout_range_iface const*)hleft;
2190 struct layout_range_iface const *right = (struct layout_range_iface const*)hright;
2191 return left->iface == right->iface;
2193 case LAYOUT_RANGE_SPACING:
2195 struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
2196 struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
2197 return left->leading == right->leading &&
2198 left->trailing == right->trailing &&
2199 left->min_advance == right->min_advance;
2201 default:
2202 FIXME("unknown range kind %d\n", hleft->kind);
2203 return FALSE;
2207 static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
2209 return left->startPosition == right->startPosition && left->length == right->length;
2212 /* Allocates range and inits it with default values from text format. */
2213 static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
2214 enum layout_range_kind kind)
2216 struct layout_range_header *h;
2218 switch (kind)
2220 case LAYOUT_RANGE_REGULAR:
2222 struct layout_range *range;
2224 range = heap_alloc(sizeof(*range));
2225 if (!range) return NULL;
2227 range->weight = layout->format.weight;
2228 range->style = layout->format.style;
2229 range->stretch = layout->format.stretch;
2230 range->fontsize = layout->format.fontsize;
2231 range->object = NULL;
2232 range->pair_kerning = FALSE;
2234 range->fontfamily = heap_strdupW(layout->format.family_name);
2235 if (!range->fontfamily) {
2236 heap_free(range);
2237 return NULL;
2240 range->collection = layout->format.collection;
2241 if (range->collection)
2242 IDWriteFontCollection_AddRef(range->collection);
2243 strcpyW(range->locale, layout->format.locale);
2245 h = &range->h;
2246 break;
2248 case LAYOUT_RANGE_UNDERLINE:
2249 case LAYOUT_RANGE_STRIKETHROUGH:
2251 struct layout_range_bool *range;
2253 range = heap_alloc(sizeof(*range));
2254 if (!range) return NULL;
2256 range->value = FALSE;
2257 h = &range->h;
2258 break;
2260 case LAYOUT_RANGE_EFFECT:
2261 case LAYOUT_RANGE_TYPOGRAPHY:
2263 struct layout_range_iface *range;
2265 range = heap_alloc(sizeof(*range));
2266 if (!range) return NULL;
2268 range->iface = NULL;
2269 h = &range->h;
2270 break;
2272 case LAYOUT_RANGE_SPACING:
2274 struct layout_range_spacing *range;
2276 range = heap_alloc(sizeof(*range));
2277 if (!range) return NULL;
2279 range->leading = 0.0f;
2280 range->trailing = 0.0f;
2281 range->min_advance = 0.0f;
2282 h = &range->h;
2283 break;
2285 default:
2286 FIXME("unknown range kind %d\n", kind);
2287 return NULL;
2290 h->kind = kind;
2291 h->range = *r;
2292 return h;
2295 static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
2297 struct layout_range_header *ret;
2299 switch (h->kind)
2301 case LAYOUT_RANGE_REGULAR:
2303 struct layout_range *from = (struct layout_range*)h;
2305 struct layout_range *range = heap_alloc(sizeof(*range));
2306 if (!range) return NULL;
2308 *range = *from;
2309 range->fontfamily = heap_strdupW(from->fontfamily);
2310 if (!range->fontfamily) {
2311 heap_free(range);
2312 return NULL;
2315 /* update refcounts */
2316 if (range->object)
2317 IDWriteInlineObject_AddRef(range->object);
2318 if (range->collection)
2319 IDWriteFontCollection_AddRef(range->collection);
2320 ret = &range->h;
2321 break;
2323 case LAYOUT_RANGE_UNDERLINE:
2324 case LAYOUT_RANGE_STRIKETHROUGH:
2326 struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
2327 if (!strike) return NULL;
2329 *strike = *(struct layout_range_bool*)h;
2330 ret = &strike->h;
2331 break;
2333 case LAYOUT_RANGE_EFFECT:
2334 case LAYOUT_RANGE_TYPOGRAPHY:
2336 struct layout_range_iface *effect = heap_alloc(sizeof(*effect));
2337 if (!effect) return NULL;
2339 *effect = *(struct layout_range_iface*)h;
2340 if (effect->iface)
2341 IUnknown_AddRef(effect->iface);
2342 ret = &effect->h;
2343 break;
2345 case LAYOUT_RANGE_SPACING:
2347 struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
2348 if (!spacing) return NULL;
2350 *spacing = *(struct layout_range_spacing*)h;
2351 ret = &spacing->h;
2352 break;
2354 default:
2355 FIXME("unknown range kind %d\n", h->kind);
2356 return NULL;
2359 ret->range = *r;
2360 return ret;
2363 static void free_layout_range(struct layout_range_header *h)
2365 if (!h)
2366 return;
2368 switch (h->kind)
2370 case LAYOUT_RANGE_REGULAR:
2372 struct layout_range *range = (struct layout_range*)h;
2374 if (range->object)
2375 IDWriteInlineObject_Release(range->object);
2376 if (range->collection)
2377 IDWriteFontCollection_Release(range->collection);
2378 heap_free(range->fontfamily);
2379 break;
2381 case LAYOUT_RANGE_EFFECT:
2382 case LAYOUT_RANGE_TYPOGRAPHY:
2384 struct layout_range_iface *range = (struct layout_range_iface*)h;
2385 if (range->iface)
2386 IUnknown_Release(range->iface);
2387 break;
2389 default:
2393 heap_free(h);
2396 static void free_layout_ranges_list(struct dwrite_textlayout *layout)
2398 struct layout_range_header *cur, *cur2;
2400 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
2401 list_remove(&cur->entry);
2402 free_layout_range(cur);
2405 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) {
2406 list_remove(&cur->entry);
2407 free_layout_range(cur);
2410 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
2411 list_remove(&cur->entry);
2412 free_layout_range(cur);
2415 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
2416 list_remove(&cur->entry);
2417 free_layout_range(cur);
2420 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
2421 list_remove(&cur->entry);
2422 free_layout_range(cur);
2425 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) {
2426 list_remove(&cur->entry);
2427 free_layout_range(cur);
2431 static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
2433 struct layout_range_header *cur;
2435 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
2437 if (cur->range.startPosition > range->startPosition)
2438 return NULL;
2440 if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
2441 (range->startPosition < cur->range.startPosition + cur->range.length))
2442 return NULL;
2443 if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
2444 return cur;
2447 return NULL;
2450 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
2452 struct layout_range *cur;
2454 LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
2455 DWRITE_TEXT_RANGE *r = &cur->h.range;
2456 if (r->startPosition <= pos && pos < r->startPosition + r->length)
2457 return cur;
2460 return NULL;
2463 static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
2465 if (*dest == value) return FALSE;
2467 if (*dest)
2468 IUnknown_Release(*dest);
2469 *dest = value;
2470 if (*dest)
2471 IUnknown_AddRef(*dest);
2473 return TRUE;
2476 static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2478 struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
2479 struct layout_range_iface *dest_iface = (struct layout_range_iface*)h;
2480 struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
2481 struct layout_range *dest = (struct layout_range*)h;
2483 BOOL changed = FALSE;
2485 switch (attr) {
2486 case LAYOUT_RANGE_ATTR_WEIGHT:
2487 changed = dest->weight != value->u.weight;
2488 dest->weight = value->u.weight;
2489 break;
2490 case LAYOUT_RANGE_ATTR_STYLE:
2491 changed = dest->style != value->u.style;
2492 dest->style = value->u.style;
2493 break;
2494 case LAYOUT_RANGE_ATTR_STRETCH:
2495 changed = dest->stretch != value->u.stretch;
2496 dest->stretch = value->u.stretch;
2497 break;
2498 case LAYOUT_RANGE_ATTR_FONTSIZE:
2499 changed = dest->fontsize != value->u.fontsize;
2500 dest->fontsize = value->u.fontsize;
2501 break;
2502 case LAYOUT_RANGE_ATTR_INLINE:
2503 changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
2504 break;
2505 case LAYOUT_RANGE_ATTR_EFFECT:
2506 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.effect);
2507 break;
2508 case LAYOUT_RANGE_ATTR_UNDERLINE:
2509 changed = dest_bool->value != value->u.underline;
2510 dest_bool->value = value->u.underline;
2511 break;
2512 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2513 changed = dest_bool->value != value->u.strikethrough;
2514 dest_bool->value = value->u.strikethrough;
2515 break;
2516 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2517 changed = dest->pair_kerning != value->u.pair_kerning;
2518 dest->pair_kerning = value->u.pair_kerning;
2519 break;
2520 case LAYOUT_RANGE_ATTR_FONTCOLL:
2521 changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
2522 break;
2523 case LAYOUT_RANGE_ATTR_LOCALE:
2524 changed = strcmpiW(dest->locale, value->u.locale) != 0;
2525 if (changed) {
2526 strcpyW(dest->locale, value->u.locale);
2527 strlwrW(dest->locale);
2529 break;
2530 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2531 changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
2532 if (changed) {
2533 heap_free(dest->fontfamily);
2534 dest->fontfamily = heap_strdupW(value->u.fontfamily);
2536 break;
2537 case LAYOUT_RANGE_ATTR_SPACING:
2538 changed = dest_spacing->leading != value->u.spacing.leading ||
2539 dest_spacing->trailing != value->u.spacing.trailing ||
2540 dest_spacing->min_advance != value->u.spacing.min_advance;
2541 dest_spacing->leading = value->u.spacing.leading;
2542 dest_spacing->trailing = value->u.spacing.trailing;
2543 dest_spacing->min_advance = value->u.spacing.min_advance;
2544 break;
2545 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2546 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.typography);
2547 break;
2548 default:
2552 return changed;
2555 static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
2557 return (inner->startPosition >= outer->startPosition) &&
2558 (inner->startPosition + inner->length <= outer->startPosition + outer->length);
2561 static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
2563 if (r) *r = h->range;
2564 return S_OK;
2567 /* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */
2568 static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2570 struct layout_range_header *cur, *right, *left, *outer;
2571 BOOL changed = FALSE;
2572 struct list *ranges;
2573 DWRITE_TEXT_RANGE r;
2575 /* ignore zero length ranges */
2576 if (value->range.length == 0)
2577 return S_OK;
2579 /* select from ranges lists */
2580 switch (attr)
2582 case LAYOUT_RANGE_ATTR_WEIGHT:
2583 case LAYOUT_RANGE_ATTR_STYLE:
2584 case LAYOUT_RANGE_ATTR_STRETCH:
2585 case LAYOUT_RANGE_ATTR_FONTSIZE:
2586 case LAYOUT_RANGE_ATTR_INLINE:
2587 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2588 case LAYOUT_RANGE_ATTR_FONTCOLL:
2589 case LAYOUT_RANGE_ATTR_LOCALE:
2590 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2591 ranges = &layout->ranges;
2592 break;
2593 case LAYOUT_RANGE_ATTR_UNDERLINE:
2594 ranges = &layout->underline_ranges;
2595 break;
2596 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2597 ranges = &layout->strike_ranges;
2598 break;
2599 case LAYOUT_RANGE_ATTR_EFFECT:
2600 ranges = &layout->effects;
2601 break;
2602 case LAYOUT_RANGE_ATTR_SPACING:
2603 ranges = &layout->spacing;
2604 break;
2605 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2606 ranges = &layout->typographies;
2607 break;
2608 default:
2609 FIXME("unknown attr kind %d\n", attr);
2610 return E_FAIL;
2613 /* If new range is completely within existing range, split existing range in two */
2614 if ((outer = find_outer_range(ranges, &value->range))) {
2616 /* no need to add same range */
2617 if (is_same_layout_attrvalue(outer, attr, value))
2618 return S_OK;
2620 /* for matching range bounds just replace data */
2621 if (is_same_text_range(&outer->range, &value->range)) {
2622 changed = set_layout_range_attrval(outer, attr, value);
2623 goto done;
2626 /* add new range to the left */
2627 if (value->range.startPosition == outer->range.startPosition) {
2628 left = alloc_layout_range_from(outer, &value->range);
2629 if (!left) return E_OUTOFMEMORY;
2631 changed = set_layout_range_attrval(left, attr, value);
2632 list_add_before(&outer->entry, &left->entry);
2633 outer->range.startPosition += value->range.length;
2634 outer->range.length -= value->range.length;
2635 goto done;
2638 /* add new range to the right */
2639 if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
2640 right = alloc_layout_range_from(outer, &value->range);
2641 if (!right) return E_OUTOFMEMORY;
2643 changed = set_layout_range_attrval(right, attr, value);
2644 list_add_after(&outer->entry, &right->entry);
2645 outer->range.length -= value->range.length;
2646 goto done;
2649 r.startPosition = value->range.startPosition + value->range.length;
2650 r.length = outer->range.length + outer->range.startPosition - r.startPosition;
2652 /* right part */
2653 right = alloc_layout_range_from(outer, &r);
2654 /* new range in the middle */
2655 cur = alloc_layout_range_from(outer, &value->range);
2656 if (!right || !cur) {
2657 free_layout_range(right);
2658 free_layout_range(cur);
2659 return E_OUTOFMEMORY;
2662 /* reuse container range as a left part */
2663 outer->range.length = value->range.startPosition - outer->range.startPosition;
2665 /* new part */
2666 set_layout_range_attrval(cur, attr, value);
2668 list_add_after(&outer->entry, &cur->entry);
2669 list_add_after(&cur->entry, &right->entry);
2671 layout->recompute = RECOMPUTE_EVERYTHING;
2672 return S_OK;
2675 /* Now it's only possible that given range contains some existing ranges, fully or partially.
2676 Update all of them. */
2677 left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
2678 if (left->range.startPosition == value->range.startPosition)
2679 changed = set_layout_range_attrval(left, attr, value);
2680 else /* need to split */ {
2681 r.startPosition = value->range.startPosition;
2682 r.length = left->range.length - value->range.startPosition + left->range.startPosition;
2683 left->range.length -= r.length;
2684 cur = alloc_layout_range_from(left, &r);
2685 changed = set_layout_range_attrval(cur, attr, value);
2686 list_add_after(&left->entry, &cur->entry);
2688 cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
2690 /* for all existing ranges covered by new one update value */
2691 while (cur && is_in_layout_range(&value->range, &cur->range)) {
2692 changed |= set_layout_range_attrval(cur, attr, value);
2693 cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
2696 /* it's possible rightmost range intersects */
2697 if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
2698 r.startPosition = cur->range.startPosition;
2699 r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
2700 left = alloc_layout_range_from(cur, &r);
2701 changed |= set_layout_range_attrval(left, attr, value);
2702 cur->range.startPosition += left->range.length;
2703 cur->range.length -= left->range.length;
2704 list_add_before(&cur->entry, &left->entry);
2707 done:
2708 if (changed) {
2709 struct list *next, *i;
2711 layout->recompute = RECOMPUTE_EVERYTHING;
2712 i = list_head(ranges);
2713 while ((next = list_next(ranges, i))) {
2714 struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
2716 cur = LIST_ENTRY(i, struct layout_range_header, entry);
2717 if (is_same_layout_attributes(cur, next_range)) {
2718 /* remove similar range */
2719 cur->range.length += next_range->range.length;
2720 list_remove(next);
2721 free_layout_range(next_range);
2723 else
2724 i = list_next(ranges, i);
2728 return S_OK;
2731 static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
2733 const WCHAR *str;
2735 switch (kind) {
2736 case LAYOUT_RANGE_ATTR_LOCALE:
2737 str = range->locale;
2738 break;
2739 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2740 str = range->fontfamily;
2741 break;
2742 default:
2743 str = NULL;
2746 return str;
2749 static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2750 UINT32 *length, DWRITE_TEXT_RANGE *r)
2752 struct layout_range *range;
2753 const WCHAR *str;
2755 range = get_layout_range_by_pos(layout, position);
2756 if (!range) {
2757 *length = 0;
2758 return S_OK;
2761 str = get_string_attribute_ptr(range, kind);
2762 *length = strlenW(str);
2763 return return_range(&range->h, r);
2766 static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2767 WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
2769 struct layout_range *range;
2770 const WCHAR *str;
2772 if (length == 0)
2773 return E_INVALIDARG;
2775 ret[0] = 0;
2776 range = get_layout_range_by_pos(layout, position);
2777 if (!range)
2778 return E_INVALIDARG;
2780 str = get_string_attribute_ptr(range, kind);
2781 if (length < strlenW(str) + 1)
2782 return E_NOT_SUFFICIENT_BUFFER;
2784 strcpyW(ret, str);
2785 return return_range(&range->h, r);
2788 static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout3 *iface, REFIID riid, void **obj)
2790 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2792 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2794 *obj = NULL;
2796 if (IsEqualIID(riid, &IID_IDWriteTextLayout3) ||
2797 IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
2798 IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
2799 IsEqualIID(riid, &IID_IDWriteTextLayout) ||
2800 IsEqualIID(riid, &IID_IUnknown))
2802 *obj = iface;
2804 else if (IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
2805 IsEqualIID(riid, &IID_IDWriteTextFormat))
2806 *obj = &This->IDWriteTextFormat1_iface;
2808 if (*obj) {
2809 IDWriteTextLayout3_AddRef(iface);
2810 return S_OK;
2813 return E_NOINTERFACE;
2816 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface)
2818 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2819 ULONG ref = InterlockedIncrement(&This->ref);
2820 TRACE("(%p)->(%d)\n", This, ref);
2821 return ref;
2824 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface)
2826 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2827 ULONG ref = InterlockedDecrement(&This->ref);
2829 TRACE("(%p)->(%d)\n", This, ref);
2831 if (!ref) {
2832 IDWriteFactory5_Release(This->factory);
2833 free_layout_ranges_list(This);
2834 free_layout_eruns(This);
2835 free_layout_runs(This);
2836 release_format_data(&This->format);
2837 heap_free(This->nominal_breakpoints);
2838 heap_free(This->actual_breakpoints);
2839 heap_free(This->clustermetrics);
2840 heap_free(This->clusters);
2841 heap_free(This->linemetrics);
2842 heap_free(This->lines);
2843 heap_free(This->str);
2844 heap_free(This);
2847 return ref;
2850 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2852 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2853 return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment);
2856 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
2858 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2859 return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment);
2862 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping)
2864 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2865 return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping);
2868 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction)
2870 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2871 return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction);
2874 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction)
2876 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2877 TRACE("(%p)->(%d)\n", This, direction);
2878 return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction);
2881 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop)
2883 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2884 TRACE("(%p)->(%.2f)\n", This, tabstop);
2885 return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop);
2888 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming,
2889 IDWriteInlineObject *trimming_sign)
2891 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2892 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
2893 return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign);
2896 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2897 FLOAT line_spacing, FLOAT baseline)
2899 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2900 TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
2901 return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline);
2904 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface)
2906 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2907 return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface);
2910 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface)
2912 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2913 return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface);
2916 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface)
2918 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2919 return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface);
2922 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface)
2924 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2925 return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface);
2928 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface)
2930 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2931 return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface);
2934 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface)
2936 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2937 return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface);
2940 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options,
2941 IDWriteInlineObject **trimming_sign)
2943 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2944 return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign);
2947 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method,
2948 FLOAT *spacing, FLOAT *baseline)
2950 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2951 return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat*)&This->IDWriteTextFormat1_iface, method, spacing, baseline);
2954 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection)
2956 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2957 return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection);
2960 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface)
2962 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2963 return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface);
2966 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
2968 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2969 return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size);
2972 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface)
2974 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2975 return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface);
2978 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface)
2980 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2981 return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface);
2984 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface)
2986 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2987 return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface);
2990 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface)
2992 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2993 return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface);
2996 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface)
2998 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2999 return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface);
3002 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
3004 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3005 return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size);
3008 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth)
3010 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3011 BOOL changed;
3013 TRACE("(%p)->(%.2f)\n", This, maxWidth);
3015 if (maxWidth < 0.0f)
3016 return E_INVALIDARG;
3018 changed = This->metrics.layoutWidth != maxWidth;
3019 This->metrics.layoutWidth = maxWidth;
3021 if (changed)
3022 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3023 return S_OK;
3026 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight)
3028 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3029 BOOL changed;
3031 TRACE("(%p)->(%.2f)\n", This, maxHeight);
3033 if (maxHeight < 0.0f)
3034 return E_INVALIDARG;
3036 changed = This->metrics.layoutHeight != maxHeight;
3037 This->metrics.layoutHeight = maxHeight;
3039 if (changed)
3040 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3041 return S_OK;
3044 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
3046 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3047 struct layout_range_attr_value value;
3049 TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
3051 value.range = range;
3052 value.u.collection = collection;
3053 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
3056 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
3058 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3059 struct layout_range_attr_value value;
3061 TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
3063 if (!name)
3064 return E_INVALIDARG;
3066 value.range = range;
3067 value.u.fontfamily = name;
3068 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
3071 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
3073 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3074 struct layout_range_attr_value value;
3076 TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
3078 if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
3079 return E_INVALIDARG;
3081 value.range = range;
3082 value.u.weight = weight;
3083 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
3086 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
3088 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3089 struct layout_range_attr_value value;
3091 TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
3093 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
3094 return E_INVALIDARG;
3096 value.range = range;
3097 value.u.style = style;
3098 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
3101 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
3103 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3104 struct layout_range_attr_value value;
3106 TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
3108 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
3109 return E_INVALIDARG;
3111 value.range = range;
3112 value.u.stretch = stretch;
3113 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
3116 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
3118 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3119 struct layout_range_attr_value value;
3121 TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
3123 if (size <= 0.0f)
3124 return E_INVALIDARG;
3126 value.range = range;
3127 value.u.fontsize = size;
3128 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
3131 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
3133 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3134 struct layout_range_attr_value value;
3136 TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
3138 value.range = range;
3139 value.u.underline = underline;
3140 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
3143 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
3145 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3146 struct layout_range_attr_value value;
3148 TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
3150 value.range = range;
3151 value.u.strikethrough = strikethrough;
3152 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
3155 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
3157 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3158 struct layout_range_attr_value value;
3160 TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
3162 value.range = range;
3163 value.u.effect = effect;
3164 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
3167 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
3169 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3170 struct layout_range_attr_value value;
3172 TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
3174 value.range = range;
3175 value.u.object = object;
3176 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
3179 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
3181 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3182 struct layout_range_attr_value value;
3184 TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range));
3186 value.range = range;
3187 value.u.typography = typography;
3188 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
3191 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
3193 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3194 struct layout_range_attr_value value;
3196 TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
3198 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
3199 return E_INVALIDARG;
3201 value.range = range;
3202 value.u.locale = locale;
3203 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
3206 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface)
3208 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3209 TRACE("(%p)\n", This);
3210 return This->metrics.layoutWidth;
3213 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface)
3215 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3216 TRACE("(%p)\n", This);
3217 return This->metrics.layoutHeight;
3220 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position,
3221 IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
3223 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3224 struct layout_range *range;
3226 TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
3228 if (position >= This->len)
3229 return S_OK;
3231 range = get_layout_range_by_pos(This, position);
3232 *collection = range->collection;
3233 if (*collection)
3234 IDWriteFontCollection_AddRef(*collection);
3236 return return_range(&range->h, r);
3239 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface,
3240 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3242 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3243 TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
3244 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
3247 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface,
3248 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
3250 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3251 TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
3252 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
3255 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface,
3256 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
3258 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3259 struct layout_range *range;
3261 TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
3263 if (position >= This->len)
3264 return S_OK;
3266 range = get_layout_range_by_pos(This, position);
3267 *weight = range->weight;
3269 return return_range(&range->h, r);
3272 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface,
3273 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
3275 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3276 struct layout_range *range;
3278 TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
3280 range = get_layout_range_by_pos(This, position);
3281 *style = range->style;
3282 return return_range(&range->h, r);
3285 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface,
3286 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
3288 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3289 struct layout_range *range;
3291 TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
3293 range = get_layout_range_by_pos(This, position);
3294 *stretch = range->stretch;
3295 return return_range(&range->h, r);
3298 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface,
3299 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
3301 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3302 struct layout_range *range;
3304 TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
3306 range = get_layout_range_by_pos(This, position);
3307 *size = range->fontsize;
3308 return return_range(&range->h, r);
3311 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface,
3312 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
3314 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3315 struct layout_range_bool *range;
3317 TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
3319 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position);
3320 *underline = range->value;
3322 return return_range(&range->h, r);
3325 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface,
3326 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
3328 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3329 struct layout_range_bool *range;
3331 TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
3333 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
3334 *strikethrough = range->value;
3336 return return_range(&range->h, r);
3339 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface,
3340 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
3342 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3343 struct layout_range_iface *range;
3345 TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
3347 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position);
3348 *effect = range->iface;
3349 if (*effect)
3350 IUnknown_AddRef(*effect);
3352 return return_range(&range->h, r);
3355 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface,
3356 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
3358 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3359 struct layout_range *range;
3361 TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
3363 if (position >= This->len)
3364 return S_OK;
3366 range = get_layout_range_by_pos(This, position);
3367 *object = range->object;
3368 if (*object)
3369 IDWriteInlineObject_AddRef(*object);
3371 return return_range(&range->h, r);
3374 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface,
3375 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
3377 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3378 struct layout_range_iface *range;
3380 TRACE("(%p)->(%u %p %p)\n", This, position, typography, r);
3382 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position);
3383 *typography = (IDWriteTypography*)range->iface;
3384 if (*typography)
3385 IDWriteTypography_AddRef(*typography);
3387 return return_range(&range->h, r);
3390 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface,
3391 UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
3393 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3394 TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
3395 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
3398 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface,
3399 UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
3401 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3402 TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
3403 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
3406 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
3407 const DWRITE_MATRIX *m)
3409 D2D1_POINT_2F vec, vec2;
3411 if (!skiptransform) {
3412 /* apply transform */
3413 vec.x = 0.0f;
3414 vec.y = coord * ppdip;
3416 vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx;
3417 vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy;
3419 /* snap */
3420 vec2.x = floorf(vec2.x + 0.5f);
3421 vec2.y = floorf(vec2.y + 0.5f);
3423 /* apply inverted transform, we don't care about X component at this point */
3424 vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3425 vec.y /= ppdip;
3427 else
3428 vec.y = floorf(coord * ppdip + 0.5f) / ppdip;
3430 return vec.y;
3433 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface,
3434 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
3436 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3437 BOOL disabled = FALSE, skiptransform = FALSE;
3438 struct layout_effective_inline *inlineobject;
3439 struct layout_effective_run *run;
3440 struct layout_strikethrough *s;
3441 struct layout_underline *u;
3442 FLOAT det = 0.0f, ppdip = 0.0f;
3443 DWRITE_MATRIX m = { 0 };
3444 HRESULT hr;
3446 TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
3448 hr = layout_compute_effective_runs(This);
3449 if (FAILED(hr))
3450 return hr;
3452 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
3453 if (FAILED(hr))
3454 return hr;
3456 if (!disabled) {
3457 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
3458 if (FAILED(hr))
3459 return hr;
3461 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
3462 if (FAILED(hr))
3463 return hr;
3465 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
3466 if (ppdip <= 0.0f ||
3467 (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
3468 (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
3469 disabled = TRUE;
3470 else
3471 skiptransform = should_skip_transform(&m, &det);
3474 #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
3475 /* 1. Regular runs */
3476 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3477 const struct regular_layout_run *regular = &run->run->u.regular;
3478 UINT32 start_glyph = regular->clustermap[run->start];
3479 DWRITE_GLYPH_RUN_DESCRIPTION descr;
3480 DWRITE_GLYPH_RUN glyph_run;
3482 /* Everything but cluster map will be reused from nominal run, as we only need
3483 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
3484 it can't be reused because it has to start with 0 index for each reported run. */
3485 glyph_run = regular->run;
3486 glyph_run.glyphCount = run->glyphcount;
3488 /* fixup glyph data arrays */
3489 glyph_run.glyphIndices += start_glyph;
3490 glyph_run.glyphAdvances += start_glyph;
3491 glyph_run.glyphOffsets += start_glyph;
3493 /* description */
3494 descr = regular->descr;
3495 descr.stringLength = run->length;
3496 descr.string += run->start;
3497 descr.clusterMap = run->clustermap;
3498 descr.textPosition += run->start;
3500 /* return value is ignored */
3501 IDWriteTextRenderer_DrawGlyphRun(renderer,
3502 context,
3503 run->origin.x + run->align_dx + origin_x,
3504 SNAP_COORD(run->origin.y + origin_y),
3505 This->measuringmode,
3506 &glyph_run,
3507 &descr,
3508 run->effect);
3511 /* 2. Inline objects */
3512 LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
3513 IDWriteTextRenderer_DrawInlineObject(renderer,
3514 context,
3515 inlineobject->origin.x + inlineobject->align_dx + origin_x,
3516 SNAP_COORD(inlineobject->origin.y + origin_y),
3517 inlineobject->object,
3518 inlineobject->is_sideways,
3519 inlineobject->is_rtl,
3520 inlineobject->effect);
3523 /* 3. Underlines */
3524 LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) {
3525 IDWriteTextRenderer_DrawUnderline(renderer,
3526 context,
3527 /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
3528 (is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x,
3529 SNAP_COORD(u->run->origin.y + origin_y),
3530 &u->u,
3531 u->run->effect);
3534 /* 4. Strikethrough */
3535 LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
3536 IDWriteTextRenderer_DrawStrikethrough(renderer,
3537 context,
3538 s->run->origin.x + s->run->align_dx + origin_x,
3539 SNAP_COORD(s->run->origin.y + origin_y),
3540 &s->s,
3541 s->run->effect);
3543 #undef SNAP_COORD
3545 return S_OK;
3548 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface,
3549 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
3551 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3552 HRESULT hr;
3554 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3556 hr = layout_compute_effective_runs(This);
3557 if (FAILED(hr))
3558 return hr;
3560 if (metrics) {
3561 UINT32 i, c = min(max_count, This->metrics.lineCount);
3562 for (i = 0; i < c; i++)
3563 memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics));
3566 *count = This->metrics.lineCount;
3567 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3570 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics)
3572 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3573 DWRITE_TEXT_METRICS1 metrics1;
3574 HRESULT hr;
3576 TRACE("(%p)->(%p)\n", This, metrics);
3578 hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1);
3579 if (hr == S_OK)
3580 memcpy(metrics, &metrics1, sizeof(*metrics));
3582 return hr;
3585 static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D1_RECT_F *ret)
3587 #define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em)
3588 ret->left = SCALE(bbox->left);
3589 ret->right = SCALE(bbox->right);
3590 ret->top = SCALE(bbox->top);
3591 ret->bottom = SCALE(bbox->bottom);
3592 #undef SCALE
3595 static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y)
3597 rect->left += x;
3598 rect->right += x;
3599 rect->top += y;
3600 rect->bottom += y;
3603 static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect)
3605 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
3608 static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src)
3610 if (d2d_rect_is_empty(dst)) {
3611 if (d2d_rect_is_empty(src)) {
3612 dst->left = dst->right = dst->top = dst->bottom = 0.0f;
3613 return;
3615 else
3616 *dst = *src;
3618 else {
3619 if (!d2d_rect_is_empty(src)) {
3620 dst->left = min(dst->left, src->left);
3621 dst->right = max(dst->right, src->right);
3622 dst->top = min(dst->top, src->top);
3623 dst->bottom = max(dst->bottom, src->bottom);
3628 static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox)
3630 const struct regular_layout_run *regular = &run->run->u.regular;
3631 UINT32 start_glyph = regular->clustermap[run->start];
3632 const DWRITE_GLYPH_RUN *glyph_run = &regular->run;
3633 DWRITE_FONT_METRICS font_metrics;
3634 D2D1_POINT_2F origin = { 0 };
3635 UINT32 i;
3637 if (run->bbox.top == run->bbox.bottom) {
3638 IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics);
3640 for (i = 0; i < run->glyphcount; i++) {
3641 D2D1_RECT_F glyph_bbox;
3642 RECT design_bbox;
3644 freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm,
3645 glyph_run->glyphIndices[i + start_glyph], &design_bbox);
3647 scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox);
3648 d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
3649 origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
3650 d2d_rect_union(&run->bbox, &glyph_bbox);
3652 /* FIXME: take care of vertical/rtl */
3653 origin.x += glyph_run->glyphAdvances[i + start_glyph];
3657 *bbox = run->bbox;
3658 d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y);
3661 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface,
3662 DWRITE_OVERHANG_METRICS *overhangs)
3664 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3665 struct layout_effective_run *run;
3666 D2D1_RECT_F bbox = { 0 };
3667 HRESULT hr;
3669 TRACE("(%p)->(%p)\n", This, overhangs);
3671 memset(overhangs, 0, sizeof(*overhangs));
3673 if (!(This->recompute & RECOMPUTE_OVERHANGS)) {
3674 *overhangs = This->overhangs;
3675 return S_OK;
3678 hr = layout_compute_effective_runs(This);
3679 if (FAILED(hr))
3680 return hr;
3682 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3683 D2D1_RECT_F run_bbox;
3685 layout_get_erun_bbox(This, run, &run_bbox);
3686 d2d_rect_union(&bbox, &run_bbox);
3689 /* FIXME: iterate over inline objects too */
3691 /* deltas from text content metrics */
3692 This->overhangs.left = -bbox.left;
3693 This->overhangs.top = -bbox.top;
3694 This->overhangs.right = bbox.right - This->metrics.layoutWidth;
3695 This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight;
3696 This->recompute &= ~RECOMPUTE_OVERHANGS;
3698 *overhangs = This->overhangs;
3700 return S_OK;
3703 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface,
3704 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
3706 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3707 HRESULT hr;
3709 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3711 hr = layout_compute(This);
3712 if (FAILED(hr))
3713 return hr;
3715 if (metrics)
3716 memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
3718 *count = This->cluster_count;
3719 return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3722 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width)
3724 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3725 UINT32 start;
3726 FLOAT width;
3727 HRESULT hr;
3729 TRACE("(%p)->(%p)\n", This, min_width);
3731 if (!min_width)
3732 return E_INVALIDARG;
3734 if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
3735 goto width_done;
3737 *min_width = 0.0f;
3738 hr = layout_compute(This);
3739 if (FAILED(hr))
3740 return hr;
3742 /* Find widest word without emergency breaking between clusters, trailing whitespaces
3743 preceding breaking point do not contribute to word width. */
3744 for (start = 0; start < This->cluster_count;) {
3745 UINT32 end = start, j, next;
3747 /* Last cluster always could be wrapped after. */
3748 while (!This->clustermetrics[end].canWrapLineAfter)
3749 end++;
3750 /* make is so current cluster range that we can wrap after is [start,end) */
3751 end++;
3753 next = end;
3755 /* Ignore trailing whitespace clusters, in case of single space range will
3756 be reduced to empty range, or [start,start+1). */
3757 while (end > start && This->clustermetrics[end-1].isWhitespace)
3758 end--;
3760 /* check if cluster range exceeds last minimal width */
3761 width = 0.0f;
3762 for (j = start; j < end; j++)
3763 width += This->clustermetrics[j].width;
3765 start = next;
3767 if (width > This->minwidth)
3768 This->minwidth = width;
3770 This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3772 width_done:
3773 *min_width = This->minwidth;
3774 return S_OK;
3777 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface,
3778 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3780 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3781 FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
3782 return E_NOTIMPL;
3785 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface,
3786 UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
3788 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3789 FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
3790 return E_NOTIMPL;
3793 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface,
3794 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3795 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3797 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3798 FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
3799 max_metricscount, actual_metricscount);
3800 return E_NOTIMPL;
3803 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled,
3804 DWRITE_TEXT_RANGE range)
3806 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3807 struct layout_range_attr_value value;
3809 TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
3811 value.range = range;
3812 value.u.pair_kerning = !!is_pairkerning_enabled;
3813 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3816 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
3817 DWRITE_TEXT_RANGE *r)
3819 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3820 struct layout_range *range;
3822 TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
3824 if (position >= This->len)
3825 return S_OK;
3827 range = get_layout_range_by_pos(This, position);
3828 *is_pairkerning_enabled = range->pair_kerning;
3830 return return_range(&range->h, r);
3833 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing,
3834 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3836 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3837 struct layout_range_attr_value value;
3839 TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
3841 if (min_advance < 0.0f)
3842 return E_INVALIDARG;
3844 value.range = range;
3845 value.u.spacing.leading = leading;
3846 value.u.spacing.trailing = trailing;
3847 value.u.spacing.min_advance = min_advance;
3848 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
3851 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading,
3852 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3854 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3855 struct layout_range_spacing *range;
3857 TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
3859 range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
3860 *leading = range->leading;
3861 *trailing = range->trailing;
3862 *min_advance = range->min_advance;
3864 return return_range(&range->h, r);
3867 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics)
3869 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3870 HRESULT hr;
3872 TRACE("(%p)->(%p)\n", This, metrics);
3874 hr = layout_compute_effective_runs(This);
3875 if (FAILED(hr))
3876 return hr;
3878 *metrics = This->metrics;
3879 return S_OK;
3882 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3884 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3886 TRACE("(%p)->(%d)\n", This, orientation);
3888 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3889 return E_INVALIDARG;
3891 This->format.vertical_orientation = orientation;
3892 return S_OK;
3895 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface)
3897 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3898 TRACE("(%p)\n", This);
3899 return This->format.vertical_orientation;
3902 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled)
3904 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3905 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
3906 return IDWriteTextFormat1_SetLastLineWrapping(&This->IDWriteTextFormat1_iface, lastline_wrapping_enabled);
3909 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface)
3911 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3912 TRACE("(%p)\n", This);
3913 return IDWriteTextFormat1_GetLastLineWrapping(&This->IDWriteTextFormat1_iface);
3916 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3918 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3919 TRACE("(%p)->(%d)\n", This, alignment);
3920 return IDWriteTextFormat1_SetOpticalAlignment(&This->IDWriteTextFormat1_iface, alignment);
3923 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface)
3925 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3926 TRACE("(%p)\n", This);
3927 return IDWriteTextFormat1_GetOpticalAlignment(&This->IDWriteTextFormat1_iface);
3930 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback)
3932 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3933 TRACE("(%p)->(%p)\n", This, fallback);
3934 return set_fontfallback_for_format(&This->format, fallback);
3937 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback)
3939 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3940 TRACE("(%p)->(%p)\n", This, fallback);
3941 return get_fontfallback_from_format(&This->format, fallback);
3944 static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface)
3946 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3948 TRACE("(%p)\n", This);
3950 This->recompute = RECOMPUTE_EVERYTHING;
3951 return S_OK;
3954 static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing)
3956 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3957 BOOL changed;
3958 HRESULT hr;
3960 TRACE("(%p)->(%p)\n", This, spacing);
3962 hr = format_set_linespacing(&This->format, spacing, &changed);
3963 if (FAILED(hr))
3964 return hr;
3966 if (changed) {
3967 if (!(This->recompute & RECOMPUTE_LINES)) {
3968 UINT32 line;
3970 switch (This->format.spacing.method)
3972 case DWRITE_LINE_SPACING_METHOD_DEFAULT:
3973 for (line = 0; line < This->metrics.lineCount; line++) {
3974 This->linemetrics[line].height = This->lines[line].height;
3975 This->linemetrics[line].baseline = This->lines[line].baseline;
3977 break;
3978 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
3979 for (line = 0; line < This->metrics.lineCount; line++) {
3980 This->linemetrics[line].height = This->format.spacing.height;
3981 This->linemetrics[line].baseline = This->format.spacing.baseline;
3983 break;
3984 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
3985 for (line = 0; line < This->metrics.lineCount; line++) {
3986 This->linemetrics[line].height = This->format.spacing.height * This->lines[line].height;
3987 This->linemetrics[line].baseline = This->format.spacing.baseline * This->lines[line].baseline;
3989 break;
3990 default:
3994 layout_set_line_positions(This);
3997 This->recompute |= RECOMPUTE_OVERHANGS;
4000 return S_OK;
4003 static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing)
4005 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4007 TRACE("(%p)->(%p)\n", This, spacing);
4009 *spacing = This->format.spacing;
4010 return S_OK;
4013 static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics,
4014 UINT32 max_count, UINT32 *count)
4016 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4017 HRESULT hr;
4019 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
4021 hr = layout_compute_effective_runs(This);
4022 if (FAILED(hr))
4023 return hr;
4025 if (metrics)
4026 memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount));
4028 *count = This->metrics.lineCount;
4029 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
4032 static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = {
4033 dwritetextlayout_QueryInterface,
4034 dwritetextlayout_AddRef,
4035 dwritetextlayout_Release,
4036 dwritetextlayout_SetTextAlignment,
4037 dwritetextlayout_SetParagraphAlignment,
4038 dwritetextlayout_SetWordWrapping,
4039 dwritetextlayout_SetReadingDirection,
4040 dwritetextlayout_SetFlowDirection,
4041 dwritetextlayout_SetIncrementalTabStop,
4042 dwritetextlayout_SetTrimming,
4043 dwritetextlayout_SetLineSpacing,
4044 dwritetextlayout_GetTextAlignment,
4045 dwritetextlayout_GetParagraphAlignment,
4046 dwritetextlayout_GetWordWrapping,
4047 dwritetextlayout_GetReadingDirection,
4048 dwritetextlayout_GetFlowDirection,
4049 dwritetextlayout_GetIncrementalTabStop,
4050 dwritetextlayout_GetTrimming,
4051 dwritetextlayout_GetLineSpacing,
4052 dwritetextlayout_GetFontCollection,
4053 dwritetextlayout_GetFontFamilyNameLength,
4054 dwritetextlayout_GetFontFamilyName,
4055 dwritetextlayout_GetFontWeight,
4056 dwritetextlayout_GetFontStyle,
4057 dwritetextlayout_GetFontStretch,
4058 dwritetextlayout_GetFontSize,
4059 dwritetextlayout_GetLocaleNameLength,
4060 dwritetextlayout_GetLocaleName,
4061 dwritetextlayout_SetMaxWidth,
4062 dwritetextlayout_SetMaxHeight,
4063 dwritetextlayout_SetFontCollection,
4064 dwritetextlayout_SetFontFamilyName,
4065 dwritetextlayout_SetFontWeight,
4066 dwritetextlayout_SetFontStyle,
4067 dwritetextlayout_SetFontStretch,
4068 dwritetextlayout_SetFontSize,
4069 dwritetextlayout_SetUnderline,
4070 dwritetextlayout_SetStrikethrough,
4071 dwritetextlayout_SetDrawingEffect,
4072 dwritetextlayout_SetInlineObject,
4073 dwritetextlayout_SetTypography,
4074 dwritetextlayout_SetLocaleName,
4075 dwritetextlayout_GetMaxWidth,
4076 dwritetextlayout_GetMaxHeight,
4077 dwritetextlayout_layout_GetFontCollection,
4078 dwritetextlayout_layout_GetFontFamilyNameLength,
4079 dwritetextlayout_layout_GetFontFamilyName,
4080 dwritetextlayout_layout_GetFontWeight,
4081 dwritetextlayout_layout_GetFontStyle,
4082 dwritetextlayout_layout_GetFontStretch,
4083 dwritetextlayout_layout_GetFontSize,
4084 dwritetextlayout_GetUnderline,
4085 dwritetextlayout_GetStrikethrough,
4086 dwritetextlayout_GetDrawingEffect,
4087 dwritetextlayout_GetInlineObject,
4088 dwritetextlayout_GetTypography,
4089 dwritetextlayout_layout_GetLocaleNameLength,
4090 dwritetextlayout_layout_GetLocaleName,
4091 dwritetextlayout_Draw,
4092 dwritetextlayout_GetLineMetrics,
4093 dwritetextlayout_GetMetrics,
4094 dwritetextlayout_GetOverhangMetrics,
4095 dwritetextlayout_GetClusterMetrics,
4096 dwritetextlayout_DetermineMinWidth,
4097 dwritetextlayout_HitTestPoint,
4098 dwritetextlayout_HitTestTextPosition,
4099 dwritetextlayout_HitTestTextRange,
4100 dwritetextlayout1_SetPairKerning,
4101 dwritetextlayout1_GetPairKerning,
4102 dwritetextlayout1_SetCharacterSpacing,
4103 dwritetextlayout1_GetCharacterSpacing,
4104 dwritetextlayout2_GetMetrics,
4105 dwritetextlayout2_SetVerticalGlyphOrientation,
4106 dwritetextlayout2_GetVerticalGlyphOrientation,
4107 dwritetextlayout2_SetLastLineWrapping,
4108 dwritetextlayout2_GetLastLineWrapping,
4109 dwritetextlayout2_SetOpticalAlignment,
4110 dwritetextlayout2_GetOpticalAlignment,
4111 dwritetextlayout2_SetFontFallback,
4112 dwritetextlayout2_GetFontFallback,
4113 dwritetextlayout3_InvalidateLayout,
4114 dwritetextlayout3_SetLineSpacing,
4115 dwritetextlayout3_GetLineSpacing,
4116 dwritetextlayout3_GetLineMetrics
4119 static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
4121 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4122 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4123 return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj);
4126 static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat1 *iface)
4128 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4129 return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface);
4132 static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat1 *iface)
4134 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4135 return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface);
4138 static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
4140 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4141 BOOL changed;
4142 HRESULT hr;
4144 TRACE("(%p)->(%d)\n", This, alignment);
4146 hr = format_set_textalignment(&This->format, alignment, &changed);
4147 if (FAILED(hr))
4148 return hr;
4150 if (changed) {
4151 /* if layout is not ready there's nothing to align */
4152 if (!(This->recompute & RECOMPUTE_LINES))
4153 layout_apply_text_alignment(This);
4154 This->recompute |= RECOMPUTE_OVERHANGS;
4157 return S_OK;
4160 static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
4162 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4163 BOOL changed;
4164 HRESULT hr;
4166 TRACE("(%p)->(%d)\n", This, alignment);
4168 hr = format_set_paralignment(&This->format, alignment, &changed);
4169 if (FAILED(hr))
4170 return hr;
4172 if (changed) {
4173 /* if layout is not ready there's nothing to align */
4174 if (!(This->recompute & RECOMPUTE_LINES))
4175 layout_apply_par_alignment(This);
4176 This->recompute |= RECOMPUTE_OVERHANGS;
4179 return S_OK;
4182 static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
4184 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4185 BOOL changed;
4186 HRESULT hr;
4188 TRACE("(%p)->(%d)\n", This, wrapping);
4190 hr = format_set_wordwrapping(&This->format, wrapping, &changed);
4191 if (FAILED(hr))
4192 return hr;
4194 if (changed)
4195 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4197 return S_OK;
4200 static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
4202 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4203 BOOL changed;
4204 HRESULT hr;
4206 TRACE("(%p)->(%d)\n", This, direction);
4208 hr = format_set_readingdirection(&This->format, direction, &changed);
4209 if (FAILED(hr))
4210 return hr;
4212 if (changed)
4213 This->recompute = RECOMPUTE_EVERYTHING;
4215 return S_OK;
4218 static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
4220 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4221 BOOL changed;
4222 HRESULT hr;
4224 TRACE("(%p)->(%d)\n", This, direction);
4226 hr = format_set_flowdirection(&This->format, direction, &changed);
4227 if (FAILED(hr))
4228 return hr;
4230 if (changed)
4231 This->recompute = RECOMPUTE_EVERYTHING;
4233 return S_OK;
4236 static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
4238 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4239 FIXME("(%p)->(%f): stub\n", This, tabstop);
4240 return E_NOTIMPL;
4243 static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
4244 IDWriteInlineObject *trimming_sign)
4246 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4247 BOOL changed;
4248 HRESULT hr;
4250 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
4252 hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed);
4254 if (changed)
4255 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4257 return hr;
4260 static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method,
4261 FLOAT height, FLOAT baseline)
4263 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4264 DWRITE_LINE_SPACING spacing;
4266 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
4268 spacing = This->format.spacing;
4269 spacing.method = method;
4270 spacing.height = height;
4271 spacing.baseline = baseline;
4272 return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing);
4275 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat1 *iface)
4277 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4278 TRACE("(%p)\n", This);
4279 return This->format.textalignment;
4282 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface)
4284 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4285 TRACE("(%p)\n", This);
4286 return This->format.paralign;
4289 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat1 *iface)
4291 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4292 TRACE("(%p)\n", This);
4293 return This->format.wrapping;
4296 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat1 *iface)
4298 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4299 TRACE("(%p)\n", This);
4300 return This->format.readingdir;
4303 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat1 *iface)
4305 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4306 TRACE("(%p)\n", This);
4307 return This->format.flow;
4310 static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
4312 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4313 FIXME("(%p): stub\n", This);
4314 return 0.0f;
4317 static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
4318 IDWriteInlineObject **trimming_sign)
4320 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4322 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
4324 *options = This->format.trimming;
4325 *trimming_sign = This->format.trimmingsign;
4326 if (*trimming_sign)
4327 IDWriteInlineObject_AddRef(*trimming_sign);
4328 return S_OK;
4331 static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
4332 FLOAT *spacing, FLOAT *baseline)
4334 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4336 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
4338 *method = This->format.spacing.method;
4339 *spacing = This->format.spacing.height;
4340 *baseline = This->format.spacing.baseline;
4341 return S_OK;
4344 static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
4346 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4348 TRACE("(%p)->(%p)\n", This, collection);
4350 *collection = This->format.collection;
4351 if (*collection)
4352 IDWriteFontCollection_AddRef(*collection);
4353 return S_OK;
4356 static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
4358 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4359 TRACE("(%p)\n", This);
4360 return This->format.family_len;
4363 static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4365 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4367 TRACE("(%p)->(%p %u)\n", This, name, size);
4369 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4370 strcpyW(name, This->format.family_name);
4371 return S_OK;
4374 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat1 *iface)
4376 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4377 TRACE("(%p)\n", This);
4378 return This->format.weight;
4381 static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat1 *iface)
4383 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4384 TRACE("(%p)\n", This);
4385 return This->format.style;
4388 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat1 *iface)
4390 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4391 TRACE("(%p)\n", This);
4392 return This->format.stretch;
4395 static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat1 *iface)
4397 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4398 TRACE("(%p)\n", This);
4399 return This->format.fontsize;
4402 static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface)
4404 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4405 TRACE("(%p)\n", This);
4406 return This->format.locale_len;
4409 static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4411 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4413 TRACE("(%p)->(%p %u)\n", This, name, size);
4415 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4416 strcpyW(name, This->format.locale);
4417 return S_OK;
4420 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4422 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4423 FIXME("(%p)->(%d): stub\n", This, orientation);
4424 return E_NOTIMPL;
4427 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
4429 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4430 FIXME("(%p): stub\n", This);
4431 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4434 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
4436 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4438 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
4440 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
4441 return S_OK;
4444 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface)
4446 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4447 TRACE("(%p)\n", This);
4448 return This->format.last_line_wrapping;
4451 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
4453 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4454 TRACE("(%p)->(%d)\n", This, alignment);
4455 return format_set_optical_alignment(&This->format, alignment);
4458 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface)
4460 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4461 TRACE("(%p)\n", This);
4462 return This->format.optical_alignment;
4465 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
4467 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4468 TRACE("(%p)->(%p)\n", This, fallback);
4469 return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4472 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
4474 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4475 TRACE("(%p)->(%p)\n", This, fallback);
4476 return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4479 static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = {
4480 dwritetextformat_layout_QueryInterface,
4481 dwritetextformat_layout_AddRef,
4482 dwritetextformat_layout_Release,
4483 dwritetextformat_layout_SetTextAlignment,
4484 dwritetextformat_layout_SetParagraphAlignment,
4485 dwritetextformat_layout_SetWordWrapping,
4486 dwritetextformat_layout_SetReadingDirection,
4487 dwritetextformat_layout_SetFlowDirection,
4488 dwritetextformat_layout_SetIncrementalTabStop,
4489 dwritetextformat_layout_SetTrimming,
4490 dwritetextformat_layout_SetLineSpacing,
4491 dwritetextformat_layout_GetTextAlignment,
4492 dwritetextformat_layout_GetParagraphAlignment,
4493 dwritetextformat_layout_GetWordWrapping,
4494 dwritetextformat_layout_GetReadingDirection,
4495 dwritetextformat_layout_GetFlowDirection,
4496 dwritetextformat_layout_GetIncrementalTabStop,
4497 dwritetextformat_layout_GetTrimming,
4498 dwritetextformat_layout_GetLineSpacing,
4499 dwritetextformat_layout_GetFontCollection,
4500 dwritetextformat_layout_GetFontFamilyNameLength,
4501 dwritetextformat_layout_GetFontFamilyName,
4502 dwritetextformat_layout_GetFontWeight,
4503 dwritetextformat_layout_GetFontStyle,
4504 dwritetextformat_layout_GetFontStretch,
4505 dwritetextformat_layout_GetFontSize,
4506 dwritetextformat_layout_GetLocaleNameLength,
4507 dwritetextformat_layout_GetLocaleName,
4508 dwritetextformat1_layout_SetVerticalGlyphOrientation,
4509 dwritetextformat1_layout_GetVerticalGlyphOrientation,
4510 dwritetextformat1_layout_SetLastLineWrapping,
4511 dwritetextformat1_layout_GetLastLineWrapping,
4512 dwritetextformat1_layout_SetOpticalAlignment,
4513 dwritetextformat1_layout_GetOpticalAlignment,
4514 dwritetextformat1_layout_SetFontFallback,
4515 dwritetextformat1_layout_GetFontFallback,
4518 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
4519 REFIID riid, void **obj)
4521 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
4522 IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
4523 IsEqualIID(riid, &IID_IUnknown))
4525 *obj = iface;
4526 IDWriteTextAnalysisSink1_AddRef(iface);
4527 return S_OK;
4530 *obj = NULL;
4531 return E_NOINTERFACE;
4534 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
4536 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4537 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4540 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
4542 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4543 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4546 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
4547 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
4549 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4550 struct layout_run *run;
4552 TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
4554 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position);
4555 if (!run)
4556 return E_OUTOFMEMORY;
4558 run->u.regular.descr.string = &layout->str[position];
4559 run->u.regular.descr.stringLength = length;
4560 run->u.regular.descr.textPosition = position;
4561 run->u.regular.sa = *sa;
4562 list_add_tail(&layout->runs, &run->entry);
4563 return S_OK;
4566 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
4567 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
4569 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4571 if (position + length > layout->len)
4572 return E_FAIL;
4574 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
4575 return S_OK;
4578 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
4579 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
4581 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4582 struct layout_run *cur_run;
4584 TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
4586 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
4587 struct regular_layout_run *cur = &cur_run->u.regular;
4588 struct layout_run *run;
4590 if (cur_run->kind == LAYOUT_RUN_INLINE)
4591 continue;
4593 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
4594 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
4595 continue;
4597 /* full hit - just set run level */
4598 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
4599 cur->run.bidiLevel = resolvedLevel;
4600 break;
4603 /* current run is fully covered, move to next one */
4604 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
4605 cur->run.bidiLevel = resolvedLevel;
4606 position += cur->descr.stringLength;
4607 length -= cur->descr.stringLength;
4608 continue;
4611 /* all fully covered runs are processed at this point, reuse existing run for remaining
4612 reported bidi range and add another run for the rest of original one */
4614 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length);
4615 if (!run)
4616 return E_OUTOFMEMORY;
4618 *run = *cur_run;
4619 run->u.regular.descr.textPosition = position + length;
4620 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
4621 run->u.regular.descr.string = &layout->str[position + length];
4623 /* reduce existing run */
4624 cur->run.bidiLevel = resolvedLevel;
4625 cur->descr.stringLength = length;
4627 list_add_after(&cur_run->entry, &run->entry);
4628 break;
4631 return S_OK;
4634 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
4635 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
4637 return E_NOTIMPL;
4640 static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
4641 UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
4642 BOOL is_sideways, BOOL is_rtl)
4644 return E_NOTIMPL;
4647 static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
4648 dwritetextlayout_sink_QueryInterface,
4649 dwritetextlayout_sink_AddRef,
4650 dwritetextlayout_sink_Release,
4651 dwritetextlayout_sink_SetScriptAnalysis,
4652 dwritetextlayout_sink_SetLineBreakpoints,
4653 dwritetextlayout_sink_SetBidiLevel,
4654 dwritetextlayout_sink_SetNumberSubstitution,
4655 dwritetextlayout_sink_SetGlyphOrientation
4658 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
4659 REFIID riid, void **obj)
4661 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
4662 IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
4663 IsEqualIID(riid, &IID_IUnknown))
4665 *obj = iface;
4666 IDWriteTextAnalysisSource1_AddRef(iface);
4667 return S_OK;
4670 *obj = NULL;
4671 return E_NOINTERFACE;
4674 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
4676 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4677 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4680 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
4682 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4683 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4686 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
4687 UINT32 position, WCHAR const** text, UINT32* text_len)
4689 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4691 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4693 if (position < layout->len) {
4694 *text = &layout->str[position];
4695 *text_len = layout->len - position;
4697 else {
4698 *text = NULL;
4699 *text_len = 0;
4702 return S_OK;
4705 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
4706 UINT32 position, WCHAR const** text, UINT32* text_len)
4708 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4710 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4712 if (position > 0 && position < layout->len) {
4713 *text = layout->str;
4714 *text_len = position;
4716 else {
4717 *text = NULL;
4718 *text_len = 0;
4721 return S_OK;
4724 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
4726 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4727 return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface);
4730 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
4731 UINT32 position, UINT32* text_len, WCHAR const** locale)
4733 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4734 struct layout_range *range = get_layout_range_by_pos(layout, position);
4736 if (position < layout->len) {
4737 struct layout_range *next;
4739 *locale = range->locale;
4740 *text_len = range->h.range.length - position;
4742 next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
4743 while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
4744 *text_len += next->h.range.length;
4745 next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
4748 *text_len = min(*text_len, layout->len - position);
4750 else {
4751 *locale = NULL;
4752 *text_len = 0;
4755 return S_OK;
4758 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
4759 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
4761 FIXME("%u %p %p: stub\n", position, text_len, substitution);
4762 return E_NOTIMPL;
4765 static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
4766 UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
4768 FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
4769 return E_NOTIMPL;
4772 static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
4773 dwritetextlayout_source_QueryInterface,
4774 dwritetextlayout_source_AddRef,
4775 dwritetextlayout_source_Release,
4776 dwritetextlayout_source_GetTextAtPosition,
4777 dwritetextlayout_source_GetTextBeforePosition,
4778 dwritetextlayout_source_GetParagraphReadingDirection,
4779 dwritetextlayout_source_GetLocaleName,
4780 dwritetextlayout_source_GetNumberSubstitution,
4781 dwritetextlayout_source_GetVerticalGlyphOrientation
4784 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
4786 struct dwrite_textformat *textformat;
4787 IDWriteTextFormat1 *format1;
4788 UINT32 len;
4789 HRESULT hr;
4791 if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
4792 layout->format = textformat->format;
4794 layout->format.locale = heap_strdupW(textformat->format.locale);
4795 layout->format.family_name = heap_strdupW(textformat->format.family_name);
4796 if (!layout->format.locale || !layout->format.family_name)
4798 heap_free(layout->format.locale);
4799 heap_free(layout->format.family_name);
4800 return E_OUTOFMEMORY;
4803 if (layout->format.trimmingsign)
4804 IDWriteInlineObject_AddRef(layout->format.trimmingsign);
4805 if (layout->format.collection)
4806 IDWriteFontCollection_AddRef(layout->format.collection);
4807 if (layout->format.fallback)
4808 IDWriteFontFallback_AddRef(layout->format.fallback);
4810 return S_OK;
4813 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
4814 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
4815 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
4816 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
4817 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
4818 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
4819 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
4820 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
4821 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
4822 layout->format.fallback = NULL;
4823 layout->format.spacing.leadingBefore = 0.0f;
4824 layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
4825 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
4826 &layout->format.spacing.height, &layout->format.spacing.baseline);
4827 if (FAILED(hr))
4828 return hr;
4830 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
4831 if (FAILED(hr))
4832 return hr;
4834 /* locale name and length */
4835 len = IDWriteTextFormat_GetLocaleNameLength(format);
4836 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
4837 if (!layout->format.locale)
4838 return E_OUTOFMEMORY;
4840 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
4841 if (FAILED(hr))
4842 return hr;
4843 layout->format.locale_len = len;
4845 /* font family name and length */
4846 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
4847 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
4848 if (!layout->format.family_name)
4849 return E_OUTOFMEMORY;
4851 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
4852 if (FAILED(hr))
4853 return hr;
4854 layout->format.family_len = len;
4856 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4857 if (hr == S_OK) {
4858 IDWriteTextFormat2 *format2;
4860 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
4861 layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4862 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
4864 if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
4865 IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
4866 IDWriteTextFormat2_Release(format2);
4869 IDWriteTextFormat1_Release(format1);
4871 else {
4872 layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4873 layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
4876 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
4879 static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
4881 struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
4882 static const DWRITE_TEXT_RANGE r = { 0, ~0u };
4883 HRESULT hr;
4885 layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl;
4886 layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl;
4887 layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
4888 layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
4889 layout->ref = 1;
4890 layout->len = desc->length;
4891 layout->recompute = RECOMPUTE_EVERYTHING;
4892 layout->nominal_breakpoints = NULL;
4893 layout->actual_breakpoints = NULL;
4894 layout->cluster_count = 0;
4895 layout->clustermetrics = NULL;
4896 layout->clusters = NULL;
4897 layout->linemetrics = NULL;
4898 layout->lines = NULL;
4899 layout->line_alloc = 0;
4900 layout->minwidth = 0.0f;
4901 list_init(&layout->eruns);
4902 list_init(&layout->inlineobjects);
4903 list_init(&layout->underlines);
4904 list_init(&layout->strikethrough);
4905 list_init(&layout->runs);
4906 list_init(&layout->ranges);
4907 list_init(&layout->strike_ranges);
4908 list_init(&layout->underline_ranges);
4909 list_init(&layout->effects);
4910 list_init(&layout->spacing);
4911 list_init(&layout->typographies);
4912 memset(&layout->format, 0, sizeof(layout->format));
4913 memset(&layout->metrics, 0, sizeof(layout->metrics));
4914 layout->metrics.layoutWidth = desc->max_width;
4915 layout->metrics.layoutHeight = desc->max_height;
4916 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4918 layout->ppdip = 0.0f;
4919 memset(&layout->transform, 0, sizeof(layout->transform));
4921 layout->str = heap_strdupnW(desc->string, desc->length);
4922 if (desc->length && !layout->str) {
4923 hr = E_OUTOFMEMORY;
4924 goto fail;
4927 hr = layout_format_from_textformat(layout, desc->format);
4928 if (FAILED(hr))
4929 goto fail;
4931 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
4932 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
4933 underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
4934 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
4935 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
4936 typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
4937 if (!range || !strike || !effect || !spacing || !typography || !underline) {
4938 free_layout_range(range);
4939 free_layout_range(strike);
4940 free_layout_range(underline);
4941 free_layout_range(effect);
4942 free_layout_range(spacing);
4943 free_layout_range(typography);
4944 hr = E_OUTOFMEMORY;
4945 goto fail;
4948 if (desc->is_gdi_compatible)
4949 layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
4950 else
4951 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4952 layout->ppdip = desc->ppdip;
4953 layout->transform = desc->transform ? *desc->transform : identity;
4955 layout->factory = desc->factory;
4956 IDWriteFactory5_AddRef(layout->factory);
4957 list_add_head(&layout->ranges, &range->entry);
4958 list_add_head(&layout->strike_ranges, &strike->entry);
4959 list_add_head(&layout->underline_ranges, &underline->entry);
4960 list_add_head(&layout->effects, &effect->entry);
4961 list_add_head(&layout->spacing, &spacing->entry);
4962 list_add_head(&layout->typographies, &typography->entry);
4963 return S_OK;
4965 fail:
4966 IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4967 return hr;
4970 HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret)
4972 struct dwrite_textlayout *layout;
4973 HRESULT hr;
4975 *ret = NULL;
4977 if (!desc->format || !desc->string)
4978 return E_INVALIDARG;
4980 layout = heap_alloc(sizeof(struct dwrite_textlayout));
4981 if (!layout) return E_OUTOFMEMORY;
4983 hr = init_textlayout(desc, layout);
4984 if (hr == S_OK)
4985 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface;
4987 return hr;
4990 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
4992 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4994 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4996 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
4997 *obj = iface;
4998 IDWriteInlineObject_AddRef(iface);
4999 return S_OK;
5002 *obj = NULL;
5003 return E_NOINTERFACE;
5006 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
5008 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5009 ULONG ref = InterlockedIncrement(&This->ref);
5010 TRACE("(%p)->(%d)\n", This, ref);
5011 return ref;
5014 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
5016 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5017 ULONG ref = InterlockedDecrement(&This->ref);
5019 TRACE("(%p)->(%d)\n", This, ref);
5021 if (!ref) {
5022 IDWriteTextLayout_Release(This->layout);
5023 heap_free(This);
5026 return ref;
5029 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
5030 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
5032 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5033 DWRITE_TEXT_METRICS metrics;
5034 DWRITE_LINE_METRICS line;
5035 UINT32 line_count;
5037 TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY, is_sideways, is_rtl, effect);
5039 IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count);
5040 IDWriteTextLayout_GetMetrics(This->layout, &metrics);
5041 return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline);
5044 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
5046 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5047 DWRITE_TEXT_METRICS metrics;
5048 HRESULT hr;
5050 TRACE("(%p)->(%p)\n", This, ret);
5052 hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
5053 if (FAILED(hr)) {
5054 memset(ret, 0, sizeof(*ret));
5055 return hr;
5058 ret->width = metrics.width;
5059 ret->height = 0.0f;
5060 ret->baseline = 0.0f;
5061 ret->supportsSideways = FALSE;
5062 return S_OK;
5065 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
5067 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5068 TRACE("(%p)->(%p)\n", This, overhangs);
5069 return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs);
5072 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
5073 DWRITE_BREAK_CONDITION *after)
5075 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5077 TRACE("(%p)->(%p %p)\n", This, before, after);
5079 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
5080 return S_OK;
5083 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
5084 dwritetrimmingsign_QueryInterface,
5085 dwritetrimmingsign_AddRef,
5086 dwritetrimmingsign_Release,
5087 dwritetrimmingsign_Draw,
5088 dwritetrimmingsign_GetMetrics,
5089 dwritetrimmingsign_GetOverhangMetrics,
5090 dwritetrimmingsign_GetBreakConditions
5093 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
5095 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
5096 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
5099 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
5101 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
5102 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
5105 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
5107 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
5108 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
5111 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
5113 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
5114 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
5117 HRESULT create_trimmingsign(IDWriteFactory5 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
5119 static const WCHAR ellipsisW = 0x2026;
5120 struct dwrite_trimmingsign *This;
5121 DWRITE_READING_DIRECTION reading;
5122 DWRITE_FLOW_DIRECTION flow;
5123 HRESULT hr;
5125 *sign = NULL;
5127 /* Validate reading/flow direction here, layout creation won't complain about
5128 invalid combinations. */
5129 reading = IDWriteTextFormat_GetReadingDirection(format);
5130 flow = IDWriteTextFormat_GetFlowDirection(format);
5132 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
5133 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
5134 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
5136 This = heap_alloc(sizeof(*This));
5137 if (!This)
5138 return E_OUTOFMEMORY;
5140 This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
5141 This->ref = 1;
5143 hr = IDWriteFactory5_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout);
5144 if (FAILED(hr)) {
5145 heap_free(This);
5146 return hr;
5149 IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
5150 IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
5151 *sign = &This->IDWriteInlineObject_iface;
5153 return S_OK;
5156 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
5158 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5160 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5162 if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
5163 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
5164 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
5165 IsEqualIID(riid, &IID_IUnknown))
5167 *obj = iface;
5168 IDWriteTextFormat2_AddRef(iface);
5169 return S_OK;
5172 *obj = NULL;
5174 return E_NOINTERFACE;
5177 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface)
5179 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5180 ULONG ref = InterlockedIncrement(&This->ref);
5181 TRACE("(%p)->(%d)\n", This, ref);
5182 return ref;
5185 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface)
5187 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5188 ULONG ref = InterlockedDecrement(&This->ref);
5190 TRACE("(%p)->(%d)\n", This, ref);
5192 if (!ref)
5194 release_format_data(&This->format);
5195 heap_free(This);
5198 return ref;
5201 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
5203 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5204 TRACE("(%p)->(%d)\n", This, alignment);
5205 return format_set_textalignment(&This->format, alignment, NULL);
5208 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
5210 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5211 TRACE("(%p)->(%d)\n", This, alignment);
5212 return format_set_paralignment(&This->format, alignment, NULL);
5215 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
5217 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5218 TRACE("(%p)->(%d)\n", This, wrapping);
5219 return format_set_wordwrapping(&This->format, wrapping, NULL);
5222 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction)
5224 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5225 TRACE("(%p)->(%d)\n", This, direction);
5226 return format_set_readingdirection(&This->format, direction, NULL);
5229 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction)
5231 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5232 TRACE("(%p)->(%d)\n", This, direction);
5233 return format_set_flowdirection(&This->format, direction, NULL);
5236 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
5238 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5239 FIXME("(%p)->(%f): stub\n", This, tabstop);
5240 return E_NOTIMPL;
5243 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
5244 IDWriteInlineObject *trimming_sign)
5246 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5247 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
5248 return format_set_trimming(&This->format, trimming, trimming_sign, NULL);
5251 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method,
5252 FLOAT height, FLOAT baseline)
5254 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5255 DWRITE_LINE_SPACING spacing;
5257 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
5259 spacing = This->format.spacing;
5260 spacing.method = method;
5261 spacing.height = height;
5262 spacing.baseline = baseline;
5264 return format_set_linespacing(&This->format, &spacing, NULL);
5267 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface)
5269 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5270 TRACE("(%p)\n", This);
5271 return This->format.textalignment;
5274 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface)
5276 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5277 TRACE("(%p)\n", This);
5278 return This->format.paralign;
5281 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface)
5283 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5284 TRACE("(%p)\n", This);
5285 return This->format.wrapping;
5288 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface)
5290 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5291 TRACE("(%p)\n", This);
5292 return This->format.readingdir;
5295 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface)
5297 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5298 TRACE("(%p)\n", This);
5299 return This->format.flow;
5302 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
5304 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5305 FIXME("(%p): stub\n", This);
5306 return 0.0f;
5309 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
5310 IDWriteInlineObject **trimming_sign)
5312 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5313 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
5315 *options = This->format.trimming;
5316 if ((*trimming_sign = This->format.trimmingsign))
5317 IDWriteInlineObject_AddRef(*trimming_sign);
5319 return S_OK;
5322 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method,
5323 FLOAT *spacing, FLOAT *baseline)
5325 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5326 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
5328 *method = This->format.spacing.method;
5329 *spacing = This->format.spacing.height;
5330 *baseline = This->format.spacing.baseline;
5331 return S_OK;
5334 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection)
5336 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5338 TRACE("(%p)->(%p)\n", This, collection);
5340 *collection = This->format.collection;
5341 IDWriteFontCollection_AddRef(*collection);
5343 return S_OK;
5346 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
5348 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5349 TRACE("(%p)\n", This);
5350 return This->format.family_len;
5353 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5355 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5357 TRACE("(%p)->(%p %u)\n", This, name, size);
5359 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
5360 strcpyW(name, This->format.family_name);
5361 return S_OK;
5364 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface)
5366 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5367 TRACE("(%p)\n", This);
5368 return This->format.weight;
5371 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface)
5373 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5374 TRACE("(%p)\n", This);
5375 return This->format.style;
5378 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface)
5380 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5381 TRACE("(%p)\n", This);
5382 return This->format.stretch;
5385 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface)
5387 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5388 TRACE("(%p)\n", This);
5389 return This->format.fontsize;
5392 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface)
5394 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5395 TRACE("(%p)\n", This);
5396 return This->format.locale_len;
5399 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5401 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5403 TRACE("(%p)->(%p %u)\n", This, name, size);
5405 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
5406 strcpyW(name, This->format.locale);
5407 return S_OK;
5410 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
5412 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5414 TRACE("(%p)->(%d)\n", This, orientation);
5416 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
5417 return E_INVALIDARG;
5419 This->format.vertical_orientation = orientation;
5420 return S_OK;
5423 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
5425 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5426 TRACE("(%p)\n", This);
5427 return This->format.vertical_orientation;
5430 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled)
5432 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5434 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
5436 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
5437 return S_OK;
5440 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface)
5442 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5443 TRACE("(%p)\n", This);
5444 return This->format.last_line_wrapping;
5447 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
5449 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5450 TRACE("(%p)->(%d)\n", This, alignment);
5451 return format_set_optical_alignment(&This->format, alignment);
5454 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface)
5456 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5457 TRACE("(%p)\n", This);
5458 return This->format.optical_alignment;
5461 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback)
5463 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5464 TRACE("(%p)->(%p)\n", This, fallback);
5465 return set_fontfallback_for_format(&This->format, fallback);
5468 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback)
5470 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5471 TRACE("(%p)->(%p)\n", This, fallback);
5472 return get_fontfallback_from_format(&This->format, fallback);
5475 static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing)
5477 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5478 TRACE("(%p)->(%p)\n", This, spacing);
5479 return format_set_linespacing(&This->format, spacing, NULL);
5482 static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
5484 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5486 TRACE("(%p)->(%p)\n", This, spacing);
5488 *spacing = This->format.spacing;
5489 return S_OK;
5492 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = {
5493 dwritetextformat_QueryInterface,
5494 dwritetextformat_AddRef,
5495 dwritetextformat_Release,
5496 dwritetextformat_SetTextAlignment,
5497 dwritetextformat_SetParagraphAlignment,
5498 dwritetextformat_SetWordWrapping,
5499 dwritetextformat_SetReadingDirection,
5500 dwritetextformat_SetFlowDirection,
5501 dwritetextformat_SetIncrementalTabStop,
5502 dwritetextformat_SetTrimming,
5503 dwritetextformat_SetLineSpacing,
5504 dwritetextformat_GetTextAlignment,
5505 dwritetextformat_GetParagraphAlignment,
5506 dwritetextformat_GetWordWrapping,
5507 dwritetextformat_GetReadingDirection,
5508 dwritetextformat_GetFlowDirection,
5509 dwritetextformat_GetIncrementalTabStop,
5510 dwritetextformat_GetTrimming,
5511 dwritetextformat_GetLineSpacing,
5512 dwritetextformat_GetFontCollection,
5513 dwritetextformat_GetFontFamilyNameLength,
5514 dwritetextformat_GetFontFamilyName,
5515 dwritetextformat_GetFontWeight,
5516 dwritetextformat_GetFontStyle,
5517 dwritetextformat_GetFontStretch,
5518 dwritetextformat_GetFontSize,
5519 dwritetextformat_GetLocaleNameLength,
5520 dwritetextformat_GetLocaleName,
5521 dwritetextformat1_SetVerticalGlyphOrientation,
5522 dwritetextformat1_GetVerticalGlyphOrientation,
5523 dwritetextformat1_SetLastLineWrapping,
5524 dwritetextformat1_GetLastLineWrapping,
5525 dwritetextformat1_SetOpticalAlignment,
5526 dwritetextformat1_GetOpticalAlignment,
5527 dwritetextformat1_SetFontFallback,
5528 dwritetextformat1_GetFontFallback,
5529 dwritetextformat2_SetLineSpacing,
5530 dwritetextformat2_GetLineSpacing
5533 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
5535 return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
5536 CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL;
5539 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
5540 DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
5542 struct dwrite_textformat *This;
5544 *format = NULL;
5546 if (size <= 0.0f)
5547 return E_INVALIDARG;
5549 if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
5550 ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
5551 ((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
5552 return E_INVALIDARG;
5554 This = heap_alloc(sizeof(struct dwrite_textformat));
5555 if (!This) return E_OUTOFMEMORY;
5557 This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl;
5558 This->ref = 1;
5559 This->format.family_name = heap_strdupW(family_name);
5560 This->format.family_len = strlenW(family_name);
5561 This->format.locale = heap_strdupW(locale);
5562 This->format.locale_len = strlenW(locale);
5563 /* force locale name to lower case, layout will inherit this modified value */
5564 strlwrW(This->format.locale);
5565 This->format.weight = weight;
5566 This->format.style = style;
5567 This->format.fontsize = size;
5568 This->format.stretch = stretch;
5569 This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
5570 This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
5571 This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
5572 This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
5573 This->format.last_line_wrapping = TRUE;
5574 This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
5575 This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
5576 This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT;
5577 This->format.spacing.height = 0.0f;
5578 This->format.spacing.baseline = 0.0f;
5579 This->format.spacing.leadingBefore = 0.0f;
5580 This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
5581 This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
5582 This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
5583 This->format.trimming.delimiter = 0;
5584 This->format.trimming.delimiterCount = 0;
5585 This->format.trimmingsign = NULL;
5586 This->format.collection = collection;
5587 This->format.fallback = NULL;
5588 IDWriteFontCollection_AddRef(collection);
5590 *format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface;
5592 return S_OK;
5595 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
5597 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5599 TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
5601 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
5602 *obj = iface;
5603 IDWriteTypography_AddRef(iface);
5604 return S_OK;
5607 *obj = NULL;
5609 return E_NOINTERFACE;
5612 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
5614 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5615 ULONG ref = InterlockedIncrement(&typography->ref);
5616 TRACE("(%p)->(%d)\n", typography, ref);
5617 return ref;
5620 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
5622 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5623 ULONG ref = InterlockedDecrement(&typography->ref);
5625 TRACE("(%p)->(%d)\n", typography, ref);
5627 if (!ref) {
5628 heap_free(typography->features);
5629 heap_free(typography);
5632 return ref;
5635 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
5637 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5639 TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
5641 if (typography->count == typography->allocated) {
5642 DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5643 if (!ptr)
5644 return E_OUTOFMEMORY;
5646 typography->features = ptr;
5647 typography->allocated *= 2;
5650 typography->features[typography->count++] = feature;
5651 return S_OK;
5654 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
5656 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5657 TRACE("(%p)\n", typography);
5658 return typography->count;
5661 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
5663 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5665 TRACE("(%p)->(%u %p)\n", typography, index, feature);
5667 if (index >= typography->count)
5668 return E_INVALIDARG;
5670 *feature = typography->features[index];
5671 return S_OK;
5674 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
5675 dwritetypography_QueryInterface,
5676 dwritetypography_AddRef,
5677 dwritetypography_Release,
5678 dwritetypography_AddFontFeature,
5679 dwritetypography_GetFontFeatureCount,
5680 dwritetypography_GetFontFeature
5683 HRESULT create_typography(IDWriteTypography **ret)
5685 struct dwrite_typography *typography;
5687 *ret = NULL;
5689 typography = heap_alloc(sizeof(*typography));
5690 if (!typography)
5691 return E_OUTOFMEMORY;
5693 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
5694 typography->ref = 1;
5695 typography->allocated = 2;
5696 typography->count = 0;
5698 typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5699 if (!typography->features) {
5700 heap_free(typography);
5701 return E_OUTOFMEMORY;
5704 *ret = &typography->IDWriteTypography_iface;
5705 return S_OK;