dwrite: Added support for uniform and proportional spacing methods.
[wine.git] / dlls / dwrite / layout.c
blob08d3aba798b792dc242e16b3998b8cb7c9c9616a
1 /*
2 * Text format and layout
4 * Copyright 2012, 2014-2016 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 <stdarg.h>
24 #include <math.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wingdi.h"
29 #include "dwrite_private.h"
30 #include "scripts.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
34 struct dwrite_textformat_data {
35 WCHAR *family_name;
36 UINT32 family_len;
37 WCHAR *locale;
38 UINT32 locale_len;
40 DWRITE_FONT_WEIGHT weight;
41 DWRITE_FONT_STYLE style;
42 DWRITE_FONT_STRETCH stretch;
44 DWRITE_PARAGRAPH_ALIGNMENT paralign;
45 DWRITE_READING_DIRECTION readingdir;
46 DWRITE_WORD_WRAPPING wrapping;
47 BOOL last_line_wrapping;
48 DWRITE_TEXT_ALIGNMENT textalignment;
49 DWRITE_FLOW_DIRECTION flow;
50 DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
51 DWRITE_OPTICAL_ALIGNMENT optical_alignment;
52 DWRITE_LINE_SPACING spacing;
54 FLOAT fontsize;
56 DWRITE_TRIMMING trimming;
57 IDWriteInlineObject *trimmingsign;
59 IDWriteFontCollection *collection;
60 IDWriteFontFallback *fallback;
63 enum layout_range_attr_kind {
64 LAYOUT_RANGE_ATTR_WEIGHT,
65 LAYOUT_RANGE_ATTR_STYLE,
66 LAYOUT_RANGE_ATTR_STRETCH,
67 LAYOUT_RANGE_ATTR_FONTSIZE,
68 LAYOUT_RANGE_ATTR_EFFECT,
69 LAYOUT_RANGE_ATTR_INLINE,
70 LAYOUT_RANGE_ATTR_UNDERLINE,
71 LAYOUT_RANGE_ATTR_STRIKETHROUGH,
72 LAYOUT_RANGE_ATTR_PAIR_KERNING,
73 LAYOUT_RANGE_ATTR_FONTCOLL,
74 LAYOUT_RANGE_ATTR_LOCALE,
75 LAYOUT_RANGE_ATTR_FONTFAMILY,
76 LAYOUT_RANGE_ATTR_SPACING,
77 LAYOUT_RANGE_ATTR_TYPOGRAPHY
80 struct layout_range_attr_value {
81 DWRITE_TEXT_RANGE range;
82 union {
83 DWRITE_FONT_WEIGHT weight;
84 DWRITE_FONT_STYLE style;
85 DWRITE_FONT_STRETCH stretch;
86 FLOAT fontsize;
87 IDWriteInlineObject *object;
88 IUnknown *effect;
89 BOOL underline;
90 BOOL strikethrough;
91 BOOL pair_kerning;
92 IDWriteFontCollection *collection;
93 const WCHAR *locale;
94 const WCHAR *fontfamily;
95 FLOAT spacing[3]; /* in arguments order - leading, trailing, advance */
96 IDWriteTypography *typography;
97 } u;
100 enum layout_range_kind {
101 LAYOUT_RANGE_REGULAR,
102 LAYOUT_RANGE_UNDERLINE,
103 LAYOUT_RANGE_STRIKETHROUGH,
104 LAYOUT_RANGE_EFFECT,
105 LAYOUT_RANGE_SPACING,
106 LAYOUT_RANGE_TYPOGRAPHY
109 struct layout_range_header {
110 struct list entry;
111 enum layout_range_kind kind;
112 DWRITE_TEXT_RANGE range;
115 struct layout_range {
116 struct layout_range_header h;
117 DWRITE_FONT_WEIGHT weight;
118 DWRITE_FONT_STYLE style;
119 FLOAT fontsize;
120 DWRITE_FONT_STRETCH stretch;
121 IDWriteInlineObject *object;
122 BOOL pair_kerning;
123 IDWriteFontCollection *collection;
124 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
125 WCHAR *fontfamily;
128 struct layout_range_bool {
129 struct layout_range_header h;
130 BOOL value;
133 struct layout_range_iface {
134 struct layout_range_header h;
135 IUnknown *iface;
138 struct layout_range_spacing {
139 struct layout_range_header h;
140 FLOAT leading;
141 FLOAT trailing;
142 FLOAT min_advance;
145 enum layout_run_kind {
146 LAYOUT_RUN_REGULAR,
147 LAYOUT_RUN_INLINE
150 struct inline_object_run {
151 IDWriteInlineObject *object;
152 UINT16 length;
155 struct regular_layout_run {
156 DWRITE_GLYPH_RUN_DESCRIPTION descr;
157 DWRITE_GLYPH_RUN run;
158 DWRITE_SCRIPT_ANALYSIS sa;
159 UINT16 *glyphs;
160 UINT16 *clustermap;
161 FLOAT *advances;
162 DWRITE_GLYPH_OFFSET *offsets;
163 /* this is actual glyph count after shaping, it's not necessary the same as reported to Draw() */
164 UINT32 glyphcount;
167 struct layout_run {
168 struct list entry;
169 enum layout_run_kind kind;
170 union {
171 struct inline_object_run object;
172 struct regular_layout_run regular;
173 } u;
174 FLOAT baseline;
175 FLOAT height;
178 struct layout_effective_run {
179 struct list entry;
180 const struct layout_run *run; /* nominal run this one is based on */
181 UINT32 start; /* relative text position, 0 means first text position of a nominal run */
182 UINT32 length; /* length in codepoints that this run covers */
183 UINT32 glyphcount; /* total glyph count in this run */
184 IUnknown *effect; /* original reference is kept only at range level */
185 FLOAT origin_x; /* baseline X position */
186 FLOAT origin_y; /* baseline Y position */
187 FLOAT align_dx; /* adjustment from text alignment */
188 FLOAT width; /* run width */
189 UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
190 UINT32 line; /* 0-based line index in line metrics array */
191 BOOL underlined; /* set if this run is underlined */
194 struct layout_effective_inline {
195 struct list entry;
196 IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */
197 IUnknown *effect; /* original reference is kept only at range level */
198 FLOAT baseline;
199 FLOAT origin_x; /* left X position */
200 FLOAT origin_y; /* left top corner Y position */
201 FLOAT align_dx; /* adjustment from text alignment */
202 FLOAT width; /* object width as it's reported it */
203 BOOL is_sideways; /* vertical flow direction flag passed to Draw */
204 BOOL is_rtl; /* bidi flag passed to Draw */
205 UINT32 line; /* 0-based line index in line metrics array */
208 struct layout_underline {
209 struct list entry;
210 const struct layout_effective_run *run;
211 DWRITE_UNDERLINE u;
214 struct layout_strikethrough {
215 struct list entry;
216 const struct layout_effective_run *run;
217 DWRITE_STRIKETHROUGH s;
220 struct layout_cluster {
221 const struct layout_run *run; /* link to nominal run this cluster belongs to */
222 UINT32 position; /* relative to run, first cluster has 0 position */
225 struct layout_line {
226 FLOAT height; /* height based on content */
227 FLOAT baseline; /* baseline based on content */
230 enum layout_recompute_mask {
231 RECOMPUTE_CLUSTERS = 1 << 0,
232 RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
233 RECOMPUTE_LINES = 1 << 2,
234 RECOMPUTE_OVERHANGS = 1 << 3,
235 RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS,
236 RECOMPUTE_EVERYTHING = 0xffff
239 struct dwrite_textlayout {
240 IDWriteTextLayout3 IDWriteTextLayout3_iface;
241 IDWriteTextFormat1 IDWriteTextFormat1_iface;
242 IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface;
243 IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface;
244 LONG ref;
246 IDWriteFactory4 *factory;
248 WCHAR *str;
249 UINT32 len;
250 struct dwrite_textformat_data format;
251 struct list strike_ranges;
252 struct list underline_ranges;
253 struct list typographies;
254 struct list effects;
255 struct list spacing;
256 struct list ranges;
257 struct list runs;
258 /* lists ready to use by Draw() */
259 struct list eruns;
260 struct list inlineobjects;
261 struct list underlines;
262 struct list strikethrough;
263 USHORT recompute;
265 DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
266 DWRITE_LINE_BREAKPOINT *actual_breakpoints;
268 struct layout_cluster *clusters;
269 DWRITE_CLUSTER_METRICS *clustermetrics;
270 UINT32 cluster_count;
271 FLOAT minwidth;
273 struct layout_line *lines;
274 DWRITE_LINE_METRICS1 *linemetrics;
275 UINT32 line_alloc;
277 DWRITE_TEXT_METRICS1 metrics;
278 DWRITE_OVERHANG_METRICS overhangs;
280 DWRITE_MEASURING_MODE measuringmode;
282 /* gdi-compatible layout specifics */
283 FLOAT ppdip;
284 DWRITE_MATRIX transform;
287 struct dwrite_textformat {
288 IDWriteTextFormat2 IDWriteTextFormat2_iface;
289 LONG ref;
290 struct dwrite_textformat_data format;
293 struct dwrite_trimmingsign {
294 IDWriteInlineObject IDWriteInlineObject_iface;
295 LONG ref;
297 IDWriteTextLayout *layout;
300 struct dwrite_typography {
301 IDWriteTypography IDWriteTypography_iface;
302 LONG ref;
304 DWRITE_FONT_FEATURE *features;
305 UINT32 allocated;
306 UINT32 count;
309 struct dwrite_vec {
310 FLOAT x;
311 FLOAT y;
314 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl;
316 static void release_format_data(struct dwrite_textformat_data *data)
318 if (data->collection) IDWriteFontCollection_Release(data->collection);
319 if (data->fallback) IDWriteFontFallback_Release(data->fallback);
320 if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
321 heap_free(data->family_name);
322 heap_free(data->locale);
325 static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout3(IDWriteTextLayout3 *iface)
327 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout3_iface);
330 static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
332 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface);
335 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface)
337 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface);
340 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface)
342 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface);
345 static inline struct dwrite_textformat *impl_from_IDWriteTextFormat2(IDWriteTextFormat2 *iface)
347 return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface);
350 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*);
352 static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
354 return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
357 static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
359 return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
362 static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr)
364 return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength);
367 static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
369 return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
372 static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
373 BOOL *changed)
375 if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
376 return E_INVALIDARG;
377 if (changed) *changed = format->textalignment != alignment;
378 format->textalignment = alignment;
379 return S_OK;
382 static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
383 DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
385 if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
386 return E_INVALIDARG;
387 if (changed) *changed = format->paralign != alignment;
388 format->paralign = alignment;
389 return S_OK;
392 static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
393 DWRITE_READING_DIRECTION direction, BOOL *changed)
395 if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
396 return E_INVALIDARG;
397 if (changed) *changed = format->readingdir != direction;
398 format->readingdir = direction;
399 return S_OK;
402 static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
403 DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
405 if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
406 return E_INVALIDARG;
407 if (changed) *changed = format->wrapping != wrapping;
408 format->wrapping = wrapping;
409 return S_OK;
412 static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format,
413 DWRITE_FLOW_DIRECTION direction, BOOL *changed)
415 if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
416 return E_INVALIDARG;
417 if (changed) *changed = format->flow != direction;
418 format->flow = direction;
419 return S_OK;
422 static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format,
423 DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed)
425 if (changed)
426 *changed = FALSE;
428 if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD)
429 return E_INVALIDARG;
431 if (changed) {
432 *changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming));
433 if (format->trimmingsign != trimming_sign)
434 *changed = TRUE;
437 format->trimming = *trimming;
438 if (format->trimmingsign)
439 IDWriteInlineObject_Release(format->trimmingsign);
440 format->trimmingsign = trimming_sign;
441 if (format->trimmingsign)
442 IDWriteInlineObject_AddRef(format->trimmingsign);
443 return S_OK;
446 static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format,
447 DWRITE_LINE_SPACING const *spacing, BOOL *changed)
449 if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f ||
450 (UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL)
451 return E_INVALIDARG;
453 if (changed)
454 *changed = memcmp(spacing, &format->spacing, sizeof(*spacing));
456 format->spacing = *spacing;
457 return S_OK;
460 static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
462 *fallback = format->fallback;
463 if (*fallback)
464 IDWriteFontFallback_AddRef(*fallback);
465 return S_OK;
468 static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
470 if (format->fallback)
471 IDWriteFontFallback_Release(format->fallback);
472 format->fallback = fallback;
473 if (fallback)
474 IDWriteFontFallback_AddRef(fallback);
475 return S_OK;
478 static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format,
479 DWRITE_OPTICAL_ALIGNMENT alignment)
481 if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS)
482 return E_INVALIDARG;
483 format->optical_alignment = alignment;
484 return S_OK;
487 static BOOL is_run_rtl(const struct layout_effective_run *run)
489 return run->run->u.regular.run.bidiLevel & 1;
492 static struct layout_run *alloc_layout_run(enum layout_run_kind kind)
494 struct layout_run *ret;
496 ret = heap_alloc(sizeof(*ret));
497 if (!ret) return NULL;
499 memset(ret, 0, sizeof(*ret));
500 ret->kind = kind;
501 if (kind == LAYOUT_RUN_REGULAR) {
502 ret->u.regular.sa.script = Script_Unknown;
503 ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
506 return ret;
509 static void free_layout_runs(struct dwrite_textlayout *layout)
511 struct layout_run *cur, *cur2;
512 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
513 list_remove(&cur->entry);
514 if (cur->kind == LAYOUT_RUN_REGULAR) {
515 if (cur->u.regular.run.fontFace)
516 IDWriteFontFace_Release(cur->u.regular.run.fontFace);
517 heap_free(cur->u.regular.glyphs);
518 heap_free(cur->u.regular.clustermap);
519 heap_free(cur->u.regular.advances);
520 heap_free(cur->u.regular.offsets);
522 heap_free(cur);
526 static void free_layout_eruns(struct dwrite_textlayout *layout)
528 struct layout_effective_inline *in, *in2;
529 struct layout_effective_run *cur, *cur2;
530 struct layout_strikethrough *s, *s2;
531 struct layout_underline *u, *u2;
533 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
534 list_remove(&cur->entry);
535 heap_free(cur->clustermap);
536 heap_free(cur);
539 LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
540 list_remove(&in->entry);
541 heap_free(in);
544 LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) {
545 list_remove(&u->entry);
546 heap_free(u);
549 LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
550 list_remove(&s->entry);
551 heap_free(s);
555 /* Used to resolve break condition by forcing stronger condition over weaker. */
556 static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
558 switch (existingbreak) {
559 case DWRITE_BREAK_CONDITION_NEUTRAL:
560 return newbreak;
561 case DWRITE_BREAK_CONDITION_CAN_BREAK:
562 return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
563 /* let's keep stronger conditions as is */
564 case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
565 case DWRITE_BREAK_CONDITION_MUST_BREAK:
566 break;
567 default:
568 ERR("unknown break condition %d\n", existingbreak);
571 return existingbreak;
574 /* This helper should be used to get effective range length, in other words it returns number of text
575 positions from range starting point to the end of the range, limited by layout text length */
576 static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
578 if (range->h.range.startPosition + range->h.range.length <= layout->len)
579 return range->h.range.length;
580 return layout->len - range->h.range.startPosition;
583 /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
584 static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
586 DWRITE_BREAK_CONDITION before, after;
587 UINT32 i, length;
588 HRESULT hr;
590 /* ignore returned conditions if failed */
591 hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
592 if (FAILED(hr))
593 after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
595 if (!layout->actual_breakpoints) {
596 layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
597 if (!layout->actual_breakpoints)
598 return E_OUTOFMEMORY;
599 memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
602 length = get_clipped_range_length(layout, cur);
603 for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
604 /* for first codepoint check if there's anything before it and update accordingly */
605 if (i == cur->h.range.startPosition) {
606 if (i > 0)
607 layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
608 override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
609 else
610 layout->actual_breakpoints[i].breakConditionBefore = before;
611 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
613 /* similar check for last codepoint */
614 else if (i == cur->h.range.startPosition + length - 1) {
615 if (i == layout->len - 1)
616 layout->actual_breakpoints[i].breakConditionAfter = after;
617 else
618 layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
619 override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
620 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
622 /* for all positions within a range disable breaks */
623 else {
624 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
625 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
628 layout->actual_breakpoints[i].isWhitespace = 0;
629 layout->actual_breakpoints[i].isSoftHyphen = 0;
632 return S_OK;
635 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
637 static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
639 if (layout->actual_breakpoints)
640 return layout->actual_breakpoints[pos];
641 return layout->nominal_breakpoints[pos];
644 static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
645 UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
647 UINT8 breakcondition;
648 UINT32 position;
649 UINT16 j;
651 /* For clusters made of control chars we report zero glyphs, and we need zero cluster
652 width as well; advances are already computed at this point and are not necessary zero. */
653 metrics->width = 0.0f;
654 if (run->run.glyphCount) {
655 for (j = start_glyph; j < stop_glyph; j++)
656 metrics->width += run->run.glyphAdvances[j];
658 metrics->length = length;
660 position = run->descr.textPosition + stop_position;
661 if (stop_glyph == run->glyphcount)
662 breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter;
663 else {
664 breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore;
665 if (stop_position) position -= 1;
668 metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
669 breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
670 if (metrics->length == 1) {
671 DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position);
672 metrics->isWhitespace = bp.isWhitespace;
673 metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]);
674 metrics->isSoftHyphen = bp.isSoftHyphen;
676 else {
677 metrics->isWhitespace = 0;
678 metrics->isNewline = 0;
679 metrics->isSoftHyphen = 0;
681 metrics->isRightToLeft = run->run.bidiLevel & 1;
682 metrics->padding = 0;
687 All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
688 On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
689 Note that there's no need to reallocate anything at this point as we allocate one cluster per
690 codepoint initially.
693 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
695 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
696 struct layout_cluster *c = &layout->clusters[*cluster];
697 const struct regular_layout_run *run = &r->u.regular;
698 UINT32 i, start = 0;
700 for (i = 0; i < run->descr.stringLength; i++) {
701 BOOL end = i == run->descr.stringLength - 1;
703 if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
704 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
705 i - start, metrics);
706 c->position = start;
707 c->run = r;
709 *cluster += 1;
710 metrics++;
711 c++;
712 start = i;
715 if (end) {
716 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
717 i - start + 1, metrics);
718 c->position = start;
719 c->run = r;
721 *cluster += 1;
722 return;
727 #define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm)
729 static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize,
730 DWRITE_FONT_METRICS *fontmetrics)
732 if (is_layout_gdi_compatible(layout)) {
733 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics);
734 if (FAILED(hr))
735 WARN("failed to get compat metrics, 0x%08x\n", hr);
737 else
738 IDWriteFontFace_GetMetrics(fontface, fontmetrics);
741 static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height)
743 *baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics);
744 *height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
747 static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
749 IDWriteFontFallback *fallback;
750 IDWriteTextAnalyzer *analyzer;
751 struct layout_range *range;
752 struct layout_run *r;
753 UINT32 cluster = 0;
754 HRESULT hr;
756 free_layout_eruns(layout);
757 free_layout_runs(layout);
759 /* Cluster data arrays are allocated once, assuming one text position per cluster. */
760 if (!layout->clustermetrics && layout->len) {
761 layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics));
762 layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters));
763 if (!layout->clustermetrics || !layout->clusters) {
764 heap_free(layout->clustermetrics);
765 heap_free(layout->clusters);
766 return E_OUTOFMEMORY;
769 layout->cluster_count = 0;
771 hr = get_textanalyzer(&analyzer);
772 if (FAILED(hr))
773 return hr;
775 LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
776 /* we don't care about ranges that don't contain any text */
777 if (range->h.range.startPosition >= layout->len)
778 break;
780 /* inline objects override actual text in a range */
781 if (range->object) {
782 hr = layout_update_breakpoints_range(layout, range);
783 if (FAILED(hr))
784 return hr;
786 r = alloc_layout_run(LAYOUT_RUN_INLINE);
787 if (!r)
788 return E_OUTOFMEMORY;
790 r->u.object.object = range->object;
791 r->u.object.length = get_clipped_range_length(layout, range);
792 list_add_tail(&layout->runs, &r->entry);
793 continue;
796 /* initial splitting by script */
797 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
798 range->h.range.startPosition, get_clipped_range_length(layout, range), (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
799 if (FAILED(hr))
800 break;
802 /* this splits it further */
803 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
804 range->h.range.startPosition, get_clipped_range_length(layout, range), (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
805 if (FAILED(hr))
806 break;
809 if (layout->format.fallback) {
810 fallback = layout->format.fallback;
811 IDWriteFontFallback_AddRef(fallback);
813 else {
814 hr = IDWriteFactory4_GetSystemFontFallback(layout->factory, &fallback);
815 if (FAILED(hr))
816 return hr;
819 /* resolve run fonts */
820 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
821 struct regular_layout_run *run = &r->u.regular;
822 IDWriteFont *font;
823 UINT32 length;
825 if (r->kind == LAYOUT_RUN_INLINE)
826 continue;
828 range = get_layout_range_by_pos(layout, run->descr.textPosition);
830 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
831 IDWriteFontCollection *collection;
833 if (range->collection) {
834 collection = range->collection;
835 IDWriteFontCollection_AddRef(collection);
837 else
838 IDWriteFactory4_GetSystemFontCollection(layout->factory, FALSE, (IDWriteFontCollection1**)&collection, FALSE);
840 hr = create_matching_font(collection, range->fontfamily, range->weight,
841 range->style, range->stretch, &font);
843 IDWriteFontCollection_Release(collection);
845 if (FAILED(hr)) {
846 WARN("%s: failed to create a font for non visual run, %s, collection %p\n", debugstr_rundescr(&run->descr),
847 debugstr_w(range->fontfamily), range->collection);
848 return hr;
851 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
852 IDWriteFont_Release(font);
853 if (FAILED(hr))
854 return hr;
856 run->run.fontEmSize = range->fontsize;
857 continue;
860 length = run->descr.stringLength;
862 while (length) {
863 UINT32 mapped_length;
864 FLOAT scale;
866 run = &r->u.regular;
868 hr = IDWriteFontFallback_MapCharacters(fallback,
869 (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
870 run->descr.textPosition,
871 run->descr.stringLength,
872 range->collection,
873 range->fontfamily,
874 range->weight,
875 range->style,
876 range->stretch,
877 &mapped_length,
878 &font,
879 &scale);
880 if (FAILED(hr)) {
881 WARN("%s: failed to map family %s, collection %p\n", debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
882 return hr;
885 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
886 IDWriteFont_Release(font);
887 if (FAILED(hr))
888 return hr;
889 run->run.fontEmSize = range->fontsize * scale;
891 if (mapped_length < length) {
892 struct regular_layout_run *nextrun;
893 struct layout_run *nextr;
895 /* keep mapped part for current run, add another run for the rest */
896 nextr = alloc_layout_run(LAYOUT_RUN_REGULAR);
897 if (!nextr)
898 return E_OUTOFMEMORY;
900 *nextr = *r;
901 nextrun = &nextr->u.regular;
902 nextrun->descr.textPosition = run->descr.textPosition + mapped_length;
903 nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
904 nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
905 run->descr.stringLength = mapped_length;
906 list_add_after(&r->entry, &nextr->entry);
907 r = nextr;
910 length -= mapped_length;
914 IDWriteFontFallback_Release(fallback);
916 /* fill run info */
917 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
918 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props = NULL;
919 DWRITE_SHAPING_TEXT_PROPERTIES *text_props = NULL;
920 struct regular_layout_run *run = &r->u.regular;
921 DWRITE_FONT_METRICS fontmetrics = { 0 };
922 UINT32 max_count;
924 /* we need to do very little in case of inline objects */
925 if (r->kind == LAYOUT_RUN_INLINE) {
926 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster];
927 struct layout_cluster *c = &layout->clusters[cluster];
928 DWRITE_INLINE_OBJECT_METRICS inlinemetrics;
930 metrics->width = 0.0f;
931 metrics->length = r->u.object.length;
932 metrics->canWrapLineAfter = 0;
933 metrics->isWhitespace = 0;
934 metrics->isNewline = 0;
935 metrics->isSoftHyphen = 0;
936 metrics->isRightToLeft = 0;
937 metrics->padding = 0;
938 c->run = r;
939 c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */
940 cluster++;
942 /* it's not fatal if GetMetrics() fails, all returned metrics are ignored */
943 hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics);
944 if (FAILED(hr)) {
945 memset(&inlinemetrics, 0, sizeof(inlinemetrics));
946 hr = S_OK;
948 metrics->width = inlinemetrics.width;
949 r->baseline = inlinemetrics.baseline;
950 r->height = inlinemetrics.height;
952 /* FIXME: use resolved breakpoints in this case too */
954 continue;
957 range = get_layout_range_by_pos(layout, run->descr.textPosition);
958 run->descr.localeName = range->locale;
959 run->clustermap = heap_alloc(run->descr.stringLength*sizeof(UINT16));
961 max_count = 3*run->descr.stringLength/2 + 16;
962 run->glyphs = heap_alloc(max_count*sizeof(UINT16));
963 if (!run->clustermap || !run->glyphs)
964 goto memerr;
966 text_props = heap_alloc(run->descr.stringLength*sizeof(DWRITE_SHAPING_TEXT_PROPERTIES));
967 glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
968 if (!text_props || !glyph_props)
969 goto memerr;
971 while (1) {
972 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength,
973 run->run.fontFace, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
974 NULL /* FIXME */, NULL, NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props,
975 &run->glyphcount);
976 if (hr == E_NOT_SUFFICIENT_BUFFER) {
977 heap_free(run->glyphs);
978 heap_free(glyph_props);
980 max_count = run->glyphcount;
982 run->glyphs = heap_alloc(max_count*sizeof(UINT16));
983 glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
984 if (!run->glyphs || !glyph_props)
985 goto memerr;
987 continue;
990 break;
993 if (FAILED(hr)) {
994 heap_free(text_props);
995 heap_free(glyph_props);
996 WARN("%s: shaping failed 0x%08x\n", debugstr_rundescr(&run->descr), hr);
997 continue;
1000 run->run.glyphIndices = run->glyphs;
1001 run->descr.clusterMap = run->clustermap;
1003 run->advances = heap_alloc(run->glyphcount*sizeof(FLOAT));
1004 run->offsets = heap_alloc(run->glyphcount*sizeof(DWRITE_GLYPH_OFFSET));
1005 if (!run->advances || !run->offsets)
1006 goto memerr;
1008 /* now set advances and offsets */
1009 if (is_layout_gdi_compatible(layout))
1010 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
1011 text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
1012 run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
1013 layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways,
1014 run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
1015 else
1016 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
1017 run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
1018 run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
1019 NULL, NULL, 0, run->advances, run->offsets);
1021 heap_free(text_props);
1022 heap_free(glyph_props);
1023 if (FAILED(hr))
1024 WARN("%s: failed to get glyph placement info, 0x%08x\n", debugstr_rundescr(&run->descr), hr);
1026 run->run.glyphAdvances = run->advances;
1027 run->run.glyphOffsets = run->offsets;
1029 /* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
1030 with valid cluster map and potentially with non-zero advances; layout code exposes those as zero width clusters. */
1031 if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
1032 run->run.glyphCount = 0;
1033 else
1034 run->run.glyphCount = run->glyphcount;
1036 /* baseline derived from font metrics */
1037 layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
1038 layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
1040 layout_set_cluster_metrics(layout, r, &cluster);
1041 continue;
1043 memerr:
1044 heap_free(text_props);
1045 heap_free(glyph_props);
1046 heap_free(run->clustermap);
1047 heap_free(run->glyphs);
1048 heap_free(run->advances);
1049 heap_free(run->offsets);
1050 run->advances = NULL;
1051 run->offsets = NULL;
1052 run->clustermap = run->glyphs = NULL;
1053 hr = E_OUTOFMEMORY;
1054 break;
1057 if (hr == S_OK) {
1058 layout->cluster_count = cluster;
1059 if (cluster)
1060 layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
1063 IDWriteTextAnalyzer_Release(analyzer);
1064 return hr;
1067 static HRESULT layout_compute(struct dwrite_textlayout *layout)
1069 HRESULT hr;
1071 if (!(layout->recompute & RECOMPUTE_CLUSTERS))
1072 return S_OK;
1074 /* nominal breakpoints are evaluated only once, because string never changes */
1075 if (!layout->nominal_breakpoints) {
1076 IDWriteTextAnalyzer *analyzer;
1077 HRESULT hr;
1079 layout->nominal_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
1080 if (!layout->nominal_breakpoints)
1081 return E_OUTOFMEMORY;
1083 hr = get_textanalyzer(&analyzer);
1084 if (FAILED(hr))
1085 return hr;
1087 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, (IDWriteTextAnalysisSource*)&layout->IDWriteTextAnalysisSource1_iface,
1088 0, layout->len, (IDWriteTextAnalysisSink*)&layout->IDWriteTextAnalysisSink1_iface);
1089 IDWriteTextAnalyzer_Release(analyzer);
1091 if (layout->actual_breakpoints) {
1092 heap_free(layout->actual_breakpoints);
1093 layout->actual_breakpoints = NULL;
1096 hr = layout_compute_runs(layout);
1098 if (TRACE_ON(dwrite)) {
1099 struct layout_run *cur;
1101 LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) {
1102 if (cur->kind == LAYOUT_RUN_INLINE)
1103 TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length);
1104 else
1105 TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition +
1106 cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel);
1110 layout->recompute &= ~RECOMPUTE_CLUSTERS;
1111 return hr;
1114 static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end)
1116 FLOAT width = 0.0f;
1117 for (; start < end; start++)
1118 width += layout->clustermetrics[start].width;
1119 return width;
1122 static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos)
1124 struct layout_range_header *cur;
1126 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
1127 DWRITE_TEXT_RANGE *r = &cur->range;
1128 if (r->startPosition <= pos && pos < r->startPosition + r->length)
1129 return cur;
1132 return NULL;
1135 static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1137 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos);
1138 return ((struct layout_range_iface*)h)->iface;
1141 static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun)
1143 return erun->run->u.regular.run.bidiLevel & 1;
1146 /* A set of parameters that additionally splits resulting runs. It happens after shaping and all text processing,
1147 no glyph changes are possible. It's understandable for drawing effects, because DrawGlyphRun() reports them as
1148 one of the arguments, but it also happens for decorations, so every effective run has uniform
1149 underline/strikethough/effect tuple. */
1150 struct layout_final_splitting_params {
1151 BOOL strikethrough;
1152 BOOL underline;
1153 IUnknown *effect;
1156 static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1158 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos);
1159 return ((struct layout_range_bool*)h)->value;
1162 static inline BOOL layout_get_underline_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1164 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->underline_ranges, pos);
1165 return ((struct layout_range_bool*)h)->value;
1168 static void layout_splitting_params_from_pos(struct dwrite_textlayout *layout, UINT32 pos,
1169 struct layout_final_splitting_params *params)
1171 params->strikethrough = layout_get_strikethrough_from_pos(layout, pos);
1172 params->underline = layout_get_underline_from_pos(layout, pos);
1173 params->effect = layout_get_effect_from_pos(layout, pos);
1176 static BOOL is_same_splitting_params(const struct layout_final_splitting_params *left,
1177 const struct layout_final_splitting_params *right)
1179 return left->strikethrough == right->strikethrough &&
1180 left->underline == right->underline &&
1181 left->effect == right->effect;
1184 static void layout_get_erun_font_metrics(struct dwrite_textlayout *layout, struct layout_effective_run *erun,
1185 DWRITE_FONT_METRICS *metrics)
1187 memset(metrics, 0, sizeof(*metrics));
1188 if (is_layout_gdi_compatible(layout)) {
1189 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
1190 erun->run->u.regular.run.fontFace,
1191 erun->run->u.regular.run.fontEmSize,
1192 layout->ppdip,
1193 &layout->transform,
1194 metrics);
1195 if (FAILED(hr))
1196 WARN("failed to get font metrics, 0x%08x\n", hr);
1198 else
1199 IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics);
1202 /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index,
1203 'cluster_count' indicates how many clusters to add, including first one. */
1204 static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster,
1205 UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params)
1207 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1208 UINT32 i, start, length, last_cluster;
1209 struct layout_effective_run *run;
1211 if (r->kind == LAYOUT_RUN_INLINE) {
1212 struct layout_effective_inline *inlineobject;
1214 inlineobject = heap_alloc(sizeof(*inlineobject));
1215 if (!inlineobject)
1216 return E_OUTOFMEMORY;
1218 inlineobject->object = r->u.object.object;
1219 inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1220 inlineobject->origin_x = is_rtl ? origin_x - inlineobject->width : origin_x;
1221 inlineobject->origin_y = 0.0f; /* set after line is built */
1222 inlineobject->align_dx = 0.0f;
1223 inlineobject->baseline = r->baseline;
1225 /* It's not clear how these two are set, possibly directionality
1226 is derived from surrounding text (replaced text could have
1227 different ranges which differ in reading direction). */
1228 inlineobject->is_sideways = FALSE;
1229 inlineobject->is_rtl = FALSE;
1230 inlineobject->line = line;
1232 /* effect assigned from start position and on is used for inline objects */
1233 inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position);
1235 list_add_tail(&layout->inlineobjects, &inlineobject->entry);
1236 return S_OK;
1239 run = heap_alloc(sizeof(*run));
1240 if (!run)
1241 return E_OUTOFMEMORY;
1243 /* No need to iterate for that, use simple fact that:
1244 <last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */
1245 last_cluster = first_cluster + cluster_count - 1;
1246 length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
1247 layout->clustermetrics[last_cluster].length;
1249 run->clustermap = heap_alloc(sizeof(UINT16)*length);
1250 if (!run->clustermap) {
1251 heap_free(run);
1252 return E_OUTOFMEMORY;
1255 run->run = r;
1256 run->start = start = layout->clusters[first_cluster].position;
1257 run->length = length;
1258 run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1260 /* Check if run direction matches paragraph direction, if it doesn't adjust by
1261 run width */
1262 if (layout_is_erun_rtl(run) ^ is_rtl)
1263 run->origin_x = is_rtl ? origin_x - run->width : origin_x + run->width;
1264 else
1265 run->origin_x = origin_x;
1267 run->origin_y = 0.0f; /* set after line is built */
1268 run->align_dx = 0.0f;
1269 run->line = line;
1271 if (r->u.regular.run.glyphCount) {
1272 /* trim from the left */
1273 run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start];
1274 /* trim from the right */
1275 if (start + length < r->u.regular.descr.stringLength - 1)
1276 run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length];
1278 else
1279 run->glyphcount = 0;
1281 /* cluster map needs to be shifted */
1282 for (i = 0; i < length; i++)
1283 run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start];
1285 run->effect = params->effect;
1286 run->underlined = params->underline;
1287 list_add_tail(&layout->eruns, &run->entry);
1289 /* Strikethrough style is guaranteed to be consistent within effective run,
1290 its width equals to run width, thickness and offset are derived from
1291 font metrics, rest of the values are from layout or run itself */
1292 if (params->strikethrough) {
1293 struct layout_strikethrough *s;
1294 DWRITE_FONT_METRICS metrics;
1296 s = heap_alloc(sizeof(*s));
1297 if (!s)
1298 return E_OUTOFMEMORY;
1300 layout_get_erun_font_metrics(layout, run, &metrics);
1301 s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1302 s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics);
1303 /* Negative offset moves it above baseline as Y coordinate grows downward. */
1304 s->s.offset = -SCALE_FONT_METRIC(metrics.strikethroughPosition, r->u.regular.run.fontEmSize, &metrics);
1305 s->s.readingDirection = layout->format.readingdir;
1306 s->s.flowDirection = layout->format.flow;
1307 s->s.localeName = r->u.regular.descr.localeName;
1308 s->s.measuringMode = layout->measuringmode;
1309 s->run = run;
1311 list_add_tail(&layout->strikethrough, &s->entry);
1314 return S_OK;
1317 static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics)
1319 UINT32 i = layout->metrics.lineCount;
1321 if (!layout->line_alloc) {
1322 layout->line_alloc = 5;
1323 layout->linemetrics = heap_alloc(layout->line_alloc * sizeof(*layout->linemetrics));
1324 layout->lines = heap_alloc(layout->line_alloc * sizeof(*layout->lines));
1325 if (!layout->linemetrics || !layout->lines) {
1326 heap_free(layout->linemetrics);
1327 heap_free(layout->lines);
1328 layout->linemetrics = NULL;
1329 layout->lines = NULL;
1330 return E_OUTOFMEMORY;
1334 if (layout->metrics.lineCount == layout->line_alloc) {
1335 DWRITE_LINE_METRICS1 *metrics;
1336 struct layout_line *lines;
1338 if ((metrics = heap_realloc(layout->linemetrics, layout->line_alloc * 2 * sizeof(*layout->linemetrics))))
1339 layout->linemetrics = metrics;
1340 if ((lines = heap_realloc(layout->lines, layout->line_alloc * 2 * sizeof(*layout->lines))))
1341 layout->lines = lines;
1343 if (!metrics || !lines)
1344 return E_OUTOFMEMORY;
1346 layout->line_alloc *= 2;
1349 layout->linemetrics[i] = *metrics;
1351 switch (layout->format.spacing.method)
1353 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
1354 if (layout->format.spacing.method == DWRITE_LINE_SPACING_METHOD_UNIFORM) {
1355 layout->linemetrics[i].height = layout->format.spacing.height;
1356 layout->linemetrics[i].baseline = layout->format.spacing.baseline;
1358 break;
1359 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
1360 if (layout->format.spacing.method == DWRITE_LINE_SPACING_METHOD_UNIFORM) {
1361 layout->linemetrics[i].height = layout->format.spacing.height * metrics->height;
1362 layout->linemetrics[i].baseline = layout->format.spacing.baseline * metrics->baseline;
1364 break;
1365 default:
1366 /* using content values */;
1369 layout->lines[i].height = metrics->height;
1370 layout->lines[i].baseline = metrics->baseline;
1372 layout->metrics.lineCount++;
1373 return S_OK;
1376 static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
1377 const struct layout_effective_run *cur)
1379 struct list *e;
1381 if (!cur)
1382 e = list_head(&layout->eruns);
1383 else
1384 e = list_next(&layout->eruns, &cur->entry);
1385 if (!e)
1386 return NULL;
1387 return LIST_ENTRY(e, struct layout_effective_run, entry);
1390 static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout,
1391 const struct layout_effective_run *cur)
1393 struct list *e;
1395 if (!cur)
1396 e = list_tail(&layout->eruns);
1397 else
1398 e = list_prev(&layout->eruns, &cur->entry);
1399 if (!e)
1400 return NULL;
1401 return LIST_ENTRY(e, struct layout_effective_run, entry);
1404 static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
1405 const struct layout_effective_inline *cur)
1407 struct list *e;
1409 if (!cur)
1410 e = list_head(&layout->inlineobjects);
1411 else
1412 e = list_next(&layout->inlineobjects, &cur->entry);
1413 if (!e)
1414 return NULL;
1415 return LIST_ENTRY(e, struct layout_effective_inline, entry);
1418 static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
1419 struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
1421 FLOAT width = 0.0f;
1423 while (erun && erun->line == line) {
1424 width += erun->width;
1425 erun = layout_get_next_erun(layout, erun);
1426 if (!erun)
1427 break;
1430 while (inrun && inrun->line == line) {
1431 width += inrun->width;
1432 inrun = layout_get_next_inline_run(layout, inrun);
1433 if (!inrun)
1434 break;
1437 return width;
1440 static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
1442 *det = m->m11 * m->m22 - m->m12 * m->m21;
1443 /* on certain conditions we can skip transform */
1444 return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
1447 static inline void layout_apply_snapping(struct dwrite_vec *vec, BOOL skiptransform, FLOAT ppdip,
1448 const DWRITE_MATRIX *m, FLOAT det)
1450 if (!skiptransform) {
1451 FLOAT vec2[2];
1453 /* apply transform */
1454 vec->x *= ppdip;
1455 vec->y *= ppdip;
1457 vec2[0] = m->m11 * vec->x + m->m21 * vec->y + m->dx;
1458 vec2[1] = m->m12 * vec->x + m->m22 * vec->y + m->dy;
1460 /* snap */
1461 vec2[0] = floorf(vec2[0] + 0.5f);
1462 vec2[1] = floorf(vec2[1] + 0.5f);
1464 /* apply inverted transform, we don't care about X component at this point */
1465 vec->x = (m->m22 * vec2[0] - m->m21 * vec2[1] + m->m21 * m->dy - m->m22 * m->dx) / det;
1466 vec->x /= ppdip;
1468 vec->y = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
1469 vec->y /= ppdip;
1471 else {
1472 vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
1473 vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
1477 static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
1479 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1480 struct layout_effective_inline *inrun;
1481 struct layout_effective_run *erun;
1483 erun = layout_get_next_erun(layout, NULL);
1484 inrun = layout_get_next_inline_run(layout, NULL);
1486 while (erun) {
1487 erun->align_dx = 0.0f;
1488 erun = layout_get_next_erun(layout, erun);
1491 while (inrun) {
1492 inrun->align_dx = 0.0f;
1493 inrun = layout_get_next_inline_run(layout, inrun);
1496 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
1499 static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout)
1501 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1502 struct layout_effective_inline *inrun;
1503 struct layout_effective_run *erun;
1504 UINT32 line;
1506 erun = layout_get_next_erun(layout, NULL);
1507 inrun = layout_get_next_inline_run(layout, NULL);
1509 for (line = 0; line < layout->metrics.lineCount; line++) {
1510 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1511 FLOAT shift = layout->metrics.layoutWidth - width;
1513 if (is_rtl)
1514 shift *= -1.0f;
1516 while (erun && erun->line == line) {
1517 erun->align_dx = shift;
1518 erun = layout_get_next_erun(layout, erun);
1521 while (inrun && inrun->line == line) {
1522 inrun->align_dx = shift;
1523 inrun = layout_get_next_inline_run(layout, inrun);
1527 layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width;
1530 static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
1531 FLOAT width, FLOAT det)
1533 if (is_layout_gdi_compatible(layout)) {
1534 struct dwrite_vec vec = { layout->metrics.layoutWidth - width, 0.0f};
1535 layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
1536 return floorf(vec.x / 2.0f);
1538 else
1539 return (layout->metrics.layoutWidth - width) / 2.0f;
1542 static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
1544 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1545 struct layout_effective_inline *inrun;
1546 struct layout_effective_run *erun;
1547 BOOL skiptransform;
1548 UINT32 line;
1549 FLOAT det;
1551 erun = layout_get_next_erun(layout, NULL);
1552 inrun = layout_get_next_inline_run(layout, NULL);
1554 skiptransform = should_skip_transform(&layout->transform, &det);
1556 for (line = 0; line < layout->metrics.lineCount; line++) {
1557 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1558 FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
1560 if (is_rtl)
1561 shift *= -1.0f;
1563 while (erun && erun->line == line) {
1564 erun->align_dx = shift;
1565 erun = layout_get_next_erun(layout, erun);
1568 while (inrun && inrun->line == line) {
1569 inrun->align_dx = shift;
1570 inrun = layout_get_next_inline_run(layout, inrun);
1574 layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f;
1577 static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
1579 switch (layout->format.textalignment)
1581 case DWRITE_TEXT_ALIGNMENT_LEADING:
1582 layout_apply_leading_alignment(layout);
1583 break;
1584 case DWRITE_TEXT_ALIGNMENT_TRAILING:
1585 layout_apply_trailing_alignment(layout);
1586 break;
1587 case DWRITE_TEXT_ALIGNMENT_CENTER:
1588 layout_apply_centered_alignment(layout);
1589 break;
1590 case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
1591 FIXME("alignment %d not implemented\n", layout->format.textalignment);
1592 break;
1593 default:
1598 static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
1600 struct layout_effective_inline *inrun;
1601 struct layout_effective_run *erun;
1602 FLOAT origin_y = 0.0f;
1603 UINT32 line;
1605 /* alignment mode defines origin, after that all run origins are updated
1606 the same way */
1608 switch (layout->format.paralign)
1610 case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
1611 origin_y = 0.0f;
1612 break;
1613 case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
1614 origin_y = layout->metrics.layoutHeight - layout->metrics.height;
1615 break;
1616 case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
1617 origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f;
1618 break;
1619 default:
1623 layout->metrics.top = origin_y;
1625 erun = layout_get_next_erun(layout, NULL);
1626 inrun = layout_get_next_inline_run(layout, NULL);
1627 for (line = 0; line < layout->metrics.lineCount; line++) {
1628 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1630 while (erun && erun->line == line) {
1631 erun->origin_y = pos_y;
1632 erun = layout_get_next_erun(layout, erun);
1635 while (inrun && inrun->line == line) {
1636 inrun->origin_y = pos_y - inrun->baseline;
1637 inrun = layout_get_next_inline_run(layout, inrun);
1640 origin_y += layout->linemetrics[line].height;
1644 struct layout_underline_splitting_params {
1645 const WCHAR *locale; /* points to range data, no additional allocation */
1646 IUnknown *effect; /* does not hold another reference */
1649 static void init_u_splitting_params_from_erun(struct layout_effective_run *erun,
1650 struct layout_underline_splitting_params *params)
1652 params->locale = erun->run->u.regular.descr.localeName;
1653 params->effect = erun->effect;
1656 static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left,
1657 struct layout_underline_splitting_params *right)
1659 return left->effect == right->effect && !strcmpiW(left->locale, right->locale);
1662 static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first,
1663 struct layout_effective_run *last)
1665 struct layout_effective_run *cur;
1666 DWRITE_FONT_METRICS metrics;
1667 FLOAT thickness, offset;
1669 if (first == layout_get_prev_erun(layout, last)) {
1670 layout_get_erun_font_metrics(layout, first, &metrics);
1671 thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics);
1672 offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics);
1674 else {
1675 FLOAT width = 0.0f;
1677 /* Single underline is added for consecutive underlined runs. In this case underline parameters are
1678 calculated as weighted average, where run width acts as a weight. */
1679 thickness = offset = 0.0f;
1680 cur = first;
1681 do {
1682 layout_get_erun_font_metrics(layout, cur, &metrics);
1684 thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1685 offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
1686 width += cur->width;
1688 cur = layout_get_next_erun(layout, cur);
1689 } while (cur != last);
1691 thickness /= width;
1692 offset /= width;
1695 cur = first;
1696 do {
1697 struct layout_underline_splitting_params params, prev_params;
1698 struct layout_effective_run *next, *w;
1699 struct layout_underline *u;
1701 init_u_splitting_params_from_erun(cur, &prev_params);
1702 while ((next = layout_get_next_erun(layout, cur)) != last) {
1703 init_u_splitting_params_from_erun(next, &params);
1704 if (!is_same_u_splitting(&prev_params, &params))
1705 break;
1706 cur = next;
1709 u = heap_alloc(sizeof(*u));
1710 if (!u)
1711 return E_OUTOFMEMORY;
1713 w = cur;
1714 u->u.width = 0.0f;
1715 while (w != next) {
1716 u->u.width += w->width;
1717 w = layout_get_next_erun(layout, w);
1720 u->u.thickness = thickness;
1721 /* Font metrics convention is to have it negative when below baseline, for rendering
1722 however Y grows from baseline down for horizontal baseline. */
1723 u->u.offset = -offset;
1724 u->u.runHeight = 0.0f; /* FIXME */
1725 u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
1726 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
1727 u->u.flowDirection = layout->format.flow;
1728 u->u.localeName = cur->run->u.regular.descr.localeName;
1729 u->u.measuringMode = layout->measuringmode;
1730 u->run = cur;
1731 list_add_tail(&layout->underlines, &u->entry);
1733 cur = next;
1734 } while (cur != last);
1736 return S_OK;
1739 /* Adds zero width line, metrics are derived from font at specified text position. */
1740 static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
1742 DWRITE_LINE_METRICS1 metrics = { 0 };
1743 DWRITE_FONT_METRICS fontmetrics;
1744 struct layout_range *range;
1745 IDWriteFontFace *fontface;
1746 IDWriteFont *font;
1747 HRESULT hr;
1749 range = get_layout_range_by_pos(layout, pos);
1750 hr = create_matching_font(range->collection,
1751 range->fontfamily,
1752 range->weight,
1753 range->style,
1754 range->stretch,
1755 &font);
1756 if (FAILED(hr))
1757 return hr;
1758 hr = IDWriteFont_CreateFontFace(font, &fontface);
1759 IDWriteFont_Release(font);
1760 if (FAILED(hr))
1761 return hr;
1763 layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics);
1764 layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
1765 IDWriteFontFace_Release(fontface);
1767 return layout_set_line_metrics(layout, &metrics);
1770 static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
1771 UINT32 *textpos)
1773 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1774 struct layout_final_splitting_params params, prev_params;
1775 DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 };
1776 UINT32 line = layout->metrics.lineCount, i;
1777 DWRITE_LINE_METRICS1 metrics = { 0 };
1778 UINT32 index, start, pos = *textpos;
1779 FLOAT descent, trailingspacewidth;
1780 BOOL append_trimming_run = FALSE;
1781 const struct layout_run *run;
1782 FLOAT width, origin_x;
1783 HRESULT hr;
1785 /* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
1786 for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
1787 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1788 struct layout_cluster *lc = &layout->clusters[index];
1789 WCHAR ch;
1791 /* This also filters out clusters added from inline objects, those are never
1792 treated as a white space. */
1793 if (!cluster->isWhitespace)
1794 break;
1796 /* Every isNewline cluster is also isWhitespace, but not every
1797 newline character cluster has isNewline set, so go back to original string. */
1798 ch = lc->run->u.regular.descr.string[lc->position];
1799 if (cluster->length == 1 && lb_is_newline_char(ch))
1800 metrics.newlineLength += cluster->length;
1802 metrics.trailingWhitespaceLength += cluster->length;
1803 trailingspacewidth += cluster->width;
1806 /* Line metrics length includes trailing whitespace length too */
1807 for (i = first_cluster; i <= last_cluster; i++)
1808 metrics.length += layout->clustermetrics[i].length;
1810 /* Ignore trailing whitespaces */
1811 while (last_cluster > first_cluster) {
1812 if (!layout->clustermetrics[last_cluster].isWhitespace)
1813 break;
1815 last_cluster--;
1818 /* Does not include trailing space width */
1819 width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
1821 /* Append trimming run if necessary */
1822 if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL &&
1823 layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) {
1824 FLOAT trimmed_width = width;
1826 hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics);
1827 if (SUCCEEDED(hr)) {
1828 while (last_cluster > first_cluster) {
1829 if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth)
1830 break;
1831 trimmed_width -= layout->clustermetrics[last_cluster--].width;
1833 append_trimming_run = TRUE;
1835 else
1836 WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr);
1838 width = trimmed_width + sign_metrics.width;
1841 layout_splitting_params_from_pos(layout, pos, &params);
1842 prev_params = params;
1843 run = layout->clusters[first_cluster].run;
1845 /* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
1846 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
1847 for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
1848 layout_splitting_params_from_pos(layout, pos, &params);
1850 if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, &params)) {
1851 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1852 if (FAILED(hr))
1853 return;
1855 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
1856 get_cluster_range_width(layout, start, i);
1857 run = layout->clusters[i].run;
1858 start = i;
1861 prev_params = params;
1862 pos += layout->clustermetrics[i].length;
1865 /* Final run from what's left from cluster range */
1866 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
1867 if (FAILED(hr))
1868 return;
1870 if (append_trimming_run) {
1871 struct layout_effective_inline *trimming_sign;
1873 trimming_sign = heap_alloc(sizeof(*trimming_sign));
1874 if (!trimming_sign)
1875 return;
1877 trimming_sign->object = layout->format.trimmingsign;
1878 trimming_sign->width = sign_metrics.width;
1879 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i);
1880 trimming_sign->origin_x = is_rtl ? origin_x - trimming_sign->width : origin_x;
1881 trimming_sign->origin_y = 0.0f; /* set after line is built */
1882 trimming_sign->align_dx = 0.0f;
1883 trimming_sign->baseline = sign_metrics.baseline;
1885 trimming_sign->is_sideways = FALSE;
1886 trimming_sign->is_rtl = FALSE;
1887 trimming_sign->line = line;
1889 trimming_sign->effect = NULL; /* FIXME */
1891 list_add_tail(&layout->inlineobjects, &trimming_sign->entry);
1894 /* Look for max baseline and descent for this line */
1895 for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) {
1896 const struct layout_run *cur = layout->clusters[index].run;
1897 FLOAT cur_descent = cur->height - cur->baseline;
1899 if (cur->baseline > metrics.baseline)
1900 metrics.baseline = cur->baseline;
1901 if (cur_descent > descent)
1902 descent = cur_descent;
1905 layout->metrics.width = max(width, layout->metrics.width);
1906 layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
1907 layout->metrics.widthIncludingTrailingWhitespace);
1909 metrics.height = descent + metrics.baseline;
1910 metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth;
1911 layout_set_line_metrics(layout, &metrics);
1913 *textpos += metrics.length;
1916 static void layout_set_line_positions(struct dwrite_textlayout *layout)
1918 struct layout_effective_inline *inrun;
1919 struct layout_effective_run *erun;
1920 FLOAT origin_y;
1921 UINT32 line;
1923 /* Now all line info is here, update effective runs positions in flow direction */
1924 erun = layout_get_next_erun(layout, NULL);
1925 inrun = layout_get_next_inline_run(layout, NULL);
1927 for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) {
1928 FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
1930 /* For all runs on this line */
1931 while (erun && erun->line == line) {
1932 erun->origin_y = pos_y;
1933 erun = layout_get_next_erun(layout, erun);
1936 /* Same for inline runs */
1937 while (inrun && inrun->line == line) {
1938 inrun->origin_y = pos_y - inrun->baseline;
1939 inrun = layout_get_next_inline_run(layout, inrun);
1942 origin_y += layout->linemetrics[line].height;
1945 layout->metrics.height = origin_y;
1947 /* Initial paragraph alignment is always near */
1948 if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
1949 layout_apply_par_alignment(layout);
1952 static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster)
1954 if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER)
1955 return TRUE;
1957 return layout->clustermetrics[cluster].canWrapLineAfter;
1960 static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
1962 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1963 struct layout_effective_run *erun, *first_underlined;
1964 UINT32 i, start, textpos, last_breaking_point;
1965 DWRITE_LINE_METRICS1 metrics;
1966 FLOAT width;
1967 UINT32 line;
1968 HRESULT hr;
1970 if (!(layout->recompute & RECOMPUTE_LINES))
1971 return S_OK;
1973 free_layout_eruns(layout);
1975 hr = layout_compute(layout);
1976 if (FAILED(hr))
1977 return hr;
1979 layout->metrics.lineCount = 0;
1980 memset(&metrics, 0, sizeof(metrics));
1982 layout->metrics.height = 0.0f;
1983 layout->metrics.width = 0.0f;
1984 layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
1986 last_breaking_point = ~0u;
1988 for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
1989 BOOL overflow = FALSE;
1991 while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
1992 /* Check for overflow */
1993 overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
1994 (layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
1995 if (overflow)
1996 break;
1998 if (layout_can_wrap_after(layout, i))
1999 last_breaking_point = i;
2000 width += layout->clustermetrics[i].width;
2001 i++;
2003 i = min(i, layout->cluster_count - 1);
2005 if (overflow) {
2006 /* Overflown on whitespace, ignore it */
2007 if (layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))
2008 i = i;
2009 /* Use most recently found breaking point */
2010 else if (last_breaking_point != ~0u) {
2011 i = last_breaking_point;
2012 last_breaking_point = ~0u;
2014 else {
2015 /* Otherwise proceed forward to next newline or breaking point */
2016 for (; i < layout->cluster_count; i++)
2017 if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline)
2018 break;
2021 i = min(i, layout->cluster_count - 1);
2023 layout_add_line(layout, start, i, &textpos);
2024 start = i + 1;
2025 width = 0.0f;
2028 /* Add dummy line if:
2029 - there's no text, metrics come from first range in this case;
2030 - last ended with a mandatory break, metrics come from last text position.
2032 if (layout->len == 0)
2033 hr = layout_set_dummy_line_metrics(layout, 0);
2034 else if (layout->clustermetrics[layout->cluster_count - 1].isNewline)
2035 hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
2036 if (FAILED(hr))
2037 return hr;
2039 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
2040 layout->metrics.top = 0.0f;
2041 layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
2043 /* Add explicit underlined runs */
2044 erun = layout_get_next_erun(layout, NULL);
2045 first_underlined = erun && erun->underlined ? erun : NULL;
2046 for (line = 0; line < layout->metrics.lineCount; line++) {
2047 while (erun && erun->line == line) {
2048 erun = layout_get_next_erun(layout, erun);
2050 if (first_underlined && (!erun || !erun->underlined)) {
2051 layout_add_underline(layout, first_underlined, erun);
2052 first_underlined = NULL;
2054 else if (!first_underlined && erun && erun->underlined)
2055 first_underlined = erun;
2059 /* Position runs in flow direction */
2060 layout_set_line_positions(layout);
2062 /* Initial alignment is always leading */
2063 if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
2064 layout_apply_text_alignment(layout);
2066 layout->recompute &= ~RECOMPUTE_LINES;
2067 return hr;
2070 static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
2071 struct layout_range_attr_value *value)
2073 struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
2074 struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
2075 struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
2076 struct layout_range const *range = (struct layout_range*)h;
2078 switch (attr) {
2079 case LAYOUT_RANGE_ATTR_WEIGHT:
2080 return range->weight == value->u.weight;
2081 case LAYOUT_RANGE_ATTR_STYLE:
2082 return range->style == value->u.style;
2083 case LAYOUT_RANGE_ATTR_STRETCH:
2084 return range->stretch == value->u.stretch;
2085 case LAYOUT_RANGE_ATTR_FONTSIZE:
2086 return range->fontsize == value->u.fontsize;
2087 case LAYOUT_RANGE_ATTR_INLINE:
2088 return range->object == value->u.object;
2089 case LAYOUT_RANGE_ATTR_EFFECT:
2090 return range_iface->iface == value->u.effect;
2091 case LAYOUT_RANGE_ATTR_UNDERLINE:
2092 return range_bool->value == value->u.underline;
2093 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2094 return range_bool->value == value->u.strikethrough;
2095 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2096 return range->pair_kerning == value->u.pair_kerning;
2097 case LAYOUT_RANGE_ATTR_FONTCOLL:
2098 return range->collection == value->u.collection;
2099 case LAYOUT_RANGE_ATTR_LOCALE:
2100 return strcmpiW(range->locale, value->u.locale) == 0;
2101 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2102 return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
2103 case LAYOUT_RANGE_ATTR_SPACING:
2104 return range_spacing->leading == value->u.spacing[0] &&
2105 range_spacing->trailing == value->u.spacing[1] &&
2106 range_spacing->min_advance == value->u.spacing[2];
2107 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2108 return range_iface->iface == (IUnknown*)value->u.typography;
2109 default:
2113 return FALSE;
2116 static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
2118 switch (hleft->kind)
2120 case LAYOUT_RANGE_REGULAR:
2122 struct layout_range const *left = (struct layout_range const*)hleft;
2123 struct layout_range const *right = (struct layout_range const*)hright;
2124 return left->weight == right->weight &&
2125 left->style == right->style &&
2126 left->stretch == right->stretch &&
2127 left->fontsize == right->fontsize &&
2128 left->object == right->object &&
2129 left->pair_kerning == right->pair_kerning &&
2130 left->collection == right->collection &&
2131 !strcmpiW(left->locale, right->locale) &&
2132 !strcmpW(left->fontfamily, right->fontfamily);
2134 case LAYOUT_RANGE_UNDERLINE:
2135 case LAYOUT_RANGE_STRIKETHROUGH:
2137 struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
2138 struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
2139 return left->value == right->value;
2141 case LAYOUT_RANGE_EFFECT:
2142 case LAYOUT_RANGE_TYPOGRAPHY:
2144 struct layout_range_iface const *left = (struct layout_range_iface const*)hleft;
2145 struct layout_range_iface const *right = (struct layout_range_iface const*)hright;
2146 return left->iface == right->iface;
2148 case LAYOUT_RANGE_SPACING:
2150 struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
2151 struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
2152 return left->leading == right->leading &&
2153 left->trailing == right->trailing &&
2154 left->min_advance == right->min_advance;
2156 default:
2157 FIXME("unknown range kind %d\n", hleft->kind);
2158 return FALSE;
2162 static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
2164 return left->startPosition == right->startPosition && left->length == right->length;
2167 /* Allocates range and inits it with default values from text format. */
2168 static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
2169 enum layout_range_kind kind)
2171 struct layout_range_header *h;
2173 switch (kind)
2175 case LAYOUT_RANGE_REGULAR:
2177 struct layout_range *range;
2179 range = heap_alloc(sizeof(*range));
2180 if (!range) return NULL;
2182 range->weight = layout->format.weight;
2183 range->style = layout->format.style;
2184 range->stretch = layout->format.stretch;
2185 range->fontsize = layout->format.fontsize;
2186 range->object = NULL;
2187 range->pair_kerning = FALSE;
2189 range->fontfamily = heap_strdupW(layout->format.family_name);
2190 if (!range->fontfamily) {
2191 heap_free(range);
2192 return NULL;
2195 range->collection = layout->format.collection;
2196 if (range->collection)
2197 IDWriteFontCollection_AddRef(range->collection);
2198 strcpyW(range->locale, layout->format.locale);
2200 h = &range->h;
2201 break;
2203 case LAYOUT_RANGE_UNDERLINE:
2204 case LAYOUT_RANGE_STRIKETHROUGH:
2206 struct layout_range_bool *range;
2208 range = heap_alloc(sizeof(*range));
2209 if (!range) return NULL;
2211 range->value = FALSE;
2212 h = &range->h;
2213 break;
2215 case LAYOUT_RANGE_EFFECT:
2216 case LAYOUT_RANGE_TYPOGRAPHY:
2218 struct layout_range_iface *range;
2220 range = heap_alloc(sizeof(*range));
2221 if (!range) return NULL;
2223 range->iface = NULL;
2224 h = &range->h;
2225 break;
2227 case LAYOUT_RANGE_SPACING:
2229 struct layout_range_spacing *range;
2231 range = heap_alloc(sizeof(*range));
2232 if (!range) return NULL;
2234 range->leading = 0.0f;
2235 range->trailing = 0.0f;
2236 range->min_advance = 0.0f;
2237 h = &range->h;
2238 break;
2240 default:
2241 FIXME("unknown range kind %d\n", kind);
2242 return NULL;
2245 h->kind = kind;
2246 h->range = *r;
2247 return h;
2250 static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
2252 struct layout_range_header *ret;
2254 switch (h->kind)
2256 case LAYOUT_RANGE_REGULAR:
2258 struct layout_range *from = (struct layout_range*)h;
2260 struct layout_range *range = heap_alloc(sizeof(*range));
2261 if (!range) return NULL;
2263 *range = *from;
2264 range->fontfamily = heap_strdupW(from->fontfamily);
2265 if (!range->fontfamily) {
2266 heap_free(range);
2267 return NULL;
2270 /* update refcounts */
2271 if (range->object)
2272 IDWriteInlineObject_AddRef(range->object);
2273 if (range->collection)
2274 IDWriteFontCollection_AddRef(range->collection);
2275 ret = &range->h;
2276 break;
2278 case LAYOUT_RANGE_UNDERLINE:
2279 case LAYOUT_RANGE_STRIKETHROUGH:
2281 struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
2282 if (!strike) return NULL;
2284 *strike = *(struct layout_range_bool*)h;
2285 ret = &strike->h;
2286 break;
2288 case LAYOUT_RANGE_EFFECT:
2289 case LAYOUT_RANGE_TYPOGRAPHY:
2291 struct layout_range_iface *effect = heap_alloc(sizeof(*effect));
2292 if (!effect) return NULL;
2294 *effect = *(struct layout_range_iface*)h;
2295 if (effect->iface)
2296 IUnknown_AddRef(effect->iface);
2297 ret = &effect->h;
2298 break;
2300 case LAYOUT_RANGE_SPACING:
2302 struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
2303 if (!spacing) return NULL;
2305 *spacing = *(struct layout_range_spacing*)h;
2306 ret = &spacing->h;
2307 break;
2309 default:
2310 FIXME("unknown range kind %d\n", h->kind);
2311 return NULL;
2314 ret->range = *r;
2315 return ret;
2318 static void free_layout_range(struct layout_range_header *h)
2320 if (!h)
2321 return;
2323 switch (h->kind)
2325 case LAYOUT_RANGE_REGULAR:
2327 struct layout_range *range = (struct layout_range*)h;
2329 if (range->object)
2330 IDWriteInlineObject_Release(range->object);
2331 if (range->collection)
2332 IDWriteFontCollection_Release(range->collection);
2333 heap_free(range->fontfamily);
2334 break;
2336 case LAYOUT_RANGE_EFFECT:
2337 case LAYOUT_RANGE_TYPOGRAPHY:
2339 struct layout_range_iface *range = (struct layout_range_iface*)h;
2340 if (range->iface)
2341 IUnknown_Release(range->iface);
2342 break;
2344 default:
2348 heap_free(h);
2351 static void free_layout_ranges_list(struct dwrite_textlayout *layout)
2353 struct layout_range_header *cur, *cur2;
2355 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
2356 list_remove(&cur->entry);
2357 free_layout_range(cur);
2360 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) {
2361 list_remove(&cur->entry);
2362 free_layout_range(cur);
2365 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
2366 list_remove(&cur->entry);
2367 free_layout_range(cur);
2370 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
2371 list_remove(&cur->entry);
2372 free_layout_range(cur);
2375 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
2376 list_remove(&cur->entry);
2377 free_layout_range(cur);
2380 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) {
2381 list_remove(&cur->entry);
2382 free_layout_range(cur);
2386 static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
2388 struct layout_range_header *cur;
2390 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
2392 if (cur->range.startPosition > range->startPosition)
2393 return NULL;
2395 if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
2396 (range->startPosition < cur->range.startPosition + cur->range.length))
2397 return NULL;
2398 if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
2399 return cur;
2402 return NULL;
2405 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
2407 struct layout_range *cur;
2409 LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
2410 DWRITE_TEXT_RANGE *r = &cur->h.range;
2411 if (r->startPosition <= pos && pos < r->startPosition + r->length)
2412 return cur;
2415 return NULL;
2418 static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
2420 if (*dest == value) return FALSE;
2422 if (*dest)
2423 IUnknown_Release(*dest);
2424 *dest = value;
2425 if (*dest)
2426 IUnknown_AddRef(*dest);
2428 return TRUE;
2431 static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2433 struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
2434 struct layout_range_iface *dest_iface = (struct layout_range_iface*)h;
2435 struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
2436 struct layout_range *dest = (struct layout_range*)h;
2438 BOOL changed = FALSE;
2440 switch (attr) {
2441 case LAYOUT_RANGE_ATTR_WEIGHT:
2442 changed = dest->weight != value->u.weight;
2443 dest->weight = value->u.weight;
2444 break;
2445 case LAYOUT_RANGE_ATTR_STYLE:
2446 changed = dest->style != value->u.style;
2447 dest->style = value->u.style;
2448 break;
2449 case LAYOUT_RANGE_ATTR_STRETCH:
2450 changed = dest->stretch != value->u.stretch;
2451 dest->stretch = value->u.stretch;
2452 break;
2453 case LAYOUT_RANGE_ATTR_FONTSIZE:
2454 changed = dest->fontsize != value->u.fontsize;
2455 dest->fontsize = value->u.fontsize;
2456 break;
2457 case LAYOUT_RANGE_ATTR_INLINE:
2458 changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
2459 break;
2460 case LAYOUT_RANGE_ATTR_EFFECT:
2461 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.effect);
2462 break;
2463 case LAYOUT_RANGE_ATTR_UNDERLINE:
2464 changed = dest_bool->value != value->u.underline;
2465 dest_bool->value = value->u.underline;
2466 break;
2467 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2468 changed = dest_bool->value != value->u.strikethrough;
2469 dest_bool->value = value->u.strikethrough;
2470 break;
2471 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2472 changed = dest->pair_kerning != value->u.pair_kerning;
2473 dest->pair_kerning = value->u.pair_kerning;
2474 break;
2475 case LAYOUT_RANGE_ATTR_FONTCOLL:
2476 changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
2477 break;
2478 case LAYOUT_RANGE_ATTR_LOCALE:
2479 changed = strcmpiW(dest->locale, value->u.locale) != 0;
2480 if (changed) {
2481 strcpyW(dest->locale, value->u.locale);
2482 strlwrW(dest->locale);
2484 break;
2485 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2486 changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
2487 if (changed) {
2488 heap_free(dest->fontfamily);
2489 dest->fontfamily = heap_strdupW(value->u.fontfamily);
2491 break;
2492 case LAYOUT_RANGE_ATTR_SPACING:
2493 changed = dest_spacing->leading != value->u.spacing[0] ||
2494 dest_spacing->trailing != value->u.spacing[1] ||
2495 dest_spacing->min_advance != value->u.spacing[2];
2496 dest_spacing->leading = value->u.spacing[0];
2497 dest_spacing->trailing = value->u.spacing[1];
2498 dest_spacing->min_advance = value->u.spacing[2];
2499 break;
2500 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2501 changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.typography);
2502 break;
2503 default:
2507 return changed;
2510 static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
2512 return (inner->startPosition >= outer->startPosition) &&
2513 (inner->startPosition + inner->length <= outer->startPosition + outer->length);
2516 static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
2518 if (r) *r = h->range;
2519 return S_OK;
2522 /* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */
2523 static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
2525 struct layout_range_header *cur, *right, *left, *outer;
2526 BOOL changed = FALSE;
2527 struct list *ranges;
2528 DWRITE_TEXT_RANGE r;
2530 /* ignore zero length ranges */
2531 if (value->range.length == 0)
2532 return S_OK;
2534 /* select from ranges lists */
2535 switch (attr)
2537 case LAYOUT_RANGE_ATTR_WEIGHT:
2538 case LAYOUT_RANGE_ATTR_STYLE:
2539 case LAYOUT_RANGE_ATTR_STRETCH:
2540 case LAYOUT_RANGE_ATTR_FONTSIZE:
2541 case LAYOUT_RANGE_ATTR_INLINE:
2542 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2543 case LAYOUT_RANGE_ATTR_FONTCOLL:
2544 case LAYOUT_RANGE_ATTR_LOCALE:
2545 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2546 ranges = &layout->ranges;
2547 break;
2548 case LAYOUT_RANGE_ATTR_UNDERLINE:
2549 ranges = &layout->underline_ranges;
2550 break;
2551 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2552 ranges = &layout->strike_ranges;
2553 break;
2554 case LAYOUT_RANGE_ATTR_EFFECT:
2555 ranges = &layout->effects;
2556 break;
2557 case LAYOUT_RANGE_ATTR_SPACING:
2558 ranges = &layout->spacing;
2559 break;
2560 case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
2561 ranges = &layout->typographies;
2562 break;
2563 default:
2564 FIXME("unknown attr kind %d\n", attr);
2565 return E_FAIL;
2568 /* If new range is completely within existing range, split existing range in two */
2569 if ((outer = find_outer_range(ranges, &value->range))) {
2571 /* no need to add same range */
2572 if (is_same_layout_attrvalue(outer, attr, value))
2573 return S_OK;
2575 /* for matching range bounds just replace data */
2576 if (is_same_text_range(&outer->range, &value->range)) {
2577 changed = set_layout_range_attrval(outer, attr, value);
2578 goto done;
2581 /* add new range to the left */
2582 if (value->range.startPosition == outer->range.startPosition) {
2583 left = alloc_layout_range_from(outer, &value->range);
2584 if (!left) return E_OUTOFMEMORY;
2586 changed = set_layout_range_attrval(left, attr, value);
2587 list_add_before(&outer->entry, &left->entry);
2588 outer->range.startPosition += value->range.length;
2589 outer->range.length -= value->range.length;
2590 goto done;
2593 /* add new range to the right */
2594 if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
2595 right = alloc_layout_range_from(outer, &value->range);
2596 if (!right) return E_OUTOFMEMORY;
2598 changed = set_layout_range_attrval(right, attr, value);
2599 list_add_after(&outer->entry, &right->entry);
2600 outer->range.length -= value->range.length;
2601 goto done;
2604 r.startPosition = value->range.startPosition + value->range.length;
2605 r.length = outer->range.length + outer->range.startPosition - r.startPosition;
2607 /* right part */
2608 right = alloc_layout_range_from(outer, &r);
2609 /* new range in the middle */
2610 cur = alloc_layout_range_from(outer, &value->range);
2611 if (!right || !cur) {
2612 free_layout_range(right);
2613 free_layout_range(cur);
2614 return E_OUTOFMEMORY;
2617 /* reuse container range as a left part */
2618 outer->range.length = value->range.startPosition - outer->range.startPosition;
2620 /* new part */
2621 set_layout_range_attrval(cur, attr, value);
2623 list_add_after(&outer->entry, &cur->entry);
2624 list_add_after(&cur->entry, &right->entry);
2626 layout->recompute = RECOMPUTE_EVERYTHING;
2627 return S_OK;
2630 /* Now it's only possible that given range contains some existing ranges, fully or partially.
2631 Update all of them. */
2632 left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
2633 if (left->range.startPosition == value->range.startPosition)
2634 changed = set_layout_range_attrval(left, attr, value);
2635 else /* need to split */ {
2636 r.startPosition = value->range.startPosition;
2637 r.length = left->range.length - value->range.startPosition + left->range.startPosition;
2638 left->range.length -= r.length;
2639 cur = alloc_layout_range_from(left, &r);
2640 changed = set_layout_range_attrval(cur, attr, value);
2641 list_add_after(&left->entry, &cur->entry);
2643 cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
2645 /* for all existing ranges covered by new one update value */
2646 while (cur && is_in_layout_range(&value->range, &cur->range)) {
2647 changed |= set_layout_range_attrval(cur, attr, value);
2648 cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
2651 /* it's possible rightmost range intersects */
2652 if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
2653 r.startPosition = cur->range.startPosition;
2654 r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
2655 left = alloc_layout_range_from(cur, &r);
2656 changed |= set_layout_range_attrval(left, attr, value);
2657 cur->range.startPosition += left->range.length;
2658 cur->range.length -= left->range.length;
2659 list_add_before(&cur->entry, &left->entry);
2662 done:
2663 if (changed) {
2664 struct list *next, *i;
2666 layout->recompute = RECOMPUTE_EVERYTHING;
2667 i = list_head(ranges);
2668 while ((next = list_next(ranges, i))) {
2669 struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
2671 cur = LIST_ENTRY(i, struct layout_range_header, entry);
2672 if (is_same_layout_attributes(cur, next_range)) {
2673 /* remove similar range */
2674 cur->range.length += next_range->range.length;
2675 list_remove(next);
2676 free_layout_range(next_range);
2678 else
2679 i = list_next(ranges, i);
2683 return S_OK;
2686 static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
2688 const WCHAR *str;
2690 switch (kind) {
2691 case LAYOUT_RANGE_ATTR_LOCALE:
2692 str = range->locale;
2693 break;
2694 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2695 str = range->fontfamily;
2696 break;
2697 default:
2698 str = NULL;
2701 return str;
2704 static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2705 UINT32 *length, DWRITE_TEXT_RANGE *r)
2707 struct layout_range *range;
2708 const WCHAR *str;
2710 range = get_layout_range_by_pos(layout, position);
2711 if (!range) {
2712 *length = 0;
2713 return S_OK;
2716 str = get_string_attribute_ptr(range, kind);
2717 *length = strlenW(str);
2718 return return_range(&range->h, r);
2721 static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2722 WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
2724 struct layout_range *range;
2725 const WCHAR *str;
2727 if (length == 0)
2728 return E_INVALIDARG;
2730 ret[0] = 0;
2731 range = get_layout_range_by_pos(layout, position);
2732 if (!range)
2733 return E_INVALIDARG;
2735 str = get_string_attribute_ptr(range, kind);
2736 if (length < strlenW(str) + 1)
2737 return E_NOT_SUFFICIENT_BUFFER;
2739 strcpyW(ret, str);
2740 return return_range(&range->h, r);
2743 static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout3 *iface, REFIID riid, void **obj)
2745 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2747 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2749 *obj = NULL;
2751 if (IsEqualIID(riid, &IID_IDWriteTextLayout3) ||
2752 IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
2753 IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
2754 IsEqualIID(riid, &IID_IDWriteTextLayout) ||
2755 IsEqualIID(riid, &IID_IUnknown))
2757 *obj = iface;
2759 else if (IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
2760 IsEqualIID(riid, &IID_IDWriteTextFormat))
2761 *obj = &This->IDWriteTextFormat1_iface;
2763 if (*obj) {
2764 IDWriteTextLayout3_AddRef(iface);
2765 return S_OK;
2768 return E_NOINTERFACE;
2771 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface)
2773 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2774 ULONG ref = InterlockedIncrement(&This->ref);
2775 TRACE("(%p)->(%d)\n", This, ref);
2776 return ref;
2779 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface)
2781 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2782 ULONG ref = InterlockedDecrement(&This->ref);
2784 TRACE("(%p)->(%d)\n", This, ref);
2786 if (!ref) {
2787 IDWriteFactory4_Release(This->factory);
2788 free_layout_ranges_list(This);
2789 free_layout_eruns(This);
2790 free_layout_runs(This);
2791 release_format_data(&This->format);
2792 heap_free(This->nominal_breakpoints);
2793 heap_free(This->actual_breakpoints);
2794 heap_free(This->clustermetrics);
2795 heap_free(This->clusters);
2796 heap_free(This->linemetrics);
2797 heap_free(This->lines);
2798 heap_free(This->str);
2799 heap_free(This);
2802 return ref;
2805 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2807 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2808 return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment);
2811 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
2813 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2814 return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment);
2817 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping)
2819 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2820 return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping);
2823 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction)
2825 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2826 return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction);
2829 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction)
2831 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2832 TRACE("(%p)->(%d)\n", This, direction);
2833 return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction);
2836 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop)
2838 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2839 TRACE("(%p)->(%.2f)\n", This, tabstop);
2840 return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop);
2843 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming,
2844 IDWriteInlineObject *trimming_sign)
2846 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2847 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
2848 return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign);
2851 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2852 FLOAT line_spacing, FLOAT baseline)
2854 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2855 TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
2856 return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline);
2859 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface)
2861 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2862 return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface);
2865 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface)
2867 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2868 return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface);
2871 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface)
2873 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2874 return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface);
2877 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface)
2879 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2880 return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface);
2883 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface)
2885 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2886 return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface);
2889 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface)
2891 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2892 return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface);
2895 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options,
2896 IDWriteInlineObject **trimming_sign)
2898 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2899 return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign);
2902 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method,
2903 FLOAT *spacing, FLOAT *baseline)
2905 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2906 return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat*)&This->IDWriteTextFormat1_iface, method, spacing, baseline);
2909 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection)
2911 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2912 return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection);
2915 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface)
2917 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2918 return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface);
2921 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
2923 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2924 return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size);
2927 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface)
2929 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2930 return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface);
2933 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface)
2935 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2936 return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface);
2939 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface)
2941 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2942 return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface);
2945 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface)
2947 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2948 return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface);
2951 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface)
2953 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2954 return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface);
2957 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
2959 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2960 return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size);
2963 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth)
2965 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2966 BOOL changed;
2968 TRACE("(%p)->(%.2f)\n", This, maxWidth);
2970 if (maxWidth < 0.0f)
2971 return E_INVALIDARG;
2973 changed = This->metrics.layoutWidth != maxWidth;
2974 This->metrics.layoutWidth = maxWidth;
2976 if (changed)
2977 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
2978 return S_OK;
2981 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight)
2983 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
2984 BOOL changed;
2986 TRACE("(%p)->(%.2f)\n", This, maxHeight);
2988 if (maxHeight < 0.0f)
2989 return E_INVALIDARG;
2991 changed = This->metrics.layoutHeight != maxHeight;
2992 This->metrics.layoutHeight = maxHeight;
2994 if (changed)
2995 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
2996 return S_OK;
2999 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
3001 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3002 struct layout_range_attr_value value;
3004 TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
3006 value.range = range;
3007 value.u.collection = collection;
3008 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
3011 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
3013 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3014 struct layout_range_attr_value value;
3016 TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
3018 if (!name)
3019 return E_INVALIDARG;
3021 value.range = range;
3022 value.u.fontfamily = name;
3023 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
3026 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
3028 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3029 struct layout_range_attr_value value;
3031 TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
3033 if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
3034 return E_INVALIDARG;
3036 value.range = range;
3037 value.u.weight = weight;
3038 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
3041 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
3043 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3044 struct layout_range_attr_value value;
3046 TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
3048 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
3049 return E_INVALIDARG;
3051 value.range = range;
3052 value.u.style = style;
3053 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
3056 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
3058 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3059 struct layout_range_attr_value value;
3061 TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
3063 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
3064 return E_INVALIDARG;
3066 value.range = range;
3067 value.u.stretch = stretch;
3068 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
3071 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
3073 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3074 struct layout_range_attr_value value;
3076 TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
3078 if (size <= 0.0f)
3079 return E_INVALIDARG;
3081 value.range = range;
3082 value.u.fontsize = size;
3083 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
3086 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
3088 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3089 struct layout_range_attr_value value;
3091 TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
3093 value.range = range;
3094 value.u.underline = underline;
3095 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
3098 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
3100 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3101 struct layout_range_attr_value value;
3103 TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
3105 value.range = range;
3106 value.u.strikethrough = strikethrough;
3107 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
3110 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
3112 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3113 struct layout_range_attr_value value;
3115 TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
3117 value.range = range;
3118 value.u.effect = effect;
3119 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
3122 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
3124 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3125 struct layout_range_attr_value value;
3127 TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
3129 value.range = range;
3130 value.u.object = object;
3131 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
3134 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
3136 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3137 struct layout_range_attr_value value;
3139 TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range));
3141 value.range = range;
3142 value.u.typography = typography;
3143 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
3146 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
3148 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3149 struct layout_range_attr_value value;
3151 TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
3153 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
3154 return E_INVALIDARG;
3156 value.range = range;
3157 value.u.locale = locale;
3158 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
3161 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface)
3163 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3164 TRACE("(%p)\n", This);
3165 return This->metrics.layoutWidth;
3168 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface)
3170 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3171 TRACE("(%p)\n", This);
3172 return This->metrics.layoutHeight;
3175 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position,
3176 IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
3178 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3179 struct layout_range *range;
3181 TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
3183 if (position >= This->len)
3184 return S_OK;
3186 range = get_layout_range_by_pos(This, position);
3187 *collection = range->collection;
3188 if (*collection)
3189 IDWriteFontCollection_AddRef(*collection);
3191 return return_range(&range->h, r);
3194 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface,
3195 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
3197 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3198 TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
3199 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
3202 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface,
3203 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
3205 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3206 TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
3207 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
3210 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface,
3211 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
3213 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3214 struct layout_range *range;
3216 TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
3218 if (position >= This->len)
3219 return S_OK;
3221 range = get_layout_range_by_pos(This, position);
3222 *weight = range->weight;
3224 return return_range(&range->h, r);
3227 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface,
3228 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
3230 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3231 struct layout_range *range;
3233 TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
3235 range = get_layout_range_by_pos(This, position);
3236 *style = range->style;
3237 return return_range(&range->h, r);
3240 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface,
3241 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
3243 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3244 struct layout_range *range;
3246 TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
3248 range = get_layout_range_by_pos(This, position);
3249 *stretch = range->stretch;
3250 return return_range(&range->h, r);
3253 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface,
3254 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
3256 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3257 struct layout_range *range;
3259 TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
3261 range = get_layout_range_by_pos(This, position);
3262 *size = range->fontsize;
3263 return return_range(&range->h, r);
3266 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface,
3267 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
3269 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3270 struct layout_range_bool *range;
3272 TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
3274 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position);
3275 *underline = range->value;
3277 return return_range(&range->h, r);
3280 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface,
3281 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
3283 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3284 struct layout_range_bool *range;
3286 TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
3288 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
3289 *strikethrough = range->value;
3291 return return_range(&range->h, r);
3294 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface,
3295 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
3297 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3298 struct layout_range_iface *range;
3300 TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
3302 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position);
3303 *effect = range->iface;
3304 if (*effect)
3305 IUnknown_AddRef(*effect);
3307 return return_range(&range->h, r);
3310 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface,
3311 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
3313 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3314 struct layout_range *range;
3316 TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
3318 if (position >= This->len)
3319 return S_OK;
3321 range = get_layout_range_by_pos(This, position);
3322 *object = range->object;
3323 if (*object)
3324 IDWriteInlineObject_AddRef(*object);
3326 return return_range(&range->h, r);
3329 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface,
3330 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
3332 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3333 struct layout_range_iface *range;
3335 TRACE("(%p)->(%u %p %p)\n", This, position, typography, r);
3337 range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position);
3338 *typography = (IDWriteTypography*)range->iface;
3339 if (*typography)
3340 IDWriteTypography_AddRef(*typography);
3342 return return_range(&range->h, r);
3345 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface,
3346 UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
3348 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3349 TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
3350 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
3353 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface,
3354 UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
3356 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3357 TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
3358 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
3361 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
3362 const DWRITE_MATRIX *m)
3364 FLOAT vec[2], vec2[2];
3366 if (!skiptransform) {
3367 /* apply transform */
3368 vec[0] = 0.0f;
3369 vec[1] = coord * ppdip;
3371 vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx;
3372 vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy;
3374 /* snap */
3375 vec2[0] = floorf(vec2[0] + 0.5f);
3376 vec2[1] = floorf(vec2[1] + 0.5f);
3378 /* apply inverted transform, we don't care about X component at this point */
3379 vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3380 vec[1] /= ppdip;
3382 else
3383 vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
3385 return vec[1];
3388 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface,
3389 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
3391 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3392 BOOL disabled = FALSE, skiptransform = FALSE;
3393 struct layout_effective_inline *inlineobject;
3394 struct layout_effective_run *run;
3395 struct layout_strikethrough *s;
3396 struct layout_underline *u;
3397 FLOAT det = 0.0f, ppdip = 0.0f;
3398 DWRITE_MATRIX m = { 0 };
3399 HRESULT hr;
3401 TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
3403 hr = layout_compute_effective_runs(This);
3404 if (FAILED(hr))
3405 return hr;
3407 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
3408 if (FAILED(hr))
3409 return hr;
3411 if (!disabled) {
3412 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
3413 if (FAILED(hr))
3414 return hr;
3416 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
3417 if (FAILED(hr))
3418 return hr;
3420 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
3421 if (ppdip <= 0.0f ||
3422 (m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
3423 (m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
3424 disabled = TRUE;
3425 else
3426 skiptransform = should_skip_transform(&m, &det);
3429 #define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
3430 /* 1. Regular runs */
3431 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3432 const struct regular_layout_run *regular = &run->run->u.regular;
3433 UINT32 start_glyph = regular->clustermap[run->start];
3434 DWRITE_GLYPH_RUN_DESCRIPTION descr;
3435 DWRITE_GLYPH_RUN glyph_run;
3437 /* Everything but cluster map will be reused from nominal run, as we only need
3438 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
3439 it can't be reused because it has to start with 0 index for each reported run. */
3440 glyph_run = regular->run;
3441 glyph_run.glyphCount = run->glyphcount;
3443 /* fixup glyph data arrays */
3444 glyph_run.glyphIndices += start_glyph;
3445 glyph_run.glyphAdvances += start_glyph;
3446 glyph_run.glyphOffsets += start_glyph;
3448 /* description */
3449 descr = regular->descr;
3450 descr.stringLength = run->length;
3451 descr.string += run->start;
3452 descr.clusterMap = run->clustermap;
3453 descr.textPosition += run->start;
3455 /* return value is ignored */
3456 IDWriteTextRenderer_DrawGlyphRun(renderer,
3457 context,
3458 run->origin_x + run->align_dx + origin_x,
3459 SNAP_COORD(run->origin_y + origin_y),
3460 This->measuringmode,
3461 &glyph_run,
3462 &descr,
3463 run->effect);
3466 /* 2. Inline objects */
3467 LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
3468 IDWriteTextRenderer_DrawInlineObject(renderer,
3469 context,
3470 inlineobject->origin_x + inlineobject->align_dx + origin_x,
3471 SNAP_COORD(inlineobject->origin_y + origin_y),
3472 inlineobject->object,
3473 inlineobject->is_sideways,
3474 inlineobject->is_rtl,
3475 inlineobject->effect);
3478 /* 3. Underlines */
3479 LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) {
3480 IDWriteTextRenderer_DrawUnderline(renderer,
3481 context,
3482 /* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
3483 (is_run_rtl(u->run) ? u->run->origin_x - u->run->width : u->run->origin_x) + u->run->align_dx + origin_x,
3484 SNAP_COORD(u->run->origin_y + origin_y),
3485 &u->u,
3486 u->run->effect);
3489 /* 4. Strikethrough */
3490 LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
3491 IDWriteTextRenderer_DrawStrikethrough(renderer,
3492 context,
3493 s->run->origin_x + s->run->align_dx + origin_x,
3494 SNAP_COORD(s->run->origin_y + origin_y),
3495 &s->s,
3496 s->run->effect);
3498 #undef SNAP_COORD
3500 return S_OK;
3503 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface,
3504 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
3506 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3507 HRESULT hr;
3509 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3511 hr = layout_compute_effective_runs(This);
3512 if (FAILED(hr))
3513 return hr;
3515 if (metrics) {
3516 UINT32 i, c = min(max_count, This->metrics.lineCount);
3517 for (i = 0; i < c; i++)
3518 memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics));
3521 *count = This->metrics.lineCount;
3522 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3525 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics)
3527 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3528 DWRITE_TEXT_METRICS1 metrics1;
3529 HRESULT hr;
3531 TRACE("(%p)->(%p)\n", This, metrics);
3533 hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1);
3534 if (hr == S_OK)
3535 memcpy(metrics, &metrics1, sizeof(*metrics));
3537 return hr;
3540 static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D_RECT_F *ret)
3542 #define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em)
3543 ret->left = SCALE(bbox->left);
3544 ret->right = SCALE(bbox->right);
3545 ret->top = SCALE(bbox->top);
3546 ret->bottom = SCALE(bbox->bottom);
3547 #undef SCALE
3550 static void d2d_rect_offset(D2D_RECT_F *rect, FLOAT x, FLOAT y)
3552 rect->left += x;
3553 rect->right += x;
3554 rect->top += y;
3555 rect->bottom += y;
3558 static BOOL d2d_rect_is_empty(const D2D_RECT_F *rect)
3560 return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
3563 static void d2d_rect_union(D2D_RECT_F *dst, const D2D_RECT_F *src)
3565 if (d2d_rect_is_empty(dst)) {
3566 if (d2d_rect_is_empty(src)) {
3567 dst->left = dst->right = dst->top = dst->bottom = 0.0f;
3568 return;
3570 else
3571 *dst = *src;
3573 else {
3574 if (!d2d_rect_is_empty(src)) {
3575 dst->left = min(dst->left, src->left);
3576 dst->right = max(dst->right, src->right);
3577 dst->top = min(dst->top, src->top);
3578 dst->bottom = max(dst->bottom, src->bottom);
3583 static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D_RECT_F *bbox)
3585 const struct regular_layout_run *regular = &run->run->u.regular;
3586 UINT32 start_glyph = regular->clustermap[run->start];
3587 const DWRITE_GLYPH_RUN *glyph_run = &regular->run;
3588 DWRITE_FONT_METRICS font_metrics;
3589 D2D_POINT_2F origin = { 0 };
3590 UINT32 i;
3592 IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics);
3594 origin.x = run->origin_x + run->align_dx;
3595 origin.y = run->origin_y;
3596 for (i = 0; i < run->glyphcount; i++) {
3597 D2D_RECT_F glyph_bbox;
3598 RECT design_bbox;
3600 freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm,
3601 glyph_run->glyphIndices[i + start_glyph], &design_bbox);
3603 scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox);
3604 d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
3605 origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
3606 d2d_rect_union(bbox, &glyph_bbox);
3608 /* FIXME: take care of vertical/rtl */
3609 origin.x += glyph_run->glyphAdvances[i + start_glyph];
3613 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface,
3614 DWRITE_OVERHANG_METRICS *overhangs)
3616 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3617 struct layout_effective_run *run;
3618 D2D_RECT_F bbox = { 0 };
3619 HRESULT hr;
3621 TRACE("(%p)->(%p)\n", This, overhangs);
3623 memset(overhangs, 0, sizeof(*overhangs));
3625 if (!(This->recompute & RECOMPUTE_OVERHANGS)) {
3626 *overhangs = This->overhangs;
3627 return S_OK;
3630 hr = layout_compute_effective_runs(This);
3631 if (FAILED(hr))
3632 return hr;
3634 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
3635 D2D_RECT_F run_bbox;
3637 layout_get_erun_bbox(This, run, &run_bbox);
3638 d2d_rect_union(&bbox, &run_bbox);
3641 /* FIXME: iterate over inline objects too */
3643 /* deltas from text content metrics */
3644 This->overhangs.left = -bbox.left;
3645 This->overhangs.top = -bbox.top;
3646 This->overhangs.right = bbox.right - This->metrics.layoutWidth;
3647 This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight;
3648 This->recompute &= ~RECOMPUTE_OVERHANGS;
3650 *overhangs = This->overhangs;
3652 return S_OK;
3655 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface,
3656 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
3658 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3659 HRESULT hr;
3661 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3663 hr = layout_compute(This);
3664 if (FAILED(hr))
3665 return hr;
3667 if (metrics)
3668 memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
3670 *count = This->cluster_count;
3671 return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3674 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width)
3676 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3677 UINT32 start;
3678 FLOAT width;
3679 HRESULT hr;
3681 TRACE("(%p)->(%p)\n", This, min_width);
3683 if (!min_width)
3684 return E_INVALIDARG;
3686 if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
3687 goto width_done;
3689 *min_width = 0.0f;
3690 hr = layout_compute(This);
3691 if (FAILED(hr))
3692 return hr;
3694 /* Find widest word without emergency breaking between clusters, trailing whitespaces
3695 preceding breaking point do not contribute to word width. */
3696 for (start = 0; start < This->cluster_count;) {
3697 UINT32 end = start, j, next;
3699 /* Last cluster always could be wrapped after. */
3700 while (!This->clustermetrics[end].canWrapLineAfter)
3701 end++;
3702 /* make is so current cluster range that we can wrap after is [start,end) */
3703 end++;
3705 next = end;
3707 /* Ignore trailing whitespace clusters, in case of single space range will
3708 be reduced to empty range, or [start,start+1). */
3709 while (end > start && This->clustermetrics[end-1].isWhitespace)
3710 end--;
3712 /* check if cluster range exceeds last minimal width */
3713 width = 0.0f;
3714 for (j = start; j < end; j++)
3715 width += This->clustermetrics[j].width;
3717 start = next;
3719 if (width > This->minwidth)
3720 This->minwidth = width;
3722 This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3724 width_done:
3725 *min_width = This->minwidth;
3726 return S_OK;
3729 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface,
3730 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3732 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3733 FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
3734 return E_NOTIMPL;
3737 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface,
3738 UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
3740 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3741 FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
3742 return E_NOTIMPL;
3745 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface,
3746 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3747 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3749 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3750 FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
3751 max_metricscount, actual_metricscount);
3752 return E_NOTIMPL;
3755 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled,
3756 DWRITE_TEXT_RANGE range)
3758 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3759 struct layout_range_attr_value value;
3761 TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
3763 value.range = range;
3764 value.u.pair_kerning = !!is_pairkerning_enabled;
3765 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3768 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
3769 DWRITE_TEXT_RANGE *r)
3771 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3772 struct layout_range *range;
3774 TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
3776 if (position >= This->len)
3777 return S_OK;
3779 range = get_layout_range_by_pos(This, position);
3780 *is_pairkerning_enabled = range->pair_kerning;
3782 return return_range(&range->h, r);
3785 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing,
3786 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3788 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3789 struct layout_range_attr_value value;
3791 TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
3793 if (min_advance < 0.0f)
3794 return E_INVALIDARG;
3796 value.range = range;
3797 value.u.spacing[0] = leading;
3798 value.u.spacing[1] = trailing;
3799 value.u.spacing[2] = min_advance;
3800 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
3803 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading,
3804 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3806 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3807 struct layout_range_spacing *range;
3809 TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
3811 range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
3812 *leading = range->leading;
3813 *trailing = range->trailing;
3814 *min_advance = range->min_advance;
3816 return return_range(&range->h, r);
3819 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics)
3821 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3822 HRESULT hr;
3824 TRACE("(%p)->(%p)\n", This, metrics);
3826 hr = layout_compute_effective_runs(This);
3827 if (FAILED(hr))
3828 return hr;
3830 *metrics = This->metrics;
3831 return S_OK;
3834 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3836 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3838 TRACE("(%p)->(%d)\n", This, orientation);
3840 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3841 return E_INVALIDARG;
3843 This->format.vertical_orientation = orientation;
3844 return S_OK;
3847 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface)
3849 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3850 TRACE("(%p)\n", This);
3851 return This->format.vertical_orientation;
3854 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled)
3856 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3857 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
3858 return IDWriteTextFormat1_SetLastLineWrapping(&This->IDWriteTextFormat1_iface, lastline_wrapping_enabled);
3861 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface)
3863 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3864 TRACE("(%p)\n", This);
3865 return IDWriteTextFormat1_GetLastLineWrapping(&This->IDWriteTextFormat1_iface);
3868 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3870 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3871 TRACE("(%p)->(%d)\n", This, alignment);
3872 return IDWriteTextFormat1_SetOpticalAlignment(&This->IDWriteTextFormat1_iface, alignment);
3875 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface)
3877 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3878 TRACE("(%p)\n", This);
3879 return IDWriteTextFormat1_GetOpticalAlignment(&This->IDWriteTextFormat1_iface);
3882 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback)
3884 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3885 TRACE("(%p)->(%p)\n", This, fallback);
3886 return set_fontfallback_for_format(&This->format, fallback);
3889 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback)
3891 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3892 TRACE("(%p)->(%p)\n", This, fallback);
3893 return get_fontfallback_from_format(&This->format, fallback);
3896 static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface)
3898 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3900 TRACE("(%p)\n", This);
3902 This->recompute = RECOMPUTE_EVERYTHING;
3903 return S_OK;
3906 static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing)
3908 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3909 BOOL changed;
3910 HRESULT hr;
3912 TRACE("(%p)->(%p)\n", This, spacing);
3914 hr = format_set_linespacing(&This->format, spacing, &changed);
3915 if (FAILED(hr))
3916 return hr;
3918 if (changed) {
3919 if (!(This->recompute & RECOMPUTE_LINES)) {
3920 UINT32 line;
3922 switch (This->format.spacing.method)
3924 case DWRITE_LINE_SPACING_METHOD_DEFAULT:
3925 for (line = 0; line < This->metrics.lineCount; line++) {
3926 This->linemetrics[line].height = This->lines[line].height;
3927 This->linemetrics[line].baseline = This->lines[line].baseline;
3929 break;
3930 case DWRITE_LINE_SPACING_METHOD_UNIFORM:
3931 for (line = 0; line < This->metrics.lineCount; line++) {
3932 This->linemetrics[line].height = This->format.spacing.height;
3933 This->linemetrics[line].baseline = This->format.spacing.baseline;
3935 break;
3936 case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
3937 for (line = 0; line < This->metrics.lineCount; line++) {
3938 This->linemetrics[line].height = This->format.spacing.height * This->lines[line].height;
3939 This->linemetrics[line].baseline = This->format.spacing.baseline * This->lines[line].baseline;
3941 break;
3942 default:
3946 layout_set_line_positions(This);
3949 This->recompute |= RECOMPUTE_OVERHANGS;
3952 return S_OK;
3955 static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing)
3957 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3959 TRACE("(%p)->(%p)\n", This, spacing);
3961 *spacing = This->format.spacing;
3962 return S_OK;
3965 static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics,
3966 UINT32 max_count, UINT32 *count)
3968 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
3969 HRESULT hr;
3971 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
3973 hr = layout_compute_effective_runs(This);
3974 if (FAILED(hr))
3975 return hr;
3977 if (metrics)
3978 memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount));
3980 *count = This->metrics.lineCount;
3981 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
3984 static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = {
3985 dwritetextlayout_QueryInterface,
3986 dwritetextlayout_AddRef,
3987 dwritetextlayout_Release,
3988 dwritetextlayout_SetTextAlignment,
3989 dwritetextlayout_SetParagraphAlignment,
3990 dwritetextlayout_SetWordWrapping,
3991 dwritetextlayout_SetReadingDirection,
3992 dwritetextlayout_SetFlowDirection,
3993 dwritetextlayout_SetIncrementalTabStop,
3994 dwritetextlayout_SetTrimming,
3995 dwritetextlayout_SetLineSpacing,
3996 dwritetextlayout_GetTextAlignment,
3997 dwritetextlayout_GetParagraphAlignment,
3998 dwritetextlayout_GetWordWrapping,
3999 dwritetextlayout_GetReadingDirection,
4000 dwritetextlayout_GetFlowDirection,
4001 dwritetextlayout_GetIncrementalTabStop,
4002 dwritetextlayout_GetTrimming,
4003 dwritetextlayout_GetLineSpacing,
4004 dwritetextlayout_GetFontCollection,
4005 dwritetextlayout_GetFontFamilyNameLength,
4006 dwritetextlayout_GetFontFamilyName,
4007 dwritetextlayout_GetFontWeight,
4008 dwritetextlayout_GetFontStyle,
4009 dwritetextlayout_GetFontStretch,
4010 dwritetextlayout_GetFontSize,
4011 dwritetextlayout_GetLocaleNameLength,
4012 dwritetextlayout_GetLocaleName,
4013 dwritetextlayout_SetMaxWidth,
4014 dwritetextlayout_SetMaxHeight,
4015 dwritetextlayout_SetFontCollection,
4016 dwritetextlayout_SetFontFamilyName,
4017 dwritetextlayout_SetFontWeight,
4018 dwritetextlayout_SetFontStyle,
4019 dwritetextlayout_SetFontStretch,
4020 dwritetextlayout_SetFontSize,
4021 dwritetextlayout_SetUnderline,
4022 dwritetextlayout_SetStrikethrough,
4023 dwritetextlayout_SetDrawingEffect,
4024 dwritetextlayout_SetInlineObject,
4025 dwritetextlayout_SetTypography,
4026 dwritetextlayout_SetLocaleName,
4027 dwritetextlayout_GetMaxWidth,
4028 dwritetextlayout_GetMaxHeight,
4029 dwritetextlayout_layout_GetFontCollection,
4030 dwritetextlayout_layout_GetFontFamilyNameLength,
4031 dwritetextlayout_layout_GetFontFamilyName,
4032 dwritetextlayout_layout_GetFontWeight,
4033 dwritetextlayout_layout_GetFontStyle,
4034 dwritetextlayout_layout_GetFontStretch,
4035 dwritetextlayout_layout_GetFontSize,
4036 dwritetextlayout_GetUnderline,
4037 dwritetextlayout_GetStrikethrough,
4038 dwritetextlayout_GetDrawingEffect,
4039 dwritetextlayout_GetInlineObject,
4040 dwritetextlayout_GetTypography,
4041 dwritetextlayout_layout_GetLocaleNameLength,
4042 dwritetextlayout_layout_GetLocaleName,
4043 dwritetextlayout_Draw,
4044 dwritetextlayout_GetLineMetrics,
4045 dwritetextlayout_GetMetrics,
4046 dwritetextlayout_GetOverhangMetrics,
4047 dwritetextlayout_GetClusterMetrics,
4048 dwritetextlayout_DetermineMinWidth,
4049 dwritetextlayout_HitTestPoint,
4050 dwritetextlayout_HitTestTextPosition,
4051 dwritetextlayout_HitTestTextRange,
4052 dwritetextlayout1_SetPairKerning,
4053 dwritetextlayout1_GetPairKerning,
4054 dwritetextlayout1_SetCharacterSpacing,
4055 dwritetextlayout1_GetCharacterSpacing,
4056 dwritetextlayout2_GetMetrics,
4057 dwritetextlayout2_SetVerticalGlyphOrientation,
4058 dwritetextlayout2_GetVerticalGlyphOrientation,
4059 dwritetextlayout2_SetLastLineWrapping,
4060 dwritetextlayout2_GetLastLineWrapping,
4061 dwritetextlayout2_SetOpticalAlignment,
4062 dwritetextlayout2_GetOpticalAlignment,
4063 dwritetextlayout2_SetFontFallback,
4064 dwritetextlayout2_GetFontFallback,
4065 dwritetextlayout3_InvalidateLayout,
4066 dwritetextlayout3_SetLineSpacing,
4067 dwritetextlayout3_GetLineSpacing,
4068 dwritetextlayout3_GetLineMetrics
4071 static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
4073 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4074 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4075 return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj);
4078 static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat1 *iface)
4080 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4081 return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface);
4084 static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat1 *iface)
4086 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4087 return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface);
4090 static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
4092 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4093 BOOL changed;
4094 HRESULT hr;
4096 TRACE("(%p)->(%d)\n", This, alignment);
4098 hr = format_set_textalignment(&This->format, alignment, &changed);
4099 if (FAILED(hr))
4100 return hr;
4102 /* if layout is not ready there's nothing to align */
4103 if (changed && !(This->recompute & RECOMPUTE_LINES))
4104 layout_apply_text_alignment(This);
4106 return S_OK;
4109 static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
4111 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4112 BOOL changed;
4113 HRESULT hr;
4115 TRACE("(%p)->(%d)\n", This, alignment);
4117 hr = format_set_paralignment(&This->format, alignment, &changed);
4118 if (FAILED(hr))
4119 return hr;
4121 /* if layout is not ready there's nothing to align */
4122 if (changed && !(This->recompute & RECOMPUTE_LINES))
4123 layout_apply_par_alignment(This);
4125 return S_OK;
4128 static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
4130 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4131 BOOL changed;
4132 HRESULT hr;
4134 TRACE("(%p)->(%d)\n", This, wrapping);
4136 hr = format_set_wordwrapping(&This->format, wrapping, &changed);
4137 if (FAILED(hr))
4138 return hr;
4140 if (changed)
4141 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4143 return S_OK;
4146 static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
4148 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4149 BOOL changed;
4150 HRESULT hr;
4152 TRACE("(%p)->(%d)\n", This, direction);
4154 hr = format_set_readingdirection(&This->format, direction, &changed);
4155 if (FAILED(hr))
4156 return hr;
4158 if (changed)
4159 This->recompute = RECOMPUTE_EVERYTHING;
4161 return S_OK;
4164 static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
4166 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4167 BOOL changed;
4168 HRESULT hr;
4170 TRACE("(%p)->(%d)\n", This, direction);
4172 hr = format_set_flowdirection(&This->format, direction, &changed);
4173 if (FAILED(hr))
4174 return hr;
4176 if (changed)
4177 This->recompute = RECOMPUTE_EVERYTHING;
4179 return S_OK;
4182 static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
4184 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4185 FIXME("(%p)->(%f): stub\n", This, tabstop);
4186 return E_NOTIMPL;
4189 static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
4190 IDWriteInlineObject *trimming_sign)
4192 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4193 BOOL changed;
4194 HRESULT hr;
4196 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
4198 hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed);
4200 if (changed)
4201 This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
4203 return hr;
4206 static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method,
4207 FLOAT height, FLOAT baseline)
4209 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4210 DWRITE_LINE_SPACING spacing;
4212 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
4214 spacing = This->format.spacing;
4215 spacing.method = method;
4216 spacing.height = height;
4217 spacing.baseline = baseline;
4218 return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing);
4221 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat1 *iface)
4223 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4224 TRACE("(%p)\n", This);
4225 return This->format.textalignment;
4228 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface)
4230 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4231 TRACE("(%p)\n", This);
4232 return This->format.paralign;
4235 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat1 *iface)
4237 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4238 TRACE("(%p)\n", This);
4239 return This->format.wrapping;
4242 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat1 *iface)
4244 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4245 TRACE("(%p)\n", This);
4246 return This->format.readingdir;
4249 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat1 *iface)
4251 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4252 TRACE("(%p)\n", This);
4253 return This->format.flow;
4256 static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
4258 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4259 FIXME("(%p): stub\n", This);
4260 return 0.0f;
4263 static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
4264 IDWriteInlineObject **trimming_sign)
4266 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4268 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
4270 *options = This->format.trimming;
4271 *trimming_sign = This->format.trimmingsign;
4272 if (*trimming_sign)
4273 IDWriteInlineObject_AddRef(*trimming_sign);
4274 return S_OK;
4277 static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
4278 FLOAT *spacing, FLOAT *baseline)
4280 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4282 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
4284 *method = This->format.spacing.method;
4285 *spacing = This->format.spacing.height;
4286 *baseline = This->format.spacing.baseline;
4287 return S_OK;
4290 static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
4292 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4294 TRACE("(%p)->(%p)\n", This, collection);
4296 *collection = This->format.collection;
4297 if (*collection)
4298 IDWriteFontCollection_AddRef(*collection);
4299 return S_OK;
4302 static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
4304 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4305 TRACE("(%p)\n", This);
4306 return This->format.family_len;
4309 static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4311 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4313 TRACE("(%p)->(%p %u)\n", This, name, size);
4315 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4316 strcpyW(name, This->format.family_name);
4317 return S_OK;
4320 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat1 *iface)
4322 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4323 TRACE("(%p)\n", This);
4324 return This->format.weight;
4327 static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat1 *iface)
4329 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4330 TRACE("(%p)\n", This);
4331 return This->format.style;
4334 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat1 *iface)
4336 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4337 TRACE("(%p)\n", This);
4338 return This->format.stretch;
4341 static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat1 *iface)
4343 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4344 TRACE("(%p)\n", This);
4345 return This->format.fontsize;
4348 static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface)
4350 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4351 TRACE("(%p)\n", This);
4352 return This->format.locale_len;
4355 static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4357 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4359 TRACE("(%p)->(%p %u)\n", This, name, size);
4361 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4362 strcpyW(name, This->format.locale);
4363 return S_OK;
4366 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4368 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4369 FIXME("(%p)->(%d): stub\n", This, orientation);
4370 return E_NOTIMPL;
4373 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
4375 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4376 FIXME("(%p): stub\n", This);
4377 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4380 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
4382 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4384 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
4386 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
4387 return S_OK;
4390 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface)
4392 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4393 TRACE("(%p)\n", This);
4394 return This->format.last_line_wrapping;
4397 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
4399 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4400 TRACE("(%p)->(%d)\n", This, alignment);
4401 return format_set_optical_alignment(&This->format, alignment);
4404 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface)
4406 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4407 TRACE("(%p)\n", This);
4408 return This->format.optical_alignment;
4411 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
4413 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4414 TRACE("(%p)->(%p)\n", This, fallback);
4415 return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4418 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
4420 struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
4421 TRACE("(%p)->(%p)\n", This, fallback);
4422 return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
4425 static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = {
4426 dwritetextformat_layout_QueryInterface,
4427 dwritetextformat_layout_AddRef,
4428 dwritetextformat_layout_Release,
4429 dwritetextformat_layout_SetTextAlignment,
4430 dwritetextformat_layout_SetParagraphAlignment,
4431 dwritetextformat_layout_SetWordWrapping,
4432 dwritetextformat_layout_SetReadingDirection,
4433 dwritetextformat_layout_SetFlowDirection,
4434 dwritetextformat_layout_SetIncrementalTabStop,
4435 dwritetextformat_layout_SetTrimming,
4436 dwritetextformat_layout_SetLineSpacing,
4437 dwritetextformat_layout_GetTextAlignment,
4438 dwritetextformat_layout_GetParagraphAlignment,
4439 dwritetextformat_layout_GetWordWrapping,
4440 dwritetextformat_layout_GetReadingDirection,
4441 dwritetextformat_layout_GetFlowDirection,
4442 dwritetextformat_layout_GetIncrementalTabStop,
4443 dwritetextformat_layout_GetTrimming,
4444 dwritetextformat_layout_GetLineSpacing,
4445 dwritetextformat_layout_GetFontCollection,
4446 dwritetextformat_layout_GetFontFamilyNameLength,
4447 dwritetextformat_layout_GetFontFamilyName,
4448 dwritetextformat_layout_GetFontWeight,
4449 dwritetextformat_layout_GetFontStyle,
4450 dwritetextformat_layout_GetFontStretch,
4451 dwritetextformat_layout_GetFontSize,
4452 dwritetextformat_layout_GetLocaleNameLength,
4453 dwritetextformat_layout_GetLocaleName,
4454 dwritetextformat1_layout_SetVerticalGlyphOrientation,
4455 dwritetextformat1_layout_GetVerticalGlyphOrientation,
4456 dwritetextformat1_layout_SetLastLineWrapping,
4457 dwritetextformat1_layout_GetLastLineWrapping,
4458 dwritetextformat1_layout_SetOpticalAlignment,
4459 dwritetextformat1_layout_GetOpticalAlignment,
4460 dwritetextformat1_layout_SetFontFallback,
4461 dwritetextformat1_layout_GetFontFallback,
4464 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
4465 REFIID riid, void **obj)
4467 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
4468 IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
4469 IsEqualIID(riid, &IID_IUnknown))
4471 *obj = iface;
4472 IDWriteTextAnalysisSink1_AddRef(iface);
4473 return S_OK;
4476 *obj = NULL;
4477 return E_NOINTERFACE;
4480 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
4482 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4483 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4486 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
4488 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4489 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4492 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
4493 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
4495 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4496 struct layout_run *run;
4498 TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
4500 run = alloc_layout_run(LAYOUT_RUN_REGULAR);
4501 if (!run)
4502 return E_OUTOFMEMORY;
4504 run->u.regular.descr.string = &layout->str[position];
4505 run->u.regular.descr.stringLength = length;
4506 run->u.regular.descr.textPosition = position;
4507 run->u.regular.sa = *sa;
4508 list_add_tail(&layout->runs, &run->entry);
4509 return S_OK;
4512 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
4513 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
4515 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4517 if (position + length > layout->len)
4518 return E_FAIL;
4520 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
4521 return S_OK;
4524 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
4525 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
4527 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
4528 struct layout_run *cur_run;
4530 TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
4532 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
4533 struct regular_layout_run *cur = &cur_run->u.regular;
4534 struct layout_run *run;
4536 if (cur_run->kind == LAYOUT_RUN_INLINE)
4537 continue;
4539 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
4540 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
4541 continue;
4543 /* full hit - just set run level */
4544 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
4545 cur->run.bidiLevel = resolvedLevel;
4546 break;
4549 /* current run is fully covered, move to next one */
4550 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
4551 cur->run.bidiLevel = resolvedLevel;
4552 position += cur->descr.stringLength;
4553 length -= cur->descr.stringLength;
4554 continue;
4557 /* all fully covered runs are processed at this point, reuse existing run for remaining
4558 reported bidi range and add another run for the rest of original one */
4560 run = alloc_layout_run(LAYOUT_RUN_REGULAR);
4561 if (!run)
4562 return E_OUTOFMEMORY;
4564 *run = *cur_run;
4565 run->u.regular.descr.textPosition = position + length;
4566 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
4567 run->u.regular.descr.string = &layout->str[position + length];
4569 /* reduce existing run */
4570 cur->run.bidiLevel = resolvedLevel;
4571 cur->descr.stringLength = length;
4573 list_add_after(&cur_run->entry, &run->entry);
4574 break;
4577 return S_OK;
4580 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
4581 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
4583 return E_NOTIMPL;
4586 static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
4587 UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
4588 BOOL is_sideways, BOOL is_rtl)
4590 return E_NOTIMPL;
4593 static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
4594 dwritetextlayout_sink_QueryInterface,
4595 dwritetextlayout_sink_AddRef,
4596 dwritetextlayout_sink_Release,
4597 dwritetextlayout_sink_SetScriptAnalysis,
4598 dwritetextlayout_sink_SetLineBreakpoints,
4599 dwritetextlayout_sink_SetBidiLevel,
4600 dwritetextlayout_sink_SetNumberSubstitution,
4601 dwritetextlayout_sink_SetGlyphOrientation
4604 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
4605 REFIID riid, void **obj)
4607 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
4608 IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
4609 IsEqualIID(riid, &IID_IUnknown))
4611 *obj = iface;
4612 IDWriteTextAnalysisSource1_AddRef(iface);
4613 return S_OK;
4616 *obj = NULL;
4617 return E_NOINTERFACE;
4620 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
4622 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4623 return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
4626 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
4628 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4629 return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4632 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
4633 UINT32 position, WCHAR const** text, UINT32* text_len)
4635 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4637 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4639 if (position < layout->len) {
4640 *text = &layout->str[position];
4641 *text_len = layout->len - position;
4643 else {
4644 *text = NULL;
4645 *text_len = 0;
4648 return S_OK;
4651 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
4652 UINT32 position, WCHAR const** text, UINT32* text_len)
4654 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4656 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
4658 if (position > 0 && position < layout->len) {
4659 *text = layout->str;
4660 *text_len = position;
4662 else {
4663 *text = NULL;
4664 *text_len = 0;
4667 return S_OK;
4670 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
4672 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4673 return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface);
4676 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
4677 UINT32 position, UINT32* text_len, WCHAR const** locale)
4679 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
4680 struct layout_range *range = get_layout_range_by_pos(layout, position);
4682 if (position < layout->len) {
4683 struct layout_range *next;
4685 *locale = range->locale;
4686 *text_len = range->h.range.length - position;
4688 next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
4689 while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
4690 *text_len += next->h.range.length;
4691 next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
4694 *text_len = min(*text_len, layout->len - position);
4696 else {
4697 *locale = NULL;
4698 *text_len = 0;
4701 return S_OK;
4704 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
4705 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
4707 FIXME("%u %p %p: stub\n", position, text_len, substitution);
4708 return E_NOTIMPL;
4711 static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
4712 UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
4714 FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
4715 return E_NOTIMPL;
4718 static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
4719 dwritetextlayout_source_QueryInterface,
4720 dwritetextlayout_source_AddRef,
4721 dwritetextlayout_source_Release,
4722 dwritetextlayout_source_GetTextAtPosition,
4723 dwritetextlayout_source_GetTextBeforePosition,
4724 dwritetextlayout_source_GetParagraphReadingDirection,
4725 dwritetextlayout_source_GetLocaleName,
4726 dwritetextlayout_source_GetNumberSubstitution,
4727 dwritetextlayout_source_GetVerticalGlyphOrientation
4730 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
4732 struct dwrite_textformat *textformat;
4733 IDWriteTextFormat1 *format1;
4734 UINT32 len;
4735 HRESULT hr;
4737 if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
4738 layout->format = textformat->format;
4740 layout->format.locale = heap_strdupW(textformat->format.locale);
4741 layout->format.family_name = heap_strdupW(textformat->format.family_name);
4742 if (!layout->format.locale || !layout->format.family_name)
4744 heap_free(layout->format.locale);
4745 heap_free(layout->format.family_name);
4746 return E_OUTOFMEMORY;
4749 if (layout->format.trimmingsign)
4750 IDWriteInlineObject_AddRef(layout->format.trimmingsign);
4751 if (layout->format.collection)
4752 IDWriteFontCollection_AddRef(layout->format.collection);
4753 if (layout->format.fallback)
4754 IDWriteFontFallback_AddRef(layout->format.fallback);
4756 return S_OK;
4759 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
4760 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
4761 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
4762 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
4763 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
4764 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
4765 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
4766 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
4767 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
4768 layout->format.fallback = NULL;
4769 layout->format.spacing.leadingBefore = 0.0f;
4770 layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
4771 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
4772 &layout->format.spacing.height, &layout->format.spacing.baseline);
4773 if (FAILED(hr))
4774 return hr;
4776 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
4777 if (FAILED(hr))
4778 return hr;
4780 /* locale name and length */
4781 len = IDWriteTextFormat_GetLocaleNameLength(format);
4782 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
4783 if (!layout->format.locale)
4784 return E_OUTOFMEMORY;
4786 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
4787 if (FAILED(hr))
4788 return hr;
4789 layout->format.locale_len = len;
4791 /* font family name and length */
4792 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
4793 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
4794 if (!layout->format.family_name)
4795 return E_OUTOFMEMORY;
4797 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
4798 if (FAILED(hr))
4799 return hr;
4800 layout->format.family_len = len;
4802 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4803 if (hr == S_OK) {
4804 IDWriteTextFormat2 *format2;
4806 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
4807 layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4808 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
4810 if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
4811 IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
4812 IDWriteTextFormat2_Release(format2);
4815 IDWriteTextFormat1_Release(format1);
4817 else {
4818 layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4819 layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
4822 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
4825 static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
4827 struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
4828 static const DWRITE_TEXT_RANGE r = { 0, ~0u };
4829 HRESULT hr;
4831 layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl;
4832 layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl;
4833 layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
4834 layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
4835 layout->ref = 1;
4836 layout->len = desc->length;
4837 layout->recompute = RECOMPUTE_EVERYTHING;
4838 layout->nominal_breakpoints = NULL;
4839 layout->actual_breakpoints = NULL;
4840 layout->cluster_count = 0;
4841 layout->clustermetrics = NULL;
4842 layout->clusters = NULL;
4843 layout->linemetrics = NULL;
4844 layout->lines = NULL;
4845 layout->line_alloc = 0;
4846 layout->minwidth = 0.0f;
4847 list_init(&layout->eruns);
4848 list_init(&layout->inlineobjects);
4849 list_init(&layout->underlines);
4850 list_init(&layout->strikethrough);
4851 list_init(&layout->runs);
4852 list_init(&layout->ranges);
4853 list_init(&layout->strike_ranges);
4854 list_init(&layout->underline_ranges);
4855 list_init(&layout->effects);
4856 list_init(&layout->spacing);
4857 list_init(&layout->typographies);
4858 memset(&layout->format, 0, sizeof(layout->format));
4859 memset(&layout->metrics, 0, sizeof(layout->metrics));
4860 layout->metrics.layoutWidth = desc->max_width;
4861 layout->metrics.layoutHeight = desc->max_height;
4862 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4864 layout->ppdip = 0.0f;
4865 memset(&layout->transform, 0, sizeof(layout->transform));
4867 layout->str = heap_strdupnW(desc->string, desc->length);
4868 if (desc->length && !layout->str) {
4869 hr = E_OUTOFMEMORY;
4870 goto fail;
4873 hr = layout_format_from_textformat(layout, desc->format);
4874 if (FAILED(hr))
4875 goto fail;
4877 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
4878 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
4879 underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
4880 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
4881 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
4882 typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
4883 if (!range || !strike || !effect || !spacing || !typography || !underline) {
4884 free_layout_range(range);
4885 free_layout_range(strike);
4886 free_layout_range(underline);
4887 free_layout_range(effect);
4888 free_layout_range(spacing);
4889 free_layout_range(typography);
4890 hr = E_OUTOFMEMORY;
4891 goto fail;
4894 if (desc->is_gdi_compatible)
4895 layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
4896 else
4897 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
4898 layout->ppdip = desc->ppdip;
4899 layout->transform = desc->transform ? *desc->transform : identity;
4901 layout->factory = desc->factory;
4902 IDWriteFactory4_AddRef(layout->factory);
4903 list_add_head(&layout->ranges, &range->entry);
4904 list_add_head(&layout->strike_ranges, &strike->entry);
4905 list_add_head(&layout->underline_ranges, &underline->entry);
4906 list_add_head(&layout->effects, &effect->entry);
4907 list_add_head(&layout->spacing, &spacing->entry);
4908 list_add_head(&layout->typographies, &typography->entry);
4909 return S_OK;
4911 fail:
4912 IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
4913 return hr;
4916 HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret)
4918 struct dwrite_textlayout *layout;
4919 HRESULT hr;
4921 *ret = NULL;
4923 if (!desc->format || !desc->string)
4924 return E_INVALIDARG;
4926 layout = heap_alloc(sizeof(struct dwrite_textlayout));
4927 if (!layout) return E_OUTOFMEMORY;
4929 hr = init_textlayout(desc, layout);
4930 if (hr == S_OK)
4931 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface;
4933 return hr;
4936 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
4938 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4940 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4942 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
4943 *obj = iface;
4944 IDWriteInlineObject_AddRef(iface);
4945 return S_OK;
4948 *obj = NULL;
4949 return E_NOINTERFACE;
4952 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
4954 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4955 ULONG ref = InterlockedIncrement(&This->ref);
4956 TRACE("(%p)->(%d)\n", This, ref);
4957 return ref;
4960 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
4962 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4963 ULONG ref = InterlockedDecrement(&This->ref);
4965 TRACE("(%p)->(%d)\n", This, ref);
4967 if (!ref) {
4968 IDWriteTextLayout_Release(This->layout);
4969 heap_free(This);
4972 return ref;
4975 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
4976 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
4978 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4979 DWRITE_TEXT_RANGE range = { 0, ~0u };
4980 DWRITE_TEXT_METRICS metrics;
4981 DWRITE_LINE_METRICS line;
4982 UINT32 line_count;
4983 HRESULT hr;
4985 TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY, is_sideways, is_rtl, effect);
4987 IDWriteTextLayout_SetDrawingEffect(This->layout, effect, range);
4988 IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count);
4989 IDWriteTextLayout_GetMetrics(This->layout, &metrics);
4990 hr = IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline);
4991 IDWriteTextLayout_SetDrawingEffect(This->layout, NULL, range);
4992 return hr;
4995 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
4997 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4998 DWRITE_TEXT_METRICS metrics;
4999 HRESULT hr;
5001 TRACE("(%p)->(%p)\n", This, ret);
5003 hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
5004 if (FAILED(hr)) {
5005 memset(ret, 0, sizeof(*ret));
5006 return hr;
5009 ret->width = metrics.width;
5010 ret->height = 0.0f;
5011 ret->baseline = 0.0f;
5012 ret->supportsSideways = FALSE;
5013 return S_OK;
5016 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
5018 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5019 TRACE("(%p)->(%p)\n", This, overhangs);
5020 return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs);
5023 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
5024 DWRITE_BREAK_CONDITION *after)
5026 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
5028 TRACE("(%p)->(%p %p)\n", This, before, after);
5030 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
5031 return S_OK;
5034 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
5035 dwritetrimmingsign_QueryInterface,
5036 dwritetrimmingsign_AddRef,
5037 dwritetrimmingsign_Release,
5038 dwritetrimmingsign_Draw,
5039 dwritetrimmingsign_GetMetrics,
5040 dwritetrimmingsign_GetOverhangMetrics,
5041 dwritetrimmingsign_GetBreakConditions
5044 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
5046 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
5047 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
5050 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
5052 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
5053 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
5056 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
5058 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
5059 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
5062 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
5064 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
5065 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
5068 HRESULT create_trimmingsign(IDWriteFactory4 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
5070 static const WCHAR ellipsisW = 0x2026;
5071 struct dwrite_trimmingsign *This;
5072 DWRITE_READING_DIRECTION reading;
5073 DWRITE_FLOW_DIRECTION flow;
5074 HRESULT hr;
5076 *sign = NULL;
5078 /* Validate reading/flow direction here, layout creation won't complain about
5079 invalid combinations. */
5080 reading = IDWriteTextFormat_GetReadingDirection(format);
5081 flow = IDWriteTextFormat_GetFlowDirection(format);
5083 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
5084 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
5085 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
5087 This = heap_alloc(sizeof(*This));
5088 if (!This)
5089 return E_OUTOFMEMORY;
5091 This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
5092 This->ref = 1;
5094 hr = IDWriteFactory4_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout);
5095 if (FAILED(hr)) {
5096 heap_free(This);
5097 return hr;
5100 IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
5101 IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
5102 *sign = &This->IDWriteInlineObject_iface;
5104 return S_OK;
5107 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
5109 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5111 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
5113 if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
5114 IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
5115 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
5116 IsEqualIID(riid, &IID_IUnknown))
5118 *obj = iface;
5119 IDWriteTextFormat2_AddRef(iface);
5120 return S_OK;
5123 *obj = NULL;
5125 return E_NOINTERFACE;
5128 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface)
5130 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5131 ULONG ref = InterlockedIncrement(&This->ref);
5132 TRACE("(%p)->(%d)\n", This, ref);
5133 return ref;
5136 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface)
5138 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5139 ULONG ref = InterlockedDecrement(&This->ref);
5141 TRACE("(%p)->(%d)\n", This, ref);
5143 if (!ref)
5145 release_format_data(&This->format);
5146 heap_free(This);
5149 return ref;
5152 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
5154 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5155 TRACE("(%p)->(%d)\n", This, alignment);
5156 return format_set_textalignment(&This->format, alignment, NULL);
5159 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
5161 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5162 TRACE("(%p)->(%d)\n", This, alignment);
5163 return format_set_paralignment(&This->format, alignment, NULL);
5166 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
5168 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5169 TRACE("(%p)->(%d)\n", This, wrapping);
5170 return format_set_wordwrapping(&This->format, wrapping, NULL);
5173 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction)
5175 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5176 TRACE("(%p)->(%d)\n", This, direction);
5177 return format_set_readingdirection(&This->format, direction, NULL);
5180 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction)
5182 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5183 TRACE("(%p)->(%d)\n", This, direction);
5184 return format_set_flowdirection(&This->format, direction, NULL);
5187 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
5189 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5190 FIXME("(%p)->(%f): stub\n", This, tabstop);
5191 return E_NOTIMPL;
5194 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
5195 IDWriteInlineObject *trimming_sign)
5197 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5198 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
5199 return format_set_trimming(&This->format, trimming, trimming_sign, NULL);
5202 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method,
5203 FLOAT height, FLOAT baseline)
5205 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5206 DWRITE_LINE_SPACING spacing;
5208 TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
5210 spacing = This->format.spacing;
5211 spacing.method = method;
5212 spacing.height = height;
5213 spacing.baseline = baseline;
5215 return format_set_linespacing(&This->format, &spacing, NULL);
5218 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface)
5220 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5221 TRACE("(%p)\n", This);
5222 return This->format.textalignment;
5225 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface)
5227 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5228 TRACE("(%p)\n", This);
5229 return This->format.paralign;
5232 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface)
5234 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5235 TRACE("(%p)\n", This);
5236 return This->format.wrapping;
5239 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface)
5241 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5242 TRACE("(%p)\n", This);
5243 return This->format.readingdir;
5246 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface)
5248 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5249 TRACE("(%p)\n", This);
5250 return This->format.flow;
5253 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
5255 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5256 FIXME("(%p): stub\n", This);
5257 return 0.0f;
5260 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
5261 IDWriteInlineObject **trimming_sign)
5263 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5264 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
5266 *options = This->format.trimming;
5267 if ((*trimming_sign = This->format.trimmingsign))
5268 IDWriteInlineObject_AddRef(*trimming_sign);
5270 return S_OK;
5273 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method,
5274 FLOAT *spacing, FLOAT *baseline)
5276 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5277 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
5279 *method = This->format.spacing.method;
5280 *spacing = This->format.spacing.height;
5281 *baseline = This->format.spacing.baseline;
5282 return S_OK;
5285 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection)
5287 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5289 TRACE("(%p)->(%p)\n", This, collection);
5291 *collection = This->format.collection;
5292 IDWriteFontCollection_AddRef(*collection);
5294 return S_OK;
5297 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
5299 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5300 TRACE("(%p)\n", This);
5301 return This->format.family_len;
5304 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5306 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5308 TRACE("(%p)->(%p %u)\n", This, name, size);
5310 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
5311 strcpyW(name, This->format.family_name);
5312 return S_OK;
5315 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface)
5317 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5318 TRACE("(%p)\n", This);
5319 return This->format.weight;
5322 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface)
5324 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5325 TRACE("(%p)\n", This);
5326 return This->format.style;
5329 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface)
5331 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5332 TRACE("(%p)\n", This);
5333 return This->format.stretch;
5336 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface)
5338 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5339 TRACE("(%p)\n", This);
5340 return This->format.fontsize;
5343 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface)
5345 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5346 TRACE("(%p)\n", This);
5347 return This->format.locale_len;
5350 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
5352 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5354 TRACE("(%p)->(%p %u)\n", This, name, size);
5356 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
5357 strcpyW(name, This->format.locale);
5358 return S_OK;
5361 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
5363 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5365 TRACE("(%p)->(%d)\n", This, orientation);
5367 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
5368 return E_INVALIDARG;
5370 This->format.vertical_orientation = orientation;
5371 return S_OK;
5374 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
5376 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5377 TRACE("(%p)\n", This);
5378 return This->format.vertical_orientation;
5381 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled)
5383 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5385 TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
5387 This->format.last_line_wrapping = !!lastline_wrapping_enabled;
5388 return S_OK;
5391 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface)
5393 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5394 TRACE("(%p)\n", This);
5395 return This->format.last_line_wrapping;
5398 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
5400 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5401 TRACE("(%p)->(%d)\n", This, alignment);
5402 return format_set_optical_alignment(&This->format, alignment);
5405 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface)
5407 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5408 TRACE("(%p)\n", This);
5409 return This->format.optical_alignment;
5412 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback)
5414 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5415 TRACE("(%p)->(%p)\n", This, fallback);
5416 return set_fontfallback_for_format(&This->format, fallback);
5419 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback)
5421 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5422 TRACE("(%p)->(%p)\n", This, fallback);
5423 return get_fontfallback_from_format(&This->format, fallback);
5426 static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing)
5428 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5429 TRACE("(%p)->(%p)\n", This, spacing);
5430 return format_set_linespacing(&This->format, spacing, NULL);
5433 static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
5435 struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
5437 TRACE("(%p)->(%p)\n", This, spacing);
5439 *spacing = This->format.spacing;
5440 return S_OK;
5443 static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = {
5444 dwritetextformat_QueryInterface,
5445 dwritetextformat_AddRef,
5446 dwritetextformat_Release,
5447 dwritetextformat_SetTextAlignment,
5448 dwritetextformat_SetParagraphAlignment,
5449 dwritetextformat_SetWordWrapping,
5450 dwritetextformat_SetReadingDirection,
5451 dwritetextformat_SetFlowDirection,
5452 dwritetextformat_SetIncrementalTabStop,
5453 dwritetextformat_SetTrimming,
5454 dwritetextformat_SetLineSpacing,
5455 dwritetextformat_GetTextAlignment,
5456 dwritetextformat_GetParagraphAlignment,
5457 dwritetextformat_GetWordWrapping,
5458 dwritetextformat_GetReadingDirection,
5459 dwritetextformat_GetFlowDirection,
5460 dwritetextformat_GetIncrementalTabStop,
5461 dwritetextformat_GetTrimming,
5462 dwritetextformat_GetLineSpacing,
5463 dwritetextformat_GetFontCollection,
5464 dwritetextformat_GetFontFamilyNameLength,
5465 dwritetextformat_GetFontFamilyName,
5466 dwritetextformat_GetFontWeight,
5467 dwritetextformat_GetFontStyle,
5468 dwritetextformat_GetFontStretch,
5469 dwritetextformat_GetFontSize,
5470 dwritetextformat_GetLocaleNameLength,
5471 dwritetextformat_GetLocaleName,
5472 dwritetextformat1_SetVerticalGlyphOrientation,
5473 dwritetextformat1_GetVerticalGlyphOrientation,
5474 dwritetextformat1_SetLastLineWrapping,
5475 dwritetextformat1_GetLastLineWrapping,
5476 dwritetextformat1_SetOpticalAlignment,
5477 dwritetextformat1_GetOpticalAlignment,
5478 dwritetextformat1_SetFontFallback,
5479 dwritetextformat1_GetFontFallback,
5480 dwritetextformat2_SetLineSpacing,
5481 dwritetextformat2_GetLineSpacing
5484 static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
5486 return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
5487 CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL;
5490 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
5491 DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
5493 struct dwrite_textformat *This;
5495 *format = NULL;
5497 if (size <= 0.0f)
5498 return E_INVALIDARG;
5500 if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
5501 ((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
5502 ((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
5503 return E_INVALIDARG;
5505 This = heap_alloc(sizeof(struct dwrite_textformat));
5506 if (!This) return E_OUTOFMEMORY;
5508 This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl;
5509 This->ref = 1;
5510 This->format.family_name = heap_strdupW(family_name);
5511 This->format.family_len = strlenW(family_name);
5512 This->format.locale = heap_strdupW(locale);
5513 This->format.locale_len = strlenW(locale);
5514 /* force locale name to lower case, layout will inherit this modified value */
5515 strlwrW(This->format.locale);
5516 This->format.weight = weight;
5517 This->format.style = style;
5518 This->format.fontsize = size;
5519 This->format.stretch = stretch;
5520 This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
5521 This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
5522 This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
5523 This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
5524 This->format.last_line_wrapping = TRUE;
5525 This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
5526 This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
5527 This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT;
5528 This->format.spacing.height = 0.0f;
5529 This->format.spacing.baseline = 0.0f;
5530 This->format.spacing.leadingBefore = 0.0f;
5531 This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
5532 This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
5533 This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
5534 This->format.trimming.delimiter = 0;
5535 This->format.trimming.delimiterCount = 0;
5536 This->format.trimmingsign = NULL;
5537 This->format.collection = collection;
5538 This->format.fallback = NULL;
5539 IDWriteFontCollection_AddRef(collection);
5541 *format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface;
5543 return S_OK;
5546 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
5548 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5550 TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
5552 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
5553 *obj = iface;
5554 IDWriteTypography_AddRef(iface);
5555 return S_OK;
5558 *obj = NULL;
5560 return E_NOINTERFACE;
5563 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
5565 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5566 ULONG ref = InterlockedIncrement(&typography->ref);
5567 TRACE("(%p)->(%d)\n", typography, ref);
5568 return ref;
5571 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
5573 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5574 ULONG ref = InterlockedDecrement(&typography->ref);
5576 TRACE("(%p)->(%d)\n", typography, ref);
5578 if (!ref) {
5579 heap_free(typography->features);
5580 heap_free(typography);
5583 return ref;
5586 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
5588 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5590 TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
5592 if (typography->count == typography->allocated) {
5593 DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5594 if (!ptr)
5595 return E_OUTOFMEMORY;
5597 typography->features = ptr;
5598 typography->allocated *= 2;
5601 typography->features[typography->count++] = feature;
5602 return S_OK;
5605 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
5607 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5608 TRACE("(%p)\n", typography);
5609 return typography->count;
5612 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
5614 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
5616 TRACE("(%p)->(%u %p)\n", typography, index, feature);
5618 if (index >= typography->count)
5619 return E_INVALIDARG;
5621 *feature = typography->features[index];
5622 return S_OK;
5625 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
5626 dwritetypography_QueryInterface,
5627 dwritetypography_AddRef,
5628 dwritetypography_Release,
5629 dwritetypography_AddFontFeature,
5630 dwritetypography_GetFontFeatureCount,
5631 dwritetypography_GetFontFeature
5634 HRESULT create_typography(IDWriteTypography **ret)
5636 struct dwrite_typography *typography;
5638 *ret = NULL;
5640 typography = heap_alloc(sizeof(*typography));
5641 if (!typography)
5642 return E_OUTOFMEMORY;
5644 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
5645 typography->ref = 1;
5646 typography->allocated = 2;
5647 typography->count = 0;
5649 typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
5650 if (!typography->features) {
5651 heap_free(typography);
5652 return E_OUTOFMEMORY;
5655 *ret = &typography->IDWriteTypography_iface;
5656 return S_OK;