Merge branch 'master' of https://github.com/h4buli/docx into feat/h4-update

# Conflicts:
#	package.json
#	src/file/document/body/body.ts
#	src/file/document/body/section-properties/section-properties.ts
#	src/file/file.ts
#	src/file/media/media.ts
#	src/file/styles/external-styles-factory.ts
#	src/file/table/table-cell.ts
This commit is contained in:
Dolan
2018-08-09 23:21:24 +01:00
19 changed files with 366 additions and 37 deletions

View File

@ -1,4 +1,5 @@
import { IXmlableObject, XmlComponent } from "file/xml-components"; import { IXmlableObject, XmlComponent } from "file/xml-components";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties/section-properties";
import { Paragraph, ParagraphProperties } from "../.."; import { Paragraph, ParagraphProperties } from "../..";
import { SectionProperties, SectionPropertiesOptions } from "./section-properties"; import { SectionProperties, SectionPropertiesOptions } from "./section-properties";

View File

@ -3,3 +3,4 @@ export * from "./footer-reference";
export * from "./header-reference"; export * from "./header-reference";
export * from "./page-size"; export * from "./page-size";
export * from "./page-number"; export * from "./page-number";
export * from "./page-border";

View File

@ -0,0 +1 @@
export * from "./page-borders";

View File

@ -0,0 +1,91 @@
import { expect } from "chai";
import { Formatter } from "../../../../../export/formatter";
import { PageBorders, PageBorderDisplay, PageBorderZOrder } from "./page-borders";
import { BorderStyle } from "../../../../styles";
describe("PageBorders", () => {
describe("#constructor()", () => {
it("should create empty element when no options are passed", () => {
const properties = new PageBorders();
const tree = new Formatter().format(properties);
expect(tree).to.equal("");
});
it("should create page borders with some configuration", () => {
const properties = new PageBorders({
pageBorders: {
display: PageBorderDisplay.FIRST_PAGE,
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage" } });
});
it("should create page borders with full configuration", () => {
const properties = new PageBorders({
pageBorders: {
display: PageBorderDisplay.FIRST_PAGE,
zOrder: PageBorderZOrder.BACK,
},
pageBorderTop: {
style: BorderStyle.DOUBLE_WAVE,
size: 10,
color: "001122",
},
pageBorderRight: {
style: BorderStyle.DOUBLE,
size: 20,
color: "223344",
},
pageBorderBottom: {
style: BorderStyle.SINGLE,
size: 30,
color: "556677",
},
pageBorderLeft: {
style: BorderStyle.DOTTED,
size: 40,
color: "889900",
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:pgBorders"]);
expect(tree["w:pgBorders"]).to.be.an.instanceof(Array);
expect(tree["w:pgBorders"][0]).to.deep.equal({ _attr: { "w:display": "firstPage", "w:zOrder": "back" } });
expect(tree["w:pgBorders"][1]).to.deep.equal({
"w:top": [
{
_attr: { "w:color": "001122", "w:size": 10, "w:val": "doubleWave" },
},
],
});
expect(tree["w:pgBorders"][2]).to.deep.equal({
"w:right": [
{
_attr: { "w:color": "223344", "w:size": 20, "w:val": "double" },
},
],
});
expect(tree["w:pgBorders"][3]).to.deep.equal({
"w:bottom": [
{
_attr: { "w:color": "556677", "w:size": 30, "w:val": "single" },
},
],
});
expect(tree["w:pgBorders"][4]).to.deep.equal({
"w:left": [
{
_attr: { "w:color": "889900", "w:size": 40, "w:val": "dotted" },
},
],
});
});
});
});

View File

@ -0,0 +1,94 @@
// http://officeopenxml.com/WPsectionBorders.php
import { XmlComponent, XmlAttributeComponent, IXmlableObject } from "file/xml-components";
import { BorderStyle } from "../../../../styles";
export enum PageBorderDisplay {
ALL_PAGES = "allPages",
FIRST_PAGE = "firstPage",
NOT_FIRST_PAGE = "notFirstPage",
}
export enum PageBorderOffsetFrom {
PAGE = "page",
TEXT = "text",
}
export enum PageBorderZOrder {
BACK = "back",
FRONT = "front",
}
export interface IPageBorderAttributes {
display?: PageBorderDisplay;
offsetFrom?: PageBorderOffsetFrom;
zOrder?: PageBorderZOrder;
}
export interface PageBorderConfiguration {
style?: BorderStyle;
size?: number;
color?: string;
space?: number;
}
export type PageBordersOptions = {
pageBorders?: IPageBorderAttributes;
pageBorderTop?: PageBorderConfiguration;
pageBorderRight?: PageBorderConfiguration;
pageBorderBottom?: PageBorderConfiguration;
pageBorderLeft?: PageBorderConfiguration;
};
class PageBordeAttributes extends XmlAttributeComponent<PageBorderConfiguration> {
protected xmlKeys = {
style: "w:val",
size: "w:size",
color: "w:color",
space: "w:space",
};
}
class PageBorder extends XmlComponent {
constructor(key: string, options: PageBorderConfiguration) {
super(key);
this.root.push(new PageBordeAttributes(options));
}
}
class PageBordersAttributes extends XmlAttributeComponent<IPageBorderAttributes> {
protected xmlKeys = {
display: "w:display",
offsetFrom: "w:offsetFrom",
zOrder: "w:zOrder",
};
}
export class PageBorders extends XmlComponent {
constructor(options?: PageBordersOptions) {
super("w:pgBorders");
if (!options) return;
let pageBordersAttributes = {};
if (options.pageBorders) {
pageBordersAttributes = {
display: options.pageBorders.display,
offsetFrom: options.pageBorders.offsetFrom,
zOrder: options.pageBorders.zOrder,
};
}
this.root.push(new PageBordersAttributes(pageBordersAttributes));
if (options.pageBorderTop) this.root.push(new PageBorder("w:top", options.pageBorderTop));
if (options.pageBorderRight) this.root.push(new PageBorder("w:right", options.pageBorderRight));
if (options.pageBorderBottom) this.root.push(new PageBorder("w:bottom", options.pageBorderBottom));
if (options.pageBorderLeft) this.root.push(new PageBorder("w:left", options.pageBorderLeft));
}
public prepForXml(): IXmlableObject {
return this.root.length > 0 ? super.prepForXml() : "";
}
}

View File

@ -2,7 +2,7 @@ import { expect } from "chai";
import { Formatter } from "../../../../export/formatter"; import { Formatter } from "../../../../export/formatter";
import { SectionProperties } from "./section-properties"; import { SectionProperties } from "./section-properties";
import { FooterReferenceType, PageNumberFormat } from "."; import { FooterReferenceType, PageNumberFormat, PageBorderOffsetFrom } from ".";
describe("SectionProperties", () => { describe("SectionProperties", () => {
describe("#constructor()", () => { describe("#constructor()", () => {
@ -155,5 +155,18 @@ describe("SectionProperties", () => {
], ],
}); });
}); });
it("should create section properties with page borders", () => {
const properties = new SectionProperties({
pageBorders: {
offsetFrom: PageBorderOffsetFrom.PAGE,
},
});
const tree = new Formatter().format(properties);
expect(Object.keys(tree)).to.deep.equal(["w:sectPr"]);
expect(tree["w:sectPr"][7]).to.deep.equal({
"w:pgBorders": [{ _attr: { "w:offsetFrom": "page" } }],
});
});
}); });
}); });

View File

@ -12,7 +12,9 @@ import { PageMargin } from "./page-margin/page-margin";
import { IPageMarginAttributes } from "./page-margin/page-margin-attributes"; import { IPageMarginAttributes } from "./page-margin/page-margin-attributes";
import { PageSize } from "./page-size/page-size"; import { PageSize } from "./page-size/page-size";
import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes"; import { IPageSizeAttributes, PageOrientation } from "./page-size/page-size-attributes";
import { FooterReferenceType, IPageNumberTypeAttributes, PageNumberType, PageNumberFormat } from ".";
// import { TitlePage } from "./title-page/title-page"; // import { TitlePage } from "./title-page/title-page";
import { FooterReferenceType, IPageNumberTypeAttributes, PageNumberType, PageNumberFormat, PageBordersOptions, PageBorders } from ".";
export type SectionPropertiesOptions = IPageSizeAttributes & export type SectionPropertiesOptions = IPageSizeAttributes &
IPageMarginAttributes & IPageMarginAttributes &
@ -20,7 +22,8 @@ export type SectionPropertiesOptions = IPageSizeAttributes &
IDocGridAttributesProperties & IDocGridAttributesProperties &
IHeaderOptions & IHeaderOptions &
IFooterOptions & IFooterOptions &
IPageNumberTypeAttributes; IPageNumberTypeAttributes &
PageBordersOptions;
export class SectionProperties extends XmlComponent { export class SectionProperties extends XmlComponent {
private readonly options: SectionPropertiesOptions; private readonly options: SectionPropertiesOptions;
@ -47,6 +50,11 @@ export class SectionProperties extends XmlComponent {
footerId: 0, footerId: 0,
pageNumberStart: undefined, pageNumberStart: undefined,
pageNumberFormatType: PageNumberFormat.DECIMAL, pageNumberFormatType: PageNumberFormat.DECIMAL,
pageBorders: undefined,
pageBorderTop: undefined,
pageBorderRight: undefined,
pageBorderBottom: undefined,
pageBorderLeft: undefined,
}; };
const mergedOptions = { const mergedOptions = {
@ -84,6 +92,24 @@ export class SectionProperties extends XmlComponent {
this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType)); this.root.push(new PageNumberType(mergedOptions.pageNumberStart, mergedOptions.pageNumberFormatType));
if (
mergedOptions.pageBorders ||
mergedOptions.pageBorderTop ||
mergedOptions.pageBorderRight ||
mergedOptions.pageBorderBottom ||
mergedOptions.pageBorderLeft
) {
this.root.push(
new PageBorders({
pageBorders: mergedOptions.pageBorders,
pageBorderTop: mergedOptions.pageBorderTop,
pageBorderRight: mergedOptions.pageBorderRight,
pageBorderBottom: mergedOptions.pageBorderBottom,
pageBorderLeft: mergedOptions.pageBorderLeft,
}),
);
}
this.options = mergedOptions; this.options = mergedOptions;
} }

View File

@ -3,7 +3,7 @@ import { XmlComponent } from "file/xml-components";
import { Paragraph } from "../paragraph"; import { Paragraph } from "../paragraph";
import { Table } from "../table"; import { Table } from "../table";
import { Body } from "./body"; import { Body } from "./body";
import { SectionPropertiesOptions } from "./body/section-properties/section-properties"; import { SectionPropertiesOptions } from "./body/section-properties";
import { DocumentAttributes } from "./document-attributes"; import { DocumentAttributes } from "./document-attributes";
export class Document extends XmlComponent { export class Document extends XmlComponent {

View File

@ -3,7 +3,6 @@ import { ContentTypes } from "./content-types/content-types";
import { CoreProperties, IPropertiesOptions } from "./core-properties"; import { CoreProperties, IPropertiesOptions } from "./core-properties";
import { Document } from "./document"; import { Document } from "./document";
import { FooterReferenceType, HeaderReference, HeaderReferenceType } from "./document/body/section-properties"; import { FooterReferenceType, HeaderReference, HeaderReferenceType } from "./document/body/section-properties";
import { SectionPropertiesOptions } from "./document/body/section-properties/section-properties";
import { FooterWrapper } from "./footer-wrapper"; import { FooterWrapper } from "./footer-wrapper";
import { FootNotes } from "./footnotes"; import { FootNotes } from "./footnotes";
import { HeaderWrapper } from "./header-wrapper"; import { HeaderWrapper } from "./header-wrapper";
@ -15,6 +14,7 @@ import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory"; import { ExternalStylesFactory } from "./styles/external-styles-factory";
import { DefaultStylesFactory } from "./styles/factory"; import { DefaultStylesFactory } from "./styles/factory";
import { Table } from "./table"; import { Table } from "./table";
import { FooterReferenceType, HeaderReferenceType, SectionPropertiesOptions } from "./document/body/section-properties";
export class File { export class File {
private readonly document: Document; private readonly document: Document;

View File

@ -61,6 +61,8 @@ export class Media {
this.map = new Map<string, IMediaData>(); this.map = new Map<string, IMediaData>();
} }
x: Math.round(dimensions.width * 9525),
y: Math.round(dimensions.height * 9525),
public getMedia(key: string): IMediaData { public getMedia(key: string): IMediaData {
const data = this.map.get(key); const data = this.map.get(key);

View File

@ -1,2 +1,3 @@
export * from "./numbering"; export * from "./numbering";
export * from "./abstract-numbering"; export * from "./abstract-numbering";
export * from "./level";

View File

@ -60,6 +60,23 @@ class LevelJc extends XmlComponent {
} }
} }
export enum LevelSuffix {
NOTHING = "nothing",
SPACE = "space",
TAB = "tab",
}
class Suffix extends XmlComponent {
constructor(value: LevelSuffix) {
super("w:suff");
this.root.push(
new Attributes({
val: value,
}),
);
}
}
export class LevelBase extends XmlComponent { export class LevelBase extends XmlComponent {
private readonly paragraphProperties: ParagraphProperties; private readonly paragraphProperties: ParagraphProperties;
private readonly runProperties: RunProperties; private readonly runProperties: RunProperties;
@ -93,6 +110,11 @@ export class LevelBase extends XmlComponent {
this.root.push(this.runProperties); this.root.push(this.runProperties);
} }
public setSuffix(value: LevelSuffix) {
this.root.push(new Suffix(value));
return this;
}
public addParagraphProperty(property: XmlComponent): Level { public addParagraphProperty(property: XmlComponent): Level {
this.paragraphProperties.push(property); this.paragraphProperties.push(property);
return this; return this;

View File

@ -0,0 +1,29 @@
export enum BorderStyle {
SINGLE = "single",
DASH_DOT_STROKED = "dashDotStroked",
DASHED = "dashed",
DASH_SMALL_GAP = "dashSmallGap",
DOT_DASH = "dotDash",
DOT_DOT_DASH = "dotDotDash",
DOTTED = "dotted",
DOUBLE = "double",
DOUBLE_WAVE = "doubleWave",
INSET = "inset",
NIL = "nil",
NONE = "none",
OUTSET = "outset",
THICK = "thick",
THICK_THIN_LARGE_GAP = "thickThinLargeGap",
THICK_THIN_MEDIUM_GAP = "thickThinMediumGap",
THICK_THIN_SMALL_GAP = "thickThinSmallGap",
THIN_THICK_LARGE_GAP = "thinThickLargeGap",
THIN_THICK_MEDIUM_GAP = "thinThickMediumGap",
THIN_THICK_SMALL_GAP = "thinThickSmallGap",
THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap",
THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap",
THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap",
THREE_D_EMBOSS = "threeDEmboss",
THREE_D_ENGRAVE = "threeDEngrave",
TRIPLE = "triple",
WAVE = "wave",
}

View File

@ -0,0 +1 @@
export * from "./border-style";

View File

@ -10,6 +10,17 @@ describe("External styles factory", () => {
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:styles xmlns:mc="first" xmlns:r="second"> <w:styles xmlns:mc="first" xmlns:r="second">
<w:docDefaults> <w:docDefaults>
<w:rPrDefault>
<w:rPr>
<w:rFonts w:ascii="Arial" w:eastAsiaTheme="minorHAnsi" w:hAnsi="Arial" w:cstheme="minorHAnsi"/>
<w:lang w:val="en-US" w:eastAsia="en-US" w:bidi="ar-SA"/>
</w:rPr>
</w:rPrDefault>
<w:pPrDefault>
<w:pPr>
<w:spacing w:after="160" w:line="259" w:lineRule="auto"/>
</w:pPr>
</w:pPrDefault>
</w:docDefaults> </w:docDefaults>
<w:latentStyles w:defLockedState="1" w:defUIPriority="99"> <w:latentStyles w:defLockedState="1" w:defUIPriority="99">
@ -51,8 +62,64 @@ describe("External styles factory", () => {
expect(importedStyle.root.length).to.equal(5); expect(importedStyle.root.length).to.equal(5);
expect(importedStyle.root[1]).to.eql({ expect(importedStyle.root[1]).to.eql({
deleted: false,
root: [
{
deleted: false,
root: [
{
deleted: false,
root: [
{
_attr: {
"w:ascii": "Arial",
"w:cstheme": "minorHAnsi",
"w:eastAsiaTheme": "minorHAnsi",
"w:hAnsi": "Arial",
},
deleted: false, deleted: false,
root: [], root: [],
rootKey: "w:rFonts",
},
{
_attr: {
"w:bidi": "ar-SA",
"w:eastAsia": "en-US",
"w:val": "en-US",
},
deleted: false,
root: [],
rootKey: "w:lang",
},
],
rootKey: "w:rPr",
},
],
rootKey: "w:rPrDefault",
},
{
deleted: false,
root: [
{
deleted: false,
root: [
{
_attr: {
"w:after": "160",
"w:line": "259",
"w:lineRule": "auto",
},
deleted: false,
root: [],
rootKey: "w:spacing",
},
],
rootKey: "w:pPr",
},
],
rootKey: "w:pPrDefault",
},
],
rootKey: "w:docDefaults", rootKey: "w:docDefaults",
}); });
expect(importedStyle.root[2]).to.eql({ expect(importedStyle.root[2]).to.eql({

View File

@ -1,5 +1,7 @@
import * as fastXmlParser from "fast-xml-parser"; import * as fastXmlParser from "fast-xml-parser";
import { ImportedXmlComponent, ImportedRootElementAttributes, parseOptions, convertToXmlComponent } from "./../../file/xml-components";
import { convertToXmlComponent, ImportedRootElementAttributes, ImportedXmlComponent, parseOptions } from "file/xml-components"; import { convertToXmlComponent, ImportedRootElementAttributes, ImportedXmlComponent, parseOptions } from "file/xml-components";
import { ImportedRootElementAttributes, parseOptions, convertToXmlComponent } from "./../../file/xml-components";
import { Styles } from "./"; import { Styles } from "./";
export class ExternalStylesFactory { export class ExternalStylesFactory {
@ -34,7 +36,12 @@ export class ExternalStylesFactory {
Object.keys(xmlStyles) Object.keys(xmlStyles)
.filter((element) => element !== "_attr" && element !== "w:style") .filter((element) => element !== "_attr" && element !== "w:style")
.forEach((element) => { .forEach((element) => {
importedStyle.push(new ImportedXmlComponent(element, xmlStyles[element]._attr)); const converted = convertToXmlComponent(element, xmlStyles[element]);
if (Array.isArray(converted)) {
converted.forEach((c) => importedStyle.push(c));
} else {
importedStyle.push(converted);
}
}); });
// convert the styles one by one // convert the styles one by one

View File

@ -1,6 +1,7 @@
import { BaseXmlComponent, XmlComponent } from "file/xml-components"; import { BaseXmlComponent, XmlComponent } from "file/xml-components";
import { DocumentDefaults } from "./defaults"; import { DocumentDefaults } from "./defaults";
import { ParagraphStyle } from "./style"; import { ParagraphStyle } from "./style";
export * from "./border";
export class Styles extends XmlComponent { export class Styles extends XmlComponent {
constructor(initialStyles?: BaseXmlComponent) { constructor(initialStyles?: BaseXmlComponent) {

View File

@ -1,7 +1,8 @@
import { expect } from "chai"; import { expect } from "chai";
import { TableCellBorders, BorderStyle, TableCellWidth, WidthType } from "./table-cell"; import { TableCellBorders, TableCellWidth, WidthType } from "./table-cell";
import { Formatter } from "../../export/formatter"; import { Formatter } from "../../export/formatter";
import { BorderStyle } from "../styles";
describe("TableCellBorders", () => { describe("TableCellBorders", () => {
describe("#prepForXml", () => { describe("#prepForXml", () => {

View File

@ -1,34 +1,5 @@
import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components"; import { IXmlableObject, XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { BorderStyle } from "../styles";
export enum BorderStyle {
SINGLE = "single",
DASH_DOT_STROKED = "dashDotStroked",
DASHED = "dashed",
DASH_SMALL_GAP = "dashSmallGap",
DOT_DASH = "dotDash",
DOT_DOT_DASH = "dotDotDash",
DOTTED = "dotted",
DOUBLE = "double",
DOUBLE_WAVE = "doubleWave",
INSET = "inset",
NIL = "nil",
NONE = "none",
OUTSET = "outset",
THICK = "thick",
THICK_THIN_LARGE_GAP = "thickThinLargeGap",
THICK_THIN_MEDIUM_GAP = "thickThinMediumGap",
THICK_THIN_SMALL_GAP = "thickThinSmallGap",
THIN_THICK_LARGE_GAP = "thinThickLargeGap",
THIN_THICK_MEDIUM_GAP = "thinThickMediumGap",
THIN_THICK_SMALL_GAP = "thinThickSmallGap",
THIN_THICK_THIN_LARGE_GAP = "thinThickThinLargeGap",
THIN_THICK_THIN_MEDIUM_GAP = "thinThickThinMediumGap",
THIN_THICK_THIN_SMALL_GAP = "thinThickThinSmallGap",
THREE_D_EMBOSS = "threeDEmboss",
THREE_D_ENGRAVE = "threeDEngrave",
TRIPLE = "triple",
WAVE = "wave",
}
interface ICellBorder { interface ICellBorder {
style: BorderStyle; style: BorderStyle;