Font variations.
Multiple Master and TrueType fonts support variation axes. This implements back-end support for axes on platforms which support it. Committed: https://skia.googlesource.com/skia/+/05773ed30920c0214d1433c07cf6360a05476c97 Committed: https://skia.googlesource.com/skia/+/3489ee0f4fa34f124f9de090d12bdc2107d52aa9 Review URL: https://codereview.chromium.org/1027373002
This commit is contained in:
parent
d223eb36a0
commit
41868fe562
95
gm/fontscalerdistortable.cpp
Normal file
95
gm/fontscalerdistortable.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include "gm.h"
|
||||
#include "Resources.h"
|
||||
#include "SkFixed.h"
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkFontMgr.h"
|
||||
#include "SkTypeface.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
class FontScalerDistortableGM : public GM {
|
||||
public:
|
||||
FontScalerDistortableGM() {
|
||||
this->setBGColor(0xFFFFFFFF);
|
||||
}
|
||||
|
||||
virtual ~FontScalerDistortableGM() { }
|
||||
|
||||
protected:
|
||||
|
||||
SkString onShortName() override {
|
||||
return SkString("fontscalerdistortable");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(550, 700);
|
||||
}
|
||||
|
||||
static void rotate_about(SkCanvas* canvas, SkScalar degrees, SkScalar px, SkScalar py) {
|
||||
canvas->translate(px, py);
|
||||
canvas->rotate(degrees);
|
||||
canvas->translate(-px, -py);
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setLCDRenderText(true);
|
||||
|
||||
SkAutoTDelete<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
|
||||
if (!distortable) {
|
||||
return;
|
||||
}
|
||||
const char* text = "abc";
|
||||
const size_t textLen = strlen(text);
|
||||
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
SkScalar x = SkIntToScalar(10);
|
||||
SkScalar y = SkIntToScalar(20);
|
||||
|
||||
SkFixed axis = SkDoubleToFixed(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5)));
|
||||
SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromFontData(
|
||||
new SkFontData(distortable->duplicate(), 0, &axis, 1)));
|
||||
paint.setTypeface(typeface);
|
||||
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
canvas->translate(SkIntToScalar(30 + i * 100), SkIntToScalar(20));
|
||||
rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10);
|
||||
|
||||
{
|
||||
SkPaint p;
|
||||
p.setAntiAlias(true);
|
||||
SkRect r;
|
||||
r.set(x - SkIntToScalar(3), SkIntToScalar(15),
|
||||
x - SkIntToScalar(1), SkIntToScalar(280));
|
||||
canvas->drawRect(r, p);
|
||||
}
|
||||
|
||||
for (int ps = 6; ps <= 22; ps++) {
|
||||
paint.setTextSize(SkIntToScalar(ps));
|
||||
canvas->drawText(text, textLen, x, y, paint);
|
||||
y += paint.getFontMetrics(NULL);
|
||||
}
|
||||
}
|
||||
canvas->translate(0, SkIntToScalar(360));
|
||||
paint.setSubpixelText(true);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static GM* MyFactory(void*) { return new FontScalerDistortableGM; }
|
||||
static GMRegistry reg(MyFactory);
|
||||
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
# Android provides at least FreeType 2.4.0
|
||||
'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020400',
|
||||
'SK_CAN_USE_DLOPEN=0',
|
||||
'SK_ANDROID_FREETYPE_HAS_MM=1',
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "SkWeakRefCnt.h"
|
||||
|
||||
class SkDescriptor;
|
||||
class SkFontData;
|
||||
class SkFontDescriptor;
|
||||
class SkScalerContext;
|
||||
struct SkScalerContextRec;
|
||||
@ -134,6 +135,12 @@ public:
|
||||
*/
|
||||
static SkTypeface* CreateFromStream(SkStreamAsset* stream, int index = 0);
|
||||
|
||||
/** Return a new typeface given font data and configuration. If the data
|
||||
is not valid font data, returns null. Ownership of the font data is
|
||||
transferred, so the caller must not reference it again.
|
||||
*/
|
||||
static SkTypeface* CreateFromFontData(SkFontData*);
|
||||
|
||||
/** Write a unique signature to a stream, sufficient to reconstruct a
|
||||
typeface referencing the same font when Deserialize is called.
|
||||
*/
|
||||
@ -283,6 +290,12 @@ public:
|
||||
*/
|
||||
SkStreamAsset* openStream(int* ttcIndex) const;
|
||||
|
||||
/**
|
||||
* Return the font data, or NULL on failure.
|
||||
* The caller is responsible for deleting the font data.
|
||||
*/
|
||||
SkFontData* createFontData() const;
|
||||
|
||||
/**
|
||||
* Return a scalercontext for the given descriptor. If this fails, then
|
||||
* if allowFailure is true, this returns NULL, else it returns a
|
||||
@ -337,6 +350,9 @@ protected:
|
||||
uint32_t glyphIDsCount) const = 0;
|
||||
|
||||
virtual SkStreamAsset* onOpenStream(int* ttcIndex) const = 0;
|
||||
// TODO: make pure virtual.
|
||||
virtual SkFontData* onCreateFontData() const;
|
||||
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor*, bool* isLocal) const = 0;
|
||||
|
||||
virtual int onCharsToGlyphs(const void* chars, Encoding, uint16_t glyphs[],
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "SkFontStyle.h"
|
||||
|
||||
class SkData;
|
||||
class SkFontData;
|
||||
class SkStreamAsset;
|
||||
class SkString;
|
||||
class SkTypeface;
|
||||
@ -94,6 +95,14 @@ public:
|
||||
*/
|
||||
SkTypeface* createFromStream(SkStreamAsset*, int ttcIndex = 0) const;
|
||||
|
||||
/**
|
||||
* Create a typeface from the specified font data.
|
||||
* Takes ownership of the font data, so the caller should not reference it again.
|
||||
* Will return NULL if the typeface could not be created.
|
||||
* The caller must call unref() on the returned object if it is not null.
|
||||
*/
|
||||
SkTypeface* createFromFontData(SkFontData*) const;
|
||||
|
||||
/**
|
||||
* Create a typeface for the specified fileName and TTC index
|
||||
* (pass 0 for none) or NULL if the file is not found, or its contents are
|
||||
@ -129,6 +138,8 @@ protected:
|
||||
|
||||
virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const = 0;
|
||||
virtual SkTypeface* onCreateFromStream(SkStreamAsset*, int ttcIndex) const = 0;
|
||||
// TODO: make pure virtual.
|
||||
virtual SkTypeface* onCreateFromFontData(SkFontData*) const;
|
||||
virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const = 0;
|
||||
|
||||
virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
|
||||
|
@ -25,6 +25,18 @@
|
||||
<alias name="tahoma" to="sans-serif" />
|
||||
<alias name="verdana" to="sans-serif" />
|
||||
|
||||
<family name="var">
|
||||
<font weight="100" style="normal"><axis tag="wght" stylevalue="0.5"/>Distortable.ttf</font>
|
||||
<font weight="200" style="normal"><axis tag="wght" stylevalue="0.65"/>Distortable.ttf</font>
|
||||
<font weight="300" style="normal"><axis tag="wght" stylevalue="0.85"/>Distortable.ttf</font>
|
||||
<font weight="400" style="normal"><axis tag="wght" stylevalue="1.0"/>Distortable.ttf</font>
|
||||
<font weight="500" style="normal"><axis tag="wght" stylevalue="1.2"/>Distortable.ttf</font>
|
||||
<font weight="600" style="normal"><axis tag="wght" stylevalue="1.4"/>Distortable.ttf</font>
|
||||
<font weight="700" style="normal"><axis tag="wght" stylevalue="1.6"/>Distortable.ttf</font>
|
||||
<font weight="800" style="normal"><axis tag="wght" stylevalue="1.8"/>Distortable.ttf</font>
|
||||
<font weight="900" style="normal"><axis tag="wght" stylevalue="2.0"/>Distortable.ttf</font>
|
||||
</family>
|
||||
|
||||
<family name="sans-serif-condensed">
|
||||
<font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
|
||||
<font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
|
||||
|
@ -43,7 +43,7 @@ public:
|
||||
|
||||
enum FontFlags {
|
||||
kEmpty_FontFlag = 0x0, //!<No flags set
|
||||
kMultiMaster_FontFlag = 0x1, //!<May be true for Type1 or CFF fonts.
|
||||
kMultiMaster_FontFlag = 0x1, //!<May be true for Type1, CFF, or TrueType fonts.
|
||||
kNotEmbeddable_FontFlag = 0x2, //!<May not be embedded.
|
||||
kNotSubsettable_FontFlag = 0x4, //!<May not be subset.
|
||||
};
|
||||
|
@ -17,12 +17,13 @@ enum {
|
||||
|
||||
// These count backwards from 0xFF, so as not to collide with the SFNT
|
||||
// defines for names in its 'name' table.
|
||||
kFontAxes = 0xFC,
|
||||
kFontIndex = 0xFD,
|
||||
kFontFileName = 0xFE, // Remove when MIN_PICTURE_VERSION > 41
|
||||
kSentinel = 0xFF,
|
||||
};
|
||||
|
||||
SkFontDescriptor::SkFontDescriptor(SkTypeface::Style style) : fFontIndex(0), fStyle(style) { }
|
||||
SkFontDescriptor::SkFontDescriptor(SkTypeface::Style style) : fStyle(style) { }
|
||||
|
||||
static void read_string(SkStream* stream, SkString* string) {
|
||||
const uint32_t length = SkToU32(stream->readPackedUInt());
|
||||
@ -40,8 +41,7 @@ static void skip_string(SkStream* stream) {
|
||||
}
|
||||
}
|
||||
|
||||
static void write_string(SkWStream* stream, const SkString& string,
|
||||
uint32_t id) {
|
||||
static void write_string(SkWStream* stream, const SkString& string, uint32_t id) {
|
||||
if (!string.isEmpty()) {
|
||||
stream->writePackedUInt(id);
|
||||
stream->writePackedUInt(string.size());
|
||||
@ -58,9 +58,12 @@ static void write_uint(SkWStream* stream, size_t n, uint32_t id) {
|
||||
stream->writePackedUInt(n);
|
||||
}
|
||||
|
||||
SkFontDescriptor::SkFontDescriptor(SkStream* stream) : fFontIndex(0) {
|
||||
SkFontDescriptor::SkFontDescriptor(SkStream* stream) {
|
||||
fStyle = (SkTypeface::Style)stream->readPackedUInt();
|
||||
|
||||
SkAutoSTMalloc<4, SkFixed> axis;
|
||||
size_t axisCount = 0;
|
||||
size_t index = 0;
|
||||
for (size_t id; (id = stream->readPackedUInt()) != kSentinel;) {
|
||||
switch (id) {
|
||||
case kFontFamilyName:
|
||||
@ -72,8 +75,15 @@ SkFontDescriptor::SkFontDescriptor(SkStream* stream) : fFontIndex(0) {
|
||||
case kPostscriptName:
|
||||
read_string(stream, &fPostscriptName);
|
||||
break;
|
||||
case kFontAxes:
|
||||
axisCount = read_uint(stream);
|
||||
axis.reset(axisCount);
|
||||
for (size_t i = 0; i < axisCount; ++i) {
|
||||
axis[i] = read_uint(stream);
|
||||
}
|
||||
break;
|
||||
case kFontIndex:
|
||||
fFontIndex = read_uint(stream);
|
||||
index = read_uint(stream);
|
||||
break;
|
||||
case kFontFileName: // Remove when MIN_PICTURE_VERSION > 41
|
||||
skip_string(stream);
|
||||
@ -88,7 +98,8 @@ SkFontDescriptor::SkFontDescriptor(SkStream* stream) : fFontIndex(0) {
|
||||
if (length > 0) {
|
||||
SkAutoTUnref<SkData> data(SkData::NewUninitialized(length));
|
||||
if (stream->read(data->writable_data(), length) == length) {
|
||||
fFontData.reset(SkNEW_ARGS(SkMemoryStream, (data)));
|
||||
fFontData.reset(new SkFontData(SkNEW_ARGS(SkMemoryStream, (data)), index,
|
||||
axis, axisCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,16 +110,25 @@ void SkFontDescriptor::serialize(SkWStream* stream) {
|
||||
write_string(stream, fFamilyName, kFontFamilyName);
|
||||
write_string(stream, fFullName, kFullName);
|
||||
write_string(stream, fPostscriptName, kPostscriptName);
|
||||
if (fFontIndex) {
|
||||
write_uint(stream, fFontIndex, kFontIndex);
|
||||
if (fFontData.get()) {
|
||||
if (fFontData->getIndex()) {
|
||||
write_uint(stream, fFontData->getIndex(), kFontIndex);
|
||||
}
|
||||
if (fFontData->getAxisCount()) {
|
||||
write_uint(stream, fFontData->getAxisCount(), kFontAxes);
|
||||
for (int i = 0; i < fFontData->getAxisCount(); ++i) {
|
||||
stream->writePackedUInt(fFontData->getAxis()[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream->writePackedUInt(kSentinel);
|
||||
|
||||
if (fFontData) {
|
||||
size_t length = fFontData->getLength();
|
||||
if (fFontData.get() && fFontData->hasStream()) {
|
||||
SkAutoTDelete<SkStreamAsset> fontData(fFontData->detachStream());
|
||||
size_t length = fontData->getLength();
|
||||
stream->writePackedUInt(length);
|
||||
stream->writeStream(fFontData, length);
|
||||
stream->writeStream(fontData, length);
|
||||
} else {
|
||||
stream->writePackedUInt(0);
|
||||
}
|
||||
|
@ -12,7 +12,42 @@
|
||||
#include "SkString.h"
|
||||
#include "SkTypeface.h"
|
||||
|
||||
class SkFontDescriptor {
|
||||
class SkFontData {
|
||||
public:
|
||||
/** This takes ownership of 'stream'. Makes a copy of the data in 'axis'. */
|
||||
SkFontData(SkStreamAsset* stream, int index, const SkFixed axis[], int axisCount)
|
||||
: fStream(stream), fIndex(index), fAxisCount(axisCount), fAxis(axisCount)
|
||||
{
|
||||
for (int i = 0; i < axisCount; ++i) {
|
||||
fAxis[i] = axis[i];
|
||||
}
|
||||
}
|
||||
SkFontData(const SkFontData& that)
|
||||
: fStream(that.fStream->duplicate())
|
||||
, fIndex(that.fIndex)
|
||||
, fAxisCount(that.fAxisCount)
|
||||
, fAxis(fAxisCount)
|
||||
{
|
||||
for (int i = 0; i < fAxisCount; ++i) {
|
||||
fAxis[i] = that.fAxis[i];
|
||||
}
|
||||
}
|
||||
bool hasStream() const { return fStream.get() != NULL; }
|
||||
SkStreamAsset* duplicateStream() const { return fStream->duplicate(); }
|
||||
SkStreamAsset* detachStream() { return fStream.detach(); }
|
||||
SkStreamAsset* getStream() { return fStream.get(); }
|
||||
int getIndex() const { return fIndex; }
|
||||
int getAxisCount() const { return fAxisCount; }
|
||||
const SkFixed* getAxis() const { return fAxis.get(); }
|
||||
|
||||
private:
|
||||
SkAutoTDelete<SkStreamAsset> fStream;
|
||||
int fIndex;
|
||||
int fAxisCount;
|
||||
SkAutoSTMalloc<4, SkFixed> fAxis;
|
||||
};
|
||||
|
||||
class SkFontDescriptor : SkNoncopyable {
|
||||
public:
|
||||
SkFontDescriptor(SkTypeface::Style = SkTypeface::kNormal);
|
||||
// Does not affect ownership of SkStream.
|
||||
@ -27,25 +62,20 @@ public:
|
||||
const char* getFullName() const { return fFullName.c_str(); }
|
||||
const char* getPostscriptName() const { return fPostscriptName.c_str(); }
|
||||
bool hasFontData() const { return fFontData.get() != NULL; }
|
||||
// Transfers ownership to the caller.
|
||||
SkStreamAsset* transferFontData() { return fFontData.detach(); }
|
||||
int getFontIndex() const { return fFontIndex; }
|
||||
SkFontData* detachFontData() { return fFontData.detach(); }
|
||||
|
||||
void setFamilyName(const char* name) { fFamilyName.set(name); }
|
||||
void setFullName(const char* name) { fFullName.set(name); }
|
||||
void setPostscriptName(const char* name) { fPostscriptName.set(name); }
|
||||
/** Set the font data only if it is necessary for serialization.
|
||||
* This method takes ownership of the stream (both reference and cursor).
|
||||
*/
|
||||
void setFontData(SkStreamAsset* stream) { fFontData.reset(stream); }
|
||||
void setFontIndex(int index) { fFontIndex = index; }
|
||||
* This method takes ownership of the font data. */
|
||||
void setFontData(SkFontData* data) { fFontData.reset(data); }
|
||||
|
||||
private:
|
||||
SkString fFamilyName;
|
||||
SkString fFullName;
|
||||
SkString fPostscriptName;
|
||||
SkAutoTDelete<SkStreamAsset> fFontData;
|
||||
int fFontIndex;
|
||||
SkAutoTDelete<SkFontData> fFontData;
|
||||
|
||||
SkTypeface::Style fStyle;
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkFontMgr.h"
|
||||
#include "SkLazyPtr.h"
|
||||
#include "SkStream.h"
|
||||
@ -133,6 +134,20 @@ SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, int ttcIndex) con
|
||||
return this->onCreateFromStream(stream, ttcIndex);
|
||||
}
|
||||
|
||||
SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const {
|
||||
if (NULL == data) {
|
||||
return NULL;
|
||||
}
|
||||
return this->onCreateFromFontData(data);
|
||||
}
|
||||
|
||||
// This implementation is temporary until it can be made pure virtual.
|
||||
SkTypeface* SkFontMgr::onCreateFromFontData(SkFontData* data) const {
|
||||
SkTypeface* ret = this->createFromStream(data->detachStream(), data->getIndex());
|
||||
delete data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SkTypeface* SkFontMgr::createFromFile(const char path[], int ttcIndex) const {
|
||||
if (NULL == path) {
|
||||
return NULL;
|
||||
|
@ -143,6 +143,11 @@ SkTypeface* SkTypeface::CreateFromStream(SkStreamAsset* stream, int index) {
|
||||
return fm->createFromStream(stream, index);
|
||||
}
|
||||
|
||||
SkTypeface* SkTypeface::CreateFromFontData(SkFontData* data) {
|
||||
SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
|
||||
return fm->createFromFontData(data);
|
||||
}
|
||||
|
||||
SkTypeface* SkTypeface::CreateFromFile(const char path[], int index) {
|
||||
SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
|
||||
return fm->createFromFile(path, index);
|
||||
@ -157,9 +162,7 @@ void SkTypeface::serialize(SkWStream* wstream) const {
|
||||
|
||||
// Embed font data if it's a local font.
|
||||
if (isLocal && !desc.hasFontData()) {
|
||||
int ttcIndex;
|
||||
desc.setFontData(this->onOpenStream(&ttcIndex));
|
||||
desc.setFontIndex(ttcIndex);
|
||||
desc.setFontData(this->onCreateFontData());
|
||||
}
|
||||
desc.serialize(wstream);
|
||||
}
|
||||
@ -171,18 +174,16 @@ void SkTypeface::serializeForcingEmbedding(SkWStream* wstream) const {
|
||||
|
||||
// Always embed font data.
|
||||
if (!desc.hasFontData()) {
|
||||
int ttcIndex;
|
||||
desc.setFontData(this->onOpenStream(&ttcIndex));
|
||||
desc.setFontIndex(ttcIndex);
|
||||
desc.setFontData(this->onCreateFontData());
|
||||
}
|
||||
desc.serialize(wstream);
|
||||
}
|
||||
|
||||
SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
|
||||
SkFontDescriptor desc(stream);
|
||||
SkStreamAsset* data = desc.transferFontData();
|
||||
SkFontData* data = desc.detachFontData();
|
||||
if (data) {
|
||||
SkTypeface* typeface = SkTypeface::CreateFromStream(data, desc.getFontIndex());
|
||||
SkTypeface* typeface = SkTypeface::CreateFromFontData(data);
|
||||
if (typeface) {
|
||||
return typeface;
|
||||
}
|
||||
@ -218,6 +219,17 @@ SkStreamAsset* SkTypeface::openStream(int* ttcIndex) const {
|
||||
return this->onOpenStream(ttcIndex);
|
||||
}
|
||||
|
||||
SkFontData* SkTypeface::createFontData() const {
|
||||
return this->onCreateFontData();
|
||||
}
|
||||
|
||||
// This implementation is temporary until this method can be made pure virtual.
|
||||
SkFontData* SkTypeface::onCreateFontData() const {
|
||||
int index;
|
||||
SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index));
|
||||
return new SkFontData(stream.detach(), index, NULL, 0);
|
||||
};
|
||||
|
||||
int SkTypeface::charsToGlyphs(const void* chars, Encoding encoding,
|
||||
uint16_t glyphs[], int glyphCount) const {
|
||||
if (glyphCount <= 0) {
|
||||
|
@ -308,7 +308,7 @@ protected:
|
||||
// TODO should the caller give us the style or should we get it from freetype?
|
||||
SkFontStyle style;
|
||||
bool isFixedWidth = false;
|
||||
if (!fScanner.scanFont(stream, 0, NULL, &style, &isFixedWidth)) {
|
||||
if (!fScanner.scanFont(stream, 0, NULL, &style, &isFixedWidth, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -885,9 +885,7 @@ SkPDFFont* SkPDFFont::Create(SkPDFCanon* canon,
|
||||
SkAdvancedTypefaceMetrics::FontType type =
|
||||
info ? info->fType : SkAdvancedTypefaceMetrics::kOther_Font;
|
||||
|
||||
if (info &&
|
||||
(info->fFlags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag)) {
|
||||
NOT_IMPLEMENTED(true, true);
|
||||
if (info && (info->fFlags & SkAdvancedTypefaceMetrics::kMultiMaster_FontFlag)) {
|
||||
return new SkPDFType3Font(info, typeface, glyphID);
|
||||
}
|
||||
if (type == SkAdvancedTypefaceMetrics::kType1CID_Font ||
|
||||
|
@ -14,9 +14,7 @@
|
||||
|
||||
#include <expat.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <limits>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LMP_SYSTEM_FONTS_FILE "/system/etc/fonts.xml"
|
||||
@ -111,32 +109,6 @@ struct FamilyData {
|
||||
SkTDArray<const TagHandler*> fHandler; // The stack of current tag handlers.
|
||||
};
|
||||
|
||||
/** Parses a null terminated string into an integer type, checking for overflow.
|
||||
* http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def
|
||||
*
|
||||
* If the string cannot be parsed into 'value', returns false and does not change 'value'.
|
||||
*/
|
||||
template <typename T> static bool parse_non_negative_integer(const char* s, T* value) {
|
||||
SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
|
||||
const T nMax = std::numeric_limits<T>::max() / 10;
|
||||
const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
|
||||
T n = 0;
|
||||
for (; *s; ++s) {
|
||||
// Check if digit
|
||||
if (*s < '0' || '9' < *s) {
|
||||
return false;
|
||||
}
|
||||
int d = *s - '0';
|
||||
// Check for overflow
|
||||
if (n > nMax || (n == nMax && d > dMax)) {
|
||||
return false;
|
||||
}
|
||||
n = (n * 10) + d;
|
||||
}
|
||||
*value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool memeq(const char* s1, const char* s2, size_t n1, size_t n2) {
|
||||
return n1 == n2 && 0 == memcmp(s1, s2, n1);
|
||||
}
|
||||
@ -174,6 +146,43 @@ static void trim_string(SkString* s) {
|
||||
|
||||
namespace lmpParser {
|
||||
|
||||
static const TagHandler axisHandler = {
|
||||
/*start*/[](FamilyData* self, const char* tag, const char** attributes) {
|
||||
FontFileInfo& file = *self->fCurrentFontInfo;
|
||||
FontFileInfo::Axis& axis = file.fAxes.push_back();
|
||||
for (size_t i = 0; ATTS_NON_NULL(attributes, i); i += 2) {
|
||||
const char* name = attributes[i];
|
||||
const char* value = attributes[i+1];
|
||||
size_t nameLen = strlen(name);
|
||||
if (MEMEQ("tag", name, nameLen)) {
|
||||
size_t valueLen = strlen(value);
|
||||
if (valueLen == 4) {
|
||||
SkFourByteTag tag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
|
||||
for (int j = 0; j < file.fAxes.count() - 1; ++j) {
|
||||
if (file.fAxes[j].fTag == tag) {
|
||||
SK_FONTCONFIGPARSER_WARNING("'%c%c%c%c' axis specified more than once",
|
||||
(tag >> 24) & 0xFF,
|
||||
(tag >> 16) & 0xFF,
|
||||
(tag >> 8) & 0xFF,
|
||||
(tag ) & 0xFF);
|
||||
}
|
||||
}
|
||||
axis.fTag = SkSetFourByteTag(value[0], value[1], value[2], value[3]);
|
||||
} else {
|
||||
SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis tag", value);
|
||||
}
|
||||
} else if (MEMEQ("stylevalue", name, nameLen)) {
|
||||
if (!parse_fixed<16>(value, &axis.fValue)) {
|
||||
SK_FONTCONFIGPARSER_WARNING("'%s' is an invalid axis stylevalue", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/*end*/NULL,
|
||||
/*tag*/NULL,
|
||||
/*chars*/NULL,
|
||||
};
|
||||
|
||||
static const TagHandler fontHandler = {
|
||||
/*start*/[](FamilyData* self, const char* tag, const char** attributes) {
|
||||
// 'weight' (non-negative integer) [default 0]
|
||||
@ -207,7 +216,13 @@ static const TagHandler fontHandler = {
|
||||
/*end*/[](FamilyData* self, const char* tag) {
|
||||
trim_string(&self->fCurrentFontInfo->fFileName);
|
||||
},
|
||||
/*tag*/NULL,
|
||||
/*tag*/[](FamilyData* self, const char* tag, const char** attributes) -> const TagHandler* {
|
||||
size_t len = strlen(tag);
|
||||
if (MEMEQ("axis", tag, len)) {
|
||||
return &axisHandler;
|
||||
}
|
||||
return NULL;
|
||||
},
|
||||
/*chars*/[](void* data, const char* s, int len) {
|
||||
FamilyData* self = static_cast<FamilyData*>(data);
|
||||
self->fCurrentFontInfo->fFileName.append(s, len);
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include "SkString.h"
|
||||
#include "SkTDArray.h"
|
||||
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
/** \class SkLanguage
|
||||
|
||||
The SkLanguage class represents a human written language, and is used by
|
||||
@ -68,6 +71,12 @@ struct FontFileInfo {
|
||||
int fIndex;
|
||||
int fWeight;
|
||||
enum class Style { kAuto, kNormal, kItalic } fStyle;
|
||||
struct Axis {
|
||||
Axis() : fTag(SkSetFourByteTag('\0','\0','\0','\0')), fValue(0) { }
|
||||
SkFourByteTag fTag;
|
||||
SkFixed fValue;
|
||||
};
|
||||
SkSTArray<4, Axis, true> fAxes;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,4 +117,100 @@ void GetCustomFontFamilies(SkTDArray<FontFamily*>& fontFamilies,
|
||||
|
||||
} // SkFontConfigParser namespace
|
||||
|
||||
|
||||
/** Parses a null terminated string into an integer type, checking for overflow.
|
||||
* http://www.w3.org/TR/html-markup/datatypes.html#common.data.integer.non-negative-def
|
||||
*
|
||||
* If the string cannot be parsed into 'value', returns false and does not change 'value'.
|
||||
*/
|
||||
template <typename T> static bool parse_non_negative_integer(const char* s, T* value) {
|
||||
SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
|
||||
|
||||
if (*s == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const T nMax = std::numeric_limits<T>::max() / 10;
|
||||
const T dMax = std::numeric_limits<T>::max() - (nMax * 10);
|
||||
T n = 0;
|
||||
for (; *s; ++s) {
|
||||
// Check if digit
|
||||
if (*s < '0' || '9' < *s) {
|
||||
return false;
|
||||
}
|
||||
T d = *s - '0';
|
||||
// Check for overflow
|
||||
if (n > nMax || (n == nMax && d > dMax)) {
|
||||
return false;
|
||||
}
|
||||
n = (n * 10) + d;
|
||||
}
|
||||
*value = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Parses a null terminated string into a signed fixed point value with bias N.
|
||||
*
|
||||
* Like http://www.w3.org/TR/html-markup/datatypes.html#common.data.float-def ,
|
||||
* but may start with '.' and does not support 'e'. '-?((:digit:+(.:digit:+)?)|(.:digit:+))'
|
||||
*
|
||||
* Checks for overflow.
|
||||
* Low bit rounding is not defined (is currently truncate).
|
||||
* Bias (N) required to allow for the sign bit and 4 bits of integer.
|
||||
*
|
||||
* If the string cannot be parsed into 'value', returns false and does not change 'value'.
|
||||
*/
|
||||
template <int N, typename T> static bool parse_fixed(const char* s, T* value) {
|
||||
SK_COMPILE_ASSERT(std::numeric_limits<T>::is_integer, T_must_be_integer);
|
||||
SK_COMPILE_ASSERT(std::numeric_limits<T>::is_signed, T_must_be_signed);
|
||||
SK_COMPILE_ASSERT(sizeof(T) * CHAR_BIT - N >= 5, N_must_leave_four_bits_plus_sign);
|
||||
|
||||
bool negate = false;
|
||||
if (*s == '-') {
|
||||
++s;
|
||||
negate = true;
|
||||
}
|
||||
if (*s == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const T nMax = (std::numeric_limits<T>::max() >> N) / 10;
|
||||
const T dMax = (std::numeric_limits<T>::max() >> N) - (nMax * 10);
|
||||
T n = 0;
|
||||
T frac = 0;
|
||||
for (; *s; ++s) {
|
||||
// Check if digit
|
||||
if (*s < '0' || '9' < *s) {
|
||||
// If it wasn't a digit, check if it is a '.' followed by something.
|
||||
if (*s != '.' || s[1] == '\0') {
|
||||
return false;
|
||||
}
|
||||
// Find the end, verify digits.
|
||||
for (++s; *s; ++s) {
|
||||
if (*s < '0' || '9' < *s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Read back toward the '.'.
|
||||
for (--s; *s != '.'; --s) {
|
||||
T d = *s - '0';
|
||||
frac = (frac + (d << N)) / 10; // This requires four bits overhead.
|
||||
}
|
||||
break;
|
||||
}
|
||||
T d = *s - '0';
|
||||
// Check for overflow
|
||||
if (n > nMax || (n == nMax && d > dMax)) {
|
||||
return false;
|
||||
}
|
||||
n = (n * 10) + d;
|
||||
}
|
||||
if (negate) {
|
||||
n = -n;
|
||||
frac = -frac;
|
||||
}
|
||||
*value = (n << N) + frac;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* SKFONTCONFIGPARSER_ANDROID_H_ */
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkDescriptor.h"
|
||||
#include "SkFDot6.h"
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkFontHost_FreeType_common.h"
|
||||
#include "SkGlyph.h"
|
||||
#include "SkMask.h"
|
||||
@ -33,6 +34,7 @@
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_LCD_FILTER_H
|
||||
#include FT_MODULE_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
#include FT_OUTLINE_H
|
||||
#include FT_SIZES_H
|
||||
#include FT_SYSTEM_H
|
||||
@ -40,6 +42,10 @@
|
||||
#include FT_TYPE1_TABLES_H
|
||||
#include FT_XFREE86_H
|
||||
|
||||
#if !defined(SK_BUILD_FOR_ANDROID) || defined(SK_ANDROID_FREETYPE_HAS_MM)
|
||||
# define SK_FREETYPE_HAS_MM 1
|
||||
#endif
|
||||
|
||||
// FT_LOAD_COLOR and the corresponding FT_Pixel_Mode::FT_PIXEL_MODE_BGRA
|
||||
// were introduced in FreeType 2.5.0.
|
||||
// The following may be removed once FreeType 2.5.0 is required to build.
|
||||
@ -273,6 +279,39 @@ SkFaceRec::SkFaceRec(SkStreamAsset* stream, uint32_t fontID)
|
||||
fFTStream.close = sk_ft_stream_close;
|
||||
}
|
||||
|
||||
#if SK_FREETYPE_HAS_MM
|
||||
static void ft_face_setup_axes(FT_Face face, const SkFontData& data) {
|
||||
if (!(face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkDEBUGCODE(
|
||||
FT_MM_Var* variations = NULL;
|
||||
if (FT_Get_MM_Var(face, &variations)) {
|
||||
SkDEBUGF(("INFO: font %s claims variations, but none found.\n", face->family_name));
|
||||
return;
|
||||
}
|
||||
SkAutoFree autoFreeVariations(variations);
|
||||
|
||||
if (static_cast<FT_UInt>(data.getAxisCount()) != variations->num_axis) {
|
||||
SkDEBUGF(("INFO: font %s has %d variations, but %d were specified.\n",
|
||||
face->family_name, variations->num_axis, data.getAxisCount()));
|
||||
return;
|
||||
}
|
||||
)
|
||||
|
||||
SkAutoSTMalloc<4, FT_Fixed> coords(data.getAxisCount());
|
||||
for (int i = 0; i < data.getAxisCount(); ++i) {
|
||||
coords[i] = data.getAxis()[i];
|
||||
}
|
||||
if (FT_Set_Var_Design_Coordinates(face, data.getAxisCount(), coords.get())) {
|
||||
SkDEBUGF(("INFO: font %s has variations, but specified variations could not be set.\n",
|
||||
face->family_name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Will return 0 on failure
|
||||
// Caller must lock gFTMutex before calling this function.
|
||||
static SkFaceRec* ref_ft_face(const SkTypeface* typeface) {
|
||||
@ -289,34 +328,38 @@ static SkFaceRec* ref_ft_face(const SkTypeface* typeface) {
|
||||
rec = rec->fNext;
|
||||
}
|
||||
|
||||
int face_index;
|
||||
SkStreamAsset* stream = typeface->openStream(&face_index);
|
||||
if (NULL == stream) {
|
||||
SkAutoTDelete<SkFontData> data(typeface->createFontData());
|
||||
if (NULL == data || !data->hasStream()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// this passes ownership of stream to the rec
|
||||
rec = SkNEW_ARGS(SkFaceRec, (stream, fontID));
|
||||
rec = SkNEW_ARGS(SkFaceRec, (data->detachStream(), fontID));
|
||||
|
||||
FT_Open_Args args;
|
||||
memset(&args, 0, sizeof(args));
|
||||
const void* memoryBase = stream->getMemoryBase();
|
||||
const void* memoryBase = rec->fSkStream->getMemoryBase();
|
||||
if (memoryBase) {
|
||||
args.flags = FT_OPEN_MEMORY;
|
||||
args.memory_base = (const FT_Byte*)memoryBase;
|
||||
args.memory_size = stream->getLength();
|
||||
args.memory_size = rec->fSkStream->getLength();
|
||||
} else {
|
||||
args.flags = FT_OPEN_STREAM;
|
||||
args.stream = &rec->fFTStream;
|
||||
}
|
||||
|
||||
FT_Error err = FT_Open_Face(gFTLibrary->library(), &args, face_index, &rec->fFace);
|
||||
if (err) { // bad filename, try the default font
|
||||
FT_Error err = FT_Open_Face(gFTLibrary->library(), &args, data->getIndex(), &rec->fFace);
|
||||
if (err) {
|
||||
SkDEBUGF(("ERROR: unable to open font '%x'\n", fontID));
|
||||
SkDELETE(rec);
|
||||
return NULL;
|
||||
}
|
||||
SkASSERT(rec->fFace);
|
||||
|
||||
#if SK_FREETYPE_HAS_MM
|
||||
ft_face_setup_axes(rec->fFace, *data);
|
||||
#endif
|
||||
|
||||
rec->fNext = gFaceRecHead;
|
||||
gFaceRecHead = rec;
|
||||
return rec;
|
||||
@ -1649,7 +1692,8 @@ bool SkTypeface_FreeType::Scanner::recognizedFont(SkStream* stream, int* numFace
|
||||
|
||||
#include "SkTSearch.h"
|
||||
bool SkTypeface_FreeType::Scanner::scanFont(
|
||||
SkStream* stream, int ttcIndex, SkString* name, SkFontStyle* style, bool* isFixedPitch) const
|
||||
SkStream* stream, int ttcIndex,
|
||||
SkString* name, SkFontStyle* style, bool* isFixedPitch, AxisDefinitions* axes) const
|
||||
{
|
||||
SkAutoMutexAcquire libraryLock(fLibraryMutex);
|
||||
|
||||
@ -1725,6 +1769,28 @@ bool SkTypeface_FreeType::Scanner::scanFont(
|
||||
*isFixedPitch = FT_IS_FIXED_WIDTH(face);
|
||||
}
|
||||
|
||||
#if SK_FREETYPE_HAS_MM
|
||||
if (axes && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
|
||||
FT_MM_Var* variations = NULL;
|
||||
FT_Error err = FT_Get_MM_Var(face, &variations);
|
||||
if (err) {
|
||||
SkDEBUGF(("INFO: font %s claims to have variations, but none found.\n",
|
||||
face->family_name));
|
||||
return false;
|
||||
}
|
||||
SkAutoFree autoFreeVariations(variations);
|
||||
|
||||
axes->reset(variations->num_axis);
|
||||
for (FT_UInt i = 0; i < variations->num_axis; ++i) {
|
||||
const FT_Var_Axis& ftAxis = variations->axis[i];
|
||||
(*axes)[i].fTag = ftAxis.tag;
|
||||
(*axes)[i].fMinimum = ftAxis.minimum;
|
||||
(*axes)[i].fDefault = ftAxis.def;
|
||||
(*axes)[i].fMaximum = ftAxis.maximum;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
FT_Done_Face(face);
|
||||
return true;
|
||||
}
|
||||
|
@ -43,9 +43,17 @@ public:
|
||||
public:
|
||||
Scanner();
|
||||
~Scanner();
|
||||
struct AxisDefinition {
|
||||
SkFourByteTag fTag;
|
||||
SkFixed fMinimum;
|
||||
SkFixed fDefault;
|
||||
SkFixed fMaximum;
|
||||
};
|
||||
using AxisDefinitions = SkSTArray<4, AxisDefinition, true>;
|
||||
bool recognizedFont(SkStream* stream, int* numFonts) const;
|
||||
bool scanFont(SkStream* stream, int ttcIndex,
|
||||
SkString* name, SkFontStyle* style, bool* isFixedPitch) const;
|
||||
SkString* name, SkFontStyle* style, bool* isFixedPitch,
|
||||
AxisDefinitions* axes) const;
|
||||
private:
|
||||
FT_Face openFace(SkStream* stream, int ttcIndex, FT_Stream ftStream) const;
|
||||
FT_Library fLibrary;
|
||||
|
@ -155,6 +155,5 @@ void FontConfigTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
|
||||
SkString name;
|
||||
this->getFamilyName(&name);
|
||||
desc->setFamilyName(name.c_str());
|
||||
desc->setFontIndex(this->getIdentity().fTTCIndex);
|
||||
*isLocalStream = SkToBool(this->getLocalStream());
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ protected:
|
||||
|
||||
void onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const override {
|
||||
desc->setFamilyName(fFamilyName.c_str());
|
||||
desc->setFontIndex(fIndex);
|
||||
*isLocal = !this->isSysFont();
|
||||
}
|
||||
|
||||
@ -306,7 +305,7 @@ protected:
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style;
|
||||
SkString name;
|
||||
if (fScanner.scanFont(stream, ttcIndex, &name, &style, &isFixedPitch)) {
|
||||
if (fScanner.scanFont(stream, ttcIndex, &name, &style, &isFixedPitch, NULL)) {
|
||||
return SkNEW_ARGS(SkTypeface_Stream, (style, isFixedPitch, false, name,
|
||||
stream.detach(), ttcIndex));
|
||||
} else {
|
||||
@ -405,7 +404,7 @@ private:
|
||||
bool isFixedPitch;
|
||||
SkString realname;
|
||||
SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
|
||||
if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch)) {
|
||||
if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch, NULL)) {
|
||||
SkDebugf("---- failed to open <%s> <%d> as a font\n",
|
||||
filename.c_str(), faceIndex);
|
||||
continue;
|
||||
@ -491,7 +490,7 @@ private:
|
||||
bool isFixedPitch;
|
||||
SkString realname;
|
||||
SkFontStyle style = SkFontStyle(); // avoid uninitialized warning
|
||||
if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch)) {
|
||||
if (!scanner.scanFont(stream, faceIndex, &realname, &style, &isFixedPitch, NULL)) {
|
||||
SkDebugf("---- failed to open <%d> <%d> as a font\n", index, faceIndex);
|
||||
return;
|
||||
}
|
||||
|
@ -428,10 +428,12 @@ static const uint32_t SkCTFontColorGlyphsTrait = (1 << 13);
|
||||
class SkTypeface_Mac : public SkTypeface {
|
||||
public:
|
||||
SkTypeface_Mac(const SkFontStyle& fs, bool isFixedPitch,
|
||||
CTFontRef fontRef, const char requestedName[], bool isLocalStream)
|
||||
CTFontRef fontRef, const char requestedName[], bool isLocalStream,
|
||||
CGFontRef originatingCGFontRef = NULL)
|
||||
: SkTypeface(fs, SkTypefaceCache::NewFontID(), isFixedPitch)
|
||||
, fRequestedName(requestedName)
|
||||
, fFontRef(fontRef) // caller has already called CFRetain for us
|
||||
, fOriginatingCGFontRef(originatingCGFontRef)
|
||||
, fHasColorGlyphs(SkToBool(CTFontGetSymbolicTraits(fFontRef) & SkCTFontColorGlyphsTrait))
|
||||
, fIsLocalStream(isLocalStream)
|
||||
{
|
||||
@ -440,11 +442,13 @@ public:
|
||||
|
||||
SkString fRequestedName;
|
||||
AutoCFRelease<CTFontRef> fFontRef;
|
||||
AutoCFRelease<CGFontRef> fOriginatingCGFontRef;
|
||||
const bool fHasColorGlyphs;
|
||||
|
||||
protected:
|
||||
int onGetUPEM() const override;
|
||||
SkStreamAsset* onOpenStream(int* ttcIndex) const override;
|
||||
SkFontData* onCreateFontData() const override;
|
||||
void onGetFamilyName(SkString* familyName) const override;
|
||||
SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const override;
|
||||
int onGetTableTags(SkFontTableTag tags[]) const override;
|
||||
@ -467,12 +471,15 @@ private:
|
||||
};
|
||||
|
||||
/** Creates a typeface without searching the cache. Takes ownership of the CTFontRef. */
|
||||
static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream) {
|
||||
static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[], bool isLocalStream,
|
||||
CGFontRef originatingCGFontRef = NULL)
|
||||
{
|
||||
SkASSERT(fontRef);
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style = SkFontStyle(computeStyleBits(fontRef, &isFixedPitch));
|
||||
|
||||
return new SkTypeface_Mac(style, isFixedPitch, fontRef, name, isLocalStream);
|
||||
return new SkTypeface_Mac(style, isFixedPitch, fontRef, name, isLocalStream,
|
||||
originatingCGFontRef);
|
||||
}
|
||||
|
||||
static bool find_by_CTFontRef(SkTypeface* cached, const SkFontStyle&, void* context) {
|
||||
@ -1752,6 +1759,115 @@ SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
|
||||
return stream;
|
||||
}
|
||||
|
||||
struct NonDefaultAxesContext {
|
||||
SkFixed* axisValue;
|
||||
CFArrayRef cgAxes;
|
||||
};
|
||||
static void set_non_default_axes(CFTypeRef key, CFTypeRef value, void* context) {
|
||||
NonDefaultAxesContext* self = static_cast<NonDefaultAxesContext*>(context);
|
||||
|
||||
if (CFGetTypeID(key) != CFStringGetTypeID() || CFGetTypeID(value) != CFNumberGetTypeID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The key is a CFString which is a string from the 'name' table.
|
||||
// Search the cgAxes for an axis with this name, and use its index to store the value.
|
||||
CFIndex keyIndex = -1;
|
||||
CFStringRef keyString = static_cast<CFStringRef>(key);
|
||||
for (CFIndex i = 0; i < CFArrayGetCount(self->cgAxes); ++i) {
|
||||
CFTypeRef cgAxis = CFArrayGetValueAtIndex(self->cgAxes, i);
|
||||
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
|
||||
CFTypeRef cgAxisName = CFDictionaryGetValue(cgAxisDict, kCGFontVariationAxisName);
|
||||
if (!cgAxisName || CFGetTypeID(cgAxisName) != CFStringGetTypeID()) {
|
||||
continue;
|
||||
}
|
||||
CFStringRef cgAxisNameString = static_cast<CFStringRef>(cgAxisName);
|
||||
if (CFStringCompare(keyString, cgAxisNameString, 0) == kCFCompareEqualTo) {
|
||||
keyIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFNumberRef valueNumber = static_cast<CFNumberRef>(value);
|
||||
double valueDouble;
|
||||
if (!CFNumberGetValue(valueNumber, kCFNumberDoubleType, &valueDouble) ||
|
||||
valueDouble < SkFixedToDouble(SK_FixedMin) || SkFixedToDouble(SK_FixedMax) < valueDouble)
|
||||
{
|
||||
return;
|
||||
}
|
||||
self->axisValue[keyIndex] = SkDoubleToFixed(valueDouble);
|
||||
}
|
||||
static bool get_variations(CTFontRef fFontRef, CFIndex* cgAxisCount,
|
||||
SkAutoSTMalloc<4, SkFixed>* axisValues)
|
||||
{
|
||||
// CTFontCopyVariationAxes and CTFontCopyVariation do not work when applied to fonts which
|
||||
// started life with CGFontCreateWithDataProvider (they simply always return NULL).
|
||||
// As a result, we are limited to CGFontCopyVariationAxes and CGFontCopyVariations.
|
||||
AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(fFontRef, NULL));
|
||||
|
||||
AutoCFRelease<CFDictionaryRef> cgVariations(CGFontCopyVariations(cgFont));
|
||||
// If a font has no variations CGFontCopyVariations returns NULL (instead of an empty dict).
|
||||
if (!cgVariations.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cgFont));
|
||||
*cgAxisCount = CFArrayGetCount(cgAxes);
|
||||
axisValues->reset(*cgAxisCount);
|
||||
|
||||
// Set all of the axes to their default values.
|
||||
// Fail if any default value cannot be determined.
|
||||
for (CFIndex i = 0; i < *cgAxisCount; ++i) {
|
||||
CFTypeRef cgAxis = CFArrayGetValueAtIndex(cgAxes, i);
|
||||
if (CFGetTypeID(cgAxis) != CFDictionaryGetTypeID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFDictionaryRef cgAxisDict = static_cast<CFDictionaryRef>(cgAxis);
|
||||
CFTypeRef axisDefaultValue = CFDictionaryGetValue(cgAxisDict,
|
||||
kCGFontVariationAxisDefaultValue);
|
||||
if (!axisDefaultValue || CFGetTypeID(axisDefaultValue) != CFNumberGetTypeID()) {
|
||||
return false;
|
||||
}
|
||||
CFNumberRef axisDefaultValueNumber = static_cast<CFNumberRef>(axisDefaultValue);
|
||||
double axisDefaultValueDouble;
|
||||
if (!CFNumberGetValue(axisDefaultValueNumber, kCFNumberDoubleType, &axisDefaultValueDouble))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (axisDefaultValueDouble < SkFixedToDouble(SK_FixedMin) ||
|
||||
SkFixedToDouble(SK_FixedMax) < axisDefaultValueDouble)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
(*axisValues)[(int)i] = SkDoubleToFixed(axisDefaultValueDouble);
|
||||
}
|
||||
|
||||
// Override the default values with the given font's stated axis values.
|
||||
NonDefaultAxesContext c = { axisValues->get(), cgAxes.get() };
|
||||
CFDictionaryApplyFunction(cgVariations, set_non_default_axes, &c);
|
||||
|
||||
return true;
|
||||
}
|
||||
SkFontData* SkTypeface_Mac::onCreateFontData() const {
|
||||
int index;
|
||||
SkAutoTDelete<SkStreamAsset> stream(this->onOpenStream(&index));
|
||||
|
||||
CFIndex cgAxisCount;
|
||||
SkAutoSTMalloc<4, SkFixed> axisValues;
|
||||
if (get_variations(fFontRef, &cgAxisCount, &axisValues)) {
|
||||
return new SkFontData(stream.detach(), index, axisValues.get(), cgAxisCount);
|
||||
}
|
||||
return new SkFontData(stream.detach(), index, NULL, 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -2247,6 +2363,89 @@ protected:
|
||||
return create_from_dataProvider(pr);
|
||||
}
|
||||
|
||||
static CFDictionaryRef get_axes(CGFontRef cg, SkFontData* fontData) {
|
||||
AutoCFRelease<CFArrayRef> cgAxes(CGFontCopyVariationAxes(cg));
|
||||
if (!cgAxes) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFIndex axisCount = CFArrayGetCount(cgAxes);
|
||||
if (0 == axisCount || axisCount != fontData->getAxisCount()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, axisCount,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
for (int i = 0; i < fontData->getAxisCount(); ++i) {
|
||||
CFTypeRef axisInfo = CFArrayGetValueAtIndex(cgAxes, i);
|
||||
if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
|
||||
return NULL;
|
||||
}
|
||||
CFDictionaryRef axisInfoDict = static_cast<CFDictionaryRef>(axisInfo);
|
||||
|
||||
CFTypeRef axisName = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisName);
|
||||
if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The variation axes can be set to any value, but cg will effectively pin them.
|
||||
// Pin them here to normalize.
|
||||
CFTypeRef min = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMinValue);
|
||||
CFTypeRef max = CFDictionaryGetValue(axisInfoDict, kCGFontVariationAxisMaxValue);
|
||||
if (!min || CFGetTypeID(min) != CFNumberGetTypeID() ||
|
||||
!max || CFGetTypeID(max) != CFNumberGetTypeID())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
CFNumberRef minNumber = static_cast<CFNumberRef>(min);
|
||||
CFNumberRef maxNumber = static_cast<CFNumberRef>(max);
|
||||
double minDouble;
|
||||
double maxDouble;
|
||||
if (!CFNumberGetValue(minNumber, kCFNumberDoubleType, &minDouble) ||
|
||||
!CFNumberGetValue(maxNumber, kCFNumberDoubleType, &maxDouble))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
double value = SkTPin(SkFixedToDouble(fontData->getAxis()[i]), minDouble, maxDouble);
|
||||
CFNumberRef valueNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType,
|
||||
&value);
|
||||
|
||||
CFDictionaryAddValue(dict, axisName, valueNumber);
|
||||
CFRelease(valueNumber);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
SkTypeface* onCreateFromFontData(SkFontData* data) const override {
|
||||
SkAutoTDelete<SkFontData> fontData(data);
|
||||
SkStreamAsset* stream = fontData->detachStream();
|
||||
AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
|
||||
if (NULL == provider) {
|
||||
return NULL;
|
||||
}
|
||||
AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
|
||||
if (NULL == cg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AutoCFRelease<CFDictionaryRef> cgVariations(get_axes(cg, fontData));
|
||||
// The CGFontRef returned by CGFontCreateCopyWithVariations when the passed CGFontRef was
|
||||
// created from a data provider does not appear to have any ownership of the underlying
|
||||
// data. The original CGFontRef must be kept alive until the copy will no longer be used.
|
||||
AutoCFRelease<CGFontRef> cgVariant;
|
||||
if (cgVariations) {
|
||||
cgVariant.reset(CGFontCreateCopyWithVariations(cg, cgVariations));
|
||||
} else {
|
||||
cgVariant.reset(cg.detach());
|
||||
}
|
||||
|
||||
CTFontRef ct = CTFontCreateWithGraphicsFont(cgVariant, 0, NULL, NULL);
|
||||
if (!ct) {
|
||||
return NULL;
|
||||
}
|
||||
return NewFromFontRef(ct, NULL, true, cg.detach());
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const override {
|
||||
AutoCFRelease<CGDataProviderRef> pr(CGDataProviderCreateWithFilename(path));
|
||||
if (NULL == pr) {
|
||||
|
@ -27,20 +27,18 @@ static const char* gTestBasePath = NULL;
|
||||
|
||||
class SkTypeface_Android : public SkTypeface_FreeType {
|
||||
public:
|
||||
SkTypeface_Android(int index,
|
||||
const SkFontStyle& style,
|
||||
SkTypeface_Android(const SkFontStyle& style,
|
||||
bool isFixedPitch,
|
||||
const SkString& familyName)
|
||||
: INHERITED(style, SkTypefaceCache::NewFontID(), isFixedPitch)
|
||||
, fIndex(index)
|
||||
, fFamilyName(familyName) { }
|
||||
, fFamilyName(familyName)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
void onGetFamilyName(SkString* familyName) const override {
|
||||
*familyName = fFamilyName;
|
||||
}
|
||||
|
||||
int fIndex;
|
||||
SkString fFamilyName;
|
||||
|
||||
private:
|
||||
@ -51,30 +49,37 @@ class SkTypeface_AndroidSystem : public SkTypeface_Android {
|
||||
public:
|
||||
SkTypeface_AndroidSystem(const SkString& pathName,
|
||||
int index,
|
||||
const SkFixed* axes, int axesCount,
|
||||
const SkFontStyle& style,
|
||||
bool isFixedPitch,
|
||||
const SkString& familyName,
|
||||
const SkLanguage& lang,
|
||||
FontVariant variantStyle)
|
||||
: INHERITED(index, style, isFixedPitch, familyName)
|
||||
: INHERITED(style, isFixedPitch, familyName)
|
||||
, fPathName(pathName)
|
||||
, fIndex(index)
|
||||
, fAxes(axes, axesCount)
|
||||
, fLang(lang)
|
||||
, fVariantStyle(variantStyle) { }
|
||||
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor* desc,
|
||||
bool* serialize) const override {
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
|
||||
SkASSERT(desc);
|
||||
SkASSERT(serialize);
|
||||
desc->setFamilyName(fFamilyName.c_str());
|
||||
desc->setFontIndex(fIndex);
|
||||
*serialize = false;
|
||||
}
|
||||
SkStreamAsset* onOpenStream(int* ttcIndex) const override {
|
||||
*ttcIndex = fIndex;
|
||||
return SkStream::NewFromFile(fPathName.c_str());
|
||||
}
|
||||
SkFontData* onCreateFontData() const override {
|
||||
return new SkFontData(SkStream::NewFromFile(fPathName.c_str()), fIndex,
|
||||
fAxes.begin(), fAxes.count());
|
||||
}
|
||||
|
||||
const SkString fPathName;
|
||||
int fIndex;
|
||||
const SkSTArray<4, SkFixed, true> fAxes;
|
||||
const SkLanguage fLang;
|
||||
const FontVariant fVariantStyle;
|
||||
|
||||
@ -83,13 +88,13 @@ public:
|
||||
|
||||
class SkTypeface_AndroidStream : public SkTypeface_Android {
|
||||
public:
|
||||
SkTypeface_AndroidStream(SkStreamAsset* stream,
|
||||
int index,
|
||||
SkTypeface_AndroidStream(SkFontData* data,
|
||||
const SkFontStyle& style,
|
||||
bool isFixedPitch,
|
||||
const SkString& familyName)
|
||||
: INHERITED(index, style, isFixedPitch, familyName)
|
||||
, fStream(stream) { }
|
||||
: INHERITED(style, isFixedPitch, familyName)
|
||||
, fData(data)
|
||||
{ }
|
||||
|
||||
virtual void onGetFontDescriptor(SkFontDescriptor* desc,
|
||||
bool* serialize) const override {
|
||||
@ -100,21 +105,24 @@ public:
|
||||
}
|
||||
|
||||
SkStreamAsset* onOpenStream(int* ttcIndex) const override {
|
||||
*ttcIndex = fIndex;
|
||||
return fStream->duplicate();
|
||||
*ttcIndex = fData->getIndex();
|
||||
return fData->duplicateStream();
|
||||
}
|
||||
|
||||
SkFontData* onCreateFontData() const override {
|
||||
return new SkFontData(*fData.get());
|
||||
}
|
||||
|
||||
private:
|
||||
SkAutoTDelete<SkStreamAsset> fStream;
|
||||
|
||||
const SkAutoTDelete<const SkFontData> fData;
|
||||
typedef SkTypeface_Android INHERITED;
|
||||
};
|
||||
|
||||
class SkFontStyleSet_Android : public SkFontStyleSet {
|
||||
typedef SkTypeface_FreeType::Scanner Scanner;
|
||||
|
||||
public:
|
||||
explicit SkFontStyleSet_Android(const FontFamily& family,
|
||||
const SkTypeface_FreeType::Scanner& scanner)
|
||||
{
|
||||
explicit SkFontStyleSet_Android(const FontFamily& family, const Scanner& scanner) {
|
||||
const SkString* cannonicalFamilyName = NULL;
|
||||
if (family.fNames.count() > 0) {
|
||||
cannonicalFamilyName = &family.fNames[0];
|
||||
@ -137,7 +145,10 @@ public:
|
||||
SkString familyName;
|
||||
SkFontStyle style;
|
||||
bool isFixedWidth;
|
||||
if (!scanner.scanFont(stream.get(), ttcIndex, &familyName, &style, &isFixedWidth)) {
|
||||
Scanner::AxisDefinitions axisDefinitions;
|
||||
if (!scanner.scanFont(stream.get(), ttcIndex,
|
||||
&familyName, &style, &isFixedWidth, &axisDefinitions))
|
||||
{
|
||||
SkDEBUGF(("Requested font file %s exists, but is not a valid font.\n",
|
||||
pathName.c_str()));
|
||||
continue;
|
||||
@ -166,8 +177,57 @@ public:
|
||||
familyName = *cannonicalFamilyName;
|
||||
}
|
||||
|
||||
SkAutoSTMalloc<4, SkFixed> axisValues(axisDefinitions.count());
|
||||
for (int i = 0; i < axisDefinitions.count(); ++i) {
|
||||
const Scanner::AxisDefinition& axisDefinition = axisDefinitions[i];
|
||||
axisValues[i] = axisDefinition.fDefault;
|
||||
for (int j = 0; j < fontFile.fAxes.count(); ++j) {
|
||||
const FontFileInfo::Axis& axisSpecified = fontFile.fAxes[j];
|
||||
if (axisDefinition.fTag == axisSpecified.fTag) {
|
||||
axisValues[i] = SkTPin(axisSpecified.fValue, axisDefinition.fMinimum,
|
||||
axisDefinition.fMaximum);
|
||||
if (axisValues[i] != axisSpecified.fValue) {
|
||||
SkDEBUGF(("Requested font axis value out of range: "
|
||||
"%s '%c%c%c%c' %f; pinned to %f.\n",
|
||||
familyName.c_str(),
|
||||
(axisDefinition.fTag >> 24) & 0xFF,
|
||||
(axisDefinition.fTag >> 16) & 0xFF,
|
||||
(axisDefinition.fTag >> 8) & 0xFF,
|
||||
(axisDefinition.fTag ) & 0xFF,
|
||||
SkFixedToDouble(axisSpecified.fValue),
|
||||
SkFixedToDouble(axisValues[i])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: warn on defaulted axis?
|
||||
}
|
||||
|
||||
SkDEBUGCODE (
|
||||
// Check for axis specified, but not matched in font.
|
||||
for (int i = 0; i < fontFile.fAxes.count(); ++i) {
|
||||
SkFourByteTag skTag = fontFile.fAxes[i].fTag;
|
||||
bool found = false;
|
||||
for (int j = 0; j < axisDefinitions.count(); ++j) {
|
||||
if (skTag == axisDefinitions[j].fTag) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
SkDEBUGF(("Requested font axis not found: %s '%c%c%c%c'\n",
|
||||
familyName.c_str(),
|
||||
(skTag >> 24) & 0xFF,
|
||||
(skTag >> 16) & 0xFF,
|
||||
(skTag >> 8) & 0xFF,
|
||||
(skTag ) & 0xFF));
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fStyles.push_back().reset(SkNEW_ARGS(SkTypeface_AndroidSystem,
|
||||
(pathName, ttcIndex,
|
||||
axisValues.get(), axisDefinitions.count(),
|
||||
style, isFixedWidth, familyName,
|
||||
lang, variant)));
|
||||
}
|
||||
@ -411,11 +471,22 @@ protected:
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style;
|
||||
SkString name;
|
||||
if (!fScanner.scanFont(stream, ttcIndex, &name, &style, &isFixedPitch)) {
|
||||
if (!fScanner.scanFont(stream, ttcIndex, &name, &style, &isFixedPitch, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return SkNEW_ARGS(SkTypeface_AndroidStream, (stream.detach(), ttcIndex,
|
||||
style, isFixedPitch, name));
|
||||
SkFontData* data(new SkFontData(stream.detach(), ttcIndex, NULL, 0));
|
||||
return SkNEW_ARGS(SkTypeface_AndroidStream, (data, style, isFixedPitch, name));
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromFontData(SkFontData* data) const override {
|
||||
SkStreamAsset* stream(data->getStream());
|
||||
bool isFixedPitch;
|
||||
SkFontStyle style;
|
||||
SkString name;
|
||||
if (!fScanner.scanFont(stream, data->getIndex(), &name, &style, &isFixedPitch, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
return SkNEW_ARGS(SkTypeface_AndroidStream, (data, style, isFixedPitch, name));
|
||||
}
|
||||
|
||||
|
||||
|
@ -375,11 +375,10 @@ static void fcpattern_from_skfontstyle(SkFontStyle style, FcPattern* pattern) {
|
||||
|
||||
class SkTypeface_stream : public SkTypeface_FreeType {
|
||||
public:
|
||||
/** @param stream does not take ownership of the reference, does take ownership of the stream.*/
|
||||
SkTypeface_stream(const SkFontStyle& style, bool fixedWidth, int index, SkStreamAsset* stream)
|
||||
/** @param data takes ownership of the font data.*/
|
||||
SkTypeface_stream(SkFontData* data, const SkFontStyle& style, bool fixedWidth)
|
||||
: INHERITED(style, SkTypefaceCache::NewFontID(), fixedWidth)
|
||||
, fStream(stream)
|
||||
, fIndex(index)
|
||||
, fData(data)
|
||||
{ };
|
||||
|
||||
void onGetFamilyName(SkString* familyName) const override {
|
||||
@ -387,18 +386,20 @@ public:
|
||||
}
|
||||
|
||||
void onGetFontDescriptor(SkFontDescriptor* desc, bool* serialize) const override {
|
||||
desc->setFontIndex(fIndex);
|
||||
*serialize = true;
|
||||
}
|
||||
|
||||
SkStreamAsset* onOpenStream(int* ttcIndex) const override {
|
||||
*ttcIndex = fIndex;
|
||||
return fStream->duplicate();
|
||||
*ttcIndex = fData->getIndex();
|
||||
return fData->duplicateStream();
|
||||
}
|
||||
|
||||
SkFontData* onCreateFontData() const override {
|
||||
return new SkFontData(*fData.get());
|
||||
}
|
||||
|
||||
private:
|
||||
SkAutoTDelete<SkStreamAsset> fStream;
|
||||
int fIndex;
|
||||
const SkAutoTDelete<const SkFontData> fData;
|
||||
|
||||
typedef SkTypeface_FreeType INHERITED;
|
||||
};
|
||||
@ -420,7 +421,6 @@ public:
|
||||
desc->setFamilyName(get_string(fPattern, FC_FAMILY));
|
||||
desc->setFullName(get_string(fPattern, FC_FULLNAME));
|
||||
desc->setPostscriptName(get_string(fPattern, FC_POSTSCRIPT_NAME));
|
||||
desc->setFontIndex(get_int(fPattern, FC_INDEX, 0));
|
||||
*serialize = false;
|
||||
}
|
||||
|
||||
@ -822,12 +822,12 @@ protected:
|
||||
|
||||
SkFontStyle style;
|
||||
bool isFixedWidth = false;
|
||||
if (!fScanner.scanFont(stream, ttcIndex, NULL, &style, &isFixedWidth)) {
|
||||
if (!fScanner.scanFont(stream, ttcIndex, NULL, &style, &isFixedWidth, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SkNEW_ARGS(SkTypeface_stream, (style, isFixedWidth, ttcIndex,
|
||||
static_cast<SkStreamAsset*>(stream.detach())));
|
||||
return SkNEW_ARGS(SkTypeface_stream, (new SkFontData(stream.detach(), ttcIndex, NULL, 0),
|
||||
style, isFixedWidth));
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const override {
|
||||
@ -838,6 +838,23 @@ protected:
|
||||
return this->createFromStream(SkStream::NewFromFile(path), ttcIndex);
|
||||
}
|
||||
|
||||
SkTypeface* onCreateFromFontData(SkFontData* fontData) const override {
|
||||
SkStreamAsset* stream(fontData->getStream());
|
||||
const size_t length = stream->getLength();
|
||||
if (length <= 0 || (1u << 30) < length) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int ttcIndex = fontData->getIndex();
|
||||
SkFontStyle style;
|
||||
bool isFixedWidth = false;
|
||||
if (!fScanner.scanFont(stream, ttcIndex, NULL, &style, &isFixedWidth, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SkNEW_ARGS(SkTypeface_stream, (fontData, style, isFixedWidth));
|
||||
}
|
||||
|
||||
virtual SkTypeface* onLegacyCreateTypeface(const char familyName[],
|
||||
unsigned styleBits) const override {
|
||||
bool bold = styleBits & SkTypeface::kBold;
|
||||
|
@ -42,7 +42,6 @@ void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
|
||||
sk_get_locale_string(familyNames.get(), NULL/*fMgr->fLocaleName.get()*/, &utf8FamilyName);
|
||||
|
||||
desc->setFamilyName(utf8FamilyName.c_str());
|
||||
desc->setFontIndex(fDWriteFontFace->GetIndex());
|
||||
*isLocalStream = SkToBool(fDWriteFontFileLoader.get());
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "SkFontConfigParser_android.h"
|
||||
#include "Test.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
DECLARE_bool(verboseFontMgr);
|
||||
|
||||
int CountFallbacks(SkTDArray<FontFamily*> fontFamilies) {
|
||||
@ -91,7 +94,55 @@ void DumpLoadedFonts(SkTDArray<FontFamily*> fontFamilies, const char* label) {
|
||||
SkDebugf("\n\n");
|
||||
}
|
||||
|
||||
template <int N, typename T> static double test_parse_fixed_r(skiatest::Reporter* reporter,
|
||||
double low, double high, double inc)
|
||||
{
|
||||
double SK_FixedMax_double = nextafter(1 << (sizeof(T) * CHAR_BIT - N - 1), 0.0);
|
||||
double SK_FixedEpsilon_double = (1.0 / (1 << N));
|
||||
double maxError = 0;
|
||||
char buffer[64];
|
||||
for (double f = low; f < high; f += inc) {
|
||||
SkString s;
|
||||
// 'sprintf' formatting as expected depends on the current locale being "C".
|
||||
// We currently expect tests and tools to run in the "C" locale.
|
||||
sprintf(buffer, "%.20f", f);
|
||||
T fix;
|
||||
bool b = parse_fixed<N>(buffer, &fix);
|
||||
if (b) {
|
||||
double f2 = fix * SK_FixedEpsilon_double;
|
||||
double error = fabs(f - f2);
|
||||
REPORTER_ASSERT(reporter, error <= SK_FixedEpsilon_double);
|
||||
maxError = SkTMax(maxError, error);
|
||||
} else {
|
||||
REPORTER_ASSERT(reporter, f < -SK_FixedMax_double || SK_FixedMax_double < f);
|
||||
}
|
||||
}
|
||||
|
||||
//SkDebugf("maxError: %.20f\n", maxError);
|
||||
return maxError;
|
||||
}
|
||||
|
||||
static void test_parse_fixed(skiatest::Reporter* reporter) {
|
||||
test_parse_fixed_r<27, int32_t>(reporter, -8.1, -7.9, 0.000001);
|
||||
test_parse_fixed_r<27, int32_t>(reporter, -0.1, 0.1, 0.000001);
|
||||
test_parse_fixed_r<27, int32_t>(reporter, 7.9, 8.1, 0.000001);
|
||||
test_parse_fixed_r<16, int32_t>(reporter, -0.125, 0.125, 1.0 / (1 << 19));
|
||||
test_parse_fixed_r<16, int32_t>(reporter, -32768.125, -32766.875, 1.0 / (1 << 17));
|
||||
test_parse_fixed_r<16, int32_t>(reporter, 32766.875, 32768.125, 1.0 / (1 << 17));
|
||||
test_parse_fixed_r<16, int32_t>(reporter, -1.1, 1.1, 0.0001);
|
||||
|
||||
SkFixed fix;
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<27>("-17.1", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>("32768", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>("", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>(".", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>("123.", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>("a", &fix));
|
||||
REPORTER_ASSERT(reporter, !parse_fixed<16>(".123a", &fix));
|
||||
}
|
||||
|
||||
DEF_TEST(FontConfigParserAndroid, reporter) {
|
||||
test_parse_fixed(reporter);
|
||||
|
||||
bool resourcesMissing = false;
|
||||
|
||||
@ -137,7 +188,7 @@ DEF_TEST(FontConfigParserAndroid, reporter) {
|
||||
NULL);
|
||||
|
||||
if (v22FontFamilies.count() > 0) {
|
||||
REPORTER_ASSERT(reporter, v22FontFamilies.count() == 53);
|
||||
REPORTER_ASSERT(reporter, v22FontFamilies.count() == 54);
|
||||
REPORTER_ASSERT(reporter, CountFallbacks(v22FontFamilies) == 42);
|
||||
|
||||
DumpLoadedFonts(v22FontFamilies, "version 22");
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "Resources.h"
|
||||
#include "SkBitmapSource.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkFixed.h"
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkMallocPixelRef.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
@ -315,21 +317,14 @@ static void compare_bitmaps(skiatest::Reporter* reporter,
|
||||
}
|
||||
REPORTER_ASSERT(reporter, 0 == pixelErrors);
|
||||
}
|
||||
|
||||
static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
|
||||
// Load typeface form file to test CreateFromFile with index.
|
||||
SkString filename = GetResourcePath("/fonts/test.ttc");
|
||||
SkTypeface* typeface = SkTypeface::CreateFromFile(filename.c_str(), 1);
|
||||
if (!typeface) {
|
||||
SkDebugf("Could not run fontstream test because test.ttc not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a paint with the typeface we loaded.
|
||||
static void serialize_and_compare_typeface(SkTypeface* typeface, const char* text,
|
||||
skiatest::Reporter* reporter)
|
||||
{
|
||||
// Create a paint with the typeface.
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorGRAY);
|
||||
paint.setTextSize(SkIntToScalar(30));
|
||||
SkSafeUnref(paint.setTypeface(typeface));
|
||||
paint.setTypeface(typeface);
|
||||
|
||||
// Paint some text.
|
||||
SkPictureRecorder recorder;
|
||||
@ -338,7 +333,7 @@ static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
|
||||
SkIntToScalar(canvasRect.height()),
|
||||
NULL, 0);
|
||||
canvas->drawColor(SK_ColorWHITE);
|
||||
canvas->drawText("A!", 2, 24, 32, paint);
|
||||
canvas->drawText(text, 2, 24, 32, paint);
|
||||
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
|
||||
|
||||
// Serlialize picture and create its clone from stream.
|
||||
@ -353,6 +348,36 @@ static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
|
||||
compare_bitmaps(reporter, origBitmap, destBitmap);
|
||||
}
|
||||
|
||||
static void TestPictureTypefaceSerialization(skiatest::Reporter* reporter) {
|
||||
{
|
||||
// Load typeface from file to test CreateFromFile with index.
|
||||
SkString filename = GetResourcePath("/fonts/test.ttc");
|
||||
SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromFile(filename.c_str(), 1));
|
||||
if (!typeface) {
|
||||
SkDebugf("Could not run fontstream test because test.ttc not found.");
|
||||
} else {
|
||||
serialize_and_compare_typeface(typeface, "A!", reporter);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Load typeface as stream to create with axis settings.
|
||||
SkAutoTDelete<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf"));
|
||||
if (!distortable) {
|
||||
SkDebugf("Could not run fontstream test because Distortable.ttf not found.");
|
||||
} else {
|
||||
SkFixed axis = SK_FixedSqrt2;
|
||||
SkAutoTUnref<SkTypeface> typeface(SkTypeface::CreateFromFontData(
|
||||
new SkFontData(distortable.detach(), 0, &axis, 1)));
|
||||
if (!typeface) {
|
||||
SkDebugf("Could not run fontstream test because Distortable.ttf not created.");
|
||||
} else {
|
||||
serialize_and_compare_typeface(typeface, "abc", reporter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_bitmap_for_canvas(SkBitmap* bitmap) {
|
||||
bitmap->allocN32Pixels(kBitmapSize, kBitmapSize);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user