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:
bungeman 2015-05-20 09:21:04 -07:00 committed by Commit bot
parent d223eb36a0
commit 41868fe562
24 changed files with 897 additions and 133 deletions

View 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);
}

View File

@ -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',
],
}
}

View File

@ -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[],

View File

@ -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[],

View File

@ -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>

View File

@ -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.
};

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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 ||

View File

@ -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);

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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));
}

View File

@ -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;

View File

@ -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());
}

View File

@ -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");

View File

@ -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);
}