Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ serde = { version = "1" }
serde_derive = { version = "1" }
flate2 = "1.0.35" # for decompression of builtin fonts
serde_json = { version = "1" }
smallvec = { version = "1.15.1", features = ["serde"] }
# Feature-gated dependencies
rust-fontconfig = { version = "2.0.0", default-features = false, features = ["std", "parsing"], optional = true }
xmlparser = { version = "0.13.6", default-features = false, optional = true }
Expand Down
12 changes: 5 additions & 7 deletions src/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2311,10 +2311,9 @@ pub fn parse_op(
if let Some(arr_obj) = op.operands.get(0) {
// parse array of numbers
if let Ok(arr) = arr_obj.as_array() {
let pattern: Vec<i64> =
arr.iter().map(|item| to_f32(item) as i64).collect();
let offset = to_f32(&op.operands[1]) as i64;
let dash = LineDashPattern::from_array(&pattern, offset);
let pattern = arr.iter().map(|item| to_f32(item)).collect();
let offset = to_f32(&op.operands[1]);
let dash = LineDashPattern{offset, pattern};
out_ops.push(Op::SetLineDashPattern { dash });
}
}
Expand Down Expand Up @@ -3239,9 +3238,8 @@ mod extgstate {

if let Some(obj) = dict.get(b"D").ok() {
if let Some(arr) = obj.as_array().ok() {
// Parse the dash pattern as a flat array of integers.
let dashes: Vec<i64> = arr.iter().filter_map(|o| parse_i64(o)).collect();
gs.line_dash_pattern = Some(LineDashPattern::from_array(&dashes, 0)); // default offset = 0
let pattern = arr.iter().filter_map(|o| parse_f32(o)).collect();
gs.line_dash_pattern = Some(LineDashPattern{offset: 0.0, pattern}); // default offset = 0
changed.insert(ChangedField::LineDashPattern);
}
}
Expand Down
109 changes: 7 additions & 102 deletions src/graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,119 +340,24 @@ impl FromIterator<(Point, bool)> for Polygon {
}

/// Line dash pattern is made up of a total width
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[derive(Debug, Clone, Default, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LineDashPattern {
/// Offset at which the dashing pattern should start, measured from the beginning ot the line
/// Default: 0 (start directly where the line starts)
pub offset: i64,
/// Length of the first dash in the dash pattern. If `None`, the line will be solid (good for
/// resetting the dash pattern)
#[serde(default)]
pub dash_1: Option<i64>,
/// Whitespace after the first dash. If `None`, whitespace will be the same as length_1st,
/// meaning that the line will have dash - whitespace - dash - whitespace in even offsets
#[serde(default)]
pub gap_1: Option<i64>,
/// Length of the second dash in the dash pattern. If None, will be equal to length_1st
#[serde(default)]
pub dash_2: Option<i64>,
/// Same as whitespace_1st, but for length_2nd
#[serde(default)]
pub gap_2: Option<i64>,
/// Length of the second dash in the dash pattern. If None, will be equal to length_1st
#[serde(default)]
pub dash_3: Option<i64>,
/// Same as whitespace_1st, but for length_3rd
#[serde(default)]
pub gap_3: Option<i64>,
pub offset: f32,
/// Dash, gap, dash, gap, ...
pub pattern: smallvec::SmallVec<[f32;8]>,
}

impl LineDashPattern {
pub fn as_array(&self) -> Vec<i64> {
[
self.dash_1,
self.gap_1,
self.dash_2,
self.gap_2,
self.dash_3,
self.gap_3,
]
.iter()
.copied()
.take_while(Option::is_some)
.flatten()
.collect()
}

pub fn get_svg_id(&self) -> String {
let dash_array = self.as_array();
dash_array
self.pattern
.iter()
.map(|num| num.to_string())
.collect::<Vec<_>>()
.join(",")
}

/// Builds a `LineDashPattern` from a slice of up to 6 integers.
///
/// - The array is interpreted in dash-gap pairs:
/// - If `dashes[0]` is present => `dash_1 = Some(...)`
/// - If `dashes[1]` is present => `gap_1 = Some(...)`
/// - If `dashes[2]` is present => `dash_2 = Some(...)`
/// - If `dashes[3]` is present => `gap_2 = Some(...)`
/// - If `dashes[4]` is present => `dash_3 = Some(...)`
/// - If `dashes[5]` is present => `gap_3 = Some(...)`
///
/// Any extra elements beyond index 5 are ignored. If the slice is empty,
/// the line is solid (all fields `None`).
pub fn from_array(dashes: &[i64], offset: i64) -> Self {
let mut pat = LineDashPattern::default();
pat.offset = offset;

match dashes.len() {
0 => {
// No dashes => solid line
// (everything is None, which is already default)
}
1 => {
pat.dash_1 = Some(dashes[0]);
}
2 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
}
3 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
}
4 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
}
5 => {
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
pat.dash_3 = Some(dashes[4]);
}
_ => {
// 6 or more elements => fill all 3 dash-gap pairs
pat.dash_1 = Some(dashes[0]);
pat.gap_1 = Some(dashes[1]);
pat.dash_2 = Some(dashes[2]);
pat.gap_2 = Some(dashes[3]);
pat.dash_3 = Some(dashes[4]);
pat.gap_3 = Some(dashes[5]);
}
}

pat
}
}

/// __See PDF Reference Page 216__ - Line join style
Expand Down Expand Up @@ -1216,9 +1121,9 @@ pub fn extgstate_to_dict(val: &ExtendedGraphicsState) -> LoDictionary {
}

// Optional parameters
if let Some(ldp) = val.line_dash_pattern {
if let Some(ldp) = &val.line_dash_pattern {
if val.changed_fields.contains(&ChangedField::LineDashPattern) {
let array = ldp.as_array().into_iter().map(Integer).collect();
let array = ldp.pattern.iter().copied().map(Real).collect();
gs_operations.push(("D".to_string(), Array(array)));
}
}
Expand Down
42 changes: 14 additions & 28 deletions src/html/border.rs
Original file line number Diff line number Diff line change
Expand Up @@ -967,13 +967,11 @@ fn render_dashed_border_side(
// Set dash pattern
ops.push(Op::SetLineDashPattern {
dash: LineDashPattern {
offset: 0,
dash_1: Some((width * 3.0) as i64),
gap_1: Some((width * 2.0) as i64),
dash_2: None,
gap_2: None,
dash_3: None,
gap_3: None,
offset: 0.0,
pattern: [
width * 3.0, // dash
width * 2.0 // gap
].into()
},
});

Expand All @@ -982,13 +980,8 @@ fn render_dashed_border_side(
// Reset dash pattern
ops.push(Op::SetLineDashPattern {
dash: LineDashPattern {
offset: 0,
dash_1: None,
gap_1: None,
dash_2: None,
gap_2: None,
dash_3: None,
gap_3: None,
offset: 0.0,
pattern: [].into()
},
});
}
Expand All @@ -1008,13 +1001,11 @@ fn render_dotted_border_side(
// Set dot pattern (dash = width, gap = width)
ops.push(Op::SetLineDashPattern {
dash: LineDashPattern {
offset: 0,
dash_1: Some(width as i64),
gap_1: Some(width as i64),
dash_2: None,
gap_2: None,
dash_3: None,
gap_3: None,
offset: 0.0,
pattern: [
width, // dash
width // gap
].into()
},
});

Expand All @@ -1027,13 +1018,8 @@ fn render_dotted_border_side(
ops.push(Op::SetLineCapStyle { cap: LineCapStyle::Butt });
ops.push(Op::SetLineDashPattern {
dash: LineDashPattern {
offset: 0,
dash_1: None,
gap_1: None,
dash_2: None,
gap_2: None,
dash_3: None,
gap_3: None,
offset: 0.0,
pattern: [].into()
},
});
}
Expand Down
14 changes: 8 additions & 6 deletions src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,13 +1199,14 @@ fn render_line_to_svg(line: &Line, gst: &GraphicsStateVec, page_height: f32) ->
// Handle dash pattern if present
let dash_array = match gst.get_dash_array() {
Some(dash) => {
let dash_array = dash.as_array();
if dash_array.is_empty() {
// Dispatch SmallVec storage only once, by taking slice.
let dash_pattern = dash.pattern.as_slice();
if dash_pattern.is_empty() {
String::new()
} else {
format!(
" stroke-dasharray=\"{}\" stroke-dashoffset=\"{}\"",
dash_array
dash_pattern
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
Expand Down Expand Up @@ -1312,13 +1313,14 @@ fn render_polygon_to_svg(polygon: &Polygon, gst: &GraphicsStateVec, page_height:
// Handle dash pattern if present
let dash_array = match gst.get_dash_array() {
Some(dash) => {
let dash_array = dash.as_array();
if dash_array.is_empty() {
// Dispatch SmallVec storage only once, by taking slice.
let dash_pattern = dash.pattern.as_slice();
if dash_pattern.is_empty() {
String::new()
} else {
format!(
" stroke-dasharray=\"{}\" stroke-dashoffset=\"{}\"",
dash_array
dash_pattern
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
Expand Down
4 changes: 2 additions & 2 deletions src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,10 +570,10 @@ pub(crate) fn translate_operations(
content.push(LoOp::new("w", vec![Real(pt.0)]));
}
Op::SetLineDashPattern { dash } => {
let dash_array_ints = dash.as_array().into_iter().map(Integer).collect();
let dash_array_floats = dash.pattern.iter().copied().map(Real).collect();
content.push(LoOp::new(
"d",
vec![Array(dash_array_ints), Integer(dash.offset)],
vec![Array(dash_array_floats), Real(dash.offset)],
));
}
Op::SetLineJoinStyle { join } => {
Expand Down