parent
dd0457bf4f
commit
5dea38a6d1
|
@ -17,5 +17,6 @@ reg.json
|
|||
docx-core/tests/output/*.docx
|
||||
docx-wasm/*.tgz
|
||||
docx-core/bindings
|
||||
docx-core/tests/output/*.json
|
||||
test.json
|
||||
image.json
|
||||
docx-core/tests/output/*.json
|
|
@ -35,6 +35,12 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
|
@ -176,6 +182,7 @@ checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
|
|||
name = "docx-rs"
|
||||
version = "0.3.4"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"image",
|
||||
"insta",
|
||||
"pretty_assertions",
|
||||
|
|
|
@ -26,6 +26,7 @@ thiserror = "1.0"
|
|||
zip = { version = "0.5.6", default-features = false, features = ["deflate"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
base64 = "0.13.0"
|
||||
image = "0.23.14"
|
||||
ts-rs = "6.1"
|
||||
|
||||
|
|
|
@ -4,17 +4,17 @@ use std::io::Read;
|
|||
use docx_rs::*;
|
||||
|
||||
pub fn main() -> Result<(), DocxError> {
|
||||
let path = std::path::Path::new("./output/image.docx");
|
||||
let path = std::path::Path::new("./output/examples/image_floating.docx");
|
||||
let file = File::create(&path).unwrap();
|
||||
let mut img = File::open("./images/cat_min.jpg").unwrap();
|
||||
let mut buf = Vec::new();
|
||||
let _ = img.read_to_end(&mut buf).unwrap();
|
||||
|
||||
let pic = Pic::new(buf)
|
||||
.size(320, 240)
|
||||
.size(320 * 9525, 240 * 9525)
|
||||
.floating()
|
||||
.offset_x(300)
|
||||
.offset_y(400);
|
||||
.offset_x(300 * 9525)
|
||||
.offset_y(400 * 9525);
|
||||
Docx::new()
|
||||
.add_paragraph(Paragraph::new().add_run(Run::new().add_image(pic)))
|
||||
.build()
|
||||
|
|
|
@ -4,13 +4,13 @@ use std::io::Read;
|
|||
use docx_rs::*;
|
||||
|
||||
pub fn main() -> Result<(), DocxError> {
|
||||
let path = std::path::Path::new("./output/image.docx");
|
||||
let path = std::path::Path::new("./output/examples/image_inline.docx");
|
||||
let file = File::create(&path).unwrap();
|
||||
let mut img = File::open("./images/cat_min.jpg").unwrap();
|
||||
let mut buf = Vec::new();
|
||||
let _ = img.read_to_end(&mut buf).unwrap();
|
||||
|
||||
let pic = Pic::new(buf).size(320, 240);
|
||||
let pic = Pic::new(buf).size(320 * 9525, 240 * 9525);
|
||||
Docx::new()
|
||||
.add_paragraph(Paragraph::new().add_run(Run::new().add_text("🐱").add_image(pic)))
|
||||
.build()
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use docx_rs::*;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub fn main() {
|
||||
let mut file = File::open("./image.docx").unwrap();
|
||||
let mut buf = vec![];
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
|
||||
let mut file = File::create("./image.json").unwrap();
|
||||
let res = read_docx(&buf).unwrap().json();
|
||||
file.write_all(res.as_bytes()).unwrap();
|
||||
file.flush().unwrap();
|
||||
}
|
|
@ -15,14 +15,14 @@ pub struct Document {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum DocumentChild {
|
||||
Paragraph(Paragraph),
|
||||
Table(Table),
|
||||
Paragraph(Box<Paragraph>),
|
||||
Table(Box<Table>),
|
||||
BookmarkStart(BookmarkStart),
|
||||
BookmarkEnd(BookmarkEnd),
|
||||
CommentStart(Box<CommentRangeStart>),
|
||||
CommentEnd(CommentRangeEnd),
|
||||
StructuredDataTag(StructuredDataTag),
|
||||
TableOfContents(TableOfContents),
|
||||
StructuredDataTag(Box<StructuredDataTag>),
|
||||
TableOfContents(Box<TableOfContents>),
|
||||
}
|
||||
|
||||
impl Serialize for DocumentChild {
|
||||
|
@ -102,7 +102,7 @@ impl Document {
|
|||
if p.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(DocumentChild::Paragraph(p));
|
||||
self.children.push(DocumentChild::Paragraph(Box::new(p)));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ impl Document {
|
|||
if t.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(DocumentChild::Table(t));
|
||||
self.children.push(DocumentChild::Table(Box::new(t)));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -198,12 +198,13 @@ impl Document {
|
|||
if t.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(DocumentChild::StructuredDataTag(t));
|
||||
self.children
|
||||
.push(DocumentChild::StructuredDataTag(Box::new(t)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_table_of_contents(mut self, t: TableOfContents) -> Self {
|
||||
self.children.push(DocumentChild::TableOfContents(t));
|
||||
self.children.push(DocumentChild::TableOfContents(Box::new(t)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ use super::*;
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DocumentRels {
|
||||
pub has_comments: bool,
|
||||
pub has_numberings: bool,
|
||||
pub image_ids: Vec<usize>,
|
||||
pub images: Vec<(String, String)>,
|
||||
pub custom_xml_count: usize,
|
||||
pub header_count: usize,
|
||||
pub footer_count: usize,
|
||||
|
@ -26,19 +26,6 @@ impl DocumentRels {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for DocumentRels {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
has_comments: false,
|
||||
has_numberings: false,
|
||||
image_ids: vec![],
|
||||
custom_xml_count: 0,
|
||||
header_count: 0,
|
||||
footer_count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for DocumentRels {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let mut b = XMLBuilder::new();
|
||||
|
@ -106,11 +93,11 @@ impl BuildXML for DocumentRels {
|
|||
)
|
||||
}
|
||||
|
||||
for id in self.image_ids.iter() {
|
||||
for (id, path) in self.images.iter() {
|
||||
b = b.relationship(
|
||||
&create_pic_rid(*id),
|
||||
id,
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||
&format!("media/image{}.jpg", *id),
|
||||
path,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use serde::Serialize;
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AGraphic {
|
||||
pub children: Vec<AGraphicData>,
|
||||
|
@ -21,12 +21,6 @@ impl AGraphic {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for AGraphic {
|
||||
fn default() -> Self {
|
||||
Self { children: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for AGraphic {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let b = XMLBuilder::new();
|
||||
|
|
|
@ -22,6 +22,7 @@ pub struct AGraphicData {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum GraphicDataChild {
|
||||
Shape(WpsShape),
|
||||
Pic(Pic),
|
||||
}
|
||||
|
||||
impl Serialize for GraphicDataChild {
|
||||
|
@ -36,6 +37,12 @@ impl Serialize for GraphicDataChild {
|
|||
t.serialize_field("data", s)?;
|
||||
t.end()
|
||||
}
|
||||
GraphicDataChild::Pic(ref s) => {
|
||||
let mut t = serializer.serialize_struct("Pic", 2)?;
|
||||
t.serialize_field("type", "pic")?;
|
||||
t.serialize_field("data", s)?;
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +101,7 @@ impl BuildXML for AGraphicData {
|
|||
for c in &self.children {
|
||||
match c {
|
||||
GraphicDataChild::Shape(t) => b = b.add_child(t),
|
||||
GraphicDataChild::Pic(t) => b = b.add_child(t),
|
||||
}
|
||||
}
|
||||
b.close().build()
|
||||
|
|
|
@ -1,105 +1,31 @@
|
|||
use super::*;
|
||||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::documents::BuildXML;
|
||||
use crate::types::*;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, PartialEq, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Drawing {
|
||||
pub position_type: DrawingPositionType,
|
||||
pub position_h: DrawingPosition,
|
||||
pub position_v: DrawingPosition,
|
||||
pub data: Option<DrawingData>,
|
||||
// TODO: Old definition, remove later
|
||||
pub children: Vec<DrawingChild>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DrawingData {
|
||||
Pic(Pic),
|
||||
}
|
||||
|
||||
// TODO: Old definition, remove later
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum DrawingChild {
|
||||
WpAnchor(WpAnchor),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
pub enum DrawingPositionType {
|
||||
Anchor,
|
||||
Inline {
|
||||
dist_t: usize,
|
||||
dist_b: usize,
|
||||
dist_l: usize,
|
||||
dist_r: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl Serialize for DrawingChild {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match *self {
|
||||
DrawingChild::WpAnchor(ref s) => {
|
||||
let mut t = serializer.serialize_struct("WpAnchor", 2)?;
|
||||
t.serialize_field("type", "anchor")?;
|
||||
t.serialize_field("data", s)?;
|
||||
t.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drawing {
|
||||
pub fn new() -> Drawing {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
// TODO: Remove later
|
||||
pub fn add_anchor(mut self, a: WpAnchor) -> Drawing {
|
||||
self.children.push(DrawingChild::WpAnchor(a));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pic(mut self, pic: Pic) -> Drawing {
|
||||
self.data = Some(DrawingData::Pic(pic));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn floating(mut self) -> Drawing {
|
||||
self.position_type = DrawingPositionType::Anchor;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position_h(mut self, pos: DrawingPosition) -> Drawing {
|
||||
self.position_h = pos;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position_v(mut self, pos: DrawingPosition) -> Drawing {
|
||||
self.position_v = pos;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Drawing {
|
||||
fn default() -> Self {
|
||||
Drawing {
|
||||
position_type: DrawingPositionType::Inline {
|
||||
dist_t: 0,
|
||||
dist_b: 0,
|
||||
dist_l: 0,
|
||||
dist_r: 0,
|
||||
},
|
||||
data: None,
|
||||
position_v: DrawingPosition::Offset(0),
|
||||
position_h: DrawingPosition::Offset(0),
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for Box<Drawing> {
|
||||
|
@ -107,29 +33,50 @@ impl BuildXML for Box<Drawing> {
|
|||
let b = XMLBuilder::new();
|
||||
let mut b = b.open_drawing();
|
||||
|
||||
if let DrawingPositionType::Inline { .. } = self.position_type {
|
||||
b = b.open_wp_inline("0", "0", "0", "0")
|
||||
match &self.data {
|
||||
Some(DrawingData::Pic(p)) => {
|
||||
if let DrawingPositionType::Inline { .. } = p.position_type {
|
||||
b = b.open_wp_inline(
|
||||
&format!("{}", p.dist_t),
|
||||
&format!("{}", p.dist_b),
|
||||
&format!("{}", p.dist_l),
|
||||
&format!("{}", p.dist_r),
|
||||
)
|
||||
} else {
|
||||
b = b
|
||||
.open_wp_anchor("0", "0", "0", "0", "0", "1", "0", "0", "1", "1905000")
|
||||
.simple_pos("0", "0")
|
||||
.open_position_h("page");
|
||||
if let DrawingPosition::Offset(x) = self.position_h {
|
||||
let x = format!("{}", crate::types::emu::from_px(x as u32));
|
||||
.open_wp_anchor(
|
||||
&format!("{}", p.dist_t),
|
||||
&format!("{}", p.dist_b),
|
||||
&format!("{}", p.dist_l),
|
||||
&format!("{}", p.dist_r),
|
||||
"0",
|
||||
if p.simple_pos { "1" } else { "0" },
|
||||
"0",
|
||||
"0",
|
||||
if p.layout_in_cell { "1" } else { "0" },
|
||||
&format!("{}", p.relative_height),
|
||||
)
|
||||
.simple_pos(
|
||||
&format!("{}", p.simple_pos_x),
|
||||
&format!("{}", p.simple_pos_y),
|
||||
)
|
||||
.open_position_h(&format!("{}", p.relative_from_h));
|
||||
|
||||
if let DrawingPosition::Offset(x) = p.position_h {
|
||||
let x = format!("{}", x as u32);
|
||||
b = b.pos_offset(&x).close();
|
||||
}
|
||||
|
||||
b = b.open_position_v("page");
|
||||
b = b.open_position_v(&format!("{}", p.relative_from_v));
|
||||
|
||||
if let DrawingPosition::Offset(y) = self.position_v {
|
||||
let y = format!("{}", crate::types::emu::from_px(y as u32));
|
||||
if let DrawingPosition::Offset(y) = p.position_v {
|
||||
let y = format!("{}", y as u32);
|
||||
b = b.pos_offset(&y).close();
|
||||
}
|
||||
}
|
||||
match &self.data {
|
||||
Some(DrawingData::Pic(p)) => {
|
||||
let w = format!("{}", crate::types::emu::from_px(p.size.0));
|
||||
let h = format!("{}", crate::types::emu::from_px(p.size.1));
|
||||
|
||||
let w = format!("{}", p.size.0);
|
||||
let h = format!("{}", p.size.1);
|
||||
b = b
|
||||
// Please see 20.4.2.7 extent (Drawing Object Size)
|
||||
// One inch equates to 914400 EMUs and a centimeter is 360000
|
||||
|
@ -149,7 +96,9 @@ impl BuildXML for Box<Drawing> {
|
|||
.close()
|
||||
.close();
|
||||
}
|
||||
None => {}
|
||||
None => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
b.close().close().build()
|
||||
}
|
||||
|
|
|
@ -353,6 +353,12 @@ impl BuildXML for Paragraph {
|
|||
}
|
||||
}
|
||||
|
||||
impl BuildXML for Box<Paragraph> {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
Paragraph::build(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use super::*;
|
||||
use image::*;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::documents::*;
|
||||
use crate::types::*;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum PicAlign {
|
||||
Left,
|
||||
Right,
|
||||
|
@ -14,48 +15,114 @@ pub enum PicAlign {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DrawingPosition {
|
||||
Offset(usize),
|
||||
Offset(i32),
|
||||
Align(PicAlign),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Pic {
|
||||
pub id: usize,
|
||||
pub id: String,
|
||||
// For writer only
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub image: Vec<u8>,
|
||||
// unit is emu
|
||||
pub size: (u32, u32),
|
||||
pub position_type: DrawingPositionType,
|
||||
/// Specifies that this object shall be positioned using the positioning information in the
|
||||
/// simplePos child element (§20.4.2.13). This positioning, when specified, positions the
|
||||
/// object on the page by placing its top left point at the x-y coordinates specified by that
|
||||
/// element.
|
||||
pub simple_pos: bool,
|
||||
// unit is emu
|
||||
pub simple_pos_x: i32,
|
||||
pub simple_pos_y: i32,
|
||||
/// Specifies how this DrawingML object behaves when its anchor is located in a table cell;
|
||||
/// and its specified position would cause it to intersect with a table cell displayed in the
|
||||
/// document. That behavior shall be as follows:
|
||||
pub layout_in_cell: bool,
|
||||
/// Specifies the relative Z-ordering of all DrawingML objects in this document. Each floating
|
||||
/// DrawingML object shall have a Z-ordering value, which determines which object is
|
||||
/// displayed when any two objects intersect. Higher values shall indicate higher Z-order;
|
||||
/// lower values shall indicate lower Z-order.
|
||||
pub relative_height: u32,
|
||||
pub allow_overlap: bool,
|
||||
pub position_h: DrawingPosition,
|
||||
pub position_v: DrawingPosition,
|
||||
pub relative_from_h: RelativeFromHType,
|
||||
pub relative_from_v: RelativeFromVType,
|
||||
/// Specifies the minimum distance which shall be maintained between the top edge of this drawing object and any subsequent text within the document when this graphical object is displayed within the document's contents.,
|
||||
/// The distance shall be measured in EMUs (English Metric Units).,
|
||||
pub dist_t: i32,
|
||||
pub dist_b: i32,
|
||||
pub dist_l: i32,
|
||||
pub dist_r: i32,
|
||||
}
|
||||
|
||||
impl Pic {
|
||||
pub fn new(buf: Vec<u8>) -> Pic {
|
||||
let id = generate_pic_id();
|
||||
pub fn
|
||||
new(buf: Vec<u8>) -> Pic {
|
||||
let id = create_pic_rid(generate_pic_id());
|
||||
let dimg = image::load_from_memory(&buf).unwrap();
|
||||
let size = dimg.dimensions();
|
||||
let mut image = vec![];
|
||||
dimg
|
||||
.write_to(&mut image, ImageFormat::Png)
|
||||
dimg.write_to(&mut image, ImageFormat::Png)
|
||||
.expect("Unable to write");
|
||||
Self {
|
||||
id,
|
||||
image,
|
||||
size,
|
||||
position_type: DrawingPositionType::Inline {
|
||||
size: (from_px(size.0), from_px(size.1)),
|
||||
position_type: DrawingPositionType::Inline,
|
||||
simple_pos: false,
|
||||
simple_pos_x: 0,
|
||||
simple_pos_y: 0,
|
||||
layout_in_cell: false,
|
||||
relative_height: 190500,
|
||||
allow_overlap: false,
|
||||
position_v: DrawingPosition::Offset(0),
|
||||
position_h: DrawingPosition::Offset(0),
|
||||
relative_from_h: RelativeFromHType::default(),
|
||||
relative_from_v: RelativeFromVType::default(),
|
||||
dist_t: 0,
|
||||
dist_b: 0,
|
||||
dist_l: 0,
|
||||
dist_r: 0,
|
||||
},
|
||||
position_h: DrawingPosition::Offset(0),
|
||||
position_v: DrawingPosition::Offset(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(mut self, w_px: u32, h_px: u32) -> Pic {
|
||||
self.size = (w_px, h_px);
|
||||
pub(crate) fn with_empty() -> Pic {
|
||||
Self {
|
||||
id: "".to_string(),
|
||||
image: vec![],
|
||||
size: (0, 0),
|
||||
position_type: DrawingPositionType::Inline,
|
||||
simple_pos: false,
|
||||
simple_pos_x: 0,
|
||||
simple_pos_y: 0,
|
||||
layout_in_cell: false,
|
||||
relative_height: 190500,
|
||||
allow_overlap: false,
|
||||
position_v: DrawingPosition::Offset(0),
|
||||
position_h: DrawingPosition::Offset(0),
|
||||
relative_from_h: RelativeFromHType::default(),
|
||||
relative_from_v: RelativeFromVType::default(),
|
||||
dist_t: 0,
|
||||
dist_b: 0,
|
||||
dist_l: 0,
|
||||
dist_r: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: impl Into<String>) -> Pic {
|
||||
self.id = id.into();
|
||||
self
|
||||
}
|
||||
|
||||
// unit is emu
|
||||
pub fn size(mut self, w_emu: u32, h_emu: u32) -> Pic {
|
||||
self.size = (w_emu, h_emu);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -64,22 +131,72 @@ impl Pic {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn offset_x(mut self, x: usize) -> Pic {
|
||||
pub fn offset_x(mut self, x: i32) -> Pic {
|
||||
self.position_h = DrawingPosition::Offset(x);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn offset_y(mut self, y: usize) -> Pic {
|
||||
pub fn offset_y(mut self, y: i32) -> Pic {
|
||||
self.position_v = DrawingPosition::Offset(y);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position_h(mut self, pos: DrawingPosition) -> Self {
|
||||
self.position_h = pos;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position_v(mut self, pos: DrawingPosition) -> Self {
|
||||
self.position_v = pos;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn relative_from_h(mut self, t: RelativeFromHType) -> Self {
|
||||
self.relative_from_h = t;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn relative_from_v(mut self, t: RelativeFromVType) -> Self {
|
||||
self.relative_from_v = t;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dist_t(mut self, v: i32) -> Self {
|
||||
self.dist_t = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dist_b(mut self, v: i32) -> Self {
|
||||
self.dist_b = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dist_l(mut self, v: i32) -> Self {
|
||||
self.dist_l = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dist_r(mut self, v: i32) -> Self {
|
||||
self.dist_r = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn simple_pos(mut self, v: bool) -> Self {
|
||||
self.simple_pos = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn relative_height(mut self, v: u32) -> Self {
|
||||
self.relative_height = v;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BuildXML for Pic {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let b = XMLBuilder::new();
|
||||
let w = format!("{}", crate::types::emu::from_px(self.size.0));
|
||||
let h = format!("{}", crate::types::emu::from_px(self.size.1));
|
||||
let w = format!("{}", self.size.0);
|
||||
let h = format!("{}", self.size.1);
|
||||
b.open_pic("http://schemas.openxmlformats.org/drawingml/2006/picture")
|
||||
.open_pic_nv_pic_pr()
|
||||
.pic_c_nv_pr("0", "")
|
||||
|
@ -88,7 +205,7 @@ impl BuildXML for Pic {
|
|||
.close()
|
||||
.close()
|
||||
.open_blip_fill()
|
||||
.a_blip(&create_pic_rid(self.id))
|
||||
.a_blip(&self.id)
|
||||
.a_src_rect()
|
||||
.open_a_stretch()
|
||||
.a_fill_rect()
|
||||
|
|
|
@ -3,7 +3,7 @@ use serde::ser::{SerializeStruct, Serializer};
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::documents::BuildXML;
|
||||
use crate::types::BreakType;
|
||||
use crate::types::*;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
|
@ -137,25 +137,12 @@ impl Run {
|
|||
}
|
||||
|
||||
pub fn add_image(mut self, pic: Pic) -> Run {
|
||||
if pic.position_type == DrawingPositionType::Anchor {
|
||||
let pos_h = pic.position_h;
|
||||
let pos_v = pic.position_v;
|
||||
self.children.push(RunChild::Drawing(Box::new(
|
||||
Drawing::new()
|
||||
.pic(pic)
|
||||
.floating()
|
||||
.position_h(pos_h)
|
||||
.position_v(pos_v),
|
||||
)));
|
||||
} else {
|
||||
self.children
|
||||
.push(RunChild::Drawing(Box::new(Drawing::new().pic(pic))));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: Remove later
|
||||
pub fn add_drawing(mut self, d: Drawing) -> Run {
|
||||
pub(crate) fn add_drawing(mut self, d: Drawing) -> Run {
|
||||
self.children.push(RunChild::Drawing(Box::new(d)));
|
||||
self
|
||||
}
|
||||
|
|
|
@ -128,6 +128,12 @@ impl BuildXML for Table {
|
|||
}
|
||||
}
|
||||
|
||||
impl BuildXML for Box<Table> {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
Table::build(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for TableChild {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
|
|
@ -5,7 +5,7 @@ use super::*;
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Footer {
|
||||
pub has_numbering: bool,
|
||||
|
@ -21,7 +21,7 @@ impl Footer {
|
|||
if p.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(FooterChild::Paragraph(p));
|
||||
self.children.push(FooterChild::Paragraph(Box::new(p)));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -29,24 +29,15 @@ impl Footer {
|
|||
if t.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(FooterChild::Table(t));
|
||||
self.children.push(FooterChild::Table(Box::new(t)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Footer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
children: vec![],
|
||||
has_numbering: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FooterChild {
|
||||
Paragraph(Paragraph),
|
||||
Table(Table),
|
||||
Paragraph(Box<Paragraph>),
|
||||
Table(Box<Table>),
|
||||
}
|
||||
|
||||
impl Serialize for FooterChild {
|
||||
|
|
|
@ -5,7 +5,7 @@ use super::*;
|
|||
use crate::documents::BuildXML;
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Header {
|
||||
pub has_numbering: bool,
|
||||
|
@ -21,7 +21,7 @@ impl Header {
|
|||
if p.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(HeaderChild::Paragraph(p));
|
||||
self.children.push(HeaderChild::Paragraph(Box::new(p)));
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -29,24 +29,15 @@ impl Header {
|
|||
if t.has_numbering {
|
||||
self.has_numbering = true
|
||||
}
|
||||
self.children.push(HeaderChild::Table(t));
|
||||
self.children.push(HeaderChild::Table(Box::new(t)));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Header {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
children: vec![],
|
||||
has_numbering: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum HeaderChild {
|
||||
Paragraph(Paragraph),
|
||||
Table(Table),
|
||||
Paragraph(Box<Paragraph>),
|
||||
Table(Box<Table>),
|
||||
}
|
||||
|
||||
impl Serialize for HeaderChild {
|
||||
|
|
|
@ -68,7 +68,23 @@ pub use web_settings::*;
|
|||
pub use webextension::*;
|
||||
pub use xml_docx::*;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::{ser, Serialize};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Image(pub Vec<u8>);
|
||||
|
||||
pub type ImageIdAndPath = (String, String);
|
||||
pub type ImageIdAndBuf = (String, Vec<u8>);
|
||||
|
||||
impl ser::Serialize for Image {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
let base64 = base64::display::Base64Display::with_config(&*self.0, base64::STANDARD);
|
||||
serializer.collect_str(&base64)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -83,7 +99,7 @@ pub struct Docx {
|
|||
pub numberings: Numberings,
|
||||
pub settings: Settings,
|
||||
pub font_table: FontTable,
|
||||
pub media: Vec<(usize, Vec<u8>)>,
|
||||
pub media: Vec<(String, Vec<u8>)>,
|
||||
pub comments_extended: CommentsExtended,
|
||||
pub web_settings: WebSettings,
|
||||
pub taskpanes: Option<Taskpanes>,
|
||||
|
@ -94,6 +110,8 @@ pub struct Docx {
|
|||
pub custom_item_rels: Vec<CustomItemRels>,
|
||||
// reader only
|
||||
pub themes: Vec<Theme>,
|
||||
// reader only
|
||||
pub images: Vec<Image>,
|
||||
}
|
||||
|
||||
impl Default for Docx {
|
||||
|
@ -133,6 +151,7 @@ impl Default for Docx {
|
|||
custom_item_props: vec![],
|
||||
custom_item_rels: vec![],
|
||||
themes: vec![],
|
||||
images: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +207,12 @@ impl Docx {
|
|||
self
|
||||
}
|
||||
|
||||
// reader only
|
||||
pub(crate) fn add_image(mut self, buf: Vec<u8>) -> Self {
|
||||
self.images.push(Image(buf));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn comments(mut self, c: Comments) -> Self {
|
||||
self.comments = c;
|
||||
self
|
||||
|
@ -429,7 +454,7 @@ impl Docx {
|
|||
|
||||
self.update_comments();
|
||||
|
||||
let tocs: Vec<(usize, TableOfContents)> = self
|
||||
let tocs: Vec<(usize, Box<TableOfContents>)> = self
|
||||
.document
|
||||
.children
|
||||
.iter()
|
||||
|
@ -453,12 +478,13 @@ impl Docx {
|
|||
|
||||
for (i, toc) in tocs {
|
||||
if toc.items.is_empty() && toc.auto {
|
||||
let children = update_document_by_toc(self.document.children, &self.styles, toc, i);
|
||||
let children =
|
||||
update_document_by_toc(self.document.children, &self.styles, *toc, i);
|
||||
self.document.children = children;
|
||||
}
|
||||
}
|
||||
|
||||
let (image_ids, images) = self.create_images();
|
||||
let (images, images_bufs) = self.create_images();
|
||||
let web_extensions = self.web_extensions.iter().map(|ext| ext.build()).collect();
|
||||
let custom_items = self.custom_items.iter().map(|xml| xml.build()).collect();
|
||||
let custom_item_props = self.custom_item_props.iter().map(|p| p.build()).collect();
|
||||
|
@ -468,7 +494,7 @@ impl Docx {
|
|||
.map(|rel| rel.build())
|
||||
.collect();
|
||||
|
||||
self.document_rels.image_ids = image_ids;
|
||||
self.document_rels.images = images;
|
||||
|
||||
let headers: Vec<Vec<u8>> = self
|
||||
.document
|
||||
|
@ -497,7 +523,7 @@ impl Docx {
|
|||
settings: self.settings.build(),
|
||||
font_table: self.font_table.build(),
|
||||
numberings: self.numberings.build(),
|
||||
media: images,
|
||||
media: images_bufs,
|
||||
headers,
|
||||
footers,
|
||||
comments_extended: self.comments_extended.build(),
|
||||
|
@ -811,9 +837,9 @@ impl Docx {
|
|||
}
|
||||
|
||||
// Traverse and collect images from document.
|
||||
fn create_images(&mut self) -> (Vec<usize>, Vec<(usize, Vec<u8>)>) {
|
||||
let mut image_ids: Vec<usize> = vec![];
|
||||
let mut images: Vec<(usize, Vec<u8>)> = vec![];
|
||||
fn create_images(&mut self) -> (Vec<ImageIdAndPath>, Vec<ImageIdAndBuf>) {
|
||||
let mut images: Vec<(String, String)> = vec![];
|
||||
let mut image_bufs: Vec<(String, Vec<u8>)> = vec![];
|
||||
|
||||
for child in &mut self.document.children {
|
||||
match child {
|
||||
|
@ -823,9 +849,12 @@ impl Docx {
|
|||
for child in &mut run.children {
|
||||
if let RunChild::Drawing(d) = child {
|
||||
if let Some(DrawingData::Pic(pic)) = &mut d.data {
|
||||
image_ids.push(pic.id);
|
||||
images.push((
|
||||
pic.id.clone(),
|
||||
format!("media/{}.jpg", pic.id),
|
||||
));
|
||||
let b = std::mem::take(&mut pic.image);
|
||||
images.push((pic.id, b));
|
||||
image_bufs.push((pic.id.clone(), b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -845,9 +874,12 @@ impl Docx {
|
|||
if let Some(DrawingData::Pic(pic)) =
|
||||
&mut d.data
|
||||
{
|
||||
image_ids.push(pic.id);
|
||||
images.push((
|
||||
pic.id.clone(),
|
||||
format!("media/{}.jpg", pic.id),
|
||||
));
|
||||
let b = std::mem::take(&mut pic.image);
|
||||
images.push((pic.id, b));
|
||||
image_bufs.push((pic.id.clone(), b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -865,7 +897,7 @@ impl Docx {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
(image_ids, images)
|
||||
(images, image_bufs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -910,7 +942,8 @@ fn update_document_by_toc(
|
|||
.toc_key(&toc_key)
|
||||
.level(*heading_level),
|
||||
);
|
||||
paragraph = paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key);
|
||||
paragraph =
|
||||
Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key));
|
||||
}
|
||||
|
||||
if let Some((_min, _max)) = toc.instr.tc_field_level_range {
|
||||
|
@ -933,7 +966,8 @@ fn update_document_by_toc(
|
|||
.toc_key(&toc_key)
|
||||
.level(*level),
|
||||
);
|
||||
paragraph = paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key);
|
||||
paragraph =
|
||||
Box::new(paragraph.wrap_by_bookmark(generate_bookmark_id(), &toc_key));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -963,6 +997,6 @@ fn update_document_by_toc(
|
|||
|
||||
let mut toc = toc;
|
||||
toc.items = items;
|
||||
children[toc_index] = DocumentChild::TableOfContents(toc);
|
||||
children[toc_index] = DocumentChild::TableOfContents(Box::new(toc));
|
||||
children
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct XMLDocx {
|
|||
pub settings: Vec<u8>,
|
||||
pub font_table: Vec<u8>,
|
||||
pub numberings: Vec<u8>,
|
||||
pub media: Vec<(usize, Vec<u8>)>,
|
||||
pub media: Vec<(String, Vec<u8>)>,
|
||||
pub headers: Vec<Vec<u8>>,
|
||||
pub footers: Vec<Vec<u8>>,
|
||||
pub comments_extended: Vec<u8>,
|
||||
|
|
|
@ -6,30 +6,222 @@ use std::str::FromStr;
|
|||
use xml::attribute::OwnedAttribute;
|
||||
use xml::reader::{EventReader, XmlEvent};
|
||||
|
||||
use crate::{DrawingPositionType, RelativeFromHType, RelativeFromVType};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn read_position_h<R: Read>(
|
||||
r: &mut EventReader<R>,
|
||||
attrs: &[OwnedAttribute],
|
||||
) -> Result<(RelativeFromHType, i32), ReaderError> {
|
||||
let mut offset: i32 = 0;
|
||||
let mut relative_from_h = RelativeFromHType::default();
|
||||
|
||||
loop {
|
||||
if let Some(h) = read(attrs, "relativeFrom") {
|
||||
if let Ok(h) = RelativeFromHType::from_str(&h) {
|
||||
relative_from_h = h;
|
||||
}
|
||||
}
|
||||
let e = r.next();
|
||||
match e {
|
||||
Ok(XmlEvent::Characters(c)) => {
|
||||
if let Ok(p) = i32::from_str(&c) {
|
||||
offset = p;
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = WpXMLElement::from_str(&name.local_name).unwrap();
|
||||
if e == WpXMLElement::PositionH {
|
||||
return Ok((relative_from_h, offset));
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ReaderError::XMLReadError),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_position_v<R: Read>(
|
||||
r: &mut EventReader<R>,
|
||||
attrs: &[OwnedAttribute],
|
||||
) -> Result<(RelativeFromVType, i32), ReaderError> {
|
||||
let mut offset: i32 = 0;
|
||||
let mut relative_from_v = RelativeFromVType::default();
|
||||
loop {
|
||||
if let Some(v) = read(attrs, "relativeFrom") {
|
||||
if let Ok(v) = RelativeFromVType::from_str(&v) {
|
||||
relative_from_v = v;
|
||||
}
|
||||
}
|
||||
|
||||
let e = r.next();
|
||||
match e {
|
||||
Ok(XmlEvent::Characters(c)) => {
|
||||
if let Ok(p) = i32::from_str(&c) {
|
||||
offset = p;
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = WpXMLElement::from_str(&name.local_name).unwrap();
|
||||
if e == WpXMLElement::PositionV {
|
||||
return Ok((relative_from_v, offset));
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ReaderError::XMLReadError),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementReader for Drawing {
|
||||
fn read<R: Read>(
|
||||
r: &mut EventReader<R>,
|
||||
_attrs: &[OwnedAttribute],
|
||||
) -> Result<Self, ReaderError> {
|
||||
let mut drawing = Drawing::new();
|
||||
let mut drawing_position_type = DrawingPositionType::Inline;
|
||||
|
||||
let mut simple_pos = false;
|
||||
let mut simple_pos_x = 0;
|
||||
let mut simple_pos_y = 0;
|
||||
let mut layout_in_cell = true;
|
||||
let mut relative_height = 0;
|
||||
let mut position_h = 0;
|
||||
let mut position_v = 0;
|
||||
let mut relative_from_h = RelativeFromHType::default();
|
||||
let mut relative_from_v = RelativeFromVType::default();
|
||||
let mut allow_overlap = true;
|
||||
let mut dist_t = 0;
|
||||
let mut dist_b = 0;
|
||||
let mut dist_l = 0;
|
||||
let mut dist_r = 0;
|
||||
|
||||
loop {
|
||||
let e = r.next();
|
||||
match e {
|
||||
Ok(XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
}) => {
|
||||
let e = WpXMLElement::from_str(&name.local_name)
|
||||
.expect("should convert to XMLElement");
|
||||
match e {
|
||||
// wp:
|
||||
if let Ok(wpe) = WpXMLElement::from_str(&name.local_name) {
|
||||
match wpe {
|
||||
WpXMLElement::Anchor => {
|
||||
let anchor = WpAnchor::read(r, &attributes)?;
|
||||
drawing = drawing.add_anchor(anchor);
|
||||
drawing_position_type = DrawingPositionType::Anchor;
|
||||
if let Some(v) = read(&attributes, "simplePos") {
|
||||
if !is_false(&v) {
|
||||
simple_pos = true;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distT") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_t = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distB") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_b = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distL") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_l = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distR") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_r = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "layoutInCell") {
|
||||
if is_false(&d) {
|
||||
layout_in_cell = false;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "relativeHeight") {
|
||||
if let Ok(d) = u32::from_str(&d) {
|
||||
relative_height = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "allowOverlap") {
|
||||
if is_false(&d) {
|
||||
allow_overlap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
WpXMLElement::Inline => {
|
||||
drawing_position_type = DrawingPositionType::Inline;
|
||||
if let Some(d) = read(&attributes, "distT") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_t = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distB") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_b = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distL") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_l = d;
|
||||
}
|
||||
}
|
||||
if let Some(d) = read(&attributes, "distR") {
|
||||
if let Ok(d) = i32::from_str(&d) {
|
||||
dist_r = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
WpXMLElement::SimplePos => {
|
||||
if let Some(x) = read(&attributes, "x") {
|
||||
if let Ok(x) = i32::from_str(&x) {
|
||||
simple_pos_x = x;
|
||||
}
|
||||
}
|
||||
if let Some(y) = read(&attributes, "y") {
|
||||
if let Ok(y) = i32::from_str(&y) {
|
||||
simple_pos_y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
WpXMLElement::PositionH => {
|
||||
if let Ok(p) = read_position_h(r, &attributes) {
|
||||
relative_from_h = p.0;
|
||||
position_h = p.1;
|
||||
}
|
||||
}
|
||||
WpXMLElement::PositionV => {
|
||||
if let Ok(p) = read_position_v(r, &attributes) {
|
||||
relative_from_v = p.0;
|
||||
position_v = p.1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// pic:
|
||||
if let Ok(PicXMLElement::Pic) = PicXMLElement::from_str(&name.local_name) {
|
||||
if let Ok(mut pic) = Pic::read(r, &attributes) {
|
||||
pic.position_type = drawing_position_type;
|
||||
pic.simple_pos = simple_pos;
|
||||
pic.simple_pos_x = simple_pos_x;
|
||||
pic.simple_pos_y = simple_pos_y;
|
||||
pic.layout_in_cell = layout_in_cell;
|
||||
pic.relative_height = relative_height;
|
||||
pic.allow_overlap = allow_overlap;
|
||||
pic.dist_r = dist_r;
|
||||
pic.dist_t = dist_t;
|
||||
pic.dist_b = dist_b;
|
||||
pic.dist_l = dist_l;
|
||||
pic.dist_r = dist_r;
|
||||
pic.relative_from_h = relative_from_h;
|
||||
pic.relative_from_v = relative_from_v;
|
||||
pic.position_v = DrawingPosition::Offset(position_v);
|
||||
pic.position_h = DrawingPosition::Offset(position_h);
|
||||
drawing = drawing.pic(pic);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = XMLElement::from_str(&name.local_name).unwrap();
|
||||
if e == XMLElement::Drawing {
|
||||
|
|
|
@ -32,6 +32,7 @@ mod numberings;
|
|||
mod paragraph;
|
||||
mod paragraph_property;
|
||||
mod paragraph_property_change;
|
||||
mod pic;
|
||||
mod read_zip;
|
||||
mod rels;
|
||||
mod run;
|
||||
|
@ -92,11 +93,14 @@ const FOOTER_TYPE: &str =
|
|||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
|
||||
const THEME_TYPE: &str =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
||||
const IMAGE_TYPE: &str =
|
||||
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
||||
// 2011
|
||||
const COMMENTS_EXTENDED_TYPE: &str =
|
||||
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
|
||||
|
||||
fn read_headers(
|
||||
|
||||
rels: &ReadDocumentRels,
|
||||
archive: &mut ZipArchive<Cursor<&[u8]>>,
|
||||
) -> HashMap<RId, Header> {
|
||||
|
@ -403,5 +407,14 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
|
|||
docx = docx.web_settings(web_settings);
|
||||
}
|
||||
}
|
||||
|
||||
// Read media
|
||||
let media = rels.find_target_path(IMAGE_TYPE);
|
||||
if let Some(paths) = media {
|
||||
if let Some((_, media)) = paths.get(0) {
|
||||
let data = read_zip(&mut archive, media.to_str().expect("should have media"))?;
|
||||
docx = docx.add_image(data);
|
||||
}
|
||||
}
|
||||
Ok(docx)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::io::Read;
|
||||
use std::str::FromStr;
|
||||
|
||||
use xml::attribute::OwnedAttribute;
|
||||
use xml::reader::{EventReader, XmlEvent};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl ElementReader for Pic {
|
||||
fn read<R: Read>(
|
||||
r: &mut EventReader<R>,
|
||||
_attrs: &[OwnedAttribute],
|
||||
) -> Result<Self, ReaderError> {
|
||||
let mut pic = Pic::with_empty();
|
||||
loop {
|
||||
let e = r.next();
|
||||
match e {
|
||||
Ok(XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
}) => {
|
||||
if let Ok(e) = AXMLElement::from_str(&name.local_name) {
|
||||
match e {
|
||||
AXMLElement::Blip => {
|
||||
if let Some(id) = read(&attributes, "embed") {
|
||||
pic = pic.id(id)
|
||||
}
|
||||
}
|
||||
AXMLElement::Off => {
|
||||
let mut offset_x: i32 = 0;
|
||||
let mut offset_y: i32 = 0;
|
||||
if let Some(x) = read(&attributes, "x") {
|
||||
if let Ok(x) = i32::from_str(&x) {
|
||||
offset_x = x;
|
||||
}
|
||||
}
|
||||
if let Some(y) = read(&attributes, "y") {
|
||||
if let Ok(y) = i32::from_str(&y) {
|
||||
offset_y = y;
|
||||
}
|
||||
}
|
||||
pic = pic.offset_x(offset_x).offset_y(offset_y);
|
||||
}
|
||||
AXMLElement::Ext => {
|
||||
let mut w: u32 = 0;
|
||||
let mut h: u32 = 0;
|
||||
if let Some(x) = read(&attributes, "cx") {
|
||||
if let Ok(x) = u32::from_str(&x) {
|
||||
w = x;
|
||||
}
|
||||
}
|
||||
if let Some(y) = read(&attributes, "cy") {
|
||||
if let Ok(y) = u32::from_str(&y) {
|
||||
h = y;
|
||||
}
|
||||
}
|
||||
pic = pic.size(w, h);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let e = PicXMLElement::from_str(&name.local_name).unwrap();
|
||||
if e == PicXMLElement::Pic {
|
||||
return Ok(pic);
|
||||
}
|
||||
}
|
||||
Err(_) => return Err(ReaderError::XMLReadError),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -160,6 +160,7 @@ pub enum McXMLElement {
|
|||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum WpXMLElement {
|
||||
Inline,
|
||||
Anchor,
|
||||
SimplePos,
|
||||
PositionH,
|
||||
|
@ -177,6 +178,7 @@ pub enum AXMLElement {
|
|||
Graphic,
|
||||
GraphicData,
|
||||
Xfrm,
|
||||
Blip,
|
||||
Off,
|
||||
Ext,
|
||||
PrstGeom,
|
||||
|
@ -216,6 +218,12 @@ pub enum VtXMLElement {
|
|||
Unsupported,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum PicXMLElement {
|
||||
Pic,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
impl FromStr for XMLElement {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
|
@ -380,6 +388,7 @@ impl FromStr for WpXMLElement {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"anchor" => Ok(WpXMLElement::Anchor),
|
||||
"inline" => Ok(WpXMLElement::Inline),
|
||||
"simplePos" => Ok(WpXMLElement::SimplePos),
|
||||
"positionH" => Ok(WpXMLElement::PositionH),
|
||||
"posOffset" => Ok(WpXMLElement::PosOffset),
|
||||
|
@ -400,6 +409,7 @@ impl FromStr for AXMLElement {
|
|||
"graphic" => Ok(AXMLElement::Graphic),
|
||||
"graphicData" => Ok(AXMLElement::GraphicData),
|
||||
"xfrm" => Ok(AXMLElement::Xfrm),
|
||||
"blip" => Ok(AXMLElement::Blip),
|
||||
"off" => Ok(AXMLElement::Off),
|
||||
"ext" => Ok(AXMLElement::Ext),
|
||||
"prstGeom" => Ok(AXMLElement::PrstGeom),
|
||||
|
@ -455,6 +465,16 @@ impl FromStr for VtXMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for PicXMLElement {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"pic" => Ok(PicXMLElement::Pic),
|
||||
_ => Ok(PicXMLElement::Unsupported),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ElementReader {
|
||||
fn read<R: Read>(r: &mut EventReader<R>, attrs: &[OwnedAttribute]) -> Result<Self, ReaderError>
|
||||
where
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone, Copy, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DrawingPositionType {
|
||||
Anchor,
|
||||
Inline,
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
// One inch equates to 914400 EMUs and a centimeter is 360000 one pixel equates to 9525
|
||||
// https://openpyxl.readthedocs.io/en/stable/api/openpyxl.utils.units.html
|
||||
type Emu = u32;
|
||||
|
||||
pub fn to_px(v: Emu) -> u32 {
|
||||
|
|
|
@ -3,6 +3,7 @@ pub mod border_position;
|
|||
pub mod border_type;
|
||||
pub mod break_type;
|
||||
pub mod doc_grid_type;
|
||||
pub mod drawing_position;
|
||||
pub mod emu;
|
||||
pub mod errors;
|
||||
pub mod field_char_type;
|
||||
|
@ -12,6 +13,7 @@ pub mod level_suffix_type;
|
|||
pub mod line_spacing_type;
|
||||
pub mod page_margin;
|
||||
pub mod page_orientation_type;
|
||||
pub mod relative_from_type;
|
||||
pub mod section_type;
|
||||
pub mod shd_type;
|
||||
pub mod special_indent_type;
|
||||
|
@ -31,6 +33,7 @@ pub use border_position::*;
|
|||
pub use border_type::*;
|
||||
pub use break_type::*;
|
||||
pub use doc_grid_type::*;
|
||||
pub use drawing_position::*;
|
||||
pub use emu::*;
|
||||
pub use errors::*;
|
||||
pub use field_char_type::*;
|
||||
|
@ -40,6 +43,7 @@ pub use level_suffix_type::*;
|
|||
pub use line_spacing_type::*;
|
||||
pub use page_margin::*;
|
||||
pub use page_orientation_type::*;
|
||||
pub use relative_from_type::*;
|
||||
pub use section_type::*;
|
||||
pub use shd_type::*;
|
||||
pub use special_indent_type::*;
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
use std::fmt;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use super::errors;
|
||||
use std::str::FromStr;
|
||||
|
||||
// @See: 20.4.3.4 ST_RelFromH (Horizontal Relative Positioning)
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RelativeFromHType {
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the position of the anchor within its run
|
||||
/// content.
|
||||
Character,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the extents of the column which contains its
|
||||
/// anchor.
|
||||
Column,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the inside margin of the current page (the
|
||||
/// left margin on odd pages, right on even pages).
|
||||
InsideMargin,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the left margin of the page.
|
||||
LeftMargin,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the page margins.
|
||||
Margin,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the outside margin of the current page (the
|
||||
/// right margin on odd pages, left on even pages).
|
||||
OutsizeMargin,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the edge of the page.
|
||||
Page,
|
||||
/// Specifies that the horizontal positioning shall be
|
||||
/// relative to the right margin of the page.
|
||||
RightMargin,
|
||||
}
|
||||
|
||||
impl Default for RelativeFromHType {
|
||||
fn default() -> Self {
|
||||
Self::Margin
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RelativeFromHType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RelativeFromHType::Character => write!(f, "character"),
|
||||
RelativeFromHType::Column => write!(f, "column"),
|
||||
RelativeFromHType::InsideMargin => write!(f, "insideMargin"),
|
||||
RelativeFromHType::LeftMargin => write!(f, "leftMargin"),
|
||||
RelativeFromHType::Margin => write!(f, "margin"),
|
||||
RelativeFromHType::OutsizeMargin => write!(f, "outsizeMargin"),
|
||||
RelativeFromHType::Page => write!(f, "page"),
|
||||
RelativeFromHType::RightMargin => write!(f, "rightMargin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RelativeFromHType {
|
||||
type Err = errors::TypeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"character" => Ok(RelativeFromHType::Character),
|
||||
"column" => Ok(RelativeFromHType::Column),
|
||||
"insideMargin" => Ok(RelativeFromHType::InsideMargin),
|
||||
"leftMargin" => Ok(RelativeFromHType::LeftMargin),
|
||||
"margin" => Ok(RelativeFromHType::Margin),
|
||||
"outsizeMargin" => Ok(RelativeFromHType::OutsizeMargin),
|
||||
"page" => Ok(RelativeFromHType::Page),
|
||||
"rightMargin" => Ok(RelativeFromHType::RightMargin),
|
||||
_ => Ok(RelativeFromHType::Margin),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum RelativeFromVType {
|
||||
BottomMargin,
|
||||
InsideMargin,
|
||||
Line,
|
||||
Margin,
|
||||
OutsizeMargin,
|
||||
Page,
|
||||
Paragraph,
|
||||
TopMargin,
|
||||
}
|
||||
|
||||
impl Default for RelativeFromVType {
|
||||
fn default() -> Self {
|
||||
Self::Margin
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RelativeFromVType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
RelativeFromVType::BottomMargin => write!(f, "bottomMargin"),
|
||||
RelativeFromVType::InsideMargin => write!(f, "insideMargin"),
|
||||
RelativeFromVType::Line => write!(f, "line"),
|
||||
RelativeFromVType::Margin => write!(f, "margin"),
|
||||
RelativeFromVType::OutsizeMargin => write!(f, "outsideMargin"),
|
||||
RelativeFromVType::Page => write!(f, "page"),
|
||||
RelativeFromVType::Paragraph => write!(f, "paragraph"),
|
||||
RelativeFromVType::TopMargin => write!(f, "topMargin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RelativeFromVType {
|
||||
type Err = errors::TypeError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"bottomMargin" => Ok(RelativeFromVType::BottomMargin),
|
||||
"insideMargin" => Ok(RelativeFromVType::InsideMargin),
|
||||
"line" => Ok(RelativeFromVType::Line),
|
||||
"margin" => Ok(RelativeFromVType::Margin),
|
||||
"outsizeMargin" => Ok(RelativeFromVType::OutsizeMargin),
|
||||
"page" => Ok(RelativeFromVType::Page),
|
||||
"paragraph" => Ok(RelativeFromVType::Paragraph),
|
||||
"topMargin" => Ok(RelativeFromVType::TopMargin),
|
||||
_ => Ok(RelativeFromVType::Margin),
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -124,6 +124,12 @@ describe("reader", () => {
|
|||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should read image inline and anchor docx", () => {
|
||||
const buffer = readFileSync("../fixtures/image_inline_and_anchor/image_inline_and_anchor.docx");
|
||||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("writer", () => {
|
||||
|
|
Binary file not shown.
2
makefile
2
makefile
|
@ -2,7 +2,7 @@ test:
|
|||
cargo test -- --test-threads=1
|
||||
|
||||
lint:
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
cargo clippy --all-targets --all-features -- -D warnings -A clippy::derivable_impls -A clippy::large-enum-variant
|
||||
|
||||
vrt:
|
||||
node vrt/index.js && reg-cli vrt/screenshot/actual vrt/screenshot/expected vrt/screenshot/diff -R vrt/report.html -I
|
||||
|
|
Loading…
Reference in New Issue