@@ -939,79 +939,166 @@ void cleanup_path(PathIterator &path,
939939 }
940940}
941941
942+ void quad2cubic (double x0, double y0,
943+ double x1, double y1,
944+ double x2, double y2,
945+ double *outx, double *outy)
946+ {
947+
948+ outx[0 ] = x0 + 2 ./3 . * (x1 - x0);
949+ outy[0 ] = y0 + 2 ./3 . * (y1 - y0);
950+ outx[1 ] = outx[0 ] + 1 ./3 . * (x2 - x0);
951+ outy[1 ] = outy[0 ] + 1 ./3 . * (y2 - y0);
952+ outx[2 ] = x2;
953+ outy[2 ] = y2;
954+ }
955+
956+ char *__append_to_string (char *p, char *buffer, size_t buffersize,
957+ const char *content)
958+ {
959+ int buffersize_int = (int )buffersize;
960+
961+ for (const char *i = content; *i; ++i) {
962+ if (p < buffer || p - buffer >= buffersize_int) {
963+ return NULL ;
964+ }
965+
966+ *p++ = *i;
967+ }
968+
969+ return p;
970+ }
971+
942972template <class PathIterator >
943- void convert_to_svg (PathIterator &path,
944- agg::trans_affine &trans,
945- agg::rect_d &clip_rect,
946- bool simplify,
947- int precision,
948- char *buffer,
949- size_t *buffersize)
973+ int __convert_to_string (PathIterator &path,
974+ int precision,
975+ char **codes,
976+ bool postfix,
977+ char *buffer,
978+ size_t *buffersize)
950979{
951980#if PY_VERSION_HEX < 0x02070000
952981 char format[64 ];
953982 snprintf (format, 64 , " %s.%dg" , " %" , precision);
954983#endif
955984
956- typedef agg::conv_transform<py::PathIterator> transformed_path_t ;
957- typedef PathNanRemover<transformed_path_t > nan_removal_t ;
958- typedef PathClipper<nan_removal_t > clipped_t ;
959- typedef PathSimplifier<clipped_t > simplify_t ;
960-
961- bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2 );
962-
963- transformed_path_t tpath (path, trans);
964- nan_removal_t nan_removed (tpath, true , path.has_curves ());
965- clipped_t clipped (nan_removed, do_clip, clip_rect);
966- simplify_t simplified (clipped, simplify, path.simplify_threshold ());
967-
968985 char *p = buffer;
986+ double x[3 ];
987+ double y[3 ];
988+ double last_x = 0.0 ;
989+ double last_y = 0.0 ;
969990
970- const char codes[] = { ' M' , ' L' , ' Q' , ' C' };
971- const int waits[] = { 1 , 1 , 2 , 3 };
972-
973- int wait = 0 ;
991+ const int sizes[] = { 1 , 1 , 2 , 3 };
992+ int size = 0 ;
974993 unsigned code;
975- double x = 0 , y = 0 ;
976- while ((code = simplified.vertex (&x, &y)) != agg::path_cmd_stop) {
977- if (wait == 0 ) {
978- *p++ = ' \n ' ;
979994
980- if (code == 0x4f ) {
981- *p++ = ' z' ;
982- *p++ = ' \n ' ;
983- continue ;
995+ while ((code = path.vertex (&x[0 ], &y[0 ])) != agg::path_cmd_stop) {
996+ if (code == 0x4f ) {
997+ if ((p = __append_to_string (p, buffer, *buffersize, codes[4 ])) == NULL ) return 1 ;
998+ } else if (code < 5 ) {
999+ size = sizes[code - 1 ];
1000+
1001+ for (int i = 1 ; i < size; ++i) {
1002+ unsigned subcode = path.vertex (&x[i], &y[i]);
1003+ if (subcode != code) {
1004+ return 2 ;
1005+ }
9841006 }
9851007
986- *p++ = codes[code - 1 ];
987- wait = waits[code - 1 ];
988- } else {
989- *p++ = ' ' ;
990- }
1008+ /* For formats that don't support quad curves, convert to
1009+ cubic curves */
1010+ if (code == CURVE3 && codes[code - 1 ][0 ] == ' \0 ' ) {
1011+ quad2cubic (last_x, last_y, x[0 ], y[0 ], x[1 ], y[1 ], x, y);
1012+ code++;
1013+ size = 3 ;
1014+ }
9911015
1016+ if (!postfix) {
1017+ if ((p = __append_to_string (p, buffer, *buffersize, codes[code - 1 ])) == NULL ) return 1 ;
1018+ if ((p = __append_to_string (p, buffer, *buffersize, " " )) == NULL ) return 1 ;
1019+ }
1020+
1021+ for (int i = 0 ; i < size; ++i) {
9921022#if PY_VERSION_HEX >= 0x02070000
993- char *str;
994- str = PyOS_double_to_string (x, ' g' , precision, 0 , NULL );
995- p += snprintf (p, *buffersize - (p - buffer), " %s" , str);
996- PyMem_Free (str);
997- *p++ = ' ' ;
998- str = PyOS_double_to_string (y, ' g' , precision, 0 , NULL );
999- p += snprintf (p, *buffersize - (p - buffer), " %s" , str);
1000- PyMem_Free (str);
1023+ char *str;
1024+ str = PyOS_double_to_string (x[i], ' g' , precision, 0 , NULL );
1025+ if ((p = __append_to_string (p, buffer, *buffersize, str)) == NULL ) {
1026+ PyMem_Free (str);
1027+ return 1 ;
1028+ }
1029+ PyMem_Free (str);
1030+ if ((p = __append_to_string (p, buffer, *buffersize, " " )) == NULL ) return 1 ;
1031+ str = PyOS_double_to_string (y[i], ' g' , precision, 0 , NULL );
1032+ if ((p = __append_to_string (p, buffer, *buffersize, str)) == NULL ) {
1033+ PyMem_Free (str);
1034+ return 1 ;
1035+ }
1036+ PyMem_Free (str);
1037+ if ((p = __append_to_string (p, buffer, *buffersize, " " )) == NULL ) return 1 ;
10011038#else
1002- char str[64 ];
1003- PyOS_ascii_formatd (str, 64 , format, x);
1004- p += snprintf (p, *buffersize - (p - buffer), " %s" , str);
1005- *p++ = ' ' ;
1006- PyOS_ascii_formatd (str, 64 , format, y);
1007- p += snprintf (p, *buffersize - (p - buffer), " %s" , str);
1039+ char str[64 ];
1040+ PyOS_ascii_formatd (str, 64 , format, x[i]);
1041+ if ((p = __append_to_string (p, buffer, *buffersize, str)) == NULL ) return 1 ;
1042+ p = __append_to_string (p, buffer, *buffersize, " " );
1043+ PyOS_ascii_formatd (str, 64 , format, y[i]);
1044+ if ((p = __append_to_string (p, buffer, *buffersize, str)) == NULL ) return 1 ;
1045+ if ((p = __append_to_string (p, buffer, *buffersize, " " )) == NULL ) return 1 ;
10081046#endif
1047+ }
1048+
1049+ if (postfix) {
1050+ if ((p = __append_to_string (p, buffer, *buffersize, codes[code - 1 ])) == NULL ) return 1 ;
1051+ }
1052+
1053+ last_x = x[size - 1 ];
1054+ last_y = y[size - 1 ];
1055+ } else {
1056+ // Unknown code value
1057+ return 2 ;
1058+ }
10091059
1010- --wait ;
1060+ if ((p = __append_to_string (p, buffer, *buffersize, " \n " )) == NULL ) return 1 ;
10111061 }
10121062
1013- *p = ' \0 ' ;
10141063 *buffersize = p - buffer;
1064+
1065+ return 0 ;
1066+ }
1067+
1068+ template <class PathIterator >
1069+ int convert_to_string (PathIterator &path,
1070+ agg::trans_affine &trans,
1071+ agg::rect_d &clip_rect,
1072+ bool simplify,
1073+ SketchParams sketch_params,
1074+ int precision,
1075+ char **codes,
1076+ bool postfix,
1077+ char *buffer,
1078+ size_t *buffersize)
1079+ {
1080+ typedef agg::conv_transform<py::PathIterator> transformed_path_t ;
1081+ typedef PathNanRemover<transformed_path_t > nan_removal_t ;
1082+ typedef PathClipper<nan_removal_t > clipped_t ;
1083+ typedef PathSimplifier<clipped_t > simplify_t ;
1084+ typedef agg::conv_curve<simplify_t > curve_t ;
1085+ typedef Sketch<curve_t > sketch_t ;
1086+
1087+ bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2 );
1088+
1089+ transformed_path_t tpath (path, trans);
1090+ nan_removal_t nan_removed (tpath, true , path.has_curves ());
1091+ clipped_t clipped (nan_removed, do_clip, clip_rect);
1092+ simplify_t simplified (clipped, simplify, path.simplify_threshold ());
1093+
1094+ if (sketch_params.scale == 0.0 ) {
1095+ return __convert_to_string (simplified, precision, codes, postfix, buffer, buffersize);
1096+ } else {
1097+ curve_t curve (simplified);
1098+ sketch_t sketch (curve, sketch_params.scale , sketch_params.length , sketch_params.randomness );
1099+ return __convert_to_string (sketch, precision, codes, postfix, buffer, buffersize);
1100+ }
1101+
10151102}
10161103
10171104#endif
0 commit comments