Merge pull request #84 from dolanmiu/feat/h4-update

Feat/h4 update
This commit is contained in:
Dolan
2018-06-22 01:22:50 +02:00
committed by GitHub
42 changed files with 897 additions and 59 deletions

View File

@ -0,0 +1,26 @@
import { XmlAttributeComponent } from "file/xml-components";
import { IDistance } from "../drawing";
export interface IAnchorAttributes extends IDistance {
allowOverlap?: "0" | "1";
behindDoc?: "0" | "1";
layoutInCell?: "0" | "1";
locked?: "0" | "1";
relativeHeight?: number;
simplePos?: "0" | "1";
}
export class AnchorAttributes extends XmlAttributeComponent<IAnchorAttributes> {
protected xmlKeys = {
distT: "distT",
distB: "distB",
distL: "distL",
distR: "distR",
allowOverlap: "allowOverlap",
behindDoc: "behindDoc",
layoutInCell: "layoutInCell",
locked: "locked",
relativeHeight: "relativeHeight",
simplePos: "simplePos",
};
}

View File

@ -0,0 +1,118 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { IDrawingOptions, TextWrapStyle } from ".././";
import { Anchor } from "./";
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
return new Anchor(
1,
{
pixels: {
x: 100,
y: 100,
},
emus: {
x: 100 * 9525,
y: 100 * 9525,
},
},
drawingOptions,
);
}
describe("Anchor", () => {
let anchor: Anchor;
describe("#constructor()", () => {
it("should create a Drawing with correct root key", () => {
anchor = createDrawing({});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.rootKey, "wp:anchor");
assert.equal(newJson.root.length, 10);
});
it("should create a Drawing with all default options", () => {
anchor = createDrawing({});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
const anchorAttributes = newJson.root[0].root;
assert.include(anchorAttributes, {
distT: 0,
distB: 0,
distL: 0,
distR: 0,
simplePos: "0",
allowOverlap: "1",
behindDoc: "0",
locked: "0",
layoutInCell: "1",
relativeHeight: 952500,
});
// 1: simple pos
assert.equal(newJson.root[1].rootKey, "wp:simplePos");
// 2: horizontal position
const horizontalPosition = newJson.root[2];
assert.equal(horizontalPosition.rootKey, "wp:positionH");
assert.include(horizontalPosition.root[0].root, {
relativeFrom: "column",
});
assert.equal(horizontalPosition.root[1].rootKey, "wp:posOffset");
assert.include(horizontalPosition.root[1].root[0], 0);
// 3: vertical position
const verticalPosition = newJson.root[3];
assert.equal(verticalPosition.rootKey, "wp:positionV");
assert.include(verticalPosition.root[0].root, {
relativeFrom: "paragraph",
});
assert.equal(verticalPosition.root[1].rootKey, "wp:posOffset");
assert.include(verticalPosition.root[1].root[0], 0);
// 4: extent
const extent = newJson.root[4];
assert.equal(extent.rootKey, "wp:extent");
assert.include(extent.root[0].root, {
cx: 952500,
cy: 952500,
});
// 5: effect extent
const effectExtent = newJson.root[5];
assert.equal(effectExtent.rootKey, "wp:effectExtent");
// 6 text wrap: none
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapNone");
// 7: doc properties
const docProperties = newJson.root[7];
assert.equal(docProperties.rootKey, "wp:docPr");
// 8: graphic frame properties
const graphicFrame = newJson.root[8];
assert.equal(graphicFrame.rootKey, "wp:cNvGraphicFramePr");
// 9: graphic
const graphic = newJson.root[9];
assert.equal(graphic.rootKey, "a:graphic");
});
it("should create a Drawing with text wrapping", () => {
anchor = createDrawing({
textWrapping: {
textWrapStyle: TextWrapStyle.SQUARE,
},
});
const newJson = Utility.jsonify(anchor);
assert.equal(newJson.root.length, 10);
// 6 text wrap: square
const textWrap = newJson.root[6];
assert.equal(textWrap.rootKey, "wp:wrapSquare");
});
});
});

View File

@ -0,0 +1,88 @@
// http://officeopenxml.com/drwPicFloating.php
import { IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { IDrawingOptions } from "../drawing";
import {
HorizontalPosition,
HorizontalPositionRelativeFrom,
IFloating,
SimplePos,
VerticalPosition,
VerticalPositionRelativeFrom,
} from "../floating";
import { Graphic } from "../inline/graphic";
import { TextWrapStyle, WrapNone, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
import { DocProperties } from "./../doc-properties/doc-properties";
import { EffectExtent } from "./../effect-extent/effect-extent";
import { Extent } from "./../extent/extent";
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
import { AnchorAttributes } from "./anchor-attributes";
const defaultOptions: IFloating = {
allowOverlap: true,
behindDocument: false,
lockAnchor: false,
layoutInCell: true,
verticalPosition: {
relative: VerticalPositionRelativeFrom.PARAGRAPH,
offset: 0,
},
horizontalPosition: {
relative: HorizontalPositionRelativeFrom.COLUMN,
offset: 0,
},
};
export class Anchor extends XmlComponent {
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
super("wp:anchor");
const floating = {
...defaultOptions,
...drawingOptions.floating,
};
this.root.push(
new AnchorAttributes({
distT: 0,
distB: 0,
distL: 0,
distR: 0,
simplePos: "0", // note: word doesn't fully support - so we use 0
allowOverlap: floating.allowOverlap === true ? "1" : "0",
behindDoc: floating.behindDocument === true ? "1" : "0",
locked: floating.lockAnchor === true ? "1" : "0",
layoutInCell: floating.layoutInCell === true ? "1" : "0",
relativeHeight: dimensions.emus.y,
}),
);
this.root.push(new SimplePos());
this.root.push(new HorizontalPosition(floating.horizontalPosition));
this.root.push(new VerticalPosition(floating.verticalPosition));
this.root.push(new Extent(dimensions.emus.x, dimensions.emus.y));
this.root.push(new EffectExtent());
if (drawingOptions.textWrapping != null) {
switch (drawingOptions.textWrapping.textWrapStyle) {
case TextWrapStyle.SQUARE:
this.root.push(new WrapSquare(drawingOptions.textWrapping));
break;
case TextWrapStyle.TIGHT:
this.root.push(new WrapTight(drawingOptions.textWrapping.distanceFromText));
break;
case TextWrapStyle.TOP_AND_BOTTOM:
this.root.push(new WrapTopAndBottom(drawingOptions.textWrapping.distanceFromText));
break;
case TextWrapStyle.NONE:
default:
this.root.push(new WrapNone());
}
} else {
this.root.push(new WrapNone());
}
this.root.push(new DocProperties());
this.root.push(new GraphicFrameProperties());
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
}
}

View File

@ -0,0 +1,2 @@
export * from "./anchor";
export * from "./anchor-attributes";

View File

@ -2,14 +2,12 @@ import { assert } from "chai";
import * as fs from "fs"; import * as fs from "fs";
import { Utility } from "../../tests/utility"; import { Utility } from "../../tests/utility";
import { Drawing } from "./"; import { Drawing, IDrawingOptions, PlacementPosition } from "./";
describe("Drawing", () => { function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
let currentBreak: Drawing; const path = "./demo/images/image1.jpeg";
return new Drawing(
beforeEach(() => { {
const path = "./demo/images/image1.jpeg";
currentBreak = new Drawing({
fileName: "test.jpg", fileName: "test.jpg",
referenceId: 1, referenceId: 1,
stream: fs.createReadStream(path), stream: fs.createReadStream(path),
@ -24,14 +22,33 @@ describe("Drawing", () => {
y: 100 * 9525, y: 100 * 9525,
}, },
}, },
}); },
}); drawingOptions,
);
}
describe("Drawing", () => {
let currentBreak: Drawing;
describe("#constructor()", () => { describe("#constructor()", () => {
it("should create a Drawing with correct root key", () => { it("should create a Drawing with correct root key", () => {
currentBreak = createDrawing();
const newJson = Utility.jsonify(currentBreak); const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.rootKey, "w:drawing"); assert.equal(newJson.rootKey, "w:drawing");
// console.log(JSON.stringify(newJson, null, 2)); });
it("should create a drawing with inline element when there are no options passed", () => {
currentBreak = createDrawing();
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.root[0].rootKey, "wp:inline");
});
it("should create a drawing with anchor element when there options are passed", () => {
currentBreak = createDrawing({
position: PlacementPosition.FLOATING,
});
const newJson = Utility.jsonify(currentBreak);
assert.equal(newJson.root[0].rootKey, "wp:anchor");
}); });
}); });
}); });

View File

@ -1,20 +1,53 @@
import { IMediaData } from "file/media"; import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { Anchor } from "./anchor";
import { IFloating } from "./floating";
import { Inline } from "./inline"; import { Inline } from "./inline";
import { ITextWrapping } from "./text-wrap";
export enum PlacementPosition {
INLINE,
FLOATING,
}
export interface IDistance {
distT?: number;
distB?: number;
distL?: number;
distR?: number;
}
export interface IDrawingOptions {
position?: PlacementPosition;
textWrapping?: ITextWrapping;
floating?: IFloating;
}
const defaultDrawingOptions: IDrawingOptions = {
position: PlacementPosition.INLINE,
};
export class Drawing extends XmlComponent { export class Drawing extends XmlComponent {
private inline: Inline; private inline: Inline;
constructor(imageData: IMediaData) { constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
super("w:drawing"); super("w:drawing");
if (imageData === undefined) { if (imageData === undefined) {
throw new Error("imageData cannot be undefined"); throw new Error("imageData cannot be undefined");
} }
this.inline = new Inline(imageData.referenceId, imageData.dimensions); const mergedOptions = {
...defaultDrawingOptions,
...drawingOptions,
};
this.root.push(this.inline); if (mergedOptions.position === PlacementPosition.INLINE) {
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
this.root.push(this.inline);
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
}
} }
public scale(factorX: number, factorY: number): void { public scale(factorX: number, factorY: number): void {

View File

@ -0,0 +1,15 @@
import { assert } from "chai";
import { VerticalPositionAlign } from ".";
import { Utility } from "../../../tests/utility";
import { Align } from "./align";
describe("Align", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new Align(VerticalPositionAlign.CENTER));
assert.equal(newJson.rootKey, "wp:align");
assert.include(newJson.root[0], VerticalPositionAlign.CENTER);
});
});
});

View File

@ -0,0 +1,10 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { XmlComponent } from "file/xml-components";
import { HorizontalPositionAlign, VerticalPositionAlign } from "./floating-position";
export class Align extends XmlComponent {
constructor(value: HorizontalPositionAlign | VerticalPositionAlign) {
super("wp:align");
this.root.push(value);
}
}

View File

@ -0,0 +1,60 @@
// http://officeopenxml.com/drwPicFloating-position.php
export enum HorizontalPositionRelativeFrom {
CHARACTER = "character",
COLUMN = "column",
INSIDE_MARGIN = "insideMargin",
LEFT_MARGIN = "leftMargin",
MARGIN = "margin",
OUTSIDE_MARGIN = "outsideMargin",
PAGE = "page",
RIGHT_MARGIN = "rightMargin",
}
export enum VerticalPositionRelativeFrom {
BOTTOM_MARGIN = "bottomMargin",
INSIDE_MARGIN = "insideMargin",
LINE = "line",
MARGIN = "margin",
OUTSIDE_MARGIN = "outsideMargin",
PAGE = "page",
PARAGRAPH = "paragraph",
TOP_MARGIN = "topMargin",
}
export enum HorizontalPositionAlign {
CENTER = "center",
INSIDE = "inside",
LEFT = "left",
OUTSIDE = "outside",
RIGHT = "right",
}
export enum VerticalPositionAlign {
BOTTOM = "bottom",
CENTER = "center",
INSIDE = "inside",
OUTSIDE = "outside",
TOP = "top",
}
export interface IHorizontalPositionOptions {
relative: HorizontalPositionRelativeFrom;
align?: HorizontalPositionAlign;
offset?: number;
}
export interface IVerticalPositionOptions {
relative: VerticalPositionRelativeFrom;
align?: VerticalPositionAlign;
offset?: number;
}
export interface IFloating {
horizontalPosition: IHorizontalPositionOptions;
verticalPosition: IVerticalPositionOptions;
allowOverlap?: boolean;
lockAnchor?: boolean;
behindDocument?: boolean;
layoutInCell?: boolean;
}

View File

@ -0,0 +1,41 @@
import { assert } from "chai";
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from ".";
import { Utility } from "../../../tests/utility";
import { HorizontalPosition } from "./horizontal-position";
describe("HorizontalPosition", () => {
describe("#constructor()", () => {
it("should create a element with position align", () => {
const newJson = Utility.jsonify(
new HorizontalPosition({
relative: HorizontalPositionRelativeFrom.MARGIN,
align: HorizontalPositionAlign.CENTER,
}),
);
assert.equal(newJson.rootKey, "wp:positionH");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
});
assert.equal(newJson.root[1].rootKey, "wp:align");
assert.include(newJson.root[1].root, "center");
});
it("should create a element with offset", () => {
const newJson = Utility.jsonify(
new HorizontalPosition({
relative: HorizontalPositionRelativeFrom.MARGIN,
offset: 40,
}),
);
assert.equal(newJson.rootKey, "wp:positionH");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
});
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
assert.include(newJson.root[1].root[0], 40);
});
});
});

View File

@ -0,0 +1,35 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { Align } from "./align";
import { HorizontalPositionRelativeFrom, IHorizontalPositionOptions } from "./floating-position";
import { PositionOffset } from "./position-offset";
interface IHorizontalPositionAttributes {
relativeFrom: HorizontalPositionRelativeFrom;
}
class HorizontalPositionAttributes extends XmlAttributeComponent<IHorizontalPositionAttributes> {
protected xmlKeys = {
relativeFrom: "relativeFrom",
};
}
export class HorizontalPosition extends XmlComponent {
constructor(horizontalPosition: IHorizontalPositionOptions) {
super("wp:positionH");
this.root.push(
new HorizontalPositionAttributes({
relativeFrom: horizontalPosition.relative,
}),
);
if (horizontalPosition.align) {
this.root.push(new Align(horizontalPosition.align));
} else if (horizontalPosition.offset !== undefined) {
this.root.push(new PositionOffset(horizontalPosition.offset));
} else {
throw new Error("There is no configuration provided for floating position (Align or offset)");
}
}
}

View File

@ -0,0 +1,4 @@
export * from "./floating-position";
export * from "./simple-pos";
export * from "./horizontal-position";
export * from "./vertical-position";

View File

@ -0,0 +1,14 @@
import { assert } from "chai";
import { Utility } from "../../../tests/utility";
import { PositionOffset } from "./position-offset";
describe("PositionOffset", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new PositionOffset(50));
assert.equal(newJson.rootKey, "wp:posOffset");
assert.equal(newJson.root[0], 50);
});
});
});

View File

@ -0,0 +1,9 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { XmlComponent } from "file/xml-components";
export class PositionOffset extends XmlComponent {
constructor(offsetValue: number) {
super("wp:posOffset");
this.root.push(offsetValue.toString());
}
}

View File

@ -0,0 +1,17 @@
import { assert } from "chai";
import { SimplePos } from "./simple-pos";
import { Utility } from "../../../tests/utility";
describe("SimplePos", () => {
describe("#constructor()", () => {
it("should create a element with correct root key", () => {
const newJson = Utility.jsonify(new SimplePos());
assert.equal(newJson.rootKey, "wp:simplePos");
assert.include(newJson.root[0].root, {
x: 0,
y: 0,
});
});
});
});

View File

@ -0,0 +1,28 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface ISimplePosAttributes {
x: number;
y: number;
}
class SimplePosAttributes extends XmlAttributeComponent<ISimplePosAttributes> {
protected xmlKeys = {
x: "x",
y: "y",
};
}
export class SimplePos extends XmlComponent {
constructor() {
super("wp:simplePos");
// NOTE: It's not fully supported in Microsoft Word, but this element is needed anyway
this.root.push(
new SimplePosAttributes({
x: 0,
y: 0,
}),
);
}
}

View File

@ -0,0 +1,41 @@
import { assert } from "chai";
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from ".";
import { Utility } from "../../../tests/utility";
import { VerticalPosition } from "./vertical-position";
describe("VerticalPosition", () => {
describe("#constructor()", () => {
it("should create a element with position align", () => {
const newJson = Utility.jsonify(
new VerticalPosition({
relative: VerticalPositionRelativeFrom.MARGIN,
align: VerticalPositionAlign.INSIDE,
}),
);
assert.equal(newJson.rootKey, "wp:positionV");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
});
assert.equal(newJson.root[1].rootKey, "wp:align");
assert.include(newJson.root[1].root, "inside");
});
it("should create a element with offset", () => {
const newJson = Utility.jsonify(
new VerticalPosition({
relative: VerticalPositionRelativeFrom.MARGIN,
offset: 40,
}),
);
assert.equal(newJson.rootKey, "wp:positionV");
assert.include(newJson.root[0].root, {
relativeFrom: "margin",
});
assert.equal(newJson.root[1].rootKey, "wp:posOffset");
assert.include(newJson.root[1].root[0], 40);
});
});
});

View File

@ -0,0 +1,35 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { Align } from "./align";
import { IVerticalPositionOptions, VerticalPositionRelativeFrom } from "./floating-position";
import { PositionOffset } from "./position-offset";
interface IVerticalPositionAttributes {
relativeFrom: VerticalPositionRelativeFrom;
}
class VerticalPositionAttributes extends XmlAttributeComponent<IVerticalPositionAttributes> {
protected xmlKeys = {
relativeFrom: "relativeFrom",
};
}
export class VerticalPosition extends XmlComponent {
constructor(verticalPosition: IVerticalPositionOptions) {
super("wp:positionV");
this.root.push(
new VerticalPositionAttributes({
relativeFrom: verticalPosition.relative,
}),
);
if (verticalPosition.align) {
this.root.push(new Align(verticalPosition.align));
} else if (verticalPosition.offset !== undefined) {
this.root.push(new PositionOffset(verticalPosition.offset));
} else {
throw new Error("There is no configuration provided for floating position (Align or offset)");
}
}
}

View File

@ -1 +1,3 @@
export { Drawing } from "./drawing"; export * from "./drawing";
export * from "./text-wrap";
export * from "./floating";

View File

@ -1,11 +1,8 @@
import { XmlAttributeComponent } from "file/xml-components"; import { XmlAttributeComponent } from "file/xml-components";
import { IDistance } from "../drawing";
export interface IInlineAttributes { // tslint:disable-next-line:no-empty-interface
distT?: number; export interface IInlineAttributes extends IDistance {}
distB?: number;
distL?: number;
distR?: number;
}
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> { export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
protected xmlKeys = { protected xmlKeys = {

View File

@ -1,11 +1,11 @@
// http://officeopenxml.com/drwPicInline.php // http://officeopenxml.com/drwPicInline.php
import { IMediaDataDimensions } from "file/media"; import { IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components"; import { XmlComponent } from "file/xml-components";
import { DocProperties } from "./doc-properties/doc-properties"; import { DocProperties } from "./../doc-properties/doc-properties";
import { EffectExtent } from "./effect-extent/effect-extent"; import { EffectExtent } from "./../effect-extent/effect-extent";
import { Extent } from "./extent/extent"; import { Extent } from "./../extent/extent";
import { Graphic } from "./graphic"; import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties"; import { Graphic } from "./../inline/graphic";
import { InlineAttributes } from "./inline-attributes"; import { InlineAttributes } from "./inline-attributes";
export class Inline extends XmlComponent { export class Inline extends XmlComponent {

View File

@ -0,0 +1,5 @@
export * from "./text-wrapping";
export * from "./wrap-none";
export * from "./wrap-square";
export * from "./wrap-tight";
export * from "./wrap-top-and-bottom";

View File

@ -0,0 +1,22 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { IDistance } from "../drawing";
export enum TextWrapStyle {
NONE,
SQUARE,
TIGHT,
TOP_AND_BOTTOM,
}
export enum WrapTextOption {
BOTH_SIDES = "bothSides",
LEFT = "left",
RIGHT = "right",
LARGEST = "largest",
}
export interface ITextWrapping {
textWrapStyle: TextWrapStyle;
wrapTextOption?: WrapTextOption;
distanceFromText?: IDistance;
}

View File

@ -0,0 +1,8 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { XmlComponent } from "file/xml-components";
export class WrapNone extends XmlComponent {
constructor() {
super("wp:wrapNone");
}
}

View File

@ -0,0 +1,31 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { ITextWrapping, WrapTextOption } from ".";
import { IDistance } from "../drawing";
interface IWrapSquareAttributes extends IDistance {
wrapText?: WrapTextOption;
}
class WrapSquareAttributes extends XmlAttributeComponent<IWrapSquareAttributes> {
protected xmlKeys = {
distT: "distT",
distB: "distB",
distL: "distL",
distR: "distR",
wrapText: "wrapText",
};
}
export class WrapSquare extends XmlComponent {
constructor(textWrapping: ITextWrapping) {
super("wp:wrapSquare");
this.root.push(
new WrapSquareAttributes({
wrapText: textWrapping.wrapTextOption || WrapTextOption.BOTH_SIDES,
...textWrapping.distanceFromText,
}),
);
}
}

View File

@ -0,0 +1,33 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { IDistance } from "../drawing";
interface IWrapTightAttributes {
distT?: number;
distB?: number;
}
class WrapTightAttributes extends XmlAttributeComponent<IWrapTightAttributes> {
protected xmlKeys = {
distT: "distT",
distB: "distB",
};
}
export class WrapTight extends XmlComponent {
constructor(distanceFromText?: IDistance) {
super("wp:wrapTight");
distanceFromText = distanceFromText || {
distT: 0,
distB: 0,
};
this.root.push(
new WrapTightAttributes({
distT: distanceFromText.distT,
distB: distanceFromText.distB,
}),
);
}
}

View File

@ -0,0 +1,33 @@
// http://officeopenxml.com/drwPicFloating-textWrap.php
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { IDistance } from "../drawing";
interface IWrapTopAndBottomAttributes {
distT?: number;
distB?: number;
}
class WrapTopAndBottomAttributes extends XmlAttributeComponent<IWrapTopAndBottomAttributes> {
protected xmlKeys = {
distT: "distT",
distB: "distB",
};
}
export class WrapTopAndBottom extends XmlComponent {
constructor(distanceFromText?: IDistance) {
super("wp:wrapTopAndBottom");
distanceFromText = distanceFromText || {
distT: 0,
distB: 0,
};
this.root.push(
new WrapTopAndBottomAttributes({
distT: distanceFromText.distT,
distB: distanceFromText.distB,
}),
);
}
}

View File

@ -1,3 +1,4 @@
import { XmlComponent } from "file/xml-components";
import { Footer } from "./footer/footer"; import { Footer } from "./footer/footer";
import { IMediaData, Media } from "./media"; import { IMediaData, Media } from "./media";
import { Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
@ -35,6 +36,10 @@ export class FooterWrapper {
this.footer.addDrawing(imageData); this.footer.addDrawing(imageData);
} }
public addChildElement(childElement: XmlComponent | string): void {
this.footer.addChildElement(childElement);
}
public createImage(image: string): void { public createImage(image: string): void {
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
this.relationships.createRelationship( this.relationships.createRelationship(

View File

@ -1,3 +1,4 @@
import { XmlComponent } from "file/xml-components";
import { Header } from "./header/header"; import { Header } from "./header/header";
import { IMediaData, Media } from "./media"; import { IMediaData, Media } from "./media";
import { Paragraph } from "./paragraph"; import { Paragraph } from "./paragraph";
@ -85,6 +86,10 @@ export class HeaderWrapper {
this.header.addDrawing(imageData); this.header.addDrawing(imageData);
} }
public addChildElement(childElement: XmlComponent | string): void {
this.header.addChildElement(childElement);
}
public createImage(image: string): void { public createImage(image: string): void {
const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount); const mediaData = this.media.addMedia(image, this.relationships.RelationshipCount);
this.relationships.createRelationship( this.relationships.createRelationship(

View File

@ -1,18 +1,19 @@
import { Drawing } from "../../drawing"; import { Drawing } from "../../drawing";
import { IDrawingOptions } from "../../drawing/drawing";
import { IMediaData } from "../../media/data"; import { IMediaData } from "../../media/data";
import { Run } from "../run"; import { Run } from "../run";
export class PictureRun extends Run { export class PictureRun extends Run {
private drawing: Drawing; private drawing: Drawing;
constructor(imageData: IMediaData) { constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
super(); super();
if (imageData === undefined) { if (imageData === undefined) {
throw new Error("imageData cannot be undefined"); throw new Error("imageData cannot be undefined");
} }
this.drawing = new Drawing(imageData); this.drawing = new Drawing(imageData, drawingOptions);
this.root.push(this.drawing); this.root.push(this.drawing);
} }

View File

@ -1,13 +1,6 @@
import * as fastXmlParser from "fast-xml-parser"; import * as fastXmlParser from "fast-xml-parser";
import { convertToXmlComponent, ImportedRootElementAttributes, ImportedXmlComponent, parseOptions } from "file/xml-components";
import { Styles } from "./"; import { Styles } from "./";
import { ImportedRootElementAttributes, ImportedXmlComponent } from "./../../file/xml-components";
const parseOptions = {
ignoreAttributes: false,
attributeNamePrefix: "",
attrNodeName: "_attr",
};
export class ExternalStylesFactory { export class ExternalStylesFactory {
/** /**
@ -45,20 +38,7 @@ export class ExternalStylesFactory {
}); });
// convert the styles one by one // convert the styles one by one
xmlStyles["w:style"].map((style) => this.convertElement("w:style", style)).forEach(importedStyle.push.bind(importedStyle)); xmlStyles["w:style"].map((style) => convertToXmlComponent("w:style", style)).forEach(importedStyle.push.bind(importedStyle));
return importedStyle; return importedStyle;
} }
// tslint:disable-next-line:no-any
public convertElement(elementName: string, element: any): ImportedXmlComponent {
const xmlElement = new ImportedXmlComponent(elementName, element._attr);
if (typeof element === "object") {
Object.keys(element)
.filter((key) => key !== "_attr")
.map((item) => this.convertElement(item, element[item]))
.forEach(xmlElement.push.bind(xmlElement));
}
return xmlElement;
}
} }

View File

@ -1,5 +1,52 @@
import { expect } from "chai"; import { expect } from "chai";
import { ImportedXmlComponent } from "./"; import { ImportedXmlComponent, convertToXmlComponent } from "./";
const xmlString = `
<w:p w:one="value 1" w:two="value 2">
<w:rPr>
<w:noProof>some value</w:noProof>
</w:rPr>
<w:r active="true">
<w:t>Text 1</w:t>
</w:r>
<w:r active="true">
<w:t>Text 2</w:t>
</w:r>
</w:p>
`;
const importedXmlElement = {
"w:p": {
_attr: { "w:one": "value 1", "w:two": "value 2" },
"w:rPr": { "w:noProof": "some value" },
"w:r": [{ _attr: { active: "true" }, "w:t": "Text 1" }, { _attr: { active: "true" }, "w:t": "Text 2" }],
},
};
const convertedXmlElement = {
deleted: false,
rootKey: "w:p",
root: [
{
deleted: false,
rootKey: "w:rPr",
root: [{ deleted: false, rootKey: "w:noProof", root: ["some value"] }],
},
{
deleted: false,
rootKey: "w:r",
root: [{ deleted: false, rootKey: "w:t", root: ["Text 1"] }],
_attr: { active: "true" },
},
{
deleted: false,
rootKey: "w:r",
root: [{ deleted: false, rootKey: "w:t", root: ["Text 2"] }],
_attr: { active: "true" },
},
],
_attr: { "w:one": "value 1", "w:two": "value 2" },
};
describe("ImportedXmlComponent", () => { describe("ImportedXmlComponent", () => {
let importedXmlComponent: ImportedXmlComponent; let importedXmlComponent: ImportedXmlComponent;
@ -31,4 +78,16 @@ describe("ImportedXmlComponent", () => {
}); });
}); });
}); });
it("should create XmlComponent from xml string", () => {
const converted = ImportedXmlComponent.fromXmlString(xmlString);
expect(converted).to.eql(convertedXmlElement);
});
describe("convertToXmlComponent", () => {
it("should convert to xml component", () => {
const converted = convertToXmlComponent("w:p", importedXmlElement["w:p"]);
expect(converted).to.eql(convertedXmlElement);
});
});
}); });

View File

@ -1,6 +1,54 @@
// tslint:disable:no-any /* tslint:disable */
// tslint:disable:variable-name import { XmlComponent, IXmlableObject } from ".";
import { IXmlableObject, XmlComponent } from "./"; import * as fastXmlParser from "fast-xml-parser";
import { flatMap } from "lodash";
export const parseOptions = {
ignoreAttributes: false,
attributeNamePrefix: "",
attrNodeName: "_attr",
};
/**
* Converts the given xml element (in json format) into XmlComponent.
* Note: If element is array, them it will return ImportedXmlComponent[]. Example for given:
* element = [
* { w:t: "val 1"},
* { w:t: "val 2"}
* ]
* will return
* [
* ImportedXmlComponent { rootKey: "w:t", root: [ "val 1" ]},
* ImportedXmlComponent { rootKey: "w:t", root: [ "val 2" ]}
* ]
*
* @param elementName name (rootKey) of the XmlComponent
* @param element the xml element in json presentation
*/
export function convertToXmlComponent(elementName: string, element: any): ImportedXmlComponent | ImportedXmlComponent[] {
const xmlElement = new ImportedXmlComponent(elementName, element._attr);
if (Array.isArray(element)) {
const out: any[] = [];
element.forEach((itemInArray) => {
out.push(convertToXmlComponent(elementName, itemInArray));
});
return flatMap(out);
} else if (typeof element === "object") {
Object.keys(element)
.filter((key) => key !== "_attr")
.map((item) => convertToXmlComponent(item, element[item]))
.forEach((converted) => {
if (Array.isArray(converted)) {
converted.forEach(xmlElement.push.bind(xmlElement));
} else {
xmlElement.push(converted);
}
});
} else if (element !== "") {
xmlElement.push(element);
}
return xmlElement;
}
/** /**
* Represents imported xml component from xml file. * Represents imported xml component from xml file.
@ -8,11 +56,10 @@ import { IXmlableObject, XmlComponent } from "./";
export class ImportedXmlComponent extends XmlComponent { export class ImportedXmlComponent extends XmlComponent {
private _attr: any; private _attr: any;
constructor(rootKey: string, attr?: any) { constructor(rootKey: string, _attr?: any) {
super(rootKey); super(rootKey);
if (_attr) {
if (attr) { this._attr = _attr;
this._attr = attr;
} }
} }
@ -42,7 +89,7 @@ export class ImportedXmlComponent extends XmlComponent {
* ] * ]
* } * }
*/ */
public prepForXml(): IXmlableObject { prepForXml(): IXmlableObject {
const result = super.prepForXml(); const result = super.prepForXml();
if (!!this._attr) { if (!!this._attr) {
if (!Array.isArray(result[this.rootKey])) { if (!Array.isArray(result[this.rootKey])) {
@ -53,9 +100,26 @@ export class ImportedXmlComponent extends XmlComponent {
return result; return result;
} }
public push(xmlComponent: XmlComponent): void { push(xmlComponent: XmlComponent) {
this.root.push(xmlComponent); this.root.push(xmlComponent);
} }
/**
* Converts the xml string to a XmlComponent tree.
*
* @param importedContent xml content of the imported component
*/
static fromXmlString(importedContent: string): ImportedXmlComponent {
const imported = fastXmlParser.parse(importedContent, parseOptions);
const elementName = Object.keys(imported)[0];
const converted = convertToXmlComponent(elementName, imported[elementName]);
if (Array.isArray(converted) && converted.length > 1) {
throw new Error("Invalid conversion, input must be one element.");
}
return Array.isArray(converted) ? converted[0] : converted;
}
} }
/** /**