fix: read theme (#425)

main
bokuweb 2022-01-27 17:23:30 +09:00 committed by GitHub
parent cee207e407
commit 22aac7b457
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 5708 additions and 37 deletions

View File

@ -0,0 +1,31 @@
use serde::Serialize;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FontSchemeFont {
pub script: String,
pub typeface: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct FontGroup {
pub latin: String,
pub ea: String,
pub cs: String,
pub fonts: Vec<FontSchemeFont>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct FontScheme {
pub major_font: FontGroup,
pub minor_font: FontGroup,
}
// For now reader only
impl FontScheme {
pub fn new() -> Self {
Self::default()
}
}

View File

@ -25,6 +25,7 @@ mod doc_var;
mod drawing;
mod fld_char;
mod font;
mod font_scheme;
mod footer_reference;
mod grid_span;
mod header_reference;
@ -135,6 +136,7 @@ pub use doc_var::*;
pub use drawing::*;
pub use fld_char::*;
pub use font::*;
pub use font_scheme::*;
pub use footer_reference::*;
pub use grid_span::*;
pub use header_reference::*;

View File

@ -28,6 +28,7 @@ mod settings;
mod styles;
mod taskpanes;
mod taskpanes_rels;
mod theme;
mod toc_key;
mod web_settings;
mod webextension;
@ -61,6 +62,7 @@ pub use settings::*;
pub use styles::*;
pub use taskpanes::*;
pub use taskpanes_rels::*;
pub use theme::*;
pub use toc_key::*;
pub use web_settings::*;
pub use webextension::*;
@ -90,6 +92,8 @@ pub struct Docx {
pub custom_items: Vec<CustomItem>,
pub custom_item_props: Vec<CustomItemProperty>,
pub custom_item_rels: Vec<CustomItemRels>,
// reader only
pub themes: Vec<Theme>,
}
impl Default for Docx {
@ -128,6 +132,7 @@ impl Default for Docx {
custom_items: vec![],
custom_item_props: vec![],
custom_item_rels: vec![],
themes: vec![],
}
}
}

View File

@ -0,0 +1,9 @@
use serde::Serialize;
use super::*;
#[derive(Debug, Clone, PartialEq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Theme {
pub font_schema: FontScheme,
}

View File

@ -0,0 +1,91 @@
#![allow(clippy::single_match)]
use std::io::Read;
use std::str::FromStr;
use xml::attribute::OwnedAttribute;
use xml::reader::{EventReader, XmlEvent};
use super::*;
fn read_typeface(attributes: &[OwnedAttribute]) -> Option<String> {
for a in attributes {
let local_name = &a.name.local_name;
if let "typeface" = local_name.as_str() {
return Some(a.value.to_string());
}
}
None
}
fn read_script_and_typeface(attributes: &[OwnedAttribute]) -> Option<(String, String)> {
let mut script = None;
let mut typeface = None;
for a in attributes {
let local_name = &a.name.local_name;
if let "script" = local_name.as_str() {
script = Some(a.value.to_string());
}
if let "typeface" = local_name.as_str() {
typeface = Some(a.value.to_string());
}
}
if let (Some(script), Some(typeface)) = (script, typeface) {
return Some((script, typeface));
}
None
}
impl ElementReader for FontGroup {
fn read<R: Read>(
r: &mut EventReader<R>,
_attrs: &[OwnedAttribute],
) -> Result<Self, ReaderError> {
let mut f = FontGroup::default();
loop {
let e = r.next();
match e {
Ok(XmlEvent::StartElement {
attributes, name, ..
}) => {
let e = AXMLElement::from_str(&name.local_name).unwrap();
match e {
AXMLElement::Latin => {
if let Some(t) = read_typeface(&attributes) {
f.latin = t;
}
}
AXMLElement::Ea => {
if let Some(t) = read_typeface(&attributes) {
f.ea = t;
}
}
AXMLElement::Cs => {
if let Some(t) = read_typeface(&attributes) {
f.cs = t;
}
}
AXMLElement::Font => {
if let Some((script, typeface)) = read_script_and_typeface(&attributes)
{
f.fonts.push(FontSchemeFont { script, typeface })
}
}
_ => {}
}
}
Ok(XmlEvent::EndElement { name, .. }) => {
let e = AXMLElement::from_str(&name.local_name).unwrap();
match e {
AXMLElement::MajorFont | AXMLElement::MinorFont => {
return Ok(f);
}
_ => {}
}
}
Err(_) => return Err(ReaderError::XMLReadError),
_ => {}
}
}
}
}

View File

@ -0,0 +1,57 @@
#![allow(clippy::single_match)]
use std::io::Read;
use std::str::FromStr;
use xml::attribute::OwnedAttribute;
use xml::reader::{EventReader, XmlEvent};
use crate::reader::*;
impl ElementReader for FontScheme {
fn read<R: Read>(
r: &mut EventReader<R>,
_attrs: &[OwnedAttribute],
) -> Result<Self, ReaderError> {
let mut fs = FontScheme::new();
loop {
let e = r.next();
match e {
Ok(XmlEvent::StartElement {
attributes, name, ..
}) => {
match name.prefix.as_deref() {
Some("a") => {
let e = AXMLElement::from_str(&name.local_name).unwrap();
match e {
AXMLElement::MajorFont => {
if let Ok(f) = FontGroup::read(r, &attributes) {
fs.major_font = f;
}
}
AXMLElement::MinorFont => {
if let Ok(f) = FontGroup::read(r, &attributes) {
fs.minor_font = f;
}
}
_ => {}
}
}
_ => {}
};
}
Ok(XmlEvent::EndElement { name, .. }) => {
let e = AXMLElement::from_str(&name.local_name).unwrap();
match e {
AXMLElement::FontScheme => {
return Ok(fs);
}
_ => {}
}
}
Err(_) => return Err(ReaderError::XMLReadError),
_ => {}
}
}
}
}

View File

@ -16,6 +16,8 @@ mod document;
mod document_rels;
mod drawing;
mod errors;
mod font_group;
mod font_scheme;
mod footer;
mod from_xml;
mod header;
@ -49,6 +51,7 @@ mod table_cell_property;
mod table_property;
mod table_row;
mod text_box_content;
mod theme;
mod web_settings;
mod wp_anchor;
mod wps_shape;
@ -87,6 +90,8 @@ const HEADER_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
const FOOTER_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
const THEME_TYPE: &str =
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
// 2011
const COMMENTS_EXTENDED_TYPE: &str =
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended";
@ -133,6 +138,23 @@ fn read_footers(
footers
}
fn read_themes(rels: &ReadDocumentRels, archive: &mut ZipArchive<Cursor<&[u8]>>) -> Vec<Theme> {
let theme_paths = rels.find_target_path(THEME_TYPE);
theme_paths
.unwrap_or_default()
.into_iter()
.filter_map(|(_rid, path)| {
let data = read_zip(archive, path.to_str().expect("should have footer path."));
if let Ok(d) = data {
if let Ok(h) = Theme::from_xml(&d[..]) {
return Some(h);
}
}
None
})
.collect()
}
pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
let mut docx = Docx::new();
let cur = Cursor::new(buf);
@ -176,6 +198,8 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
let headers = read_headers(&rels, &mut archive);
let footers = read_footers(&rels, &mut archive);
docx.themes = read_themes(&rels, &mut archive);
// Read commentsExtended
let comments_extended_path = rels.find_target_path(COMMENTS_EXTENDED_TYPE);
let comments_extended = if let Some(comments_extended_path) = comments_extended_path {
@ -379,6 +403,5 @@ pub fn read_docx(buf: &[u8]) -> Result<Docx, ReaderError> {
docx = docx.web_settings(web_settings);
}
}
Ok(docx)
}

View File

@ -0,0 +1,36 @@
use std::io::Read;
use std::str::FromStr;
use crate::reader::*;
use xml::reader::{EventReader, XmlEvent};
impl FromXML for Theme {
fn from_xml<R: Read>(reader: R) -> Result<Self, ReaderError> {
let mut parser = EventReader::new(reader);
let mut theme = Self::default();
loop {
let e = parser.next();
match e {
Ok(XmlEvent::StartElement {
attributes, name, ..
}) => {
let e = AXMLElement::from_str(&name.local_name).unwrap();
#[allow(clippy::single_match)]
match e {
AXMLElement::FontScheme => {
if let Ok(f) = FontScheme::read(&mut parser, &attributes) {
theme.font_schema = f;
}
continue;
}
_ => {}
}
}
Ok(XmlEvent::EndDocument) => break,
Err(_) => return Err(ReaderError::XMLReadError),
_ => {}
}
}
Ok(theme)
}
}

View File

@ -181,6 +181,13 @@ pub enum AXMLElement {
PrstGeom,
SolidFill,
Ln,
FontScheme,
MajorFont,
MinorFont,
Latin,
Ea,
Cs,
Font,
Unsupported,
}
@ -396,6 +403,13 @@ impl FromStr for AXMLElement {
"prstGeom" => Ok(AXMLElement::PrstGeom),
"solidFill" => Ok(AXMLElement::SolidFill),
"ln" => Ok(AXMLElement::Ln),
"fontScheme" => Ok(AXMLElement::FontScheme),
"majorFont" => Ok(AXMLElement::MajorFont),
"minorFont" => Ok(AXMLElement::MinorFont),
"latin" => Ok(AXMLElement::Latin),
"ea" => Ok(AXMLElement::Ea),
"cs" => Ok(AXMLElement::Cs),
"font" => Ok(AXMLElement::Font),
_ => Ok(AXMLElement::Unsupported),
}
}

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 it is too large Load Diff