xref: /aosp_15_r20/external/skia/modules/svg/src/SkSVGOpenTypeSVGDecoder.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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 "modules/svg/include/SkSVGOpenTypeSVGDecoder.h"
9 
10 #include "include/codec/SkCodec.h"
11 #include "include/codec/SkJpegDecoder.h"
12 #include "include/codec/SkPngDecoder.h"
13 #include "include/core/SkColor.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkOpenTypeSVGDecoder.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkSize.h"
18 #include "include/core/SkSpan.h"
19 #include "include/core/SkStream.h"
20 #include "include/core/SkString.h"
21 #include "include/core/SkTypes.h"
22 #include "modules/skresources/include/SkResources.h"
23 #include "modules/svg/include/SkSVGAttribute.h"
24 #include "modules/svg/include/SkSVGDOM.h"
25 #include "modules/svg/include/SkSVGRenderContext.h"
26 #include "modules/svg/include/SkSVGTypes.h"
27 #include "src/base/SkBase64.h"
28 #include "src/core/SkEnumerate.h"
29 #include "src/core/SkTHash.h"
30 
31 #include <array>
32 #include <cstring>
33 #include <memory>
34 #include <utility>
35 
36 using namespace skia_private;
37 
38 namespace {
39 class DataResourceProvider final : public skresources::ResourceProvider {
40 public:
Make()41     static sk_sp<skresources::ResourceProvider> Make() {
42         return sk_sp<skresources::ResourceProvider>(new DataResourceProvider());
43     }
44 
loadImageAsset(const char rpath[],const char rname[],const char rid[]) const45     sk_sp<skresources::ImageAsset> loadImageAsset(const char rpath[],
46                                                   const char rname[],
47                                                   const char rid[]) const override {
48         if (auto data = decode_datauri("data:image/", rname)) {
49             std::unique_ptr<SkCodec> codec = nullptr;
50             if (SkPngDecoder::IsPng(data->bytes(), data->size())) {
51                 codec = SkPngDecoder::Decode(data, nullptr);
52             } else if (SkJpegDecoder::IsJpeg(data->bytes(), data->size())) {
53                 codec = SkJpegDecoder::Decode(data, nullptr);
54             } else {
55                 // The spec says only JPEG or PNG should be used to encode the embedded data.
56                 // https://learn.microsoft.com/en-us/typography/opentype/spec/svg#svg-capability-requirements-and-restrictions
57                 SkDEBUGFAIL("Unsupported codec");
58                 return nullptr;
59             }
60             if (!codec) {
61                 return nullptr;
62             }
63             return skresources::MultiFrameImageAsset::Make(std::move(codec));
64         }
65         return nullptr;
66     }
67 
68 private:
69     DataResourceProvider() = default;
70 
decode_datauri(const char prefix[],const char uri[])71     static sk_sp<SkData> decode_datauri(const char prefix[], const char uri[]) {
72         // We only handle B64 encoded image dataURIs: data:image/<type>;base64,<data>
73         // (https://en.wikipedia.org/wiki/Data_URI_scheme)
74         static constexpr char kDataURIEncodingStr[] = ";base64,";
75 
76         const size_t prefixLen = strlen(prefix);
77         if (strncmp(uri, prefix, prefixLen) != 0) {
78             return nullptr;
79         }
80 
81         const char* encoding = strstr(uri + prefixLen, kDataURIEncodingStr);
82         if (!encoding) {
83             return nullptr;
84         }
85 
86         const char* b64Data = encoding + std::size(kDataURIEncodingStr) - 1;
87         size_t b64DataLen = strlen(b64Data);
88         size_t dataLen;
89         if (SkBase64::Decode(b64Data, b64DataLen, nullptr, &dataLen) != SkBase64::kNoError) {
90             return nullptr;
91         }
92 
93         sk_sp<SkData> data = SkData::MakeUninitialized(dataLen);
94         void* rawData = data->writable_data();
95         if (SkBase64::Decode(b64Data, b64DataLen, rawData, &dataLen) != SkBase64::kNoError) {
96             return nullptr;
97         }
98 
99         return data;
100     }
101 
102     using INHERITED = ResourceProvider;
103 };
104 }  // namespace
105 
SkSVGOpenTypeSVGDecoder(sk_sp<SkSVGDOM> skSvg,size_t approximateSize)106 SkSVGOpenTypeSVGDecoder::SkSVGOpenTypeSVGDecoder(sk_sp<SkSVGDOM> skSvg, size_t approximateSize)
107     : fSkSvg(std::move(skSvg))
108     , fApproximateSize(approximateSize)
109 {}
110 
111 SkSVGOpenTypeSVGDecoder::~SkSVGOpenTypeSVGDecoder() = default;
112 
Make(const uint8_t * svg,size_t svgLength)113 std::unique_ptr<SkOpenTypeSVGDecoder> SkSVGOpenTypeSVGDecoder::Make(const uint8_t* svg,
114                                                                     size_t svgLength) {
115     std::unique_ptr<SkStreamAsset> stream = SkMemoryStream::MakeDirect(svg, svgLength);
116     if (!stream) {
117         return nullptr;
118     }
119     SkSVGDOM::Builder builder;
120     builder.setResourceProvider(DataResourceProvider::Make());
121     // We shouldn't need to set this builder's font manager or shaping utils because hopefully
122     // the SVG we are decoding doesn't itself have <text> tags.
123     sk_sp<SkSVGDOM> skSvg = builder.make(*stream);
124     if (!skSvg) {
125         return nullptr;
126     }
127     return std::unique_ptr<SkOpenTypeSVGDecoder>(
128         new SkSVGOpenTypeSVGDecoder(std::move(skSvg), svgLength));
129 }
130 
approximateSize()131 size_t SkSVGOpenTypeSVGDecoder::approximateSize() {
132     // TODO
133     return fApproximateSize;
134 }
135 
render(SkCanvas & canvas,int upem,SkGlyphID glyphId,SkColor foregroundColor,SkSpan<SkColor> palette)136 bool SkSVGOpenTypeSVGDecoder::render(SkCanvas& canvas, int upem, SkGlyphID glyphId,
137                                      SkColor foregroundColor, SkSpan<SkColor> palette) {
138     SkSize emSize = SkSize::Make(SkScalar(upem), SkScalar(upem));
139     fSkSvg->setContainerSize(emSize);
140 
141     SkSVGPresentationContext pctx;
142     pctx.fInherited.fColor.set(foregroundColor);
143 
144     THashMap<SkString, SkSVGColorType> namedColors;
145     if (!palette.empty()) {
146         for (auto&& [i, color] : SkMakeEnumerate(palette)) {
147             constexpr const size_t colorStringLen = sizeof("color") - 1;
148             char colorIdString[colorStringLen + kSkStrAppendU32_MaxSize + 1] = "color";
149             *SkStrAppendU32(colorIdString + colorStringLen, i) = 0;
150 
151             namedColors.set(SkString(colorIdString), color);
152         }
153         pctx.fNamedColors = &namedColors;
154     }
155 
156     constexpr const size_t glyphStringLen = sizeof("glyph") - 1;
157     char glyphIdString[glyphStringLen + kSkStrAppendU32_MaxSize + 1] = "glyph";
158     *SkStrAppendU32(glyphIdString + glyphStringLen, glyphId) = 0;
159 
160     fSkSvg->renderNode(&canvas, pctx, glyphIdString);
161     return true;
162 }
163