diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
commit | 35a96bde514a8897f6f0fcc41c5833bf63df2e2a (patch) | |
tree | 657d15a03cc46bd099fc2c6546a7a4ad43815d9f /src/svg/svg-affine.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.tar.xz inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.zip |
Adding upstream version 1.0.2.upstream/1.0.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/svg/svg-affine.cpp')
-rw-r--r-- | src/svg/svg-affine.cpp | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/src/svg/svg-affine.cpp b/src/svg/svg-affine.cpp new file mode 100644 index 0000000..e5fcce6 --- /dev/null +++ b/src/svg/svg-affine.cpp @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SVG data parser + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Raph Levien <raph@acm.org> + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 1999 Raph Levien + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <cstring> +#include <string> +#include <cstdlib> +#include <cstdio> +#include <glib.h> +#include <2geom/transforms.h> +#include "svg.h" +#include "preferences.h" + +bool +sp_svg_transform_read(gchar const *str, Geom::Affine *transform) +{ + int idx; + char keyword[32]; + double args[6]; + int n_args; + size_t key_len; + + if (str == nullptr) return false; + + Geom::Affine a(Geom::identity()); + + idx = 0; + while (str[idx]) { + /* skip initial whitespace */ + while (g_ascii_isspace (str[idx])) idx++; + + // SVG2: allow commas in separation of transforms + if (str[idx] == ',') { + ++idx; + while (g_ascii_isspace(str[idx])) + ++idx; + } + + /* parse keyword */ + for (key_len = 0; key_len < sizeof (keyword); key_len++) { + char c; + + c = str[idx]; + if (g_ascii_isalpha (c) || c == '-') { + keyword[key_len] = str[idx++]; + } else { + break; + } + } + if (key_len >= sizeof (keyword)) return false; + keyword[key_len] = '\0'; + + /* skip whitespace */ + while (g_ascii_isspace (str[idx])) idx++; + + if (str[idx] != '(') return false; + idx++; + + for (n_args = 0; n_args < 6; n_args++) { + char c; + char *end_ptr; + + /* skip whitespace */ + while (g_ascii_isspace (str[idx])) idx++; + c = str[idx]; + if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') { + if (n_args == sizeof (args) / sizeof (args[0])) return false; /* Too many args */ + args[n_args] = g_ascii_strtod (str + idx, &end_ptr); + + //printf("took %d chars from '%s' to make %f\n", + // end_ptr-(str+idx), + // str+idx, + // args[n_args]); + + idx = end_ptr - (char *) str; + + while (g_ascii_isspace (str[idx])) idx++; + + /* skip optional comma */ + if (str[idx] == ',') idx++; + } else if (c == ')') { + break; + } else { + return false; + } + } + idx++; + + /* ok, have parsed keyword and args, now modify the transform */ + if (!strcmp (keyword, "matrix")) { + if (n_args != 6) return false; + a = (*((Geom::Affine *) &(args)[0])) * a; + } else if (!strcmp (keyword, "translate")) { + if (n_args == 1) { + args[1] = 0; + } else if (n_args != 2) { + return false; + } + a = Geom::Translate(args[0], args[1]) * a; + } else if (!strcmp (keyword, "scale")) { + if (n_args == 1) { + args[1] = args[0]; + } else if (n_args != 2) { + return false; + } + a = Geom::Scale(args[0], args[1]) * a; + } else if (!strcmp (keyword, "rotate")) { + if (n_args != 1 && n_args != 3) { + return false; + } + Geom::Rotate const rot(Geom::rad_from_deg(args[0])); + if (n_args == 3) { + a = ( Geom::Translate(-args[1], -args[2]) + * rot + * Geom::Translate(args[1], args[2]) + * Geom::Affine(a) ); + } else { + a = rot * a; + } + } else if (!strcmp (keyword, "skewX")) { + if (n_args != 1) return false; + a = ( Geom::Affine(1, 0, + tan(args[0] * M_PI / 180.0), 1, + 0, 0) + * a ); + } else if (!strcmp (keyword, "skewY")) { + if (n_args != 1) return false; + a = ( Geom::Affine(1, tan(args[0] * M_PI / 180.0), + 0, 1, + 0, 0) + * a ); + } else { + return false; /* unknown keyword */ + } + /* Skip trailing whitespace */ + while (g_ascii_isspace (str[idx])) idx++; + } + + *transform = a; + return true; +} + +#define EQ(a,b) (fabs ((a) - (b)) < 1e-9) + +gchar * +sp_svg_transform_write(Geom::Affine const &transform) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + // this must be a bit grater than EPSILON + double e = 1e-5 * transform.descrim(); + int prec = prefs->getInt("/options/svgoutput/numericprecision", 8); + int min_exp = prefs->getInt("/options/svgoutput/minimumexponent", -8); + + // Special case: when all fields of the affine are zero, + // the optimized transformation is scale(0) + if (transform[0] == 0 && transform[1] == 0 && transform[2] == 0 && + transform[3] == 0 && transform[4] == 0 && transform[5] == 0) + { + return g_strdup("scale(0)"); + } + + // FIXME legacy C code! + // the function sp_svg_number_write_de is stopping me from using a proper C++ string + + gchar c[256]; // string buffer + unsigned p = 0; // position in the buffer + + if (transform.isIdentity()) { + // We are more or less identity, so no transform attribute needed: + return nullptr; + } else if (transform.isScale()) { + // We are more or less a uniform scale + strcpy (c + p, "scale("); + p += 6; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[0], prec, min_exp ); + if (Geom::are_near(transform[0], transform[3], e)) { + c[p++] = ')'; + c[p] = '\000'; + } else { + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[3], prec, min_exp ); + c[p++] = ')'; + c[p] = '\000'; + } + } else if (transform.isTranslation()) { + // We are more or less a pure translation + strcpy (c + p, "translate("); + p += 10; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[4], prec, min_exp ); + if (Geom::are_near(transform[5], 0.0, e)) { + c[p++] = ')'; + c[p] = '\000'; + } else { + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[5], prec, min_exp ); + c[p++] = ')'; + c[p] = '\000'; + } + } else if (transform.isRotation()) { + // We are more or less a pure rotation + strcpy(c + p, "rotate("); + p += 7; + + double angle = std::atan2(transform[1], transform[0]) * (180 / M_PI); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, angle, prec, min_exp); + + c[p++] = ')'; + c[p] = '\000'; + } else if (transform.withoutTranslation().isRotation()) { + // Solution found by Johan Engelen + // Refer to the matrix in svg-affine-test.h + + // We are a rotation about a special axis + strcpy(c + p, "rotate("); + p += 7; + + double angle = std::atan2(transform[1], transform[0]) * (180 / M_PI); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, angle, prec, min_exp); + c[p++] = ','; + + Geom::Affine const& m = transform; + double tx = (m[2]*m[5]+m[4]-m[4]*m[3]) / (1-m[3]-m[0]+m[0]*m[3]-m[2]*m[1]); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, tx, prec, min_exp); + + c[p++] = ','; + + double ty = (m[1]*tx + m[5]) / (1 - m[3]); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, ty, prec, min_exp); + + c[p++] = ')'; + c[p] = '\000'; + } else if (transform.isHShear()) { + // We are more or less a pure skewX + strcpy(c + p, "skewX("); + p += 6; + + double angle = atan(transform[2]) * (180 / M_PI); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, angle, prec, min_exp); + + c[p++] = ')'; + c[p] = '\000'; + } else if (transform.isVShear()) { + // We are more or less a pure skewY + strcpy(c + p, "skewY("); + p += 6; + + double angle = atan(transform[1]) * (180 / M_PI); + p += sp_svg_number_write_de(c + p, sizeof(c) - p, angle, prec, min_exp); + + c[p++] = ')'; + c[p] = '\000'; + } else { + strcpy (c + p, "matrix("); + p += 7; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[0], prec, min_exp ); + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[1], prec, min_exp ); + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[2], prec, min_exp ); + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[3], prec, min_exp ); + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[4], prec, min_exp ); + c[p++] = ','; + p += sp_svg_number_write_de( c + p, sizeof(c) - p, transform[5], prec, min_exp ); + c[p++] = ')'; + c[p] = '\000'; + } + + assert(p <= sizeof(c)); + return g_strdup(c); +} + + +gchar * +sp_svg_transform_write(Geom::Affine const *transform) +{ + return sp_svg_transform_write(*transform); +} + +/* + 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:fileencoding=utf-8:textwidth=99 : |