Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/reader/xlsx/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub(crate) fn read(
xml_read_loop!(
reader,
Event::Start(ref e) => {
if e.name().into_inner() == b"xdr:wsDr" {
if matches!(e.name().into_inner(), b"xdr:wsDr" | b"wsDr") {
let mut obj = WorksheetDrawing::default();
obj.set_attributes(
&mut reader,
Expand Down
36 changes: 10 additions & 26 deletions src/reader/xlsx/worksheet.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,18 @@
use std::collections::HashMap;

use quick_xml::{
Reader,
events::Event,
};
use quick_xml::{Reader, escape, events::Event};

use super::{
XlsxError,
driver::{
get_attribute,
get_attribute_value,
xml_read_loop,
},
driver::{get_attribute, get_attribute_value, xml_read_loop},
};
use crate::{
helper::formula::FormulaToken,
structs::{
Cells,
Columns,
ConditionalFormatting,
DataValidations,
Hyperlink,
OleObjects,
Row,
SharedStringTable,
SheetProtection,
Stylesheet,
Worksheet,
Cells, Columns, ConditionalFormatting, DataValidations, Hyperlink, OleObjects, Row,
SharedStringTable, SheetProtection, Stylesheet, Worksheet,
office2010::excel::DataValidations as DataValidations2010,
raw::{
RawRelationships,
RawWorksheet,
},
raw::{RawRelationships, RawWorksheet},
},
};

Expand Down Expand Up @@ -287,12 +268,15 @@ fn get_hyperlink(

let coordition = get_attribute(e, b"ref").unwrap_or_default();
if let Some(v) = get_attribute(e, b"location") {
hyperlink.set_url(v);
hyperlink.set_url(escape::unescape(&v).unwrap().to_string());
hyperlink.set_location(true);
}
if let Some(v) = get_attribute(e, b"tooltip") {
hyperlink.set_tooltip(escape::unescape(&v).unwrap().to_string());
}
if let Some(v) = get_attribute(e, b"r:id") {
let relationship = raw_relationships.unwrap().relationship_by_rid(&v);
hyperlink.set_url(relationship.target());
hyperlink.set_url(escape::unescape(relationship.target()).unwrap().to_string());
}
Comment on lines 269 to 280
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new XML-unescaping for location, tooltip, and relationship targets changes hyperlink semantics and is meant to fix escaped query parameters. There doesn’t appear to be a regression test asserting that a hyperlink with an escaped attribute/relationship target (e.g. containing &) is read back as the unescaped string, or that tooltip is preserved. Adding an integration test fixture (or a minimal constructed xlsx) that exercises these cases would help prevent future regressions.

Copilot uses AI. Check for mistakes.
(coordition, hyperlink)
}
4 changes: 2 additions & 2 deletions src/structs/drawing/spreadsheet/blip_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,11 @@ impl BlipFill {
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:blipFill" {
if matches!(e.name().into_inner(), b"xdr:blipFill" | b"blipFill") {
return;
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:blipFill")
Event::Eof => panic!("Error: Could not find {} end element", "blipFill")
);
}

Expand Down
36 changes: 13 additions & 23 deletions src/structs/drawing/spreadsheet/marker_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,24 @@
use std::io::Cursor;

use quick_xml::{
Reader,
Writer,
events::{
BytesStart,
Event,
},
Reader, Writer,
events::{BytesStart, Event},
};

use crate::{
helper::coordinate::{
adjustment_insert_coordinate,
adjustment_remove_coordinate,
coordinate_from_index,
index_from_coordinate,
is_remove_coordinate,
adjustment_insert_coordinate, adjustment_remove_coordinate, coordinate_from_index,
index_from_coordinate, is_remove_coordinate,
},
traits::AdjustmentCoordinate,
writer::driver::{
write_end_tag,
write_start_tag,
write_text_node,
},
writer::driver::{write_end_tag, write_start_tag, write_text_node},
};

#[derive(Clone, Default, Debug)]
pub struct MarkerType {
col: u32,
col: u32,
col_off: i32,
row: u32,
row: u32,
row_off: i32,
}
impl MarkerType {
Expand Down Expand Up @@ -153,19 +142,20 @@ impl MarkerType {
match reader.read_event_into(&mut buf) {
Ok(Event::Text(e)) => string_value = e.unescape().unwrap().to_string(),
Ok(Event::End(ref e)) => match e.name().into_inner() {
b"xdr:col" => {
b"xdr:col" | b"col" => {
self.col = string_value.parse::<u32>().unwrap();
}
b"xdr:colOff" => {
b"xdr:colOff" | b"colOff" => {
self.col_off = string_value.parse::<i32>().unwrap();
}
b"xdr:row" => {
b"xdr:row" | b"row" => {
self.row = string_value.parse::<u32>().unwrap();
}
b"xdr:rowOff" => {
b"xdr:rowOff" | b"rowOff" => {
self.row_off = string_value.parse::<i32>().unwrap();
}
b"xdr:from" | b"xdr:to" => return,
b"xdr:from" | b"from" => return,
b"xdr:to" | b"to" => return,
_ => (),
},
Ok(Event::Eof) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ impl NonVisualDrawingProperties {
xml_read_loop!(
reader,
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:cNvPr" {
if matches!(e.name().into_inner(), b"xdr:cNvPr" | b"cNvPr") {
return;
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:cNvPr")
Event::Eof => panic!("Error: Could not find {} end element", "cNvPr")
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ impl NonVisualPictureDrawingProperties {
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:cNvPicPr" {
if matches!(e.name().into_inner(), b"xdr:cNvPicPr" | b"cNvPicPr") {
return
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:cNvPicPr")
Event::Eof => panic!("Error: Could not find {} end element", "cNvPicPr")
);
}

Expand Down
12 changes: 6 additions & 6 deletions src/structs/drawing/spreadsheet/non_visual_picture_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ impl NonVisualPictureProperties {
reader,
Event::Start(ref e) => {
match e.name().into_inner() {
b"xdr:cNvPicPr" => {
b"xdr:cNvPicPr" | b"cNvPicPr" => {
self.non_visual_picture_drawing_properties
.set_attributes(reader, e, false);
}
b"xdr:cNvPr" => {
b"xdr:cNvPr" | b"cNvPr" => {
self.non_visual_drawing_properties
.set_attributes(reader, e, false);
}
Expand All @@ -119,23 +119,23 @@ impl NonVisualPictureProperties {
},
Event::Empty(ref e) => {
match e.name().into_inner() {
b"xdr:cNvPicPr" => {
b"xdr:cNvPicPr" | b"cNvPicPr" => {
self.non_visual_picture_drawing_properties
.set_attributes(reader, e, true);
}
b"xdr:cNvPr" => {
b"xdr:cNvPr" | b"cNvPr" => {
self.non_visual_drawing_properties
.set_attributes(reader, e, true);
}
_ => (),
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:nvPicPr" {
if matches!(e.name().into_inner(), b"xdr:nvPicPr" | b"nvPicPr") {
return;
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:nvPicPr")
Event::Eof => panic!("Error: Could not find {} end element", "nvPicPr")
);
}

Expand Down
14 changes: 7 additions & 7 deletions src/structs/drawing/spreadsheet/one_cell_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,20 +202,20 @@ impl OneCellAnchor {
reader,
Event::Start(ref e) => {
match e.name().into_inner() {
b"xdr:from" => {
b"xdr:from" | b"from" => {
self.from_marker.set_attributes(reader, e);
}
b"xdr:grpSp" => {
b"xdr:grpSp" | b"grpSp" => {
let mut obj = GroupShape::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_group_shape(obj);
}
b"xdr:sp" => {
b"xdr:sp" | b"sp" => {
let mut obj = Shape::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_shape(obj);
}
b"xdr:pic" => {
b"xdr:pic" | b"pic" => {
let mut obj = Picture::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_picture(obj);
Comment on lines +208 to 221
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OneCellAnchor now accepts unprefixed grpSp/sp tags and delegates to GroupShape/Shape, but those parsers currently only match xdr:-prefixed tag names for their internal elements and end tags. If a drawing uses a default namespace (no xdr: prefixes), parsing a sp/grpSp here will likely never hit the expected end element and can panic at EOF. Either update the delegated parsers to accept unprefixed tags too, or limit unprefixed handling here to elements whose parsers are already prefix-agnostic (e.g. from, pic).

Copilot uses AI. Check for mistakes.
Expand All @@ -224,16 +224,16 @@ impl OneCellAnchor {
}
},
Event::Empty(ref e) => {
if e.name().into_inner() == b"xdr:ext" {
if matches!(e.name().into_inner(), b"xdr:ext" | b"ext") {
self.extent.set_attributes(reader, e);
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:oneCellAnchor" {
if matches!(e.name().into_inner(), b"xdr:oneCellAnchor" | b"oneCellAnchor") {
return
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:oneCellAnchor")
Event::Eof => panic!("Error: Could not find {} end element", "oneCellAnchor")
);
}

Expand Down
10 changes: 5 additions & 5 deletions src/structs/drawing/spreadsheet/picture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,25 +129,25 @@ impl Picture {
reader,
Event::Start(ref e) => {
match e.name().into_inner() {
b"xdr:nvPicPr" => {
b"xdr:nvPicPr" | b"nvPicPr" => {
self.non_visual_picture_properties.set_attributes(reader, e);
}
b"xdr:blipFill" => {
b"xdr:blipFill" | b"blipFill" => {
self.blip_fill
.set_attributes(reader, e, drawing_relationships);
}
b"xdr:spPr" => {
b"xdr:spPr" | b"spPr" => {
self.shape_properties.set_attributes(reader, e, drawing_relationships);
}
_ => (),
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:pic" {
if matches!(e.name().into_inner(), b"xdr:pic" | b"pic") {
return;
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:pic")
Event::Eof => panic!("Error: Could not find {} end element", "pic")
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/structs/drawing/spreadsheet/shape_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,11 @@ impl ShapeProperties {
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:spPr" {
if matches!(e.name().into_inner(), b"xdr:spPr" | b"spPr") {
return;
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:spPr")
Event::Eof => panic!("Error: Could not find {} end element", "spPr")
);
}

Expand Down
18 changes: 9 additions & 9 deletions src/structs/drawing/spreadsheet/two_cell_anchor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,33 +342,33 @@ impl TwoCellAnchor {
reader,
Event::Start(ref e) => {
match e.name().into_inner() {
b"xdr:from" => {
b"xdr:from" | b"from" => {
self.from_marker.set_attributes(reader, e);
}
b"xdr:to" => {
b"xdr:to" | b"to" => {
self.to_marker.set_attributes(reader, e);
}
b"xdr:grpSp" => {
b"xdr:grpSp" | b"grpSp" => {
let mut obj = GroupShape::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_group_shape(obj);
}
b"xdr:graphicFrame" => {
b"xdr:graphicFrame" | b"graphicFrame" => {
let mut obj = GraphicFrame::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_graphic_frame(obj);
}
b"xdr:sp" => {
b"xdr:sp" | b"sp" => {
let mut obj = Shape::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_shape(obj);
}
b"xdr:cxnSp" => {
b"xdr:cxnSp" | b"cxnSp" => {
let mut obj = ConnectionShape::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_connection_shape(obj);
}
b"xdr:pic" => {
b"xdr:pic" | b"pic" => {
Comment on lines +351 to +371
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TwoCellAnchor now accepts unprefixed tags (e.g. graphicFrame, sp, grpSp, cxnSp) and immediately delegates to the corresponding parsers, but several of those parsers still only recognize xdr:-prefixed start/end tags (e.g. GraphicFrame returns only on xdr:graphicFrame, and Shape/GroupShape also key off xdr:). With an unprefixed drawing XML (default namespace), this will likely fail to terminate the delegated parse loop and panic at EOF. Consider either (a) making the downstream parsers prefix-agnostic as well, or (b) matching by local-name (strip prefix) consistently across the drawing reader so prefix variations can’t desync parent/child parsing.

Copilot uses AI. Check for mistakes.
let mut obj = Picture::default();
obj.set_attributes(reader, e, drawing_relationships);
self.set_picture(obj);
Expand All @@ -377,11 +377,11 @@ impl TwoCellAnchor {
}
},
Event::End(ref e) => {
if e.name().into_inner() == b"xdr:twoCellAnchor" {
if matches!(e.name().into_inner(), b"xdr:twoCellAnchor" | b"twoCellAnchor") {
return
}
},
Event::Eof => panic!("Error: Could not find {} end element", "xdr:twoCellAnchor")
Event::Eof => panic!("Error: Could not find {} end element", "twoCellAnchor")
);
}

Expand Down
Loading
Loading