httpapi/tests: Added some basic tests for session and group creation.
[wine.git] / dlls / dwrite / layout.c
blob104b039231ec0cc363bf5b36c957767dfb56e986
1 /*
2 * Text format and layout
4 * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <math.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "dwrite_private.h"
31 #include "scripts.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
35 struct dwrite_textformat_data {
36 WCHAR *family_name;
37 UINT32 family_len;
38 WCHAR *locale;
39 UINT32 locale_len;
41 DWRITE_FONT_WEIGHT weight;
42 DWRITE_FONT_STYLE style;
43 DWRITE_FONT_STRETCH stretch;
45 DWRITE_PARAGRAPH_ALIGNMENT paralign;
46 DWRITE_READING_DIRECTION readingdir;
47 DWRITE_WORD_WRAPPING wrapping;
48 BOOL last_line_wrapping;
49 DWRITE_TEXT_ALIGNMENT textalignment;
50 DWRITE_FLOW_DIRECTION flow;
51 DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
52 DWRITE_OPTICAL_ALIGNMENT optical_alignment;
53 DWRITE_LINE_SPACING spacing;
55 FLOAT fontsize;
57 DWRITE_TRIMMING trimming;
58 IDWriteInlineObject *trimmingsign;
60 IDWriteFontCollection *collection;
61 IDWriteFontFallback *fallback;
64 enum layout_range_attr_kind {
65 LAYOUT_RANGE_ATTR_WEIGHT,
66 LAYOUT_RANGE_ATTR_STYLE,
67 LAYOUT_RANGE_ATTR_STRETCH,
68 LAYOUT_RANGE_ATTR_FONTSIZE,
69 LAYOUT_RANGE_ATTR_EFFECT,
70 LAYOUT_RANGE_ATTR_INLINE,
71 LAYOUT_RANGE_ATTR_UNDERLINE,
72 LAYOUT_RANGE_ATTR_STRIKETHROUGH,
73 LAYOUT_RANGE_ATTR_PAIR_KERNING,
74 LAYOUT_RANGE_ATTR_FONTCOLL,
75 LAYOUT_RANGE_ATTR_LOCALE,
76 LAYOUT_RANGE_ATTR_FONTFAMILY,
77 LAYOUT_RANGE_ATTR_SPACING,
78 LAYOUT_RANGE_ATTR_TYPOGRAPHY
81 struct layout_range_attr_value {
82 DWRITE_TEXT_RANGE range;
83 union {
84 DWRITE_FONT_WEIGHT weight;
85 DWRITE_FONT_STYLE style;
86 DWRITE_FONT_STRETCH stretch;
87 FLOAT fontsize;
88 IDWriteInlineObject *object;
89 IUnknown *effect;
90 BOOL underline;
91 BOOL strikethrough;
92 BOOL pair_kerning;
93 IDWriteFontCollection *collection;
94 const WCHAR *locale;
95 const WCHAR *fontfamily;
96 struct {
97 FLOAT leading;
98 FLOAT trailing;
99 FLOAT min_advance;
100 } spacing;
101 IDWriteTypography *typography;
102 } u;
105 enum layout_range_kind {
106 LAYOUT_RANGE_REGULAR,
107 LAYOUT_RANGE_UNDERLINE,
108 LAYOUT_RANGE_STRIKETHROUGH,
109 LAYOUT_RANGE_EFFECT,
110 LAYOUT_RANGE_SPACING,
111 LAYOUT_RANGE_TYPOGRAPHY
114 struct layout_range_header {
115 struct list entry;
116 enum layout_range_kind kind;
117 DWRITE_TEXT_RANGE range;
120 struct layout_range {
121 struct layout_range_header h;
122 DWRITE_FONT_WEIGHT weight;
123 DWRITE_FONT_STYLE style;
124 FLOAT fontsize;
125 DWRITE_FONT_STRETCH stretch;
126 IDWriteInlineObject *object;
127 BOOL pair_kerning;
128 IDWriteFontCollection *collection;
129 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
130 WCHAR *fontfamily;
133 struct layout_range_bool {
134 struct layout_range_header h;
135 BOOL value;
138 struct layout_range_iface {
139 struct layout_range_header h;
140 IUnknown *iface;
143 struct layout_range_spacing {
144 struct layout_range_header h;
145 FLOAT leading;
146 FLOAT trailing;
147 FLOAT min_advance;
150 enum layout_run_kind {
151 LAYOUT_RUN_REGULAR,
152 LAYOUT_RUN_INLINE
155 struct inline_object_run {
156 IDWriteInlineObject *object;
157 UINT16 length;
160 struct regular_layout_run {
161 DWRITE_GLYPH_RUN_DESCRIPTION descr;
162 DWRITE_GLYPH_RUN run;
163 DWRITE_SCRIPT_ANALYSIS sa;
164 UINT16 *glyphs;
165 UINT16 *clustermap;
166 FLOAT *advances;
167 DWRITE_GLYPH_OFFSET *offsets;
168 UINT32 glyphcount; /* actual glyph count after shaping, not necessarily the same as reported to Draw() */
171 struct layout_run {
172 struct list entry;
173 enum layout_run_kind kind;
174 union {
175 struct inline_object_run object;
176 struct regular_layout_run regular;
177 } u;
178 FLOAT baseline;
179 FLOAT height;
180 UINT32 start_position; /* run text position in range [0, layout-text-length) */
183 struct layout_effective_run {
184 struct list entry;
185 const struct layout_run *run; /* nominal run this one is based on */
186 UINT32 start; /* relative text position, 0 means first text position of a nominal run */
187 UINT32 length; /* length in codepoints that this run covers */
188 UINT32 glyphcount; /* total glyph count in this run */
189 IUnknown *effect; /* original reference is kept only at range level */
190 D2D1_POINT_2F origin; /* baseline origin */
191 FLOAT align_dx; /* adjustment from text alignment */
192 FLOAT width; /* run width */
193 UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
194 UINT32 line; /* 0-based line index in line metrics array */
195 BOOL underlined; /* set if this run is underlined */
196 D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */
199 struct layout_effective_inline {
200 struct list entry;
201 IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */
202 IUnknown *effect; /* original reference is kept only at range level */
203 FLOAT baseline;
204 D2D1_POINT_2F origin; /* left top corner */
205 FLOAT align_dx; /* adjustment from text alignment */
206 FLOAT width; /* object width as it's reported it */
207 BOOL is_sideways; /* vertical flow direction flag passed to Draw */
208 BOOL is_rtl; /* bidi flag passed to Draw */
209 UINT32 line; /* 0-based line index in line metrics array */
212 struct layout_underline {
213 struct list entry;
214 const struct layout_effective_run *run;
215 DWRITE_UNDERLINE u;
218 struct layout_strikethrough {
219 struct list entry;
220 const struct layout_effective_run *run;
221 DWRITE_STRIKETHROUGH s;
224 struct layout_cluster {
225 const struct layout_run *run; /* link to nominal run this cluster belongs to */
226 UINT32 position; /* relative to run, first cluster has 0 position */
229 struct layout_line {
230 FLOAT height; /* height based on content */
231 FLOAT baseline; /* baseline based on content */
234 enum layout_recompute_mask {
235 RECOMPUTE_CLUSTERS = 1 << 0,
236 RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
237 RECOMPUTE_LINES = 1 << 2,
238 RECOMPUTE_OVERHANGS = 1 << 3,
239 RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS,
240 RECOMPUTE_EVERYTHING = 0xffff
243 struct dwrite_textlayout {
244 IDWriteTextLayout3 IDWriteTextLayout3_iface;
245 IDWriteTextFormat1 IDWriteTextFormat1_iface;
246 IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface;
247 IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface;
248 LONG ref;
250 IDWriteFactory5 *factory;
252 WCHAR *str;
253 UINT32 len;
254 struct dwrite_textformat_data format;
255 struct list strike_ranges;
256 struct list underline_ranges;
257 struct list typographies;
258 struct list effects;
259 struct list spacing;
260 struct list ranges;
261 struct list runs;
262 /* lists ready to use by Draw() */
263 struct list eruns;
264 struct list inlineobjects;
265 struct list underlines;
266 struct list strikethrough;
267 USHORT recompute;
269 DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
270 DWRITE_LINE_BREAKPOINT *actual_breakpoints;
272 struct layout_cluster *clusters;
273 DWRITE_CLUSTER_METRICS *clustermetrics;
274 UINT32 cluster_count;
275 FLOAT minwidth;
277 struct layout_line *lines;
278 DWRITE_LINE_METRICS1 *linemetrics;
279 UINT32 line_alloc;
281 DWRITE_TEXT_METRICS1 metrics;
282 DWRITE_OVERHANG_METRICS overhangs;
284 DWRITE_MEASURING_MODE measuringmode;
286 /* gdi-compatible layout specifics */
287 FLOAT ppdip;
288 DWRITE_MATRIX transform;
291 struct dwrite_textformat {
292 IDWriteTextFormat2 IDWriteTextFormat2_iface;
293 LONG ref;
294 struct dwrite_textformat_data format;
297 struct dwrite_trimmingsign {
298 IDWriteInlineObject IDWriteInlineObject_iface;
299 LONG ref;
301 IDWriteTextLayout *layout;
304 struct dwrite_typography {
305 IDWriteTypography IDWriteTypography_iface;
306 LONG ref;
308 DWRITE_FONT_FEATURE *features;
309 UINT32 allocated;
310 UINT32 count;
313 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl;
315 static void release_format_data(struct dwrite_textformat_data *data)
317 if (data->collection) IDWriteFontCollection_Release(data->collection);
318 if (data->fallback) IDWriteFontFallback_Release(data->fallback);
319 if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
320 heap_free(data->family_name);
321 heap_free(data->locale);
324 static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout3(IDWriteTextLayout3 *iface)
326 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout3_iface);
329 static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
331 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface);
334 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface)
336 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface);
339 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface)
341 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface);
344 static inline struct dwrite_textformat *impl_from_IDWriteTextFormat2(IDWriteTextFormat2 *iface)
346 return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface);
349 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*);
351 static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
353 return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
356 static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
358 return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
361 static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr)
363 return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength);
366 static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
368 return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
371 static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
372 BOOL *changed)
374 if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
375 return E_INVALIDARG;
376 if (changed) *changed = format->textalignment != alignment;
377 format->textalignment = alignment;
378 return S_OK;
381 static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
382 DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
384 if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
385 return E_INVALIDARG;
386 if (changed) *changed = format->paralign != alignment;
387 format->paralign = alignment;
388 return S_OK;
391 static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
392 DWRITE_READING_DIRECTION direction, BOOL *changed)
394 if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
395 return E_INVALIDARG;
396 if (changed) *changed = format->readingdir != direction;
397 format->readingdir = direction;
398 return S_OK;
401 static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
402 DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
404 if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
405 return E_INVALIDARG;
406 if (changed) *changed = format->wrapping != wrapping;
407 format->wrapping = wrapping;
408 return S_OK;
411 static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format,
412 DWRITE_FLOW_DIRECTION direction, BOOL *changed)
414 if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
415 return E_INVALIDARG;
416 if (changed) *changed = format->flow != direction;
417 format->flow = direction;
418 return S_OK;
421 static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format,
422 DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed)
424 if (changed)
425 *changed = FALSE;
427 if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD)
428 return E_INVALIDARG;
430 if (changed) {
431 *changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming));
432 if (format->trimmingsign != trimming_sign)
433 *changed = TRUE;
436 format->trimming = *trimming;
437 if (format->trimmingsign)
438 IDWriteInlineObject_Release(format->trimmingsign);
439 format->trimmingsign = trimming_sign;
440 if (format->trimmingsign)
441 IDWriteInlineObject_AddRef(format->trimmingsign);
442 return S_OK;
445 static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format,
446 DWRITE_LINE_SPACING const *spacing, BOOL *changed)
448 if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f ||
449 (UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL)
450 return E_INVALIDARG;
452 if (changed)
453 *changed = memcmp(spacing, &format->spacing, sizeof(*spacing));
455 format->spacing = *spacing;
456 return S_OK;
459 static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
461 *fallback = format->fallback;
462 if (*fallback)
463 IDWriteFontFallback_AddRef(*fallback);
464 return S_OK;
467 static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
469 if (format->fallback)
470 IDWriteFontFallback_Release(format->fallback);
471 format->fallback = fallback;
472 if (fallback)
473 IDWriteFontFallback_AddRef(fallback);
474 return S_OK;
477 static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format,
478 DWRITE_OPTICAL_ALIGNMENT alignment)
480 if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS)
481 return E_INVALIDARG;
482 format->optical_alignment = alignment;
483 return S_OK;
486 static BOOL is_run_rtl(const struct layout_effective_run *run)
488 return run->run->u.regular.run.bidiLevel & 1;
491 static struct layout_run *alloc_layout_run(enum layout_run_kind kind, UINT32 start_position)
493 struct layout_run *ret;
495 ret = heap_alloc(sizeof(*ret));
496 if (!ret) return NULL;
498 memset(ret, 0, sizeof(*ret));
499 ret->kind = kind;
500 if (kind == LAYOUT_RUN_REGULAR) {
501 ret->u.regular.sa.script = Script_Unknown;
502 ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
504 ret->start_position = start_position;
506 return ret;
509 static void free_layout_runs(struct dwrite_textlayout *layout)
511 struct layout_run *cur, *cur2;
512 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
513 list_remove(&cur->entry);
514 if (cur->kind == LAYOUT_RUN_REGULAR) {
515 if (cur->u.regular.run.fontFace)
516 IDWriteFontFace_Release(cur->u.regular.run.fontFace);
517 heap_free(cur->u.regular.glyphs);
518 heap_free(cur->u.regular.clustermap);
519 heap_free(cur->u.regular.advances);
520 heap_free(cur->u.regular.offsets);
522 heap_free(cur);
526 static void free_layout_eruns(struct dwrite_textlayout *layout)
528 struct layout_effective_inline *in, *in2;
529 struct layout_effective_run *cur, *cur2;
530 struct layout_strikethrough *s, *s2;
531 struct layout_underline *u, *u2;
533 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
534 list_remove(&cur->entry);
535 heap_free(cur->clustermap);
536 heap_free(cur);
539 LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
540 list_remove(&in->entry);
541 heap_free(in);
544 LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) {
545 list_remove(&u->entry);
546 heap_free(u);
549 LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
550 list_remove(&s->entry);
551 heap_free(s);
555 /* Used to resolve break condition by forcing stronger condition over weaker. */
556 static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
558 switch (existingbreak) {
559 case DWRITE_BREAK_CONDITION_NEUTRAL:
560 return newbreak;
561 case DWRITE_BREAK_CONDITION_CAN_BREAK:
562 return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
563 /* let's keep stronger conditions as is */
564 case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
565 case DWRITE_BREAK_CONDITION_MUST_BREAK:
566 break;
567 default:
568 ERR("unknown break condition %d\n", existingbreak);
571 return existingbreak;
574 /* This helper should be used to get effective range length, in other words it returns number of text
575 positions from range starting point to the end of the range, limited by layout text length */
576 static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
578 if (range->h.range.startPosition + range->h.range.length <= layout->len)
579 return range->h.range.length;
580 return layout->len - range->h.range.startPosition;
583 /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
584 static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
586 DWRITE_BREAK_CONDITION before, after;
587 UINT32 i, length;
588 HRESULT hr;
590 /* ignore returned conditions if failed */
591 hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
592 if (FAILED(hr))
593 after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
595 if (!layout->actual_breakpoints) {
596 layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
597 if (!layout->actual_breakpoints)
598 return E_OUTOFMEMORY;
599 memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
602 length = get_clipped_range_length(layout, cur);
603 for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
604 /* for first codepoint check if there's anything before it and update accordingly */
605 if (i == cur->h.range.startPosition) {
606 if (i > 0)
607 layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
608 override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
609 else
610 layout->actual_breakpoints[i].breakConditionBefore = before;
611 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
613 /* similar check for last codepoint */
614 else if (i == cur->h.range.startPosition + length - 1) {
615 if (i == layout->len - 1)
616 layout->actual_breakpoints[i].breakConditionAfter = after;
617 else
618 layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
619 override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
620 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
622 /* for all positions within a range disable breaks */
623 else {
624 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
625 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
628 layout->actual_breakpoints[i].isWhitespace = 0;
629 layout->actual_breakpoints[i].isSoftHyphen = 0;
632 return S_OK;
635 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
637 static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
639 if (layout->actual_breakpoints)
640 return layout->actual_breakpoints[pos];
641 return layout->nominal_breakpoints[pos];
644 static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
645 UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
647 UINT8 breakcondition;
648 UINT32 position;
649 UINT16 j;
651 /* For clusters made of control chars we report zero glyphs, and we need zero cluster
652 width as well; advances are already computed at this point and are not necessary zero. */
653 metrics->width = 0.0f;
654 if (run->run.glyphCount) {
655 for (j = start_glyph; j < stop_glyph; j++)
656 metrics->width += run->run.glyphAdvances[j];
658 metrics->length = length;
660 position = run->descr.textPosition + stop_position;
661 if (stop_glyph == run->glyphcount)
662 breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter;
663 else {
664 breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore;
665 if (stop_position) position -= 1;
668 metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
669 breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
670 if (metrics->length == 1) {
671 DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position);
672 metrics->isWhitespace = bp.isWhitespace;
673 metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]);
674 metrics->isSoftHyphen = bp.isSoftHyphen;
676 else {
677 metrics->isWhitespace = 0;
678 metrics->isNewline = 0;
679 metrics->isSoftHyphen = 0;
681 metrics->isRightToLeft = run->run.bidiLevel & 1;
682 metrics->padding = 0;
687 All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
688 On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
689 Note that there's no need to reallocate anything at this point as we allocate one cluster per
690 codepoint initially.
693 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
695 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
696 struct layout_cluster *c = &layout->clusters[*cluster];
697 const struct regular_layout_run *run = &r->u.regular;
698 UINT32 i, start = 0;
700 assert(r->kind == LAYOUT_RUN_REGULAR);
702 for (i = 0; i < run->descr.stringLength; i++) {
703 BOOL end = i == run->descr.stringLength - 1;
705 if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
706 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
707 i - start, metrics);
708 c->position = start;
709 c->run = r;
711 *cluster += 1;
712 metrics++;
713 c++;
714 start = i;
717 if (end) {
718 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
719 i - start + 1, metrics);
720 c->position = start;
721 c->run = r;
723 *cluster += 1;
724 return;
729 #define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm)
731 static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize,
732 DWRITE_FONT_METRICS *fontmetrics)
734 if (is_layout_gdi_compatible(layout)) {
735 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics);
736 if (FAILED(hr))
737 WARN("failed to get compat metrics, 0x%08x\n", hr);
739 else
740 IDWriteFontFace_GetMetrics(fontface, fontmetrics);
743 static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height)
745 *baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics);
746 *height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
749 static HRESULT layout_itemize(struct dwrite_textlayout *layout)
751 IDWriteTextAnalyzer *analyzer;
752 struct layout_range *range;
753 struct layout_run *r;
754 HRESULT hr = S_OK;
756 analyzer = get_text_analyzer();
758 LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
759 /* We don't care about ranges that don't contain any text. */
760 if (range->h.range.startPosition >= layout->len)
761 break;
763 /* Inline objects override actual text in range. */
764 if (range->object) {
765 hr = layout_update_breakpoints_range(layout, range);
766 if (FAILED(hr))
767 return hr;
769 r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition);
770 if (!r)
771 return E_OUTOFMEMORY;
773 r->u.object.object = range->object;
774 r->u.object.length = get_clipped_range_length(layout, range);
775 list_add_tail(&layout->runs, &r->entry);
776 continue;
779 /* Initial splitting by script. */
780 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
781 range->h.range.startPosition, get_clipped_range_length(layout, range),
782 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
783 if (FAILED(hr))
784 break;
786 /* Splitting further by bidi levels. */
787 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
788 range->h.range.startPosition, get_clipped_range_length(layout, range),
789 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
790 if (FAILED(hr))
791 break;
794 return hr;
797 static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout)
799 IDWriteFontCollection *sys_collection;
800 IDWriteFontFallback *fallback = NULL;
801 struct layout_range *range;
802 struct layout_run *r;
803 HRESULT hr;
805 if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE,
806 (IDWriteFontCollection1 **)&sys_collection, FALSE))) {
807 WARN("Failed to get system collection, hr %#x.\n", hr);
808 return hr;
811 if (layout->format.fallback) {
812 fallback = layout->format.fallback;
813 IDWriteFontFallback_AddRef(fallback);
815 else {
816 if (FAILED(hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback))) {
817 WARN("Failed to get system fallback, hr %#x.\n", hr);
818 goto fatal;
822 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
823 struct regular_layout_run *run = &r->u.regular;
824 IDWriteFont *font;
825 UINT32 length;
827 if (r->kind == LAYOUT_RUN_INLINE)
828 continue;
830 range = get_layout_range_by_pos(layout, run->descr.textPosition);
832 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
833 IDWriteFontCollection *collection;
835 collection = range->collection ? range->collection : sys_collection;
837 if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style,
838 range->stretch, &font))) {
839 WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n",
840 debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
841 break;
844 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
845 IDWriteFont_Release(font);
846 if (FAILED(hr)) {
847 WARN("Failed to create font face, hr %#x.\n", hr);
848 break;
851 run->run.fontEmSize = range->fontsize;
852 continue;
855 length = run->descr.stringLength;
857 while (length) {
858 UINT32 mapped_length;
859 FLOAT scale;
861 run = &r->u.regular;
863 hr = IDWriteFontFallback_MapCharacters(fallback,
864 (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
865 run->descr.textPosition,
866 run->descr.stringLength,
867 range->collection,
868 range->fontfamily,
869 range->weight,
870 range->style,
871 range->stretch,
872 &mapped_length,
873 &font,
874 &scale);
875 if (FAILED(hr)) {
876 WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr),
877 debugstr_w(range->fontfamily), range->collection, hr);
878 goto fatal;
881 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
882 IDWriteFont_Release(font);
883 if (FAILED(hr)) {
884 WARN("Failed to create font face, hr %#x.\n", hr);
885 goto fatal;
888 run->run.fontEmSize = range->fontsize * scale;
890 if (mapped_length < length) {
891 struct regular_layout_run *nextrun;
892 struct layout_run *nextr;
894 /* keep mapped part for current run, add another run for the rest */
895 nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0);
896 if (!nextr) {
897 hr = E_OUTOFMEMORY;
898 goto fatal;
901 *nextr = *r;
902 nextr->start_position = run->descr.textPosition + mapped_length;
903 nextrun = &nextr->u.regular;
904 nextrun->descr.textPosition = nextr->start_position;
905 nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
906 nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
907 run->descr.stringLength = mapped_length;
908 list_add_after(&r->entry, &nextr->entry);
909 r = nextr;
912 length -= mapped_length;
916 fatal:
917 IDWriteFontCollection_Release(sys_collection);
918 if (fallback)
919 IDWriteFontFallback_Release(fallback);
921 return hr;
924 static HRESULT layout_shape_run(struct dwrite_textlayout *layout, struct regular_layout_run *run)
926 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
927 DWRITE_SHAPING_TEXT_PROPERTIES *text_props;
928 IDWriteTextAnalyzer *analyzer;
929 struct layout_range *range;
930 UINT32 max_count;
931 HRESULT hr;
933 range = get_layout_range_by_pos(layout, run->descr.textPosition);
934 run->descr.localeName = range->locale;
935 run->clustermap = heap_alloc(run->descr.stringLength * sizeof(*run->clustermap));
937 max_count = 3 * run->descr.stringLength / 2 + 16;
938 run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
939 if (!run->clustermap || !run->glyphs)
940 return E_OUTOFMEMORY;
942 text_props = heap_alloc(run->descr.stringLength * sizeof(*text_props));
943 glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
944 if (!text_props || !glyph_props) {
945 heap_free(text_props);
946 heap_free(glyph_props);
947 return E_OUTOFMEMORY;
950 analyzer = get_text_analyzer();
952 for (;;) {
953 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength, run->run.fontFace,
954 run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL /* FIXME */, NULL,
955 NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props, &run->glyphcount);
956 if (hr == E_NOT_SUFFICIENT_BUFFER) {
957 heap_free(run->glyphs);
958 heap_free(glyph_props);
960 max_count = run->glyphcount;
962 run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
963 glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
964 if (!run->glyphs || !glyph_props) {
965 hr = E_OUTOFMEMORY;
966 break;
969 continue;
972 break;
975 if (FAILED(hr)) {
976 heap_free(text_props);
977 heap_free(glyph_props);
978 WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
979 return hr;
982 run->run.glyphIndices = run->glyphs;
983 run->descr.clusterMap = run->clustermap;
985 run->advances = heap_alloc(run->glyphcount * sizeof(*run->advances));
986 run->offsets = heap_alloc(run->glyphcount * sizeof(*run->offsets));
987 if (!run->advances || !run->offsets)
988 return E_OUTOFMEMORY;
990 /* Get advances and offsets. */
991 if (is_layout_gdi_compatible(layout))
992 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
993 text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
994 run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
995 layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1,
996 &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
997 else
998 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
999 run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
1000 run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
1001 NULL, NULL, 0, run->advances, run->offsets);
1003 heap_free(text_props);
1004 heap_free(glyph_props);
1005 if (FAILED(hr)) {
1006 memset(run->advances, 0, run->glyphcount * sizeof(*run->advances));
1007 memset(run->offsets, 0, run->glyphcount * sizeof(*run->offsets));
1008 WARN("%s: failed to get glyph placement info, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
1011 run->run.glyphAdvances = run->advances;
1012 run->run.glyphOffsets = run->offsets;
1014 /* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
1015 with valid cluster map and potentially with non-zero advances; layout code exposes those as zero
1016 width clusters. */
1017 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
1018 run->run.glyphCount = 0;
1019 else
1020 run->run.glyphCount = run->glyphcount;
1022 return S_OK;
1025 static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
1027 struct layout_run *r;
1028 UINT32 cluster = 0;
1029 HRESULT hr;
1031 free_layout_eruns(layout);
1032 free_layout_runs(layout);
1034 /* Cluster data arrays are allocated once, assuming one text position per cluster. */
1035 if (!layout->clustermetrics && layout->len) {
1036 layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics));
1037 layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters));
1038 if (!layout->clustermetrics || !layout->clusters) {
1039 heap_free(layout->clustermetrics);
1040 heap_free(layout->clusters);
1041 return E_OUTOFMEMORY;
1044 layout->cluster_count = 0;
1046 if (FAILED(hr = layout_itemize(layout))) {
1047 WARN("Itemization failed, hr %#x.\n", hr);
1048 return hr;
1051 if (FAILED(hr = layout_resolve_fonts(layout))) {
1052 WARN("Failed to resolve layout fonts, hr %#x.\n", hr);
1053 return hr;
1056 /* fill run info */
1057 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
1058 struct regular_layout_run *run = &r->u.regular;
1059 DWRITE_FONT_METRICS fontmetrics = { 0 };
1061 /* we need to do very little in case of inline objects */
1062 if (r->kind == LAYOUT_RUN_INLINE) {
1063 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster];
1064 struct layout_cluster *c = &layout->clusters[cluster];
1065 DWRITE_INLINE_OBJECT_METRICS inlinemetrics;
1067 metrics->width = 0.0f;
1068 metrics->length = r->u.object.length;
1069 metrics->canWrapLineAfter = 0;
1070 metrics->isWhitespace = 0;
1071 metrics->isNewline = 0;
1072 metrics->isSoftHyphen = 0;
1073 metrics->isRightToLeft = 0;
1074 metrics->padding = 0;
1075 c->run = r;
1076 c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */
1077 cluster++;
1079 /* it's not fatal if GetMetrics() fails, all returned metrics are ignored */
1080 hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics);
1081 if (FAILED(hr)) {
1082 memset(&inlinemetrics, 0, sizeof(inlinemetrics));
1083 hr = S_OK;
1085 metrics->width = inlinemetrics.width;
1086 r->baseline = inlinemetrics.baseline;
1087 r->height = inlinemetrics.height;
1089 /* FIXME: use resolved breakpoints in this case too */
1091 continue;
1094 if (FAILED(hr = layout_shape_run(layout, run)))
1095 WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
1097 /* baseline derived from font metrics */
1098 layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
1099 layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
1101 layout_set_cluster_metrics(layout, r, &cluster);
1104 if (hr == S_OK) {
1105 layout->cluster_count = cluster;
1106 if (cluster)
1107 layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
1110 return hr;
1113 static HRESULT layout_compute(struct dwrite_textlayout *layout)
1115 HRESULT hr;
1117 if (!(layout->recompute & RECOMPUTE_CLUSTERS))
1118 return S_OK;
1120 /* nominal breakpoints are evaluated only once, because string never changes */
1121 if (!layout->nominal_breakpoints) {
1122 IDWriteTextAnalyzer *analyzer;
1124 layout->nominal_breakpoints = heap_alloc(layout->len * sizeof(*layout->nominal_breakpoints));
1125 if (!layout->nominal_breakpoints)
1126 return E_OUTOFMEMORY;
1128 analyzer = get_text_analyzer();
1130 if (FAILED(hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer,
1131 (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
1132 0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface)))
1133 WARN("Line breakpoints analysis failed, hr %#x.\n", hr);
1136 heap_free(layout->actual_breakpoints);
1137 layout->actual_breakpoints = NULL;
1139 hr = layout_compute_runs(layout);
1141 if (TRACE_ON(dwrite)) {
1142 struct layout_run *cur;
1144 LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) {
1145 if (cur->kind == LAYOUT_RUN_INLINE)
1146 TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length);
1147 else
1148 TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition +
1149 cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel);
1153 layout->recompute &= ~RECOMPUTE_CLUSTERS;
1154 return hr;
1157 static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end)
1159 FLOAT width = 0.0f;
1160 for (; start < end; start++)
1161 width += layout->clustermetrics[start].width;
1162 return width;
1165 static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos)
1167 struct layout_range_header *cur;
1169 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
1170 DWRITE_TEXT_RANGE *r = &cur->range;
1171 if (r->startPosition <= pos && pos < r->startPosition + r->length)
1172 return cur;
1175 return NULL;
1178 static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1180 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos);
1181 return ((struct layout_range_iface*)h)->iface;
1184 static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun)
1186 return erun->run->u.regular.run.bidiLevel & 1;
1189 /* A set of parameters that additionally splits resulting runs. It happens after shaping and all text processing,
1190 no glyph changes are possible. It's understandable for drawing effects, because DrawGlyphRun() reports them as
1191 one of the arguments, but it also happens for decorations, so every effective run has uniform
1192 underline/strikethough/effect tuple. */
1193 struct layout_final_splitting_params {
1194 BOOL strikethrough;
1195 BOOL underline;
1196 IUnknown *effect;
1199 static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1201 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos);
1202 return ((struct layout_range_bool*)h)->value;
1205 static inline BOOL layout_get_underline_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1207 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->underline_ranges, pos);
1208 return ((struct layout_range_bool*)h)->value;
1211 static void layout_splitting_params_from_pos(struct dwrite_textlayout *layout, UINT32 pos,
1212 struct layout_final_splitting_params *params)
1214 params->strikethrough = layout_get_strikethrough_from_pos(layout, pos);
1215 params->underline = layout_get_underline_from_pos(layout, pos);
1216 params->effect = layout_get_effect_from_pos(layout, pos);
1219 static BOOL is_same_splitting_params(const struct layout_final_splitting_params *left,
1220 const struct layout_final_splitting_params *right)
1222 return left->strikethrough == right->strikethrough &&
1223 left->underline == right->underline &&
1224 left->effect == right->effect;
1227 static void layout_get_erun_font_metrics(struct dwrite_textlayout *layout, struct layout_effective_run *erun,
1228 DWRITE_FONT_METRICS *metrics)
1230 memset(metrics, 0, sizeof(*metrics));
1231 if (is_layout_gdi_compatible(layout)) {
1232 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
1233 erun->run->u.regular.run.fontFace,
1234 erun->run->u.regular.run.fontEmSize,
1235 layout->ppdip,
1236 &layout->transform,
1237 metrics);
1238 if (FAILED(hr))
1239 WARN("failed to get font metrics, 0x%08x\n", hr);
1241 else
1242 IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics);
1245 /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index,
1246 'cluster_count' indicates how many clusters to add, including first one. */
1247 static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster,
1248 UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params)
1250 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1251 UINT32 i, start, length, last_cluster;
1252 struct layout_effective_run *run;
1254 if (r->kind == LAYOUT_RUN_INLINE) {
1255 struct layout_effective_inline *inlineobject;
1257 inlineobject = heap_alloc(sizeof(*inlineobject));
1258 if (!inlineobject)
1259 return E_OUTOFMEMORY;
1261 inlineobject->object = r->u.object.object;
1262 inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1263 inlineobject->origin.x = is_rtl ? origin_x - inlineobject->width : origin_x;
1264 inlineobject->origin.y = 0.0f; /* set after line is built */
1265 inlineobject->align_dx = 0.0f;
1266 inlineobject->baseline = r->baseline;
1268 /* It's not clear how these two are set, possibly directionality
1269 is derived from surrounding text (replaced text could have
1270 different ranges which differ in reading direction). */
1271 inlineobject->is_sideways = FALSE;
1272 inlineobject->is_rtl = FALSE;
1273 inlineobject->line = line;
1275 /* effect assigned from start position and on is used for inline objects */
1276 inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position +
1277 layout->clusters[first_cluster].run->start_position);
1279 list_add_tail(&layout->inlineobjects, &inlineobject->entry);
1280 return S_OK;
1283 run = heap_alloc(sizeof(*run));
1284 if (!run)
1285 return E_OUTOFMEMORY;
1287 /* No need to iterate for that, use simple fact that:
1288 <last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */
1289 last_cluster = first_cluster + cluster_count - 1;
1290 length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
1291 layout->clustermetrics[last_cluster].length;
1293 run->clustermap = heap_alloc(sizeof(UINT16)*length);
1294 if (!run->clustermap) {
1295 heap_free(run);
1296 return E_OUTOFMEMORY;
1299 run->run = r;
1300 run->start = start = layout->clusters[first_cluster].position;
1301 run->length = length;
1302 run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1303 memset(&run->bbox, 0, sizeof(run->bbox));
1305 /* Check if run direction matches paragraph direction, if it doesn't adjust by
1306 run width */
1307 if (layout_is_erun_rtl(run) ^ is_rtl)
1308 run->origin.x = is_rtl ? origin_x - run->width : origin_x + run->width;
1309 else
1310 run->origin.x = origin_x;
1312 run->origin.y = 0.0f; /* set after line is built */
1313 run->align_dx = 0.0f;
1314 run->line = line;
1316 if (r->u.regular.run.glyphCount) {
1317 /* Trim leading and trailing clusters. */
1318 run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start];
1319 if (start + length < r->u.regular.descr.stringLength)
1320 run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length];
1322 else
1323 run->glyphcount = 0;
1325 /* cluster map needs to be shifted */
1326 for (i = 0; i < length; i++)
1327 run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start];
1329 run->effect = params->effect;
1330 run->underlined = params->underline;
1331 list_add_tail(&layout->eruns, &run->entry);
1333 /* Strikethrough style is guaranteed to be consistent within effective run,
1334 its width equals to run width, thickness and offset are derived from
1335 font metrics, rest of the values are from layout or run itself */
1336 if (params->strikethrough) {
1337 struct layout_strikethrough *s;
1338 DWRITE_FONT_METRICS metrics;
1340 s = heap_alloc(sizeof(*s));
1341 if (!s)
1342 return E_OUTOFMEMORY;
1344 layout_get_erun_font_metrics(layout, run, &metrics);
1345 s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1346 s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics);
1347 /* Negative offset moves it above baseline as Y coordinate grows downward. */
1348 s->s.offset = -SCALE_FONT_METRIC(metrics.strikethroughPosition, r->u.regular.run.fontEmSize, &metrics);
1349 s->s.readingDirection = layout->format.readingdir;
1350 s->s.flowDirection = layout->format.flow;
1351 s->s.localeName = r->u.regular.descr.localeName;
1352 s->s.measuringMode = layout->measuringmode;
1353 s->run = run;
1355 list_add_tail(&layout->strikethrough, &s->entry);
1358 return S_OK;
1361 static 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->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_IDWriteTextFormat1) ||
2818 IsEqualIID(riid, &IID_IDWriteTextFormat))
2819 *obj = &This->IDWriteTextFormat1_iface;
2821 if (*obj) {
2822 IDWriteTextLayout3_AddRef(iface);
2823 return S_OK;
2826 WARN("%s not implemented.\n", debugstr_guid(riid));
2828 return E_NOINTERFACE;
2831 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface)
2833 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2834 ULONG ref = InterlockedIncrement(&This->ref);
2835 TRACE("(%p)->(%d)\n", This, ref);
2836 return ref;
2839 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface)
2841 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2842 ULONG ref = InterlockedDecrement(&This->ref);
2844 TRACE("(%p)->(%d)\n", This, ref);
2846 if (!ref) {
2847 IDWriteFactory5_Release(This->factory);
2848 free_layout_ranges_list(This);
2849 free_layout_eruns(This);
2850 free_layout_runs(This);
2851 release_format_data(&This->format);
2852 heap_free(This->nominal_breakpoints);
2853 heap_free(This->actual_breakpoints);
2854 heap_free(This->clustermetrics);
2855 heap_free(This->clusters);
2856 heap_free(This->linemetrics);
2857 heap_free(This->lines);
2858 heap_free(This->str);
2859 heap_free(This);
2862 return ref;
2865 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2867 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2868 return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment);
2871 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
2873 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2874 return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment);
2877 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping)
2879 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2880 return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping);
2883 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction)
2885 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2886 return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction);
2889 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction)
2891 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2892 TRACE("(%p)->(%d)\n", This, direction);
2893 return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction);
2896 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop)
2898 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2899 TRACE("(%p)->(%.2f)\n", This, tabstop);
2900 return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop);
2903 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming,
2904 IDWriteInlineObject *trimming_sign)
2906 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2907 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
2908 return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign);
2911 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2912 FLOAT line_spacing, FLOAT baseline)
2914 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2915 TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
2916 return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline);
2919 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface)
2921 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2922 return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface);
2925 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface)
2927 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2928 return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface);
2931 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface)
2933 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2934 return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface);
2937 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface)
2939 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2940 return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface);
2943 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface)
2945 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2946 return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface);
2949 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface)
2951 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2952 return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface);
2955 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options,
2956 IDWriteInlineObject **trimming_sign)
2958 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2959 return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign);
2962 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method,
2963 FLOAT *spacing, FLOAT *baseline)
2965 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2966 return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat*)&This->IDWriteTextFormat1_iface, method, spacing, baseline);
2969 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection)
2971 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2972 return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection);
2975 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface)
2977 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2978 return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface);
2981 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
2983 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2984 return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size);
2987 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface)
2989 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2990 return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface);
2993 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface)
2995 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2996 return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface);
2999 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface)
3001 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3002 return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface);
3005 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface)
3007 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3008 return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface);
3011 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface)
3013 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3014 return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface);
3017 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
3019 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3020 return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size);
3023 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth)
3025 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3026 BOOL changed;
3028 TRACE("(%p)->(%.2f)\n", This, maxWidth);
3030 if (maxWidth < 0.0f)
3031 return E_INVALIDARG;
3033 changed = This->metrics.layoutWidth != maxWidth;
3034 This->metrics.layoutWidth = maxWidth;
3036 if (changed)
3037 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3038 return S_OK;
3041 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight)
3043 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3044 BOOL changed;
3046 TRACE("(%p)->(%.2f)\n", This, maxHeight);
3048 if (maxHeight < 0.0f)
3049 return E_INVALIDARG;
3051 changed = This->metrics.layoutHeight != maxHeight;
3052 This->metrics.layoutHeight = maxHeight;
3054 if (changed)
3055 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3056 return S_OK;
3059 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
3061 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3062 struct layout_range_attr_value value;
3064 TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
3066 value.range = range;
3067 value.u.collection = collection;
3068 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
3071 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
3073 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3074 struct layout_range_attr_value value;
3076 TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
3078 if (!name)
3079 return E_INVALIDARG;
3081 value.range = range;
3082 value.u.fontfamily = name;
3083 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
3086 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
3088 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3089 struct layout_range_attr_value value;
3091 TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
3093 if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
3094 return E_INVALIDARG;
3096 value.range = range;
3097 value.u.weight = weight;
3098 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
3101 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
3103 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3104 struct layout_range_attr_value value;
3106 TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
3108 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
3109 return E_INVALIDARG;
3111 value.range = range;
3112 value.u.style = style;
3113 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
3116 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
3118 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3119 struct layout_range_attr_value value;
3121 TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
3123 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
3124 return E_INVALIDARG;
3126 value.range = range;
3127 value.u.stretch = stretch;
3128 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
3131 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
3133 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3134 struct layout_range_attr_value value;
3136 TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
3138 if (size <= 0.0f)
3139 return E_INVALIDARG;
3141 value.range = range;
3142 value.u.fontsize = size;
3143 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
3146 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
3148 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3149 struct layout_range_attr_value value;
3151 TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
3153 value.range = range;
3154 value.u.underline = underline;
3155 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
3158 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
3160 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3161 struct layout_range_attr_value value;
3163 TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
3165 value.range = range;
3166 value.u.strikethrough = strikethrough;
3167 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
3170 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
3172 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3173 struct layout_range_attr_value value;
3175 TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
3177 value.range = range;
3178 value.u.effect = effect;
3179 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
3182 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
3184 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3185 struct layout_range_attr_value value;
3187 TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
3189 value.range = range;
3190 value.u.object = object;
3191 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
3194 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
3196 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3197 struct layout_range_attr_value value;
3199 TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range));
3201 value.range = range;
3202 value.u.typography = typography;
3203 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
3206 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
3208 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3209 struct layout_range_attr_value value;
3211 TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
3213 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
3214 return E_INVALIDARG;
3216 value.range = range;
3217 value.u.locale = locale;
3218 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
3221 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface)
3223 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3224 TRACE("(%p)\n", This);
3225 return This->metrics.layoutWidth;
3228 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface)
3230 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3231 TRACE("(%p)\n", This);
3232 return This->metrics.layoutHeight;
3235 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position,
3236 IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
3238 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3239 struct layout_range *range;
3241 TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
3243 if (position >= This->len)
3244 return S_OK;
3246 range = get_layout_range_by_pos(This, position);
3247 *collection = range->collection;
3248 if (*collection)
3249 IDWriteFontCollection_AddRef(*collection);
3251 return return_range(&range->h, r);
3254 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface,
3255 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3257 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3258 TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
3259 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
3262 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface,
3263 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
3265 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3266 TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
3267 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
3270 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface,
3271 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
3273 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3274 struct layout_range *range;
3276 TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
3278 if (position >= This->len)
3279 return S_OK;
3281 range = get_layout_range_by_pos(This, position);
3282 *weight = range->weight;
3284 return return_range(&range->h, r);
3287 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface,
3288 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
3290 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3291 struct layout_range *range;
3293 TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
3295 range = get_layout_range_by_pos(This, position);
3296 *style = range->style;
3297 return return_range(&range->h, r);
3300 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface,
3301 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
3303 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3304 struct layout_range *range;
3306 TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
3308 range = get_layout_range_by_pos(This, position);
3309 *stretch = range->stretch;
3310 return return_range(&range->h, r);
3313 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface,
3314 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
3316 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3317 struct layout_range *range;
3319 TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
3321 range = get_layout_range_by_pos(This, position);
3322 *size = range->fontsize;
3323 return return_range(&range->h, r);
3326 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface,
3327 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
3329 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3330 struct layout_range_bool *range;
3332 TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
3334 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position);
3335 *underline = range->value;
3337 return return_range(&range->h, r);
3340 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface,
3341 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
3343 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3344 struct layout_range_bool *range;
3346 TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
3348 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
3349 *strikethrough = range->value;
3351 return return_range(&range->h, r);
3354 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface,
3355 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
3357 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3358 struct layout_range_iface *range;
3360 TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
3362 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position);
3363 *effect = range->iface;
3364 if (*effect)
3365 IUnknown_AddRef(*effect);
3367 return return_range(&range->h, r);
3370 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface,
3371 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
3373 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3374 struct layout_range *range;
3376 TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
3378 if (position >= This->len)
3379 return S_OK;
3381 range = get_layout_range_by_pos(This, position);
3382 *object = range->object;
3383 if (*object)
3384 IDWriteInlineObject_AddRef(*object);
3386 return return_range(&range->h, r);
3389 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface,
3390 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
3392 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3393 struct layout_range_iface *range;
3395 TRACE("(%p)->(%u %p %p)\n", This, position, typography, r);
3397 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position);
3398 *typography = (IDWriteTypography*)range->iface;
3399 if (*typography)
3400 IDWriteTypography_AddRef(*typography);
3402 return return_range(&range->h, r);
3405 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface,
3406 UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
3408 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3409 TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
3410 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
3413 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface,
3414 UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
3416 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3417 TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
3418 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
3421 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
3422 const DWRITE_MATRIX *m)
3424 D2D1_POINT_2F vec, vec2;
3426 if (!skiptransform) {
3427 /* apply transform */
3428 vec.x = 0.0f;
3429 vec.y = coord * ppdip;
3431 vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx;
3432 vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy;
3434 /* snap */
3435 vec2.x = floorf(vec2.x + 0.5f);
3436 vec2.y = floorf(vec2.y + 0.5f);
3438 /* apply inverted transform, we don't care about X component at this point */
3439 vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3440 vec.y /= ppdip;
3442 else
3443 vec.y = floorf(coord * ppdip + 0.5f) / ppdip;
3445 return vec.y;
3448 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface,
3449 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
3451 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3452 BOOL disabled = FALSE, skiptransform = FALSE;
3453 struct layout_effective_inline *inlineobject;
3454 struct layout_effective_run *run;
3455 struct layout_strikethrough *s;
3456 struct layout_underline *u;
3457 FLOAT det = 0.0f, ppdip = 0.0f;
3458 DWRITE_MATRIX m = { 0 };
3459 HRESULT hr;
3461 TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
3463 hr = layout_compute_effective_runs(This);
3464 if (FAILED(hr))
3465 return hr;
3467 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
3468 if (FAILED(hr))
3469 return hr;
3471 if (!disabled) {
3472 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
3473 if (FAILED(hr))
3474 return hr;
3476 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
3477 if (FAILED(hr))
3478 return hr;
3480 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
3481 if (ppdip <= 0.0f ||
3482 (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
3483 (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
3484 disabled = TRUE;
3485 else
3486 skiptransform = should_skip_transform(&m, &det);
3489 #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
3490 /* 1. Regular runs */
3491 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3492 const struct regular_layout_run *regular = &run->run->u.regular;
3493 UINT32 start_glyph = regular->clustermap[run->start];
3494 DWRITE_GLYPH_RUN_DESCRIPTION descr;
3495 DWRITE_GLYPH_RUN glyph_run;
3497 /* Everything but cluster map will be reused from nominal run, as we only need
3498 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
3499 it can't be reused because it has to start with 0 index for each reported run. */
3500 glyph_run = regular->run;
3501 glyph_run.glyphCount = run->glyphcount;
3503 /* fixup glyph data arrays */
3504 glyph_run.glyphIndices += start_glyph;
3505 glyph_run.glyphAdvances += start_glyph;
3506 glyph_run.glyphOffsets += start_glyph;
3508 /* description */
3509 descr = regular->descr;
3510 descr.stringLength = run->length;
3511 descr.string += run->start;
3512 descr.clusterMap = run->clustermap;
3513 descr.textPosition += run->start;
3515 /* return value is ignored */
3516 IDWriteTextRenderer_DrawGlyphRun(renderer,
3517 context,
3518 run->origin.x + run->align_dx + origin_x,
3519 SNAP_COORD(run->origin.y + origin_y),
3520 This->measuringmode,
3521 &glyph_run,
3522 &descr,
3523 run->effect);
3526 /* 2. Inline objects */
3527 LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
3528 IDWriteTextRenderer_DrawInlineObject(renderer,
3529 context,
3530 inlineobject->origin.x + inlineobject->align_dx + origin_x,
3531 SNAP_COORD(inlineobject->origin.y + origin_y),
3532 inlineobject->object,
3533 inlineobject->is_sideways,
3534 inlineobject->is_rtl,
3535 inlineobject->effect);
3538 /* 3. Underlines */
3539 LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) {
3540 IDWriteTextRenderer_DrawUnderline(renderer,
3541 context,
3542 /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
3543 (is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x,
3544 SNAP_COORD(u->run->origin.y + origin_y),
3545 &u->u,
3546 u->run->effect);
3549 /* 4. Strikethrough */
3550 LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
3551 IDWriteTextRenderer_DrawStrikethrough(renderer,
3552 context,
3553 s->run->origin.x + s->run->align_dx + origin_x,
3554 SNAP_COORD(s->run->origin.y + origin_y),
3555 &s->s,
3556 s->run->effect);
3558 #undef SNAP_COORD
3560 return S_OK;
3563 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface,
3564 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
3566 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3567 HRESULT hr;
3569 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3571 hr = layout_compute_effective_runs(This);
3572 if (FAILED(hr))
3573 return hr;
3575 if (metrics) {
3576 UINT32 i, c = min(max_count, This->metrics.lineCount);
3577 for (i = 0; i < c; i++)
3578 memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics));
3581 *count = This->metrics.lineCount;
3582 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3585 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics)
3587 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3588 DWRITE_TEXT_METRICS1 metrics1;
3589 HRESULT hr;
3591 TRACE("(%p)->(%p)\n", This, metrics);
3593 hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1);
3594 if (hr == S_OK)
3595 memcpy(metrics, &metrics1, sizeof(*metrics));
3597 return hr;
3600 static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D1_RECT_F *ret)
3602 #define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em)
3603 ret->left = SCALE(bbox->left);
3604 ret->right = SCALE(bbox->right);
3605 ret->top = SCALE(bbox->top);
3606 ret->bottom = SCALE(bbox->bottom);
3607 #undef SCALE
3610 static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y)
3612 rect->left += x;
3613 rect->right += x;
3614 rect->top += y;
3615 rect->bottom += y;
3618 static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect)
3620 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
3623 static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src)
3625 if (d2d_rect_is_empty(dst)) {
3626 if (d2d_rect_is_empty(src)) {
3627 dst->left = dst->right = dst->top = dst->bottom = 0.0f;
3628 return;
3630 else
3631 *dst = *src;
3633 else {
3634 if (!d2d_rect_is_empty(src)) {
3635 dst->left = min(dst->left, src->left);
3636 dst->right = max(dst->right, src->right);
3637 dst->top = min(dst->top, src->top);
3638 dst->bottom = max(dst->bottom, src->bottom);
3643 static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox)
3645 const struct regular_layout_run *regular = &run->run->u.regular;
3646 UINT32 start_glyph = regular->clustermap[run->start];
3647 const DWRITE_GLYPH_RUN *glyph_run = &regular->run;
3648 DWRITE_FONT_METRICS font_metrics;
3649 D2D1_POINT_2F origin = { 0 };
3650 UINT32 i;
3652 if (run->bbox.top == run->bbox.bottom) {
3653 IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics);
3655 for (i = 0; i < run->glyphcount; i++) {
3656 D2D1_RECT_F glyph_bbox;
3657 RECT design_bbox;
3659 freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm,
3660 glyph_run->glyphIndices[i + start_glyph], &design_bbox);
3662 scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox);
3663 d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
3664 origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
3665 d2d_rect_union(&run->bbox, &glyph_bbox);
3667 /* FIXME: take care of vertical/rtl */
3668 origin.x += glyph_run->glyphAdvances[i + start_glyph];
3672 *bbox = run->bbox;
3673 d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y);
3676 static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run,
3677 D2D1_RECT_F *bbox)
3679 DWRITE_OVERHANG_METRICS overhang_metrics = { 0 };
3680 DWRITE_INLINE_OBJECT_METRICS metrics = { 0 };
3681 HRESULT hr;
3683 if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) {
3684 WARN("Failed to get inline object metrics, hr %#x.\n", hr);
3685 memset(bbox, 0, sizeof(*bbox));
3686 return;
3689 bbox->left = run->origin.x + run->align_dx;
3690 bbox->right = bbox->left + metrics.width;
3691 bbox->top = run->origin.y;
3692 bbox->bottom = bbox->top + metrics.height;
3694 IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics);
3696 bbox->left -= overhang_metrics.left;
3697 bbox->right += overhang_metrics.right;
3698 bbox->top -= overhang_metrics.top;
3699 bbox->bottom += overhang_metrics.bottom;
3702 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface,
3703 DWRITE_OVERHANG_METRICS *overhangs)
3705 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3706 struct layout_effective_inline *inline_run;
3707 struct layout_effective_run *run;
3708 D2D1_RECT_F bbox = { 0 };
3709 HRESULT hr;
3711 TRACE("(%p)->(%p)\n", This, overhangs);
3713 memset(overhangs, 0, sizeof(*overhangs));
3715 if (!(This->recompute & RECOMPUTE_OVERHANGS)) {
3716 *overhangs = This->overhangs;
3717 return S_OK;
3720 hr = layout_compute_effective_runs(This);
3721 if (FAILED(hr))
3722 return hr;
3724 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3725 D2D1_RECT_F run_bbox;
3727 layout_get_erun_bbox(This, run, &run_bbox);
3728 d2d_rect_union(&bbox, &run_bbox);
3731 LIST_FOR_EACH_ENTRY(inline_run, &This->inlineobjects, struct layout_effective_inline, entry) {
3732 D2D1_RECT_F object_bbox;
3734 layout_get_inlineobj_bbox(This, inline_run, &object_bbox);
3735 d2d_rect_union(&bbox, &object_bbox);
3738 /* Deltas from layout box. */
3739 This->overhangs.left = -bbox.left;
3740 This->overhangs.top = -bbox.top;
3741 This->overhangs.right = bbox.right - This->metrics.layoutWidth;
3742 This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight;
3743 This->recompute &= ~RECOMPUTE_OVERHANGS;
3745 *overhangs = This->overhangs;
3747 return S_OK;
3750 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface,
3751 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
3753 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3754 HRESULT hr;
3756 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3758 hr = layout_compute(This);
3759 if (FAILED(hr))
3760 return hr;
3762 if (metrics)
3763 memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
3765 *count = This->cluster_count;
3766 return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3769 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width)
3771 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3772 UINT32 start;
3773 FLOAT width;
3774 HRESULT hr;
3776 TRACE("(%p)->(%p)\n", This, min_width);
3778 if (!min_width)
3779 return E_INVALIDARG;
3781 if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
3782 goto width_done;
3784 *min_width = 0.0f;
3785 hr = layout_compute(This);
3786 if (FAILED(hr))
3787 return hr;
3789 /* Find widest word without emergency breaking between clusters, trailing whitespaces
3790 preceding breaking point do not contribute to word width. */
3791 for (start = 0; start < This->cluster_count;) {
3792 UINT32 end = start, j, next;
3794 /* Last cluster always could be wrapped after. */
3795 while (!This->clustermetrics[end].canWrapLineAfter)
3796 end++;
3797 /* make is so current cluster range that we can wrap after is [start,end) */
3798 end++;
3800 next = end;
3802 /* Ignore trailing whitespace clusters, in case of single space range will
3803 be reduced to empty range, or [start,start+1). */
3804 while (end > start && This->clustermetrics[end-1].isWhitespace)
3805 end--;
3807 /* check if cluster range exceeds last minimal width */
3808 width = 0.0f;
3809 for (j = start; j < end; j++)
3810 width += This->clustermetrics[j].width;
3812 start = next;
3814 if (width > This->minwidth)
3815 This->minwidth = width;
3817 This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3819 width_done:
3820 *min_width = This->minwidth;
3821 return S_OK;
3824 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface,
3825 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3827 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3828 FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
3829 return E_NOTIMPL;
3832 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface,
3833 UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
3835 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3836 FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
3837 return E_NOTIMPL;
3840 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface,
3841 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3842 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3844 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3845 FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
3846 max_metricscount, actual_metricscount);
3847 return E_NOTIMPL;
3850 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled,
3851 DWRITE_TEXT_RANGE range)
3853 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3854 struct layout_range_attr_value value;
3856 TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
3858 value.range = range;
3859 value.u.pair_kerning = !!is_pairkerning_enabled;
3860 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3863 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
3864 DWRITE_TEXT_RANGE *r)
3866 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3867 struct layout_range *range;
3869 TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
3871 if (position >= This->len)
3872 return S_OK;
3874 range = get_layout_range_by_pos(This, position);
3875 *is_pairkerning_enabled = range->pair_kerning;
3877 return return_range(&range->h, r);
3880 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing,
3881 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3883 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3884 struct layout_range_attr_value value;
3886 TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
3888 if (min_advance < 0.0f)
3889 return E_INVALIDARG;
3891 value.range = range;
3892 value.u.spacing.leading = leading;
3893 value.u.spacing.trailing = trailing;
3894 value.u.spacing.min_advance = min_advance;
3895 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
3898 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading,
3899 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3901 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3902 struct layout_range_spacing *range;
3904 TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
3906 range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
3907 *leading = range->leading;
3908 *trailing = range->trailing;
3909 *min_advance = range->min_advance;
3911 return return_range(&range->h, r);
3914 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics)
3916 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3917 HRESULT hr;
3919 TRACE("(%p)->(%p)\n", This, metrics);
3921 hr = layout_compute_effective_runs(This);
3922 if (FAILED(hr))
3923 return hr;
3925 *metrics = This->metrics;
3926 return S_OK;
3929 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3931 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3933 TRACE("(%p)->(%d)\n", This, orientation);
3935 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3936 return E_INVALIDARG;
3938 This->format.vertical_orientation = orientation;
3939 return S_OK;
3942 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface)
3944 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3945 TRACE("(%p)\n", This);
3946 return This->format.vertical_orientation;
3949 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled)
3951 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3952 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
3953 return IDWriteTextFormat1_SetLastLineWrapping(&This->IDWriteTextFormat1_iface, lastline_wrapping_enabled);
3956 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface)
3958 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3959 TRACE("(%p)\n", This);
3960 return IDWriteTextFormat1_GetLastLineWrapping(&This->IDWriteTextFormat1_iface);
3963 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3965 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3966 TRACE("(%p)->(%d)\n", This, alignment);
3967 return IDWriteTextFormat1_SetOpticalAlignment(&This->IDWriteTextFormat1_iface, alignment);
3970 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface)
3972 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3973 TRACE("(%p)\n", This);
3974 return IDWriteTextFormat1_GetOpticalAlignment(&This->IDWriteTextFormat1_iface);
3977 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback)
3979 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3980 TRACE("(%p)->(%p)\n", This, fallback);
3981 return set_fontfallback_for_format(&This->format, fallback);
3984 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback)
3986 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3987 TRACE("(%p)->(%p)\n", This, fallback);
3988 return get_fontfallback_from_format(&This->format, fallback);
3991 static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface)
3993 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3995 TRACE("(%p)\n", This);
3997 This->recompute = RECOMPUTE_EVERYTHING;
3998 return S_OK;
4001 static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing)
4003 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4004 BOOL changed;
4005 HRESULT hr;
4007 TRACE("(%p)->(%p)\n", This, spacing);
4009 hr = format_set_linespacing(&This->format, spacing, &changed);
4010 if (FAILED(hr))
4011 return hr;
4013 if (changed) {
4014 if (!(This->recompute & RECOMPUTE_LINES)) {
4015 UINT32 line;
4017 for (line = 0; line < This->metrics.lineCount; line++)
4018 layout_apply_line_spacing(This, line);
4020 layout_set_line_positions(This);
4023 This->recompute |= RECOMPUTE_OVERHANGS;
4026 return S_OK;
4029 static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing)
4031 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4033 TRACE("(%p)->(%p)\n", This, spacing);
4035 *spacing = This->format.spacing;
4036 return S_OK;
4039 static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics,
4040 UINT32 max_count, UINT32 *count)
4042 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
4043 HRESULT hr;
4045 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
4047 hr = layout_compute_effective_runs(This);
4048 if (FAILED(hr))
4049 return hr;
4051 if (metrics)
4052 memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount));
4054 *count = This->metrics.lineCount;
4055 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
4058 static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = {
4059 dwritetextlayout_QueryInterface,
4060 dwritetextlayout_AddRef,
4061 dwritetextlayout_Release,
4062 dwritetextlayout_SetTextAlignment,
4063 dwritetextlayout_SetParagraphAlignment,
4064 dwritetextlayout_SetWordWrapping,
4065 dwritetextlayout_SetReadingDirection,
4066 dwritetextlayout_SetFlowDirection,
4067 dwritetextlayout_SetIncrementalTabStop,
4068 dwritetextlayout_SetTrimming,
4069 dwritetextlayout_SetLineSpacing,
4070 dwritetextlayout_GetTextAlignment,
4071 dwritetextlayout_GetParagraphAlignment,
4072 dwritetextlayout_GetWordWrapping,
4073 dwritetextlayout_GetReadingDirection,
4074 dwritetextlayout_GetFlowDirection,
4075 dwritetextlayout_GetIncrementalTabStop,
4076 dwritetextlayout_GetTrimming,
4077 dwritetextlayout_GetLineSpacing,
4078 dwritetextlayout_GetFontCollection,
4079 dwritetextlayout_GetFontFamilyNameLength,
4080 dwritetextlayout_GetFontFamilyName,
4081 dwritetextlayout_GetFontWeight,
4082 dwritetextlayout_GetFontStyle,
4083 dwritetextlayout_GetFontStretch,
4084 dwritetextlayout_GetFontSize,
4085 dwritetextlayout_GetLocaleNameLength,
4086 dwritetextlayout_GetLocaleName,
4087 dwritetextlayout_SetMaxWidth,
4088 dwritetextlayout_SetMaxHeight,
4089 dwritetextlayout_SetFontCollection,
4090 dwritetextlayout_SetFontFamilyName,
4091 dwritetextlayout_SetFontWeight,
4092 dwritetextlayout_SetFontStyle,
4093 dwritetextlayout_SetFontStretch,
4094 dwritetextlayout_SetFontSize,
4095 dwritetextlayout_SetUnderline,
4096 dwritetextlayout_SetStrikethrough,
4097 dwritetextlayout_SetDrawingEffect,
4098 dwritetextlayout_SetInlineObject,
4099 dwritetextlayout_SetTypography,
4100 dwritetextlayout_SetLocaleName,
4101 dwritetextlayout_GetMaxWidth,
4102 dwritetextlayout_GetMaxHeight,
4103 dwritetextlayout_layout_GetFontCollection,
4104 dwritetextlayout_layout_GetFontFamilyNameLength,
4105 dwritetextlayout_layout_GetFontFamilyName,
4106 dwritetextlayout_layout_GetFontWeight,
4107 dwritetextlayout_layout_GetFontStyle,
4108 dwritetextlayout_layout_GetFontStretch,
4109 dwritetextlayout_layout_GetFontSize,
4110 dwritetextlayout_GetUnderline,
4111 dwritetextlayout_GetStrikethrough,
4112 dwritetextlayout_GetDrawingEffect,
4113 dwritetextlayout_GetInlineObject,
4114 dwritetextlayout_GetTypography,
4115 dwritetextlayout_layout_GetLocaleNameLength,
4116 dwritetextlayout_layout_GetLocaleName,
4117 dwritetextlayout_Draw,
4118 dwritetextlayout_GetLineMetrics,
4119 dwritetextlayout_GetMetrics,
4120 dwritetextlayout_GetOverhangMetrics,
4121 dwritetextlayout_GetClusterMetrics,
4122 dwritetextlayout_DetermineMinWidth,
4123 dwritetextlayout_HitTestPoint,
4124 dwritetextlayout_HitTestTextPosition,
4125 dwritetextlayout_HitTestTextRange,
4126 dwritetextlayout1_SetPairKerning,
4127 dwritetextlayout1_GetPairKerning,
4128 dwritetextlayout1_SetCharacterSpacing,
4129 dwritetextlayout1_GetCharacterSpacing,
4130 dwritetextlayout2_GetMetrics,
4131 dwritetextlayout2_SetVerticalGlyphOrientation,
4132 dwritetextlayout2_GetVerticalGlyphOrientation,
4133 dwritetextlayout2_SetLastLineWrapping,
4134 dwritetextlayout2_GetLastLineWrapping,
4135 dwritetextlayout2_SetOpticalAlignment,
4136 dwritetextlayout2_GetOpticalAlignment,
4137 dwritetextlayout2_SetFontFallback,
4138 dwritetextlayout2_GetFontFallback,
4139 dwritetextlayout3_InvalidateLayout,
4140 dwritetextlayout3_SetLineSpacing,
4141 dwritetextlayout3_GetLineSpacing,
4142 dwritetextlayout3_GetLineMetrics
4145 static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
4147 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4148 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4149 return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj);
4152 static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat1 *iface)
4154 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4155 return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface);
4158 static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat1 *iface)
4160 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4161 return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface);
4164 static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
4166 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4167 BOOL changed;
4168 HRESULT hr;
4170 TRACE("(%p)->(%d)\n", This, alignment);
4172 hr = format_set_textalignment(&This->format, alignment, &changed);
4173 if (FAILED(hr))
4174 return hr;
4176 if (changed) {
4177 /* if layout is not ready there's nothing to align */
4178 if (!(This->recompute & RECOMPUTE_LINES))
4179 layout_apply_text_alignment(This);
4180 This->recompute |= RECOMPUTE_OVERHANGS;
4183 return S_OK;
4186 static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
4188 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4189 BOOL changed;
4190 HRESULT hr;
4192 TRACE("(%p)->(%d)\n", This, alignment);
4194 hr = format_set_paralignment(&This->format, alignment, &changed);
4195 if (FAILED(hr))
4196 return hr;
4198 if (changed) {
4199 /* if layout is not ready there's nothing to align */
4200 if (!(This->recompute & RECOMPUTE_LINES))
4201 layout_apply_par_alignment(This);
4202 This->recompute |= RECOMPUTE_OVERHANGS;
4205 return S_OK;
4208 static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
4210 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4211 BOOL changed;
4212 HRESULT hr;
4214 TRACE("(%p)->(%d)\n", This, wrapping);
4216 hr = format_set_wordwrapping(&This->format, wrapping, &changed);
4217 if (FAILED(hr))
4218 return hr;
4220 if (changed)
4221 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4223 return S_OK;
4226 static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
4228 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4229 BOOL changed;
4230 HRESULT hr;
4232 TRACE("(%p)->(%d)\n", This, direction);
4234 hr = format_set_readingdirection(&This->format, direction, &changed);
4235 if (FAILED(hr))
4236 return hr;
4238 if (changed)
4239 This->recompute = RECOMPUTE_EVERYTHING;
4241 return S_OK;
4244 static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
4246 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4247 BOOL changed;
4248 HRESULT hr;
4250 TRACE("(%p)->(%d)\n", This, direction);
4252 hr = format_set_flowdirection(&This->format, direction, &changed);
4253 if (FAILED(hr))
4254 return hr;
4256 if (changed)
4257 This->recompute = RECOMPUTE_EVERYTHING;
4259 return S_OK;
4262 static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
4264 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4265 FIXME("(%p)->(%f): stub\n", This, tabstop);
4266 return S_OK;
4269 static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
4270 IDWriteInlineObject *trimming_sign)
4272 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4273 BOOL changed;
4274 HRESULT hr;
4276 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
4278 hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed);
4280 if (changed)
4281 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4283 return hr;
4286 static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method,
4287 FLOAT height, FLOAT baseline)
4289 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4290 DWRITE_LINE_SPACING spacing;
4292 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
4294 spacing = This->format.spacing;
4295 spacing.method = method;
4296 spacing.height = height;
4297 spacing.baseline = baseline;
4298 return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing);
4301 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat1 *iface)
4303 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4304 TRACE("(%p)\n", This);
4305 return This->format.textalignment;
4308 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface)
4310 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4311 TRACE("(%p)\n", This);
4312 return This->format.paralign;
4315 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat1 *iface)
4317 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4318 TRACE("(%p)\n", This);
4319 return This->format.wrapping;
4322 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat1 *iface)
4324 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4325 TRACE("(%p)\n", This);
4326 return This->format.readingdir;
4329 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat1 *iface)
4331 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4332 TRACE("(%p)\n", This);
4333 return This->format.flow;
4336 static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
4338 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4339 FIXME("(%p): stub\n", This);
4340 return 0.0f;
4343 static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
4344 IDWriteInlineObject **trimming_sign)
4346 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4348 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
4350 *options = This->format.trimming;
4351 *trimming_sign = This->format.trimmingsign;
4352 if (*trimming_sign)
4353 IDWriteInlineObject_AddRef(*trimming_sign);
4354 return S_OK;
4357 static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
4358 FLOAT *spacing, FLOAT *baseline)
4360 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4362 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
4364 *method = This->format.spacing.method;
4365 *spacing = This->format.spacing.height;
4366 *baseline = This->format.spacing.baseline;
4367 return S_OK;
4370 static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
4372 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4374 TRACE("(%p)->(%p)\n", This, collection);
4376 *collection = This->format.collection;
4377 if (*collection)
4378 IDWriteFontCollection_AddRef(*collection);
4379 return S_OK;
4382 static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
4384 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4385 TRACE("(%p)\n", This);
4386 return This->format.family_len;
4389 static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4391 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4393 TRACE("(%p)->(%p %u)\n", This, name, size);
4395 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4396 strcpyW(name, This->format.family_name);
4397 return S_OK;
4400 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat1 *iface)
4402 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4403 TRACE("(%p)\n", This);
4404 return This->format.weight;
4407 static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat1 *iface)
4409 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4410 TRACE("(%p)\n", This);
4411 return This->format.style;
4414 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat1 *iface)
4416 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4417 TRACE("(%p)\n", This);
4418 return This->format.stretch;
4421 static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat1 *iface)
4423 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4424 TRACE("(%p)\n", This);
4425 return This->format.fontsize;
4428 static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface)
4430 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4431 TRACE("(%p)\n", This);
4432 return This->format.locale_len;
4435 static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4437 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4439 TRACE("(%p)->(%p %u)\n", This, name, size);
4441 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4442 strcpyW(name, This->format.locale);
4443 return S_OK;
4446 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4448 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4449 FIXME("(%p)->(%d): stub\n", This, orientation);
4450 return E_NOTIMPL;
4453 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
4455 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4456 FIXME("(%p): stub\n", This);
4457 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4460 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
4462 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4464 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
4466 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
4467 return S_OK;
4470 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface)
4472 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4473 TRACE("(%p)\n", This);
4474 return This->format.last_line_wrapping;
4477 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
4479 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4480 TRACE("(%p)->(%d)\n", This, alignment);
4481 return format_set_optical_alignment(&This->format, alignment);
4484 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface)
4486 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4487 TRACE("(%p)\n", This);
4488 return This->format.optical_alignment;
4491 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
4493 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4494 TRACE("(%p)->(%p)\n", This, fallback);
4495 return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4498 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
4500 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4501 TRACE("(%p)->(%p)\n", This, fallback);
4502 return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4505 static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = {
4506 dwritetextformat_layout_QueryInterface,
4507 dwritetextformat_layout_AddRef,
4508 dwritetextformat_layout_Release,
4509 dwritetextformat_layout_SetTextAlignment,
4510 dwritetextformat_layout_SetParagraphAlignment,
4511 dwritetextformat_layout_SetWordWrapping,
4512 dwritetextformat_layout_SetReadingDirection,
4513 dwritetextformat_layout_SetFlowDirection,
4514 dwritetextformat_layout_SetIncrementalTabStop,
4515 dwritetextformat_layout_SetTrimming,
4516 dwritetextformat_layout_SetLineSpacing,
4517 dwritetextformat_layout_GetTextAlignment,
4518 dwritetextformat_layout_GetParagraphAlignment,
4519 dwritetextformat_layout_GetWordWrapping,
4520 dwritetextformat_layout_GetReadingDirection,
4521 dwritetextformat_layout_GetFlowDirection,
4522 dwritetextformat_layout_GetIncrementalTabStop,
4523 dwritetextformat_layout_GetTrimming,
4524 dwritetextformat_layout_GetLineSpacing,
4525 dwritetextformat_layout_GetFontCollection,
4526 dwritetextformat_layout_GetFontFamilyNameLength,
4527 dwritetextformat_layout_GetFontFamilyName,
4528 dwritetextformat_layout_GetFontWeight,
4529 dwritetextformat_layout_GetFontStyle,
4530 dwritetextformat_layout_GetFontStretch,
4531 dwritetextformat_layout_GetFontSize,
4532 dwritetextformat_layout_GetLocaleNameLength,
4533 dwritetextformat_layout_GetLocaleName,
4534 dwritetextformat1_layout_SetVerticalGlyphOrientation,
4535 dwritetextformat1_layout_GetVerticalGlyphOrientation,
4536 dwritetextformat1_layout_SetLastLineWrapping,
4537 dwritetextformat1_layout_GetLastLineWrapping,
4538 dwritetextformat1_layout_SetOpticalAlignment,
4539 dwritetextformat1_layout_GetOpticalAlignment,
4540 dwritetextformat1_layout_SetFontFallback,
4541 dwritetextformat1_layout_GetFontFallback,
4544 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
4545 REFIID riid, void **obj)
4547 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
4548 IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
4549 IsEqualIID(riid, &IID_IUnknown))
4551 *obj = iface;
4552 IDWriteTextAnalysisSink1_AddRef(iface);
4553 return S_OK;
4556 WARN("%s not implemented.\n", debugstr_guid(riid));
4558 *obj = NULL;
4559 return E_NOINTERFACE;
4562 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
4564 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4565 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4568 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
4570 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4571 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4574 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
4575 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
4577 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4578 struct layout_run *run;
4580 TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
4582 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position);
4583 if (!run)
4584 return E_OUTOFMEMORY;
4586 run->u.regular.descr.string = &layout->str[position];
4587 run->u.regular.descr.stringLength = length;
4588 run->u.regular.descr.textPosition = position;
4589 run->u.regular.sa = *sa;
4590 list_add_tail(&layout->runs, &run->entry);
4591 return S_OK;
4594 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
4595 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
4597 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4599 if (position + length > layout->len)
4600 return E_FAIL;
4602 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
4603 return S_OK;
4606 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
4607 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
4609 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4610 struct layout_run *cur_run;
4612 TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
4614 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
4615 struct regular_layout_run *cur = &cur_run->u.regular;
4616 struct layout_run *run;
4618 if (cur_run->kind == LAYOUT_RUN_INLINE)
4619 continue;
4621 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
4622 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
4623 continue;
4625 /* full hit - just set run level */
4626 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
4627 cur->run.bidiLevel = resolvedLevel;
4628 break;
4631 /* current run is fully covered, move to next one */
4632 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
4633 cur->run.bidiLevel = resolvedLevel;
4634 position += cur->descr.stringLength;
4635 length -= cur->descr.stringLength;
4636 continue;
4639 /* all fully covered runs are processed at this point, reuse existing run for remaining
4640 reported bidi range and add another run for the rest of original one */
4642 run = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length);
4643 if (!run)
4644 return E_OUTOFMEMORY;
4646 *run = *cur_run;
4647 run->u.regular.descr.textPosition = position + length;
4648 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
4649 run->u.regular.descr.string = &layout->str[position + length];
4651 /* reduce existing run */
4652 cur->run.bidiLevel = resolvedLevel;
4653 cur->descr.stringLength = length;
4655 list_add_after(&cur_run->entry, &run->entry);
4656 break;
4659 return S_OK;
4662 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
4663 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
4665 return E_NOTIMPL;
4668 static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
4669 UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
4670 BOOL is_sideways, BOOL is_rtl)
4672 return E_NOTIMPL;
4675 static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
4676 dwritetextlayout_sink_QueryInterface,
4677 dwritetextlayout_sink_AddRef,
4678 dwritetextlayout_sink_Release,
4679 dwritetextlayout_sink_SetScriptAnalysis,
4680 dwritetextlayout_sink_SetLineBreakpoints,
4681 dwritetextlayout_sink_SetBidiLevel,
4682 dwritetextlayout_sink_SetNumberSubstitution,
4683 dwritetextlayout_sink_SetGlyphOrientation
4686 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
4687 REFIID riid, void **obj)
4689 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
4690 IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
4691 IsEqualIID(riid, &IID_IUnknown))
4693 *obj = iface;
4694 IDWriteTextAnalysisSource1_AddRef(iface);
4695 return S_OK;
4698 WARN("%s not implemented.\n", debugstr_guid(riid));
4700 *obj = NULL;
4701 return E_NOINTERFACE;
4704 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
4706 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4707 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4710 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
4712 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4713 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4716 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
4717 UINT32 position, WCHAR const** text, UINT32* text_len)
4719 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4721 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4723 if (position < layout->len) {
4724 *text = &layout->str[position];
4725 *text_len = layout->len - position;
4727 else {
4728 *text = NULL;
4729 *text_len = 0;
4732 return S_OK;
4735 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
4736 UINT32 position, WCHAR const** text, UINT32* text_len)
4738 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4740 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4742 if (position > 0 && position < layout->len) {
4743 *text = layout->str;
4744 *text_len = position;
4746 else {
4747 *text = NULL;
4748 *text_len = 0;
4751 return S_OK;
4754 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
4756 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4757 return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface);
4760 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
4761 UINT32 position, UINT32* text_len, WCHAR const** locale)
4763 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4764 struct layout_range *range = get_layout_range_by_pos(layout, position);
4766 if (position < layout->len) {
4767 struct layout_range *next;
4769 *locale = range->locale;
4770 *text_len = range->h.range.length - position;
4772 next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
4773 while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
4774 *text_len += next->h.range.length;
4775 next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
4778 *text_len = min(*text_len, layout->len - position);
4780 else {
4781 *locale = NULL;
4782 *text_len = 0;
4785 return S_OK;
4788 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
4789 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
4791 FIXME("%u %p %p: stub\n", position, text_len, substitution);
4792 return E_NOTIMPL;
4795 static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
4796 UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
4798 FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
4799 return E_NOTIMPL;
4802 static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
4803 dwritetextlayout_source_QueryInterface,
4804 dwritetextlayout_source_AddRef,
4805 dwritetextlayout_source_Release,
4806 dwritetextlayout_source_GetTextAtPosition,
4807 dwritetextlayout_source_GetTextBeforePosition,
4808 dwritetextlayout_source_GetParagraphReadingDirection,
4809 dwritetextlayout_source_GetLocaleName,
4810 dwritetextlayout_source_GetNumberSubstitution,
4811 dwritetextlayout_source_GetVerticalGlyphOrientation
4814 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
4816 struct dwrite_textformat *textformat;
4817 IDWriteTextFormat1 *format1;
4818 UINT32 len;
4819 HRESULT hr;
4821 if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
4822 layout->format = textformat->format;
4824 layout->format.locale = heap_strdupW(textformat->format.locale);
4825 layout->format.family_name = heap_strdupW(textformat->format.family_name);
4826 if (!layout->format.locale || !layout->format.family_name)
4828 heap_free(layout->format.locale);
4829 heap_free(layout->format.family_name);
4830 return E_OUTOFMEMORY;
4833 if (layout->format.trimmingsign)
4834 IDWriteInlineObject_AddRef(layout->format.trimmingsign);
4835 if (layout->format.collection)
4836 IDWriteFontCollection_AddRef(layout->format.collection);
4837 if (layout->format.fallback)
4838 IDWriteFontFallback_AddRef(layout->format.fallback);
4840 return S_OK;
4843 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
4844 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
4845 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
4846 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
4847 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
4848 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
4849 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
4850 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
4851 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
4852 layout->format.fallback = NULL;
4853 layout->format.spacing.leadingBefore = 0.0f;
4854 layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
4855 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
4856 &layout->format.spacing.height, &layout->format.spacing.baseline);
4857 if (FAILED(hr))
4858 return hr;
4860 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
4861 if (FAILED(hr))
4862 return hr;
4864 /* locale name and length */
4865 len = IDWriteTextFormat_GetLocaleNameLength(format);
4866 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
4867 if (!layout->format.locale)
4868 return E_OUTOFMEMORY;
4870 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
4871 if (FAILED(hr))
4872 return hr;
4873 layout->format.locale_len = len;
4875 /* font family name and length */
4876 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
4877 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
4878 if (!layout->format.family_name)
4879 return E_OUTOFMEMORY;
4881 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
4882 if (FAILED(hr))
4883 return hr;
4884 layout->format.family_len = len;
4886 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4887 if (hr == S_OK) {
4888 IDWriteTextFormat2 *format2;
4890 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
4891 layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4892 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
4894 if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
4895 IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
4896 IDWriteTextFormat2_Release(format2);
4899 IDWriteTextFormat1_Release(format1);
4901 else {
4902 layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4903 layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
4906 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
4909 static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
4911 struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
4912 static const DWRITE_TEXT_RANGE r = { 0, ~0u };
4913 HRESULT hr;
4915 layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl;
4916 layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl;
4917 layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
4918 layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
4919 layout->ref = 1;
4920 layout->len = desc->length;
4921 layout->recompute = RECOMPUTE_EVERYTHING;
4922 layout->nominal_breakpoints = NULL;
4923 layout->actual_breakpoints = NULL;
4924 layout->cluster_count = 0;
4925 layout->clustermetrics = NULL;
4926 layout->clusters = NULL;
4927 layout->linemetrics = NULL;
4928 layout->lines = NULL;
4929 layout->line_alloc = 0;
4930 layout->minwidth = 0.0f;
4931 list_init(&layout->eruns);
4932 list_init(&layout->inlineobjects);
4933 list_init(&layout->underlines);
4934 list_init(&layout->strikethrough);
4935 list_init(&layout->runs);
4936 list_init(&layout->ranges);
4937 list_init(&layout->strike_ranges);
4938 list_init(&layout->underline_ranges);
4939 list_init(&layout->effects);
4940 list_init(&layout->spacing);
4941 list_init(&layout->typographies);
4942 memset(&layout->format, 0, sizeof(layout->format));
4943 memset(&layout->metrics, 0, sizeof(layout->metrics));
4944 layout->metrics.layoutWidth = desc->max_width;
4945 layout->metrics.layoutHeight = desc->max_height;
4946 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4948 layout->ppdip = 0.0f;
4949 memset(&layout->transform, 0, sizeof(layout->transform));
4951 layout->str = heap_strdupnW(desc->string, desc->length);
4952 if (desc->length && !layout->str) {
4953 hr = E_OUTOFMEMORY;
4954 goto fail;
4957 hr = layout_format_from_textformat(layout, desc->format);
4958 if (FAILED(hr))
4959 goto fail;
4961 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
4962 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
4963 underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
4964 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
4965 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
4966 typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
4967 if (!range || !strike || !effect || !spacing || !typography || !underline) {
4968 free_layout_range(range);
4969 free_layout_range(strike);
4970 free_layout_range(underline);
4971 free_layout_range(effect);
4972 free_layout_range(spacing);
4973 free_layout_range(typography);
4974 hr = E_OUTOFMEMORY;
4975 goto fail;
4978 if (desc->is_gdi_compatible)
4979 layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
4980 else
4981 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4982 layout->ppdip = desc->ppdip;
4983 layout->transform = desc->transform ? *desc->transform : identity;
4985 layout->factory = desc->factory;
4986 IDWriteFactory5_AddRef(layout->factory);
4987 list_add_head(&layout->ranges, &range->entry);
4988 list_add_head(&layout->strike_ranges, &strike->entry);
4989 list_add_head(&layout->underline_ranges, &underline->entry);
4990 list_add_head(&layout->effects, &effect->entry);
4991 list_add_head(&layout->spacing, &spacing->entry);
4992 list_add_head(&layout->typographies, &typography->entry);
4993 return S_OK;
4995 fail:
4996 IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4997 return hr;
5000 HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret)
5002 struct dwrite_textlayout *layout;
5003 HRESULT hr;
5005 *ret = NULL;
5007 if (!desc->format || !desc->string)
5008 return E_INVALIDARG;
5010 layout = heap_alloc(sizeof(struct dwrite_textlayout));
5011 if (!layout) return E_OUTOFMEMORY;
5013 hr = init_textlayout(desc, layout);
5014 if (hr == S_OK)
5015 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface;
5017 return hr;
5020 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
5022 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5024 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5026 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
5027 *obj = iface;
5028 IDWriteInlineObject_AddRef(iface);
5029 return S_OK;
5032 WARN("%s not implemented.\n", debugstr_guid(riid));
5034 *obj = NULL;
5035 return E_NOINTERFACE;
5038 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
5040 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5041 ULONG ref = InterlockedIncrement(&This->ref);
5042 TRACE("(%p)->(%d)\n", This, ref);
5043 return ref;
5046 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
5048 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5049 ULONG ref = InterlockedDecrement(&This->ref);
5051 TRACE("(%p)->(%d)\n", This, ref);
5053 if (!ref) {
5054 IDWriteTextLayout_Release(This->layout);
5055 heap_free(This);
5058 return ref;
5061 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
5062 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
5064 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5065 DWRITE_LINE_METRICS line;
5066 UINT32 line_count;
5068 TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY,
5069 is_sideways, is_rtl, effect);
5071 IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count);
5072 return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline);
5075 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
5077 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5078 DWRITE_TEXT_METRICS metrics;
5079 HRESULT hr;
5081 TRACE("(%p)->(%p)\n", This, ret);
5083 hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
5084 if (FAILED(hr)) {
5085 memset(ret, 0, sizeof(*ret));
5086 return hr;
5089 ret->width = metrics.width;
5090 ret->height = 0.0f;
5091 ret->baseline = 0.0f;
5092 ret->supportsSideways = FALSE;
5093 return S_OK;
5096 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
5098 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5099 TRACE("(%p)->(%p)\n", This, overhangs);
5100 return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs);
5103 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
5104 DWRITE_BREAK_CONDITION *after)
5106 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5108 TRACE("(%p)->(%p %p)\n", This, before, after);
5110 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
5111 return S_OK;
5114 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
5115 dwritetrimmingsign_QueryInterface,
5116 dwritetrimmingsign_AddRef,
5117 dwritetrimmingsign_Release,
5118 dwritetrimmingsign_Draw,
5119 dwritetrimmingsign_GetMetrics,
5120 dwritetrimmingsign_GetOverhangMetrics,
5121 dwritetrimmingsign_GetBreakConditions
5124 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
5126 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
5127 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
5130 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
5132 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
5133 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
5136 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
5138 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
5139 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
5142 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
5144 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
5145 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
5148 HRESULT create_trimmingsign(IDWriteFactory5 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
5150 static const WCHAR ellipsisW = 0x2026;
5151 struct dwrite_trimmingsign *This;
5152 DWRITE_READING_DIRECTION reading;
5153 DWRITE_FLOW_DIRECTION flow;
5154 HRESULT hr;
5156 *sign = NULL;
5158 /* Validate reading/flow direction here, layout creation won't complain about
5159 invalid combinations. */
5160 reading = IDWriteTextFormat_GetReadingDirection(format);
5161 flow = IDWriteTextFormat_GetFlowDirection(format);
5163 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
5164 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
5165 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
5167 This = heap_alloc(sizeof(*This));
5168 if (!This)
5169 return E_OUTOFMEMORY;
5171 This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
5172 This->ref = 1;
5174 hr = IDWriteFactory5_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout);
5175 if (FAILED(hr)) {
5176 heap_free(This);
5177 return hr;
5180 IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
5181 IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
5182 IDWriteTextLayout_SetTextAlignment(This->layout, DWRITE_TEXT_ALIGNMENT_LEADING);
5184 *sign = &This->IDWriteInlineObject_iface;
5186 return S_OK;
5189 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
5191 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5193 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5195 if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
5196 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
5197 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
5198 IsEqualIID(riid, &IID_IUnknown))
5200 *obj = iface;
5201 IDWriteTextFormat2_AddRef(iface);
5202 return S_OK;
5205 WARN("%s not implemented.\n", debugstr_guid(riid));
5207 *obj = NULL;
5209 return E_NOINTERFACE;
5212 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface)
5214 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5215 ULONG ref = InterlockedIncrement(&This->ref);
5216 TRACE("(%p)->(%d)\n", This, ref);
5217 return ref;
5220 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface)
5222 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5223 ULONG ref = InterlockedDecrement(&This->ref);
5225 TRACE("(%p)->(%d)\n", This, ref);
5227 if (!ref)
5229 release_format_data(&This->format);
5230 heap_free(This);
5233 return ref;
5236 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
5238 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5239 TRACE("(%p)->(%d)\n", This, alignment);
5240 return format_set_textalignment(&This->format, alignment, NULL);
5243 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
5245 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5246 TRACE("(%p)->(%d)\n", This, alignment);
5247 return format_set_paralignment(&This->format, alignment, NULL);
5250 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
5252 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5253 TRACE("(%p)->(%d)\n", This, wrapping);
5254 return format_set_wordwrapping(&This->format, wrapping, NULL);
5257 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction)
5259 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5260 TRACE("(%p)->(%d)\n", This, direction);
5261 return format_set_readingdirection(&This->format, direction, NULL);
5264 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction)
5266 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5267 TRACE("(%p)->(%d)\n", This, direction);
5268 return format_set_flowdirection(&This->format, direction, NULL);
5271 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
5273 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5274 FIXME("(%p)->(%f): stub\n", This, tabstop);
5275 return S_OK;
5278 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
5279 IDWriteInlineObject *trimming_sign)
5281 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5282 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
5283 return format_set_trimming(&This->format, trimming, trimming_sign, NULL);
5286 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method,
5287 FLOAT height, FLOAT baseline)
5289 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5290 DWRITE_LINE_SPACING spacing;
5292 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
5294 spacing = This->format.spacing;
5295 spacing.method = method;
5296 spacing.height = height;
5297 spacing.baseline = baseline;
5299 return format_set_linespacing(&This->format, &spacing, NULL);
5302 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface)
5304 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5305 TRACE("(%p)\n", This);
5306 return This->format.textalignment;
5309 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface)
5311 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5312 TRACE("(%p)\n", This);
5313 return This->format.paralign;
5316 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface)
5318 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5319 TRACE("(%p)\n", This);
5320 return This->format.wrapping;
5323 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface)
5325 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5326 TRACE("(%p)\n", This);
5327 return This->format.readingdir;
5330 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface)
5332 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5333 TRACE("(%p)\n", This);
5334 return This->format.flow;
5337 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
5339 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5340 FIXME("(%p): stub\n", This);
5341 return 0.0f;
5344 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
5345 IDWriteInlineObject **trimming_sign)
5347 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5348 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
5350 *options = This->format.trimming;
5351 if ((*trimming_sign = This->format.trimmingsign))
5352 IDWriteInlineObject_AddRef(*trimming_sign);
5354 return S_OK;
5357 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method,
5358 FLOAT *spacing, FLOAT *baseline)
5360 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5361 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
5363 *method = This->format.spacing.method;
5364 *spacing = This->format.spacing.height;
5365 *baseline = This->format.spacing.baseline;
5366 return S_OK;
5369 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection)
5371 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5373 TRACE("(%p)->(%p)\n", This, collection);
5375 *collection = This->format.collection;
5376 IDWriteFontCollection_AddRef(*collection);
5378 return S_OK;
5381 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
5383 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5384 TRACE("(%p)\n", This);
5385 return This->format.family_len;
5388 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5390 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5392 TRACE("(%p)->(%p %u)\n", This, name, size);
5394 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
5395 strcpyW(name, This->format.family_name);
5396 return S_OK;
5399 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface)
5401 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5402 TRACE("(%p)\n", This);
5403 return This->format.weight;
5406 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface)
5408 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5409 TRACE("(%p)\n", This);
5410 return This->format.style;
5413 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface)
5415 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5416 TRACE("(%p)\n", This);
5417 return This->format.stretch;
5420 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface)
5422 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5423 TRACE("(%p)\n", This);
5424 return This->format.fontsize;
5427 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface)
5429 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5430 TRACE("(%p)\n", This);
5431 return This->format.locale_len;
5434 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5436 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5438 TRACE("(%p)->(%p %u)\n", This, name, size);
5440 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
5441 strcpyW(name, This->format.locale);
5442 return S_OK;
5445 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
5447 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5449 TRACE("(%p)->(%d)\n", This, orientation);
5451 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
5452 return E_INVALIDARG;
5454 This->format.vertical_orientation = orientation;
5455 return S_OK;
5458 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
5460 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5461 TRACE("(%p)\n", This);
5462 return This->format.vertical_orientation;
5465 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled)
5467 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5469 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
5471 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
5472 return S_OK;
5475 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface)
5477 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5478 TRACE("(%p)\n", This);
5479 return This->format.last_line_wrapping;
5482 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
5484 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5485 TRACE("(%p)->(%d)\n", This, alignment);
5486 return format_set_optical_alignment(&This->format, alignment);
5489 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface)
5491 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5492 TRACE("(%p)\n", This);
5493 return This->format.optical_alignment;
5496 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback)
5498 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5499 TRACE("(%p)->(%p)\n", This, fallback);
5500 return set_fontfallback_for_format(&This->format, fallback);
5503 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback)
5505 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5506 TRACE("(%p)->(%p)\n", This, fallback);
5507 return get_fontfallback_from_format(&This->format, fallback);
5510 static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing)
5512 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5513 TRACE("(%p)->(%p)\n", This, spacing);
5514 return format_set_linespacing(&This->format, spacing, NULL);
5517 static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
5519 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5521 TRACE("(%p)->(%p)\n", This, spacing);
5523 *spacing = This->format.spacing;
5524 return S_OK;
5527 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = {
5528 dwritetextformat_QueryInterface,
5529 dwritetextformat_AddRef,
5530 dwritetextformat_Release,
5531 dwritetextformat_SetTextAlignment,
5532 dwritetextformat_SetParagraphAlignment,
5533 dwritetextformat_SetWordWrapping,
5534 dwritetextformat_SetReadingDirection,
5535 dwritetextformat_SetFlowDirection,
5536 dwritetextformat_SetIncrementalTabStop,
5537 dwritetextformat_SetTrimming,
5538 dwritetextformat_SetLineSpacing,
5539 dwritetextformat_GetTextAlignment,
5540 dwritetextformat_GetParagraphAlignment,
5541 dwritetextformat_GetWordWrapping,
5542 dwritetextformat_GetReadingDirection,
5543 dwritetextformat_GetFlowDirection,
5544 dwritetextformat_GetIncrementalTabStop,
5545 dwritetextformat_GetTrimming,
5546 dwritetextformat_GetLineSpacing,
5547 dwritetextformat_GetFontCollection,
5548 dwritetextformat_GetFontFamilyNameLength,
5549 dwritetextformat_GetFontFamilyName,
5550 dwritetextformat_GetFontWeight,
5551 dwritetextformat_GetFontStyle,
5552 dwritetextformat_GetFontStretch,
5553 dwritetextformat_GetFontSize,
5554 dwritetextformat_GetLocaleNameLength,
5555 dwritetextformat_GetLocaleName,
5556 dwritetextformat1_SetVerticalGlyphOrientation,
5557 dwritetextformat1_GetVerticalGlyphOrientation,
5558 dwritetextformat1_SetLastLineWrapping,
5559 dwritetextformat1_GetLastLineWrapping,
5560 dwritetextformat1_SetOpticalAlignment,
5561 dwritetextformat1_GetOpticalAlignment,
5562 dwritetextformat1_SetFontFallback,
5563 dwritetextformat1_GetFontFallback,
5564 dwritetextformat2_SetLineSpacing,
5565 dwritetextformat2_GetLineSpacing
5568 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
5570 return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
5571 CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL;
5574 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
5575 DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
5577 struct dwrite_textformat *This;
5579 *format = NULL;
5581 if (size <= 0.0f)
5582 return E_INVALIDARG;
5584 if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
5585 ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
5586 ((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
5587 return E_INVALIDARG;
5589 This = heap_alloc(sizeof(struct dwrite_textformat));
5590 if (!This) return E_OUTOFMEMORY;
5592 This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl;
5593 This->ref = 1;
5594 This->format.family_name = heap_strdupW(family_name);
5595 This->format.family_len = strlenW(family_name);
5596 This->format.locale = heap_strdupW(locale);
5597 This->format.locale_len = strlenW(locale);
5598 /* force locale name to lower case, layout will inherit this modified value */
5599 strlwrW(This->format.locale);
5600 This->format.weight = weight;
5601 This->format.style = style;
5602 This->format.fontsize = size;
5603 This->format.stretch = stretch;
5604 This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
5605 This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
5606 This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
5607 This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
5608 This->format.last_line_wrapping = TRUE;
5609 This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
5610 This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
5611 This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT;
5612 This->format.spacing.height = 0.0f;
5613 This->format.spacing.baseline = 0.0f;
5614 This->format.spacing.leadingBefore = 0.0f;
5615 This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
5616 This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
5617 This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
5618 This->format.trimming.delimiter = 0;
5619 This->format.trimming.delimiterCount = 0;
5620 This->format.trimmingsign = NULL;
5621 This->format.collection = collection;
5622 This->format.fallback = NULL;
5623 IDWriteFontCollection_AddRef(collection);
5625 *format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface;
5627 return S_OK;
5630 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
5632 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5634 TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
5636 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
5637 *obj = iface;
5638 IDWriteTypography_AddRef(iface);
5639 return S_OK;
5642 WARN("%s not implemented.\n", debugstr_guid(riid));
5644 *obj = NULL;
5646 return E_NOINTERFACE;
5649 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
5651 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5652 ULONG ref = InterlockedIncrement(&typography->ref);
5653 TRACE("(%p)->(%d)\n", typography, ref);
5654 return ref;
5657 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
5659 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5660 ULONG ref = InterlockedDecrement(&typography->ref);
5662 TRACE("(%p)->(%d)\n", typography, ref);
5664 if (!ref) {
5665 heap_free(typography->features);
5666 heap_free(typography);
5669 return ref;
5672 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
5674 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5676 TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
5678 if (typography->count == typography->allocated) {
5679 DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5680 if (!ptr)
5681 return E_OUTOFMEMORY;
5683 typography->features = ptr;
5684 typography->allocated *= 2;
5687 typography->features[typography->count++] = feature;
5688 return S_OK;
5691 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
5693 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5694 TRACE("(%p)\n", typography);
5695 return typography->count;
5698 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
5700 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5702 TRACE("(%p)->(%u %p)\n", typography, index, feature);
5704 if (index >= typography->count)
5705 return E_INVALIDARG;
5707 *feature = typography->features[index];
5708 return S_OK;
5711 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
5712 dwritetypography_QueryInterface,
5713 dwritetypography_AddRef,
5714 dwritetypography_Release,
5715 dwritetypography_AddFontFeature,
5716 dwritetypography_GetFontFeatureCount,
5717 dwritetypography_GetFontFeature
5720 HRESULT create_typography(IDWriteTypography **ret)
5722 struct dwrite_typography *typography;
5724 *ret = NULL;
5726 typography = heap_alloc(sizeof(*typography));
5727 if (!typography)
5728 return E_OUTOFMEMORY;
5730 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
5731 typography->ref = 1;
5732 typography->allocated = 2;
5733 typography->count = 0;
5735 typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5736 if (!typography->features) {
5737 heap_free(typography);
5738 return E_OUTOFMEMORY;
5741 *ret = &typography->IDWriteTypography_iface;
5742 return S_OK;