Line data Source code
1 : /* File: draw_svg_path_data.c; Copyright and License: see below */
2 :
3 : #include "draw_svg_path_data.h"
4 : #include "layout/layout_visible_set.h"
5 : #include "u8/u8_trace.h"
6 : #include <stdio.h>
7 : #include <stdlib.h>
8 : #include <assert.h>
9 :
10 : /*! \brief states of parsing drawing directives */
11 : enum draw_svg_path_data_expect_enum {
12 : DRAW_SVG_PATH_DATA_EXPECT_COMMAND, /*!< expecting initial drawing directive */
13 : DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ, /*!< expecting drawing directive or continuation of coordinate sequence */
14 : DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_X, /*!< coordinate sequence, expecting cubic curve control_point1_X float */
15 : DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_Y, /*!< coordinate sequence, expecting cubic curve control_point1_Y float */
16 : DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_X, /*!< coordinate sequence, expecting cubic curve control_point2_X float */
17 : DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_Y, /*!< coordinate sequence, expecting cubic curve control_point2_Y float */
18 : DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_X, /*!< coordinate sequence, expecting quadratic curve control_point_X float */
19 : DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_Y, /*!< coordinate sequence, expecting quadratic curve control_point_Y float */
20 : DRAW_SVG_PATH_DATA_EXPECT_ARC_RX, /*!< arc parameter sequence, expecting arc rx float */
21 : DRAW_SVG_PATH_DATA_EXPECT_ARC_RY, /*!< arc parameter sequence, expecting arc rx float */
22 : DRAW_SVG_PATH_DATA_EXPECT_ARC_PHI, /*!< arc parameter sequence, expecting arc x axis rotation-angle float */
23 : DRAW_SVG_PATH_DATA_EXPECT_ARC_LARGE, /*!< arc parameter sequence, expecting large arc flag */
24 : DRAW_SVG_PATH_DATA_EXPECT_ARC_SWEEP, /*!< arc parameter sequence, expecting sweep arc flag */
25 : DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X, /*!< coordinate sequence, expecting end_x float */
26 : DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y, /*!< coordinate sequence, expecting end_y float */
27 : DRAW_SVG_PATH_DATA_EXPECT_EXIT, /*!< double quotes end the processing of path drawing commands */
28 : };
29 :
30 28 : u8_error_t draw_svg_path_data_private_parse ( const draw_svg_path_data_t *this_,
31 : bool draw,
32 : utf8stringviewtokenizer_t *tok_iterator,
33 : geometry_rectangle_t *io_view_rect,
34 : u8_error_info_t *out_err_info,
35 : const geometry_rectangle_t *target_bounds,
36 : cairo_t *cr )
37 : {
38 28 : U8_TRACE_BEGIN();
39 28 : assert( NULL != tok_iterator );
40 28 : assert( UTF8STRINGVIEWTOKENMODE_FLOAT_ONLY == utf8stringviewtokenizer_get_mode( tok_iterator ) );
41 28 : assert( NULL != io_view_rect );
42 28 : assert( NULL != out_err_info );
43 28 : assert( NULL != target_bounds );
44 28 : assert( ( ! draw ) || ( NULL != cr ) );
45 28 : u8_error_t result = U8_ERROR_NONE;
46 :
47 : /* calculate scale and shift to convert view rect to target bounds */
48 28 : const double view_width = geometry_rectangle_get_width( io_view_rect ) < 0.0001 ? 1.0 : geometry_rectangle_get_width( io_view_rect );
49 28 : const double scale_x = geometry_rectangle_get_width( target_bounds ) / view_width;
50 28 : const double shift_x = geometry_rectangle_get_left( target_bounds ) - ( scale_x * geometry_rectangle_get_left( io_view_rect ) );
51 28 : const double view_height = geometry_rectangle_get_height( io_view_rect ) < 0.0001 ? 1.0 : geometry_rectangle_get_height( io_view_rect );
52 28 : const double scale_y = geometry_rectangle_get_height( target_bounds ) / view_height;
53 28 : const double shift_y = geometry_rectangle_get_top( target_bounds ) - ( scale_y * geometry_rectangle_get_top( io_view_rect ) );
54 :
55 : /* states while parsing: */
56 28 : enum draw_svg_path_data_expect_enum parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND;
57 28 : char last_command = ' ';
58 28 : double command_end_x = 0.0; /* abscissa, absolute values */
59 28 : double command_end_y = 0.0; /* ordinate, absolute values */
60 28 : double coord_ctrl1_x = 0.0; /* abscissa, absolute values */
61 28 : double coord_ctrl1_y = 0.0; /* ordinate, absolute values */
62 28 : double coord_ctrl2_x = 0.0; /* abscissa, absolute values */
63 28 : double coord_ctrl2_y = 0.0; /* ordinate, absolute values */
64 28 : double subpath_start_x = 0.0; /* abscissa, absolute values */
65 28 : double subpath_start_y = 0.0; /* ordinate, absolute values */
66 28 : double command_start_x = 0.0; /* abscissa, absolute values */
67 28 : double command_start_y = 0.0; /* ordinate, absolute values */
68 28 : double arc_r_x = 0.0; /* major ellipsis radius */
69 28 : double arc_r_y = 0.0; /* minor ellipsis radius */
70 28 : double arc_phi = 0.0; /* angle between major ellipsis radius and x-axis (unit: rad) */
71 28 : bool arc_large_arc = false; /* true if the arc is spanning more than 180 degree / 1*pi */
72 28 : bool arc_sweep_positive = false; /* true if the arc is traversed in positive-angle direction */
73 28 : bool view_rect_drop_0_0 = true; /* if io_view_rect only contains the initial point (0,0), true states that */
74 : /* this (0,0) point shall be ignored when updating the io_view_rect. */
75 :
76 : /* init draw */
77 28 : if ( draw )
78 : {
79 0 : assert( NULL != cr );
80 0 : cairo_move_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
81 : }
82 :
83 367 : while( utf8stringviewtokenizer_has_next( tok_iterator )
84 367 : && ( parser_state != DRAW_SVG_PATH_DATA_EXPECT_EXIT )
85 711 : && ( result == U8_ERROR_NONE ) )
86 : {
87 339 : const utf8stringview_t tok = utf8stringviewtokenizer_next( tok_iterator );
88 339 : assert( utf8stringview_get_length( &tok ) > 0 ); /* otherwise this would not be a token */
89 339 : U8_TRACE_INFO_VIEW( "token:", tok );
90 :
91 339 : switch ( parser_state )
92 : {
93 37 : case DRAW_SVG_PATH_DATA_EXPECT_COMMAND:
94 : {
95 37 : const char current = *utf8stringview_get_start( &tok );
96 37 : if (( utf8stringview_equals_str( &tok, "\"" ) )||( utf8stringview_equals_str( &tok, "\'" ) ))
97 : {
98 : /* no subpath here to draw */
99 : /* end of d attribute, back to caller */
100 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_EXIT;
101 : }
102 33 : else if (( current=='z' )||( current=='Z' ))
103 : {
104 : /* no line_to here because there is no subpath to end */
105 4 : if ( draw )
106 : {
107 0 : assert( NULL != cr );
108 0 : cairo_move_to ( cr, subpath_start_x, subpath_start_y );
109 : }
110 : /* end of subpath */
111 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND;
112 4 : last_command = current;
113 : }
114 29 : else if (( current=='m' )||( current=='M' )||( current=='l' )||( current=='L' ))
115 : {
116 8 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
117 8 : last_command = current;
118 : }
119 21 : else if (( current=='h' )||( current=='H' ))
120 : {
121 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
122 2 : last_command = current;
123 : }
124 19 : else if (( current=='v' )||( current=='V' ))
125 : {
126 2 : command_end_x = command_start_x;
127 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y;
128 2 : last_command = current;
129 : }
130 17 : else if (( current=='c' )||( current=='C' ))
131 : {
132 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_X;
133 2 : last_command = current;
134 : }
135 15 : else if (( current=='s' )||( current=='S' ))
136 : {
137 : /* there was no preceding c/C/s/S command, initialize first control point to command start point: */
138 2 : coord_ctrl1_x = command_start_x;
139 2 : coord_ctrl1_y = command_start_y;
140 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_X;
141 2 : last_command = current;
142 : }
143 13 : else if (( current=='q' )||( current=='Q' ))
144 : {
145 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_X;
146 2 : last_command = current;
147 : }
148 11 : else if (( current=='t' )||( current=='T' ))
149 : {
150 : /* there was no preceding q/Q/t/T command, initialize next control point to command start point: */
151 2 : coord_ctrl1_x = command_start_x;
152 2 : coord_ctrl1_y = command_start_y;
153 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
154 2 : last_command = current;
155 : }
156 9 : else if (( current=='a' )||( current=='A' ))
157 : {
158 7 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_RX;
159 7 : last_command = current;
160 : }
161 : else
162 : {
163 : /* unexpected token */
164 2 : u8_error_info_init_line( out_err_info,
165 : U8_ERROR_PARSER_STRUCTURE,
166 2 : utf8stringviewtokenizer_get_line( tok_iterator )
167 : );
168 2 : result |= U8_ERROR_PARSER_STRUCTURE;
169 : }
170 : }
171 37 : break;
172 :
173 59 : case DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ:
174 : {
175 59 : const char current = *utf8stringview_get_start( &tok );
176 59 : if (( utf8stringview_equals_str( &tok, "\"" ) )||( utf8stringview_equals_str( &tok, "\'" ) ))
177 : {
178 : /* end of d attribute, back to caller */
179 19 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_EXIT;
180 : }
181 40 : else if ( current=='z' || current=='Z' )
182 : {
183 : /* close subpath */
184 5 : if ( draw )
185 : {
186 0 : assert( NULL != cr );
187 0 : cairo_line_to ( cr, subpath_start_x * scale_x + shift_x, subpath_start_y * scale_y + shift_y );
188 : }
189 5 : command_start_x = subpath_start_x;
190 5 : command_start_y = subpath_start_y;
191 : /* end of subpath */
192 5 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND;
193 5 : last_command = current;
194 : }
195 35 : else if (( current=='m' )||( current=='M' )||( current=='l' )||( current=='L' ))
196 : {
197 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
198 4 : last_command = current;
199 : }
200 31 : else if (( current=='h' )||( current=='H' ))
201 : {
202 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
203 4 : last_command = current;
204 : }
205 27 : else if (( current=='v' )||( current=='V' ))
206 : {
207 4 : command_end_x = command_start_x;
208 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y;
209 4 : last_command = current;
210 : }
211 23 : else if (( current=='c' )||( current=='C' ))
212 : {
213 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_X;
214 2 : last_command = current;
215 : }
216 21 : else if (( current=='s' )||( current=='S' ))
217 : {
218 2 : if (( last_command == 'c' )||( last_command == 'C' )||( last_command == 's' )||( last_command == 'S' ))
219 : {
220 : /* init the first control point as mirror of the last control point: */
221 2 : coord_ctrl1_x = command_start_x + (command_start_x-coord_ctrl2_x);
222 2 : coord_ctrl1_y = command_start_y + (command_start_y-coord_ctrl2_y);
223 : }
224 : else
225 : {
226 0 : coord_ctrl1_x = command_start_x;
227 0 : coord_ctrl1_y = command_start_y;
228 : }
229 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_X;
230 2 : last_command = current;
231 : }
232 19 : else if (( current=='q' )||( current=='Q' ))
233 : {
234 2 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_X;
235 2 : last_command = current;
236 : }
237 17 : else if (( current=='t' )||( current=='T' ))
238 : {
239 3 : if (( last_command == 'q' )||( last_command == 'Q' )||( last_command == 't' )||( last_command == 'T' ))
240 : {
241 : /* init the next control point as mirror of the last control point: */
242 2 : coord_ctrl1_x = command_start_x + (command_start_x-coord_ctrl1_x);
243 2 : coord_ctrl1_y = command_start_y + (command_start_y-coord_ctrl1_y);
244 : }
245 : else
246 : {
247 1 : coord_ctrl1_x = command_start_x;
248 1 : coord_ctrl1_y = command_start_y;
249 : }
250 3 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
251 3 : last_command = current;
252 : }
253 14 : else if (( current=='a' )||( current=='A' ))
254 : {
255 0 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_RX;
256 0 : last_command = current;
257 : }
258 14 : else if ( current==',' )
259 : {
260 : /* skip */
261 : }
262 : else
263 : {
264 : /* read coordinate */
265 14 : double value = 0.0;
266 : unsigned int byte_length;
267 : const u8_error_t float_err
268 14 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
269 14 : if ( float_err != U8_ERROR_NONE )
270 : {
271 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
272 0 : u8_error_info_init_line( out_err_info,
273 : U8_ERROR_PARSER_STRUCTURE,
274 0 : utf8stringviewtokenizer_get_line( tok_iterator )
275 : );
276 0 : result |= U8_ERROR_PARSER_STRUCTURE;
277 : }
278 : else
279 : {
280 : /* U8_TRACE_INFO_INT( "line", utf8stringviewtokenizer_get_line( tok_iterator ) ); */
281 14 : assert( byte_length == utf8stringview_get_length( &tok ) );
282 : }
283 14 : const bool is_absolute = ( last_command <= 'Z' );
284 14 : const double value_x_abs = is_absolute ? value : ( command_start_x + value );
285 : /* do command */
286 14 : if (( last_command=='v' )||( last_command=='V' ))
287 : {
288 1 : command_end_x = command_start_x;
289 1 : command_end_y = is_absolute ? value : ( command_start_y + value );
290 : /* draw */
291 1 : if ( draw )
292 : {
293 0 : assert( NULL != cr );
294 0 : cairo_line_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
295 : }
296 : /* update state */
297 1 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
298 1 : command_start_y = command_end_y;
299 : /* continue with next */
300 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ;
301 : }
302 13 : else if (( last_command=='h' )||( last_command=='H' ))
303 : {
304 1 : command_end_x = value_x_abs;
305 1 : command_end_y = command_start_y;
306 : /* draw */
307 1 : if ( draw )
308 : {
309 0 : assert( NULL != cr );
310 0 : cairo_line_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
311 : }
312 : /* update state */
313 1 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
314 1 : command_start_x = command_end_x;
315 : /* continue with next */
316 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ;
317 : }
318 12 : else if (( last_command=='c' )||( last_command=='C' ))
319 : {
320 1 : coord_ctrl1_x = value_x_abs;
321 : /* reading last_command parameters */
322 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_Y;
323 : }
324 11 : else if (( last_command=='s' )||( last_command=='S' ))
325 : {
326 : /* init the first control point as mirror of the last control point: */
327 : {
328 1 : coord_ctrl1_x = command_start_x + (command_start_x-coord_ctrl2_x);
329 1 : coord_ctrl1_y = command_start_y + (command_start_y-coord_ctrl2_y);
330 : }
331 1 : coord_ctrl2_x = value_x_abs;
332 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_Y;
333 : }
334 10 : else if (( last_command=='q' )||( last_command=='Q' ))
335 : {
336 1 : coord_ctrl1_x = value_x_abs;
337 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_Y;
338 : }
339 9 : else if (( last_command=='t' )||( last_command=='T' ))
340 : {
341 : /* init the next control point as mirror of the last control point: */
342 : {
343 1 : coord_ctrl1_x = command_start_x + (command_start_x-coord_ctrl1_x);
344 1 : coord_ctrl1_y = command_start_y + (command_start_y-coord_ctrl1_y);
345 : }
346 1 : command_end_x = value_x_abs;
347 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y;
348 : }
349 8 : else if (( last_command=='a' )||( last_command=='A' ))
350 : {
351 : /* store major radius value to appropriate variable */
352 1 : arc_r_x = value;
353 : /* continue reading last_command parameters */
354 1 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_RY;
355 : }
356 : else
357 : {
358 7 : command_end_x = value_x_abs;
359 : /* continue reading last_command parameters */
360 7 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y;
361 : }
362 : }
363 : }
364 59 : break;
365 :
366 46 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X:
367 : {
368 46 : if ( utf8stringview_equals_str( &tok, "," ) )
369 : {
370 : /* skip */
371 : }
372 : else
373 : {
374 : /* read coordinate */
375 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
376 46 : double value = 0.0;
377 : unsigned int byte_length;
378 : const u8_error_t float_err
379 46 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
380 46 : if ( float_err != U8_ERROR_NONE )
381 : {
382 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
383 2 : u8_error_info_init_line( out_err_info,
384 : U8_ERROR_PARSER_STRUCTURE,
385 2 : utf8stringviewtokenizer_get_line( tok_iterator )
386 : );
387 2 : result |= U8_ERROR_PARSER_STRUCTURE;
388 : }
389 : else
390 : {
391 44 : assert( byte_length == utf8stringview_get_length( &tok ) );
392 : }
393 46 : const bool is_absolute = ( last_command <= 'Z' );
394 46 : command_end_x = is_absolute ? value : ( command_start_x + value );
395 : /* do command */
396 46 : if (( last_command=='h' )||( last_command=='H' ))
397 : {
398 6 : command_end_y = command_start_y;
399 : /* draw */
400 6 : if ( draw )
401 : {
402 0 : assert( NULL != cr );
403 0 : cairo_line_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
404 : }
405 : /* update state */
406 6 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
407 6 : command_start_x = command_end_x;
408 : /* continue with next */
409 6 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ;
410 : }
411 : else
412 : {
413 : /* continue reading more parameters for last_command */
414 40 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y;
415 : }
416 : }
417 : }
418 46 : break;
419 :
420 93 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_END_Y:
421 : {
422 93 : if ( utf8stringview_equals_str( &tok, "," ) )
423 : {
424 : /* skip */
425 : }
426 : else
427 : {
428 : /* read coordinate */
429 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
430 52 : double value = 0.0;
431 : unsigned int byte_length;
432 : const u8_error_t float_err
433 52 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
434 52 : if ( float_err != U8_ERROR_NONE )
435 : {
436 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
437 1 : u8_error_info_init_line( out_err_info,
438 : U8_ERROR_PARSER_STRUCTURE,
439 1 : utf8stringviewtokenizer_get_line( tok_iterator )
440 : );
441 1 : result |= U8_ERROR_PARSER_STRUCTURE;
442 : }
443 : else
444 : {
445 51 : assert( byte_length == utf8stringview_get_length( &tok ) );
446 : }
447 52 : const bool is_absolute = ( last_command <= 'Z' );
448 52 : command_end_y = is_absolute ? value : ( command_start_y + value );
449 : /* do command */
450 52 : if (( last_command=='m' )||( last_command=='M' ))
451 : {
452 : /* draw */
453 4 : if ( draw )
454 : {
455 0 : assert( NULL != cr );
456 0 : cairo_move_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
457 : }
458 : /* update state */
459 4 : subpath_start_x = command_end_x;
460 4 : subpath_start_y = command_end_y;
461 4 : command_start_x = command_end_x;
462 4 : command_start_y = command_end_y;
463 4 : last_command = is_absolute ? 'L' : 'l'; /* following coordinates are implicitely line-to commands */
464 : /* init or update the io_view_rect parameter */
465 4 : if ( geometry_rectangle_is_point( io_view_rect ) && view_rect_drop_0_0 )
466 : {
467 : /* when we have the first coordinate, the io_view_rect is not a point anymore... */
468 3 : geometry_rectangle_init( io_view_rect, subpath_start_x, subpath_start_y, 0.0, 0.0 );
469 3 : view_rect_drop_0_0 = false;
470 : }
471 : else
472 : {
473 1 : geometry_rectangle_embrace( io_view_rect, subpath_start_x, subpath_start_y );
474 : }
475 : }
476 48 : else if (( last_command=='l' )||( last_command=='L' )||( last_command=='v' )||( last_command=='V' ))
477 : {
478 : /* draw */
479 20 : if ( draw )
480 : {
481 0 : assert( NULL != cr );
482 0 : cairo_line_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
483 : }
484 : /* update state */
485 20 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
486 20 : command_start_x = command_end_x;
487 20 : command_start_y = command_end_y;
488 : }
489 28 : else if (( last_command=='c' )||( last_command=='C' )||( last_command=='s' )||( last_command=='S' ))
490 : {
491 : /* draw */
492 10 : if ( draw )
493 : {
494 0 : assert( NULL != cr );
495 0 : cairo_curve_to( cr,
496 0 : coord_ctrl1_x * scale_x + shift_x,
497 0 : coord_ctrl1_y * scale_y + shift_y,
498 0 : coord_ctrl2_x * scale_x + shift_x,
499 0 : coord_ctrl2_y * scale_y + shift_y,
500 0 : command_end_x * scale_x + shift_x,
501 0 : command_end_y * scale_y + shift_y
502 : );
503 : }
504 : /* update state */
505 10 : geometry_rectangle_embrace( io_view_rect, coord_ctrl1_x, coord_ctrl1_y );
506 10 : geometry_rectangle_embrace( io_view_rect, coord_ctrl2_x, coord_ctrl2_y );
507 10 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
508 10 : command_start_x = command_end_x;
509 10 : command_start_y = command_end_y;
510 : }
511 18 : else if (( last_command=='q' )||( last_command=='Q' )||( last_command=='t' )||( last_command=='T' ))
512 : {
513 : /* draw */
514 10 : if ( draw )
515 : {
516 0 : assert( NULL != cr );
517 0 : cairo_curve_to( cr,
518 0 : coord_ctrl1_x * scale_x + shift_x,
519 0 : coord_ctrl1_y * scale_y + shift_y,
520 0 : coord_ctrl1_x * scale_x + shift_x, /* same control point */
521 0 : coord_ctrl1_y * scale_y + shift_y, /* same control point */
522 0 : command_end_x * scale_x + shift_x,
523 0 : command_end_y * scale_y + shift_y
524 : );
525 : }
526 : /* update state */
527 10 : geometry_rectangle_embrace( io_view_rect, coord_ctrl1_x, coord_ctrl1_y );
528 10 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
529 10 : command_start_x = command_end_x;
530 10 : command_start_y = command_end_y;
531 : }
532 8 : else if (( last_command=='a' )||( last_command=='A' ))
533 8 : {
534 8 : double center_x = 0.0;
535 8 : double center_y = 0.0;
536 8 : double start_angle = 0.0;
537 8 : double delta_angle= 0.0;
538 : const u8_error_t arc_err
539 8 : = draw_svg_path_data_private_get_arc_center( this_,
540 : command_start_x,
541 : command_start_y,
542 : command_end_x,
543 : command_end_y,
544 : arc_large_arc,
545 : arc_sweep_positive,
546 : arc_r_x,
547 : arc_r_y,
548 : arc_phi,
549 : ¢er_x,
550 : ¢er_y,
551 : &start_angle,
552 : &delta_angle
553 : );
554 :
555 : /* draw */
556 8 : if ( draw )
557 : {
558 0 : assert( NULL != cr );
559 0 : if ( arc_err == U8_ERROR_NONE )
560 : {
561 0 : const double ellipsis_ratio = ( arc_r_x > 0.001 ) ? ( arc_r_y / arc_r_x ) : 1000.0;
562 : cairo_matrix_t orig_matrix;
563 0 : cairo_get_matrix( cr, &orig_matrix );
564 : /* scale and shift viewport to icon size */
565 0 : cairo_translate( cr, shift_x, shift_y );
566 0 : cairo_scale( cr, scale_x, scale_y );
567 : /* setup SVG transformations */
568 0 : cairo_translate( cr, center_x, center_y );
569 0 : cairo_rotate( cr, arc_phi );
570 0 : cairo_scale( cr, 1.0, ellipsis_ratio );
571 0 : if ( delta_angle < 0.0 )
572 : {
573 0 : cairo_arc_negative( cr,
574 : 0.0,
575 : 0.0,
576 : arc_r_x,
577 : start_angle,
578 : start_angle + delta_angle
579 : );
580 : }
581 : else
582 : {
583 0 : cairo_arc( cr,
584 : 0.0,
585 : 0.0,
586 : arc_r_x,
587 : start_angle,
588 : start_angle + delta_angle
589 : );
590 : }
591 0 : cairo_set_matrix( cr, &orig_matrix );
592 : }
593 0 : else if ( arc_err == U8_ERROR_EDGE_CASE_PARAM )
594 : {
595 0 : cairo_line_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
596 : }
597 : else
598 : {
599 0 : cairo_move_to ( cr, command_end_x * scale_x + shift_x, command_end_y * scale_y + shift_y );
600 : }
601 : }
602 : /* update state */
603 8 : geometry_rectangle_embrace( io_view_rect, center_x, center_y - arc_r_y );
604 8 : geometry_rectangle_embrace( io_view_rect, center_x - arc_r_x, center_y );
605 8 : geometry_rectangle_embrace( io_view_rect, center_x, center_y + arc_r_y );
606 8 : geometry_rectangle_embrace( io_view_rect, center_x + arc_r_x, center_y );
607 8 : geometry_rectangle_embrace( io_view_rect, command_end_x, command_end_y );
608 8 : command_start_x = command_end_x;
609 8 : command_start_y = command_end_y;
610 : }
611 : else
612 : {
613 0 : U8_TRACE_INFO("inconsistent statemachine");
614 0 : assert(false);
615 : }
616 : /* continue with next */
617 52 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COMMAND_OR_COORD_SEQ;
618 : }
619 : }
620 93 : break;
621 :
622 4 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_X:
623 : {
624 4 : if ( utf8stringview_equals_str( &tok, "," ) )
625 : {
626 : /* skip */
627 : }
628 : else
629 : {
630 : /* read coordinate */
631 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
632 4 : double value = 0.0;
633 : unsigned int byte_length;
634 : const u8_error_t float_err
635 4 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
636 4 : if ( float_err != U8_ERROR_NONE )
637 : {
638 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
639 0 : u8_error_info_init_line( out_err_info,
640 : U8_ERROR_PARSER_STRUCTURE,
641 0 : utf8stringviewtokenizer_get_line( tok_iterator )
642 : );
643 0 : result |= U8_ERROR_PARSER_STRUCTURE;
644 : }
645 : else
646 : {
647 4 : assert( byte_length == utf8stringview_get_length( &tok ) );
648 : }
649 4 : const bool is_absolute = ( last_command <= 'Z' );
650 4 : coord_ctrl1_x = is_absolute ? value : ( command_start_x + value );
651 : /* continue reading last_command parameters */
652 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_Y;
653 : }
654 : }
655 4 : break;
656 :
657 10 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL1_Y:
658 : {
659 10 : if ( utf8stringview_equals_str( &tok, "," ) )
660 : {
661 : /* skip */
662 : }
663 : else
664 : {
665 : /* read coordinate */
666 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
667 5 : double value = 0.0;
668 : unsigned int byte_length;
669 : const u8_error_t float_err
670 5 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
671 5 : if ( float_err != U8_ERROR_NONE )
672 : {
673 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
674 0 : u8_error_info_init_line( out_err_info,
675 : U8_ERROR_PARSER_STRUCTURE,
676 0 : utf8stringviewtokenizer_get_line( tok_iterator )
677 : );
678 0 : result |= U8_ERROR_PARSER_STRUCTURE;
679 : }
680 : else
681 : {
682 5 : assert( byte_length == utf8stringview_get_length( &tok ) );
683 : }
684 5 : const bool is_absolute = ( last_command <= 'Z' );
685 5 : coord_ctrl1_y = is_absolute ? value : ( command_start_y + value );
686 : /* continue reading last_command parameters */
687 5 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_X;
688 : }
689 : }
690 10 : break;
691 :
692 9 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_X:
693 : {
694 9 : if ( utf8stringview_equals_str( &tok, "," ) )
695 : {
696 : /* skip */
697 : }
698 : else
699 : {
700 : /* read coordinate */
701 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
702 9 : double value = 0.0;
703 : unsigned int byte_length;
704 : const u8_error_t float_err
705 9 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
706 9 : if ( float_err != U8_ERROR_NONE )
707 : {
708 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
709 0 : u8_error_info_init_line( out_err_info,
710 : U8_ERROR_PARSER_STRUCTURE,
711 0 : utf8stringviewtokenizer_get_line( tok_iterator )
712 : );
713 0 : result |= U8_ERROR_PARSER_STRUCTURE;
714 : }
715 : else
716 : {
717 9 : assert( byte_length == utf8stringview_get_length( &tok ) );
718 : }
719 9 : const bool is_absolute = ( last_command <= 'Z' );
720 9 : coord_ctrl2_x = is_absolute ? value : ( command_start_x + value );
721 : /* continue reading last_command parameters */
722 9 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_Y;
723 : }
724 : }
725 9 : break;
726 :
727 20 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_CTRL2_Y:
728 : {
729 20 : if ( utf8stringview_equals_str( &tok, "," ) )
730 : {
731 : /* skip */
732 : }
733 : else
734 : {
735 : /* read coordinate */
736 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
737 10 : double value = 0.0;
738 : unsigned int byte_length;
739 : const u8_error_t float_err
740 10 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
741 10 : if ( float_err != U8_ERROR_NONE )
742 : {
743 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
744 0 : u8_error_info_init_line( out_err_info,
745 : U8_ERROR_PARSER_STRUCTURE,
746 0 : utf8stringviewtokenizer_get_line( tok_iterator )
747 : );
748 0 : result |= U8_ERROR_PARSER_STRUCTURE;
749 : }
750 : else
751 : {
752 10 : assert( byte_length == utf8stringview_get_length( &tok ) );
753 : }
754 10 : const bool is_absolute = ( last_command <= 'Z' );
755 10 : coord_ctrl2_y = is_absolute ? value : ( command_start_y + value );
756 : /* continue with next */
757 10 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
758 : }
759 : }
760 20 : break;
761 :
762 4 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_X:
763 : {
764 4 : if ( utf8stringview_equals_str( &tok, "," ) )
765 : {
766 : /* skip */
767 : }
768 : else
769 : {
770 : /* read coordinate */
771 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
772 4 : double value = 0.0;
773 : unsigned int byte_length;
774 : const u8_error_t float_err
775 4 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
776 4 : if ( float_err != U8_ERROR_NONE )
777 : {
778 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
779 0 : u8_error_info_init_line( out_err_info,
780 : U8_ERROR_PARSER_STRUCTURE,
781 0 : utf8stringviewtokenizer_get_line( tok_iterator )
782 : );
783 0 : result |= U8_ERROR_PARSER_STRUCTURE;
784 : }
785 : else
786 : {
787 4 : assert( byte_length == utf8stringview_get_length( &tok ) );
788 : }
789 4 : const bool is_absolute = ( last_command <= 'Z' );
790 4 : coord_ctrl1_x = is_absolute ? value : ( command_start_x + value );
791 : /* continue reading last_command parameters */
792 4 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_Y;
793 : }
794 : }
795 4 : break;
796 :
797 10 : case DRAW_SVG_PATH_DATA_EXPECT_COORD_QCTRL_Y:
798 : {
799 10 : if ( utf8stringview_equals_str( &tok, "," ) )
800 : {
801 : /* skip */
802 : }
803 : else
804 : {
805 : /* read coordinate */
806 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
807 5 : double value = 0.0;
808 : unsigned int byte_length;
809 : const u8_error_t float_err
810 5 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
811 5 : if ( float_err != U8_ERROR_NONE )
812 : {
813 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
814 0 : u8_error_info_init_line( out_err_info,
815 : U8_ERROR_PARSER_STRUCTURE,
816 0 : utf8stringviewtokenizer_get_line( tok_iterator )
817 : );
818 0 : result |= U8_ERROR_PARSER_STRUCTURE;
819 : }
820 : else
821 : {
822 5 : assert( byte_length == utf8stringview_get_length( &tok ) );
823 : }
824 5 : const bool is_absolute = ( last_command <= 'Z' );
825 5 : coord_ctrl1_y = is_absolute ? value : ( command_start_y + value );
826 : /* continue with next */
827 5 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
828 : }
829 : }
830 10 : break;
831 :
832 7 : case DRAW_SVG_PATH_DATA_EXPECT_ARC_RX:
833 : {
834 7 : if ( utf8stringview_equals_str( &tok, "," ) )
835 : {
836 : /* skip */
837 : }
838 : else
839 : {
840 : /* read coordinate */
841 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
842 7 : double value = 0.0;
843 : unsigned int byte_length;
844 : const u8_error_t float_err
845 7 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
846 7 : if ( float_err != U8_ERROR_NONE )
847 : {
848 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
849 0 : u8_error_info_init_line( out_err_info,
850 : U8_ERROR_PARSER_STRUCTURE,
851 0 : utf8stringviewtokenizer_get_line( tok_iterator )
852 : );
853 0 : result |= U8_ERROR_PARSER_STRUCTURE;
854 : }
855 : else
856 : {
857 7 : assert( byte_length == utf8stringview_get_length( &tok ) );
858 : }
859 : /* store value to appropriate variable */
860 7 : arc_r_x = value;
861 : /* continue reading last_command parameters */
862 7 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_RY;
863 : }
864 : }
865 7 : break;
866 :
867 16 : case DRAW_SVG_PATH_DATA_EXPECT_ARC_RY:
868 : {
869 16 : if ( utf8stringview_equals_str( &tok, "," ) )
870 : {
871 : /* skip */
872 : }
873 : else
874 : {
875 : /* read coordinate */
876 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
877 8 : double value = 0.0;
878 : unsigned int byte_length;
879 : const u8_error_t float_err
880 8 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
881 8 : if ( float_err != U8_ERROR_NONE )
882 : {
883 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
884 0 : u8_error_info_init_line( out_err_info,
885 : U8_ERROR_PARSER_STRUCTURE,
886 0 : utf8stringviewtokenizer_get_line( tok_iterator )
887 : );
888 0 : result |= U8_ERROR_PARSER_STRUCTURE;
889 : }
890 : else
891 : {
892 8 : assert( byte_length == utf8stringview_get_length( &tok ) );
893 : }
894 : /* store value to appropriate variable */
895 8 : arc_r_y = value;
896 : /* continue reading last_command parameters */
897 8 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_PHI;
898 : }
899 : }
900 16 : break;
901 :
902 8 : case DRAW_SVG_PATH_DATA_EXPECT_ARC_PHI:
903 : {
904 8 : if ( utf8stringview_equals_str( &tok, "," ) )
905 : {
906 : /* skip */
907 : }
908 : else
909 : {
910 : /* read coordinate */
911 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
912 8 : double value = 0.0;
913 : unsigned int byte_length;
914 : const u8_error_t float_err
915 8 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
916 8 : if ( float_err != U8_ERROR_NONE )
917 : {
918 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
919 0 : u8_error_info_init_line( out_err_info,
920 : U8_ERROR_PARSER_STRUCTURE,
921 0 : utf8stringviewtokenizer_get_line( tok_iterator )
922 : );
923 0 : result |= U8_ERROR_PARSER_STRUCTURE;
924 : }
925 : else
926 : {
927 8 : assert( byte_length == utf8stringview_get_length( &tok ) );
928 : }
929 : /* store value to appropriate variable */
930 8 : arc_phi = value * ( M_PI / 180.0 );
931 : /* continue reading last_command parameters */
932 8 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_LARGE;
933 : }
934 : }
935 8 : break;
936 :
937 8 : case DRAW_SVG_PATH_DATA_EXPECT_ARC_LARGE:
938 : {
939 8 : if ( utf8stringview_equals_str( &tok, "," ) )
940 : {
941 : /* skip */
942 : }
943 : else
944 : {
945 : /* read coordinate */
946 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
947 8 : double value = 0.0;
948 : unsigned int byte_length;
949 : const u8_error_t float_err
950 8 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
951 8 : if ( float_err != U8_ERROR_NONE )
952 : {
953 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
954 0 : u8_error_info_init_line( out_err_info,
955 : U8_ERROR_PARSER_STRUCTURE,
956 0 : utf8stringviewtokenizer_get_line( tok_iterator )
957 : );
958 0 : result |= U8_ERROR_PARSER_STRUCTURE;
959 : }
960 : else
961 : {
962 8 : assert( byte_length == utf8stringview_get_length( &tok ) );
963 : }
964 : /* store value to appropriate variable */
965 8 : arc_large_arc = ( value > 0.0001 );
966 : /* continue reading last_command parameters */
967 8 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_ARC_SWEEP;
968 : }
969 : }
970 8 : break;
971 :
972 8 : case DRAW_SVG_PATH_DATA_EXPECT_ARC_SWEEP:
973 : {
974 8 : if ( utf8stringview_equals_str( &tok, "," ) )
975 : {
976 : /* skip */
977 : }
978 : else
979 : {
980 : /* read coordinate */
981 : /* utf8string_parse_float is faster than utf8stringview_parse_float; only it may not stop at view end but at terminating 0 after svg-path */
982 8 : double value = 0.0;
983 : unsigned int byte_length;
984 : const u8_error_t float_err
985 8 : = utf8string_parse_float( utf8stringview_get_start( &tok ), &byte_length, &value );
986 8 : if ( float_err != U8_ERROR_NONE )
987 : {
988 : /* float_err could be a UTF8ERROR_NOT_FOUND or a UTF8ERROR_OUT_OF_RANGE */
989 0 : u8_error_info_init_line( out_err_info,
990 : U8_ERROR_PARSER_STRUCTURE,
991 0 : utf8stringviewtokenizer_get_line( tok_iterator )
992 : );
993 0 : result |= U8_ERROR_PARSER_STRUCTURE;
994 : }
995 : else
996 : {
997 8 : assert( byte_length == utf8stringview_get_length( &tok ) );
998 : }
999 : /* store value to appropriate variable */
1000 8 : arc_sweep_positive = ( value > 0.0001 );
1001 : /* continue reading last_command parameters */
1002 8 : parser_state = DRAW_SVG_PATH_DATA_EXPECT_COORD_END_X;
1003 : }
1004 : }
1005 8 : break;
1006 :
1007 0 : case DRAW_SVG_PATH_DATA_EXPECT_EXIT: /* or */
1008 : default:
1009 : {
1010 : /* the outer while loop should already have been exited */
1011 0 : assert( false );
1012 : }
1013 : break;
1014 : }
1015 : }
1016 :
1017 : /* report error on unfinished drawing */
1018 28 : if (( result == U8_ERROR_NONE )&&( parser_state != DRAW_SVG_PATH_DATA_EXPECT_EXIT ))
1019 : {
1020 0 : result |= U8_ERROR_PARSER_STRUCTURE;
1021 : /* if no other error encountered yet, report this one: */
1022 0 : if ( ! u8_error_info_is_error( out_err_info ) )
1023 : {
1024 0 : u8_error_info_init_line( out_err_info,
1025 : U8_ERROR_PARSER_STRUCTURE,
1026 0 : utf8stringviewtokenizer_get_line( tok_iterator )
1027 : );
1028 : }
1029 : }
1030 :
1031 : /* check if anything was drawn at all */
1032 28 : if ( geometry_rectangle_is_point( io_view_rect ) && view_rect_drop_0_0 )
1033 : {
1034 3 : result |= U8_ERROR_NOT_FOUND;
1035 : }
1036 :
1037 28 : U8_TRACE_END_ERR(result);
1038 28 : return result;
1039 : }
1040 :
1041 :
1042 : /*
1043 : Copyright 2023-2024 Andreas Warnke
1044 : http://www.apache.org/licenses/LICENSE-2.0
1045 :
1046 : Licensed under the Apache License, Version 2.0 (the "License");
1047 : you may not use this file except in compliance with the License.
1048 : You may obtain a copy of the License at
1049 :
1050 :
1051 : Unless required by applicable law or agreed to in writing, software
1052 : distributed under the License is distributed on an "AS IS" BASIS,
1053 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1054 : See the License for the specific language governing permissions and
1055 : limitations under the License.
1056 : */
|