///|
/// Position keywords used by cpdf-style text and layout operations.
pub(all) enum PdfPosition {
PdfPositionPosCentre(Double, Double)
PdfPositionPosLeft(Double, Double)
PdfPositionPosRight(Double, Double)
PdfPositionTop(Double)
PdfPositionTopLeft(Double, Double)
PdfPositionTopRight(Double, Double)
PdfPositionLeft(Double)
PdfPositionBottomLeft(Double, Double)
PdfPositionBottom(Double)
PdfPositionBottomRight(Double, Double)
PdfPositionRight(Double)
PdfPositionDiagonal
PdfPositionReverseDiagonal
PdfPositionCentre
} derive(Debug, Eq, ToJson)
///|
/// Absolute placement calculated from a position keyword and page bounds.
pub(all) struct PdfCalculatedPosition {
x : Double
y : Double
rotation : Double
} derive(Debug, Eq, ToJson)
///|
fn pdf_position_distance(ignore_distance : Bool, distance : Double) -> Double {
if ignore_distance {
0.0
} else {
distance
}
}
///|
fn pdf_position_calculated(
x : Double,
y : Double,
rotation : Double,
) -> PdfCalculatedPosition {
{ x, y, rotation, }
}
///|
fn pdf_position_diagonal(
bounds : PdfRectangle,
text_width : Double,
) -> PdfCalculatedPosition {
let angle = @math.atan(
(bounds.max_y - bounds.min_y) / (bounds.max_x - bounds.min_x),
)
let center_x = (bounds.max_x + bounds.min_x) / 2.0
let center_y = (bounds.max_y + bounds.min_y) / 2.0
let half_width = text_width / 2.0
let dx = half_width * @math.cos(angle)
let dy = half_width * @math.sin(angle)
pdf_position_calculated(center_x - dx, center_y - dy, angle)
}
///|
fn pdf_position_reverse_diagonal(
bounds : PdfRectangle,
text_width : Double,
) -> PdfCalculatedPosition {
let angle = @math.atan(
(bounds.max_y - bounds.min_y) / (bounds.max_x - bounds.min_x),
)
let center_x = (bounds.max_x + bounds.min_x) / 2.0
let center_y = (bounds.max_y + bounds.min_y) / 2.0
let half_width = text_width / 2.0
let dx = half_width * @math.cos(angle)
let dy = half_width * @math.sin(angle)
pdf_position_calculated(
center_x - dx,
bounds.max_y + bounds.min_y - (center_y - dy),
angle - (2.0 * @math.PI - (@math.PI - 2.0 * angle) * 2.0 / 2.0) + @math.PI,
)
}
///|
/// Calculate the absolute placement for text of `text_width` in `bounds`.
///
/// When `ignore_distance` is true, edge-distance values in the position are
/// treated as zero, matching cpdf's `calculate_position` behavior.
pub fn pdf_calculate_position(
ignore_distance : Bool,
text_width : Double,
bounds : PdfRectangle,
position : PdfPosition,
) -> PdfCalculatedPosition {
let rotation = 0.0
match position {
PdfPositionCentre =>
pdf_position_calculated(
(bounds.min_x + bounds.max_x) / 2.0 - text_width / 2.0,
(bounds.min_y + bounds.max_y) / 2.0,
rotation,
)
PdfPositionDiagonal => pdf_position_diagonal(bounds, text_width)
PdfPositionReverseDiagonal =>
pdf_position_reverse_diagonal(bounds, text_width)
PdfPositionPosLeft(x, y) =>
pdf_position_calculated(bounds.min_x + x, bounds.min_y + y, rotation)
PdfPositionPosCentre(x, y) =>
pdf_position_calculated(
bounds.min_x + x - text_width / 2.0,
bounds.min_y + y,
rotation,
)
PdfPositionPosRight(x, y) =>
pdf_position_calculated(
bounds.min_x + x - text_width,
bounds.min_y + y,
rotation,
)
PdfPositionTop(distance) => {
let d = pdf_position_distance(ignore_distance, distance)
pdf_position_calculated(
(bounds.min_x + bounds.max_x) / 2.0 - text_width / 2.0,
bounds.max_y - d,
rotation,
)
}
PdfPositionTopLeft(x, y) => {
let x = pdf_position_distance(ignore_distance, x)
let y = pdf_position_distance(ignore_distance, y)
pdf_position_calculated(bounds.min_x + x, bounds.max_y - y, rotation)
}
PdfPositionTopRight(x, y) => {
let x = pdf_position_distance(ignore_distance, x)
let y = pdf_position_distance(ignore_distance, y)
pdf_position_calculated(
bounds.max_x - x - text_width,
bounds.max_y - y,
rotation,
)
}
PdfPositionLeft(distance) => {
let d = pdf_position_distance(ignore_distance, distance)
pdf_position_calculated(
bounds.min_x + d,
(bounds.max_y + bounds.min_y) / 2.0,
rotation,
)
}
PdfPositionBottomLeft(x, y) => {
let x = pdf_position_distance(ignore_distance, x)
let y = pdf_position_distance(ignore_distance, y)
pdf_position_calculated(bounds.min_x + x, bounds.min_y + y, rotation)
}
PdfPositionBottom(distance) => {
let d = pdf_position_distance(ignore_distance, distance)
pdf_position_calculated(
(bounds.min_x + bounds.max_x) / 2.0 - text_width / 2.0,
bounds.min_y + d,
rotation,
)
}
PdfPositionBottomRight(x, y) => {
let x = pdf_position_distance(ignore_distance, x)
let y = pdf_position_distance(ignore_distance, y)
pdf_position_calculated(
bounds.max_x - x - text_width,
bounds.min_y + y,
rotation,
)
}
PdfPositionRight(distance) => {
let d = pdf_position_distance(ignore_distance, distance)
pdf_position_calculated(
bounds.max_x - d - text_width,
(bounds.max_y + bounds.min_y) / 2.0,
rotation,
)
}
}
}