Skip to content

Commit

Permalink
Support image in header footer (#680)
Browse files Browse the repository at this point in the history
* wip

* wip

* wip

* fix: image in header/footer

* fix: Changelog
  • Loading branch information
bokuweb committed Feb 19, 2024
1 parent 8224ff0 commit 72adfa6
Show file tree
Hide file tree
Showing 55 changed files with 4,534 additions and 223 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## @0.4.8 (19. Feb, 2024)

- Fixed a bug, image in header/footer is not stored in media when read.
- Fixed a bug, image in header/footer is broken.

## docx-wasm@0.0.278-rc27 (17. Jan, 2024)

- Support part of `pPrDefault`.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docx-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "docx-rs"
version = "0.4.7"
version = "0.4.8"
authors = ["bokuweb <bokuweb12@gmail.com>"]
repository = "https://github.com/bokuweb/docx-rs"
edition = "2018"
Expand Down
16 changes: 16 additions & 0 deletions docx-core/examples/image_in_header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use docx_rs::{Docx, Header, Paragraph, Pic, Run};
use std::{error::Error, io::Cursor};

fn main() -> Result<(), Box<dyn Error>> {
let cat = Pic::new(include_bytes!("../../images/cat_min.jpg"));
let header =
Header::new().add_paragraph(Paragraph::new().add_run(Run::new().add_image(cat.clone())));
let mut out = Vec::new();
let docx = Docx::new()
.header(header)
.add_paragraph(Paragraph::new().add_run(Run::new().add_image(cat)));
docx.build().pack(Cursor::new(&mut out))?;

std::fs::write("/tmp/out.docx", &out)?;
Ok(())
}
43 changes: 43 additions & 0 deletions docx-core/src/documents/footer_rels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::documents::BuildXML;
use crate::{xml_builder::*, ImageIdAndPath};
use serde::Serialize;

#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct FooterRels {
pub images: Vec<(String, String)>,
}

impl FooterRels {
pub fn new() -> FooterRels {
Default::default()
}

pub fn add_image(mut self, id: impl Into<String>, path: impl Into<String>) -> Self {
self.images.push((id.into(), path.into()));
self
}

pub(crate) fn set_images(&mut self, images: Vec<ImageIdAndPath>) {
self.images = images;
}
}

impl BuildXML for FooterRels {
fn build(&self) -> Vec<u8> {
let mut b = XMLBuilder::new();
b = b
.declaration(None)
.open_relationships("http://schemas.openxmlformats.org/package/2006/relationships");

for (id, path) in self.images.iter() {
b = b.relationship(
id,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
path,
)
}

b.close().build()
}
}
43 changes: 43 additions & 0 deletions docx-core/src/documents/header_rels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::documents::BuildXML;
use crate::{xml_builder::*, ImageIdAndPath};
use serde::Serialize;

#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct HeaderRels {
pub images: Vec<(String, String)>,
}

impl HeaderRels {
pub fn new() -> HeaderRels {
Default::default()
}

pub fn add_image(mut self, id: impl Into<String>, path: impl Into<String>) -> Self {
self.images.push((id.into(), path.into()));
self
}

pub(crate) fn set_images(&mut self, images: Vec<ImageIdAndPath>) {
self.images = images;
}
}

impl BuildXML for HeaderRels {
fn build(&self) -> Vec<u8> {
let mut b = XMLBuilder::new();
b = b
.declaration(None)
.open_relationships("http://schemas.openxmlformats.org/package/2006/relationships");

for (id, path) in self.images.iter() {
b = b.relationship(
id,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
path,
)
}

b.close().build()
}
}
155 changes: 155 additions & 0 deletions docx-core/src/documents/image_collector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use crate::{
DeleteChild, DrawingData, InsertChild, Paragraph, ParagraphChild, RunChild,
StructuredDataTagChild, Table, TableCellContent, TableChild, TableRowChild, TocContent,
};

pub(crate) fn collect_images_from_paragraph(
paragraph: &mut Paragraph,
images: &mut Vec<(String, String)>,
image_bufs: &mut Vec<(String, Vec<u8>)>,
id_prefix: Option<&str>,
) {
for child in &mut paragraph.children {
if let ParagraphChild::Run(run) = child {
for child in &mut run.children {
if let RunChild::Drawing(d) = child {
if let Some(DrawingData::Pic(pic)) = &mut d.data {
let b = std::mem::take(&mut pic.image);
let buf = image_bufs.iter().find(|x| x.0 == pic.id || x.1 == b);
let pic_id = if let Some(prefix) = id_prefix {
format!("{}{}", prefix, pic.id)
} else {
pic.id.clone()
};
if buf.as_ref().is_none() {
images.push((
pic_id.clone(),
// For now only png supported
format!("media/{}.png", pic_id),
));
image_bufs.push((pic_id.clone(), b));
pic.id = pic_id;
} else {
pic.id = buf.unwrap().0.clone();
}
}
}
}
} else if let ParagraphChild::Insert(ins) = child {
for child in &mut ins.children {
match child {
InsertChild::Run(run) => {
for child in &mut run.children {
if let RunChild::Drawing(d) = child {
if let Some(DrawingData::Pic(pic)) = &mut d.data {
images.push((
pic.id.clone(),
// For now only png supported
format!("media/{}.png", pic.id),
));
let b = std::mem::take(&mut pic.image);
image_bufs.push((pic.id.clone(), b));
}
}
}
}
InsertChild::Delete(del) => {
for d in &mut del.children {
if let DeleteChild::Run(run) = d {
for child in &mut run.children {
if let RunChild::Drawing(d) = child {
if let Some(DrawingData::Pic(pic)) = &mut d.data {
images.push((
pic.id.clone(),
// For now only png supported
format!("media/{}.png", pic.id),
));
let b = std::mem::take(&mut pic.image);
image_bufs.push((pic.id.clone(), b));
}
}
}
}
}
}
_ => {}
}
}
} else if let ParagraphChild::Delete(del) = child {
for d in &mut del.children {
if let DeleteChild::Run(run) = d {
for child in &mut run.children {
if let RunChild::Drawing(d) = child {
if let Some(DrawingData::Pic(pic)) = &mut d.data {
images.push((
pic.id.clone(),
// For now only png supported
format!("media/{}.png", pic.id),
));
let b = std::mem::take(&mut pic.image);
image_bufs.push((pic.id.clone(), b));
}
}
}
}
}
}
}
}

pub(crate) fn collect_images_from_table(
table: &mut Table,
images: &mut Vec<(String, String)>,
image_bufs: &mut Vec<(String, Vec<u8>)>,
id_prefix: Option<&str>,
) {
for TableChild::TableRow(row) in &mut table.rows {
for TableRowChild::TableCell(cell) in &mut row.cells {
for content in &mut cell.children {
match content {
TableCellContent::Paragraph(paragraph) => {
collect_images_from_paragraph(paragraph, images, image_bufs, id_prefix);
}
TableCellContent::Table(table) => {
collect_images_from_table(table, images, image_bufs, id_prefix)
}
TableCellContent::StructuredDataTag(tag) => {
for child in &mut tag.children {
if let StructuredDataTagChild::Paragraph(paragraph) = child {
collect_images_from_paragraph(
paragraph, images, image_bufs, id_prefix,
);
}
if let StructuredDataTagChild::Table(table) = child {
collect_images_from_table(table, images, image_bufs, id_prefix);
}
}
}
TableCellContent::TableOfContents(t) => {
for child in &mut t.before_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(
paragraph, images, image_bufs, id_prefix,
);
}
if let TocContent::Table(table) = child {
collect_images_from_table(table, images, image_bufs, id_prefix);
}
}

for child in &mut t.after_contents {
if let TocContent::Paragraph(paragraph) = child {
collect_images_from_paragraph(
paragraph, images, image_bufs, id_prefix,
);
}
if let TocContent::Table(table) = child {
collect_images_from_table(table, images, image_bufs, id_prefix);
}
}
}
}
}
}
}
}

0 comments on commit 72adfa6

Please sign in to comment.