msvcrt: Use fpclass constants from public header.
[wine/zf.git] / dlls / dwrite / layout.c
blob1f6201a6a93753b2a4432c3834a15f456af81cfe
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
37 WCHAR *family_name;
38 UINT32 family_len;
39 WCHAR *locale;
40 UINT32 locale_len;
42 DWRITE_FONT_WEIGHT weight;
43 DWRITE_FONT_STYLE style;
44 DWRITE_FONT_STRETCH stretch;
46 DWRITE_PARAGRAPH_ALIGNMENT paralign;
47 DWRITE_READING_DIRECTION readingdir;
48 DWRITE_WORD_WRAPPING wrapping;
49 BOOL last_line_wrapping;
50 DWRITE_TEXT_ALIGNMENT textalignment;
51 DWRITE_FLOW_DIRECTION flow;
52 DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
53 DWRITE_OPTICAL_ALIGNMENT optical_alignment;
54 DWRITE_LINE_SPACING spacing;
55 DWRITE_AUTOMATIC_FONT_AXES automatic_axes;
57 FLOAT fontsize;
58 FLOAT tabstop;
60 DWRITE_TRIMMING trimming;
61 IDWriteInlineObject *trimmingsign;
63 IDWriteFontCollection *collection;
64 IDWriteFontFallback *fallback;
67 enum layout_range_attr_kind {
68 LAYOUT_RANGE_ATTR_WEIGHT,
69 LAYOUT_RANGE_ATTR_STYLE,
70 LAYOUT_RANGE_ATTR_STRETCH,
71 LAYOUT_RANGE_ATTR_FONTSIZE,
72 LAYOUT_RANGE_ATTR_EFFECT,
73 LAYOUT_RANGE_ATTR_INLINE,
74 LAYOUT_RANGE_ATTR_UNDERLINE,
75 LAYOUT_RANGE_ATTR_STRIKETHROUGH,
76 LAYOUT_RANGE_ATTR_PAIR_KERNING,
77 LAYOUT_RANGE_ATTR_FONTCOLL,
78 LAYOUT_RANGE_ATTR_LOCALE,
79 LAYOUT_RANGE_ATTR_FONTFAMILY,
80 LAYOUT_RANGE_ATTR_SPACING,
81 LAYOUT_RANGE_ATTR_TYPOGRAPHY
84 struct layout_range_attr_value {
85 DWRITE_TEXT_RANGE range;
86 union {
87 DWRITE_FONT_WEIGHT weight;
88 DWRITE_FONT_STYLE style;
89 DWRITE_FONT_STRETCH stretch;
90 FLOAT fontsize;
91 IDWriteInlineObject *object;
92 IUnknown *effect;
93 BOOL underline;
94 BOOL strikethrough;
95 BOOL pair_kerning;
96 IDWriteFontCollection *collection;
97 const WCHAR *locale;
98 const WCHAR *fontfamily;
99 struct {
100 FLOAT leading;
101 FLOAT trailing;
102 FLOAT min_advance;
103 } spacing;
104 IDWriteTypography *typography;
105 } u;
108 enum layout_range_kind {
109 LAYOUT_RANGE_REGULAR,
110 LAYOUT_RANGE_UNDERLINE,
111 LAYOUT_RANGE_STRIKETHROUGH,
112 LAYOUT_RANGE_EFFECT,
113 LAYOUT_RANGE_SPACING,
114 LAYOUT_RANGE_TYPOGRAPHY
117 struct layout_range_header {
118 struct list entry;
119 enum layout_range_kind kind;
120 DWRITE_TEXT_RANGE range;
123 struct layout_range {
124 struct layout_range_header h;
125 DWRITE_FONT_WEIGHT weight;
126 DWRITE_FONT_STYLE style;
127 FLOAT fontsize;
128 DWRITE_FONT_STRETCH stretch;
129 IDWriteInlineObject *object;
130 BOOL pair_kerning;
131 IDWriteFontCollection *collection;
132 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
133 WCHAR *fontfamily;
136 struct layout_range_bool {
137 struct layout_range_header h;
138 BOOL value;
141 struct layout_range_iface {
142 struct layout_range_header h;
143 IUnknown *iface;
146 struct layout_range_spacing {
147 struct layout_range_header h;
148 FLOAT leading;
149 FLOAT trailing;
150 FLOAT min_advance;
153 enum layout_run_kind {
154 LAYOUT_RUN_REGULAR,
155 LAYOUT_RUN_INLINE
158 struct inline_object_run {
159 IDWriteInlineObject *object;
160 UINT16 length;
163 struct regular_layout_run {
164 DWRITE_GLYPH_RUN_DESCRIPTION descr;
165 DWRITE_GLYPH_RUN run;
166 DWRITE_SCRIPT_ANALYSIS sa;
167 UINT16 *glyphs;
168 UINT16 *clustermap;
169 FLOAT *advances;
170 DWRITE_GLYPH_OFFSET *offsets;
171 UINT32 glyphcount; /* actual glyph count after shaping, not necessarily the same as reported to Draw() */
174 struct layout_run
176 struct list entry;
177 enum layout_run_kind kind;
178 union
180 struct inline_object_run object;
181 struct regular_layout_run regular;
182 } u;
183 float baseline;
184 float height;
185 unsigned int start_position; /* run text position in range [0, layout-text-length) */
188 struct layout_effective_run {
189 struct list entry;
190 const struct layout_run *run; /* nominal run this one is based on */
191 UINT32 start; /* relative text position, 0 means first text position of a nominal run */
192 UINT32 length; /* length in codepoints that this run covers */
193 UINT32 glyphcount; /* total glyph count in this run */
194 IUnknown *effect; /* original reference is kept only at range level */
195 D2D1_POINT_2F origin; /* baseline origin */
196 FLOAT align_dx; /* adjustment from text alignment */
197 FLOAT width; /* run width */
198 UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
199 UINT32 line; /* 0-based line index in line metrics array */
200 BOOL underlined; /* set if this run is underlined */
201 D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */
204 struct layout_effective_inline {
205 struct list entry;
206 IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */
207 IUnknown *effect; /* original reference is kept only at range level */
208 FLOAT baseline;
209 D2D1_POINT_2F origin; /* left top corner */
210 FLOAT align_dx; /* adjustment from text alignment */
211 FLOAT width; /* object width as it's reported it */
212 BOOL is_sideways; /* vertical flow direction flag passed to Draw */
213 BOOL is_rtl; /* bidi flag passed to Draw */
214 UINT32 line; /* 0-based line index in line metrics array */
217 struct layout_underline {
218 struct list entry;
219 const struct layout_effective_run *run;
220 DWRITE_UNDERLINE u;
223 struct layout_strikethrough {
224 struct list entry;
225 const struct layout_effective_run *run;
226 DWRITE_STRIKETHROUGH s;
229 struct layout_cluster {
230 const struct layout_run *run; /* link to nominal run this cluster belongs to */
231 UINT32 position; /* relative to run, first cluster has 0 position */
234 struct layout_line
236 float height; /* height based on content */
237 float baseline; /* baseline based on content */
238 DWRITE_LINE_METRICS1 metrics;
241 enum layout_recompute_mask {
242 RECOMPUTE_CLUSTERS = 1 << 0,
243 RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
244 RECOMPUTE_LINES = 1 << 2,
245 RECOMPUTE_OVERHANGS = 1 << 3,
246 RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS,
247 RECOMPUTE_EVERYTHING = 0xffff
250 struct dwrite_textlayout
252 IDWriteTextLayout4 IDWriteTextLayout4_iface;
253 IDWriteTextFormat3 IDWriteTextFormat3_iface;
254 IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface;
255 IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface;
256 LONG refcount;
258 IDWriteFactory7 *factory;
260 WCHAR *str;
261 UINT32 len;
262 struct dwrite_textformat_data format;
263 struct list strike_ranges;
264 struct list underline_ranges;
265 struct list typographies;
266 struct list effects;
267 struct list spacing;
268 struct list ranges;
269 struct list runs;
270 /* lists ready to use by Draw() */
271 struct list eruns;
272 struct list inlineobjects;
273 struct list underlines;
274 struct list strikethrough;
275 USHORT recompute;
277 DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
278 DWRITE_LINE_BREAKPOINT *actual_breakpoints;
280 struct layout_cluster *clusters;
281 DWRITE_CLUSTER_METRICS *clustermetrics;
282 UINT32 cluster_count;
283 FLOAT minwidth;
285 struct layout_line *lines;
286 size_t lines_size;
288 DWRITE_TEXT_METRICS1 metrics;
289 DWRITE_OVERHANG_METRICS overhangs;
291 DWRITE_MEASURING_MODE measuringmode;
293 /* gdi-compatible layout specifics */
294 FLOAT ppdip;
295 DWRITE_MATRIX transform;
298 struct dwrite_textformat
300 IDWriteTextFormat3 IDWriteTextFormat3_iface;
301 LONG refcount;
302 struct dwrite_textformat_data format;
305 struct dwrite_trimmingsign
307 IDWriteInlineObject IDWriteInlineObject_iface;
308 LONG refcount;
310 IDWriteTextLayout *layout;
313 struct dwrite_typography {
314 IDWriteTypography IDWriteTypography_iface;
315 LONG refcount;
317 DWRITE_FONT_FEATURE *features;
318 size_t capacity;
319 size_t count;
322 static const IDWriteTextFormat3Vtbl dwritetextformatvtbl;
324 static void release_format_data(struct dwrite_textformat_data *data)
326 if (data->collection) IDWriteFontCollection_Release(data->collection);
327 if (data->fallback) IDWriteFontFallback_Release(data->fallback);
328 if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
329 heap_free(data->family_name);
330 heap_free(data->locale);
333 static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout4(IDWriteTextLayout4 *iface)
335 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout4_iface);
338 static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat3(IDWriteTextFormat3 *iface)
340 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat3_iface);
343 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface)
345 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface);
348 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface)
350 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface);
353 static inline struct dwrite_textformat *impl_from_IDWriteTextFormat3(IDWriteTextFormat3 *iface)
355 return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat3_iface);
358 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*);
360 static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
362 return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
365 static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
367 return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
370 static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr)
372 return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength);
375 static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
377 return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
380 static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
381 BOOL *changed)
383 if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
384 return E_INVALIDARG;
385 if (changed) *changed = format->textalignment != alignment;
386 format->textalignment = alignment;
387 return S_OK;
390 static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
391 DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
393 if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
394 return E_INVALIDARG;
395 if (changed) *changed = format->paralign != alignment;
396 format->paralign = alignment;
397 return S_OK;
400 static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
401 DWRITE_READING_DIRECTION direction, BOOL *changed)
403 if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
404 return E_INVALIDARG;
405 if (changed) *changed = format->readingdir != direction;
406 format->readingdir = direction;
407 return S_OK;
410 static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
411 DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
413 if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
414 return E_INVALIDARG;
415 if (changed) *changed = format->wrapping != wrapping;
416 format->wrapping = wrapping;
417 return S_OK;
420 static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format,
421 DWRITE_FLOW_DIRECTION direction, BOOL *changed)
423 if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
424 return E_INVALIDARG;
425 if (changed) *changed = format->flow != direction;
426 format->flow = direction;
427 return S_OK;
430 static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format,
431 DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed)
433 if (changed)
434 *changed = FALSE;
436 if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD)
437 return E_INVALIDARG;
439 if (changed) {
440 *changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming));
441 if (format->trimmingsign != trimming_sign)
442 *changed = TRUE;
445 format->trimming = *trimming;
446 if (format->trimmingsign)
447 IDWriteInlineObject_Release(format->trimmingsign);
448 format->trimmingsign = trimming_sign;
449 if (format->trimmingsign)
450 IDWriteInlineObject_AddRef(format->trimmingsign);
451 return S_OK;
454 static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format,
455 DWRITE_LINE_SPACING const *spacing, BOOL *changed)
457 if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f ||
458 (UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL)
459 return E_INVALIDARG;
461 if (changed)
462 *changed = memcmp(spacing, &format->spacing, sizeof(*spacing));
464 format->spacing = *spacing;
465 return S_OK;
468 static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
470 *fallback = format->fallback;
471 if (*fallback)
472 IDWriteFontFallback_AddRef(*fallback);
473 return S_OK;
476 static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
478 if (format->fallback)
479 IDWriteFontFallback_Release(format->fallback);
480 format->fallback = fallback;
481 if (fallback)
482 IDWriteFontFallback_AddRef(fallback);
483 return S_OK;
486 static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format,
487 DWRITE_OPTICAL_ALIGNMENT alignment)
489 if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS)
490 return E_INVALIDARG;
491 format->optical_alignment = alignment;
492 return S_OK;
495 static BOOL is_run_rtl(const struct layout_effective_run *run)
497 return run->run->u.regular.run.bidiLevel & 1;
500 static HRESULT alloc_layout_run(enum layout_run_kind kind, unsigned int start_position,
501 struct layout_run **run)
503 if (!(*run = heap_alloc_zero(sizeof(**run))))
504 return E_OUTOFMEMORY;
506 (*run)->kind = kind;
507 (*run)->start_position = start_position;
509 return S_OK;
512 static void free_layout_runs(struct dwrite_textlayout *layout)
514 struct layout_run *cur, *cur2;
515 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
516 list_remove(&cur->entry);
517 if (cur->kind == LAYOUT_RUN_REGULAR) {
518 if (cur->u.regular.run.fontFace)
519 IDWriteFontFace_Release(cur->u.regular.run.fontFace);
520 heap_free(cur->u.regular.glyphs);
521 heap_free(cur->u.regular.clustermap);
522 heap_free(cur->u.regular.advances);
523 heap_free(cur->u.regular.offsets);
525 heap_free(cur);
529 static void free_layout_eruns(struct dwrite_textlayout *layout)
531 struct layout_effective_inline *in, *in2;
532 struct layout_effective_run *cur, *cur2;
533 struct layout_strikethrough *s, *s2;
534 struct layout_underline *u, *u2;
536 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
537 list_remove(&cur->entry);
538 heap_free(cur->clustermap);
539 heap_free(cur);
542 LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
543 list_remove(&in->entry);
544 heap_free(in);
547 LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) {
548 list_remove(&u->entry);
549 heap_free(u);
552 LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
553 list_remove(&s->entry);
554 heap_free(s);
558 /* Used to resolve break condition by forcing stronger condition over weaker. */
559 static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
561 switch (existingbreak) {
562 case DWRITE_BREAK_CONDITION_NEUTRAL:
563 return newbreak;
564 case DWRITE_BREAK_CONDITION_CAN_BREAK:
565 return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
566 /* let's keep stronger conditions as is */
567 case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
568 case DWRITE_BREAK_CONDITION_MUST_BREAK:
569 break;
570 default:
571 ERR("unknown break condition %d\n", existingbreak);
574 return existingbreak;
577 /* This helper should be used to get effective range length, in other words it returns number of text
578 positions from range starting point to the end of the range, limited by layout text length */
579 static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
581 if (range->h.range.startPosition + range->h.range.length <= layout->len)
582 return range->h.range.length;
583 return layout->len - range->h.range.startPosition;
586 /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
587 static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
589 DWRITE_BREAK_CONDITION before, after;
590 UINT32 i, length;
591 HRESULT hr;
593 /* ignore returned conditions if failed */
594 hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
595 if (FAILED(hr))
596 after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
598 if (!layout->actual_breakpoints) {
599 layout->actual_breakpoints = heap_calloc(layout->len, sizeof(*layout->actual_breakpoints));
600 if (!layout->actual_breakpoints)
601 return E_OUTOFMEMORY;
602 memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
605 length = get_clipped_range_length(layout, cur);
606 for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
607 /* for first codepoint check if there's anything before it and update accordingly */
608 if (i == cur->h.range.startPosition) {
609 if (i > 0)
610 layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
611 override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
612 else
613 layout->actual_breakpoints[i].breakConditionBefore = before;
614 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
616 /* similar check for last codepoint */
617 else if (i == cur->h.range.startPosition + length - 1) {
618 if (i == layout->len - 1)
619 layout->actual_breakpoints[i].breakConditionAfter = after;
620 else
621 layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
622 override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
623 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
625 /* for all positions within a range disable breaks */
626 else {
627 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
628 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
631 layout->actual_breakpoints[i].isWhitespace = 0;
632 layout->actual_breakpoints[i].isSoftHyphen = 0;
635 return S_OK;
638 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
640 static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
642 if (layout->actual_breakpoints)
643 return layout->actual_breakpoints[pos];
644 return layout->nominal_breakpoints[pos];
647 static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
648 UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
650 UINT8 breakcondition;
651 UINT32 position;
652 UINT16 j;
654 /* For clusters made of control chars we report zero glyphs, and we need zero cluster
655 width as well; advances are already computed at this point and are not necessary zero. */
656 metrics->width = 0.0f;
657 if (run->run.glyphCount) {
658 for (j = start_glyph; j < stop_glyph; j++)
659 metrics->width += run->run.glyphAdvances[j];
661 metrics->length = length;
663 position = run->descr.textPosition + stop_position;
664 if (stop_glyph == run->glyphcount)
665 breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter;
666 else {
667 breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore;
668 if (stop_position) position -= 1;
671 metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
672 breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
673 if (metrics->length == 1) {
674 DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position);
675 metrics->isWhitespace = bp.isWhitespace;
676 metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]);
677 metrics->isSoftHyphen = bp.isSoftHyphen;
679 else {
680 metrics->isWhitespace = 0;
681 metrics->isNewline = 0;
682 metrics->isSoftHyphen = 0;
684 metrics->isRightToLeft = run->run.bidiLevel & 1;
685 metrics->padding = 0;
690 All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
691 On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
692 Note that there's no need to reallocate anything at this point as we allocate one cluster per
693 codepoint initially.
696 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
698 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
699 struct layout_cluster *c = &layout->clusters[*cluster];
700 const struct regular_layout_run *run = &r->u.regular;
701 UINT32 i, start = 0;
703 assert(r->kind == LAYOUT_RUN_REGULAR);
705 for (i = 0; i < run->descr.stringLength; i++) {
706 BOOL end = i == run->descr.stringLength - 1;
708 if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
709 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
710 i - start, metrics);
711 c->position = start;
712 c->run = r;
714 *cluster += 1;
715 metrics++;
716 c++;
717 start = i;
720 if (end) {
721 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
722 i - start + 1, metrics);
723 c->position = start;
724 c->run = r;
726 *cluster += 1;
727 return;
732 #define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm)
734 static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize,
735 DWRITE_FONT_METRICS *fontmetrics)
737 if (is_layout_gdi_compatible(layout)) {
738 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics);
739 if (FAILED(hr))
740 WARN("failed to get compat metrics, 0x%08x\n", hr);
742 else
743 IDWriteFontFace_GetMetrics(fontface, fontmetrics);
746 static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height)
748 *baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics);
749 *height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
752 static HRESULT layout_itemize(struct dwrite_textlayout *layout)
754 IDWriteTextAnalyzer *analyzer;
755 struct layout_range *range;
756 struct layout_run *r;
757 HRESULT hr = S_OK;
759 analyzer = get_text_analyzer();
761 LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
762 /* We don't care about ranges that don't contain any text. */
763 if (range->h.range.startPosition >= layout->len)
764 break;
766 /* Inline objects override actual text in range. */
767 if (range->object) {
768 hr = layout_update_breakpoints_range(layout, range);
769 if (FAILED(hr))
770 return hr;
772 if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition, &r)))
773 return hr;
775 r->u.object.object = range->object;
776 r->u.object.length = get_clipped_range_length(layout, range);
777 list_add_tail(&layout->runs, &r->entry);
778 continue;
781 /* Initial splitting by script. */
782 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
783 range->h.range.startPosition, get_clipped_range_length(layout, range),
784 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
785 if (FAILED(hr))
786 break;
788 /* Splitting further by bidi levels. */
789 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
790 range->h.range.startPosition, get_clipped_range_length(layout, range),
791 (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
792 if (FAILED(hr))
793 break;
796 return hr;
799 static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout)
801 IDWriteFontCollection *sys_collection;
802 IDWriteFontFallback *fallback = NULL;
803 struct layout_range *range;
804 struct layout_run *r;
805 HRESULT hr;
807 if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection((IDWriteFactory5 *)layout->factory, FALSE,
808 (IDWriteFontCollection1 **)&sys_collection, FALSE))) {
809 WARN("Failed to get system collection, hr %#x.\n", hr);
810 return hr;
813 if (layout->format.fallback) {
814 fallback = layout->format.fallback;
815 IDWriteFontFallback_AddRef(fallback);
817 else {
818 if (FAILED(hr = IDWriteFactory7_GetSystemFontFallback(layout->factory, &fallback))) {
819 WARN("Failed to get system fallback, hr %#x.\n", hr);
820 goto fatal;
824 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
825 struct regular_layout_run *run = &r->u.regular;
826 IDWriteFont *font;
827 UINT32 length;
829 if (r->kind == LAYOUT_RUN_INLINE)
830 continue;
832 range = get_layout_range_by_pos(layout, run->descr.textPosition);
834 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
835 IDWriteFontCollection *collection;
837 collection = range->collection ? range->collection : sys_collection;
839 if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style,
840 range->stretch, &font))) {
841 WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n",
842 debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
843 break;
846 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
847 IDWriteFont_Release(font);
848 if (FAILED(hr)) {
849 WARN("Failed to create font face, hr %#x.\n", hr);
850 break;
853 run->run.fontEmSize = range->fontsize;
854 continue;
857 length = run->descr.stringLength;
859 while (length) {
860 UINT32 mapped_length;
861 FLOAT scale;
863 run = &r->u.regular;
865 hr = IDWriteFontFallback_MapCharacters(fallback,
866 (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
867 run->descr.textPosition,
868 run->descr.stringLength,
869 range->collection,
870 range->fontfamily,
871 range->weight,
872 range->style,
873 range->stretch,
874 &mapped_length,
875 &font,
876 &scale);
877 if (FAILED(hr)) {
878 WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr),
879 debugstr_w(range->fontfamily), range->collection, hr);
880 goto fatal;
883 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
884 IDWriteFont_Release(font);
885 if (FAILED(hr)) {
886 WARN("Failed to create font face, hr %#x.\n", hr);
887 goto fatal;
890 run->run.fontEmSize = range->fontsize * scale;
892 if (mapped_length < length)
894 struct regular_layout_run *nextrun;
895 struct layout_run *nextr;
897 /* keep mapped part for current run, add another run for the rest */
898 if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0, &nextr)))
899 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_calloc(run->descr.stringLength, sizeof(*run->clustermap));
937 max_count = 3 * run->descr.stringLength / 2 + 16;
938 run->glyphs = heap_calloc(max_count, sizeof(*run->glyphs));
939 if (!run->clustermap || !run->glyphs)
940 return E_OUTOFMEMORY;
942 text_props = heap_calloc(run->descr.stringLength, sizeof(*text_props));
943 glyph_props = heap_calloc(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_calloc(max_count, sizeof(*run->glyphs));
963 glyph_props = heap_calloc(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_calloc(run->glyphcount, sizeof(*run->advances));
986 run->offsets = heap_calloc(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_calloc(layout->len, sizeof(*layout->clustermetrics));
1037 layout->clusters = heap_calloc(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_calloc(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_calloc(length, sizeof(*run->clustermap));
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->lines[line].metrics.height = layout->lines[line].height;
1367 layout->lines[line].metrics.baseline = layout->lines[line].baseline;
1368 break;
1369 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
1370 layout->lines[line].metrics.height = layout->format.spacing.height;
1371 layout->lines[line].metrics.baseline = layout->format.spacing.baseline;
1372 break;
1373 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
1374 layout->lines[line].metrics.height = layout->lines[line].height * layout->format.spacing.height;
1375 layout->lines[line].metrics.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 size_t i = layout->metrics.lineCount;
1386 if (!dwrite_array_reserve((void **)&layout->lines, &layout->lines_size, layout->metrics.lineCount + 1,
1387 sizeof(*layout->lines)))
1389 return E_OUTOFMEMORY;
1392 layout->lines[i].metrics = *metrics;
1393 layout->lines[i].height = metrics->height;
1394 layout->lines[i].baseline = metrics->baseline;
1396 if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT)
1397 layout_apply_line_spacing(layout, i);
1399 layout->metrics.lineCount++;
1400 return S_OK;
1403 static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
1404 const struct layout_effective_run *cur)
1406 struct list *e;
1408 if (!cur)
1409 e = list_head(&layout->eruns);
1410 else
1411 e = list_next(&layout->eruns, &cur->entry);
1412 if (!e)
1413 return NULL;
1414 return LIST_ENTRY(e, struct layout_effective_run, entry);
1417 static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout,
1418 const struct layout_effective_run *cur)
1420 struct list *e;
1422 if (!cur)
1423 e = list_tail(&layout->eruns);
1424 else
1425 e = list_prev(&layout->eruns, &cur->entry);
1426 if (!e)
1427 return NULL;
1428 return LIST_ENTRY(e, struct layout_effective_run, entry);
1431 static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
1432 const struct layout_effective_inline *cur)
1434 struct list *e;
1436 if (!cur)
1437 e = list_head(&layout->inlineobjects);
1438 else
1439 e = list_next(&layout->inlineobjects, &cur->entry);
1440 if (!e)
1441 return NULL;
1442 return LIST_ENTRY(e, struct layout_effective_inline, entry);
1445 static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
1446 struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
1448 FLOAT width = 0.0f;
1450 while (erun && erun->line == line) {
1451 width += erun->width;
1452 erun = layout_get_next_erun(layout, erun);
1453 if (!erun)
1454 break;
1457 while (inrun && inrun->line == line) {
1458 width += inrun->width;
1459 inrun = layout_get_next_inline_run(layout, inrun);
1460 if (!inrun)
1461 break;
1464 return width;
1467 static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
1469 *det = m->m11 * m->m22 - m->m12 * m->m21;
1470 /* on certain conditions we can skip transform */
1471 return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
1474 static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, FLOAT ppdip,
1475 const DWRITE_MATRIX *m, FLOAT det)
1477 if (!skiptransform) {
1478 D2D1_POINT_2F vec2;
1480 /* apply transform */
1481 vec->x *= ppdip;
1482 vec->y *= ppdip;
1484 vec2.x = m->m11 * vec->x + m->m21 * vec->y + m->dx;
1485 vec2.y = m->m12 * vec->x + m->m22 * vec->y + m->dy;
1487 /* snap */
1488 vec2.x = floorf(vec2.x + 0.5f);
1489 vec2.y = floorf(vec2.y + 0.5f);
1491 /* apply inverted transform, we don't care about X component at this point */
1492 vec->x = (m->m22 * vec2.x - m->m21 * vec2.y + m->m21 * m->dy - m->m22 * m->dx) / det;
1493 vec->x /= ppdip;
1495 vec->y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
1496 vec->y /= ppdip;
1498 else {
1499 vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
1500 vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
1504 static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
1506 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1507 struct layout_effective_inline *inrun;
1508 struct layout_effective_run *erun;
1510 erun = layout_get_next_erun(layout, NULL);
1511 inrun = layout_get_next_inline_run(layout, NULL);
1513 while (erun) {
1514 erun->align_dx = 0.0f;
1515 erun = layout_get_next_erun(layout, erun);
1518 while (inrun) {
1519 inrun->align_dx = 0.0f;
1520 inrun = layout_get_next_inline_run(layout, inrun);
1523 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
1526 static void layout_apply_trailing_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;
1531 UINT32 line;
1533 erun = layout_get_next_erun(layout, NULL);
1534 inrun = layout_get_next_inline_run(layout, NULL);
1536 for (line = 0; line < layout->metrics.lineCount; line++) {
1537 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1538 FLOAT shift = layout->metrics.layoutWidth - width;
1540 if (is_rtl)
1541 shift *= -1.0f;
1543 while (erun && erun->line == line) {
1544 erun->align_dx = shift;
1545 erun = layout_get_next_erun(layout, erun);
1548 while (inrun && inrun->line == line) {
1549 inrun->align_dx = shift;
1550 inrun = layout_get_next_inline_run(layout, inrun);
1554 layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width;
1557 static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
1558 FLOAT width, FLOAT det)
1560 if (is_layout_gdi_compatible(layout)) {
1561 D2D1_POINT_2F vec = { layout->metrics.layoutWidth - width, 0.0f};
1562 layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
1563 return floorf(vec.x / 2.0f);
1565 else
1566 return (layout->metrics.layoutWidth - width) / 2.0f;
1569 static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
1571 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1572 struct layout_effective_inline *inrun;
1573 struct layout_effective_run *erun;
1574 BOOL skiptransform;
1575 UINT32 line;
1576 FLOAT det;
1578 erun = layout_get_next_erun(layout, NULL);
1579 inrun = layout_get_next_inline_run(layout, NULL);
1581 skiptransform = should_skip_transform(&layout->transform, &det);
1583 for (line = 0; line < layout->metrics.lineCount; line++) {
1584 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1585 FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
1587 if (is_rtl)
1588 shift *= -1.0f;
1590 while (erun && erun->line == line) {
1591 erun->align_dx = shift;
1592 erun = layout_get_next_erun(layout, erun);
1595 while (inrun && inrun->line == line) {
1596 inrun->align_dx = shift;
1597 inrun = layout_get_next_inline_run(layout, inrun);
1601 layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f;
1604 static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
1606 switch (layout->format.textalignment)
1608 case DWRITE_TEXT_ALIGNMENT_LEADING:
1609 layout_apply_leading_alignment(layout);
1610 break;
1611 case DWRITE_TEXT_ALIGNMENT_TRAILING:
1612 layout_apply_trailing_alignment(layout);
1613 break;
1614 case DWRITE_TEXT_ALIGNMENT_CENTER:
1615 layout_apply_centered_alignment(layout);
1616 break;
1617 case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
1618 FIXME("alignment %d not implemented\n", layout->format.textalignment);
1619 break;
1620 default:
1625 static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
1627 struct layout_effective_inline *inrun;
1628 struct layout_effective_run *erun;
1629 FLOAT origin_y = 0.0f;
1630 UINT32 line;
1632 /* alignment mode defines origin, after that all run origins are updated
1633 the same way */
1635 switch (layout->format.paralign)
1637 case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
1638 origin_y = 0.0f;
1639 break;
1640 case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
1641 origin_y = layout->metrics.layoutHeight - layout->metrics.height;
1642 break;
1643 case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
1644 origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f;
1645 break;
1646 default:
1650 layout->metrics.top = origin_y;
1652 erun = layout_get_next_erun(layout, NULL);
1653 inrun = layout_get_next_inline_run(layout, NULL);
1654 for (line = 0; line < layout->metrics.lineCount; line++)
1656 float pos_y = origin_y + layout->lines[line].metrics.baseline;
1658 while (erun && erun->line == line) {
1659 erun->origin.y = pos_y;
1660 erun = layout_get_next_erun(layout, erun);
1663 while (inrun && inrun->line == line) {
1664 inrun->origin.y = pos_y - inrun->baseline;
1665 inrun = layout_get_next_inline_run(layout, inrun);
1668 origin_y += layout->lines[line].metrics.height;
1672 struct layout_underline_splitting_params {
1673 const WCHAR *locale; /* points to range data, no additional allocation */
1674 IUnknown *effect; /* does not hold another reference */
1677 static void init_u_splitting_params_from_erun(struct layout_effective_run *erun,
1678 struct layout_underline_splitting_params *params)
1680 params->locale = erun->run->u.regular.descr.localeName;
1681 params->effect = erun->effect;
1684 static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left,
1685 struct layout_underline_splitting_params *right)
1687 return left->effect == right->effect && !strcmpiW(left->locale, right->locale);
1690 static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first,
1691 struct layout_effective_run *last)
1693 FLOAT thickness, offset, runheight;
1694 struct layout_effective_run *cur;
1695 DWRITE_FONT_METRICS metrics;
1697 if (first == layout_get_prev_erun(layout, last)) {
1698 layout_get_erun_font_metrics(layout, first, &metrics);
1699 thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics);
1700 offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics);
1701 runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics);
1703 else {
1704 FLOAT width = 0.0f;
1706 /* Single underline is added for consecutive underlined runs. In this case underline parameters are
1707 calculated as weighted average, where run width acts as a weight. */
1708 thickness = offset = runheight = 0.0f;
1709 cur = first;
1710 do {
1711 layout_get_erun_font_metrics(layout, cur, &metrics);
1713 thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1714 offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1715 runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight);
1716 width += cur->width;
1718 cur = layout_get_next_erun(layout, cur);
1719 } while (cur != last);
1721 thickness /= width;
1722 offset /= width;
1725 cur = first;
1726 do {
1727 struct layout_underline_splitting_params params, prev_params;
1728 struct layout_effective_run *next, *w;
1729 struct layout_underline *u;
1731 init_u_splitting_params_from_erun(cur, &prev_params);
1732 while ((next = layout_get_next_erun(layout, cur)) != last) {
1733 init_u_splitting_params_from_erun(next, &params);
1734 if (!is_same_u_splitting(&prev_params, &params))
1735 break;
1736 cur = next;
1739 u = heap_alloc(sizeof(*u));
1740 if (!u)
1741 return E_OUTOFMEMORY;
1743 w = cur;
1744 u->u.width = 0.0f;
1745 while (w != next) {
1746 u->u.width += w->width;
1747 w = layout_get_next_erun(layout, w);
1750 u->u.thickness = thickness;
1751 /* Font metrics convention is to have it negative when below baseline, for rendering
1752 however Y grows from baseline down for horizontal baseline. */
1753 u->u.offset = -offset;
1754 u->u.runHeight = runheight;
1755 u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
1756 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
1757 u->u.flowDirection = layout->format.flow;
1758 u->u.localeName = cur->run->u.regular.descr.localeName;
1759 u->u.measuringMode = layout->measuringmode;
1760 u->run = cur;
1761 list_add_tail(&layout->underlines, &u->entry);
1763 cur = next;
1764 } while (cur != last);
1766 return S_OK;
1769 /* Adds zero width line, metrics are derived from font at specified text position. */
1770 static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
1772 DWRITE_LINE_METRICS1 metrics = { 0 };
1773 DWRITE_FONT_METRICS fontmetrics;
1774 struct layout_range *range;
1775 IDWriteFontFace *fontface;
1776 IDWriteFont *font;
1777 HRESULT hr;
1779 range = get_layout_range_by_pos(layout, pos);
1780 hr = create_matching_font(range->collection,
1781 range->fontfamily,
1782 range->weight,
1783 range->style,
1784 range->stretch,
1785 &font);
1786 if (FAILED(hr))
1787 return hr;
1788 hr = IDWriteFont_CreateFontFace(font, &fontface);
1789 IDWriteFont_Release(font);
1790 if (FAILED(hr))
1791 return hr;
1793 layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics);
1794 layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
1795 IDWriteFontFace_Release(fontface);
1797 return layout_set_line_metrics(layout, &metrics);
1800 static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
1801 UINT32 *textpos)
1803 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1804 struct layout_final_splitting_params params, prev_params;
1805 DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 };
1806 UINT32 line = layout->metrics.lineCount, i;
1807 DWRITE_LINE_METRICS1 metrics = { 0 };
1808 UINT32 index, start, pos = *textpos;
1809 FLOAT descent, trailingspacewidth;
1810 BOOL append_trimming_run = FALSE;
1811 const struct layout_run *run;
1812 float width = 0.0f, origin_x;
1813 HRESULT hr;
1815 /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
1816 for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
1817 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1818 struct layout_cluster *lc = &layout->clusters[index];
1819 WCHAR ch;
1821 /* This also filters out clusters added from inline objects, those are never
1822 treated as a white space. */
1823 if (!cluster->isWhitespace)
1824 break;
1826 /* Every isNewline cluster is also isWhitespace, but not every
1827 newline character cluster has isNewline set, so go back to original string. */
1828 ch = lc->run->u.regular.descr.string[lc->position];
1829 if (cluster->length == 1 && lb_is_newline_char(ch))
1830 metrics.newlineLength += cluster->length;
1832 metrics.trailingWhitespaceLength += cluster->length;
1833 trailingspacewidth += cluster->width;
1835 if (index == 0)
1836 break;
1839 /* Line metrics length includes trailing whitespace length too */
1840 for (i = first_cluster; i <= last_cluster; i++)
1841 metrics.length += layout->clustermetrics[i].length;
1843 /* Ignore trailing whitespaces */
1844 while (last_cluster > first_cluster) {
1845 if (!layout->clustermetrics[last_cluster].isWhitespace)
1846 break;
1848 last_cluster--;
1851 /* Does not include trailing space width */
1852 if (!layout->clustermetrics[last_cluster].isWhitespace)
1853 width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
1855 /* Append trimming run if necessary */
1856 if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL &&
1857 layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) {
1858 FLOAT trimmed_width = width;
1860 hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics);
1861 if (SUCCEEDED(hr)) {
1862 while (last_cluster > first_cluster) {
1863 if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth)
1864 break;
1865 if (layout->format.trimming.granularity == DWRITE_TRIMMING_GRANULARITY_CHARACTER)
1866 trimmed_width -= layout->clustermetrics[last_cluster--].width;
1867 else {
1868 while (last_cluster > first_cluster) {
1869 trimmed_width -= layout->clustermetrics[last_cluster].width;
1870 if (layout->clustermetrics[last_cluster--].canWrapLineAfter)
1871 break;
1875 append_trimming_run = TRUE;
1877 else
1878 WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr);
1880 width = trimmed_width + sign_metrics.width;
1883 layout_splitting_params_from_pos(layout, pos, &params);
1884 prev_params = params;
1885 run = layout->clusters[first_cluster].run;
1887 /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
1888 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
1889 for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
1890 layout_splitting_params_from_pos(layout, pos, &params);
1892 if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, &params)) {
1893 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1894 if (FAILED(hr))
1895 return;
1897 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
1898 get_cluster_range_width(layout, start, i);
1899 run = layout->clusters[i].run;
1900 start = i;
1903 prev_params = params;
1904 pos += layout->clustermetrics[i].length;
1907 /* Final run from what's left from cluster range */
1908 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1909 if (FAILED(hr))
1910 return;
1912 if (get_cluster_range_width(layout, start, i) + sign_metrics.width > layout->metrics.layoutWidth)
1913 append_trimming_run = FALSE;
1915 if (append_trimming_run) {
1916 struct layout_effective_inline *trimming_sign;
1918 trimming_sign = heap_alloc(sizeof(*trimming_sign));
1919 if (!trimming_sign)
1920 return;
1922 trimming_sign->object = layout->format.trimmingsign;
1923 trimming_sign->width = sign_metrics.width;
1924 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i);
1925 trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x;
1926 trimming_sign->origin.y = 0.0f; /* set after line is built */
1927 trimming_sign->align_dx = 0.0f;
1928 trimming_sign->baseline = sign_metrics.baseline;
1930 trimming_sign->is_sideways = FALSE;
1931 trimming_sign->is_rtl = FALSE;
1932 trimming_sign->line = line;
1934 trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position +
1935 layout->clusters[i].run->start_position);
1937 list_add_tail(&layout->inlineobjects, &trimming_sign->entry);
1940 /* Look for max baseline and descent for this line */
1941 for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) {
1942 const struct layout_run *cur = layout->clusters[index].run;
1943 FLOAT cur_descent = cur->height - cur->baseline;
1945 if (cur->baseline > metrics.baseline)
1946 metrics.baseline = cur->baseline;
1947 if (cur_descent > descent)
1948 descent = cur_descent;
1951 layout->metrics.width = max(width, layout->metrics.width);
1952 layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
1953 layout->metrics.widthIncludingTrailingWhitespace);
1955 metrics.height = descent + metrics.baseline;
1956 metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth;
1957 layout_set_line_metrics(layout, &metrics);
1959 *textpos += metrics.length;
1962 static void layout_set_line_positions(struct dwrite_textlayout *layout)
1964 struct layout_effective_inline *inrun;
1965 struct layout_effective_run *erun;
1966 FLOAT origin_y;
1967 UINT32 line;
1969 /* Now all line info is here, update effective runs positions in flow direction */
1970 erun = layout_get_next_erun(layout, NULL);
1971 inrun = layout_get_next_inline_run(layout, NULL);
1973 for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++)
1975 float pos_y = origin_y + layout->lines[line].metrics.baseline;
1977 /* For all runs on this line */
1978 while (erun && erun->line == line) {
1979 erun->origin.y = pos_y;
1980 erun = layout_get_next_erun(layout, erun);
1983 /* Same for inline runs */
1984 while (inrun && inrun->line == line) {
1985 inrun->origin.y = pos_y - inrun->baseline;
1986 inrun = layout_get_next_inline_run(layout, inrun);
1989 origin_y += layout->lines[line].metrics.height;
1992 layout->metrics.height = origin_y;
1994 /* Initial paragraph alignment is always near */
1995 if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
1996 layout_apply_par_alignment(layout);
1999 static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster)
2001 if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER)
2002 return TRUE;
2004 return layout->clustermetrics[cluster].canWrapLineAfter;
2007 static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
2009 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
2010 struct layout_effective_run *erun, *first_underlined;
2011 UINT32 i, start, textpos, last_breaking_point;
2012 DWRITE_LINE_METRICS1 metrics;
2013 FLOAT width;
2014 UINT32 line;
2015 HRESULT hr;
2017 if (!(layout->recompute & RECOMPUTE_LINES))
2018 return S_OK;
2020 free_layout_eruns(layout);
2022 hr = layout_compute(layout);
2023 if (FAILED(hr))
2024 return hr;
2026 layout->metrics.lineCount = 0;
2027 memset(&metrics, 0, sizeof(metrics));
2029 layout->metrics.height = 0.0f;
2030 layout->metrics.width = 0.0f;
2031 layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
2033 last_breaking_point = ~0u;
2035 for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
2036 BOOL overflow = FALSE;
2038 while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
2039 /* Check for overflow */
2040 overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
2041 (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
2042 if (overflow)
2043 break;
2045 if (layout_can_wrap_after(layout, i))
2046 last_breaking_point = i;
2047 width += layout->clustermetrics[i].width;
2048 i++;
2050 i = min(i, layout->cluster_count - 1);
2052 /* Ignore if overflown on whitespace */
2053 if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) {
2054 /* Use most recently found breaking point */
2055 if (last_breaking_point != ~0u) {
2056 i = last_breaking_point;
2057 last_breaking_point = ~0u;
2059 else {
2060 /* Otherwise proceed forward to next newline or breaking point */
2061 for (; i < layout->cluster_count; i++)
2062 if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline)
2063 break;
2066 i = min(i, layout->cluster_count - 1);
2068 layout_add_line(layout, start, i, &textpos);
2069 start = i + 1;
2070 width = 0.0f;
2073 /* Add dummy line if:
2074 - there's no text, metrics come from first range in this case;
2075 - last ended with a mandatory break, metrics come from last text position.
2077 if (layout->len == 0)
2078 hr = layout_set_dummy_line_metrics(layout, 0);
2079 else if (layout->cluster_count && layout->clustermetrics[layout->cluster_count - 1].isNewline)
2080 hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
2081 if (FAILED(hr))
2082 return hr;
2084 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
2085 layout->metrics.top = 0.0f;
2086 layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
2088 /* Add explicit underlined runs */
2089 erun = layout_get_next_erun(layout, NULL);
2090 first_underlined = erun && erun->underlined ? erun : NULL;
2091 for (line = 0; line < layout->metrics.lineCount; line++) {
2092 while (erun && erun->line == line) {
2093 erun = layout_get_next_erun(layout, erun);
2095 if (first_underlined && (!erun || !erun->underlined)) {
2096 layout_add_underline(layout, first_underlined, erun);
2097 first_underlined = NULL;
2099 else if (!first_underlined && erun && erun->underlined)
2100 first_underlined = erun;
2104 /* Position runs in flow direction */
2105 layout_set_line_positions(layout);
2107 /* Initial alignment is always leading */
2108 if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
2109 layout_apply_text_alignment(layout);
2111 layout->recompute &= ~RECOMPUTE_LINES;
2112 return hr;
2115 static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
2116 struct layout_range_attr_value *value)
2118 struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
2119 struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
2120 struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
2121 struct layout_range const *range = (struct layout_range*)h;
2123 switch (attr) {
2124 case LAYOUT_RANGE_ATTR_WEIGHT:
2125 return range->weight == value->u.weight;
2126 case LAYOUT_RANGE_ATTR_STYLE:
2127 return range->style == value->u.style;
2128 case LAYOUT_RANGE_ATTR_STRETCH:
2129 return range->stretch == value->u.stretch;
2130 case LAYOUT_RANGE_ATTR_FONTSIZE:
2131 return range->fontsize == value->u.fontsize;
2132 case LAYOUT_RANGE_ATTR_INLINE:
2133 return range->object == value->u.object;
2134 case LAYOUT_RANGE_ATTR_EFFECT:
2135 return range_iface->iface == value->u.effect;
2136 case LAYOUT_RANGE_ATTR_UNDERLINE:
2137 return range_bool->value == value->u.underline;
2138 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2139 return range_bool->value == value->u.strikethrough;
2140 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2141 return range->pair_kerning == value->u.pair_kerning;
2142 case LAYOUT_RANGE_ATTR_FONTCOLL:
2143 return range->collection == value->u.collection;
2144 case LAYOUT_RANGE_ATTR_LOCALE:
2145 return strcmpiW(range->locale, value->u.locale) == 0;
2146 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2147 return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
2148 case LAYOUT_RANGE_ATTR_SPACING:
2149 return range_spacing->leading == value->u.spacing.leading &&
2150 range_spacing->trailing == value->u.spacing.trailing &&
2151 range_spacing->min_advance == value->u.spacing.min_advance;
2152 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2153 return range_iface->iface == (IUnknown*)value->u.typography;
2154 default:
2158 return FALSE;
2161 static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
2163 switch (hleft->kind)
2165 case LAYOUT_RANGE_REGULAR:
2167 struct layout_range const *left = (struct layout_range const*)hleft;
2168 struct layout_range const *right = (struct layout_range const*)hright;
2169 return left->weight == right->weight &&
2170 left->style == right->style &&
2171 left->stretch == right->stretch &&
2172 left->fontsize == right->fontsize &&
2173 left->object == right->object &&
2174 left->pair_kerning == right->pair_kerning &&
2175 left->collection == right->collection &&
2176 !strcmpiW(left->locale, right->locale) &&
2177 !strcmpW(left->fontfamily, right->fontfamily);
2179 case LAYOUT_RANGE_UNDERLINE:
2180 case LAYOUT_RANGE_STRIKETHROUGH:
2182 struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
2183 struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
2184 return left->value == right->value;
2186 case LAYOUT_RANGE_EFFECT:
2187 case LAYOUT_RANGE_TYPOGRAPHY:
2189 struct layout_range_iface const *left = (struct layout_range_iface const*)hleft;
2190 struct layout_range_iface const *right = (struct layout_range_iface const*)hright;
2191 return left->iface == right->iface;
2193 case LAYOUT_RANGE_SPACING:
2195 struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
2196 struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
2197 return left->leading == right->leading &&
2198 left->trailing == right->trailing &&
2199 left->min_advance == right->min_advance;
2201 default:
2202 FIXME("unknown range kind %d\n", hleft->kind);
2203 return FALSE;
2207 static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
2209 return left->startPosition == right->startPosition && left->length == right->length;
2212 /* Allocates range and inits it with default values from text format. */
2213 static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
2214 enum layout_range_kind kind)
2216 struct layout_range_header *h;
2218 switch (kind)
2220 case LAYOUT_RANGE_REGULAR:
2222 struct layout_range *range;
2224 range = heap_alloc_zero(sizeof(*range));
2225 if (!range) return NULL;
2227 range->weight = layout->format.weight;
2228 range->style = layout->format.style;
2229 range->stretch = layout->format.stretch;
2230 range->fontsize = layout->format.fontsize;
2232 range->fontfamily = heap_strdupW(layout->format.family_name);
2233 if (!range->fontfamily)
2235 heap_free(range);
2236 return NULL;
2239 range->collection = layout->format.collection;
2240 if (range->collection)
2241 IDWriteFontCollection_AddRef(range->collection);
2242 strcpyW(range->locale, layout->format.locale);
2244 h = &range->h;
2245 break;
2247 case LAYOUT_RANGE_UNDERLINE:
2248 case LAYOUT_RANGE_STRIKETHROUGH:
2250 struct layout_range_bool *range;
2252 range = heap_alloc_zero(sizeof(*range));
2253 if (!range) return NULL;
2255 h = &range->h;
2256 break;
2258 case LAYOUT_RANGE_EFFECT:
2259 case LAYOUT_RANGE_TYPOGRAPHY:
2261 struct layout_range_iface *range;
2263 range = heap_alloc_zero(sizeof(*range));
2264 if (!range) return NULL;
2266 h = &range->h;
2267 break;
2269 case LAYOUT_RANGE_SPACING:
2271 struct layout_range_spacing *range;
2273 range = heap_alloc_zero(sizeof(*range));
2274 if (!range) return NULL;
2276 h = &range->h;
2277 break;
2279 default:
2280 FIXME("unknown range kind %d\n", kind);
2281 return NULL;
2284 h->kind = kind;
2285 h->range = *r;
2286 return h;
2289 static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
2291 struct layout_range_header *ret;
2293 switch (h->kind)
2295 case LAYOUT_RANGE_REGULAR:
2297 struct layout_range *from = (struct layout_range*)h;
2299 struct layout_range *range = heap_alloc(sizeof(*range));
2300 if (!range) return NULL;
2302 *range = *from;
2303 range->fontfamily = heap_strdupW(from->fontfamily);
2304 if (!range->fontfamily) {
2305 heap_free(range);
2306 return NULL;
2309 /* update refcounts */
2310 if (range->object)
2311 IDWriteInlineObject_AddRef(range->object);
2312 if (range->collection)
2313 IDWriteFontCollection_AddRef(range->collection);
2314 ret = &range->h;
2315 break;
2317 case LAYOUT_RANGE_UNDERLINE:
2318 case LAYOUT_RANGE_STRIKETHROUGH:
2320 struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
2321 if (!strike) return NULL;
2323 *strike = *(struct layout_range_bool*)h;
2324 ret = &strike->h;
2325 break;
2327 case LAYOUT_RANGE_EFFECT:
2328 case LAYOUT_RANGE_TYPOGRAPHY:
2330 struct layout_range_iface *effect = heap_alloc(sizeof(*effect));
2331 if (!effect) return NULL;
2333 *effect = *(struct layout_range_iface*)h;
2334 if (effect->iface)
2335 IUnknown_AddRef(effect->iface);
2336 ret = &effect->h;
2337 break;
2339 case LAYOUT_RANGE_SPACING:
2341 struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
2342 if (!spacing) return NULL;
2344 *spacing = *(struct layout_range_spacing*)h;
2345 ret = &spacing->h;
2346 break;
2348 default:
2349 FIXME("unknown range kind %d\n", h->kind);
2350 return NULL;
2353 ret->range = *r;
2354 return ret;
2357 static void free_layout_range(struct layout_range_header *h)
2359 if (!h)
2360 return;
2362 switch (h->kind)
2364 case LAYOUT_RANGE_REGULAR:
2366 struct layout_range *range = (struct layout_range*)h;
2368 if (range->object)
2369 IDWriteInlineObject_Release(range->object);
2370 if (range->collection)
2371 IDWriteFontCollection_Release(range->collection);
2372 heap_free(range->fontfamily);
2373 break;
2375 case LAYOUT_RANGE_EFFECT:
2376 case LAYOUT_RANGE_TYPOGRAPHY:
2378 struct layout_range_iface *range = (struct layout_range_iface*)h;
2379 if (range->iface)
2380 IUnknown_Release(range->iface);
2381 break;
2383 default:
2387 heap_free(h);
2390 static void free_layout_ranges_list(struct dwrite_textlayout *layout)
2392 struct layout_range_header *cur, *cur2;
2394 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
2395 list_remove(&cur->entry);
2396 free_layout_range(cur);
2399 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) {
2400 list_remove(&cur->entry);
2401 free_layout_range(cur);
2404 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
2405 list_remove(&cur->entry);
2406 free_layout_range(cur);
2409 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
2410 list_remove(&cur->entry);
2411 free_layout_range(cur);
2414 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
2415 list_remove(&cur->entry);
2416 free_layout_range(cur);
2419 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) {
2420 list_remove(&cur->entry);
2421 free_layout_range(cur);
2425 static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
2427 struct layout_range_header *cur;
2429 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
2431 if (cur->range.startPosition > range->startPosition)
2432 return NULL;
2434 if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
2435 (range->startPosition < cur->range.startPosition + cur->range.length))
2436 return NULL;
2437 if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
2438 return cur;
2441 return NULL;
2444 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
2446 struct layout_range *cur;
2448 LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
2449 DWRITE_TEXT_RANGE *r = &cur->h.range;
2450 if (r->startPosition <= pos && pos < r->startPosition + r->length)
2451 return cur;
2454 return NULL;
2457 static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
2459 if (*dest == value) return FALSE;
2461 if (*dest)
2462 IUnknown_Release(*dest);
2463 *dest = value;
2464 if (*dest)
2465 IUnknown_AddRef(*dest);
2467 return TRUE;
2470 static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2472 struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
2473 struct layout_range_iface *dest_iface = (struct layout_range_iface*)h;
2474 struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
2475 struct layout_range *dest = (struct layout_range*)h;
2477 BOOL changed = FALSE;
2479 switch (attr) {
2480 case LAYOUT_RANGE_ATTR_WEIGHT:
2481 changed = dest->weight != value->u.weight;
2482 dest->weight = value->u.weight;
2483 break;
2484 case LAYOUT_RANGE_ATTR_STYLE:
2485 changed = dest->style != value->u.style;
2486 dest->style = value->u.style;
2487 break;
2488 case LAYOUT_RANGE_ATTR_STRETCH:
2489 changed = dest->stretch != value->u.stretch;
2490 dest->stretch = value->u.stretch;
2491 break;
2492 case LAYOUT_RANGE_ATTR_FONTSIZE:
2493 changed = dest->fontsize != value->u.fontsize;
2494 dest->fontsize = value->u.fontsize;
2495 break;
2496 case LAYOUT_RANGE_ATTR_INLINE:
2497 changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
2498 break;
2499 case LAYOUT_RANGE_ATTR_EFFECT:
2500 changed = set_layout_range_iface_attr(&dest_iface->iface, value->u.effect);
2501 break;
2502 case LAYOUT_RANGE_ATTR_UNDERLINE:
2503 changed = dest_bool->value != value->u.underline;
2504 dest_bool->value = value->u.underline;
2505 break;
2506 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2507 changed = dest_bool->value != value->u.strikethrough;
2508 dest_bool->value = value->u.strikethrough;
2509 break;
2510 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2511 changed = dest->pair_kerning != value->u.pair_kerning;
2512 dest->pair_kerning = value->u.pair_kerning;
2513 break;
2514 case LAYOUT_RANGE_ATTR_FONTCOLL:
2515 changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
2516 break;
2517 case LAYOUT_RANGE_ATTR_LOCALE:
2518 changed = strcmpiW(dest->locale, value->u.locale) != 0;
2519 if (changed) {
2520 strcpyW(dest->locale, value->u.locale);
2521 strlwrW(dest->locale);
2523 break;
2524 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2525 changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
2526 if (changed) {
2527 heap_free(dest->fontfamily);
2528 dest->fontfamily = heap_strdupW(value->u.fontfamily);
2530 break;
2531 case LAYOUT_RANGE_ATTR_SPACING:
2532 changed = dest_spacing->leading != value->u.spacing.leading ||
2533 dest_spacing->trailing != value->u.spacing.trailing ||
2534 dest_spacing->min_advance != value->u.spacing.min_advance;
2535 dest_spacing->leading = value->u.spacing.leading;
2536 dest_spacing->trailing = value->u.spacing.trailing;
2537 dest_spacing->min_advance = value->u.spacing.min_advance;
2538 break;
2539 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2540 changed = set_layout_range_iface_attr(&dest_iface->iface, (IUnknown*)value->u.typography);
2541 break;
2542 default:
2546 return changed;
2549 static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
2551 return (inner->startPosition >= outer->startPosition) &&
2552 (inner->startPosition + inner->length <= outer->startPosition + outer->length);
2555 static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
2557 if (r) *r = h->range;
2558 return S_OK;
2561 /* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */
2562 static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2564 struct layout_range_header *cur, *right, *left, *outer;
2565 BOOL changed = FALSE;
2566 struct list *ranges;
2567 DWRITE_TEXT_RANGE r;
2569 /* ignore zero length ranges */
2570 if (value->range.length == 0)
2571 return S_OK;
2573 /* select from ranges lists */
2574 switch (attr)
2576 case LAYOUT_RANGE_ATTR_WEIGHT:
2577 case LAYOUT_RANGE_ATTR_STYLE:
2578 case LAYOUT_RANGE_ATTR_STRETCH:
2579 case LAYOUT_RANGE_ATTR_FONTSIZE:
2580 case LAYOUT_RANGE_ATTR_INLINE:
2581 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2582 case LAYOUT_RANGE_ATTR_FONTCOLL:
2583 case LAYOUT_RANGE_ATTR_LOCALE:
2584 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2585 ranges = &layout->ranges;
2586 break;
2587 case LAYOUT_RANGE_ATTR_UNDERLINE:
2588 ranges = &layout->underline_ranges;
2589 break;
2590 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2591 ranges = &layout->strike_ranges;
2592 break;
2593 case LAYOUT_RANGE_ATTR_EFFECT:
2594 ranges = &layout->effects;
2595 break;
2596 case LAYOUT_RANGE_ATTR_SPACING:
2597 ranges = &layout->spacing;
2598 break;
2599 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2600 ranges = &layout->typographies;
2601 break;
2602 default:
2603 FIXME("unknown attr kind %d\n", attr);
2604 return E_FAIL;
2607 /* If new range is completely within existing range, split existing range in two */
2608 if ((outer = find_outer_range(ranges, &value->range))) {
2610 /* no need to add same range */
2611 if (is_same_layout_attrvalue(outer, attr, value))
2612 return S_OK;
2614 /* for matching range bounds just replace data */
2615 if (is_same_text_range(&outer->range, &value->range)) {
2616 changed = set_layout_range_attrval(outer, attr, value);
2617 goto done;
2620 /* add new range to the left */
2621 if (value->range.startPosition == outer->range.startPosition) {
2622 left = alloc_layout_range_from(outer, &value->range);
2623 if (!left) return E_OUTOFMEMORY;
2625 changed = set_layout_range_attrval(left, attr, value);
2626 list_add_before(&outer->entry, &left->entry);
2627 outer->range.startPosition += value->range.length;
2628 outer->range.length -= value->range.length;
2629 goto done;
2632 /* add new range to the right */
2633 if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
2634 right = alloc_layout_range_from(outer, &value->range);
2635 if (!right) return E_OUTOFMEMORY;
2637 changed = set_layout_range_attrval(right, attr, value);
2638 list_add_after(&outer->entry, &right->entry);
2639 outer->range.length -= value->range.length;
2640 goto done;
2643 r.startPosition = value->range.startPosition + value->range.length;
2644 r.length = outer->range.length + outer->range.startPosition - r.startPosition;
2646 /* right part */
2647 right = alloc_layout_range_from(outer, &r);
2648 /* new range in the middle */
2649 cur = alloc_layout_range_from(outer, &value->range);
2650 if (!right || !cur) {
2651 free_layout_range(right);
2652 free_layout_range(cur);
2653 return E_OUTOFMEMORY;
2656 /* reuse container range as a left part */
2657 outer->range.length = value->range.startPosition - outer->range.startPosition;
2659 /* new part */
2660 set_layout_range_attrval(cur, attr, value);
2662 list_add_after(&outer->entry, &cur->entry);
2663 list_add_after(&cur->entry, &right->entry);
2665 layout->recompute = RECOMPUTE_EVERYTHING;
2666 return S_OK;
2669 /* Now it's only possible that given range contains some existing ranges, fully or partially.
2670 Update all of them. */
2671 left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
2672 if (left->range.startPosition == value->range.startPosition)
2673 changed = set_layout_range_attrval(left, attr, value);
2674 else /* need to split */ {
2675 r.startPosition = value->range.startPosition;
2676 r.length = left->range.length - value->range.startPosition + left->range.startPosition;
2677 left->range.length -= r.length;
2678 cur = alloc_layout_range_from(left, &r);
2679 changed = set_layout_range_attrval(cur, attr, value);
2680 list_add_after(&left->entry, &cur->entry);
2682 cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
2684 /* for all existing ranges covered by new one update value */
2685 while (cur && is_in_layout_range(&value->range, &cur->range)) {
2686 changed |= set_layout_range_attrval(cur, attr, value);
2687 cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
2690 /* it's possible rightmost range intersects */
2691 if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
2692 r.startPosition = cur->range.startPosition;
2693 r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
2694 left = alloc_layout_range_from(cur, &r);
2695 changed |= set_layout_range_attrval(left, attr, value);
2696 cur->range.startPosition += left->range.length;
2697 cur->range.length -= left->range.length;
2698 list_add_before(&cur->entry, &left->entry);
2701 done:
2702 if (changed) {
2703 struct list *next, *i;
2705 layout->recompute = RECOMPUTE_EVERYTHING;
2706 i = list_head(ranges);
2707 while ((next = list_next(ranges, i))) {
2708 struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
2710 cur = LIST_ENTRY(i, struct layout_range_header, entry);
2711 if (is_same_layout_attributes(cur, next_range)) {
2712 /* remove similar range */
2713 cur->range.length += next_range->range.length;
2714 list_remove(next);
2715 free_layout_range(next_range);
2717 else
2718 i = list_next(ranges, i);
2722 return S_OK;
2725 static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
2727 const WCHAR *str;
2729 switch (kind) {
2730 case LAYOUT_RANGE_ATTR_LOCALE:
2731 str = range->locale;
2732 break;
2733 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2734 str = range->fontfamily;
2735 break;
2736 default:
2737 str = NULL;
2740 return str;
2743 static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2744 UINT32 *length, DWRITE_TEXT_RANGE *r)
2746 struct layout_range *range;
2747 const WCHAR *str;
2749 range = get_layout_range_by_pos(layout, position);
2750 if (!range) {
2751 *length = 0;
2752 return S_OK;
2755 str = get_string_attribute_ptr(range, kind);
2756 *length = strlenW(str);
2757 return return_range(&range->h, r);
2760 static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2761 WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
2763 struct layout_range *range;
2764 const WCHAR *str;
2766 if (length == 0)
2767 return E_INVALIDARG;
2769 ret[0] = 0;
2770 range = get_layout_range_by_pos(layout, position);
2771 if (!range)
2772 return E_INVALIDARG;
2774 str = get_string_attribute_ptr(range, kind);
2775 if (length < strlenW(str) + 1)
2776 return E_NOT_SUFFICIENT_BUFFER;
2778 strcpyW(ret, str);
2779 return return_range(&range->h, r);
2782 static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout4 *iface, REFIID riid, void **obj)
2784 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2786 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
2788 *obj = NULL;
2790 if (IsEqualIID(riid, &IID_IDWriteTextLayout4) ||
2791 IsEqualIID(riid, &IID_IDWriteTextLayout3) ||
2792 IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
2793 IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
2794 IsEqualIID(riid, &IID_IDWriteTextLayout) ||
2795 IsEqualIID(riid, &IID_IUnknown))
2797 *obj = iface;
2799 else if (IsEqualIID(riid, &IID_IDWriteTextFormat3) ||
2800 IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
2801 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
2802 IsEqualIID(riid, &IID_IDWriteTextFormat))
2804 *obj = &layout->IDWriteTextFormat3_iface;
2807 if (*obj) {
2808 IDWriteTextLayout4_AddRef(iface);
2809 return S_OK;
2812 WARN("%s not implemented.\n", debugstr_guid(riid));
2814 return E_NOINTERFACE;
2817 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout4 *iface)
2819 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2820 ULONG refcount = InterlockedIncrement(&layout->refcount);
2822 TRACE("%p, refcount %u.\n", iface, refcount);
2824 return refcount;
2827 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout4 *iface)
2829 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2830 ULONG refcount = InterlockedDecrement(&layout->refcount);
2832 TRACE("%p, refcount %u.\n", iface, refcount);
2834 if (!refcount)
2836 IDWriteFactory7_Release(layout->factory);
2837 free_layout_ranges_list(layout);
2838 free_layout_eruns(layout);
2839 free_layout_runs(layout);
2840 release_format_data(&layout->format);
2841 heap_free(layout->nominal_breakpoints);
2842 heap_free(layout->actual_breakpoints);
2843 heap_free(layout->clustermetrics);
2844 heap_free(layout->clusters);
2845 heap_free(layout->lines);
2846 heap_free(layout->str);
2847 heap_free(layout);
2850 return refcount;
2853 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout4 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2855 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2856 return IDWriteTextFormat3_SetTextAlignment(&layout->IDWriteTextFormat3_iface, alignment);
2859 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout4 *iface,
2860 DWRITE_PARAGRAPH_ALIGNMENT alignment)
2862 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2863 return IDWriteTextFormat3_SetParagraphAlignment(&layout->IDWriteTextFormat3_iface, alignment);
2866 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout4 *iface, DWRITE_WORD_WRAPPING wrapping)
2868 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2869 return IDWriteTextFormat3_SetWordWrapping(&layout->IDWriteTextFormat3_iface, wrapping);
2872 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout4 *iface,
2873 DWRITE_READING_DIRECTION direction)
2875 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2876 return IDWriteTextFormat3_SetReadingDirection(&layout->IDWriteTextFormat3_iface, direction);
2879 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout4 *iface, DWRITE_FLOW_DIRECTION direction)
2881 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2882 return IDWriteTextFormat3_SetFlowDirection(&layout->IDWriteTextFormat3_iface, direction);
2885 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout4 *iface, FLOAT tabstop)
2887 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2888 return IDWriteTextFormat3_SetIncrementalTabStop(&layout->IDWriteTextFormat3_iface, tabstop);
2891 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout4 *iface, DWRITE_TRIMMING const *trimming,
2892 IDWriteInlineObject *trimming_sign)
2894 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2895 return IDWriteTextFormat3_SetTrimming(&layout->IDWriteTextFormat3_iface, trimming, trimming_sign);
2898 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout4 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2899 FLOAT line_spacing, FLOAT baseline)
2901 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2902 return IDWriteTextFormat1_SetLineSpacing((IDWriteTextFormat1 *)&layout->IDWriteTextFormat3_iface, spacing,
2903 line_spacing, baseline);
2906 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout4 *iface)
2908 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2909 return IDWriteTextFormat3_GetTextAlignment(&layout->IDWriteTextFormat3_iface);
2912 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout4 *iface)
2914 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2915 return IDWriteTextFormat3_GetParagraphAlignment(&layout->IDWriteTextFormat3_iface);
2918 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout4 *iface)
2920 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2921 return IDWriteTextFormat3_GetWordWrapping(&layout->IDWriteTextFormat3_iface);
2924 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout4 *iface)
2926 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2927 return IDWriteTextFormat3_GetReadingDirection(&layout->IDWriteTextFormat3_iface);
2930 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout4 *iface)
2932 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2933 return IDWriteTextFormat3_GetFlowDirection(&layout->IDWriteTextFormat3_iface);
2936 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout4 *iface)
2938 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2939 return IDWriteTextFormat3_GetIncrementalTabStop(&layout->IDWriteTextFormat3_iface);
2942 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout4 *iface, DWRITE_TRIMMING *options,
2943 IDWriteInlineObject **trimming_sign)
2945 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2946 return IDWriteTextFormat3_GetTrimming(&layout->IDWriteTextFormat3_iface, options, trimming_sign);
2949 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout4 *iface, DWRITE_LINE_SPACING_METHOD *method,
2950 FLOAT *spacing, FLOAT *baseline)
2952 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2953 return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat *)&layout->IDWriteTextFormat3_iface, method,
2954 spacing, baseline);
2957 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout4 *iface, IDWriteFontCollection **collection)
2959 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2960 return IDWriteTextFormat3_GetFontCollection(&layout->IDWriteTextFormat3_iface, collection);
2963 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout4 *iface)
2965 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2966 return IDWriteTextFormat3_GetFontFamilyNameLength(&layout->IDWriteTextFormat3_iface);
2969 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout4 *iface, WCHAR *name, UINT32 size)
2971 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2972 return IDWriteTextFormat3_GetFontFamilyName(&layout->IDWriteTextFormat3_iface, name, size);
2975 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout4 *iface)
2977 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2978 return IDWriteTextFormat3_GetFontWeight(&layout->IDWriteTextFormat3_iface);
2981 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout4 *iface)
2983 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2984 return IDWriteTextFormat3_GetFontStyle(&layout->IDWriteTextFormat3_iface);
2987 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout4 *iface)
2989 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2990 return IDWriteTextFormat3_GetFontStretch(&layout->IDWriteTextFormat3_iface);
2993 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout4 *iface)
2995 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
2996 return IDWriteTextFormat3_GetFontSize(&layout->IDWriteTextFormat3_iface);
2999 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout4 *iface)
3001 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3002 return IDWriteTextFormat3_GetLocaleNameLength(&layout->IDWriteTextFormat3_iface);
3005 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout4 *iface, WCHAR *name, UINT32 size)
3007 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3008 return IDWriteTextFormat3_GetLocaleName(&layout->IDWriteTextFormat3_iface, name, size);
3011 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout4 *iface, FLOAT maxWidth)
3013 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3014 BOOL changed;
3016 TRACE("%p, %.8e.\n", iface, maxWidth);
3018 if (maxWidth < 0.0f)
3019 return E_INVALIDARG;
3021 changed = layout->metrics.layoutWidth != maxWidth;
3022 layout->metrics.layoutWidth = maxWidth;
3024 if (changed)
3025 layout->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3026 return S_OK;
3029 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout4 *iface, FLOAT maxHeight)
3031 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3032 BOOL changed;
3034 TRACE("%p, %.8e.\n", iface, maxHeight);
3036 if (maxHeight < 0.0f)
3037 return E_INVALIDARG;
3039 changed = layout->metrics.layoutHeight != maxHeight;
3040 layout->metrics.layoutHeight = maxHeight;
3042 if (changed)
3043 layout->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
3044 return S_OK;
3047 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout4 *iface, IDWriteFontCollection *collection,
3048 DWRITE_TEXT_RANGE range)
3050 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3051 struct layout_range_attr_value value;
3053 TRACE("%p, %p, %s.\n", iface, collection, debugstr_range(&range));
3055 value.range = range;
3056 value.u.collection = collection;
3057 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
3060 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout4 *iface, WCHAR const *name,
3061 DWRITE_TEXT_RANGE range)
3063 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3064 struct layout_range_attr_value value;
3066 TRACE("%p, %s, %s.\n", iface, debugstr_w(name), debugstr_range(&range));
3068 if (!name)
3069 return E_INVALIDARG;
3071 value.range = range;
3072 value.u.fontfamily = name;
3073 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
3076 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout4 *iface, DWRITE_FONT_WEIGHT weight,
3077 DWRITE_TEXT_RANGE range)
3079 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3080 struct layout_range_attr_value value;
3082 TRACE("%p, %d, %s.\n", iface, weight, debugstr_range(&range));
3084 if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
3085 return E_INVALIDARG;
3087 value.range = range;
3088 value.u.weight = weight;
3089 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_WEIGHT, &value);
3092 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout4 *iface, DWRITE_FONT_STYLE style,
3093 DWRITE_TEXT_RANGE range)
3095 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3096 struct layout_range_attr_value value;
3098 TRACE("%p, %d, %s.\n", iface, style, debugstr_range(&range));
3100 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
3101 return E_INVALIDARG;
3103 value.range = range;
3104 value.u.style = style;
3105 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_STYLE, &value);
3108 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout4 *iface, DWRITE_FONT_STRETCH stretch,
3109 DWRITE_TEXT_RANGE range)
3111 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3112 struct layout_range_attr_value value;
3114 TRACE("%p, %d, %s.\n", iface, stretch, debugstr_range(&range));
3116 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
3117 return E_INVALIDARG;
3119 value.range = range;
3120 value.u.stretch = stretch;
3121 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_STRETCH, &value);
3124 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout4 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
3126 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3127 struct layout_range_attr_value value;
3129 TRACE("%p, %.8e, %s.\n", iface, size, debugstr_range(&range));
3131 if (size <= 0.0f)
3132 return E_INVALIDARG;
3134 value.range = range;
3135 value.u.fontsize = size;
3136 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
3139 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout4 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
3141 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3142 struct layout_range_attr_value value;
3144 TRACE("%p, %d, %s.\n", iface, underline, debugstr_range(&range));
3146 value.range = range;
3147 value.u.underline = underline;
3148 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
3151 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout4 *iface, BOOL strikethrough,
3152 DWRITE_TEXT_RANGE range)
3154 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3155 struct layout_range_attr_value value;
3157 TRACE("%p, %d, %s.\n", iface, strikethrough, debugstr_range(&range));
3159 value.range = range;
3160 value.u.strikethrough = strikethrough;
3161 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
3164 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout4 *iface, IUnknown* effect,
3165 DWRITE_TEXT_RANGE range)
3167 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3168 struct layout_range_attr_value value;
3170 TRACE("%p, %p, %s.\n", iface, effect, debugstr_range(&range));
3172 value.range = range;
3173 value.u.effect = effect;
3174 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_EFFECT, &value);
3177 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout4 *iface, IDWriteInlineObject *object,
3178 DWRITE_TEXT_RANGE range)
3180 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3181 struct layout_range_attr_value value;
3183 TRACE("%p, %p, %s.\n", iface, object, debugstr_range(&range));
3185 value.range = range;
3186 value.u.object = object;
3187 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_INLINE, &value);
3190 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout4 *iface, IDWriteTypography *typography,
3191 DWRITE_TEXT_RANGE range)
3193 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3194 struct layout_range_attr_value value;
3196 TRACE("%p, %p, %s.\n", iface, typography, debugstr_range(&range));
3198 value.range = range;
3199 value.u.typography = typography;
3200 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
3203 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout4 *iface, WCHAR const* locale,
3204 DWRITE_TEXT_RANGE range)
3206 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3207 struct layout_range_attr_value value;
3209 TRACE("%p, %s, %s.\n", iface, debugstr_w(locale), debugstr_range(&range));
3211 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
3212 return E_INVALIDARG;
3214 value.range = range;
3215 value.u.locale = locale;
3216 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_LOCALE, &value);
3219 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout4 *iface)
3221 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3223 TRACE("%p.\n", iface);
3225 return layout->metrics.layoutWidth;
3228 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout4 *iface)
3230 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3232 TRACE("%p.\n", iface);
3234 return layout->metrics.layoutHeight;
3237 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout4 *iface, UINT32 position,
3238 IDWriteFontCollection **collection, DWRITE_TEXT_RANGE *r)
3240 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3241 struct layout_range *range;
3243 TRACE("%p, %u, %p, %p.\n", iface, position, collection, r);
3245 if (position >= layout->len)
3246 return S_OK;
3248 range = get_layout_range_by_pos(layout, position);
3249 *collection = range->collection;
3250 if (*collection)
3251 IDWriteFontCollection_AddRef(*collection);
3253 return return_range(&range->h, r);
3256 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout4 *iface,
3257 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3259 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3261 TRACE("%p, %d, %p, %p.\n", iface, position, length, r);
3263 return get_string_attribute_length(layout, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
3266 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout4 *iface,
3267 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
3269 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3271 TRACE("%p, %u, %p, %u, %p.\n", iface, position, name, length, r);
3273 return get_string_attribute_value(layout, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
3276 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout4 *iface,
3277 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
3279 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3280 struct layout_range *range;
3282 TRACE("%p, %u, %p, %p.\n", iface, position, weight, r);
3284 if (position >= layout->len)
3285 return S_OK;
3287 range = get_layout_range_by_pos(layout, position);
3288 *weight = range->weight;
3290 return return_range(&range->h, r);
3293 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout4 *iface,
3294 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
3296 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3297 struct layout_range *range;
3299 TRACE("%p, %u, %p, %p.\n", iface, position, style, r);
3301 range = get_layout_range_by_pos(layout, position);
3302 *style = range->style;
3303 return return_range(&range->h, r);
3306 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout4 *iface,
3307 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
3309 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3310 struct layout_range *range;
3312 TRACE("%p, %u, %p, %p.\n", iface, position, stretch, r);
3314 range = get_layout_range_by_pos(layout, position);
3315 *stretch = range->stretch;
3316 return return_range(&range->h, r);
3319 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout4 *iface,
3320 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
3322 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3323 struct layout_range *range;
3325 TRACE("%p, %u, %p, %p.\n", iface, position, size, r);
3327 range = get_layout_range_by_pos(layout, position);
3328 *size = range->fontsize;
3329 return return_range(&range->h, r);
3332 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout4 *iface,
3333 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
3335 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3336 struct layout_range_bool *range;
3338 TRACE("%p, %u, %p, %p.\n", iface, position, underline, r);
3340 range = (struct layout_range_bool *)get_layout_range_header_by_pos(&layout->underline_ranges, position);
3341 *underline = range->value;
3343 return return_range(&range->h, r);
3346 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout4 *iface,
3347 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
3349 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3350 struct layout_range_bool *range;
3352 TRACE("%p, %u, %p, %p.\n", iface, position, strikethrough, r);
3354 range = (struct layout_range_bool *)get_layout_range_header_by_pos(&layout->strike_ranges, position);
3355 *strikethrough = range->value;
3357 return return_range(&range->h, r);
3360 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout4 *iface,
3361 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
3363 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3364 struct layout_range_iface *range;
3366 TRACE("%p, %u, %p, %p.\n", iface, position, effect, r);
3368 range = (struct layout_range_iface *)get_layout_range_header_by_pos(&layout->effects, position);
3369 *effect = range->iface;
3370 if (*effect)
3371 IUnknown_AddRef(*effect);
3373 return return_range(&range->h, r);
3376 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout4 *iface,
3377 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
3379 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3380 struct layout_range *range;
3382 TRACE("%p, %u, %p, %p.\n", iface, position, object, r);
3384 if (position >= layout->len)
3385 return S_OK;
3387 range = get_layout_range_by_pos(layout, position);
3388 *object = range->object;
3389 if (*object)
3390 IDWriteInlineObject_AddRef(*object);
3392 return return_range(&range->h, r);
3395 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout4 *iface,
3396 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
3398 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3399 struct layout_range_iface *range;
3401 TRACE("%p, %u, %p, %p.\n", iface, position, typography, r);
3403 range = (struct layout_range_iface *)get_layout_range_header_by_pos(&layout->typographies, position);
3404 *typography = (IDWriteTypography *)range->iface;
3405 if (*typography)
3406 IDWriteTypography_AddRef(*typography);
3408 return return_range(&range->h, r);
3411 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout4 *iface,
3412 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3414 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3416 TRACE("%p, %u, %p, %p.\n", iface, position, length, r);
3418 return get_string_attribute_length(layout, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
3421 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout4 *iface,
3422 UINT32 position, WCHAR *locale, UINT32 length, DWRITE_TEXT_RANGE *r)
3424 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3426 TRACE("%p, %u, %p, %u, %p.\n", iface, position, locale, length, r);
3428 return get_string_attribute_value(layout, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
3431 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
3432 const DWRITE_MATRIX *m)
3434 D2D1_POINT_2F vec, vec2;
3436 if (!skiptransform) {
3437 /* apply transform */
3438 vec.x = 0.0f;
3439 vec.y = coord * ppdip;
3441 vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx;
3442 vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy;
3444 /* snap */
3445 vec2.x = floorf(vec2.x + 0.5f);
3446 vec2.y = floorf(vec2.y + 0.5f);
3448 /* apply inverted transform, we don't care about X component at this point */
3449 vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3450 vec.y /= ppdip;
3452 else
3453 vec.y = floorf(coord * ppdip + 0.5f) / ppdip;
3455 return vec.y;
3458 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout4 *iface,
3459 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
3461 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3462 BOOL disabled = FALSE, skiptransform = FALSE;
3463 struct layout_effective_inline *inlineobject;
3464 struct layout_effective_run *run;
3465 struct layout_strikethrough *s;
3466 struct layout_underline *u;
3467 FLOAT det = 0.0f, ppdip = 0.0f;
3468 DWRITE_MATRIX m = { 0 };
3469 HRESULT hr;
3471 TRACE("%p, %p, %p, %.8e, %.8e.\n", iface, context, renderer, origin_x, origin_y);
3473 hr = layout_compute_effective_runs(layout);
3474 if (FAILED(hr))
3475 return hr;
3477 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
3478 if (FAILED(hr))
3479 return hr;
3481 if (!disabled) {
3482 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
3483 if (FAILED(hr))
3484 return hr;
3486 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
3487 if (FAILED(hr))
3488 return hr;
3490 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
3491 if (ppdip <= 0.0f ||
3492 (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
3493 (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
3494 disabled = TRUE;
3495 else
3496 skiptransform = should_skip_transform(&m, &det);
3499 #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
3500 /* 1. Regular runs */
3501 LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, entry)
3503 const struct regular_layout_run *regular = &run->run->u.regular;
3504 UINT32 start_glyph = regular->clustermap[run->start];
3505 DWRITE_GLYPH_RUN_DESCRIPTION descr;
3506 DWRITE_GLYPH_RUN glyph_run;
3508 /* Everything but cluster map will be reused from nominal run, as we only need
3509 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
3510 it can't be reused because it has to start with 0 index for each reported run. */
3511 glyph_run = regular->run;
3512 glyph_run.glyphCount = run->glyphcount;
3514 /* fixup glyph data arrays */
3515 glyph_run.glyphIndices += start_glyph;
3516 glyph_run.glyphAdvances += start_glyph;
3517 glyph_run.glyphOffsets += start_glyph;
3519 /* description */
3520 descr = regular->descr;
3521 descr.stringLength = run->length;
3522 descr.string += run->start;
3523 descr.clusterMap = run->clustermap;
3524 descr.textPosition += run->start;
3526 /* return value is ignored */
3527 IDWriteTextRenderer_DrawGlyphRun(renderer,
3528 context,
3529 run->origin.x + run->align_dx + origin_x,
3530 SNAP_COORD(run->origin.y + origin_y),
3531 layout->measuringmode,
3532 &glyph_run,
3533 &descr,
3534 run->effect);
3537 /* 2. Inline objects */
3538 LIST_FOR_EACH_ENTRY(inlineobject, &layout->inlineobjects, struct layout_effective_inline, entry)
3540 IDWriteTextRenderer_DrawInlineObject(renderer,
3541 context,
3542 inlineobject->origin.x + inlineobject->align_dx + origin_x,
3543 SNAP_COORD(inlineobject->origin.y + origin_y),
3544 inlineobject->object,
3545 inlineobject->is_sideways,
3546 inlineobject->is_rtl,
3547 inlineobject->effect);
3550 /* 3. Underlines */
3551 LIST_FOR_EACH_ENTRY(u, &layout->underlines, struct layout_underline, entry)
3553 IDWriteTextRenderer_DrawUnderline(renderer,
3554 context,
3555 /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
3556 (is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x,
3557 SNAP_COORD(u->run->origin.y + origin_y),
3558 &u->u,
3559 u->run->effect);
3562 /* 4. Strikethrough */
3563 LIST_FOR_EACH_ENTRY(s, &layout->strikethrough, struct layout_strikethrough, entry)
3565 IDWriteTextRenderer_DrawStrikethrough(renderer,
3566 context,
3567 s->run->origin.x + s->run->align_dx + origin_x,
3568 SNAP_COORD(s->run->origin.y + origin_y),
3569 &s->s,
3570 s->run->effect);
3572 #undef SNAP_COORD
3574 return S_OK;
3577 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout4 *iface,
3578 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
3580 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3581 unsigned int line_count;
3582 HRESULT hr;
3583 size_t i;
3585 TRACE("%p, %p, %u, %p.\n", iface, metrics, max_count, count);
3587 if (FAILED(hr = layout_compute_effective_runs(layout)))
3588 return hr;
3590 if (metrics)
3592 line_count = min(max_count, layout->metrics.lineCount);
3593 for (i = 0; i < line_count; ++i)
3594 memcpy(&metrics[i], &layout->lines[i].metrics, sizeof(*metrics));
3597 *count = layout->metrics.lineCount;
3598 return max_count >= layout->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3601 static HRESULT layout_update_metrics(struct dwrite_textlayout *layout)
3603 return layout_compute_effective_runs(layout);
3606 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout4 *iface, DWRITE_TEXT_METRICS *metrics)
3608 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3609 HRESULT hr;
3611 TRACE("%p, %p.\n", iface, metrics);
3613 hr = layout_update_metrics(layout);
3614 if (hr == S_OK)
3615 memcpy(metrics, &layout->metrics, sizeof(*metrics));
3617 return hr;
3620 static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y)
3622 rect->left += x;
3623 rect->right += x;
3624 rect->top += y;
3625 rect->bottom += y;
3628 static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect)
3630 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
3633 static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src)
3635 if (d2d_rect_is_empty(dst)) {
3636 if (d2d_rect_is_empty(src)) {
3637 dst->left = dst->right = dst->top = dst->bottom = 0.0f;
3638 return;
3640 else
3641 *dst = *src;
3643 else {
3644 if (!d2d_rect_is_empty(src)) {
3645 dst->left = min(dst->left, src->left);
3646 dst->right = max(dst->right, src->right);
3647 dst->top = min(dst->top, src->top);
3648 dst->bottom = max(dst->bottom, src->bottom);
3653 static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox)
3655 const struct regular_layout_run *regular = &run->run->u.regular;
3656 UINT32 start_glyph = regular->clustermap[run->start];
3657 const DWRITE_GLYPH_RUN *glyph_run = &regular->run;
3658 D2D1_POINT_2F origin = { 0 };
3659 float rtl_factor;
3660 UINT32 i;
3662 if (run->bbox.top == run->bbox.bottom)
3664 struct dwrite_glyphbitmap glyph_bitmap;
3665 RECT *bbox;
3667 memset(&glyph_bitmap, 0, sizeof(glyph_bitmap));
3668 glyph_bitmap.fontface = (IDWriteFontFace4 *)glyph_run->fontFace;
3669 glyph_bitmap.simulations = IDWriteFontFace_GetSimulations(glyph_run->fontFace);
3670 glyph_bitmap.emsize = glyph_run->fontEmSize;
3671 glyph_bitmap.nohint = layout->measuringmode == DWRITE_MEASURING_MODE_NATURAL;
3673 bbox = &glyph_bitmap.bbox;
3675 rtl_factor = glyph_run->bidiLevel & 1 ? -1.0f : 1.0f;
3676 for (i = 0; i < run->glyphcount; i++) {
3677 D2D1_RECT_F glyph_bbox;
3679 /* FIXME: take care of vertical/rtl */
3680 if (glyph_run->bidiLevel & 1)
3681 origin.x -= glyph_run->glyphAdvances[i + start_glyph];
3683 glyph_bitmap.glyph = glyph_run->glyphIndices[i + start_glyph];
3684 freetype_get_glyph_bbox(&glyph_bitmap);
3686 glyph_bbox.left = bbox->left;
3687 glyph_bbox.top = bbox->top;
3688 glyph_bbox.right = bbox->right;
3689 glyph_bbox.bottom = bbox->bottom;
3691 d2d_rect_offset(&glyph_bbox, origin.x + rtl_factor * glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
3692 origin.y - glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
3694 d2d_rect_union(&run->bbox, &glyph_bbox);
3696 if (!(glyph_run->bidiLevel & 1))
3697 origin.x += glyph_run->glyphAdvances[i + start_glyph];
3701 *bbox = run->bbox;
3702 d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y);
3705 static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run,
3706 D2D1_RECT_F *bbox)
3708 DWRITE_OVERHANG_METRICS overhang_metrics = { 0 };
3709 DWRITE_INLINE_OBJECT_METRICS metrics = { 0 };
3710 HRESULT hr;
3712 if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) {
3713 WARN("Failed to get inline object metrics, hr %#x.\n", hr);
3714 memset(bbox, 0, sizeof(*bbox));
3715 return;
3718 bbox->left = run->origin.x + run->align_dx;
3719 bbox->right = bbox->left + metrics.width;
3720 bbox->top = run->origin.y;
3721 bbox->bottom = bbox->top + metrics.height;
3723 IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics);
3725 bbox->left -= overhang_metrics.left;
3726 bbox->right += overhang_metrics.right;
3727 bbox->top -= overhang_metrics.top;
3728 bbox->bottom += overhang_metrics.bottom;
3731 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout4 *iface,
3732 DWRITE_OVERHANG_METRICS *overhangs)
3734 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3735 struct layout_effective_inline *inline_run;
3736 struct layout_effective_run *run;
3737 D2D1_RECT_F bbox = { 0 };
3738 HRESULT hr;
3740 TRACE("%p, %p.\n", iface, overhangs);
3742 memset(overhangs, 0, sizeof(*overhangs));
3744 if (!(layout->recompute & RECOMPUTE_OVERHANGS))
3746 *overhangs = layout->overhangs;
3747 return S_OK;
3750 hr = layout_compute_effective_runs(layout);
3751 if (FAILED(hr))
3752 return hr;
3754 LIST_FOR_EACH_ENTRY(run, &layout->eruns, struct layout_effective_run, entry)
3756 D2D1_RECT_F run_bbox;
3758 layout_get_erun_bbox(layout, run, &run_bbox);
3759 d2d_rect_union(&bbox, &run_bbox);
3762 LIST_FOR_EACH_ENTRY(inline_run, &layout->inlineobjects, struct layout_effective_inline, entry)
3764 D2D1_RECT_F object_bbox;
3766 layout_get_inlineobj_bbox(layout, inline_run, &object_bbox);
3767 d2d_rect_union(&bbox, &object_bbox);
3770 /* Deltas from layout box. */
3771 layout->overhangs.left = -bbox.left;
3772 layout->overhangs.top = -bbox.top;
3773 layout->overhangs.right = bbox.right - layout->metrics.layoutWidth;
3774 layout->overhangs.bottom = bbox.bottom - layout->metrics.layoutHeight;
3775 layout->recompute &= ~RECOMPUTE_OVERHANGS;
3777 *overhangs = layout->overhangs;
3779 return S_OK;
3782 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout4 *iface,
3783 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
3785 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3786 HRESULT hr;
3788 TRACE("%p, %p, %u, %p.\n", iface, metrics, max_count, count);
3790 hr = layout_compute(layout);
3791 if (FAILED(hr))
3792 return hr;
3794 if (metrics)
3795 memcpy(metrics, layout->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS) * min(max_count, layout->cluster_count));
3797 *count = layout->cluster_count;
3798 return max_count >= layout->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3801 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout4 *iface, FLOAT* min_width)
3803 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3804 UINT32 start;
3805 FLOAT width;
3806 HRESULT hr;
3808 TRACE("%p, %p.\n", iface, min_width);
3810 if (!min_width)
3811 return E_INVALIDARG;
3813 if (!(layout->recompute & RECOMPUTE_MINIMAL_WIDTH))
3814 goto width_done;
3816 *min_width = 0.0f;
3817 hr = layout_compute(layout);
3818 if (FAILED(hr))
3819 return hr;
3821 /* Find widest word without emergency breaking between clusters, trailing whitespaces
3822 preceding breaking point do not contribute to word width. */
3823 for (start = 0; start < layout->cluster_count;)
3825 UINT32 end = start, j, next;
3827 /* Last cluster always could be wrapped after. */
3828 while (!layout->clustermetrics[end].canWrapLineAfter)
3829 end++;
3830 /* make is so current cluster range that we can wrap after is [start,end) */
3831 end++;
3833 next = end;
3835 /* Ignore trailing whitespace clusters, in case of single space range will
3836 be reduced to empty range, or [start,start+1). */
3837 while (end > start && layout->clustermetrics[end-1].isWhitespace)
3838 end--;
3840 /* check if cluster range exceeds last minimal width */
3841 width = 0.0f;
3842 for (j = start; j < end; j++)
3843 width += layout->clustermetrics[j].width;
3845 start = next;
3847 if (width > layout->minwidth)
3848 layout->minwidth = width;
3850 layout->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3852 width_done:
3853 *min_width = layout->minwidth;
3854 return S_OK;
3857 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout4 *iface,
3858 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3860 FIXME("%p, %.8e, %.8e, %p, %p, %p): stub\n", iface, pointX, pointY, is_trailinghit, is_inside, metrics);
3862 return E_NOTIMPL;
3865 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout4 *iface,
3866 UINT32 textPosition, BOOL is_trailinghit, FLOAT *pointX, FLOAT *pointY, DWRITE_HIT_TEST_METRICS *metrics)
3868 FIXME("%p, %u, %d, %p, %p, %p): stub\n", iface, textPosition, is_trailinghit, pointX, pointY, metrics);
3870 return E_NOTIMPL;
3873 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout4 *iface,
3874 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3875 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3877 FIXME("%p, %u, %u, %f, %f, %p, %u, %p): stub\n", iface, textPosition, textLength, originX, originY, metrics,
3878 max_metricscount, actual_metricscount);
3880 return E_NOTIMPL;
3883 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout4 *iface, BOOL is_pairkerning_enabled,
3884 DWRITE_TEXT_RANGE range)
3886 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3887 struct layout_range_attr_value value;
3889 TRACE("%p, %d, %s.\n", iface, is_pairkerning_enabled, debugstr_range(&range));
3891 value.range = range;
3892 value.u.pair_kerning = !!is_pairkerning_enabled;
3893 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3896 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout4 *iface, UINT32 position,
3897 BOOL *is_pairkerning_enabled, DWRITE_TEXT_RANGE *r)
3899 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3900 struct layout_range *range;
3902 TRACE("%p, %u, %p, %p.\n", iface, position, is_pairkerning_enabled, r);
3904 if (position >= layout->len)
3905 return S_OK;
3907 range = get_layout_range_by_pos(layout, position);
3908 *is_pairkerning_enabled = range->pair_kerning;
3910 return return_range(&range->h, r);
3913 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout4 *iface, FLOAT leading, FLOAT trailing,
3914 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3916 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3917 struct layout_range_attr_value value;
3919 TRACE("%p, %.8e, %.8e, %.8e, %s.\n", iface, leading, trailing, min_advance, debugstr_range(&range));
3921 if (min_advance < 0.0f)
3922 return E_INVALIDARG;
3924 value.range = range;
3925 value.u.spacing.leading = leading;
3926 value.u.spacing.trailing = trailing;
3927 value.u.spacing.min_advance = min_advance;
3928 return set_layout_range_attr(layout, LAYOUT_RANGE_ATTR_SPACING, &value);
3931 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout4 *iface, UINT32 position, FLOAT *leading,
3932 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3934 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3935 struct layout_range_spacing *range;
3937 TRACE("%p, %u, %p, %p, %p, %p.\n", iface, position, leading, trailing, min_advance, r);
3939 range = (struct layout_range_spacing *)get_layout_range_header_by_pos(&layout->spacing, position);
3940 *leading = range->leading;
3941 *trailing = range->trailing;
3942 *min_advance = range->min_advance;
3944 return return_range(&range->h, r);
3947 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout4 *iface, DWRITE_TEXT_METRICS1 *metrics)
3949 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3950 HRESULT hr;
3952 TRACE("%p, %p.\n", iface, metrics);
3954 if (SUCCEEDED(hr = layout_update_metrics(layout)))
3955 *metrics = layout->metrics;
3957 return hr;
3960 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout4 *iface,
3961 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3963 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3965 TRACE("%p, %d.\n", iface, orientation);
3967 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3968 return E_INVALIDARG;
3970 layout->format.vertical_orientation = orientation;
3971 return S_OK;
3974 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout4 *iface)
3976 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3978 TRACE("%p.\n", iface);
3980 return layout->format.vertical_orientation;
3983 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout4 *iface, BOOL lastline_wrapping_enabled)
3985 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3987 TRACE("%p, %d.\n", iface, lastline_wrapping_enabled);
3989 return IDWriteTextFormat3_SetLastLineWrapping(&layout->IDWriteTextFormat3_iface, lastline_wrapping_enabled);
3992 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout4 *iface)
3994 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
3996 TRACE("%p.\n", iface);
3998 return IDWriteTextFormat3_GetLastLineWrapping(&layout->IDWriteTextFormat3_iface);
4001 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout4 *iface,
4002 DWRITE_OPTICAL_ALIGNMENT alignment)
4004 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4006 TRACE("%p, %d.\n", iface, alignment);
4008 return IDWriteTextFormat3_SetOpticalAlignment(&layout->IDWriteTextFormat3_iface, alignment);
4011 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout4 *iface)
4013 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4015 TRACE("%p.\n", iface);
4017 return IDWriteTextFormat3_GetOpticalAlignment(&layout->IDWriteTextFormat3_iface);
4020 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout4 *iface, IDWriteFontFallback *fallback)
4022 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4024 TRACE("%p, %p.\n", iface, fallback);
4026 return set_fontfallback_for_format(&layout->format, fallback);
4029 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout4 *iface, IDWriteFontFallback **fallback)
4031 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4033 TRACE("%p, %p.\n", iface, fallback);
4035 return get_fontfallback_from_format(&layout->format, fallback);
4038 static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout4 *iface)
4040 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4042 TRACE("%p.\n", iface);
4044 layout->recompute = RECOMPUTE_EVERYTHING;
4045 return S_OK;
4048 static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout4 *iface, DWRITE_LINE_SPACING const *spacing)
4050 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4051 BOOL changed;
4052 HRESULT hr;
4054 TRACE("%p, %p.\n", iface, spacing);
4056 hr = format_set_linespacing(&layout->format, spacing, &changed);
4057 if (FAILED(hr))
4058 return hr;
4060 if (changed)
4062 if (!(layout->recompute & RECOMPUTE_LINES))
4064 UINT32 line;
4066 for (line = 0; line < layout->metrics.lineCount; line++)
4067 layout_apply_line_spacing(layout, line);
4069 layout_set_line_positions(layout);
4072 layout->recompute |= RECOMPUTE_OVERHANGS;
4075 return S_OK;
4078 static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout4 *iface, DWRITE_LINE_SPACING *spacing)
4080 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4082 TRACE("%p, %p.\n", iface, spacing);
4084 *spacing = layout->format.spacing;
4085 return S_OK;
4088 static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout4 *iface, DWRITE_LINE_METRICS1 *metrics,
4089 UINT32 max_count, UINT32 *count)
4091 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4092 unsigned int line_count;
4093 HRESULT hr;
4094 size_t i;
4096 TRACE("%p, %p, %u, %p.\n", iface, metrics, max_count, count);
4098 if (FAILED(hr = layout_compute_effective_runs(layout)))
4099 return hr;
4101 if (metrics)
4103 line_count = min(max_count, layout->metrics.lineCount);
4104 for (i = 0; i < line_count; ++i)
4105 metrics[i] = layout->lines[i].metrics;
4108 *count = layout->metrics.lineCount;
4109 return max_count >= layout->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
4112 static HRESULT WINAPI dwritetextlayout4_SetFontAxisValues(IDWriteTextLayout4 *iface,
4113 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 num_values, DWRITE_TEXT_RANGE range)
4115 FIXME("%p, %p, %u, %s.\n", iface, axis_values, num_values, debugstr_range(&range));
4117 return E_NOTIMPL;
4120 static UINT32 WINAPI dwritetextlayout4_GetFontAxisValueCount(IDWriteTextLayout4 *iface, UINT32 pos)
4122 FIXME("%p, %u.\n", iface, pos);
4124 return 0;
4127 static HRESULT WINAPI dwritetextlayout4_GetFontAxisValues(IDWriteTextLayout4 *iface, UINT32 pos,
4128 DWRITE_FONT_AXIS_VALUE *values, UINT32 num_values, DWRITE_TEXT_RANGE *range)
4130 FIXME("%p, %u, %p, %u, %p.\n", iface, pos, values, num_values, range);
4132 return E_NOTIMPL;
4135 static DWRITE_AUTOMATIC_FONT_AXES WINAPI dwritetextlayout4_GetAutomaticFontAxes(IDWriteTextLayout4 *iface)
4137 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4139 TRACE("%p.\n", iface);
4141 return layout->format.automatic_axes;
4144 static HRESULT WINAPI dwritetextlayout4_SetAutomaticFontAxes(IDWriteTextLayout4 *iface,
4145 DWRITE_AUTOMATIC_FONT_AXES axes)
4147 struct dwrite_textlayout *layout = impl_from_IDWriteTextLayout4(iface);
4149 TRACE("%p, %d.\n", iface, axes);
4151 if ((unsigned int)axes > DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE)
4152 return E_INVALIDARG;
4154 layout->format.automatic_axes = axes;
4155 return S_OK;
4158 static const IDWriteTextLayout4Vtbl dwritetextlayoutvtbl =
4160 dwritetextlayout_QueryInterface,
4161 dwritetextlayout_AddRef,
4162 dwritetextlayout_Release,
4163 dwritetextlayout_SetTextAlignment,
4164 dwritetextlayout_SetParagraphAlignment,
4165 dwritetextlayout_SetWordWrapping,
4166 dwritetextlayout_SetReadingDirection,
4167 dwritetextlayout_SetFlowDirection,
4168 dwritetextlayout_SetIncrementalTabStop,
4169 dwritetextlayout_SetTrimming,
4170 dwritetextlayout_SetLineSpacing,
4171 dwritetextlayout_GetTextAlignment,
4172 dwritetextlayout_GetParagraphAlignment,
4173 dwritetextlayout_GetWordWrapping,
4174 dwritetextlayout_GetReadingDirection,
4175 dwritetextlayout_GetFlowDirection,
4176 dwritetextlayout_GetIncrementalTabStop,
4177 dwritetextlayout_GetTrimming,
4178 dwritetextlayout_GetLineSpacing,
4179 dwritetextlayout_GetFontCollection,
4180 dwritetextlayout_GetFontFamilyNameLength,
4181 dwritetextlayout_GetFontFamilyName,
4182 dwritetextlayout_GetFontWeight,
4183 dwritetextlayout_GetFontStyle,
4184 dwritetextlayout_GetFontStretch,
4185 dwritetextlayout_GetFontSize,
4186 dwritetextlayout_GetLocaleNameLength,
4187 dwritetextlayout_GetLocaleName,
4188 dwritetextlayout_SetMaxWidth,
4189 dwritetextlayout_SetMaxHeight,
4190 dwritetextlayout_SetFontCollection,
4191 dwritetextlayout_SetFontFamilyName,
4192 dwritetextlayout_SetFontWeight,
4193 dwritetextlayout_SetFontStyle,
4194 dwritetextlayout_SetFontStretch,
4195 dwritetextlayout_SetFontSize,
4196 dwritetextlayout_SetUnderline,
4197 dwritetextlayout_SetStrikethrough,
4198 dwritetextlayout_SetDrawingEffect,
4199 dwritetextlayout_SetInlineObject,
4200 dwritetextlayout_SetTypography,
4201 dwritetextlayout_SetLocaleName,
4202 dwritetextlayout_GetMaxWidth,
4203 dwritetextlayout_GetMaxHeight,
4204 dwritetextlayout_layout_GetFontCollection,
4205 dwritetextlayout_layout_GetFontFamilyNameLength,
4206 dwritetextlayout_layout_GetFontFamilyName,
4207 dwritetextlayout_layout_GetFontWeight,
4208 dwritetextlayout_layout_GetFontStyle,
4209 dwritetextlayout_layout_GetFontStretch,
4210 dwritetextlayout_layout_GetFontSize,
4211 dwritetextlayout_GetUnderline,
4212 dwritetextlayout_GetStrikethrough,
4213 dwritetextlayout_GetDrawingEffect,
4214 dwritetextlayout_GetInlineObject,
4215 dwritetextlayout_GetTypography,
4216 dwritetextlayout_layout_GetLocaleNameLength,
4217 dwritetextlayout_layout_GetLocaleName,
4218 dwritetextlayout_Draw,
4219 dwritetextlayout_GetLineMetrics,
4220 dwritetextlayout_GetMetrics,
4221 dwritetextlayout_GetOverhangMetrics,
4222 dwritetextlayout_GetClusterMetrics,
4223 dwritetextlayout_DetermineMinWidth,
4224 dwritetextlayout_HitTestPoint,
4225 dwritetextlayout_HitTestTextPosition,
4226 dwritetextlayout_HitTestTextRange,
4227 dwritetextlayout1_SetPairKerning,
4228 dwritetextlayout1_GetPairKerning,
4229 dwritetextlayout1_SetCharacterSpacing,
4230 dwritetextlayout1_GetCharacterSpacing,
4231 dwritetextlayout2_GetMetrics,
4232 dwritetextlayout2_SetVerticalGlyphOrientation,
4233 dwritetextlayout2_GetVerticalGlyphOrientation,
4234 dwritetextlayout2_SetLastLineWrapping,
4235 dwritetextlayout2_GetLastLineWrapping,
4236 dwritetextlayout2_SetOpticalAlignment,
4237 dwritetextlayout2_GetOpticalAlignment,
4238 dwritetextlayout2_SetFontFallback,
4239 dwritetextlayout2_GetFontFallback,
4240 dwritetextlayout3_InvalidateLayout,
4241 dwritetextlayout3_SetLineSpacing,
4242 dwritetextlayout3_GetLineSpacing,
4243 dwritetextlayout3_GetLineMetrics,
4244 dwritetextlayout4_SetFontAxisValues,
4245 dwritetextlayout4_GetFontAxisValueCount,
4246 dwritetextlayout4_GetFontAxisValues,
4247 dwritetextlayout4_GetAutomaticFontAxes,
4248 dwritetextlayout4_SetAutomaticFontAxes,
4251 static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat3 *iface, REFIID riid, void **obj)
4253 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4255 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
4257 return IDWriteTextLayout4_QueryInterface(&layout->IDWriteTextLayout4_iface, riid, obj);
4260 static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat3 *iface)
4262 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4263 return IDWriteTextLayout4_AddRef(&layout->IDWriteTextLayout4_iface);
4266 static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat3 *iface)
4268 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4269 return IDWriteTextLayout4_Release(&layout->IDWriteTextLayout4_iface);
4272 static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat3 *iface,
4273 DWRITE_TEXT_ALIGNMENT alignment)
4275 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4276 BOOL changed;
4277 HRESULT hr;
4279 TRACE("%p, %d.\n", iface, alignment);
4281 hr = format_set_textalignment(&layout->format, alignment, &changed);
4282 if (FAILED(hr))
4283 return hr;
4285 if (changed)
4287 /* if layout is not ready there's nothing to align */
4288 if (!(layout->recompute & RECOMPUTE_LINES))
4289 layout_apply_text_alignment(layout);
4290 layout->recompute |= RECOMPUTE_OVERHANGS;
4293 return S_OK;
4296 static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat3 *iface,
4297 DWRITE_PARAGRAPH_ALIGNMENT alignment)
4299 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4300 BOOL changed;
4301 HRESULT hr;
4303 TRACE("%p, %d.\n", iface, alignment);
4305 hr = format_set_paralignment(&layout->format, alignment, &changed);
4306 if (FAILED(hr))
4307 return hr;
4309 if (changed)
4311 /* if layout is not ready there's nothing to align */
4312 if (!(layout->recompute & RECOMPUTE_LINES))
4313 layout_apply_par_alignment(layout);
4314 layout->recompute |= RECOMPUTE_OVERHANGS;
4317 return S_OK;
4320 static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat3 *iface, DWRITE_WORD_WRAPPING wrapping)
4322 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4323 BOOL changed;
4324 HRESULT hr;
4326 TRACE("%p, %d.\n", iface, wrapping);
4328 hr = format_set_wordwrapping(&layout->format, wrapping, &changed);
4329 if (FAILED(hr))
4330 return hr;
4332 if (changed)
4333 layout->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4335 return S_OK;
4338 static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat3 *iface,
4339 DWRITE_READING_DIRECTION direction)
4341 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4342 BOOL changed;
4343 HRESULT hr;
4345 TRACE("%p, %d.\n", iface, direction);
4347 hr = format_set_readingdirection(&layout->format, direction, &changed);
4348 if (FAILED(hr))
4349 return hr;
4351 if (changed)
4352 layout->recompute = RECOMPUTE_EVERYTHING;
4354 return S_OK;
4357 static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat3 *iface,
4358 DWRITE_FLOW_DIRECTION direction)
4360 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4361 BOOL changed;
4362 HRESULT hr;
4364 TRACE("%p, %d.\n", iface, direction);
4366 hr = format_set_flowdirection(&layout->format, direction, &changed);
4367 if (FAILED(hr))
4368 return hr;
4370 if (changed)
4371 layout->recompute = RECOMPUTE_EVERYTHING;
4373 return S_OK;
4376 static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat3 *iface, FLOAT tabstop)
4378 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4380 TRACE("%p, %.8e.\n", iface, tabstop);
4382 if (tabstop <= 0.0f)
4383 return E_INVALIDARG;
4385 layout->format.tabstop = tabstop;
4386 return S_OK;
4389 static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat3 *iface, DWRITE_TRIMMING const *trimming,
4390 IDWriteInlineObject *trimming_sign)
4392 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4393 BOOL changed;
4394 HRESULT hr;
4396 TRACE("%p, %p, %p.\n", iface, trimming, trimming_sign);
4398 hr = format_set_trimming(&layout->format, trimming, trimming_sign, &changed);
4400 if (changed)
4401 layout->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4403 return hr;
4406 static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat3 *iface,
4407 DWRITE_LINE_SPACING_METHOD method, FLOAT height, FLOAT baseline)
4409 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4410 DWRITE_LINE_SPACING spacing;
4412 TRACE("%p, %d, %.8e, %.8e.\n", iface, method, height, baseline);
4414 spacing = layout->format.spacing;
4415 spacing.method = method;
4416 spacing.height = height;
4417 spacing.baseline = baseline;
4418 return IDWriteTextLayout4_SetLineSpacing(&layout->IDWriteTextLayout4_iface, &spacing);
4421 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat3 *iface)
4423 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4425 TRACE("%p.\n", iface);
4427 return layout->format.textalignment;
4430 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat3 *iface)
4432 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4434 TRACE("%p.\n", iface);
4436 return layout->format.paralign;
4439 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat3 *iface)
4441 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4443 TRACE("%p.\n", iface);
4445 return layout->format.wrapping;
4448 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat3 *iface)
4450 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4452 TRACE("%p.\n", iface);
4454 return layout->format.readingdir;
4457 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat3 *iface)
4459 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4461 TRACE("%p.\n", iface);
4463 return layout->format.flow;
4466 static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat3 *iface)
4468 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4470 TRACE("%p.\n", iface);
4472 return layout->format.tabstop;
4475 static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat3 *iface, DWRITE_TRIMMING *options,
4476 IDWriteInlineObject **trimming_sign)
4478 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4480 TRACE("%p, %p, %p.\n", iface, options, trimming_sign);
4482 *options = layout->format.trimming;
4483 *trimming_sign = layout->format.trimmingsign;
4484 if (*trimming_sign)
4485 IDWriteInlineObject_AddRef(*trimming_sign);
4486 return S_OK;
4489 static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat3 *iface,
4490 DWRITE_LINE_SPACING_METHOD *method, FLOAT *spacing, FLOAT *baseline)
4492 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4494 TRACE("%p, %p, %p, %p.\n", iface, method, spacing, baseline);
4496 *method = layout->format.spacing.method;
4497 *spacing = layout->format.spacing.height;
4498 *baseline = layout->format.spacing.baseline;
4499 return S_OK;
4502 static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat3 *iface,
4503 IDWriteFontCollection **collection)
4505 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4507 TRACE("%p, %p.\n", iface, collection);
4509 *collection = layout->format.collection;
4510 if (*collection)
4511 IDWriteFontCollection_AddRef(*collection);
4512 return S_OK;
4515 static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat3 *iface)
4517 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4519 TRACE("%p.\n", iface);
4521 return layout->format.family_len;
4524 static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat3 *iface, WCHAR *name, UINT32 size)
4526 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4528 TRACE("%p, %p, %u.\n", iface, name, size);
4530 if (size <= layout->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4531 strcpyW(name, layout->format.family_name);
4532 return S_OK;
4535 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat3 *iface)
4537 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4539 TRACE("%p.\n", iface);
4541 return layout->format.weight;
4544 static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat3 *iface)
4546 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4548 TRACE("%p.\n", iface);
4550 return layout->format.style;
4553 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat3 *iface)
4555 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4557 TRACE("%p.\n", iface);
4559 return layout->format.stretch;
4562 static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat3 *iface)
4564 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4566 TRACE("%p.\n", iface);
4568 return layout->format.fontsize;
4571 static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat3 *iface)
4573 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4575 TRACE("%p.\n", iface);
4577 return layout->format.locale_len;
4580 static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat3 *iface, WCHAR *name, UINT32 size)
4582 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4584 TRACE("%p, %p, %u.\n", iface, name, size);
4586 if (size <= layout->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4587 strcpyW(name, layout->format.locale);
4588 return S_OK;
4591 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat3 *iface,
4592 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4594 FIXME("%p, %d: stub\n", iface, orientation);
4596 return E_NOTIMPL;
4599 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat3 *iface)
4601 FIXME("%p: stub\n", iface);
4603 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4606 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat3 *iface,
4607 BOOL lastline_wrapping_enabled)
4609 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4611 TRACE("%p, %d.\n", iface, lastline_wrapping_enabled);
4613 layout->format.last_line_wrapping = !!lastline_wrapping_enabled;
4614 return S_OK;
4617 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat3 *iface)
4619 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4621 TRACE("%p.\n", iface);
4623 return layout->format.last_line_wrapping;
4626 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat3 *iface,
4627 DWRITE_OPTICAL_ALIGNMENT alignment)
4629 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4631 TRACE("%p, %d.\n", iface, alignment);
4633 return format_set_optical_alignment(&layout->format, alignment);
4636 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat3 *iface)
4638 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4640 TRACE("%p.\n", iface);
4642 return layout->format.optical_alignment;
4645 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat3 *iface,
4646 IDWriteFontFallback *fallback)
4648 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4650 TRACE("%p, %p.\n", iface, fallback);
4652 return IDWriteTextLayout4_SetFontFallback(&layout->IDWriteTextLayout4_iface, fallback);
4655 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat3 *iface,
4656 IDWriteFontFallback **fallback)
4658 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4660 TRACE("%p, %p.\n", iface, fallback);
4662 return IDWriteTextLayout4_GetFontFallback(&layout->IDWriteTextLayout4_iface, fallback);
4665 static HRESULT WINAPI dwritetextformat2_layout_SetLineSpacing(IDWriteTextFormat3 *iface,
4666 DWRITE_LINE_SPACING const *spacing)
4668 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4669 return IDWriteTextLayout4_SetLineSpacing(&layout->IDWriteTextLayout4_iface, spacing);
4672 static HRESULT WINAPI dwritetextformat2_layout_GetLineSpacing(IDWriteTextFormat3 *iface, DWRITE_LINE_SPACING *spacing)
4674 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4675 return IDWriteTextLayout4_GetLineSpacing(&layout->IDWriteTextLayout4_iface, spacing);
4678 static HRESULT WINAPI dwritetextformat3_layout_SetFontAxisValues(IDWriteTextFormat3 *iface,
4679 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 num_values)
4681 FIXME("%p, %p, %u.\n", iface, axis_values, num_values);
4683 return E_NOTIMPL;
4686 static UINT32 WINAPI dwritetextformat3_layout_GetFontAxisValueCount(IDWriteTextFormat3 *iface)
4688 FIXME("%p.\n", iface);
4690 return 0;
4693 static HRESULT WINAPI dwritetextformat3_layout_GetFontAxisValues(IDWriteTextFormat3 *iface,
4694 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 num_values)
4696 FIXME("%p, %p, %u.\n", iface, axis_values, num_values);
4698 return E_NOTIMPL;
4701 static DWRITE_AUTOMATIC_FONT_AXES WINAPI dwritetextformat3_layout_GetAutomaticFontAxes(IDWriteTextFormat3 *iface)
4703 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4704 return IDWriteTextLayout4_GetAutomaticFontAxes(&layout->IDWriteTextLayout4_iface);
4707 static HRESULT WINAPI dwritetextformat3_layout_SetAutomaticFontAxes(IDWriteTextFormat3 *iface,
4708 DWRITE_AUTOMATIC_FONT_AXES axes)
4710 struct dwrite_textlayout *layout = impl_layout_from_IDWriteTextFormat3(iface);
4711 return IDWriteTextLayout4_SetAutomaticFontAxes(&layout->IDWriteTextLayout4_iface, axes);
4714 static const IDWriteTextFormat3Vtbl dwritetextformat3_layout_vtbl =
4716 dwritetextformat_layout_QueryInterface,
4717 dwritetextformat_layout_AddRef,
4718 dwritetextformat_layout_Release,
4719 dwritetextformat_layout_SetTextAlignment,
4720 dwritetextformat_layout_SetParagraphAlignment,
4721 dwritetextformat_layout_SetWordWrapping,
4722 dwritetextformat_layout_SetReadingDirection,
4723 dwritetextformat_layout_SetFlowDirection,
4724 dwritetextformat_layout_SetIncrementalTabStop,
4725 dwritetextformat_layout_SetTrimming,
4726 dwritetextformat_layout_SetLineSpacing,
4727 dwritetextformat_layout_GetTextAlignment,
4728 dwritetextformat_layout_GetParagraphAlignment,
4729 dwritetextformat_layout_GetWordWrapping,
4730 dwritetextformat_layout_GetReadingDirection,
4731 dwritetextformat_layout_GetFlowDirection,
4732 dwritetextformat_layout_GetIncrementalTabStop,
4733 dwritetextformat_layout_GetTrimming,
4734 dwritetextformat_layout_GetLineSpacing,
4735 dwritetextformat_layout_GetFontCollection,
4736 dwritetextformat_layout_GetFontFamilyNameLength,
4737 dwritetextformat_layout_GetFontFamilyName,
4738 dwritetextformat_layout_GetFontWeight,
4739 dwritetextformat_layout_GetFontStyle,
4740 dwritetextformat_layout_GetFontStretch,
4741 dwritetextformat_layout_GetFontSize,
4742 dwritetextformat_layout_GetLocaleNameLength,
4743 dwritetextformat_layout_GetLocaleName,
4744 dwritetextformat1_layout_SetVerticalGlyphOrientation,
4745 dwritetextformat1_layout_GetVerticalGlyphOrientation,
4746 dwritetextformat1_layout_SetLastLineWrapping,
4747 dwritetextformat1_layout_GetLastLineWrapping,
4748 dwritetextformat1_layout_SetOpticalAlignment,
4749 dwritetextformat1_layout_GetOpticalAlignment,
4750 dwritetextformat1_layout_SetFontFallback,
4751 dwritetextformat1_layout_GetFontFallback,
4752 dwritetextformat2_layout_SetLineSpacing,
4753 dwritetextformat2_layout_GetLineSpacing,
4754 dwritetextformat3_layout_SetFontAxisValues,
4755 dwritetextformat3_layout_GetFontAxisValueCount,
4756 dwritetextformat3_layout_GetFontAxisValues,
4757 dwritetextformat3_layout_GetAutomaticFontAxes,
4758 dwritetextformat3_layout_SetAutomaticFontAxes,
4761 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
4762 REFIID riid, void **obj)
4764 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
4765 IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
4766 IsEqualIID(riid, &IID_IUnknown))
4768 *obj = iface;
4769 IDWriteTextAnalysisSink1_AddRef(iface);
4770 return S_OK;
4773 WARN("%s not implemented.\n", debugstr_guid(riid));
4775 *obj = NULL;
4776 return E_NOINTERFACE;
4779 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
4781 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4782 return IDWriteTextLayout4_AddRef(&layout->IDWriteTextLayout4_iface);
4785 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
4787 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4788 return IDWriteTextLayout4_Release(&layout->IDWriteTextLayout4_iface);
4791 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
4792 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
4794 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4795 struct layout_run *run;
4796 HRESULT hr;
4798 TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
4800 if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_REGULAR, position, &run)))
4801 return hr;
4803 run->u.regular.descr.string = &layout->str[position];
4804 run->u.regular.descr.stringLength = length;
4805 run->u.regular.descr.textPosition = position;
4806 run->u.regular.sa = *sa;
4807 list_add_tail(&layout->runs, &run->entry);
4808 return S_OK;
4811 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
4812 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
4814 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4816 if (position + length > layout->len)
4817 return E_FAIL;
4819 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
4820 return S_OK;
4823 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
4824 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
4826 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4827 struct layout_run *cur_run;
4828 HRESULT hr;
4830 TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
4832 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
4833 struct regular_layout_run *cur = &cur_run->u.regular;
4834 struct layout_run *run;
4836 if (cur_run->kind == LAYOUT_RUN_INLINE)
4837 continue;
4839 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
4840 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
4841 continue;
4843 /* full hit - just set run level */
4844 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
4845 cur->run.bidiLevel = resolvedLevel;
4846 break;
4849 /* current run is fully covered, move to next one */
4850 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
4851 cur->run.bidiLevel = resolvedLevel;
4852 position += cur->descr.stringLength;
4853 length -= cur->descr.stringLength;
4854 continue;
4857 /* all fully covered runs are processed at this point, reuse existing run for remaining
4858 reported bidi range and add another run for the rest of original one */
4860 if (FAILED(hr = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length, &run)))
4861 return hr;
4863 *run = *cur_run;
4864 run->u.regular.descr.textPosition = position + length;
4865 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
4866 run->u.regular.descr.string = &layout->str[position + length];
4868 /* reduce existing run */
4869 cur->run.bidiLevel = resolvedLevel;
4870 cur->descr.stringLength = length;
4872 list_add_after(&cur_run->entry, &run->entry);
4873 break;
4876 return S_OK;
4879 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
4880 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
4882 return E_NOTIMPL;
4885 static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
4886 UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
4887 BOOL is_sideways, BOOL is_rtl)
4889 return E_NOTIMPL;
4892 static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
4893 dwritetextlayout_sink_QueryInterface,
4894 dwritetextlayout_sink_AddRef,
4895 dwritetextlayout_sink_Release,
4896 dwritetextlayout_sink_SetScriptAnalysis,
4897 dwritetextlayout_sink_SetLineBreakpoints,
4898 dwritetextlayout_sink_SetBidiLevel,
4899 dwritetextlayout_sink_SetNumberSubstitution,
4900 dwritetextlayout_sink_SetGlyphOrientation
4903 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
4904 REFIID riid, void **obj)
4906 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
4907 IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
4908 IsEqualIID(riid, &IID_IUnknown))
4910 *obj = iface;
4911 IDWriteTextAnalysisSource1_AddRef(iface);
4912 return S_OK;
4915 WARN("%s not implemented.\n", debugstr_guid(riid));
4917 *obj = NULL;
4918 return E_NOINTERFACE;
4921 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
4923 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4924 return IDWriteTextLayout4_AddRef(&layout->IDWriteTextLayout4_iface);
4927 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
4929 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4930 return IDWriteTextLayout4_Release(&layout->IDWriteTextLayout4_iface);
4933 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
4934 UINT32 position, WCHAR const** text, UINT32* text_len)
4936 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4938 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4940 if (position < layout->len) {
4941 *text = &layout->str[position];
4942 *text_len = layout->len - position;
4944 else {
4945 *text = NULL;
4946 *text_len = 0;
4949 return S_OK;
4952 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
4953 UINT32 position, WCHAR const** text, UINT32* text_len)
4955 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4957 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4959 if (position > 0 && position < layout->len) {
4960 *text = layout->str;
4961 *text_len = position;
4963 else {
4964 *text = NULL;
4965 *text_len = 0;
4968 return S_OK;
4971 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
4973 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4974 return IDWriteTextLayout4_GetReadingDirection(&layout->IDWriteTextLayout4_iface);
4977 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
4978 UINT32 position, UINT32* text_len, WCHAR const** locale)
4980 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4981 struct layout_range *range = get_layout_range_by_pos(layout, position);
4983 if (position < layout->len) {
4984 struct layout_range *next;
4986 *locale = range->locale;
4987 *text_len = range->h.range.length - position;
4989 next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
4990 while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
4991 *text_len += next->h.range.length;
4992 next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
4995 *text_len = min(*text_len, layout->len - position);
4997 else {
4998 *locale = NULL;
4999 *text_len = 0;
5002 return S_OK;
5005 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
5006 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
5008 FIXME("%u %p %p: stub\n", position, text_len, substitution);
5009 return E_NOTIMPL;
5012 static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
5013 UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
5015 FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
5016 return E_NOTIMPL;
5019 static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
5020 dwritetextlayout_source_QueryInterface,
5021 dwritetextlayout_source_AddRef,
5022 dwritetextlayout_source_Release,
5023 dwritetextlayout_source_GetTextAtPosition,
5024 dwritetextlayout_source_GetTextBeforePosition,
5025 dwritetextlayout_source_GetParagraphReadingDirection,
5026 dwritetextlayout_source_GetLocaleName,
5027 dwritetextlayout_source_GetNumberSubstitution,
5028 dwritetextlayout_source_GetVerticalGlyphOrientation
5031 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
5033 struct dwrite_textformat *textformat;
5034 IDWriteTextFormat1 *format1;
5035 IDWriteTextFormat3 *format3;
5036 UINT32 len;
5037 HRESULT hr;
5039 if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
5040 layout->format = textformat->format;
5042 layout->format.locale = heap_strdupW(textformat->format.locale);
5043 layout->format.family_name = heap_strdupW(textformat->format.family_name);
5044 if (!layout->format.locale || !layout->format.family_name)
5046 heap_free(layout->format.locale);
5047 heap_free(layout->format.family_name);
5048 return E_OUTOFMEMORY;
5051 if (layout->format.trimmingsign)
5052 IDWriteInlineObject_AddRef(layout->format.trimmingsign);
5053 if (layout->format.collection)
5054 IDWriteFontCollection_AddRef(layout->format.collection);
5055 if (layout->format.fallback)
5056 IDWriteFontFallback_AddRef(layout->format.fallback);
5058 return S_OK;
5061 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
5062 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
5063 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
5064 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
5065 layout->format.tabstop = IDWriteTextFormat_GetIncrementalTabStop(format);
5066 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
5067 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
5068 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
5069 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
5070 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
5071 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
5072 &layout->format.spacing.height, &layout->format.spacing.baseline);
5073 if (FAILED(hr))
5074 return hr;
5076 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
5077 if (FAILED(hr))
5078 return hr;
5080 /* locale name and length */
5081 len = IDWriteTextFormat_GetLocaleNameLength(format);
5082 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
5083 if (!layout->format.locale)
5084 return E_OUTOFMEMORY;
5086 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
5087 if (FAILED(hr))
5088 return hr;
5089 layout->format.locale_len = len;
5091 /* font family name and length */
5092 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
5093 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
5094 if (!layout->format.family_name)
5095 return E_OUTOFMEMORY;
5097 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
5098 if (FAILED(hr))
5099 return hr;
5100 layout->format.family_len = len;
5102 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
5103 if (hr == S_OK)
5105 IDWriteTextFormat2 *format2;
5107 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
5108 layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5109 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
5111 if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
5112 IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
5113 IDWriteTextFormat2_Release(format2);
5116 IDWriteTextFormat1_Release(format1);
5119 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat3, (void **)&format3);
5120 if (hr == S_OK)
5122 layout->format.automatic_axes = IDWriteTextFormat3_GetAutomaticFontAxes(format3);
5123 IDWriteTextFormat3_Release(format3);
5126 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
5129 static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
5131 struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
5132 static const DWRITE_TEXT_RANGE r = { 0, ~0u };
5133 HRESULT hr;
5135 layout->IDWriteTextLayout4_iface.lpVtbl = &dwritetextlayoutvtbl;
5136 layout->IDWriteTextFormat3_iface.lpVtbl = &dwritetextformat3_layout_vtbl;
5137 layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
5138 layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
5139 layout->refcount = 1;
5140 layout->len = desc->length;
5141 layout->recompute = RECOMPUTE_EVERYTHING;
5142 list_init(&layout->eruns);
5143 list_init(&layout->inlineobjects);
5144 list_init(&layout->underlines);
5145 list_init(&layout->strikethrough);
5146 list_init(&layout->runs);
5147 list_init(&layout->ranges);
5148 list_init(&layout->strike_ranges);
5149 list_init(&layout->underline_ranges);
5150 list_init(&layout->effects);
5151 list_init(&layout->spacing);
5152 list_init(&layout->typographies);
5153 layout->metrics.layoutWidth = desc->max_width;
5154 layout->metrics.layoutHeight = desc->max_height;
5156 layout->str = heap_strdupnW(desc->string, desc->length);
5157 if (desc->length && !layout->str) {
5158 hr = E_OUTOFMEMORY;
5159 goto fail;
5162 hr = layout_format_from_textformat(layout, desc->format);
5163 if (FAILED(hr))
5164 goto fail;
5166 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
5167 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
5168 underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
5169 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
5170 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
5171 typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
5172 if (!range || !strike || !effect || !spacing || !typography || !underline) {
5173 free_layout_range(range);
5174 free_layout_range(strike);
5175 free_layout_range(underline);
5176 free_layout_range(effect);
5177 free_layout_range(spacing);
5178 free_layout_range(typography);
5179 hr = E_OUTOFMEMORY;
5180 goto fail;
5183 layout->measuringmode = desc->is_gdi_compatible ? (desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL :
5184 DWRITE_MEASURING_MODE_GDI_CLASSIC) : DWRITE_MEASURING_MODE_NATURAL;
5185 layout->ppdip = desc->ppdip;
5186 layout->transform = desc->transform ? *desc->transform : identity;
5188 layout->factory = desc->factory;
5189 IDWriteFactory7_AddRef(layout->factory);
5190 list_add_head(&layout->ranges, &range->entry);
5191 list_add_head(&layout->strike_ranges, &strike->entry);
5192 list_add_head(&layout->underline_ranges, &underline->entry);
5193 list_add_head(&layout->effects, &effect->entry);
5194 list_add_head(&layout->spacing, &spacing->entry);
5195 list_add_head(&layout->typographies, &typography->entry);
5196 return S_OK;
5198 fail:
5199 IDWriteTextLayout4_Release(&layout->IDWriteTextLayout4_iface);
5200 return hr;
5203 HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **layout)
5205 struct dwrite_textlayout *object;
5206 HRESULT hr;
5208 *layout = NULL;
5210 if (!desc->format || !desc->string)
5211 return E_INVALIDARG;
5213 if (!(object = heap_alloc_zero(sizeof(*object))))
5214 return E_OUTOFMEMORY;
5216 hr = init_textlayout(desc, object);
5217 if (hr == S_OK)
5218 *layout = (IDWriteTextLayout *)&object->IDWriteTextLayout4_iface;
5220 return hr;
5223 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
5225 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
5227 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
5228 *obj = iface;
5229 IDWriteInlineObject_AddRef(iface);
5230 return S_OK;
5233 WARN("%s not implemented.\n", debugstr_guid(riid));
5235 *obj = NULL;
5236 return E_NOINTERFACE;
5239 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
5241 struct dwrite_trimmingsign *sign = impl_from_IDWriteInlineObject(iface);
5242 ULONG refcount = InterlockedIncrement(&sign->refcount);
5244 TRACE("%p, refcount %d.\n", iface, refcount);
5246 return refcount;
5249 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
5251 struct dwrite_trimmingsign *sign = impl_from_IDWriteInlineObject(iface);
5252 ULONG refcount = InterlockedDecrement(&sign->refcount);
5254 TRACE("%p, refcount %d.\n", iface, refcount);
5256 if (!refcount)
5258 IDWriteTextLayout_Release(sign->layout);
5259 heap_free(sign);
5262 return refcount;
5265 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
5266 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
5268 struct dwrite_trimmingsign *sign = impl_from_IDWriteInlineObject(iface);
5269 DWRITE_LINE_METRICS line;
5270 UINT32 line_count;
5272 TRACE("%p, %p, %p, %.2f, %.2f, %d, %d, %p.\n", iface, context, renderer, originX, originY,
5273 is_sideways, is_rtl, effect);
5275 IDWriteTextLayout_GetLineMetrics(sign->layout, &line, 1, &line_count);
5276 return IDWriteTextLayout_Draw(sign->layout, context, renderer, originX, originY - line.baseline);
5279 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
5281 struct dwrite_trimmingsign *sign = impl_from_IDWriteInlineObject(iface);
5282 DWRITE_TEXT_METRICS metrics;
5283 HRESULT hr;
5285 TRACE("%p, %p.\n", iface, ret);
5287 hr = IDWriteTextLayout_GetMetrics(sign->layout, &metrics);
5288 if (FAILED(hr))
5290 memset(ret, 0, sizeof(*ret));
5291 return hr;
5294 ret->width = metrics.width;
5295 ret->height = 0.0f;
5296 ret->baseline = 0.0f;
5297 ret->supportsSideways = FALSE;
5298 return S_OK;
5301 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
5303 struct dwrite_trimmingsign *sign = impl_from_IDWriteInlineObject(iface);
5305 TRACE("%p, %p.\n", iface, overhangs);
5307 return IDWriteTextLayout_GetOverhangMetrics(sign->layout, overhangs);
5310 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
5311 DWRITE_BREAK_CONDITION *after)
5313 TRACE("%p, %p, %p.\n", iface, before, after);
5315 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
5316 return S_OK;
5319 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl =
5321 dwritetrimmingsign_QueryInterface,
5322 dwritetrimmingsign_AddRef,
5323 dwritetrimmingsign_Release,
5324 dwritetrimmingsign_Draw,
5325 dwritetrimmingsign_GetMetrics,
5326 dwritetrimmingsign_GetOverhangMetrics,
5327 dwritetrimmingsign_GetBreakConditions
5330 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
5332 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
5333 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
5336 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
5338 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
5339 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
5342 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
5344 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
5345 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
5348 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
5350 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
5351 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
5354 HRESULT create_trimmingsign(IDWriteFactory7 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
5356 static const WCHAR ellipsisW = 0x2026;
5357 struct dwrite_trimmingsign *object;
5358 DWRITE_READING_DIRECTION reading;
5359 DWRITE_FLOW_DIRECTION flow;
5360 HRESULT hr;
5362 *sign = NULL;
5364 /* Validate reading/flow direction here, layout creation won't complain about
5365 invalid combinations. */
5366 reading = IDWriteTextFormat_GetReadingDirection(format);
5367 flow = IDWriteTextFormat_GetFlowDirection(format);
5369 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
5370 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
5371 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
5373 if (!(object = heap_alloc(sizeof(*object))))
5374 return E_OUTOFMEMORY;
5376 object->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
5377 object->refcount = 1;
5379 hr = IDWriteFactory7_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &object->layout);
5380 if (FAILED(hr))
5382 heap_free(object);
5383 return hr;
5386 IDWriteTextLayout_SetWordWrapping(object->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
5387 IDWriteTextLayout_SetParagraphAlignment(object->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
5388 IDWriteTextLayout_SetTextAlignment(object->layout, DWRITE_TEXT_ALIGNMENT_LEADING);
5390 *sign = &object->IDWriteInlineObject_iface;
5392 return S_OK;
5395 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat3 *iface, REFIID riid, void **obj)
5397 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
5399 if (IsEqualIID(riid, &IID_IDWriteTextFormat3) ||
5400 IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
5401 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
5402 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
5403 IsEqualIID(riid, &IID_IUnknown))
5405 *obj = iface;
5406 IDWriteTextFormat3_AddRef(iface);
5407 return S_OK;
5410 WARN("%s not implemented.\n", debugstr_guid(riid));
5412 *obj = NULL;
5414 return E_NOINTERFACE;
5417 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat3 *iface)
5419 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5420 ULONG refcount = InterlockedIncrement(&format->refcount);
5422 TRACE("%p, refcount %d.\n", iface, refcount);
5424 return refcount;
5427 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat3 *iface)
5429 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5430 ULONG refcount = InterlockedDecrement(&format->refcount);
5432 TRACE("%p, refcount %d.\n", iface, refcount);
5434 if (!refcount)
5436 release_format_data(&format->format);
5437 heap_free(format);
5440 return refcount;
5443 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
5445 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5447 TRACE("%p, %d.\n", iface, alignment);
5449 return format_set_textalignment(&format->format, alignment, NULL);
5452 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat3 *iface,
5453 DWRITE_PARAGRAPH_ALIGNMENT alignment)
5455 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5457 TRACE("%p, %d.\n", iface, alignment);
5459 return format_set_paralignment(&format->format, alignment, NULL);
5462 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat3 *iface, DWRITE_WORD_WRAPPING wrapping)
5464 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5466 TRACE("%p, %d.\n", iface, wrapping);
5468 return format_set_wordwrapping(&format->format, wrapping, NULL);
5471 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat3 *iface, DWRITE_READING_DIRECTION direction)
5473 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5475 TRACE("%p, %d.\n", iface, direction);
5477 return format_set_readingdirection(&format->format, direction, NULL);
5480 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat3 *iface, DWRITE_FLOW_DIRECTION direction)
5482 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5484 TRACE("%p, %d.\n", iface, direction);
5486 return format_set_flowdirection(&format->format, direction, NULL);
5489 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat3 *iface, FLOAT tabstop)
5491 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5493 TRACE("%p, %f.\n", iface, tabstop);
5495 if (tabstop <= 0.0f)
5496 return E_INVALIDARG;
5498 format->format.tabstop = tabstop;
5499 return S_OK;
5502 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat3 *iface, DWRITE_TRIMMING const *trimming,
5503 IDWriteInlineObject *trimming_sign)
5505 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5507 TRACE("%p, %p, %p.\n", iface, trimming, trimming_sign);
5509 return format_set_trimming(&format->format, trimming, trimming_sign, NULL);
5512 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat3 *iface, DWRITE_LINE_SPACING_METHOD method,
5513 FLOAT height, FLOAT baseline)
5515 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5516 DWRITE_LINE_SPACING spacing;
5518 TRACE("%p, %d, %f, %f.\n", iface, method, height, baseline);
5520 spacing = format->format.spacing;
5521 spacing.method = method;
5522 spacing.height = height;
5523 spacing.baseline = baseline;
5525 return format_set_linespacing(&format->format, &spacing, NULL);
5528 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat3 *iface)
5530 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5532 TRACE("%p.\n", iface);
5534 return format->format.textalignment;
5537 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat3 *iface)
5539 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5541 TRACE("%p.\n", iface);
5543 return format->format.paralign;
5546 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat3 *iface)
5548 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5550 TRACE("%p.\n", iface);
5552 return format->format.wrapping;
5555 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat3 *iface)
5557 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5559 TRACE("%p.\n", iface);
5561 return format->format.readingdir;
5564 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat3 *iface)
5566 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5568 TRACE("%p.\n", iface);
5570 return format->format.flow;
5573 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat3 *iface)
5575 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5577 TRACE("%p.\n", iface);
5579 return format->format.tabstop;
5582 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat3 *iface, DWRITE_TRIMMING *options,
5583 IDWriteInlineObject **trimming_sign)
5585 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5587 TRACE("%p, %p, %p.\n", iface, options, trimming_sign);
5589 *options = format->format.trimming;
5590 if ((*trimming_sign = format->format.trimmingsign))
5591 IDWriteInlineObject_AddRef(*trimming_sign);
5593 return S_OK;
5596 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat3 *iface, DWRITE_LINE_SPACING_METHOD *method,
5597 FLOAT *spacing, FLOAT *baseline)
5599 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5601 TRACE("%p, %p, %p, %p.\n", iface, method, spacing, baseline);
5603 *method = format->format.spacing.method;
5604 *spacing = format->format.spacing.height;
5605 *baseline = format->format.spacing.baseline;
5606 return S_OK;
5609 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat3 *iface, IDWriteFontCollection **collection)
5611 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5613 TRACE("%p, %p.\n", iface, collection);
5615 *collection = format->format.collection;
5616 IDWriteFontCollection_AddRef(*collection);
5618 return S_OK;
5621 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat3 *iface)
5623 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5625 TRACE("%p.\n", iface);
5627 return format->format.family_len;
5630 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat3 *iface, WCHAR *name, UINT32 size)
5632 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5634 TRACE("%p, %p, %u.\n", iface, name, size);
5636 if (size <= format->format.family_len)
5637 return E_NOT_SUFFICIENT_BUFFER;
5638 strcpyW(name, format->format.family_name);
5639 return S_OK;
5642 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat3 *iface)
5644 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5646 TRACE("%p.\n", iface);
5648 return format->format.weight;
5651 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat3 *iface)
5653 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5655 TRACE("%p.\n", iface);
5657 return format->format.style;
5660 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat3 *iface)
5662 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5664 TRACE("%p.\n", iface);
5666 return format->format.stretch;
5669 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat3 *iface)
5671 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5673 TRACE("%p.\n", iface);
5675 return format->format.fontsize;
5678 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat3 *iface)
5680 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5682 TRACE("%p.\n", iface);
5684 return format->format.locale_len;
5687 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat3 *iface, WCHAR *name, UINT32 size)
5689 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5691 TRACE("%p, %p %u.\n", iface, name, size);
5693 if (size <= format->format.locale_len)
5694 return E_NOT_SUFFICIENT_BUFFER;
5695 strcpyW(name, format->format.locale);
5696 return S_OK;
5699 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat3 *iface,
5700 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
5702 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5704 TRACE("%p, %d.\n", iface, orientation);
5706 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
5707 return E_INVALIDARG;
5709 format->format.vertical_orientation = orientation;
5710 return S_OK;
5713 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat3 *iface)
5715 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5717 TRACE("%p.\n", iface);
5719 return format->format.vertical_orientation;
5722 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat3 *iface, BOOL lastline_wrapping_enabled)
5724 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5726 TRACE("%p, %d.\n", iface, lastline_wrapping_enabled);
5728 format->format.last_line_wrapping = !!lastline_wrapping_enabled;
5729 return S_OK;
5732 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat3 *iface)
5734 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5736 TRACE("%p.\n", iface);
5738 return format->format.last_line_wrapping;
5741 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
5743 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5745 TRACE("%p, %d.\n", iface, alignment);
5747 return format_set_optical_alignment(&format->format, alignment);
5750 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat3 *iface)
5752 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5754 TRACE("%p.\n", iface);
5756 return format->format.optical_alignment;
5759 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat3 *iface, IDWriteFontFallback *fallback)
5761 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5763 TRACE("%p, %p.\n", iface, fallback);
5765 return set_fontfallback_for_format(&format->format, fallback);
5768 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat3 *iface, IDWriteFontFallback **fallback)
5770 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5772 TRACE("%p, %p.\n", iface, fallback);
5774 return get_fontfallback_from_format(&format->format, fallback);
5777 static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat3 *iface, DWRITE_LINE_SPACING const *spacing)
5779 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5781 TRACE("%p, %p.\n", iface, spacing);
5783 return format_set_linespacing(&format->format, spacing, NULL);
5786 static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat3 *iface, DWRITE_LINE_SPACING *spacing)
5788 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5790 TRACE("%p, %p.\n", iface, spacing);
5792 *spacing = format->format.spacing;
5793 return S_OK;
5796 static HRESULT WINAPI dwritetextformat3_SetFontAxisValues(IDWriteTextFormat3 *iface,
5797 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 num_values)
5799 FIXME("%p, %p, %u.\n", iface, axis_values, num_values);
5801 return E_NOTIMPL;
5804 static UINT32 WINAPI dwritetextformat3_GetFontAxisValueCount(IDWriteTextFormat3 *iface)
5806 FIXME("%p.\n", iface);
5808 return 0;
5811 static HRESULT WINAPI dwritetextformat3_GetFontAxisValues(IDWriteTextFormat3 *iface,
5812 DWRITE_FONT_AXIS_VALUE const *axis_values, UINT32 num_values)
5814 FIXME("%p, %p, %u.\n", iface, axis_values, num_values);
5816 return E_NOTIMPL;
5819 static DWRITE_AUTOMATIC_FONT_AXES WINAPI dwritetextformat3_GetAutomaticFontAxes(IDWriteTextFormat3 *iface)
5821 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5823 TRACE("%p.\n", iface);
5825 return format->format.automatic_axes;
5828 static HRESULT WINAPI dwritetextformat3_SetAutomaticFontAxes(IDWriteTextFormat3 *iface, DWRITE_AUTOMATIC_FONT_AXES axes)
5830 struct dwrite_textformat *format = impl_from_IDWriteTextFormat3(iface);
5832 TRACE("%p, %d.\n", iface, axes);
5834 format->format.automatic_axes = axes;
5836 return S_OK;
5839 static const IDWriteTextFormat3Vtbl dwritetextformatvtbl =
5841 dwritetextformat_QueryInterface,
5842 dwritetextformat_AddRef,
5843 dwritetextformat_Release,
5844 dwritetextformat_SetTextAlignment,
5845 dwritetextformat_SetParagraphAlignment,
5846 dwritetextformat_SetWordWrapping,
5847 dwritetextformat_SetReadingDirection,
5848 dwritetextformat_SetFlowDirection,
5849 dwritetextformat_SetIncrementalTabStop,
5850 dwritetextformat_SetTrimming,
5851 dwritetextformat_SetLineSpacing,
5852 dwritetextformat_GetTextAlignment,
5853 dwritetextformat_GetParagraphAlignment,
5854 dwritetextformat_GetWordWrapping,
5855 dwritetextformat_GetReadingDirection,
5856 dwritetextformat_GetFlowDirection,
5857 dwritetextformat_GetIncrementalTabStop,
5858 dwritetextformat_GetTrimming,
5859 dwritetextformat_GetLineSpacing,
5860 dwritetextformat_GetFontCollection,
5861 dwritetextformat_GetFontFamilyNameLength,
5862 dwritetextformat_GetFontFamilyName,
5863 dwritetextformat_GetFontWeight,
5864 dwritetextformat_GetFontStyle,
5865 dwritetextformat_GetFontStretch,
5866 dwritetextformat_GetFontSize,
5867 dwritetextformat_GetLocaleNameLength,
5868 dwritetextformat_GetLocaleName,
5869 dwritetextformat1_SetVerticalGlyphOrientation,
5870 dwritetextformat1_GetVerticalGlyphOrientation,
5871 dwritetextformat1_SetLastLineWrapping,
5872 dwritetextformat1_GetLastLineWrapping,
5873 dwritetextformat1_SetOpticalAlignment,
5874 dwritetextformat1_GetOpticalAlignment,
5875 dwritetextformat1_SetFontFallback,
5876 dwritetextformat1_GetFontFallback,
5877 dwritetextformat2_SetLineSpacing,
5878 dwritetextformat2_GetLineSpacing,
5879 dwritetextformat3_SetFontAxisValues,
5880 dwritetextformat3_GetFontAxisValueCount,
5881 dwritetextformat3_GetFontAxisValues,
5882 dwritetextformat3_GetAutomaticFontAxes,
5883 dwritetextformat3_SetAutomaticFontAxes,
5886 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
5888 return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
5889 CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat3_iface) : NULL;
5892 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight,
5893 DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
5895 struct dwrite_textformat *object;
5897 *format = NULL;
5899 if (size <= 0.0f)
5900 return E_INVALIDARG;
5902 if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
5903 ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
5904 ((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
5905 return E_INVALIDARG;
5907 if (!(object = heap_alloc_zero(sizeof(*object))))
5908 return E_OUTOFMEMORY;
5910 object->IDWriteTextFormat3_iface.lpVtbl = &dwritetextformatvtbl;
5911 object->refcount = 1;
5912 object->format.family_name = heap_strdupW(family_name);
5913 object->format.family_len = strlenW(family_name);
5914 object->format.locale = heap_strdupW(locale);
5915 object->format.locale_len = strlenW(locale);
5916 /* Force locale name to lower case, layout will inherit this modified value. */
5917 strlwrW(object->format.locale);
5918 object->format.weight = weight;
5919 object->format.style = style;
5920 object->format.fontsize = size;
5921 object->format.tabstop = 4.0f * size;
5922 object->format.stretch = stretch;
5923 object->format.last_line_wrapping = TRUE;
5924 object->format.collection = collection;
5925 IDWriteFontCollection_AddRef(object->format.collection);
5927 *format = (IDWriteTextFormat *)&object->IDWriteTextFormat3_iface;
5929 return S_OK;
5932 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
5934 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
5936 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
5937 *obj = iface;
5938 IDWriteTypography_AddRef(iface);
5939 return S_OK;
5942 WARN("%s not implemented.\n", debugstr_guid(riid));
5944 *obj = NULL;
5946 return E_NOINTERFACE;
5949 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
5951 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5952 ULONG refcount = InterlockedIncrement(&typography->refcount);
5954 TRACE("%p, refcount %d.\n", iface, refcount);
5956 return refcount;
5959 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
5961 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5962 ULONG refcount = InterlockedDecrement(&typography->refcount);
5964 TRACE("%p, refcount %d.\n", iface, refcount);
5966 if (!refcount)
5968 heap_free(typography->features);
5969 heap_free(typography);
5972 return refcount;
5975 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
5977 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5979 TRACE("%p, %s, %u.\n", iface, debugstr_tag(feature.nameTag), feature.parameter);
5981 if (!dwrite_array_reserve((void **)&typography->features, &typography->capacity, typography->count + 1,
5982 sizeof(*typography->features)))
5984 return E_OUTOFMEMORY;
5987 typography->features[typography->count++] = feature;
5989 return S_OK;
5992 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
5994 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5996 TRACE("%p.\n", iface);
5998 return typography->count;
6001 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index,
6002 DWRITE_FONT_FEATURE *feature)
6004 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
6006 TRACE("%p, %u, %p.\n", iface, index, feature);
6008 if (index >= typography->count)
6009 return E_INVALIDARG;
6011 *feature = typography->features[index];
6012 return S_OK;
6015 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
6016 dwritetypography_QueryInterface,
6017 dwritetypography_AddRef,
6018 dwritetypography_Release,
6019 dwritetypography_AddFontFeature,
6020 dwritetypography_GetFontFeatureCount,
6021 dwritetypography_GetFontFeature
6024 HRESULT create_typography(IDWriteTypography **ret)
6026 struct dwrite_typography *typography;
6028 *ret = NULL;
6030 typography = heap_alloc_zero(sizeof(*typography));
6031 if (!typography)
6032 return E_OUTOFMEMORY;
6034 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
6035 typography->refcount = 1;
6037 *ret = &typography->IDWriteTypography_iface;
6039 return S_OK;