новые иконки в OpenBoard
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
OpenBoard/src/pdf-merger/Transformation.h

345 lines
9.8 KiB

/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The file defines some classes for transformation of PDF content stream.
*/
#ifndef TRANSFORMATION_H
#define TRANSFORMATION_H
#include <sstream>
#include <string>
#include <math.h>
#include <vector>
#include <iostream>
#include "Utils.h"
namespace merge_lib
{
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
class TransformationMatrix
{
public:
TransformationMatrix(double a = 1, double b = 0, double c = 0, double d = 1, double e = 0, double f = 0):
_a(a), _b(b), _c(c), _d(d), _e(e), _f(f)
{}
TransformationMatrix(const TransformationMatrix & copy)
{
setParameters(copy._a, copy._b, copy._c, copy._d, copy._e, copy._f);
}
void setParameters(double a, double b, double c, double d, double e, double f)
{
_a = Utils::normalizeValue(a);
_b = Utils::normalizeValue(b);
_c = Utils::normalizeValue(c);
_d = Utils::normalizeValue(d);
_e = Utils::normalizeValue(e);
_f = Utils::normalizeValue(f);
}
void add(const TransformationMatrix & tm)
{
double newA = _a*tm._a + _b*tm._c;
double newB = _a*tm._b + _b*tm._d;
double newC = _c*tm._a + _d*tm._c;
double newD = _c*tm._b + _d*tm._d;
double newE = _e*tm._a + _f*tm._c + tm._e;
double newF = _e*tm._b + _f*tm._d + tm._f;
// we need to round the values to avoid not-needed transformation
// since 1.e-17 is not 0 from PDF point of view, while such double
// value really means 0.
_a = Utils::normalizeValue(newA);
_b = Utils::normalizeValue(newB);
_c = Utils::normalizeValue(newC);
_d = Utils::normalizeValue(newD);
_e = Utils::normalizeValue(newE);
_f = Utils::normalizeValue(newF);
}
std::string getValue()
{
std::ostringstream value;
value << "[ " << _a << " " << _b << " " << _c << " " << _d << " " << _e << " " << _f << " ]\n";
return value.str();
}
std::string getCMT()
{
std::ostringstream buf;
buf << std::fixed << _a <<" "<< _b <<" "<< _c <<" "<< _d << " "<< _e << " "<< _f << " cm\n";
return buf.str();
}
void recalculateCoordinates(double & x, double &y)
{
double inputX = x;
double inputY = y;
x = _a*inputX + _c*inputY + _e;
y = _b*inputX + _d*inputY + _f;
}
private:
double _a, _b, _c, _d, _e, _f;
};
// base class of transformation CMT
class Transformation
{
public:
Transformation(): _tm(){};
virtual Transformation * getClone() const = 0;
std::string getCMT()
{
return _tm.getCMT();
}
virtual ~Transformation() {};
const TransformationMatrix & getMatrix()
{
return _tm;
}
void addMatrix(const TransformationMatrix & tm)
{
_tm.add(tm);
}
protected:
TransformationMatrix _tm;
};
// rotation CMT
class Rotation: public Transformation
{
public:
Rotation(double angle):Transformation(),_angle(angle)
{
double cosValue = cos(_angle * (M_PI / 180));
double sinValue = sin(_angle * (M_PI / 180));
_tm.setParameters(cosValue, sinValue, -sinValue, cosValue, 0, 0);
};
virtual ~Rotation(){};
virtual Transformation * getClone() const
{
return new Rotation(_angle);
}
protected:
double _angle; // number of degrees to rotate
};
// translation CMT
class Translation: public Transformation
{
public:
Translation(double x, double y):Transformation(),_x(x),_y(y)
{
_tm.setParameters(1, 0, 0, 1, _x, _y);
};
virtual ~Translation(){};
virtual Transformation * getClone() const
{
return new Translation(_x, _y);
}
protected:
double _x;
double _y;
};
// scaling CMT
class Scaling: public Transformation
{
public:
Scaling(double x):Transformation(),_x(x)
{
_tm.setParameters(_x, 0, 0, _x, 0, 0);
};
virtual Transformation * getClone() const
{
return new Scaling(_x);
}
protected:
double _x; // the value to multiply the content
};
// transformation can consist of one or several
// operations like rotation, scaling, translation
typedef std::vector<Transformation *> PageTransformations;
// This is interface class for setting transformation parameters
//
class TransformationDescription
{
public:
TransformationDescription( double x = 0, // leftBottomX coordinate
double y = 0, // leftBottomY coordinate
double scale = 1, // scale (by default = 1 = NONE
int angel = 0): // rotation (0,90,180,270)
_x(x),_y(y),_scale(scale),_angel(angel)
{
if( _angel )
{
_transforms.push_back(new Rotation(_angel));
}
if( !Utils::doubleEquals(_scale,1) && !Utils::doubleEquals(_scale,0) )
{
_transforms.push_back(new Scaling(_scale));
}
}
virtual ~TransformationDescription()
{
for(size_t i = 0;i<_annotsTransforms.size();i++)
{
if( _annotsTransforms[i] )
{
delete _annotsTransforms[i];
_annotsTransforms[i] = 0;
}
_annotsTransforms.clear();
}
for(size_t i = 0;i<_transforms.size();i++)
{
if( _transforms[i] )
{
delete _transforms[i];
_transforms[i] = 0;
}
}
_transforms.clear();
}
void addRotation(int rotation)
{
if( rotation )
{
_angel = (_angel - rotation)%360;
// /Rotation rotate the object, while _angel rotate the coordinate system
// where object is located, that's why
// we should compensate that
_transforms.push_back(new Rotation(360-rotation));
}
}
const PageTransformations & getTransformations() const
{
return _transforms;
}
const PageTransformations getAnnotsTransformations() const
{
PageTransformations trans;
trans = _transforms;
for(size_t i = 0; i < _annotsTransforms.size(); ++i)
{
trans.push_back(_annotsTransforms[i]);
}
return trans;
}
void addAnnotsTransformation( Transformation & trans )
{
_annotsTransforms.push_back(trans.getClone());
}
// method recalculates the final translation in order to put
// object into needed x,y coordinates.
// Page is located from position 0,0
void recalculateTranslation(double width, double height)
{
double dx1 = 0;
double dy1 = 0;
double scaling = ( Utils::doubleEquals(_scale,0))?1:_scale;
switch(_angel)
{
case 0:
dx1 = _x/scaling;
dy1 = _y/scaling;
break;
case -270:
case 90:
dx1 = _y/scaling ;
dy1 = - _x /scaling - height;
break;
case 180:
case -180:
dx1 = - _x /scaling - width;
dy1 = - _y /scaling - height;
break;
case 270:
case -90:
dx1 = - _y/scaling - width;
dy1 = _x/scaling;
break;
default:
std::cerr<<"Unsupported rotation parameter"<<_angel<<std::endl;
break;
}
//std::cerr<< "dx1 = "<<dx1<<"dy1 = "<<dy1<<std::endl;
if( ! (Utils::doubleEquals(dx1,0) && Utils::doubleEquals(dy1,0)) )
{
// This translation is needed to put transformed content into
// desired coordinates
_transforms.push_back(new Translation(dx1,dy1));
}
}
TransformationDescription( const TransformationDescription & copy)
{
*this = copy;
}
TransformationDescription& operator = (const TransformationDescription &copy)
{
if( this != &copy )
{
for(size_t i = 0;i < copy._annotsTransforms.size();i++)
{
_annotsTransforms.push_back(copy._annotsTransforms[i]->getClone());
}
for(size_t i = 0; i < copy._transforms.size(); ++i)
{
_transforms.push_back(copy._transforms[i]->getClone());
}
_x = copy._x;
_y = copy._y;
_scale = copy._scale;
_angel = copy._angel;
}
return *this;
}
std::string getCMT()
{
std::stringstream content;
for(size_t i = 0;i<_transforms.size();i++)
{
content<<_transforms[i]->getCMT();
}
return content.str();
}
private:
double _x;
double _y;
double _scale;
int _angel;
PageTransformations _transforms;
PageTransformations _annotsTransforms;
};
}
#endif