LMMS
Loading...
Searching...
No Matches
juce_SVGParser.cpp
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30{
31public:
32 //==============================================================================
33 explicit SVGState (const XmlElement* topLevel, const File& svgFile = {})
34 : originalFile (svgFile), topLevelXml (topLevel, nullptr)
35 {
36 }
37
38 struct XmlPath
39 {
40 XmlPath (const XmlElement* e, const XmlPath* p) noexcept : xml (e), parent (p) {}
41
42 const XmlElement& operator*() const noexcept { jassert (xml != nullptr); return *xml; }
43 const XmlElement* operator->() const noexcept { return xml; }
44 XmlPath getChild (const XmlElement* e) const noexcept { return XmlPath (e, this); }
45
46 template <typename OperationType>
47 bool applyOperationToChildWithID (const String& id, OperationType& op) const
48 {
49 for (auto* e : xml->getChildIterator())
50 {
51 XmlPath child (e, this);
52
53 if (e->compareAttribute ("id", id)
54 && ! child->hasTagName ("defs"))
55 return op (child);
56
57 if (child.applyOperationToChildWithID (id, op))
58 return true;
59 }
60
61 return false;
62 }
63
66 };
67
68 //==============================================================================
69 struct UsePathOp
70 {
73
74 bool operator() (const XmlPath& xmlPath) const
75 {
76 return state->parsePathElement (xmlPath, *targetPath);
77 }
78 };
79
80 struct UseTextOp
81 {
85
86 bool operator() (const XmlPath& xmlPath)
87 {
88 target = state->parseText (xmlPath, true, transform);
89 return target != nullptr;
90 }
91 };
92
94 {
98
99 bool operator() (const XmlPath& xmlPath)
100 {
101 target = state->parseImage (xmlPath, true, transform);
102 return target != nullptr;
103 }
104 };
105
107 {
110
111 bool operator() (const XmlPath& xmlPath)
112 {
113 return state->applyClipPath (*target, xmlPath);
114 }
115 };
116
118 {
121
122 bool operator() (const XmlPath& xml) const
123 {
124 return state->addGradientStopsIn (*gradient, xml);
125 }
126 };
127
129 {
131 const Path* path;
132 float opacity;
134
135 bool operator() (const XmlPath& xml)
136 {
137 if (xml->hasTagNameIgnoringNamespace ("linearGradient")
138 || xml->hasTagNameIgnoringNamespace ("radialGradient"))
139 {
140 fillType = state->getGradientFillType (xml, *path, opacity);
141 return true;
142 }
143
144 return false;
145 }
146 };
147
148 //==============================================================================
150 {
151 auto drawable = new DrawableComposite();
152 setCommonAttributes (*drawable, xml);
153
154 SVGState newState (*this);
155
156 if (xml->hasAttribute ("transform"))
157 newState.addTransform (xml);
158
159 newState.width = getCoordLength (xml->getStringAttribute ("width", String (newState.width)), viewBoxW);
160 newState.height = getCoordLength (xml->getStringAttribute ("height", String (newState.height)), viewBoxH);
161
162 if (newState.width <= 0) newState.width = 100;
163 if (newState.height <= 0) newState.height = 100;
164
165 Point<float> viewboxXY;
166
167 if (xml->hasAttribute ("viewBox"))
168 {
169 auto viewBoxAtt = xml->getStringAttribute ("viewBox");
170 auto viewParams = viewBoxAtt.getCharPointer();
171 Point<float> vwh;
172
173 if (parseCoords (viewParams, viewboxXY, true)
174 && parseCoords (viewParams, vwh, true)
175 && vwh.x > 0
176 && vwh.y > 0)
177 {
178 newState.viewBoxW = vwh.x;
179 newState.viewBoxH = vwh.y;
180
181 auto placementFlags = parsePlacementFlags (xml->getStringAttribute ("preserveAspectRatio").trim());
182
183 if (placementFlags != 0)
184 newState.transform = RectanglePlacement (placementFlags)
185 .getTransformToFit (Rectangle<float> (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y),
186 Rectangle<float> (newState.width, newState.height))
187 .followedBy (newState.transform);
188 }
189 }
190 else
191 {
192 if (viewBoxW == 0.0f) newState.viewBoxW = newState.width;
193 if (viewBoxH == 0.0f) newState.viewBoxH = newState.height;
194 }
195
196 newState.parseSubElements (xml, *drawable);
197
198 drawable->setContentArea ({ viewboxXY.x, viewboxXY.y, newState.viewBoxW, newState.viewBoxH });
199 drawable->resetBoundingBoxToContentArea();
200
201 return drawable;
202 }
203
204 //==============================================================================
205 void parsePathString (Path& path, const String& pathString) const
206 {
207 auto d = pathString.getCharPointer().findEndOfWhitespace();
208
209 Point<float> subpathStart, last, last2, p1, p2, p3;
210 juce_wchar currentCommand = 0, previousCommand = 0;
211 bool isRelative = true;
212 bool carryOn = true;
213
214 while (! d.isEmpty())
215 {
216 if (CharPointer_ASCII ("MmLlHhVvCcSsQqTtAaZz").indexOf (*d) >= 0)
217 {
218 currentCommand = d.getAndAdvance();
219 isRelative = currentCommand >= 'a';
220 }
221
222 switch (currentCommand)
223 {
224 case 'M':
225 case 'm':
226 case 'L':
227 case 'l':
228 if (parseCoordsOrSkip (d, p1, false))
229 {
230 if (isRelative)
231 p1 += last;
232
233 if (currentCommand == 'M' || currentCommand == 'm')
234 {
235 subpathStart = p1;
236 path.startNewSubPath (p1);
237 currentCommand = 'l';
238 }
239 else
240 path.lineTo (p1);
241
242 last2 = last = p1;
243 }
244 break;
245
246 case 'H':
247 case 'h':
248 if (parseCoord (d, p1.x, false, true))
249 {
250 if (isRelative)
251 p1.x += last.x;
252
253 path.lineTo (p1.x, last.y);
254
255 last2.x = last.x;
256 last.x = p1.x;
257 }
258 else
259 {
260 ++d;
261 }
262 break;
263
264 case 'V':
265 case 'v':
266 if (parseCoord (d, p1.y, false, false))
267 {
268 if (isRelative)
269 p1.y += last.y;
270
271 path.lineTo (last.x, p1.y);
272
273 last2.y = last.y;
274 last.y = p1.y;
275 }
276 else
277 {
278 ++d;
279 }
280 break;
281
282 case 'C':
283 case 'c':
284 if (parseCoordsOrSkip (d, p1, false)
285 && parseCoordsOrSkip (d, p2, false)
286 && parseCoordsOrSkip (d, p3, false))
287 {
288 if (isRelative)
289 {
290 p1 += last;
291 p2 += last;
292 p3 += last;
293 }
294
295 path.cubicTo (p1, p2, p3);
296
297 last2 = p2;
298 last = p3;
299 }
300 break;
301
302 case 'S':
303 case 's':
304 if (parseCoordsOrSkip (d, p1, false)
305 && parseCoordsOrSkip (d, p3, false))
306 {
307 if (isRelative)
308 {
309 p1 += last;
310 p3 += last;
311 }
312
313 p2 = last;
314
315 if (CharPointer_ASCII ("CcSs").indexOf (previousCommand) >= 0)
316 p2 += (last - last2);
317
318 path.cubicTo (p2, p1, p3);
319
320 last2 = p1;
321 last = p3;
322 }
323 break;
324
325 case 'Q':
326 case 'q':
327 if (parseCoordsOrSkip (d, p1, false)
328 && parseCoordsOrSkip (d, p2, false))
329 {
330 if (isRelative)
331 {
332 p1 += last;
333 p2 += last;
334 }
335
336 path.quadraticTo (p1, p2);
337
338 last2 = p1;
339 last = p2;
340 }
341 break;
342
343 case 'T':
344 case 't':
345 if (parseCoordsOrSkip (d, p1, false))
346 {
347 if (isRelative)
348 p1 += last;
349
350 p2 = last;
351
352 if (CharPointer_ASCII ("QqTt").indexOf (previousCommand) >= 0)
353 p2 += (last - last2);
354
355 path.quadraticTo (p2, p1);
356
357 last2 = p2;
358 last = p1;
359 }
360 break;
361
362 case 'A':
363 case 'a':
364 if (parseCoordsOrSkip (d, p1, false))
365 {
366 String num;
367 bool flagValue = false;
368
369 if (parseNextNumber (d, num, false))
370 {
371 auto angle = degreesToRadians (parseSafeFloat (num));
372
373 if (parseNextFlag (d, flagValue))
374 {
375 auto largeArc = flagValue;
376
377 if (parseNextFlag (d, flagValue))
378 {
379 auto sweep = flagValue;
380
381 if (parseCoordsOrSkip (d, p2, false))
382 {
383 if (isRelative)
384 p2 += last;
385
386 if (last != p2)
387 {
388 double centreX, centreY, startAngle, deltaAngle;
389 double rx = p1.x, ry = p1.y;
390
391 endpointToCentreParameters (last.x, last.y, p2.x, p2.y,
392 angle, largeArc, sweep,
393 rx, ry, centreX, centreY,
394 startAngle, deltaAngle);
395
396 path.addCentredArc ((float) centreX, (float) centreY,
397 (float) rx, (float) ry,
398 angle, (float) startAngle, (float) (startAngle + deltaAngle),
399 false);
400
401 path.lineTo (p2);
402 }
403
404 last2 = last;
405 last = p2;
406 }
407 }
408 }
409 }
410 }
411
412 break;
413
414 case 'Z':
415 case 'z':
416 path.closeSubPath();
417 last = last2 = subpathStart;
418 d.incrementToEndOfWhitespace();
419 currentCommand = 'M';
420 break;
421
422 default:
423 carryOn = false;
424 break;
425 }
426
427 if (! carryOn)
428 break;
429
430 previousCommand = currentCommand;
431 }
432
433 // paths that finish back at their start position often seem to be
434 // left without a 'z', so need to be closed explicitly..
435 if (path.getCurrentPosition() == subpathStart)
436 path.closeSubPath();
437 }
438
439private:
440 //==============================================================================
443 float width = 512, height = 512, viewBoxW = 0, viewBoxH = 0;
446
447 static bool isNone (const String& s) noexcept
448 {
449 return s.equalsIgnoreCase ("none");
450 }
451
452 static void setCommonAttributes (Drawable& d, const XmlPath& xml)
453 {
454 auto compID = xml->getStringAttribute ("id");
455 d.setName (compID);
456 d.setComponentID (compID);
457
458 if (isNone (xml->getStringAttribute ("display")))
459 d.setVisible (false);
460 }
461
462 //==============================================================================
463 void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable, bool shouldParseClip = true)
464 {
465 for (auto* e : xml->getChildIterator())
466 {
467 const XmlPath child (xml.getChild (e));
468
469 if (auto* drawable = parseSubElement (child))
470 {
471 parentDrawable.addChildComponent (drawable);
472
473 if (! isNone (getStyleAttribute (child, "display")))
474 drawable->setVisible (true);
475
476 if (shouldParseClip)
477 parseClipPath (child, *drawable);
478 }
479 }
480 }
481
483 {
484 {
485 Path path;
486 if (parsePathElement (xml, path))
487 return parseShape (xml, path);
488 }
489
490 auto tag = xml->getTagNameWithoutNamespace();
491
492 if (tag == "g") return parseGroupElement (xml, true);
493 if (tag == "svg") return parseSVGElement (xml);
494 if (tag == "text") return parseText (xml, true);
495 if (tag == "image") return parseImage (xml, true);
496 if (tag == "switch") return parseSwitch (xml);
497 if (tag == "a") return parseLinkElement (xml);
498 if (tag == "use") return parseUseOther (xml);
499 if (tag == "style") parseCSSStyle (xml);
500 if (tag == "defs") parseDefs (xml);
501
502 return nullptr;
503 }
504
505 bool parsePathElement (const XmlPath& xml, Path& path) const
506 {
507 auto tag = xml->getTagNameWithoutNamespace();
508
509 if (tag == "path") { parsePath (xml, path); return true; }
510 if (tag == "rect") { parseRect (xml, path); return true; }
511 if (tag == "circle") { parseCircle (xml, path); return true; }
512 if (tag == "ellipse") { parseEllipse (xml, path); return true; }
513 if (tag == "line") { parseLine (xml, path); return true; }
514 if (tag == "polyline") { parsePolygon (xml, true, path); return true; }
515 if (tag == "polygon") { parsePolygon (xml, false, path); return true; }
516 if (tag == "use") { return parseUsePath (xml, path); }
517
518 return false;
519 }
520
522 {
523 if (auto* group = xml->getChildByName ("g"))
524 return parseGroupElement (xml.getChild (group), true);
525
526 return nullptr;
527 }
528
529 DrawableComposite* parseGroupElement (const XmlPath& xml, bool shouldParseTransform)
530 {
531 if (shouldParseTransform && xml->hasAttribute ("transform"))
532 {
533 SVGState newState (*this);
534 newState.addTransform (xml);
535
536 return newState.parseGroupElement (xml, false);
537 }
538
539 auto* drawable = new DrawableComposite();
540 setCommonAttributes (*drawable, xml);
541 parseSubElements (xml, *drawable);
542
543 drawable->resetContentAreaAndBoundingBoxToFitChildren();
544 return drawable;
545 }
546
548 {
549 return parseGroupElement (xml, true); // TODO: support for making this clickable
550 }
551
552 //==============================================================================
553 void parsePath (const XmlPath& xml, Path& path) const
554 {
555 parsePathString (path, xml->getStringAttribute ("d"));
556
557 if (getStyleAttribute (xml, "fill-rule").trim().equalsIgnoreCase ("evenodd"))
558 path.setUsingNonZeroWinding (false);
559 }
560
561 void parseRect (const XmlPath& xml, Path& rect) const
562 {
563 const bool hasRX = xml->hasAttribute ("rx");
564 const bool hasRY = xml->hasAttribute ("ry");
565
566 if (hasRX || hasRY)
567 {
568 float rx = getCoordLength (xml, "rx", viewBoxW);
569 float ry = getCoordLength (xml, "ry", viewBoxH);
570
571 if (! hasRX)
572 rx = ry;
573 else if (! hasRY)
574 ry = rx;
575
577 getCoordLength (xml, "y", viewBoxH),
578 getCoordLength (xml, "width", viewBoxW),
579 getCoordLength (xml, "height", viewBoxH),
580 rx, ry);
581 }
582 else
583 {
584 rect.addRectangle (getCoordLength (xml, "x", viewBoxW),
585 getCoordLength (xml, "y", viewBoxH),
586 getCoordLength (xml, "width", viewBoxW),
587 getCoordLength (xml, "height", viewBoxH));
588 }
589 }
590
591 void parseCircle (const XmlPath& xml, Path& circle) const
592 {
593 auto cx = getCoordLength (xml, "cx", viewBoxW);
594 auto cy = getCoordLength (xml, "cy", viewBoxH);
595 auto radius = getCoordLength (xml, "r", viewBoxW);
596
597 circle.addEllipse (cx - radius, cy - radius, radius * 2.0f, radius * 2.0f);
598 }
599
600 void parseEllipse (const XmlPath& xml, Path& ellipse) const
601 {
602 auto cx = getCoordLength (xml, "cx", viewBoxW);
603 auto cy = getCoordLength (xml, "cy", viewBoxH);
604 auto radiusX = getCoordLength (xml, "rx", viewBoxW);
605 auto radiusY = getCoordLength (xml, "ry", viewBoxH);
606
607 ellipse.addEllipse (cx - radiusX, cy - radiusY, radiusX * 2.0f, radiusY * 2.0f);
608 }
609
610 void parseLine (const XmlPath& xml, Path& line) const
611 {
612 auto x1 = getCoordLength (xml, "x1", viewBoxW);
613 auto y1 = getCoordLength (xml, "y1", viewBoxH);
614 auto x2 = getCoordLength (xml, "x2", viewBoxW);
615 auto y2 = getCoordLength (xml, "y2", viewBoxH);
616
617 line.startNewSubPath (x1, y1);
618 line.lineTo (x2, y2);
619 }
620
621 void parsePolygon (const XmlPath& xml, bool isPolyline, Path& path) const
622 {
623 auto pointsAtt = xml->getStringAttribute ("points");
624 auto points = pointsAtt.getCharPointer();
626
627 if (parseCoords (points, p, true))
628 {
629 Point<float> first (p), last;
630
631 path.startNewSubPath (first);
632
633 while (parseCoords (points, p, true))
634 {
635 last = p;
636 path.lineTo (p);
637 }
638
639 if ((! isPolyline) || first == last)
640 path.closeSubPath();
641 }
642 }
643
644 static String getLinkedID (const XmlPath& xml)
645 {
646 auto link = xml->getStringAttribute ("xlink:href");
647
648 if (link.startsWithChar ('#'))
649 return link.substring (1);
650
651 return {};
652 }
653
654 bool parseUsePath (const XmlPath& xml, Path& path) const
655 {
656 auto linkedID = getLinkedID (xml);
657
658 if (linkedID.isNotEmpty())
659 {
660 UsePathOp op = { this, &path };
661 return topLevelXml.applyOperationToChildWithID (linkedID, op);
662 }
663
664 return false;
665 }
666
667 Drawable* parseUseOther (const XmlPath& xml) const
668 {
669 if (auto* drawableText = parseText (xml, false)) return drawableText;
670 if (auto* drawableImage = parseImage (xml, false)) return drawableImage;
671
672 return nullptr;
673 }
674
675 static String parseURL (const String& str)
676 {
677 if (str.startsWithIgnoreCase ("url"))
678 return str.fromFirstOccurrenceOf ("#", false, false)
679 .upToLastOccurrenceOf (")", false, false).trim();
680
681 return {};
682 }
683
684 //==============================================================================
685 Drawable* parseShape (const XmlPath& xml, Path& path,
686 bool shouldParseTransform = true,
687 AffineTransform* additonalTransform = nullptr) const
688 {
689 if (shouldParseTransform && xml->hasAttribute ("transform"))
690 {
691 SVGState newState (*this);
692 newState.addTransform (xml);
693
694 return newState.parseShape (xml, path, false, additonalTransform);
695 }
696
697 auto dp = new DrawablePath();
698 setCommonAttributes (*dp, xml);
699 dp->setFill (Colours::transparentBlack);
700
702
703 if (additonalTransform != nullptr)
704 path.applyTransform (*additonalTransform);
705
706 dp->setPath (path);
707
708 dp->setFill (getPathFillType (path, xml, "fill",
709 getStyleAttribute (xml, "fill-opacity"),
710 getStyleAttribute (xml, "opacity"),
713
714 auto strokeType = getStyleAttribute (xml, "stroke");
715
716 if (strokeType.isNotEmpty() && ! isNone (strokeType))
717 {
718 dp->setStrokeFill (getPathFillType (path, xml, "stroke",
719 getStyleAttribute (xml, "stroke-opacity"),
720 getStyleAttribute (xml, "opacity"),
722
723 dp->setStrokeType (getStrokeFor (xml));
724 }
725
726 auto strokeDashArray = getStyleAttribute (xml, "stroke-dasharray");
727
728 if (strokeDashArray.isNotEmpty())
729 parseDashArray (strokeDashArray, *dp);
730
731 return dp;
732 }
733
734 static bool pathContainsClosedSubPath (const Path& path) noexcept
735 {
736 for (Path::Iterator iter (path); iter.next();)
737 if (iter.elementType == Path::Iterator::closePath)
738 return true;
739
740 return false;
741 }
742
743 void parseDashArray (const String& dashList, DrawablePath& dp) const
744 {
745 if (dashList.equalsIgnoreCase ("null") || isNone (dashList))
746 return;
747
748 Array<float> dashLengths;
749
750 for (auto t = dashList.getCharPointer();;)
751 {
752 float value;
753 if (! parseCoord (t, value, true, true))
754 break;
755
756 dashLengths.add (value);
757
758 t.incrementToEndOfWhitespace();
759
760 if (*t == ',')
761 ++t;
762 }
763
764 if (dashLengths.size() > 0)
765 {
766 auto* dashes = dashLengths.getRawDataPointer();
767
768 for (int i = 0; i < dashLengths.size(); ++i)
769 {
770 if (dashes[i] <= 0) // SVG uses zero-length dashes to mean a dotted line
771 {
772 if (dashLengths.size() == 1)
773 return;
774
775 const float nonZeroLength = 0.001f;
776 dashes[i] = nonZeroLength;
777
778 const int pairedIndex = i ^ 1;
779
780 if (isPositiveAndBelow (pairedIndex, dashLengths.size())
781 && dashes[pairedIndex] > nonZeroLength)
782 dashes[pairedIndex] -= nonZeroLength;
783 }
784 }
785
786 dp.setDashLengths (dashLengths);
787 }
788 }
789
790 bool parseClipPath (const XmlPath& xml, Drawable& d)
791 {
792 const String clipPath (getStyleAttribute (xml, "clip-path"));
793
794 if (clipPath.isNotEmpty())
795 {
796 auto urlID = parseURL (clipPath);
797
798 if (urlID.isNotEmpty())
799 {
800 GetClipPathOp op = { this, &d };
801 return topLevelXml.applyOperationToChildWithID (urlID, op);
802 }
803 }
804
805 return false;
806 }
807
808 bool applyClipPath (Drawable& target, const XmlPath& xmlPath)
809 {
810 if (xmlPath->hasTagNameIgnoringNamespace ("clipPath"))
811 {
812 std::unique_ptr<DrawableComposite> drawableClipPath (new DrawableComposite());
813
814 parseSubElements (xmlPath, *drawableClipPath, false);
815
816 if (drawableClipPath->getNumChildComponents() > 0)
817 {
818 setCommonAttributes (*drawableClipPath, xmlPath);
819 target.setClipPath (std::move (drawableClipPath));
820 return true;
821 }
822 }
823
824 return false;
825 }
826
827 bool addGradientStopsIn (ColourGradient& cg, const XmlPath& fillXml) const
828 {
829 bool result = false;
830
831 if (fillXml.xml != nullptr)
832 {
833 for (auto* e : fillXml->getChildWithTagNameIterator ("stop"))
834 {
835 auto col = parseColour (fillXml.getChild (e), "stop-color", Colours::black);
836
837 auto opacity = getStyleAttribute (fillXml.getChild (e), "stop-opacity", "1");
838 col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, parseSafeFloat (opacity)));
839
840 auto offset = parseSafeFloat (e->getStringAttribute ("offset"));
841
842 if (e->getStringAttribute ("offset").containsChar ('%'))
843 offset *= 0.01f;
844
845 cg.addColour (jlimit (0.0f, 1.0f, offset), col);
846 result = true;
847 }
848 }
849
850 return result;
851 }
852
854 const Path& path,
855 const float opacity) const
856 {
857 ColourGradient gradient;
858
859 {
860 auto linkedID = getLinkedID (fillXml);
861
862 if (linkedID.isNotEmpty())
863 {
864 SetGradientStopsOp op = { this, &gradient, };
865 topLevelXml.applyOperationToChildWithID (linkedID, op);
866 }
867 }
868
869 addGradientStopsIn (gradient, fillXml);
870
871 if (int numColours = gradient.getNumColours())
872 {
873 if (gradient.getColourPosition (0) > 0)
874 gradient.addColour (0.0, gradient.getColour (0));
875
876 if (gradient.getColourPosition (numColours - 1) < 1.0)
877 gradient.addColour (1.0, gradient.getColour (numColours - 1));
878 }
879 else
880 {
881 gradient.addColour (0.0, Colours::black);
882 gradient.addColour (1.0, Colours::black);
883 }
884
885 if (opacity < 1.0f)
886 gradient.multiplyOpacity (opacity);
887
888 jassert (gradient.getNumColours() > 0);
889
890 gradient.isRadial = fillXml->hasTagNameIgnoringNamespace ("radialGradient");
891
892 float gradientWidth = viewBoxW;
893 float gradientHeight = viewBoxH;
894 float dx = 0.0f;
895 float dy = 0.0f;
896
897 const bool userSpace = fillXml->getStringAttribute ("gradientUnits").equalsIgnoreCase ("userSpaceOnUse");
898
899 if (! userSpace)
900 {
901 auto bounds = path.getBounds();
902 dx = bounds.getX();
903 dy = bounds.getY();
904 gradientWidth = bounds.getWidth();
905 gradientHeight = bounds.getHeight();
906 }
907
908 if (gradient.isRadial)
909 {
910 if (userSpace)
911 gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("cx", "50%"), gradientWidth),
912 dy + getCoordLength (fillXml->getStringAttribute ("cy", "50%"), gradientHeight));
913 else
914 gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("cx", "50%"), 1.0f),
915 dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("cy", "50%"), 1.0f));
916
917 auto radius = getCoordLength (fillXml->getStringAttribute ("r", "50%"), gradientWidth);
918 gradient.point2 = gradient.point1 + Point<float> (radius, 0.0f);
919
920 //xxx (the fx, fy focal point isn't handled properly here..)
921 }
922 else
923 {
924 if (userSpace)
925 {
926 gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x1", "0%"), gradientWidth),
927 dy + getCoordLength (fillXml->getStringAttribute ("y1", "0%"), gradientHeight));
928
929 gradient.point2.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x2", "100%"), gradientWidth),
930 dy + getCoordLength (fillXml->getStringAttribute ("y2", "0%"), gradientHeight));
931 }
932 else
933 {
934 gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x1", "0%"), 1.0f),
935 dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y1", "0%"), 1.0f));
936
937 gradient.point2.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x2", "100%"), 1.0f),
938 dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y2", "0%"), 1.0f));
939 }
940
941 if (gradient.point1 == gradient.point2)
942 return Colour (gradient.getColour (gradient.getNumColours() - 1));
943 }
944
945 FillType type (gradient);
946
947 auto gradientTransform = parseTransform (fillXml->getStringAttribute ("gradientTransform"));
948
949 if (gradient.isRadial)
950 {
951 type.transform = gradientTransform;
952 }
953 else
954 {
955 // Transform the perpendicular vector into the new coordinate space for the gradient.
956 // This vector is now the slope of the linear gradient as it should appear in the new coord space
957 auto perpendicular = Point<float> (gradient.point2.y - gradient.point1.y,
958 gradient.point1.x - gradient.point2.x)
959 .transformedBy (gradientTransform.withAbsoluteTranslation (0, 0));
960
961 auto newGradPoint1 = gradient.point1.transformedBy (gradientTransform);
962 auto newGradPoint2 = gradient.point2.transformedBy (gradientTransform);
963
964 // Project the transformed gradient vector onto the transformed slope of the linear
965 // gradient as it should appear in the new coordinate space
966 const float scale = perpendicular.getDotProduct (newGradPoint2 - newGradPoint1)
967 / perpendicular.getDotProduct (perpendicular);
968
969 type.gradient->point1 = newGradPoint1;
970 type.gradient->point2 = newGradPoint2 - perpendicular * scale;
971 }
972
973 return type;
974 }
975
977 const XmlPath& xml,
978 StringRef fillAttribute,
979 const String& fillOpacity,
980 const String& overallOpacity,
981 const Colour defaultColour) const
982 {
983 float opacity = 1.0f;
984
985 if (overallOpacity.isNotEmpty())
986 opacity = jlimit (0.0f, 1.0f, parseSafeFloat (overallOpacity));
987
988 if (fillOpacity.isNotEmpty())
989 opacity *= jlimit (0.0f, 1.0f, parseSafeFloat (fillOpacity));
990
991 String fill (getStyleAttribute (xml, fillAttribute));
992 String urlID = parseURL (fill);
993
994 if (urlID.isNotEmpty())
995 {
996 GetFillTypeOp op = { this, &path, opacity, FillType() };
997
998 if (topLevelXml.applyOperationToChildWithID (urlID, op))
999 return op.fillType;
1000 }
1001
1002 if (isNone (fill))
1004
1005 return parseColour (xml, fillAttribute, defaultColour).withMultipliedAlpha (opacity);
1006 }
1007
1009 {
1010 if (join.equalsIgnoreCase ("round")) return PathStrokeType::curved;
1011 if (join.equalsIgnoreCase ("bevel")) return PathStrokeType::beveled;
1012
1014 }
1015
1017 {
1018 if (cap.equalsIgnoreCase ("round")) return PathStrokeType::rounded;
1019 if (cap.equalsIgnoreCase ("square")) return PathStrokeType::square;
1020
1021 return PathStrokeType::butt;
1022 }
1023
1024 float getStrokeWidth (const String& strokeWidth) const noexcept
1025 {
1026 auto transformScale = std::sqrt (std::abs (transform.getDeterminant()));
1027 return transformScale * getCoordLength (strokeWidth, viewBoxW);
1028 }
1029
1031 {
1032 return PathStrokeType (getStrokeWidth (getStyleAttribute (xml, "stroke-width", "1")),
1033 getJointStyle (getStyleAttribute (xml, "stroke-linejoin")),
1034 getEndCapStyle (getStyleAttribute (xml, "stroke-linecap")));
1035 }
1036
1037 //==============================================================================
1038 Drawable* useText (const XmlPath& xml) const
1039 {
1040 auto translation = AffineTransform::translation (parseSafeFloat (xml->getStringAttribute ("x")),
1041 parseSafeFloat (xml->getStringAttribute ("y")));
1042
1043 UseTextOp op = { this, &translation, nullptr };
1044
1045 auto linkedID = getLinkedID (xml);
1046
1047 if (linkedID.isNotEmpty())
1048 topLevelXml.applyOperationToChildWithID (linkedID, op);
1049
1050 return op.target;
1051 }
1052
1053 Drawable* parseText (const XmlPath& xml, bool shouldParseTransform,
1054 AffineTransform* additonalTransform = nullptr) const
1055 {
1056 if (shouldParseTransform && xml->hasAttribute ("transform"))
1057 {
1058 SVGState newState (*this);
1059 newState.addTransform (xml);
1060
1061 return newState.parseText (xml, false, additonalTransform);
1062 }
1063
1064 if (xml->hasTagName ("use"))
1065 return useText (xml);
1066
1067 if (! xml->hasTagName ("text") && ! xml->hasTagNameIgnoringNamespace ("tspan"))
1068 return nullptr;
1069
1070 Array<float> xCoords, yCoords, dxCoords, dyCoords;
1071
1072 getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true);
1073 getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false);
1074 getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true);
1075 getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false);
1076
1077 auto font = getFont (xml);
1078 auto anchorStr = getStyleAttribute (xml, "text-anchor");
1079
1080 auto dc = new DrawableComposite();
1081 setCommonAttributes (*dc, xml);
1082
1083 for (auto* e : xml->getChildIterator())
1084 {
1085 if (e->isTextElement())
1086 {
1087 auto text = e->getText().trim();
1088
1089 auto dt = new DrawableText();
1090 dc->addAndMakeVisible (dt);
1091
1092 dt->setText (text);
1093 dt->setFont (font, true);
1094
1095 if (additonalTransform != nullptr)
1096 dt->setTransform (transform.followedBy (*additonalTransform));
1097 else
1098 dt->setTransform (transform);
1099
1100 dt->setColour (parseColour (xml, "fill", Colours::black)
1101 .withMultipliedAlpha (parseSafeFloat (getStyleAttribute (xml, "fill-opacity", "1"))));
1102
1103 Rectangle<float> bounds (xCoords[0], yCoords[0] - font.getAscent(),
1104 font.getStringWidthFloat (text), font.getHeight());
1105
1106 if (anchorStr == "middle") bounds.setX (bounds.getX() - bounds.getWidth() / 2.0f);
1107 else if (anchorStr == "end") bounds.setX (bounds.getX() - bounds.getWidth());
1108
1109 dt->setBoundingBox (bounds);
1110 }
1111 else if (e->hasTagNameIgnoringNamespace ("tspan"))
1112 {
1113 dc->addAndMakeVisible (parseText (xml.getChild (e), true));
1114 }
1115 }
1116
1117 return dc;
1118 }
1119
1120 Font getFont (const XmlPath& xml) const
1121 {
1122 Font f;
1123 auto family = getStyleAttribute (xml, "font-family").unquoted();
1124
1125 if (family.isNotEmpty())
1126 f.setTypefaceName (family);
1127
1128 if (getStyleAttribute (xml, "font-style").containsIgnoreCase ("italic"))
1129 f.setItalic (true);
1130
1131 if (getStyleAttribute (xml, "font-weight").containsIgnoreCase ("bold"))
1132 f.setBold (true);
1133
1134 return f.withPointHeight (getCoordLength (getStyleAttribute (xml, "font-size", "15"), 1.0f));
1135 }
1136
1137 //==============================================================================
1138 Drawable* useImage (const XmlPath& xml) const
1139 {
1140 auto translation = AffineTransform::translation (parseSafeFloat (xml->getStringAttribute ("x")),
1141 parseSafeFloat (xml->getStringAttribute ("y")));
1142
1143 UseImageOp op = { this, &translation, nullptr };
1144
1145 auto linkedID = getLinkedID (xml);
1146
1147 if (linkedID.isNotEmpty())
1148 topLevelXml.applyOperationToChildWithID (linkedID, op);
1149
1150 return op.target;
1151 }
1152
1153 Drawable* parseImage (const XmlPath& xml, bool shouldParseTransform,
1154 AffineTransform* additionalTransform = nullptr) const
1155 {
1156 if (shouldParseTransform && xml->hasAttribute ("transform"))
1157 {
1158 SVGState newState (*this);
1159 newState.addTransform (xml);
1160
1161 return newState.parseImage (xml, false, additionalTransform);
1162 }
1163
1164 if (xml->hasTagName ("use"))
1165 return useImage (xml);
1166
1167 if (! xml->hasTagName ("image"))
1168 return nullptr;
1169
1170 auto link = xml->getStringAttribute ("xlink:href");
1171
1172 std::unique_ptr<InputStream> inputStream;
1173 MemoryOutputStream imageStream;
1174
1175 if (link.startsWith ("data:"))
1176 {
1177 const auto indexOfComma = link.indexOf (",");
1178 auto format = link.substring (5, indexOfComma).trim();
1179 auto indexOfSemi = format.indexOf (";");
1180
1181 if (format.substring (indexOfSemi + 1).trim().equalsIgnoreCase ("base64"))
1182 {
1183 auto mime = format.substring (0, indexOfSemi).trim();
1184
1185 if (mime.equalsIgnoreCase ("image/png") || mime.equalsIgnoreCase ("image/jpeg"))
1186 {
1187 auto base64text = link.substring (indexOfComma + 1).removeCharacters ("\t\n\r ");
1188
1189 if (Base64::convertFromBase64 (imageStream, base64text))
1190 inputStream.reset (new MemoryInputStream (imageStream.getData(), imageStream.getDataSize(), false));
1191 }
1192 }
1193 }
1194 else
1195 {
1196 auto linkedFile = originalFile.getParentDirectory().getChildFile (link);
1197
1198 if (linkedFile.existsAsFile())
1199 inputStream = linkedFile.createInputStream();
1200 }
1201
1202 if (inputStream != nullptr)
1203 {
1204 auto image = ImageFileFormat::loadFrom (*inputStream);
1205
1206 if (image.isValid())
1207 {
1208 auto* di = new DrawableImage();
1209
1210 setCommonAttributes (*di, xml);
1211
1212 Rectangle<float> imageBounds (parseSafeFloat (xml->getStringAttribute ("x")),
1214 parseSafeFloat (xml->getStringAttribute ("width", String (image.getWidth()))),
1215 parseSafeFloat (xml->getStringAttribute ("height", String (image.getHeight()))));
1216
1217 di->setImage (image.rescaled ((int) imageBounds.getWidth(),
1218 (int) imageBounds.getHeight()));
1219
1220 di->setTransformToFit (imageBounds, RectanglePlacement (parsePlacementFlags (xml->getStringAttribute ("preserveAspectRatio").trim())));
1221
1222 if (additionalTransform != nullptr)
1223 di->setTransform (di->getTransform().followedBy (transform).followedBy (*additionalTransform));
1224 else
1225 di->setTransform (di->getTransform().followedBy (transform));
1226
1227 return di;
1228 }
1229 }
1230
1231 return nullptr;
1232 }
1233
1234 //==============================================================================
1235 void addTransform (const XmlPath& xml)
1236 {
1237 transform = parseTransform (xml->getStringAttribute ("transform"))
1238 .followedBy (transform);
1239 }
1240
1241 //==============================================================================
1242 bool parseCoord (String::CharPointerType& s, float& value, bool allowUnits, bool isX) const
1243 {
1244 String number;
1245
1246 if (! parseNextNumber (s, number, allowUnits))
1247 {
1248 value = 0;
1249 return false;
1250 }
1251
1252 value = getCoordLength (number, isX ? viewBoxW : viewBoxH);
1253 return true;
1254 }
1255
1256 bool parseCoords (String::CharPointerType& s, Point<float>& p, bool allowUnits) const
1257 {
1258 return parseCoord (s, p.x, allowUnits, true)
1259 && parseCoord (s, p.y, allowUnits, false);
1260 }
1261
1262 bool parseCoordsOrSkip (String::CharPointerType& s, Point<float>& p, bool allowUnits) const
1263 {
1264 if (parseCoords (s, p, allowUnits))
1265 return true;
1266
1267 if (! s.isEmpty()) ++s;
1268 return false;
1269 }
1270
1271 float getCoordLength (const String& s, const float sizeForProportions) const noexcept
1272 {
1273 auto n = parseSafeFloat (s);
1274 auto len = s.length();
1275
1276 if (len > 2)
1277 {
1278 auto dpi = 96.0f;
1279
1280 auto n1 = s[len - 2];
1281 auto n2 = s[len - 1];
1282
1283 if (n1 == 'i' && n2 == 'n') n *= dpi;
1284 else if (n1 == 'm' && n2 == 'm') n *= dpi / 25.4f;
1285 else if (n1 == 'c' && n2 == 'm') n *= dpi / 2.54f;
1286 else if (n1 == 'p' && n2 == 'c') n *= 15.0f;
1287 else if (n2 == '%') n *= 0.01f * sizeForProportions;
1288 }
1289
1290 return n;
1291 }
1292
1293 float getCoordLength (const XmlPath& xml, const char* attName, const float sizeForProportions) const noexcept
1294 {
1295 return getCoordLength (xml->getStringAttribute (attName), sizeForProportions);
1296 }
1297
1298 void getCoordList (Array<float>& coords, const String& list, bool allowUnits, bool isX) const
1299 {
1300 auto text = list.getCharPointer();
1301 float value;
1302
1303 while (parseCoord (text, value, allowUnits, isX))
1304 coords.add (value);
1305 }
1306
1307 static float parseSafeFloat (const String& s)
1308 {
1309 auto n = s.getFloatValue();
1310 return (std::isnan (n) || std::isinf (n)) ? 0.0f : n;
1311 }
1312
1313 //==============================================================================
1314 void parseCSSStyle (const XmlPath& xml)
1315 {
1316 cssStyleText = xml->getAllSubText() + "\n" + cssStyleText;
1317 }
1318
1319 void parseDefs (const XmlPath& xml)
1320 {
1321 if (auto* style = xml->getChildByName ("style"))
1323 }
1324
1325 static String::CharPointerType findStyleItem (String::CharPointerType source, String::CharPointerType name)
1326 {
1327 auto nameLength = (int) name.length();
1328
1329 while (! source.isEmpty())
1330 {
1331 if (source.getAndAdvance() == '.'
1332 && CharacterFunctions::compareIgnoreCaseUpTo (source, name, nameLength) == 0)
1333 {
1334 auto endOfName = (source + nameLength).findEndOfWhitespace();
1335
1336 if (*endOfName == '{')
1337 return endOfName;
1338
1339 if (*endOfName == ',')
1340 return CharacterFunctions::find (endOfName, (juce_wchar) '{');
1341 }
1342 }
1343
1344 return source;
1345 }
1346
1347 String getStyleAttribute (const XmlPath& xml, StringRef attributeName, const String& defaultValue = String()) const
1348 {
1349 if (xml->hasAttribute (attributeName))
1350 return xml->getStringAttribute (attributeName, defaultValue);
1351
1352 auto styleAtt = xml->getStringAttribute ("style");
1353
1354 if (styleAtt.isNotEmpty())
1355 {
1356 auto value = getAttributeFromStyleList (styleAtt, attributeName, {});
1357
1358 if (value.isNotEmpty())
1359 return value;
1360 }
1361 else if (xml->hasAttribute ("class"))
1362 {
1363 for (auto i = cssStyleText.getCharPointer();;)
1364 {
1365 auto openBrace = findStyleItem (i, xml->getStringAttribute ("class").getCharPointer());
1366
1367 if (openBrace.isEmpty())
1368 break;
1369
1370 auto closeBrace = CharacterFunctions::find (openBrace, (juce_wchar) '}');
1371
1372 if (closeBrace.isEmpty())
1373 break;
1374
1375 auto value = getAttributeFromStyleList (String (openBrace + 1, closeBrace),
1376 attributeName, defaultValue);
1377 if (value.isNotEmpty())
1378 return value;
1379
1380 i = closeBrace + 1;
1381 }
1382 }
1383
1384 if (xml.parent != nullptr)
1385 return getStyleAttribute (*xml.parent, attributeName, defaultValue);
1386
1387 return defaultValue;
1388 }
1389
1390 String getInheritedAttribute (const XmlPath& xml, StringRef attributeName) const
1391 {
1392 if (xml->hasAttribute (attributeName))
1393 return xml->getStringAttribute (attributeName);
1394
1395 if (xml.parent != nullptr)
1396 return getInheritedAttribute (*xml.parent, attributeName);
1397
1398 return {};
1399 }
1400
1401 static int parsePlacementFlags (const String& align) noexcept
1402 {
1403 if (align.isEmpty())
1404 return 0;
1405
1406 if (isNone (align))
1408
1409 return (align.containsIgnoreCase ("slice") ? RectanglePlacement::fillDestination : 0)
1410 | (align.containsIgnoreCase ("xMin") ? RectanglePlacement::xLeft
1411 : (align.containsIgnoreCase ("xMax") ? RectanglePlacement::xRight
1413 | (align.containsIgnoreCase ("yMin") ? RectanglePlacement::yTop
1414 : (align.containsIgnoreCase ("yMax") ? RectanglePlacement::yBottom
1416 }
1417
1418 //==============================================================================
1420 {
1421 return CharacterFunctions::isLetter (c) || c == '-';
1422 }
1423
1424 static String getAttributeFromStyleList (const String& list, StringRef attributeName, const String& defaultValue)
1425 {
1426 int i = 0;
1427
1428 for (;;)
1429 {
1430 i = list.indexOf (i, attributeName);
1431
1432 if (i < 0)
1433 break;
1434
1435 if ((i == 0 || (i > 0 && ! isIdentifierChar (list [i - 1])))
1436 && ! isIdentifierChar (list [i + attributeName.length()]))
1437 {
1438 i = list.indexOfChar (i, ':');
1439
1440 if (i < 0)
1441 break;
1442
1443 int end = list.indexOfChar (i, ';');
1444
1445 if (end < 0)
1446 end = 0x7ffff;
1447
1448 return list.substring (i + 1, end).trim();
1449 }
1450
1451 ++i;
1452 }
1453
1454 return defaultValue;
1455 }
1456
1457 //==============================================================================
1458 static bool isStartOfNumber (juce_wchar c) noexcept
1459 {
1460 return CharacterFunctions::isDigit (c) || c == '-' || c == '+';
1461 }
1462
1463 static bool parseNextNumber (String::CharPointerType& text, String& value, bool allowUnits)
1464 {
1465 auto s = text;
1466
1467 while (s.isWhitespace() || *s == ',')
1468 ++s;
1469
1470 auto start = s;
1471
1472 if (isStartOfNumber (*s))
1473 ++s;
1474
1475 while (s.isDigit())
1476 ++s;
1477
1478 if (*s == '.')
1479 {
1480 ++s;
1481
1482 while (s.isDigit())
1483 ++s;
1484 }
1485
1486 if ((*s == 'e' || *s == 'E') && isStartOfNumber (s[1]))
1487 {
1488 s += 2;
1489
1490 while (s.isDigit())
1491 ++s;
1492 }
1493
1494 if (allowUnits)
1495 while (s.isLetter())
1496 ++s;
1497
1498 if (s == start)
1499 {
1500 text = s;
1501 return false;
1502 }
1503
1504 value = String (start, s);
1505
1506 while (s.isWhitespace() || *s == ',')
1507 ++s;
1508
1509 text = s;
1510 return true;
1511 }
1512
1513 static bool parseNextFlag (String::CharPointerType& text, bool& value)
1514 {
1515 while (text.isWhitespace() || *text == ',')
1516 ++text;
1517
1518 if (*text != '0' && *text != '1')
1519 return false;
1520
1521 value = *(text++) != '0';
1522
1523 while (text.isWhitespace() || *text == ',')
1524 ++text;
1525
1526 return true;
1527 }
1528
1529 //==============================================================================
1530 Colour parseColour (const XmlPath& xml, StringRef attributeName, const Colour defaultColour) const
1531 {
1532 auto text = getStyleAttribute (xml, attributeName);
1533
1534 if (text.startsWithChar ('#'))
1535 {
1536 uint32 hex[8] = { 0 };
1537 hex[6] = hex[7] = 15;
1538
1539 int numChars = 0;
1540 auto s = text.getCharPointer();
1541
1542 while (numChars < 8)
1543 {
1544 auto hexValue = CharacterFunctions::getHexDigitValue (*++s);
1545
1546 if (hexValue >= 0)
1547 hex[numChars++] = (uint32) hexValue;
1548 else
1549 break;
1550 }
1551
1552 if (numChars <= 3)
1553 return Colour ((uint8) (hex[0] * 0x11),
1554 (uint8) (hex[1] * 0x11),
1555 (uint8) (hex[2] * 0x11));
1556
1557 return Colour ((uint8) ((hex[0] << 4) + hex[1]),
1558 (uint8) ((hex[2] << 4) + hex[3]),
1559 (uint8) ((hex[4] << 4) + hex[5]),
1560 (uint8) ((hex[6] << 4) + hex[7]));
1561 }
1562
1563 if (text.startsWith ("rgb") || text.startsWith ("hsl"))
1564 {
1565 auto tokens = [&text]
1566 {
1567 auto openBracket = text.indexOfChar ('(');
1568 auto closeBracket = text.indexOfChar (openBracket, ')');
1569
1570 StringArray arr;
1571
1572 if (openBracket >= 3 && closeBracket > openBracket)
1573 {
1574 arr.addTokens (text.substring (openBracket + 1, closeBracket), ",", "");
1575 arr.trim();
1576 arr.removeEmptyStrings();
1577 }
1578
1579 return arr;
1580 }();
1581
1582 auto alpha = [&tokens, &text]
1583 {
1584 if ((text.startsWith ("rgba") || text.startsWith ("hsla")) && tokens.size() == 4)
1585 return parseSafeFloat (tokens[3]);
1586
1587 return 1.0f;
1588 }();
1589
1590 if (text.startsWith ("hsl"))
1591 return Colour::fromHSL (parseSafeFloat (tokens[0]) / 360.0f,
1592 parseSafeFloat (tokens[1]) / 100.0f,
1593 parseSafeFloat (tokens[2]) / 100.0f,
1594 alpha);
1595
1596 if (tokens[0].containsChar ('%'))
1597 return Colour ((uint8) roundToInt (2.55f * parseSafeFloat (tokens[0])),
1598 (uint8) roundToInt (2.55f * parseSafeFloat (tokens[1])),
1599 (uint8) roundToInt (2.55f * parseSafeFloat (tokens[2])),
1600 alpha);
1601
1602 return Colour ((uint8) tokens[0].getIntValue(),
1603 (uint8) tokens[1].getIntValue(),
1604 (uint8) tokens[2].getIntValue(),
1605 alpha);
1606 }
1607
1608 if (text == "inherit")
1609 {
1610 for (const XmlPath* p = xml.parent; p != nullptr; p = p->parent)
1611 if (getStyleAttribute (*p, attributeName).isNotEmpty())
1612 return parseColour (*p, attributeName, defaultColour);
1613 }
1614
1615 return Colours::findColourForName (text, defaultColour);
1616 }
1617
1619 {
1621
1622 while (t.isNotEmpty())
1623 {
1624 StringArray tokens;
1625 tokens.addTokens (t.fromFirstOccurrenceOf ("(", false, false)
1626 .upToFirstOccurrenceOf (")", false, false),
1627 ", ", "");
1628
1629 tokens.removeEmptyStrings (true);
1630
1631 float numbers[6];
1632
1633 for (int i = 0; i < numElementsInArray (numbers); ++i)
1634 numbers[i] = parseSafeFloat (tokens[i]);
1635
1636 AffineTransform trans;
1637
1638 if (t.startsWithIgnoreCase ("matrix"))
1639 {
1640 trans = AffineTransform (numbers[0], numbers[2], numbers[4],
1641 numbers[1], numbers[3], numbers[5]);
1642 }
1643 else if (t.startsWithIgnoreCase ("translate"))
1644 {
1645 trans = AffineTransform::translation (numbers[0], numbers[1]);
1646 }
1647 else if (t.startsWithIgnoreCase ("scale"))
1648 {
1649 trans = AffineTransform::scale (numbers[0], numbers[tokens.size() > 1 ? 1 : 0]);
1650 }
1651 else if (t.startsWithIgnoreCase ("rotate"))
1652 {
1653 trans = AffineTransform::rotation (degreesToRadians (numbers[0]), numbers[1], numbers[2]);
1654 }
1655 else if (t.startsWithIgnoreCase ("skewX"))
1656 {
1657 trans = AffineTransform::shear (std::tan (degreesToRadians (numbers[0])), 0.0f);
1658 }
1659 else if (t.startsWithIgnoreCase ("skewY"))
1660 {
1661 trans = AffineTransform::shear (0.0f, std::tan (degreesToRadians (numbers[0])));
1662 }
1663
1664 result = trans.followedBy (result);
1665 t = t.fromFirstOccurrenceOf (")", false, false).trimStart();
1666 }
1667
1668 return result;
1669 }
1670
1671 static void endpointToCentreParameters (double x1, double y1,
1672 double x2, double y2,
1673 double angle,
1674 bool largeArc, bool sweep,
1675 double& rx, double& ry,
1676 double& centreX, double& centreY,
1677 double& startAngle, double& deltaAngle) noexcept
1678 {
1679 const double midX = (x1 - x2) * 0.5;
1680 const double midY = (y1 - y2) * 0.5;
1681
1682 const double cosAngle = std::cos (angle);
1683 const double sinAngle = std::sin (angle);
1684 const double xp = cosAngle * midX + sinAngle * midY;
1685 const double yp = cosAngle * midY - sinAngle * midX;
1686 const double xp2 = xp * xp;
1687 const double yp2 = yp * yp;
1688
1689 double rx2 = rx * rx;
1690 double ry2 = ry * ry;
1691
1692 const double s = (xp2 / rx2) + (yp2 / ry2);
1693 double c;
1694
1695 if (s <= 1.0)
1696 {
1697 c = std::sqrt (jmax (0.0, ((rx2 * ry2) - (rx2 * yp2) - (ry2 * xp2))
1698 / (( rx2 * yp2) + (ry2 * xp2))));
1699
1700 if (largeArc == sweep)
1701 c = -c;
1702 }
1703 else
1704 {
1705 const double s2 = std::sqrt (s);
1706 rx *= s2;
1707 ry *= s2;
1708 c = 0;
1709 }
1710
1711 const double cpx = ((rx * yp) / ry) * c;
1712 const double cpy = ((-ry * xp) / rx) * c;
1713
1714 centreX = ((x1 + x2) * 0.5) + (cosAngle * cpx) - (sinAngle * cpy);
1715 centreY = ((y1 + y2) * 0.5) + (sinAngle * cpx) + (cosAngle * cpy);
1716
1717 const double ux = (xp - cpx) / rx;
1718 const double uy = (yp - cpy) / ry;
1719 const double vx = (-xp - cpx) / rx;
1720 const double vy = (-yp - cpy) / ry;
1721
1722 const double length = juce_hypot (ux, uy);
1723
1724 startAngle = acos (jlimit (-1.0, 1.0, ux / length));
1725
1726 if (uy < 0)
1727 startAngle = -startAngle;
1728
1729 startAngle += MathConstants<double>::halfPi;
1730
1731 deltaAngle = acos (jlimit (-1.0, 1.0, ((ux * vx) + (uy * vy))
1732 / (length * juce_hypot (vx, vy))));
1733
1734 if ((ux * vy) - (uy * vx) < 0)
1735 deltaAngle = -deltaAngle;
1736
1737 if (sweep)
1738 {
1739 if (deltaAngle < 0)
1740 deltaAngle += MathConstants<double>::twoPi;
1741 }
1742 else
1743 {
1744 if (deltaAngle > 0)
1745 deltaAngle -= MathConstants<double>::twoPi;
1746 }
1747
1748 deltaAngle = fmod (deltaAngle, MathConstants<double>::twoPi);
1749 }
1750
1751 SVGState (const SVGState&) = default;
1752 SVGState& operator= (const SVGState&) = delete;
1753};
1754
1755
1756//==============================================================================
1757std::unique_ptr<Drawable> Drawable::createFromSVG (const XmlElement& svgDocument)
1758{
1759 if (! svgDocument.hasTagNameIgnoringNamespace ("svg"))
1760 return {};
1761
1762 SVGState state (&svgDocument);
1763 return std::unique_ptr<Drawable> (state.parseSVGElement (SVGState::XmlPath (&svgDocument, {})));
1764}
1765
1766std::unique_ptr<Drawable> Drawable::createFromSVGFile (const File& svgFile)
1767{
1768 if (auto xml = parseXMLIfTagMatches (svgFile, "svg"))
1769 return createFromSVG (*xml);
1770
1771 return {};
1772}
1773
1775{
1776 SVGState state (nullptr);
1777 Path p;
1778 state.parsePathString (p, svgPath);
1779 return p;
1780}
1781
1782} // namespace juce
#define noexcept
Definition DistrhoDefines.h:72
#define nullptr
Definition DistrhoDefines.h:75
static float dt(char val)
Definition EnvelopeFreeEdit.cpp:124
int dp
Definition Spc_Cpu.h:149
CAdPlugDatabase::CRecord::RecordType type
Definition adplugdb.cpp:93
Definition juce_AffineTransform.h:43
static AffineTransform scale(float factorX, float factorY) noexcept
Definition juce_AffineTransform.cpp:141
AffineTransform followedBy(const AffineTransform &other) const noexcept
Definition juce_AffineTransform.cpp:65
static AffineTransform translation(float deltaX, float deltaY) noexcept
Definition juce_AffineTransform.cpp:81
static AffineTransform shear(float shearX, float shearY) noexcept
Definition juce_AffineTransform.cpp:165
static AffineTransform rotation(float angleInRadians) noexcept
Definition juce_AffineTransform.cpp:106
Definition juce_Array.h:56
int size() const noexcept
Definition juce_Array.h:215
ElementType * getRawDataPointer() noexcept
Definition juce_Array.h:310
void add(const ElementType &newElement)
Definition juce_Array.h:418
Definition juce_CharPointer_ASCII.h:38
static bool isDigit(char character) noexcept
Definition juce_CharacterFunctions.cpp:69
static int compareIgnoreCaseUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
Definition juce_CharacterFunctions.h:666
static bool isLetter(char character) noexcept
Definition juce_CharacterFunctions.cpp:79
static int getHexDigitValue(juce_wchar digit) noexcept
Definition juce_CharacterFunctions.cpp:112
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
Definition juce_CharacterFunctions.h:708
Definition juce_ColourGradient.h:38
Colour getColour(int index) const noexcept
Definition juce_ColourGradient.cpp:161
Point< float > point2
Definition juce_ColourGradient.h:195
bool isRadial
Definition juce_ColourGradient.h:202
int getNumColours() const noexcept
Definition juce_ColourGradient.cpp:148
void multiplyOpacity(float multiplier) noexcept
Definition juce_ColourGradient.cpp:141
Point< float > point1
Definition juce_ColourGradient.h:195
int addColour(double proportionAlongGradient, Colour colour)
Definition juce_ColourGradient.cpp:113
double getColourPosition(int index) const noexcept
Definition juce_ColourGradient.cpp:153
Definition juce_Colour.h:38
static Colour fromHSL(float hue, float saturation, float lightness, float alpha) noexcept
Definition juce_Colour.cpp:265
void addChildComponent(Component *child, int zOrder=-1)
Definition juce_Component.cpp:1548
Definition juce_DrawableComposite.h:43
Definition juce_Drawable.h:38
static std::unique_ptr< Drawable > createFromSVGFile(const File &svgFile)
Definition juce_SVGParser.cpp:1766
static Path parseSVGPath(const String &svgPath)
Definition juce_SVGParser.cpp:1774
static std::unique_ptr< Drawable > createFromSVG(const XmlElement &svgDocument)
Definition juce_SVGParser.cpp:1757
void setClipPath(std::unique_ptr< Drawable > drawableClipPath)
Definition juce_Drawable.cpp:114
Definition juce_DrawableImage.h:38
Definition juce_DrawablePath.h:40
Definition juce_DrawableText.h:38
Definition juce_File.h:45
Definition juce_FillType.h:41
Definition juce_Font.h:42
static Image loadFrom(InputStream &input)
Definition juce_ImageFileFormat.cpp:79
Definition juce_MemoryInputStream.h:36
Definition juce_MemoryOutputStream.h:36
const void * getData() const noexcept
Definition juce_MemoryOutputStream.cpp:148
size_t getDataSize() const noexcept
Definition juce_MemoryOutputStream.h:80
Definition juce_Path.h:725
bool next() noexcept
Definition juce_Path.cpp:1490
@ closePath
Definition juce_Path.h:747
Definition juce_Path.h:65
void addRoundedRectangle(float x, float y, float width, float height, float cornerSize)
Definition juce_Path.cpp:412
void startNewSubPath(float startX, float startY)
Definition juce_Path.cpp:216
void addEllipse(float x, float y, float width, float height)
Definition juce_Path.cpp:446
Point< float > getCurrentPosition() const
Definition juce_Path.cpp:298
void quadraticTo(float controlPointX, float controlPointY, float endPointX, float endPointY)
Definition juce_Path.cpp:249
void cubicTo(float controlPoint1X, float controlPoint1Y, float controlPoint2X, float controlPoint2Y, float endPointX, float endPointY)
Definition juce_Path.cpp:268
void addRectangle(float x, float y, float width, float height)
Definition juce_Path.cpp:323
void applyTransform(const AffineTransform &transform) noexcept
Definition juce_Path.cpp:821
void addCentredArc(float centreX, float centreY, float radiusX, float radiusY, float rotationOfEllipse, float fromRadians, float toRadians, bool startAsNewSubPath=false)
Definition juce_Path.cpp:483
void closeSubPath()
Definition juce_Path.cpp:292
Rectangle< float > getBounds() const noexcept
Definition juce_Path.cpp:200
void lineTo(float endX, float endY)
Definition juce_Path.cpp:233
void setUsingNonZeroWinding(bool isNonZeroWinding) noexcept
Definition juce_Path.cpp:168
Definition juce_PathStrokeType.h:42
JointStyle
Definition juce_PathStrokeType.h:47
@ curved
Definition juce_PathStrokeType.h:53
@ beveled
Definition juce_PathStrokeType.h:54
@ mitered
Definition juce_PathStrokeType.h:48
EndCapStyle
Definition juce_PathStrokeType.h:60
@ rounded
Definition juce_PathStrokeType.h:64
@ square
Definition juce_PathStrokeType.h:62
@ butt
Definition juce_PathStrokeType.h:61
Definition juce_Point.h:42
constexpr FloatType getDotProduct(Point other) const noexcept
Definition juce_Point.h:212
void setXY(ValueType newX, ValueType newY) noexcept
Definition juce_Point.h:85
ValueType y
Definition juce_Point.h:247
Point transformedBy(const AffineTransform &transform) const noexcept
Definition juce_Point.h:223
ValueType x
Definition juce_Point.h:246
Definition juce_Rectangle.h:67
ValueType getHeight() const noexcept
Definition juce_Rectangle.h:136
ValueType getX() const noexcept
Definition juce_Rectangle.h:127
ValueType getWidth() const noexcept
Definition juce_Rectangle.h:133
void setX(ValueType newX) noexcept
Definition juce_Rectangle.h:195
Definition juce_RectanglePlacement.h:40
AffineTransform getTransformToFit(const Rectangle< float > &source, const Rectangle< float > &destination) const noexcept
Definition juce_RectanglePlacement.cpp:82
@ fillDestination
Definition juce_RectanglePlacement.h:101
@ yMid
Definition juce_RectanglePlacement.h:84
@ yTop
Definition juce_RectanglePlacement.h:76
@ xMid
Definition juce_RectanglePlacement.h:71
@ xRight
Definition juce_RectanglePlacement.h:67
@ stretchToFit
Definition juce_RectanglePlacement.h:90
@ yBottom
Definition juce_RectanglePlacement.h:80
@ xLeft
Definition juce_RectanglePlacement.h:64
Definition juce_SVGParser.cpp:30
static bool parseNextFlag(String::CharPointerType &text, bool &value)
Definition juce_SVGParser.cpp:1513
static String getAttributeFromStyleList(const String &list, StringRef attributeName, const String &defaultValue)
Definition juce_SVGParser.cpp:1424
void addTransform(const XmlPath &xml)
Definition juce_SVGParser.cpp:1235
PathStrokeType getStrokeFor(const XmlPath &xml) const
Definition juce_SVGParser.cpp:1030
float height
Definition juce_SVGParser.cpp:443
static AffineTransform parseTransform(String t)
Definition juce_SVGParser.cpp:1618
String getInheritedAttribute(const XmlPath &xml, StringRef attributeName) const
Definition juce_SVGParser.cpp:1390
static bool isIdentifierChar(juce_wchar c)
Definition juce_SVGParser.cpp:1419
static String getLinkedID(const XmlPath &xml)
Definition juce_SVGParser.cpp:644
float getStrokeWidth(const String &strokeWidth) const noexcept
Definition juce_SVGParser.cpp:1024
bool parseCoords(String::CharPointerType &s, Point< float > &p, bool allowUnits) const
Definition juce_SVGParser.cpp:1256
bool parsePathElement(const XmlPath &xml, Path &path) const
Definition juce_SVGParser.cpp:505
float getCoordLength(const String &s, const float sizeForProportions) const noexcept
Definition juce_SVGParser.cpp:1271
static float parseSafeFloat(const String &s)
Definition juce_SVGParser.cpp:1307
const File originalFile
Definition juce_SVGParser.cpp:441
FillType getPathFillType(const Path &path, const XmlPath &xml, StringRef fillAttribute, const String &fillOpacity, const String &overallOpacity, const Colour defaultColour) const
Definition juce_SVGParser.cpp:976
float width
Definition juce_SVGParser.cpp:443
static PathStrokeType::JointStyle getJointStyle(const String &join) noexcept
Definition juce_SVGParser.cpp:1008
static bool isStartOfNumber(juce_wchar c) noexcept
Definition juce_SVGParser.cpp:1458
void parseDefs(const XmlPath &xml)
Definition juce_SVGParser.cpp:1319
void parseSubElements(const XmlPath &xml, DrawableComposite &parentDrawable, bool shouldParseClip=true)
Definition juce_SVGParser.cpp:463
Colour parseColour(const XmlPath &xml, StringRef attributeName, const Colour defaultColour) const
Definition juce_SVGParser.cpp:1530
static String parseURL(const String &str)
Definition juce_SVGParser.cpp:675
void parseRect(const XmlPath &xml, Path &rect) const
Definition juce_SVGParser.cpp:561
Font getFont(const XmlPath &xml) const
Definition juce_SVGParser.cpp:1120
static void endpointToCentreParameters(double x1, double y1, double x2, double y2, double angle, bool largeArc, bool sweep, double &rx, double &ry, double &centreX, double &centreY, double &startAngle, double &deltaAngle) noexcept
Definition juce_SVGParser.cpp:1671
bool addGradientStopsIn(ColourGradient &cg, const XmlPath &fillXml) const
Definition juce_SVGParser.cpp:827
String cssStyleText
Definition juce_SVGParser.cpp:445
void parseCircle(const XmlPath &xml, Path &circle) const
Definition juce_SVGParser.cpp:591
const XmlPath topLevelXml
Definition juce_SVGParser.cpp:442
void parseDashArray(const String &dashList, DrawablePath &dp) const
Definition juce_SVGParser.cpp:743
void parseLine(const XmlPath &xml, Path &line) const
Definition juce_SVGParser.cpp:610
static PathStrokeType::EndCapStyle getEndCapStyle(const String &cap) noexcept
Definition juce_SVGParser.cpp:1016
void parsePathString(Path &path, const String &pathString) const
Definition juce_SVGParser.cpp:205
void parsePolygon(const XmlPath &xml, bool isPolyline, Path &path) const
Definition juce_SVGParser.cpp:621
static bool parseNextNumber(String::CharPointerType &text, String &value, bool allowUnits)
Definition juce_SVGParser.cpp:1463
bool parseUsePath(const XmlPath &xml, Path &path) const
Definition juce_SVGParser.cpp:654
AffineTransform transform
Definition juce_SVGParser.cpp:444
void parsePath(const XmlPath &xml, Path &path) const
Definition juce_SVGParser.cpp:553
void getCoordList(Array< float > &coords, const String &list, bool allowUnits, bool isX) const
Definition juce_SVGParser.cpp:1298
Drawable * parseShape(const XmlPath &xml, Path &path, bool shouldParseTransform=true, AffineTransform *additonalTransform=nullptr) const
Definition juce_SVGParser.cpp:685
bool parseCoord(String::CharPointerType &s, float &value, bool allowUnits, bool isX) const
Definition juce_SVGParser.cpp:1242
bool applyClipPath(Drawable &target, const XmlPath &xmlPath)
Definition juce_SVGParser.cpp:808
Drawable * parseImage(const XmlPath &xml, bool shouldParseTransform, AffineTransform *additionalTransform=nullptr) const
Definition juce_SVGParser.cpp:1153
DrawableComposite * parseLinkElement(const XmlPath &xml)
Definition juce_SVGParser.cpp:547
Drawable * useText(const XmlPath &xml) const
Definition juce_SVGParser.cpp:1038
SVGState(const XmlElement *topLevel, const File &svgFile={})
Definition juce_SVGParser.cpp:33
float getCoordLength(const XmlPath &xml, const char *attName, const float sizeForProportions) const noexcept
Definition juce_SVGParser.cpp:1293
static bool isNone(const String &s) noexcept
Definition juce_SVGParser.cpp:447
DrawableComposite * parseGroupElement(const XmlPath &xml, bool shouldParseTransform)
Definition juce_SVGParser.cpp:529
void parseEllipse(const XmlPath &xml, Path &ellipse) const
Definition juce_SVGParser.cpp:600
bool parseCoordsOrSkip(String::CharPointerType &s, Point< float > &p, bool allowUnits) const
Definition juce_SVGParser.cpp:1262
FillType getGradientFillType(const XmlPath &fillXml, const Path &path, const float opacity) const
Definition juce_SVGParser.cpp:853
static bool pathContainsClosedSubPath(const Path &path) noexcept
Definition juce_SVGParser.cpp:734
static String::CharPointerType findStyleItem(String::CharPointerType source, String::CharPointerType name)
Definition juce_SVGParser.cpp:1325
DrawableComposite * parseSwitch(const XmlPath &xml)
Definition juce_SVGParser.cpp:521
Drawable * parseSVGElement(const XmlPath &xml)
Definition juce_SVGParser.cpp:149
bool parseClipPath(const XmlPath &xml, Drawable &d)
Definition juce_SVGParser.cpp:790
Drawable * parseText(const XmlPath &xml, bool shouldParseTransform, AffineTransform *additonalTransform=nullptr) const
Definition juce_SVGParser.cpp:1053
static void setCommonAttributes(Drawable &d, const XmlPath &xml)
Definition juce_SVGParser.cpp:452
void parseCSSStyle(const XmlPath &xml)
Definition juce_SVGParser.cpp:1314
Drawable * useImage(const XmlPath &xml) const
Definition juce_SVGParser.cpp:1138
Drawable * parseUseOther(const XmlPath &xml) const
Definition juce_SVGParser.cpp:667
Drawable * parseSubElement(const XmlPath &xml)
Definition juce_SVGParser.cpp:482
float viewBoxW
Definition juce_SVGParser.cpp:443
String getStyleAttribute(const XmlPath &xml, StringRef attributeName, const String &defaultValue=String()) const
Definition juce_SVGParser.cpp:1347
static int parsePlacementFlags(const String &align) noexcept
Definition juce_SVGParser.cpp:1401
float viewBoxH
Definition juce_SVGParser.cpp:443
SVGState(const SVGState &)=default
Definition juce_StringArray.h:35
void removeEmptyStrings(bool removeWhitespaceStrings=true)
Definition juce_StringArray.cpp:250
int size() const noexcept
Definition juce_StringArray.h:136
void trim()
Definition juce_StringArray.cpp:266
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Definition juce_StringArray.cpp:329
Definition juce_String.h:53
CharPointerType getCharPointer() const noexcept
Definition juce_String.h:1153
bool equalsIgnoreCase(const String &other) const noexcept
Definition juce_String.cpp:590
String trim() const
Definition juce_String.cpp:1656
bool startsWithIgnoreCase(StringRef text) const noexcept
Definition juce_String.cpp:1398
String upToLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1595
String substring(int startIndex, int endIndex) const
Definition juce_String.cpp:1498
bool isNotEmpty() const noexcept
Definition juce_String.h:316
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1565
Definition juce_StringRef.h:62
int length() const noexcept
Definition juce_StringRef.h:105
Definition juce_XmlElement.h:83
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Definition juce_XmlElement.cpp:678
String getTagNameWithoutNamespace() const
Definition juce_XmlElement.cpp:486
Iterator< GetNextElementWithTagName > getChildWithTagNameIterator(StringRef name) const
Definition juce_XmlElement.h:730
String getAllSubText() const
Definition juce_XmlElement.cpp:928
bool hasAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:549
bool hasTagName(StringRef possibleTagName) const noexcept
Definition juce_XmlElement.cpp:470
Iterator< GetNextElement > getChildIterator() const
Definition juce_XmlElement.h:715
const String & getStringAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:555
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
Definition juce_XmlElement.cpp:491
* e
Definition inflate.c:1404
unsigned * xp
Definition inflate.c:1587
struct huft * t
Definition inflate.c:943
unsigned d
Definition inflate.c:940
register unsigned i
Definition inflate.c:1575
unsigned s
Definition inflate.c:1555
unsigned f
Definition inflate.c:1572
static PuglViewHint int value
Definition pugl.h:1708
static const char * name
Definition pugl.h:1582
static uintptr_t parent
Definition pugl.h:1644
virtual ASIOError start()=0
#define jassert(expression)
static const SerdStyle style
Definition sratom.c:36
const Colour transparentBlack
Definition juce_Colours.h:40
JUCE_API Colour findColourForName(const String &colourName, Colour defaultColour)
Definition juce_Colours.cpp:30
const Colour black
Definition juce_Colours.h:50
Definition carla_juce.cpp:31
std::unique_ptr< XmlElement > parseXMLIfTagMatches(const String &textToParse, StringRef requiredTag)
Definition juce_XmlDocument.cpp:51
unsigned int uint32
Definition juce_MathsFunctions.h:45
constexpr Type jmax(Type a, Type b)
Definition juce_MathsFunctions.h:94
RangedDirectoryIterator end(const RangedDirectoryIterator &)
Definition juce_RangedDirectoryIterator.h:184
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Definition juce_MathsFunctions.h:262
wchar_t juce_wchar
Definition juce_CharacterFunctions.h:42
Type juce_hypot(Type a, Type b) noexcept
Definition juce_MathsFunctions.h:352
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:279
constexpr FloatType degreesToRadians(FloatType degrees) noexcept
Definition juce_MathsFunctions.h:409
unsigned char uint8
Definition juce_MathsFunctions.h:37
int roundToInt(const FloatType value) noexcept
Definition juce_MathsFunctions.h:465
@ list
Definition juce_AccessibilityRole.h:56
@ image
Definition juce_AccessibilityRole.h:42
@ group
Definition juce_AccessibilityRole.h:61
constexpr int numElementsInArray(Type(&)[N]) noexcept
Definition juce_MathsFunctions.h:344
png_uint_32 length
Definition png.c:2247
static bool convertFromBase64(OutputStream &binaryOutput, StringRef base64TextInput)
Definition juce_Base64.cpp:73
static constexpr FloatType halfPi
Definition juce_MathsFunctions.h:388
static constexpr FloatType twoPi
Definition juce_MathsFunctions.h:385
Definition juce_SVGParser.cpp:107
Drawable * target
Definition juce_SVGParser.cpp:109
SVGState * state
Definition juce_SVGParser.cpp:108
Definition juce_SVGParser.cpp:129
const Path * path
Definition juce_SVGParser.cpp:131
FillType fillType
Definition juce_SVGParser.cpp:133
float opacity
Definition juce_SVGParser.cpp:132
const SVGState * state
Definition juce_SVGParser.cpp:130
Definition juce_SVGParser.cpp:118
const SVGState * state
Definition juce_SVGParser.cpp:119
ColourGradient * gradient
Definition juce_SVGParser.cpp:120
Definition juce_SVGParser.cpp:94
const SVGState * state
Definition juce_SVGParser.cpp:95
AffineTransform * transform
Definition juce_SVGParser.cpp:96
Drawable * target
Definition juce_SVGParser.cpp:97
Definition juce_SVGParser.cpp:70
Path * targetPath
Definition juce_SVGParser.cpp:72
const SVGState * state
Definition juce_SVGParser.cpp:71
Definition juce_SVGParser.cpp:81
const SVGState * state
Definition juce_SVGParser.cpp:82
AffineTransform * transform
Definition juce_SVGParser.cpp:83
Drawable * target
Definition juce_SVGParser.cpp:84
Definition juce_SVGParser.cpp:39
const XmlElement * xml
Definition juce_SVGParser.cpp:64
XmlPath(const XmlElement *e, const XmlPath *p) noexcept
Definition juce_SVGParser.cpp:40
const XmlElement * operator->() const noexcept
Definition juce_SVGParser.cpp:43
XmlPath getChild(const XmlElement *e) const noexcept
Definition juce_SVGParser.cpp:44
const XmlPath * parent
Definition juce_SVGParser.cpp:65
const XmlElement & operator*() const noexcept
Definition juce_SVGParser.cpp:42
bool applyOperationToChildWithID(const String &id, OperationType &op) const
Definition juce_SVGParser.cpp:47
const char * text
Definition swell-functions.h:167
int n
Definition crypt.c:458
uch * p
Definition crypt.c:594
return c
Definition crypt.c:175
int result
Definition process.c:1455
typedef int(UZ_EXP MsgFn)()
dy
Definition zipinfo.c:2288
_WDL_CSTRING_PREFIX void INT_PTR const char * format
Definition wdlcstring.h:263
#define const
Definition zconf.h:137