5744 lines
200 KiB
C
5744 lines
200 KiB
C
/*
|
|
* Text format and layout
|
|
*
|
|
* Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#define COBJMACROS
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "dwrite_private.h"
|
|
#include "scripts.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
|
|
|
|
struct dwrite_textformat_data {
|
|
WCHAR *family_name;
|
|
UINT32 family_len;
|
|
WCHAR *locale;
|
|
UINT32 locale_len;
|
|
|
|
DWRITE_FONT_WEIGHT weight;
|
|
DWRITE_FONT_STYLE style;
|
|
DWRITE_FONT_STRETCH stretch;
|
|
|
|
DWRITE_PARAGRAPH_ALIGNMENT paralign;
|
|
DWRITE_READING_DIRECTION readingdir;
|
|
DWRITE_WORD_WRAPPING wrapping;
|
|
BOOL last_line_wrapping;
|
|
DWRITE_TEXT_ALIGNMENT textalignment;
|
|
DWRITE_FLOW_DIRECTION flow;
|
|
DWRITE_VERTICAL_GLYPH_ORIENTATION vertical_orientation;
|
|
DWRITE_OPTICAL_ALIGNMENT optical_alignment;
|
|
DWRITE_LINE_SPACING spacing;
|
|
|
|
FLOAT fontsize;
|
|
|
|
DWRITE_TRIMMING trimming;
|
|
IDWriteInlineObject *trimmingsign;
|
|
|
|
IDWriteFontCollection *collection;
|
|
IDWriteFontFallback *fallback;
|
|
};
|
|
|
|
enum layout_range_attr_kind {
|
|
LAYOUT_RANGE_ATTR_WEIGHT,
|
|
LAYOUT_RANGE_ATTR_STYLE,
|
|
LAYOUT_RANGE_ATTR_STRETCH,
|
|
LAYOUT_RANGE_ATTR_FONTSIZE,
|
|
LAYOUT_RANGE_ATTR_EFFECT,
|
|
LAYOUT_RANGE_ATTR_INLINE,
|
|
LAYOUT_RANGE_ATTR_UNDERLINE,
|
|
LAYOUT_RANGE_ATTR_STRIKETHROUGH,
|
|
LAYOUT_RANGE_ATTR_PAIR_KERNING,
|
|
LAYOUT_RANGE_ATTR_FONTCOLL,
|
|
LAYOUT_RANGE_ATTR_LOCALE,
|
|
LAYOUT_RANGE_ATTR_FONTFAMILY,
|
|
LAYOUT_RANGE_ATTR_SPACING,
|
|
LAYOUT_RANGE_ATTR_TYPOGRAPHY
|
|
};
|
|
|
|
struct layout_range_attr_value {
|
|
DWRITE_TEXT_RANGE range;
|
|
union {
|
|
DWRITE_FONT_WEIGHT weight;
|
|
DWRITE_FONT_STYLE style;
|
|
DWRITE_FONT_STRETCH stretch;
|
|
FLOAT fontsize;
|
|
IDWriteInlineObject *object;
|
|
IUnknown *effect;
|
|
BOOL underline;
|
|
BOOL strikethrough;
|
|
BOOL pair_kerning;
|
|
IDWriteFontCollection *collection;
|
|
const WCHAR *locale;
|
|
const WCHAR *fontfamily;
|
|
struct {
|
|
FLOAT leading;
|
|
FLOAT trailing;
|
|
FLOAT min_advance;
|
|
} spacing;
|
|
IDWriteTypography *typography;
|
|
} u;
|
|
};
|
|
|
|
enum layout_range_kind {
|
|
LAYOUT_RANGE_REGULAR,
|
|
LAYOUT_RANGE_UNDERLINE,
|
|
LAYOUT_RANGE_STRIKETHROUGH,
|
|
LAYOUT_RANGE_EFFECT,
|
|
LAYOUT_RANGE_SPACING,
|
|
LAYOUT_RANGE_TYPOGRAPHY
|
|
};
|
|
|
|
struct layout_range_header {
|
|
struct list entry;
|
|
enum layout_range_kind kind;
|
|
DWRITE_TEXT_RANGE range;
|
|
};
|
|
|
|
struct layout_range {
|
|
struct layout_range_header h;
|
|
DWRITE_FONT_WEIGHT weight;
|
|
DWRITE_FONT_STYLE style;
|
|
FLOAT fontsize;
|
|
DWRITE_FONT_STRETCH stretch;
|
|
IDWriteInlineObject *object;
|
|
BOOL pair_kerning;
|
|
IDWriteFontCollection *collection;
|
|
WCHAR locale[LOCALE_NAME_MAX_LENGTH];
|
|
WCHAR *fontfamily;
|
|
};
|
|
|
|
struct layout_range_bool {
|
|
struct layout_range_header h;
|
|
BOOL value;
|
|
};
|
|
|
|
struct layout_range_iface {
|
|
struct layout_range_header h;
|
|
IUnknown *iface;
|
|
};
|
|
|
|
struct layout_range_spacing {
|
|
struct layout_range_header h;
|
|
FLOAT leading;
|
|
FLOAT trailing;
|
|
FLOAT min_advance;
|
|
};
|
|
|
|
enum layout_run_kind {
|
|
LAYOUT_RUN_REGULAR,
|
|
LAYOUT_RUN_INLINE
|
|
};
|
|
|
|
struct inline_object_run {
|
|
IDWriteInlineObject *object;
|
|
UINT16 length;
|
|
};
|
|
|
|
struct regular_layout_run {
|
|
DWRITE_GLYPH_RUN_DESCRIPTION descr;
|
|
DWRITE_GLYPH_RUN run;
|
|
DWRITE_SCRIPT_ANALYSIS sa;
|
|
UINT16 *glyphs;
|
|
UINT16 *clustermap;
|
|
FLOAT *advances;
|
|
DWRITE_GLYPH_OFFSET *offsets;
|
|
UINT32 glyphcount; /* actual glyph count after shaping, not necessarily the same as reported to Draw() */
|
|
};
|
|
|
|
struct layout_run {
|
|
struct list entry;
|
|
enum layout_run_kind kind;
|
|
union {
|
|
struct inline_object_run object;
|
|
struct regular_layout_run regular;
|
|
} u;
|
|
FLOAT baseline;
|
|
FLOAT height;
|
|
UINT32 start_position; /* run text position in range [0, layout-text-length) */
|
|
};
|
|
|
|
struct layout_effective_run {
|
|
struct list entry;
|
|
const struct layout_run *run; /* nominal run this one is based on */
|
|
UINT32 start; /* relative text position, 0 means first text position of a nominal run */
|
|
UINT32 length; /* length in codepoints that this run covers */
|
|
UINT32 glyphcount; /* total glyph count in this run */
|
|
IUnknown *effect; /* original reference is kept only at range level */
|
|
D2D1_POINT_2F origin; /* baseline origin */
|
|
FLOAT align_dx; /* adjustment from text alignment */
|
|
FLOAT width; /* run width */
|
|
UINT16 *clustermap; /* effective clustermap, allocated separately, is not reused from nominal map */
|
|
UINT32 line; /* 0-based line index in line metrics array */
|
|
BOOL underlined; /* set if this run is underlined */
|
|
D2D1_RECT_F bbox; /* ink run box, top == bottom means it wasn't estimated yet */
|
|
};
|
|
|
|
struct layout_effective_inline {
|
|
struct list entry;
|
|
IDWriteInlineObject *object; /* inline object, set explicitly or added when trimming a line */
|
|
IUnknown *effect; /* original reference is kept only at range level */
|
|
FLOAT baseline;
|
|
D2D1_POINT_2F origin; /* left top corner */
|
|
FLOAT align_dx; /* adjustment from text alignment */
|
|
FLOAT width; /* object width as it's reported it */
|
|
BOOL is_sideways; /* vertical flow direction flag passed to Draw */
|
|
BOOL is_rtl; /* bidi flag passed to Draw */
|
|
UINT32 line; /* 0-based line index in line metrics array */
|
|
};
|
|
|
|
struct layout_underline {
|
|
struct list entry;
|
|
const struct layout_effective_run *run;
|
|
DWRITE_UNDERLINE u;
|
|
};
|
|
|
|
struct layout_strikethrough {
|
|
struct list entry;
|
|
const struct layout_effective_run *run;
|
|
DWRITE_STRIKETHROUGH s;
|
|
};
|
|
|
|
struct layout_cluster {
|
|
const struct layout_run *run; /* link to nominal run this cluster belongs to */
|
|
UINT32 position; /* relative to run, first cluster has 0 position */
|
|
};
|
|
|
|
struct layout_line {
|
|
FLOAT height; /* height based on content */
|
|
FLOAT baseline; /* baseline based on content */
|
|
};
|
|
|
|
enum layout_recompute_mask {
|
|
RECOMPUTE_CLUSTERS = 1 << 0,
|
|
RECOMPUTE_MINIMAL_WIDTH = 1 << 1,
|
|
RECOMPUTE_LINES = 1 << 2,
|
|
RECOMPUTE_OVERHANGS = 1 << 3,
|
|
RECOMPUTE_LINES_AND_OVERHANGS = RECOMPUTE_LINES | RECOMPUTE_OVERHANGS,
|
|
RECOMPUTE_EVERYTHING = 0xffff
|
|
};
|
|
|
|
struct dwrite_textlayout {
|
|
IDWriteTextLayout3 IDWriteTextLayout3_iface;
|
|
IDWriteTextFormat1 IDWriteTextFormat1_iface;
|
|
IDWriteTextAnalysisSink1 IDWriteTextAnalysisSink1_iface;
|
|
IDWriteTextAnalysisSource1 IDWriteTextAnalysisSource1_iface;
|
|
LONG ref;
|
|
|
|
IDWriteFactory5 *factory;
|
|
|
|
WCHAR *str;
|
|
UINT32 len;
|
|
struct dwrite_textformat_data format;
|
|
struct list strike_ranges;
|
|
struct list underline_ranges;
|
|
struct list typographies;
|
|
struct list effects;
|
|
struct list spacing;
|
|
struct list ranges;
|
|
struct list runs;
|
|
/* lists ready to use by Draw() */
|
|
struct list eruns;
|
|
struct list inlineobjects;
|
|
struct list underlines;
|
|
struct list strikethrough;
|
|
USHORT recompute;
|
|
|
|
DWRITE_LINE_BREAKPOINT *nominal_breakpoints;
|
|
DWRITE_LINE_BREAKPOINT *actual_breakpoints;
|
|
|
|
struct layout_cluster *clusters;
|
|
DWRITE_CLUSTER_METRICS *clustermetrics;
|
|
UINT32 cluster_count;
|
|
FLOAT minwidth;
|
|
|
|
struct layout_line *lines;
|
|
DWRITE_LINE_METRICS1 *linemetrics;
|
|
UINT32 line_alloc;
|
|
|
|
DWRITE_TEXT_METRICS1 metrics;
|
|
DWRITE_OVERHANG_METRICS overhangs;
|
|
|
|
DWRITE_MEASURING_MODE measuringmode;
|
|
|
|
/* gdi-compatible layout specifics */
|
|
FLOAT ppdip;
|
|
DWRITE_MATRIX transform;
|
|
};
|
|
|
|
struct dwrite_textformat {
|
|
IDWriteTextFormat2 IDWriteTextFormat2_iface;
|
|
LONG ref;
|
|
struct dwrite_textformat_data format;
|
|
};
|
|
|
|
struct dwrite_trimmingsign {
|
|
IDWriteInlineObject IDWriteInlineObject_iface;
|
|
LONG ref;
|
|
|
|
IDWriteTextLayout *layout;
|
|
};
|
|
|
|
struct dwrite_typography {
|
|
IDWriteTypography IDWriteTypography_iface;
|
|
LONG ref;
|
|
|
|
DWRITE_FONT_FEATURE *features;
|
|
UINT32 allocated;
|
|
UINT32 count;
|
|
};
|
|
|
|
static const IDWriteTextFormat2Vtbl dwritetextformatvtbl;
|
|
|
|
static void release_format_data(struct dwrite_textformat_data *data)
|
|
{
|
|
if (data->collection) IDWriteFontCollection_Release(data->collection);
|
|
if (data->fallback) IDWriteFontFallback_Release(data->fallback);
|
|
if (data->trimmingsign) IDWriteInlineObject_Release(data->trimmingsign);
|
|
heap_free(data->family_name);
|
|
heap_free(data->locale);
|
|
}
|
|
|
|
static inline struct dwrite_textlayout *impl_from_IDWriteTextLayout3(IDWriteTextLayout3 *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static inline struct dwrite_textlayout *impl_layout_from_IDWriteTextFormat1(IDWriteTextFormat1 *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSink1(IDWriteTextAnalysisSink1 *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSink1_iface);
|
|
}
|
|
|
|
static inline struct dwrite_textlayout *impl_from_IDWriteTextAnalysisSource1(IDWriteTextAnalysisSource1 *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_textlayout, IDWriteTextAnalysisSource1_iface);
|
|
}
|
|
|
|
static inline struct dwrite_textformat *impl_from_IDWriteTextFormat2(IDWriteTextFormat2 *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface);
|
|
}
|
|
|
|
static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat*);
|
|
|
|
static inline struct dwrite_trimmingsign *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_trimmingsign, IDWriteInlineObject_iface);
|
|
}
|
|
|
|
static inline struct dwrite_typography *impl_from_IDWriteTypography(IDWriteTypography *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, struct dwrite_typography, IDWriteTypography_iface);
|
|
}
|
|
|
|
static inline const char *debugstr_rundescr(const DWRITE_GLYPH_RUN_DESCRIPTION *descr)
|
|
{
|
|
return wine_dbg_sprintf("[%u,%u)", descr->textPosition, descr->textPosition + descr->stringLength);
|
|
}
|
|
|
|
static inline BOOL is_layout_gdi_compatible(struct dwrite_textlayout *layout)
|
|
{
|
|
return layout->measuringmode != DWRITE_MEASURING_MODE_NATURAL;
|
|
}
|
|
|
|
static inline HRESULT format_set_textalignment(struct dwrite_textformat_data *format, DWRITE_TEXT_ALIGNMENT alignment,
|
|
BOOL *changed)
|
|
{
|
|
if ((UINT32)alignment > DWRITE_TEXT_ALIGNMENT_JUSTIFIED)
|
|
return E_INVALIDARG;
|
|
if (changed) *changed = format->textalignment != alignment;
|
|
format->textalignment = alignment;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_paralignment(struct dwrite_textformat_data *format,
|
|
DWRITE_PARAGRAPH_ALIGNMENT alignment, BOOL *changed)
|
|
{
|
|
if ((UINT32)alignment > DWRITE_PARAGRAPH_ALIGNMENT_CENTER)
|
|
return E_INVALIDARG;
|
|
if (changed) *changed = format->paralign != alignment;
|
|
format->paralign = alignment;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_readingdirection(struct dwrite_textformat_data *format,
|
|
DWRITE_READING_DIRECTION direction, BOOL *changed)
|
|
{
|
|
if ((UINT32)direction > DWRITE_READING_DIRECTION_BOTTOM_TO_TOP)
|
|
return E_INVALIDARG;
|
|
if (changed) *changed = format->readingdir != direction;
|
|
format->readingdir = direction;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_wordwrapping(struct dwrite_textformat_data *format,
|
|
DWRITE_WORD_WRAPPING wrapping, BOOL *changed)
|
|
{
|
|
if ((UINT32)wrapping > DWRITE_WORD_WRAPPING_CHARACTER)
|
|
return E_INVALIDARG;
|
|
if (changed) *changed = format->wrapping != wrapping;
|
|
format->wrapping = wrapping;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_flowdirection(struct dwrite_textformat_data *format,
|
|
DWRITE_FLOW_DIRECTION direction, BOOL *changed)
|
|
{
|
|
if ((UINT32)direction > DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT)
|
|
return E_INVALIDARG;
|
|
if (changed) *changed = format->flow != direction;
|
|
format->flow = direction;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_trimming(struct dwrite_textformat_data *format,
|
|
DWRITE_TRIMMING const *trimming, IDWriteInlineObject *trimming_sign, BOOL *changed)
|
|
{
|
|
if (changed)
|
|
*changed = FALSE;
|
|
|
|
if ((UINT32)trimming->granularity > DWRITE_TRIMMING_GRANULARITY_WORD)
|
|
return E_INVALIDARG;
|
|
|
|
if (changed) {
|
|
*changed = !!memcmp(&format->trimming, trimming, sizeof(*trimming));
|
|
if (format->trimmingsign != trimming_sign)
|
|
*changed = TRUE;
|
|
}
|
|
|
|
format->trimming = *trimming;
|
|
if (format->trimmingsign)
|
|
IDWriteInlineObject_Release(format->trimmingsign);
|
|
format->trimmingsign = trimming_sign;
|
|
if (format->trimmingsign)
|
|
IDWriteInlineObject_AddRef(format->trimmingsign);
|
|
return S_OK;
|
|
}
|
|
|
|
static inline HRESULT format_set_linespacing(struct dwrite_textformat_data *format,
|
|
DWRITE_LINE_SPACING const *spacing, BOOL *changed)
|
|
{
|
|
if (spacing->height < 0.0f || spacing->leadingBefore < 0.0f || spacing->leadingBefore > 1.0f ||
|
|
(UINT32)spacing->method > DWRITE_LINE_SPACING_METHOD_PROPORTIONAL)
|
|
return E_INVALIDARG;
|
|
|
|
if (changed)
|
|
*changed = memcmp(spacing, &format->spacing, sizeof(*spacing));
|
|
|
|
format->spacing = *spacing;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT get_fontfallback_from_format(const struct dwrite_textformat_data *format, IDWriteFontFallback **fallback)
|
|
{
|
|
*fallback = format->fallback;
|
|
if (*fallback)
|
|
IDWriteFontFallback_AddRef(*fallback);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT set_fontfallback_for_format(struct dwrite_textformat_data *format, IDWriteFontFallback *fallback)
|
|
{
|
|
if (format->fallback)
|
|
IDWriteFontFallback_Release(format->fallback);
|
|
format->fallback = fallback;
|
|
if (fallback)
|
|
IDWriteFontFallback_AddRef(fallback);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT format_set_optical_alignment(struct dwrite_textformat_data *format,
|
|
DWRITE_OPTICAL_ALIGNMENT alignment)
|
|
{
|
|
if ((UINT32)alignment > DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS)
|
|
return E_INVALIDARG;
|
|
format->optical_alignment = alignment;
|
|
return S_OK;
|
|
}
|
|
|
|
static BOOL is_run_rtl(const struct layout_effective_run *run)
|
|
{
|
|
return run->run->u.regular.run.bidiLevel & 1;
|
|
}
|
|
|
|
static struct layout_run *alloc_layout_run(enum layout_run_kind kind, UINT32 start_position)
|
|
{
|
|
struct layout_run *ret;
|
|
|
|
ret = heap_alloc(sizeof(*ret));
|
|
if (!ret) return NULL;
|
|
|
|
memset(ret, 0, sizeof(*ret));
|
|
ret->kind = kind;
|
|
if (kind == LAYOUT_RUN_REGULAR) {
|
|
ret->u.regular.sa.script = Script_Unknown;
|
|
ret->u.regular.sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
|
|
}
|
|
ret->start_position = start_position;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void free_layout_runs(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_run *cur, *cur2;
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->runs, struct layout_run, entry) {
|
|
list_remove(&cur->entry);
|
|
if (cur->kind == LAYOUT_RUN_REGULAR) {
|
|
if (cur->u.regular.run.fontFace)
|
|
IDWriteFontFace_Release(cur->u.regular.run.fontFace);
|
|
heap_free(cur->u.regular.glyphs);
|
|
heap_free(cur->u.regular.clustermap);
|
|
heap_free(cur->u.regular.advances);
|
|
heap_free(cur->u.regular.offsets);
|
|
}
|
|
heap_free(cur);
|
|
}
|
|
}
|
|
|
|
static void free_layout_eruns(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_effective_inline *in, *in2;
|
|
struct layout_effective_run *cur, *cur2;
|
|
struct layout_strikethrough *s, *s2;
|
|
struct layout_underline *u, *u2;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->eruns, struct layout_effective_run, entry) {
|
|
list_remove(&cur->entry);
|
|
heap_free(cur->clustermap);
|
|
heap_free(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(in, in2, &layout->inlineobjects, struct layout_effective_inline, entry) {
|
|
list_remove(&in->entry);
|
|
heap_free(in);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(u, u2, &layout->underlines, struct layout_underline, entry) {
|
|
list_remove(&u->entry);
|
|
heap_free(u);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(s, s2, &layout->strikethrough, struct layout_strikethrough, entry) {
|
|
list_remove(&s->entry);
|
|
heap_free(s);
|
|
}
|
|
}
|
|
|
|
/* Used to resolve break condition by forcing stronger condition over weaker. */
|
|
static inline DWRITE_BREAK_CONDITION override_break_condition(DWRITE_BREAK_CONDITION existingbreak, DWRITE_BREAK_CONDITION newbreak)
|
|
{
|
|
switch (existingbreak) {
|
|
case DWRITE_BREAK_CONDITION_NEUTRAL:
|
|
return newbreak;
|
|
case DWRITE_BREAK_CONDITION_CAN_BREAK:
|
|
return newbreak == DWRITE_BREAK_CONDITION_NEUTRAL ? existingbreak : newbreak;
|
|
/* let's keep stronger conditions as is */
|
|
case DWRITE_BREAK_CONDITION_MAY_NOT_BREAK:
|
|
case DWRITE_BREAK_CONDITION_MUST_BREAK:
|
|
break;
|
|
default:
|
|
ERR("unknown break condition %d\n", existingbreak);
|
|
}
|
|
|
|
return existingbreak;
|
|
}
|
|
|
|
/* This helper should be used to get effective range length, in other words it returns number of text
|
|
positions from range starting point to the end of the range, limited by layout text length */
|
|
static inline UINT32 get_clipped_range_length(const struct dwrite_textlayout *layout, const struct layout_range *range)
|
|
{
|
|
if (range->h.range.startPosition + range->h.range.length <= layout->len)
|
|
return range->h.range.length;
|
|
return layout->len - range->h.range.startPosition;
|
|
}
|
|
|
|
/* Actual breakpoint data gets updated with break condition required by inline object set for range 'cur'. */
|
|
static HRESULT layout_update_breakpoints_range(struct dwrite_textlayout *layout, const struct layout_range *cur)
|
|
{
|
|
DWRITE_BREAK_CONDITION before, after;
|
|
UINT32 i, length;
|
|
HRESULT hr;
|
|
|
|
/* ignore returned conditions if failed */
|
|
hr = IDWriteInlineObject_GetBreakConditions(cur->object, &before, &after);
|
|
if (FAILED(hr))
|
|
after = before = DWRITE_BREAK_CONDITION_NEUTRAL;
|
|
|
|
if (!layout->actual_breakpoints) {
|
|
layout->actual_breakpoints = heap_alloc(sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
|
|
if (!layout->actual_breakpoints)
|
|
return E_OUTOFMEMORY;
|
|
memcpy(layout->actual_breakpoints, layout->nominal_breakpoints, sizeof(DWRITE_LINE_BREAKPOINT)*layout->len);
|
|
}
|
|
|
|
length = get_clipped_range_length(layout, cur);
|
|
for (i = cur->h.range.startPosition; i < length + cur->h.range.startPosition; i++) {
|
|
/* for first codepoint check if there's anything before it and update accordingly */
|
|
if (i == cur->h.range.startPosition) {
|
|
if (i > 0)
|
|
layout->actual_breakpoints[i].breakConditionBefore = layout->actual_breakpoints[i-1].breakConditionAfter =
|
|
override_break_condition(layout->actual_breakpoints[i-1].breakConditionAfter, before);
|
|
else
|
|
layout->actual_breakpoints[i].breakConditionBefore = before;
|
|
layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
}
|
|
/* similar check for last codepoint */
|
|
else if (i == cur->h.range.startPosition + length - 1) {
|
|
if (i == layout->len - 1)
|
|
layout->actual_breakpoints[i].breakConditionAfter = after;
|
|
else
|
|
layout->actual_breakpoints[i].breakConditionAfter = layout->actual_breakpoints[i+1].breakConditionBefore =
|
|
override_break_condition(layout->actual_breakpoints[i+1].breakConditionBefore, after);
|
|
layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
}
|
|
/* for all positions within a range disable breaks */
|
|
else {
|
|
layout->actual_breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
layout->actual_breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
|
|
}
|
|
|
|
layout->actual_breakpoints[i].isWhitespace = 0;
|
|
layout->actual_breakpoints[i].isSoftHyphen = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos);
|
|
|
|
static inline DWRITE_LINE_BREAKPOINT get_effective_breakpoint(const struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
if (layout->actual_breakpoints)
|
|
return layout->actual_breakpoints[pos];
|
|
return layout->nominal_breakpoints[pos];
|
|
}
|
|
|
|
static inline void init_cluster_metrics(const struct dwrite_textlayout *layout, const struct regular_layout_run *run,
|
|
UINT16 start_glyph, UINT16 stop_glyph, UINT32 stop_position, UINT16 length, DWRITE_CLUSTER_METRICS *metrics)
|
|
{
|
|
UINT8 breakcondition;
|
|
UINT32 position;
|
|
UINT16 j;
|
|
|
|
/* For clusters made of control chars we report zero glyphs, and we need zero cluster
|
|
width as well; advances are already computed at this point and are not necessary zero. */
|
|
metrics->width = 0.0f;
|
|
if (run->run.glyphCount) {
|
|
for (j = start_glyph; j < stop_glyph; j++)
|
|
metrics->width += run->run.glyphAdvances[j];
|
|
}
|
|
metrics->length = length;
|
|
|
|
position = run->descr.textPosition + stop_position;
|
|
if (stop_glyph == run->glyphcount)
|
|
breakcondition = get_effective_breakpoint(layout, position).breakConditionAfter;
|
|
else {
|
|
breakcondition = get_effective_breakpoint(layout, position).breakConditionBefore;
|
|
if (stop_position) position -= 1;
|
|
}
|
|
|
|
metrics->canWrapLineAfter = breakcondition == DWRITE_BREAK_CONDITION_CAN_BREAK ||
|
|
breakcondition == DWRITE_BREAK_CONDITION_MUST_BREAK;
|
|
if (metrics->length == 1) {
|
|
DWRITE_LINE_BREAKPOINT bp = get_effective_breakpoint(layout, position);
|
|
metrics->isWhitespace = bp.isWhitespace;
|
|
metrics->isNewline = metrics->canWrapLineAfter && lb_is_newline_char(layout->str[position]);
|
|
metrics->isSoftHyphen = bp.isSoftHyphen;
|
|
}
|
|
else {
|
|
metrics->isWhitespace = 0;
|
|
metrics->isNewline = 0;
|
|
metrics->isSoftHyphen = 0;
|
|
}
|
|
metrics->isRightToLeft = run->run.bidiLevel & 1;
|
|
metrics->padding = 0;
|
|
}
|
|
|
|
/*
|
|
|
|
All clusters in a 'run' will be added to 'layout' data, starting at index pointed to by 'cluster'.
|
|
On return 'cluster' is updated to point to next metrics struct to be filled in on next call.
|
|
Note that there's no need to reallocate anything at this point as we allocate one cluster per
|
|
codepoint initially.
|
|
|
|
*/
|
|
static void layout_set_cluster_metrics(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 *cluster)
|
|
{
|
|
DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[*cluster];
|
|
struct layout_cluster *c = &layout->clusters[*cluster];
|
|
const struct regular_layout_run *run = &r->u.regular;
|
|
UINT32 i, start = 0;
|
|
|
|
assert(r->kind == LAYOUT_RUN_REGULAR);
|
|
|
|
for (i = 0; i < run->descr.stringLength; i++) {
|
|
BOOL end = i == run->descr.stringLength - 1;
|
|
|
|
if (run->descr.clusterMap[start] != run->descr.clusterMap[i]) {
|
|
init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->descr.clusterMap[i], i,
|
|
i - start, metrics);
|
|
c->position = start;
|
|
c->run = r;
|
|
|
|
*cluster += 1;
|
|
metrics++;
|
|
c++;
|
|
start = i;
|
|
}
|
|
|
|
if (end) {
|
|
init_cluster_metrics(layout, run, run->descr.clusterMap[start], run->glyphcount, i,
|
|
i - start + 1, metrics);
|
|
c->position = start;
|
|
c->run = r;
|
|
|
|
*cluster += 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SCALE_FONT_METRIC(metric, emSize, metrics) ((FLOAT)(metric) * (emSize) / (FLOAT)(metrics)->designUnitsPerEm)
|
|
|
|
static void layout_get_font_metrics(struct dwrite_textlayout *layout, IDWriteFontFace *fontface, FLOAT emsize,
|
|
DWRITE_FONT_METRICS *fontmetrics)
|
|
{
|
|
if (is_layout_gdi_compatible(layout)) {
|
|
HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emsize, layout->ppdip, &layout->transform, fontmetrics);
|
|
if (FAILED(hr))
|
|
WARN("failed to get compat metrics, 0x%08x\n", hr);
|
|
}
|
|
else
|
|
IDWriteFontFace_GetMetrics(fontface, fontmetrics);
|
|
}
|
|
|
|
static void layout_get_font_height(FLOAT emsize, DWRITE_FONT_METRICS *fontmetrics, FLOAT *baseline, FLOAT *height)
|
|
{
|
|
*baseline = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->lineGap, emsize, fontmetrics);
|
|
*height = SCALE_FONT_METRIC(fontmetrics->ascent + fontmetrics->descent + fontmetrics->lineGap, emsize, fontmetrics);
|
|
}
|
|
|
|
static HRESULT layout_itemize(struct dwrite_textlayout *layout)
|
|
{
|
|
IDWriteTextAnalyzer *analyzer;
|
|
struct layout_range *range;
|
|
struct layout_run *r;
|
|
HRESULT hr = S_OK;
|
|
|
|
analyzer = get_text_analyzer();
|
|
|
|
LIST_FOR_EACH_ENTRY(range, &layout->ranges, struct layout_range, h.entry) {
|
|
/* We don't care about ranges that don't contain any text. */
|
|
if (range->h.range.startPosition >= layout->len)
|
|
break;
|
|
|
|
/* Inline objects override actual text in range. */
|
|
if (range->object) {
|
|
hr = layout_update_breakpoints_range(layout, range);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
r = alloc_layout_run(LAYOUT_RUN_INLINE, range->h.range.startPosition);
|
|
if (!r)
|
|
return E_OUTOFMEMORY;
|
|
|
|
r->u.object.object = range->object;
|
|
r->u.object.length = get_clipped_range_length(layout, range);
|
|
list_add_tail(&layout->runs, &r->entry);
|
|
continue;
|
|
}
|
|
|
|
/* Initial splitting by script. */
|
|
hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
|
range->h.range.startPosition, get_clipped_range_length(layout, range),
|
|
(IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
|
|
if (FAILED(hr))
|
|
break;
|
|
|
|
/* Splitting further by bidi levels. */
|
|
hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, (IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
|
range->h.range.startPosition, get_clipped_range_length(layout, range),
|
|
(IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface);
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT layout_resolve_fonts(struct dwrite_textlayout *layout)
|
|
{
|
|
IDWriteFontCollection *sys_collection;
|
|
IDWriteFontFallback *fallback = NULL;
|
|
struct layout_range *range;
|
|
struct layout_run *r;
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = IDWriteFactory5_GetSystemFontCollection(layout->factory, FALSE,
|
|
(IDWriteFontCollection1 **)&sys_collection, FALSE))) {
|
|
WARN("Failed to get system collection, hr %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
if (layout->format.fallback) {
|
|
fallback = layout->format.fallback;
|
|
IDWriteFontFallback_AddRef(fallback);
|
|
}
|
|
else {
|
|
if (FAILED(hr = IDWriteFactory5_GetSystemFontFallback(layout->factory, &fallback))) {
|
|
WARN("Failed to get system fallback, hr %#x.\n", hr);
|
|
goto fatal;
|
|
}
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
|
struct regular_layout_run *run = &r->u.regular;
|
|
IDWriteFont *font;
|
|
UINT32 length;
|
|
|
|
if (r->kind == LAYOUT_RUN_INLINE)
|
|
continue;
|
|
|
|
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
|
|
|
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
|
|
IDWriteFontCollection *collection;
|
|
|
|
collection = range->collection ? range->collection : sys_collection;
|
|
|
|
if (FAILED(hr = create_matching_font(collection, range->fontfamily, range->weight, range->style,
|
|
range->stretch, &font))) {
|
|
WARN("%s: failed to create matching font for non visual run, family %s, collection %p\n",
|
|
debugstr_rundescr(&run->descr), debugstr_w(range->fontfamily), range->collection);
|
|
break;
|
|
}
|
|
|
|
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
|
IDWriteFont_Release(font);
|
|
if (FAILED(hr)) {
|
|
WARN("Failed to create font face, hr %#x.\n", hr);
|
|
break;
|
|
}
|
|
|
|
run->run.fontEmSize = range->fontsize;
|
|
continue;
|
|
}
|
|
|
|
length = run->descr.stringLength;
|
|
|
|
while (length) {
|
|
UINT32 mapped_length;
|
|
FLOAT scale;
|
|
|
|
run = &r->u.regular;
|
|
|
|
hr = IDWriteFontFallback_MapCharacters(fallback,
|
|
(IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
|
run->descr.textPosition,
|
|
run->descr.stringLength,
|
|
range->collection,
|
|
range->fontfamily,
|
|
range->weight,
|
|
range->style,
|
|
range->stretch,
|
|
&mapped_length,
|
|
&font,
|
|
&scale);
|
|
if (FAILED(hr)) {
|
|
WARN("%s: failed to map family %s, collection %p, hr %#x.\n", debugstr_rundescr(&run->descr),
|
|
debugstr_w(range->fontfamily), range->collection, hr);
|
|
goto fatal;
|
|
}
|
|
|
|
hr = IDWriteFont_CreateFontFace(font, &run->run.fontFace);
|
|
IDWriteFont_Release(font);
|
|
if (FAILED(hr)) {
|
|
WARN("Failed to create font face, hr %#x.\n", hr);
|
|
goto fatal;
|
|
}
|
|
|
|
run->run.fontEmSize = range->fontsize * scale;
|
|
|
|
if (mapped_length < length) {
|
|
struct regular_layout_run *nextrun;
|
|
struct layout_run *nextr;
|
|
|
|
/* keep mapped part for current run, add another run for the rest */
|
|
nextr = alloc_layout_run(LAYOUT_RUN_REGULAR, 0);
|
|
if (!nextr) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto fatal;
|
|
}
|
|
|
|
*nextr = *r;
|
|
nextr->start_position = run->descr.textPosition + mapped_length;
|
|
nextrun = &nextr->u.regular;
|
|
nextrun->descr.textPosition = nextr->start_position;
|
|
nextrun->descr.stringLength = run->descr.stringLength - mapped_length;
|
|
nextrun->descr.string = &layout->str[nextrun->descr.textPosition];
|
|
run->descr.stringLength = mapped_length;
|
|
list_add_after(&r->entry, &nextr->entry);
|
|
r = nextr;
|
|
}
|
|
|
|
length -= mapped_length;
|
|
}
|
|
}
|
|
|
|
fatal:
|
|
IDWriteFontCollection_Release(sys_collection);
|
|
if (fallback)
|
|
IDWriteFontFallback_Release(fallback);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT layout_shape_run(struct dwrite_textlayout *layout, struct regular_layout_run *run)
|
|
{
|
|
DWRITE_SHAPING_GLYPH_PROPERTIES *glyph_props;
|
|
DWRITE_SHAPING_TEXT_PROPERTIES *text_props;
|
|
IDWriteTextAnalyzer *analyzer;
|
|
struct layout_range *range;
|
|
UINT32 max_count;
|
|
HRESULT hr;
|
|
|
|
range = get_layout_range_by_pos(layout, run->descr.textPosition);
|
|
run->descr.localeName = range->locale;
|
|
run->clustermap = heap_alloc(run->descr.stringLength * sizeof(*run->clustermap));
|
|
|
|
max_count = 3 * run->descr.stringLength / 2 + 16;
|
|
run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
|
|
if (!run->clustermap || !run->glyphs)
|
|
return E_OUTOFMEMORY;
|
|
|
|
text_props = heap_alloc(run->descr.stringLength * sizeof(*text_props));
|
|
glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
|
|
if (!text_props || !glyph_props) {
|
|
heap_free(text_props);
|
|
heap_free(glyph_props);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
analyzer = get_text_analyzer();
|
|
|
|
for (;;) {
|
|
hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, run->descr.string, run->descr.stringLength, run->run.fontFace,
|
|
run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL /* FIXME */, NULL,
|
|
NULL, 0, max_count, run->clustermap, text_props, run->glyphs, glyph_props, &run->glyphcount);
|
|
if (hr == E_NOT_SUFFICIENT_BUFFER) {
|
|
heap_free(run->glyphs);
|
|
heap_free(glyph_props);
|
|
|
|
max_count = run->glyphcount;
|
|
|
|
run->glyphs = heap_alloc(max_count * sizeof(*run->glyphs));
|
|
glyph_props = heap_alloc(max_count * sizeof(*glyph_props));
|
|
if (!run->glyphs || !glyph_props) {
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
heap_free(text_props);
|
|
heap_free(glyph_props);
|
|
WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
|
return hr;
|
|
}
|
|
|
|
run->run.glyphIndices = run->glyphs;
|
|
run->descr.clusterMap = run->clustermap;
|
|
|
|
run->advances = heap_alloc(run->glyphcount * sizeof(*run->advances));
|
|
run->offsets = heap_alloc(run->glyphcount * sizeof(*run->offsets));
|
|
if (!run->advances || !run->offsets)
|
|
return E_OUTOFMEMORY;
|
|
|
|
/* Get advances and offsets. */
|
|
if (is_layout_gdi_compatible(layout))
|
|
hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
|
|
text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
|
|
run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform,
|
|
layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1,
|
|
&run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
|
|
else
|
|
hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props,
|
|
run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, run->run.fontFace,
|
|
run->run.fontEmSize, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName,
|
|
NULL, NULL, 0, run->advances, run->offsets);
|
|
|
|
heap_free(text_props);
|
|
heap_free(glyph_props);
|
|
if (FAILED(hr)) {
|
|
memset(run->advances, 0, run->glyphcount * sizeof(*run->advances));
|
|
memset(run->offsets, 0, run->glyphcount * sizeof(*run->offsets));
|
|
WARN("%s: failed to get glyph placement info, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
|
}
|
|
|
|
run->run.glyphAdvances = run->advances;
|
|
run->run.glyphOffsets = run->offsets;
|
|
|
|
/* Special treatment for runs that don't produce visual output, shaping code adds normal glyphs for them,
|
|
with valid cluster map and potentially with non-zero advances; layout code exposes those as zero
|
|
width clusters. */
|
|
if (run->sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL)
|
|
run->run.glyphCount = 0;
|
|
else
|
|
run->run.glyphCount = run->glyphcount;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_run *r;
|
|
UINT32 cluster = 0;
|
|
HRESULT hr;
|
|
|
|
free_layout_eruns(layout);
|
|
free_layout_runs(layout);
|
|
|
|
/* Cluster data arrays are allocated once, assuming one text position per cluster. */
|
|
if (!layout->clustermetrics && layout->len) {
|
|
layout->clustermetrics = heap_alloc(layout->len*sizeof(*layout->clustermetrics));
|
|
layout->clusters = heap_alloc(layout->len*sizeof(*layout->clusters));
|
|
if (!layout->clustermetrics || !layout->clusters) {
|
|
heap_free(layout->clustermetrics);
|
|
heap_free(layout->clusters);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
layout->cluster_count = 0;
|
|
|
|
if (FAILED(hr = layout_itemize(layout))) {
|
|
WARN("Itemization failed, hr %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = layout_resolve_fonts(layout))) {
|
|
WARN("Failed to resolve layout fonts, hr %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
/* fill run info */
|
|
LIST_FOR_EACH_ENTRY(r, &layout->runs, struct layout_run, entry) {
|
|
struct regular_layout_run *run = &r->u.regular;
|
|
DWRITE_FONT_METRICS fontmetrics = { 0 };
|
|
|
|
/* we need to do very little in case of inline objects */
|
|
if (r->kind == LAYOUT_RUN_INLINE) {
|
|
DWRITE_CLUSTER_METRICS *metrics = &layout->clustermetrics[cluster];
|
|
struct layout_cluster *c = &layout->clusters[cluster];
|
|
DWRITE_INLINE_OBJECT_METRICS inlinemetrics;
|
|
|
|
metrics->width = 0.0f;
|
|
metrics->length = r->u.object.length;
|
|
metrics->canWrapLineAfter = 0;
|
|
metrics->isWhitespace = 0;
|
|
metrics->isNewline = 0;
|
|
metrics->isSoftHyphen = 0;
|
|
metrics->isRightToLeft = 0;
|
|
metrics->padding = 0;
|
|
c->run = r;
|
|
c->position = 0; /* there's always one cluster per inline object, so 0 is valid value */
|
|
cluster++;
|
|
|
|
/* it's not fatal if GetMetrics() fails, all returned metrics are ignored */
|
|
hr = IDWriteInlineObject_GetMetrics(r->u.object.object, &inlinemetrics);
|
|
if (FAILED(hr)) {
|
|
memset(&inlinemetrics, 0, sizeof(inlinemetrics));
|
|
hr = S_OK;
|
|
}
|
|
metrics->width = inlinemetrics.width;
|
|
r->baseline = inlinemetrics.baseline;
|
|
r->height = inlinemetrics.height;
|
|
|
|
/* FIXME: use resolved breakpoints in this case too */
|
|
|
|
continue;
|
|
}
|
|
|
|
if (FAILED(hr = layout_shape_run(layout, run)))
|
|
WARN("%s: shaping failed, hr %#x.\n", debugstr_rundescr(&run->descr), hr);
|
|
|
|
/* baseline derived from font metrics */
|
|
layout_get_font_metrics(layout, run->run.fontFace, run->run.fontEmSize, &fontmetrics);
|
|
layout_get_font_height(run->run.fontEmSize, &fontmetrics, &r->baseline, &r->height);
|
|
|
|
layout_set_cluster_metrics(layout, r, &cluster);
|
|
}
|
|
|
|
if (hr == S_OK) {
|
|
layout->cluster_count = cluster;
|
|
if (cluster)
|
|
layout->clustermetrics[cluster-1].canWrapLineAfter = 1;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT layout_compute(struct dwrite_textlayout *layout)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!(layout->recompute & RECOMPUTE_CLUSTERS))
|
|
return S_OK;
|
|
|
|
/* nominal breakpoints are evaluated only once, because string never changes */
|
|
if (!layout->nominal_breakpoints) {
|
|
IDWriteTextAnalyzer *analyzer;
|
|
|
|
layout->nominal_breakpoints = heap_alloc(layout->len * sizeof(*layout->nominal_breakpoints));
|
|
if (!layout->nominal_breakpoints)
|
|
return E_OUTOFMEMORY;
|
|
|
|
analyzer = get_text_analyzer();
|
|
|
|
if (FAILED(hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer,
|
|
(IDWriteTextAnalysisSource *)&layout->IDWriteTextAnalysisSource1_iface,
|
|
0, layout->len, (IDWriteTextAnalysisSink *)&layout->IDWriteTextAnalysisSink1_iface)))
|
|
WARN("Line breakpoints analysis failed, hr %#x.\n", hr);
|
|
}
|
|
|
|
heap_free(layout->actual_breakpoints);
|
|
layout->actual_breakpoints = NULL;
|
|
|
|
hr = layout_compute_runs(layout);
|
|
|
|
if (TRACE_ON(dwrite)) {
|
|
struct layout_run *cur;
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, &layout->runs, struct layout_run, entry) {
|
|
if (cur->kind == LAYOUT_RUN_INLINE)
|
|
TRACE("run inline object %p, len %u\n", cur->u.object.object, cur->u.object.length);
|
|
else
|
|
TRACE("run [%u,%u], len %u, bidilevel %u\n", cur->u.regular.descr.textPosition, cur->u.regular.descr.textPosition +
|
|
cur->u.regular.descr.stringLength-1, cur->u.regular.descr.stringLength, cur->u.regular.run.bidiLevel);
|
|
}
|
|
}
|
|
|
|
layout->recompute &= ~RECOMPUTE_CLUSTERS;
|
|
return hr;
|
|
}
|
|
|
|
static inline FLOAT get_cluster_range_width(struct dwrite_textlayout *layout, UINT32 start, UINT32 end)
|
|
{
|
|
FLOAT width = 0.0f;
|
|
for (; start < end; start++)
|
|
width += layout->clustermetrics[start].width;
|
|
return width;
|
|
}
|
|
|
|
static struct layout_range_header *get_layout_range_header_by_pos(struct list *ranges, UINT32 pos)
|
|
{
|
|
struct layout_range_header *cur;
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
|
|
DWRITE_TEXT_RANGE *r = &cur->range;
|
|
if (r->startPosition <= pos && pos < r->startPosition + r->length)
|
|
return cur;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline IUnknown *layout_get_effect_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
struct layout_range_header *h = get_layout_range_header_by_pos(&layout->effects, pos);
|
|
return ((struct layout_range_iface*)h)->iface;
|
|
}
|
|
|
|
static inline BOOL layout_is_erun_rtl(const struct layout_effective_run *erun)
|
|
{
|
|
return erun->run->u.regular.run.bidiLevel & 1;
|
|
}
|
|
|
|
/* A set of parameters that additionally splits resulting runs. It happens after shaping and all text processing,
|
|
no glyph changes are possible. It's understandable for drawing effects, because DrawGlyphRun() reports them as
|
|
one of the arguments, but it also happens for decorations, so every effective run has uniform
|
|
underline/strikethough/effect tuple. */
|
|
struct layout_final_splitting_params {
|
|
BOOL strikethrough;
|
|
BOOL underline;
|
|
IUnknown *effect;
|
|
};
|
|
|
|
static inline BOOL layout_get_strikethrough_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
struct layout_range_header *h = get_layout_range_header_by_pos(&layout->strike_ranges, pos);
|
|
return ((struct layout_range_bool*)h)->value;
|
|
}
|
|
|
|
static inline BOOL layout_get_underline_from_pos(struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
struct layout_range_header *h = get_layout_range_header_by_pos(&layout->underline_ranges, pos);
|
|
return ((struct layout_range_bool*)h)->value;
|
|
}
|
|
|
|
static void layout_splitting_params_from_pos(struct dwrite_textlayout *layout, UINT32 pos,
|
|
struct layout_final_splitting_params *params)
|
|
{
|
|
params->strikethrough = layout_get_strikethrough_from_pos(layout, pos);
|
|
params->underline = layout_get_underline_from_pos(layout, pos);
|
|
params->effect = layout_get_effect_from_pos(layout, pos);
|
|
}
|
|
|
|
static BOOL is_same_splitting_params(const struct layout_final_splitting_params *left,
|
|
const struct layout_final_splitting_params *right)
|
|
{
|
|
return left->strikethrough == right->strikethrough &&
|
|
left->underline == right->underline &&
|
|
left->effect == right->effect;
|
|
}
|
|
|
|
static void layout_get_erun_font_metrics(struct dwrite_textlayout *layout, struct layout_effective_run *erun,
|
|
DWRITE_FONT_METRICS *metrics)
|
|
{
|
|
memset(metrics, 0, sizeof(*metrics));
|
|
if (is_layout_gdi_compatible(layout)) {
|
|
HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
|
|
erun->run->u.regular.run.fontFace,
|
|
erun->run->u.regular.run.fontEmSize,
|
|
layout->ppdip,
|
|
&layout->transform,
|
|
metrics);
|
|
if (FAILED(hr))
|
|
WARN("failed to get font metrics, 0x%08x\n", hr);
|
|
}
|
|
else
|
|
IDWriteFontFace_GetMetrics(erun->run->u.regular.run.fontFace, metrics);
|
|
}
|
|
|
|
/* Effective run is built from consecutive clusters of a single nominal run, 'first_cluster' is 0 based cluster index,
|
|
'cluster_count' indicates how many clusters to add, including first one. */
|
|
static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const struct layout_run *r, UINT32 first_cluster,
|
|
UINT32 cluster_count, UINT32 line, FLOAT origin_x, struct layout_final_splitting_params *params)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
UINT32 i, start, length, last_cluster;
|
|
struct layout_effective_run *run;
|
|
|
|
if (r->kind == LAYOUT_RUN_INLINE) {
|
|
struct layout_effective_inline *inlineobject;
|
|
|
|
inlineobject = heap_alloc(sizeof(*inlineobject));
|
|
if (!inlineobject)
|
|
return E_OUTOFMEMORY;
|
|
|
|
inlineobject->object = r->u.object.object;
|
|
inlineobject->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
|
|
inlineobject->origin.x = is_rtl ? origin_x - inlineobject->width : origin_x;
|
|
inlineobject->origin.y = 0.0f; /* set after line is built */
|
|
inlineobject->align_dx = 0.0f;
|
|
inlineobject->baseline = r->baseline;
|
|
|
|
/* It's not clear how these two are set, possibly directionality
|
|
is derived from surrounding text (replaced text could have
|
|
different ranges which differ in reading direction). */
|
|
inlineobject->is_sideways = FALSE;
|
|
inlineobject->is_rtl = FALSE;
|
|
inlineobject->line = line;
|
|
|
|
/* effect assigned from start position and on is used for inline objects */
|
|
inlineobject->effect = layout_get_effect_from_pos(layout, layout->clusters[first_cluster].position +
|
|
layout->clusters[first_cluster].run->start_position);
|
|
|
|
list_add_tail(&layout->inlineobjects, &inlineobject->entry);
|
|
return S_OK;
|
|
}
|
|
|
|
run = heap_alloc(sizeof(*run));
|
|
if (!run)
|
|
return E_OUTOFMEMORY;
|
|
|
|
/* No need to iterate for that, use simple fact that:
|
|
<last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */
|
|
last_cluster = first_cluster + cluster_count - 1;
|
|
length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
|
|
layout->clustermetrics[last_cluster].length;
|
|
|
|
run->clustermap = heap_alloc(sizeof(UINT16)*length);
|
|
if (!run->clustermap) {
|
|
heap_free(run);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
run->run = r;
|
|
run->start = start = layout->clusters[first_cluster].position;
|
|
run->length = length;
|
|
run->width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
|
|
memset(&run->bbox, 0, sizeof(run->bbox));
|
|
|
|
/* Check if run direction matches paragraph direction, if it doesn't adjust by
|
|
run width */
|
|
if (layout_is_erun_rtl(run) ^ is_rtl)
|
|
run->origin.x = is_rtl ? origin_x - run->width : origin_x + run->width;
|
|
else
|
|
run->origin.x = origin_x;
|
|
|
|
run->origin.y = 0.0f; /* set after line is built */
|
|
run->align_dx = 0.0f;
|
|
run->line = line;
|
|
|
|
if (r->u.regular.run.glyphCount) {
|
|
/* Trim leading and trailing clusters. */
|
|
run->glyphcount = r->u.regular.run.glyphCount - r->u.regular.clustermap[start];
|
|
if (start + length < r->u.regular.descr.stringLength)
|
|
run->glyphcount -= r->u.regular.run.glyphCount - r->u.regular.clustermap[start + length];
|
|
}
|
|
else
|
|
run->glyphcount = 0;
|
|
|
|
/* cluster map needs to be shifted */
|
|
for (i = 0; i < length; i++)
|
|
run->clustermap[i] = r->u.regular.clustermap[start + i] - r->u.regular.clustermap[start];
|
|
|
|
run->effect = params->effect;
|
|
run->underlined = params->underline;
|
|
list_add_tail(&layout->eruns, &run->entry);
|
|
|
|
/* Strikethrough style is guaranteed to be consistent within effective run,
|
|
its width equals to run width, thickness and offset are derived from
|
|
font metrics, rest of the values are from layout or run itself */
|
|
if (params->strikethrough) {
|
|
struct layout_strikethrough *s;
|
|
DWRITE_FONT_METRICS metrics;
|
|
|
|
s = heap_alloc(sizeof(*s));
|
|
if (!s)
|
|
return E_OUTOFMEMORY;
|
|
|
|
layout_get_erun_font_metrics(layout, run, &metrics);
|
|
s->s.width = get_cluster_range_width(layout, first_cluster, first_cluster + cluster_count);
|
|
s->s.thickness = SCALE_FONT_METRIC(metrics.strikethroughThickness, r->u.regular.run.fontEmSize, &metrics);
|
|
/* Negative offset moves it above baseline as Y coordinate grows downward. */
|
|
s->s.offset = -SCALE_FONT_METRIC(metrics.strikethroughPosition, r->u.regular.run.fontEmSize, &metrics);
|
|
s->s.readingDirection = layout->format.readingdir;
|
|
s->s.flowDirection = layout->format.flow;
|
|
s->s.localeName = r->u.regular.descr.localeName;
|
|
s->s.measuringMode = layout->measuringmode;
|
|
s->run = run;
|
|
|
|
list_add_tail(&layout->strikethrough, &s->entry);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static void layout_apply_line_spacing(struct dwrite_textlayout *layout, UINT32 line)
|
|
{
|
|
switch (layout->format.spacing.method)
|
|
{
|
|
case DWRITE_LINE_SPACING_METHOD_DEFAULT:
|
|
layout->linemetrics[line].height = layout->lines[line].height;
|
|
layout->linemetrics[line].baseline = layout->lines[line].baseline;
|
|
break;
|
|
case DWRITE_LINE_SPACING_METHOD_UNIFORM:
|
|
layout->linemetrics[line].height = layout->format.spacing.height;
|
|
layout->linemetrics[line].baseline = layout->format.spacing.baseline;
|
|
break;
|
|
case DWRITE_LINE_SPACING_METHOD_PROPORTIONAL:
|
|
layout->linemetrics[line].height = layout->lines[line].height * layout->format.spacing.height;
|
|
layout->linemetrics[line].baseline = layout->lines[line].baseline * layout->format.spacing.baseline;
|
|
break;
|
|
default:
|
|
ERR("Unknown spacing method %u\n", layout->format.spacing.method);
|
|
}
|
|
}
|
|
|
|
static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics)
|
|
{
|
|
UINT32 i = layout->metrics.lineCount;
|
|
|
|
if (!layout->line_alloc) {
|
|
layout->line_alloc = 5;
|
|
layout->linemetrics = heap_alloc(layout->line_alloc * sizeof(*layout->linemetrics));
|
|
layout->lines = heap_alloc(layout->line_alloc * sizeof(*layout->lines));
|
|
if (!layout->linemetrics || !layout->lines) {
|
|
heap_free(layout->linemetrics);
|
|
heap_free(layout->lines);
|
|
layout->linemetrics = NULL;
|
|
layout->lines = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (layout->metrics.lineCount == layout->line_alloc) {
|
|
DWRITE_LINE_METRICS1 *metrics;
|
|
struct layout_line *lines;
|
|
|
|
if ((metrics = heap_realloc(layout->linemetrics, layout->line_alloc * 2 * sizeof(*layout->linemetrics))))
|
|
layout->linemetrics = metrics;
|
|
if ((lines = heap_realloc(layout->lines, layout->line_alloc * 2 * sizeof(*layout->lines))))
|
|
layout->lines = lines;
|
|
|
|
if (!metrics || !lines)
|
|
return E_OUTOFMEMORY;
|
|
|
|
layout->line_alloc *= 2;
|
|
}
|
|
|
|
layout->linemetrics[i] = *metrics;
|
|
layout->lines[i].height = metrics->height;
|
|
layout->lines[i].baseline = metrics->baseline;
|
|
|
|
if (layout->format.spacing.method != DWRITE_LINE_SPACING_METHOD_DEFAULT)
|
|
layout_apply_line_spacing(layout, i);
|
|
|
|
layout->metrics.lineCount++;
|
|
return S_OK;
|
|
}
|
|
|
|
static inline struct layout_effective_run *layout_get_next_erun(struct dwrite_textlayout *layout,
|
|
const struct layout_effective_run *cur)
|
|
{
|
|
struct list *e;
|
|
|
|
if (!cur)
|
|
e = list_head(&layout->eruns);
|
|
else
|
|
e = list_next(&layout->eruns, &cur->entry);
|
|
if (!e)
|
|
return NULL;
|
|
return LIST_ENTRY(e, struct layout_effective_run, entry);
|
|
}
|
|
|
|
static inline struct layout_effective_run *layout_get_prev_erun(struct dwrite_textlayout *layout,
|
|
const struct layout_effective_run *cur)
|
|
{
|
|
struct list *e;
|
|
|
|
if (!cur)
|
|
e = list_tail(&layout->eruns);
|
|
else
|
|
e = list_prev(&layout->eruns, &cur->entry);
|
|
if (!e)
|
|
return NULL;
|
|
return LIST_ENTRY(e, struct layout_effective_run, entry);
|
|
}
|
|
|
|
static inline struct layout_effective_inline *layout_get_next_inline_run(struct dwrite_textlayout *layout,
|
|
const struct layout_effective_inline *cur)
|
|
{
|
|
struct list *e;
|
|
|
|
if (!cur)
|
|
e = list_head(&layout->inlineobjects);
|
|
else
|
|
e = list_next(&layout->inlineobjects, &cur->entry);
|
|
if (!e)
|
|
return NULL;
|
|
return LIST_ENTRY(e, struct layout_effective_inline, entry);
|
|
}
|
|
|
|
static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
|
|
struct layout_effective_run *erun, struct layout_effective_inline *inrun, UINT32 line)
|
|
{
|
|
FLOAT width = 0.0f;
|
|
|
|
while (erun && erun->line == line) {
|
|
width += erun->width;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
if (!erun)
|
|
break;
|
|
}
|
|
|
|
while (inrun && inrun->line == line) {
|
|
width += inrun->width;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
if (!inrun)
|
|
break;
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det)
|
|
{
|
|
*det = m->m11 * m->m22 - m->m12 * m->m21;
|
|
/* on certain conditions we can skip transform */
|
|
return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f);
|
|
}
|
|
|
|
static inline void layout_apply_snapping(D2D1_POINT_2F *vec, BOOL skiptransform, FLOAT ppdip,
|
|
const DWRITE_MATRIX *m, FLOAT det)
|
|
{
|
|
if (!skiptransform) {
|
|
D2D1_POINT_2F vec2;
|
|
|
|
/* apply transform */
|
|
vec->x *= ppdip;
|
|
vec->y *= ppdip;
|
|
|
|
vec2.x = m->m11 * vec->x + m->m21 * vec->y + m->dx;
|
|
vec2.y = m->m12 * vec->x + m->m22 * vec->y + m->dy;
|
|
|
|
/* snap */
|
|
vec2.x = floorf(vec2.x + 0.5f);
|
|
vec2.y = floorf(vec2.y + 0.5f);
|
|
|
|
/* apply inverted transform, we don't care about X component at this point */
|
|
vec->x = (m->m22 * vec2.x - m->m21 * vec2.y + m->m21 * m->dy - m->m22 * m->dx) / det;
|
|
vec->x /= ppdip;
|
|
|
|
vec->y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
|
|
vec->y /= ppdip;
|
|
}
|
|
else {
|
|
vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip;
|
|
vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip;
|
|
}
|
|
}
|
|
|
|
static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
struct layout_effective_inline *inrun;
|
|
struct layout_effective_run *erun;
|
|
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
inrun = layout_get_next_inline_run(layout, NULL);
|
|
|
|
while (erun) {
|
|
erun->align_dx = 0.0f;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
}
|
|
|
|
while (inrun) {
|
|
inrun->align_dx = 0.0f;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
}
|
|
|
|
layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
|
|
}
|
|
|
|
static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
struct layout_effective_inline *inrun;
|
|
struct layout_effective_run *erun;
|
|
UINT32 line;
|
|
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
inrun = layout_get_next_inline_run(layout, NULL);
|
|
|
|
for (line = 0; line < layout->metrics.lineCount; line++) {
|
|
FLOAT width = layout_get_line_width(layout, erun, inrun, line);
|
|
FLOAT shift = layout->metrics.layoutWidth - width;
|
|
|
|
if (is_rtl)
|
|
shift *= -1.0f;
|
|
|
|
while (erun && erun->line == line) {
|
|
erun->align_dx = shift;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
}
|
|
|
|
while (inrun && inrun->line == line) {
|
|
inrun->align_dx = shift;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
}
|
|
}
|
|
|
|
layout->metrics.left = is_rtl ? 0.0f : layout->metrics.layoutWidth - layout->metrics.width;
|
|
}
|
|
|
|
static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform,
|
|
FLOAT width, FLOAT det)
|
|
{
|
|
if (is_layout_gdi_compatible(layout)) {
|
|
D2D1_POINT_2F vec = { layout->metrics.layoutWidth - width, 0.0f};
|
|
layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det);
|
|
return floorf(vec.x / 2.0f);
|
|
}
|
|
else
|
|
return (layout->metrics.layoutWidth - width) / 2.0f;
|
|
}
|
|
|
|
static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
struct layout_effective_inline *inrun;
|
|
struct layout_effective_run *erun;
|
|
BOOL skiptransform;
|
|
UINT32 line;
|
|
FLOAT det;
|
|
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
inrun = layout_get_next_inline_run(layout, NULL);
|
|
|
|
skiptransform = should_skip_transform(&layout->transform, &det);
|
|
|
|
for (line = 0; line < layout->metrics.lineCount; line++) {
|
|
FLOAT width = layout_get_line_width(layout, erun, inrun, line);
|
|
FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det);
|
|
|
|
if (is_rtl)
|
|
shift *= -1.0f;
|
|
|
|
while (erun && erun->line == line) {
|
|
erun->align_dx = shift;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
}
|
|
|
|
while (inrun && inrun->line == line) {
|
|
inrun->align_dx = shift;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
}
|
|
}
|
|
|
|
layout->metrics.left = (layout->metrics.layoutWidth - layout->metrics.width) / 2.0f;
|
|
}
|
|
|
|
static void layout_apply_text_alignment(struct dwrite_textlayout *layout)
|
|
{
|
|
switch (layout->format.textalignment)
|
|
{
|
|
case DWRITE_TEXT_ALIGNMENT_LEADING:
|
|
layout_apply_leading_alignment(layout);
|
|
break;
|
|
case DWRITE_TEXT_ALIGNMENT_TRAILING:
|
|
layout_apply_trailing_alignment(layout);
|
|
break;
|
|
case DWRITE_TEXT_ALIGNMENT_CENTER:
|
|
layout_apply_centered_alignment(layout);
|
|
break;
|
|
case DWRITE_TEXT_ALIGNMENT_JUSTIFIED:
|
|
FIXME("alignment %d not implemented\n", layout->format.textalignment);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
static void layout_apply_par_alignment(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_effective_inline *inrun;
|
|
struct layout_effective_run *erun;
|
|
FLOAT origin_y = 0.0f;
|
|
UINT32 line;
|
|
|
|
/* alignment mode defines origin, after that all run origins are updated
|
|
the same way */
|
|
|
|
switch (layout->format.paralign)
|
|
{
|
|
case DWRITE_PARAGRAPH_ALIGNMENT_NEAR:
|
|
origin_y = 0.0f;
|
|
break;
|
|
case DWRITE_PARAGRAPH_ALIGNMENT_FAR:
|
|
origin_y = layout->metrics.layoutHeight - layout->metrics.height;
|
|
break;
|
|
case DWRITE_PARAGRAPH_ALIGNMENT_CENTER:
|
|
origin_y = (layout->metrics.layoutHeight - layout->metrics.height) / 2.0f;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
layout->metrics.top = origin_y;
|
|
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
inrun = layout_get_next_inline_run(layout, NULL);
|
|
for (line = 0; line < layout->metrics.lineCount; line++) {
|
|
FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
|
|
|
|
while (erun && erun->line == line) {
|
|
erun->origin.y = pos_y;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
}
|
|
|
|
while (inrun && inrun->line == line) {
|
|
inrun->origin.y = pos_y - inrun->baseline;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
}
|
|
|
|
origin_y += layout->linemetrics[line].height;
|
|
}
|
|
}
|
|
|
|
struct layout_underline_splitting_params {
|
|
const WCHAR *locale; /* points to range data, no additional allocation */
|
|
IUnknown *effect; /* does not hold another reference */
|
|
};
|
|
|
|
static void init_u_splitting_params_from_erun(struct layout_effective_run *erun,
|
|
struct layout_underline_splitting_params *params)
|
|
{
|
|
params->locale = erun->run->u.regular.descr.localeName;
|
|
params->effect = erun->effect;
|
|
}
|
|
|
|
static BOOL is_same_u_splitting(struct layout_underline_splitting_params *left,
|
|
struct layout_underline_splitting_params *right)
|
|
{
|
|
return left->effect == right->effect && !strcmpiW(left->locale, right->locale);
|
|
}
|
|
|
|
static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct layout_effective_run *first,
|
|
struct layout_effective_run *last)
|
|
{
|
|
FLOAT thickness, offset, runheight;
|
|
struct layout_effective_run *cur;
|
|
DWRITE_FONT_METRICS metrics;
|
|
|
|
if (first == layout_get_prev_erun(layout, last)) {
|
|
layout_get_erun_font_metrics(layout, first, &metrics);
|
|
thickness = SCALE_FONT_METRIC(metrics.underlineThickness, first->run->u.regular.run.fontEmSize, &metrics);
|
|
offset = SCALE_FONT_METRIC(metrics.underlinePosition, first->run->u.regular.run.fontEmSize, &metrics);
|
|
runheight = SCALE_FONT_METRIC(metrics.capHeight, first->run->u.regular.run.fontEmSize, &metrics);
|
|
}
|
|
else {
|
|
FLOAT width = 0.0f;
|
|
|
|
/* Single underline is added for consecutive underlined runs. In this case underline parameters are
|
|
calculated as weighted average, where run width acts as a weight. */
|
|
thickness = offset = runheight = 0.0f;
|
|
cur = first;
|
|
do {
|
|
layout_get_erun_font_metrics(layout, cur, &metrics);
|
|
|
|
thickness += SCALE_FONT_METRIC(metrics.underlineThickness, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
|
|
offset += SCALE_FONT_METRIC(metrics.underlinePosition, cur->run->u.regular.run.fontEmSize, &metrics) * cur->width;
|
|
runheight = max(SCALE_FONT_METRIC(metrics.capHeight, cur->run->u.regular.run.fontEmSize, &metrics), runheight);
|
|
width += cur->width;
|
|
|
|
cur = layout_get_next_erun(layout, cur);
|
|
} while (cur != last);
|
|
|
|
thickness /= width;
|
|
offset /= width;
|
|
}
|
|
|
|
cur = first;
|
|
do {
|
|
struct layout_underline_splitting_params params, prev_params;
|
|
struct layout_effective_run *next, *w;
|
|
struct layout_underline *u;
|
|
|
|
init_u_splitting_params_from_erun(cur, &prev_params);
|
|
while ((next = layout_get_next_erun(layout, cur)) != last) {
|
|
init_u_splitting_params_from_erun(next, ¶ms);
|
|
if (!is_same_u_splitting(&prev_params, ¶ms))
|
|
break;
|
|
cur = next;
|
|
}
|
|
|
|
u = heap_alloc(sizeof(*u));
|
|
if (!u)
|
|
return E_OUTOFMEMORY;
|
|
|
|
w = cur;
|
|
u->u.width = 0.0f;
|
|
while (w != next) {
|
|
u->u.width += w->width;
|
|
w = layout_get_next_erun(layout, w);
|
|
}
|
|
|
|
u->u.thickness = thickness;
|
|
/* Font metrics convention is to have it negative when below baseline, for rendering
|
|
however Y grows from baseline down for horizontal baseline. */
|
|
u->u.offset = -offset;
|
|
u->u.runHeight = runheight;
|
|
u->u.readingDirection = is_run_rtl(cur) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT :
|
|
DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
u->u.flowDirection = layout->format.flow;
|
|
u->u.localeName = cur->run->u.regular.descr.localeName;
|
|
u->u.measuringMode = layout->measuringmode;
|
|
u->run = cur;
|
|
list_add_tail(&layout->underlines, &u->entry);
|
|
|
|
cur = next;
|
|
} while (cur != last);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/* Adds zero width line, metrics are derived from font at specified text position. */
|
|
static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
DWRITE_LINE_METRICS1 metrics = { 0 };
|
|
DWRITE_FONT_METRICS fontmetrics;
|
|
struct layout_range *range;
|
|
IDWriteFontFace *fontface;
|
|
IDWriteFont *font;
|
|
HRESULT hr;
|
|
|
|
range = get_layout_range_by_pos(layout, pos);
|
|
hr = create_matching_font(range->collection,
|
|
range->fontfamily,
|
|
range->weight,
|
|
range->style,
|
|
range->stretch,
|
|
&font);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = IDWriteFont_CreateFontFace(font, &fontface);
|
|
IDWriteFont_Release(font);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
layout_get_font_metrics(layout, fontface, range->fontsize, &fontmetrics);
|
|
layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
|
|
IDWriteFontFace_Release(fontface);
|
|
|
|
return layout_set_line_metrics(layout, &metrics);
|
|
}
|
|
|
|
static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
|
|
UINT32 *textpos)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
struct layout_final_splitting_params params, prev_params;
|
|
DWRITE_INLINE_OBJECT_METRICS sign_metrics = { 0 };
|
|
UINT32 line = layout->metrics.lineCount, i;
|
|
DWRITE_LINE_METRICS1 metrics = { 0 };
|
|
UINT32 index, start, pos = *textpos;
|
|
FLOAT descent, trailingspacewidth;
|
|
BOOL append_trimming_run = FALSE;
|
|
const struct layout_run *run;
|
|
FLOAT width, origin_x;
|
|
HRESULT hr;
|
|
|
|
/* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
|
|
for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
|
|
DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
|
|
struct layout_cluster *lc = &layout->clusters[index];
|
|
WCHAR ch;
|
|
|
|
/* This also filters out clusters added from inline objects, those are never
|
|
treated as a white space. */
|
|
if (!cluster->isWhitespace)
|
|
break;
|
|
|
|
/* Every isNewline cluster is also isWhitespace, but not every
|
|
newline character cluster has isNewline set, so go back to original string. */
|
|
ch = lc->run->u.regular.descr.string[lc->position];
|
|
if (cluster->length == 1 && lb_is_newline_char(ch))
|
|
metrics.newlineLength += cluster->length;
|
|
|
|
metrics.trailingWhitespaceLength += cluster->length;
|
|
trailingspacewidth += cluster->width;
|
|
}
|
|
|
|
/* Line metrics length includes trailing whitespace length too */
|
|
for (i = first_cluster; i <= last_cluster; i++)
|
|
metrics.length += layout->clustermetrics[i].length;
|
|
|
|
/* Ignore trailing whitespaces */
|
|
while (last_cluster > first_cluster) {
|
|
if (!layout->clustermetrics[last_cluster].isWhitespace)
|
|
break;
|
|
|
|
last_cluster--;
|
|
}
|
|
|
|
/* Does not include trailing space width */
|
|
width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
|
|
|
|
/* Append trimming run if necessary */
|
|
if (width > layout->metrics.layoutWidth && layout->format.trimmingsign != NULL &&
|
|
layout->format.trimming.granularity != DWRITE_TRIMMING_GRANULARITY_NONE) {
|
|
FLOAT trimmed_width = width;
|
|
|
|
hr = IDWriteInlineObject_GetMetrics(layout->format.trimmingsign, &sign_metrics);
|
|
if (SUCCEEDED(hr)) {
|
|
while (last_cluster > first_cluster) {
|
|
if (trimmed_width + sign_metrics.width <= layout->metrics.layoutWidth)
|
|
break;
|
|
if (layout->format.trimming.granularity == DWRITE_TRIMMING_GRANULARITY_CHARACTER)
|
|
trimmed_width -= layout->clustermetrics[last_cluster--].width;
|
|
else {
|
|
while (last_cluster > first_cluster) {
|
|
trimmed_width -= layout->clustermetrics[last_cluster].width;
|
|
if (layout->clustermetrics[last_cluster--].canWrapLineAfter)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
append_trimming_run = TRUE;
|
|
}
|
|
else
|
|
WARN("Failed to get trimming sign metrics, lines won't be trimmed, hr %#x.\n", hr);
|
|
|
|
width = trimmed_width + sign_metrics.width;
|
|
}
|
|
|
|
layout_splitting_params_from_pos(layout, pos, ¶ms);
|
|
prev_params = params;
|
|
run = layout->clusters[first_cluster].run;
|
|
|
|
/* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
|
|
origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
|
|
for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
|
|
layout_splitting_params_from_pos(layout, pos, ¶ms);
|
|
|
|
if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, ¶ms)) {
|
|
hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
|
|
get_cluster_range_width(layout, start, i);
|
|
run = layout->clusters[i].run;
|
|
start = i;
|
|
}
|
|
|
|
prev_params = params;
|
|
pos += layout->clustermetrics[i].length;
|
|
}
|
|
|
|
/* Final run from what's left from cluster range */
|
|
hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
if (append_trimming_run) {
|
|
struct layout_effective_inline *trimming_sign;
|
|
|
|
trimming_sign = heap_alloc(sizeof(*trimming_sign));
|
|
if (!trimming_sign)
|
|
return;
|
|
|
|
trimming_sign->object = layout->format.trimmingsign;
|
|
trimming_sign->width = sign_metrics.width;
|
|
origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) : get_cluster_range_width(layout, start, i);
|
|
trimming_sign->origin.x = is_rtl ? origin_x - trimming_sign->width : origin_x;
|
|
trimming_sign->origin.y = 0.0f; /* set after line is built */
|
|
trimming_sign->align_dx = 0.0f;
|
|
trimming_sign->baseline = sign_metrics.baseline;
|
|
|
|
trimming_sign->is_sideways = FALSE;
|
|
trimming_sign->is_rtl = FALSE;
|
|
trimming_sign->line = line;
|
|
|
|
trimming_sign->effect = layout_get_effect_from_pos(layout, layout->clusters[i].position +
|
|
layout->clusters[i].run->start_position);
|
|
|
|
list_add_tail(&layout->inlineobjects, &trimming_sign->entry);
|
|
}
|
|
|
|
/* Look for max baseline and descent for this line */
|
|
for (index = first_cluster, metrics.baseline = 0.0f, descent = 0.0f; index <= last_cluster; index++) {
|
|
const struct layout_run *cur = layout->clusters[index].run;
|
|
FLOAT cur_descent = cur->height - cur->baseline;
|
|
|
|
if (cur->baseline > metrics.baseline)
|
|
metrics.baseline = cur->baseline;
|
|
if (cur_descent > descent)
|
|
descent = cur_descent;
|
|
}
|
|
|
|
layout->metrics.width = max(width, layout->metrics.width);
|
|
layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
|
|
layout->metrics.widthIncludingTrailingWhitespace);
|
|
|
|
metrics.height = descent + metrics.baseline;
|
|
metrics.isTrimmed = append_trimming_run || width > layout->metrics.layoutWidth;
|
|
layout_set_line_metrics(layout, &metrics);
|
|
|
|
*textpos += metrics.length;
|
|
}
|
|
|
|
static void layout_set_line_positions(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_effective_inline *inrun;
|
|
struct layout_effective_run *erun;
|
|
FLOAT origin_y;
|
|
UINT32 line;
|
|
|
|
/* Now all line info is here, update effective runs positions in flow direction */
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
inrun = layout_get_next_inline_run(layout, NULL);
|
|
|
|
for (line = 0, origin_y = 0.0f; line < layout->metrics.lineCount; line++) {
|
|
FLOAT pos_y = origin_y + layout->linemetrics[line].baseline;
|
|
|
|
/* For all runs on this line */
|
|
while (erun && erun->line == line) {
|
|
erun->origin.y = pos_y;
|
|
erun = layout_get_next_erun(layout, erun);
|
|
}
|
|
|
|
/* Same for inline runs */
|
|
while (inrun && inrun->line == line) {
|
|
inrun->origin.y = pos_y - inrun->baseline;
|
|
inrun = layout_get_next_inline_run(layout, inrun);
|
|
}
|
|
|
|
origin_y += layout->linemetrics[line].height;
|
|
}
|
|
|
|
layout->metrics.height = origin_y;
|
|
|
|
/* Initial paragraph alignment is always near */
|
|
if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
|
|
layout_apply_par_alignment(layout);
|
|
}
|
|
|
|
static BOOL layout_can_wrap_after(const struct dwrite_textlayout *layout, UINT32 cluster)
|
|
{
|
|
if (layout->format.wrapping == DWRITE_WORD_WRAPPING_CHARACTER)
|
|
return TRUE;
|
|
|
|
return layout->clustermetrics[cluster].canWrapLineAfter;
|
|
}
|
|
|
|
static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
|
|
{
|
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
|
struct layout_effective_run *erun, *first_underlined;
|
|
UINT32 i, start, textpos, last_breaking_point;
|
|
DWRITE_LINE_METRICS1 metrics;
|
|
FLOAT width;
|
|
UINT32 line;
|
|
HRESULT hr;
|
|
|
|
if (!(layout->recompute & RECOMPUTE_LINES))
|
|
return S_OK;
|
|
|
|
free_layout_eruns(layout);
|
|
|
|
hr = layout_compute(layout);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
layout->metrics.lineCount = 0;
|
|
memset(&metrics, 0, sizeof(metrics));
|
|
|
|
layout->metrics.height = 0.0f;
|
|
layout->metrics.width = 0.0f;
|
|
layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
|
|
|
|
last_breaking_point = ~0u;
|
|
|
|
for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
|
|
BOOL overflow = FALSE;
|
|
|
|
while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
|
|
/* Check for overflow */
|
|
overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
|
|
(layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
|
|
if (overflow)
|
|
break;
|
|
|
|
if (layout_can_wrap_after(layout, i))
|
|
last_breaking_point = i;
|
|
width += layout->clustermetrics[i].width;
|
|
i++;
|
|
}
|
|
i = min(i, layout->cluster_count - 1);
|
|
|
|
/* Ignore if overflown on whitespace */
|
|
if (overflow && !(layout->clustermetrics[i].isWhitespace && layout_can_wrap_after(layout, i))) {
|
|
/* Use most recently found breaking point */
|
|
if (last_breaking_point != ~0u) {
|
|
i = last_breaking_point;
|
|
last_breaking_point = ~0u;
|
|
}
|
|
else {
|
|
/* Otherwise proceed forward to next newline or breaking point */
|
|
for (; i < layout->cluster_count; i++)
|
|
if (layout_can_wrap_after(layout, i) || layout->clustermetrics[i].isNewline)
|
|
break;
|
|
}
|
|
}
|
|
i = min(i, layout->cluster_count - 1);
|
|
|
|
layout_add_line(layout, start, i, &textpos);
|
|
start = i + 1;
|
|
width = 0.0f;
|
|
}
|
|
|
|
/* Add dummy line if:
|
|
- there's no text, metrics come from first range in this case;
|
|
- last ended with a mandatory break, metrics come from last text position.
|
|
*/
|
|
if (layout->len == 0)
|
|
hr = layout_set_dummy_line_metrics(layout, 0);
|
|
else if (layout->clustermetrics[layout->cluster_count - 1].isNewline)
|
|
hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
|
|
layout->metrics.top = 0.0f;
|
|
layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
|
|
|
|
/* Add explicit underlined runs */
|
|
erun = layout_get_next_erun(layout, NULL);
|
|
first_underlined = erun && erun->underlined ? erun : NULL;
|
|
for (line = 0; line < layout->metrics.lineCount; line++) {
|
|
while (erun && erun->line == line) {
|
|
erun = layout_get_next_erun(layout, erun);
|
|
|
|
if (first_underlined && (!erun || !erun->underlined)) {
|
|
layout_add_underline(layout, first_underlined, erun);
|
|
first_underlined = NULL;
|
|
}
|
|
else if (!first_underlined && erun && erun->underlined)
|
|
first_underlined = erun;
|
|
}
|
|
}
|
|
|
|
/* Position runs in flow direction */
|
|
layout_set_line_positions(layout);
|
|
|
|
/* Initial alignment is always leading */
|
|
if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
|
|
layout_apply_text_alignment(layout);
|
|
|
|
layout->recompute &= ~RECOMPUTE_LINES;
|
|
return hr;
|
|
}
|
|
|
|
static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
|
|
struct layout_range_attr_value *value)
|
|
{
|
|
struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
|
|
struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
|
|
struct layout_range_bool const *range_bool = (struct layout_range_bool*)h;
|
|
struct layout_range const *range = (struct layout_range*)h;
|
|
|
|
switch (attr) {
|
|
case LAYOUT_RANGE_ATTR_WEIGHT:
|
|
return range->weight == value->u.weight;
|
|
case LAYOUT_RANGE_ATTR_STYLE:
|
|
return range->style == value->u.style;
|
|
case LAYOUT_RANGE_ATTR_STRETCH:
|
|
return range->stretch == value->u.stretch;
|
|
case LAYOUT_RANGE_ATTR_FONTSIZE:
|
|
return range->fontsize == value->u.fontsize;
|
|
case LAYOUT_RANGE_ATTR_INLINE:
|
|
return range->object == value->u.object;
|
|
case LAYOUT_RANGE_ATTR_EFFECT:
|
|
return range_iface->iface == value->u.effect;
|
|
case LAYOUT_RANGE_ATTR_UNDERLINE:
|
|
return range_bool->value == value->u.underline;
|
|
case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
|
|
return range_bool->value == value->u.strikethrough;
|
|
case LAYOUT_RANGE_ATTR_PAIR_KERNING:
|
|
return range->pair_kerning == value->u.pair_kerning;
|
|
case LAYOUT_RANGE_ATTR_FONTCOLL:
|
|
return range->collection == value->u.collection;
|
|
case LAYOUT_RANGE_ATTR_LOCALE:
|
|
return strcmpiW(range->locale, value->u.locale) == 0;
|
|
case LAYOUT_RANGE_ATTR_FONTFAMILY:
|
|
return strcmpW(range->fontfamily, value->u.fontfamily) == 0;
|
|
case LAYOUT_RANGE_ATTR_SPACING:
|
|
return range_spacing->leading == value->u.spacing.leading &&
|
|
range_spacing->trailing == value->u.spacing.trailing &&
|
|
range_spacing->min_advance == value->u.spacing.min_advance;
|
|
case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
|
|
return range_iface->iface == (IUnknown*)value->u.typography;
|
|
default:
|
|
;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static inline BOOL is_same_layout_attributes(struct layout_range_header const *hleft, struct layout_range_header const *hright)
|
|
{
|
|
switch (hleft->kind)
|
|
{
|
|
case LAYOUT_RANGE_REGULAR:
|
|
{
|
|
struct layout_range const *left = (struct layout_range const*)hleft;
|
|
struct layout_range const *right = (struct layout_range const*)hright;
|
|
return left->weight == right->weight &&
|
|
left->style == right->style &&
|
|
left->stretch == right->stretch &&
|
|
left->fontsize == right->fontsize &&
|
|
left->object == right->object &&
|
|
left->pair_kerning == right->pair_kerning &&
|
|
left->collection == right->collection &&
|
|
!strcmpiW(left->locale, right->locale) &&
|
|
!strcmpW(left->fontfamily, right->fontfamily);
|
|
}
|
|
case LAYOUT_RANGE_UNDERLINE:
|
|
case LAYOUT_RANGE_STRIKETHROUGH:
|
|
{
|
|
struct layout_range_bool const *left = (struct layout_range_bool const*)hleft;
|
|
struct layout_range_bool const *right = (struct layout_range_bool const*)hright;
|
|
return left->value == right->value;
|
|
}
|
|
case LAYOUT_RANGE_EFFECT:
|
|
case LAYOUT_RANGE_TYPOGRAPHY:
|
|
{
|
|
struct layout_range_iface const *left = (struct layout_range_iface const*)hleft;
|
|
struct layout_range_iface const *right = (struct layout_range_iface const*)hright;
|
|
return left->iface == right->iface;
|
|
}
|
|
case LAYOUT_RANGE_SPACING:
|
|
{
|
|
struct layout_range_spacing const *left = (struct layout_range_spacing const*)hleft;
|
|
struct layout_range_spacing const *right = (struct layout_range_spacing const*)hright;
|
|
return left->leading == right->leading &&
|
|
left->trailing == right->trailing &&
|
|
left->min_advance == right->min_advance;
|
|
}
|
|
default:
|
|
FIXME("unknown range kind %d\n", hleft->kind);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static inline BOOL is_same_text_range(const DWRITE_TEXT_RANGE *left, const DWRITE_TEXT_RANGE *right)
|
|
{
|
|
return left->startPosition == right->startPosition && left->length == right->length;
|
|
}
|
|
|
|
/* Allocates range and inits it with default values from text format. */
|
|
static struct layout_range_header *alloc_layout_range(struct dwrite_textlayout *layout, const DWRITE_TEXT_RANGE *r,
|
|
enum layout_range_kind kind)
|
|
{
|
|
struct layout_range_header *h;
|
|
|
|
switch (kind)
|
|
{
|
|
case LAYOUT_RANGE_REGULAR:
|
|
{
|
|
struct layout_range *range;
|
|
|
|
range = heap_alloc(sizeof(*range));
|
|
if (!range) return NULL;
|
|
|
|
range->weight = layout->format.weight;
|
|
range->style = layout->format.style;
|
|
range->stretch = layout->format.stretch;
|
|
range->fontsize = layout->format.fontsize;
|
|
range->object = NULL;
|
|
range->pair_kerning = FALSE;
|
|
|
|
range->fontfamily = heap_strdupW(layout->format.family_name);
|
|
if (!range->fontfamily) {
|
|
heap_free(range);
|
|
return NULL;
|
|
}
|
|
|
|
range->collection = layout->format.collection;
|
|
if (range->collection)
|
|
IDWriteFontCollection_AddRef(range->collection);
|
|
strcpyW(range->locale, layout->format.locale);
|
|
|
|
h = &range->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_UNDERLINE:
|
|
case LAYOUT_RANGE_STRIKETHROUGH:
|
|
{
|
|
struct layout_range_bool *range;
|
|
|
|
range = heap_alloc(sizeof(*range));
|
|
if (!range) return NULL;
|
|
|
|
range->value = FALSE;
|
|
h = &range->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_EFFECT:
|
|
case LAYOUT_RANGE_TYPOGRAPHY:
|
|
{
|
|
struct layout_range_iface *range;
|
|
|
|
range = heap_alloc(sizeof(*range));
|
|
if (!range) return NULL;
|
|
|
|
range->iface = NULL;
|
|
h = &range->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_SPACING:
|
|
{
|
|
struct layout_range_spacing *range;
|
|
|
|
range = heap_alloc(sizeof(*range));
|
|
if (!range) return NULL;
|
|
|
|
range->leading = 0.0f;
|
|
range->trailing = 0.0f;
|
|
range->min_advance = 0.0f;
|
|
h = &range->h;
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("unknown range kind %d\n", kind);
|
|
return NULL;
|
|
}
|
|
|
|
h->kind = kind;
|
|
h->range = *r;
|
|
return h;
|
|
}
|
|
|
|
static struct layout_range_header *alloc_layout_range_from(struct layout_range_header *h, const DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct layout_range_header *ret;
|
|
|
|
switch (h->kind)
|
|
{
|
|
case LAYOUT_RANGE_REGULAR:
|
|
{
|
|
struct layout_range *from = (struct layout_range*)h;
|
|
|
|
struct layout_range *range = heap_alloc(sizeof(*range));
|
|
if (!range) return NULL;
|
|
|
|
*range = *from;
|
|
range->fontfamily = heap_strdupW(from->fontfamily);
|
|
if (!range->fontfamily) {
|
|
heap_free(range);
|
|
return NULL;
|
|
}
|
|
|
|
/* update refcounts */
|
|
if (range->object)
|
|
IDWriteInlineObject_AddRef(range->object);
|
|
if (range->collection)
|
|
IDWriteFontCollection_AddRef(range->collection);
|
|
ret = &range->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_UNDERLINE:
|
|
case LAYOUT_RANGE_STRIKETHROUGH:
|
|
{
|
|
struct layout_range_bool *strike = heap_alloc(sizeof(*strike));
|
|
if (!strike) return NULL;
|
|
|
|
*strike = *(struct layout_range_bool*)h;
|
|
ret = &strike->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_EFFECT:
|
|
case LAYOUT_RANGE_TYPOGRAPHY:
|
|
{
|
|
struct layout_range_iface *effect = heap_alloc(sizeof(*effect));
|
|
if (!effect) return NULL;
|
|
|
|
*effect = *(struct layout_range_iface*)h;
|
|
if (effect->iface)
|
|
IUnknown_AddRef(effect->iface);
|
|
ret = &effect->h;
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_SPACING:
|
|
{
|
|
struct layout_range_spacing *spacing = heap_alloc(sizeof(*spacing));
|
|
if (!spacing) return NULL;
|
|
|
|
*spacing = *(struct layout_range_spacing*)h;
|
|
ret = &spacing->h;
|
|
break;
|
|
}
|
|
default:
|
|
FIXME("unknown range kind %d\n", h->kind);
|
|
return NULL;
|
|
}
|
|
|
|
ret->range = *r;
|
|
return ret;
|
|
}
|
|
|
|
static void free_layout_range(struct layout_range_header *h)
|
|
{
|
|
if (!h)
|
|
return;
|
|
|
|
switch (h->kind)
|
|
{
|
|
case LAYOUT_RANGE_REGULAR:
|
|
{
|
|
struct layout_range *range = (struct layout_range*)h;
|
|
|
|
if (range->object)
|
|
IDWriteInlineObject_Release(range->object);
|
|
if (range->collection)
|
|
IDWriteFontCollection_Release(range->collection);
|
|
heap_free(range->fontfamily);
|
|
break;
|
|
}
|
|
case LAYOUT_RANGE_EFFECT:
|
|
case LAYOUT_RANGE_TYPOGRAPHY:
|
|
{
|
|
struct layout_range_iface *range = (struct layout_range_iface*)h;
|
|
if (range->iface)
|
|
IUnknown_Release(range->iface);
|
|
break;
|
|
}
|
|
default:
|
|
;
|
|
}
|
|
|
|
heap_free(h);
|
|
}
|
|
|
|
static void free_layout_ranges_list(struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_range_header *cur, *cur2;
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->ranges, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->underline_ranges, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->strike_ranges, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->effects, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->spacing, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &layout->typographies, struct layout_range_header, entry) {
|
|
list_remove(&cur->entry);
|
|
free_layout_range(cur);
|
|
}
|
|
}
|
|
|
|
static struct layout_range_header *find_outer_range(struct list *ranges, const DWRITE_TEXT_RANGE *range)
|
|
{
|
|
struct layout_range_header *cur;
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, ranges, struct layout_range_header, entry) {
|
|
|
|
if (cur->range.startPosition > range->startPosition)
|
|
return NULL;
|
|
|
|
if ((cur->range.startPosition + cur->range.length < range->startPosition + range->length) &&
|
|
(range->startPosition < cur->range.startPosition + cur->range.length))
|
|
return NULL;
|
|
if (cur->range.startPosition + cur->range.length >= range->startPosition + range->length)
|
|
return cur;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct layout_range *get_layout_range_by_pos(struct dwrite_textlayout *layout, UINT32 pos)
|
|
{
|
|
struct layout_range *cur;
|
|
|
|
LIST_FOR_EACH_ENTRY(cur, &layout->ranges, struct layout_range, h.entry) {
|
|
DWRITE_TEXT_RANGE *r = &cur->h.range;
|
|
if (r->startPosition <= pos && pos < r->startPosition + r->length)
|
|
return cur;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline BOOL set_layout_range_iface_attr(IUnknown **dest, IUnknown *value)
|
|
{
|
|
if (*dest == value) return FALSE;
|
|
|
|
if (*dest)
|
|
IUnknown_Release(*dest);
|
|
*dest = value;
|
|
if (*dest)
|
|
IUnknown_AddRef(*dest);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL set_layout_range_attrval(struct layout_range_header *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
|
|
{
|
|
struct layout_range_spacing *dest_spacing = (struct layout_range_spacing*)h;
|
|
struct layout_range_iface *dest_iface = (struct layout_range_iface*)h;
|
|
struct layout_range_bool *dest_bool = (struct layout_range_bool*)h;
|
|
struct layout_range *dest = (struct layout_range*)h;
|
|
|
|
BOOL changed = FALSE;
|
|
|
|
switch (attr) {
|
|
case LAYOUT_RANGE_ATTR_WEIGHT:
|
|
changed = dest->weight != value->u.weight;
|
|
dest->weight = value->u.weight;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_STYLE:
|
|
changed = dest->style != value->u.style;
|
|
dest->style = value->u.style;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_STRETCH:
|
|
changed = dest->stretch != value->u.stretch;
|
|
dest->stretch = value->u.stretch;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_FONTSIZE:
|
|
changed = dest->fontsize != value->u.fontsize;
|
|
dest->fontsize = value->u.fontsize;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_INLINE:
|
|
changed = set_layout_range_iface_attr((IUnknown**)&dest->object, (IUnknown*)value->u.object);
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_EFFECT:
|
|
changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.effect);
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_UNDERLINE:
|
|
changed = dest_bool->value != value->u.underline;
|
|
dest_bool->value = value->u.underline;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
|
|
changed = dest_bool->value != value->u.strikethrough;
|
|
dest_bool->value = value->u.strikethrough;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_PAIR_KERNING:
|
|
changed = dest->pair_kerning != value->u.pair_kerning;
|
|
dest->pair_kerning = value->u.pair_kerning;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_FONTCOLL:
|
|
changed = set_layout_range_iface_attr((IUnknown**)&dest->collection, (IUnknown*)value->u.collection);
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_LOCALE:
|
|
changed = strcmpiW(dest->locale, value->u.locale) != 0;
|
|
if (changed) {
|
|
strcpyW(dest->locale, value->u.locale);
|
|
strlwrW(dest->locale);
|
|
}
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_FONTFAMILY:
|
|
changed = strcmpW(dest->fontfamily, value->u.fontfamily) != 0;
|
|
if (changed) {
|
|
heap_free(dest->fontfamily);
|
|
dest->fontfamily = heap_strdupW(value->u.fontfamily);
|
|
}
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_SPACING:
|
|
changed = dest_spacing->leading != value->u.spacing.leading ||
|
|
dest_spacing->trailing != value->u.spacing.trailing ||
|
|
dest_spacing->min_advance != value->u.spacing.min_advance;
|
|
dest_spacing->leading = value->u.spacing.leading;
|
|
dest_spacing->trailing = value->u.spacing.trailing;
|
|
dest_spacing->min_advance = value->u.spacing.min_advance;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
|
|
changed = set_layout_range_iface_attr((IUnknown**)&dest_iface->iface, (IUnknown*)value->u.typography);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static inline BOOL is_in_layout_range(const DWRITE_TEXT_RANGE *outer, const DWRITE_TEXT_RANGE *inner)
|
|
{
|
|
return (inner->startPosition >= outer->startPosition) &&
|
|
(inner->startPosition + inner->length <= outer->startPosition + outer->length);
|
|
}
|
|
|
|
static inline HRESULT return_range(const struct layout_range_header *h, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
if (r) *r = h->range;
|
|
return S_OK;
|
|
}
|
|
|
|
/* Sets attribute value for given range, does all needed splitting/merging of existing ranges. */
|
|
static HRESULT set_layout_range_attr(struct dwrite_textlayout *layout, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
|
|
{
|
|
struct layout_range_header *cur, *right, *left, *outer;
|
|
BOOL changed = FALSE;
|
|
struct list *ranges;
|
|
DWRITE_TEXT_RANGE r;
|
|
|
|
/* ignore zero length ranges */
|
|
if (value->range.length == 0)
|
|
return S_OK;
|
|
|
|
/* select from ranges lists */
|
|
switch (attr)
|
|
{
|
|
case LAYOUT_RANGE_ATTR_WEIGHT:
|
|
case LAYOUT_RANGE_ATTR_STYLE:
|
|
case LAYOUT_RANGE_ATTR_STRETCH:
|
|
case LAYOUT_RANGE_ATTR_FONTSIZE:
|
|
case LAYOUT_RANGE_ATTR_INLINE:
|
|
case LAYOUT_RANGE_ATTR_PAIR_KERNING:
|
|
case LAYOUT_RANGE_ATTR_FONTCOLL:
|
|
case LAYOUT_RANGE_ATTR_LOCALE:
|
|
case LAYOUT_RANGE_ATTR_FONTFAMILY:
|
|
ranges = &layout->ranges;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_UNDERLINE:
|
|
ranges = &layout->underline_ranges;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_STRIKETHROUGH:
|
|
ranges = &layout->strike_ranges;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_EFFECT:
|
|
ranges = &layout->effects;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_SPACING:
|
|
ranges = &layout->spacing;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_TYPOGRAPHY:
|
|
ranges = &layout->typographies;
|
|
break;
|
|
default:
|
|
FIXME("unknown attr kind %d\n", attr);
|
|
return E_FAIL;
|
|
}
|
|
|
|
/* If new range is completely within existing range, split existing range in two */
|
|
if ((outer = find_outer_range(ranges, &value->range))) {
|
|
|
|
/* no need to add same range */
|
|
if (is_same_layout_attrvalue(outer, attr, value))
|
|
return S_OK;
|
|
|
|
/* for matching range bounds just replace data */
|
|
if (is_same_text_range(&outer->range, &value->range)) {
|
|
changed = set_layout_range_attrval(outer, attr, value);
|
|
goto done;
|
|
}
|
|
|
|
/* add new range to the left */
|
|
if (value->range.startPosition == outer->range.startPosition) {
|
|
left = alloc_layout_range_from(outer, &value->range);
|
|
if (!left) return E_OUTOFMEMORY;
|
|
|
|
changed = set_layout_range_attrval(left, attr, value);
|
|
list_add_before(&outer->entry, &left->entry);
|
|
outer->range.startPosition += value->range.length;
|
|
outer->range.length -= value->range.length;
|
|
goto done;
|
|
}
|
|
|
|
/* add new range to the right */
|
|
if (value->range.startPosition + value->range.length == outer->range.startPosition + outer->range.length) {
|
|
right = alloc_layout_range_from(outer, &value->range);
|
|
if (!right) return E_OUTOFMEMORY;
|
|
|
|
changed = set_layout_range_attrval(right, attr, value);
|
|
list_add_after(&outer->entry, &right->entry);
|
|
outer->range.length -= value->range.length;
|
|
goto done;
|
|
}
|
|
|
|
r.startPosition = value->range.startPosition + value->range.length;
|
|
r.length = outer->range.length + outer->range.startPosition - r.startPosition;
|
|
|
|
/* right part */
|
|
right = alloc_layout_range_from(outer, &r);
|
|
/* new range in the middle */
|
|
cur = alloc_layout_range_from(outer, &value->range);
|
|
if (!right || !cur) {
|
|
free_layout_range(right);
|
|
free_layout_range(cur);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
/* reuse container range as a left part */
|
|
outer->range.length = value->range.startPosition - outer->range.startPosition;
|
|
|
|
/* new part */
|
|
set_layout_range_attrval(cur, attr, value);
|
|
|
|
list_add_after(&outer->entry, &cur->entry);
|
|
list_add_after(&cur->entry, &right->entry);
|
|
|
|
layout->recompute = RECOMPUTE_EVERYTHING;
|
|
return S_OK;
|
|
}
|
|
|
|
/* Now it's only possible that given range contains some existing ranges, fully or partially.
|
|
Update all of them. */
|
|
left = get_layout_range_header_by_pos(ranges, value->range.startPosition);
|
|
if (left->range.startPosition == value->range.startPosition)
|
|
changed = set_layout_range_attrval(left, attr, value);
|
|
else /* need to split */ {
|
|
r.startPosition = value->range.startPosition;
|
|
r.length = left->range.length - value->range.startPosition + left->range.startPosition;
|
|
left->range.length -= r.length;
|
|
cur = alloc_layout_range_from(left, &r);
|
|
changed = set_layout_range_attrval(cur, attr, value);
|
|
list_add_after(&left->entry, &cur->entry);
|
|
}
|
|
cur = LIST_ENTRY(list_next(ranges, &left->entry), struct layout_range_header, entry);
|
|
|
|
/* for all existing ranges covered by new one update value */
|
|
while (cur && is_in_layout_range(&value->range, &cur->range)) {
|
|
changed |= set_layout_range_attrval(cur, attr, value);
|
|
cur = LIST_ENTRY(list_next(ranges, &cur->entry), struct layout_range_header, entry);
|
|
}
|
|
|
|
/* it's possible rightmost range intersects */
|
|
if (cur && (cur->range.startPosition < value->range.startPosition + value->range.length)) {
|
|
r.startPosition = cur->range.startPosition;
|
|
r.length = value->range.startPosition + value->range.length - cur->range.startPosition;
|
|
left = alloc_layout_range_from(cur, &r);
|
|
changed |= set_layout_range_attrval(left, attr, value);
|
|
cur->range.startPosition += left->range.length;
|
|
cur->range.length -= left->range.length;
|
|
list_add_before(&cur->entry, &left->entry);
|
|
}
|
|
|
|
done:
|
|
if (changed) {
|
|
struct list *next, *i;
|
|
|
|
layout->recompute = RECOMPUTE_EVERYTHING;
|
|
i = list_head(ranges);
|
|
while ((next = list_next(ranges, i))) {
|
|
struct layout_range_header *next_range = LIST_ENTRY(next, struct layout_range_header, entry);
|
|
|
|
cur = LIST_ENTRY(i, struct layout_range_header, entry);
|
|
if (is_same_layout_attributes(cur, next_range)) {
|
|
/* remove similar range */
|
|
cur->range.length += next_range->range.length;
|
|
list_remove(next);
|
|
free_layout_range(next_range);
|
|
}
|
|
else
|
|
i = list_next(ranges, i);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static inline const WCHAR *get_string_attribute_ptr(struct layout_range *range, enum layout_range_attr_kind kind)
|
|
{
|
|
const WCHAR *str;
|
|
|
|
switch (kind) {
|
|
case LAYOUT_RANGE_ATTR_LOCALE:
|
|
str = range->locale;
|
|
break;
|
|
case LAYOUT_RANGE_ATTR_FONTFAMILY:
|
|
str = range->fontfamily;
|
|
break;
|
|
default:
|
|
str = NULL;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
static HRESULT get_string_attribute_length(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
|
|
UINT32 *length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct layout_range *range;
|
|
const WCHAR *str;
|
|
|
|
range = get_layout_range_by_pos(layout, position);
|
|
if (!range) {
|
|
*length = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
str = get_string_attribute_ptr(range, kind);
|
|
*length = strlenW(str);
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT get_string_attribute_value(struct dwrite_textlayout *layout, enum layout_range_attr_kind kind, UINT32 position,
|
|
WCHAR *ret, UINT32 length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct layout_range *range;
|
|
const WCHAR *str;
|
|
|
|
if (length == 0)
|
|
return E_INVALIDARG;
|
|
|
|
ret[0] = 0;
|
|
range = get_layout_range_by_pos(layout, position);
|
|
if (!range)
|
|
return E_INVALIDARG;
|
|
|
|
str = get_string_attribute_ptr(range, kind);
|
|
if (length < strlenW(str) + 1)
|
|
return E_NOT_SUFFICIENT_BUFFER;
|
|
|
|
strcpyW(ret, str);
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_QueryInterface(IDWriteTextLayout3 *iface, REFIID riid, void **obj)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
|
|
|
|
*obj = NULL;
|
|
|
|
if (IsEqualIID(riid, &IID_IDWriteTextLayout3) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextLayout2) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextLayout1) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextLayout) ||
|
|
IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*obj = iface;
|
|
}
|
|
else if (IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextFormat))
|
|
*obj = &This->IDWriteTextFormat1_iface;
|
|
|
|
if (*obj) {
|
|
IDWriteTextLayout3_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_AddRef(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_Release(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
|
|
if (!ref) {
|
|
IDWriteFactory5_Release(This->factory);
|
|
free_layout_ranges_list(This);
|
|
free_layout_eruns(This);
|
|
free_layout_runs(This);
|
|
release_format_data(&This->format);
|
|
heap_free(This->nominal_breakpoints);
|
|
heap_free(This->actual_breakpoints);
|
|
heap_free(This->clustermetrics);
|
|
heap_free(This->clusters);
|
|
heap_free(This->linemetrics);
|
|
heap_free(This->lines);
|
|
heap_free(This->str);
|
|
heap_free(This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetTextAlignment(IDWriteTextLayout3 *iface, DWRITE_TEXT_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_SetTextAlignment(&This->IDWriteTextFormat1_iface, alignment);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetParagraphAlignment(IDWriteTextLayout3 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_SetParagraphAlignment(&This->IDWriteTextFormat1_iface, alignment);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetWordWrapping(IDWriteTextLayout3 *iface, DWRITE_WORD_WRAPPING wrapping)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_SetWordWrapping(&This->IDWriteTextFormat1_iface, wrapping);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetReadingDirection(IDWriteTextLayout3 *iface, DWRITE_READING_DIRECTION direction)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_SetReadingDirection(&This->IDWriteTextFormat1_iface, direction);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFlowDirection(IDWriteTextLayout3 *iface, DWRITE_FLOW_DIRECTION direction)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%d)\n", This, direction);
|
|
return IDWriteTextFormat1_SetFlowDirection(&This->IDWriteTextFormat1_iface, direction);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetIncrementalTabStop(IDWriteTextLayout3 *iface, FLOAT tabstop)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%.2f)\n", This, tabstop);
|
|
return IDWriteTextFormat1_SetIncrementalTabStop(&This->IDWriteTextFormat1_iface, tabstop);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING const *trimming,
|
|
IDWriteInlineObject *trimming_sign)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
|
|
return IDWriteTextFormat1_SetTrimming(&This->IDWriteTextFormat1_iface, trimming, trimming_sign);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD spacing,
|
|
FLOAT line_spacing, FLOAT baseline)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%d %.2f %.2f)\n", This, spacing, line_spacing, baseline);
|
|
return IDWriteTextFormat1_SetLineSpacing(&This->IDWriteTextFormat1_iface, spacing, line_spacing, baseline);
|
|
}
|
|
|
|
static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextlayout_GetTextAlignment(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetTextAlignment(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextlayout_GetParagraphAlignment(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetParagraphAlignment(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_WORD_WRAPPING WINAPI dwritetextlayout_GetWordWrapping(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetWordWrapping(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_GetReadingDirection(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetReadingDirection(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_FLOW_DIRECTION WINAPI dwritetextlayout_GetFlowDirection(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFlowDirection(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextlayout_GetIncrementalTabStop(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetIncrementalTabStop(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetTrimming(IDWriteTextLayout3 *iface, DWRITE_TRIMMING *options,
|
|
IDWriteInlineObject **trimming_sign)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetTrimming(&This->IDWriteTextFormat1_iface, options, trimming_sign);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING_METHOD *method,
|
|
FLOAT *spacing, FLOAT *baseline)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat_GetLineSpacing((IDWriteTextFormat*)&This->IDWriteTextFormat1_iface, method, spacing, baseline);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection **collection)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontCollection(&This->IDWriteTextFormat1_iface, collection);
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextlayout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontFamilyNameLength(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontFamilyName(&This->IDWriteTextFormat1_iface, name, size);
|
|
}
|
|
|
|
static DWRITE_FONT_WEIGHT WINAPI dwritetextlayout_GetFontWeight(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontWeight(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_FONT_STYLE WINAPI dwritetextlayout_GetFontStyle(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontStyle(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static DWRITE_FONT_STRETCH WINAPI dwritetextlayout_GetFontStretch(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontStretch(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextlayout_GetFontSize(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetFontSize(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextlayout_GetLocaleNameLength(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetLocaleNameLength(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetLocaleName(IDWriteTextLayout3 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
return IDWriteTextFormat1_GetLocaleName(&This->IDWriteTextFormat1_iface, name, size);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetMaxWidth(IDWriteTextLayout3 *iface, FLOAT maxWidth)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
BOOL changed;
|
|
|
|
TRACE("(%p)->(%.2f)\n", This, maxWidth);
|
|
|
|
if (maxWidth < 0.0f)
|
|
return E_INVALIDARG;
|
|
|
|
changed = This->metrics.layoutWidth != maxWidth;
|
|
This->metrics.layoutWidth = maxWidth;
|
|
|
|
if (changed)
|
|
This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetMaxHeight(IDWriteTextLayout3 *iface, FLOAT maxHeight)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
BOOL changed;
|
|
|
|
TRACE("(%p)->(%.2f)\n", This, maxHeight);
|
|
|
|
if (maxHeight < 0.0f)
|
|
return E_INVALIDARG;
|
|
|
|
changed = This->metrics.layoutHeight != maxHeight;
|
|
This->metrics.layoutHeight = maxHeight;
|
|
|
|
if (changed)
|
|
This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontCollection(IDWriteTextLayout3 *iface, IDWriteFontCollection* collection, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%p %s)\n", This, collection, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.collection = collection;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTCOLL, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontFamilyName(IDWriteTextLayout3 *iface, WCHAR const *name, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%s %s)\n", This, debugstr_w(name), debugstr_range(&range));
|
|
|
|
if (!name)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.fontfamily = name;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTFAMILY, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontWeight(IDWriteTextLayout3 *iface, DWRITE_FONT_WEIGHT weight, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, weight, debugstr_range(&range));
|
|
|
|
if ((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.weight = weight;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_WEIGHT, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontStyle(IDWriteTextLayout3 *iface, DWRITE_FONT_STYLE style, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, style, debugstr_range(&range));
|
|
|
|
if ((UINT32)style > DWRITE_FONT_STYLE_ITALIC)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.style = style;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STYLE, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontStretch(IDWriteTextLayout3 *iface, DWRITE_FONT_STRETCH stretch, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, stretch, debugstr_range(&range));
|
|
|
|
if (stretch == DWRITE_FONT_STRETCH_UNDEFINED || (UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.stretch = stretch;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRETCH, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetFontSize(IDWriteTextLayout3 *iface, FLOAT size, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%.2f %s)\n", This, size, debugstr_range(&range));
|
|
|
|
if (size <= 0.0f)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.fontsize = size;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_FONTSIZE, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetUnderline(IDWriteTextLayout3 *iface, BOOL underline, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, underline, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.underline = underline;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_UNDERLINE, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetStrikethrough(IDWriteTextLayout3 *iface, BOOL strikethrough, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, strikethrough, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.strikethrough = strikethrough;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_STRIKETHROUGH, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetDrawingEffect(IDWriteTextLayout3 *iface, IUnknown* effect, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%p %s)\n", This, effect, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.effect = effect;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_EFFECT, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetInlineObject(IDWriteTextLayout3 *iface, IDWriteInlineObject *object, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%p %s)\n", This, object, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.object = object;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_INLINE, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetTypography(IDWriteTextLayout3 *iface, IDWriteTypography* typography, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%p %s)\n", This, typography, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.typography = typography;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_TYPOGRAPHY, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_SetLocaleName(IDWriteTextLayout3 *iface, WCHAR const* locale, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%s %s)\n", This, debugstr_w(locale), debugstr_range(&range));
|
|
|
|
if (!locale || strlenW(locale) > LOCALE_NAME_MAX_LENGTH-1)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.locale = locale;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_LOCALE, &value);
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextlayout_GetMaxWidth(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->metrics.layoutWidth;
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextlayout_GetMaxHeight(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->metrics.layoutHeight;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontCollection(IDWriteTextLayout3 *iface, UINT32 position,
|
|
IDWriteFontCollection** collection, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, collection, r);
|
|
|
|
if (position >= This->len)
|
|
return S_OK;
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*collection = range->collection;
|
|
if (*collection)
|
|
IDWriteFontCollection_AddRef(*collection);
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyNameLength(IDWriteTextLayout3 *iface,
|
|
UINT32 position, UINT32 *length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%d %p %p)\n", This, position, length, r);
|
|
return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, length, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontFamilyName(IDWriteTextLayout3 *iface,
|
|
UINT32 position, WCHAR *name, UINT32 length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%u %p %u %p)\n", This, position, name, length, r);
|
|
return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_FONTFAMILY, position, name, length, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontWeight(IDWriteTextLayout3 *iface,
|
|
UINT32 position, DWRITE_FONT_WEIGHT *weight, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, weight, r);
|
|
|
|
if (position >= This->len)
|
|
return S_OK;
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*weight = range->weight;
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontStyle(IDWriteTextLayout3 *iface,
|
|
UINT32 position, DWRITE_FONT_STYLE *style, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, style, r);
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*style = range->style;
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontStretch(IDWriteTextLayout3 *iface,
|
|
UINT32 position, DWRITE_FONT_STRETCH *stretch, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, stretch, r);
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*stretch = range->stretch;
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetFontSize(IDWriteTextLayout3 *iface,
|
|
UINT32 position, FLOAT *size, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, size, r);
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*size = range->fontsize;
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetUnderline(IDWriteTextLayout3 *iface,
|
|
UINT32 position, BOOL *underline, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_bool *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, underline, r);
|
|
|
|
range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->underline_ranges, position);
|
|
*underline = range->value;
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetStrikethrough(IDWriteTextLayout3 *iface,
|
|
UINT32 position, BOOL *strikethrough, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_bool *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, strikethrough, r);
|
|
|
|
range = (struct layout_range_bool*)get_layout_range_header_by_pos(&This->strike_ranges, position);
|
|
*strikethrough = range->value;
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetDrawingEffect(IDWriteTextLayout3 *iface,
|
|
UINT32 position, IUnknown **effect, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_iface *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, effect, r);
|
|
|
|
range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->effects, position);
|
|
*effect = range->iface;
|
|
if (*effect)
|
|
IUnknown_AddRef(*effect);
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetInlineObject(IDWriteTextLayout3 *iface,
|
|
UINT32 position, IDWriteInlineObject **object, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, object, r);
|
|
|
|
if (position >= This->len)
|
|
return S_OK;
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*object = range->object;
|
|
if (*object)
|
|
IDWriteInlineObject_AddRef(*object);
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetTypography(IDWriteTextLayout3 *iface,
|
|
UINT32 position, IDWriteTypography** typography, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_iface *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, typography, r);
|
|
|
|
range = (struct layout_range_iface*)get_layout_range_header_by_pos(&This->typographies, position);
|
|
*typography = (IDWriteTypography*)range->iface;
|
|
if (*typography)
|
|
IDWriteTypography_AddRef(*typography);
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetLocaleNameLength(IDWriteTextLayout3 *iface,
|
|
UINT32 position, UINT32* length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, length, r);
|
|
return get_string_attribute_length(This, LAYOUT_RANGE_ATTR_LOCALE, position, length, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout3 *iface,
|
|
UINT32 position, WCHAR* locale, UINT32 length, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%u %p %u %p)\n", This, position, locale, length, r);
|
|
return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
|
|
}
|
|
|
|
static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
|
|
const DWRITE_MATRIX *m)
|
|
{
|
|
D2D1_POINT_2F vec, vec2;
|
|
|
|
if (!skiptransform) {
|
|
/* apply transform */
|
|
vec.x = 0.0f;
|
|
vec.y = coord * ppdip;
|
|
|
|
vec2.x = m->m11 * vec.x + m->m21 * vec.y + m->dx;
|
|
vec2.y = m->m12 * vec.x + m->m22 * vec.y + m->dy;
|
|
|
|
/* snap */
|
|
vec2.x = floorf(vec2.x + 0.5f);
|
|
vec2.y = floorf(vec2.y + 0.5f);
|
|
|
|
/* apply inverted transform, we don't care about X component at this point */
|
|
vec.y = (-m->m12 * vec2.x + m->m11 * vec2.y - (m->m11 * m->dy - m->m12 * m->dx)) / det;
|
|
vec.y /= ppdip;
|
|
}
|
|
else
|
|
vec.y = floorf(coord * ppdip + 0.5f) / ppdip;
|
|
|
|
return vec.y;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout3 *iface,
|
|
void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
BOOL disabled = FALSE, skiptransform = FALSE;
|
|
struct layout_effective_inline *inlineobject;
|
|
struct layout_effective_run *run;
|
|
struct layout_strikethrough *s;
|
|
struct layout_underline *u;
|
|
FLOAT det = 0.0f, ppdip = 0.0f;
|
|
DWRITE_MATRIX m = { 0 };
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
|
|
|
|
hr = layout_compute_effective_runs(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (!disabled) {
|
|
hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/* it's only allowed to have a diagonal/antidiagonal transform matrix */
|
|
if (ppdip <= 0.0f ||
|
|
(m.m11 * m.m22 != 0.0f && (m.m12 != 0.0f || m.m21 != 0.0f)) ||
|
|
(m.m12 * m.m21 != 0.0f && (m.m11 != 0.0f || m.m22 != 0.0f)))
|
|
disabled = TRUE;
|
|
else
|
|
skiptransform = should_skip_transform(&m, &det);
|
|
}
|
|
|
|
#define SNAP_COORD(x) (disabled ? (x) : renderer_apply_snapping((x), skiptransform, ppdip, det, &m))
|
|
/* 1. Regular runs */
|
|
LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
|
|
const struct regular_layout_run *regular = &run->run->u.regular;
|
|
UINT32 start_glyph = regular->clustermap[run->start];
|
|
DWRITE_GLYPH_RUN_DESCRIPTION descr;
|
|
DWRITE_GLYPH_RUN glyph_run;
|
|
|
|
/* Everything but cluster map will be reused from nominal run, as we only need
|
|
to adjust some pointers. Cluster map however is rebuilt when effective run is added,
|
|
it can't be reused because it has to start with 0 index for each reported run. */
|
|
glyph_run = regular->run;
|
|
glyph_run.glyphCount = run->glyphcount;
|
|
|
|
/* fixup glyph data arrays */
|
|
glyph_run.glyphIndices += start_glyph;
|
|
glyph_run.glyphAdvances += start_glyph;
|
|
glyph_run.glyphOffsets += start_glyph;
|
|
|
|
/* description */
|
|
descr = regular->descr;
|
|
descr.stringLength = run->length;
|
|
descr.string += run->start;
|
|
descr.clusterMap = run->clustermap;
|
|
descr.textPosition += run->start;
|
|
|
|
/* return value is ignored */
|
|
IDWriteTextRenderer_DrawGlyphRun(renderer,
|
|
context,
|
|
run->origin.x + run->align_dx + origin_x,
|
|
SNAP_COORD(run->origin.y + origin_y),
|
|
This->measuringmode,
|
|
&glyph_run,
|
|
&descr,
|
|
run->effect);
|
|
}
|
|
|
|
/* 2. Inline objects */
|
|
LIST_FOR_EACH_ENTRY(inlineobject, &This->inlineobjects, struct layout_effective_inline, entry) {
|
|
IDWriteTextRenderer_DrawInlineObject(renderer,
|
|
context,
|
|
inlineobject->origin.x + inlineobject->align_dx + origin_x,
|
|
SNAP_COORD(inlineobject->origin.y + origin_y),
|
|
inlineobject->object,
|
|
inlineobject->is_sideways,
|
|
inlineobject->is_rtl,
|
|
inlineobject->effect);
|
|
}
|
|
|
|
/* 3. Underlines */
|
|
LIST_FOR_EACH_ENTRY(u, &This->underlines, struct layout_underline, entry) {
|
|
IDWriteTextRenderer_DrawUnderline(renderer,
|
|
context,
|
|
/* horizontal underline always grows from left to right, width is always added to origin regardless of run direction */
|
|
(is_run_rtl(u->run) ? u->run->origin.x - u->run->width : u->run->origin.x) + u->run->align_dx + origin_x,
|
|
SNAP_COORD(u->run->origin.y + origin_y),
|
|
&u->u,
|
|
u->run->effect);
|
|
}
|
|
|
|
/* 4. Strikethrough */
|
|
LIST_FOR_EACH_ENTRY(s, &This->strikethrough, struct layout_strikethrough, entry) {
|
|
IDWriteTextRenderer_DrawStrikethrough(renderer,
|
|
context,
|
|
s->run->origin.x + s->run->align_dx + origin_x,
|
|
SNAP_COORD(s->run->origin.y + origin_y),
|
|
&s->s,
|
|
s->run->effect);
|
|
}
|
|
#undef SNAP_COORD
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetLineMetrics(IDWriteTextLayout3 *iface,
|
|
DWRITE_LINE_METRICS *metrics, UINT32 max_count, UINT32 *count)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
|
|
|
|
hr = layout_compute_effective_runs(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (metrics) {
|
|
UINT32 i, c = min(max_count, This->metrics.lineCount);
|
|
for (i = 0; i < c; i++)
|
|
memcpy(metrics + i, This->linemetrics + i, sizeof(*metrics));
|
|
}
|
|
|
|
*count = This->metrics.lineCount;
|
|
return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS *metrics)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
DWRITE_TEXT_METRICS1 metrics1;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, metrics);
|
|
|
|
hr = IDWriteTextLayout3_GetMetrics(iface, &metrics1);
|
|
if (hr == S_OK)
|
|
memcpy(metrics, &metrics1, sizeof(*metrics));
|
|
|
|
return hr;
|
|
}
|
|
|
|
static void scale_glyph_bbox(RECT *bbox, FLOAT emSize, UINT16 units_per_em, D2D1_RECT_F *ret)
|
|
{
|
|
#define SCALE(x) ((FLOAT)x * emSize / (FLOAT)units_per_em)
|
|
ret->left = SCALE(bbox->left);
|
|
ret->right = SCALE(bbox->right);
|
|
ret->top = SCALE(bbox->top);
|
|
ret->bottom = SCALE(bbox->bottom);
|
|
#undef SCALE
|
|
}
|
|
|
|
static void d2d_rect_offset(D2D1_RECT_F *rect, FLOAT x, FLOAT y)
|
|
{
|
|
rect->left += x;
|
|
rect->right += x;
|
|
rect->top += y;
|
|
rect->bottom += y;
|
|
}
|
|
|
|
static BOOL d2d_rect_is_empty(const D2D1_RECT_F *rect)
|
|
{
|
|
return ((rect->left >= rect->right) || (rect->top >= rect->bottom));
|
|
}
|
|
|
|
static void d2d_rect_union(D2D1_RECT_F *dst, const D2D1_RECT_F *src)
|
|
{
|
|
if (d2d_rect_is_empty(dst)) {
|
|
if (d2d_rect_is_empty(src)) {
|
|
dst->left = dst->right = dst->top = dst->bottom = 0.0f;
|
|
return;
|
|
}
|
|
else
|
|
*dst = *src;
|
|
}
|
|
else {
|
|
if (!d2d_rect_is_empty(src)) {
|
|
dst->left = min(dst->left, src->left);
|
|
dst->right = max(dst->right, src->right);
|
|
dst->top = min(dst->top, src->top);
|
|
dst->bottom = max(dst->bottom, src->bottom);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void layout_get_erun_bbox(struct dwrite_textlayout *layout, struct layout_effective_run *run, D2D1_RECT_F *bbox)
|
|
{
|
|
const struct regular_layout_run *regular = &run->run->u.regular;
|
|
UINT32 start_glyph = regular->clustermap[run->start];
|
|
const DWRITE_GLYPH_RUN *glyph_run = ®ular->run;
|
|
DWRITE_FONT_METRICS font_metrics;
|
|
D2D1_POINT_2F origin = { 0 };
|
|
UINT32 i;
|
|
|
|
if (run->bbox.top == run->bbox.bottom) {
|
|
IDWriteFontFace_GetMetrics(glyph_run->fontFace, &font_metrics);
|
|
|
|
for (i = 0; i < run->glyphcount; i++) {
|
|
D2D1_RECT_F glyph_bbox;
|
|
RECT design_bbox;
|
|
|
|
freetype_get_design_glyph_bbox((IDWriteFontFace4 *)glyph_run->fontFace, font_metrics.designUnitsPerEm,
|
|
glyph_run->glyphIndices[i + start_glyph], &design_bbox);
|
|
|
|
scale_glyph_bbox(&design_bbox, glyph_run->fontEmSize, font_metrics.designUnitsPerEm, &glyph_bbox);
|
|
d2d_rect_offset(&glyph_bbox, origin.x + glyph_run->glyphOffsets[i + start_glyph].advanceOffset,
|
|
origin.y + glyph_run->glyphOffsets[i + start_glyph].ascenderOffset);
|
|
d2d_rect_union(&run->bbox, &glyph_bbox);
|
|
|
|
/* FIXME: take care of vertical/rtl */
|
|
origin.x += glyph_run->glyphAdvances[i + start_glyph];
|
|
}
|
|
}
|
|
|
|
*bbox = run->bbox;
|
|
d2d_rect_offset(bbox, run->origin.x + run->align_dx, run->origin.y);
|
|
}
|
|
|
|
static void layout_get_inlineobj_bbox(struct dwrite_textlayout *layout, struct layout_effective_inline *run,
|
|
D2D1_RECT_F *bbox)
|
|
{
|
|
DWRITE_OVERHANG_METRICS overhang_metrics = { 0 };
|
|
DWRITE_INLINE_OBJECT_METRICS metrics = { 0 };
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = IDWriteInlineObject_GetMetrics(run->object, &metrics))) {
|
|
WARN("Failed to get inline object metrics, hr %#x.\n", hr);
|
|
memset(bbox, 0, sizeof(*bbox));
|
|
return;
|
|
}
|
|
|
|
bbox->left = run->origin.x + run->align_dx;
|
|
bbox->right = bbox->left + metrics.width;
|
|
bbox->top = run->origin.y;
|
|
bbox->bottom = bbox->top + metrics.height;
|
|
|
|
IDWriteInlineObject_GetOverhangMetrics(run->object, &overhang_metrics);
|
|
|
|
bbox->left -= overhang_metrics.left;
|
|
bbox->right += overhang_metrics.right;
|
|
bbox->top -= overhang_metrics.top;
|
|
bbox->bottom += overhang_metrics.bottom;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetOverhangMetrics(IDWriteTextLayout3 *iface,
|
|
DWRITE_OVERHANG_METRICS *overhangs)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_effective_inline *inline_run;
|
|
struct layout_effective_run *run;
|
|
D2D1_RECT_F bbox = { 0 };
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, overhangs);
|
|
|
|
memset(overhangs, 0, sizeof(*overhangs));
|
|
|
|
if (!(This->recompute & RECOMPUTE_OVERHANGS)) {
|
|
*overhangs = This->overhangs;
|
|
return S_OK;
|
|
}
|
|
|
|
hr = layout_compute_effective_runs(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
|
|
D2D1_RECT_F run_bbox;
|
|
|
|
layout_get_erun_bbox(This, run, &run_bbox);
|
|
d2d_rect_union(&bbox, &run_bbox);
|
|
}
|
|
|
|
LIST_FOR_EACH_ENTRY(inline_run, &This->inlineobjects, struct layout_effective_inline, entry) {
|
|
D2D1_RECT_F object_bbox;
|
|
|
|
layout_get_inlineobj_bbox(This, inline_run, &object_bbox);
|
|
d2d_rect_union(&bbox, &object_bbox);
|
|
}
|
|
|
|
/* Deltas from layout box. */
|
|
This->overhangs.left = -bbox.left;
|
|
This->overhangs.top = -bbox.top;
|
|
This->overhangs.right = bbox.right - This->metrics.layoutWidth;
|
|
This->overhangs.bottom = bbox.bottom - This->metrics.layoutHeight;
|
|
This->recompute &= ~RECOMPUTE_OVERHANGS;
|
|
|
|
*overhangs = This->overhangs;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_GetClusterMetrics(IDWriteTextLayout3 *iface,
|
|
DWRITE_CLUSTER_METRICS *metrics, UINT32 max_count, UINT32 *count)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
|
|
|
|
hr = layout_compute(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (metrics)
|
|
memcpy(metrics, This->clustermetrics, sizeof(DWRITE_CLUSTER_METRICS)*min(max_count, This->cluster_count));
|
|
|
|
*count = This->cluster_count;
|
|
return max_count >= This->cluster_count ? S_OK : E_NOT_SUFFICIENT_BUFFER;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_DetermineMinWidth(IDWriteTextLayout3 *iface, FLOAT* min_width)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
UINT32 start;
|
|
FLOAT width;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, min_width);
|
|
|
|
if (!min_width)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(This->recompute & RECOMPUTE_MINIMAL_WIDTH))
|
|
goto width_done;
|
|
|
|
*min_width = 0.0f;
|
|
hr = layout_compute(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/* Find widest word without emergency breaking between clusters, trailing whitespaces
|
|
preceding breaking point do not contribute to word width. */
|
|
for (start = 0; start < This->cluster_count;) {
|
|
UINT32 end = start, j, next;
|
|
|
|
/* Last cluster always could be wrapped after. */
|
|
while (!This->clustermetrics[end].canWrapLineAfter)
|
|
end++;
|
|
/* make is so current cluster range that we can wrap after is [start,end) */
|
|
end++;
|
|
|
|
next = end;
|
|
|
|
/* Ignore trailing whitespace clusters, in case of single space range will
|
|
be reduced to empty range, or [start,start+1). */
|
|
while (end > start && This->clustermetrics[end-1].isWhitespace)
|
|
end--;
|
|
|
|
/* check if cluster range exceeds last minimal width */
|
|
width = 0.0f;
|
|
for (j = start; j < end; j++)
|
|
width += This->clustermetrics[j].width;
|
|
|
|
start = next;
|
|
|
|
if (width > This->minwidth)
|
|
This->minwidth = width;
|
|
}
|
|
This->recompute &= ~RECOMPUTE_MINIMAL_WIDTH;
|
|
|
|
width_done:
|
|
*min_width = This->minwidth;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_HitTestPoint(IDWriteTextLayout3 *iface,
|
|
FLOAT pointX, FLOAT pointY, BOOL* is_trailinghit, BOOL* is_inside, DWRITE_HIT_TEST_METRICS *metrics)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
FIXME("(%p)->(%f %f %p %p %p): stub\n", This, pointX, pointY, is_trailinghit, is_inside, metrics);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_HitTestTextPosition(IDWriteTextLayout3 *iface,
|
|
UINT32 textPosition, BOOL is_trailinghit, FLOAT* pointX, FLOAT* pointY, DWRITE_HIT_TEST_METRICS *metrics)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
FIXME("(%p)->(%u %d %p %p %p): stub\n", This, textPosition, is_trailinghit, pointX, pointY, metrics);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_HitTestTextRange(IDWriteTextLayout3 *iface,
|
|
UINT32 textPosition, UINT32 textLength, FLOAT originX, FLOAT originY,
|
|
DWRITE_HIT_TEST_METRICS *metrics, UINT32 max_metricscount, UINT32* actual_metricscount)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
FIXME("(%p)->(%u %u %f %f %p %u %p): stub\n", This, textPosition, textLength, originX, originY, metrics,
|
|
max_metricscount, actual_metricscount);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout1_SetPairKerning(IDWriteTextLayout3 *iface, BOOL is_pairkerning_enabled,
|
|
DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%d %s)\n", This, is_pairkerning_enabled, debugstr_range(&range));
|
|
|
|
value.range = range;
|
|
value.u.pair_kerning = !!is_pairkerning_enabled;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_PAIR_KERNING, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout1_GetPairKerning(IDWriteTextLayout3 *iface, UINT32 position, BOOL *is_pairkerning_enabled,
|
|
DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range *range;
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", This, position, is_pairkerning_enabled, r);
|
|
|
|
if (position >= This->len)
|
|
return S_OK;
|
|
|
|
range = get_layout_range_by_pos(This, position);
|
|
*is_pairkerning_enabled = range->pair_kerning;
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout1_SetCharacterSpacing(IDWriteTextLayout3 *iface, FLOAT leading, FLOAT trailing,
|
|
FLOAT min_advance, DWRITE_TEXT_RANGE range)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_attr_value value;
|
|
|
|
TRACE("(%p)->(%.2f %.2f %.2f %s)\n", This, leading, trailing, min_advance, debugstr_range(&range));
|
|
|
|
if (min_advance < 0.0f)
|
|
return E_INVALIDARG;
|
|
|
|
value.range = range;
|
|
value.u.spacing.leading = leading;
|
|
value.u.spacing.trailing = trailing;
|
|
value.u.spacing.min_advance = min_advance;
|
|
return set_layout_range_attr(This, LAYOUT_RANGE_ATTR_SPACING, &value);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout1_GetCharacterSpacing(IDWriteTextLayout3 *iface, UINT32 position, FLOAT *leading,
|
|
FLOAT *trailing, FLOAT *min_advance, DWRITE_TEXT_RANGE *r)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
struct layout_range_spacing *range;
|
|
|
|
TRACE("(%p)->(%u %p %p %p %p)\n", This, position, leading, trailing, min_advance, r);
|
|
|
|
range = (struct layout_range_spacing*)get_layout_range_header_by_pos(&This->spacing, position);
|
|
*leading = range->leading;
|
|
*trailing = range->trailing;
|
|
*min_advance = range->min_advance;
|
|
|
|
return return_range(&range->h, r);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_GetMetrics(IDWriteTextLayout3 *iface, DWRITE_TEXT_METRICS1 *metrics)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, metrics);
|
|
|
|
hr = layout_compute_effective_runs(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
*metrics = This->metrics;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_SetVerticalGlyphOrientation(IDWriteTextLayout3 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
|
|
TRACE("(%p)->(%d)\n", This, orientation);
|
|
|
|
if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
|
|
return E_INVALIDARG;
|
|
|
|
This->format.vertical_orientation = orientation;
|
|
return S_OK;
|
|
}
|
|
|
|
static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextlayout2_GetVerticalGlyphOrientation(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.vertical_orientation;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_SetLastLineWrapping(IDWriteTextLayout3 *iface, BOOL lastline_wrapping_enabled)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
|
|
return IDWriteTextFormat1_SetLastLineWrapping(&This->IDWriteTextFormat1_iface, lastline_wrapping_enabled);
|
|
}
|
|
|
|
static BOOL WINAPI dwritetextlayout2_GetLastLineWrapping(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)\n", This);
|
|
return IDWriteTextFormat1_GetLastLineWrapping(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_SetOpticalAlignment(IDWriteTextLayout3 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
return IDWriteTextFormat1_SetOpticalAlignment(&This->IDWriteTextFormat1_iface, alignment);
|
|
}
|
|
|
|
static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextlayout2_GetOpticalAlignment(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)\n", This);
|
|
return IDWriteTextFormat1_GetOpticalAlignment(&This->IDWriteTextFormat1_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_SetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback *fallback)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return set_fontfallback_for_format(&This->format, fallback);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout2_GetFontFallback(IDWriteTextLayout3 *iface, IDWriteFontFallback **fallback)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return get_fontfallback_from_format(&This->format, fallback);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout3_InvalidateLayout(IDWriteTextLayout3 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
This->recompute = RECOMPUTE_EVERYTHING;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout3_SetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING const *spacing)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, spacing);
|
|
|
|
hr = format_set_linespacing(&This->format, spacing, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed) {
|
|
if (!(This->recompute & RECOMPUTE_LINES)) {
|
|
UINT32 line;
|
|
|
|
for (line = 0; line < This->metrics.lineCount; line++)
|
|
layout_apply_line_spacing(This, line);
|
|
|
|
layout_set_line_positions(This);
|
|
}
|
|
|
|
This->recompute |= RECOMPUTE_OVERHANGS;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout3_GetLineSpacing(IDWriteTextLayout3 *iface, DWRITE_LINE_SPACING *spacing)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
|
|
TRACE("(%p)->(%p)\n", This, spacing);
|
|
|
|
*spacing = This->format.spacing;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout3_GetLineMetrics(IDWriteTextLayout3 *iface, DWRITE_LINE_METRICS1 *metrics,
|
|
UINT32 max_count, UINT32 *count)
|
|
{
|
|
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout3(iface);
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p %u %p)\n", This, metrics, max_count, count);
|
|
|
|
hr = layout_compute_effective_runs(This);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (metrics)
|
|
memcpy(metrics, This->linemetrics, sizeof(*metrics) * min(max_count, This->metrics.lineCount));
|
|
|
|
*count = This->metrics.lineCount;
|
|
return max_count >= This->metrics.lineCount ? S_OK : E_NOT_SUFFICIENT_BUFFER;
|
|
}
|
|
|
|
static const IDWriteTextLayout3Vtbl dwritetextlayoutvtbl = {
|
|
dwritetextlayout_QueryInterface,
|
|
dwritetextlayout_AddRef,
|
|
dwritetextlayout_Release,
|
|
dwritetextlayout_SetTextAlignment,
|
|
dwritetextlayout_SetParagraphAlignment,
|
|
dwritetextlayout_SetWordWrapping,
|
|
dwritetextlayout_SetReadingDirection,
|
|
dwritetextlayout_SetFlowDirection,
|
|
dwritetextlayout_SetIncrementalTabStop,
|
|
dwritetextlayout_SetTrimming,
|
|
dwritetextlayout_SetLineSpacing,
|
|
dwritetextlayout_GetTextAlignment,
|
|
dwritetextlayout_GetParagraphAlignment,
|
|
dwritetextlayout_GetWordWrapping,
|
|
dwritetextlayout_GetReadingDirection,
|
|
dwritetextlayout_GetFlowDirection,
|
|
dwritetextlayout_GetIncrementalTabStop,
|
|
dwritetextlayout_GetTrimming,
|
|
dwritetextlayout_GetLineSpacing,
|
|
dwritetextlayout_GetFontCollection,
|
|
dwritetextlayout_GetFontFamilyNameLength,
|
|
dwritetextlayout_GetFontFamilyName,
|
|
dwritetextlayout_GetFontWeight,
|
|
dwritetextlayout_GetFontStyle,
|
|
dwritetextlayout_GetFontStretch,
|
|
dwritetextlayout_GetFontSize,
|
|
dwritetextlayout_GetLocaleNameLength,
|
|
dwritetextlayout_GetLocaleName,
|
|
dwritetextlayout_SetMaxWidth,
|
|
dwritetextlayout_SetMaxHeight,
|
|
dwritetextlayout_SetFontCollection,
|
|
dwritetextlayout_SetFontFamilyName,
|
|
dwritetextlayout_SetFontWeight,
|
|
dwritetextlayout_SetFontStyle,
|
|
dwritetextlayout_SetFontStretch,
|
|
dwritetextlayout_SetFontSize,
|
|
dwritetextlayout_SetUnderline,
|
|
dwritetextlayout_SetStrikethrough,
|
|
dwritetextlayout_SetDrawingEffect,
|
|
dwritetextlayout_SetInlineObject,
|
|
dwritetextlayout_SetTypography,
|
|
dwritetextlayout_SetLocaleName,
|
|
dwritetextlayout_GetMaxWidth,
|
|
dwritetextlayout_GetMaxHeight,
|
|
dwritetextlayout_layout_GetFontCollection,
|
|
dwritetextlayout_layout_GetFontFamilyNameLength,
|
|
dwritetextlayout_layout_GetFontFamilyName,
|
|
dwritetextlayout_layout_GetFontWeight,
|
|
dwritetextlayout_layout_GetFontStyle,
|
|
dwritetextlayout_layout_GetFontStretch,
|
|
dwritetextlayout_layout_GetFontSize,
|
|
dwritetextlayout_GetUnderline,
|
|
dwritetextlayout_GetStrikethrough,
|
|
dwritetextlayout_GetDrawingEffect,
|
|
dwritetextlayout_GetInlineObject,
|
|
dwritetextlayout_GetTypography,
|
|
dwritetextlayout_layout_GetLocaleNameLength,
|
|
dwritetextlayout_layout_GetLocaleName,
|
|
dwritetextlayout_Draw,
|
|
dwritetextlayout_GetLineMetrics,
|
|
dwritetextlayout_GetMetrics,
|
|
dwritetextlayout_GetOverhangMetrics,
|
|
dwritetextlayout_GetClusterMetrics,
|
|
dwritetextlayout_DetermineMinWidth,
|
|
dwritetextlayout_HitTestPoint,
|
|
dwritetextlayout_HitTestTextPosition,
|
|
dwritetextlayout_HitTestTextRange,
|
|
dwritetextlayout1_SetPairKerning,
|
|
dwritetextlayout1_GetPairKerning,
|
|
dwritetextlayout1_SetCharacterSpacing,
|
|
dwritetextlayout1_GetCharacterSpacing,
|
|
dwritetextlayout2_GetMetrics,
|
|
dwritetextlayout2_SetVerticalGlyphOrientation,
|
|
dwritetextlayout2_GetVerticalGlyphOrientation,
|
|
dwritetextlayout2_SetLastLineWrapping,
|
|
dwritetextlayout2_GetLastLineWrapping,
|
|
dwritetextlayout2_SetOpticalAlignment,
|
|
dwritetextlayout2_GetOpticalAlignment,
|
|
dwritetextlayout2_SetFontFallback,
|
|
dwritetextlayout2_GetFontFallback,
|
|
dwritetextlayout3_InvalidateLayout,
|
|
dwritetextlayout3_SetLineSpacing,
|
|
dwritetextlayout3_GetLineSpacing,
|
|
dwritetextlayout3_GetLineMetrics
|
|
};
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_QueryInterface(IDWriteTextFormat1 *iface, REFIID riid, void **obj)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
|
|
return IDWriteTextLayout3_QueryInterface(&This->IDWriteTextLayout3_iface, riid, obj);
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextformat_layout_AddRef(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
return IDWriteTextLayout3_AddRef(&This->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextformat_layout_Release(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
return IDWriteTextLayout3_Release(&This->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetTextAlignment(IDWriteTextFormat1 *iface, DWRITE_TEXT_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
|
|
hr = format_set_textalignment(&This->format, alignment, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed) {
|
|
/* if layout is not ready there's nothing to align */
|
|
if (!(This->recompute & RECOMPUTE_LINES))
|
|
layout_apply_text_alignment(This);
|
|
This->recompute |= RECOMPUTE_OVERHANGS;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetParagraphAlignment(IDWriteTextFormat1 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
|
|
hr = format_set_paralignment(&This->format, alignment, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed) {
|
|
/* if layout is not ready there's nothing to align */
|
|
if (!(This->recompute & RECOMPUTE_LINES))
|
|
layout_apply_par_alignment(This);
|
|
This->recompute |= RECOMPUTE_OVERHANGS;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetWordWrapping(IDWriteTextFormat1 *iface, DWRITE_WORD_WRAPPING wrapping)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%d)\n", This, wrapping);
|
|
|
|
hr = format_set_wordwrapping(&This->format, wrapping, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed)
|
|
This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetReadingDirection(IDWriteTextFormat1 *iface, DWRITE_READING_DIRECTION direction)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%d)\n", This, direction);
|
|
|
|
hr = format_set_readingdirection(&This->format, direction, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed)
|
|
This->recompute = RECOMPUTE_EVERYTHING;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetFlowDirection(IDWriteTextFormat1 *iface, DWRITE_FLOW_DIRECTION direction)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%d)\n", This, direction);
|
|
|
|
hr = format_set_flowdirection(&This->format, direction, &changed);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (changed)
|
|
This->recompute = RECOMPUTE_EVERYTHING;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetIncrementalTabStop(IDWriteTextFormat1 *iface, FLOAT tabstop)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
FIXME("(%p)->(%f): stub\n", This, tabstop);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING const *trimming,
|
|
IDWriteInlineObject *trimming_sign)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
BOOL changed;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
|
|
|
|
hr = format_set_trimming(&This->format, trimming, trimming_sign, &changed);
|
|
|
|
if (changed)
|
|
This->recompute |= RECOMPUTE_LINES_AND_OVERHANGS;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_SetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD method,
|
|
FLOAT height, FLOAT baseline)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
DWRITE_LINE_SPACING spacing;
|
|
|
|
TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
|
|
|
|
spacing = This->format.spacing;
|
|
spacing.method = method;
|
|
spacing.height = height;
|
|
spacing.baseline = baseline;
|
|
return IDWriteTextLayout3_SetLineSpacing(&This->IDWriteTextLayout3_iface, &spacing);
|
|
}
|
|
|
|
static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_layout_GetTextAlignment(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.textalignment;
|
|
}
|
|
|
|
static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_layout_GetParagraphAlignment(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.paralign;
|
|
}
|
|
|
|
static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_layout_GetWordWrapping(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.wrapping;
|
|
}
|
|
|
|
static DWRITE_READING_DIRECTION WINAPI dwritetextformat_layout_GetReadingDirection(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.readingdir;
|
|
}
|
|
|
|
static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_layout_GetFlowDirection(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.flow;
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextformat_layout_GetIncrementalTabStop(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
FIXME("(%p): stub\n", This);
|
|
return 0.0f;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_GetTrimming(IDWriteTextFormat1 *iface, DWRITE_TRIMMING *options,
|
|
IDWriteInlineObject **trimming_sign)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
|
|
|
|
*options = This->format.trimming;
|
|
*trimming_sign = This->format.trimmingsign;
|
|
if (*trimming_sign)
|
|
IDWriteInlineObject_AddRef(*trimming_sign);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_GetLineSpacing(IDWriteTextFormat1 *iface, DWRITE_LINE_SPACING_METHOD *method,
|
|
FLOAT *spacing, FLOAT *baseline)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
|
|
|
|
*method = This->format.spacing.method;
|
|
*spacing = This->format.spacing.height;
|
|
*baseline = This->format.spacing.baseline;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_GetFontCollection(IDWriteTextFormat1 *iface, IDWriteFontCollection **collection)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%p)\n", This, collection);
|
|
|
|
*collection = This->format.collection;
|
|
if (*collection)
|
|
IDWriteFontCollection_AddRef(*collection);
|
|
return S_OK;
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextformat_layout_GetFontFamilyNameLength(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.family_len;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_GetFontFamilyName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%p %u)\n", This, name, size);
|
|
|
|
if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
|
|
strcpyW(name, This->format.family_name);
|
|
return S_OK;
|
|
}
|
|
|
|
static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_layout_GetFontWeight(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.weight;
|
|
}
|
|
|
|
static DWRITE_FONT_STYLE WINAPI dwritetextformat_layout_GetFontStyle(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.style;
|
|
}
|
|
|
|
static DWRITE_FONT_STRETCH WINAPI dwritetextformat_layout_GetFontStretch(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.stretch;
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextformat_layout_GetFontSize(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.fontsize;
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextformat_layout_GetLocaleNameLength(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.locale_len;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_layout_GetLocaleName(IDWriteTextFormat1 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%p %u)\n", This, name, size);
|
|
|
|
if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
|
|
strcpyW(name, This->format.locale);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_layout_SetVerticalGlyphOrientation(IDWriteTextFormat1 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
FIXME("(%p)->(%d): stub\n", This, orientation);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_layout_GetVerticalGlyphOrientation(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
FIXME("(%p): stub\n", This);
|
|
return DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_layout_SetLastLineWrapping(IDWriteTextFormat1 *iface, BOOL lastline_wrapping_enabled)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
|
|
TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
|
|
|
|
This->format.last_line_wrapping = !!lastline_wrapping_enabled;
|
|
return S_OK;
|
|
}
|
|
|
|
static BOOL WINAPI dwritetextformat1_layout_GetLastLineWrapping(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.last_line_wrapping;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_layout_SetOpticalAlignment(IDWriteTextFormat1 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
return format_set_optical_alignment(&This->format, alignment);
|
|
}
|
|
|
|
static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_layout_GetOpticalAlignment(IDWriteTextFormat1 *iface)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.optical_alignment;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_layout_SetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback *fallback)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return IDWriteTextLayout3_SetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_layout_GetFontFallback(IDWriteTextFormat1 *iface, IDWriteFontFallback **fallback)
|
|
{
|
|
struct dwrite_textlayout *This = impl_layout_from_IDWriteTextFormat1(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return IDWriteTextLayout3_GetFontFallback(&This->IDWriteTextLayout3_iface, fallback);
|
|
}
|
|
|
|
static const IDWriteTextFormat1Vtbl dwritetextformat1_layout_vtbl = {
|
|
dwritetextformat_layout_QueryInterface,
|
|
dwritetextformat_layout_AddRef,
|
|
dwritetextformat_layout_Release,
|
|
dwritetextformat_layout_SetTextAlignment,
|
|
dwritetextformat_layout_SetParagraphAlignment,
|
|
dwritetextformat_layout_SetWordWrapping,
|
|
dwritetextformat_layout_SetReadingDirection,
|
|
dwritetextformat_layout_SetFlowDirection,
|
|
dwritetextformat_layout_SetIncrementalTabStop,
|
|
dwritetextformat_layout_SetTrimming,
|
|
dwritetextformat_layout_SetLineSpacing,
|
|
dwritetextformat_layout_GetTextAlignment,
|
|
dwritetextformat_layout_GetParagraphAlignment,
|
|
dwritetextformat_layout_GetWordWrapping,
|
|
dwritetextformat_layout_GetReadingDirection,
|
|
dwritetextformat_layout_GetFlowDirection,
|
|
dwritetextformat_layout_GetIncrementalTabStop,
|
|
dwritetextformat_layout_GetTrimming,
|
|
dwritetextformat_layout_GetLineSpacing,
|
|
dwritetextformat_layout_GetFontCollection,
|
|
dwritetextformat_layout_GetFontFamilyNameLength,
|
|
dwritetextformat_layout_GetFontFamilyName,
|
|
dwritetextformat_layout_GetFontWeight,
|
|
dwritetextformat_layout_GetFontStyle,
|
|
dwritetextformat_layout_GetFontStretch,
|
|
dwritetextformat_layout_GetFontSize,
|
|
dwritetextformat_layout_GetLocaleNameLength,
|
|
dwritetextformat_layout_GetLocaleName,
|
|
dwritetextformat1_layout_SetVerticalGlyphOrientation,
|
|
dwritetextformat1_layout_GetVerticalGlyphOrientation,
|
|
dwritetextformat1_layout_SetLastLineWrapping,
|
|
dwritetextformat1_layout_GetLastLineWrapping,
|
|
dwritetextformat1_layout_SetOpticalAlignment,
|
|
dwritetextformat1_layout_GetOpticalAlignment,
|
|
dwritetextformat1_layout_SetFontFallback,
|
|
dwritetextformat1_layout_GetFontFallback,
|
|
};
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_QueryInterface(IDWriteTextAnalysisSink1 *iface,
|
|
REFIID riid, void **obj)
|
|
{
|
|
if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink1) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) ||
|
|
IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*obj = iface;
|
|
IDWriteTextAnalysisSink1_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
*obj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_sink_AddRef(IDWriteTextAnalysisSink1 *iface)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
|
|
return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_sink_Release(IDWriteTextAnalysisSink1 *iface)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
|
|
return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_SetScriptAnalysis(IDWriteTextAnalysisSink1 *iface,
|
|
UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
|
|
struct layout_run *run;
|
|
|
|
TRACE("[%u,%u) script=%u:%s\n", position, position + length, sa->script, debugstr_sa_script(sa->script));
|
|
|
|
run = alloc_layout_run(LAYOUT_RUN_REGULAR, position);
|
|
if (!run)
|
|
return E_OUTOFMEMORY;
|
|
|
|
run->u.regular.descr.string = &layout->str[position];
|
|
run->u.regular.descr.stringLength = length;
|
|
run->u.regular.descr.textPosition = position;
|
|
run->u.regular.sa = *sa;
|
|
list_add_tail(&layout->runs, &run->entry);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_SetLineBreakpoints(IDWriteTextAnalysisSink1 *iface,
|
|
UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
|
|
|
|
if (position + length > layout->len)
|
|
return E_FAIL;
|
|
|
|
memcpy(&layout->nominal_breakpoints[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_SetBidiLevel(IDWriteTextAnalysisSink1 *iface, UINT32 position,
|
|
UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSink1(iface);
|
|
struct layout_run *cur_run;
|
|
|
|
TRACE("[%u,%u) %u %u\n", position, position + length, explicitLevel, resolvedLevel);
|
|
|
|
LIST_FOR_EACH_ENTRY(cur_run, &layout->runs, struct layout_run, entry) {
|
|
struct regular_layout_run *cur = &cur_run->u.regular;
|
|
struct layout_run *run;
|
|
|
|
if (cur_run->kind == LAYOUT_RUN_INLINE)
|
|
continue;
|
|
|
|
/* FIXME: levels are reported in a natural forward direction, so start loop from a run we ended on */
|
|
if (position < cur->descr.textPosition || position >= cur->descr.textPosition + cur->descr.stringLength)
|
|
continue;
|
|
|
|
/* full hit - just set run level */
|
|
if (cur->descr.textPosition == position && cur->descr.stringLength == length) {
|
|
cur->run.bidiLevel = resolvedLevel;
|
|
break;
|
|
}
|
|
|
|
/* current run is fully covered, move to next one */
|
|
if (cur->descr.textPosition == position && cur->descr.stringLength < length) {
|
|
cur->run.bidiLevel = resolvedLevel;
|
|
position += cur->descr.stringLength;
|
|
length -= cur->descr.stringLength;
|
|
continue;
|
|
}
|
|
|
|
/* all fully covered runs are processed at this point, reuse existing run for remaining
|
|
reported bidi range and add another run for the rest of original one */
|
|
|
|
run = alloc_layout_run(LAYOUT_RUN_REGULAR, position + length);
|
|
if (!run)
|
|
return E_OUTOFMEMORY;
|
|
|
|
*run = *cur_run;
|
|
run->u.regular.descr.textPosition = position + length;
|
|
run->u.regular.descr.stringLength = cur->descr.stringLength - length;
|
|
run->u.regular.descr.string = &layout->str[position + length];
|
|
|
|
/* reduce existing run */
|
|
cur->run.bidiLevel = resolvedLevel;
|
|
cur->descr.stringLength = length;
|
|
|
|
list_add_after(&cur_run->entry, &run->entry);
|
|
break;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_SetNumberSubstitution(IDWriteTextAnalysisSink1 *iface,
|
|
UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_sink_SetGlyphOrientation(IDWriteTextAnalysisSink1 *iface,
|
|
UINT32 position, UINT32 length, DWRITE_GLYPH_ORIENTATION_ANGLE angle, UINT8 adjusted_bidi_level,
|
|
BOOL is_sideways, BOOL is_rtl)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IDWriteTextAnalysisSink1Vtbl dwritetextlayoutsinkvtbl = {
|
|
dwritetextlayout_sink_QueryInterface,
|
|
dwritetextlayout_sink_AddRef,
|
|
dwritetextlayout_sink_Release,
|
|
dwritetextlayout_sink_SetScriptAnalysis,
|
|
dwritetextlayout_sink_SetLineBreakpoints,
|
|
dwritetextlayout_sink_SetBidiLevel,
|
|
dwritetextlayout_sink_SetNumberSubstitution,
|
|
dwritetextlayout_sink_SetGlyphOrientation
|
|
};
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_QueryInterface(IDWriteTextAnalysisSource1 *iface,
|
|
REFIID riid, void **obj)
|
|
{
|
|
if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource1) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) ||
|
|
IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*obj = iface;
|
|
IDWriteTextAnalysisSource1_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
*obj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_source_AddRef(IDWriteTextAnalysisSource1 *iface)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
return IDWriteTextLayout3_AddRef(&layout->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextlayout_source_Release(IDWriteTextAnalysisSource1 *iface)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
return IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_GetTextAtPosition(IDWriteTextAnalysisSource1 *iface,
|
|
UINT32 position, WCHAR const** text, UINT32* text_len)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
|
|
|
|
if (position < layout->len) {
|
|
*text = &layout->str[position];
|
|
*text_len = layout->len - position;
|
|
}
|
|
else {
|
|
*text = NULL;
|
|
*text_len = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_GetTextBeforePosition(IDWriteTextAnalysisSource1 *iface,
|
|
UINT32 position, WCHAR const** text, UINT32* text_len)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
|
|
TRACE("(%p)->(%u %p %p)\n", layout, position, text, text_len);
|
|
|
|
if (position > 0 && position < layout->len) {
|
|
*text = layout->str;
|
|
*text_len = position;
|
|
}
|
|
else {
|
|
*text = NULL;
|
|
*text_len = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static DWRITE_READING_DIRECTION WINAPI dwritetextlayout_source_GetParagraphReadingDirection(IDWriteTextAnalysisSource1 *iface)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
return IDWriteTextLayout3_GetReadingDirection(&layout->IDWriteTextLayout3_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_GetLocaleName(IDWriteTextAnalysisSource1 *iface,
|
|
UINT32 position, UINT32* text_len, WCHAR const** locale)
|
|
{
|
|
struct dwrite_textlayout *layout = impl_from_IDWriteTextAnalysisSource1(iface);
|
|
struct layout_range *range = get_layout_range_by_pos(layout, position);
|
|
|
|
if (position < layout->len) {
|
|
struct layout_range *next;
|
|
|
|
*locale = range->locale;
|
|
*text_len = range->h.range.length - position;
|
|
|
|
next = LIST_ENTRY(list_next(&layout->ranges, &range->h.entry), struct layout_range, h.entry);
|
|
while (next && next->h.range.startPosition < layout->len && !strcmpW(range->locale, next->locale)) {
|
|
*text_len += next->h.range.length;
|
|
next = LIST_ENTRY(list_next(&layout->ranges, &next->h.entry), struct layout_range, h.entry);
|
|
}
|
|
|
|
*text_len = min(*text_len, layout->len - position);
|
|
}
|
|
else {
|
|
*locale = NULL;
|
|
*text_len = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_GetNumberSubstitution(IDWriteTextAnalysisSource1 *iface,
|
|
UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
|
|
{
|
|
FIXME("%u %p %p: stub\n", position, text_len, substitution);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextlayout_source_GetVerticalGlyphOrientation(IDWriteTextAnalysisSource1 *iface,
|
|
UINT32 position, UINT32 *length, DWRITE_VERTICAL_GLYPH_ORIENTATION *orientation, UINT8 *bidi_level)
|
|
{
|
|
FIXME("%u %p %p %p: stub\n", position, length, orientation, bidi_level);
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
static const IDWriteTextAnalysisSource1Vtbl dwritetextlayoutsourcevtbl = {
|
|
dwritetextlayout_source_QueryInterface,
|
|
dwritetextlayout_source_AddRef,
|
|
dwritetextlayout_source_Release,
|
|
dwritetextlayout_source_GetTextAtPosition,
|
|
dwritetextlayout_source_GetTextBeforePosition,
|
|
dwritetextlayout_source_GetParagraphReadingDirection,
|
|
dwritetextlayout_source_GetLocaleName,
|
|
dwritetextlayout_source_GetNumberSubstitution,
|
|
dwritetextlayout_source_GetVerticalGlyphOrientation
|
|
};
|
|
|
|
static HRESULT layout_format_from_textformat(struct dwrite_textlayout *layout, IDWriteTextFormat *format)
|
|
{
|
|
struct dwrite_textformat *textformat;
|
|
IDWriteTextFormat1 *format1;
|
|
UINT32 len;
|
|
HRESULT hr;
|
|
|
|
if ((textformat = unsafe_impl_from_IDWriteTextFormat(format))) {
|
|
layout->format = textformat->format;
|
|
|
|
layout->format.locale = heap_strdupW(textformat->format.locale);
|
|
layout->format.family_name = heap_strdupW(textformat->format.family_name);
|
|
if (!layout->format.locale || !layout->format.family_name)
|
|
{
|
|
heap_free(layout->format.locale);
|
|
heap_free(layout->format.family_name);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (layout->format.trimmingsign)
|
|
IDWriteInlineObject_AddRef(layout->format.trimmingsign);
|
|
if (layout->format.collection)
|
|
IDWriteFontCollection_AddRef(layout->format.collection);
|
|
if (layout->format.fallback)
|
|
IDWriteFontFallback_AddRef(layout->format.fallback);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
layout->format.weight = IDWriteTextFormat_GetFontWeight(format);
|
|
layout->format.style = IDWriteTextFormat_GetFontStyle(format);
|
|
layout->format.stretch = IDWriteTextFormat_GetFontStretch(format);
|
|
layout->format.fontsize= IDWriteTextFormat_GetFontSize(format);
|
|
layout->format.textalignment = IDWriteTextFormat_GetTextAlignment(format);
|
|
layout->format.paralign = IDWriteTextFormat_GetParagraphAlignment(format);
|
|
layout->format.wrapping = IDWriteTextFormat_GetWordWrapping(format);
|
|
layout->format.readingdir = IDWriteTextFormat_GetReadingDirection(format);
|
|
layout->format.flow = IDWriteTextFormat_GetFlowDirection(format);
|
|
layout->format.fallback = NULL;
|
|
layout->format.spacing.leadingBefore = 0.0f;
|
|
layout->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
|
|
hr = IDWriteTextFormat_GetLineSpacing(format, &layout->format.spacing.method,
|
|
&layout->format.spacing.height, &layout->format.spacing.baseline);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = IDWriteTextFormat_GetTrimming(format, &layout->format.trimming, &layout->format.trimmingsign);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
/* locale name and length */
|
|
len = IDWriteTextFormat_GetLocaleNameLength(format);
|
|
layout->format.locale = heap_alloc((len+1)*sizeof(WCHAR));
|
|
if (!layout->format.locale)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = IDWriteTextFormat_GetLocaleName(format, layout->format.locale, len+1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
layout->format.locale_len = len;
|
|
|
|
/* font family name and length */
|
|
len = IDWriteTextFormat_GetFontFamilyNameLength(format);
|
|
layout->format.family_name = heap_alloc((len+1)*sizeof(WCHAR));
|
|
if (!layout->format.family_name)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = IDWriteTextFormat_GetFontFamilyName(format, layout->format.family_name, len+1);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
layout->format.family_len = len;
|
|
|
|
hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
|
|
if (hr == S_OK) {
|
|
IDWriteTextFormat2 *format2;
|
|
|
|
layout->format.vertical_orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
|
|
layout->format.optical_alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
|
|
IDWriteTextFormat1_GetFontFallback(format1, &layout->format.fallback);
|
|
|
|
if (IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
|
|
IDWriteTextFormat2_GetLineSpacing(format2, &layout->format.spacing);
|
|
IDWriteTextFormat2_Release(format2);
|
|
}
|
|
|
|
IDWriteTextFormat1_Release(format1);
|
|
}
|
|
else {
|
|
layout->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
|
|
layout->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
|
|
}
|
|
|
|
return IDWriteTextFormat_GetFontCollection(format, &layout->format.collection);
|
|
}
|
|
|
|
static HRESULT init_textlayout(const struct textlayout_desc *desc, struct dwrite_textlayout *layout)
|
|
{
|
|
struct layout_range_header *range, *strike, *underline, *effect, *spacing, *typography;
|
|
static const DWRITE_TEXT_RANGE r = { 0, ~0u };
|
|
HRESULT hr;
|
|
|
|
layout->IDWriteTextLayout3_iface.lpVtbl = &dwritetextlayoutvtbl;
|
|
layout->IDWriteTextFormat1_iface.lpVtbl = &dwritetextformat1_layout_vtbl;
|
|
layout->IDWriteTextAnalysisSink1_iface.lpVtbl = &dwritetextlayoutsinkvtbl;
|
|
layout->IDWriteTextAnalysisSource1_iface.lpVtbl = &dwritetextlayoutsourcevtbl;
|
|
layout->ref = 1;
|
|
layout->len = desc->length;
|
|
layout->recompute = RECOMPUTE_EVERYTHING;
|
|
layout->nominal_breakpoints = NULL;
|
|
layout->actual_breakpoints = NULL;
|
|
layout->cluster_count = 0;
|
|
layout->clustermetrics = NULL;
|
|
layout->clusters = NULL;
|
|
layout->linemetrics = NULL;
|
|
layout->lines = NULL;
|
|
layout->line_alloc = 0;
|
|
layout->minwidth = 0.0f;
|
|
list_init(&layout->eruns);
|
|
list_init(&layout->inlineobjects);
|
|
list_init(&layout->underlines);
|
|
list_init(&layout->strikethrough);
|
|
list_init(&layout->runs);
|
|
list_init(&layout->ranges);
|
|
list_init(&layout->strike_ranges);
|
|
list_init(&layout->underline_ranges);
|
|
list_init(&layout->effects);
|
|
list_init(&layout->spacing);
|
|
list_init(&layout->typographies);
|
|
memset(&layout->format, 0, sizeof(layout->format));
|
|
memset(&layout->metrics, 0, sizeof(layout->metrics));
|
|
layout->metrics.layoutWidth = desc->max_width;
|
|
layout->metrics.layoutHeight = desc->max_height;
|
|
layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
|
|
|
|
layout->ppdip = 0.0f;
|
|
memset(&layout->transform, 0, sizeof(layout->transform));
|
|
|
|
layout->str = heap_strdupnW(desc->string, desc->length);
|
|
if (desc->length && !layout->str) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
hr = layout_format_from_textformat(layout, desc->format);
|
|
if (FAILED(hr))
|
|
goto fail;
|
|
|
|
range = alloc_layout_range(layout, &r, LAYOUT_RANGE_REGULAR);
|
|
strike = alloc_layout_range(layout, &r, LAYOUT_RANGE_STRIKETHROUGH);
|
|
underline = alloc_layout_range(layout, &r, LAYOUT_RANGE_UNDERLINE);
|
|
effect = alloc_layout_range(layout, &r, LAYOUT_RANGE_EFFECT);
|
|
spacing = alloc_layout_range(layout, &r, LAYOUT_RANGE_SPACING);
|
|
typography = alloc_layout_range(layout, &r, LAYOUT_RANGE_TYPOGRAPHY);
|
|
if (!range || !strike || !effect || !spacing || !typography || !underline) {
|
|
free_layout_range(range);
|
|
free_layout_range(strike);
|
|
free_layout_range(underline);
|
|
free_layout_range(effect);
|
|
free_layout_range(spacing);
|
|
free_layout_range(typography);
|
|
hr = E_OUTOFMEMORY;
|
|
goto fail;
|
|
}
|
|
|
|
if (desc->is_gdi_compatible)
|
|
layout->measuringmode = desc->use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
|
|
else
|
|
layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
|
|
layout->ppdip = desc->ppdip;
|
|
layout->transform = desc->transform ? *desc->transform : identity;
|
|
|
|
layout->factory = desc->factory;
|
|
IDWriteFactory5_AddRef(layout->factory);
|
|
list_add_head(&layout->ranges, &range->entry);
|
|
list_add_head(&layout->strike_ranges, &strike->entry);
|
|
list_add_head(&layout->underline_ranges, &underline->entry);
|
|
list_add_head(&layout->effects, &effect->entry);
|
|
list_add_head(&layout->spacing, &spacing->entry);
|
|
list_add_head(&layout->typographies, &typography->entry);
|
|
return S_OK;
|
|
|
|
fail:
|
|
IDWriteTextLayout3_Release(&layout->IDWriteTextLayout3_iface);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT create_textlayout(const struct textlayout_desc *desc, IDWriteTextLayout **ret)
|
|
{
|
|
struct dwrite_textlayout *layout;
|
|
HRESULT hr;
|
|
|
|
*ret = NULL;
|
|
|
|
if (!desc->format || !desc->string)
|
|
return E_INVALIDARG;
|
|
|
|
layout = heap_alloc(sizeof(struct dwrite_textlayout));
|
|
if (!layout) return E_OUTOFMEMORY;
|
|
|
|
hr = init_textlayout(desc, layout);
|
|
if (hr == S_OK)
|
|
*ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout3_iface;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetrimmingsign_QueryInterface(IDWriteInlineObject *iface, REFIID riid, void **obj)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteInlineObject)) {
|
|
*obj = iface;
|
|
IDWriteInlineObject_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
*obj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetrimmingsign_AddRef(IDWriteInlineObject *iface)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetrimmingsign_Release(IDWriteInlineObject *iface)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
|
|
if (!ref) {
|
|
IDWriteTextLayout_Release(This->layout);
|
|
heap_free(This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetrimmingsign_Draw(IDWriteInlineObject *iface, void *context, IDWriteTextRenderer *renderer,
|
|
FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *effect)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
DWRITE_LINE_METRICS line;
|
|
UINT32 line_count;
|
|
|
|
TRACE("(%p)->(%p %p %.2f %.2f %d %d %p)\n", This, context, renderer, originX, originY,
|
|
is_sideways, is_rtl, effect);
|
|
|
|
IDWriteTextLayout_GetLineMetrics(This->layout, &line, 1, &line_count);
|
|
return IDWriteTextLayout_Draw(This->layout, context, renderer, originX, originY - line.baseline);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetrimmingsign_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *ret)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
DWRITE_TEXT_METRICS metrics;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p)->(%p)\n", This, ret);
|
|
|
|
hr = IDWriteTextLayout_GetMetrics(This->layout, &metrics);
|
|
if (FAILED(hr)) {
|
|
memset(ret, 0, sizeof(*ret));
|
|
return hr;
|
|
}
|
|
|
|
ret->width = metrics.width;
|
|
ret->height = 0.0f;
|
|
ret->baseline = 0.0f;
|
|
ret->supportsSideways = FALSE;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetrimmingsign_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
TRACE("(%p)->(%p)\n", This, overhangs);
|
|
return IDWriteTextLayout_GetOverhangMetrics(This->layout, overhangs);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetrimmingsign_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
|
|
DWRITE_BREAK_CONDITION *after)
|
|
{
|
|
struct dwrite_trimmingsign *This = impl_from_IDWriteInlineObject(iface);
|
|
|
|
TRACE("(%p)->(%p %p)\n", This, before, after);
|
|
|
|
*before = *after = DWRITE_BREAK_CONDITION_NEUTRAL;
|
|
return S_OK;
|
|
}
|
|
|
|
static const IDWriteInlineObjectVtbl dwritetrimmingsignvtbl = {
|
|
dwritetrimmingsign_QueryInterface,
|
|
dwritetrimmingsign_AddRef,
|
|
dwritetrimmingsign_Release,
|
|
dwritetrimmingsign_Draw,
|
|
dwritetrimmingsign_GetMetrics,
|
|
dwritetrimmingsign_GetOverhangMetrics,
|
|
dwritetrimmingsign_GetBreakConditions
|
|
};
|
|
|
|
static inline BOOL is_reading_direction_horz(DWRITE_READING_DIRECTION direction)
|
|
{
|
|
return (direction == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT) ||
|
|
(direction == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
|
|
}
|
|
|
|
static inline BOOL is_reading_direction_vert(DWRITE_READING_DIRECTION direction)
|
|
{
|
|
return (direction == DWRITE_READING_DIRECTION_TOP_TO_BOTTOM) ||
|
|
(direction == DWRITE_READING_DIRECTION_BOTTOM_TO_TOP);
|
|
}
|
|
|
|
static inline BOOL is_flow_direction_horz(DWRITE_FLOW_DIRECTION direction)
|
|
{
|
|
return (direction == DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT) ||
|
|
(direction == DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT);
|
|
}
|
|
|
|
static inline BOOL is_flow_direction_vert(DWRITE_FLOW_DIRECTION direction)
|
|
{
|
|
return (direction == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM) ||
|
|
(direction == DWRITE_FLOW_DIRECTION_BOTTOM_TO_TOP);
|
|
}
|
|
|
|
HRESULT create_trimmingsign(IDWriteFactory5 *factory, IDWriteTextFormat *format, IDWriteInlineObject **sign)
|
|
{
|
|
static const WCHAR ellipsisW = 0x2026;
|
|
struct dwrite_trimmingsign *This;
|
|
DWRITE_READING_DIRECTION reading;
|
|
DWRITE_FLOW_DIRECTION flow;
|
|
HRESULT hr;
|
|
|
|
*sign = NULL;
|
|
|
|
/* Validate reading/flow direction here, layout creation won't complain about
|
|
invalid combinations. */
|
|
reading = IDWriteTextFormat_GetReadingDirection(format);
|
|
flow = IDWriteTextFormat_GetFlowDirection(format);
|
|
|
|
if ((is_reading_direction_horz(reading) && is_flow_direction_horz(flow)) ||
|
|
(is_reading_direction_vert(reading) && is_flow_direction_vert(flow)))
|
|
return DWRITE_E_FLOWDIRECTIONCONFLICTS;
|
|
|
|
This = heap_alloc(sizeof(*This));
|
|
if (!This)
|
|
return E_OUTOFMEMORY;
|
|
|
|
This->IDWriteInlineObject_iface.lpVtbl = &dwritetrimmingsignvtbl;
|
|
This->ref = 1;
|
|
|
|
hr = IDWriteFactory5_CreateTextLayout(factory, &ellipsisW, 1, format, 0.0f, 0.0f, &This->layout);
|
|
if (FAILED(hr)) {
|
|
heap_free(This);
|
|
return hr;
|
|
}
|
|
|
|
IDWriteTextLayout_SetWordWrapping(This->layout, DWRITE_WORD_WRAPPING_NO_WRAP);
|
|
IDWriteTextLayout_SetParagraphAlignment(This->layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
|
|
IDWriteTextLayout_SetTextAlignment(This->layout, DWRITE_TEXT_ALIGNMENT_LEADING);
|
|
|
|
*sign = &This->IDWriteInlineObject_iface;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_QueryInterface(IDWriteTextFormat2 *iface, REFIID riid, void **obj)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
|
|
|
|
if (IsEqualIID(riid, &IID_IDWriteTextFormat2) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextFormat1) ||
|
|
IsEqualIID(riid, &IID_IDWriteTextFormat) ||
|
|
IsEqualIID(riid, &IID_IUnknown))
|
|
{
|
|
*obj = iface;
|
|
IDWriteTextFormat2_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
*obj = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextformat_AddRef(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetextformat_Release(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p)->(%d)\n", This, ref);
|
|
|
|
if (!ref)
|
|
{
|
|
release_format_data(&This->format);
|
|
heap_free(This);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetTextAlignment(IDWriteTextFormat2 *iface, DWRITE_TEXT_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
return format_set_textalignment(&This->format, alignment, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetParagraphAlignment(IDWriteTextFormat2 *iface, DWRITE_PARAGRAPH_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
return format_set_paralignment(&This->format, alignment, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetWordWrapping(IDWriteTextFormat2 *iface, DWRITE_WORD_WRAPPING wrapping)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, wrapping);
|
|
return format_set_wordwrapping(&This->format, wrapping, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetReadingDirection(IDWriteTextFormat2 *iface, DWRITE_READING_DIRECTION direction)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, direction);
|
|
return format_set_readingdirection(&This->format, direction, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetFlowDirection(IDWriteTextFormat2 *iface, DWRITE_FLOW_DIRECTION direction)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, direction);
|
|
return format_set_flowdirection(&This->format, direction, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetIncrementalTabStop(IDWriteTextFormat2 *iface, FLOAT tabstop)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
FIXME("(%p)->(%f): stub\n", This, tabstop);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING const *trimming,
|
|
IDWriteInlineObject *trimming_sign)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p %p)\n", This, trimming, trimming_sign);
|
|
return format_set_trimming(&This->format, trimming, trimming_sign, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD method,
|
|
FLOAT height, FLOAT baseline)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
DWRITE_LINE_SPACING spacing;
|
|
|
|
TRACE("(%p)->(%d %f %f)\n", This, method, height, baseline);
|
|
|
|
spacing = This->format.spacing;
|
|
spacing.method = method;
|
|
spacing.height = height;
|
|
spacing.baseline = baseline;
|
|
|
|
return format_set_linespacing(&This->format, &spacing, NULL);
|
|
}
|
|
|
|
static DWRITE_TEXT_ALIGNMENT WINAPI dwritetextformat_GetTextAlignment(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.textalignment;
|
|
}
|
|
|
|
static DWRITE_PARAGRAPH_ALIGNMENT WINAPI dwritetextformat_GetParagraphAlignment(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.paralign;
|
|
}
|
|
|
|
static DWRITE_WORD_WRAPPING WINAPI dwritetextformat_GetWordWrapping(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.wrapping;
|
|
}
|
|
|
|
static DWRITE_READING_DIRECTION WINAPI dwritetextformat_GetReadingDirection(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.readingdir;
|
|
}
|
|
|
|
static DWRITE_FLOW_DIRECTION WINAPI dwritetextformat_GetFlowDirection(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.flow;
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextformat_GetIncrementalTabStop(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
FIXME("(%p): stub\n", This);
|
|
return 0.0f;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_GetTrimming(IDWriteTextFormat2 *iface, DWRITE_TRIMMING *options,
|
|
IDWriteInlineObject **trimming_sign)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p %p)\n", This, options, trimming_sign);
|
|
|
|
*options = This->format.trimming;
|
|
if ((*trimming_sign = This->format.trimmingsign))
|
|
IDWriteInlineObject_AddRef(*trimming_sign);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING_METHOD *method,
|
|
FLOAT *spacing, FLOAT *baseline)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p %p %p)\n", This, method, spacing, baseline);
|
|
|
|
*method = This->format.spacing.method;
|
|
*spacing = This->format.spacing.height;
|
|
*baseline = This->format.spacing.baseline;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_GetFontCollection(IDWriteTextFormat2 *iface, IDWriteFontCollection **collection)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%p)\n", This, collection);
|
|
|
|
*collection = This->format.collection;
|
|
IDWriteFontCollection_AddRef(*collection);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextformat_GetFontFamilyNameLength(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.family_len;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_GetFontFamilyName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%p %u)\n", This, name, size);
|
|
|
|
if (size <= This->format.family_len) return E_NOT_SUFFICIENT_BUFFER;
|
|
strcpyW(name, This->format.family_name);
|
|
return S_OK;
|
|
}
|
|
|
|
static DWRITE_FONT_WEIGHT WINAPI dwritetextformat_GetFontWeight(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.weight;
|
|
}
|
|
|
|
static DWRITE_FONT_STYLE WINAPI dwritetextformat_GetFontStyle(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.style;
|
|
}
|
|
|
|
static DWRITE_FONT_STRETCH WINAPI dwritetextformat_GetFontStretch(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.stretch;
|
|
}
|
|
|
|
static FLOAT WINAPI dwritetextformat_GetFontSize(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.fontsize;
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetextformat_GetLocaleNameLength(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.locale_len;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat_GetLocaleName(IDWriteTextFormat2 *iface, WCHAR *name, UINT32 size)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%p %u)\n", This, name, size);
|
|
|
|
if (size <= This->format.locale_len) return E_NOT_SUFFICIENT_BUFFER;
|
|
strcpyW(name, This->format.locale);
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_SetVerticalGlyphOrientation(IDWriteTextFormat2 *iface, DWRITE_VERTICAL_GLYPH_ORIENTATION orientation)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%d)\n", This, orientation);
|
|
|
|
if ((UINT32)orientation > DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED)
|
|
return E_INVALIDARG;
|
|
|
|
This->format.vertical_orientation = orientation;
|
|
return S_OK;
|
|
}
|
|
|
|
static DWRITE_VERTICAL_GLYPH_ORIENTATION WINAPI dwritetextformat1_GetVerticalGlyphOrientation(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.vertical_orientation;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_SetLastLineWrapping(IDWriteTextFormat2 *iface, BOOL lastline_wrapping_enabled)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%d)\n", This, lastline_wrapping_enabled);
|
|
|
|
This->format.last_line_wrapping = !!lastline_wrapping_enabled;
|
|
return S_OK;
|
|
}
|
|
|
|
static BOOL WINAPI dwritetextformat1_GetLastLineWrapping(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.last_line_wrapping;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_SetOpticalAlignment(IDWriteTextFormat2 *iface, DWRITE_OPTICAL_ALIGNMENT alignment)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%d)\n", This, alignment);
|
|
return format_set_optical_alignment(&This->format, alignment);
|
|
}
|
|
|
|
static DWRITE_OPTICAL_ALIGNMENT WINAPI dwritetextformat1_GetOpticalAlignment(IDWriteTextFormat2 *iface)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)\n", This);
|
|
return This->format.optical_alignment;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_SetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback *fallback)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return set_fontfallback_for_format(&This->format, fallback);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat1_GetFontFallback(IDWriteTextFormat2 *iface, IDWriteFontFallback **fallback)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p)\n", This, fallback);
|
|
return get_fontfallback_from_format(&This->format, fallback);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat2_SetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING const *spacing)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
TRACE("(%p)->(%p)\n", This, spacing);
|
|
return format_set_linespacing(&This->format, spacing, NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetextformat2_GetLineSpacing(IDWriteTextFormat2 *iface, DWRITE_LINE_SPACING *spacing)
|
|
{
|
|
struct dwrite_textformat *This = impl_from_IDWriteTextFormat2(iface);
|
|
|
|
TRACE("(%p)->(%p)\n", This, spacing);
|
|
|
|
*spacing = This->format.spacing;
|
|
return S_OK;
|
|
}
|
|
|
|
static const IDWriteTextFormat2Vtbl dwritetextformatvtbl = {
|
|
dwritetextformat_QueryInterface,
|
|
dwritetextformat_AddRef,
|
|
dwritetextformat_Release,
|
|
dwritetextformat_SetTextAlignment,
|
|
dwritetextformat_SetParagraphAlignment,
|
|
dwritetextformat_SetWordWrapping,
|
|
dwritetextformat_SetReadingDirection,
|
|
dwritetextformat_SetFlowDirection,
|
|
dwritetextformat_SetIncrementalTabStop,
|
|
dwritetextformat_SetTrimming,
|
|
dwritetextformat_SetLineSpacing,
|
|
dwritetextformat_GetTextAlignment,
|
|
dwritetextformat_GetParagraphAlignment,
|
|
dwritetextformat_GetWordWrapping,
|
|
dwritetextformat_GetReadingDirection,
|
|
dwritetextformat_GetFlowDirection,
|
|
dwritetextformat_GetIncrementalTabStop,
|
|
dwritetextformat_GetTrimming,
|
|
dwritetextformat_GetLineSpacing,
|
|
dwritetextformat_GetFontCollection,
|
|
dwritetextformat_GetFontFamilyNameLength,
|
|
dwritetextformat_GetFontFamilyName,
|
|
dwritetextformat_GetFontWeight,
|
|
dwritetextformat_GetFontStyle,
|
|
dwritetextformat_GetFontStretch,
|
|
dwritetextformat_GetFontSize,
|
|
dwritetextformat_GetLocaleNameLength,
|
|
dwritetextformat_GetLocaleName,
|
|
dwritetextformat1_SetVerticalGlyphOrientation,
|
|
dwritetextformat1_GetVerticalGlyphOrientation,
|
|
dwritetextformat1_SetLastLineWrapping,
|
|
dwritetextformat1_GetLastLineWrapping,
|
|
dwritetextformat1_SetOpticalAlignment,
|
|
dwritetextformat1_GetOpticalAlignment,
|
|
dwritetextformat1_SetFontFallback,
|
|
dwritetextformat1_GetFontFallback,
|
|
dwritetextformat2_SetLineSpacing,
|
|
dwritetextformat2_GetLineSpacing
|
|
};
|
|
|
|
static struct dwrite_textformat *unsafe_impl_from_IDWriteTextFormat(IDWriteTextFormat *iface)
|
|
{
|
|
return (iface->lpVtbl == (IDWriteTextFormatVtbl*)&dwritetextformatvtbl) ?
|
|
CONTAINING_RECORD(iface, struct dwrite_textformat, IDWriteTextFormat2_iface) : NULL;
|
|
}
|
|
|
|
HRESULT create_textformat(const WCHAR *family_name, IDWriteFontCollection *collection, DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style,
|
|
DWRITE_FONT_STRETCH stretch, FLOAT size, const WCHAR *locale, IDWriteTextFormat **format)
|
|
{
|
|
struct dwrite_textformat *This;
|
|
|
|
*format = NULL;
|
|
|
|
if (size <= 0.0f)
|
|
return E_INVALIDARG;
|
|
|
|
if (((UINT32)weight > DWRITE_FONT_WEIGHT_ULTRA_BLACK) ||
|
|
((UINT32)stretch > DWRITE_FONT_STRETCH_ULTRA_EXPANDED) ||
|
|
((UINT32)style > DWRITE_FONT_STYLE_ITALIC))
|
|
return E_INVALIDARG;
|
|
|
|
This = heap_alloc(sizeof(struct dwrite_textformat));
|
|
if (!This) return E_OUTOFMEMORY;
|
|
|
|
This->IDWriteTextFormat2_iface.lpVtbl = &dwritetextformatvtbl;
|
|
This->ref = 1;
|
|
This->format.family_name = heap_strdupW(family_name);
|
|
This->format.family_len = strlenW(family_name);
|
|
This->format.locale = heap_strdupW(locale);
|
|
This->format.locale_len = strlenW(locale);
|
|
/* force locale name to lower case, layout will inherit this modified value */
|
|
strlwrW(This->format.locale);
|
|
This->format.weight = weight;
|
|
This->format.style = style;
|
|
This->format.fontsize = size;
|
|
This->format.stretch = stretch;
|
|
This->format.textalignment = DWRITE_TEXT_ALIGNMENT_LEADING;
|
|
This->format.optical_alignment = DWRITE_OPTICAL_ALIGNMENT_NONE;
|
|
This->format.paralign = DWRITE_PARAGRAPH_ALIGNMENT_NEAR;
|
|
This->format.wrapping = DWRITE_WORD_WRAPPING_WRAP;
|
|
This->format.last_line_wrapping = TRUE;
|
|
This->format.readingdir = DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
|
|
This->format.flow = DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM;
|
|
This->format.spacing.method = DWRITE_LINE_SPACING_METHOD_DEFAULT;
|
|
This->format.spacing.height = 0.0f;
|
|
This->format.spacing.baseline = 0.0f;
|
|
This->format.spacing.leadingBefore = 0.0f;
|
|
This->format.spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_DEFAULT;
|
|
This->format.vertical_orientation = DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT;
|
|
This->format.trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
|
|
This->format.trimming.delimiter = 0;
|
|
This->format.trimming.delimiterCount = 0;
|
|
This->format.trimmingsign = NULL;
|
|
This->format.collection = collection;
|
|
This->format.fallback = NULL;
|
|
IDWriteFontCollection_AddRef(collection);
|
|
|
|
*format = (IDWriteTextFormat*)&This->IDWriteTextFormat2_iface;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetypography_QueryInterface(IDWriteTypography *iface, REFIID riid, void **obj)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
|
|
TRACE("(%p)->(%s %p)\n", typography, debugstr_guid(riid), obj);
|
|
|
|
if (IsEqualIID(riid, &IID_IDWriteTypography) || IsEqualIID(riid, &IID_IUnknown)) {
|
|
*obj = iface;
|
|
IDWriteTypography_AddRef(iface);
|
|
return S_OK;
|
|
}
|
|
|
|
WARN("%s not implemented.\n", debugstr_guid(riid));
|
|
|
|
*obj = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetypography_AddRef(IDWriteTypography *iface)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
ULONG ref = InterlockedIncrement(&typography->ref);
|
|
TRACE("(%p)->(%d)\n", typography, ref);
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI dwritetypography_Release(IDWriteTypography *iface)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
ULONG ref = InterlockedDecrement(&typography->ref);
|
|
|
|
TRACE("(%p)->(%d)\n", typography, ref);
|
|
|
|
if (!ref) {
|
|
heap_free(typography->features);
|
|
heap_free(typography);
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetypography_AddFontFeature(IDWriteTypography *iface, DWRITE_FONT_FEATURE feature)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
|
|
TRACE("(%p)->(%x %u)\n", typography, feature.nameTag, feature.parameter);
|
|
|
|
if (typography->count == typography->allocated) {
|
|
DWRITE_FONT_FEATURE *ptr = heap_realloc(typography->features, 2*typography->allocated*sizeof(DWRITE_FONT_FEATURE));
|
|
if (!ptr)
|
|
return E_OUTOFMEMORY;
|
|
|
|
typography->features = ptr;
|
|
typography->allocated *= 2;
|
|
}
|
|
|
|
typography->features[typography->count++] = feature;
|
|
return S_OK;
|
|
}
|
|
|
|
static UINT32 WINAPI dwritetypography_GetFontFeatureCount(IDWriteTypography *iface)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
TRACE("(%p)\n", typography);
|
|
return typography->count;
|
|
}
|
|
|
|
static HRESULT WINAPI dwritetypography_GetFontFeature(IDWriteTypography *iface, UINT32 index, DWRITE_FONT_FEATURE *feature)
|
|
{
|
|
struct dwrite_typography *typography = impl_from_IDWriteTypography(iface);
|
|
|
|
TRACE("(%p)->(%u %p)\n", typography, index, feature);
|
|
|
|
if (index >= typography->count)
|
|
return E_INVALIDARG;
|
|
|
|
*feature = typography->features[index];
|
|
return S_OK;
|
|
}
|
|
|
|
static const IDWriteTypographyVtbl dwritetypographyvtbl = {
|
|
dwritetypography_QueryInterface,
|
|
dwritetypography_AddRef,
|
|
dwritetypography_Release,
|
|
dwritetypography_AddFontFeature,
|
|
dwritetypography_GetFontFeatureCount,
|
|
dwritetypography_GetFontFeature
|
|
};
|
|
|
|
HRESULT create_typography(IDWriteTypography **ret)
|
|
{
|
|
struct dwrite_typography *typography;
|
|
|
|
*ret = NULL;
|
|
|
|
typography = heap_alloc(sizeof(*typography));
|
|
if (!typography)
|
|
return E_OUTOFMEMORY;
|
|
|
|
typography->IDWriteTypography_iface.lpVtbl = &dwritetypographyvtbl;
|
|
typography->ref = 1;
|
|
typography->allocated = 2;
|
|
typography->count = 0;
|
|
|
|
typography->features = heap_alloc(typography->allocated*sizeof(DWRITE_FONT_FEATURE));
|
|
if (!typography->features) {
|
|
heap_free(typography);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*ret = &typography->IDWriteTypography_iface;
|
|
return S_OK;
|
|
}
|