1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include <cmath>
9 
10 #include "cxfcolor.h"
11 #include "cxfcolorspecification.h"
12 #include "cxfdocument.h"
13 #include "cxfmeasurementspec.h"
14 
15 #include "colormgmt/sccolormgmtstructs.h"
16 
CxfColor(CxfDocument * cxfDoc)17 CxfColor::CxfColor(CxfDocument* cxfDoc)
18 {
19 	m_cxfDoc = cxfDoc;
20 	m_colorSpec = nullptr;
21 }
22 
isValid() const23 bool CxfColor::isValid() const
24 {
25 	bool valid = true;
26 	valid &= (m_cxfDoc != nullptr);
27 	valid &= (m_colorSpec != nullptr);
28 	return valid;
29 }
30 
reset()31 void CxfColor::reset()
32 {
33 	m_colorSpec = nullptr;
34 }
35 
CxfColorRGB(CxfDocument * cxfDoc)36 CxfColorRGB::CxfColorRGB(CxfDocument* cxfDoc)
37            : CxfColor(cxfDoc)
38 {
39 	m_maxRange = 255.0;
40 	m_values[0] = 0.0;
41 	m_values[1] = 0.0;
42 	m_values[2] = 0.0;
43 }
44 
parse(QDomElement & colorElem)45 bool CxfColorRGB::parse(QDomElement& colorElem)
46 {
47 	bool convOk = false;
48 	bool gotRed(false);
49 	bool gotGreen(false);
50 	bool gotBlue(false);
51 
52 	reset();
53 
54 	QString colorSpec = colorElem.attribute("ColorSpecification");
55 	if (colorSpec.isEmpty())
56 		return false;
57 
58 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
59 	if (!m_colorSpec)
60 		return false;
61 
62 	QDomElement maxRangeElem = colorElem.firstChildElement("MaxRange");
63 	if (!maxRangeElem.isNull())
64 	{
65 		QString str = maxRangeElem.text();
66 		double maxRange = str.toDouble(&convOk);
67 		if (!convOk)
68 			return false;
69 		m_maxRange = maxRange;
70 	}
71 
72 	QDomNodeList childNodes = colorElem.childNodes();
73 	for (int i = 0; i < childNodes.count(); ++i)
74 	{
75 		QDomNode childNode = childNodes.at(i);
76 		if (!childNode.isElement())
77 			continue;
78 		QDomElement childElem = childNode.toElement();
79 
80 		QString tagName = childElem.tagName();
81 		if (tagName == "R")
82 		{
83 			QString str = childElem.text();
84 			double red = str.toDouble(&gotRed);
85 			if (!gotRed)
86 				return false;
87 			m_values[0] = qMax(0.0, qMin(red, m_maxRange));
88 			continue;
89 		}
90 		if (tagName == "G")
91 		{
92 			QString str = childElem.text();
93 			double green = str.toDouble(&gotGreen);
94 			if (!gotGreen)
95 				return false;
96 			m_values[1] = qMax(0.0, qMin(green, m_maxRange));
97 			continue;
98 		}
99 		if (tagName == "B")
100 		{
101 			QString str = childElem.text();
102 			double blue = str.toDouble(&gotBlue);
103 			if (!gotBlue)
104 				return false;
105 			m_values[2] = qMax(0.0, qMin(blue, m_maxRange));
106 			continue;
107 		}
108 	}
109 
110 	return (gotRed && gotGreen && gotBlue);
111 }
112 
reset()113 void CxfColorRGB::reset()
114 {
115 	CxfColor::reset();
116 
117 	m_maxRange = 255.0;
118 	m_values[0] = 0.0;
119 	m_values[1] = 0.0;
120 	m_values[2] = 0.0;
121 }
122 
CxfColorSRGB(CxfDocument * cxfDoc)123 CxfColorSRGB::CxfColorSRGB(CxfDocument* cxfDoc)
124 	        : CxfColorRGB(cxfDoc)
125 {
126 
127 }
128 
CxfColorAdobeRGB(CxfDocument * cxfDoc)129 CxfColorAdobeRGB::CxfColorAdobeRGB(CxfDocument* cxfDoc)
130 	            : CxfColorRGB(cxfDoc)
131 {
132 
133 }
134 
CxfColorHTML(CxfDocument * cxfDoc)135 CxfColorHTML::CxfColorHTML(CxfDocument* cxfDoc)
136 	        : CxfColorRGB(cxfDoc)
137 {
138 
139 }
140 
parse(QDomElement & colorElem)141 bool CxfColorHTML::parse(QDomElement& colorElem)
142 {
143 	bool convOk = false;
144 
145 	reset();
146 
147 	QString colorSpec = colorElem.attribute("ColorSpecification");
148 	if (colorSpec.isEmpty())
149 		return false;
150 
151 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
152 	if (!m_colorSpec)
153 		return false;
154 
155 	QString html = colorElem.attribute("HTML");
156 	if (html.length() < 6)
157 		return false;
158 
159 	QString rStr = html.mid(0, 2);
160 	int red = rStr.toInt(&convOk, 16);
161 	if (!convOk)
162 		return false;
163 	m_values[0] = red;
164 
165 	QString gStr = html.mid(2, 2);
166 	int green = gStr.toInt(&convOk, 16);
167 	if (!convOk)
168 		return false;
169 	m_values[1] = green;
170 
171 	QString bStr = html.mid(4, 2);
172 	int blue = bStr.toInt(&convOk, 16);
173 	if (!convOk)
174 		return false;
175 	m_values[2] = blue;
176 
177 	return true;
178 }
179 
CxfColorCMYK(CxfDocument * cxfDoc)180 CxfColorCMYK::CxfColorCMYK(CxfDocument* cxfDoc)
181                 : CxfColor(cxfDoc)
182 {
183 	m_values[0] = 0.0;
184 	m_values[1] = 0.0;
185 	m_values[2] = 0.0;
186 	m_values[3] = 0.0;
187 }
188 
parse(QDomElement & colorElem)189 bool CxfColorCMYK::parse(QDomElement& colorElem)
190 {
191 	bool gotCyan(false);
192 	bool gotMagenta(false);
193 	bool gotYellow(false);
194 	bool gotBlack(false);
195 
196 	reset();
197 
198 	QString colorSpec = colorElem.attribute("ColorSpecification");
199 	if (colorSpec.isEmpty())
200 		return false;
201 
202 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
203 	if (!m_colorSpec)
204 		return false;
205 
206 	QDomNodeList childNodes = colorElem.childNodes();
207 	for (int i = 0; i < childNodes.count(); ++i)
208 	{
209 		QDomNode childNode = childNodes.at(i);
210 		if (!childNode.isElement())
211 			continue;
212 		QDomElement childElem = childNode.toElement();
213 
214 		QString tagName = childElem.tagName();
215 		if (tagName == "Cyan")
216 		{
217 			QString str = childElem.text();
218 			double cyan = str.toDouble(&gotCyan);
219 			if (!gotCyan)
220 				return false;
221 			m_values[0] = qMax(0.0, qMin(cyan, 100.0));
222 			continue;
223 		}
224 		if (tagName == "Magenta")
225 		{
226 			QString str = childElem.text();
227 			double magenta = str.toDouble(&gotMagenta);
228 			if (!gotMagenta)
229 				return false;
230 			m_values[1] = qMax(0.0, qMin(magenta, 100.0));
231 			continue;
232 		}
233 		if (tagName == "Yellow")
234 		{
235 			QString str = childElem.text();
236 			double yellow = str.toDouble(&gotYellow);
237 			if (!gotYellow)
238 				return false;
239 			m_values[2] = qMax(0.0, qMin(yellow, 100.0));
240 			continue;
241 		}
242 		if (tagName == "Black")
243 		{
244 			QString str = childElem.text();
245 			double black = str.toDouble(&gotBlack);
246 			if (!gotBlack)
247 				return false;
248 			m_values[3] = qMax(0.0, qMin(black, 100.0));
249 			continue;
250 		}
251 	}
252 
253 	return (gotCyan && gotMagenta && gotYellow && gotBlack);
254 }
255 
reset()256 void CxfColorCMYK::reset()
257 {
258 	CxfColor::reset();
259 
260 	m_values[0] = 0.0;
261 	m_values[1] = 0.0;
262 	m_values[2] = 0.0;
263 	m_values[3] = 0.0;
264 }
265 
CxfColorCIELab(CxfDocument * cxfDoc)266 CxfColorCIELab::CxfColorCIELab(CxfDocument* cxfDoc)
267 	          : CxfColor(cxfDoc)
268 {
269 	m_values[0] = 0.0;
270 	m_values[1] = 0.0;
271 	m_values[2] = 0.0;
272 }
273 
parse(QDomElement & colorElem)274 bool CxfColorCIELab::parse(QDomElement& colorElem)
275 {
276 	bool gotLab_L(false);
277 	bool gotLab_a(false);
278 	bool gotLab_b(false);
279 
280 	reset();
281 
282 	QString colorSpec = colorElem.attribute("ColorSpecification");
283 	if (colorSpec.isEmpty())
284 		return false;
285 
286 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
287 	if (!m_colorSpec)
288 		return false;
289 
290 	QDomNodeList childNodes = colorElem.childNodes();
291 	for (int i = 0; i < childNodes.count(); ++i)
292 	{
293 		QDomNode childNode = childNodes.at(i);
294 		if (!childNode.isElement())
295 			continue;
296 		QDomElement childElem = childNode.toElement();
297 
298 		QString tagName = childElem.tagName();
299 		if (tagName == "L")
300 		{
301 			QString str = childElem.text();
302 			double lab_L = str.toDouble(&gotLab_L);
303 			if (!gotLab_L)
304 				return false;
305 			m_values[0] = qMax(0.0, qMin(lab_L, 100.0));
306 			continue;
307 		}
308 		if (tagName == "A")
309 		{
310 			QString str = childElem.text();
311 			double lab_a = str.toDouble(&gotLab_a);
312 			if (!gotLab_a)
313 				return false;
314 			m_values[1] = lab_a;
315 			continue;
316 		}
317 		if (tagName == "B")
318 		{
319 			QString str = childElem.text();
320 			double lab_b = str.toDouble(&gotLab_b);
321 			if (!gotLab_b)
322 				return false;
323 			m_values[2] = lab_b;
324 			continue;
325 		}
326 	}
327 
328 	return (gotLab_L && gotLab_a && gotLab_b);
329 }
330 
reset()331 void CxfColorCIELab::reset()
332 {
333 	CxfColor::reset();
334 
335 	m_values[0] = 0.0;
336 	m_values[1] = 0.0;
337 	m_values[2] = 0.0;
338 }
339 
CxfColorCIELCh(CxfDocument * cxfDoc)340 CxfColorCIELCh::CxfColorCIELCh(CxfDocument* cxfDoc)
341 	          : CxfColor(cxfDoc)
342 {
343 	m_values[0] = 0.0;
344 	m_values[1] = 0.0;
345 	m_values[2] = 0.0;
346 }
347 
lab() const348 ScLab CxfColorCIELCh::lab() const
349 {
350 	ScLab lab;
351 	lab.L = m_values[0];
352 	lab.a = m_values[1] * cos(m_values[2] * M_PI / 180.0);
353 	lab.b = m_values[1] * cos(m_values[2] * M_PI / 180.0);
354 	return lab;
355 }
356 
parse(QDomElement & colorElem)357 bool CxfColorCIELCh::parse(QDomElement& colorElem)
358 {
359 	bool gotLCh_L(false);
360 	bool gotLCh_C(false);
361 	bool gotLCh_h(false);
362 
363 	reset();
364 
365 	QString colorSpec = colorElem.attribute("ColorSpecification");
366 	if (colorSpec.isEmpty())
367 		return false;
368 
369 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
370 	if (!m_colorSpec)
371 		return false;
372 
373 	QDomNodeList childNodes = colorElem.childNodes();
374 	for (int i = 0; i < childNodes.count(); ++i)
375 	{
376 		QDomNode childNode = childNodes.at(i);
377 		if (!childNode.isElement())
378 			continue;
379 		QDomElement childElem = childNode.toElement();
380 
381 		QString tagName = childElem.tagName();
382 		if (tagName == "L")
383 		{
384 			QString str = childElem.text();
385 			double lch_L = str.toDouble(&gotLCh_L);
386 			if (!gotLCh_L)
387 				return false;
388 			m_values[0] = qMax(0.0, qMin(lch_L, 100.0));
389 			continue;
390 		}
391 		if (tagName == "C")
392 		{
393 			QString str = childElem.text();
394 			double lch_C = str.toDouble(&gotLCh_C);
395 			if (!gotLCh_C)
396 				return false;
397 			m_values[1] = qMax(0.0, lch_C);
398 			continue;
399 		}
400 		if (tagName == "H")
401 		{
402 			QString str = childElem.text();
403 			double lch_h = str.toDouble(&gotLCh_h);
404 			if (!gotLCh_h)
405 				return false;
406 			while (lch_h < 0)
407 				lch_h += 360.0;
408 			while (lch_h > 360)
409 				lch_h -= 360.0;
410 			m_values[2] = lch_h;
411 			continue;
412 		}
413 	}
414 
415 	return (gotLCh_L && gotLCh_C && gotLCh_h);
416 }
417 
reset()418 void CxfColorCIELCh::reset()
419 {
420 	CxfColor::reset();
421 
422 	m_values[0] = 0.0;
423 	m_values[1] = 0.0;
424 	m_values[2] = 0.0;
425 }
426 
CxfColorCIEXYZ(CxfDocument * cxfDoc)427 CxfColorCIEXYZ::CxfColorCIEXYZ(CxfDocument* cxfDoc)
428               : CxfColor(cxfDoc)
429 {
430 	m_values[0] = 0.0;
431 	m_values[1] = 0.0;
432 	m_values[2] = 0.0;
433 }
434 
parse(QDomElement & colorElem)435 bool CxfColorCIEXYZ::parse(QDomElement& colorElem)
436 {
437 	bool gotXYZ_X(false);
438 	bool gotXYZ_Y(false);
439 	bool gotXYZ_Z(false);
440 
441 	reset();
442 
443 	QString colorSpec = colorElem.attribute("ColorSpecification");
444 	if (colorSpec.isEmpty())
445 		return false;
446 
447 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
448 	if (!m_colorSpec)
449 		return false;
450 
451 	QDomNodeList childNodes = colorElem.childNodes();
452 	for (int i = 0; i < childNodes.count(); ++i)
453 	{
454 		QDomNode childNode = childNodes.at(i);
455 		if (!childNode.isElement())
456 			continue;
457 		QDomElement childElem = childNode.toElement();
458 
459 		QString tagName = childElem.tagName();
460 		if (tagName == "X")
461 		{
462 			QString str = childElem.text();
463 			double xyz_x = str.toDouble(&gotXYZ_X);
464 			if (!gotXYZ_X)
465 				return false;
466 			m_values[0]  = qMax(0.0, qMin(xyz_x, 100.0));
467 			m_values[0] /= 100.0;
468 			continue;
469 		}
470 		if (tagName == "Y")
471 		{
472 			QString str = childElem.text();
473 			double xyz_y = str.toDouble(&gotXYZ_Y);
474 			if (!gotXYZ_Y)
475 				return false;
476 			m_values[1] = qMax(0.0, qMin(xyz_y, 100.0));
477 			m_values[1] /= 100.0;
478 			continue;
479 		}
480 		if (tagName == "Z")
481 		{
482 			QString str = childElem.text();
483 			double xyz_z = str.toDouble(&gotXYZ_Z);
484 			if (!gotXYZ_Z)
485 				return false;
486 			m_values[2] = qMax(0.0, qMin(xyz_z, 100.0));
487 			m_values[2] /= 100.0;
488 			continue;
489 		}
490 	}
491 
492 	return (gotXYZ_X && gotXYZ_Y && gotXYZ_Z);
493 }
494 
reset()495 void CxfColorCIEXYZ::reset()
496 {
497 	CxfColor::reset();
498 
499 	m_values[0] = 0.0;
500 	m_values[1] = 0.0;
501 	m_values[2] = 0.0;
502 }
503 
CxfReflectanceSpectrum(CxfDocument * cxfDoc)504 CxfReflectanceSpectrum::CxfReflectanceSpectrum(CxfDocument* cxfDoc)
505                       : CxfColor(cxfDoc)
506 {
507 	m_wavelengthStart = 0;
508 }
509 
parse(QDomElement & colorElem)510 bool CxfReflectanceSpectrum::parse(QDomElement& colorElem)
511 {
512 	bool convOk = false;
513 
514 	reset();
515 
516 	QString colorSpec = colorElem.attribute("ColorSpecification");
517 	if (colorSpec.isEmpty())
518 		return false;
519 
520 	m_colorSpec = m_cxfDoc->colorSpecification(colorSpec);
521 	if (!m_colorSpec)
522 		return false;
523 
524 	if (!m_colorSpec->hasWavelengthRange())
525 		return false;
526 
527 	QString str = colorElem.attribute("StartWL");
528 	if (str.length() > 0)
529 	{
530 		int wlStart = str.toInt(&convOk);
531 		if (!convOk || (wlStart < 360) || (wlStart > 400))
532 			return false;
533 		m_wavelengthStart = wlStart;
534 	}
535 
536 	QString spectrum = colorElem.text().trimmed();
537 	if (spectrum.isEmpty())
538 		return false;
539 
540 	QStringList values = spectrum.split(QChar(' '), Qt::SkipEmptyParts);
541 	if (values.count() <= 0)
542 		return false;
543 
544 	m_values.reserve(values.count());
545 	for (int i = 0; i < values.count(); ++i)
546 	{
547 		str = values.at(i);
548 		double d = str.toDouble(&convOk);
549 		if (!convOk)
550 			return false;
551 		d = qMax(0.0, qMin(d, 1.0));
552 		m_values.append(d);
553 	}
554 
555 	return true;
556 }
557 
reset()558 void CxfReflectanceSpectrum::reset()
559 {
560 	m_values.clear();
561 	m_wavelengthStart = 0;
562 }
563 
wavelengthStart() const564 int CxfReflectanceSpectrum::wavelengthStart() const
565 {
566 	if (m_wavelengthStart > 0)
567 		return m_wavelengthStart;
568 	return m_colorSpec->measurementSpec().wavelengthStart();
569 }
570 
wavelengthIncrement() const571 int CxfReflectanceSpectrum::wavelengthIncrement() const
572 {
573 	return m_colorSpec->measurementSpec().wavelengthIncrement();
574 }
575 
wavelengths() const576 QVector<int> CxfReflectanceSpectrum::wavelengths() const
577 {
578 	if (m_values.count() <= 0)
579 		return QVector<int>();
580 	int wlStart = wavelengthStart();
581 	int wlInc   = m_colorSpec->measurementSpec().wavelengthIncrement();
582 	return CxfMeasurementSpec::wavelengths(wlStart, wlInc, m_values.count());
583 }
584