From daeb5a85b6ded478aaac942c439ef5fd6bb8874f Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 24 Dec 2018 16:28:08 -0500 Subject: [PATCH] Transformation matrix --- libqpdf/QPDFMatrix.cc | 90 ++++++++++++++++++++++++++++++++++++++ libqpdf/build.mk | 1 + libqpdf/qpdf/QPDFMatrix.hh | 43 ++++++++++++++++++ libtests/build.mk | 1 + libtests/matrix.cc | 55 +++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 libqpdf/QPDFMatrix.cc create mode 100644 libqpdf/qpdf/QPDFMatrix.hh create mode 100644 libtests/matrix.cc diff --git a/libqpdf/QPDFMatrix.cc b/libqpdf/QPDFMatrix.cc new file mode 100644 index 00000000..28a338f0 --- /dev/null +++ b/libqpdf/QPDFMatrix.cc @@ -0,0 +1,90 @@ +#include +#include + +QPDFMatrix::QPDFMatrix() : + a(1.0), + b(0.0), + c(0.0), + d(1.0), + e(0.0), + f(0.0) +{ +} + +QPDFMatrix::QPDFMatrix(double a, double b, double c, + double d, double e, double f) : + a(a), + b(b), + c(c), + d(d), + e(e), + f(f) +{ +} + +std::string +QPDFMatrix::unparse() const +{ + return (QUtil::double_to_string(a, 5) + " " + + QUtil::double_to_string(b, 5) + " " + + QUtil::double_to_string(c, 5) + " " + + QUtil::double_to_string(d, 5) + " " + + QUtil::double_to_string(e, 5) + " " + + QUtil::double_to_string(f, 5)); +} + +void +QPDFMatrix::concat(QPDFMatrix const& other) +{ + double ap = (this->a * other.a) + (this->c * other.b); + double bp = (this->b * other.a) + (this->d * other.b); + double cp = (this->a * other.c) + (this->c * other.d); + double dp = (this->b * other.c) + (this->d * other.d); + double ep = (this->a * other.e) + (this->c * other.f) + this->e; + double fp = (this->b * other.e) + (this->d * other.f) + this->f; + this-> a = ap; + this-> b = bp; + this-> c = cp; + this-> d = dp; + this-> e = ep; + this-> f = fp; +} + +void +QPDFMatrix::scale(double sx, double sy) +{ + concat(QPDFMatrix(sx, 0, 0, sy, 0, 0)); +} + +void +QPDFMatrix::translate(double tx, double ty) +{ + concat(QPDFMatrix(1, 0, 0, 1, tx, ty)); +} + +void +QPDFMatrix::rotatex90(int angle) +{ + switch (angle) + { + case 90: + concat(QPDFMatrix(0, 1, -1, 0, 0, 0)); + break; + case 180: + concat(QPDFMatrix(-1, 0, 0, -1, 0, 0)); + break; + case 270: + concat(QPDFMatrix(0, -1, 1, 0, 0, 0)); + break; + default: + // ignore + break; + } +} + +void +QPDFMatrix::transform(double x, double y, double& xp, double& yp) +{ + xp = (this->a * x) + (this->c * y) + this->e; + yp = (this->b * x) + (this->d * y) + this->f; +} diff --git a/libqpdf/build.mk b/libqpdf/build.mk index e063df0c..4ef0688d 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -41,6 +41,7 @@ SRCS_libqpdf = \ libqpdf/QPDFAnnotationObjectHelper.cc \ libqpdf/QPDFExc.cc \ libqpdf/QPDFFormFieldObjectHelper.cc \ + libqpdf/QPDFMatrix.cc \ libqpdf/QPDFNameTreeObjectHelper.cc \ libqpdf/QPDFNumberTreeObjectHelper.cc \ libqpdf/QPDFObjGen.cc \ diff --git a/libqpdf/qpdf/QPDFMatrix.hh b/libqpdf/qpdf/QPDFMatrix.hh new file mode 100644 index 00000000..dcf8b195 --- /dev/null +++ b/libqpdf/qpdf/QPDFMatrix.hh @@ -0,0 +1,43 @@ +#ifndef QPDFMATRIX_HH +#define QPDFMATRIX_HH + +#include +#include + +class QPDFMatrix +{ + public: + QPDF_DLL + QPDFMatrix(); + QPDF_DLL + QPDFMatrix(double a, double b, double c, + double d, double e, double f); + + QPDF_DLL + std::string unparse() const; + + // This is not part of the public API. Just provide the methods we + // need as we need them. + QPDF_DLL + void concat(QPDFMatrix const& other); + QPDF_DLL + void scale(double sx, double sy); + QPDF_DLL + void translate(double tx, double ty); + // Any value other than 90, 180, or 270 is ignored + QPDF_DLL + void rotatex90(int angle); + + QPDF_DLL + void transform(double x, double y, double& xp, double& yp); + + private: + double a; + double b; + double c; + double d; + double e; + double f; +}; + +#endif // QPDFMATRIX_HH diff --git a/libtests/build.mk b/libtests/build.mk index 214bd17c..5c1ee56a 100644 --- a/libtests/build.mk +++ b/libtests/build.mk @@ -12,6 +12,7 @@ BINS_libtests = \ input_source \ json \ lzw \ + matrix \ md5 \ numrange \ pointer_holder \ diff --git a/libtests/matrix.cc b/libtests/matrix.cc new file mode 100644 index 00000000..d27e96cf --- /dev/null +++ b/libtests/matrix.cc @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +static void check(QPDFMatrix const& m, std::string const& exp) +{ + std::string u = m.unparse(); + if (u != exp) + { + std::cout << "got " << u << ", wanted " << exp << std::endl; + } +} + +static void check_xy(double x, double y, std::string const& exp) +{ + std::string u = (QUtil::double_to_string(x, 2) + " " + + QUtil::double_to_string(y, 2)); + if (u != exp) + { + std::cout << "got " << u << ", wanted " << exp << std::endl; + } +} + +int main() +{ + QPDFMatrix m; + check(m, "1.00000 0.00000 0.00000 1.00000 0.00000 0.00000"); + m.translate(10, 20); + check(m, "1.00000 0.00000 0.00000 1.00000 10.00000 20.00000"); + m.scale(1.5, 2); + check(m, "1.50000 0.00000 0.00000 2.00000 10.00000 20.00000"); + m.translate(30, 40); + check(m, "1.50000 0.00000 0.00000 2.00000 55.00000 100.00000"); + m.concat(QPDFMatrix(1, 2, 3, 4, 5, 6)); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + m.rotatex90(90); + check(m, "4.50000 8.00000 -1.50000 -4.00000 62.50000 112.00000"); + m.rotatex90(180); + check(m, "-4.50000 -8.00000 1.50000 4.00000 62.50000 112.00000"); + m.rotatex90(270); + check(m, "-1.50000 -4.00000 -4.50000 -8.00000 62.50000 112.00000"); + m.rotatex90(180); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + m.rotatex90(12345); + check(m, "1.50000 4.00000 4.50000 8.00000 62.50000 112.00000"); + + double xp = 0; + double yp = 0; + m.transform(240, 480, xp, yp); + check_xy(xp, yp, "2582.50 4912.00"); + + std::cout << "matrix tests done" << std::endl; + return 0; +}