///|
fn pdf_tweak_number(object : @syntax.PdfObject) -> Double? {
match object {
PdfReal(value) => Some(value)
PdfInteger(value) => Some(value.to_double())
_ => None
}
}
///|
fn pdf_tweak_ctm_scale(stack : ArrayView[@geometry.TransformMatrix]) -> Double {
if stack.length() == 0 {
1.0
} else {
stack[stack.length() - 1].decompose().scale
}
}
///|
fn pdf_tweak_extgstate_line_width(
document : PdfDocument,
resources : @syntax.PdfObject,
name : @core.PdfName,
) -> Double? {
match document.lookup_direct(pdf_tweak_extgstate_key(), resources) {
Some(extgstate) =>
match document.lookup_direct(name, extgstate) {
Some(gdict) =>
match document.lookup_direct(pdf_tweak_line_width_key(), gdict) {
Some(value) => pdf_tweak_number(value)
None => None
}
None => None
}
None => None
}
}
///|
/// Rewrite line widths to honor the cpdf thin-lines threshold.
///
/// Positive `width` is a minimum width. Negative `width` uses its absolute
/// value as a maximum width, matching `cpdftweak.ml`.
pub fn pdf_thin_lines_ops(
document : PdfDocument,
resources : @syntax.PdfObject,
ops : ArrayView[@content.PdfContentOp],
width : Double,
) -> Array[@content.PdfContentOp] raise @core.PdfError {
let limit = if width < 0.0 { -width } else { width }
let is_minimum = width >= 0.0
let ctm_stack : Array[@geometry.TransformMatrix] = [
@geometry.transform_identity_matrix,
]
let output : Array[@content.PdfContentOp] = []
for op in ops {
match op {
Op_w(line_width) => {
let adjusted_limit = limit / pdf_tweak_ctm_scale(ctm_stack)
let keep = if is_minimum {
line_width >= adjusted_limit
} else {
line_width <= adjusted_limit
}
if keep {
output.push(op)
} else {
output.push(Op_w(adjusted_limit))
}
}
Op_cm(matrix) => {
if ctm_stack.length() == 0 {
raise MatrixMalformed
}
let index = ctm_stack.length() - 1
ctm_stack[index] = @geometry.transform_matrix_compose(
ctm_stack[index],
matrix,
)
output.push(op)
}
Op_q => {
if ctm_stack.length() == 0 {
raise MatrixMalformed
}
ctm_stack.push(ctm_stack[ctm_stack.length() - 1])
output.push(op)
}
Op_Q => {
if ctm_stack.length() == 0 {
raise MatrixMalformed
}
ignore(ctm_stack.pop())
output.push(op)
}
Op_gs(name) => {
output.push(op)
match pdf_tweak_extgstate_line_width(document, resources, name) {
Some(line_width) => output.push(Op_w(line_width))
None => ()
}
}
_ => output.push(op)
}
}
if limit > 1.0 || limit < 1.0 {
output.insert(0, Op_w(limit))
}
output
}
///|
fn PdfDocument::pdf_tweak_thin_lines_page(
self : PdfDocument,
page : @page.PdfPage,
width : Double,
) -> @page.PdfPage raise @core.PdfError {
let ops = self.pdf_page_parse_content_ops(page.resources, page.content)
{
..page,
content: [
pdf_content_stream_of_ops(
pdf_thin_lines_ops(self, page.resources, ops, width),
),
],
}
}
///|
/// Set a minimum line width on selected one-based pages.
///
/// Passing a negative `width` sets the corresponding maximum line width, as in
/// cpdf's `thinlines` implementation.
pub fn PdfDocument::thin_lines(
self : PdfDocument,
page_numbers : ArrayView[Int],
width : Double,
) -> PdfDocument raise @core.PdfError {
let document = self.copy()
let pages = document.pages_of_pagetree()
let selected_pages = @core.pdf_number_set(page_numbers)
let rewritten : Array[@page.PdfPage] = Array(capacity=pages.length())
for index in 0.. PdfDocument raise @core.PdfError {
document.thin_lines(page_numbers, width)
}
///|
/// Compatibility wrapper matching cpdftweak's `thinlines` name and order.
pub fn pdf_thinlines(
page_numbers : ArrayView[Int],
width : Double,
document : PdfDocument,
) -> PdfDocument raise @core.PdfError {
document.thin_lines(page_numbers, width)
}