Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPBitmapFormat.h"
25 : #include "SPHtmlParser.h"
26 : #include "SPLog.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::bitmap::custom {
29 :
30 1500 : static size_t detectSvgSize(StringView value) {
31 1500 : StringView str(value);
32 1500 : auto fRes = str.readFloat();
33 1500 : if (!fRes.valid()) {
34 0 : return 0;
35 : }
36 :
37 1500 : auto fvalue = fRes.get();
38 1500 : if (fvalue == 0.0f) {
39 0 : return 0;
40 : }
41 :
42 1500 : str.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
43 :
44 1500 : if (str == "px" || str.empty()) {
45 : // do nothing
46 900 : } else if (str == "pt") {
47 225 : fvalue = fvalue * 4.0f / 3.0f;
48 675 : } else if (str == "pc") {
49 225 : fvalue = fvalue * 15.0f;
50 450 : } else if (str == "mm") {
51 225 : fvalue = fvalue * 3.543307f;
52 225 : } else if (str == "cm") {
53 225 : fvalue = fvalue * 35.43307f;
54 : } else {
55 0 : log::error("Bitmap", "Invalid size metric in svg: %s", str.data());
56 0 : return 0;
57 : }
58 :
59 1500 : return size_t(ceilf(fvalue));
60 : }
61 :
62 2525 : static bool detectSvg(StringView str, uint32_t &w, uint32_t &h) {
63 2525 : str.skipUntilString("<svg", true);
64 2525 : if (!str.starts_with("<svg")) {
65 1775 : return false;
66 : }
67 750 : str += "<svg"_len;
68 :
69 750 : if (!str.empty() && str.is<StringView::CharGroup<CharGroupId::WhiteSpace>>()) {
70 750 : bool found = false;
71 750 : bool isSvg = false;
72 750 : uint32_t width = 0;
73 750 : uint32_t height = 0;
74 3700 : while (!found && !str.empty()) {
75 2950 : str.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
76 2950 : auto key = html::Tag_readAttrName(str);
77 2950 : auto value = html::Tag_readAttrValue(str);
78 2950 : if (!key.empty() && !value.empty()) {
79 2950 : if (key == "xmlns") {
80 750 : if (value.is("http://www.w3.org/2000/svg")) {
81 750 : isSvg = true;
82 : }
83 2200 : } else if (key == "width") {
84 750 : width = detectSvgSize(value);
85 1450 : } else if (key == "height") {
86 750 : height = detectSvgSize(value);
87 : }
88 2950 : if (isSvg && width && height) {
89 750 : found = true;
90 : }
91 : }
92 : }
93 750 : if (isSvg) {
94 750 : w = width;
95 750 : h = height;
96 : }
97 750 : return isSvg;
98 : }
99 :
100 0 : return false;
101 : }
102 :
103 1725 : static bool detectSvg(const StringView &buf) {
104 1725 : uint32_t w = 0, h = 0;
105 3450 : return detectSvg(buf, w, h);
106 : }
107 :
108 1725 : static bool isSvg(const uint8_t * data, size_t dataLen) {
109 1725 : if (dataLen <= 127) {
110 0 : return false;
111 : }
112 :
113 1725 : return detectSvg(StringView((const char *)data, dataLen));
114 : }
115 :
116 800 : static bool getSvgImageSize(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
117 800 : if (detectSvg(StringView((const char *)data.data(), data.size()), width, height)) {
118 225 : return true;
119 : }
120 :
121 575 : return false;
122 : }
123 :
124 650 : static bool isTiff(const uint8_t * data, size_t dataLen) {
125 650 : if (dataLen <= 4) {
126 0 : return false;
127 : }
128 :
129 : static const char* TIFF_II = "II";
130 : static const char* TIFF_MM = "MM";
131 :
132 1075 : return (memcmp(data, TIFF_II, 2) == 0 && *(static_cast<const unsigned char*>(data) + 2) == 42 && *(static_cast<const unsigned char*>(data) + 3) == 0) ||
133 1075 : (memcmp(data, TIFF_MM, 2) == 0 && *(static_cast<const unsigned char*>(data) + 2) == 0 && *(static_cast<const unsigned char*>(data) + 3) == 42);
134 : }
135 :
136 : template <typename Reader>
137 150 : static bool getTiffImageSizeImpl(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
138 150 : auto reader = Reader(data.data() + 4, 4);
139 150 : auto offset = reader.readUnsigned32();
140 :
141 150 : data.clear();
142 150 : if (file.seekAndRead(offset, data, 2) != 2) {
143 0 : return false;
144 : }
145 150 : auto size = Reader(data.data(), 2).readUnsigned16();
146 150 : auto dictSize = size * 12;
147 150 : offset += 2;
148 150 : while (dictSize > 0) {
149 150 : data.clear();
150 150 : size_t blockSize = min(12 * 21, dictSize);
151 150 : if (file.read(data, blockSize) != blockSize) {
152 0 : return false;
153 : }
154 :
155 150 : auto blocks = blockSize / 12;
156 150 : reader = Reader(data.data(), blockSize);
157 :
158 300 : for (uint16_t i = 0; i < blocks; ++i) {
159 300 : auto tagid = reader.readUnsigned16();
160 300 : auto type = reader.readUnsigned16();
161 300 : auto count = reader.readUnsigned32();
162 300 : if (tagid == 256 && count == 1) {
163 150 : if (type == 3) {
164 150 : width = reader.readUnsigned16();
165 150 : reader.offset(2);
166 0 : } else if (type == 4) {
167 0 : width = reader.readUnsigned32();
168 : } else {
169 0 : reader.offset(4);
170 : }
171 150 : } else if (tagid == 257 && count == 1) {
172 150 : if (type == 3) {
173 150 : height = reader.readUnsigned16();
174 150 : reader.offset(2);
175 0 : } else if (type == 4) {
176 0 : height = reader.readUnsigned32();
177 : } else {
178 0 : reader.offset(4);
179 : }
180 150 : return true;
181 : } else {
182 0 : if (tagid > 257) {
183 0 : return false;
184 : }
185 0 : reader.offset(4);
186 : }
187 : }
188 : }
189 0 : return false;
190 : }
191 :
192 200 : static bool getTiffImageSize(const io::Producer &file, StackBuffer<512> &data, uint32_t &width, uint32_t &height) {
193 200 : if (isTiff(data.data(), data.size())) {
194 150 : if (memcmp(data.data(), "II", 2) == 0) {
195 75 : if (getTiffImageSizeImpl<BytesViewTemplate<Endian::Little>>(file, data, width, height)) {
196 75 : return true;
197 : }
198 : } else {
199 75 : if (getTiffImageSizeImpl<BytesViewTemplate<Endian::Big>>(file, data, width, height)) {
200 75 : return true;
201 : }
202 : }
203 : }
204 50 : return false;
205 : }
206 :
207 : }
|