bug 985220: remove the old GDI, Uniscribe and DWrite text-shaping code paths, as we now use HarfBuzz or Graphite for all shaping on Windows. r=jdaggett

This commit is contained in:
Jonathan Kew 2014-03-19 12:27:44 +00:00
parent cb0fa693b1
commit 4216251038
16 changed files with 87 additions and 1516 deletions

View File

@ -7,7 +7,6 @@
#include "mozilla/MemoryReporting.h"
#include "gfxDWriteShaper.h"
#include "gfxHarfBuzzShaper.h"
#include <algorithm>
#include "gfxGraphiteShaper.h"
@ -15,8 +14,6 @@
#include "gfxContext.h"
#include <dwrite.h>
#include "gfxDWriteTextAnalysis.h"
#include "harfbuzz/hb.h"
// Chosen this as to resemble DWrite's own oblique face style.
@ -137,10 +134,30 @@ gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
&mStyle, mNeedsBold, anAAOption);
}
void
gfxDWriteFont::CreatePlatformShaper()
bool
gfxDWriteFont::ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping)
{
mPlatformShaper = new gfxDWriteShaper(this);
bool ok = false;
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
if (!ok && mHarfBuzzShaper) {
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return ok;
}
const gfxFont::Metrics&
@ -595,7 +612,7 @@ gfxDWriteFont::Measure(gfxTextRun *aTextRun,
}
bool
gfxDWriteFont::ProvidesGlyphWidths()
gfxDWriteFont::ProvidesGlyphWidths() const
{
return !mUseSubpixelPositions ||
(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);

View File

@ -53,7 +53,7 @@ public:
gfxContext *aContextForTightBoundingBox,
Spacing *aSpacing);
virtual bool ProvidesGlyphWidths();
virtual bool ProvidesGlyphWidths() const;
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
@ -71,9 +71,13 @@ public:
virtual cairo_scaled_font_t *GetCairoScaledFont();
protected:
friend class gfxDWriteShaper;
virtual void CreatePlatformShaper();
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText,
bool aPreferPlatformShaping = false);
bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);

View File

@ -1,230 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDWriteShaper.h"
#include "gfxWindowsPlatform.h"
#include <dwrite.h>
#include "gfxDWriteTextAnalysis.h"
#include "nsCRT.h"
bool
gfxDWriteShaper::ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
HRESULT hr;
// TODO: Handle TEXT_DISABLE_OPTIONAL_LIGATURES
DWRITE_READING_DIRECTION readingDirection =
aShapedText->IsRightToLeft()
? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
: DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
gfxShapedText::CompressedGlyph g;
IDWriteTextAnalyzer *analyzer =
gfxWindowsPlatform::GetPlatform()->GetDWriteAnalyzer();
if (!analyzer) {
return false;
}
/**
* There's an internal 16-bit limit on some things inside the analyzer,
* but we never attempt to shape a word longer than 32K characters
* in a single call, so we cannot exceed that limit.
*/
UINT32 length = aLength;
char16ptr_t text = aText;
TextAnalysis analysis(text, length, nullptr, readingDirection);
TextAnalysis::Run *runHead;
hr = analysis.GenerateResults(analyzer, &runHead);
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to generate results.");
return false;
}
int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
UINT32 maxGlyphs = 0;
trymoreglyphs:
if ((UINT32_MAX - 3 * length / 2 + 16) < maxGlyphs) {
// This isn't going to work, we're going to cross the UINT32 upper
// limit. Give up.
NS_WARNING("Shaper needs to generate more than 2^32 glyphs?!");
return false;
}
maxGlyphs += 3 * length / 2 + 16;
AutoFallibleTArray<UINT16, 400> clusters;
AutoFallibleTArray<UINT16, 400> indices;
AutoFallibleTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
AutoFallibleTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
if (!clusters.SetLength(length) ||
!indices.SetLength(maxGlyphs) ||
!textProperties.SetLength(maxGlyphs) ||
!glyphProperties.SetLength(maxGlyphs)) {
NS_WARNING("Shaper failed to allocate memory.");
return false;
}
UINT32 actualGlyphs;
hr = analyzer->GetGlyphs(text, length,
font->GetFontFace(), FALSE,
readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
&runHead->mScript, nullptr, nullptr, nullptr, nullptr, 0,
maxGlyphs, clusters.Elements(), textProperties.Elements(),
indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
// We increase the amount of glyphs and try again.
goto trymoreglyphs;
}
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyphs.");
return false;
}
WORD gID = indices[0];
AutoFallibleTArray<FLOAT, 400> advances;
AutoFallibleTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
if (!advances.SetLength(actualGlyphs) ||
!glyphOffsets.SetLength(actualGlyphs)) {
NS_WARNING("Shaper failed to allocate memory.");
return false;
}
if (!static_cast<gfxDWriteFont*>(mFont)->mUseSubpixelPositions) {
hr = analyzer->GetGdiCompatibleGlyphPlacements(
text,
clusters.Elements(),
textProperties.Elements(),
length,
indices.Elements(),
glyphProperties.Elements(),
actualGlyphs,
font->GetFontFace(),
font->GetAdjustedSize(),
1.0,
nullptr,
FALSE,
FALSE,
FALSE,
&runHead->mScript,
nullptr,
nullptr,
nullptr,
0,
advances.Elements(),
glyphOffsets.Elements());
} else {
hr = analyzer->GetGlyphPlacements(text,
clusters.Elements(),
textProperties.Elements(),
length,
indices.Elements(),
glyphProperties.Elements(),
actualGlyphs,
font->GetFontFace(),
font->GetAdjustedSize(),
FALSE,
FALSE,
&runHead->mScript,
nullptr,
nullptr,
nullptr,
0,
advances.Elements(),
glyphOffsets.Elements());
}
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyph placements.");
return false;
}
nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
for (unsigned int c = 0; c < length; c++) {
uint32_t k = clusters[c];
uint32_t absC = aOffset + c;
if (c > 0 && k == clusters[c - 1]) {
// This is a cluster continuation. No glyph here.
gfxShapedText::CompressedGlyph &g = charGlyphs[absC];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(g.IsClusterStart(), false, 0);
continue;
}
// Count glyphs for this character
uint32_t glyphCount = actualGlyphs - k;
uint32_t nextClusterOffset;
for (nextClusterOffset = c + 1;
nextClusterOffset < length; ++nextClusterOffset) {
if (clusters[nextClusterOffset] > k) {
glyphCount = clusters[nextClusterOffset] - k;
break;
}
}
int32_t advance = (int32_t)(advances[k] * appUnitsPerDevPixel);
if (glyphCount == 1 && advance >= 0 &&
glyphOffsets[k].advanceOffset == 0 &&
glyphOffsets[k].ascenderOffset == 0 &&
charGlyphs[absC].IsClusterStart() &&
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
charGlyphs[absC].SetSimpleGlyph(advance, indices[k]);
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(
glyphCount - detailedGlyphs.Length())) {
continue;
}
}
float totalAdvance = 0;
for (unsigned int z = 0; z < glyphCount; z++) {
detailedGlyphs[z].mGlyphID = indices[k + z];
detailedGlyphs[z].mAdvance =
(int32_t)(advances[k + z]
* appUnitsPerDevPixel);
if (readingDirection ==
DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
detailedGlyphs[z].mXOffset =
(totalAdvance +
glyphOffsets[k + z].advanceOffset)
* appUnitsPerDevPixel;
} else {
detailedGlyphs[z].mXOffset =
glyphOffsets[k + z].advanceOffset *
appUnitsPerDevPixel;
}
detailedGlyphs[z].mYOffset =
-glyphOffsets[k + z].ascenderOffset *
appUnitsPerDevPixel;
totalAdvance += advances[k + z];
}
aShapedText->SetGlyphs(
absC,
g.SetComplex(charGlyphs[absC].IsClusterStart(),
true,
glyphCount),
detailedGlyphs.Elements());
}
}
return true;
}

View File

@ -1,36 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_DWRITESHAPER_H
#define GFX_DWRITESHAPER_H
#include "gfxDWriteFonts.h"
/**
* \brief Class representing a DWrite font shaper.
*/
class gfxDWriteShaper : public gfxFontShaper
{
public:
gfxDWriteShaper(gfxDWriteFont *aFont)
: gfxFontShaper(aFont)
{
MOZ_COUNT_CTOR(gfxDWriteShaper);
}
virtual ~gfxDWriteShaper()
{
MOZ_COUNT_DTOR(gfxDWriteShaper);
}
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
};
#endif /* GFX_DWRITESHAPER_H */

View File

@ -1,257 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxDWriteTextAnalysis.h"
TextAnalysis::TextAnalysis(const wchar_t* text,
UINT32 textLength,
const wchar_t* localeName,
DWRITE_READING_DIRECTION readingDirection)
: mText(text)
, mTextLength(textLength)
, mLocaleName(localeName)
, mReadingDirection(readingDirection)
, mCurrentRun(nullptr)
{
}
TextAnalysis::~TextAnalysis()
{
// delete runs, except mRunHead which is part of the TextAnalysis object
for (Run *run = mRunHead.nextRun; run;) {
Run *origRun = run;
run = run->nextRun;
delete origRun;
}
}
STDMETHODIMP
TextAnalysis::GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
OUT Run **runHead)
{
// Analyzes the text using the script analyzer and returns
// the result as a series of runs.
HRESULT hr = S_OK;
// Initially start out with one result that covers the entire range.
// This result will be subdivided by the analysis processes.
mRunHead.mTextStart = 0;
mRunHead.mTextLength = mTextLength;
mRunHead.mBidiLevel =
(mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
mRunHead.nextRun = nullptr;
mCurrentRun = &mRunHead;
// Call each of the analyzers in sequence, recording their results.
if (SUCCEEDED(hr = textAnalyzer->AnalyzeScript(this,
0,
mTextLength,
this))) {
*runHead = &mRunHead;
}
return hr;
}
////////////////////////////////////////////////////////////////////////////////
// IDWriteTextAnalysisSource source implementation
IFACEMETHODIMP
TextAnalysis::GetTextAtPosition(UINT32 textPosition,
OUT WCHAR const** textString,
OUT UINT32* textLength)
{
if (textPosition >= mTextLength) {
// No text at this position, valid query though.
*textString = nullptr;
*textLength = 0;
} else {
*textString = mText + textPosition;
*textLength = mTextLength - textPosition;
}
return S_OK;
}
IFACEMETHODIMP
TextAnalysis::GetTextBeforePosition(UINT32 textPosition,
OUT WCHAR const** textString,
OUT UINT32* textLength)
{
if (textPosition == 0 || textPosition > mTextLength) {
// Either there is no text before here (== 0), or this
// is an invalid position. The query is considered valid thouh.
*textString = nullptr;
*textLength = 0;
} else {
*textString = mText;
*textLength = textPosition;
}
return S_OK;
}
DWRITE_READING_DIRECTION STDMETHODCALLTYPE
TextAnalysis::GetParagraphReadingDirection()
{
// We support only a single reading direction.
return mReadingDirection;
}
IFACEMETHODIMP
TextAnalysis::GetLocaleName(UINT32 textPosition,
OUT UINT32* textLength,
OUT WCHAR const** localeName)
{
// Single locale name is used, valid until the end of the string.
*localeName = mLocaleName;
*textLength = mTextLength - textPosition;
return S_OK;
}
IFACEMETHODIMP
TextAnalysis::GetNumberSubstitution(UINT32 textPosition,
OUT UINT32* textLength,
OUT IDWriteNumberSubstitution** numberSubstitution)
{
// We do not support number substitution.
*numberSubstitution = nullptr;
*textLength = mTextLength - textPosition;
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// IDWriteTextAnalysisSink implementation
IFACEMETHODIMP
TextAnalysis::SetLineBreakpoints(UINT32 textPosition,
UINT32 textLength,
DWRITE_LINE_BREAKPOINT const* lineBreakpoints)
{
// We don't use this for now.
return S_OK;
}
IFACEMETHODIMP
TextAnalysis::SetScriptAnalysis(UINT32 textPosition,
UINT32 textLength,
DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis)
{
SetCurrentRun(textPosition);
SplitCurrentRun(textPosition);
while (textLength > 0) {
Run *run = FetchNextRun(&textLength);
run->mScript = *scriptAnalysis;
}
return S_OK;
}
IFACEMETHODIMP
TextAnalysis::SetBidiLevel(UINT32 textPosition,
UINT32 textLength,
UINT8 explicitLevel,
UINT8 resolvedLevel)
{
// We don't use this for now.
return S_OK;
}
IFACEMETHODIMP
TextAnalysis::SetNumberSubstitution(UINT32 textPosition,
UINT32 textLength,
IDWriteNumberSubstitution* numberSubstitution)
{
// We don't use this for now.
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// Run modification.
TextAnalysis::Run *
TextAnalysis::FetchNextRun(IN OUT UINT32* 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
// being returned.
Run *origRun = mCurrentRun;
// Split the tail if needed (the length remaining is less than the
// current run's size).
if (*textLength < mCurrentRun->mTextLength) {
SplitCurrentRun(mCurrentRun->mTextStart + *textLength);
} else {
// Just advance the current run.
mCurrentRun = mCurrentRun->nextRun;
}
*textLength -= origRun->mTextLength;
// Return a reference to the run that was just current.
return origRun;
}
void TextAnalysis::SetCurrentRun(UINT32 textPosition)
{
// Move the current run to the given position.
// Since the analyzers generally return results in a forward manner,
// this will usually just return early. If not, find the
// corresponding run for the text position.
if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
return;
}
for (Run *run = &mRunHead; run; run = run->nextRun) {
if (run->ContainsTextPosition(textPosition)) {
mCurrentRun = run;
return;
}
}
NS_NOTREACHED("We should always be able to find the text position in one \
of our runs");
}
void TextAnalysis::SplitCurrentRun(UINT32 splitPosition)
{
if (!mCurrentRun) {
NS_ASSERTION(false, "SplitCurrentRun called without current run.");
// Shouldn't be calling this when no current run is set!
return;
}
// Split the current run.
if (splitPosition <= mCurrentRun->mTextStart) {
// No need to split, already the start of a run
// or before it. Usually the first.
return;
}
Run *newRun = new Run;
*newRun = *mCurrentRun;
// Insert the new run in our linked list.
newRun->nextRun = mCurrentRun->nextRun;
mCurrentRun->nextRun = newRun;
// Adjust runs' text positions and lengths.
UINT32 splitPoint = splitPosition - mCurrentRun->mTextStart;
newRun->mTextStart += splitPoint;
newRun->mTextLength -= splitPoint;
mCurrentRun->mTextLength = splitPoint;
mCurrentRun = newRun;
}

View File

@ -1,146 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_DWRITETEXTANALYSIS_H
#define GFX_DWRITETEXTANALYSIS_H
#include "gfxDWriteCommon.h"
// Helper source/sink class for text analysis.
class TextAnalysis
: public IDWriteTextAnalysisSource,
public IDWriteTextAnalysisSink
{
public:
// IUnknown interface
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
{
if (iid == __uuidof(IDWriteTextAnalysisSource)) {
*ppObject = static_cast<IDWriteTextAnalysisSource*>(this);
return S_OK;
} else if (iid == __uuidof(IDWriteTextAnalysisSink)) {
*ppObject = static_cast<IDWriteTextAnalysisSink*>(this);
return S_OK;
} else if (iid == __uuidof(IUnknown)) {
*ppObject =
static_cast<IUnknown*>(static_cast<IDWriteTextAnalysisSource*>(this));
return S_OK;
} else {
return E_NOINTERFACE;
}
}
IFACEMETHOD_(ULONG, AddRef)()
{
return 1;
}
IFACEMETHOD_(ULONG, Release)()
{
return 1;
}
// A single contiguous run of characters containing the same analysis
// 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
// text
DWRITE_SCRIPT_ANALYSIS mScript;
UINT8 mBidiLevel;
bool mIsSideways;
inline bool ContainsTextPosition(UINT32 aTextPosition) const
{
return aTextPosition >= mTextStart
&& aTextPosition < mTextStart + mTextLength;
}
Run *nextRun;
};
public:
TextAnalysis(const wchar_t* text,
UINT32 textLength,
const wchar_t* localeName,
DWRITE_READING_DIRECTION readingDirection);
~TextAnalysis();
STDMETHODIMP GenerateResults(IDWriteTextAnalyzer* textAnalyzer,
Run **runHead);
// IDWriteTextAnalysisSource implementation
IFACEMETHODIMP GetTextAtPosition(UINT32 textPosition,
OUT WCHAR const** textString,
OUT UINT32* textLength);
IFACEMETHODIMP GetTextBeforePosition(UINT32 textPosition,
OUT WCHAR const** textString,
OUT UINT32* textLength);
IFACEMETHODIMP_(DWRITE_READING_DIRECTION)
GetParagraphReadingDirection() throw();
IFACEMETHODIMP GetLocaleName(UINT32 textPosition,
OUT UINT32* textLength,
OUT WCHAR const** localeName);
IFACEMETHODIMP
GetNumberSubstitution(UINT32 textPosition,
OUT UINT32* textLength,
OUT IDWriteNumberSubstitution** numberSubstitution);
// IDWriteTextAnalysisSink implementation
IFACEMETHODIMP
SetScriptAnalysis(UINT32 textPosition,
UINT32 textLength,
DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis);
IFACEMETHODIMP
SetLineBreakpoints(UINT32 textPosition,
UINT32 textLength,
const DWRITE_LINE_BREAKPOINT* lineBreakpoints);
IFACEMETHODIMP SetBidiLevel(UINT32 textPosition,
UINT32 textLength,
UINT8 explicitLevel,
UINT8 resolvedLevel);
IFACEMETHODIMP
SetNumberSubstitution(UINT32 textPosition,
UINT32 textLength,
IDWriteNumberSubstitution* numberSubstitution);
protected:
Run *FetchNextRun(IN OUT UINT32* textLength);
void SetCurrentRun(UINT32 textPosition);
void SplitCurrentRun(UINT32 splitPosition);
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_t* mText;
const wchar_t* mLocaleName;
DWRITE_READING_DIRECTION mReadingDirection;
// Current processing state.
Run *mCurrentRun;
// Output is a list of runs starting here
Run mRunHead;
};
#endif /* GFX_DWRITETEXTANALYSIS_H */

View File

@ -25,7 +25,7 @@ public:
virtual uint32_t GetSpaceGlyph();
virtual bool ProvidesGetGlyph() const { return true; }
virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector);
virtual bool ProvidesGlyphWidths() { return true; }
virtual bool ProvidesGlyphWidths() const { return true; }
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; };

View File

@ -3979,7 +3979,6 @@ gfxFont::ShapeText(gfxContext *aContext,
if (!ok) {
if (!mPlatformShaper) {
CreatePlatformShaper();
NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
}
if (mPlatformShaper) {
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,

View File

@ -1951,7 +1951,7 @@ protected:
// subclasses may provide (possibly hinted) glyph widths (in font units);
// if they do not override this, harfbuzz will use unhinted widths
// derived from the font tables
virtual bool ProvidesGlyphWidths() {
virtual bool ProvidesGlyphWidths() const {
return false;
}

View File

@ -8,8 +8,6 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/WindowsVersion.h"
#include "gfxGDIShaper.h"
#include "gfxUniscribeShaper.h"
#include "gfxHarfBuzzShaper.h"
#include <algorithm>
#include "gfxGraphiteShaper.h"
@ -56,9 +54,7 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
if (FontCanSupportGraphite()) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
if (FontCanSupportHarfBuzz()) {
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}
gfxGDIFont::~gfxGDIFont()
@ -75,12 +71,6 @@ gfxGDIFont::~gfxGDIFont()
delete mMetrics;
}
void
gfxGDIFont::CreatePlatformShaper()
{
mPlatformShaper = new gfxGDIShaper(this);
}
gfxFont*
gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
{
@ -88,29 +78,6 @@ gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
&mStyle, mNeedsBold, anAAOption);
}
static bool
UseUniscribe(gfxShapedText *aShapedText,
char16ptr_t aText,
uint32_t aLength)
{
uint32_t flags = aShapedText->Flags();
bool useGDI;
bool isXP = !IsVistaOrLater();
// bug 561304 - Uniscribe bug produces bad positioning at certain
// font sizes on XP, so default to GDI on XP using logic of 3.6
useGDI = isXP &&
(flags &
(gfxTextRunFactory::TEXT_OPTIMIZE_SPEED |
gfxTextRunFactory::TEXT_IS_RTL)
) == gfxTextRunFactory::TEXT_OPTIMIZE_SPEED;
return !useGDI ||
ScriptIsComplex(aText, aLength, SIC_COMPLEX) == S_OK;
}
bool
gfxGDIFont::ShapeText(gfxContext *aContext,
const char16_t *aText,
@ -128,8 +95,6 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
return false;
}
bool ok = false;
// Ensure the cairo font is set up, so there's no risk it'll fall back to
// creating a "toy" font internally (see bug 544617).
// We must check that this succeeded, otherwise we risk cairo creating the
@ -138,83 +103,8 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
return false;
}
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
if (!ok && mHarfBuzzShaper) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript) ||
(!IsVistaOrLater() &&
ScriptShapingType(aScript) == SHAPING_INDIC &&
!Preferences::GetBool("gfx.font_rendering.winxp-indic-uniscribe",
false))) {
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
}
}
if (!ok) {
GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry());
bool preferUniscribe =
(!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI;
if (preferUniscribe || UseUniscribe(aShapedText, aText, aLength)) {
// first try Uniscribe
if (!mUniscribeShaper) {
mUniscribeShaper = new gfxUniscribeShaper(this);
}
ok = mUniscribeShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
if (!ok) {
// fallback to GDI shaping
if (!mPlatformShaper) {
CreatePlatformShaper();
}
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset,
aLength, aScript, aShapedText);
}
} else {
// first use GDI
if (!mPlatformShaper) {
CreatePlatformShaper();
}
ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
if (!ok) {
// try Uniscribe if GDI failed
if (!mUniscribeShaper) {
mUniscribeShaper = new gfxUniscribeShaper(this);
}
// use Uniscribe shaping
ok = mUniscribeShaper->ShapeText(aContext, aText,
aOffset, aLength,
aScript, aShapedText);
}
}
#if DEBUG
if (!ok) {
NS_ConvertUTF16toUTF8 name(GetName());
char msg[256];
sprintf(msg,
"text shaping with both uniscribe and GDI failed for"
" font: %s",
name.get());
NS_WARNING(msg);
}
#endif
}
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return ok;
return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
aShapedText, aPreferPlatformShaping);
}
const gfxFont::Metrics&
@ -537,6 +427,42 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize,
}
}
uint32_t
gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
{
// Callback used only for fonts that lack a 'cmap' table.
// We don't support variation selector sequences or non-BMP characters
// in the legacy bitmap, vector or postscript fonts that might use
// this code path.
if (aUnicode > 0xffff || aVarSelector) {
return 0;
}
if (!mGlyphIDs) {
mGlyphIDs = new nsDataHashtable<nsUint32HashKey,uint32_t>(128);
}
uint32_t gid;
if (mGlyphIDs->Get(aUnicode, &gid)) {
return gid;
}
AutoDC dc;
AutoSelectFont fs(dc.GetDC(), GetHFONT());
wchar_t ch = aUnicode;
WORD glyph;
DWORD ret = GetGlyphIndicesW(dc.GetDC(), &ch, 1, &glyph,
GGI_MARK_NONEXISTING_GLYPHS);
if (ret == GDI_ERROR || glyph == 0xFFFF) {
return 0;
}
mGlyphIDs->Put(aUnicode, glyph);
return glyph;
}
int32_t
gfxGDIFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID)
{

View File

@ -49,7 +49,15 @@ public:
/* required for MathML to suppress effects of ClearType "padding" */
virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption);
virtual bool ProvidesGlyphWidths() { return true; }
// If the font has a cmap table, we handle it purely with harfbuzz;
// but if not (e.g. .fon fonts), we'll use a GDI callback to get glyphs.
virtual bool ProvidesGetGlyph() const {
return !mFontEntry->HasCmapTable();
}
virtual uint32_t GetGlyph(uint32_t aUnicode, uint32_t aVarSelector);
virtual bool ProvidesGlyphWidths() const { return true; }
// get hinted glyph width in pixels as 16.16 fixed-point value
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID);
@ -62,9 +70,7 @@ public:
virtual FontType GetType() const { return FONT_TYPE_GDI; }
protected:
virtual void CreatePlatformShaper();
/* override to check for uniscribe failure and fall back to GDI */
/* override to ensure the cairo font is set up properly */
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
@ -80,10 +86,6 @@ protected:
// italics.
void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
// mPlatformShaper is used for the GDI shaper, mUniscribeShaper
// for the Uniscribe version if needed
nsAutoPtr<gfxFontShaper> mUniscribeShaper;
HFONT mFont;
cairo_font_face_t *mFontFace;
@ -92,6 +94,9 @@ protected:
bool mNeedsBold;
// cache of glyph IDs (used for non-sfnt fonts only)
nsAutoPtr<nsDataHashtable<nsUint32HashKey,uint32_t> > mGlyphIDs;
// cache of glyph widths in 16.16 fixed-point pixels
nsAutoPtr<nsDataHashtable<nsUint32HashKey,int32_t> > mGlyphWidths;
};

View File

@ -1,96 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//#define FORCE_PR_LOG
#include "gfxGDIShaper.h"
/**********************************************************************
*
* class gfxGDIShaper
*
**********************************************************************/
bool
gfxGDIShaper::ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
DCFromContext dc(aContext);
AutoSelectFont selectFont(dc, static_cast<gfxGDIFont*>(mFont)->GetHFONT());
uint32_t length = aLength;
AutoFallibleTArray<WORD,500> glyphArray;
if (!glyphArray.SetLength(length)) {
return false;
}
WORD *glyphs = glyphArray.Elements();
DWORD ret = ::GetGlyphIndicesW(dc, char16ptr_t(aText), length,
glyphs, GGI_MARK_NONEXISTING_GLYPHS);
if (ret == GDI_ERROR) {
return false;
}
for (int k = 0; k < length; k++) {
if (glyphs[k] == 0xFFFF)
return false;
}
SIZE size;
AutoFallibleTArray<int,500> partialWidthArray;
if (!partialWidthArray.SetLength(length)) {
return false;
}
BOOL success = ::GetTextExtentExPointI(dc,
glyphs,
length,
INT_MAX,
nullptr,
partialWidthArray.Elements(),
&size);
if (!success) {
return false;
}
gfxTextRun::CompressedGlyph g;
gfxTextRun::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
uint32_t i;
int32_t lastWidth = 0;
int32_t appUnitsPerDevPixel = aShapedText->GetAppUnitsPerDevUnit();
for (i = 0; i < length; ++i) {
uint32_t offset = aOffset + i;
int32_t advancePixels = partialWidthArray[i] - lastWidth;
lastWidth = partialWidthArray[i];
int32_t advanceAppUnits = advancePixels * appUnitsPerDevPixel;
WCHAR glyph = glyphs[i];
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(aText[i]),
"Invalid character detected!");
bool atClusterStart = charGlyphs[offset].IsClusterStart();
if (advanceAppUnits >= 0 &&
gfxShapedWord::CompressedGlyph::IsSimpleAdvance(advanceAppUnits) &&
gfxShapedWord::CompressedGlyph::IsSimpleGlyphID(glyph) &&
atClusterStart)
{
charGlyphs[offset].SetSimpleGlyph(advanceAppUnits, glyph);
} else {
gfxShapedText::DetailedGlyph details;
details.mGlyphID = glyph;
details.mAdvance = advanceAppUnits;
details.mXOffset = 0;
details.mYOffset = 0;
aShapedText->SetGlyphs(offset,
g.SetComplex(atClusterStart, true, 1),
&details);
}
}
return true;
}

View File

@ -1,33 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_GDISHAPER_H
#define GFX_GDISHAPER_H
#include "gfxGDIFont.h"
class gfxGDIShaper : public gfxFontShaper
{
public:
gfxGDIShaper(gfxGDIFont *aFont)
: gfxFontShaper(aFont)
{
MOZ_COUNT_CTOR(gfxGDIShaper);
}
virtual ~gfxGDIShaper()
{
MOZ_COUNT_DTOR(gfxGDIShaper);
}
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
};
#endif /* GFX_GDISHAPER_H */

View File

@ -1,527 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gfxTypes.h"
#include "gfxContext.h"
#include "gfxUniscribeShaper.h"
#include "gfxWindowsPlatform.h"
#include "gfxFontTest.h"
#include "cairo.h"
#include "cairo-win32.h"
#include <windows.h>
#include "nsTArray.h"
#include "prinit.h"
/**********************************************************************
*
* class gfxUniscribeShaper
*
**********************************************************************/
#define ESTIMATE_MAX_GLYPHS(L) (((3 * (L)) >> 1) + 16)
class UniscribeItem
{
public:
UniscribeItem(gfxContext *aContext, HDC aDC,
gfxUniscribeShaper *aShaper,
const char16_t *aString, uint32_t aLength,
SCRIPT_ITEM *aItem, uint32_t aIVS) :
mContext(aContext), mDC(aDC),
mShaper(aShaper),
mItemString(aString), mItemLength(aLength),
mAlternativeString(nullptr), mScriptItem(aItem),
mScript(aItem->a.eScript),
mNumGlyphs(0), mMaxGlyphs(ESTIMATE_MAX_GLYPHS(aLength)),
mFontSelected(false), mIVS(aIVS)
{
// See bug 394751 for details.
NS_ASSERTION(mMaxGlyphs < 65535,
"UniscribeItem is too big, ScriptShape() will fail!");
}
~UniscribeItem() {
free(mAlternativeString);
}
bool AllocateBuffers() {
return (mGlyphs.SetLength(mMaxGlyphs) &&
mClusters.SetLength(mItemLength + 1) &&
mAttr.SetLength(mMaxGlyphs));
}
/* possible return values:
* S_OK - things succeeded
* GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping()
*/
HRESULT Shape() {
HRESULT rv;
HDC shapeDC = nullptr;
char16ptr_t str = mAlternativeString ? mAlternativeString : mItemString;
mScriptItem->a.fLogicalOrder = true;
SCRIPT_ANALYSIS sa = mScriptItem->a;
while (true) {
rv = ScriptShape(shapeDC, mShaper->ScriptCache(),
str, mItemLength,
mMaxGlyphs, &sa,
mGlyphs.Elements(), mClusters.Elements(),
mAttr.Elements(), &mNumGlyphs);
if (rv == E_OUTOFMEMORY) {
mMaxGlyphs *= 2;
if (!mGlyphs.SetLength(mMaxGlyphs) ||
!mAttr.SetLength(mMaxGlyphs)) {
return E_OUTOFMEMORY;
}
continue;
}
// Uniscribe can't do shaping with some fonts, so it sets the
// fNoGlyphIndex flag in the SCRIPT_ANALYSIS structure to indicate
// this. This occurs with CFF fonts loaded with
// AddFontMemResourceEx but it's not clear what the other cases
// are. We return an error so our caller can try fallback shaping.
// see http://msdn.microsoft.com/en-us/library/ms776520(VS.85).aspx
if (sa.fNoGlyphIndex) {
return GDI_ERROR;
}
if (rv == E_PENDING) {
if (shapeDC == mDC) {
// we already tried this once, something failed, give up
return E_PENDING;
}
SelectFont();
shapeDC = mDC;
continue;
}
// http://msdn.microsoft.com/en-us/library/dd368564(VS.85).aspx:
// Uniscribe will return this if "the font corresponding to the
// DC does not support the script required by the run...".
// In this case, we'll set the script code to SCRIPT_UNDEFINED
// and try again, so that we'll at least get glyphs even though
// they won't necessarily have proper shaping.
// (We probably shouldn't have selected this font at all,
// but it's too late to fix that here.)
if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
sa.eScript = SCRIPT_UNDEFINED;
NS_WARNING("Uniscribe says font does not support script needed");
continue;
}
// Prior to Windows 7, Uniscribe didn't support Ideographic Variation
// Selectors. Replace the UVS glyph manually.
if (mIVS) {
uint32_t lastChar = str[mItemLength - 1];
if (NS_IS_LOW_SURROGATE(lastChar)
&& NS_IS_HIGH_SURROGATE(str[mItemLength - 2])) {
lastChar = SURROGATE_TO_UCS4(str[mItemLength - 2], lastChar);
}
uint16_t glyphId = mShaper->GetFont()->GetUVSGlyph(lastChar, mIVS);
if (glyphId) {
mGlyphs[mNumGlyphs - 1] = glyphId;
}
}
return rv;
}
}
bool ShapingEnabled() {
return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
}
void DisableShaping() {
mScriptItem->a.eScript = SCRIPT_UNDEFINED;
// Note: If we disable the shaping by using SCRIPT_UNDEFINED and
// the string has the surrogate pair, ScriptShape API is
// *sometimes* crashed. Therefore, we should replace the surrogate
// pair to U+FFFD. See bug 341500.
GenerateAlternativeString();
}
void EnableShaping() {
mScriptItem->a.eScript = mScript;
if (mAlternativeString) {
free(mAlternativeString);
mAlternativeString = nullptr;
}
}
bool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, uint32_t aGlyphIndex) {
return (mGlyphs[aGlyphIndex] == aSFP->wgDefault);
}
HRESULT Place() {
HRESULT rv;
HDC placeDC = nullptr;
if (!mOffsets.SetLength(mNumGlyphs) ||
!mAdvances.SetLength(mNumGlyphs)) {
return E_OUTOFMEMORY;
}
SCRIPT_ANALYSIS sa = mScriptItem->a;
while (true) {
rv = ScriptPlace(placeDC, mShaper->ScriptCache(),
mGlyphs.Elements(), mNumGlyphs,
mAttr.Elements(), &sa,
mAdvances.Elements(), mOffsets.Elements(), nullptr);
if (rv == E_PENDING) {
SelectFont();
placeDC = mDC;
continue;
}
if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
sa.eScript = SCRIPT_UNDEFINED;
continue;
}
break;
}
return rv;
}
void ScriptFontProperties(SCRIPT_FONTPROPERTIES *sfp) {
HRESULT rv;
memset(sfp, 0, sizeof(SCRIPT_FONTPROPERTIES));
sfp->cBytes = sizeof(SCRIPT_FONTPROPERTIES);
rv = ScriptGetFontProperties(nullptr, mShaper->ScriptCache(),
sfp);
if (rv == E_PENDING) {
SelectFont();
rv = ScriptGetFontProperties(mDC, mShaper->ScriptCache(),
sfp);
}
}
void SaveGlyphs(gfxShapedText *aShapedText, uint32_t aOffset) {
uint32_t offsetInRun = mScriptItem->iCharPos;
// XXX We should store this in the item and only fetch it once
SCRIPT_FONTPROPERTIES sfp;
ScriptFontProperties(&sfp);
uint32_t offset = 0;
nsAutoTArray<gfxShapedText::DetailedGlyph,1> detailedGlyphs;
gfxShapedText::CompressedGlyph g;
gfxShapedText::CompressedGlyph *charGlyphs =
aShapedText->GetCharacterGlyphs();
const uint32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
while (offset < mItemLength) {
uint32_t runOffset = aOffset + offsetInRun + offset;
bool atClusterStart = charGlyphs[runOffset].IsClusterStart();
if (offset > 0 && mClusters[offset] == mClusters[offset - 1]) {
gfxShapedText::CompressedGlyph &g = charGlyphs[runOffset];
NS_ASSERTION(!g.IsSimpleGlyph(), "overwriting a simple glyph");
g.SetComplex(atClusterStart, false, 0);
} else {
// Count glyphs for this character
uint32_t k = mClusters[offset];
uint32_t glyphCount = mNumGlyphs - k;
uint32_t nextClusterOffset;
bool missing = IsGlyphMissing(&sfp, k);
for (nextClusterOffset = offset + 1; nextClusterOffset < mItemLength; ++nextClusterOffset) {
if (mClusters[nextClusterOffset] > k) {
glyphCount = mClusters[nextClusterOffset] - k;
break;
}
}
uint32_t j;
for (j = 1; j < glyphCount; ++j) {
if (IsGlyphMissing(&sfp, k + j)) {
missing = true;
}
}
int32_t advance = mAdvances[k]*appUnitsPerDevUnit;
WORD glyph = mGlyphs[k];
NS_ASSERTION(!gfxFontGroup::IsInvalidChar(mItemString[offset]),
"invalid character detected");
if (missing) {
if (NS_IS_HIGH_SURROGATE(mItemString[offset]) &&
offset + 1 < mItemLength &&
NS_IS_LOW_SURROGATE(mItemString[offset + 1])) {
aShapedText->SetMissingGlyph(runOffset,
SURROGATE_TO_UCS4(mItemString[offset],
mItemString[offset + 1]),
mShaper->GetFont());
} else {
aShapedText->SetMissingGlyph(runOffset, mItemString[offset],
mShaper->GetFont());
}
} else if (glyphCount == 1 && advance >= 0 &&
mOffsets[k].dv == 0 && mOffsets[k].du == 0 &&
gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxShapedText::CompressedGlyph::IsSimpleGlyphID(glyph) &&
atClusterStart)
{
charGlyphs[runOffset].SetSimpleGlyph(advance, glyph);
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(glyphCount - detailedGlyphs.Length()))
return;
}
uint32_t i;
for (i = 0; i < glyphCount; ++i) {
gfxTextRun::DetailedGlyph *details = &detailedGlyphs[i];
details->mGlyphID = mGlyphs[k + i];
details->mAdvance = mAdvances[k + i] * appUnitsPerDevUnit;
details->mXOffset = float(mOffsets[k + i].du) * appUnitsPerDevUnit *
aShapedText->GetDirection();
details->mYOffset = - float(mOffsets[k + i].dv) * appUnitsPerDevUnit;
}
aShapedText->SetGlyphs(runOffset,
g.SetComplex(atClusterStart, true,
glyphCount),
detailedGlyphs.Elements());
}
}
++offset;
}
}
void SelectFont() {
if (mFontSelected)
return;
cairo_t *cr = mContext->GetCairo();
cairo_set_font_face(cr, mShaper->GetFont()->CairoFontFace());
cairo_set_font_size(cr, mShaper->GetFont()->GetAdjustedSize());
cairo_scaled_font_t *scaledFont = mShaper->GetFont()->CairoScaledFont();
cairo_win32_scaled_font_select_font(scaledFont, mDC);
mFontSelected = true;
}
private:
void GenerateAlternativeString() {
if (mAlternativeString)
free(mAlternativeString);
mAlternativeString = (char16_t *)malloc(mItemLength * sizeof(char16_t));
if (!mAlternativeString)
return;
memcpy((void *)mAlternativeString, (const void *)mItemString,
mItemLength * sizeof(char16_t));
for (uint32_t i = 0; i < mItemLength; i++) {
if (NS_IS_HIGH_SURROGATE(mItemString[i]) || NS_IS_LOW_SURROGATE(mItemString[i]))
mAlternativeString[i] = char16_t(0xFFFD);
}
}
private:
nsRefPtr<gfxContext> mContext;
HDC mDC;
gfxUniscribeShaper *mShaper;
SCRIPT_ITEM *mScriptItem;
WORD mScript;
public:
// these point to the full string/length of the item
const char16_t *mItemString;
const uint32_t mItemLength;
private:
char16_t *mAlternativeString;
#define AVERAGE_ITEM_LENGTH 40
AutoFallibleTArray<WORD, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mGlyphs;
AutoFallibleTArray<WORD, AVERAGE_ITEM_LENGTH + 1> mClusters;
AutoFallibleTArray<SCRIPT_VISATTR, uint32_t(ESTIMATE_MAX_GLYPHS(AVERAGE_ITEM_LENGTH))> mAttr;
AutoFallibleTArray<GOFFSET, 2 * AVERAGE_ITEM_LENGTH> mOffsets;
AutoFallibleTArray<int, 2 * AVERAGE_ITEM_LENGTH> mAdvances;
#undef AVERAGE_ITEM_LENGTH
int mMaxGlyphs;
int mNumGlyphs;
uint32_t mIVS;
bool mFontSelected;
};
class Uniscribe
{
public:
Uniscribe(const char16_t *aString,
gfxShapedText *aShapedText,
uint32_t aOffset, uint32_t aLength):
mString(aString), mShapedText(aShapedText),
mOffset(aOffset), mLength(aLength)
{
}
void Init() {
memset(&mControl, 0, sizeof(SCRIPT_CONTROL));
memset(&mState, 0, sizeof(SCRIPT_STATE));
// Lock the direction. Don't allow the itemizer to change directions
// based on character type.
mState.uBidiLevel = mShapedText->IsRightToLeft() ? 1 : 0;
mState.fOverrideDirection = true;
}
public:
int Itemize() {
HRESULT rv;
int maxItems = 5;
Init();
// Allocate space for one more item than expected, to handle a rare
// overflow in ScriptItemize (pre XP SP2). See bug 366643.
if (!mItems.SetLength(maxItems + 1)) {
return 0;
}
while ((rv = ScriptItemize(mString, mLength,
maxItems, &mControl, &mState,
mItems.Elements(), &mNumItems)) == E_OUTOFMEMORY) {
maxItems *= 2;
if (!mItems.SetLength(maxItems + 1)) {
return 0;
}
Init();
}
return mNumItems;
}
SCRIPT_ITEM *ScriptItem(uint32_t i) {
NS_ASSERTION(i <= (uint32_t)mNumItems, "Trying to get out of bounds item");
return &mItems[i];
}
private:
char16ptr_t mString;
gfxShapedText *mShapedText;
uint32_t mOffset;
uint32_t mLength;
SCRIPT_CONTROL mControl;
SCRIPT_STATE mState;
FallibleTArray<SCRIPT_ITEM> mItems;
int mNumItems;
};
bool
gfxUniscribeShaper::ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText)
{
DCFromContext aDC(aContext);
bool result = true;
HRESULT rv;
Uniscribe us(aText, aShapedText, aOffset, aLength);
/* itemize the string */
int numItems = us.Itemize();
uint32_t length = aLength;
SaveDC(aDC);
uint32_t ivs = 0;
for (int i = 0; i < numItems; ++i) {
int iCharPos = us.ScriptItem(i)->iCharPos;
int iCharPosNext = us.ScriptItem(i+1)->iCharPos;
if (ivs) {
iCharPos += 2;
if (iCharPos >= iCharPosNext) {
ivs = 0;
continue;
}
}
if (i+1 < numItems && iCharPosNext <= length - 2
&& aText[iCharPosNext] == H_SURROGATE(kUnicodeVS17)
&& uint32_t(aText[iCharPosNext + 1]) - L_SURROGATE(kUnicodeVS17)
<= L_SURROGATE(kUnicodeVS256) - L_SURROGATE(kUnicodeVS17)) {
ivs = SURROGATE_TO_UCS4(aText[iCharPosNext],
aText[iCharPosNext + 1]);
} else {
ivs = 0;
}
UniscribeItem item(aContext, aDC, this,
aText + iCharPos,
iCharPosNext - iCharPos,
us.ScriptItem(i), ivs);
if (!item.AllocateBuffers()) {
result = false;
break;
}
if (!item.ShapingEnabled()) {
item.EnableShaping();
}
rv = item.Shape();
if (FAILED(rv)) {
// we know we have the glyphs to display this font already
// so Uniscribe just doesn't know how to shape the script.
// Render the glyphs without shaping.
item.DisableShaping();
rv = item.Shape();
}
#ifdef DEBUG
if (FAILED(rv)) {
NS_WARNING("Uniscribe failed to shape with font");
}
#endif
if (SUCCEEDED(rv)) {
rv = item.Place();
#ifdef DEBUG
if (FAILED(rv)) {
// crap fonts may fail when placing (e.g. funky free fonts)
NS_WARNING("Uniscribe failed to place with font");
}
#endif
}
if (FAILED(rv)) {
// Uniscribe doesn't like this font for some reason.
// Returning FALSE will make the gfxGDIFont retry with the
// "dumb" GDI shaper, unless useUniscribeOnly was set.
result = false;
break;
}
item.SaveGlyphs(aShapedText, aOffset);
}
RestoreDC(aDC, -1);
return result;
}

View File

@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_UNISCRIBESHAPER_H
#define GFX_UNISCRIBESHAPER_H
#include "gfxTypes.h"
#include "gfxGDIFont.h"
#include <usp10.h>
#include <cairo-win32.h>
class gfxUniscribeShaper : public gfxFontShaper
{
public:
gfxUniscribeShaper(gfxGDIFont *aFont)
: gfxFontShaper(aFont)
, mScriptCache(nullptr)
{
MOZ_COUNT_CTOR(gfxUniscribeShaper);
}
virtual ~gfxUniscribeShaper()
{
MOZ_COUNT_DTOR(gfxUniscribeShaper);
}
virtual bool ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
gfxShapedText *aShapedText);
SCRIPT_CACHE *ScriptCache() { return &mScriptCache; }
gfxGDIFont *GetFont() { return static_cast<gfxGDIFont*>(mFont); }
private:
SCRIPT_CACHE mScriptCache;
enum {
kUnicodeVS17 = gfxFontUtils::kUnicodeVS17,
kUnicodeVS256 = gfxFontUtils::kUnicodeVS256
};
};
#endif /* GFX_UNISCRIBESHAPER_H */

View File

@ -170,13 +170,11 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'gfxWindowsPlatform.h',
'gfxWindowsSurface.h',
]
# gfxGDIFontList.cpp and gfxGDIShaper.cpp force NSPR logging, so they cannot be built in unified mode.
# gfxGDIFontList.cpp forces NSPR logging, so it cannot be built in unified mode.
SOURCES += [
'gfxGDIFont.cpp',
'gfxGDIFontList.cpp',
'gfxGDIShaper.cpp',
'gfxPDFSurface.cpp',
'gfxUniscribeShaper.cpp',
'gfxWindowsNativeDrawing.cpp',
'gfxWindowsPlatform.cpp',
'gfxWindowsSurface.cpp',
@ -188,8 +186,6 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'gfxDWriteCommon.cpp',
'gfxDWriteFontList.cpp',
'gfxDWriteFonts.cpp',
'gfxDWriteShaper.cpp',
'gfxDWriteTextAnalysis.cpp',
]
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.