Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
This file is part of the iText (R) project.
Copyright (c) 1998-2024 Apryse Group NV
Authors: Apryse Software.
This program is offered under a commercial and under the AGPL license.
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
AGPL licensing:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package com.itextpdf.layout.minmaxwidth;
import com.itextpdf.kernel.geom.Rectangle;
/**
* Class for min-max-width of rotated elements.
* Also contains heuristic methods for it calculation based on the assumption that area of element stays the same
* when we try to layout it with different available width (available width is between min-width and max-width).
*/
public class RotationMinMaxWidth extends MinMaxWidth {
private double minWidthOrigin;
private double maxWidthOrigin;
private double minWidthHeight;
private double maxWidthHeight;
/**
* Create new instance
*
* @param minWidth min-width of rotated element
* @param maxWidth max-width of rotated element
* @param minWidthOrigin the width of not rotated element, that will have min-width after rotation
* @param maxWidthOrigin the width of not rotated element, that will have max-width after rotation
* @param minWidthHeight the height of rotated element, that have min-width as its rotated width
* @param maxWidthHeight the height of rotated element, that have min-width as its rotated width
*/
public RotationMinMaxWidth(double minWidth, double maxWidth, double minWidthOrigin, double maxWidthOrigin, double minWidthHeight, double maxWidthHeight) {
super((float) minWidth, (float) maxWidth, 0);
this.maxWidthOrigin = maxWidthOrigin;
this.minWidthOrigin = minWidthOrigin;
this.minWidthHeight = minWidthHeight;
this.maxWidthHeight = maxWidthHeight;
}
public double getMinWidthOrigin() {
return minWidthOrigin;
}
public double getMaxWidthOrigin() {
return maxWidthOrigin;
}
public double getMinWidthHeight() {
return minWidthHeight;
}
public double getMaxWidthHeight() {
return maxWidthHeight;
}
/**
* Heuristic method, based on the assumption that area of element stays the same, when we try to
* layout it with different available width (available width is between min-width and max-width).
*
* @param angle rotation angle in radians
* @param area the constant area
* @param elementMinMaxWidth NOT rotated element min-max-width
* @return possible min-max-width of element after rotation
*/
public static RotationMinMaxWidth calculate(double angle, double area, MinMaxWidth elementMinMaxWidth) {
WidthFunction function = new WidthFunction(angle, area);
return calculate(function, elementMinMaxWidth.getMinWidth(), elementMinMaxWidth.getMaxWidth());
}
/**
* Heuristic method, based on the assumption that area of element stays the same, when we try to
* layout it with different available width (available width is between min-width and max-width).
*
* @param angle rotation angle in radians
* @param area the constant area
* @param elementMinMaxWidth NOT rotated element min-max-width
* @param availableWidth the maximum width of area the element will occupy after rotation.
* @return possible min-max-width of element after rotation
*/
public static RotationMinMaxWidth calculate(double angle, double area, MinMaxWidth elementMinMaxWidth, double availableWidth) {
WidthFunction function = new WidthFunction(angle, area);
WidthFunction.Interval validArguments = function.getValidOriginalWidths(availableWidth);
if (validArguments == null) {
return null;
}
double xMin = Math.max(elementMinMaxWidth.getMinWidth(), validArguments.getMin());
double xMax = Math.min(elementMinMaxWidth.getMaxWidth(), validArguments.getMax());
if (xMax < xMin) {
//Initially the null was returned in this case, but this result in old layout logic that looks worse in most cases.
//The difference between min and max is not that big and not critical.
double rotatedWidth = function.getRotatedWidth(xMin);
double rotatedHeight = function.getRotatedHeight(xMin);
return new RotationMinMaxWidth(rotatedWidth, rotatedWidth, xMin, xMin, rotatedHeight, rotatedHeight);
}
return calculate(function, xMin, xMax);
}
/**
* Utility method for calculating rotated width of area in a similar way to other calculations in this class.
*
* @param area the initial area
* @param angle the rotation angle in radians
* @return width of rotated area
*/
public static double calculateRotatedWidth(Rectangle area, double angle) {
return area.getWidth() * cos(angle) + area.getHeight() * sin(angle);
}
/**
* This method use derivative of function defined on interval: [xMin, xMax] to find its local minimum and maximum.
* It also calculate other handy values needed for the creation of {@link RotationMinMaxWidth}.
*
* @param func the {@link WidthFunction#getRotatedWidth(double)} of this instance is used as analysed function
* @param xMin the smallest possible value of function argument
* @param xMax the biggest possible value of function argument
* @return the calculated {@link RotationMinMaxWidth}
*/
private static RotationMinMaxWidth calculate(WidthFunction func, double xMin, double xMax) {
double minWidthOrigin;
double maxWidthOrigin;
//Derivative sign change point
double x0 = func.getWidthDerivativeZeroPoint();
//The point x0 may be in three different positions in relation to function interval.
if (x0 < xMin) {
//The function is decreasing in this case on whole interval so the local mim and max are on interval borders
minWidthOrigin = xMin;
maxWidthOrigin = xMax;
}
else if (x0 > xMax) {
//The function is increasing in this case on whole interval so the local mim and max are on interval borders
minWidthOrigin = xMax;
maxWidthOrigin = xMin;
}
else {
//The function derivative changes its sign from negative to positive on function interval in point x0,
//so its local min is x0, and its local maximum is on one of interval borders
minWidthOrigin = x0;
maxWidthOrigin = func.getRotatedWidth(xMax) > func.getRotatedWidth(xMin) ? xMax : xMin;
}
return new RotationMinMaxWidth(func.getRotatedWidth(minWidthOrigin), func.getRotatedWidth(maxWidthOrigin),
minWidthOrigin, maxWidthOrigin, func.getRotatedHeight(minWidthOrigin), func.getRotatedHeight(maxWidthOrigin));
}
private static double sin(double angle) {
return correctSinCos(Math.abs((Math.sin(angle))));
}
private static double cos(double angle) {
return correctSinCos(Math.abs((Math.cos(angle))));
}
private static double correctSinCos(double value) {
if (MinMaxWidthUtils.isEqual(value, 0)) {
return 0;
} else if (MinMaxWidthUtils.isEqual(value, 1)) {
return 1;
}
return value;
}
/**
* Class that represents functions used, for calculation of width of element after rotation
* based on it's NOT rotated width and assumption, that area of element stays the same when
* we try to layout it with different available width.
* Contains handy methods for function analysis.
*/
private static class WidthFunction {
private double sin;
private double cos;
private double area;
/**
* Create new instance
*
* @param angle rotation angle in radians
* @param area the constant area
*/
public WidthFunction(double angle, double area) {
this.sin = sin(angle);
this.cos = cos(angle);
this.area = area;
}
/**
* Function used for width calculations of rotated element. This function is continuous on interval: (0, Infinity)
*
* @param x width value of NOT rotated element
* @return width of rotated element
*/
public double getRotatedWidth(double x) {
return x * cos + area * sin / x;
}
/**
* Function used for height calculations of rotated element. This function is continuous on interval: (0, Infinity)
*
* @param x width value of NOT rotated element
* @return width of rotated element
*/
public double getRotatedHeight(double x) {
return x * sin + area * cos / x;
}
/**
* Get's possible values of NOT rotated width of all element that have therer rotated width less that availableWidth
*
* @param availableWidth the highest possible width of rotated element.
* @return interval that specify biggest and smallest possible values of NOT rotated width of such elements.
*/
public Interval getValidOriginalWidths(double availableWidth) {
double minWidth;
double maxWidth;
if (cos == 0) {
minWidth = area * sin / availableWidth;
maxWidth = MinMaxWidthUtils.getInfWidth();
} else if (sin == 0) {
minWidth = 0;
maxWidth = availableWidth / cos;
} else {
double D = availableWidth * availableWidth - 4 * area * sin * cos;
if (D < 0) {
return null;
}
minWidth = (availableWidth - Math.sqrt(D)) / (2 * cos);
maxWidth = (availableWidth + Math.sqrt(D)) / (2 * cos);
}
return new Interval(minWidth, maxWidth);
}
/**
* Gets the argument of {@link #getRotatedWidth(double)} that results in zero derivative.
* In case we have {@link #sin}{@code == 0} or {@link #sin}{@code == 0} the function doesn't have
* zero derivative on defined interval, but value returned by this method fits well in the calculations above.
*
* @return the argument of {@link #getRotatedWidth(double)} that results in zero derivative
*/
public double getWidthDerivativeZeroPoint() {
return Math.sqrt(area * sin / cos);
}
public static class Interval {
private double min;
private double max;
public Interval(double min, double max) {
this.min = min;
this.max = max;
}
public double getMin() {
return min;
}
public double getMax() {
return max;
}
}
}
}