1 /*
2 * Copyright 2021 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkFontMgr.h"
9 #include "include/core/SkRefCnt.h"
10 #include "include/core/SkTypeface.h"
11 #include "include/ports/SkTypeface_mac.h"
12 #include "src/core/SkZip.h"
13 #include "tests/Test.h"
14
15 #include <stdarg.h>
16 #include <string>
17 #include <vector>
18
19 #if 0
20 static void SkMaybeDebugf(const char format[], ...) {
21 va_list args;
22 va_start(args, format);
23 vprintf(format, args);
24 va_end(args);
25 }
26 #else
SkMaybeDebugf(const char format[],...)27 static void SkMaybeDebugf(const char format[], ...) { }
28 #endif
29
DEF_TEST(TypefaceMacVariation,reporter)30 DEF_TEST(TypefaceMacVariation, reporter) {
31 auto makeSystemFont = [](float size) -> CTFontRef {
32 // kCTFontUIFontSystem, kCTFontUIFontMessage
33 return CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, size, nullptr);
34 };
35
36 auto tagToString = [](SkFourByteTag tag) -> std::string {
37 char buffer[5];
38 buffer[0] = (tag & 0xff000000) >> 24;
39 buffer[1] = (tag & 0xff0000) >> 16;
40 buffer[2] = (tag & 0xff00) >> 8;
41 buffer[3] = tag & 0xff;
42 buffer[4] = 0;
43 return std::string(buffer);
44 };
45
46 // This typeface has the issue.
47 sk_sp<SkTypeface> typeface(SkMakeTypefaceFromCTFont(makeSystemFont(30)));
48
49 // Since MakeFromFile creates at default size 12, these two are more comparable.
50 // The first one has the issue and the second does not.
51 //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(12));
52 //typeface = SkTypeface::MakeFromFile("/System/Library/Fonts/SFNS.ttf");
53
54 // Setting the initial opsz <= min, the reported wght axis is strange, but draws the same?
55 //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17));
56 //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17.01));
57
58 // Setting the initial opsz >= max, the requested variation doesn't take effect?
59 //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(95.9));
60 //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(96));
61
62 if (!typeface) {
63 REPORTER_ASSERT(reporter, typeface);
64 return;
65 }
66 using Coordinate = SkFontArguments::VariationPosition::Coordinate;
67 using Axis = SkFontParameters::Variation::Axis;
68
69 const int originalPositionCount = typeface->getVariationDesignPosition(nullptr, 0);
70 std::vector<Coordinate> originalPosition(originalPositionCount);
71 const int retrievedOriginalPositionCount =
72 typeface->getVariationDesignPosition(originalPosition.data(), originalPosition.size());
73 if (!(retrievedOriginalPositionCount == originalPositionCount)) {
74 REPORTER_ASSERT(reporter, retrievedOriginalPositionCount == originalPositionCount);
75 return;
76 }
77
78 constexpr SkFourByteTag kGRADTag = SkSetFourByteTag('G', 'R', 'A', 'D');
79 constexpr SkFourByteTag kWghtTag = SkSetFourByteTag('w', 'g', 'h', 't');
80 constexpr SkFourByteTag kWdthTag = SkSetFourByteTag('w', 'd', 't', 'h');
81 constexpr SkFourByteTag kOpszTag = SkSetFourByteTag('o', 'p', 's', 'z');
82
83 SkMaybeDebugf("Original: ");
84 for (auto& originalCoordinate : originalPosition) {
85 SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(),
86 originalCoordinate.value);
87 }
88 SkMaybeDebugf("\n\n");
89
90 const int originalAxisCount = typeface->getVariationDesignParameters(nullptr, 0);
91 std::vector<Axis> originalAxes(originalAxisCount);
92 const int returnedOriginalAxisCount =
93 typeface->getVariationDesignParameters(originalAxes.data(), originalAxes.size());
94 if (!(returnedOriginalAxisCount == originalAxisCount)) {
95 REPORTER_ASSERT(reporter, returnedOriginalAxisCount == originalAxisCount);
96 return;
97 }
98
99 for (bool omitOpsz : {false, true}) {
100 for (SkFourByteTag axisToBump : { 0u, kOpszTag, kWdthTag, kGRADTag }) {
101 for (float testCoordinate : {100, 300, 400, 500, 700, 900}) {
102 std::vector<Coordinate> expectedPosition;
103 std::vector<Coordinate> requestPosition;
104 SkMaybeDebugf("Request : ");
105 for (auto& originalCoordinate : originalPosition) {
106 float requestValue = originalCoordinate.value;
107 if (originalCoordinate.axis == kOpszTag && omitOpsz) {
108 SkMaybeDebugf("#%s: %f# ", tagToString(originalCoordinate.axis).c_str(),
109 requestValue);
110 } else {
111 if (originalCoordinate.axis == axisToBump) {
112 // CoreText floats for the variation coordinates have limited precision.
113 // 'opsz' sems to have more precision since it is set somewhat independently.
114 //requestValue = nextafter(requestValue, HUGE_VALF); // Does not work.
115 requestValue += requestValue / 1024.0f; // Expect at least 10 bits.
116 }
117 if (originalCoordinate.axis == kWghtTag) {
118 requestValue = testCoordinate;
119 }
120 SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(),
121 requestValue);
122 requestPosition.push_back({originalCoordinate.axis, requestValue});
123 }
124
125 float expectedValue = requestValue;
126 for (auto& originalAxis : originalAxes) {
127 if (originalAxis.tag == originalCoordinate.axis) {
128 expectedValue = std::min(expectedValue, originalAxis.max);
129 expectedValue = std::max(expectedValue, originalAxis.min);
130 }
131 }
132 expectedPosition.push_back({originalCoordinate.axis, expectedValue});
133 }
134 SkMaybeDebugf("\n");
135
136 SkMaybeDebugf("Expected: ");
137 for (auto& expectedCoordinate : expectedPosition) {
138 SkMaybeDebugf("(%s: %f) ", tagToString(expectedCoordinate.axis).c_str(),
139 expectedCoordinate.value);
140 }
141 SkMaybeDebugf("\n");
142
143 SkFontArguments::VariationPosition variationPosition =
144 { requestPosition.data(), (int)requestPosition.size() };
145 sk_sp<SkTypeface> cloneTypeface(
146 typeface->makeClone(SkFontArguments().setVariationDesignPosition(variationPosition)));
147
148 const int cloneAxisCount = cloneTypeface->getVariationDesignPosition(nullptr, 0);
149 std::vector<Coordinate> clonePosition(cloneAxisCount);
150 const int retrievedCloneAxisCount =
151 cloneTypeface->getVariationDesignPosition(clonePosition.data(), clonePosition.size());
152 if (!(retrievedCloneAxisCount == cloneAxisCount)) {
153 REPORTER_ASSERT(reporter, retrievedCloneAxisCount == cloneAxisCount);
154 continue;
155 }
156
157 SkMaybeDebugf("Result : ");
158 for (auto& cloneCoordinate : clonePosition) {
159 SkMaybeDebugf("(%s: %f) ", tagToString(cloneCoordinate.axis).c_str(),
160 cloneCoordinate.value);
161 }
162 SkMaybeDebugf("\n");
163
164 if (clonePosition.size() != expectedPosition.size()) {
165 REPORTER_ASSERT(reporter, clonePosition.size() == expectedPosition.size());
166 continue;
167 }
168
169 auto compareCoordinate = [](const Coordinate& a, const Coordinate& b) -> bool {
170 return a.axis < b.axis;
171 };
172 std::sort(clonePosition.begin(), clonePosition.end(), compareCoordinate);
173 std::sort(expectedPosition.begin(), expectedPosition.end(), compareCoordinate);
174 for (const auto&& [clone, expected] : SkMakeZip(clonePosition, expectedPosition)) {
175 REPORTER_ASSERT(reporter, clone.axis == expected.axis);
176 REPORTER_ASSERT(reporter, clone.value == expected.value);
177 }
178
179 SkMaybeDebugf("\n");
180 }
181 }
182 }
183 }
184