Commit 0af465b9 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

improved handling of edge cases in the glyph outline conversion code

parent af298bf5
Loading
Loading
Loading
Loading
+117 −59
Original line number Diff line number Diff line
@@ -57,12 +57,21 @@ void* font_get_freetype(FontRef font) {
  return font->ft_font;
}

enum class GlyphPointType : char { ON = 'x', OFF2 = '2', OFF3 = '3' };

struct GlyphContour {
  std::vector<Point> points;
  std::vector<GlyphPointType> tags;
};

ReturnCode font_get_glyph_path(
    FontRef font,
    double font_size,
    double dpi,
    uint32_t codepoint,
    Path* path) {
  *path = Path{};

  // load the glyph using freetype
  auto font_size_ft = font_size * (72.0 / dpi) * 64;
  if (FT_Set_Char_Size(font->ft_font, 0, font_size_ft, dpi, dpi)) {
@@ -85,95 +94,144 @@ ReturnCode font_get_glyph_path(
  auto glyph_outline = &((FT_OutlineGlyph) glyph)->outline;

  // retrieve the glyph outline data from freetype
  enum class GlyphPointType { MOVE, SIMPLE, CONTROL };

  std::vector<Point> glyph_points;
  std::vector<GlyphPointType> glyph_point_tags;
  std::vector<GlyphContour> contours;

  auto glyph_outline_tags = glyph_outline->tags;
  for (size_t n = 0; n < glyph_outline->n_contours; n++) {
    auto glyph_outline_idx = n == 0 ? 0 : glyph_outline->contours[n - 1] + 1;
    auto glyph_outline_end = glyph_outline->contours[n];

    // read contour points
    GlyphContour contour;
    for (size_t i = glyph_outline_idx; i <= glyph_outline_end; ++i) {
      Point p;
      p.x = glyph_outline->points[i].x / 64.0;
      p.y = -glyph_outline->points[i].y / 64.0;

      GlyphPointType pt;
      if (i == glyph_outline_idx) {
        pt = GlyphPointType::MOVE;
      } else if (int(glyph_outline->tags[i]) & 1) {
        pt = GlyphPointType::SIMPLE;
      } else {
        pt = GlyphPointType::CONTROL;
      switch (FT_CURVE_TAG(glyph_outline->tags[i])) {
        case FT_CURVE_TAG_ON:
          pt = GlyphPointType::ON;
          break;
        case FT_CURVE_TAG_CONIC:
          pt = GlyphPointType::OFF2;
          break;
        case FT_CURVE_TAG_CUBIC:
          pt = GlyphPointType::OFF3;
          break;
        default:
          return ERROR;
      }

      glyph_points.push_back(p);
      glyph_point_tags.push_back(pt);
      contour.points.push_back(p);
      contour.tags.push_back(pt);
    }

    // close the path
    {
      Point p;
      p.x = glyph_outline->points[glyph_outline_idx].x / 64.0;
      p.y = -glyph_outline->points[glyph_outline_idx].y / 64.0;
      glyph_points.push_back(p);
      glyph_point_tags.push_back(GlyphPointType::SIMPLE);
    }
    contours.push_back(contour);
  }

  FT_Done_Glyph(glyph);

  // convert the glyph outline to a path object
  *path = Path{};
  // convert the contours to the output path object
  for (auto& c : contours) {
    if (c.points.empty()) {
      return ERROR;
    }

    // insert virtual "on" points for each pair of successive OFF2 points
    for (size_t i = 0; i + 1 < c.points.size(); ++i) {
      if (c.tags[i + 0] == GlyphPointType::OFF2 &&
          c.tags[i + 1] == GlyphPointType::OFF2) {
        c.tags.insert(
            c.tags.begin() + i + 1,
            GlyphPointType::ON);

  for (size_t i = 0; i < glyph_points.size(); ++i) {
    const auto& p = glyph_points[i];
    const auto& pt = glyph_point_tags[i];
        c.points.insert(
            c.points.begin() + i + 1,
            vec2_mul(vec2_add(c.points[i + 0], c.points[i + 1]), 0.5));
      }
    }

    // edge case: first point can be OFF2 -> start with last point
    if (c.tags.front() == GlyphPointType::OFF2 &&
        c.tags.back() == GlyphPointType::ON) {
      c.points.insert(c.points.begin(), c.points.back());
      c.tags.insert(c.tags.begin(), c.tags.back());
    }

    // move at begin of contour
    if (pt == GlyphPointType::MOVE) {
      path->moveTo(glyph_points[i].x, glyph_points[i].y);
    // edge case: first and last point can be OFF2 -> start with virtual point
    if (c.tags.front() == GlyphPointType::OFF2 &&
        c.tags.back() == GlyphPointType::OFF2) {
      c.points.insert(
          c.points.begin(),
          vec2_mul(vec2_add(c.points.front(), c.points.back()), 0.5));

      c.tags.insert(c.tags.begin(), GlyphPointType::ON);
    }

    // the last point in the contour uses the first as its end point
    c.points.push_back(c.points[0]);
    c.tags.push_back(c.tags[0]);

    // now every contour should end and begin with an "on" point
    assert(c.tags.front() == GlyphPointType::ON);
    assert(c.tags.back() == GlyphPointType::ON);
    assert(c.points.size() == c.tags.size());

    // translate the resolved contour to the path object representation
    path->moveTo(c.points[0].x, c.points[0].y);

    for (size_t i = 0; i < c.points.size() - 1; ) {
      const auto& p = c.points[i];
      const auto& pt = c.tags[i];

      // line segment
      if (i + 1 < c.points.size() &&
          c.tags[i + 0] == GlyphPointType::ON &&
          c.tags[i + 1] == GlyphPointType::ON) {
        path->lineTo(c.points[i + 1].x, c.points[i + 1].y);
        i += 1;
        continue;
      }

      // third order bezier
    if (i + 2 < glyph_points.size() &&
        glyph_point_tags[i + 0] == GlyphPointType::CONTROL &&
        glyph_point_tags[i + 1] == GlyphPointType::CONTROL) {
      //assert(glyph_point_tags[i + 2] != GlyphPointType::CONTROL);

      if (i + 3 < c.points.size() &&
          c.tags[i + 0] == GlyphPointType::ON &&
          c.tags[i + 1] == GlyphPointType::OFF3 &&
          c.tags[i + 2] == GlyphPointType::OFF3 &&
          c.tags[i + 3] == GlyphPointType::ON) {
        path->cubicCurveTo(
          glyph_points[i + 0].x,
          glyph_points[i + 0].y,
          glyph_points[i + 1].x,
          glyph_points[i + 1].y,
          glyph_points[i + 2].x,
          glyph_points[i + 2].y);

      i += 2;
            c.points[i + 1].x,
            c.points[i + 1].y,
            c.points[i + 2].x,
            c.points[i + 2].y,
            c.points[i + 3].x,
            c.points[i + 3].y);

        i += 3;
        continue;
      }

      // second order bezier
    if (i + 1 < glyph_points.size() &&
        glyph_point_tags[i] == GlyphPointType::CONTROL) {
      //assert(glyph_point_tags[i + 1] != GlyphPointType::CONTROL);

      if (i + 2 < c.points.size() &&
          c.tags[i + 0] == GlyphPointType::ON &&
          c.tags[i + 1] == GlyphPointType::OFF2 &&
          c.tags[i + 2] == GlyphPointType::ON) {
        path->quadraticCurveTo(
          glyph_points[i + 0].x,
          glyph_points[i + 0].y,
          glyph_points[i + 1].x,
          glyph_points[i + 1].y);
            c.points[i + 1].x,
            c.points[i + 1].y,
            c.points[i + 2].x,
            c.points[i + 2].y);

      i += 1;
        i += 2;
        continue;
      }

    // simple line segments
    path->lineTo(glyph_points[i].x, glyph_points[i].y);
      // invalid contour
      break;
    }

    // close the path
    path->closePath();
  }

  return OK;
+2 −0
Original line number Diff line number Diff line
@@ -95,6 +95,8 @@ std::string svg_path_data(const Path& path) {
        path_data << fmt::format("a{} {} 0 1 0 {} 0 ", cmd[2], cmd[2], cmd[2] * 2);
        path_data << fmt::format("a{} {} 0 1 0 {} 0 ", cmd[2], cmd[2], -cmd[2] * 2);
        break;
      case PathCommand::CLOSE:
        path_data << "Z";
      default:
        break; // not yet implemented
    }
+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@
  <path stroke-width="1.333333" stroke="#ffffff" fill="none" d="M82.4313 98.8533 L885.333 98.8533 "/>
  <path stroke-width="1.333333" stroke="#ffffff" fill="none" d="M82.4313 56.76 L885.333 56.76 "/>
  <path stroke-width="1.333333" stroke="#ffffff" fill="none" d="M82.4313 14.6667 L885.333 14.6667 "/>
  <path fill="#888888" d="M82.4313 14.6667 L91.9896 104.307 L103.191 159.51 L114.392 155.097 L125.593 199.672 L136.794 227.759 L147.996 246.483 L159.197 257.954 L170.398 287.32 L181.599 235.225 L192.8 251.627 L204.002 301.903 L215.203 237.689 L226.404 245.53 L237.605 295.14 L248.806 288.447 L260.008 321.532 L271.209 344.178 L282.41 378.76 L293.611 391.594 L304.812 409.464 L316.014 270.37 L327.215 166.698 L338.416 138.629 L349.617 163.433 L360.818 220.766 L372.02 300.263 L383.221 293.933 L394.422 341.925 L405.623 358.148 L416.824 380.958 L428.026 360.28 L439.227 385.504 L450.428 337.171 L461.629 297.629 L472.83 379.852 L484.032 390.309 L495.233 403.484 L506.434 435.6 L517.635 369.012 L528.836 339.658 L540.038 342.725 L551.239 358.99 L562.44 315.349 L573.641 383.753 L584.842 337.315 L596.044 318.447 L607.245 320.517 L618.446 345.424 L629.647 294.066 L640.848 285.576 L652.05 252.368 L663.251 294.032 L674.452 335.871 L685.653 369.075 L696.854 305.107 L708.056 335.233 L719.257 328.793 L730.458 386.069 L741.659 402.573 L752.86 385.054 L764.062 398.351 L775.263 384.675 L786.464 361.94 L797.665 369.359 L808.866 354.243 L820.068 331.44 L831.269 344.088 L842.47 364.684 L853.671 289.797 L864.872 279.774 L876.074 239.133 L885.333 243.345 L885.333 435.6 L876.074 435.6 L864.872 435.6 L853.671 435.6 L842.47 435.6 L831.269 435.6 L820.068 435.6 L808.866 435.6 L797.665 435.6 L786.464 435.6 L775.263 435.6 L764.062 435.6 L752.86 435.6 L741.659 435.6 L730.458 435.6 L719.257 435.6 L708.056 435.6 L696.854 435.6 L685.653 435.6 L674.452 435.6 L663.251 435.6 L652.05 435.6 L640.848 435.6 L629.647 435.6 L618.446 435.6 L607.245 435.6 L596.044 435.6 L584.842 435.6 L573.641 435.6 L562.44 435.6 L551.239 435.6 L540.038 435.6 L528.836 435.6 L517.635 435.6 L506.434 435.6 L495.233 435.6 L484.032 435.6 L472.83 435.6 L461.629 435.6 L450.428 435.6 L439.227 435.6 L428.026 435.6 L416.824 435.6 L405.623 435.6 L394.422 435.6 L383.221 435.6 L372.02 435.6 L360.818 435.6 L349.617 435.6 L338.416 435.6 L327.215 435.6 L316.014 435.6 L304.812 435.6 L293.611 435.6 L282.41 435.6 L271.209 435.6 L260.008 435.6 L248.806 435.6 L237.605 435.6 L226.404 435.6 L215.203 435.6 L204.002 435.6 L192.8 435.6 L181.599 435.6 L170.398 435.6 L159.197 435.6 L147.996 435.6 L136.794 435.6 L125.593 435.6 L114.392 435.6 L103.191 435.6 L91.9896 435.6 L82.4313 435.6 "/>
  <path fill="#888888" d="M82.4313 14.6667 L91.9896 104.307 L103.191 159.51 L114.392 155.097 L125.593 199.672 L136.794 227.759 L147.996 246.483 L159.197 257.954 L170.398 287.32 L181.599 235.225 L192.8 251.627 L204.002 301.903 L215.203 237.689 L226.404 245.53 L237.605 295.14 L248.806 288.447 L260.008 321.532 L271.209 344.178 L282.41 378.76 L293.611 391.594 L304.812 409.464 L316.014 270.37 L327.215 166.698 L338.416 138.629 L349.617 163.433 L360.818 220.766 L372.02 300.263 L383.221 293.933 L394.422 341.925 L405.623 358.148 L416.824 380.958 L428.026 360.28 L439.227 385.504 L450.428 337.171 L461.629 297.629 L472.83 379.852 L484.032 390.309 L495.233 403.484 L506.434 435.6 L517.635 369.012 L528.836 339.658 L540.038 342.725 L551.239 358.99 L562.44 315.349 L573.641 383.753 L584.842 337.315 L596.044 318.447 L607.245 320.517 L618.446 345.424 L629.647 294.066 L640.848 285.576 L652.05 252.368 L663.251 294.032 L674.452 335.871 L685.653 369.075 L696.854 305.107 L708.056 335.233 L719.257 328.793 L730.458 386.069 L741.659 402.573 L752.86 385.054 L764.062 398.351 L775.263 384.675 L786.464 361.94 L797.665 369.359 L808.866 354.243 L820.068 331.44 L831.269 344.088 L842.47 364.684 L853.671 289.797 L864.872 279.774 L876.074 239.133 L885.333 243.345 L885.333 435.6 L876.074 435.6 L864.872 435.6 L853.671 435.6 L842.47 435.6 L831.269 435.6 L820.068 435.6 L808.866 435.6 L797.665 435.6 L786.464 435.6 L775.263 435.6 L764.062 435.6 L752.86 435.6 L741.659 435.6 L730.458 435.6 L719.257 435.6 L708.056 435.6 L696.854 435.6 L685.653 435.6 L674.452 435.6 L663.251 435.6 L652.05 435.6 L640.848 435.6 L629.647 435.6 L618.446 435.6 L607.245 435.6 L596.044 435.6 L584.842 435.6 L573.641 435.6 L562.44 435.6 L551.239 435.6 L540.038 435.6 L528.836 435.6 L517.635 435.6 L506.434 435.6 L495.233 435.6 L484.032 435.6 L472.83 435.6 L461.629 435.6 L450.428 435.6 L439.227 435.6 L428.026 435.6 L416.824 435.6 L405.623 435.6 L394.422 435.6 L383.221 435.6 L372.02 435.6 L360.818 435.6 L349.617 435.6 L338.416 435.6 L327.215 435.6 L316.014 435.6 L304.812 435.6 L293.611 435.6 L282.41 435.6 L271.209 435.6 L260.008 435.6 L248.806 435.6 L237.605 435.6 L226.404 435.6 L215.203 435.6 L204.002 435.6 L192.8 435.6 L181.599 435.6 L170.398 435.6 L159.197 435.6 L147.996 435.6 L136.794 435.6 L125.593 435.6 L114.392 435.6 L103.191 435.6 L91.9896 435.6 L82.4313 435.6 Z"/>
  <path stroke-width="0.000000" stroke="#000000" fill="none" d="M82.4313 435.6 L885.333 435.6 "/>
  <path stroke-width="0.000000" stroke="#000000" fill="none" d="M82.4313 430.267 L82.4313 435.6 "/>
  <path stroke-width="0.000000" stroke="#000000" fill="none" d="M162.721 430.267 L162.721 435.6 "/>
+2 −2
Original line number Diff line number Diff line
<svg xmlns="http://www.w3.org/2000/svg" width="1200.000000" height="480.000000" viewBox="0 0 1200 480">
  <rect width="1200.000000" height="480.000000" fill="#ffffff"/>
  <path fill="#cccccc" d="M14.6667 106.992 L161 91.344 L307.333 71.784 L600 110.904 L892.667 157.848 L1185.33 110.904 L1185.33 365.184 L892.667 310.416 L600 333.888 L307.333 318.24 L161 357.36 L14.6667 373.008 "/>
  <path fill="#888888" d="M14.6667 118.728 L161 110.904 L307.333 91.344 L600 134.376 L892.667 189.144 L1185.33 122.64 L1185.33 310.416 L892.667 267.384 L600 318.24 L307.333 298.68 L161 318.24 L14.6667 333.888 "/>
  <path fill="#cccccc" d="M14.6667 106.992 L161 91.344 L307.333 71.784 L600 110.904 L892.667 157.848 L1185.33 110.904 L1185.33 365.184 L892.667 310.416 L600 333.888 L307.333 318.24 L161 357.36 L14.6667 373.008 Z"/>
  <path fill="#888888" d="M14.6667 118.728 L161 110.904 L307.333 91.344 L600 134.376 L892.667 189.144 L1185.33 122.64 L1185.33 310.416 L892.667 267.384 L600 318.24 L307.333 298.68 L161 318.24 L14.6667 333.888 Z"/>
  <path stroke-width="2.000000" stroke="#000000" fill="none" d="M14.6667 235.188 L307.333 199.628 L600 264.372 L892.667 222.67 L1185.33 226.504 "/>
  <path fill="#000000" d="M18.6667 235.188 M10.6667 235.188 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0 "/>
  <path fill="#000000" d="M311.333 199.628 M303.333 199.628 a4 4 0 1 0 8 0 a4 4 0 1 0 -8 0 "/>
+1 −1
Original line number Diff line number Diff line
<svg xmlns="http://www.w3.org/2000/svg" width="1000.000000" height="400.000000" viewBox="0 0 1000 400">
  <rect width="1000.000000" height="400.000000" fill="#ffffff"/>
  <path fill="#999999" d="M272.819 355.6 L257.048 351.541 L130.528 346.785 L136.255 342.029 L162.912 337.272 L102.167 332.516 L97.7443 327.76 L136.136 323.003 L172.208 318.247 L99.0765 313.491 L143.42 308.734 L126.604 303.978 L223.024 299.222 L192.292 294.465 L151.526 289.709 L198.682 284.953 L143.75 280.196 L120.349 275.44 L101.095 270.684 L100.33 265.927 L100.426 261.171 L188.453 256.415 L524.335 251.658 L780.184 246.902 L768.131 242.146 L746.164 237.389 L447.472 232.633 L367.714 227.877 L217.589 223.121 L139.735 218.364 L112.308 213.608 L119.512 208.852 L110.747 204.095 L119.46 199.339 L205.966 194.583 L213.535 189.826 L216.603 185.07 L250.343 180.314 L234.833 175.557 L208.222 170.801 L220.834 166.045 L263.919 161.288 L330.576 156.532 L323.026 151.776 L366.857 147.019 L297.723 142.263 L266.88 137.507 L195.479 132.75 L232.609 127.994 L355.617 123.238 L379.237 118.481 L320.028 113.725 L307.76 108.969 L324.691 104.212 L356.097 99.4561 L231.461 94.6998 L190.994 89.9434 L302.373 85.1871 L229.904 80.4308 L149.036 75.6745 L157.481 70.9181 L241.289 66.1618 L179.013 61.4055 L172.367 56.6492 L159.939 51.8928 L173.102 47.1365 L160.66 42.3802 L207.226 37.6239 L166.436 32.8675 L191.902 28.1112 L217.425 23.3549 L194.823 18.5986 L149.539 14.6667 L83.4938 14.6667 L83.4938 18.5986 L83.4938 23.3549 L83.4938 28.1112 L83.4938 32.8675 L83.4938 37.6239 L83.4938 42.3802 L83.4938 47.1365 L83.4938 51.8928 L83.4938 56.6492 L83.4938 61.4055 L83.4938 66.1618 L83.4938 70.9181 L83.4938 75.6745 L83.4938 80.4308 L83.4938 85.1871 L83.4938 89.9434 L83.4938 94.6998 L83.4938 99.4561 L83.4938 104.212 L83.4938 108.969 L83.4938 113.725 L83.4938 118.481 L83.4938 123.238 L83.4938 127.994 L83.4938 132.75 L83.4938 137.507 L83.4938 142.263 L83.4938 147.019 L83.4938 151.776 L83.4938 156.532 L83.4938 161.288 L83.4938 166.045 L83.4938 170.801 L83.4938 175.557 L83.4938 180.314 L83.4938 185.07 L83.4938 189.826 L83.4938 194.583 L83.4938 199.339 L83.4938 204.095 L83.4938 208.852 L83.4938 213.608 L83.4938 218.364 L83.4938 223.121 L83.4938 227.877 L83.4938 232.633 L83.4938 237.389 L83.4938 242.146 L83.4938 246.902 L83.4938 251.658 L83.4938 256.415 L83.4938 261.171 L83.4938 265.927 L83.4938 270.684 L83.4938 275.44 L83.4938 280.196 L83.4938 284.953 L83.4938 289.709 L83.4938 294.465 L83.4938 299.222 L83.4938 303.978 L83.4938 308.734 L83.4938 313.491 L83.4938 318.247 L83.4938 323.003 L83.4938 327.76 L83.4938 332.516 L83.4938 337.272 L83.4938 342.029 L83.4938 346.785 L83.4938 351.541 L83.4938 355.6 "/>
  <path fill="#999999" d="M272.819 355.6 L257.048 351.541 L130.528 346.785 L136.255 342.029 L162.912 337.272 L102.167 332.516 L97.7443 327.76 L136.136 323.003 L172.208 318.247 L99.0765 313.491 L143.42 308.734 L126.604 303.978 L223.024 299.222 L192.292 294.465 L151.526 289.709 L198.682 284.953 L143.75 280.196 L120.349 275.44 L101.095 270.684 L100.33 265.927 L100.426 261.171 L188.453 256.415 L524.335 251.658 L780.184 246.902 L768.131 242.146 L746.164 237.389 L447.472 232.633 L367.714 227.877 L217.589 223.121 L139.735 218.364 L112.308 213.608 L119.512 208.852 L110.747 204.095 L119.46 199.339 L205.966 194.583 L213.535 189.826 L216.603 185.07 L250.343 180.314 L234.833 175.557 L208.222 170.801 L220.834 166.045 L263.919 161.288 L330.576 156.532 L323.026 151.776 L366.857 147.019 L297.723 142.263 L266.88 137.507 L195.479 132.75 L232.609 127.994 L355.617 123.238 L379.237 118.481 L320.028 113.725 L307.76 108.969 L324.691 104.212 L356.097 99.4561 L231.461 94.6998 L190.994 89.9434 L302.373 85.1871 L229.904 80.4308 L149.036 75.6745 L157.481 70.9181 L241.289 66.1618 L179.013 61.4055 L172.367 56.6492 L159.939 51.8928 L173.102 47.1365 L160.66 42.3802 L207.226 37.6239 L166.436 32.8675 L191.902 28.1112 L217.425 23.3549 L194.823 18.5986 L149.539 14.6667 L83.4938 14.6667 L83.4938 18.5986 L83.4938 23.3549 L83.4938 28.1112 L83.4938 32.8675 L83.4938 37.6239 L83.4938 42.3802 L83.4938 47.1365 L83.4938 51.8928 L83.4938 56.6492 L83.4938 61.4055 L83.4938 66.1618 L83.4938 70.9181 L83.4938 75.6745 L83.4938 80.4308 L83.4938 85.1871 L83.4938 89.9434 L83.4938 94.6998 L83.4938 99.4561 L83.4938 104.212 L83.4938 108.969 L83.4938 113.725 L83.4938 118.481 L83.4938 123.238 L83.4938 127.994 L83.4938 132.75 L83.4938 137.507 L83.4938 142.263 L83.4938 147.019 L83.4938 151.776 L83.4938 156.532 L83.4938 161.288 L83.4938 166.045 L83.4938 170.801 L83.4938 175.557 L83.4938 180.314 L83.4938 185.07 L83.4938 189.826 L83.4938 194.583 L83.4938 199.339 L83.4938 204.095 L83.4938 208.852 L83.4938 213.608 L83.4938 218.364 L83.4938 223.121 L83.4938 227.877 L83.4938 232.633 L83.4938 237.389 L83.4938 242.146 L83.4938 246.902 L83.4938 251.658 L83.4938 256.415 L83.4938 261.171 L83.4938 265.927 L83.4938 270.684 L83.4938 275.44 L83.4938 280.196 L83.4938 284.953 L83.4938 289.709 L83.4938 294.465 L83.4938 299.222 L83.4938 303.978 L83.4938 308.734 L83.4938 313.491 L83.4938 318.247 L83.4938 323.003 L83.4938 327.76 L83.4938 332.516 L83.4938 337.272 L83.4938 342.029 L83.4938 346.785 L83.4938 351.541 L83.4938 355.6 Z"/>
  <path stroke-width="1.333333" stroke="#000000" fill="none" d="M83.4938 355.6 L985.333 355.6 "/>
  <path stroke-width="1.333333" stroke="#000000" fill="none" d="M83.4938 350.267 L83.4938 355.6 "/>
  <path stroke-width="1.333333" stroke="#000000" fill="none" d="M173.678 350.267 L173.678 355.6 "/>
Loading