Improve spacing (#367)
* fix: line spacing interfaces * fix: js interface * fix: js if * fix: reader and testingmain
parent
f1044c887c
commit
c6a555aa75
|
@ -4,7 +4,7 @@ use std::fs::File;
|
|||
use std::io::{Read, Write};
|
||||
|
||||
pub fn main() {
|
||||
let mut file = File::open("./fixtures/custom/custom.docx").unwrap();
|
||||
let mut file = File::open("./spacing.docx").unwrap();
|
||||
let mut buf = vec![];
|
||||
file.read_to_end(&mut buf).unwrap();
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::xml_builder::*;
|
|||
use crate::line_spacing_type::LineSpacingType;
|
||||
use serde::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct LineSpacing {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
@ -14,28 +14,45 @@ pub struct LineSpacing {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
after: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
before_lines: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
after_lines: Option<u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
line: Option<u32>,
|
||||
}
|
||||
|
||||
impl LineSpacing {
|
||||
pub fn new(spacing: Option<LineSpacingType>) -> Self {
|
||||
Self {
|
||||
line_rule: spacing,
|
||||
before: None,
|
||||
after: None,
|
||||
line: None,
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn before(mut self, before: Option<u32>) -> Self {
|
||||
self.before = before;
|
||||
|
||||
pub fn line_rule(mut self, t: LineSpacingType) -> Self {
|
||||
self.line_rule = Some(t);
|
||||
self
|
||||
}
|
||||
pub fn after(mut self, after: Option<u32>) -> Self {
|
||||
self.after = after;
|
||||
|
||||
pub fn before(mut self, before: u32) -> Self {
|
||||
self.before = Some(before);
|
||||
self
|
||||
}
|
||||
pub fn line(mut self, line: Option<u32>) -> Self {
|
||||
self.line = line;
|
||||
|
||||
pub fn after(mut self, after: u32) -> Self {
|
||||
self.after = Some(after);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn before_lines(mut self, before: u32) -> Self {
|
||||
self.before_lines = Some(before);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn after_lines(mut self, after: u32) -> Self {
|
||||
self.after_lines = Some(after);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn line(mut self, line: u32) -> Self {
|
||||
self.line = Some(line);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +60,15 @@ impl LineSpacing {
|
|||
impl BuildXML for LineSpacing {
|
||||
fn build(&self) -> Vec<u8> {
|
||||
let b = XMLBuilder::new();
|
||||
b.line_spacing(self.before, self.after, self.line, self.line_rule)
|
||||
.build()
|
||||
b.line_spacing(
|
||||
self.before,
|
||||
self.after,
|
||||
self.line,
|
||||
self.before_lines,
|
||||
self.after_lines,
|
||||
self.line_rule,
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,8 +82,9 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_spacing() {
|
||||
let b = LineSpacing::new(Some(LineSpacingType::Auto))
|
||||
.line(Some(100))
|
||||
let b = LineSpacing::new()
|
||||
.line_rule(LineSpacingType::Auto)
|
||||
.line(100)
|
||||
.build();
|
||||
assert_eq!(
|
||||
str::from_utf8(&b).unwrap(),
|
||||
|
@ -67,12 +92,26 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spacing_after_lines() {
|
||||
let b = LineSpacing::new()
|
||||
.line_rule(LineSpacingType::Auto)
|
||||
.after_lines(100)
|
||||
.build();
|
||||
assert_eq!(
|
||||
str::from_utf8(&b).unwrap(),
|
||||
r#"<w:spacing w:afterLines="100" w:lineRule="auto" />"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spacing_json() {
|
||||
let s = LineSpacing {
|
||||
line_rule: Some(LineSpacingType::Auto),
|
||||
before: None,
|
||||
after: None,
|
||||
before_lines: None,
|
||||
after_lines: None,
|
||||
line: Some(100),
|
||||
};
|
||||
assert_eq!(
|
||||
|
|
|
@ -244,16 +244,8 @@ impl Paragraph {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn line_spacing(
|
||||
mut self,
|
||||
before: Option<u32>,
|
||||
after: Option<u32>,
|
||||
line: Option<u32>,
|
||||
spacing_type: Option<LineSpacingType>,
|
||||
) -> Self {
|
||||
self.property = self
|
||||
.property
|
||||
.line_spacing(before, after, line, spacing_type);
|
||||
pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
|
||||
self.property = self.property.line_spacing(spacing);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -334,8 +326,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_line_spacing_and_character_spacing() {
|
||||
let spacing = LineSpacing::new()
|
||||
.line_rule(LineSpacingType::Auto)
|
||||
.before(20)
|
||||
.after(30)
|
||||
.line(200);
|
||||
let b = Paragraph::new()
|
||||
.line_spacing(Some(20), Some(30), Some(200), Some(LineSpacingType::Auto))
|
||||
.line_spacing(spacing)
|
||||
.add_run(Run::new().add_text("Hello"))
|
||||
.build();
|
||||
assert_eq!(
|
||||
|
|
|
@ -2,7 +2,7 @@ use serde::Serialize;
|
|||
|
||||
use super::*;
|
||||
use crate::documents::BuildXML;
|
||||
use crate::types::{AlignmentType, LineSpacingType, SpecialIndentType};
|
||||
use crate::types::{AlignmentType, SpecialIndentType};
|
||||
use crate::xml_builder::*;
|
||||
|
||||
#[derive(Serialize, Debug, Clone, PartialEq)]
|
||||
|
@ -78,19 +78,8 @@ impl ParagraphProperty {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn line_spacing(
|
||||
mut self,
|
||||
before: Option<u32>,
|
||||
after: Option<u32>,
|
||||
line: Option<u32>,
|
||||
spacing_type: Option<LineSpacingType>,
|
||||
) -> Self {
|
||||
self.line_spacing = Some(
|
||||
LineSpacing::new(spacing_type)
|
||||
.after(after)
|
||||
.before(before)
|
||||
.line(line),
|
||||
);
|
||||
pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
|
||||
self.line_spacing = Some(spacing);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -173,6 +162,7 @@ impl BuildXML for ParagraphProperty {
|
|||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::types::LineSpacingType;
|
||||
#[cfg(test)]
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::str;
|
||||
|
@ -238,9 +228,10 @@ mod tests {
|
|||
#[test]
|
||||
fn test_line_spacing() {
|
||||
let props = ParagraphProperty::new();
|
||||
let bytes = props
|
||||
.line_spacing(None, None, Some(100), Some(LineSpacingType::AtLeast))
|
||||
.build();
|
||||
let spacing = LineSpacing::new()
|
||||
.line_rule(LineSpacingType::AtLeast)
|
||||
.line(100);
|
||||
let bytes = props.line_spacing(spacing).build();
|
||||
assert_eq!(
|
||||
str::from_utf8(&bytes).unwrap(),
|
||||
r#"<w:pPr><w:rPr /><w:spacing w:line="100" w:lineRule="atLeast" /></w:pPr>"#
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
use crate::line_spacing_type::LineSpacingType;
|
||||
use crate::LineSpacing;
|
||||
use crate::ReaderError;
|
||||
use std::str::FromStr;
|
||||
use xml::attribute::OwnedAttribute;
|
||||
|
||||
pub type LineSpacingResult = Result<
|
||||
(
|
||||
Option<u32>,
|
||||
Option<u32>,
|
||||
Option<u32>,
|
||||
Option<LineSpacingType>,
|
||||
),
|
||||
ReaderError,
|
||||
>;
|
||||
|
||||
pub fn read_line_spacing(attributes: &[OwnedAttribute]) -> LineSpacingResult {
|
||||
let mut before: Option<u32> = None;
|
||||
let mut after: Option<u32> = None;
|
||||
let mut line: Option<u32> = None;
|
||||
let mut spacing_type: Option<LineSpacingType> = None;
|
||||
pub fn read_line_spacing(attributes: &[OwnedAttribute]) -> Result<LineSpacing, ReaderError> {
|
||||
let mut spacing = LineSpacing::new();
|
||||
for a in attributes {
|
||||
let local_name = &a.name.local_name;
|
||||
if local_name == "before" {
|
||||
before = Some(u32::from_str(&a.value)?);
|
||||
} else if local_name == "after" {
|
||||
after = Some(u32::from_str(&a.value)?);
|
||||
} else if local_name == "line" {
|
||||
line = Some(u32::from_str(&a.value)?);
|
||||
} else if local_name == "lineRule" {
|
||||
spacing_type = Some(LineSpacingType::from_str(&a.value)?);
|
||||
match local_name.as_str() {
|
||||
"before" => {
|
||||
spacing = spacing.before(u32::from_str(&a.value)?);
|
||||
}
|
||||
"after" => {
|
||||
spacing = spacing.after(u32::from_str(&a.value)?);
|
||||
}
|
||||
"line" => {
|
||||
spacing = spacing.line(u32::from_str(&a.value)?);
|
||||
}
|
||||
"lineRule" => {
|
||||
spacing = spacing.line_rule(LineSpacingType::from_str(&a.value)?);
|
||||
}
|
||||
"beforeLines" => {
|
||||
spacing = spacing.before_lines(u32::from_str(&a.value)?);
|
||||
}
|
||||
"afterLines" => {
|
||||
spacing = spacing.after_lines(u32::from_str(&a.value)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok((before, after, line, spacing_type))
|
||||
Ok(spacing)
|
||||
}
|
||||
|
|
|
@ -40,9 +40,11 @@ impl ElementReader for ParagraphProperty {
|
|||
continue;
|
||||
}
|
||||
XMLElement::Spacing => {
|
||||
let (before, after, line, spacing_type) =
|
||||
attributes::line_spacing::read_line_spacing(&attributes)?;
|
||||
p = p.line_spacing(before, after, line, spacing_type);
|
||||
if let Ok(spacing) =
|
||||
attributes::line_spacing::read_line_spacing(&attributes)
|
||||
{
|
||||
p = p.line_spacing(spacing);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
XMLElement::Justification => {
|
||||
|
|
|
@ -167,11 +167,15 @@ impl XMLBuilder {
|
|||
before: Option<u32>,
|
||||
after: Option<u32>,
|
||||
line: Option<u32>,
|
||||
before_lines: Option<u32>,
|
||||
after_lines: Option<u32>,
|
||||
spacing: Option<LineSpacingType>,
|
||||
) -> Self {
|
||||
let mut xml_event = XmlEvent::start_element("w:spacing");
|
||||
let before_val: String;
|
||||
let after_val: String;
|
||||
let before_lines_val: String;
|
||||
let after_lines_val: String;
|
||||
let line_val: String;
|
||||
|
||||
if let Some(before) = before {
|
||||
|
@ -182,6 +186,14 @@ impl XMLBuilder {
|
|||
after_val = format!("{}", after);
|
||||
xml_event = xml_event.attr("w:after", &after_val)
|
||||
}
|
||||
if let Some(before_lines) = before_lines {
|
||||
before_lines_val = format!("{}", before_lines);
|
||||
xml_event = xml_event.attr("w:beforeLines", &before_lines_val)
|
||||
}
|
||||
if let Some(after_lines) = after_lines {
|
||||
after_lines_val = format!("{}", after_lines);
|
||||
xml_event = xml_event.attr("w:afterLines", &after_lines_val)
|
||||
}
|
||||
if let Some(line) = line {
|
||||
line_val = format!("{}", line);
|
||||
xml_event = xml_event.attr("w:line", &line_val)
|
||||
|
|
|
@ -420,21 +420,36 @@ pub fn date() -> Result<(), DocxError> {
|
|||
pub fn line_spacing() -> Result<(), DocxError> {
|
||||
let path = std::path::Path::new("./tests/output/line_spacing.docx");
|
||||
let file = std::fs::File::create(&path).unwrap();
|
||||
|
||||
Docx::new()
|
||||
.add_paragraph(
|
||||
Paragraph::new()
|
||||
.add_run(Run::new().add_text(DUMMY))
|
||||
.line_spacing(Some(300), None, Some(300), Some(LineSpacingType::Auto)),
|
||||
.line_spacing(
|
||||
LineSpacing::new()
|
||||
.before(300)
|
||||
.line(300)
|
||||
.line_rule(LineSpacingType::Auto),
|
||||
),
|
||||
)
|
||||
.add_paragraph(
|
||||
Paragraph::new()
|
||||
.add_run(Run::new().add_text(DUMMY))
|
||||
.line_spacing(None, None, Some(300), Some(LineSpacingType::AtLeast)),
|
||||
.line_spacing(
|
||||
LineSpacing::new()
|
||||
.line(300)
|
||||
.line_rule(LineSpacingType::AtLeast),
|
||||
),
|
||||
)
|
||||
.add_paragraph(
|
||||
Paragraph::new()
|
||||
.add_run(Run::new().add_text(DUMMY).character_spacing(100))
|
||||
.line_spacing(None, Some(300), Some(300), Some(LineSpacingType::Exact)),
|
||||
.line_spacing(
|
||||
LineSpacing::new()
|
||||
.after(300)
|
||||
.line(300)
|
||||
.line_rule(LineSpacingType::Exact),
|
||||
),
|
||||
)
|
||||
.build()
|
||||
.pack(file)?;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Paragraph } from "./paragraph";
|
||||
import { Paragraph, ParagraphProperty } from "./paragraph";
|
||||
import { Insert } from "./insert";
|
||||
import { Delete } from "./delete";
|
||||
import { DeleteText } from "./delete-text";
|
||||
|
@ -339,6 +339,51 @@ export class Docx {
|
|||
return comment;
|
||||
}
|
||||
|
||||
buildLineSpacing(p: ParagraphProperty): wasm.LineSpacing | null {
|
||||
const { lineSpacing } = p;
|
||||
if (lineSpacing == null) return null;
|
||||
let kind;
|
||||
switch (lineSpacing._lineRule) {
|
||||
case "atLeast": {
|
||||
kind = wasm.LineSpacingType.AtLeast;
|
||||
break;
|
||||
}
|
||||
case "auto": {
|
||||
kind = wasm.LineSpacingType.Auto;
|
||||
break;
|
||||
}
|
||||
case "exact": {
|
||||
kind = wasm.LineSpacingType.Exact;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let spacing = wasm.createLineSpacing();
|
||||
if (lineSpacing._before != null) {
|
||||
spacing = spacing.before(lineSpacing._before);
|
||||
}
|
||||
|
||||
if (lineSpacing._after != null) {
|
||||
spacing = spacing.after(lineSpacing._after);
|
||||
}
|
||||
|
||||
if (lineSpacing._beforeLines != null) {
|
||||
spacing = spacing.before_lines(lineSpacing._beforeLines);
|
||||
}
|
||||
|
||||
if (lineSpacing._afterLines != null) {
|
||||
spacing = spacing.after_lines(lineSpacing._afterLines);
|
||||
}
|
||||
|
||||
if (lineSpacing._line != null) {
|
||||
spacing = spacing.line(lineSpacing._line);
|
||||
}
|
||||
|
||||
if (kind != null) {
|
||||
spacing = spacing.line_rule(kind);
|
||||
}
|
||||
return spacing;
|
||||
}
|
||||
|
||||
buildParagraph(p: Paragraph) {
|
||||
let paragraph = wasm.createParagraph();
|
||||
p.children.forEach((child) => {
|
||||
|
@ -424,28 +469,10 @@ export class Docx {
|
|||
}
|
||||
|
||||
if (typeof p.property.lineSpacing !== "undefined") {
|
||||
const { lineSpacing } = p.property;
|
||||
let kind;
|
||||
switch (p.property.lineSpacing.lineRule) {
|
||||
case "atLeast": {
|
||||
kind = wasm.LineSpacingType.AtLeast;
|
||||
break;
|
||||
}
|
||||
case "auto": {
|
||||
kind = wasm.LineSpacingType.Auto;
|
||||
break;
|
||||
}
|
||||
case "exact": {
|
||||
kind = wasm.LineSpacingType.Exact;
|
||||
break;
|
||||
}
|
||||
const spacing = this.buildLineSpacing(p.property);
|
||||
if (spacing) {
|
||||
paragraph = paragraph.line_spacing(spacing);
|
||||
}
|
||||
paragraph = paragraph.line_spacing(
|
||||
lineSpacing.before,
|
||||
lineSpacing.after,
|
||||
lineSpacing.line,
|
||||
kind
|
||||
);
|
||||
}
|
||||
|
||||
if (p.property.runProperty.italic) {
|
||||
|
|
|
@ -1,181 +1,205 @@
|
|||
import {Run, RunProperty, RunFonts, createDefaultRunProperty} from "./run";
|
||||
import {Insert} from "./insert";
|
||||
import {Delete} from "./delete";
|
||||
import {BookmarkStart} from "./bookmark-start";
|
||||
import {BookmarkEnd} from "./bookmark-end";
|
||||
import {Comment} from "./comment";
|
||||
import {CommentEnd} from "./comment-end";
|
||||
import { Run, RunProperty, RunFonts, createDefaultRunProperty } from "./run";
|
||||
import { Insert } from "./insert";
|
||||
import { Delete } from "./delete";
|
||||
import { BookmarkStart } from "./bookmark-start";
|
||||
import { BookmarkEnd } from "./bookmark-end";
|
||||
import { Comment } from "./comment";
|
||||
import { CommentEnd } from "./comment-end";
|
||||
|
||||
export type ParagraphChild =
|
||||
| Run
|
||||
| Insert
|
||||
| Delete
|
||||
| BookmarkStart
|
||||
| BookmarkEnd
|
||||
| Comment
|
||||
| CommentEnd;
|
||||
| Run
|
||||
| Insert
|
||||
| Delete
|
||||
| BookmarkStart
|
||||
| BookmarkEnd
|
||||
| Comment
|
||||
| CommentEnd;
|
||||
|
||||
export type AlignmentType =
|
||||
| "center"
|
||||
| "left"
|
||||
| "right"
|
||||
| "both"
|
||||
| "justified"
|
||||
| "distribute"
|
||||
| "end";
|
||||
| "center"
|
||||
| "left"
|
||||
| "right"
|
||||
| "both"
|
||||
| "justified"
|
||||
| "distribute"
|
||||
| "end";
|
||||
|
||||
export type SpecialIndentKind = "firstLine" | "hanging";
|
||||
|
||||
export type LineSpacingType = "atLeast" | "auto" | "exact"
|
||||
export type LineSpacingType = "atLeast" | "auto" | "exact";
|
||||
|
||||
export class LineSpacing {
|
||||
_before?: number;
|
||||
_after?: number;
|
||||
_beforeLines?: number;
|
||||
_afterLines?: number;
|
||||
_line?: number;
|
||||
_lineRule?: LineSpacingType;
|
||||
|
||||
before(v: number) {
|
||||
this._before = v;
|
||||
return this;
|
||||
}
|
||||
after(v: number) {
|
||||
this._after = v;
|
||||
return this;
|
||||
}
|
||||
beforeLines(v: number) {
|
||||
this._beforeLines = v;
|
||||
return this;
|
||||
}
|
||||
afterLines(v: number) {
|
||||
this._afterLines = v;
|
||||
return this;
|
||||
}
|
||||
line(v: number) {
|
||||
this._line = v;
|
||||
return this;
|
||||
}
|
||||
lineRule(v: LineSpacingType) {
|
||||
this._lineRule = v;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export type ParagraphProperty = {
|
||||
align?: AlignmentType;
|
||||
styleId?: string;
|
||||
indent?: {
|
||||
left: number;
|
||||
specialIndentKind?: SpecialIndentKind;
|
||||
specialIndentSize?: number;
|
||||
};
|
||||
numbering?: {
|
||||
id: number;
|
||||
level: number;
|
||||
};
|
||||
lineSpacing?: {
|
||||
before?: number;
|
||||
after?: number;
|
||||
line?: number;
|
||||
lineRule?: LineSpacingType;
|
||||
};
|
||||
runProperty: RunProperty;
|
||||
keepNext: boolean;
|
||||
keepLines: boolean;
|
||||
pageBreakBefore: boolean;
|
||||
windowControl: boolean;
|
||||
align?: AlignmentType;
|
||||
styleId?: string;
|
||||
indent?: {
|
||||
left: number;
|
||||
specialIndentKind?: SpecialIndentKind;
|
||||
specialIndentSize?: number;
|
||||
};
|
||||
numbering?: {
|
||||
id: number;
|
||||
level: number;
|
||||
};
|
||||
lineSpacing?: LineSpacing;
|
||||
runProperty: RunProperty;
|
||||
keepNext: boolean;
|
||||
keepLines: boolean;
|
||||
pageBreakBefore: boolean;
|
||||
windowControl: boolean;
|
||||
};
|
||||
|
||||
export const createDefaultParagraphProperty = (): ParagraphProperty => {
|
||||
return {
|
||||
runProperty: createDefaultRunProperty(),
|
||||
keepNext: false,
|
||||
keepLines: false,
|
||||
pageBreakBefore: false,
|
||||
windowControl: false,
|
||||
};
|
||||
return {
|
||||
runProperty: createDefaultRunProperty(),
|
||||
keepNext: false,
|
||||
keepLines: false,
|
||||
pageBreakBefore: false,
|
||||
windowControl: false,
|
||||
};
|
||||
};
|
||||
|
||||
export class Paragraph {
|
||||
hasNumberings = false;
|
||||
children: ParagraphChild[] = [];
|
||||
property: ParagraphProperty = createDefaultParagraphProperty();
|
||||
hasNumberings = false;
|
||||
children: ParagraphChild[] = [];
|
||||
property: ParagraphProperty = createDefaultParagraphProperty();
|
||||
|
||||
addRun(run: Run) {
|
||||
this.children.push(run);
|
||||
return this;
|
||||
}
|
||||
addRun(run: Run) {
|
||||
this.children.push(run);
|
||||
return this;
|
||||
}
|
||||
|
||||
addInsert(ins: Insert) {
|
||||
this.children.push(ins);
|
||||
return this;
|
||||
}
|
||||
addInsert(ins: Insert) {
|
||||
this.children.push(ins);
|
||||
return this;
|
||||
}
|
||||
|
||||
addDelete(del: Delete) {
|
||||
this.children.push(del);
|
||||
return this;
|
||||
}
|
||||
addDelete(del: Delete) {
|
||||
this.children.push(del);
|
||||
return this;
|
||||
}
|
||||
|
||||
addBookmarkStart(id: number, name: string) {
|
||||
this.children.push(new BookmarkStart(id, name));
|
||||
return this;
|
||||
}
|
||||
addBookmarkStart(id: number, name: string) {
|
||||
this.children.push(new BookmarkStart(id, name));
|
||||
return this;
|
||||
}
|
||||
|
||||
addBookmarkEnd(id: number) {
|
||||
this.children.push(new BookmarkEnd(id));
|
||||
return this;
|
||||
}
|
||||
addBookmarkEnd(id: number) {
|
||||
this.children.push(new BookmarkEnd(id));
|
||||
return this;
|
||||
}
|
||||
|
||||
addCommentStart(comment: Comment) {
|
||||
this.children.push(comment);
|
||||
return this;
|
||||
}
|
||||
addCommentStart(comment: Comment) {
|
||||
this.children.push(comment);
|
||||
return this;
|
||||
}
|
||||
|
||||
addCommentEnd(end: CommentEnd) {
|
||||
this.children.push(end);
|
||||
return this;
|
||||
}
|
||||
addCommentEnd(end: CommentEnd) {
|
||||
this.children.push(end);
|
||||
return this;
|
||||
}
|
||||
|
||||
align(type: AlignmentType) {
|
||||
this.property.align = type;
|
||||
return this;
|
||||
}
|
||||
align(type: AlignmentType) {
|
||||
this.property.align = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
style(id: string) {
|
||||
this.property.styleId = id;
|
||||
return this;
|
||||
}
|
||||
style(id: string) {
|
||||
this.property.styleId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
indent(
|
||||
left: number,
|
||||
specialIndentKind?: SpecialIndentKind,
|
||||
specialIndentSize?: number
|
||||
) {
|
||||
this.property.indent = {left, specialIndentKind, specialIndentSize};
|
||||
return this;
|
||||
}
|
||||
indent(
|
||||
left: number,
|
||||
specialIndentKind?: SpecialIndentKind,
|
||||
specialIndentSize?: number
|
||||
) {
|
||||
this.property.indent = { left, specialIndentKind, specialIndentSize };
|
||||
return this;
|
||||
}
|
||||
|
||||
numbering(id: number, level: number) {
|
||||
this.hasNumberings = true;
|
||||
this.property.numbering = {id, level};
|
||||
return this;
|
||||
}
|
||||
numbering(id: number, level: number) {
|
||||
this.hasNumberings = true;
|
||||
this.property.numbering = { id, level };
|
||||
return this;
|
||||
}
|
||||
|
||||
lineSpacing(before?: number, after?: number, line?: number, lineRule?: LineSpacingType) {
|
||||
this.property.lineSpacing = {
|
||||
before,
|
||||
after,
|
||||
line,
|
||||
lineRule
|
||||
}
|
||||
return this;
|
||||
}
|
||||
lineSpacing(spacing: LineSpacing) {
|
||||
this.property.lineSpacing = spacing;
|
||||
return this;
|
||||
}
|
||||
|
||||
keepNext(v: boolean) {
|
||||
this.property = {...this.property, keepNext: v};
|
||||
return this;
|
||||
}
|
||||
keepNext(v: boolean) {
|
||||
this.property = { ...this.property, keepNext: v };
|
||||
return this;
|
||||
}
|
||||
|
||||
keepLines(v: boolean) {
|
||||
this.property = {...this.property, keepLines: v};
|
||||
return this;
|
||||
}
|
||||
keepLines(v: boolean) {
|
||||
this.property = { ...this.property, keepLines: v };
|
||||
return this;
|
||||
}
|
||||
|
||||
pageBreakBefore(v: boolean) {
|
||||
this.property = {...this.property, pageBreakBefore: v};
|
||||
return this;
|
||||
}
|
||||
pageBreakBefore(v: boolean) {
|
||||
this.property = { ...this.property, pageBreakBefore: v };
|
||||
return this;
|
||||
}
|
||||
|
||||
windowControl(v: boolean) {
|
||||
this.property = {...this.property, windowControl: v};
|
||||
return this;
|
||||
}
|
||||
windowControl(v: boolean) {
|
||||
this.property = { ...this.property, windowControl: v };
|
||||
return this;
|
||||
}
|
||||
|
||||
// run property
|
||||
size(size: number) {
|
||||
this.property.runProperty = {...this.property.runProperty, size};
|
||||
return this;
|
||||
}
|
||||
// run property
|
||||
size(size: number) {
|
||||
this.property.runProperty = { ...this.property.runProperty, size };
|
||||
return this;
|
||||
}
|
||||
|
||||
bold() {
|
||||
this.property.runProperty = {...this.property.runProperty, bold: true};
|
||||
return this;
|
||||
}
|
||||
bold() {
|
||||
this.property.runProperty = { ...this.property.runProperty, bold: true };
|
||||
return this;
|
||||
}
|
||||
|
||||
italic() {
|
||||
this.property.runProperty = {...this.property.runProperty, italic: true};
|
||||
return this;
|
||||
}
|
||||
italic() {
|
||||
this.property.runProperty = { ...this.property.runProperty, italic: true };
|
||||
return this;
|
||||
}
|
||||
|
||||
fonts(fonts: RunFonts) {
|
||||
this.property.runProperty = {...this.property.runProperty, fonts};
|
||||
return this;
|
||||
}
|
||||
fonts(fonts: RunFonts) {
|
||||
this.property.runProperty = { ...this.property.runProperty, fonts };
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ mod footer;
|
|||
mod insert;
|
||||
mod level;
|
||||
mod level_override;
|
||||
mod line_spacing;
|
||||
mod numbering;
|
||||
mod page_margin;
|
||||
mod paragraph;
|
||||
|
@ -28,6 +29,7 @@ pub use footer::*;
|
|||
pub use insert::*;
|
||||
pub use level::*;
|
||||
pub use level_override::*;
|
||||
pub use line_spacing::*;
|
||||
pub use numbering::*;
|
||||
pub use page_margin::*;
|
||||
pub use paragraph::*;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct LineSpacing(docx_rs::LineSpacing);
|
||||
|
||||
#[wasm_bindgen(js_name = createLineSpacing)]
|
||||
pub fn create_line_spacing() -> LineSpacing {
|
||||
LineSpacing(docx_rs::LineSpacing::new())
|
||||
}
|
||||
|
||||
impl LineSpacing {
|
||||
pub fn take(self) -> docx_rs::LineSpacing {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl LineSpacing {
|
||||
pub fn line_rule(mut self, t: docx_rs::LineSpacingType) -> Self {
|
||||
self.0 = self.0.line_rule(t);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn before(mut self, before: u32) -> Self {
|
||||
self.0 = self.0.before(before);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn after(mut self, after: u32) -> Self {
|
||||
self.0 = self.0.after(after);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn before_lines(mut self, before: u32) -> Self {
|
||||
self.0 = self.0.before_lines(before);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn after_lines(mut self, after: u32) -> Self {
|
||||
self.0 = self.0.after_lines(after);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn line(mut self, line: u32) -> Self {
|
||||
self.0 = self.0.line(line);
|
||||
self
|
||||
}
|
||||
}
|
|
@ -112,14 +112,8 @@ impl Paragraph {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn line_spacing(
|
||||
mut self,
|
||||
before: Option<u32>,
|
||||
after: Option<u32>,
|
||||
line: Option<u32>,
|
||||
spacing_type: Option<docx_rs::LineSpacingType>,
|
||||
) -> Self {
|
||||
self.0 = self.0.line_spacing(before, after, line, spacing_type);
|
||||
pub fn line_spacing(mut self, spacing: LineSpacing) -> Self {
|
||||
self.0 = self.0.line_spacing(spacing.take());
|
||||
self
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -74,6 +74,12 @@ describe("reader", () => {
|
|||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("should read afterLines docx", () => {
|
||||
const buffer = readFileSync("../fixtures/after_lines/after_lines.docx");
|
||||
const json = w.readDocx(buffer);
|
||||
expect(json).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("writer", () => {
|
||||
|
@ -352,7 +358,9 @@ describe("writer", () => {
|
|||
test("should write line spacing", () => {
|
||||
const p = new w.Paragraph()
|
||||
.addRun(new w.Run().addText("Hello "))
|
||||
.lineSpacing(100, "", 100, 1);
|
||||
.lineSpacing(
|
||||
new w.LineSpacing().before(100).after(0).line(100).afterLines(400)
|
||||
);
|
||||
const buffer = new w.Docx().addParagraph(p).build();
|
||||
writeFileSync("../output/line_spacing.docx", buffer);
|
||||
const z = new Zip(Buffer.from(buffer));
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue