1- #include " ofxSvg.h"
21#include " ofConstants.h"
2+ #include " ofxSvg.h"
33#include < locale>
44
55using std::string;
66using std::vector;
77
8- extern " C" {
9- #include " svgtiny.h"
8+ extern " C" {
9+ #include " svgtiny.h"
1010}
11- ofxSvg::~ofxSvg (){
11+ ofxSvg::~ofxSvg () {
1212 paths.clear ();
1313}
1414
@@ -20,14 +20,14 @@ float ofxSvg::getHeight() const {
2020 return height;
2121}
2222
23- int ofxSvg::getNumPath (){
23+ int ofxSvg::getNumPath () {
2424 return paths.size ();
2525}
26- ofPath & ofxSvg::getPathAt (int n){
26+ ofPath & ofxSvg::getPathAt (int n) {
2727 return paths[n];
2828}
2929
30- void ofxSvg::load (of::filesystem::path fileName){
30+ void ofxSvg::load (of::filesystem::path fileName) {
3131 // fileName = ofToDataPath(fileName);
3232 std::string file = ofToDataPath (fileName);
3333
@@ -39,58 +39,57 @@ void ofxSvg::load(of::filesystem::path fileName){
3939 // }
4040
4141 // ofBuffer buffer = ofBufferFromFile(fileName);
42-
42+
4343 // loadFromString(buffer.getText(), fileName);
4444
45- if (file.compare (" " ) == 0 ){
45+ if (file.compare (" " ) == 0 ) {
4646 ofLogError (" ofxSVG" ) << " load(): path does not exist: \" " << file << " \" " ;
4747 return ;
4848 }
4949
5050 ofBuffer buffer = ofBufferFromFile (file);
51-
52- loadFromString (buffer.getText (), file);
5351
52+ loadFromString (buffer.getText (), file);
5453}
5554
56- void ofxSvg::loadFromString (std::string stringdata, std::string urlstring){
57-
55+ void ofxSvg::loadFromString (std::string stringdata, std::string urlstring) {
56+
5857 // goes some way to improving SVG compatibility
5958 fixSvgString (stringdata);
6059
61- const char * data = stringdata.c_str ();
60+ const char * data = stringdata.c_str ();
6261 int size = stringdata.size ();
63- const char * url = urlstring.c_str ();
62+ const char * url = urlstring.c_str ();
6463
6564 struct svgtiny_diagram * diagram = svgtiny_create ();
6665 // Switch to "C" locale as svgtiny expect it to parse floating points (issue 6644)
67- std::locale prev_locale = std::locale::global ( std::locale::classic () );
66+ std::locale prev_locale = std::locale::global (std::locale::classic ());
6867 svgtiny_code code = svgtiny_parse (diagram, data, size, url, 0 , 0 );
6968 // Restore locale
70- std::locale::global ( prev_locale );
69+ std::locale::global (prev_locale);
7170
72- if (code != svgtiny_OK){
71+ if (code != svgtiny_OK) {
7372 string msg;
74- switch (code){
75- case svgtiny_OUT_OF_MEMORY:
76- msg = " svgtiny_OUT_OF_MEMORY" ;
77- break ;
73+ switch (code) {
74+ case svgtiny_OUT_OF_MEMORY:
75+ msg = " svgtiny_OUT_OF_MEMORY" ;
76+ break ;
7877
79- /* case svgtiny_LIBXML_ERROR:
78+ /* case svgtiny_LIBXML_ERROR:
8079 msg = "svgtiny_LIBXML_ERROR";
8180 break;*/
8281
83- case svgtiny_NOT_SVG:
84- msg = " svgtiny_NOT_SVG" ;
85- break ;
82+ case svgtiny_NOT_SVG:
83+ msg = " svgtiny_NOT_SVG" ;
84+ break ;
8685
87- case svgtiny_SVG_ERROR:
88- msg = " svgtiny_SVG_ERROR: line " + ofToString (diagram->error_line ) + " : " + diagram->error_message ;
89- break ;
86+ case svgtiny_SVG_ERROR:
87+ msg = " svgtiny_SVG_ERROR: line " + ofToString (diagram->error_line ) + " : " + diagram->error_message ;
88+ break ;
9089
91- default :
92- msg = " unknown svgtiny_code " + ofToString (code);
93- break ;
90+ default :
91+ msg = " unknown svgtiny_code " + ofToString (code);
92+ break ;
9493 }
9594 ofLogError (" ofxSVG" ) << " load(): couldn't parse \" " << urlstring << " \" : " << msg;
9695 }
@@ -100,142 +99,161 @@ void ofxSvg::loadFromString(std::string stringdata, std::string urlstring){
10099 svgtiny_free (diagram);
101100}
102101
103- void ofxSvg::fixSvgString (std::string& xmlstring) {
104-
102+ void ofxSvg::fixSvgString (std::string & xmlstring) {
103+
105104 ofXml xml;
106-
105+
107106 xml.parse (xmlstring);
108-
107+
109108 // so it turns out that if the stroke width is <1 it rounds it down to 0,
110109 // and makes it disappear because svgtiny stores strokewidth as an integer!
111110 ofXml::Search strokeWidthElements = xml.find (" //*[@stroke-width]" );
112- if (!strokeWidthElements.empty ()) {
113-
114- for (ofXml & element: strokeWidthElements){
111+ if (!strokeWidthElements.empty ()) {
112+ for (ofXml & element : strokeWidthElements) {
115113 // cout << element.toString() << endl;
116114 float strokewidth = element.getAttribute (" stroke-width" ).getFloatValue ();
117- strokewidth = MAX (1 ,round (strokewidth));
115+ strokewidth = MAX (1 , round (strokewidth));
118116 element.getAttribute (" stroke-width" ).set (strokewidth);
119-
120117 }
121118 }
122-
119+
120+ // Affinity Designer does not set width/height as pixels but as a percentage
121+ // and relies on the "viewBox" to convey the size of things. this applies the
122+ // viewBox to the width and height.
123+
124+ std::vector<std::string> rect;
125+ for (ofXml & element : xml.find (" //*[@viewBox]" )) {
126+ rect = ofSplitString (element.getAttribute (" viewBox" ).getValue (), " " );
127+ }
128+
129+ if (rect.size () == 4 ) {
130+
131+ for (ofXml & element : xml.find (" //*[@width]" )) {
132+ if (element.getAttribute (" width" ).getValue () == " 100%" ) {
133+ auto w = ofToFloat (rect.at (2 ));
134+ ofLogWarning (" ofxSvg::fixSvgString()" ) << " the SVG size is provided as percentage, which svgtiny translates to 0. The width is corrected from the viewBox width: " << w;
135+ element.getAttribute (" width" ).set (w);
136+ }
137+ }
138+
139+ for (ofXml & element : xml.find (" //*[@height]" )) {
140+ if (element.getAttribute (" height" ).getValue () == " 100%" ) {
141+ auto w = ofToFloat (rect.at (3 ));
142+ ofLogWarning (" ofxSvg::fixSvgString()" ) << " the SVG size is provided as percentage, which svgtiny translates to 0. The height is corrected from the viewBox height: " << w;
143+ element.getAttribute (" height" ).set (w);
144+ }
145+ }
146+ }
147+
123148 // lib svgtiny doesn't remove elements with display = none, so this code fixes that
124-
149+
125150 bool finished = false ;
126- while (!finished) {
127-
128- ofXml::Search invisibleElements = xml.find (" //*[@display=\" none\" ]" );
129-
130- if (invisibleElements.empty ()) {
151+ while (!finished) {
152+
153+ ofXml::Search invisibleElements = xml.find (" //*[@display=\" none\" ]" );
154+
155+ if (invisibleElements.empty ()) {
131156 finished = true ;
132157 } else {
133- const ofXml& element = invisibleElements[0 ];
158+ const ofXml & element = invisibleElements[0 ];
134159 ofXml parent = element.getParent ();
135- if (parent && element) parent.removeChild (element);
160+ if (parent && element) parent.removeChild (element);
136161 }
137-
138162 }
139-
163+
140164 // implement the SVG "use" element by expanding out those elements into
141165 // XML that svgtiny will parse correctly.
142166 ofXml::Search useElements = xml.find (" //use" );
143- if (!useElements.empty ()) {
144-
145- for (ofXml & element: useElements){
146-
167+ if (!useElements.empty ()) {
168+
169+ for (ofXml & element : useElements) {
170+
147171 // get the id attribute
148172 string id = element.getAttribute (" xlink:href" ).getValue ();
149173 // remove the leading "#" from the id
150174 id.erase (id.begin ());
151-
175+
152176 // find the original definition of that element - TODO add defs into path?
153- string searchstring =" //*[@id='" +id+ " ']" ;
177+ string searchstring = " //*[@id='" + id + " ']" ;
154178 ofXml idelement = xml.findFirst (searchstring);
155-
179+
156180 // if we found one then use it! (find first returns an empty xml on failure)
157- if (idelement.getAttribute (" id" ).getValue ()!= " " ) {
158-
181+ if (idelement.getAttribute (" id" ).getValue () != " " ) {
182+
159183 // make a copy of that element
160184 element.appendChild (idelement);
161-
185+
162186 // then turn the use element into a g element
163187 element.setName (" g" );
164-
165188 }
166189 }
167190 }
168-
191+
169192 xmlstring = xml.toString ();
170-
171193}
172194
173- void ofxSvg::draw (){
174- for (int i = 0 ; i < (int )paths.size (); i++){
195+ void ofxSvg::draw () {
196+ for (int i = 0 ; i < (int )paths.size (); i++) {
175197 paths[i].draw ();
176198 }
177199}
178200
179- void ofxSvg::setupDiagram (struct svgtiny_diagram * diagram){
201+ void ofxSvg::setupDiagram (struct svgtiny_diagram * diagram) {
180202
181203 width = diagram->width ;
182204 height = diagram->height ;
183205
184206 paths.clear ();
185207
186- for (int i = 0 ; i < (int )diagram->shape_count ; i++){
187- if (diagram->shape [i].path ){
208+ for (int i = 0 ; i < (int )diagram->shape_count ; i++) {
209+ if (diagram->shape [i].path ) {
188210 paths.push_back (ofPath ());
189- setupShape (&diagram->shape [i],paths.back ());
190- }else if (diagram->shape [i].text ){
211+ setupShape (&diagram->shape [i], paths.back ());
212+ } else if (diagram->shape [i].text ) {
191213 ofLogWarning (" ofxSVG" ) << " setupDiagram(): text: not implemented yet" ;
192214 }
193215 }
194216}
195217
196- void ofxSvg::setupShape (struct svgtiny_shape * shape, ofPath & path){
218+ void ofxSvg::setupShape (struct svgtiny_shape * shape, ofPath & path) {
197219 float * p = shape->path ;
198220
199221 path.setFilled (false );
200222
201- if (shape->fill != svgtiny_TRANSPARENT){
223+ if (shape->fill != svgtiny_TRANSPARENT) {
202224 path.setFilled (true );
203225 path.setFillHexColor (shape->fill );
204226 path.setPolyWindingMode (OF_POLY_WINDING_NONZERO);
205- }
227+ }
206228
207- if (shape->stroke != svgtiny_TRANSPARENT){
229+ if (shape->stroke != svgtiny_TRANSPARENT) {
208230 path.setStrokeWidth (shape->stroke_width );
209231 path.setStrokeHexColor (shape->stroke );
210232 }
211233
212- for (int i = 0 ; i < (int )shape->path_length ;){
213- if (p[i] == svgtiny_PATH_MOVE){
234+ for (int i = 0 ; i < (int )shape->path_length ;) {
235+ if (p[i] == svgtiny_PATH_MOVE) {
214236 path.moveTo (p[i + 1 ], p[i + 2 ]);
215237 i += 3 ;
216- }
217- else if (p[i] == svgtiny_PATH_CLOSE){
238+ } else if (p[i] == svgtiny_PATH_CLOSE) {
218239 path.close ();
219240
220241 i += 1 ;
221- }
222- else if (p[i] == svgtiny_PATH_LINE){
242+ } else if (p[i] == svgtiny_PATH_LINE) {
223243 path.lineTo (p[i + 1 ], p[i + 2 ]);
224244 i += 3 ;
225- }
226- else if (p[i] == svgtiny_PATH_BEZIER){
245+ } else if (p[i] == svgtiny_PATH_BEZIER) {
227246 path.bezierTo (p[i + 1 ], p[i + 2 ],
228- p[i + 3 ], p[i + 4 ],
229- p[i + 5 ], p[i + 6 ]);
247+ p[i + 3 ], p[i + 4 ],
248+ p[i + 5 ], p[i + 6 ]);
230249 i += 7 ;
231- }
232- else {
250+ } else {
233251 ofLogError (" ofxSVG" ) << " setupShape(): SVG parse error" ;
234252 i += 1 ;
235253 }
236254 }
237255}
238256
239- const vector <ofPath> & ofxSvg::getPaths () const {
240- return paths;
257+ const vector<ofPath> & ofxSvg::getPaths () const {
258+ return paths;
241259}
0 commit comments