wimgapi: Add new stub dll.
[wine.git] / dlls / dwrite / layout.c
blob22c36a3c5d1357a29f173a5d78a84b959e85f517
1 /*
2 * Text format and layout
4 * Copyright 2012, 2014-2015 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"
31 #include "wine/list.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
35 struct dwrite_textformat_data {
36 WCHAR *family_name;
37 UINT32 family_len;
38 WCHAR *locale;
39 UINT32 locale_len;
41 DWRITE_FONT_WEIGHT weight;
42 DWRITE_FONT_STYLE style;
43 DWRITE_FONT_STRETCH stretch;
45 DWRITE_PARAGRAPH_ALIGNMENT paralign;
46 DWRITE_READING_DIRECTION readingdir;
47 DWRITE_WORD_WRAPPING wrapping;
48 DWRITE_TEXT_ALIGNMENT textalignment;
49 DWRITE_FLOW_DIRECTION flow;
50 DWRITE_LINE_SPACING_METHOD spacingmethod;
51 DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
53 FLOAT spacing;
54 FLOAT baseline;
55 FLOAT fontsize;
57 DWRITE_TRIMMING trimming;
58 IDWriteInlineObject *trimmingsign;
60 IDWriteFontCollection *collection;
61 IDWriteFontFallback *fallback;
64 enum layout_range_attr_kind {
65 LAYOUT_RANGE_ATTR_WEIGHT,
66 LAYOUT_RANGE_ATTR_STYLE,
67 LAYOUT_RANGE_ATTR_STRETCH,
68 LAYOUT_RANGE_ATTR_FONTSIZE,
69 LAYOUT_RANGE_ATTR_EFFECT,
70 LAYOUT_RANGE_ATTR_INLINE,
71 LAYOUT_RANGE_ATTR_UNDERLINE,
72 LAYOUT_RANGE_ATTR_STRIKETHROUGH,
73 LAYOUT_RANGE_ATTR_PAIR_KERNING,
74 LAYOUT_RANGE_ATTR_FONTCOLL,
75 LAYOUT_RANGE_ATTR_LOCALE,
76 LAYOUT_RANGE_ATTR_FONTFAMILY,
77 LAYOUT_RANGE_ATTR_SPACING
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 } u;
99 enum layout_range_kind {
100 LAYOUT_RANGE_REGULAR,
101 LAYOUT_RANGE_STRIKETHROUGH,
102 LAYOUT_RANGE_EFFECT,
103 LAYOUT_RANGE_SPACING
106 struct layout_range_header {
107 struct list entry;
108 enum layout_range_kind kind;
109 DWRITE_TEXT_RANGE range;
112 struct layout_range {
113 struct layout_range_header h;
114 DWRITE_FONT_WEIGHT weight;
115 DWRITE_FONT_STYLE style;
116 FLOAT fontsize;
117 DWRITE_FONT_STRETCH stretch;
118 IDWriteInlineObject *object;
119 BOOL underline;
120 BOOL pair_kerning;
121 IDWriteFontCollection *collection;
122 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
123 WCHAR *fontfamily;
126 struct layout_range_bool {
127 struct layout_range_header h;
128 BOOL value;
131 struct layout_range_effect {
132 struct layout_range_header h;
133 IUnknown *effect;
136 struct layout_range_spacing {
137 struct layout_range_header h;
138 FLOAT leading;
139 FLOAT trailing;
140 FLOAT min_advance;
143 enum layout_run_kind {
144 LAYOUT_RUN_REGULAR,
145 LAYOUT_RUN_INLINE
148 struct inline_object_run {
149 IDWriteInlineObject *object;
150 UINT16 length;
153 struct regular_layout_run {
154 DWRITE_GLYPH_RUN_DESCRIPTION descr;
155 DWRITE_GLYPH_RUN run;
156 DWRITE_SCRIPT_ANALYSIS sa;
157 UINT16 *glyphs;
158 UINT16 *clustermap;
159 FLOAT *advances;
160 DWRITE_GLYPH_OFFSET *offsets;
161 /* this is actual glyph count after shaping, it's not necessary the same as reported to Draw() */
162 UINT32 glyphcount;
165 struct layout_run {
166 struct list entry;
167 enum layout_run_kind kind;
168 union {
169 struct inline_object_run object;
170 struct regular_layout_run regular;
171 } u;
172 FLOAT baseline;
173 FLOAT height;
176 struct layout_effective_run {
177 struct list entry;
178 const struct layout_run *run; /* nominal run this one is based on */
179 UINT32 start; /* relative text position, 0 means first text position of a nominal run */
180 UINT32 length; /* length in codepoints that this run covers */
181 UINT32 glyphcount; /* total glyph count in this run */
182 FLOAT origin_x; /* baseline X position */
183 FLOAT origin_y; /* baseline Y position */
184 FLOAT align_dx; /* adjustment from text alignment */
185 FLOAT width; /* run width */
186 UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
187 UINT32 line;
190 struct layout_effective_inline {
191 struct list entry;
192 IDWriteInlineObject *object;
193 IUnknown *effect;
194 FLOAT origin_x;
195 FLOAT origin_y;
196 FLOAT align_dx;
197 FLOAT width;
198 BOOL is_sideways;
199 BOOL is_rtl;
200 UINT32 line;
203 struct layout_strikethrough {
204 struct list entry;
205 const struct layout_effective_run *run;
206 DWRITE_STRIKETHROUGH s;
209 struct layout_cluster {
210 const struct layout_run *run; /* link to nominal run this cluster belongs to */
211 UINT32 position; /* relative to run, first cluster has 0 position */
214 enum layout_recompute_mask {
215 RECOMPUTE_NOMINAL_RUNS = 1 << 0,
216 RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
217 RECOMPUTE_EFFECTIVE_RUNS = 1 << 2,
218 RECOMPUTE_EVERYTHING = 0xffff
221 struct dwrite_textlayout {
222 IDWriteTextLayout2 IDWriteTextLayout2_iface;
223 IDWriteTextFormat1 IDWriteTextFormat1_iface;
224 IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface;
225 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
226 LONG ref;
228 WCHAR *str;
229 UINT32 len;
230 struct dwrite_textformat_data format;
231 struct list strike_ranges;
232 struct list effects;
233 struct list spacing;
234 struct list ranges;
235 struct list runs;
236 /* lists ready to use by Draw() */
237 struct list eruns;
238 struct list inlineobjects;
239 struct list strikethrough;
240 USHORT recompute;
242 DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
243 DWRITE_LINE_BREAKPOINT *actual_breakpoints;
245 struct layout_cluster *clusters;
246 DWRITE_CLUSTER_METRICS *clustermetrics;
247 UINT32 cluster_count;
248 FLOAT minwidth;
250 DWRITE_LINE_METRICS *lines;
251 UINT32 line_alloc;
253 DWRITE_TEXT_METRICS1 metrics;
255 DWRITE_MEASURING_MODE measuringmode;
257 /* gdi-compatible layout specifics */
258 FLOAT ppdip;
259 DWRITE_MATRIX transform;
262 struct dwrite_textformat {
263 IDWriteTextFormat1 IDWriteTextFormat1_iface;
264 LONG ref;
265 struct dwrite_textformat_data format;
268 struct dwrite_trimmingsign {
269 IDWriteInlineObject IDWriteInlineObject_iface;
270 LONG ref;
272 IDWriteTextLayout *layout;
275 struct dwrite_typography {
276 IDWriteTypography IDWriteTypography_iface;
277 LONG ref;
279 DWRITE_FONT_FEATURE *features;
280 UINT32 allocated;
281 UINT32 count;
284 struct dwrite_vec {
285 FLOAT x;
286 FLOAT y;
289 static const IDWriteTextFormat1Vtbl dwritetextformatvtbl;
291 static void release_format_data(struct dwrite_textformat_data *data)
293 if (data->collection) IDWriteFontCollection_Release(data->collection);
294 if (data->fallback) IDWriteFontFallback_Release(data->fallback);
295 if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
296 heap_free(data->family_name);
297 heap_free(data->locale);
300 static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout2(IDWriteTextLayout2 *iface)
302 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout2_iface);
305 static inline struct dwrite_textlayout *impl_layout_form_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
307 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface);
310 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface)
312 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink_iface);
315 static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
317 return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource_iface);
320 static inline struct dwrite_textformat *impl_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
322 return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat1_iface);
325 static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
327 return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
330 static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
332 return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
335 static inline const char *debugstr_run(const struct regular_layout_run *run)
337 return wine_dbg_sprintf("[%u,%u)", run->descr.textPosition, run->descr.textPosition +
338 run->descr.stringLength);
341 static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
343 return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
346 static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
347 BOOL *changed)
349 if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
350 return E_INVALIDARG;
351 if (changed) *changed = format->textalignment != alignment;
352 format->textalignment = alignment;
353 return S_OK;
356 static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
357 DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
359 if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
360 return E_INVALIDARG;
361 if (changed) *changed = format->paralign != alignment;
362 format->paralign = alignment;
363 return S_OK;
366 static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
367 DWRITE_READING_DIRECTION direction, BOOL *changed)
369 if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
370 return E_INVALIDARG;
371 if (changed) *changed = format->readingdir != direction;
372 format->readingdir = direction;
373 return S_OK;
376 static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
377 DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
379 if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
380 return E_INVALIDARG;
381 if (changed) *changed = format->wrapping != wrapping;
382 format->wrapping = wrapping;
383 return S_OK;
386 static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
388 *fallback = format->fallback;
389 if (*fallback)
390 IDWriteFontFallback_AddRef(*fallback);
391 return S_OK;
394 static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
396 if (format->fallback)
397 IDWriteFontFallback_Release(format->fallback);
398 format->fallback = fallback;
399 if (fallback)
400 IDWriteFontFallback_AddRef(fallback);
401 return S_OK;
404 static struct layout_run *alloc_layout_run(enum layout_run_kind kind)
406 struct layout_run *ret;
408 ret = heap_alloc(sizeof(*ret));
409 if (!ret) return NULL;
411 memset(ret, 0, sizeof(*ret));
412 ret->kind = kind;
413 if (kind == LAYOUT_RUN_REGULAR) {
414 ret->u.regular.sa.script = Script_Unknown;
415 ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
418 return ret;
421 static void free_layout_runs(struct dwrite_textlayout *layout)
423 struct layout_run *cur, *cur2;
424 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
425 list_remove(&cur->entry);
426 if (cur->kind == LAYOUT_RUN_REGULAR) {
427 if (cur->u.regular.run.fontFace)
428 IDWriteFontFace_Release(cur->u.regular.run.fontFace);
429 heap_free(cur->u.regular.glyphs);
430 heap_free(cur->u.regular.clustermap);
431 heap_free(cur->u.regular.advances);
432 heap_free(cur->u.regular.offsets);
434 heap_free(cur);
438 static void free_layout_eruns(struct dwrite_textlayout *layout)
440 struct layout_effective_inline *in, *in2;
441 struct layout_effective_run *cur, *cur2;
442 struct layout_strikethrough *s, *s2;
444 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
445 list_remove(&cur->entry);
446 heap_free(cur->clustermap);
447 heap_free(cur);
450 LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
451 list_remove(&in->entry);
452 heap_free(in);
455 LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
456 list_remove(&s->entry);
457 heap_free(s);
461 /* Used to resolve break condition by forcing stronger condition over weaker. */
462 static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
464 switch (existingbreak) {
465 case DWRITE_BREAK_CONDITION_NEUTRAL:
466 return newbreak;
467 case DWRITE_BREAK_CONDITION_CAN_BREAK:
468 return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
469 /* let's keep stronger conditions as is */
470 case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
471 case DWRITE_BREAK_CONDITION_MUST_BREAK:
472 break;
473 default:
474 ERR("unknown break condition %d\n", existingbreak);
477 return existingbreak;
480 /* This helper should be used to get effective range length, in other words it returns number of text
481 positions from range starting point to the end of the range, limited by layout text length */
482 static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
484 if (range->h.range.startPosition + range->h.range.length <= layout->len)
485 return range->h.range.length;
486 return layout->len - range->h.range.startPosition;
489 /* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
490 static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
492 DWRITE_BREAK_CONDITION before, after;
493 UINT32 i, length;
494 HRESULT hr;
496 /* ignore returned conditions if failed */
497 hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
498 if (FAILED(hr))
499 after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
501 if (!layout->actual_breakpoints) {
502 layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
503 if (!layout->actual_breakpoints)
504 return E_OUTOFMEMORY;
505 memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
508 length = get_clipped_range_length(layout, cur);
509 for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
510 /* for first codepoint check if there's anything before it and update accordingly */
511 if (i == cur->h.range.startPosition) {
512 if (i > 0)
513 layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
514 override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
515 else
516 layout->actual_breakpoints[i].breakConditionBefore = before;
517 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
519 /* similar check for last codepoint */
520 else if (i == cur->h.range.startPosition + length - 1) {
521 if (i == layout->len - 1)
522 layout->actual_breakpoints[i].breakConditionAfter = after;
523 else
524 layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
525 override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
526 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
528 /* for all positions within a range disable breaks */
529 else {
530 layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
531 layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
534 layout->actual_breakpoints[i].isWhitespace = FALSE;
535 layout->actual_breakpoints[i].isSoftHyphen = FALSE;
538 return S_OK;
541 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
543 static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
545 if (layout->actual_breakpoints)
546 return layout->actual_breakpoints[pos];
547 return layout->nominal_breakpoints[pos];
550 static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
551 UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
553 UINT8 breakcondition;
554 UINT32 position;
555 UINT16 j;
557 /* For clusters made of control chars we report zero glyphs, and we need zero cluster
558 width as well; advances are already computed at this point and are not necessary zero. */
559 metrics->width = 0.0;
560 if (run->run.glyphCount) {
561 for (j = start_glyph; j < stop_glyph; j++)
562 metrics->width += run->run.glyphAdvances[j];
564 metrics->length = length;
566 position = stop_position;
567 if (stop_glyph == run->glyphcount)
568 breakcondition = get_effective_breakpoint(layout, stop_position).breakConditionAfter;
569 else {
570 breakcondition = get_effective_breakpoint(layout, stop_position).breakConditionBefore;
571 if (stop_position) position = stop_position - 1;
574 metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
575 breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
576 if (metrics->length == 1) {
577 WORD type = 0;
579 GetStringTypeW(CT_CTYPE1, &layout->str[position], 1, &type);
580 metrics->isWhitespace = !!(type & C1_SPACE);
581 metrics->isNewline = FALSE /* FIXME */;
582 metrics->isSoftHyphen = layout->str[position] == 0x00ad /* Unicode Soft Hyphen */;
584 else {
585 metrics->isWhitespace = FALSE;
586 metrics->isNewline = FALSE;
587 metrics->isSoftHyphen = FALSE;
589 metrics->isRightToLeft = run->run.bidiLevel & 1;
590 metrics->padding = 0;
595 All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
596 On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
597 Note that there's no need to reallocate anything at this point as we allocate one cluster per
598 codepoint initially.
601 static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
603 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
604 struct layout_cluster *c = &layout->clusters[*cluster];
605 const struct regular_layout_run *run = &r->u.regular;
606 UINT32 i, start = 0;
608 for (i = 0; i < run->descr.stringLength; i++) {
609 BOOL end = i == run->descr.stringLength - 1;
611 if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
612 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
613 i - start, metrics);
614 c->position = start;
615 c->run = r;
617 *cluster += 1;
618 metrics++;
619 c++;
620 start = i;
623 if (end) {
624 init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
625 i - start + 1, metrics);
626 c->position = start;
627 c->run = r;
629 *cluster += 1;
630 return;
635 static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
637 return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm;
640 static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
642 IDWriteTextAnalyzer *analyzer;
643 struct layout_range *range;
644 struct layout_run *r;
645 UINT32 cluster = 0;
646 HRESULT hr;
648 free_layout_eruns(layout);
649 free_layout_runs(layout);
651 /* Cluster data arrays are allocated once, assuming one text position per cluster. */
652 if (!layout->clustermetrics) {
653 layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics));
654 layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters));
655 if (!layout->clustermetrics || !layout->clusters) {
656 heap_free(layout->clustermetrics);
657 heap_free(layout->clusters);
658 return E_OUTOFMEMORY;
661 layout->cluster_count = 0;
663 hr = get_textanalyzer(&analyzer);
664 if (FAILED(hr))
665 return hr;
667 LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
668 /* we don't care about ranges that don't contain any text */
669 if (range->h.range.startPosition >= layout->len)
670 break;
672 /* inline objects override actual text in a range */
673 if (range->object) {
674 hr = layout_update_breakpoints_range(layout, range);
675 if (FAILED(hr))
676 return hr;
678 r = alloc_layout_run(LAYOUT_RUN_INLINE);
679 if (!r)
680 return E_OUTOFMEMORY;
682 r->u.object.object = range->object;
683 r->u.object.length = get_clipped_range_length(layout, range);
684 list_add_tail(&layout->runs, &r->entry);
685 continue;
688 /* initial splitting by script */
689 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &layout->IDWriteTextAnalysisSource_iface,
690 range->h.range.startPosition, get_clipped_range_length(layout, range), &layout->IDWriteTextAnalysisSink_iface);
691 if (FAILED(hr))
692 break;
694 /* this splits it further */
695 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &layout->IDWriteTextAnalysisSource_iface,
696 range->h.range.startPosition, get_clipped_range_length(layout, range), &layout->IDWriteTextAnalysisSink_iface);
697 if (FAILED(hr))
698 break;
701 /* fill run info */
702 LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
703 DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props = NULL;
704 DWRITE_SHAPING_TEXT_PROPERTIES *text_props = NULL;
705 struct regular_layout_run *run = &r->u.regular;
706 DWRITE_FONT_METRICS fontmetrics = { 0 };
707 IDWriteFontFamily *family;
708 UINT32 index, max_count;
709 IDWriteFont *font;
710 BOOL exists = TRUE;
712 /* we need to do very little in case of inline objects */
713 if (r->kind == LAYOUT_RUN_INLINE) {
714 DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster];
715 struct layout_cluster *c = &layout->clusters[cluster];
716 DWRITE_INLINE_OBJECT_METRICS inlinemetrics;
718 metrics->width = 0.0;
719 metrics->length = r->u.object.length;
720 metrics->canWrapLineAfter = FALSE;
721 metrics->isWhitespace = FALSE;
722 metrics->isNewline = FALSE;
723 metrics->isSoftHyphen = FALSE;
724 metrics->isRightToLeft = FALSE;
725 metrics->padding = 0;
726 c->run = r;
727 c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */
728 cluster++;
730 /* it's not fatal if GetMetrics() fails, all returned metrics are ignored */
731 hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics);
732 if (FAILED(hr)) {
733 memset(&inlinemetrics, 0, sizeof(inlinemetrics));
734 hr = S_OK;
736 metrics->width = inlinemetrics.width;
737 r->baseline = inlinemetrics.baseline;
738 r->height = inlinemetrics.height;
740 /* FIXME: use resolved breakpoints in this case too */
742 continue;
745 range = get_layout_range_by_pos(layout, run->descr.textPosition);
747 hr = IDWriteFontCollection_FindFamilyName(range->collection, range->fontfamily, &index, &exists);
748 if (FAILED(hr) || !exists) {
749 WARN("%s: family %s not found in collection %p\n", debugstr_run(run), debugstr_w(range->fontfamily), range->collection);
750 continue;
753 hr = IDWriteFontCollection_GetFontFamily(range->collection, index, &family);
754 if (FAILED(hr))
755 continue;
757 hr = IDWriteFontFamily_GetFirstMatchingFont(family, range->weight, range->stretch, range->style, &font);
758 IDWriteFontFamily_Release(family);
759 if (FAILED(hr)) {
760 WARN("%s: failed to get a matching font\n", debugstr_run(run));
761 continue;
764 hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
765 IDWriteFont_Release(font);
766 if (FAILED(hr))
767 continue;
769 run->run.fontEmSize = range->fontsize;
770 run->descr.localeName = range->locale;
771 run->clustermap = heap_alloc(run->descr.stringLength*sizeof(UINT16));
773 max_count = 3*run->descr.stringLength/2 + 16;
774 run->glyphs = heap_alloc(max_count*sizeof(UINT16));
775 if (!run->clustermap || !run->glyphs)
776 goto memerr;
778 text_props = heap_alloc(run->descr.stringLength*sizeof(DWRITE_SHAPING_TEXT_PROPERTIES));
779 glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
780 if (!text_props || !glyph_props)
781 goto memerr;
783 while (1) {
784 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength,
785 run->run.fontFace, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
786 NULL /* FIXME */, NULL, NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props,
787 &run->glyphcount);
788 if (hr == E_NOT_SUFFICIENT_BUFFER) {
789 heap_free(run->glyphs);
790 heap_free(glyph_props);
792 max_count = run->glyphcount;
794 run->glyphs = heap_alloc(max_count*sizeof(UINT16));
795 glyph_props = heap_alloc(max_count*sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES));
796 if (!run->glyphs || !glyph_props)
797 goto memerr;
799 continue;
802 break;
805 if (FAILED(hr)) {
806 heap_free(text_props);
807 heap_free(glyph_props);
808 WARN("%s: shaping failed 0x%08x\n", debugstr_run(run), hr);
809 continue;
812 run->run.glyphIndices = run->glyphs;
813 run->descr.clusterMap = run->clustermap;
815 run->advances = heap_alloc(run->glyphcount*sizeof(FLOAT));
816 run->offsets = heap_alloc(run->glyphcount*sizeof(DWRITE_GLYPH_OFFSET));
817 if (!run->advances || !run->offsets)
818 goto memerr;
820 /* now set advances and offsets */
821 if (is_layout_gdi_compatible(layout))
822 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
823 text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
824 run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
825 layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways,
826 run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
827 else
828 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
829 run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
830 run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
831 NULL, NULL, 0, run->advances, run->offsets);
833 heap_free(text_props);
834 heap_free(glyph_props);
835 if (FAILED(hr))
836 WARN("%s: failed to get glyph placement info, 0x%08x\n", debugstr_run(run), hr);
838 run->run.glyphAdvances = run->advances;
839 run->run.glyphOffsets = run->offsets;
841 /* Special treatment of control script, shaping code adds normal glyphs for it,
842 with non-zero advances, and layout code exposes those as zero width clusters,
843 so we have to do it manually. */
844 if (run->sa.script == Script_Common)
845 run->run.glyphCount = 0;
846 else
847 run->run.glyphCount = run->glyphcount;
849 /* baseline derived from font metrics */
850 if (is_layout_gdi_compatible(layout)) {
851 hr = IDWriteFontFace_GetGdiCompatibleMetrics(run->run.fontFace,
852 run->run.fontEmSize,
853 layout->ppdip,
854 &layout->transform,
855 &fontmetrics);
856 if (FAILED(hr))
857 WARN("failed to get compat metrics, 0x%08x\n", hr);
859 else
860 IDWriteFontFace_GetMetrics(run->run.fontFace, &fontmetrics);
862 r->baseline = get_scaled_font_metric(fontmetrics.ascent, run->run.fontEmSize, &fontmetrics);
863 r->height = get_scaled_font_metric(fontmetrics.ascent + fontmetrics.descent, run->run.fontEmSize, &fontmetrics);
865 layout_set_cluster_metrics(layout, r, &cluster);
867 continue;
869 memerr:
870 heap_free(text_props);
871 heap_free(glyph_props);
872 heap_free(run->clustermap);
873 heap_free(run->glyphs);
874 heap_free(run->advances);
875 heap_free(run->offsets);
876 run->advances = NULL;
877 run->offsets = NULL;
878 run->clustermap = run->glyphs = NULL;
879 hr = E_OUTOFMEMORY;
880 break;
883 if (hr == S_OK) {
884 layout->cluster_count = cluster;
885 if (cluster)
886 layout->clustermetrics[cluster-1].canWrapLineAfter = TRUE;
889 IDWriteTextAnalyzer_Release(analyzer);
890 return hr;
893 static HRESULT layout_compute(struct dwrite_textlayout *layout)
895 HRESULT hr;
897 if (!(layout->recompute & RECOMPUTE_NOMINAL_RUNS))
898 return S_OK;
900 /* nominal breakpoints are evaluated only once, because string never changes */
901 if (!layout->nominal_breakpoints) {
902 IDWriteTextAnalyzer *analyzer;
903 HRESULT hr;
905 layout->nominal_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
906 if (!layout->nominal_breakpoints)
907 return E_OUTOFMEMORY;
909 hr = get_textanalyzer(&analyzer);
910 if (FAILED(hr))
911 return hr;
913 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &layout->IDWriteTextAnalysisSource_iface,
914 0, layout->len, &layout->IDWriteTextAnalysisSink_iface);
915 IDWriteTextAnalyzer_Release(analyzer);
917 if (layout->actual_breakpoints) {
918 heap_free(layout->actual_breakpoints);
919 layout->actual_breakpoints = NULL;
922 hr = layout_compute_runs(layout);
924 if (TRACE_ON(dwrite)) {
925 struct layout_run *cur;
927 LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) {
928 if (cur->kind == LAYOUT_RUN_INLINE)
929 TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length);
930 else
931 TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition +
932 cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel);
936 layout->recompute &= ~RECOMPUTE_NOMINAL_RUNS;
937 return hr;
940 static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end)
942 FLOAT width = 0.0;
943 for (; start < end; start++)
944 width += layout->clustermetrics[start].width;
945 return width;
948 static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos)
950 struct layout_range_header *cur;
952 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
953 DWRITE_TEXT_RANGE *r = &cur->range;
954 if (r->startPosition <= pos && pos < r->startPosition + r->length)
955 return cur;
958 return NULL;
961 static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
963 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos);
964 return ((struct layout_range_effect*)h)->effect;
967 static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun)
969 return erun->run->u.regular.run.bidiLevel & 1;
972 /* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index,
973 'cluster_count' indicates how many clusters to add, including first one. */
974 static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster,
975 UINT32 cluster_count, UINT32 line, FLOAT origin_x, BOOL strikethrough)
977 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
978 UINT32 i, start, length, last_cluster;
979 struct layout_effective_run *run;
981 if (r->kind == LAYOUT_RUN_INLINE) {
982 struct layout_effective_inline *inlineobject;
984 inlineobject = heap_alloc(sizeof(*inlineobject));
985 if (!inlineobject)
986 return E_OUTOFMEMORY;
988 inlineobject->object = r->u.object.object;
989 inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
990 inlineobject->origin_x = is_rtl ? origin_x - inlineobject->width : origin_x;
991 inlineobject->origin_y = 0.0; /* set after line is built */
992 inlineobject->align_dx = 0.0;
994 /* It's not clear how these two are set, possibly directionality
995 is derived from surrounding text (replaced text could have
996 different ranges which differ in reading direction). */
997 inlineobject->is_sideways = FALSE;
998 inlineobject->is_rtl = FALSE;
999 inlineobject->line = line;
1001 /* effect assigned from start position and on is used for inline objects */
1002 inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position);
1004 list_add_tail(&layout->inlineobjects, &inlineobject->entry);
1005 return S_OK;
1008 run = heap_alloc(sizeof(*run));
1009 if (!run)
1010 return E_OUTOFMEMORY;
1012 /* No need to iterate for that, use simple fact that:
1013 <last cluster position> = first cluster position> + <sum of cluster lengths not including last one> */
1014 last_cluster = first_cluster + cluster_count - 1;
1015 length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
1016 layout->clustermetrics[last_cluster].length;
1018 run->clustermap = heap_alloc(sizeof(UINT16)*length);
1019 if (!run->clustermap) {
1020 heap_free(run);
1021 return E_OUTOFMEMORY;
1024 run->run = r;
1025 run->start = start = layout->clusters[first_cluster].position;
1026 run->length = length;
1027 run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1029 /* Check if run direction matches paragraph direction, if it doesn't adjust by
1030 run width */
1031 if (layout_is_erun_rtl(run) ^ is_rtl)
1032 run->origin_x = is_rtl ? origin_x - run->width : origin_x + run->width;
1033 else
1034 run->origin_x = origin_x;
1036 run->origin_y = 0.0; /* set after line is built */
1037 run->align_dx = 0.0;
1038 run->line = line;
1040 if (r->u.regular.run.glyphCount) {
1041 /* trim from the left */
1042 run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start];
1043 /* trim from the right */
1044 if (start + length < r->u.regular.descr.stringLength - 1)
1045 run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length];
1047 else
1048 run->glyphcount = 0;
1050 /* cluster map needs to be shifted */
1051 for (i = 0; i < length; i++)
1052 run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start];
1054 list_add_tail(&layout->eruns, &run->entry);
1056 /* Strikethrough style is guaranteed to be consistent within effective run,
1057 it's width equals to run width, thikness and offset are derived from
1058 font metrics, rest of the values are from layout or run itself */
1059 if (strikethrough) {
1060 DWRITE_FONT_METRICS metrics = { 0 };
1061 struct layout_strikethrough *s;
1063 s = heap_alloc(sizeof(*s));
1064 if (!s)
1065 return E_OUTOFMEMORY;
1067 if (is_layout_gdi_compatible(layout)) {
1068 HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
1069 r->u.regular.run.fontFace,
1070 r->u.regular.run.fontEmSize,
1071 layout->ppdip,
1072 &layout->transform,
1073 &metrics);
1074 if (FAILED(hr))
1075 WARN("failed to get font metrics, 0x%08x\n", hr);
1077 else
1078 IDWriteFontFace_GetMetrics(r->u.regular.run.fontFace, &metrics);
1080 s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
1081 s->s.thickness = metrics.strikethroughThickness;
1082 s->s.offset = metrics.strikethroughPosition;
1083 s->s.readingDirection = layout->format.readingdir;
1084 s->s.flowDirection = layout->format.flow;
1085 s->s.localeName = r->u.regular.descr.localeName;
1086 s->s.measuringMode = layout->measuringmode;
1087 s->run = run;
1089 list_add_tail(&layout->strikethrough, &s->entry);
1092 return S_OK;
1095 static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS *metrics, UINT32 *line)
1097 if (!layout->line_alloc) {
1098 layout->line_alloc = 5;
1099 layout->lines = heap_alloc(layout->line_alloc*sizeof(*layout->lines));
1100 if (!layout->lines)
1101 return E_OUTOFMEMORY;
1104 if (layout->metrics.lineCount == layout->line_alloc) {
1105 DWRITE_LINE_METRICS *l = heap_realloc(layout->lines, layout->line_alloc*2*sizeof(*layout->lines));
1106 if (!l)
1107 return E_OUTOFMEMORY;
1108 layout->lines = l;
1109 layout->line_alloc *= 2;
1112 layout->lines[*line] = *metrics;
1113 layout->metrics.lineCount += 1;
1114 *line += 1;
1115 return S_OK;
1118 static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
1120 struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos);
1121 return ((struct layout_range_bool*)h)->value;
1124 static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
1125 const struct layout_effective_run *cur)
1127 struct list *e;
1129 if (!cur)
1130 e = list_head(&layout->eruns);
1131 else
1132 e = list_next(&layout->eruns, &cur->entry);
1133 if (!e)
1134 return NULL;
1135 return LIST_ENTRY(e, struct layout_effective_run, entry);
1138 static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
1139 const struct layout_effective_inline *cur)
1141 struct list *e;
1143 if (!cur)
1144 e = list_head(&layout->inlineobjects);
1145 else
1146 e = list_next(&layout->inlineobjects, &cur->entry);
1147 if (!e)
1148 return NULL;
1149 return LIST_ENTRY(e, struct layout_effective_inline, entry);
1152 static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
1153 struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
1155 FLOAT width = 0.0;
1157 while (erun && erun->line == line) {
1158 width += erun->width;
1159 erun = layout_get_next_erun(layout, erun);
1160 if (!erun)
1161 break;
1164 while (inrun && inrun->line == line) {
1165 width += inrun->width;
1166 inrun = layout_get_next_inline_run(layout, inrun);
1167 if (!inrun)
1168 break;
1171 return width;
1174 static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
1176 *det = m->m11 * m->m22 - m->m12 * m->m21;
1177 /* on certain conditions we can skip transform */
1178 return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
1181 static inline void layout_apply_snapping(struct dwrite_vec *vec, BOOL skiptransform, FLOAT ppdip,
1182 const DWRITE_MATRIX *m, FLOAT det)
1184 if (!skiptransform) {
1185 FLOAT vec2[2];
1187 /* apply transform */
1188 vec->x *= ppdip;
1189 vec->y *= ppdip;
1191 vec2[0] = m->m11 * vec->x + m->m21 * vec->y + m->dx;
1192 vec2[1] = m->m12 * vec->x + m->m22 * vec->y + m->dy;
1194 /* snap */
1195 vec2[0] = floorf(vec2[0] + 0.5f);
1196 vec2[1] = floorf(vec2[1] + 0.5f);
1198 /* apply inverted transform, we don't care about X component at this point */
1199 vec->x = (m->m22 * vec2[0] - m->m21 * vec2[1] + m->m21 * m->dy - m->m22 * m->dx) / det;
1200 vec->x /= ppdip;
1202 vec->y = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
1203 vec->y /= ppdip;
1205 else {
1206 vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
1207 vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
1211 static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
1213 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1214 struct layout_effective_inline *inrun;
1215 struct layout_effective_run *erun;
1217 erun = layout_get_next_erun(layout, NULL);
1218 inrun = layout_get_next_inline_run(layout, NULL);
1220 while (erun) {
1221 erun->align_dx = 0.0;
1222 erun = layout_get_next_erun(layout, erun);
1225 while (inrun) {
1226 inrun->align_dx = 0.0;
1227 inrun = layout_get_next_inline_run(layout, inrun);
1230 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0;
1233 static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout)
1235 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1236 struct layout_effective_inline *inrun;
1237 struct layout_effective_run *erun;
1238 UINT32 line;
1240 erun = layout_get_next_erun(layout, NULL);
1241 inrun = layout_get_next_inline_run(layout, NULL);
1243 for (line = 0; line < layout->metrics.lineCount; line++) {
1244 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1245 FLOAT shift = layout->metrics.layoutWidth - width;
1247 if (is_rtl)
1248 shift *= -1.0;
1250 while (erun && erun->line == line) {
1251 erun->align_dx = shift;
1252 erun = layout_get_next_erun(layout, erun);
1255 while (inrun && inrun->line == line) {
1256 inrun->align_dx = shift;
1257 inrun = layout_get_next_inline_run(layout, inrun);
1261 layout->metrics.left = is_rtl ? 0.0 : layout->metrics.layoutWidth - layout->metrics.width;
1264 static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
1265 FLOAT width, FLOAT det)
1267 if (is_layout_gdi_compatible(layout)) {
1268 struct dwrite_vec vec = { layout->metrics.layoutWidth - width, 0.0 };
1269 layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
1270 return floorf(vec.x / 2.0f);
1272 else
1273 return (layout->metrics.layoutWidth - width) / 2.0f;
1276 static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
1278 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1279 struct layout_effective_inline *inrun;
1280 struct layout_effective_run *erun;
1281 BOOL skiptransform;
1282 UINT32 line;
1283 FLOAT det;
1285 erun = layout_get_next_erun(layout, NULL);
1286 inrun = layout_get_next_inline_run(layout, NULL);
1288 skiptransform = should_skip_transform(&layout->transform, &det);
1290 for (line = 0; line < layout->metrics.lineCount; line++) {
1291 FLOAT width = layout_get_line_width(layout, erun, inrun, line);
1292 FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
1294 if (is_rtl)
1295 shift *= -1.0;
1297 while (erun && erun->line == line) {
1298 erun->align_dx = shift;
1299 erun = layout_get_next_erun(layout, erun);
1302 while (inrun && inrun->line == line) {
1303 inrun->align_dx = shift;
1304 inrun = layout_get_next_inline_run(layout, inrun);
1308 layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0;
1311 static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
1313 switch (layout->format.textalignment)
1315 case DWRITE_TEXT_ALIGNMENT_LEADING:
1316 layout_apply_leading_alignment(layout);
1317 break;
1318 case DWRITE_TEXT_ALIGNMENT_TRAILING:
1319 layout_apply_trailing_alignment(layout);
1320 break;
1321 case DWRITE_TEXT_ALIGNMENT_CENTER:
1322 layout_apply_centered_alignment(layout);
1323 break;
1324 case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
1325 FIXME("alignment %d not implemented\n", layout->format.textalignment);
1326 break;
1327 default:
1332 static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
1334 struct layout_effective_inline *inrun;
1335 struct layout_effective_run *erun;
1336 FLOAT origin_y = 0.0;
1337 UINT32 line;
1339 /* alignment mode defines origin, after that all run origins are updated
1340 the same way */
1342 switch (layout->format.paralign)
1344 case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
1345 origin_y = 0.0;
1346 break;
1347 case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
1348 origin_y = layout->metrics.layoutHeight - layout->metrics.height;
1349 break;
1350 case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
1351 origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0;
1352 break;
1353 default:
1357 layout->metrics.top = origin_y;
1359 erun = layout_get_next_erun(layout, NULL);
1360 inrun = layout_get_next_inline_run(layout, NULL);
1361 for (line = 0; line < layout->metrics.lineCount; line++) {
1362 origin_y += layout->lines[line].baseline;
1364 while (erun && erun->line == line) {
1365 erun->origin_y = origin_y;
1366 erun = layout_get_next_erun(layout, erun);
1369 while (inrun && inrun->line == line) {
1370 inrun->origin_y = origin_y;
1371 inrun = layout_get_next_inline_run(layout, inrun);
1376 static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
1378 BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
1379 struct layout_effective_inline *inrun;
1380 struct layout_effective_run *erun;
1381 const struct layout_run *run;
1382 DWRITE_LINE_METRICS metrics;
1383 FLOAT width, origin_x, origin_y;
1384 UINT32 i, start, line, textpos;
1385 HRESULT hr;
1386 BOOL s[2];
1388 if (!(layout->recompute & RECOMPUTE_EFFECTIVE_RUNS))
1389 return S_OK;
1391 hr = layout_compute(layout);
1392 if (FAILED(hr))
1393 return hr;
1395 layout->metrics.lineCount = 0;
1396 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0;
1397 line = 0;
1398 run = layout->clusters[0].run;
1399 memset(&metrics, 0, sizeof(metrics));
1400 s[0] = s[1] = layout_get_strikethrough_from_pos(layout, 0);
1402 for (i = 0, start = 0, textpos = 0, width = 0.0; i < layout->cluster_count; i++) {
1403 BOOL overflow;
1405 s[1] = layout_get_strikethrough_from_pos(layout, textpos);
1407 /* switched to next nominal run, at this point all previous pending clusters are already
1408 checked for layout line overflow, so new effective run will fit in current line */
1409 if (run != layout->clusters[i].run || s[0] != s[1]) {
1410 hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, s[0]);
1411 if (FAILED(hr))
1412 return hr;
1413 origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
1414 get_cluster_range_width(layout, start, i);
1415 run = layout->clusters[i].run;
1416 start = i;
1419 overflow = layout->clustermetrics[i].canWrapLineAfter &&
1420 (width + layout->clustermetrics[i].width > layout->metrics.layoutWidth);
1421 /* check if we got new */
1422 if (overflow ||
1423 layout->clustermetrics[i].isNewline || /* always wrap on new line */
1424 i == layout->cluster_count - 1) /* end of the text */ {
1426 UINT32 strlength, last_cluster, index;
1427 FLOAT descent, trailingspacewidth;
1429 if (!overflow) {
1430 width += layout->clustermetrics[i].width;
1431 metrics.length += layout->clustermetrics[i].length;
1432 last_cluster = i;
1434 else
1435 last_cluster = i ? i - 1 : i;
1437 if (i >= start) {
1438 hr = layout_add_effective_run(layout, run, start, last_cluster - start + 1, line, origin_x, s[0]);
1439 if (FAILED(hr))
1440 return hr;
1441 /* we don't need to update origin for next run as we're going to wrap */
1444 /* take a look at clusters we got for this line in reverse order to set
1445 trailing properties for current line */
1446 strlength = metrics.length;
1447 index = last_cluster;
1448 trailingspacewidth = 0.0;
1449 while (strlength) {
1450 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1452 if (!cluster->isNewline && !cluster->isWhitespace)
1453 break;
1455 if (cluster->isNewline) {
1456 metrics.trailingWhitespaceLength += cluster->length;
1457 metrics.newlineLength += cluster->length;
1460 if (cluster->isWhitespace) {
1461 metrics.trailingWhitespaceLength += cluster->length;
1462 trailingspacewidth += cluster->width;
1465 strlength -= cluster->length;
1466 index--;
1469 /* look for max baseline and descent for this line */
1470 strlength = metrics.length;
1471 index = last_cluster;
1472 metrics.baseline = 0.0;
1473 descent = 0.0;
1474 while (strlength) {
1475 DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
1476 const struct layout_run *cur = layout->clusters[index].run;
1477 FLOAT cur_descent = cur->height - cur->baseline;
1479 if (cur->baseline > metrics.baseline)
1480 metrics.baseline = cur->baseline;
1482 if (cur_descent > descent)
1483 descent = cur_descent;
1485 strlength -= cluster->length;
1486 index--;
1488 metrics.height = descent + metrics.baseline;
1490 if (width > layout->metrics.widthIncludingTrailingWhitespace)
1491 layout->metrics.widthIncludingTrailingWhitespace = width;
1492 if (width - trailingspacewidth > layout->metrics.width)
1493 layout->metrics.width = width - trailingspacewidth;
1495 metrics.isTrimmed = width > layout->metrics.layoutWidth;
1496 hr = layout_set_line_metrics(layout, &metrics, &line);
1497 if (FAILED(hr))
1498 return hr;
1500 width = layout->clustermetrics[i].width;
1501 memset(&metrics, 0, sizeof(metrics));
1502 origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0;
1503 start = i;
1505 else {
1506 metrics.length += layout->clustermetrics[i].length;
1507 width += layout->clustermetrics[i].width;
1510 s[0] = s[1];
1511 textpos += layout->clustermetrics[i].length;
1514 layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0;
1515 layout->metrics.top = 0.0;
1516 layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
1517 layout->metrics.height = 0.0;
1519 /* Now all line info is here, update effective runs positions in flow direction */
1520 erun = layout_get_next_erun(layout, NULL);
1521 inrun = layout_get_next_inline_run(layout, NULL);
1523 origin_y = 0.0;
1524 for (line = 0; line < layout->metrics.lineCount; line++) {
1526 origin_y += layout->lines[line].baseline;
1528 /* For all runs on this line */
1529 while (erun && erun->line == line) {
1530 erun->origin_y = origin_y;
1531 erun = layout_get_next_erun(layout, erun);
1534 /* Same for inline runs */
1535 while (inrun && inrun->line == line) {
1536 inrun->origin_y = origin_y;
1537 inrun = layout_get_next_inline_run(layout, inrun);
1540 layout->metrics.height += layout->lines[line].height;
1543 /* initial alignment is always leading */
1544 if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
1545 layout_apply_text_alignment(layout);
1547 /* initial paragraph alignment is always near */
1548 if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
1549 layout_apply_par_alignment(layout);
1551 layout->metrics.heightIncludingTrailingWhitespace = layout->metrics.height; /* FIXME: not true for vertical text */
1553 layout->recompute &= ~RECOMPUTE_EFFECTIVE_RUNS;
1554 return hr;
1557 static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
1559 struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
1560 struct layout_range_effect const *range_effect = (struct layout_range_effect*)h;
1561 struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
1562 struct layout_range const *range = (struct layout_range*)h;
1564 switch (attr) {
1565 case LAYOUT_RANGE_ATTR_WEIGHT:
1566 return range->weight == value->u.weight;
1567 case LAYOUT_RANGE_ATTR_STYLE:
1568 return range->style == value->u.style;
1569 case LAYOUT_RANGE_ATTR_STRETCH:
1570 return range->stretch == value->u.stretch;
1571 case LAYOUT_RANGE_ATTR_FONTSIZE:
1572 return range->fontsize == value->u.fontsize;
1573 case LAYOUT_RANGE_ATTR_INLINE:
1574 return range->object == value->u.object;
1575 case LAYOUT_RANGE_ATTR_EFFECT:
1576 return range_effect->effect == value->u.effect;
1577 case LAYOUT_RANGE_ATTR_UNDERLINE:
1578 return range->underline == value->u.underline;
1579 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
1580 return range_bool->value == value->u.strikethrough;
1581 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
1582 return range->pair_kerning == value->u.pair_kerning;
1583 case LAYOUT_RANGE_ATTR_FONTCOLL:
1584 return range->collection == value->u.collection;
1585 case LAYOUT_RANGE_ATTR_LOCALE:
1586 return strcmpW(range->locale, value->u.locale) == 0;
1587 case LAYOUT_RANGE_ATTR_FONTFAMILY:
1588 return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
1589 case LAYOUT_RANGE_ATTR_SPACING:
1590 return range_spacing->leading == value->u.spacing[0] &&
1591 range_spacing->trailing == value->u.spacing[1] &&
1592 range_spacing->min_advance == value->u.spacing[2];
1593 default:
1597 return FALSE;
1600 static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
1602 switch (hleft->kind)
1604 case LAYOUT_RANGE_REGULAR:
1606 struct layout_range const *left = (struct layout_range const*)hleft;
1607 struct layout_range const *right = (struct layout_range const*)hright;
1608 return left->weight == right->weight &&
1609 left->style == right->style &&
1610 left->stretch == right->stretch &&
1611 left->fontsize == right->fontsize &&
1612 left->object == right->object &&
1613 left->underline == right->underline &&
1614 left->pair_kerning == right->pair_kerning &&
1615 left->collection == right->collection &&
1616 !strcmpW(left->locale, right->locale) &&
1617 !strcmpW(left->fontfamily, right->fontfamily);
1619 case LAYOUT_RANGE_STRIKETHROUGH:
1621 struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
1622 struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
1623 return left->value == right->value;
1625 case LAYOUT_RANGE_EFFECT:
1627 struct layout_range_effect const *left = (struct layout_range_effect const*)hleft;
1628 struct layout_range_effect const *right = (struct layout_range_effect const*)hright;
1629 return left->effect == right->effect;
1631 case LAYOUT_RANGE_SPACING:
1633 struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
1634 struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
1635 return left->leading == right->leading &&
1636 left->trailing == right->trailing &&
1637 left->min_advance == right->min_advance;
1639 default:
1640 FIXME("unknown range kind %d\n", hleft->kind);
1641 return FALSE;
1645 static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
1647 return left->startPosition == right->startPosition && left->length == right->length;
1650 /* Allocates range and inits it with default values from text format. */
1651 static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
1652 enum layout_range_kind kind)
1654 struct layout_range_header *h;
1656 switch (kind)
1658 case LAYOUT_RANGE_REGULAR:
1660 struct layout_range *range;
1662 range = heap_alloc(sizeof(*range));
1663 if (!range) return NULL;
1665 range->weight = layout->format.weight;
1666 range->style = layout->format.style;
1667 range->stretch = layout->format.stretch;
1668 range->fontsize = layout->format.fontsize;
1669 range->object = NULL;
1670 range->underline = FALSE;
1671 range->pair_kerning = FALSE;
1673 range->fontfamily = heap_strdupW(layout->format.family_name);
1674 if (!range->fontfamily) {
1675 heap_free(range);
1676 return NULL;
1679 range->collection = layout->format.collection;
1680 if (range->collection)
1681 IDWriteFontCollection_AddRef(range->collection);
1682 strcpyW(range->locale, layout->format.locale);
1684 h = &range->h;
1685 break;
1687 case LAYOUT_RANGE_STRIKETHROUGH:
1689 struct layout_range_bool *range;
1691 range = heap_alloc(sizeof(*range));
1692 if (!range) return NULL;
1694 range->value = FALSE;
1695 h = &range->h;
1696 break;
1698 case LAYOUT_RANGE_EFFECT:
1700 struct layout_range_effect *range;
1702 range = heap_alloc(sizeof(*range));
1703 if (!range) return NULL;
1705 range->effect = NULL;
1706 h = &range->h;
1707 break;
1709 case LAYOUT_RANGE_SPACING:
1711 struct layout_range_spacing *range;
1713 range = heap_alloc(sizeof(*range));
1714 if (!range) return NULL;
1716 range->leading = 0.0;
1717 range->trailing = 0.0;
1718 range->min_advance = 0.0;
1719 h = &range->h;
1720 break;
1722 default:
1723 FIXME("unknown range kind %d\n", kind);
1724 return NULL;
1727 h->kind = kind;
1728 h->range = *r;
1729 return h;
1732 static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
1734 struct layout_range_header *ret;
1736 switch (h->kind)
1738 case LAYOUT_RANGE_REGULAR:
1740 struct layout_range *from = (struct layout_range*)h;
1742 struct layout_range *range = heap_alloc(sizeof(*range));
1743 if (!range) return NULL;
1745 *range = *from;
1746 range->fontfamily = heap_strdupW(from->fontfamily);
1747 if (!range->fontfamily) {
1748 heap_free(range);
1749 return NULL;
1752 /* update refcounts */
1753 if (range->object)
1754 IDWriteInlineObject_AddRef(range->object);
1755 if (range->collection)
1756 IDWriteFontCollection_AddRef(range->collection);
1757 ret = &range->h;
1758 break;
1760 case LAYOUT_RANGE_STRIKETHROUGH:
1762 struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
1763 if (!strike) return NULL;
1765 *strike = *(struct layout_range_bool*)h;
1766 ret = &strike->h;
1767 break;
1769 case LAYOUT_RANGE_EFFECT:
1771 struct layout_range_effect *effect = heap_alloc(sizeof(*effect));
1772 if (!effect) return NULL;
1774 *effect = *(struct layout_range_effect*)h;
1775 if (effect->effect)
1776 IUnknown_AddRef(effect->effect);
1777 ret = &effect->h;
1778 break;
1780 case LAYOUT_RANGE_SPACING:
1782 struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
1783 if (!spacing) return NULL;
1785 *spacing = *(struct layout_range_spacing*)h;
1786 ret = &spacing->h;
1787 break;
1789 default:
1790 FIXME("unknown range kind %d\n", h->kind);
1791 return NULL;
1794 ret->range = *r;
1795 return ret;
1798 static void free_layout_range(struct layout_range_header *h)
1800 if (!h)
1801 return;
1803 switch (h->kind)
1805 case LAYOUT_RANGE_REGULAR:
1807 struct layout_range *range = (struct layout_range*)h;
1809 if (range->object)
1810 IDWriteInlineObject_Release(range->object);
1811 if (range->collection)
1812 IDWriteFontCollection_Release(range->collection);
1813 heap_free(range->fontfamily);
1814 break;
1816 case LAYOUT_RANGE_EFFECT:
1818 struct layout_range_effect *effect = (struct layout_range_effect*)h;
1819 if (effect->effect)
1820 IUnknown_Release(effect->effect);
1821 break;
1823 default:
1827 heap_free(h);
1830 static void free_layout_ranges_list(struct dwrite_textlayout *layout)
1832 struct layout_range_header *cur, *cur2;
1834 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
1835 list_remove(&cur->entry);
1836 free_layout_range(cur);
1839 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
1840 list_remove(&cur->entry);
1841 free_layout_range(cur);
1844 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
1845 list_remove(&cur->entry);
1846 free_layout_range(cur);
1849 LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
1850 list_remove(&cur->entry);
1851 free_layout_range(cur);
1855 static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
1857 struct layout_range_header *cur;
1859 LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
1861 if (cur->range.startPosition > range->startPosition)
1862 return NULL;
1864 if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
1865 (range->startPosition < cur->range.startPosition + cur->range.length))
1866 return NULL;
1867 if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
1868 return cur;
1871 return NULL;
1874 static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
1876 struct layout_range *cur;
1878 LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
1879 DWRITE_TEXT_RANGE *r = &cur->h.range;
1880 if (r->startPosition <= pos && pos < r->startPosition + r->length)
1881 return cur;
1884 return NULL;
1887 static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
1889 if (*dest == value) return FALSE;
1891 if (*dest)
1892 IUnknown_Release(*dest);
1893 *dest = value;
1894 if (*dest)
1895 IUnknown_AddRef(*dest);
1897 return TRUE;
1900 static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
1902 struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
1903 struct layout_range_effect *dest_effect = (struct layout_range_effect*)h;
1904 struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
1905 struct layout_range *dest = (struct layout_range*)h;
1907 BOOL changed = FALSE;
1909 switch (attr) {
1910 case LAYOUT_RANGE_ATTR_WEIGHT:
1911 changed = dest->weight != value->u.weight;
1912 dest->weight = value->u.weight;
1913 break;
1914 case LAYOUT_RANGE_ATTR_STYLE:
1915 changed = dest->style != value->u.style;
1916 dest->style = value->u.style;
1917 break;
1918 case LAYOUT_RANGE_ATTR_STRETCH:
1919 changed = dest->stretch != value->u.stretch;
1920 dest->stretch = value->u.stretch;
1921 break;
1922 case LAYOUT_RANGE_ATTR_FONTSIZE:
1923 changed = dest->fontsize != value->u.fontsize;
1924 dest->fontsize = value->u.fontsize;
1925 break;
1926 case LAYOUT_RANGE_ATTR_INLINE:
1927 changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
1928 break;
1929 case LAYOUT_RANGE_ATTR_EFFECT:
1930 changed = set_layout_range_iface_attr((IUnknown**)&dest_effect->effect, (IUnknown*)value->u.effect);
1931 break;
1932 case LAYOUT_RANGE_ATTR_UNDERLINE:
1933 changed = dest->underline != value->u.underline;
1934 dest->underline = value->u.underline;
1935 break;
1936 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
1937 changed = dest_bool->value != value->u.strikethrough;
1938 dest_bool->value = value->u.strikethrough;
1939 break;
1940 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
1941 changed = dest->pair_kerning != value->u.pair_kerning;
1942 dest->pair_kerning = value->u.pair_kerning;
1943 break;
1944 case LAYOUT_RANGE_ATTR_FONTCOLL:
1945 changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
1946 break;
1947 case LAYOUT_RANGE_ATTR_LOCALE:
1948 changed = strcmpW(dest->locale, value->u.locale) != 0;
1949 if (changed)
1950 strcpyW(dest->locale, value->u.locale);
1951 break;
1952 case LAYOUT_RANGE_ATTR_FONTFAMILY:
1953 changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
1954 if (changed) {
1955 heap_free(dest->fontfamily);
1956 dest->fontfamily = heap_strdupW(value->u.fontfamily);
1958 break;
1959 case LAYOUT_RANGE_ATTR_SPACING:
1960 changed = dest_spacing->leading != value->u.spacing[0] ||
1961 dest_spacing->trailing != value->u.spacing[1] ||
1962 dest_spacing->min_advance != value->u.spacing[2];
1963 dest_spacing->leading = value->u.spacing[0];
1964 dest_spacing->trailing = value->u.spacing[1];
1965 dest_spacing->min_advance = value->u.spacing[2];
1966 break;
1967 default:
1971 return changed;
1974 static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
1976 return (inner->startPosition >= outer->startPosition) &&
1977 (inner->startPosition + inner->length <= outer->startPosition + outer->length);
1980 static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
1982 if (r) *r = h->range;
1983 return S_OK;
1986 /* Set attribute value for given range, does all needed splitting/merging of existing ranges. */
1987 static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
1989 struct layout_range_header *cur, *right, *left, *outer;
1990 BOOL changed = FALSE;
1991 struct list *ranges;
1992 DWRITE_TEXT_RANGE r;
1994 /* ignore zero length ranges */
1995 if (value->range.length == 0)
1996 return S_OK;
1998 /* select from ranges lists */
1999 switch (attr)
2001 case LAYOUT_RANGE_ATTR_WEIGHT:
2002 case LAYOUT_RANGE_ATTR_STYLE:
2003 case LAYOUT_RANGE_ATTR_STRETCH:
2004 case LAYOUT_RANGE_ATTR_FONTSIZE:
2005 case LAYOUT_RANGE_ATTR_INLINE:
2006 case LAYOUT_RANGE_ATTR_UNDERLINE:
2007 case LAYOUT_RANGE_ATTR_PAIR_KERNING:
2008 case LAYOUT_RANGE_ATTR_FONTCOLL:
2009 case LAYOUT_RANGE_ATTR_LOCALE:
2010 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2011 ranges = &layout->ranges;
2012 break;
2013 case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
2014 ranges = &layout->strike_ranges;
2015 break;
2016 case LAYOUT_RANGE_ATTR_EFFECT:
2017 ranges = &layout->effects;
2018 break;
2019 case LAYOUT_RANGE_ATTR_SPACING:
2020 ranges = &layout->spacing;
2021 break;
2022 default:
2023 FIXME("unknown attr kind %d\n", attr);
2024 return E_FAIL;
2027 /* If new range is completely within existing range, split existing range in two */
2028 if ((outer = find_outer_range(ranges, &value->range))) {
2030 /* no need to add same range */
2031 if (is_same_layout_attrvalue(outer, attr, value))
2032 return S_OK;
2034 /* for matching range bounds just replace data */
2035 if (is_same_text_range(&outer->range, &value->range)) {
2036 changed = set_layout_range_attrval(outer, attr, value);
2037 goto done;
2040 /* add new range to the left */
2041 if (value->range.startPosition == outer->range.startPosition) {
2042 left = alloc_layout_range_from(outer, &value->range);
2043 if (!left) return E_OUTOFMEMORY;
2045 changed = set_layout_range_attrval(left, attr, value);
2046 list_add_before(&outer->entry, &left->entry);
2047 outer->range.startPosition += value->range.length;
2048 outer->range.length -= value->range.length;
2049 goto done;
2052 /* add new range to the right */
2053 if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
2054 right = alloc_layout_range_from(outer, &value->range);
2055 if (!right) return E_OUTOFMEMORY;
2057 changed = set_layout_range_attrval(right, attr, value);
2058 list_add_after(&outer->entry, &right->entry);
2059 outer->range.length -= value->range.length;
2060 goto done;
2063 r.startPosition = value->range.startPosition + value->range.length;
2064 r.length = outer->range.length + outer->range.startPosition - r.startPosition;
2066 /* right part */
2067 right = alloc_layout_range_from(outer, &r);
2068 /* new range in the middle */
2069 cur = alloc_layout_range_from(outer, &value->range);
2070 if (!right || !cur) {
2071 free_layout_range(right);
2072 free_layout_range(cur);
2073 return E_OUTOFMEMORY;
2076 /* reuse container range as a left part */
2077 outer->range.length = value->range.startPosition - outer->range.startPosition;
2079 /* new part */
2080 set_layout_range_attrval(cur, attr, value);
2082 list_add_after(&outer->entry, &cur->entry);
2083 list_add_after(&cur->entry, &right->entry);
2085 return S_OK;
2088 /* Now it's only possible that given range contains some existing ranges, fully or partially.
2089 Update all of them. */
2090 left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
2091 if (left->range.startPosition == value->range.startPosition)
2092 changed = set_layout_range_attrval(left, attr, value);
2093 else /* need to split */ {
2094 r.startPosition = value->range.startPosition;
2095 r.length = left->range.length - value->range.startPosition + left->range.startPosition;
2096 left->range.length -= r.length;
2097 cur = alloc_layout_range_from(left, &r);
2098 changed = set_layout_range_attrval(cur, attr, value);
2099 list_add_after(&left->entry, &cur->entry);
2101 cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
2103 /* for all existing ranges covered by new one update value */
2104 while (cur && is_in_layout_range(&value->range, &cur->range)) {
2105 changed = set_layout_range_attrval(cur, attr, value);
2106 cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
2109 /* it's possible rightmost range intersects */
2110 if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
2111 r.startPosition = cur->range.startPosition;
2112 r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
2113 left = alloc_layout_range_from(cur, &r);
2114 changed = set_layout_range_attrval(left, attr, value);
2115 cur->range.startPosition += left->range.length;
2116 cur->range.length -= left->range.length;
2117 list_add_before(&cur->entry, &left->entry);
2120 done:
2121 if (changed) {
2122 struct list *next, *i;
2124 layout->recompute = RECOMPUTE_EVERYTHING;
2125 i = list_head(ranges);
2126 while ((next = list_next(ranges, i))) {
2127 struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
2129 cur = LIST_ENTRY(i, struct layout_range_header, entry);
2130 if (is_same_layout_attributes(cur, next_range)) {
2131 /* remove similar range */
2132 cur->range.length += next_range->range.length;
2133 list_remove(next);
2134 free_layout_range(next_range);
2136 else
2137 i = list_next(ranges, i);
2141 return S_OK;
2144 static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
2146 const WCHAR *str;
2148 switch (kind) {
2149 case LAYOUT_RANGE_ATTR_LOCALE:
2150 str = range->locale;
2151 break;
2152 case LAYOUT_RANGE_ATTR_FONTFAMILY:
2153 str = range->fontfamily;
2154 break;
2155 default:
2156 str = NULL;
2159 return str;
2162 static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2163 UINT32 *length, DWRITE_TEXT_RANGE *r)
2165 struct layout_range *range;
2166 const WCHAR *str;
2168 range = get_layout_range_by_pos(layout, position);
2169 if (!range) {
2170 *length = 0;
2171 return S_OK;
2174 str = get_string_attribute_ptr(range, kind);
2175 *length = strlenW(str);
2176 return return_range(&range->h, r);
2179 static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
2180 WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
2182 struct layout_range *range;
2183 const WCHAR *str;
2185 if (length == 0)
2186 return E_INVALIDARG;
2188 ret[0] = 0;
2189 range = get_layout_range_by_pos(layout, position);
2190 if (!range)
2191 return E_INVALIDARG;
2193 str = get_string_attribute_ptr(range, kind);
2194 if (length < strlenW(str) + 1)
2195 return E_NOT_SUFFICIENT_BUFFER;
2197 strcpyW(ret, str);
2198 return return_range(&range->h, r);
2201 static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout2 *iface, REFIID riid, void **obj)
2203 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2205 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2207 *obj = NULL;
2209 if (IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
2210 IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
2211 IsEqualIID(riid, &IID_IDWriteTextLayout) ||
2212 IsEqualIID(riid, &IID_IUnknown))
2214 *obj = iface;
2216 else if (IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
2217 IsEqualIID(riid, &IID_IDWriteTextFormat))
2218 *obj = &This->IDWriteTextFormat1_iface;
2220 if (*obj) {
2221 IDWriteTextLayout2_AddRef(iface);
2222 return S_OK;
2225 return E_NOINTERFACE;
2228 static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout2 *iface)
2230 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2231 ULONG ref = InterlockedIncrement(&This->ref);
2232 TRACE("(%p)->(%d)\n", This, ref);
2233 return ref;
2236 static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout2 *iface)
2238 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2239 ULONG ref = InterlockedDecrement(&This->ref);
2241 TRACE("(%p)->(%d)\n", This, ref);
2243 if (!ref) {
2244 free_layout_ranges_list(This);
2245 free_layout_eruns(This);
2246 free_layout_runs(This);
2247 release_format_data(&This->format);
2248 heap_free(This->nominal_breakpoints);
2249 heap_free(This->actual_breakpoints);
2250 heap_free(This->clustermetrics);
2251 heap_free(This->clusters);
2252 heap_free(This->lines);
2253 heap_free(This->str);
2254 heap_free(This);
2257 return ref;
2260 static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
2262 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2263 return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment);
2266 static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
2268 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2269 return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment);
2272 static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout2 *iface, DWRITE_WORD_WRAPPING wrapping)
2274 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2275 return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping);
2278 static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout2 *iface, DWRITE_READING_DIRECTION direction)
2280 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2281 return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction);
2284 static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout2 *iface, DWRITE_FLOW_DIRECTION direction)
2286 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2287 TRACE("(%p)->(%d)\n", This, direction);
2288 return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction);
2291 static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout2 *iface, FLOAT tabstop)
2293 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2294 TRACE("(%p)->(%.2f)\n", This, tabstop);
2295 return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop);
2298 static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout2 *iface, DWRITE_TRIMMING const *trimming,
2299 IDWriteInlineObject *trimming_sign)
2301 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2302 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
2303 return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign);
2306 static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout2 *iface, DWRITE_LINE_SPACING_METHOD spacing,
2307 FLOAT line_spacing, FLOAT baseline)
2309 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2310 TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
2311 return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline);
2314 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout2 *iface)
2316 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2317 return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface);
2320 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout2 *iface)
2322 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2323 return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface);
2326 static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout2 *iface)
2328 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2329 return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface);
2332 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout2 *iface)
2334 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2335 return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface);
2338 static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout2 *iface)
2340 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2341 return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface);
2344 static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout2 *iface)
2346 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2347 return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface);
2350 static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout2 *iface, DWRITE_TRIMMING *options,
2351 IDWriteInlineObject **trimming_sign)
2353 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2354 return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign);
2357 static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout2 *iface, DWRITE_LINE_SPACING_METHOD *method,
2358 FLOAT *spacing, FLOAT *baseline)
2360 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2361 return IDWriteTextFormat1_GetLineSpacing(&This->IDWriteTextFormat1_iface, method, spacing, baseline);
2364 static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout2 *iface, IDWriteFontCollection **collection)
2366 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2367 return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection);
2370 static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout2 *iface)
2372 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2373 return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface);
2376 static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout2 *iface, WCHAR *name, UINT32 size)
2378 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2379 return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size);
2382 static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout2 *iface)
2384 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2385 return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface);
2388 static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout2 *iface)
2390 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2391 return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface);
2394 static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout2 *iface)
2396 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2397 return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface);
2400 static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout2 *iface)
2402 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2403 return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface);
2406 static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout2 *iface)
2408 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2409 return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface);
2412 static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout2 *iface, WCHAR *name, UINT32 size)
2414 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2415 return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size);
2418 static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout2 *iface, FLOAT maxWidth)
2420 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2422 TRACE("(%p)->(%.2f)\n", This, maxWidth);
2424 if (maxWidth < 0.0)
2425 return E_INVALIDARG;
2427 This->metrics.layoutWidth = maxWidth;
2428 return S_OK;
2431 static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout2 *iface, FLOAT maxHeight)
2433 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2435 TRACE("(%p)->(%.2f)\n", This, maxHeight);
2437 if (maxHeight < 0.0)
2438 return E_INVALIDARG;
2440 This->metrics.layoutHeight = maxHeight;
2441 return S_OK;
2444 static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout2 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
2446 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2447 struct layout_range_attr_value value;
2449 TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
2451 value.range = range;
2452 value.u.collection = collection;
2453 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
2456 static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout2 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
2458 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2459 struct layout_range_attr_value value;
2461 TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
2463 if (!name)
2464 return E_INVALIDARG;
2466 value.range = range;
2467 value.u.fontfamily = name;
2468 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
2471 static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout2 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
2473 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2474 struct layout_range_attr_value value;
2476 TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
2478 value.range = range;
2479 value.u.weight = weight;
2480 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
2483 static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout2 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
2485 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2486 struct layout_range_attr_value value;
2488 TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
2490 if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
2491 return E_INVALIDARG;
2493 value.range = range;
2494 value.u.style = style;
2495 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
2498 static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout2 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
2500 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2501 struct layout_range_attr_value value;
2503 TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
2505 if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
2506 return E_INVALIDARG;
2508 value.range = range;
2509 value.u.stretch = stretch;
2510 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
2513 static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout2 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
2515 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2516 struct layout_range_attr_value value;
2518 TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
2520 if (size <= 0.0)
2521 return E_INVALIDARG;
2523 value.range = range;
2524 value.u.fontsize = size;
2525 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
2528 static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout2 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
2530 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2531 struct layout_range_attr_value value;
2533 TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
2535 value.range = range;
2536 value.u.underline = underline;
2537 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
2540 static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout2 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
2542 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2543 struct layout_range_attr_value value;
2545 TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
2547 value.range = range;
2548 value.u.strikethrough = strikethrough;
2549 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
2552 static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout2 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
2554 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2555 struct layout_range_attr_value value;
2557 TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
2559 value.range = range;
2560 value.u.effect = effect;
2561 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
2564 static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout2 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
2566 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2567 struct layout_range_attr_value value;
2569 TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
2571 value.range = range;
2572 value.u.object = object;
2573 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
2576 static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout2 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
2578 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2579 FIXME("(%p)->(%p %s): stub\n", This, typography, debugstr_range(&range));
2580 return E_NOTIMPL;
2583 static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout2 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
2585 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2586 struct layout_range_attr_value value;
2588 TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
2590 if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
2591 return E_INVALIDARG;
2593 value.range = range;
2594 value.u.locale = locale;
2595 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
2598 static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout2 *iface)
2600 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2601 TRACE("(%p)\n", This);
2602 return This->metrics.layoutWidth;
2605 static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout2 *iface)
2607 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2608 TRACE("(%p)\n", This);
2609 return This->metrics.layoutHeight;
2612 static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout2 *iface, UINT32 position,
2613 IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
2615 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2616 struct layout_range *range;
2618 TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
2620 if (position >= This->len)
2621 return S_OK;
2623 range = get_layout_range_by_pos(This, position);
2624 *collection = range->collection;
2625 if (*collection)
2626 IDWriteFontCollection_AddRef(*collection);
2628 return return_range(&range->h, r);
2631 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout2 *iface,
2632 UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
2634 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2635 TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
2636 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
2639 static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout2 *iface,
2640 UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
2642 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2643 TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
2644 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
2647 static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout2 *iface,
2648 UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
2650 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2651 struct layout_range *range;
2653 TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
2655 if (position >= This->len)
2656 return S_OK;
2658 range = get_layout_range_by_pos(This, position);
2659 *weight = range->weight;
2661 return return_range(&range->h, r);
2664 static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout2 *iface,
2665 UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
2667 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2668 struct layout_range *range;
2670 TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
2672 range = get_layout_range_by_pos(This, position);
2673 *style = range->style;
2674 return return_range(&range->h, r);
2677 static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout2 *iface,
2678 UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
2680 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2681 struct layout_range *range;
2683 TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
2685 range = get_layout_range_by_pos(This, position);
2686 *stretch = range->stretch;
2687 return return_range(&range->h, r);
2690 static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout2 *iface,
2691 UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
2693 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2694 struct layout_range *range;
2696 TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
2698 range = get_layout_range_by_pos(This, position);
2699 *size = range->fontsize;
2700 return return_range(&range->h, r);
2703 static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout2 *iface,
2704 UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
2706 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2707 struct layout_range *range;
2709 TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
2711 if (position >= This->len)
2712 return S_OK;
2714 range = get_layout_range_by_pos(This, position);
2715 *underline = range->underline;
2717 return return_range(&range->h, r);
2720 static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout2 *iface,
2721 UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
2723 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2724 struct layout_range_bool *range;
2726 TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
2728 range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
2729 *strikethrough = range->value;
2731 return return_range(&range->h, r);
2734 static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout2 *iface,
2735 UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
2737 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2738 struct layout_range_effect *range;
2740 TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
2742 range = (struct layout_range_effect*)get_layout_range_header_by_pos(&This->effects, position);
2743 *effect = range->effect;
2744 if (*effect)
2745 IUnknown_AddRef(*effect);
2747 return return_range(&range->h, r);
2750 static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout2 *iface,
2751 UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
2753 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2754 struct layout_range *range;
2756 TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
2758 if (position >= This->len)
2759 return S_OK;
2761 range = get_layout_range_by_pos(This, position);
2762 *object = range->object;
2763 if (*object)
2764 IDWriteInlineObject_AddRef(*object);
2766 return return_range(&range->h, r);
2769 static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout2 *iface,
2770 UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *range)
2772 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2773 FIXME("(%p)->(%u %p %p): stub\n", This, position, typography, range);
2774 return E_NOTIMPL;
2777 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout2 *iface,
2778 UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
2780 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2781 TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
2782 return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
2785 static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout2 *iface,
2786 UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
2788 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2789 TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
2790 return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
2793 static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
2794 const DWRITE_MATRIX *m)
2796 FLOAT vec[2], vec2[2];
2798 if (!skiptransform) {
2799 /* apply transform */
2800 vec[0] = 0.0;
2801 vec[1] = coord * ppdip;
2803 vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx;
2804 vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy;
2806 /* snap */
2807 vec2[0] = floorf(vec2[0] + 0.5f);
2808 vec2[1] = floorf(vec2[1] + 0.5f);
2810 /* apply inverted transform, we don't care about X component at this point */
2811 vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
2812 vec[1] /= ppdip;
2814 else
2815 vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
2817 return vec[1];
2820 static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
2821 void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
2823 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2824 BOOL disabled = FALSE, skiptransform = FALSE;
2825 struct layout_effective_inline *inlineobject;
2826 struct layout_effective_run *run;
2827 struct layout_strikethrough *s;
2828 FLOAT det = 0.0, ppdip = 0.0;
2829 DWRITE_MATRIX m = { 0 };
2830 HRESULT hr;
2832 TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
2834 hr = layout_compute_effective_runs(This);
2835 if (FAILED(hr))
2836 return hr;
2838 hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
2839 if (FAILED(hr))
2840 return hr;
2842 if (!disabled) {
2843 hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
2844 if (FAILED(hr))
2845 return hr;
2847 hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
2848 if (FAILED(hr))
2849 return hr;
2851 /* it's only allowed to have a diagonal/antidiagonal transform matrix */
2852 if (ppdip <= 0.0 ||
2853 (m.m11 * m.m22 != 0.0 && (m.m12 != 0.0 || m.m21 != 0.0)) ||
2854 (m.m12 * m.m21 != 0.0 && (m.m11 != 0.0 || m.m22 != 0.0)))
2855 disabled = TRUE;
2856 else
2857 skiptransform = should_skip_transform(&m, &det);
2860 #define SNAP_COORD(x) renderer_apply_snapping((x), skiptransform, ppdip, det, &m)
2861 /* 1. Regular runs */
2862 LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
2863 const struct regular_layout_run *regular = &run->run->u.regular;
2864 UINT32 start_glyph = regular->clustermap[run->start];
2865 DWRITE_GLYPH_RUN_DESCRIPTION descr;
2866 DWRITE_GLYPH_RUN glyph_run;
2868 /* Everything but cluster map will be reused from nominal run, as we only need
2869 to adjust some pointers. Cluster map however is rebuilt when effective run is added,
2870 it can't be reused because it has to start with 0 index for each reported run. */
2871 glyph_run = regular->run;
2872 glyph_run.glyphCount = run->glyphcount;
2874 /* fixup glyph data arrays */
2875 glyph_run.glyphIndices += start_glyph;
2876 glyph_run.glyphAdvances += start_glyph;
2877 glyph_run.glyphOffsets += start_glyph;
2879 /* description */
2880 descr = regular->descr;
2881 descr.stringLength = run->length;
2882 descr.string += run->start;
2883 descr.clusterMap = run->clustermap;
2884 descr.textPosition += run->start;
2886 /* return value is ignored */
2887 IDWriteTextRenderer_DrawGlyphRun(renderer,
2888 context,
2889 run->origin_x + run->align_dx + origin_x,
2890 disabled ? run->origin_y + origin_y : SNAP_COORD(run->origin_y + origin_y),
2891 This->measuringmode,
2892 &glyph_run,
2893 &descr,
2894 NULL);
2897 /* 2. Inline objects */
2898 LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
2899 IDWriteTextRenderer_DrawInlineObject(renderer,
2900 context,
2901 inlineobject->origin_x + inlineobject->align_dx + origin_x,
2902 disabled ? inlineobject->origin_y + origin_y : SNAP_COORD(inlineobject->origin_y + origin_y),
2903 inlineobject->object,
2904 inlineobject->is_sideways,
2905 inlineobject->is_rtl,
2906 inlineobject->effect);
2909 /* TODO: 3. Underlines */
2911 /* 4. Strikethrough */
2912 LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
2913 IDWriteTextRenderer_DrawStrikethrough(renderer,
2914 context,
2915 s->run->origin_x,
2916 disabled ? s->run->origin_y : SNAP_COORD(s->run->origin_y),
2917 &s->s,
2918 NULL);
2920 #undef SNAP_COORD
2922 return S_OK;
2925 static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout2 *iface,
2926 DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
2928 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2929 HRESULT hr;
2931 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
2933 hr = layout_compute_effective_runs(This);
2934 if (FAILED(hr))
2935 return hr;
2937 if (metrics)
2938 memcpy(metrics, This->lines, sizeof(*metrics)*min(max_count, This->metrics.lineCount));
2940 *count = This->metrics.lineCount;
2941 return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
2944 static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout2 *iface, DWRITE_TEXT_METRICS *metrics)
2946 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2947 DWRITE_TEXT_METRICS1 metrics1;
2948 HRESULT hr;
2950 TRACE("(%p)->(%p)\n", This, metrics);
2952 hr = IDWriteTextLayout2_GetMetrics(iface, &metrics1);
2953 if (hr == S_OK)
2954 memcpy(metrics, &metrics1, sizeof(*metrics));
2956 return hr;
2959 static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout2 *iface, DWRITE_OVERHANG_METRICS *overhangs)
2961 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2962 FIXME("(%p)->(%p): stub\n", This, overhangs);
2963 return E_NOTIMPL;
2966 static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout2 *iface,
2967 DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
2969 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2970 HRESULT hr;
2972 TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
2974 hr = layout_compute(This);
2975 if (FAILED(hr))
2976 return hr;
2978 if (metrics)
2979 memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
2981 *count = This->cluster_count;
2982 return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
2985 /* Only to be used with DetermineMinWidth() to find the longest cluster sequence that we don't want to try
2986 too hard to break. */
2987 static inline BOOL is_terminal_cluster(struct dwrite_textlayout *layout, UINT32 index)
2989 if (layout->clustermetrics[index].isWhitespace || layout->clustermetrics[index].isNewline ||
2990 (index == layout->cluster_count - 1))
2991 return TRUE;
2992 /* check next one */
2993 return (index < layout->cluster_count - 1) && layout->clustermetrics[index+1].isWhitespace;
2996 static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout2 *iface, FLOAT* min_width)
2998 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
2999 FLOAT width;
3000 HRESULT hr;
3001 UINT32 i;
3003 TRACE("(%p)->(%p)\n", This, min_width);
3005 if (!min_width)
3006 return E_INVALIDARG;
3008 if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
3009 goto width_done;
3011 *min_width = 0.0;
3012 hr = layout_compute(This);
3013 if (FAILED(hr))
3014 return hr;
3016 for (i = 0; i < This->cluster_count;) {
3017 if (is_terminal_cluster(This, i)) {
3018 width = This->clustermetrics[i].width;
3019 i++;
3021 else {
3022 width = 0.0;
3023 while (!is_terminal_cluster(This, i)) {
3024 width += This->clustermetrics[i].width;
3025 i++;
3027 /* count last one too */
3028 width += This->clustermetrics[i].width;
3031 if (width > This->minwidth)
3032 This->minwidth = width;
3034 This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
3036 width_done:
3037 *min_width = This->minwidth;
3038 return S_OK;
3041 static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout2 *iface,
3042 FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
3044 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3045 FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
3046 return E_NOTIMPL;
3049 static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout2 *iface,
3050 UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
3052 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3053 FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
3054 return E_NOTIMPL;
3057 static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout2 *iface,
3058 UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
3059 DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
3061 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3062 FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
3063 max_metricscount, actual_metricscount);
3064 return E_NOTIMPL;
3067 static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout2 *iface, BOOL is_pairkerning_enabled,
3068 DWRITE_TEXT_RANGE range)
3070 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3071 struct layout_range_attr_value value;
3073 TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
3075 value.range = range;
3076 value.u.pair_kerning = !!is_pairkerning_enabled;
3077 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
3080 static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout2 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
3081 DWRITE_TEXT_RANGE *r)
3083 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3084 struct layout_range *range;
3086 TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
3088 if (position >= This->len)
3089 return S_OK;
3091 range = get_layout_range_by_pos(This, position);
3092 *is_pairkerning_enabled = range->pair_kerning;
3094 return return_range(&range->h, r);
3097 static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout2 *iface, FLOAT leading, FLOAT trailing,
3098 FLOAT min_advance, DWRITE_TEXT_RANGE range)
3100 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3101 struct layout_range_attr_value value;
3103 TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
3105 if (min_advance < 0.0)
3106 return E_INVALIDARG;
3108 value.range = range;
3109 value.u.spacing[0] = leading;
3110 value.u.spacing[1] = trailing;
3111 value.u.spacing[2] = min_advance;
3112 return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
3115 static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout2 *iface, UINT32 position, FLOAT *leading,
3116 FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
3118 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3119 struct layout_range_spacing *range;
3121 TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
3123 range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
3124 *leading = range->leading;
3125 *trailing = range->trailing;
3126 *min_advance = range->min_advance;
3128 return return_range(&range->h, r);
3131 static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout2 *iface, DWRITE_TEXT_METRICS1 *metrics)
3133 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3134 HRESULT hr;
3136 TRACE("(%p)->(%p)\n", This, metrics);
3138 hr = layout_compute_effective_runs(This);
3139 if (FAILED(hr))
3140 return hr;
3142 *metrics = This->metrics;
3143 return S_OK;
3146 static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3148 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3150 TRACE("(%p)->(%d)\n", This, orientation);
3152 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
3153 return E_INVALIDARG;
3155 This->format.vertical_orientation = orientation;
3156 return S_OK;
3159 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout2 *iface)
3161 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3162 TRACE("(%p)\n", This);
3163 return This->format.vertical_orientation;
3166 static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout2 *iface, BOOL lastline_wrapping_enabled)
3168 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3169 FIXME("(%p)->(%d): stub\n", This, lastline_wrapping_enabled);
3170 return E_NOTIMPL;
3173 static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout2 *iface)
3175 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3176 FIXME("(%p): stub\n", This);
3177 return FALSE;
3180 static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3182 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3183 FIXME("(%p)->(%d): stub\n", This, alignment);
3184 return E_NOTIMPL;
3187 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout2 *iface)
3189 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3190 FIXME("(%p): stub\n", This);
3191 return DWRITE_OPTICAL_ALIGNMENT_NONE;
3194 static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout2 *iface, IDWriteFontFallback *fallback)
3196 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3197 TRACE("(%p)->(%p)\n", This, fallback);
3198 return set_fontfallback_for_format(&This->format, fallback);
3201 static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout2 *iface, IDWriteFontFallback **fallback)
3203 struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
3204 TRACE("(%p)->(%p)\n", This, fallback);
3205 return get_fontfallback_from_format(&This->format, fallback);
3208 static const IDWriteTextLayout2Vtbl dwritetextlayoutvtbl = {
3209 dwritetextlayout_QueryInterface,
3210 dwritetextlayout_AddRef,
3211 dwritetextlayout_Release,
3212 dwritetextlayout_SetTextAlignment,
3213 dwritetextlayout_SetParagraphAlignment,
3214 dwritetextlayout_SetWordWrapping,
3215 dwritetextlayout_SetReadingDirection,
3216 dwritetextlayout_SetFlowDirection,
3217 dwritetextlayout_SetIncrementalTabStop,
3218 dwritetextlayout_SetTrimming,
3219 dwritetextlayout_SetLineSpacing,
3220 dwritetextlayout_GetTextAlignment,
3221 dwritetextlayout_GetParagraphAlignment,
3222 dwritetextlayout_GetWordWrapping,
3223 dwritetextlayout_GetReadingDirection,
3224 dwritetextlayout_GetFlowDirection,
3225 dwritetextlayout_GetIncrementalTabStop,
3226 dwritetextlayout_GetTrimming,
3227 dwritetextlayout_GetLineSpacing,
3228 dwritetextlayout_GetFontCollection,
3229 dwritetextlayout_GetFontFamilyNameLength,
3230 dwritetextlayout_GetFontFamilyName,
3231 dwritetextlayout_GetFontWeight,
3232 dwritetextlayout_GetFontStyle,
3233 dwritetextlayout_GetFontStretch,
3234 dwritetextlayout_GetFontSize,
3235 dwritetextlayout_GetLocaleNameLength,
3236 dwritetextlayout_GetLocaleName,
3237 dwritetextlayout_SetMaxWidth,
3238 dwritetextlayout_SetMaxHeight,
3239 dwritetextlayout_SetFontCollection,
3240 dwritetextlayout_SetFontFamilyName,
3241 dwritetextlayout_SetFontWeight,
3242 dwritetextlayout_SetFontStyle,
3243 dwritetextlayout_SetFontStretch,
3244 dwritetextlayout_SetFontSize,
3245 dwritetextlayout_SetUnderline,
3246 dwritetextlayout_SetStrikethrough,
3247 dwritetextlayout_SetDrawingEffect,
3248 dwritetextlayout_SetInlineObject,
3249 dwritetextlayout_SetTypography,
3250 dwritetextlayout_SetLocaleName,
3251 dwritetextlayout_GetMaxWidth,
3252 dwritetextlayout_GetMaxHeight,
3253 dwritetextlayout_layout_GetFontCollection,
3254 dwritetextlayout_layout_GetFontFamilyNameLength,
3255 dwritetextlayout_layout_GetFontFamilyName,
3256 dwritetextlayout_layout_GetFontWeight,
3257 dwritetextlayout_layout_GetFontStyle,
3258 dwritetextlayout_layout_GetFontStretch,
3259 dwritetextlayout_layout_GetFontSize,
3260 dwritetextlayout_GetUnderline,
3261 dwritetextlayout_GetStrikethrough,
3262 dwritetextlayout_GetDrawingEffect,
3263 dwritetextlayout_GetInlineObject,
3264 dwritetextlayout_GetTypography,
3265 dwritetextlayout_layout_GetLocaleNameLength,
3266 dwritetextlayout_layout_GetLocaleName,
3267 dwritetextlayout_Draw,
3268 dwritetextlayout_GetLineMetrics,
3269 dwritetextlayout_GetMetrics,
3270 dwritetextlayout_GetOverhangMetrics,
3271 dwritetextlayout_GetClusterMetrics,
3272 dwritetextlayout_DetermineMinWidth,
3273 dwritetextlayout_HitTestPoint,
3274 dwritetextlayout_HitTestTextPosition,
3275 dwritetextlayout_HitTestTextRange,
3276 dwritetextlayout1_SetPairKerning,
3277 dwritetextlayout1_GetPairKerning,
3278 dwritetextlayout1_SetCharacterSpacing,
3279 dwritetextlayout1_GetCharacterSpacing,
3280 dwritetextlayout2_GetMetrics,
3281 dwritetextlayout2_SetVerticalGlyphOrientation,
3282 dwritetextlayout2_GetVerticalGlyphOrientation,
3283 dwritetextlayout2_SetLastLineWrapping,
3284 dwritetextlayout2_GetLastLineWrapping,
3285 dwritetextlayout2_SetOpticalAlignment,
3286 dwritetextlayout2_GetOpticalAlignment,
3287 dwritetextlayout2_SetFontFallback,
3288 dwritetextlayout2_GetFontFallback
3291 static HRESULT WINAPI dwritetextformat1_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
3293 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3294 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
3295 return IDWriteTextLayout2_QueryInterface(&This->IDWriteTextLayout2_iface, riid, obj);
3298 static ULONG WINAPI dwritetextformat1_layout_AddRef(IDWriteTextFormat1 *iface)
3300 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3301 return IDWriteTextLayout2_AddRef(&This->IDWriteTextLayout2_iface);
3304 static ULONG WINAPI dwritetextformat1_layout_Release(IDWriteTextFormat1 *iface)
3306 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3307 return IDWriteTextLayout2_Release(&This->IDWriteTextLayout2_iface);
3310 static HRESULT WINAPI dwritetextformat1_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
3312 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3313 BOOL changed;
3314 HRESULT hr;
3316 TRACE("(%p)->(%d)\n", This, alignment);
3318 hr = format_set_textalignment(&This->format, alignment, &changed);
3319 if (FAILED(hr))
3320 return hr;
3322 /* if layout is not ready there's nothing to align */
3323 if (changed && !(This->recompute & RECOMPUTE_EFFECTIVE_RUNS))
3324 layout_apply_text_alignment(This);
3326 return S_OK;
3329 static HRESULT WINAPI dwritetextformat1_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
3331 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3332 BOOL changed;
3333 HRESULT hr;
3335 TRACE("(%p)->(%d)\n", This, alignment);
3337 hr = format_set_paralignment(&This->format, alignment, &changed);
3338 if (FAILED(hr))
3339 return hr;
3341 /* if layout is not ready there's nothing to align */
3342 if (changed && !(This->recompute & RECOMPUTE_EFFECTIVE_RUNS))
3343 layout_apply_par_alignment(This);
3345 return S_OK;
3348 static HRESULT WINAPI dwritetextformat1_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
3350 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3351 BOOL changed;
3352 HRESULT hr;
3354 TRACE("(%p)->(%d)\n", This, wrapping);
3356 hr = format_set_wordwrapping(&This->format, wrapping, &changed);
3357 if (FAILED(hr))
3358 return hr;
3360 if (changed)
3361 This->recompute |= RECOMPUTE_EFFECTIVE_RUNS;
3363 return S_OK;
3366 static HRESULT WINAPI dwritetextformat1_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
3368 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3369 BOOL changed;
3370 HRESULT hr;
3372 TRACE("(%p)->(%d)\n", This, direction);
3374 hr = format_set_readingdirection(&This->format, direction, &changed);
3375 if (FAILED(hr))
3376 return hr;
3378 if (changed)
3379 This->recompute = RECOMPUTE_EVERYTHING;
3381 return S_OK;
3384 static HRESULT WINAPI dwritetextformat1_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
3386 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3387 FIXME("(%p)->(%d): stub\n", This, direction);
3388 return E_NOTIMPL;
3391 static HRESULT WINAPI dwritetextformat1_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
3393 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3394 FIXME("(%p)->(%f): stub\n", This, tabstop);
3395 return E_NOTIMPL;
3398 static HRESULT WINAPI dwritetextformat1_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
3399 IDWriteInlineObject *trimming_sign)
3401 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3402 FIXME("(%p)->(%p %p): stub\n", This, trimming, trimming_sign);
3403 return E_NOTIMPL;
3406 static HRESULT WINAPI dwritetextformat1_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD spacing,
3407 FLOAT line_spacing, FLOAT baseline)
3409 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3410 FIXME("(%p)->(%d %f %f): stub\n", This, spacing, line_spacing, baseline);
3411 return E_NOTIMPL;
3414 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat1_layout_GetTextAlignment(IDWriteTextFormat1 *iface)
3416 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3417 TRACE("(%p)\n", This);
3418 return This->format.textalignment;
3421 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat1_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface)
3423 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3424 TRACE("(%p)\n", This);
3425 return This->format.paralign;
3428 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat1_layout_GetWordWrapping(IDWriteTextFormat1 *iface)
3430 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3431 TRACE("(%p)\n", This);
3432 return This->format.wrapping;
3435 static DWRITE_READING_DIRECTION WINAPI dwritetextformat1_layout_GetReadingDirection(IDWriteTextFormat1 *iface)
3437 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3438 TRACE("(%p)\n", This);
3439 return This->format.readingdir;
3442 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat1_layout_GetFlowDirection(IDWriteTextFormat1 *iface)
3444 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3445 TRACE("(%p)\n", This);
3446 return This->format.flow;
3449 static FLOAT WINAPI dwritetextformat1_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
3451 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3452 FIXME("(%p): stub\n", This);
3453 return 0.0;
3456 static HRESULT WINAPI dwritetextformat1_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
3457 IDWriteInlineObject **trimming_sign)
3459 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3461 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
3463 *options = This->format.trimming;
3464 *trimming_sign = This->format.trimmingsign;
3465 if (*trimming_sign)
3466 IDWriteInlineObject_AddRef(*trimming_sign);
3467 return S_OK;
3470 static HRESULT WINAPI dwritetextformat1_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
3471 FLOAT *spacing, FLOAT *baseline)
3473 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3475 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
3477 *method = This->format.spacingmethod;
3478 *spacing = This->format.spacing;
3479 *baseline = This->format.baseline;
3480 return S_OK;
3483 static HRESULT WINAPI dwritetextformat1_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
3485 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3487 TRACE("(%p)->(%p)\n", This, collection);
3489 *collection = This->format.collection;
3490 if (*collection)
3491 IDWriteFontCollection_AddRef(*collection);
3492 return S_OK;
3495 static UINT32 WINAPI dwritetextformat1_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
3497 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3498 TRACE("(%p)\n", This);
3499 return This->format.family_len;
3502 static HRESULT WINAPI dwritetextformat1_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
3504 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3506 TRACE("(%p)->(%p %u)\n", This, name, size);
3508 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
3509 strcpyW(name, This->format.family_name);
3510 return S_OK;
3513 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat1_layout_GetFontWeight(IDWriteTextFormat1 *iface)
3515 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3516 TRACE("(%p)\n", This);
3517 return This->format.weight;
3520 static DWRITE_FONT_STYLE WINAPI dwritetextformat1_layout_GetFontStyle(IDWriteTextFormat1 *iface)
3522 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3523 TRACE("(%p)\n", This);
3524 return This->format.style;
3527 static DWRITE_FONT_STRETCH WINAPI dwritetextformat1_layout_GetFontStretch(IDWriteTextFormat1 *iface)
3529 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3530 TRACE("(%p)\n", This);
3531 return This->format.stretch;
3534 static FLOAT WINAPI dwritetextformat1_layout_GetFontSize(IDWriteTextFormat1 *iface)
3536 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3537 TRACE("(%p)\n", This);
3538 return This->format.fontsize;
3541 static UINT32 WINAPI dwritetextformat1_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface)
3543 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3544 TRACE("(%p)\n", This);
3545 return This->format.locale_len;
3548 static HRESULT WINAPI dwritetextformat1_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
3550 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3552 TRACE("(%p)->(%p %u)\n", This, name, size);
3554 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
3555 strcpyW(name, This->format.locale);
3556 return S_OK;
3559 static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
3561 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3562 FIXME("(%p)->(%d): stub\n", This, orientation);
3563 return E_NOTIMPL;
3566 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
3568 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3569 FIXME("(%p): stub\n", This);
3570 return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
3573 static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
3575 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3576 FIXME("(%p)->(%d): stub\n", This, lastline_wrapping_enabled);
3577 return E_NOTIMPL;
3580 static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface)
3582 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3583 FIXME("(%p): stub\n", This);
3584 return FALSE;
3587 static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
3589 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3590 FIXME("(%p)->(%d): stub\n", This, alignment);
3591 return E_NOTIMPL;
3594 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface)
3596 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3597 FIXME("(%p): stub\n", This);
3598 return DWRITE_OPTICAL_ALIGNMENT_NONE;
3601 static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
3603 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3604 TRACE("(%p)->(%p)\n", This, fallback);
3605 return IDWriteTextLayout2_SetFontFallback(&This->IDWriteTextLayout2_iface, fallback);
3608 static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
3610 struct dwrite_textlayout *This = impl_layout_form_IDWriteTextFormat1(iface);
3611 TRACE("(%p)->(%p)\n", This, fallback);
3612 return IDWriteTextLayout2_GetFontFallback(&This->IDWriteTextLayout2_iface, fallback);
3615 static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = {
3616 dwritetextformat1_layout_QueryInterface,
3617 dwritetextformat1_layout_AddRef,
3618 dwritetextformat1_layout_Release,
3619 dwritetextformat1_layout_SetTextAlignment,
3620 dwritetextformat1_layout_SetParagraphAlignment,
3621 dwritetextformat1_layout_SetWordWrapping,
3622 dwritetextformat1_layout_SetReadingDirection,
3623 dwritetextformat1_layout_SetFlowDirection,
3624 dwritetextformat1_layout_SetIncrementalTabStop,
3625 dwritetextformat1_layout_SetTrimming,
3626 dwritetextformat1_layout_SetLineSpacing,
3627 dwritetextformat1_layout_GetTextAlignment,
3628 dwritetextformat1_layout_GetParagraphAlignment,
3629 dwritetextformat1_layout_GetWordWrapping,
3630 dwritetextformat1_layout_GetReadingDirection,
3631 dwritetextformat1_layout_GetFlowDirection,
3632 dwritetextformat1_layout_GetIncrementalTabStop,
3633 dwritetextformat1_layout_GetTrimming,
3634 dwritetextformat1_layout_GetLineSpacing,
3635 dwritetextformat1_layout_GetFontCollection,
3636 dwritetextformat1_layout_GetFontFamilyNameLength,
3637 dwritetextformat1_layout_GetFontFamilyName,
3638 dwritetextformat1_layout_GetFontWeight,
3639 dwritetextformat1_layout_GetFontStyle,
3640 dwritetextformat1_layout_GetFontStretch,
3641 dwritetextformat1_layout_GetFontSize,
3642 dwritetextformat1_layout_GetLocaleNameLength,
3643 dwritetextformat1_layout_GetLocaleName,
3644 dwritetextformat1_layout_SetVerticalGlyphOrientation,
3645 dwritetextformat1_layout_GetVerticalGlyphOrientation,
3646 dwritetextformat1_layout_SetLastLineWrapping,
3647 dwritetextformat1_layout_GetLastLineWrapping,
3648 dwritetextformat1_layout_SetOpticalAlignment,
3649 dwritetextformat1_layout_GetOpticalAlignment,
3650 dwritetextformat1_layout_SetFontFallback,
3651 dwritetextformat1_layout_GetFontFallback
3654 static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink *iface,
3655 REFIID riid, void **obj)
3657 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown)) {
3658 *obj = iface;
3659 IDWriteTextAnalysisSink_AddRef(iface);
3660 return S_OK;
3663 *obj = NULL;
3664 return E_NOINTERFACE;
3667 static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink *iface)
3669 return 2;
3672 static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink *iface)
3674 return 1;
3677 static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
3678 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
3680 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink(iface);
3681 struct layout_run *run;
3683 TRACE("%u %u script=%d\n", position, length, sa->script);
3685 run = alloc_layout_run(LAYOUT_RUN_REGULAR);
3686 if (!run)
3687 return E_OUTOFMEMORY;
3689 run->u.regular.descr.string = &layout->str[position];
3690 run->u.regular.descr.stringLength = length;
3691 run->u.regular.descr.textPosition = position;
3692 run->u.regular.sa = *sa;
3693 list_add_tail(&layout->runs, &run->entry);
3694 return S_OK;
3697 static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
3698 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
3700 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink(iface);
3702 if (position + length > layout->len)
3703 return E_FAIL;
3705 memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
3706 return S_OK;
3709 static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink *iface, UINT32 position,
3710 UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
3712 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink(iface);
3713 struct layout_run *cur_run;
3715 TRACE("%u %u %u %u\n", position, length, explicitLevel, resolvedLevel);
3717 LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
3718 struct regular_layout_run *cur = &cur_run->u.regular;
3719 struct layout_run *run;
3721 if (cur_run->kind == LAYOUT_RUN_INLINE)
3722 continue;
3724 /* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
3725 if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
3726 continue;
3728 /* full hit - just set run level */
3729 if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
3730 cur->run.bidiLevel = resolvedLevel;
3731 break;
3734 /* current run is fully covered, move to next one */
3735 if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
3736 cur->run.bidiLevel = resolvedLevel;
3737 position += cur->descr.stringLength;
3738 length -= cur->descr.stringLength;
3739 continue;
3742 /* all fully covered runs are processed at this point, reuse existing run for remaining
3743 reported bidi range and add another run for the rest of original one */
3745 run = alloc_layout_run(LAYOUT_RUN_REGULAR);
3746 if (!run)
3747 return E_OUTOFMEMORY;
3749 *run = *cur_run;
3750 run->u.regular.descr.textPosition = position + length;
3751 run->u.regular.descr.stringLength = cur->descr.stringLength - length;
3752 run->u.regular.descr.string = &layout->str[position + length];
3754 /* reduce existing run */
3755 cur->run.bidiLevel = resolvedLevel;
3756 cur->descr.stringLength = length;
3758 list_add_after(&cur_run->entry, &run->entry);
3759 break;
3762 return S_OK;
3765 static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
3766 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
3768 return E_NOTIMPL;
3771 static const IDWriteTextAnalysisSinkVtbl dwritetextlayoutsinkvtbl = {
3772 dwritetextlayout_sink_QueryInterface,
3773 dwritetextlayout_sink_AddRef,
3774 dwritetextlayout_sink_Release,
3775 dwritetextlayout_sink_SetScriptAnalysis,
3776 dwritetextlayout_sink_SetLineBreakpoints,
3777 dwritetextlayout_sink_SetBidiLevel,
3778 dwritetextlayout_sink_SetNumberSubstitution
3781 static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource *iface,
3782 REFIID riid, void **obj)
3784 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
3785 IsEqualIID(riid, &IID_IUnknown))
3787 *obj = iface;
3788 IDWriteTextAnalysisSource_AddRef(iface);
3789 return S_OK;
3792 *obj = NULL;
3793 return E_NOINTERFACE;
3796 static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource *iface)
3798 return 2;
3801 static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource *iface)
3803 return 1;
3806 static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
3807 UINT32 position, WCHAR const** text, UINT32* text_len)
3809 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface);
3811 TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
3813 if (position < layout->len) {
3814 *text = &layout->str[position];
3815 *text_len = layout->len - position;
3817 else {
3818 *text = NULL;
3819 *text_len = 0;
3822 return S_OK;
3825 static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
3826 UINT32 position, WCHAR const** text, UINT32* text_len)
3828 FIXME("%u %p %p: stub\n", position, text, text_len);
3829 return E_NOTIMPL;
3832 static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource *iface)
3834 struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource(iface);
3835 return IDWriteTextLayout2_GetReadingDirection(&layout->IDWriteTextLayout2_iface);
3838 static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource *iface,
3839 UINT32 position, UINT32* text_len, WCHAR const** locale)
3841 FIXME("%u %p %p: stub\n", position, text_len, locale);
3842 return E_NOTIMPL;
3845 static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
3846 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
3848 FIXME("%u %p %p: stub\n", position, text_len, substitution);
3849 return E_NOTIMPL;
3852 static const IDWriteTextAnalysisSourceVtbl dwritetextlayoutsourcevtbl = {
3853 dwritetextlayout_source_QueryInterface,
3854 dwritetextlayout_source_AddRef,
3855 dwritetextlayout_source_Release,
3856 dwritetextlayout_source_GetTextAtPosition,
3857 dwritetextlayout_source_GetTextBeforePosition,
3858 dwritetextlayout_source_GetParagraphReadingDirection,
3859 dwritetextlayout_source_GetLocaleName,
3860 dwritetextlayout_source_GetNumberSubstitution
3863 static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
3865 IDWriteTextFormat1 *format1;
3866 UINT32 len;
3867 HRESULT hr;
3869 layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
3870 layout->format.style = IDWriteTextFormat_GetFontStyle(format);
3871 layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
3872 layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
3873 layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
3874 layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
3875 layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
3876 layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
3877 layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
3878 layout->format.fallback = NULL;
3879 hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacingmethod,
3880 &layout->format.spacing, &layout->format.baseline);
3881 if (FAILED(hr))
3882 return hr;
3884 hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
3885 if (FAILED(hr))
3886 return hr;
3888 /* locale name and length */
3889 len = IDWriteTextFormat_GetLocaleNameLength(format);
3890 layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
3891 if (!layout->format.locale)
3892 return E_OUTOFMEMORY;
3894 hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
3895 if (FAILED(hr))
3896 return hr;
3897 layout->format.locale_len = len;
3899 /* font family name and length */
3900 len = IDWriteTextFormat_GetFontFamilyNameLength(format);
3901 layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
3902 if (!layout->format.family_name)
3903 return E_OUTOFMEMORY;
3905 hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
3906 if (FAILED(hr))
3907 return hr;
3908 layout->format.family_len = len;
3910 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
3911 if (hr == S_OK) {
3912 layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
3913 IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
3914 IDWriteTextFormat1_Release(format1);
3916 else
3917 layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
3919 return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
3922 static HRESULT init_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *format, FLOAT maxwidth, FLOAT maxheight, struct dwrite_textlayout *layout)
3924 struct layout_range_header *range, *strike, *effect, *spacing;
3925 DWRITE_TEXT_RANGE r = { 0, ~0u };
3926 HRESULT hr;
3928 layout->IDWriteTextLayout2_iface.lpVtbl = &dwritetextlayoutvtbl;
3929 layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl;
3930 layout->IDWriteTextAnalysisSink_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
3931 layout->IDWriteTextAnalysisSource_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
3932 layout->ref = 1;
3933 layout->len = len;
3934 layout->recompute = RECOMPUTE_EVERYTHING;
3935 layout->nominal_breakpoints = NULL;
3936 layout->actual_breakpoints = NULL;
3937 layout->cluster_count = 0;
3938 layout->clustermetrics = NULL;
3939 layout->clusters = NULL;
3940 layout->lines = NULL;
3941 layout->line_alloc = 0;
3942 layout->minwidth = 0.0;
3943 list_init(&layout->eruns);
3944 list_init(&layout->inlineobjects);
3945 list_init(&layout->strikethrough);
3946 list_init(&layout->runs);
3947 list_init(&layout->ranges);
3948 list_init(&layout->strike_ranges);
3949 list_init(&layout->effects);
3950 list_init(&layout->spacing);
3951 memset(&layout->format, 0, sizeof(layout->format));
3952 memset(&layout->metrics, 0, sizeof(layout->metrics));
3953 layout->metrics.layoutWidth = maxwidth;
3954 layout->metrics.layoutHeight = maxheight;
3955 layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
3957 layout->ppdip = 0.0;
3958 memset(&layout->transform, 0, sizeof(layout->transform));
3960 layout->str = heap_strdupnW(str, len);
3961 if (len && !layout->str) {
3962 hr = E_OUTOFMEMORY;
3963 goto fail;
3966 hr = layout_format_from_textformat(layout, format);
3967 if (FAILED(hr))
3968 goto fail;
3970 range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
3971 strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
3972 effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
3973 spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
3974 if (!range || !strike || !effect || !spacing) {
3975 free_layout_range(range);
3976 free_layout_range(strike);
3977 free_layout_range(effect);
3978 free_layout_range(spacing);
3979 hr = E_OUTOFMEMORY;
3980 goto fail;
3983 list_add_head(&layout->ranges, &range->entry);
3984 list_add_head(&layout->strike_ranges, &strike->entry);
3985 list_add_head(&layout->effects, &effect->entry);
3986 list_add_head(&layout->spacing, &spacing->entry);
3987 return S_OK;
3989 fail:
3990 IDWriteTextLayout2_Release(&layout->IDWriteTextLayout2_iface);
3991 return hr;
3994 HRESULT create_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *format, FLOAT maxwidth, FLOAT maxheight, IDWriteTextLayout **ret)
3996 struct dwrite_textlayout *layout;
3997 HRESULT hr;
3999 *ret = NULL;
4001 layout = heap_alloc(sizeof(struct dwrite_textlayout));
4002 if (!layout) return E_OUTOFMEMORY;
4004 hr = init_textlayout(str, len, format, maxwidth, maxheight, layout);
4005 if (hr == S_OK)
4006 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout2_iface;
4008 return hr;
4011 HRESULT create_gdicompat_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *format, FLOAT maxwidth, FLOAT maxheight,
4012 FLOAT ppdip, const DWRITE_MATRIX *transform, BOOL use_gdi_natural, IDWriteTextLayout **ret)
4014 struct dwrite_textlayout *layout;
4015 HRESULT hr;
4017 *ret = NULL;
4019 layout = heap_alloc(sizeof(struct dwrite_textlayout));
4020 if (!layout) return E_OUTOFMEMORY;
4022 hr = init_textlayout(str, len, format, maxwidth, maxheight, layout);
4023 if (hr == S_OK) {
4024 layout->measuringmode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
4026 /* set gdi-specific properties */
4027 layout->ppdip = ppdip;
4028 layout->transform = transform ? *transform : identity;
4030 *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout2_iface;
4033 return hr;
4036 static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
4038 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4040 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4042 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
4043 *obj = iface;
4044 IDWriteInlineObject_AddRef(iface);
4045 return S_OK;
4048 *obj = NULL;
4049 return E_NOINTERFACE;
4052 static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
4054 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4055 ULONG ref = InterlockedIncrement(&This->ref);
4056 TRACE("(%p)->(%d)\n", This, ref);
4057 return ref;
4060 static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
4062 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4063 ULONG ref = InterlockedDecrement(&This->ref);
4065 TRACE("(%p)->(%d)\n", This, ref);
4067 if (!ref) {
4068 IDWriteTextLayout_Release(This->layout);
4069 heap_free(This);
4072 return ref;
4075 static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
4076 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
4078 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4079 DWRITE_TEXT_RANGE range = { 0, ~0u };
4081 TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY, is_sideways, is_rtl, effect);
4083 IDWriteTextLayout_SetDrawingEffect(This->layout, effect, range);
4084 return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY);
4087 static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
4089 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4090 DWRITE_TEXT_METRICS metrics;
4091 HRESULT hr;
4093 TRACE("(%p)->(%p)\n", This, ret);
4095 hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
4096 if (FAILED(hr)) {
4097 memset(ret, 0, sizeof(*ret));
4098 return hr;
4101 ret->width = metrics.width;
4102 ret->height = 0.0;
4103 ret->baseline = 0.0;
4104 ret->supportsSideways = FALSE;
4105 return S_OK;
4108 static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
4110 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4111 FIXME("(%p)->(%p): stub\n", This, overhangs);
4112 return E_NOTIMPL;
4115 static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
4116 DWRITE_BREAK_CONDITION *after)
4118 struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
4120 TRACE("(%p)->(%p %p)\n", This, before, after);
4122 *before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
4123 return S_OK;
4126 static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
4127 dwritetrimmingsign_QueryInterface,
4128 dwritetrimmingsign_AddRef,
4129 dwritetrimmingsign_Release,
4130 dwritetrimmingsign_Draw,
4131 dwritetrimmingsign_GetMetrics,
4132 dwritetrimmingsign_GetOverhangMetrics,
4133 dwritetrimmingsign_GetBreakConditions
4136 static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
4138 return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
4139 (direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
4142 static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
4144 return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
4145 (direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
4148 static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
4150 return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
4151 (direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
4154 static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
4156 return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
4157 (direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
4160 HRESULT create_trimmingsign(IDWriteFactory2 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
4162 static const WCHAR ellipsisW = 0x2026;
4163 struct dwrite_trimmingsign *This;
4164 DWRITE_READING_DIRECTION reading;
4165 DWRITE_FLOW_DIRECTION flow;
4166 HRESULT hr;
4168 *sign = NULL;
4170 /* Validate reading/flow direction here, layout creation won't complain about
4171 invalid combinations. */
4172 reading = IDWriteTextFormat_GetReadingDirection(format);
4173 flow = IDWriteTextFormat_GetFlowDirection(format);
4175 if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
4176 (is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
4177 return DWRITE_E_FLOWDIRECTIONCONFLICTS;
4179 This = heap_alloc(sizeof(*This));
4180 if (!This)
4181 return E_OUTOFMEMORY;
4183 This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
4184 This->ref = 1;
4186 hr = IDWriteFactory2_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0, 0.0, &This->layout);
4187 if (FAILED(hr)) {
4188 heap_free(This);
4189 return hr;
4192 IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4193 *sign = &This->IDWriteInlineObject_iface;
4195 return S_OK;
4198 static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
4200 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4202 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
4204 if (IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
4205 IsEqualIID(riid, &IID_IDWriteTextFormat) ||
4206 IsEqualIID(riid, &IID_IUnknown))
4208 *obj = iface;
4209 IDWriteTextFormat1_AddRef(iface);
4210 return S_OK;
4213 *obj = NULL;
4215 return E_NOINTERFACE;
4218 static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat1 *iface)
4220 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4221 ULONG ref = InterlockedIncrement(&This->ref);
4222 TRACE("(%p)->(%d)\n", This, ref);
4223 return ref;
4226 static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat1 *iface)
4228 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4229 ULONG ref = InterlockedDecrement(&This->ref);
4231 TRACE("(%p)->(%d)\n", This, ref);
4233 if (!ref)
4235 release_format_data(&This->format);
4236 heap_free(This);
4239 return ref;
4242 static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
4244 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4245 TRACE("(%p)->(%d)\n", This, alignment);
4246 return format_set_textalignment(&This->format, alignment, NULL);
4249 static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
4251 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4252 TRACE("(%p)->(%d)\n", This, alignment);
4253 return format_set_paralignment(&This->format, alignment, NULL);
4256 static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
4258 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4259 TRACE("(%p)->(%d)\n", This, wrapping);
4260 return format_set_wordwrapping(&This->format, wrapping, NULL);
4263 static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
4265 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4266 TRACE("(%p)->(%d)\n", This, direction);
4267 return format_set_readingdirection(&This->format, direction, NULL);
4270 static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
4272 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4274 TRACE("(%p)->(%d)\n", This, direction);
4276 if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
4277 return E_INVALIDARG;
4279 This->format.flow = direction;
4280 return S_OK;
4283 static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
4285 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4286 FIXME("(%p)->(%f): stub\n", This, tabstop);
4287 return E_NOTIMPL;
4290 static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
4291 IDWriteInlineObject *trimming_sign)
4293 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4294 TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
4296 This->format.trimming = *trimming;
4297 if (This->format.trimmingsign)
4298 IDWriteInlineObject_Release(This->format.trimmingsign);
4299 This->format.trimmingsign = trimming_sign;
4300 if (This->format.trimmingsign)
4301 IDWriteInlineObject_AddRef(This->format.trimmingsign);
4302 return S_OK;
4305 static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method,
4306 FLOAT spacing, FLOAT baseline)
4308 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4310 TRACE("(%p)->(%d %f %f)\n", This, method, spacing, baseline);
4312 if (spacing < 0.0 || (UINT32)method > DWRITE_LINE_SPACING_METHOD_UNIFORM)
4313 return E_INVALIDARG;
4315 This->format.spacingmethod = method;
4316 This->format.spacing = spacing;
4317 This->format.baseline = baseline;
4318 return S_OK;
4321 static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat1 *iface)
4323 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4324 TRACE("(%p)\n", This);
4325 return This->format.textalignment;
4328 static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat1 *iface)
4330 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4331 TRACE("(%p)\n", This);
4332 return This->format.paralign;
4335 static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat1 *iface)
4337 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4338 TRACE("(%p)\n", This);
4339 return This->format.wrapping;
4342 static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat1 *iface)
4344 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4345 TRACE("(%p)\n", This);
4346 return This->format.readingdir;
4349 static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat1 *iface)
4351 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4352 TRACE("(%p)\n", This);
4353 return This->format.flow;
4356 static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
4358 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4359 FIXME("(%p): stub\n", This);
4360 return 0.0;
4363 static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
4364 IDWriteInlineObject **trimming_sign)
4366 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4367 TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
4369 *options = This->format.trimming;
4370 if ((*trimming_sign = This->format.trimmingsign))
4371 IDWriteInlineObject_AddRef(*trimming_sign);
4373 return S_OK;
4376 static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
4377 FLOAT *spacing, FLOAT *baseline)
4379 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4380 TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
4382 *method = This->format.spacingmethod;
4383 *spacing = This->format.spacing;
4384 *baseline = This->format.baseline;
4385 return S_OK;
4388 static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
4390 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4392 TRACE("(%p)->(%p)\n", This, collection);
4394 *collection = This->format.collection;
4395 IDWriteFontCollection_AddRef(*collection);
4397 return S_OK;
4400 static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
4402 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4403 TRACE("(%p)\n", This);
4404 return This->format.family_len;
4407 static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4409 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4411 TRACE("(%p)->(%p %u)\n", This, name, size);
4413 if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
4414 strcpyW(name, This->format.family_name);
4415 return S_OK;
4418 static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat1 *iface)
4420 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4421 TRACE("(%p)\n", This);
4422 return This->format.weight;
4425 static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat1 *iface)
4427 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4428 TRACE("(%p)\n", This);
4429 return This->format.style;
4432 static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat1 *iface)
4434 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4435 TRACE("(%p)\n", This);
4436 return This->format.stretch;
4439 static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat1 *iface)
4441 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4442 TRACE("(%p)\n", This);
4443 return This->format.fontsize;
4446 static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat1 *iface)
4448 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4449 TRACE("(%p)\n", This);
4450 return This->format.locale_len;
4453 static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
4455 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4457 TRACE("(%p)->(%p %u)\n", This, name, size);
4459 if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
4460 strcpyW(name, This->format.locale);
4461 return S_OK;
4464 static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
4466 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4468 TRACE("(%p)->(%d)\n", This, orientation);
4470 if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
4471 return E_INVALIDARG;
4473 This->format.vertical_orientation = orientation;
4474 return S_OK;
4477 static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
4479 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4480 TRACE("(%p)\n", This);
4481 return This->format.vertical_orientation;
4484 static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
4486 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4487 FIXME("(%p)->(%d): stub\n", This, lastline_wrapping_enabled);
4488 return E_NOTIMPL;
4491 static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat1 *iface)
4493 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4494 FIXME("(%p): stub\n", This);
4495 return FALSE;
4498 static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
4500 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4501 FIXME("(%p)->(%d): stub\n", This, alignment);
4502 return E_NOTIMPL;
4505 static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat1 *iface)
4507 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4508 FIXME("(%p): stub\n", This);
4509 return DWRITE_OPTICAL_ALIGNMENT_NONE;
4512 static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
4514 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4515 TRACE("(%p)->(%p)\n", This, fallback);
4516 return set_fontfallback_for_format(&This->format, fallback);
4519 static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
4521 struct dwrite_textformat *This = impl_from_IDWriteTextFormat1(iface);
4522 TRACE("(%p)->(%p)\n", This, fallback);
4523 return get_fontfallback_from_format(&This->format, fallback);
4526 static const IDWriteTextFormat1Vtbl dwritetextformatvtbl = {
4527 dwritetextformat_QueryInterface,
4528 dwritetextformat_AddRef,
4529 dwritetextformat_Release,
4530 dwritetextformat_SetTextAlignment,
4531 dwritetextformat_SetParagraphAlignment,
4532 dwritetextformat_SetWordWrapping,
4533 dwritetextformat_SetReadingDirection,
4534 dwritetextformat_SetFlowDirection,
4535 dwritetextformat_SetIncrementalTabStop,
4536 dwritetextformat_SetTrimming,
4537 dwritetextformat_SetLineSpacing,
4538 dwritetextformat_GetTextAlignment,
4539 dwritetextformat_GetParagraphAlignment,
4540 dwritetextformat_GetWordWrapping,
4541 dwritetextformat_GetReadingDirection,
4542 dwritetextformat_GetFlowDirection,
4543 dwritetextformat_GetIncrementalTabStop,
4544 dwritetextformat_GetTrimming,
4545 dwritetextformat_GetLineSpacing,
4546 dwritetextformat_GetFontCollection,
4547 dwritetextformat_GetFontFamilyNameLength,
4548 dwritetextformat_GetFontFamilyName,
4549 dwritetextformat_GetFontWeight,
4550 dwritetextformat_GetFontStyle,
4551 dwritetextformat_GetFontStretch,
4552 dwritetextformat_GetFontSize,
4553 dwritetextformat_GetLocaleNameLength,
4554 dwritetextformat_GetLocaleName,
4555 dwritetextformat1_SetVerticalGlyphOrientation,
4556 dwritetextformat1_GetVerticalGlyphOrientation,
4557 dwritetextformat1_SetLastLineWrapping,
4558 dwritetextformat1_GetLastLineWrapping,
4559 dwritetextformat1_SetOpticalAlignment,
4560 dwritetextformat1_GetOpticalAlignment,
4561 dwritetextformat1_SetFontFallback,
4562 dwritetextformat1_GetFontFallback
4565 HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
4566 DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
4568 struct dwrite_textformat *This;
4570 *format = NULL;
4572 This = heap_alloc(sizeof(struct dwrite_textformat));
4573 if (!This) return E_OUTOFMEMORY;
4575 This->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformatvtbl;
4576 This->ref = 1;
4577 This->format.family_name = heap_strdupW(family_name);
4578 This->format.family_len = strlenW(family_name);
4579 This->format.locale = heap_strdupW(locale);
4580 This->format.locale_len = strlenW(locale);
4581 This->format.weight = weight;
4582 This->format.style = style;
4583 This->format.fontsize = size;
4584 This->format.stretch = stretch;
4585 This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
4586 This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
4587 This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
4588 This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
4589 This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
4590 This->format.spacingmethod = DWRITE_LINE_SPACING_METHOD_DEFAULT;
4591 This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
4592 This->format.spacing = 0.0;
4593 This->format.baseline = 0.0;
4594 This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
4595 This->format.trimming.delimiter = 0;
4596 This->format.trimming.delimiterCount = 0;
4597 This->format.trimmingsign = NULL;
4598 This->format.collection = collection;
4599 This->format.fallback = NULL;
4600 IDWriteFontCollection_AddRef(collection);
4602 *format = (IDWriteTextFormat*)&This->IDWriteTextFormat1_iface;
4604 return S_OK;
4607 static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
4609 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4611 TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
4613 if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
4614 *obj = iface;
4615 IDWriteTypography_AddRef(iface);
4616 return S_OK;
4619 *obj = NULL;
4621 return E_NOINTERFACE;
4624 static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
4626 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4627 ULONG ref = InterlockedIncrement(&typography->ref);
4628 TRACE("(%p)->(%d)\n", typography, ref);
4629 return ref;
4632 static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
4634 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4635 ULONG ref = InterlockedDecrement(&typography->ref);
4637 TRACE("(%p)->(%d)\n", typography, ref);
4639 if (!ref) {
4640 heap_free(typography->features);
4641 heap_free(typography);
4644 return ref;
4647 static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
4649 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4651 TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
4653 if (typography->count == typography->allocated) {
4654 DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
4655 if (!ptr)
4656 return E_OUTOFMEMORY;
4658 typography->features = ptr;
4659 typography->allocated *= 2;
4662 typography->features[typography->count++] = feature;
4663 return S_OK;
4666 static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
4668 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4669 TRACE("(%p)\n", typography);
4670 return typography->count;
4673 static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
4675 struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
4677 TRACE("(%p)->(%u %p)\n", typography, index, feature);
4679 if (index >= typography->count)
4680 return E_INVALIDARG;
4682 *feature = typography->features[index];
4683 return S_OK;
4686 static const IDWriteTypographyVtbl dwritetypographyvtbl = {
4687 dwritetypography_QueryInterface,
4688 dwritetypography_AddRef,
4689 dwritetypography_Release,
4690 dwritetypography_AddFontFeature,
4691 dwritetypography_GetFontFeatureCount,
4692 dwritetypography_GetFontFeature
4695 HRESULT create_typography(IDWriteTypography **ret)
4697 struct dwrite_typography *typography;
4699 *ret = NULL;
4701 typography = heap_alloc(sizeof(*typography));
4702 if (!typography)
4703 return E_OUTOFMEMORY;
4705 typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
4706 typography->ref = 1;
4707 typography->allocated = 2;
4708 typography->count = 0;
4710 typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
4711 if (!typography->features) {
4712 heap_free(typography);
4713 return E_OUTOFMEMORY;
4716 *ret = &typography->IDWriteTypography_iface;
4717 return S_OK;