///|
/// Boolean polygon operation used by cpdf's GPC-backed clipping helpers.
pub(all) enum PdfClipOperation {
PdfClipDifference
PdfClipIntersection
PdfClipExclusiveOr
PdfClipUnion
} derive(Debug, Eq, ToJson)
///|
/// A vertex in a cpdf/GPC polygon contour.
pub(all) struct PdfClipVertex {
x : Double
y : Double
} derive(Debug, Eq, ToJson)
///|
/// One contour in a cpdf/GPC polygon.
///
/// `hole=true` matches cpdf's `1` hole flag. `hole=false` matches cpdf's `0`
/// exterior-contour flag.
pub(all) struct PdfClipContour {
hole : Bool
vertices : Array[PdfClipVertex]
} derive(Debug, Eq, ToJson)
///|
/// A cpdf/GPC polygon represented as an ordered list of contours.
pub(all) struct PdfClipPolygon {
contours : Array[PdfClipContour]
} derive(Debug, Eq, ToJson)
///|
/// Build a cpdf/GPC polygon vertex.
pub fn pdf_clip_vertex(x : Double, y : Double) -> PdfClipVertex {
{ x, y, }
}
///|
/// Build a cpdf/GPC polygon contour, copying the input vertex view.
pub fn pdf_clip_contour(
hole : Bool,
vertices : ArrayView[PdfClipVertex],
) -> PdfClipContour {
{ hole, vertices: vertices.to_owned(), }
}
///|
/// Build a cpdf/GPC polygon, copying the input contours and their vertices.
pub fn pdf_clip_polygon(contours : ArrayView[PdfClipContour]) -> PdfClipPolygon {
let copied : Array[PdfClipContour] = []
for contour in contours {
copied.push(pdf_clip_contour(contour.hole, contour.vertices))
}
{ contours: copied, }
}
///|
/// Return the cpdf null GPC polygon.
pub fn pdf_clip_null_polygon() -> PdfClipPolygon {
{ contours: [], }
}
///|
/// Source-spelled compatibility wrapper for cpdfclip's null GPC polygon.
pub fn pdf_nullpoly() -> PdfClipPolygon {
pdf_clip_null_polygon()
}
///|
/// Number of contours in this polygon.
pub fn PdfClipPolygon::num_contours(self : PdfClipPolygon) -> Int {
self.contours.length()
}
///|
/// Number of vertices in this contour.
pub fn PdfClipContour::num_vertices(self : PdfClipContour) -> Int {
self.vertices.length()
}
///|
/// Return cpdf's integer hole flag for this contour.
pub fn PdfClipContour::hole_flag(self : PdfClipContour) -> Int {
if self.hole {
1
} else {
0
}
}
///|
/// Return the integer operation code used by cpdf's GPC binding.
pub fn PdfClipOperation::gpc_code(self : PdfClipOperation) -> Int {
match self {
PdfClipDifference => 0
PdfClipIntersection => 1
PdfClipExclusiveOr => 2
PdfClipUnion => 3
}
}
///|
/// Build the cpdf/GPC rectangle polygon for `[xmin, xmax] x [ymin, ymax]`.
pub fn pdf_clip_polygon_of_box(
xmin : Double,
xmax : Double,
ymin : Double,
ymax : Double,
) -> PdfClipPolygon {
pdf_clip_polygon([
pdf_clip_contour(false, [
pdf_clip_vertex(xmin, ymin),
pdf_clip_vertex(xmax, ymin),
pdf_clip_vertex(xmax, ymax),
pdf_clip_vertex(xmin, ymax),
]),
])
}
///|
/// Source-spelled compatibility wrapper for cpdfclip's rectangle polygon.
pub fn pdf_gpc_polygon_of_box(
xmin : Double,
xmax : Double,
ymin : Double,
ymax : Double,
) -> PdfClipPolygon {
pdf_clip_polygon_of_box(xmin, xmax, ymin, ymax)
}
///|
/// Build a cpdf/GPC polygon from parallel hole flags and vertex arrays.
///
/// Raises `@core.PdfError::BadNumberArgument` when the inputs have different lengths.
pub fn pdf_clip_make_polygon(
holes : ArrayView[Bool],
vertex_arrays : ArrayView[Array[PdfClipVertex]],
) -> PdfClipPolygon raise @core.PdfError {
if holes.length() != vertex_arrays.length() {
raise BadNumberArgument("pdf_clip_make_polygon: unequal length inputs")
}
let contours : Array[PdfClipContour] = []
for index in 0.. PdfClipPolygon raise @core.PdfError {
pdf_clip_make_polygon(holes, vertex_arrays)
}
///|
fn pdf_clip_float_text(value : Double) -> String {
let scaled = (value * 1000000.0).round().to_int()
let negative = scaled < 0
let magnitude = if negative { -scaled } else { scaled }
let whole = magnitude / 1000000
let fraction = magnitude % 1000000
let mut fraction_text = fraction.to_string()
while fraction_text.length() < 6 {
fraction_text = "0" + fraction_text
}
(if negative { "-" } else { "" }) + whole.to_string() + "." + fraction_text
}
///|
fn pdf_gpcml_write_ascii(
output : Array[Byte],
position : Int,
text : String,
) -> Int {
let mut current = position
for byte in @ascii.encode(text) {
output[current] = byte
current += 1
}
current
}
///|
fn pdf_gpcml_write_vertex(
output : Array[Byte],
position : Int,
vertex : PdfClipVertex,
) -> Int {
let mut current = pdf_gpcml_write_ascii(output, position, " {x = ")
current = pdf_gpcml_write_ascii(
output,
current,
pdf_clip_float_text(vertex.x),
)
current = pdf_gpcml_write_ascii(output, current, ", y = ")
current = pdf_gpcml_write_ascii(
output,
current,
pdf_clip_float_text(vertex.y),
)
pdf_gpcml_write_ascii(output, current, "}\n")
}
///|
fn pdf_gpcml_write_contour(
output : Array[Byte],
position : Int,
contour : PdfClipContour,
) -> Int {
let mut current = pdf_gpcml_write_ascii(output, position, " Contour with ")
current = pdf_gpcml_write_ascii(
output,
current,
contour.vertices.length().to_string(),
)
current = pdf_gpcml_write_ascii(output, current, " vertices and hole ")
current = pdf_gpcml_write_ascii(
output,
current,
contour.hole_flag().to_string(),
)
output[current] = 10
current += 1
for vertex in contour.vertices {
current = pdf_gpcml_write_vertex(output, current, vertex)
}
current
}
///|
fn pdf_gpcml_text_length(text : String) -> Int {
@ascii.encode(text).length()
}
///|
fn pdf_gpcml_vertex_text_length(vertex : PdfClipVertex) -> Int {
pdf_gpcml_text_length(" {x = ") +
pdf_gpcml_text_length(pdf_clip_float_text(vertex.x)) +
pdf_gpcml_text_length(", y = ") +
pdf_gpcml_text_length(pdf_clip_float_text(vertex.y)) +
pdf_gpcml_text_length("}\n")
}
///|
fn pdf_gpcml_contour_text_length(contour : PdfClipContour) -> Int {
let mut length = pdf_gpcml_text_length(" Contour with ") +
pdf_gpcml_text_length(contour.vertices.length().to_string()) +
pdf_gpcml_text_length(" vertices and hole ") +
pdf_gpcml_text_length(contour.hole_flag().to_string()) +
1
for vertex in contour.vertices {
length += pdf_gpcml_vertex_text_length(vertex)
}
length
}
///|
fn pdf_gpcml_polygon_text_length(polygon : PdfClipPolygon) -> Int {
let mut length = pdf_gpcml_text_length("Contours: ") +
pdf_gpcml_text_length(polygon.contours.length().to_string()) +
1
for contour in polygon.contours {
length += pdf_gpcml_contour_text_length(contour)
}
length
}
///|
/// Return the text cpdfclip's debug polygon printer writes to stdout.
pub fn pdf_gpcml_printpolygon(polygon : PdfClipPolygon) -> @core.PdfBytes {
let output = Array::make(pdf_gpcml_polygon_text_length(polygon), b'\x00')
let mut position = pdf_gpcml_write_ascii(output, 0, "Contours: ")
position = pdf_gpcml_write_ascii(
output,
position,
polygon.contours.length().to_string(),
)
output[position] = 10
position += 1
for contour in polygon.contours {
position = pdf_gpcml_write_contour(output, position, contour)
}
Bytes::from_array(output)
}