class SK_API SkFontStyle {
public:
enum Weight {
- kThin_Weight = 100,
- kExtraLight_Weight = 200,
- kLight_Weight = 300,
- kNormal_Weight = 400,
- kMedium_Weight = 500,
- kSemiBold_Weight = 600,
- kBold_Weight = 700,
- kExtraBold_Weight = 800,
- kBlack_Weight = 900
+ kInvisible_Weight = 0,
+ kThin_Weight = 100,
+ kExtraLight_Weight = 200,
+ kLight_Weight = 300,
+ kNormal_Weight = 400,
+ kMedium_Weight = 500,
+ kSemiBold_Weight = 600,
+ kBold_Weight = 700,
+ kExtraBold_Weight = 800,
+ kBlack_Weight = 900,
+ kExtraBlack_Weight = 1000
};
enum Width {
/** Sets the fixedPitch bit. If used, must be called in the constructor. */
void setIsFixedPitch(bool isFixedPitch) { fIsFixedPitch = isFixedPitch; }
+ /** Sets the font style. If used, must be called in the constructor. */
+ void setFontStyle(SkFontStyle style) { fStyle = style; }
friend class SkScalerContext;
static SkTypeface* GetDefaultTypeface(Style style = SkTypeface::kNormal);
SkFontStyle::SkFontStyle(int weight, int width, Slant slant) {
fUnion.fU32 = 0;
- fUnion.fR.fWeight = SkTPin<int>(weight, kThin_Weight, kBlack_Weight);
+ fUnion.fR.fWeight = SkTPin<int>(weight, kInvisible_Weight, kExtraBlack_Weight);
fUnion.fR.fWidth = SkTPin<int>(width, kUltraCondensed_Width, kUltaExpanded_Width);
fUnion.fR.fSlant = SkTPin<int>(slant, kUpright_Slant, kOblique_Slant);
}
{ SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
{ SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
{ SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
- { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
+ { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
};
int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
weightRanges, SK_ARRAY_COUNT(weightRanges));
{ SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
{ SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
{ SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
- { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
+ { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
};
int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
{ "standard", SkFontStyle::kNormal_Weight },
{ "thin", SkFontStyle::kThin_Weight },
{ "ultra", SkFontStyle::kExtraBold_Weight },
- { "ultrablack", 1000 },
+ { "ultrablack", SkFontStyle::kExtraBlack_Weight },
{ "ultrabold", SkFontStyle::kExtraBold_Weight },
- { "ultraheavy", 1000 },
+ { "ultraheavy", SkFontStyle::kExtraBlack_Weight },
{ "ultralight", SkFontStyle::kExtraLight_Weight },
};
int const index = SkStrLCSearch(&commonWeights[0].name, SK_ARRAY_COUNT(commonWeights),
///////////////////////////////////////////////////////////////////////////////
-static bool find_dict_float(CFDictionaryRef dict, CFStringRef name, float* value) {
+static bool find_dict_CGFloat(CFDictionaryRef dict, CFStringRef name, CGFloat* value) {
CFNumberRef num;
return CFDictionaryGetValueIfPresent(dict, name, (const void**)&num)
&& CFNumberIsFloatType(num)
- && CFNumberGetValue(num, kCFNumberFloatType, value);
+ && CFNumberGetValue(num, kCFNumberCGFloatType, value);
}
-static int unit_weight_to_fontstyle(float unit) {
- float value;
- if (unit < 0) {
- value = 100 + (1 + unit) * 300;
- } else {
- value = 400 + unit * 500;
+template <typename S, typename D, typename C> struct LinearInterpolater {
+ struct Mapping {
+ S src_val;
+ D dst_val;
+ };
+ constexpr LinearInterpolater(Mapping const mapping[], int mappingCount)
+ : fMapping(mapping), fMappingCount(mappingCount) {}
+
+ static D map(S value, S src_min, S src_max, D dst_min, D dst_max) {
+ SkASSERT(src_min < src_max);
+ SkASSERT(dst_min <= dst_max);
+ return C()(dst_min + (((value - src_min) * (dst_max - dst_min)) / (src_max - src_min)));
}
- return sk_float_round2int(value);
-}
-static int unit_width_to_fontstyle(float unit) {
- float value;
- if (unit < 0) {
- value = 1 + (1 + unit) * 4;
- } else {
- value = 5 + unit * 4;
+ int map(S val) const {
+ // -Inf to [0]
+ if (val < fMapping[0].src_val) {
+ return fMapping[0].dst_val;
+ }
+
+ // Linear from [i] to [i+1]
+ for (int i = 0; i < fMappingCount - 1; ++i) {
+ if (val < fMapping[i+1].src_val) {
+ return map(val, fMapping[i].src_val, fMapping[i+1].src_val,
+ fMapping[i].dst_val, fMapping[i+1].dst_val);
+ }
+ }
+
+ // From [n] to +Inf
+ // if (fcweight < Inf)
+ return fMapping[fMappingCount - 1].dst_val;
}
- return sk_float_round2int(value);
+
+ Mapping const * fMapping;
+ int fMappingCount;
+};
+
+struct RoundCGFloatToInt {
+ int operator()(CGFloat s) { return s + 0.5; }
+};
+
+static int ct_weight_to_fontstyle(CGFloat cgWeight) {
+ using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+ // Values determined by creating font data with every weight, creating a CTFont,
+ // and asking the CTFont for its weight. See TypefaceStyle test for basics.
+
+ // Note that Mac supports the old OS2 version A so 0 through 10 are as if multiplied by 100.
+ // However, on this end we can't tell.
+ static constexpr Interpolator::Mapping weightMappings[] = {
+ { -1.00, 0 },
+ { -0.70, 100 },
+ { -0.50, 200 },
+ { -0.23, 300 },
+ { 0.00, 400 },
+ { 0.20, 500 },
+ { 0.30, 600 },
+ { 0.40, 700 },
+ { 0.60, 800 },
+ { 0.80, 900 },
+ { 1.00, 1000 },
+ };
+ static constexpr Interpolator interpolater(weightMappings, SK_ARRAY_COUNT(weightMappings));
+ return interpolater.map(cgWeight);
+}
+
+static int ct_width_to_fontstyle(CGFloat cgWidth) {
+ using Interpolator = LinearInterpolater<CGFloat, int, RoundCGFloatToInt>;
+
+ // Values determined by creating font data with every width, creating a CTFont,
+ // and asking the CTFont for its width. See TypefaceStyle test for basics.
+ static constexpr Interpolator::Mapping widthMappings[] = {
+ { -0.5, 0 },
+ { 0.5, 10 },
+ };
+ static constexpr Interpolator interpolater(widthMappings, SK_ARRAY_COUNT(widthMappings));
+ return interpolater.map(cgWidth);
}
static SkFontStyle fontstyle_from_descriptor(CTFontDescriptorRef desc) {
return SkFontStyle();
}
- float weight, width, slant;
- if (!find_dict_float(dict, kCTFontWeightTrait, &weight)) {
+ CGFloat weight, width, slant;
+ if (!find_dict_CGFloat(dict, kCTFontWeightTrait, &weight)) {
weight = 0;
}
- if (!find_dict_float(dict, kCTFontWidthTrait, &width)) {
+ if (!find_dict_CGFloat(dict, kCTFontWidthTrait, &width)) {
width = 0;
}
- if (!find_dict_float(dict, kCTFontSlantTrait, &slant)) {
+ if (!find_dict_CGFloat(dict, kCTFontSlantTrait, &slant)) {
slant = 0;
}
- return SkFontStyle(unit_weight_to_fontstyle(weight),
- unit_width_to_fontstyle(width),
+ return SkFontStyle(ct_weight_to_fontstyle(weight),
+ ct_width_to_fontstyle(width),
slant ? SkFontStyle::kItalic_Slant
: SkFontStyle::kUpright_Slant);
}
, fLogFont(lf)
, fSerializeAsStream(serializeAsStream)
{
-
- // If the font has cubic outlines, it will not be rendered with ClearType.
HFONT font = CreateFontIndirect(&lf);
HDC deviceContext = ::CreateCompatibleDC(nullptr);
// The fixed pitch bit is set if the font is *not* fixed pitch.
this->setIsFixedPitch((textMetric.tmPitchAndFamily & TMPF_FIXED_PITCH) == 0);
+ this->setFontStyle(SkFontStyle(textMetric.tmWeight, style.width(), style.slant()));
// Used a logfont on a memory context, should never get a device font.
// Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
+ // If the font has cubic outlines, it will not be rendered with ClearType.
fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
(textMetric.tmPitchAndFamily & TMPF_DEVICE));
}
{ SkTFixed<FC_WEIGHT_BOLD>::value, SkTFixed<SkFS::kBold_Weight>::value },
{ SkTFixed<FC_WEIGHT_EXTRABOLD>::value, SkTFixed<SkFS::kExtraBold_Weight>::value },
{ SkTFixed<FC_WEIGHT_BLACK>::value, SkTFixed<SkFS::kBlack_Weight>::value },
- { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<1000>::value },
+ { SkTFixed<FC_WEIGHT_EXTRABLACK>::value, SkTFixed<SkFS::kExtraBlack_Weight>::value },
};
int weight = map_ranges(get_int(pattern, FC_WEIGHT, FC_WEIGHT_REGULAR),
weightRanges, SK_ARRAY_COUNT(weightRanges));
{ SkTFixed<SkFS::kBold_Weight>::value, SkTFixed<FC_WEIGHT_BOLD>::value },
{ SkTFixed<SkFS::kExtraBold_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABOLD>::value },
{ SkTFixed<SkFS::kBlack_Weight>::value, SkTFixed<FC_WEIGHT_BLACK>::value },
- { SkTFixed<1000>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
+ { SkTFixed<SkFS::kExtraBlack_Weight>::value, SkTFixed<FC_WEIGHT_EXTRABLACK>::value },
};
int weight = map_ranges(style.weight(), weightRanges, SK_ARRAY_COUNT(weightRanges));
* found in the LICENSE file.
*/
+#include "SkData.h"
+#include "SkOTTable_OS_2.h"
+#include "SkSFNTHeader.h"
+#include "SkStream.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
#include "SkTypefaceCache.h"
+#include "Resources.h"
#include "Test.h"
+#include <memory>
+
+static void TypefaceStyle_test(skiatest::Reporter* reporter,
+ uint16_t weight, uint16_t width, SkData* data)
+{
+ sk_sp<SkData> dataCopy;
+ SkData* dataToUse = data;
+ if (!dataToUse->unique()) {
+ dataCopy = SkData::MakeWithCopy(data->data(), data->size());
+ dataToUse = dataCopy.get();
+ }
+ SkSFNTHeader* sfntHeader = static_cast<SkSFNTHeader*>(dataToUse->writable_data());
+
+ SkSFNTHeader::TableDirectoryEntry* tableEntry =
+ SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
+ SkSFNTHeader::TableDirectoryEntry* os2TableEntry = nullptr;
+ int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
+ for (int tableEntryIndex = 0; tableEntryIndex < numTables; ++tableEntryIndex) {
+ if (SkOTTableOS2::TAG == tableEntry[tableEntryIndex].tag) {
+ os2TableEntry = tableEntry + tableEntryIndex;
+ break;
+ }
+ }
+ SkASSERT_RELEASE(os2TableEntry);
+
+ size_t os2TableOffset = SkEndian_SwapBE32(os2TableEntry->offset);
+ SkOTTableOS2_V0* os2Table = SkTAddOffset<SkOTTableOS2_V0>(sfntHeader, os2TableOffset);
+ os2Table->usWeightClass.value = SkEndian_SwapBE16(weight);
+ using WidthType = SkOTTableOS2_V0::WidthClass::Value;
+ os2Table->usWidthClass.value = static_cast<WidthType>(SkEndian_SwapBE16(width));
+
+ sk_sp<SkTypeface> newTypeface(SkTypeface::MakeFromStream(new SkMemoryStream(dataToUse)));
+ SkASSERT_RELEASE(newTypeface);
+
+ SkFontStyle newStyle = newTypeface->fontStyle();
+
+ //printf("%d, %f\n", weight, (newStyle.weight() - (float)0x7FFF) / (float)0x7FFF);
+ //printf("%d, %f\n", width , (newStyle.width() - (float)0x7F) / (float)0x7F);
+ //printf("%d, %d\n", weight, newStyle.weight());
+ //printf("%d, %d\n", width , newStyle.width());
+
+ // Some back-ends (CG, GDI, DW) support OS/2 version A which uses 0 - 10 (but all differently).
+ REPORTER_ASSERT(reporter,
+ newStyle.weight() == weight ||
+ (weight <= 10 && newStyle.weight() == 100 * weight) ||
+ (weight == 4 && newStyle.weight() == 350) || // GDI weirdness
+ (weight == 5 && newStyle.weight() == 400) || // GDI weirdness
+ (weight == 0 && newStyle.weight() == 1) || // DW weirdness
+ (weight == 1000 && newStyle.weight() == 999) // DW weirdness
+ );
+
+ // Some back-ends (GDI) don't support width, ensure these always report 'medium'.
+ REPORTER_ASSERT(reporter,
+ newStyle.width() == width ||
+ newStyle.width() == 5);
+}
+DEF_TEST(TypefaceStyle, reporter) {
+ std::unique_ptr<SkStreamAsset> stream(GetResourceAsStream("/fonts/Em.ttf"));
+ if (!stream) {
+ REPORT_FAILURE(reporter, "/fonts/Em.ttf", SkString("Cannot load resource"));
+ return;
+ }
+ sk_sp<SkData> data(SkData::MakeFromStream(stream.get(), stream->getLength()));
+
+ using SkFS = SkFontStyle;
+ for (int weight = SkFS::kInvisible_Weight; weight <= SkFS::kExtraBlack_Weight; ++weight) {
+ TypefaceStyle_test(reporter, weight, 5, data.get());
+ }
+ for (int width = SkFS::kUltraCondensed_Width; width <= SkFS::kUltaExpanded_Width; ++width) {
+ TypefaceStyle_test(reporter, 400, width, data.get());
+ }
+}
+
DEF_TEST(Typeface, reporter) {
sk_sp<SkTypeface> t1(SkTypeface::MakeFromName(nullptr, SkFontStyle()));