import-svg.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #include "import-svg.h"
  2. #include <cstdio>
  3. #include <tinyxml2.h>
  4. #ifdef _WIN32
  5. #pragma warning(disable:4996)
  6. #endif
  7. namespace msdfgen {
  8. #define REQUIRE(cond) { if (!(cond)) return false; }
  9. static bool readNodeType(char &output, const char *&pathDef) {
  10. int shift;
  11. char nodeType;
  12. if (sscanf(pathDef, " %c%n", &nodeType, &shift) == 1 && nodeType != '+' && nodeType != '-' && nodeType != '.' && nodeType != ',' && (nodeType < '0' || nodeType > '9')) {
  13. pathDef += shift;
  14. output = nodeType;
  15. return true;
  16. }
  17. return false;
  18. }
  19. static bool readCoord(Point2 &output, const char *&pathDef) {
  20. int shift;
  21. double x, y;
  22. if (sscanf(pathDef, "%lf%lf%n", &x, &y, &shift) == 2) {
  23. output.x = x;
  24. output.y = y;
  25. pathDef += shift;
  26. return true;
  27. }
  28. if (sscanf(pathDef, "%lf,%lf%n", &x, &y, &shift) == 2) {
  29. output.x = x;
  30. output.y = y;
  31. pathDef += shift;
  32. return true;
  33. }
  34. return false;
  35. }
  36. static bool readDouble(double &output, const char *&pathDef) {
  37. int shift;
  38. double v;
  39. if (sscanf(pathDef, "%lf%n", &v, &shift) == 1) {
  40. pathDef += shift;
  41. output = v;
  42. return true;
  43. }
  44. return false;
  45. }
  46. static bool buildFromPath(Shape &shape, const char *pathDef) {
  47. char nodeType;
  48. Point2 prevNode(0, 0);
  49. while (readNodeType(nodeType, pathDef)) {
  50. Contour &contour = shape.addContour();
  51. bool contourStart = true;
  52. Point2 startPoint;
  53. Point2 controlPoint[2];
  54. Point2 node;
  55. while (true) {
  56. switch (nodeType) {
  57. case 'M':
  58. REQUIRE(contourStart);
  59. REQUIRE(readCoord(node, pathDef));
  60. startPoint = node;
  61. nodeType = 'L';
  62. break;
  63. case 'm':
  64. REQUIRE(contourStart);
  65. REQUIRE(readCoord(node, pathDef));
  66. node += prevNode;
  67. startPoint = node;
  68. nodeType = 'l';
  69. break;
  70. case 'Z':
  71. case 'z':
  72. if (prevNode != startPoint)
  73. contour.addEdge(new LinearSegment(prevNode, startPoint));
  74. goto NEXT_CONTOUR;
  75. case 'L':
  76. REQUIRE(readCoord(node, pathDef));
  77. contour.addEdge(new LinearSegment(prevNode, node));
  78. break;
  79. case 'l':
  80. REQUIRE(readCoord(node, pathDef));
  81. node += prevNode;
  82. contour.addEdge(new LinearSegment(prevNode, node));
  83. break;
  84. case 'H':
  85. REQUIRE(readDouble(node.x, pathDef));
  86. contour.addEdge(new LinearSegment(prevNode, node));
  87. break;
  88. case 'h':
  89. REQUIRE(readDouble(node.x, pathDef));
  90. node.x += prevNode.x;
  91. contour.addEdge(new LinearSegment(prevNode, node));
  92. break;
  93. case 'V':
  94. REQUIRE(readDouble(node.y, pathDef));
  95. contour.addEdge(new LinearSegment(prevNode, node));
  96. break;
  97. case 'v':
  98. REQUIRE(readDouble(node.y, pathDef));
  99. node.y += prevNode.y;
  100. contour.addEdge(new LinearSegment(prevNode, node));
  101. break;
  102. case 'Q':
  103. REQUIRE(readCoord(controlPoint[0], pathDef));
  104. REQUIRE(readCoord(node, pathDef));
  105. contour.addEdge(new QuadraticSegment(prevNode, controlPoint[0], node));
  106. break;
  107. case 'q':
  108. REQUIRE(readCoord(controlPoint[0], pathDef));
  109. REQUIRE(readCoord(node, pathDef));
  110. controlPoint[0] += prevNode;
  111. node += prevNode;
  112. contour.addEdge(new QuadraticSegment(prevNode, controlPoint[0], node));
  113. break;
  114. // TODO T, t
  115. case 'C':
  116. REQUIRE(readCoord(controlPoint[0], pathDef));
  117. REQUIRE(readCoord(controlPoint[1], pathDef));
  118. REQUIRE(readCoord(node, pathDef));
  119. contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node));
  120. break;
  121. case 'c':
  122. REQUIRE(readCoord(controlPoint[0], pathDef));
  123. REQUIRE(readCoord(controlPoint[1], pathDef));
  124. REQUIRE(readCoord(node, pathDef));
  125. controlPoint[0] += prevNode;
  126. controlPoint[1] += prevNode;
  127. node += prevNode;
  128. contour.addEdge(new CubicSegment(prevNode, controlPoint[0], controlPoint[1], node));
  129. break;
  130. // TODO S, s
  131. // TODO A, a
  132. default:
  133. REQUIRE(false);
  134. }
  135. contourStart &= nodeType == 'M' || nodeType == 'm';
  136. prevNode = node;
  137. readNodeType(nodeType, pathDef);
  138. }
  139. NEXT_CONTOUR:;
  140. }
  141. return true;
  142. }
  143. bool loadSvgShape(Shape &output, const char *filename, Vector2 *dimensions) {
  144. tinyxml2::XMLDocument doc;
  145. if (doc.LoadFile(filename))
  146. return false;
  147. tinyxml2::XMLElement *root = doc.FirstChildElement("svg");
  148. if (!root)
  149. return false;
  150. tinyxml2::XMLElement *path = root->FirstChildElement("path");
  151. if (!path) {
  152. tinyxml2::XMLElement *g = root->FirstChildElement("g");
  153. if (g)
  154. path = g->FirstChildElement("path");
  155. }
  156. if (!path)
  157. return false;
  158. const char *pd = path->Attribute("d");
  159. if (!pd)
  160. return false;
  161. output.contours.clear();
  162. output.inverseYAxis = true;
  163. if (dimensions) {
  164. dimensions->x = root->DoubleAttribute("width");
  165. dimensions->y = root->DoubleAttribute("height");
  166. }
  167. return buildFromPath(output, pd);
  168. }
  169. }