1 /*
2 * Copyright 2024 The Android Open Source Project
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 #include "src/ports/SkFontScanner_fontations_priv.h"
8 #include "src/ports/SkTypeface_fontations_priv.h"
9 #include "src/ports/fontations/src/skpath_bridge.h"
10 #include "src/sfnt/SkOTUtils.h"
11
12 using namespace skia_private;
13
14 namespace {
make_bridge_font_ref(SkData * fontData,uint32_t index)15 rust::Box<::fontations_ffi::BridgeFontRef> make_bridge_font_ref(SkData* fontData, uint32_t index) {
16 rust::Slice<const uint8_t> slice{fontData->bytes(), fontData->size()};
17 return fontations_ffi::make_font_ref(slice, index);
18 }
19 // TODO(drott): Remove this once SkData::MakeFromStream is able to do this itself.
make_data_avoiding_copy(SkStreamAsset * stream)20 sk_sp<SkData> make_data_avoiding_copy(SkStreamAsset* stream) {
21 if (!stream) {
22 return SkData::MakeEmpty();
23 }
24 if (stream->getData()) {
25 return stream->getData();
26 }
27 if (stream->getMemoryBase() && stream->getLength()) {
28 return SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength());
29 }
30
31 return SkData::MakeFromStream(stream, stream->getLength());
32 }
33 } // namespace
34
SkFontScanner_Fontations()35 SkFontScanner_Fontations::SkFontScanner_Fontations() {}
36
~SkFontScanner_Fontations()37 SkFontScanner_Fontations::~SkFontScanner_Fontations() {}
38
scanFile(SkStreamAsset * stream,int * numFaces) const39 bool SkFontScanner_Fontations::scanFile(SkStreamAsset* stream, int* numFaces) const {
40 sk_sp<SkData> fontData = make_data_avoiding_copy(stream);
41 stream->rewind();
42 rust::Slice<const uint8_t> slice{fontData->bytes(), fontData->size()};
43 ::std::uint32_t num_fonts;
44 if (!fontations_ffi::font_or_collection(slice, num_fonts)) {
45 return false;
46 }
47 *numFaces = num_fonts == 0 ? 1 : num_fonts;
48 return true;
49 }
50
scanFace(SkStreamAsset * stream,int faceIndex,int * numInstances) const51 bool SkFontScanner_Fontations::scanFace(SkStreamAsset* stream,
52 int faceIndex,
53 int* numInstances) const {
54 sk_sp<SkData> fontData = make_data_avoiding_copy(stream);
55 rust::Box<fontations_ffi::BridgeFontRef> fontRef =
56 make_bridge_font_ref(fontData.get(), faceIndex);
57 stream->rewind();
58 if (!fontations_ffi::font_ref_is_valid(*fontRef)) {
59 return false;
60 }
61
62 if (numInstances) {
63 *numInstances = fontations_ffi::num_named_instances(*fontRef);
64 }
65 return true;
66 }
67
make_normalized_coords(fontations_ffi::BridgeFontRef const & bridgeFontRef,const SkFontArguments::VariationPosition & variationPosition)68 rust::Box<fontations_ffi::BridgeNormalizedCoords> make_normalized_coords(
69 fontations_ffi::BridgeFontRef const& bridgeFontRef,
70 const SkFontArguments::VariationPosition& variationPosition) {
71 // Cast is safe because of static_assert matching the structs above.
72 rust::Slice<const fontations_ffi::SkiaDesignCoordinate> coordinates(
73 reinterpret_cast<const fontations_ffi::SkiaDesignCoordinate*>(
74 variationPosition.coordinates),
75 variationPosition.coordinateCount);
76 return resolve_into_normalized_coords(bridgeFontRef, coordinates);
77 }
78
scanInstance(SkStreamAsset * stream,int faceIndex,int instanceIndex,SkString * name,SkFontStyle * style,bool * isFixedPitch,AxisDefinitions * axes,VariationPosition * position) const79 bool SkFontScanner_Fontations::scanInstance(SkStreamAsset* stream,
80 int faceIndex,
81 int instanceIndex,
82 SkString* name,
83 SkFontStyle* style,
84 bool* isFixedPitch,
85 AxisDefinitions* axes,
86 VariationPosition* position) const {
87 sk_sp<SkData> fontData = make_data_avoiding_copy(stream);
88 rust::Box<fontations_ffi::BridgeFontRef> bridgeFontFaceRef =
89 make_bridge_font_ref(fontData.get(), faceIndex);
90 stream->rewind();
91 if (!fontations_ffi::font_ref_is_valid(*bridgeFontFaceRef)) {
92 return false;
93 }
94
95 if (name != nullptr) {
96 rust::String readFamilyName = fontations_ffi::family_name(*bridgeFontFaceRef);
97 *name = SkString(readFamilyName.data(), readFamilyName.size());
98 }
99
100 if (isFixedPitch != nullptr) {
101 *isFixedPitch = false; // TODO
102 }
103
104 if (style != nullptr) {
105 auto num = SkToInt(fontations_ffi::num_named_instances(*bridgeFontFaceRef));
106 if (instanceIndex > num) {
107 return false;
108 } else if (instanceIndex == 0) {
109 // This is the default instance
110 rust::Slice<const fontations_ffi::SkiaDesignCoordinate> coordinates;
111 rust::Box<fontations_ffi::BridgeNormalizedCoords> normalizedCoords =
112 resolve_into_normalized_coords(*bridgeFontFaceRef, coordinates);
113 fontations_ffi::BridgeFontStyle fontStyle;
114 if (fontations_ffi::get_font_style(*bridgeFontFaceRef, *normalizedCoords, fontStyle)) {
115 *style = SkFontStyle(fontStyle.weight, fontStyle.width, (SkFontStyle::Slant)fontStyle.slant);
116 } else {
117 *style = SkFontStyle::Normal();
118 }
119 } else {
120 std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> extractedCoords =
121 nullptr;
122 size_t numNamedInstanceCoords =
123 fontations_ffi::coordinates_for_shifted_named_instance_index(
124 *bridgeFontFaceRef,
125 instanceIndex << 16,
126 rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate>());
127 extractedCoords.reset(new SkFontArguments::VariationPosition::Coordinate[numNamedInstanceCoords]);
128
129 rust::cxxbridge1::Slice<fontations_ffi::SkiaDesignCoordinate> targetSlice(
130 reinterpret_cast<fontations_ffi::SkiaDesignCoordinate*>(extractedCoords.get()),
131 numNamedInstanceCoords);
132 size_t retrievedNamedInstanceCoords =
133 fontations_ffi::coordinates_for_shifted_named_instance_index(
134 *bridgeFontFaceRef, faceIndex + (instanceIndex << 16), targetSlice);
135 if (numNamedInstanceCoords != retrievedNamedInstanceCoords) {
136 return false;
137 }
138
139 SkFontArguments::VariationPosition variationPosition;
140 variationPosition.coordinateCount = numNamedInstanceCoords;
141 variationPosition.coordinates = extractedCoords.get();
142
143 rust::Box<fontations_ffi::BridgeNormalizedCoords> normalizedCoords =
144 make_normalized_coords(*bridgeFontFaceRef, variationPosition);
145 fontations_ffi::BridgeFontStyle fontStyle;
146 if (fontations_ffi::get_font_style(*bridgeFontFaceRef, *normalizedCoords, fontStyle)) {
147 *style = SkFontStyle(fontStyle.weight,
148 fontStyle.width,
149 static_cast<SkFontStyle::Slant>(fontStyle.slant));
150 }
151 if (position) {
152 position->reset(variationPosition.coordinateCount);
153 for (int i = 0; i < variationPosition.coordinateCount; ++i) {
154 (*position)[i] = variationPosition.coordinates[i];
155 }
156 }
157 }
158 }
159
160 if (axes != nullptr) {
161 rust::Box<fontations_ffi::BridgeFontRef> bridgeFontNamedInstanceRef =
162 make_bridge_font_ref(fontData.get(), faceIndex + (instanceIndex << 16));
163 stream->rewind();
164 auto size = SkToInt(fontations_ffi::num_axes(*bridgeFontNamedInstanceRef));
165 axes->reset(size);
166 auto variationAxes = std::make_unique<SkFontParameters::Variation::Axis[]>(size);
167 sk_fontations::AxisWrapper axisWrapper(variationAxes.get(), size);
168 auto size1 = fontations_ffi::populate_axes(*bridgeFontNamedInstanceRef, axisWrapper);
169 SkASSERT(size == size1);
170 for (auto i = 0; i < size; ++i) {
171 const auto var = variationAxes[i];
172 (*axes)[i].tag = var.tag;
173 (*axes)[i].min = var.min;
174 (*axes)[i].def = var.def;
175 (*axes)[i].max = var.max;
176 }
177 }
178
179 return true;
180 }
181
MakeFromStream(std::unique_ptr<SkStreamAsset> stream,const SkFontArguments & args) const182 sk_sp<SkTypeface> SkFontScanner_Fontations::MakeFromStream(std::unique_ptr<SkStreamAsset> stream,
183 const SkFontArguments& args) const {
184 return SkTypeface_Fontations::MakeFromStream(std::move(stream), args);
185 }
186
getFactoryId() const187 SkTypeface::FactoryId SkFontScanner_Fontations::getFactoryId() const {
188 return SkTypeface_Fontations::FactoryId;
189 }
190
SkFontScanner_Make_Fontations()191 std::unique_ptr<SkFontScanner> SkFontScanner_Make_Fontations() {
192 return std::make_unique<SkFontScanner_Fontations>();
193 }
194