• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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