|
|
|
@ -1,5 +1,5 @@ |
|
|
|
|
/*
|
|
|
|
|
* Copyright © 2015 Ebrahim Byagowi |
|
|
|
|
* Copyright © 2015-2016 Ebrahim Byagowi |
|
|
|
|
* |
|
|
|
|
* This is part of HarfBuzz, a text shaping library. |
|
|
|
|
* |
|
|
|
@ -25,7 +25,11 @@ |
|
|
|
|
#define HB_SHAPER directwrite |
|
|
|
|
#include "hb-shaper-impl-private.hh" |
|
|
|
|
|
|
|
|
|
#include <dwrite.h> |
|
|
|
|
#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION |
|
|
|
|
#include <DWrite.h> |
|
|
|
|
#else |
|
|
|
|
#include <DWrite_1.h> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#include "hb-directwrite.h" |
|
|
|
|
|
|
|
|
@ -176,7 +180,8 @@ _hb_rename_font(hb_blob_t *blob, wchar_t *new_name) |
|
|
|
|
hb_directwrite_shaper_face_data_t * |
|
|
|
|
_hb_directwrite_shaper_face_data_create(hb_face_t *face) |
|
|
|
|
{ |
|
|
|
|
hb_directwrite_shaper_face_data_t *data = (hb_directwrite_shaper_face_data_t *)calloc(1, sizeof (hb_directwrite_shaper_face_data_t)); |
|
|
|
|
hb_directwrite_shaper_face_data_t *data = |
|
|
|
|
(hb_directwrite_shaper_face_data_t *) calloc (1, sizeof (hb_directwrite_shaper_face_data_t)); |
|
|
|
|
if (unlikely (!data)) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
@ -244,26 +249,30 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font) |
|
|
|
|
{ |
|
|
|
|
if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL; |
|
|
|
|
|
|
|
|
|
hb_directwrite_shaper_font_data_t *data = (hb_directwrite_shaper_font_data_t *) calloc (1, sizeof (hb_directwrite_shaper_font_data_t)); |
|
|
|
|
hb_directwrite_shaper_font_data_t *data = |
|
|
|
|
(hb_directwrite_shaper_font_data_t *) calloc (1, sizeof (hb_directwrite_shaper_font_data_t)); |
|
|
|
|
if (unlikely (!data)) |
|
|
|
|
return NULL; |
|
|
|
|
|
|
|
|
|
data->hdc = GetDC (NULL); |
|
|
|
|
|
|
|
|
|
if (unlikely (!populate_log_font (&data->log_font, font))) { |
|
|
|
|
if (unlikely (!populate_log_font (&data->log_font, font))) |
|
|
|
|
{ |
|
|
|
|
DEBUG_MSG (DIRECTWRITE, font, "Font populate_log_font() failed"); |
|
|
|
|
_hb_directwrite_shaper_font_data_destroy (data); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
data->hfont = CreateFontIndirectW (&data->log_font); |
|
|
|
|
if (unlikely (!data->hfont)) { |
|
|
|
|
if (unlikely (!data->hfont)) |
|
|
|
|
{ |
|
|
|
|
DEBUG_MSG (DIRECTWRITE, font, "Font CreateFontIndirectW() failed"); |
|
|
|
|
_hb_directwrite_shaper_font_data_destroy (data); |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!SelectObject (data->hdc, data->hfont)) { |
|
|
|
|
if (!SelectObject (data->hdc, data->hfont)) |
|
|
|
|
{ |
|
|
|
|
DEBUG_MSG (DIRECTWRITE, font, "Font SelectObject() failed"); |
|
|
|
|
_hb_directwrite_shaper_font_data_destroy (data); |
|
|
|
|
return NULL; |
|
|
|
@ -333,16 +342,16 @@ public: |
|
|
|
|
// results.
|
|
|
|
|
struct Run |
|
|
|
|
{ |
|
|
|
|
UINT32 mTextStart; // starting text position of this run
|
|
|
|
|
UINT32 mTextLength; // number of contiguous code units covered
|
|
|
|
|
UINT32 mGlyphStart; // starting glyph in the glyphs array
|
|
|
|
|
UINT32 mGlyphCount; // number of glyphs associated with this run of
|
|
|
|
|
uint32_t mTextStart; // starting text position of this run
|
|
|
|
|
uint32_t mTextLength; // number of contiguous code units covered
|
|
|
|
|
uint32_t mGlyphStart; // starting glyph in the glyphs array
|
|
|
|
|
uint32_t mGlyphCount; // number of glyphs associated with this run of
|
|
|
|
|
// text
|
|
|
|
|
DWRITE_SCRIPT_ANALYSIS mScript; |
|
|
|
|
UINT8 mBidiLevel; |
|
|
|
|
uint8_t mBidiLevel; |
|
|
|
|
bool mIsSideways; |
|
|
|
|
|
|
|
|
|
inline bool ContainsTextPosition(UINT32 aTextPosition) const |
|
|
|
|
inline bool ContainsTextPosition(uint32_t aTextPosition) const |
|
|
|
|
{ |
|
|
|
|
return aTextPosition >= mTextStart |
|
|
|
|
&& aTextPosition < mTextStart + mTextLength; |
|
|
|
@ -353,7 +362,7 @@ public: |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
TextAnalysis(const wchar_t* text, |
|
|
|
|
UINT32 textLength, |
|
|
|
|
uint32_t textLength, |
|
|
|
|
const wchar_t* localeName, |
|
|
|
|
DWRITE_READING_DIRECTION readingDirection) |
|
|
|
|
: mText(text) |
|
|
|
@ -367,7 +376,7 @@ public: |
|
|
|
|
for (Run *run = mRunHead.nextRun; run;) { |
|
|
|
|
Run *origRun = run; |
|
|
|
|
run = run->nextRun; |
|
|
|
|
delete origRun; |
|
|
|
|
free (origRun); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -388,10 +397,7 @@ public: |
|
|
|
|
mCurrentRun = &mRunHead; |
|
|
|
|
|
|
|
|
|
// Call each of the analyzers in sequence, recording their results.
|
|
|
|
|
if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this, |
|
|
|
|
0, |
|
|
|
|
mTextLength, |
|
|
|
|
this))) { |
|
|
|
|
if (SUCCEEDED (hr = textAnalyzer->AnalyzeScript (this, 0, mTextLength, this))) { |
|
|
|
|
*runHead = &mRunHead; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -400,9 +406,9 @@ public: |
|
|
|
|
|
|
|
|
|
// IDWriteTextAnalysisSource implementation
|
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP GetTextAtPosition(UINT32 textPosition, |
|
|
|
|
OUT WCHAR const** textString, |
|
|
|
|
OUT UINT32* textLength) |
|
|
|
|
IFACEMETHODIMP GetTextAtPosition(uint32_t textPosition, |
|
|
|
|
OUT wchar_t const** textString, |
|
|
|
|
OUT uint32_t* textLength) |
|
|
|
|
{ |
|
|
|
|
if (textPosition >= mTextLength) { |
|
|
|
|
// No text at this position, valid query though.
|
|
|
|
@ -416,9 +422,9 @@ public: |
|
|
|
|
return S_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP GetTextBeforePosition(UINT32 textPosition, |
|
|
|
|
OUT WCHAR const** textString, |
|
|
|
|
OUT UINT32* textLength) |
|
|
|
|
IFACEMETHODIMP GetTextBeforePosition(uint32_t textPosition, |
|
|
|
|
OUT wchar_t const** textString, |
|
|
|
|
OUT uint32_t* textLength) |
|
|
|
|
{ |
|
|
|
|
if (textPosition == 0 || textPosition > mTextLength) { |
|
|
|
|
// Either there is no text before here (== 0), or this
|
|
|
|
@ -436,15 +442,15 @@ public: |
|
|
|
|
IFACEMETHODIMP_(DWRITE_READING_DIRECTION) |
|
|
|
|
GetParagraphReadingDirection() { return mReadingDirection; } |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP GetLocaleName(UINT32 textPosition, |
|
|
|
|
UINT32* textLength, |
|
|
|
|
WCHAR const** localeName) { |
|
|
|
|
IFACEMETHODIMP GetLocaleName(uint32_t textPosition, |
|
|
|
|
uint32_t* textLength, |
|
|
|
|
wchar_t const** localeName) { |
|
|
|
|
return S_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP |
|
|
|
|
GetNumberSubstitution(UINT32 textPosition, |
|
|
|
|
OUT UINT32* textLength, |
|
|
|
|
GetNumberSubstitution(uint32_t textPosition, |
|
|
|
|
OUT uint32_t* textLength, |
|
|
|
|
OUT IDWriteNumberSubstitution** numberSubstitution) |
|
|
|
|
{ |
|
|
|
|
// We do not support number substitution.
|
|
|
|
@ -457,8 +463,8 @@ public: |
|
|
|
|
// IDWriteTextAnalysisSink implementation
|
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP |
|
|
|
|
SetScriptAnalysis(UINT32 textPosition, |
|
|
|
|
UINT32 textLength, |
|
|
|
|
SetScriptAnalysis(uint32_t textPosition, |
|
|
|
|
uint32_t textLength, |
|
|
|
|
DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) |
|
|
|
|
{ |
|
|
|
|
SetCurrentRun(textPosition); |
|
|
|
@ -472,22 +478,22 @@ public: |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP |
|
|
|
|
SetLineBreakpoints(UINT32 textPosition, |
|
|
|
|
UINT32 textLength, |
|
|
|
|
SetLineBreakpoints(uint32_t textPosition, |
|
|
|
|
uint32_t textLength, |
|
|
|
|
const DWRITE_LINE_BREAKPOINT* lineBreakpoints) { return S_OK; } |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP SetBidiLevel(UINT32 textPosition, |
|
|
|
|
UINT32 textLength, |
|
|
|
|
UINT8 explicitLevel, |
|
|
|
|
UINT8 resolvedLevel) { return S_OK; } |
|
|
|
|
IFACEMETHODIMP SetBidiLevel(uint32_t textPosition, |
|
|
|
|
uint32_t textLength, |
|
|
|
|
uint8_t explicitLevel, |
|
|
|
|
uint8_t resolvedLevel) { return S_OK; } |
|
|
|
|
|
|
|
|
|
IFACEMETHODIMP |
|
|
|
|
SetNumberSubstitution(UINT32 textPosition, |
|
|
|
|
UINT32 textLength, |
|
|
|
|
SetNumberSubstitution(uint32_t textPosition, |
|
|
|
|
uint32_t textLength, |
|
|
|
|
IDWriteNumberSubstitution* numberSubstitution) { return S_OK; } |
|
|
|
|
|
|
|
|
|
protected: |
|
|
|
|
Run *FetchNextRun(IN OUT UINT32* textLength) |
|
|
|
|
Run *FetchNextRun(IN OUT uint32_t* textLength) |
|
|
|
|
{ |
|
|
|
|
// Used by the sink setters, this returns a reference to the next run.
|
|
|
|
|
// Position and length are adjusted to now point after the current run
|
|
|
|
@ -509,7 +515,7 @@ protected: |
|
|
|
|
return origRun; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SetCurrentRun(UINT32 textPosition) |
|
|
|
|
void SetCurrentRun(uint32_t textPosition) |
|
|
|
|
{ |
|
|
|
|
// Move the current run to the given position.
|
|
|
|
|
// Since the analyzers generally return results in a forward manner,
|
|
|
|
@ -530,7 +536,7 @@ protected: |
|
|
|
|
// of our runs");
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void SplitCurrentRun(UINT32 splitPosition) |
|
|
|
|
void SplitCurrentRun(uint32_t splitPosition) |
|
|
|
|
{ |
|
|
|
|
if (!mCurrentRun) { |
|
|
|
|
//NS_ASSERTION(false, "SplitCurrentRun called without current run.");
|
|
|
|
@ -543,7 +549,7 @@ protected: |
|
|
|
|
// or before it. Usually the first.
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
Run *newRun = new Run; |
|
|
|
|
Run *newRun = (Run*) malloc (sizeof (Run)); |
|
|
|
|
|
|
|
|
|
*newRun = *mCurrentRun; |
|
|
|
|
|
|
|
|
@ -552,7 +558,7 @@ protected: |
|
|
|
|
mCurrentRun->nextRun = newRun; |
|
|
|
|
|
|
|
|
|
// Adjust runs' text positions and lengths.
|
|
|
|
|
UINT32 splitPoint = splitPosition - mCurrentRun->mTextStart; |
|
|
|
|
uint32_t splitPoint = splitPosition - mCurrentRun->mTextStart; |
|
|
|
|
newRun->mTextStart += splitPoint; |
|
|
|
|
newRun->mTextLength -= splitPoint; |
|
|
|
|
mCurrentRun->mTextLength = splitPoint; |
|
|
|
@ -563,9 +569,9 @@ protected: |
|
|
|
|
// Input
|
|
|
|
|
// (weak references are fine here, since this class is a transient
|
|
|
|
|
// stack-based helper that doesn't need to copy data)
|
|
|
|
|
UINT32 mTextLength; |
|
|
|
|
const WCHAR* mText; |
|
|
|
|
const WCHAR* mLocaleName; |
|
|
|
|
uint32_t mTextLength; |
|
|
|
|
const wchar_t* mText; |
|
|
|
|
const wchar_t* mLocaleName; |
|
|
|
|
DWRITE_READING_DIRECTION mReadingDirection; |
|
|
|
|
|
|
|
|
|
// Current processing state.
|
|
|
|
@ -575,6 +581,10 @@ protected: |
|
|
|
|
Run mRunHead; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static inline uint16_t hb_uint16_swap (const uint16_t v) |
|
|
|
|
{ return (v >> 8) | (v << 8); } |
|
|
|
|
static inline uint32_t hb_uint32_swap (const uint32_t v) |
|
|
|
|
{ return (hb_uint16_swap(v) << 16) | hb_uint16_swap(v >> 16); } |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* shaper |
|
|
|
@ -592,11 +602,15 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, |
|
|
|
|
hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font); |
|
|
|
|
|
|
|
|
|
// factory probably should be cached
|
|
|
|
|
#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION |
|
|
|
|
IDWriteFactory* dwriteFactory; |
|
|
|
|
DWriteCreateFactory( |
|
|
|
|
#else |
|
|
|
|
IDWriteFactory1* dwriteFactory; |
|
|
|
|
#endif |
|
|
|
|
DWriteCreateFactory ( |
|
|
|
|
DWRITE_FACTORY_TYPE_SHARED, |
|
|
|
|
__uuidof(IDWriteFactory), |
|
|
|
|
reinterpret_cast<IUnknown**>(&dwriteFactory) |
|
|
|
|
__uuidof (IDWriteFactory), |
|
|
|
|
(IUnknown**) &dwriteFactory |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
IDWriteGdiInterop *gdiInterop; |
|
|
|
@ -604,8 +618,14 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, |
|
|
|
|
IDWriteFontFace* fontFace; |
|
|
|
|
gdiInterop->CreateFontFaceFromHdc (font_data->hdc, &fontFace); |
|
|
|
|
|
|
|
|
|
#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION |
|
|
|
|
IDWriteTextAnalyzer* analyzer; |
|
|
|
|
dwriteFactory->CreateTextAnalyzer (&analyzer); |
|
|
|
|
dwriteFactory->CreateTextAnalyzer(&analyzer); |
|
|
|
|
#else |
|
|
|
|
IDWriteTextAnalyzer* analyzer0; |
|
|
|
|
dwriteFactory->CreateTextAnalyzer (&analyzer0); |
|
|
|
|
IDWriteTextAnalyzer1* analyzer = (IDWriteTextAnalyzer1*) analyzer0; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
unsigned int scratch_size; |
|
|
|
|
hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); |
|
|
|
@ -620,7 +640,7 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, |
|
|
|
|
|
|
|
|
|
#define utf16_index() var1.u32 |
|
|
|
|
|
|
|
|
|
ALLOCATE_ARRAY(WCHAR, pchars, buffer->len * 2); |
|
|
|
|
ALLOCATE_ARRAY(wchar_t, textString, buffer->len * 2); |
|
|
|
|
|
|
|
|
|
unsigned int chars_len = 0; |
|
|
|
|
for (unsigned int i = 0; i < buffer->len; i++) |
|
|
|
@ -628,17 +648,17 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, |
|
|
|
|
hb_codepoint_t c = buffer->info[i].codepoint; |
|
|
|
|
buffer->info[i].utf16_index() = chars_len; |
|
|
|
|
if (likely(c <= 0xFFFFu)) |
|
|
|
|
pchars[chars_len++] = c; |
|
|
|
|
textString[chars_len++] = c; |
|
|
|
|
else if (unlikely(c > 0x10FFFFu)) |
|
|
|
|
pchars[chars_len++] = 0xFFFDu; |
|
|
|
|
textString[chars_len++] = 0xFFFDu; |
|
|
|
|
else { |
|
|
|
|
pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); |
|
|
|
|
pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); |
|
|
|
|
textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); |
|
|
|
|
textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ALLOCATE_ARRAY(WORD, log_clusters, chars_len); |
|
|
|
|
if (num_features) |
|
|
|
|
// if (num_features)
|
|
|
|
|
{ |
|
|
|
|
/* Need log_clusters to assign features. */ |
|
|
|
|
chars_len = 0; |
|
|
|
@ -664,164 +684,287 @@ _hb_directwrite_shape(hb_shape_plan_t *shape_plan, |
|
|
|
|
* but we never attempt to shape a word longer than 64K characters |
|
|
|
|
* in a single gfxShapedWord, so we cannot exceed that limit. |
|
|
|
|
*/ |
|
|
|
|
UINT32 length = buffer->len; |
|
|
|
|
uint32_t textLength = buffer->len; |
|
|
|
|
|
|
|
|
|
TextAnalysis analysis(pchars, length, NULL, readingDirection); |
|
|
|
|
TextAnalysis analysis(textString, textLength, NULL, readingDirection); |
|
|
|
|
TextAnalysis::Run *runHead; |
|
|
|
|
hr = analysis.GenerateResults(analyzer, &runHead); |
|
|
|
|
|
|
|
|
|
if (FAILED(hr)) { |
|
|
|
|
//NS_WARNING("Analyzer failed to generate results.");
|
|
|
|
|
#define FAIL(...) \ |
|
|
|
|
HB_STMT_START { \
|
|
|
|
|
DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \
|
|
|
|
|
return false; \
|
|
|
|
|
} HB_STMT_END; |
|
|
|
|
|
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to generate results."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
UINT32 maxGlyphs = 3 * length / 2 + 16; |
|
|
|
|
uint32_t maxGlyphCount = 3 * textLength / 2 + 16; |
|
|
|
|
uint32_t glyphCount; |
|
|
|
|
bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); |
|
|
|
|
|
|
|
|
|
const wchar_t localeName[20] = {0}; |
|
|
|
|
if (buffer->props.language != NULL) |
|
|
|
|
{ |
|
|
|
|
mbstowcs ((wchar_t*) localeName, |
|
|
|
|
hb_language_to_string (buffer->props.language), 20); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DWRITE_TYPOGRAPHIC_FEATURES singleFeatures; |
|
|
|
|
singleFeatures.featureCount = num_features; |
|
|
|
|
if (num_features) |
|
|
|
|
{ |
|
|
|
|
DWRITE_FONT_FEATURE* dwfeatureArray = (DWRITE_FONT_FEATURE*) |
|
|
|
|
malloc (sizeof (DWRITE_FONT_FEATURE) * num_features); |
|
|
|
|
for (unsigned int i = 0; i < num_features; ++i) |
|
|
|
|
{ |
|
|
|
|
dwfeatureArray[i].nameTag = (DWRITE_FONT_FEATURE_TAG) |
|
|
|
|
hb_uint32_swap (features[i].tag); |
|
|
|
|
dwfeatureArray[i].parameter = features[i].value; |
|
|
|
|
} |
|
|
|
|
singleFeatures.features = dwfeatureArray; |
|
|
|
|
} |
|
|
|
|
const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures = |
|
|
|
|
(const DWRITE_TYPOGRAPHIC_FEATURES*) &singleFeatures; |
|
|
|
|
const uint32_t featureRangeLengths[] = { textLength }; |
|
|
|
|
|
|
|
|
|
#define INITIAL_GLYPH_SIZE 400 |
|
|
|
|
UINT16* clusters = (UINT16*)malloc(INITIAL_GLYPH_SIZE * sizeof(UINT16)); |
|
|
|
|
UINT16* glyphs = (UINT16*)malloc(INITIAL_GLYPH_SIZE * sizeof(UINT16)); |
|
|
|
|
retry_getglyphs: |
|
|
|
|
uint16_t* clusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); |
|
|
|
|
uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); |
|
|
|
|
DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*) |
|
|
|
|
malloc(INITIAL_GLYPH_SIZE * sizeof(DWRITE_SHAPING_TEXT_PROPERTIES)); |
|
|
|
|
malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES)); |
|
|
|
|
DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*) |
|
|
|
|
malloc(INITIAL_GLYPH_SIZE * sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES)); |
|
|
|
|
|
|
|
|
|
UINT32 actualGlyphs; |
|
|
|
|
|
|
|
|
|
bool backward = HB_DIRECTION_IS_BACKWARD(buffer->props.direction); |
|
|
|
|
|
|
|
|
|
wchar_t lang[4]; |
|
|
|
|
mbstowcs(lang, hb_language_to_string(buffer->props.language), 4); |
|
|
|
|
hr = analyzer->GetGlyphs(pchars, length, |
|
|
|
|
fontFace, FALSE, |
|
|
|
|
buffer->props.direction, |
|
|
|
|
&runHead->mScript, (const wchar_t*)lang, NULL, NULL, NULL, 0, |
|
|
|
|
maxGlyphs, clusters, textProperties, |
|
|
|
|
glyphs, glyphProperties, &actualGlyphs); |
|
|
|
|
|
|
|
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) { |
|
|
|
|
free(clusters); |
|
|
|
|
free(glyphs); |
|
|
|
|
free(textProperties); |
|
|
|
|
free(glyphProperties); |
|
|
|
|
|
|
|
|
|
clusters = (UINT16*)malloc(INITIAL_GLYPH_SIZE * sizeof(UINT16)); |
|
|
|
|
glyphs = (UINT16*)malloc(INITIAL_GLYPH_SIZE * sizeof(UINT16)); |
|
|
|
|
textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*) |
|
|
|
|
malloc(INITIAL_GLYPH_SIZE * sizeof(DWRITE_SHAPING_TEXT_PROPERTIES)); |
|
|
|
|
glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*) |
|
|
|
|
malloc(INITIAL_GLYPH_SIZE * sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES)); |
|
|
|
|
|
|
|
|
|
hr = analyzer->GetGlyphs(pchars, length, |
|
|
|
|
fontFace, FALSE, |
|
|
|
|
buffer->props.direction, |
|
|
|
|
&runHead->mScript, (const wchar_t*)lang, NULL, NULL, NULL, 0, |
|
|
|
|
maxGlyphs, clusters, textProperties, |
|
|
|
|
glyphs, glyphProperties, &actualGlyphs); |
|
|
|
|
} |
|
|
|
|
if (FAILED(hr)) { |
|
|
|
|
//NS_WARNING("Analyzer failed to get glyphs.");
|
|
|
|
|
malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES)); |
|
|
|
|
|
|
|
|
|
hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE, |
|
|
|
|
isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures, |
|
|
|
|
featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices, |
|
|
|
|
glyphProperties, &glyphCount); |
|
|
|
|
|
|
|
|
|
if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))) |
|
|
|
|
{ |
|
|
|
|
free (clusterMap); |
|
|
|
|
free (glyphIndices); |
|
|
|
|
free (textProperties); |
|
|
|
|
free (glyphProperties); |
|
|
|
|
|
|
|
|
|
maxGlyphCount *= 2; |
|
|
|
|
|
|
|
|
|
goto retry_getglyphs; |
|
|
|
|
} |
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get glyphs."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FLOAT advances[400]; |
|
|
|
|
DWRITE_GLYPH_OFFSET offsets[400]; |
|
|
|
|
|
|
|
|
|
float* glyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); |
|
|
|
|
DWRITE_GLYPH_OFFSET* glyphOffsets = (DWRITE_GLYPH_OFFSET*) |
|
|
|
|
malloc(maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); |
|
|
|
|
|
|
|
|
|
/* The -2 in the following is to compensate for possible
|
|
|
|
|
* alignment needed after the WORD array. sizeof(WORD) == 2. */ |
|
|
|
|
unsigned int glyphs_size = (scratch_size * sizeof (int)-2) |
|
|
|
|
/ (sizeof (WORD) + |
|
|
|
|
4 + // sizeof (SCRIPT_GLYPHPROP) +
|
|
|
|
|
sizeof (int) + |
|
|
|
|
8 + // sizeof (GOFFSET) +
|
|
|
|
|
sizeof (uint32_t)); |
|
|
|
|
ALLOCATE_ARRAY(uint32_t, vis_clusters, glyphs_size); |
|
|
|
|
unsigned int glyphs_size = (scratch_size * sizeof(int) - 2) |
|
|
|
|
/ (sizeof(WORD) + |
|
|
|
|
sizeof(DWRITE_SHAPING_GLYPH_PROPERTIES) + |
|
|
|
|
sizeof(int) + |
|
|
|
|
sizeof(DWRITE_GLYPH_OFFSET) + |
|
|
|
|
sizeof(uint32_t)); |
|
|
|
|
ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); |
|
|
|
|
|
|
|
|
|
#undef ALLOCATE_ARRAY |
|
|
|
|
|
|
|
|
|
hr = analyzer->GetGlyphPlacements(pchars, |
|
|
|
|
clusters, |
|
|
|
|
textProperties, |
|
|
|
|
length, |
|
|
|
|
glyphs, |
|
|
|
|
glyphProperties, |
|
|
|
|
actualGlyphs, |
|
|
|
|
fontFace, |
|
|
|
|
face->get_upem(), |
|
|
|
|
FALSE, |
|
|
|
|
FALSE, |
|
|
|
|
&runHead->mScript, |
|
|
|
|
NULL, |
|
|
|
|
NULL, |
|
|
|
|
NULL, |
|
|
|
|
0, |
|
|
|
|
advances, |
|
|
|
|
offsets); |
|
|
|
|
|
|
|
|
|
if (FAILED(hr)) { |
|
|
|
|
//NS_WARNING("Analyzer failed to get glyph placements.");
|
|
|
|
|
int fontEmSize = font->face->get_upem(); |
|
|
|
|
if (fontEmSize < 0) |
|
|
|
|
fontEmSize = -fontEmSize; |
|
|
|
|
|
|
|
|
|
if (fontEmSize < 0) |
|
|
|
|
fontEmSize = -fontEmSize; |
|
|
|
|
double x_mult = (double) font->x_scale / fontEmSize; |
|
|
|
|
double y_mult = (double) font->y_scale / fontEmSize; |
|
|
|
|
|
|
|
|
|
hr = analyzer->GetGlyphPlacements (textString, |
|
|
|
|
clusterMap, textProperties, textLength, glyphIndices, |
|
|
|
|
glyphProperties, glyphCount, fontFace, fontEmSize, |
|
|
|
|
FALSE, isRightToLeft, &runHead->mScript, localeName, |
|
|
|
|
&dwFeatures, featureRangeLengths, 1, |
|
|
|
|
glyphAdvances, glyphOffsets); |
|
|
|
|
|
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get glyph placements."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION |
|
|
|
|
|
|
|
|
|
DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities = |
|
|
|
|
(DWRITE_JUSTIFICATION_OPPORTUNITY*) |
|
|
|
|
malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY)); |
|
|
|
|
hr = analyzer->GetJustificationOpportunities (fontFace, fontEmSize, |
|
|
|
|
runHead->mScript, textLength, glyphCount, textString, clusterMap, |
|
|
|
|
glyphProperties, justificationOpportunities); |
|
|
|
|
|
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get justification opportunities."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: get lineWith from somewhere
|
|
|
|
|
float lineWidth = 60000; |
|
|
|
|
|
|
|
|
|
float* justifiedGlyphAdvances = |
|
|
|
|
(float*) malloc (maxGlyphCount * sizeof (float)); |
|
|
|
|
DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) |
|
|
|
|
malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET)); |
|
|
|
|
hr = analyzer->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities, |
|
|
|
|
glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets); |
|
|
|
|
|
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get justified glyph advances."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DWRITE_SCRIPT_PROPERTIES scriptProperties; |
|
|
|
|
hr = analyzer->GetScriptProperties (runHead->mScript, &scriptProperties); |
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get script properties."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
uint32_t justificationCharacter = scriptProperties.justificationCharacter; |
|
|
|
|
|
|
|
|
|
// if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
|
|
|
|
|
if (justificationCharacter != 32) |
|
|
|
|
{ |
|
|
|
|
retry_getjustifiedglyphs: |
|
|
|
|
uint16_t* modifiedClusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); |
|
|
|
|
uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t)); |
|
|
|
|
float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float)); |
|
|
|
|
DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*) |
|
|
|
|
malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET)); |
|
|
|
|
uint32_t actualGlyphsCount; |
|
|
|
|
hr = analyzer->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript, |
|
|
|
|
textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices, |
|
|
|
|
glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets, |
|
|
|
|
glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices, |
|
|
|
|
modifiedGlyphAdvances, modifiedGlyphOffsets); |
|
|
|
|
|
|
|
|
|
if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)) |
|
|
|
|
{ |
|
|
|
|
maxGlyphCount = actualGlyphsCount; |
|
|
|
|
free (modifiedClusterMap); |
|
|
|
|
free (modifiedGlyphIndices); |
|
|
|
|
free (modifiedGlyphAdvances); |
|
|
|
|
free (modifiedGlyphOffsets); |
|
|
|
|
|
|
|
|
|
unsigned int glyphs_len = actualGlyphs; |
|
|
|
|
maxGlyphCount = actualGlyphsCount; |
|
|
|
|
|
|
|
|
|
goto retry_getjustifiedglyphs; |
|
|
|
|
} |
|
|
|
|
if (FAILED (hr)) |
|
|
|
|
{ |
|
|
|
|
FAIL ("Analyzer failed to get justified glyphs."); |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
free (clusterMap); |
|
|
|
|
free (glyphIndices); |
|
|
|
|
free (glyphAdvances); |
|
|
|
|
free (glyphOffsets); |
|
|
|
|
|
|
|
|
|
glyphCount = actualGlyphsCount; |
|
|
|
|
clusterMap = modifiedClusterMap; |
|
|
|
|
glyphIndices = modifiedGlyphIndices; |
|
|
|
|
glyphAdvances = modifiedGlyphAdvances; |
|
|
|
|
glyphOffsets = modifiedGlyphOffsets; |
|
|
|
|
|
|
|
|
|
free(justifiedGlyphAdvances); |
|
|
|
|
free(justifiedGlyphOffsets); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
free(glyphAdvances); |
|
|
|
|
free(glyphOffsets); |
|
|
|
|
|
|
|
|
|
glyphAdvances = justifiedGlyphAdvances; |
|
|
|
|
glyphOffsets = justifiedGlyphOffsets; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
free(justificationOpportunities); |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/* Ok, we've got everything we need, now compose output buffer,
|
|
|
|
|
* very, *very*, carefully! */ |
|
|
|
|
|
|
|
|
|
/* Calculate visual-clusters. That's what we ship. */ |
|
|
|
|
for (unsigned int i = 0; i < glyphs_len; i++) |
|
|
|
|
for (unsigned int i = 0; i < glyphCount; i++) |
|
|
|
|
vis_clusters[i] = -1; |
|
|
|
|
for (unsigned int i = 0; i < buffer->len; i++) { |
|
|
|
|
uint32_t *p = &vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; |
|
|
|
|
//*p = MIN (*p, buffer->info[i].cluster);
|
|
|
|
|
for (unsigned int i = 0; i < buffer->len; i++) |
|
|
|
|
{ |
|
|
|
|
uint32_t *p = |
|
|
|
|
&vis_clusters[log_clusters[buffer->info[i].utf16_index()]]; |
|
|
|
|
*p = MIN (*p, buffer->info[i].cluster); |
|
|
|
|
} |
|
|
|
|
for (unsigned int i = 1; i < glyphs_len; i++) |
|
|
|
|
for (unsigned int i = 1; i < glyphCount; i++) |
|
|
|
|
if (vis_clusters[i] == -1) |
|
|
|
|
vis_clusters[i] = vis_clusters[i - 1]; |
|
|
|
|
|
|
|
|
|
#undef utf16_index |
|
|
|
|
|
|
|
|
|
//if (unlikely (!buffer->ensure (glyphs_len)))
|
|
|
|
|
// FAIL ("Buffer in error");
|
|
|
|
|
if (unlikely (!buffer->ensure (glyphCount))) |
|
|
|
|
FAIL ("Buffer in error"); |
|
|
|
|
|
|
|
|
|
#undef FAIL |
|
|
|
|
|
|
|
|
|
/* Set glyph infos */ |
|
|
|
|
buffer->len = 0; |
|
|
|
|
for (unsigned int i = 0; i < glyphs_len; i++) |
|
|
|
|
for (unsigned int i = 0; i < glyphCount; i++) |
|
|
|
|
{ |
|
|
|
|
hb_glyph_info_t *info = &buffer->info[buffer->len++]; |
|
|
|
|
|
|
|
|
|
info->codepoint = glyphs[i]; |
|
|
|
|
info->codepoint = glyphIndices[i]; |
|
|
|
|
info->cluster = vis_clusters[i]; |
|
|
|
|
|
|
|
|
|
/* The rest is crap. Let's store position info there for now. */ |
|
|
|
|
info->mask = advances[i]; |
|
|
|
|
info->var1.u32 = offsets[i].ascenderOffset; |
|
|
|
|
info->var2.u32 = -offsets[i].advanceOffset; |
|
|
|
|
info->mask = glyphAdvances[i]; |
|
|
|
|
info->var1.i32 = glyphOffsets[i].advanceOffset; |
|
|
|
|
info->var2.i32 = glyphOffsets[i].ascenderOffset; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
free(clusters); |
|
|
|
|
free(glyphs); |
|
|
|
|
free(textProperties); |
|
|
|
|
free(glyphProperties); |
|
|
|
|
|
|
|
|
|
/* Set glyph positions */ |
|
|
|
|
buffer->clear_positions (); |
|
|
|
|
for (unsigned int i = 0; i < glyphs_len; i++) |
|
|
|
|
for (unsigned int i = 0; i < glyphCount; i++) |
|
|
|
|
{ |
|
|
|
|
hb_glyph_info_t *info = &buffer->info[i]; |
|
|
|
|
hb_glyph_position_t *pos = &buffer->pos[i]; |
|
|
|
|
|
|
|
|
|
/* TODO vertical */ |
|
|
|
|
pos->x_advance = info->mask; |
|
|
|
|
pos->x_offset = backward ? -info->var1.u32 : info->var1.u32; |
|
|
|
|
pos->y_offset = info->var2.u32; |
|
|
|
|
pos->x_advance = x_mult * (int32_t) info->mask; |
|
|
|
|
pos->x_offset = |
|
|
|
|
x_mult * (isRightToLeft ? -info->var1.i32 : info->var1.i32); |
|
|
|
|
pos->y_offset = y_mult * info->var2.i32; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (backward) |
|
|
|
|
if (isRightToLeft) |
|
|
|
|
hb_buffer_reverse (buffer); |
|
|
|
|
|
|
|
|
|
free (clusterMap); |
|
|
|
|
free (glyphIndices); |
|
|
|
|
free (textProperties); |
|
|
|
|
free (glyphProperties); |
|
|
|
|
free (glyphAdvances); |
|
|
|
|
free (glyphOffsets); |
|
|
|
|
|
|
|
|
|
if (num_features) |
|
|
|
|
free (singleFeatures.features); |
|
|
|
|
|
|
|
|
|
/* Wow, done! */ |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|