summaryrefslogtreecommitdiffstats
path: root/src/object/sp-polygon.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/object/sp-polygon.cpp')
-rw-r--r--src/object/sp-polygon.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/object/sp-polygon.cpp b/src/object/sp-polygon.cpp
new file mode 100644
index 0000000..3f07bbc
--- /dev/null
+++ b/src/object/sp-polygon.cpp
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * SVG <polygon> implementation
+ *
+ * Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "attributes.h"
+#include "sp-polygon.h"
+#include "display/curve.h"
+#include <glibmm/i18n.h>
+#include <2geom/curves.h>
+#include "helper/geom-curves.h"
+#include "svg/stringstream.h"
+#include "xml/repr.h"
+#include "document.h"
+
+SPPolygon::SPPolygon() : SPShape() {
+}
+
+SPPolygon::~SPPolygon() = default;
+
+void SPPolygon::build(SPDocument *document, Inkscape::XML::Node *repr) {
+ SPPolygon* object = this;
+
+ SPShape::build(document, repr);
+
+ object->readAttr(SPAttr::POINTS);
+}
+
+/*
+ * sp_svg_write_polygon: Write points attribute for polygon tag.
+ * pathv may only contain paths with only straight line segments
+ * Return value: points attribute string.
+ */
+static gchar *sp_svg_write_polygon(Geom::PathVector const & pathv)
+{
+ Inkscape::SVGOStringStream os;
+
+ for (const auto & pit : pathv) {
+ for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_default(); ++cit) {
+ if ( is_straight_curve(*cit) )
+ {
+ os << cit->finalPoint()[0] << "," << cit->finalPoint()[1] << " ";
+ } else {
+ g_error("sp_svg_write_polygon: polygon path contains non-straight line segments");
+ }
+ }
+ }
+
+ return g_strdup(os.str().c_str());
+}
+
+Inkscape::XML::Node* SPPolygon::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) {
+ // Tolerable workaround: we need to update the object's curve before we set points=
+ // because it's out of sync when e.g. some extension attrs of the polygon or star are changed in XML editor
+ this->set_shape();
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = xml_doc->createElement("svg:polygon");
+ }
+
+ /* We can safely write points here, because all subclasses require it too (Lauris) */
+ /* While saving polygon element without points attribute _curve is NULL (see bug 1202753) */
+ if (this->_curve != nullptr) {
+ gchar *str = sp_svg_write_polygon(this->_curve->get_pathvector());
+ repr->setAttribute("points", str);
+ g_free(str);
+ }
+
+ SPShape::write(xml_doc, repr, flags);
+
+ return repr;
+}
+
+/**
+ * @brief Parse a double from the string passed by pointer and advance the string start.
+ *
+ * @param[in,out] p A pointer to a string (representing a piece of the `points` attribute).
+ * @param[out] v The parsed value.
+ * @return Parse status.
+ */
+SPPolyParseError sp_poly_get_value(char const **p, double *v)
+{
+ while (**p != '\0' && (**p == ',' || **p == '\x20' || **p == '\x9' || **p == '\xD' || **p == '\xA')) {
+ (*p)++;
+ }
+
+ if (**p == '\0') {
+ return POLY_END_OF_STRING;
+ }
+
+ gchar *e = nullptr;
+ double value = g_ascii_strtod(*p, &e);
+ if (e == *p) {
+ return POLY_INVALID_NUMBER;
+ }
+ if (std::isnan(value)) {
+ return POLY_NOT_A_NUMBER;
+ }
+ if (std::isinf(value)) {
+ return POLY_INFINITE_VALUE;
+ }
+
+ *p = e;
+ *v = value;
+ return POLY_OK;
+}
+
+/**
+ * @brief Print a warning message related to the parsing of a 'points' attribute.
+ */
+static void sp_poly_print_warning(char const *points, char const *error_location, SPPolyParseError error)
+{
+ switch (error) {
+ case POLY_END_OF_STRING: // Unexpected end of string!
+ {
+ size_t constexpr MAX_DISPLAY_SIZE = 64;
+ Glib::ustring s{points};
+ if (s.size() > MAX_DISPLAY_SIZE) {
+ s = "... " + s.substr(s.size() - MAX_DISPLAY_SIZE);
+ }
+ g_warning("Error parsing a 'points' attribute: string ended unexpectedly!\n\t\"%s\"", s.c_str());
+ break;
+ }
+ case POLY_INVALID_NUMBER:
+ g_warning("Invalid number in the 'points' attribute:\n\t\"(...) %s\"", error_location);
+ break;
+
+ case POLY_INFINITE_VALUE:
+ g_warning("Infinity is not allowed in the 'points' attribute:\n\t\"(...) %s\"", error_location);
+ break;
+
+ case POLY_NOT_A_NUMBER:
+ g_warning("NaN-value is not allowed in the 'points' attribute:\n\t\"(...) %s\"", error_location);
+ break;
+
+ case POLY_OK:
+ default:
+ break;
+ }
+}
+
+/**
+ * @brief Parse a 'points' attribute, printing a warning when an error occurs.
+ *
+ * @param points The points attribute.
+ * @return The corresponding polyline curve (open).
+ */
+SPCurve sp_poly_parse_curve(char const *points)
+{
+ SPCurve result;
+ char const *cptr = points;
+ bool has_pt = false;
+
+ while (true) {
+ double x, y;
+
+ if (auto error = sp_poly_get_value(&cptr, &x)) {
+ // If the error is something other than end of input, we must report it.
+ // End of input is allowed when scanning for the next x coordinate: it
+ // simply means that we have reached the end of the coordinate list.
+ if (error != POLY_END_OF_STRING) {
+ sp_poly_print_warning(points, cptr, error);
+ }
+ break;
+ }
+ if (auto error = sp_poly_get_value(&cptr, &y)) {
+ // End of input is not allowed when scanning for y.
+ sp_poly_print_warning(points, cptr, error);
+ break;
+ }
+
+ if (has_pt) {
+ result.lineto(x, y);
+ } else {
+ result.moveto(x, y);
+ has_pt = true;
+ }
+ }
+ return result;
+}
+
+void SPPolygon::set(SPAttr key, const gchar* value) {
+ switch (key) {
+ case SPAttr::POINTS: {
+ if (!value) {
+ /* fixme: The points attribute is required. We should handle its absence as per
+ * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing. */
+ break;
+ }
+
+ auto curve = sp_poly_parse_curve(value);
+ curve.closepath();
+ setCurve(std::move(curve));
+ break;
+ }
+ default:
+ SPShape::set(key, value);
+ break;
+ }
+}
+
+const char* SPPolygon::typeName() const {
+ return "path";
+}
+
+gchar* SPPolygon::description() const {
+ return g_strdup(_("<b>Polygon</b>"));
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :