Commit df003d3e authored by Paul Asmuth's avatar Paul Asmuth
Browse files

improved SVG output formatting

- Set the output precision of all numbers to three digits after
  the decimal point, except for integers, which are formatted as
  integers

- Always use {fill, stroke}-opacity instead of #RGBA hex codes

- Remove the redundant fill-opacity=1, stroke-opactity=1 and
  fill=black attributes
parent 595544ae
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -146,5 +146,9 @@ std::ostream& operator <<(std::ostream& os, const Color& c) {
  return os;
}

bool color_is_black(const Color& c) {
  return c[0] == 0 && c[1] == 0 && c[2] == 0;
}

} // namespace clip
+2 −0
Original line number Diff line number Diff line
@@ -54,5 +54,7 @@ protected:

std::ostream& operator <<(std::ostream& os, const Color& c);

bool color_is_black(const Color& c);

} // namespace clip
+86 −29
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
#include "graphics/draw_cmd.h"
#include "export_svg.h"

#include <sstream>
#include <iomanip>

using std::bind;
using namespace std::placeholders;

@@ -31,6 +34,21 @@ struct SVGData {

using SVGDataRef = std::shared_ptr<SVGData>;

std::string svg_number(double num) {
  double num_int;
  if (std::modf(num, &num_int) == 0) {
    return std::to_string(int(num_int));
  }

  std::stringstream buf;
  buf
    << std::fixed
    << std::setprecision(3)
    << num;

  return buf.str();
}

std::string svg_attr(const std::string& name, const std::string& val) {
  std::string buf = " ";

@@ -52,8 +70,24 @@ std::string svg_attr(const std::string& name, const std::string& val) {
  return buf;
}

std::string svg_attr(const std::string& name, const std::string& val, double pred) {
  if (pred) {
    return svg_attr(name, val);
  } else {
    return {};
  }
}

std::string svg_attr(const std::string& name, double val) {
  return svg_attr(name, std::to_string(val));
  return svg_attr(name, svg_number(val));
}

std::string svg_attr(const std::string& name, double val, bool pred) {
  if (pred) {
    return svg_attr(name, svg_number(val));
  } else {
    return {};
  }
}

std::string svg_body(const std::string& in) {
@@ -80,31 +114,42 @@ std::string svg_body(const std::string& in) {

std::string svg_path_data(const Path& path) {
  std::stringstream path_data;
  for (const auto& cmd : path) {
  for (size_t i = 0; i < path.size(); ++i) {
    if (i > 0) {
      path_data << " ";
    }

    const auto& cmd = path[i];
    switch (cmd.command) {
      case PathCommand::MOVE_TO:
        path_data << fmt::format("M{} {} ", cmd[0], cmd[1]);
        path_data << fmt::format(
            "M{} {}",
            svg_number(cmd[0]),
            svg_number(cmd[1]));
        break;
      case PathCommand::LINE_TO:
        path_data << fmt::format("L{} {} ", cmd[0], cmd[1]);
        path_data << fmt::format(
            "L{} {}",
            svg_number(cmd[0]),
            svg_number(cmd[1]));
        break;
      case PathCommand::QUADRATIC_CURVE_TO:
        path_data << fmt::format(
            "Q{} {} {} {}",
            cmd[0],
            cmd[1],
            cmd[2],
            cmd[3]);
            svg_number(cmd[0]),
            svg_number(cmd[1]),
            svg_number(cmd[2]),
            svg_number(cmd[3]));
        break;
      case PathCommand::CUBIC_CURVE_TO:
        path_data << fmt::format(
            "C{} {} {} {} {} {}",
            cmd[0],
            cmd[1],
            cmd[2],
            cmd[3],
            cmd[4],
            cmd[5]);
            svg_number(cmd[0]),
            svg_number(cmd[1]),
            svg_number(cmd[2]),
            svg_number(cmd[3]),
            svg_number(cmd[4]),
            svg_number(cmd[5]));
        break;
      case PathCommand::CLOSE:
        path_data << "Z";
@@ -121,7 +166,9 @@ std::string svg_poly_data(const Polygon2& poly) {

  std::stringstream poly_data;
  for (const auto& v : poly.vertices) {
    poly_data << fmt::format("{} {} ", v.x, v.y);
    poly_data << svg_number(v.x);
    poly_data << " ";
    poly_data << svg_number(v.y);
  }

  return poly_data.str();
@@ -146,10 +193,11 @@ Status svg_add_path_compound(
      << "  "
      << "<path"
      << svg_attr("d", svg_path_data(path_transform(path, svg->proj)))
      << svg_attr("fill", fill_style.color.to_hex_str())
      << svg_attr("fill-opacity", fill_style.color.component(3))
      << svg_attr("fill", fill_style.color.to_hex_str(), !color_is_black(fill_style.color))
      << svg_attr("fill-opacity", fill_style.color.component(3), fill_style.color.component(3) != 1)
      << svg_attr("stroke", stroke_style.color.to_hex_str())
      << svg_attr("stroke-opacity", stroke_style.color.component(3), stroke_style.color.component(3) != 1)
      << svg_attr("stroke-width", stroke_style.width.value)
      << svg_attr("stroke", stroke_style.color.to_hex_str(4))
      << svg_antialiasing_attrs(antialiasing_mode)
      << "/>"
      << "\n";
@@ -166,8 +214,8 @@ Status svg_add_path_fill_solid(
      << "  "
      << "<path"
      << svg_attr("d", svg_path_data(path_transform(path, svg->proj)))
      << svg_attr("fill", style.color.to_hex_str())
      << svg_attr("fill-opacity", style.color.component(3))
      << svg_attr("fill", style.color.to_hex_str(), !color_is_black(style.color))
      << svg_attr("fill-opacity", style.color.component(3), style.color.component(3) != 1)
      << svg_antialiasing_attrs(antialiasing_mode)
      << "/>"
      << "\n";
@@ -191,7 +239,8 @@ Status svg_add_path_fill_hatch(
      << "  "
      << "<path"
      << svg_attr("d", svg_path_data(path_transform(hatched, svg->proj)))
      << svg_attr("fill", style.color.to_hex_str(4))
      << svg_attr("fill", style.color.to_hex_str(), !color_is_black(style.color))
      << svg_attr("fill-opacity", style.color.component(3), style.color.component(3) != 1)
      << svg_antialiasing_attrs(antialiasing_mode)
      << "/>"
      << "\n";
@@ -209,8 +258,9 @@ Status svg_add_path_stroke_solid(
      << "<path"
      << svg_attr("d", svg_path_data(path_transform(path, svg->proj)))
      << svg_attr("fill", "none")
      << svg_attr("stroke", style.color.to_hex_str())
      << svg_attr("stroke-opacity", style.color.component(3), style.color.component(3) != 1)
      << svg_attr("stroke-width", style.width.value)
      << svg_attr("stroke", style.color.to_hex_str(4))
      << svg_antialiasing_attrs(antialiasing_mode)
      << "/>"
      << "\n";
@@ -225,7 +275,11 @@ Status svg_add_path_stroke_dash(
    SVGDataRef svg) {
  std::string svg_dash_pattern;
  for (const auto& v : style.pattern) {
    svg_dash_pattern += fmt::format("{} ", v.value);
    if (!svg_dash_pattern.empty()) {
      svg_dash_pattern += " ";
    }

    svg_dash_pattern += svg_number(v.value);
  }

  svg->buffer
@@ -233,8 +287,9 @@ Status svg_add_path_stroke_dash(
      << "<path"
      << svg_attr("d", svg_path_data(path_transform(path, svg->proj)))
      << svg_attr("fill", "none")
      << svg_attr("stroke", style.color.to_hex_str())
      << svg_attr("stroke-opacity", style.color.component(3), style.color.component(3) != 1)
      << svg_attr("stroke-width", style.width.value)
      << svg_attr("stroke", style.color.to_hex_str(4))
      << svg_attr("stroke-dasharray", svg_dash_pattern)
      << svg_attr("stroke-dashoffset", style.offset.value)
      << svg_antialiasing_attrs(antialiasing_mode)
@@ -332,7 +387,8 @@ Status svg_add_text_elem_native(
    << "<text"
    << svg_attr("x", origin.x)
    << svg_attr("y", origin.y)
    << svg_attr("fill", style.color.to_hex_str(4))
    << svg_attr("fill", style.color.to_hex_str(), !color_is_black(style.color))
    << svg_attr("fill-opacity", style.color.component(3), style.color.component(3) != 1)
    << svg_attr("font-size", style.font_size.value)
    << svg_attr("font-family", style.font.font_family_css)
    << svg_attr("font-weight", style.font.font_weight_css)
@@ -375,7 +431,8 @@ Status svg_add_text_elem_embed(
      svg->buffer
          << "  "
          << "<path"
          << svg_attr("fill", style.color.to_hex_str(4))
          << svg_attr("fill", style.color.to_hex_str(), !color_is_black(style.color))
          << svg_attr("fill-opacity", style.color.component(3), style.color.component(3) != 1)
          << svg_attr("d", svg_path_data(path_transform(gp, gt)))
          << "/>"
          << "\n";
@@ -425,7 +482,7 @@ ReturnCode export_svg(
      << svg_attr("fill-opacity", layer->background_color.component(3))
      << "/>\n"
    << svg->buffer.str()
    << "</svg>";
    << "</svg>\n";

  *buffer = svg_doc.str();
  return OK;
+4 −4
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Generated by clip v0.7.0 (clip-lang.org) -->
<svg xmlns="http://www.w3.org/2000/svg" width="100.000000" height="100.000000">
  <rect width="100.000000" height="100.000000" fill="#ffffff" fill-opacity="1.000000"/>
  <path d="M75 25 L75 75 L25 75 L25 25 Z" fill="#000000" fill-opacity="1.000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <rect width="100" height="100" fill="#ffffff" fill-opacity="1"/>
  <path d="M75 25 L75 75 L25 75 L25 25 Z"/>
</svg>
+4 −4
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Generated by clip v0.7.0 (clip-lang.org) -->
<svg xmlns="http://www.w3.org/2000/svg" width="100.000000" height="100.000000">
  <rect width="100.000000" height="100.000000" fill="#ffffff" fill-opacity="1.000000"/>
  <path d="M73.1144 25 L75 26.8856 L26.8856 75 L25 73.1144 M61.8007 25 L65.5719 25 L25 65.5719 L25 61.8007 M75 34.4281 L75 38.1993 L38.1993 75 L34.4281 75 M50.487 25 L54.2582 25 L25 54.2582 L25 50.487 M75 45.7418 L75 49.513 L49.513 75 L45.7418 75 M39.1733 25 L42.9445 25 L25 42.9445 L25 39.1733 M75 57.0555 L75 60.8267 L60.8267 75 L57.0555 75 M27.8595 25 L31.6308 25 L25 31.6308 L25 27.8595 M75 68.3692 L75 72.1405 L72.1405 75 L68.3692 75 " fill="#000000ff"/>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <rect width="100" height="100" fill="#ffffff" fill-opacity="1"/>
  <path d="M73.114 25 L75 26.886 L26.886 75 L25 73.114 M61.801 25 L65.572 25 L25 65.572 L25 61.801 M75 34.428 L75 38.199 L38.199 75 L34.428 75 M50.487 25 L54.258 25 L25 54.258 L25 50.487 M75 45.742 L75 49.513 L49.513 75 L45.742 75 M39.173 25 L42.944 25 L25 42.944 L25 39.173 M75 57.056 L75 60.827 L60.827 75 L57.056 75 M27.860 25 L31.631 25 L25 31.631 L25 27.860 M75 68.369 L75 72.140 L72.140 75 L68.369 75"/>
</svg>
Loading