| /* | 
 |  * Copyright 2021 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "include/core/SkFontMgr.h" | 
 | #include "include/core/SkRefCnt.h" | 
 | #include "include/core/SkTypeface.h" | 
 | #include "include/ports/SkTypeface_mac.h" | 
 | #include "src/core/SkZip.h" | 
 | #include "tests/Test.h" | 
 |  | 
 | #include <stdarg.h> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #if 0 | 
 | static void SkMaybeDebugf(const char format[], ...) { | 
 |     va_list args; | 
 |     va_start(args, format); | 
 |     vprintf(format, args); | 
 |     va_end(args); | 
 | } | 
 | #else | 
 | static void SkMaybeDebugf(const char format[], ...) { } | 
 | #endif | 
 |  | 
 | DEF_TEST(TypefaceMacVariation, reporter) { | 
 |     auto makeSystemFont = [](float size) -> CTFontRef { | 
 |         // kCTFontUIFontSystem, kCTFontUIFontMessage | 
 |         return CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, size, nullptr); | 
 |     }; | 
 |  | 
 |     auto tagToString = [](SkFourByteTag tag) -> std::string { | 
 |       char buffer[5]; | 
 |       buffer[0] = (tag & 0xff000000) >> 24; | 
 |       buffer[1] = (tag & 0xff0000) >> 16; | 
 |       buffer[2] = (tag & 0xff00) >> 8; | 
 |       buffer[3] = tag & 0xff; | 
 |       buffer[4] = 0; | 
 |       return std::string(buffer); | 
 |     }; | 
 |  | 
 |     // This typeface has the issue. | 
 |     sk_sp<SkTypeface> typeface(SkMakeTypefaceFromCTFont(makeSystemFont(30))); | 
 |  | 
 |     // Since MakeFromFile creates at default size 12, these two are more comparable. | 
 |     // The first one has the issue and the second does not. | 
 |     //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(12)); | 
 |     //typeface = SkTypeface::MakeFromFile("/System/Library/Fonts/SFNS.ttf"); | 
 |  | 
 |     // Setting the initial opsz <= min, the reported wght axis is strange, but draws the same? | 
 |     //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17)); | 
 |     //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(17.01)); | 
 |  | 
 |     // Setting the initial opsz >= max, the requested variation doesn't take effect? | 
 |     //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(95.9)); | 
 |     //typeface = SkMakeTypefaceFromCTFont(makeSystemFont(96)); | 
 |  | 
 |     if (!typeface) { | 
 |         REPORTER_ASSERT(reporter, typeface); | 
 |         return; | 
 |     } | 
 |     using Coordinate = SkFontArguments::VariationPosition::Coordinate; | 
 |     using Axis = SkFontParameters::Variation::Axis; | 
 |  | 
 |     const int originalPositionCount = typeface->getVariationDesignPosition(nullptr, 0); | 
 |     std::vector<Coordinate> originalPosition(originalPositionCount); | 
 |     const int retrievedOriginalPositionCount = | 
 |         typeface->getVariationDesignPosition(originalPosition.data(), originalPosition.size()); | 
 |     if (!(retrievedOriginalPositionCount == originalPositionCount)) { | 
 |         REPORTER_ASSERT(reporter, retrievedOriginalPositionCount == originalPositionCount); | 
 |         return; | 
 |     } | 
 |  | 
 |     constexpr SkFourByteTag kGRADTag = SkSetFourByteTag('G', 'R', 'A', 'D'); | 
 |     constexpr SkFourByteTag kWghtTag = SkSetFourByteTag('w', 'g', 'h', 't'); | 
 |     constexpr SkFourByteTag kWdthTag = SkSetFourByteTag('w', 'd', 't', 'h'); | 
 |     constexpr SkFourByteTag kOpszTag = SkSetFourByteTag('o', 'p', 's', 'z'); | 
 |  | 
 |     SkMaybeDebugf("Original: "); | 
 |     for (auto& originalCoordinate : originalPosition) { | 
 |         SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(), | 
 |                                    originalCoordinate.value); | 
 |     } | 
 |     SkMaybeDebugf("\n\n"); | 
 |  | 
 |     const int originalAxisCount = typeface->getVariationDesignParameters(nullptr, 0); | 
 |     std::vector<Axis> originalAxes(originalAxisCount); | 
 |     const int returnedOriginalAxisCount = | 
 |         typeface->getVariationDesignParameters(originalAxes.data(), originalAxes.size()); | 
 |     if (!(returnedOriginalAxisCount == originalAxisCount)) { | 
 |         REPORTER_ASSERT(reporter, returnedOriginalAxisCount == originalAxisCount); | 
 |         return; | 
 |     } | 
 |  | 
 |     for (bool omitOpsz : {false, true}) { | 
 |     for (SkFourByteTag axisToBump : { 0u, kOpszTag, kWdthTag, kGRADTag }) { | 
 |     for (float testCoordinate : {100, 300, 400, 500, 700, 900}) { | 
 |         std::vector<Coordinate> expectedPosition; | 
 |         std::vector<Coordinate> requestPosition; | 
 |         SkMaybeDebugf("Request : "); | 
 |         for (auto& originalCoordinate : originalPosition) { | 
 |             float requestValue = originalCoordinate.value; | 
 |             if (originalCoordinate.axis == kOpszTag && omitOpsz) { | 
 |                 SkMaybeDebugf("#%s: %f# ", tagToString(originalCoordinate.axis).c_str(), | 
 |                                            requestValue); | 
 |             } else { | 
 |                 if (originalCoordinate.axis == axisToBump) { | 
 |                     // CoreText floats for the variation coordinates have limited precision. | 
 |                     // 'opsz' sems to have more precision since it is set somewhat independently. | 
 |                     //requestValue = nextafter(requestValue, HUGE_VALF); // Does not work. | 
 |                     requestValue += requestValue / 1024.0f; // Expect at least 10 bits. | 
 |                 } | 
 |                 if (originalCoordinate.axis == kWghtTag) { | 
 |                     requestValue = testCoordinate; | 
 |                 } | 
 |                 SkMaybeDebugf("(%s: %f) ", tagToString(originalCoordinate.axis).c_str(), | 
 |                                            requestValue); | 
 |                 requestPosition.push_back({originalCoordinate.axis, requestValue}); | 
 |             } | 
 |  | 
 |             float expectedValue = requestValue; | 
 |             for (auto& originalAxis : originalAxes) { | 
 |                 if (originalAxis.tag == originalCoordinate.axis) { | 
 |                     expectedValue = std::min(expectedValue, originalAxis.max); | 
 |                     expectedValue = std::max(expectedValue, originalAxis.min); | 
 |                 } | 
 |             } | 
 |             expectedPosition.push_back({originalCoordinate.axis, expectedValue}); | 
 |         } | 
 |         SkMaybeDebugf("\n"); | 
 |  | 
 |         SkMaybeDebugf("Expected: "); | 
 |         for (auto& expectedCoordinate : expectedPosition) { | 
 |              SkMaybeDebugf("(%s: %f) ", tagToString(expectedCoordinate.axis).c_str(), | 
 |                                         expectedCoordinate.value); | 
 |         } | 
 |         SkMaybeDebugf("\n"); | 
 |  | 
 |         SkFontArguments::VariationPosition variationPosition = | 
 |             { requestPosition.data(), (int)requestPosition.size() }; | 
 |         sk_sp<SkTypeface> cloneTypeface( | 
 |             typeface->makeClone(SkFontArguments().setVariationDesignPosition(variationPosition))); | 
 |  | 
 |         const int cloneAxisCount = cloneTypeface->getVariationDesignPosition(nullptr, 0); | 
 |         std::vector<Coordinate> clonePosition(cloneAxisCount); | 
 |         const int retrievedCloneAxisCount = | 
 |             cloneTypeface->getVariationDesignPosition(clonePosition.data(), clonePosition.size()); | 
 |         if (!(retrievedCloneAxisCount == cloneAxisCount)) { | 
 |             REPORTER_ASSERT(reporter, retrievedCloneAxisCount == cloneAxisCount); | 
 |             continue; | 
 |         } | 
 |  | 
 |         SkMaybeDebugf("Result  : "); | 
 |         for (auto& cloneCoordinate : clonePosition) { | 
 |              SkMaybeDebugf("(%s: %f) ", tagToString(cloneCoordinate.axis).c_str(), | 
 |                                         cloneCoordinate.value); | 
 |         } | 
 |         SkMaybeDebugf("\n"); | 
 |  | 
 |         if (clonePosition.size() != expectedPosition.size()) { | 
 |             REPORTER_ASSERT(reporter, clonePosition.size() == expectedPosition.size()); | 
 |             continue; | 
 |         } | 
 |  | 
 |         auto compareCoordinate = [](const Coordinate& a, const Coordinate& b) -> bool { | 
 |             return a.axis < b.axis; | 
 |         }; | 
 |         std::sort(clonePosition.begin(), clonePosition.end(), compareCoordinate); | 
 |         std::sort(expectedPosition.begin(), expectedPosition.end(), compareCoordinate); | 
 |         for (const auto&& [clone, expected] : SkMakeZip(clonePosition, expectedPosition)) { | 
 |             REPORTER_ASSERT(reporter, clone.axis == expected.axis); | 
 |             REPORTER_ASSERT(reporter, clone.value == expected.value); | 
 |         } | 
 |  | 
 |         SkMaybeDebugf("\n"); | 
 |     } | 
 |     } | 
 |     } | 
 | } |