dxgi: Return actual swap effect for D3D11 swapchains.
[wine.git] / dlls / dwrite / layout.c
blob10d591457cc21c53783e11b78b5c2a9dc3baa976
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 IDWriteTextFormat2 IDWriteTextFormat2_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_IDWriteTextFormat2(IDWriteTextFormat2 *iface)
331 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat2_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 void layout_apply_line_spacing(struct dwrite_textlayout *layout, UINT32 line)
1363 switch (layout->format.spacing.method)
1365 case DWRITE_LINE_SPACING_METHOD_DEFAULT:
1366 layout->linemetrics[line].height = layout->lines[line].height;
1367 layout->linemetrics[line].baseline = layout->lines[line].baseline;
1368 break;
1369 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
1370 layout->linemetrics[line].height = layout->format.spacing.height;
1371 layout->linemetrics[line].baseline = layout->format.spacing.baseline;
1372 break;
1373 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
1374 layout->linemetrics[line].height = layout->lines[line].height * layout->format.spacing.height;
1375 layout->linemetrics[line].baseline = layout->lines[line].baseline * layout->format.spacing.baseline;
1376 break;
1377 default:
1378 ERR("Unknown spacing method %u\n", layout->format.spacing.method);
1382 static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics)
1384 UINT32 i = layout->metrics.lineCount;
1386 if (!layout->line_alloc) {
1387 layout->line_alloc = 5;
1388 layout->linemetrics = heap_alloc(layout->line_alloc * sizeof(*layout->linemetrics));
1389 layout->lines = heap_alloc(layout->line_alloc * sizeof(*layout->lines));
1390 if (!layout->linemetrics || !layout->lines) {
1391 heap_free(layout->linemetrics);
1392 heap_free(layout->lines);
1393 layout->linemetrics = NULL;
1394 layout->lines = NULL;
1395 return E_OUTOFMEMORY;
1399 if (layout->metrics.lineCount == layout->line_alloc) {
1400 DWRITE_LINE_METRICS1 *metrics;
1401 struct layout_line *lines;
1403 if ((metrics = heap_realloc(layout->linemetrics, layout->line_alloc * 2 * sizeof(*layout->linemetrics))))
1404 layout->linemetrics = metrics;
1405 if ((lines = heap_realloc(layout->lines, layout->line_alloc * 2 * sizeof(*layout->lines))))
1406 layout->lines = lines;
1408 if (!metrics || !lines)
1409 return E_OUTOFMEMORY;
1411 layout->line_alloc *= 2;
1414 layout->linemetrics[i] = *metrics;
1415 layout->lines[i].height = metrics->height;
1416 layout->lines[i].baseline = metrics->baseline;
1418 if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT)
1419 layout_apply_line_spacing(layout, i);
1421 layout->metrics.lineCount++;
1422 return S_OK;
1425 static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
1426 const struct layout_effective_run *cur)
1428 struct list *e;
1430 if (!cur)
1431 e = list_head(&layout->eruns);
1432 else
1433 e = list_next(&layout->eruns, &cur->entry);
1434 if (!e)
1435 return NULL;
1436 return LIST_ENTRY(e, struct layout_effective_run, entry);
1439 static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout,
1440 const struct layout_effective_run *cur)
1442 struct list *e;
1444 if (!cur)
1445 e = list_tail(&layout->eruns);
1446 else
1447 e = list_prev(&layout->eruns, &cur->entry);
1448 if (!e)
1449 return NULL;
1450 return LIST_ENTRY(e, struct layout_effective_run, entry);
1453 static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
1454 const struct layout_effective_inline *cur)
1456 struct list *e;
1458 if (!cur)
1459 e = list_head(&layout->inlineobjects);
1460 else
1461 e = list_next(&layout->inlineobjects, &cur->entry);
1462 if (!e)
1463 return NULL;
1464 return LIST_ENTRY(e, struct layout_effective_inline, entry);
1467 static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
1468 struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
1470 FLOAT width = 0.0f;
1472 while (erun && erun->line == line) {
1473 width += erun->width;
1474 erun = layout_get_next_erun(layout, erun);
1475 if (!erun)
1476 break;
1479 while (inrun && inrun->line == line) {
1480 width += inrun->width;
1481 inrun = layout_get_next_inline_run(layout, inrun);
1482 if (!inrun)
1483 break;
1486 return width;
1489 static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
1491 *det = m->m11 * m->m22 - m->m12 * m->m21;
1492 /* on certain conditions we can skip transform */
1493 return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
1496 static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, FLOAT ppdip,
1497 const DWRITE_MATRIX *m, FLOAT det)
1499 if (!skiptransform) {
1500 D2D1_POINT_2F vec2;
1502 /* apply transform */
1503 vec->x *= ppdip;
1504 vec->y *= ppdip;
1506 vec2.x = m->m11 * vec->x + m->m21 * vec->y + m->dx;
1507 vec2.y = m->m12 * vec->x + m->m22 * vec->y + m->dy;
1509 /* snap */
1510 vec2.x = floorf(vec2.x + 0.5f);
1511 vec2.y = floorf(vec2.y + 0.5f);
1513 /* apply inverted transform, we don't care about X component at this point */
1514 vec->x = (m->m22 * vec2.x - m->m21 * vec2.y + m->m21 * m->dy - m->m22 * m->dx) / det;
1515 vec->x /= ppdip;
1517 vec->y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
1518 vec->y /= ppdip;
1520 else {
1521 vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
1522 vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
1526 static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
1528 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1529 struct layout_effective_inline *inrun;
1530 struct layout_effective_run *erun;
1532 erun = layout_get_next_erun(layout, NULL);
1533 inrun = layout_get_next_inline_run(layout, NULL);
1535 while (erun) {
1536 erun->align_dx = 0.0f;
1537 erun = layout_get_next_erun(layout, erun);
1540 while (inrun) {
1541 inrun->align_dx = 0.0f;
1542 inrun = layout_get_next_inline_run(layout, inrun);
1545 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
1548 static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout)
1550 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1551 struct layout_effective_inline *inrun;
1552 struct layout_effective_run *erun;
1553 UINT32 line;
1555 erun = layout_get_next_erun(layout, NULL);
1556 inrun = layout_get_next_inline_run(layout, NULL);
1558 for (line = 0; line < layout->metrics.lineCount; line++) {
1559 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1560 FLOAT shift = layout->metrics.layoutWidth - width;
1562 if (is_rtl)
1563 shift *= -1.0f;
1565 while (erun && erun->line == line) {
1566 erun->align_dx = shift;
1567 erun = layout_get_next_erun(layout, erun);
1570 while (inrun && inrun->line == line) {
1571 inrun->align_dx = shift;
1572 inrun = layout_get_next_inline_run(layout, inrun);
1576 layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width;
1579 static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
1580 FLOAT width, FLOAT det)
1582 if (is_layout_gdi_compatible(layout)) {
1583 D2D1_POINT_2F vec = { layout->metrics.layoutWidth - width, 0.0f};
1584 layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
1585 return floorf(vec.x / 2.0f);
1587 else
1588 return (layout->metrics.layoutWidth - width) / 2.0f;
1591 static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
1593 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1594 struct layout_effective_inline *inrun;
1595 struct layout_effective_run *erun;
1596 BOOL skiptransform;
1597 UINT32 line;
1598 FLOAT det;
1600 erun = layout_get_next_erun(layout, NULL);
1601 inrun = layout_get_next_inline_run(layout, NULL);
1603 skiptransform = should_skip_transform(&layout->transform, &det);
1605 for (line = 0; line < layout->metrics.lineCount; line++) {
1606 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1607 FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
1609 if (is_rtl)
1610 shift *= -1.0f;
1612 while (erun && erun->line == line) {
1613 erun->align_dx = shift;
1614 erun = layout_get_next_erun(layout, erun);
1617 while (inrun && inrun->line == line) {
1618 inrun->align_dx = shift;
1619 inrun = layout_get_next_inline_run(layout, inrun);
1623 layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f;
1626 static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
1628 switch (layout->format.textalignment)
1630 case DWRITE_TEXT_ALIGNMENT_LEADING:
1631 layout_apply_leading_alignment(layout);
1632 break;
1633 case DWRITE_TEXT_ALIGNMENT_TRAILING:
1634 layout_apply_trailing_alignment(layout);
1635 break;
1636 case DWRITE_TEXT_ALIGNMENT_CENTER:
1637 layout_apply_centered_alignment(layout);
1638 break;
1639 case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
1640 FIXME("alignment %d not implemented\n", layout->format.textalignment);
1641 break;
1642 default:
1647 static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
1649 struct layout_effective_inline *inrun;
1650 struct layout_effective_run *erun;
1651 FLOAT origin_y = 0.0f;
1652 UINT32 line;
1654 /* alignment mode defines origin, after that all run origins are updated
1655 the same way */
1657 switch (layout->format.paralign)
1659 case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
1660 origin_y = 0.0f;
1661 break;
1662 case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
1663 origin_y = layout->metrics.layoutHeight - layout->metrics.height;
1664 break;
1665 case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
1666 origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f;
1667 break;
1668 default:
1672 layout->metrics.top = origin_y;
1674 erun = layout_get_next_erun(layout, NULL);
1675 inrun = layout_get_next_inline_run(layout, NULL);
1676 for (line = 0; line < layout->metrics.lineCount; line++) {
1677 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1679 while (erun && erun->line == line) {
1680 erun->origin.y = pos_y;
1681 erun = layout_get_next_erun(layout, erun);
1684 while (inrun && inrun->line == line) {
1685 inrun->origin.y = pos_y - inrun->baseline;
1686 inrun = layout_get_next_inline_run(layout, inrun);
1689 origin_y += layout->linemetrics[line].height;
1693 struct layout_underline_splitting_params {
1694 const WCHAR *locale; /* points to range data, no additional allocation */
1695 IUnknown *effect; /* does not hold another reference */
1698 static void init_u_splitting_params_from_erun(struct layout_effective_run *erun,
1699 struct layout_underline_splitting_params *params)
1701 params->locale = erun->run->u.regular.descr.localeName;
1702 params->effect = erun->effect;
1705 static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left,
1706 struct layout_underline_splitting_params *right)
1708 return left->effect == right->effect && !strcmpiW(left->locale, right->locale);
1711 static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first,
1712 struct layout_effective_run *last)
1714 FLOAT thickness, offset, runheight;
1715 struct layout_effective_run *cur;
1716 DWRITE_FONT_METRICS metrics;
1718 if (first == layout_get_prev_erun(layout, last)) {
1719 layout_get_erun_font_metrics(layout, first, &metrics);
1720 thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics);
1721 offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics);
1722 runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics);
1724 else {
1725 FLOAT width = 0.0f;
1727 /* Single underline is added for consecutive underlined runs. In this case underline parameters are
1728 calculated as weighted average, where run width acts as a weight. */
1729 thickness = offset = runheight = 0.0f;
1730 cur = first;
1731 do {
1732 layout_get_erun_font_metrics(layout, cur, &metrics);
1734 thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1735 offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1736 runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight);
1737 width += cur->width;
1739 cur = layout_get_next_erun(layout, cur);
1740 } while (cur != last);
1742 thickness /= width;
1743 offset /= width;
1746 cur = first;
1747 do {
1748 struct layout_underline_splitting_params params, prev_params;
1749 struct layout_effective_run *next, *w;
1750 struct layout_underline *u;
1752 init_u_splitting_params_from_erun(cur, &prev_params);
1753 while ((next = layout_get_next_erun(layout, cur)) != last) {
1754 init_u_splitting_params_from_erun(next, &params);
1755 if (!is_same_u_splitting(&prev_params, &params))
1756 break;
1757 cur = next;
1760 u = heap_alloc(sizeof(*u));
1761 if (!u)
1762 return E_OUTOFMEMORY;
1764 w = cur;
1765 u->u.width = 0.0f;
1766 while (w != next) {
1767 u->u.width += w->width;
1768 w = layout_get_next_erun(layout, w);
1771 u->u.thickness = thickness;
1772 /* Font metrics convention is to have it negative when below baseline, for rendering
1773 however Y grows from baseline down for horizontal baseline. */
1774 u->u.offset = -offset;
1775 u->u.runHeight = runheight;
1776 u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
1777 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
1778 u->u.flowDirection = layout->format.flow;
1779 u->u.localeName = cur->run->u.regular.descr.localeName;
1780 u->u.measuringMode = layout->measuringmode;
1781 u->run = cur;
1782 list_add_tail(&layout->underlines, &u->entry);
1784 cur = next;
1785 } while (cur != last);
1787 return S_OK;
1790 /* Adds zero width line, metrics are derived from font at specified text position. */
1791 static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
1793 DWRITE_LINE_METRICS1 metrics = { 0 };
1794 DWRITE_FONT_METRICS fontmetrics;
1795 struct layout_range *range;
1796 IDWriteFontFace *fontface;
1797 IDWriteFont *font;
1798 HRESULT hr;
1800 range = get_layout_range_by_pos(layout, pos);
1801 hr = create_matching_font(range->collection,
1802 range->fontfamily,
1803 range->weight,
1804 range->style,
1805 range->stretch,
1806 &font);
1807 if (FAILED(hr))
1808 return hr;
1809 hr = IDWriteFont_CreateFontFace(font, &fontface);
1810 IDWriteFont_Release(font);
1811 if (FAILED(hr))
1812 return hr;
1814 layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics);
1815 layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
1816 IDWriteFontFace_Release(fontface);
1818 return layout_set_line_metrics(layout, &metrics);
1821 static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
1822 UINT32 *textpos)
1824 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1825 struct layout_final_splitting_params params, prev_params;
1826 DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 };
1827 UINT32 line = layout->metrics.lineCount, i;
1828 DWRITE_LINE_METRICS1 metrics = { 0 };
1829 UINT32 index, start, pos = *textpos;
1830 FLOAT descent, trailingspacewidth;
1831 BOOL append_trimming_run = FALSE;
1832 const struct layout_run *run;
1833 FLOAT width, origin_x;
1834 HRESULT hr;
1836 /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
1837 for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
1838 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1839 struct layout_cluster *lc = &layout->clusters[index];
1840 WCHAR ch;
1842 /* This also filters out clusters added from inline objects, those are never
1843 treated as a white space. */
1844 if (!cluster->isWhitespace)
1845 break;
1847 /* Every isNewline cluster is also isWhitespace, but not every
1848 newline character cluster has isNewline set, so go back to original string. */
1849 ch = lc->run->u.regular.descr.string[lc->position];
1850 if (cluster->length == 1 && lb_is_newline_char(ch))
1851 metrics.newlineLength += cluster->length;
1853 metrics.trailingWhitespaceLength += cluster->length;
1854 trailingspacewidth += cluster->width;
1857 /* Line metrics length includes trailing whitespace length too */
1858 for (i = first_cluster; i <= last_cluster; i++)
1859 metrics.length += layout->clustermetrics[i].length;
1861 /* Ignore trailing whitespaces */
1862 while (last_cluster > first_cluster) {
1863 if (!layout->clustermetrics[last_cluster].isWhitespace)
1864 break;
1866 last_cluster--;
1869 /* Does not include trailing space width */
1870 width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
1872 /* Append trimming run if necessary */
1873 if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL &&
1874 layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) {
1875 FLOAT trimmed_width = width;
1877 hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics);
1878 if (SUCCEEDED(hr)) {
1879 while (last_cluster > first_cluster) {
1880 if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth)
1881 break;
1882 if (layout->format.trimming.granularity == DWRITE_TRIMMING_GRANULARITY_CHARACTER)
1883 trimmed_width -= layout->clustermetrics[last_cluster--].width;
1884 else {
1885 while (last_cluster > first_cluster) {
1886 trimmed_width -= layout->clustermetrics[last_cluster].width;
1887 if (layout->clustermetrics[last_cluster--].canWrapLineAfter)
1888 break;
1892 append_trimming_run = TRUE;
1894 else
1895 WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr);
1897 width = trimmed_width + sign_metrics.width;
1900 layout_splitting_params_from_pos(layout, pos, &params);
1901 prev_params = params;
1902 run = layout->clusters[first_cluster].run;
1904 /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
1905 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
1906 for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
1907 layout_splitting_params_from_pos(layout, pos, &params);
1909 if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, &params)) {
1910 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1911 if (FAILED(hr))
1912 return;
1914 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
1915 get_cluster_range_width(layout, start, i);
1916 run = layout->clusters[i].run;
1917 start = i;
1920 prev_params = params;
1921 pos += layout->clustermetrics[i].length;
1924 /* Final run from what's left from cluster range */
1925 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1926 if (FAILED(hr))
1927 return;
1929 if (append_trimming_run) {
1930 struct layout_effective_inline *trimming_sign;
1932 trimming_sign = heap_alloc(sizeof(*trimming_sign));
1933 if (!trimming_sign)
1934 return;
1936 trimming_sign->object = layout->format.trimmingsign;
1937 trimming_sign->width = sign_metrics.width;
1938 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i);
1939 trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x;
1940 trimming_sign->origin.y = 0.0f; /* set after line is built */
1941 trimming_sign->align_dx = 0.0f;
1942 trimming_sign->baseline = sign_metrics.baseline;
1944 trimming_sign->is_sideways = FALSE;
1945 trimming_sign->is_rtl = FALSE;
1946 trimming_sign->line = line;
1948 trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position +
1949 layout->clusters[i].run->start_position);
1951 list_add_tail(&layout->inlineobjects, &trimming_sign->entry);
1954 /* Look for max baseline and descent for this line */
1955 for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) {
1956 const struct layout_run *cur = layout->clusters[index].run;
1957 FLOAT cur_descent = cur->height - cur->baseline;
1959 if (cur->baseline > metrics.baseline)
1960 metrics.baseline = cur->baseline;
1961 if (cur_descent > descent)
1962 descent = cur_descent;
1965 layout->metrics.width = max(width, layout->metrics.width);
1966 layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
1967 layout->metrics.widthIncludingTrailingWhitespace);
1969 metrics.height = descent + metrics.baseline;
1970 metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth;
1971 layout_set_line_metrics(layout, &metrics);
1973 *textpos += metrics.length;
1976 static void layout_set_line_positions(struct dwrite_textlayout *layout)
1978 struct layout_effective_inline *inrun;
1979 struct layout_effective_run *erun;
1980 FLOAT origin_y;
1981 UINT32 line;
1983 /* Now all line info is here, update effective runs positions in flow direction */
1984 erun = layout_get_next_erun(layout, NULL);
1985 inrun = layout_get_next_inline_run(layout, NULL);
1987 for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) {
1988 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1990 /* For all runs on this line */
1991 while (erun && erun->line == line) {
1992 erun->origin.y = pos_y;
1993 erun = layout_get_next_erun(layout, erun);
1996 /* Same for inline runs */
1997 while (inrun && inrun->line == line) {
1998 inrun->origin.y = pos_y - inrun->baseline;
1999 inrun = layout_get_next_inline_run(layout, inrun);
2002 origin_y += layout->linemetrics[line].height;
2005 layout->metrics.height = origin_y;
2007 /* Initial paragraph alignment is always near */
2008 if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
2009 layout_apply_par_alignment(layout);
2012 static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster)
2014 if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER)
2015 return TRUE;
2017 return layout->clustermetrics[cluster].canWrapLineAfter;
2020 static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
2022 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
2023 struct layout_effective_run *erun, *first_underlined;
2024 UINT32 i, start, textpos, last_breaking_point;
2025 DWRITE_LINE_METRICS1 metrics;
2026 FLOAT width;
2027 UINT32 line;
2028 HRESULT hr;
2030 if (!(layout->recompute & RECOMPUTE_LINES))
2031 return S_OK;
2033 free_layout_eruns(layout);
2035 hr = layout_compute(layout);
2036 if (FAILED(hr))
2037 return hr;
2039 layout->metrics.lineCount = 0;
2040 memset(&metrics, 0, sizeof(metrics));
2042 layout->metrics.height = 0.0f;
2043 layout->metrics.width = 0.0f;
2044 layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
2046 last_breaking_point = ~0u;
2048 for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
2049 BOOL overflow = FALSE;
2051 while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
2052 /* Check for overflow */
2053 overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
2054 (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
2055 if (overflow)
2056 break;
2058 if (layout_can_wrap_after(layout, i))
2059 last_breaking_point = i;
2060 width += layout->clustermetrics[i].width;
2061 i++;
2063 i = min(i, layout->cluster_count - 1);
2065 /* Ignore if overflown on whitespace */
2066 if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) {
2067 /* Use most recently found breaking point */
2068 if (last_breaking_point != ~0u) {
2069 i = last_breaking_point;
2070 last_breaking_point = ~0u;
2072 else {
2073 /* Otherwise proceed forward to next newline or breaking point */
2074 for (; i < layout->cluster_count; i++)
2075 if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline)
2076 break;
2079 i = min(i, layout->cluster_count - 1);
2081 layout_add_line(layout, start, i, &textpos);
2082 start = i + 1;
2083 width = 0.0f;
2086 /* Add dummy line if:
2087 - there's no text, metrics come from first range in this case;
2088 - last ended with a mandatory break, metrics come from last text position.
2090 if (layout->len == 0)
2091 hr = layout_set_dummy_line_metrics(layout, 0);
2092 else if (layout->cluster_count && layout->clustermetrics[layout->cluster_count - 1].isNewline)
2093 hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
2094 if (FAILED(hr))
2095 return hr;
2097 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
2098 layout->metrics.top = 0.0f;
2099 layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
2101 /* Add explicit underlined runs */
2102 erun = layout_get_next_erun(layout, NULL);
2103 first_underlined = erun && erun->underlined ? erun : NULL;
2104 for (line = 0; line < layout->metrics.lineCount; line++) {
2105 while (erun && erun->line == line) {
2106 erun = layout_get_next_erun(layout, erun);
2108 if (first_underlined && (!erun || !erun->underlined)) {
2109 layout_add_underline(layout, first_underlined, erun);
2110 first_underlined = NULL;
2112 else if (!first_underlined && erun && erun->underlined)
2113 first_underlined = erun;
2117 /* Position runs in flow direction */
2118 layout_set_line_positions(layout);
2120 /* Initial alignment is always leading */
2121 if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
2122 layout_apply_text_alignment(layout);
2124 layout->recompute &= ~RECOMPUTE_LINES;
2125 return hr;
2128 static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
2129 struct layout_range_attr_value *value)
2131 struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
2132 struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
2133 struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
2134 struct layout_range const *range = (struct layout_range*)h;
2136 switch (attr) {
2137 case LAYOUT_RANGE_ATTR_WEIGHT:
2138 return range->weight == value->u.weight;
2139 case LAYOUT_RANGE_ATTR_STYLE:
2140 return range->style == value->u.style;
2141 case LAYOUT_RANGE_ATTR_STRETCH:
2142 return range->stretch == value->u.stretch;
2143 case LAYOUT_RANGE_ATTR_FONTSIZE:
2144 return range->fontsize == value->u.fontsize;
2145 case LAYOUT_RANGE_ATTR_INLINE:
2146 return range->object == value->u.object;
2147 case LAYOUT_RANGE_ATTR_EFFECT:
2148 return range_iface->iface == value->u.effect;
2149 case LAYOUT_RANGE_ATTR_UNDERLINE:
2150 return range_bool->value == value->u.underline;
2151 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2152 return range_bool->value == value->u.strikethrough;
2153 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2154 return range->pair_kerning == value->u.pair_kerning;
2155 case LAYOUT_RANGE_ATTR_FONTCOLL:
2156 return range->collection == value->u.collection;
2157 case LAYOUT_RANGE_ATTR_LOCALE:
2158 return strcmpiW(range->locale, value->u.locale) == 0;
2159 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2160 return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
2161 case LAYOUT_RANGE_ATTR_SPACING:
2162 return range_spacing->leading == value->u.spacing.leading &&
2163 range_spacing->trailing == value->u.spacing.trailing &&
2164 range_spacing->min_advance == value->u.spacing.min_advance;
2165 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2166 return range_iface->iface == (IUnknown*)value->u.typography;
2167 default:
2171 return FALSE;
2174 static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
2176 switch (hleft->kind)
2178 case LAYOUT_RANGE_REGULAR:
2180 struct layout_range const *left = (struct layout_range const*)hleft;
2181 struct layout_range const *right = (struct layout_range const*)hright;
2182 return left->weight == right->weight &&
2183 left->style == right->style &&
2184 left->stretch == right->stretch &&
2185 left->fontsize == right->fontsize &&
2186 left->object == right->object &&
2187 left->pair_kerning == right->pair_kerning &&
2188 left->collection == right->collection &&
2189 !strcmpiW(left->locale, right->locale) &&
2190 !strcmpW(left->fontfamily, right->fontfamily);
2192 case LAYOUT_RANGE_UNDERLINE:
2193 case LAYOUT_RANGE_STRIKETHROUGH:
2195 struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
2196 struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
2197 return left->value == right->value;
2199 case LAYOUT_RANGE_EFFECT:
2200 case LAYOUT_RANGE_TYPOGRAPHY:
2202 struct layout_range_iface const *left = (struct layout_range_iface const*)hleft;
2203 struct layout_range_iface const *right = (struct layout_range_iface const*)hright;
2204 return left->iface == right->iface;
2206 case LAYOUT_RANGE_SPACING:
2208 struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
2209 struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
2210 return left->leading == right->leading &&
2211 left->trailing == right->trailing &&
2212 left->min_advance == right->min_advance;
2214 default:
2215 FIXME("unknown range kind %d\n", hleft->kind);
2216 return FALSE;
2220 static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
2222 return left->startPosition == right->startPosition && left->length == right->length;
2225 /* Allocates range and inits it with default values from text format. */
2226 static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
2227 enum layout_range_kind kind)
2229 struct layout_range_header *h;
2231 switch (kind)
2233 case LAYOUT_RANGE_REGULAR:
2235 struct layout_range *range;
2237 range = heap_alloc(sizeof(*range));
2238 if (!range) return NULL;
2240 range->weight = layout->format.weight;
2241 range->style = layout->format.style;
2242 range->stretch = layout->format.stretch;
2243 range->fontsize = layout->format.fontsize;
2244 range->object = NULL;
2245 range->pair_kerning = FALSE;
2247 range->fontfamily = heap_strdupW(layout->format.family_name);
2248 if (!range->fontfamily) {
2249 heap_free(range);
2250 return NULL;
2253 range->collection = layout->format.collection;
2254 if (range->collection)
2255 IDWriteFontCollection_AddRef(range->collection);
2256 strcpyW(range->locale, layout->format.locale);
2258 h = &range->h;
2259 break;
2261 case LAYOUT_RANGE_UNDERLINE:
2262 case LAYOUT_RANGE_STRIKETHROUGH:
2264 struct layout_range_bool *range;
2266 range = heap_alloc(sizeof(*range));
2267 if (!range) return NULL;
2269 range->value = FALSE;
2270 h = &range->h;
2271 break;
2273 case LAYOUT_RANGE_EFFECT:
2274 case LAYOUT_RANGE_TYPOGRAPHY:
2276 struct layout_range_iface *range;
2278 range = heap_alloc(sizeof(*range));
2279 if (!range) return NULL;
2281 range->iface = NULL;
2282 h = &range->h;
2283 break;
2285 case LAYOUT_RANGE_SPACING:
2287 struct layout_range_spacing *range;
2289 range = heap_alloc(sizeof(*range));
2290 if (!range) return NULL;
2292 range->leading = 0.0f;
2293 range->trailing = 0.0f;
2294 range->min_advance = 0.0f;
2295 h = &range->h;
2296 break;
2298 default:
2299 FIXME("unknown range kind %d\n", kind);
2300 return NULL;
2303 h->kind = kind;
2304 h->range = *r;
2305 return h;
2308 static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
2310 struct layout_range_header *ret;
2312 switch (h->kind)
2314 case LAYOUT_RANGE_REGULAR:
2316 struct layout_range *from = (struct layout_range*)h;
2318 struct layout_range *range = heap_alloc(sizeof(*range));
2319 if (!range) return NULL;
2321 *range = *from;
2322 range->fontfamily = heap_strdupW(from->fontfamily);
2323 if (!range->fontfamily) {
2324 heap_free(range);
2325 return NULL;
2328 /* update refcounts */
2329 if (range->object)
2330 IDWriteInlineObject_AddRef(range->object);
2331 if (range->collection)
2332 IDWriteFontCollection_AddRef(range->collection);
2333 ret = &range->h;
2334 break;
2336 case LAYOUT_RANGE_UNDERLINE:
2337 case LAYOUT_RANGE_STRIKETHROUGH:
2339 struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
2340 if (!strike) return NULL;
2342 *strike = *(struct layout_range_bool*)h;
2343 ret = &strike->h;
2344 break;
2346 case LAYOUT_RANGE_EFFECT:
2347 case LAYOUT_RANGE_TYPOGRAPHY:
2349 struct layout_range_iface *effect = heap_alloc(sizeof(*effect));
2350 if (!effect) return NULL;
2352 *effect = *(struct layout_range_iface*)h;
2353 if (effect->iface)
2354 IUnknown_AddRef(effect->iface);
2355 ret = &effect->h;
2356 break;
2358 case LAYOUT_RANGE_SPACING:
2360 struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
2361 if (!spacing) return NULL;
2363 *spacing = *(struct layout_range_spacing*)h;
2364 ret = &spacing->h;
2365 break;
2367 default:
2368 FIXME("unknown range kind %d\n", h->kind);
2369 return NULL;
2372 ret->range = *r;
2373 return ret;
2376 static void free_layout_range(struct layout_range_header *h)
2378 if (!h)
2379 return;
2381 switch (h->kind)
2383 case LAYOUT_RANGE_REGULAR:
2385 struct layout_range *range = (struct layout_range*)h;
2387 if (range->object)
2388 IDWriteInlineObject_Release(range->object);
2389 if (range->collection)
2390 IDWriteFontCollection_Release(range->collection);
2391 heap_free(range->fontfamily);
2392 break;
2394 case LAYOUT_RANGE_EFFECT:
2395 case LAYOUT_RANGE_TYPOGRAPHY:
2397 struct layout_range_iface *range = (struct layout_range_iface*)h;
2398 if (range->iface)
2399 IUnknown_Release(range->iface);
2400 break;
2402 default:
2406 heap_free(h);
2409 static void free_layout_ranges_list(struct dwrite_textlayout *layout)
2411 struct layout_range_header *cur, *cur2;
2413 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
2414 list_remove(&cur->entry);
2415 free_layout_range(cur);
2418 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) {
2419 list_remove(&cur->entry);
2420 free_layout_range(cur);
2423 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
2424 list_remove(&cur->entry);
2425 free_layout_range(cur);
2428 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
2429 list_remove(&cur->entry);
2430 free_layout_range(cur);
2433 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
2434 list_remove(&cur->entry);
2435 free_layout_range(cur);
2438 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) {
2439 list_remove(&cur->entry);
2440 free_layout_range(cur);
2444 static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
2446 struct layout_range_header *cur;
2448 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
2450 if (cur->range.startPosition > range->startPosition)
2451 return NULL;
2453 if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
2454 (range->startPosition < cur->range.startPosition + cur->range.length))
2455 return NULL;
2456 if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
2457 return cur;
2460 return NULL;
2463 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
2465 struct layout_range *cur;
2467 LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
2468 DWRITE_TEXT_RANGE *r = &cur->h.range;
2469 if (r->startPosition <= pos && pos < r->startPosition + r->length)
2470 return cur;
2473 return NULL;
2476 static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
2478 if (*dest == value) return FALSE;
2480 if (*dest)
2481 IUnknown_Release(*dest);
2482 *dest = value;
2483 if (*dest)
2484 IUnknown_AddRef(*dest);
2486 return TRUE;
2489 static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2491 struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
2492 struct layout_range_iface *dest_iface = (struct layout_range_iface*)h;
2493 struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
2494 struct layout_range *dest = (struct layout_range*)h;
2496 BOOL changed = FALSE;
2498 switch (attr) {
2499 case LAYOUT_RANGE_ATTR_WEIGHT:
2500 changed = dest->weight != value->u.weight;
2501 dest->weight = value->u.weight;
2502 break;
2503 case LAYOUT_RANGE_ATTR_STYLE:
2504 changed = dest->style != value->u.style;
2505 dest->style = value->u.style;
2506 break;
2507 case LAYOUT_RANGE_ATTR_STRETCH:
2508 changed = dest->stretch != value->u.stretch;
2509 dest->stretch = value->u.stretch;
2510 break;
2511 case LAYOUT_RANGE_ATTR_FONTSIZE:
2512 changed = dest->fontsize != value->u.fontsize;
2513 dest->fontsize = value->u.fontsize;
2514 break;
2515 case LAYOUT_RANGE_ATTR_INLINE:
2516 changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
2517 break;
2518 case LAYOUT_RANGE_ATTR_EFFECT:
2519 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.effect);
2520 break;
2521 case LAYOUT_RANGE_ATTR_UNDERLINE:
2522 changed = dest_bool->value != value->u.underline;
2523 dest_bool->value = value->u.underline;
2524 break;
2525 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2526 changed = dest_bool->value != value->u.strikethrough;
2527 dest_bool->value = value->u.strikethrough;
2528 break;
2529 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2530 changed = dest->pair_kerning != value->u.pair_kerning;
2531 dest->pair_kerning = value->u.pair_kerning;
2532 break;
2533 case LAYOUT_RANGE_ATTR_FONTCOLL:
2534 changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
2535 break;
2536 case LAYOUT_RANGE_ATTR_LOCALE:
2537 changed = strcmpiW(dest->locale, value->u.locale) != 0;
2538 if (changed) {
2539 strcpyW(dest->locale, value->u.locale);
2540 strlwrW(dest->locale);
2542 break;
2543 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2544 changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
2545 if (changed) {
2546 heap_free(dest->fontfamily);
2547 dest->fontfamily = heap_strdupW(value->u.fontfamily);
2549 break;
2550 case LAYOUT_RANGE_ATTR_SPACING:
2551 changed = dest_spacing->leading != value->u.spacing.leading ||
2552 dest_spacing->trailing != value->u.spacing.trailing ||
2553 dest_spacing->min_advance != value->u.spacing.min_advance;
2554 dest_spacing->leading = value->u.spacing.leading;
2555 dest_spacing->trailing = value->u.spacing.trailing;
2556 dest_spacing->min_advance = value->u.spacing.min_advance;
2557 break;
2558 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2559 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.typography);
2560 break;
2561 default:
2565 return changed;
2568 static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
2570 return (inner->startPosition >= outer->startPosition) &&
2571 (inner->startPosition + inner->length <= outer->startPosition + outer->length);
2574 static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
2576 if (r) *r = h->range;
2577 return S_OK;
2580 /* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */
2581 static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2583 struct layout_range_header *cur, *right, *left, *outer;
2584 BOOL changed = FALSE;
2585 struct list *ranges;
2586 DWRITE_TEXT_RANGE r;
2588 /* ignore zero length ranges */
2589 if (value->range.length == 0)
2590 return S_OK;
2592 /* select from ranges lists */
2593 switch (attr)
2595 case LAYOUT_RANGE_ATTR_WEIGHT:
2596 case LAYOUT_RANGE_ATTR_STYLE:
2597 case LAYOUT_RANGE_ATTR_STRETCH:
2598 case LAYOUT_RANGE_ATTR_FONTSIZE:
2599 case LAYOUT_RANGE_ATTR_INLINE:
2600 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2601 case LAYOUT_RANGE_ATTR_FONTCOLL:
2602 case LAYOUT_RANGE_ATTR_LOCALE:
2603 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2604 ranges = &layout->ranges;
2605 break;
2606 case LAYOUT_RANGE_ATTR_UNDERLINE:
2607 ranges = &layout->underline_ranges;
2608 break;
2609 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2610 ranges = &layout->strike_ranges;
2611 break;
2612 case LAYOUT_RANGE_ATTR_EFFECT:
2613 ranges = &layout->effects;
2614 break;
2615 case LAYOUT_RANGE_ATTR_SPACING:
2616 ranges = &layout->spacing;
2617 break;
2618 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2619 ranges = &layout->typographies;
2620 break;
2621 default:
2622 FIXME("unknown attr kind %d\n", attr);
2623 return E_FAIL;
2626 /* If new range is completely within existing range, split existing range in two */
2627 if ((outer = find_outer_range(ranges, &value->range))) {
2629 /* no need to add same range */
2630 if (is_same_layout_attrvalue(outer, attr, value))
2631 return S_OK;
2633 /* for matching range bounds just replace data */
2634 if (is_same_text_range(&outer->range, &value->range)) {
2635 changed = set_layout_range_attrval(outer, attr, value);
2636 goto done;
2639 /* add new range to the left */
2640 if (value->range.startPosition == outer->range.startPosition) {
2641 left = alloc_layout_range_from(outer, &value->range);
2642 if (!left) return E_OUTOFMEMORY;
2644 changed = set_layout_range_attrval(left, attr, value);
2645 list_add_before(&outer->entry, &left->entry);
2646 outer->range.startPosition += value->range.length;
2647 outer->range.length -= value->range.length;
2648 goto done;
2651 /* add new range to the right */
2652 if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
2653 right = alloc_layout_range_from(outer, &value->range);
2654 if (!right) return E_OUTOFMEMORY;
2656 changed = set_layout_range_attrval(right, attr, value);
2657 list_add_after(&outer->entry, &right->entry);
2658 outer->range.length -= value->range.length;
2659 goto done;
2662 r.startPosition = value->range.startPosition + value->range.length;
2663 r.length = outer->range.length + outer->range.startPosition - r.startPosition;
2665 /* right part */
2666 right = alloc_layout_range_from(outer, &r);
2667 /* new range in the middle */
2668 cur = alloc_layout_range_from(outer, &value->range);
2669 if (!right || !cur) {
2670 free_layout_range(right);
2671 free_layout_range(cur);
2672 return E_OUTOFMEMORY;
2675 /* reuse container range as a left part */
2676 outer->range.length = value->range.startPosition - outer->range.startPosition;
2678 /* new part */
2679 set_layout_range_attrval(cur, attr, value);
2681 list_add_after(&outer->entry, &cur->entry);
2682 list_add_after(&cur->entry, &right->entry);
2684 layout->recompute = RECOMPUTE_EVERYTHING;
2685 return S_OK;
2688 /* Now it's only possible that given range contains some existing ranges, fully or partially.
2689 Update all of them. */
2690 left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
2691 if (left->range.startPosition == value->range.startPosition)
2692 changed = set_layout_range_attrval(left, attr, value);
2693 else /* need to split */ {
2694 r.startPosition = value->range.startPosition;
2695 r.length = left->range.length - value->range.startPosition + left->range.startPosition;
2696 left->range.length -= r.length;
2697 cur = alloc_layout_range_from(left, &r);
2698 changed = set_layout_range_attrval(cur, attr, value);
2699 list_add_after(&left->entry, &cur->entry);
2701 cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
2703 /* for all existing ranges covered by new one update value */
2704 while (cur && is_in_layout_range(&value->range, &cur->range)) {
2705 changed |= set_layout_range_attrval(cur, attr, value);
2706 cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
2709 /* it's possible rightmost range intersects */
2710 if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
2711 r.startPosition = cur->range.startPosition;
2712 r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
2713 left = alloc_layout_range_from(cur, &r);
2714 changed |= set_layout_range_attrval(left, attr, value);
2715 cur->range.startPosition += left->range.length;
2716 cur->range.length -= left->range.length;
2717 list_add_before(&cur->entry, &left->entry);
2720 done:
2721 if (changed) {
2722 struct list *next, *i;
2724 layout->recompute = RECOMPUTE_EVERYTHING;
2725 i = list_head(ranges);
2726 while ((next = list_next(ranges, i))) {
2727 struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
2729 cur = LIST_ENTRY(i, struct layout_range_header, entry);
2730 if (is_same_layout_attributes(cur, next_range)) {
2731 /* remove similar range */
2732 cur->range.length += next_range->range.length;
2733 list_remove(next);
2734 free_layout_range(next_range);
2736 else
2737 i = list_next(ranges, i);
2741 return S_OK;
2744 static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
2746 const WCHAR *str;
2748 switch (kind) {
2749 case LAYOUT_RANGE_ATTR_LOCALE:
2750 str = range->locale;
2751 break;
2752 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2753 str = range->fontfamily;
2754 break;
2755 default:
2756 str = NULL;
2759 return str;
2762 static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2763 UINT32 *length, DWRITE_TEXT_RANGE *r)
2765 struct layout_range *range;
2766 const WCHAR *str;
2768 range = get_layout_range_by_pos(layout, position);
2769 if (!range) {
2770 *length = 0;
2771 return S_OK;
2774 str = get_string_attribute_ptr(range, kind);
2775 *length = strlenW(str);
2776 return return_range(&range->h, r);
2779 static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2780 WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
2782 struct layout_range *range;
2783 const WCHAR *str;
2785 if (length == 0)
2786 return E_INVALIDARG;
2788 ret[0] = 0;
2789 range = get_layout_range_by_pos(layout, position);
2790 if (!range)
2791 return E_INVALIDARG;
2793 str = get_string_attribute_ptr(range, kind);
2794 if (length < strlenW(str) + 1)
2795 return E_NOT_SUFFICIENT_BUFFER;
2797 strcpyW(ret, str);
2798 return return_range(&range->h, r);
2801 static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout3 *iface, REFIID riid, void **obj)
2803 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2805 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2807 *obj = NULL;
2809 if (IsEqualIID(riid, &IID_IDWriteTextLayout3) ||
2810 IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
2811 IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
2812 IsEqualIID(riid, &IID_IDWriteTextLayout) ||
2813 IsEqualIID(riid, &IID_IUnknown))
2815 *obj = iface;
2817 else if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
2818 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
2819 IsEqualIID(riid, &IID_IDWriteTextFormat))
2820 *obj = &This->IDWriteTextFormat2_iface;
2822 if (*obj) {
2823 IDWriteTextLayout3_AddRef(iface);
2824 return S_OK;
2827 WARN("%s not implemented.\n", debugstr_guid(riid));
2829 return E_NOINTERFACE;
2832 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface)
2834 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2835 ULONG ref = InterlockedIncrement(&This->ref);
2836 TRACE("(%p)->(%d)\n", This, ref);
2837 return ref;
2840 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface)
2842 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2843 ULONG ref = InterlockedDecrement(&This->ref);
2845 TRACE("(%p)->(%d)\n", This, ref);
2847 if (!ref) {
2848 IDWriteFactory5_Release(This->factory);
2849 free_layout_ranges_list(This);
2850 free_layout_eruns(This);
2851 free_layout_runs(This);
2852 release_format_data(&This->format);
2853 heap_free(This->nominal_breakpoints);
2854 heap_free(This->actual_breakpoints);
2855 heap_free(This->clustermetrics);
2856 heap_free(This->clusters);
2857 heap_free(This->linemetrics);
2858 heap_free(This->lines);
2859 heap_free(This->str);
2860 heap_free(This);
2863 return ref;
2866 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2868 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2869 return IDWriteTextFormat2_SetTextAlignment(&This->IDWriteTextFormat2_iface, alignment);
2872 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
2874 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2875 return IDWriteTextFormat2_SetParagraphAlignment(&This->IDWriteTextFormat2_iface, alignment);
2878 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping)
2880 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2881 return IDWriteTextFormat2_SetWordWrapping(&This->IDWriteTextFormat2_iface, wrapping);
2884 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction)
2886 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2887 return IDWriteTextFormat2_SetReadingDirection(&This->IDWriteTextFormat2_iface, direction);
2890 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction)
2892 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2893 TRACE("(%p)->(%d)\n", This, direction);
2894 return IDWriteTextFormat2_SetFlowDirection(&This->IDWriteTextFormat2_iface, direction);
2897 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop)
2899 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2900 TRACE("(%p)->(%.2f)\n", This, tabstop);
2901 return IDWriteTextFormat2_SetIncrementalTabStop(&This->IDWriteTextFormat2_iface, tabstop);
2904 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming,
2905 IDWriteInlineObject *trimming_sign)
2907 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2908 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
2909 return IDWriteTextFormat2_SetTrimming(&This->IDWriteTextFormat2_iface, trimming, trimming_sign);
2912 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2913 FLOAT line_spacing, FLOAT baseline)
2915 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2916 TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
2917 return IDWriteTextFormat1_SetLineSpacing((IDWriteTextFormat1 *)&This->IDWriteTextFormat2_iface, spacing,
2918 line_spacing, baseline);
2921 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface)
2923 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2924 return IDWriteTextFormat2_GetTextAlignment(&This->IDWriteTextFormat2_iface);
2927 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface)
2929 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2930 return IDWriteTextFormat2_GetParagraphAlignment(&This->IDWriteTextFormat2_iface);
2933 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface)
2935 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2936 return IDWriteTextFormat2_GetWordWrapping(&This->IDWriteTextFormat2_iface);
2939 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface)
2941 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2942 return IDWriteTextFormat2_GetReadingDirection(&This->IDWriteTextFormat2_iface);
2945 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface)
2947 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2948 return IDWriteTextFormat2_GetFlowDirection(&This->IDWriteTextFormat2_iface);
2951 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface)
2953 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2954 return IDWriteTextFormat2_GetIncrementalTabStop(&This->IDWriteTextFormat2_iface);
2957 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options,
2958 IDWriteInlineObject **trimming_sign)
2960 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2961 return IDWriteTextFormat2_GetTrimming(&This->IDWriteTextFormat2_iface, options, trimming_sign);
2964 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method,
2965 FLOAT *spacing, FLOAT *baseline)
2967 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2968 return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat *)&This->IDWriteTextFormat2_iface, method,
2969 spacing, baseline);
2972 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection)
2974 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2975 return IDWriteTextFormat2_GetFontCollection(&This->IDWriteTextFormat2_iface, collection);
2978 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface)
2980 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2981 return IDWriteTextFormat2_GetFontFamilyNameLength(&This->IDWriteTextFormat2_iface);
2984 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
2986 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2987 return IDWriteTextFormat2_GetFontFamilyName(&This->IDWriteTextFormat2_iface, name, size);
2990 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface)
2992 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2993 return IDWriteTextFormat2_GetFontWeight(&This->IDWriteTextFormat2_iface);
2996 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface)
2998 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2999 return IDWriteTextFormat2_GetFontStyle(&This->IDWriteTextFormat2_iface);
3002 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface)
3004 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3005 return IDWriteTextFormat2_GetFontStretch(&This->IDWriteTextFormat2_iface);
3008 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface)
3010 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3011 return IDWriteTextFormat2_GetFontSize(&This->IDWriteTextFormat2_iface);
3014 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface)
3016 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3017 return IDWriteTextFormat2_GetLocaleNameLength(&This->IDWriteTextFormat2_iface);
3020 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
3022 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3023 return IDWriteTextFormat2_GetLocaleName(&This->IDWriteTextFormat2_iface, name, size);
3026 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth)
3028 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3029 BOOL changed;
3031 TRACE("(%p)->(%.2f)\n", This, maxWidth);
3033 if (maxWidth < 0.0f)
3034 return E_INVALIDARG;
3036 changed = This->metrics.layoutWidth != maxWidth;
3037 This->metrics.layoutWidth = maxWidth;
3039 if (changed)
3040 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3041 return S_OK;
3044 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight)
3046 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3047 BOOL changed;
3049 TRACE("(%p)->(%.2f)\n", This, maxHeight);
3051 if (maxHeight < 0.0f)
3052 return E_INVALIDARG;
3054 changed = This->metrics.layoutHeight != maxHeight;
3055 This->metrics.layoutHeight = maxHeight;
3057 if (changed)
3058 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3059 return S_OK;
3062 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
3064 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3065 struct layout_range_attr_value value;
3067 TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
3069 value.range = range;
3070 value.u.collection = collection;
3071 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
3074 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
3076 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3077 struct layout_range_attr_value value;
3079 TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
3081 if (!name)
3082 return E_INVALIDARG;
3084 value.range = range;
3085 value.u.fontfamily = name;
3086 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
3089 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
3091 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3092 struct layout_range_attr_value value;
3094 TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
3096 if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
3097 return E_INVALIDARG;
3099 value.range = range;
3100 value.u.weight = weight;
3101 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
3104 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
3106 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3107 struct layout_range_attr_value value;
3109 TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
3111 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
3112 return E_INVALIDARG;
3114 value.range = range;
3115 value.u.style = style;
3116 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
3119 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
3121 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3122 struct layout_range_attr_value value;
3124 TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
3126 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
3127 return E_INVALIDARG;
3129 value.range = range;
3130 value.u.stretch = stretch;
3131 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
3134 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
3136 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3137 struct layout_range_attr_value value;
3139 TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
3141 if (size <= 0.0f)
3142 return E_INVALIDARG;
3144 value.range = range;
3145 value.u.fontsize = size;
3146 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
3149 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
3151 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3152 struct layout_range_attr_value value;
3154 TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
3156 value.range = range;
3157 value.u.underline = underline;
3158 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
3161 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
3163 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3164 struct layout_range_attr_value value;
3166 TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
3168 value.range = range;
3169 value.u.strikethrough = strikethrough;
3170 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
3173 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
3175 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3176 struct layout_range_attr_value value;
3178 TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
3180 value.range = range;
3181 value.u.effect = effect;
3182 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
3185 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
3187 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3188 struct layout_range_attr_value value;
3190 TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
3192 value.range = range;
3193 value.u.object = object;
3194 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
3197 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
3199 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3200 struct layout_range_attr_value value;
3202 TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range));
3204 value.range = range;
3205 value.u.typography = typography;
3206 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
3209 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
3211 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3212 struct layout_range_attr_value value;
3214 TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
3216 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
3217 return E_INVALIDARG;
3219 value.range = range;
3220 value.u.locale = locale;
3221 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
3224 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface)
3226 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3227 TRACE("(%p)\n", This);
3228 return This->metrics.layoutWidth;
3231 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface)
3233 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3234 TRACE("(%p)\n", This);
3235 return This->metrics.layoutHeight;
3238 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position,
3239 IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
3241 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3242 struct layout_range *range;
3244 TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
3246 if (position >= This->len)
3247 return S_OK;
3249 range = get_layout_range_by_pos(This, position);
3250 *collection = range->collection;
3251 if (*collection)
3252 IDWriteFontCollection_AddRef(*collection);
3254 return return_range(&range->h, r);
3257 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface,
3258 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3260 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3261 TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
3262 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
3265 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface,
3266 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
3268 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3269 TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
3270 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
3273 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface,
3274 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
3276 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3277 struct layout_range *range;
3279 TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
3281 if (position >= This->len)
3282 return S_OK;
3284 range = get_layout_range_by_pos(This, position);
3285 *weight = range->weight;
3287 return return_range(&range->h, r);
3290 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface,
3291 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
3293 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3294 struct layout_range *range;
3296 TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
3298 range = get_layout_range_by_pos(This, position);
3299 *style = range->style;
3300 return return_range(&range->h, r);
3303 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface,
3304 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
3306 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3307 struct layout_range *range;
3309 TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
3311 range = get_layout_range_by_pos(This, position);
3312 *stretch = range->stretch;
3313 return return_range(&range->h, r);
3316 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface,
3317 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
3319 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3320 struct layout_range *range;
3322 TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
3324 range = get_layout_range_by_pos(This, position);
3325 *size = range->fontsize;
3326 return return_range(&range->h, r);
3329 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface,
3330 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
3332 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3333 struct layout_range_bool *range;
3335 TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
3337 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position);
3338 *underline = range->value;
3340 return return_range(&range->h, r);
3343 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface,
3344 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
3346 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3347 struct layout_range_bool *range;
3349 TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
3351 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
3352 *strikethrough = range->value;
3354 return return_range(&range->h, r);
3357 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface,
3358 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
3360 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3361 struct layout_range_iface *range;
3363 TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
3365 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position);
3366 *effect = range->iface;
3367 if (*effect)
3368 IUnknown_AddRef(*effect);
3370 return return_range(&range->h, r);
3373 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface,
3374 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
3376 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3377 struct layout_range *range;
3379 TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
3381 if (position >= This->len)
3382 return S_OK;
3384 range = get_layout_range_by_pos(This, position);
3385 *object = range->object;
3386 if (*object)
3387 IDWriteInlineObject_AddRef(*object);
3389 return return_range(&range->h, r);
3392 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface,
3393 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
3395 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3396 struct layout_range_iface *range;
3398 TRACE("(%p)->(%u %p %p)\n", This, position, typography, r);
3400 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position);
3401 *typography = (IDWriteTypography*)range->iface;
3402 if (*typography)
3403 IDWriteTypography_AddRef(*typography);
3405 return return_range(&range->h, r);
3408 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface,
3409 UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
3411 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3412 TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
3413 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
3416 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface,
3417 UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
3419 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3420 TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
3421 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
3424 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
3425 const DWRITE_MATRIX *m)
3427 D2D1_POINT_2F vec, vec2;
3429 if (!skiptransform) {
3430 /* apply transform */
3431 vec.x = 0.0f;
3432 vec.y = coord * ppdip;
3434 vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx;
3435 vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy;
3437 /* snap */
3438 vec2.x = floorf(vec2.x + 0.5f);
3439 vec2.y = floorf(vec2.y + 0.5f);
3441 /* apply inverted transform, we don't care about X component at this point */
3442 vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3443 vec.y /= ppdip;
3445 else
3446 vec.y = floorf(coord * ppdip + 0.5f) / ppdip;
3448 return vec.y;
3451 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface,
3452 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
3454 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3455 BOOL disabled = FALSE, skiptransform = FALSE;
3456 struct layout_effective_inline *inlineobject;
3457 struct layout_effective_run *run;
3458 struct layout_strikethrough *s;
3459 struct layout_underline *u;
3460 FLOAT det = 0.0f, ppdip = 0.0f;
3461 DWRITE_MATRIX m = { 0 };
3462 HRESULT hr;
3464 TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
3466 hr = layout_compute_effective_runs(This);
3467 if (FAILED(hr))
3468 return hr;
3470 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
3471 if (FAILED(hr))
3472 return hr;
3474 if (!disabled) {
3475 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
3476 if (FAILED(hr))
3477 return hr;
3479 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
3480 if (FAILED(hr))
3481 return hr;
3483 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
3484 if (ppdip <= 0.0f ||
3485 (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
3486 (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
3487 disabled = TRUE;
3488 else
3489 skiptransform = should_skip_transform(&m, &det);
3492 #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
3493 /* 1. Regular runs */
3494 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3495 const struct regular_layout_run *regular = &run->run->u.regular;
3496 UINT32 start_glyph = regular->clustermap[run->start];
3497 DWRITE_GLYPH_RUN_DESCRIPTION descr;
3498 DWRITE_GLYPH_RUN glyph_run;
3500 /* Everything but cluster map will be reused from nominal run, as we only need
3501 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
3502 it can't be reused because it has to start with 0 index for each reported run. */
3503 glyph_run = regular->run;
3504 glyph_run.glyphCount = run->glyphcount;
3506 /* fixup glyph data arrays */
3507 glyph_run.glyphIndices += start_glyph;
3508 glyph_run.glyphAdvances += start_glyph;
3509 glyph_run.glyphOffsets += start_glyph;
3511 /* description */
3512 descr = regular->descr;
3513 descr.stringLength = run->length;
3514 descr.string += run->start;
3515 descr.clusterMap = run->clustermap;
3516 descr.textPosition += run->start;
3518 /* return value is ignored */
3519 IDWriteTextRenderer_DrawGlyphRun(renderer,
3520 context,
3521 run->origin.x + run->align_dx + origin_x,
3522 SNAP_COORD(run->origin.y + origin_y),
3523 This->measuringmode,
3524 &glyph_run,
3525 &descr,
3526 run->effect);
3529 /* 2. Inline objects */
3530 LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
3531 IDWriteTextRenderer_DrawInlineObject(renderer,
3532 context,
3533 inlineobject->origin.x + inlineobject->align_dx + origin_x,
3534 SNAP_COORD(inlineobject->origin.y + origin_y),
3535 inlineobject->object,
3536 inlineobject->is_sideways,
3537 inlineobject->is_rtl,
3538 inlineobject->effect);
3541 /* 3. Underlines */
3542 LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) {
3543 IDWriteTextRenderer_DrawUnderline(renderer,
3544 context,
3545 /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
3546 (is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x,
3547 SNAP_COORD(u->run->origin.y + origin_y),
3548 &u->u,
3549 u->run->effect);
3552 /* 4. Strikethrough */
3553 LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
3554 IDWriteTextRenderer_DrawStrikethrough(renderer,
3555 context,
3556 s->run->origin.x + s->run->align_dx + origin_x,
3557 SNAP_COORD(s->run->origin.y + origin_y),
3558 &s->s,
3559 s->run->effect);
3561 #undef SNAP_COORD
3563 return S_OK;
3566 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface,
3567 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
3569 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3570 HRESULT hr;
3572 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3574 hr = layout_compute_effective_runs(This);
3575 if (FAILED(hr))
3576 return hr;
3578 if (metrics) {
3579 UINT32 i, c = min(max_count, This->metrics.lineCount);
3580 for (i = 0; i < c; i++)
3581 memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics));
3584 *count = This->metrics.lineCount;
3585 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3588 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics)
3590 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3591 DWRITE_TEXT_METRICS1 metrics1;
3592 HRESULT hr;
3594 TRACE("(%p)->(%p)\n", This, metrics);
3596 hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1);
3597 if (hr == S_OK)
3598 memcpy(metrics, &metrics1, sizeof(*metrics));
3600 return hr;
3603 static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D1_RECT_F *ret)
3605 #define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em)
3606 ret->left = SCALE(bbox->left);
3607 ret->right = SCALE(bbox->right);
3608 ret->top = SCALE(bbox->top);
3609 ret->bottom = SCALE(bbox->bottom);
3610 #undef SCALE
3613 static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y)
3615 rect->left += x;
3616 rect->right += x;
3617 rect->top += y;
3618 rect->bottom += y;
3621 static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect)
3623 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
3626 static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src)
3628 if (d2d_rect_is_empty(dst)) {
3629 if (d2d_rect_is_empty(src)) {
3630 dst->left = dst->right = dst->top = dst->bottom = 0.0f;
3631 return;
3633 else
3634 *dst = *src;
3636 else {
3637 if (!d2d_rect_is_empty(src)) {
3638 dst->left = min(dst->left, src->left);
3639 dst->right = max(dst->right, src->right);
3640 dst->top = min(dst->top, src->top);
3641 dst->bottom = max(dst->bottom, src->bottom);
3646 static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox)
3648 const struct regular_layout_run *regular = &run->run->u.regular;
3649 UINT32 start_glyph = regular->clustermap[run->start];
3650 const DWRITE_GLYPH_RUN *glyph_run = &regular->run;
3651 DWRITE_FONT_METRICS font_metrics;
3652 D2D1_POINT_2F origin = { 0 };
3653 UINT32 i;
3655 if (run->bbox.top == run->bbox.bottom) {
3656 IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics);
3658 for (i = 0; i < run->glyphcount; i++) {
3659 D2D1_RECT_F glyph_bbox;
3660 RECT design_bbox;
3662 freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm,
3663 glyph_run->glyphIndices[i + start_glyph], &design_bbox);
3665 scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox);
3666 d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
3667 origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
3668 d2d_rect_union(&run->bbox, &glyph_bbox);
3670 /* FIXME: take care of vertical/rtl */
3671 origin.x += glyph_run->glyphAdvances[i + start_glyph];
3675 *bbox = run->bbox;
3676 d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y);
3679 static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run,
3680 D2D1_RECT_F *bbox)
3682 DWRITE_OVERHANG_METRICS overhang_metrics = { 0 };
3683 DWRITE_INLINE_OBJECT_METRICS metrics = { 0 };
3684 HRESULT hr;
3686 if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) {
3687 WARN("Failed to get inline object metrics, hr %#x.\n", hr);
3688 memset(bbox, 0, sizeof(*bbox));
3689 return;
3692 bbox->left = run->origin.x + run->align_dx;
3693 bbox->right = bbox->left + metrics.width;
3694 bbox->top = run->origin.y;
3695 bbox->bottom = bbox->top + metrics.height;
3697 IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics);
3699 bbox->left -= overhang_metrics.left;
3700 bbox->right += overhang_metrics.right;
3701 bbox->top -= overhang_metrics.top;
3702 bbox->bottom += overhang_metrics.bottom;
3705 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface,
3706 DWRITE_OVERHANG_METRICS *overhangs)
3708 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3709 struct layout_effective_inline *inline_run;
3710 struct layout_effective_run *run;
3711 D2D1_RECT_F bbox = { 0 };
3712 HRESULT hr;
3714 TRACE("(%p)->(%p)\n", This, overhangs);
3716 memset(overhangs, 0, sizeof(*overhangs));
3718 if (!(This->recompute & RECOMPUTE_OVERHANGS)) {
3719 *overhangs = This->overhangs;
3720 return S_OK;
3723 hr = layout_compute_effective_runs(This);
3724 if (FAILED(hr))
3725 return hr;
3727 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3728 D2D1_RECT_F run_bbox;
3730 layout_get_erun_bbox(This, run, &run_bbox);
3731 d2d_rect_union(&bbox, &run_bbox);
3734 LIST_FOR_EACH_ENTRY(inline_run, &This->inlineobjects, struct layout_effective_inline, entry) {
3735 D2D1_RECT_F object_bbox;
3737 layout_get_inlineobj_bbox(This, inline_run, &object_bbox);
3738 d2d_rect_union(&bbox, &object_bbox);
3741 /* Deltas from layout box. */
3742 This->overhangs.left = -bbox.left;
3743 This->overhangs.top = -bbox.top;
3744 This->overhangs.right = bbox.right - This->metrics.layoutWidth;
3745 This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight;
3746 This->recompute &= ~RECOMPUTE_OVERHANGS;
3748 *overhangs = This->overhangs;
3750 return S_OK;
3753 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface,
3754 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
3756 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3757 HRESULT hr;
3759 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3761 hr = layout_compute(This);
3762 if (FAILED(hr))
3763 return hr;
3765 if (metrics)
3766 memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
3768 *count = This->cluster_count;
3769 return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3772 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width)
3774 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3775 UINT32 start;
3776 FLOAT width;
3777 HRESULT hr;
3779 TRACE("(%p)->(%p)\n", This, min_width);
3781 if (!min_width)
3782 return E_INVALIDARG;
3784 if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
3785 goto width_done;
3787 *min_width = 0.0f;
3788 hr = layout_compute(This);
3789 if (FAILED(hr))
3790 return hr;
3792 /* Find widest word without emergency breaking between clusters, trailing whitespaces
3793 preceding breaking point do not contribute to word width. */
3794 for (start = 0; start < This->cluster_count;) {
3795 UINT32 end = start, j, next;
3797 /* Last cluster always could be wrapped after. */
3798 while (!This->clustermetrics[end].canWrapLineAfter)
3799 end++;
3800 /* make is so current cluster range that we can wrap after is [start,end) */
3801 end++;
3803 next = end;
3805 /* Ignore trailing whitespace clusters, in case of single space range will
3806 be reduced to empty range, or [start,start+1). */
3807 while (end > start && This->clustermetrics[end-1].isWhitespace)
3808 end--;
3810 /* check if cluster range exceeds last minimal width */
3811 width = 0.0f;
3812 for (j = start; j < end; j++)
3813 width += This->clustermetrics[j].width;
3815 start = next;
3817 if (width > This->minwidth)
3818 This->minwidth = width;
3820 This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3822 width_done:
3823 *min_width = This->minwidth;
3824 return S_OK;
3827 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface,
3828 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3830 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3831 FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
3832 return E_NOTIMPL;
3835 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface,
3836 UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
3838 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3839 FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
3840 return E_NOTIMPL;
3843 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface,
3844 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3845 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3847 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3848 FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
3849 max_metricscount, actual_metricscount);
3850 return E_NOTIMPL;
3853 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled,
3854 DWRITE_TEXT_RANGE range)
3856 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3857 struct layout_range_attr_value value;
3859 TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
3861 value.range = range;
3862 value.u.pair_kerning = !!is_pairkerning_enabled;
3863 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3866 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
3867 DWRITE_TEXT_RANGE *r)
3869 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3870 struct layout_range *range;
3872 TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
3874 if (position >= This->len)
3875 return S_OK;
3877 range = get_layout_range_by_pos(This, position);
3878 *is_pairkerning_enabled = range->pair_kerning;
3880 return return_range(&range->h, r);
3883 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing,
3884 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3886 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3887 struct layout_range_attr_value value;
3889 TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
3891 if (min_advance < 0.0f)
3892 return E_INVALIDARG;
3894 value.range = range;
3895 value.u.spacing.leading = leading;
3896 value.u.spacing.trailing = trailing;
3897 value.u.spacing.min_advance = min_advance;
3898 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
3901 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading,
3902 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3904 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3905 struct layout_range_spacing *range;
3907 TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
3909 range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
3910 *leading = range->leading;
3911 *trailing = range->trailing;
3912 *min_advance = range->min_advance;
3914 return return_range(&range->h, r);
3917 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics)
3919 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3920 HRESULT hr;
3922 TRACE("(%p)->(%p)\n", This, metrics);
3924 hr = layout_compute_effective_runs(This);
3925 if (FAILED(hr))
3926 return hr;
3928 *metrics = This->metrics;
3929 return S_OK;
3932 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3934 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3936 TRACE("(%p)->(%d)\n", This, orientation);
3938 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3939 return E_INVALIDARG;
3941 This->format.vertical_orientation = orientation;
3942 return S_OK;
3945 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface)
3947 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3948 TRACE("(%p)\n", This);
3949 return This->format.vertical_orientation;
3952 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled)
3954 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3955 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
3956 return IDWriteTextFormat2_SetLastLineWrapping(&This->IDWriteTextFormat2_iface, lastline_wrapping_enabled);
3959 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface)
3961 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3962 TRACE("(%p)\n", This);
3963 return IDWriteTextFormat2_GetLastLineWrapping(&This->IDWriteTextFormat2_iface);
3966 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3968 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3969 TRACE("(%p)->(%d)\n", This, alignment);
3970 return IDWriteTextFormat2_SetOpticalAlignment(&This->IDWriteTextFormat2_iface, alignment);
3973 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface)
3975 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3976 TRACE("(%p)\n", This);
3977 return IDWriteTextFormat2_GetOpticalAlignment(&This->IDWriteTextFormat2_iface);
3980 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback)
3982 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3983 TRACE("(%p)->(%p)\n", This, fallback);
3984 return set_fontfallback_for_format(&This->format, fallback);
3987 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback)
3989 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3990 TRACE("(%p)->(%p)\n", This, fallback);
3991 return get_fontfallback_from_format(&This->format, fallback);
3994 static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface)
3996 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3998 TRACE("(%p)\n", This);
4000 This->recompute = RECOMPUTE_EVERYTHING;
4001 return S_OK;
4004 static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing)
4006 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4007 BOOL changed;
4008 HRESULT hr;
4010 TRACE("(%p)->(%p)\n", This, spacing);
4012 hr = format_set_linespacing(&This->format, spacing, &changed);
4013 if (FAILED(hr))
4014 return hr;
4016 if (changed) {
4017 if (!(This->recompute & RECOMPUTE_LINES)) {
4018 UINT32 line;
4020 for (line = 0; line < This->metrics.lineCount; line++)
4021 layout_apply_line_spacing(This, line);
4023 layout_set_line_positions(This);
4026 This->recompute |= RECOMPUTE_OVERHANGS;
4029 return S_OK;
4032 static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing)
4034 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4036 TRACE("(%p)->(%p)\n", This, spacing);
4038 *spacing = This->format.spacing;
4039 return S_OK;
4042 static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics,
4043 UINT32 max_count, UINT32 *count)
4045 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4046 HRESULT hr;
4048 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
4050 hr = layout_compute_effective_runs(This);
4051 if (FAILED(hr))
4052 return hr;
4054 if (metrics)
4055 memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount));
4057 *count = This->metrics.lineCount;
4058 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
4061 static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = {
4062 dwritetextlayout_QueryInterface,
4063 dwritetextlayout_AddRef,
4064 dwritetextlayout_Release,
4065 dwritetextlayout_SetTextAlignment,
4066 dwritetextlayout_SetParagraphAlignment,
4067 dwritetextlayout_SetWordWrapping,
4068 dwritetextlayout_SetReadingDirection,
4069 dwritetextlayout_SetFlowDirection,
4070 dwritetextlayout_SetIncrementalTabStop,
4071 dwritetextlayout_SetTrimming,
4072 dwritetextlayout_SetLineSpacing,
4073 dwritetextlayout_GetTextAlignment,
4074 dwritetextlayout_GetParagraphAlignment,
4075 dwritetextlayout_GetWordWrapping,
4076 dwritetextlayout_GetReadingDirection,
4077 dwritetextlayout_GetFlowDirection,
4078 dwritetextlayout_GetIncrementalTabStop,
4079 dwritetextlayout_GetTrimming,
4080 dwritetextlayout_GetLineSpacing,
4081 dwritetextlayout_GetFontCollection,
4082 dwritetextlayout_GetFontFamilyNameLength,
4083 dwritetextlayout_GetFontFamilyName,
4084 dwritetextlayout_GetFontWeight,
4085 dwritetextlayout_GetFontStyle,
4086 dwritetextlayout_GetFontStretch,
4087 dwritetextlayout_GetFontSize,
4088 dwritetextlayout_GetLocaleNameLength,
4089 dwritetextlayout_GetLocaleName,
4090 dwritetextlayout_SetMaxWidth,
4091 dwritetextlayout_SetMaxHeight,
4092 dwritetextlayout_SetFontCollection,
4093 dwritetextlayout_SetFontFamilyName,
4094 dwritetextlayout_SetFontWeight,
4095 dwritetextlayout_SetFontStyle,
4096 dwritetextlayout_SetFontStretch,
4097 dwritetextlayout_SetFontSize,
4098 dwritetextlayout_SetUnderline,
4099 dwritetextlayout_SetStrikethrough,
4100 dwritetextlayout_SetDrawingEffect,
4101 dwritetextlayout_SetInlineObject,
4102 dwritetextlayout_SetTypography,
4103 dwritetextlayout_SetLocaleName,
4104 dwritetextlayout_GetMaxWidth,
4105 dwritetextlayout_GetMaxHeight,
4106 dwritetextlayout_layout_GetFontCollection,
4107 dwritetextlayout_layout_GetFontFamilyNameLength,
4108 dwritetextlayout_layout_GetFontFamilyName,
4109 dwritetextlayout_layout_GetFontWeight,
4110 dwritetextlayout_layout_GetFontStyle,
4111 dwritetextlayout_layout_GetFontStretch,
4112 dwritetextlayout_layout_GetFontSize,
4113 dwritetextlayout_GetUnderline,
4114 dwritetextlayout_GetStrikethrough,
4115 dwritetextlayout_GetDrawingEffect,
4116 dwritetextlayout_GetInlineObject,
4117 dwritetextlayout_GetTypography,
4118 dwritetextlayout_layout_GetLocaleNameLength,
4119 dwritetextlayout_layout_GetLocaleName,
4120 dwritetextlayout_Draw,
4121 dwritetextlayout_GetLineMetrics,
4122 dwritetextlayout_GetMetrics,
4123 dwritetextlayout_GetOverhangMetrics,
4124 dwritetextlayout_GetClusterMetrics,
4125 dwritetextlayout_DetermineMinWidth,
4126 dwritetextlayout_HitTestPoint,
4127 dwritetextlayout_HitTestTextPosition,
4128 dwritetextlayout_HitTestTextRange,
4129 dwritetextlayout1_SetPairKerning,
4130 dwritetextlayout1_GetPairKerning,
4131 dwritetextlayout1_SetCharacterSpacing,
4132 dwritetextlayout1_GetCharacterSpacing,
4133 dwritetextlayout2_GetMetrics,
4134 dwritetextlayout2_SetVerticalGlyphOrientation,
4135 dwritetextlayout2_GetVerticalGlyphOrientation,
4136 dwritetextlayout2_SetLastLineWrapping,
4137 dwritetextlayout2_GetLastLineWrapping,
4138 dwritetextlayout2_SetOpticalAlignment,
4139 dwritetextlayout2_GetOpticalAlignment,
4140 dwritetextlayout2_SetFontFallback,
4141 dwritetextlayout2_GetFontFallback,
4142 dwritetextlayout3_InvalidateLayout,
4143 dwritetextlayout3_SetLineSpacing,
4144 dwritetextlayout3_GetLineSpacing,
4145 dwritetextlayout3_GetLineMetrics
4148 static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
4150 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4151 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4152 return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj);
4155 static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat2 *iface)
4157 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4158 return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface);
4161 static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat2 *iface)
4163 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4164 return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface);
4167 static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat2 *iface,
4168 DWRITE_TEXT_ALIGNMENT alignment)
4170 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4171 BOOL changed;
4172 HRESULT hr;
4174 TRACE("(%p)->(%d)\n", This, alignment);
4176 hr = format_set_textalignment(&This->format, alignment, &changed);
4177 if (FAILED(hr))
4178 return hr;
4180 if (changed) {
4181 /* if layout is not ready there's nothing to align */
4182 if (!(This->recompute & RECOMPUTE_LINES))
4183 layout_apply_text_alignment(This);
4184 This->recompute |= RECOMPUTE_OVERHANGS;
4187 return S_OK;
4190 static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat2 *iface,
4191 DWRITE_PARAGRAPH_ALIGNMENT alignment)
4193 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4194 BOOL changed;
4195 HRESULT hr;
4197 TRACE("(%p)->(%d)\n", This, alignment);
4199 hr = format_set_paralignment(&This->format, alignment, &changed);
4200 if (FAILED(hr))
4201 return hr;
4203 if (changed) {
4204 /* if layout is not ready there's nothing to align */
4205 if (!(This->recompute & RECOMPUTE_LINES))
4206 layout_apply_par_alignment(This);
4207 This->recompute |= RECOMPUTE_OVERHANGS;
4210 return S_OK;
4213 static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
4215 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4216 BOOL changed;
4217 HRESULT hr;
4219 TRACE("(%p)->(%d)\n", This, wrapping);
4221 hr = format_set_wordwrapping(&This->format, wrapping, &changed);
4222 if (FAILED(hr))
4223 return hr;
4225 if (changed)
4226 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4228 return S_OK;
4231 static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat2 *iface,
4232 DWRITE_READING_DIRECTION direction)
4234 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4235 BOOL changed;
4236 HRESULT hr;
4238 TRACE("(%p)->(%d)\n", This, direction);
4240 hr = format_set_readingdirection(&This->format, direction, &changed);
4241 if (FAILED(hr))
4242 return hr;
4244 if (changed)
4245 This->recompute = RECOMPUTE_EVERYTHING;
4247 return S_OK;
4250 static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat2 *iface,
4251 DWRITE_FLOW_DIRECTION direction)
4253 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4254 BOOL changed;
4255 HRESULT hr;
4257 TRACE("(%p)->(%d)\n", This, direction);
4259 hr = format_set_flowdirection(&This->format, direction, &changed);
4260 if (FAILED(hr))
4261 return hr;
4263 if (changed)
4264 This->recompute = RECOMPUTE_EVERYTHING;
4266 return S_OK;
4269 static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
4271 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4272 FIXME("(%p)->(%f): stub\n", This, tabstop);
4273 return S_OK;
4276 static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
4277 IDWriteInlineObject *trimming_sign)
4279 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4280 BOOL changed;
4281 HRESULT hr;
4283 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
4285 hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed);
4287 if (changed)
4288 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4290 return hr;
4293 static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat2 *iface,
4294 DWRITE_LINE_SPACING_METHOD method, FLOAT height, FLOAT baseline)
4296 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4297 DWRITE_LINE_SPACING spacing;
4299 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
4301 spacing = This->format.spacing;
4302 spacing.method = method;
4303 spacing.height = height;
4304 spacing.baseline = baseline;
4305 return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing);
4308 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat2 *iface)
4310 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4311 TRACE("(%p)\n", This);
4312 return This->format.textalignment;
4315 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat2 *iface)
4317 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4318 TRACE("(%p)\n", This);
4319 return This->format.paralign;
4322 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat2 *iface)
4324 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4325 TRACE("(%p)\n", This);
4326 return This->format.wrapping;
4329 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat2 *iface)
4331 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4332 TRACE("(%p)\n", This);
4333 return This->format.readingdir;
4336 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat2 *iface)
4338 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4339 TRACE("(%p)\n", This);
4340 return This->format.flow;
4343 static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
4345 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4346 FIXME("(%p): stub\n", This);
4347 return 0.0f;
4350 static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
4351 IDWriteInlineObject **trimming_sign)
4353 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4355 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
4357 *options = This->format.trimming;
4358 *trimming_sign = This->format.trimmingsign;
4359 if (*trimming_sign)
4360 IDWriteInlineObject_AddRef(*trimming_sign);
4361 return S_OK;
4364 static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat2 *iface,
4365 DWRITE_LINE_SPACING_METHOD *method, FLOAT *spacing, FLOAT *baseline)
4367 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4369 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
4371 *method = This->format.spacing.method;
4372 *spacing = This->format.spacing.height;
4373 *baseline = This->format.spacing.baseline;
4374 return S_OK;
4377 static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat2 *iface,
4378 IDWriteFontCollection **collection)
4380 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4382 TRACE("(%p)->(%p)\n", This, collection);
4384 *collection = This->format.collection;
4385 if (*collection)
4386 IDWriteFontCollection_AddRef(*collection);
4387 return S_OK;
4390 static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
4392 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4393 TRACE("(%p)\n", This);
4394 return This->format.family_len;
4397 static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
4399 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4401 TRACE("(%p)->(%p %u)\n", This, name, size);
4403 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4404 strcpyW(name, This->format.family_name);
4405 return S_OK;
4408 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat2 *iface)
4410 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4411 TRACE("(%p)\n", This);
4412 return This->format.weight;
4415 static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat2 *iface)
4417 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4418 TRACE("(%p)\n", This);
4419 return This->format.style;
4422 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat2 *iface)
4424 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4425 TRACE("(%p)\n", This);
4426 return This->format.stretch;
4429 static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat2 *iface)
4431 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4432 TRACE("(%p)\n", This);
4433 return This->format.fontsize;
4436 static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat2 *iface)
4438 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4439 TRACE("(%p)\n", This);
4440 return This->format.locale_len;
4443 static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
4445 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4447 TRACE("(%p)->(%p %u)\n", This, name, size);
4449 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4450 strcpyW(name, This->format.locale);
4451 return S_OK;
4454 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface,
4455 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4457 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4458 FIXME("(%p)->(%d): stub\n", This, orientation);
4459 return E_NOTIMPL;
4462 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
4464 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4465 FIXME("(%p): stub\n", This);
4466 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4469 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat2 *iface,
4470 BOOL lastline_wrapping_enabled)
4472 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4474 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
4476 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
4477 return S_OK;
4480 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat2 *iface)
4482 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4483 TRACE("(%p)\n", This);
4484 return This->format.last_line_wrapping;
4487 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat2 *iface,
4488 DWRITE_OPTICAL_ALIGNMENT alignment)
4490 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4491 TRACE("(%p)->(%d)\n", This, alignment);
4492 return format_set_optical_alignment(&This->format, alignment);
4495 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat2 *iface)
4497 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4498 TRACE("(%p)\n", This);
4499 return This->format.optical_alignment;
4502 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat2 *iface,
4503 IDWriteFontFallback *fallback)
4505 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4506 TRACE("(%p)->(%p)\n", This, fallback);
4507 return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4510 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat2 *iface,
4511 IDWriteFontFallback **fallback)
4513 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4514 TRACE("(%p)->(%p)\n", This, fallback);
4515 return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4518 static HRESULT WINAPI dwritetextformat2_layout_SetLineSpacing(IDWriteTextFormat2 *iface,
4519 DWRITE_LINE_SPACING const *spacing)
4521 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4522 return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, spacing);
4525 static HRESULT WINAPI dwritetextformat2_layout_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
4527 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat2(iface);
4528 return IDWriteTextLayout3_GetLineSpacing(&This->IDWriteTextLayout3_iface, spacing);
4531 static const IDWriteTextFormat2Vtbl dwritetextformat2_layout_vtbl = {
4532 dwritetextformat_layout_QueryInterface,
4533 dwritetextformat_layout_AddRef,
4534 dwritetextformat_layout_Release,
4535 dwritetextformat_layout_SetTextAlignment,
4536 dwritetextformat_layout_SetParagraphAlignment,
4537 dwritetextformat_layout_SetWordWrapping,
4538 dwritetextformat_layout_SetReadingDirection,
4539 dwritetextformat_layout_SetFlowDirection,
4540 dwritetextformat_layout_SetIncrementalTabStop,
4541 dwritetextformat_layout_SetTrimming,
4542 dwritetextformat_layout_SetLineSpacing,
4543 dwritetextformat_layout_GetTextAlignment,
4544 dwritetextformat_layout_GetParagraphAlignment,
4545 dwritetextformat_layout_GetWordWrapping,
4546 dwritetextformat_layout_GetReadingDirection,
4547 dwritetextformat_layout_GetFlowDirection,
4548 dwritetextformat_layout_GetIncrementalTabStop,
4549 dwritetextformat_layout_GetTrimming,
4550 dwritetextformat_layout_GetLineSpacing,
4551 dwritetextformat_layout_GetFontCollection,
4552 dwritetextformat_layout_GetFontFamilyNameLength,
4553 dwritetextformat_layout_GetFontFamilyName,
4554 dwritetextformat_layout_GetFontWeight,
4555 dwritetextformat_layout_GetFontStyle,
4556 dwritetextformat_layout_GetFontStretch,
4557 dwritetextformat_layout_GetFontSize,
4558 dwritetextformat_layout_GetLocaleNameLength,
4559 dwritetextformat_layout_GetLocaleName,
4560 dwritetextformat1_layout_SetVerticalGlyphOrientation,
4561 dwritetextformat1_layout_GetVerticalGlyphOrientation,
4562 dwritetextformat1_layout_SetLastLineWrapping,
4563 dwritetextformat1_layout_GetLastLineWrapping,
4564 dwritetextformat1_layout_SetOpticalAlignment,
4565 dwritetextformat1_layout_GetOpticalAlignment,
4566 dwritetextformat1_layout_SetFontFallback,
4567 dwritetextformat1_layout_GetFontFallback,
4568 dwritetextformat2_layout_SetLineSpacing,
4569 dwritetextformat2_layout_GetLineSpacing,
4572 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
4573 REFIID riid, void **obj)
4575 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
4576 IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
4577 IsEqualIID(riid, &IID_IUnknown))
4579 *obj = iface;
4580 IDWriteTextAnalysisSink1_AddRef(iface);
4581 return S_OK;
4584 WARN("%s not implemented.\n", debugstr_guid(riid));
4586 *obj = NULL;
4587 return E_NOINTERFACE;
4590 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
4592 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4593 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4596 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
4598 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4599 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4602 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
4603 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
4605 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4606 struct layout_run *run;
4608 TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
4610 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position);
4611 if (!run)
4612 return E_OUTOFMEMORY;
4614 run->u.regular.descr.string = &layout->str[position];
4615 run->u.regular.descr.stringLength = length;
4616 run->u.regular.descr.textPosition = position;
4617 run->u.regular.sa = *sa;
4618 list_add_tail(&layout->runs, &run->entry);
4619 return S_OK;
4622 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
4623 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
4625 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4627 if (position + length > layout->len)
4628 return E_FAIL;
4630 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
4631 return S_OK;
4634 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
4635 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
4637 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4638 struct layout_run *cur_run;
4640 TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
4642 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
4643 struct regular_layout_run *cur = &cur_run->u.regular;
4644 struct layout_run *run;
4646 if (cur_run->kind == LAYOUT_RUN_INLINE)
4647 continue;
4649 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
4650 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
4651 continue;
4653 /* full hit - just set run level */
4654 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
4655 cur->run.bidiLevel = resolvedLevel;
4656 break;
4659 /* current run is fully covered, move to next one */
4660 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
4661 cur->run.bidiLevel = resolvedLevel;
4662 position += cur->descr.stringLength;
4663 length -= cur->descr.stringLength;
4664 continue;
4667 /* all fully covered runs are processed at this point, reuse existing run for remaining
4668 reported bidi range and add another run for the rest of original one */
4670 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length);
4671 if (!run)
4672 return E_OUTOFMEMORY;
4674 *run = *cur_run;
4675 run->u.regular.descr.textPosition = position + length;
4676 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
4677 run->u.regular.descr.string = &layout->str[position + length];
4679 /* reduce existing run */
4680 cur->run.bidiLevel = resolvedLevel;
4681 cur->descr.stringLength = length;
4683 list_add_after(&cur_run->entry, &run->entry);
4684 break;
4687 return S_OK;
4690 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
4691 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
4693 return E_NOTIMPL;
4696 static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
4697 UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
4698 BOOL is_sideways, BOOL is_rtl)
4700 return E_NOTIMPL;
4703 static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
4704 dwritetextlayout_sink_QueryInterface,
4705 dwritetextlayout_sink_AddRef,
4706 dwritetextlayout_sink_Release,
4707 dwritetextlayout_sink_SetScriptAnalysis,
4708 dwritetextlayout_sink_SetLineBreakpoints,
4709 dwritetextlayout_sink_SetBidiLevel,
4710 dwritetextlayout_sink_SetNumberSubstitution,
4711 dwritetextlayout_sink_SetGlyphOrientation
4714 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
4715 REFIID riid, void **obj)
4717 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
4718 IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
4719 IsEqualIID(riid, &IID_IUnknown))
4721 *obj = iface;
4722 IDWriteTextAnalysisSource1_AddRef(iface);
4723 return S_OK;
4726 WARN("%s not implemented.\n", debugstr_guid(riid));
4728 *obj = NULL;
4729 return E_NOINTERFACE;
4732 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
4734 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4735 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4738 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
4740 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4741 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4744 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
4745 UINT32 position, WCHAR const** text, UINT32* text_len)
4747 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4749 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4751 if (position < layout->len) {
4752 *text = &layout->str[position];
4753 *text_len = layout->len - position;
4755 else {
4756 *text = NULL;
4757 *text_len = 0;
4760 return S_OK;
4763 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
4764 UINT32 position, WCHAR const** text, UINT32* text_len)
4766 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4768 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4770 if (position > 0 && position < layout->len) {
4771 *text = layout->str;
4772 *text_len = position;
4774 else {
4775 *text = NULL;
4776 *text_len = 0;
4779 return S_OK;
4782 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
4784 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4785 return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface);
4788 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
4789 UINT32 position, UINT32* text_len, WCHAR const** locale)
4791 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4792 struct layout_range *range = get_layout_range_by_pos(layout, position);
4794 if (position < layout->len) {
4795 struct layout_range *next;
4797 *locale = range->locale;
4798 *text_len = range->h.range.length - position;
4800 next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
4801 while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
4802 *text_len += next->h.range.length;
4803 next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
4806 *text_len = min(*text_len, layout->len - position);
4808 else {
4809 *locale = NULL;
4810 *text_len = 0;
4813 return S_OK;
4816 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
4817 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
4819 FIXME("%u %p %p: stub\n", position, text_len, substitution);
4820 return E_NOTIMPL;
4823 static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
4824 UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
4826 FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
4827 return E_NOTIMPL;
4830 static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
4831 dwritetextlayout_source_QueryInterface,
4832 dwritetextlayout_source_AddRef,
4833 dwritetextlayout_source_Release,
4834 dwritetextlayout_source_GetTextAtPosition,
4835 dwritetextlayout_source_GetTextBeforePosition,
4836 dwritetextlayout_source_GetParagraphReadingDirection,
4837 dwritetextlayout_source_GetLocaleName,
4838 dwritetextlayout_source_GetNumberSubstitution,
4839 dwritetextlayout_source_GetVerticalGlyphOrientation
4842 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
4844 struct dwrite_textformat *textformat;
4845 IDWriteTextFormat1 *format1;
4846 UINT32 len;
4847 HRESULT hr;
4849 if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
4850 layout->format = textformat->format;
4852 layout->format.locale = heap_strdupW(textformat->format.locale);
4853 layout->format.family_name = heap_strdupW(textformat->format.family_name);
4854 if (!layout->format.locale || !layout->format.family_name)
4856 heap_free(layout->format.locale);
4857 heap_free(layout->format.family_name);
4858 return E_OUTOFMEMORY;
4861 if (layout->format.trimmingsign)
4862 IDWriteInlineObject_AddRef(layout->format.trimmingsign);
4863 if (layout->format.collection)
4864 IDWriteFontCollection_AddRef(layout->format.collection);
4865 if (layout->format.fallback)
4866 IDWriteFontFallback_AddRef(layout->format.fallback);
4868 return S_OK;
4871 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
4872 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
4873 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
4874 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
4875 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
4876 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
4877 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
4878 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
4879 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
4880 layout->format.fallback = NULL;
4881 layout->format.spacing.leadingBefore = 0.0f;
4882 layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
4883 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
4884 &layout->format.spacing.height, &layout->format.spacing.baseline);
4885 if (FAILED(hr))
4886 return hr;
4888 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
4889 if (FAILED(hr))
4890 return hr;
4892 /* locale name and length */
4893 len = IDWriteTextFormat_GetLocaleNameLength(format);
4894 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
4895 if (!layout->format.locale)
4896 return E_OUTOFMEMORY;
4898 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
4899 if (FAILED(hr))
4900 return hr;
4901 layout->format.locale_len = len;
4903 /* font family name and length */
4904 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
4905 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
4906 if (!layout->format.family_name)
4907 return E_OUTOFMEMORY;
4909 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
4910 if (FAILED(hr))
4911 return hr;
4912 layout->format.family_len = len;
4914 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4915 if (hr == S_OK) {
4916 IDWriteTextFormat2 *format2;
4918 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
4919 layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4920 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
4922 if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
4923 IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
4924 IDWriteTextFormat2_Release(format2);
4927 IDWriteTextFormat1_Release(format1);
4929 else {
4930 layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4931 layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
4934 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
4937 static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
4939 struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
4940 static const DWRITE_TEXT_RANGE r = { 0, ~0u };
4941 HRESULT hr;
4943 layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl;
4944 layout->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformat2_layout_vtbl;
4945 layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
4946 layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
4947 layout->ref = 1;
4948 layout->len = desc->length;
4949 layout->recompute = RECOMPUTE_EVERYTHING;
4950 layout->nominal_breakpoints = NULL;
4951 layout->actual_breakpoints = NULL;
4952 layout->cluster_count = 0;
4953 layout->clustermetrics = NULL;
4954 layout->clusters = NULL;
4955 layout->linemetrics = NULL;
4956 layout->lines = NULL;
4957 layout->line_alloc = 0;
4958 layout->minwidth = 0.0f;
4959 list_init(&layout->eruns);
4960 list_init(&layout->inlineobjects);
4961 list_init(&layout->underlines);
4962 list_init(&layout->strikethrough);
4963 list_init(&layout->runs);
4964 list_init(&layout->ranges);
4965 list_init(&layout->strike_ranges);
4966 list_init(&layout->underline_ranges);
4967 list_init(&layout->effects);
4968 list_init(&layout->spacing);
4969 list_init(&layout->typographies);
4970 memset(&layout->format, 0, sizeof(layout->format));
4971 memset(&layout->metrics, 0, sizeof(layout->metrics));
4972 layout->metrics.layoutWidth = desc->max_width;
4973 layout->metrics.layoutHeight = desc->max_height;
4974 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4976 layout->ppdip = 0.0f;
4977 memset(&layout->transform, 0, sizeof(layout->transform));
4979 layout->str = heap_strdupnW(desc->string, desc->length);
4980 if (desc->length && !layout->str) {
4981 hr = E_OUTOFMEMORY;
4982 goto fail;
4985 hr = layout_format_from_textformat(layout, desc->format);
4986 if (FAILED(hr))
4987 goto fail;
4989 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
4990 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
4991 underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
4992 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
4993 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
4994 typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
4995 if (!range || !strike || !effect || !spacing || !typography || !underline) {
4996 free_layout_range(range);
4997 free_layout_range(strike);
4998 free_layout_range(underline);
4999 free_layout_range(effect);
5000 free_layout_range(spacing);
5001 free_layout_range(typography);
5002 hr = E_OUTOFMEMORY;
5003 goto fail;
5006 if (desc->is_gdi_compatible)
5007 layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
5008 else
5009 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
5010 layout->ppdip = desc->ppdip;
5011 layout->transform = desc->transform ? *desc->transform : identity;
5013 layout->factory = desc->factory;
5014 IDWriteFactory5_AddRef(layout->factory);
5015 list_add_head(&layout->ranges, &range->entry);
5016 list_add_head(&layout->strike_ranges, &strike->entry);
5017 list_add_head(&layout->underline_ranges, &underline->entry);
5018 list_add_head(&layout->effects, &effect->entry);
5019 list_add_head(&layout->spacing, &spacing->entry);
5020 list_add_head(&layout->typographies, &typography->entry);
5021 return S_OK;
5023 fail:
5024 IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
5025 return hr;
5028 HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret)
5030 struct dwrite_textlayout *layout;
5031 HRESULT hr;
5033 *ret = NULL;
5035 if (!desc->format || !desc->string)
5036 return E_INVALIDARG;
5038 layout = heap_alloc(sizeof(struct dwrite_textlayout));
5039 if (!layout) return E_OUTOFMEMORY;
5041 hr = init_textlayout(desc, layout);
5042 if (hr == S_OK)
5043 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface;
5045 return hr;
5048 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
5050 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5052 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5054 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
5055 *obj = iface;
5056 IDWriteInlineObject_AddRef(iface);
5057 return S_OK;
5060 WARN("%s not implemented.\n", debugstr_guid(riid));
5062 *obj = NULL;
5063 return E_NOINTERFACE;
5066 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
5068 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5069 ULONG ref = InterlockedIncrement(&This->ref);
5070 TRACE("(%p)->(%d)\n", This, ref);
5071 return ref;
5074 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
5076 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5077 ULONG ref = InterlockedDecrement(&This->ref);
5079 TRACE("(%p)->(%d)\n", This, ref);
5081 if (!ref) {
5082 IDWriteTextLayout_Release(This->layout);
5083 heap_free(This);
5086 return ref;
5089 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
5090 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
5092 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5093 DWRITE_LINE_METRICS line;
5094 UINT32 line_count;
5096 TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY,
5097 is_sideways, is_rtl, effect);
5099 IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count);
5100 return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline);
5103 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
5105 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5106 DWRITE_TEXT_METRICS metrics;
5107 HRESULT hr;
5109 TRACE("(%p)->(%p)\n", This, ret);
5111 hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
5112 if (FAILED(hr)) {
5113 memset(ret, 0, sizeof(*ret));
5114 return hr;
5117 ret->width = metrics.width;
5118 ret->height = 0.0f;
5119 ret->baseline = 0.0f;
5120 ret->supportsSideways = FALSE;
5121 return S_OK;
5124 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
5126 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5127 TRACE("(%p)->(%p)\n", This, overhangs);
5128 return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs);
5131 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
5132 DWRITE_BREAK_CONDITION *after)
5134 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5136 TRACE("(%p)->(%p %p)\n", This, before, after);
5138 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
5139 return S_OK;
5142 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
5143 dwritetrimmingsign_QueryInterface,
5144 dwritetrimmingsign_AddRef,
5145 dwritetrimmingsign_Release,
5146 dwritetrimmingsign_Draw,
5147 dwritetrimmingsign_GetMetrics,
5148 dwritetrimmingsign_GetOverhangMetrics,
5149 dwritetrimmingsign_GetBreakConditions
5152 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
5154 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
5155 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
5158 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
5160 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
5161 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
5164 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
5166 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
5167 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
5170 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
5172 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
5173 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
5176 HRESULT create_trimmingsign(IDWriteFactory5 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
5178 static const WCHAR ellipsisW = 0x2026;
5179 struct dwrite_trimmingsign *This;
5180 DWRITE_READING_DIRECTION reading;
5181 DWRITE_FLOW_DIRECTION flow;
5182 HRESULT hr;
5184 *sign = NULL;
5186 /* Validate reading/flow direction here, layout creation won't complain about
5187 invalid combinations. */
5188 reading = IDWriteTextFormat_GetReadingDirection(format);
5189 flow = IDWriteTextFormat_GetFlowDirection(format);
5191 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
5192 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
5193 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
5195 This = heap_alloc(sizeof(*This));
5196 if (!This)
5197 return E_OUTOFMEMORY;
5199 This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
5200 This->ref = 1;
5202 hr = IDWriteFactory5_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout);
5203 if (FAILED(hr)) {
5204 heap_free(This);
5205 return hr;
5208 IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
5209 IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
5210 IDWriteTextLayout_SetTextAlignment(This->layout, DWRITE_TEXT_ALIGNMENT_LEADING);
5212 *sign = &This->IDWriteInlineObject_iface;
5214 return S_OK;
5217 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
5219 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5221 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5223 if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
5224 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
5225 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
5226 IsEqualIID(riid, &IID_IUnknown))
5228 *obj = iface;
5229 IDWriteTextFormat2_AddRef(iface);
5230 return S_OK;
5233 WARN("%s not implemented.\n", debugstr_guid(riid));
5235 *obj = NULL;
5237 return E_NOINTERFACE;
5240 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface)
5242 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5243 ULONG ref = InterlockedIncrement(&This->ref);
5244 TRACE("(%p)->(%d)\n", This, ref);
5245 return ref;
5248 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface)
5250 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5251 ULONG ref = InterlockedDecrement(&This->ref);
5253 TRACE("(%p)->(%d)\n", This, ref);
5255 if (!ref)
5257 release_format_data(&This->format);
5258 heap_free(This);
5261 return ref;
5264 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
5266 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5267 TRACE("(%p)->(%d)\n", This, alignment);
5268 return format_set_textalignment(&This->format, alignment, NULL);
5271 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
5273 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5274 TRACE("(%p)->(%d)\n", This, alignment);
5275 return format_set_paralignment(&This->format, alignment, NULL);
5278 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
5280 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5281 TRACE("(%p)->(%d)\n", This, wrapping);
5282 return format_set_wordwrapping(&This->format, wrapping, NULL);
5285 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction)
5287 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5288 TRACE("(%p)->(%d)\n", This, direction);
5289 return format_set_readingdirection(&This->format, direction, NULL);
5292 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction)
5294 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5295 TRACE("(%p)->(%d)\n", This, direction);
5296 return format_set_flowdirection(&This->format, direction, NULL);
5299 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
5301 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5302 FIXME("(%p)->(%f): stub\n", This, tabstop);
5303 return S_OK;
5306 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
5307 IDWriteInlineObject *trimming_sign)
5309 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5310 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
5311 return format_set_trimming(&This->format, trimming, trimming_sign, NULL);
5314 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method,
5315 FLOAT height, FLOAT baseline)
5317 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5318 DWRITE_LINE_SPACING spacing;
5320 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
5322 spacing = This->format.spacing;
5323 spacing.method = method;
5324 spacing.height = height;
5325 spacing.baseline = baseline;
5327 return format_set_linespacing(&This->format, &spacing, NULL);
5330 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface)
5332 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5333 TRACE("(%p)\n", This);
5334 return This->format.textalignment;
5337 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface)
5339 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5340 TRACE("(%p)\n", This);
5341 return This->format.paralign;
5344 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface)
5346 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5347 TRACE("(%p)\n", This);
5348 return This->format.wrapping;
5351 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface)
5353 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5354 TRACE("(%p)\n", This);
5355 return This->format.readingdir;
5358 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface)
5360 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5361 TRACE("(%p)\n", This);
5362 return This->format.flow;
5365 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
5367 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5368 FIXME("(%p): stub\n", This);
5369 return 0.0f;
5372 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
5373 IDWriteInlineObject **trimming_sign)
5375 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5376 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
5378 *options = This->format.trimming;
5379 if ((*trimming_sign = This->format.trimmingsign))
5380 IDWriteInlineObject_AddRef(*trimming_sign);
5382 return S_OK;
5385 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method,
5386 FLOAT *spacing, FLOAT *baseline)
5388 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5389 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
5391 *method = This->format.spacing.method;
5392 *spacing = This->format.spacing.height;
5393 *baseline = This->format.spacing.baseline;
5394 return S_OK;
5397 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection)
5399 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5401 TRACE("(%p)->(%p)\n", This, collection);
5403 *collection = This->format.collection;
5404 IDWriteFontCollection_AddRef(*collection);
5406 return S_OK;
5409 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
5411 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5412 TRACE("(%p)\n", This);
5413 return This->format.family_len;
5416 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5418 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5420 TRACE("(%p)->(%p %u)\n", This, name, size);
5422 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
5423 strcpyW(name, This->format.family_name);
5424 return S_OK;
5427 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface)
5429 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5430 TRACE("(%p)\n", This);
5431 return This->format.weight;
5434 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface)
5436 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5437 TRACE("(%p)\n", This);
5438 return This->format.style;
5441 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface)
5443 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5444 TRACE("(%p)\n", This);
5445 return This->format.stretch;
5448 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface)
5450 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5451 TRACE("(%p)\n", This);
5452 return This->format.fontsize;
5455 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface)
5457 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5458 TRACE("(%p)\n", This);
5459 return This->format.locale_len;
5462 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5464 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5466 TRACE("(%p)->(%p %u)\n", This, name, size);
5468 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
5469 strcpyW(name, This->format.locale);
5470 return S_OK;
5473 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
5475 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5477 TRACE("(%p)->(%d)\n", This, orientation);
5479 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
5480 return E_INVALIDARG;
5482 This->format.vertical_orientation = orientation;
5483 return S_OK;
5486 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
5488 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5489 TRACE("(%p)\n", This);
5490 return This->format.vertical_orientation;
5493 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled)
5495 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5497 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
5499 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
5500 return S_OK;
5503 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface)
5505 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5506 TRACE("(%p)\n", This);
5507 return This->format.last_line_wrapping;
5510 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
5512 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5513 TRACE("(%p)->(%d)\n", This, alignment);
5514 return format_set_optical_alignment(&This->format, alignment);
5517 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface)
5519 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5520 TRACE("(%p)\n", This);
5521 return This->format.optical_alignment;
5524 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback)
5526 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5527 TRACE("(%p)->(%p)\n", This, fallback);
5528 return set_fontfallback_for_format(&This->format, fallback);
5531 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback)
5533 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5534 TRACE("(%p)->(%p)\n", This, fallback);
5535 return get_fontfallback_from_format(&This->format, fallback);
5538 static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing)
5540 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5541 TRACE("(%p)->(%p)\n", This, spacing);
5542 return format_set_linespacing(&This->format, spacing, NULL);
5545 static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
5547 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5549 TRACE("(%p)->(%p)\n", This, spacing);
5551 *spacing = This->format.spacing;
5552 return S_OK;
5555 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = {
5556 dwritetextformat_QueryInterface,
5557 dwritetextformat_AddRef,
5558 dwritetextformat_Release,
5559 dwritetextformat_SetTextAlignment,
5560 dwritetextformat_SetParagraphAlignment,
5561 dwritetextformat_SetWordWrapping,
5562 dwritetextformat_SetReadingDirection,
5563 dwritetextformat_SetFlowDirection,
5564 dwritetextformat_SetIncrementalTabStop,
5565 dwritetextformat_SetTrimming,
5566 dwritetextformat_SetLineSpacing,
5567 dwritetextformat_GetTextAlignment,
5568 dwritetextformat_GetParagraphAlignment,
5569 dwritetextformat_GetWordWrapping,
5570 dwritetextformat_GetReadingDirection,
5571 dwritetextformat_GetFlowDirection,
5572 dwritetextformat_GetIncrementalTabStop,
5573 dwritetextformat_GetTrimming,
5574 dwritetextformat_GetLineSpacing,
5575 dwritetextformat_GetFontCollection,
5576 dwritetextformat_GetFontFamilyNameLength,
5577 dwritetextformat_GetFontFamilyName,
5578 dwritetextformat_GetFontWeight,
5579 dwritetextformat_GetFontStyle,
5580 dwritetextformat_GetFontStretch,
5581 dwritetextformat_GetFontSize,
5582 dwritetextformat_GetLocaleNameLength,
5583 dwritetextformat_GetLocaleName,
5584 dwritetextformat1_SetVerticalGlyphOrientation,
5585 dwritetextformat1_GetVerticalGlyphOrientation,
5586 dwritetextformat1_SetLastLineWrapping,
5587 dwritetextformat1_GetLastLineWrapping,
5588 dwritetextformat1_SetOpticalAlignment,
5589 dwritetextformat1_GetOpticalAlignment,
5590 dwritetextformat1_SetFontFallback,
5591 dwritetextformat1_GetFontFallback,
5592 dwritetextformat2_SetLineSpacing,
5593 dwritetextformat2_GetLineSpacing
5596 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
5598 return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
5599 CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL;
5602 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
5603 DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
5605 struct dwrite_textformat *This;
5607 *format = NULL;
5609 if (size <= 0.0f)
5610 return E_INVALIDARG;
5612 if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
5613 ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
5614 ((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
5615 return E_INVALIDARG;
5617 This = heap_alloc(sizeof(struct dwrite_textformat));
5618 if (!This) return E_OUTOFMEMORY;
5620 This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl;
5621 This->ref = 1;
5622 This->format.family_name = heap_strdupW(family_name);
5623 This->format.family_len = strlenW(family_name);
5624 This->format.locale = heap_strdupW(locale);
5625 This->format.locale_len = strlenW(locale);
5626 /* force locale name to lower case, layout will inherit this modified value */
5627 strlwrW(This->format.locale);
5628 This->format.weight = weight;
5629 This->format.style = style;
5630 This->format.fontsize = size;
5631 This->format.stretch = stretch;
5632 This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
5633 This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
5634 This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
5635 This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
5636 This->format.last_line_wrapping = TRUE;
5637 This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
5638 This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
5639 This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT;
5640 This->format.spacing.height = 0.0f;
5641 This->format.spacing.baseline = 0.0f;
5642 This->format.spacing.leadingBefore = 0.0f;
5643 This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
5644 This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
5645 This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
5646 This->format.trimming.delimiter = 0;
5647 This->format.trimming.delimiterCount = 0;
5648 This->format.trimmingsign = NULL;
5649 This->format.collection = collection;
5650 This->format.fallback = NULL;
5651 IDWriteFontCollection_AddRef(collection);
5653 *format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface;
5655 return S_OK;
5658 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
5660 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5662 TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
5664 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
5665 *obj = iface;
5666 IDWriteTypography_AddRef(iface);
5667 return S_OK;
5670 WARN("%s not implemented.\n", debugstr_guid(riid));
5672 *obj = NULL;
5674 return E_NOINTERFACE;
5677 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
5679 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5680 ULONG ref = InterlockedIncrement(&typography->ref);
5681 TRACE("(%p)->(%d)\n", typography, ref);
5682 return ref;
5685 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
5687 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5688 ULONG ref = InterlockedDecrement(&typography->ref);
5690 TRACE("(%p)->(%d)\n", typography, ref);
5692 if (!ref) {
5693 heap_free(typography->features);
5694 heap_free(typography);
5697 return ref;
5700 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
5702 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5704 TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
5706 if (typography->count == typography->allocated) {
5707 DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5708 if (!ptr)
5709 return E_OUTOFMEMORY;
5711 typography->features = ptr;
5712 typography->allocated *= 2;
5715 typography->features[typography->count++] = feature;
5716 return S_OK;
5719 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
5721 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5722 TRACE("(%p)\n", typography);
5723 return typography->count;
5726 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
5728 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5730 TRACE("(%p)->(%u %p)\n", typography, index, feature);
5732 if (index >= typography->count)
5733 return E_INVALIDARG;
5735 *feature = typography->features[index];
5736 return S_OK;
5739 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
5740 dwritetypography_QueryInterface,
5741 dwritetypography_AddRef,
5742 dwritetypography_Release,
5743 dwritetypography_AddFontFeature,
5744 dwritetypography_GetFontFeatureCount,
5745 dwritetypography_GetFontFeature
5748 HRESULT create_typography(IDWriteTypography **ret)
5750 struct dwrite_typography *typography;
5752 *ret = NULL;
5754 typography = heap_alloc(sizeof(*typography));
5755 if (!typography)
5756 return E_OUTOFMEMORY;
5758 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
5759 typography->ref = 1;
5760 typography->allocated = 2;
5761 typography->count = 0;
5763 typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5764 if (!typography->features) {
5765 heap_free(typography);
5766 return E_OUTOFMEMORY;
5769 *ret = &typography->IDWriteTypography_iface;
5770 return S_OK;