/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <svx/svdtrans.hxx>
#include <math.h>
#include <svx/xpoly.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/virdev.hxx>
#include <tools/bigint.hxx>
#include <tools/UnitConversion.hxx>
#include <unotools/syslocale.hxx>
#include <unotools/localedatawrapper.hxx>
#include <sal/log.hxx>
void MoveXPoly(XPolygon& rPoly, const Size& S)
{
rPoly.Move(S.Width(),S.Height());
}
void ResizeRect(tools::Rectangle& rRect, const Point& rRef, const Fraction& rxFact, const Fraction& ryFact)
{
Fraction aXFact(rxFact);
Fraction aYFact(ryFact);
if (!aXFact.IsValid()) {
SAL_WARN( "svx.svdraw", "invalid fraction xFract, using Fraction(1,1)" );
aXFact = Fraction(1,1);
tools::Long nWdt = rRect.Right() - rRect.Left();
if (nWdt == 0) rRect.AdjustRight( 1 );
}
rRect.SetLeft( rRef.X() + basegfx::fround<tools::Long>( (rRect.Left() - rRef.X()) * double(aXFact) ) );
rRect.SetRight( rRef.X() + basegfx::fround<tools::Long>( (rRect.Right() - rRef.X()) * double(aXFact) ) );
if (!aYFact.IsValid()) {
SAL_WARN( "svx.svdraw", "invalid fraction yFract, using Fraction(1,1)" );
aYFact = Fraction(1,1);
tools::Long nHgt = rRect.Bottom() - rRect.Top();
if (nHgt == 0) rRect.AdjustBottom( 1 );
}
rRect.SetTop( rRef.Y() + basegfx::fround<tools::Long>( (rRect.Top() - rRef.Y()) * double(aYFact) ) );
rRect.SetBottom( rRef.Y() + basegfx::fround<tools::Long>( (rRect.Bottom() - rRef.Y()) * double(aYFact) ) );
rRect.Normalize();
}
void ResizePoly(tools::Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
{
sal_uInt16 nCount=rPoly.GetSize();
for (sal_uInt16 i=0; i<nCount; i++) {
ResizePoint(rPoly[i],rRef,xFact,yFact);
}
}
void ResizeXPoly(XPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
{
sal_uInt16 nCount=rPoly.GetPointCount();
for (sal_uInt16 i=0; i<nCount; i++) {
ResizePoint(rPoly[i],rRef,xFact,yFact);
}
}
void RotatePoly(tools::Polygon& rPoly, const Point& rRef, double sn, double cs)
{
sal_uInt16 nCount=rPoly.GetSize();
for (sal_uInt16 i=0; i<nCount; i++) {
RotatePoint(rPoly[i],rRef,sn,cs);
}
}
void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs)
{
sal_uInt16 nCount=rPoly.GetPointCount();
for (sal_uInt16 i=0; i<nCount; i++) {
RotatePoint(rPoly[i],rRef,sn,cs);
}
}
void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs)
{
sal_uInt16 nCount=rPoly.Count();
for (sal_uInt16 i=0; i<nCount; i++) {
RotateXPoly(rPoly[i],rRef,sn,cs);
}
}
void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2)
{
tools::Long mx=rRef2.X()-rRef1.X();
tools::Long my=rRef2.Y()-rRef1.Y();
if (mx==0) { // vertical axis
tools::Long dx=rRef1.X()-rPnt.X();
rPnt.AdjustX(2*dx );
} else if (my==0) { // horizontal axis
tools::Long dy=rRef1.Y()-rPnt.Y();
rPnt.AdjustY(2*dy );
} else if (mx==my) { // diagonal axis '\'
tools::Long dx1=rPnt.X()-rRef1.X();
tools::Long dy1=rPnt.Y()-rRef1.Y();
rPnt.setX(rRef1.X()+dy1 );
rPnt.setY(rRef1.Y()+dx1 );
} else if (mx==-my) { // diagonal axis '/'
tools::Long dx1=rPnt.X()-rRef1.X();
tools::Long dy1=rPnt.Y()-rRef1.Y();
rPnt.setX(rRef1.X()-dy1 );
rPnt.setY(rRef1.Y()-dx1 );
} else { // arbitrary axis
// TODO: Optimize this! Raise perpendicular on the mirroring axis..?
Degree100 nRefAngle=GetAngle(rRef2-rRef1);
rPnt-=rRef1;
Degree100 nPntAngle=GetAngle(rPnt);
Degree100 nAngle=2_deg100*(nRefAngle-nPntAngle);
double a = toRadians(nAngle);
double nSin=sin(a);
double nCos=cos(a);
RotatePoint(rPnt,Point(),nSin,nCos);
rPnt+=rRef1;
}
}
void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2)
{
sal_uInt16 nCount=rPoly.GetPointCount();
for (sal_uInt16 i=0; i<nCount; i++) {
MirrorPoint(rPoly[i],rRef1,rRef2);
}
}
void ShearPoly(tools::Polygon& rPoly, const Point& rRef, double tn)
{
sal_uInt16 nCount=rPoly.GetSize();
for (sal_uInt16 i=0; i<nCount; i++) {
ShearPoint(rPoly[i],rRef,tn);
}
}
void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, bool bVShear)
{
sal_uInt16 nCount=rPoly.GetPointCount();
for (sal_uInt16 i=0; i<nCount; i++) {
ShearPoint(rPoly[i],rRef,tn,bVShear);
}
}
double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert)
{
bool bC1=pC1!=nullptr;
bool bC2=pC2!=nullptr;
tools::Long x0=rPnt.X();
tools::Long y0=rPnt.Y();
tools::Long cx=rCenter.X();
tools::Long cy=rCenter.Y();
double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
double sn=sin(nAngle);
double cs=cos(nAngle);
RotatePoint(rPnt,rCenter,sn,cs);
if (bC1) {
if (bVert) {
// move into the direction of the center, as a basic position for the rotation
pC1->AdjustY( -y0 );
// resize, account for the distance from the center
pC1->setY(basegfx::fround<tools::Long>(static_cast<double>(pC1->Y()) /rRad.X()*(cx-pC1->X())) );
pC1->AdjustY(cy );
} else {
// move into the direction of the center, as a basic position for the rotation
pC1->AdjustX( -x0 );
// resize, account for the distance from the center
tools::Long nPntRad=cy-pC1->Y();
double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
pC1->setX(basegfx::fround<tools::Long>(static_cast<double>(pC1->X()) * nFact));
pC1->AdjustX(cx );
}
RotatePoint(*pC1,rCenter,sn,cs);
}
if (bC2) {
if (bVert) {
// move into the direction of the center, as a basic position for the rotation
pC2->AdjustY( -y0 );
// resize, account for the distance from the center
pC2->setY(basegfx::fround<tools::Long>(static_cast<double>(pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())) );
pC2->AdjustY(cy );
} else {
// move into the direction of the center, as a basic position for the rotation
pC2->AdjustX( -x0 );
// resize, account for the distance from the center
tools::Long nPntRad=rCenter.Y()-pC2->Y();
double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
pC2->setX(basegfx::fround<tools::Long>(static_cast<double>(pC2->X()) * nFact));
pC2->AdjustX(cx );
}
RotatePoint(*pC2,rCenter,sn,cs);
}
rSin=sn;
rCos=cs;
return nAngle;
}
double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert)
{
bool bC1=pC1!=nullptr;
bool bC2=pC2!=nullptr;
tools::Long x0=rPnt.X();
tools::Long y0=rPnt.Y();
tools::Long dx1=0,dy1=0;
tools::Long dxC1=0,dyC1=0;
tools::Long dxC2=0,dyC2=0;
if (bVert) {
tools::Long nStart=rCenter.X()-rRad.X();
dx1=rPnt.X()-nStart;
rPnt.setX(nStart );
if (bC1) {
dxC1=pC1->X()-nStart;
pC1->setX(nStart );
}
if (bC2) {
dxC2=pC2->X()-nStart;
pC2->setX(nStart );
}
} else {
tools::Long nStart=rCenter.Y()-rRad.Y();
dy1=rPnt.Y()-nStart;
rPnt.setY(nStart );
if (bC1) {
dyC1=pC1->Y()-nStart;
pC1->setY(nStart );
}
if (bC2) {
dyC2=pC2->Y()-nStart;
pC2->setY(nStart );
}
}
double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
double sn=sin(nAngle);
double cs=cos(nAngle);
RotatePoint(rPnt,rCenter,sn,cs);
if (bC1) { if (bVert) pC1->AdjustY( -(y0-rCenter.Y()) ); else pC1->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC1,rCenter,sn,cs); }
if (bC2) { if (bVert) pC2->AdjustY( -(y0-rCenter.Y()) ); else pC2->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC2,rCenter,sn,cs); }
if (bVert) {
rPnt.AdjustX(dx1 );
if (bC1) pC1->AdjustX(dxC1 );
if (bC2) pC2->AdjustX(dxC2 );
} else {
rPnt.AdjustY(dy1 );
if (bC1) pC1->AdjustY(dyC1 );
if (bC2) pC2->AdjustY(dyC2 );
}
rSin=sn;
rCos=cs;
return nAngle;
}
double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
const Point& rRad, double& rSin, double& rCos, bool bVert,
const tools::Rectangle& rRefRect)
{
tools::Long y0=rPnt.Y();
CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert);
if (bVert) {
} else {
tools::Long nTop=rRefRect.Top();
tools::Long nBtm=rRefRect.Bottom();
tools::Long nHgt=nBtm-nTop;
tools::Long dy=rPnt.Y()-y0;
double a=static_cast<double>(y0-nTop)/nHgt;
a*=dy;
rPnt.setY(y0 + basegfx::fround<tools::Long>(a));
}
return 0.0;
}
void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
{
double nSin,nCos;
sal_uInt16 nPointCnt=rPoly.GetPointCount();
sal_uInt16 i=0;
while (i<nPointCnt) {
Point* pPnt=&rPoly[i];
Point* pC1=nullptr;
Point* pC2=nullptr;
if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
pC1=pPnt;
i++;
pPnt=&rPoly[i];
}
i++;
if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
pC2=&rPoly[i];
i++;
}
CrookRotateXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
}
}
void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
{
double nSin,nCos;
sal_uInt16 nPointCnt=rPoly.GetPointCount();
sal_uInt16 i=0;
while (i<nPointCnt) {
Point* pPnt=&rPoly[i];
Point* pC1=nullptr;
Point* pC2=nullptr;
if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
pC1=pPnt;
i++;
pPnt=&rPoly[i];
}
i++;
if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
pC2=&rPoly[i];
i++;
}
CrookSlantXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
}
}
void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
{
double nSin,nCos;
sal_uInt16 nPointCnt=rPoly.GetPointCount();
sal_uInt16 i=0;
while (i<nPointCnt) {
Point* pPnt=&rPoly[i];
Point* pC1=nullptr;
Point* pC2=nullptr;
if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
pC1=pPnt;
i++;
pPnt=&rPoly[i];
}
i++;
if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
pC2=&rPoly[i];
i++;
}
CrookStretchXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert,rRefRect);
}
}
void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
{
sal_uInt16 nPolyCount=rPoly.Count();
for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
CrookRotatePoly(rPoly[nPolyNum],rCenter,rRad,bVert);
}
}
void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
{
sal_uInt16 nPolyCount=rPoly.Count();
for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
CrookSlantPoly(rPoly[nPolyNum],rCenter,rRad,bVert);
}
}
void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
{
sal_uInt16 nPolyCount=rPoly.Count();
for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
CrookStretchPoly(rPoly[nPolyNum],rCenter,rRad,bVert,rRefRect);
}
}
Degree100 GetAngle(const Point& rPnt)
{
Degree100 a;
if (rPnt.Y()==0) {
if (rPnt.X()<0) a=-18000_deg100;
} else if (rPnt.X()==0) {
if (rPnt.Y()>0) a=-9000_deg100;
else a=9000_deg100;
} else {
a = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan2(-static_cast<double>(rPnt.Y()), static_cast<double>(rPnt.X())))));
}
return a;
}
Degree100 NormAngle18000(Degree100 a)
{
while (a<-18000_deg100) a+=36000_deg100;
while (a>=18000_deg100) a-=36000_deg100;
return a;
}
Degree100 NormAngle36000(Degree100 a)
{
a %= 36000_deg100;
if (a < 0_deg100)
a += 36000_deg100;
return a;
}
sal_uInt16 GetAngleSector(Degree100 nAngle) { return (NormAngle36000(nAngle) / 9000_deg100).get(); }
tools::Long GetLen(const Point& rPnt)
{
tools::Long x=std::abs(rPnt.X());
tools::Long y=std::abs(rPnt.Y());
if (x+y<0x8000) { // because 7FFF * 7FFF * 2 = 7FFE0002
x*=x;
y*=y;
x+=y;
x = basegfx::fround<tools::Long>(sqrt(x));
return x;
} else {
double nx=x;
double ny=y;
nx*=nx;
ny*=ny;
nx+=ny;
nx=sqrt(nx);
if (nx>0x7FFFFFFF) {
return 0x7FFFFFFF; // we can't go any further, for fear of an overrun!
} else {
return basegfx::fround<tools::Long>(nx);
}
}
}
void GeoStat::RecalcSinCos()
{
if (m_nRotationAngle==0_deg100) {
mfSinRotationAngle=0.0;
mfCosRotationAngle=1.0;
} else {
double a = toRadians(m_nRotationAngle);
mfSinRotationAngle=sin(a);
mfCosRotationAngle=cos(a);
}
}
void GeoStat::RecalcTan()
{
if (m_nShearAngle==0_deg100) {
mfTanShearAngle=0.0;
} else {
double a = toRadians(m_nShearAngle);
mfTanShearAngle=tan(a);
}
}
tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo)
{
tools::Polygon aPol(5);
aPol[0]=rRect.TopLeft();
aPol[1]=rRect.TopRight();
aPol[2]=rRect.BottomRight();
aPol[3]=rRect.BottomLeft();
aPol[4]=rRect.TopLeft();
if (rGeo.m_nShearAngle) ShearPoly(aPol,rRect.TopLeft(),rGeo.mfTanShearAngle);
if (rGeo.m_nRotationAngle) RotatePoly(aPol,rRect.TopLeft(),rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle);
return aPol;
}
namespace svx
{
tools::Rectangle polygonToRectangle(const tools::Polygon& rPolygon, GeoStat& rGeo)
{
rGeo.m_nRotationAngle = GetAngle(rPolygon[1] - rPolygon[0]);
rGeo.m_nRotationAngle = NormAngle36000(rGeo.m_nRotationAngle);
// rotation successful
rGeo.RecalcSinCos();
Point aPoint1(rPolygon[1] - rPolygon[0]);
if (rGeo.m_nRotationAngle)
RotatePoint(aPoint1, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation
tools::Long nWidth = aPoint1.X();
Point aPoint0(rPolygon[0]);
Point aPoint3(rPolygon[3] - rPolygon[0]);
if (rGeo.m_nRotationAngle)
RotatePoint(aPoint3, Point(0,0), -rGeo.mfSinRotationAngle, rGeo.mfCosRotationAngle); // -Sin to reverse rotation
tools::Long nHeight = aPoint3.Y();
Degree100 nShearAngle = GetAngle(aPoint3);
nShearAngle -= 27000_deg100; // ShearWink is measured against a vertical line
nShearAngle = -nShearAngle; // negating, because '+' is shearing clock-wise
bool bMirror = aPoint3.Y() < 0;
if (bMirror)
{ // "exchange of points" when mirroring
nHeight = -nHeight;
nShearAngle += 18000_deg100;
aPoint0 = rPolygon[3];
}
nShearAngle = NormAngle18000(nShearAngle);
if (nShearAngle < -9000_deg100 || nShearAngle > 9000_deg100)
{
nShearAngle = NormAngle18000(nShearAngle + 18000_deg100);
}
if (nShearAngle < -SDRMAXSHEAR)
nShearAngle = -SDRMAXSHEAR; // limit ShearWinkel (shear angle) to +/- 89.00 deg
if (nShearAngle > SDRMAXSHEAR)
nShearAngle = SDRMAXSHEAR;
rGeo.m_nShearAngle = nShearAngle;
rGeo.RecalcTan();
Point aRU(aPoint0);
aRU.AdjustX(nWidth);
aRU.AdjustY(nHeight);
return tools::Rectangle(aPoint0, aRU);
}
} // end svx
void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho)
{
tools::Long dx=rPt.X()-rPt0.X();
tools::Long dy=rPt.Y()-rPt0.Y();
tools::Long dxa=std::abs(dx);
tools::Long dya=std::abs(dy);
if (dx==0 || dy==0 || dxa==dya) return;
if (dxa>=dya*2) { rPt.setY(rPt0.Y() ); return; }
if (dya>=dxa*2) { rPt.setX(rPt0.X() ); return; }
if ((dxa<dya) != bBigOrtho) {
rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
} else {
rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
}
}
void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho)
{
tools::Long dx=rPt.X()-rPt0.X();
tools::Long dy=rPt.Y()-rPt0.Y();
tools::Long dxa=std::abs(dx);
tools::Long dya=std::abs(dy);
if ((dxa<dya) != bBigOrtho) {
rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
} else {
rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
}
}
tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv)
{
if (!nDiv)
return 0x7fffffff;
return BigInt::Scale(nVal, nMul, nDiv);
}
static FrPair toPair(o3tl::Length eFrom, o3tl::Length eTo)
{
const auto [nNum, nDen] = o3tl::getConversionMulDiv(eFrom, eTo);
return FrPair(nNum, nDen);
}
// How many eU units fit into a mm, respectively an inch?
// Or: How many mm, respectively inches, are there in an eU (and then give me the inverse)
static FrPair GetInchOrMM(MapUnit eU)
{
switch (eU) {
case MapUnit::Map1000thInch: return toPair(o3tl::Length::in, o3tl::Length::in1000);
case MapUnit::Map100thInch : return toPair(o3tl::Length::in, o3tl::Length::in100);
case MapUnit::Map10thInch : return toPair(o3tl::Length::in, o3tl::Length::in10);
case MapUnit::MapInch : return toPair(o3tl::Length::in, o3tl::Length::in);
case MapUnit::MapPoint : return toPair(o3tl::Length::in, o3tl::Length::pt);
case MapUnit::MapTwip : return toPair(o3tl::Length::in, o3tl::Length::twip);
case MapUnit::Map100thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm100);
case MapUnit::Map10thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm10);
case MapUnit::MapMM : return toPair(o3tl::Length::mm, o3tl::Length::mm);
case MapUnit::MapCM : return toPair(o3tl::Length::mm, o3tl::Length::cm);
case MapUnit::MapPixel : {
ScopedVclPtrInstance< VirtualDevice > pVD;
pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
Point aP(pVD->PixelToLogic(Point(64,64))); // 64 pixels for more accuracy
return FrPair(6400,aP.X(),6400,aP.Y());
}
case MapUnit::MapAppFont: case MapUnit::MapSysFont: {
ScopedVclPtrInstance< VirtualDevice > pVD;
pVD->SetMapMode(MapMode(eU));
Point aP(pVD->LogicToPixel(Point(32,32))); // 32 units for more accuracy
pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
aP=pVD->PixelToLogic(aP);
return FrPair(3200,aP.X(),3200,aP.Y());
}
default: break;
}
return Fraction(1,1);
}
// Calculate the factor that we need to convert units from eS to eD.
// e. g. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100.
FrPair GetMapFactor(MapUnit eS, MapUnit eD)
{
if (eS==eD) return FrPair(1,1,1,1);
const auto eFrom = MapToO3tlLength(eS, o3tl::Length::invalid);
const auto eTo = MapToO3tlLength(eD, o3tl::Length::invalid);
if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
return toPair(eFrom, eTo);
FrPair aS(GetInchOrMM(eS));
FrPair aD(GetInchOrMM(eD));
bool bSInch=IsInch(eS);
bool bDInch=IsInch(eD);
FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y());
if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); }
if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); }
return aRet;
};
FrPair GetMapFactor(FieldUnit eS, FieldUnit eD)
{
if (eS==eD) return FrPair(1,1,1,1);
auto eFrom = FieldToO3tlLength(eS), eTo = FieldToO3tlLength(eD);
if (eFrom == o3tl::Length::invalid)
{
if (eTo == o3tl::Length::invalid)
return FrPair(1,1,1,1);
eFrom = IsInch(eD) ? o3tl::Length::in : o3tl::Length::mm;
}
else if (eTo == o3tl::Length::invalid)
eTo = IsInch(eS) ? o3tl::Length::in : o3tl::Length::mm;
return toPair(eFrom, eTo);
};
void SdrFormatter::Undirty()
{
const o3tl::Length eFrom = MapToO3tlLength(m_eSrcMU, o3tl::Length::invalid);
const o3tl::Length eTo = MapToO3tlLength(m_eDstMU, o3tl::Length::invalid);
if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
{
const auto [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo);
sal_Int64 nMul = mul;
sal_Int64 nDiv = div;
short nComma = 0;
// shorten trailing zeros for dividend
while (0 == (nMul % 10))
{
nComma--;
nMul /= 10;
}
// shorten trailing zeros for divisor
while (0 == (nDiv % 10))
{
nComma++;
nDiv /= 10;
}
m_nMul = nMul;
m_nDiv = nDiv;
m_nComma = nComma;
}
else
{
m_nMul = m_nDiv = 1;
m_nComma = 0;
}
m_bDirty=false;
}
OUString SdrFormatter::GetStr(tools::Long nVal) const
{
static constexpr OUString aNullCode(u"0"_ustr);
if(!nVal)
{
return aNullCode;
}
// we may lose some decimal places here, because of MulDiv instead of Real
bool bNeg(nVal < 0);
SvtSysLocale aSysLoc;
const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData();
if (m_bDirty)
const_cast<SdrFormatter*>(this)->Undirty();
sal_Int16 nC(m_nComma);
if(bNeg)
nVal = -nVal;
while(nC <= -3)
{
nVal *= 1000;
nC += 3;
}
while(nC <= -1)
{
nVal *= 10;
nC++;
}
if(m_nMul != m_nDiv)
nVal = BigMulDiv(nVal, m_nMul, m_nDiv);
OUStringBuffer aStr = OUString::number(nVal);
if(nC > 0 && aStr.getLength() <= nC )
{
// decimal separator necessary
sal_Int32 nCount(nC - aStr.getLength());
if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero())
nCount++;
for(sal_Int32 i=0; i<nCount; i++)
aStr.insert(0, aNullCode);
// remove superfluous decimal points
sal_Int32 nNumDigits(LocaleDataWrapper::getNumDigits());
sal_Int32 nWeg(nC - nNumDigits);
if(nWeg > 0)
{
// TODO: we should round here
aStr.remove(aStr.getLength() - nWeg, nWeg);
nC = nNumDigits;
}
}
// remember everything before the decimal separator for later
sal_Int32 nForComma(aStr.getLength() - nC);
if(nC > 0)
{
// insert comma char (decimal separator)
// remove trailing zeros
while(nC > 0 && aStr[aStr.getLength() - 1] == aNullCode.getStr()[0])
{
aStr.remove(aStr.getLength() - 1, 1);
nC--;
}
if(nC > 0)
{
// do we still have decimal places?
sal_Unicode cDec(rLoc.getNumDecimalSep()[0]);
aStr.insert(nForComma, cDec);
}
}
// add in thousands separator (if necessary)
if( nForComma > 3 )
{
const OUString& aThoSep( rLoc.getNumThousandSep() );
if ( aThoSep.getLength() > 0 )
{
sal_Unicode cTho( aThoSep[0] );
sal_Int32 i(nForComma - 3);
while(i > 0)
{
aStr.insert(i, cTho);
i -= 3;
}
}
}
if(aStr.isEmpty())
aStr.append(aNullCode);
if(bNeg && (aStr.getLength() > 1 || aStr[0] != aNullCode.getStr()[0]))
{
aStr.insert(0, "-");
}
return aStr.makeStringAndClear();
}
OUString SdrFormatter::GetUnitStr(MapUnit eUnit)
{
switch(eUnit)
{
// metrically
case MapUnit::Map100thMM :
return u"/100mm"_ustr;
case MapUnit::Map10thMM :
return u"/10mm"_ustr;
case MapUnit::MapMM :
return u"mm"_ustr;
case MapUnit::MapCM :
return u"cm"_ustr;
// Inch
case MapUnit::Map1000thInch:
return u"/1000\""_ustr;
case MapUnit::Map100thInch :
return u"/100\""_ustr;
case MapUnit::Map10thInch :
return u"/10\""_ustr;
case MapUnit::MapInch :
return u"\""_ustr;
case MapUnit::MapPoint :
return u"pt"_ustr;
case MapUnit::MapTwip :
return u"twip"_ustr;
// others
case MapUnit::MapPixel :
return u"pixel"_ustr;
case MapUnit::MapSysFont :
return u"sysfont"_ustr;
case MapUnit::MapAppFont :
return u"appfont"_ustr;
case MapUnit::MapRelative :
return u"%"_ustr;
default:
return OUString();
}
}
OUString SdrFormatter::GetUnitStr(FieldUnit eUnit)
{
switch(eUnit)
{
default :
case FieldUnit::NONE :
case FieldUnit::CUSTOM :
return OUString();
// metrically
case FieldUnit::MM_100TH:
return u"/100mm"_ustr;
case FieldUnit::MM :
return u"mm"_ustr;
case FieldUnit::CM :
return u"cm"_ustr;
case FieldUnit::M :
return u"m"_ustr;
case FieldUnit::KM :
return u"km"_ustr;
// Inch
case FieldUnit::TWIP :
return u"twip"_ustr;
case FieldUnit::POINT :
return u"pt"_ustr;
case FieldUnit::PICA :
return u"pica"_ustr;
case FieldUnit::INCH :
return u"\""_ustr;
case FieldUnit::FOOT :
return u"ft"_ustr;
case FieldUnit::MILE :
return u"mile(s)"_ustr;
// others
case FieldUnit::PERCENT:
return u"%"_ustr;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'insert' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'remove' is required to be utilized.
↑ V530 The return value of function 'insert' is required to be utilized.
↑ V530 The return value of function 'insert' is required to be utilized.