Merge branch 'master' of https://github.com/h4buli/docx into feat/h4-update
# Conflicts: # package.json # src/file/drawing/drawing.ts # src/file/media/media.ts # src/file/paragraph/run/picture-run.ts # src/file/styles/external-styles-factory.ts # src/file/xml-components/imported-xml-component.ts
This commit is contained in:
26
src/file/drawing/anchor/anchor-attributes.ts
Normal file
26
src/file/drawing/anchor/anchor-attributes.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { Distance } from "../drawing";
|
||||||
|
|
||||||
|
export interface IAnchorAttributes extends Distance {
|
||||||
|
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",
|
||||||
|
};
|
||||||
|
}
|
118
src/file/drawing/anchor/anchor.spec.ts
Normal file
118
src/file/drawing/anchor/anchor.spec.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { DrawingOptions, TextWrapStyle } from ".././";
|
||||||
|
import { Anchor } from "./";
|
||||||
|
|
||||||
|
function createDrawing(drawingOptions: DrawingOptions) {
|
||||||
|
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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
88
src/file/drawing/anchor/anchor.ts
Normal file
88
src/file/drawing/anchor/anchor.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating.php
|
||||||
|
import { IMediaDataDimensions } from "file/media";
|
||||||
|
import { XmlComponent } from "file/xml-components";
|
||||||
|
import { DocProperties } from "./../doc-properties/doc-properties";
|
||||||
|
import { EffectExtent } from "./../effect-extent/effect-extent";
|
||||||
|
import { Extent } from "./../extent/extent";
|
||||||
|
import { Graphic } from "./../graphic";
|
||||||
|
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
|
||||||
|
import { AnchorAttributes } from "./anchor-attributes";
|
||||||
|
import { DrawingOptions } from "../drawing";
|
||||||
|
import {
|
||||||
|
SimplePos,
|
||||||
|
HorizontalPosition,
|
||||||
|
VerticalPosition,
|
||||||
|
Floating,
|
||||||
|
VerticalPositionRelativeFrom,
|
||||||
|
HorizontalPositionRelativeFrom,
|
||||||
|
} from "../floating";
|
||||||
|
import { WrapNone, TextWrapStyle, WrapSquare, WrapTight, WrapTopAndBottom } from "../text-wrap";
|
||||||
|
|
||||||
|
const defaultOptions: Floating = {
|
||||||
|
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: DrawingOptions) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
2
src/file/drawing/anchor/index.ts
Normal file
2
src/file/drawing/anchor/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./anchor";
|
||||||
|
export * from "./anchor-attributes";
|
@ -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, DrawingOptions, PlacementPosition } from "./";
|
||||||
|
|
||||||
describe("Drawing", () => {
|
function createDrawing(drawingOptions?: DrawingOptions) {
|
||||||
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");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,23 +1,49 @@
|
|||||||
import { IMediaData } from "file/media";
|
import { IMediaData } from "file/media";
|
||||||
import { XmlComponent } from "file/xml-components";
|
import { XmlComponent } from "file/xml-components";
|
||||||
import { Inline } from "./inline";
|
import { Inline } from "./inline";
|
||||||
|
import { Anchor } from "./anchor";
|
||||||
|
import { TextWrapping } from "./text-wrap";
|
||||||
|
import { Floating } from "./floating";
|
||||||
|
|
||||||
|
export enum PlacementPosition {
|
||||||
|
INLINE,
|
||||||
|
FLOATING,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Distance {
|
||||||
|
distT?: number;
|
||||||
|
distB?: number;
|
||||||
|
distL?: number;
|
||||||
|
distR?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DrawingOptions {
|
||||||
|
position?: PlacementPosition;
|
||||||
|
textWrapping?: TextWrapping;
|
||||||
|
floating?: Floating;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultDrawingOptions: DrawingOptions = {
|
||||||
|
position: PlacementPosition.INLINE,
|
||||||
|
};
|
||||||
|
|
||||||
export class Drawing extends XmlComponent {
|
export class Drawing extends XmlComponent {
|
||||||
private inline: Inline;
|
constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) {
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
|
||||||
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.root.push(new Inline(imageData.referenceId, imageData.dimensions));
|
||||||
|
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
|
||||||
public scale(factorX: number, factorY: number): void {
|
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
|
||||||
this.inline.scale(factorX, factorY);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
src/file/drawing/floating/align.spec.ts
Normal file
15
src/file/drawing/floating/align.spec.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { Align } from "./align";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { VerticalPositionAlign } from ".";
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
10
src/file/drawing/floating/align.ts
Normal file
10
src/file/drawing/floating/align.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
60
src/file/drawing/floating/floating-position.ts
Normal file
60
src/file/drawing/floating/floating-position.ts
Normal 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 HorizontalPositionOptions {
|
||||||
|
relative: HorizontalPositionRelativeFrom;
|
||||||
|
align?: HorizontalPositionAlign;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VerticalPositionOptions {
|
||||||
|
relative: VerticalPositionRelativeFrom;
|
||||||
|
align?: VerticalPositionAlign;
|
||||||
|
offset?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Floating {
|
||||||
|
horizontalPosition: HorizontalPositionOptions;
|
||||||
|
verticalPosition: VerticalPositionOptions;
|
||||||
|
allowOverlap?: boolean;
|
||||||
|
lockAnchor?: boolean;
|
||||||
|
behindDocument?: boolean;
|
||||||
|
layoutInCell?: boolean;
|
||||||
|
}
|
41
src/file/drawing/floating/horizontal-position.spec.ts
Normal file
41
src/file/drawing/floating/horizontal-position.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { HorizontalPosition } from "./horizontal-position";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { HorizontalPositionRelativeFrom, HorizontalPositionAlign } from ".";
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
35
src/file/drawing/floating/horizontal-position.ts
Normal file
35
src/file/drawing/floating/horizontal-position.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { HorizontalPositionRelativeFrom, HorizontalPositionOptions } from "./floating-position";
|
||||||
|
import { Align } from "./align";
|
||||||
|
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: HorizontalPositionOptions) {
|
||||||
|
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)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
src/file/drawing/floating/index.ts
Normal file
4
src/file/drawing/floating/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./floating-position";
|
||||||
|
export * from "./simple-pos";
|
||||||
|
export * from "./horizontal-position";
|
||||||
|
export * from "./vertical-position";
|
14
src/file/drawing/floating/position-offset.spec.ts
Normal file
14
src/file/drawing/floating/position-offset.spec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { PositionOffset } from "./position-offset";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
9
src/file/drawing/floating/position-offset.ts
Normal file
9
src/file/drawing/floating/position-offset.ts
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
17
src/file/drawing/floating/simple-pos.spec.ts
Normal file
17
src/file/drawing/floating/simple-pos.spec.ts
Normal 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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
28
src/file/drawing/floating/simple-pos.ts
Normal file
28
src/file/drawing/floating/simple-pos.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } 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,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
41
src/file/drawing/floating/vertical-position.spec.ts
Normal file
41
src/file/drawing/floating/vertical-position.spec.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
|
||||||
|
import { VerticalPosition } from "./vertical-position";
|
||||||
|
import { Utility } from "../../../tests/utility";
|
||||||
|
import { VerticalPositionRelativeFrom, VerticalPositionAlign } from ".";
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
35
src/file/drawing/floating/vertical-position.ts
Normal file
35
src/file/drawing/floating/vertical-position.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-position.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { VerticalPositionRelativeFrom, VerticalPositionOptions } from "./floating-position";
|
||||||
|
import { Align } from "./align";
|
||||||
|
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: VerticalPositionOptions) {
|
||||||
|
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)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,3 @@
|
|||||||
export { Drawing } from "./drawing";
|
export * from "./drawing";
|
||||||
|
export * from "./text-wrap";
|
||||||
|
export * from "./floating";
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import { XmlAttributeComponent } from "file/xml-components";
|
import { XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { Distance } from "../drawing";
|
||||||
|
|
||||||
export interface IInlineAttributes {
|
export interface IInlineAttributes extends Distance {}
|
||||||
distT?: number;
|
|
||||||
distB?: number;
|
|
||||||
distL?: number;
|
|
||||||
distR?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
|
export class InlineAttributes extends XmlAttributeComponent<IInlineAttributes> {
|
||||||
protected xmlKeys = {
|
protected xmlKeys = {
|
||||||
|
@ -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 { Graphic } from "./../graphic";
|
||||||
import { GraphicFrameProperties } from "./graphic-frame/graphic-frame-properties";
|
import { GraphicFrameProperties } from "./../graphic-frame/graphic-frame-properties";
|
||||||
import { InlineAttributes } from "./inline-attributes";
|
import { InlineAttributes } from "./inline-attributes";
|
||||||
|
|
||||||
export class Inline extends XmlComponent {
|
export class Inline extends XmlComponent {
|
||||||
|
5
src/file/drawing/text-wrap/index.ts
Normal file
5
src/file/drawing/text-wrap/index.ts
Normal 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";
|
22
src/file/drawing/text-wrap/text-wrapping.ts
Normal file
22
src/file/drawing/text-wrap/text-wrapping.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { Distance } 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 TextWrapping {
|
||||||
|
textWrapStyle: TextWrapStyle;
|
||||||
|
wrapTextOption?: WrapTextOption;
|
||||||
|
distanceFromText?: Distance;
|
||||||
|
}
|
8
src/file/drawing/text-wrap/wrap-none.ts
Normal file
8
src/file/drawing/text-wrap/wrap-none.ts
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
31
src/file/drawing/text-wrap/wrap-square.ts
Normal file
31
src/file/drawing/text-wrap/wrap-square.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { TextWrapping, WrapTextOption } from ".";
|
||||||
|
import { Distance } from "../drawing";
|
||||||
|
|
||||||
|
interface IWrapSquareAttributes extends Distance {
|
||||||
|
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: TextWrapping) {
|
||||||
|
super("wp:wrapSquare");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapSquareAttributes({
|
||||||
|
wrapText: textWrapping.wrapTextOption || WrapTextOption.BOTH_SIDES,
|
||||||
|
...textWrapping.distanceFromText,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
src/file/drawing/text-wrap/wrap-tight.ts
Normal file
33
src/file/drawing/text-wrap/wrap-tight.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { Distance } 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?: Distance) {
|
||||||
|
super("wp:wrapTight");
|
||||||
|
|
||||||
|
distanceFromText = distanceFromText || {
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapTightAttributes({
|
||||||
|
distT: distanceFromText.distT,
|
||||||
|
distB: distanceFromText.distB,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
33
src/file/drawing/text-wrap/wrap-top-and-bottom.ts
Normal file
33
src/file/drawing/text-wrap/wrap-top-and-bottom.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// http://officeopenxml.com/drwPicFloating-textWrap.php
|
||||||
|
import { XmlComponent, XmlAttributeComponent } from "file/xml-components";
|
||||||
|
import { Distance } 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?: Distance) {
|
||||||
|
super("wp:wrapTopAndBottom");
|
||||||
|
|
||||||
|
distanceFromText = distanceFromText || {
|
||||||
|
distT: 0,
|
||||||
|
distB: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new WrapTopAndBottomAttributes({
|
||||||
|
distT: distanceFromText.distT,
|
||||||
|
distB: distanceFromText.distB,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media";
|
|||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
import { XmlComponent } from ".";
|
||||||
|
|
||||||
export class FooterWrapper {
|
export class FooterWrapper {
|
||||||
private readonly footer: Footer;
|
private readonly footer: Footer;
|
||||||
@ -35,6 +36,10 @@ export class FooterWrapper {
|
|||||||
this.footer.addDrawing(imageData);
|
this.footer.addDrawing(imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addChildElement(childElement: XmlComponent | string) {
|
||||||
|
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(
|
||||||
|
@ -3,6 +3,7 @@ import { IMediaData, Media } from "./media";
|
|||||||
import { Paragraph } from "./paragraph";
|
import { Paragraph } from "./paragraph";
|
||||||
import { Relationships } from "./relationships";
|
import { Relationships } from "./relationships";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
import { XmlComponent } from ".";
|
||||||
|
|
||||||
export class FirstPageHeaderWrapper {
|
export class FirstPageHeaderWrapper {
|
||||||
private readonly header: Header;
|
private readonly header: Header;
|
||||||
@ -85,6 +86,10 @@ export class HeaderWrapper {
|
|||||||
this.header.addDrawing(imageData);
|
this.header.addDrawing(imageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addChildElement(childElement: XmlComponent | string) {
|
||||||
|
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(
|
||||||
|
@ -11,6 +11,7 @@ export class Media {
|
|||||||
this.map = new Map<string, IMediaData>();
|
this.map = new Map<string, IMediaData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createMedia(key: string, relationshipsCount, dimensions, data: fs.ReadStream | Buffer, filePath?: string) {
|
||||||
public getMedia(key: string): IMediaData {
|
public getMedia(key: string): IMediaData {
|
||||||
const data = this.map.get(key);
|
const data = this.map.get(key);
|
||||||
|
|
||||||
|
@ -1,31 +1,16 @@
|
|||||||
import { Drawing } from "../../drawing";
|
import { Drawing } from "../../drawing";
|
||||||
import { IMediaData } from "../../media/data";
|
import { IMediaData } from "../../media/data";
|
||||||
import { Run } from "../run";
|
import { Run } from "../run";
|
||||||
|
import { DrawingOptions } from "../../drawing/drawing";
|
||||||
|
|
||||||
export class PictureRun extends Run {
|
export class PictureRun extends Run {
|
||||||
private drawing: Drawing;
|
constructor(imageData: IMediaData, drawingOptions?: DrawingOptions) {
|
||||||
|
|
||||||
constructor(imageData: IMediaData) {
|
|
||||||
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.root.push(new Drawing(imageData, drawingOptions));
|
||||||
|
|
||||||
this.root.push(this.drawing);
|
|
||||||
}
|
|
||||||
|
|
||||||
public scale(factorX: number, factorY?: number): void {
|
|
||||||
if (!factorX) {
|
|
||||||
factorX = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!factorY) {
|
|
||||||
factorY = factorX;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.drawing.scale(factorX, factorY);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,6 @@
|
|||||||
import * as fastXmlParser from "fast-xml-parser";
|
|
||||||
|
|
||||||
import { Styles } from "./";
|
import { Styles } from "./";
|
||||||
import { ImportedRootElementAttributes, ImportedXmlComponent } from "./../../file/xml-components";
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
|
import { ImportedXmlComponent, ImportedRootElementAttributes, parseOptions, convertToXmlComponent } 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,53 @@
|
|||||||
// tslint:disable:no-any
|
import { XmlComponent, IXmlableObject } from ".";
|
||||||
// tslint:disable:variable-name
|
import * as fastXmlParser from "fast-xml-parser";
|
||||||
import { IXmlableObject, XmlComponent } from "./";
|
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 +55,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 +88,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 +99,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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user