Skip to content

Commit 7254cdb

Browse files
committed
[feat] added hyperlinks
1 parent 9edfc0b commit 7254cdb

File tree

1 file changed

+52
-2
lines changed

1 file changed

+52
-2
lines changed

src/lib.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ struct InnerCell {
9898
style: Option<u32>
9999
}
100100

101+
#[derive(Clone)]
102+
struct Hyperlink {
103+
cell: String,
104+
url: String,
105+
}
101106
impl InnerCell {
102107
pub fn new(cell: String, style: &Option<u32>) -> InnerCell {
103108
InnerCell {
@@ -132,9 +137,9 @@ pub fn import_to_xlsx(raw_data: &JsValue) -> Vec<u8> {
132137
let w = Cursor::new(buf);
133138
let mut zip = zip::ZipWriter::new(w);
134139
let options = FileOptions::default().compression_method(zip::CompressionMethod::Stored).unix_permissions(0o755);
135-
136140
for (sheet_index, sheet) in data.data.iter().enumerate() {
137141
let mut rows: Vec<Vec<InnerCell>> = vec!();
142+
let mut hyperlinks: Vec<Hyperlink> = vec![];
138143
match &sheet.cells {
139144
Some(cells) => {
140145
for (row_index, row) in cells.iter().enumerate() {
@@ -144,7 +149,12 @@ pub fn import_to_xlsx(raw_data: &JsValue) -> Vec<u8> {
144149
Some(cell) => {
145150
let cell_name = cell_offsets_to_index(row_index, col_index);
146151
let mut inner_cell = InnerCell::new(cell_name, &cell.s);
147-
152+
if let Some(link) = &cell.hyperlink {
153+
hyperlinks.push(Hyperlink {
154+
cell: inner_cell.cell.clone(),
155+
url: link.clone(),
156+
});
157+
}
148158
match &cell.v {
149159
Some(value) => {
150160
if !value.is_empty() {
@@ -222,6 +232,9 @@ pub fn import_to_xlsx(raw_data: &JsValue) -> Vec<u8> {
222232
}
223233

224234
let sheet_info = get_sheet_info(sheet.name.clone(), sheet_index);
235+
let rels_path = format!("xl/worksheets/_rels/sheet{}.xml.rels", sheet_index + 1);
236+
zip.start_file(rels_path.clone(), options).unwrap();
237+
zip.write_all(get_sheet_rels(&hyperlinks).as_bytes()).unwrap();
225238
zip.start_file(sheet_info.0.clone(), options).unwrap();
226239
zip.write_all(
227240
get_sheet_data(
@@ -232,6 +245,7 @@ pub fn import_to_xlsx(raw_data: &JsValue) -> Vec<u8> {
232245
sheet.frozen_rows,
233246
sheet.frozen_cols,
234247
&sheet.validations,
248+
&hyperlinks,
235249
)
236250
.as_bytes(),
237251
).unwrap();
@@ -448,6 +462,7 @@ fn get_sheet_data(
448462
frozen_rows: Option<u32>,
449463
frozen_cols: Option<u32>,
450464
validations: &Option<Vec<DataValidation>>,
465+
hyperlinks: &[Hyperlink],
451466
) -> String {
452467
let mut worksheet = Element::new("worksheet");
453468
let mut sheet_view = Element::new("sheetView");
@@ -651,6 +666,22 @@ fn get_sheet_data(
651666
worksheet_children.push(dv_el);
652667
}
653668
}
669+
if !hyperlinks.is_empty() {
670+
let mut hyperlinks_el = Element::new("hyperlinks");
671+
let mut hyperlink_children = vec![];
672+
673+
for (i, h) in hyperlinks.iter().enumerate() {
674+
let mut hyperlink_el = Element::new("hyperlink");
675+
hyperlink_el
676+
.add_attr("r:id", format!("rId{}", 1000 + i))
677+
.add_attr("ref", &h.cell);
678+
hyperlink_children.push(hyperlink_el);
679+
}
680+
681+
hyperlinks_el.add_children(hyperlink_children);
682+
worksheet_children.push(hyperlinks_el);
683+
}
684+
654685
worksheet
655686
.add_attr("xmlns:xm", "http://schemas.microsoft.com/office/excel/2006/main")
656687
.add_attr("xmlns:x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac")
@@ -689,6 +720,25 @@ fn get_sheet_info(name: Option<String>, index: usize) -> (String, String) {
689720
(format!("xl/worksheets/sheet{}.xml", index + 1), sheet_name)
690721
}
691722

723+
fn get_sheet_rels(hyperlinks: &[Hyperlink]) -> String {
724+
let mut rels = Element::new("Relationships");
725+
rels.add_attr("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships");
726+
727+
let mut children = vec![];
728+
729+
for (i, link) in hyperlinks.iter().enumerate() {
730+
let mut rel = Element::new("Relationship");
731+
rel.add_attr("Id", format!("rId{}", 1000 + i));
732+
rel.add_attr("Type", "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink");
733+
rel.add_attr("Target", &link.url);
734+
rel.add_attr("TargetMode", "External");
735+
children.push(rel);
736+
}
737+
738+
rels.add_children(children);
739+
rels.to_xml()
740+
}
741+
692742
fn get_nav(sheets: Vec<(String, String)>) -> (String, String, String) {
693743
let mut content_types = Element::new("Types");
694744
content_types.add_attr("xmlns", "http://schemas.openxmlformats.org/package/2006/content-types");

0 commit comments

Comments
 (0)