Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
parser.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2023 AUTHORS
4 *
5 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
6 */
7
8#include "parser.h"
9
10#include <iostream>
11#include <sstream>
12
13#include "spaces/cms.h"
14#include "spaces/cmyk.h"
15#include "spaces/gray.h"
16#include "spaces/hsl.h"
17#include "spaces/hsluv.h"
18#include "spaces/hsv.h"
19#include "spaces/lab.h"
20#include "spaces/lch.h"
21#include "spaces/linear-rgb.h"
22#include "spaces/luv.h"
23#include "spaces/named.h"
24#include "spaces/okhsl.h"
25#include "spaces/oklab.h"
26#include "spaces/oklch.h"
27#include "spaces/rgb.h"
28#include "spaces/xyz.h"
29#include "utils.h"
30
31namespace Inkscape::Colors {
32
53
68bool Parsers::parse(std::string const &input, Space::Type &type, std::string &cms, std::vector<double> &values,
69 std::vector<double> &fallback) const
70{
71 std::istringstream ss(input);
72 return _parse(ss, type, cms, values, fallback);
73}
74
86bool Parsers::_parse(std::istringstream &ss, Space::Type &type, std::string &name, std::vector<double> &values,
87 std::vector<double> &fallback) const
88{
89 auto ptype = Parser::getCssPrefix(ss);
90 auto iter = _parsers.find(ptype);
91 if (iter == _parsers.end())
92 return false;
93
94 for (auto &parser : iter->second) {
95 auto pos = ss.tellg();
96 bool more = false;
97 values.clear();
98
99 name = parser->parseColor(ss, values, more);
100
101 // We have an RGB color but there's more string, look for an icc-color next.
102 if (more && ptype == "#") {
103 std::vector<double> icc_values;
104 if (_parse(ss, type, name, icc_values, fallback)) {
105 if (type == Space::Type::CMS) {
106 fallback = std::move(values);
107 values = std::move(icc_values);
108 return true;
109 }
110 }
111 }
112
113 if (!values.empty()) {
114 type = parser->getType();
115 return true;
116 }
117
118 ss.clear();
119 ss.seekg(pos);
120 }
121 return false;
122}
123
128{
129 // Map parsers by their expected prefix for quicker lookup
130 auto const [it, inserted] = _parsers.emplace(parser->getPrefix(), std::vector<std::shared_ptr<Parser>>{});
131 it->second.emplace_back(parser);
132}
133
143std::string Parser::parseColor(std::istringstream &ss, std::vector<double> &output, bool &more) const
144{
145 if (!parse(ss, output, more)) {
146 output.clear();
147 }
148 return "";
149}
150
151bool HueParser::parse(std::istringstream &ss, std::vector<double> &output) const
152{
153 bool end = false;
154 return append_css_value(ss, output, end, ',', 360) && append_css_value(ss, output, end, ',') &&
155 append_css_value(ss, output, end, !_alpha ? '/' : ',') && (append_css_value(ss, output, end) || !_alpha) &&
156 end;
157}
158
162bool HexParser::parse(std::istringstream &ss, std::vector<double> &output, bool &more) const
163{
164 unsigned int hex;
165 unsigned int size = 0;
166
167 size = ss.tellg();
168 ss >> std::hex >> hex;
169 // This mess is required because istream countg is inconsistant
170 size = (ss.tellg() == -1 ? ss.str().size() : (int)ss.tellg()) - size;
171
172 if (size == 3 || size == 4) { // #rgb(a)
173 for (int p = (4 * (size - 1)); p >= 0; p -= 4) {
174 auto val = ((hex & (0xf << p)) >> p);
175 output.emplace_back((val + (val << 4)) / 255.0);
176 }
177 } else if (size == 6 || size == 8) { // #rrggbb(aa)
178 if (size == 6)
179 hex <<= 8;
180 output.emplace_back(SP_RGBA32_R_F(hex));
181 output.emplace_back(SP_RGBA32_G_F(hex));
182 output.emplace_back(SP_RGBA32_B_F(hex));
183 if (size == 8)
184 output.emplace_back(SP_RGBA32_A_F(hex));
185 }
186 ss >> std::ws;
187 more = (ss.peek() == 'i'); // icc-color is next
188 return !output.empty();
189}
190
194bool CssParser::parse(std::istringstream &ss, std::vector<double> &output) const
195{
196 bool end = false;
197 while (!end && output.size() < _channels + 1) {
198 if (!append_css_value(ss, output, end, output.size() == _channels - 1 ? '/' : 0x0))
199 break;
200 }
201 return end;
202}
203
213std::string Parser::getCssPrefix(std::istringstream &ss)
214{
215 std::string token;
216 ss >> std::ws;
217 if (ss.peek() == '#') {
218 return {(char)ss.get()};
219 }
220 auto pos = ss.tellg();
221 if (!std::getline(ss, token, '(') || ss.eof()) {
222 ss.seekg(pos);
223 return ""; // No paren
224 }
225
226 if (token == "color") {
227 // CSS Color module 4 color() function
228 ss >> token;
229 }
230 return token;
231}
232
244bool Parser::css_number(std::istringstream &ss, double &value, std::string &unit, bool &end, char const sep)
245{
246 ss.imbue(std::locale("C"));
247
248 /*
249 * LLVM uses libc++ and this C library has a broken double parser compared to GCC/libstdc++
250 * So we have written our own double parser to get around the limitations for APPLE
251 *
252 * Detailed description can be found at:
253 * https://github.com/tardate/LittleCodingKata/blob/main/cpp/DoubleTrouble/README.md
254 */
255#ifdef __APPLE__
256 bool parsed = false;
257 bool dot = false;
258
259 ss >> std::ws;
260 std::string result;
261 while (true) {
262 auto c = ss.peek();
263
264 if (c == '-' && result.empty()) {
265 result += ss.get();
266 } else if (c == '.') {
267 if (dot)
268 break;
269 dot = true;
270 result += ss.get();
271 } else if (c >= '0' && c <= '9') {
272 result += ss.get();
273 parsed = true;
274 } else {
275 break;
276 }
277 }
278 if (parsed) {
279 value = std::stod(result);
280 }
281#else
282 bool parsed = (bool)(ss >> value);
283#endif
284 if (!parsed) {
285 ss.clear();
286 return false;
287 }
288
289 unit.clear();
290 auto c = ss.peek();
291 if (c == '.' || (c >= '0' && c <= '9')) {
292 return true;
293 }
294 while (ss && (c = ss.get())) {
295 if (c == ')') {
296 end = true;
297 break;
298 } else if (c == sep) {
299 break;
300 }
301 if (c == ' ') {
302 auto p = ss.peek();
303 if (p != ' ' && p != sep && p != ')') {
304 break;
305 }
306 } else {
307 unit += c;
308 }
309 }
310 return true;
311}
312
324bool Parser::append_css_value(std::istringstream &ss, std::vector<double> &output, bool &end, char const sep,
325 double scale)
326{
327 double value;
328 std::string unit;
329 if (!end && css_number(ss, value, unit, end, sep)) {
330 if (unit == "%") {
331 value /= 100;
332 } else if (unit == "deg") {
333 value /= 360;
334 } else if (unit == "turn") {
335 // no need to modify
336 } else if (!unit.empty()) {
337 std::cerr << "Unknown unit in css color parsing '" << unit.c_str() << "'\n";
338 return false;
339 } else {
340 value /= scale;
341 }
342 output.emplace_back(value);
343 return true;
344 }
345 return false;
346}
347
348}; // namespace Inkscape::Colors
349
350/*
351 Local Variables:
352 mode:c++
353 c-file-style:"stroustrup"
354 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
355 indent-tabs-mode:nil
356 fill-column:99
357 End:
358*/
359// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
constexpr Coord get() const
Definition point.h:110
bool parse(std::istringstream &ss, std::vector< double > &output) const override
Prase the given string stream as a CSS Color Module Level 4/5 string.
Definition parser.cpp:194
unsigned int _channels
Definition parser.h:89
bool parse(std::istringstream &input, std::vector< double > &output, bool &more) const override
Parse either a hex code or an rgb() css string.
Definition parser.cpp:162
bool parse(std::istringstream &ss, std::vector< double > &output) const override
Definition parser.cpp:151
static bool css_number(std::istringstream &ss, double &value, std::string &unit, bool &end, char const sep=0x0)
Parse a CSS color number after the function name.
Definition parser.cpp:244
static bool append_css_value(std::istringstream &ss, std::vector< double > &output, bool &end, char const sep=0x0, double scale=1.0)
Parse a CSS color number and format it according to it's unit.
Definition parser.cpp:324
static std::string getCssPrefix(std::istringstream &ss)
Parse CSS color numbers after the function name.
Definition parser.cpp:213
std::string const getPrefix() const
Definition parser.h:29
virtual std::string parseColor(std::istringstream &ss, std::vector< double > &output, bool &more) const
Parse this specific color format into output values.
Definition parser.cpp:143
virtual bool parse(std::istringstream &ss, std::vector< double > &output) const
Definition parser.h:36
bool parse(std::string const &input, Space::Type &type, std::string &cms, std::vector< double > &values, std::vector< double > &fallback) const
Turn a string into a color data, used in Color object creation.
Definition parser.cpp:68
std::map< std::string, std::vector< std::shared_ptr< Parser > > > _parsers
Definition parser.h:120
void addParser(Parser *parser)
Add a prser to the list of parser objects used when parsing color strings.
Definition parser.cpp:127
bool _parse(std::istringstream &ss, Space::Type &type, std::string &cms, std::vector< double > &values, std::vector< double > &fallback) const
Internal recursive parser that scans through a string stream.
Definition parser.cpp:86
constexpr double SP_RGBA32_G_F(uint32_t v)
Definition utils.h:47
constexpr double SP_RGBA32_R_F(uint32_t v)
Definition utils.h:43
constexpr double SP_RGBA32_A_F(uint32_t v)
Definition utils.h:55
constexpr double SP_RGBA32_B_F(uint32_t v)
Definition utils.h:51
Css & result
double c[8][4]
Geom::Point end
A set of useful color modifying functions which do not fit as generic methods on the color class itse...
Definition profile.cpp:24
void dot(Cairo::RefPtr< Cairo::Context > &cr, double x, double y)
Glib::ustring name
Definition toolbars.cpp:55