Merge branch 'master' into feat/improve-docs

# Conflicts:
#	src/file/footer-wrapper.spec.ts
This commit is contained in:
Dolan
2019-01-03 02:12:05 +00:00
27 changed files with 360 additions and 208 deletions

View File

@ -8,7 +8,20 @@ import { Anchor } from "./anchor";
function createDrawing(drawingOptions: IDrawingOptions): Anchor {
return new Anchor(
1,
{
fileName: "test.png",
stream: new Buffer(""),
dimensions: {
pixels: {
x: 0,
y: 0,
},
emus: {
x: 0,
y: 0,
},
},
},
{
pixels: {
x: 100,

View File

@ -1,5 +1,5 @@
// http://officeopenxml.com/drwPicFloating.php
import { IMediaDataDimensions } from "file/media";
import { IMediaData, IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { IDrawingOptions } from "../drawing";
import {
@ -34,7 +34,7 @@ const defaultOptions: IFloating = {
};
export class Anchor extends XmlComponent {
constructor(referenceId: number, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
constructor(mediaData: IMediaData, dimensions: IMediaDataDimensions, drawingOptions: IDrawingOptions) {
super("wp:anchor");
const floating = {
@ -83,6 +83,6 @@ export class Anchor extends XmlComponent {
this.root.push(new DocProperties());
this.root.push(new GraphicFrameProperties());
this.root.push(new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y));
this.root.push(new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y));
}
}

View File

@ -11,7 +11,6 @@ function createDrawing(drawingOptions?: IDrawingOptions): Drawing {
return new Drawing(
{
fileName: "test.jpg",
referenceId: 1,
stream: Buffer.from(imageBase64Data, "base64"),
path: path,
dimensions: {

View File

@ -33,20 +33,16 @@ export class Drawing extends XmlComponent {
constructor(imageData: IMediaData, drawingOptions?: IDrawingOptions) {
super("w:drawing");
if (imageData === undefined) {
throw new Error("imageData cannot be undefined");
}
const mergedOptions = {
...defaultDrawingOptions,
...drawingOptions,
};
if (mergedOptions.position === PlacementPosition.INLINE) {
this.inline = new Inline(imageData.referenceId, imageData.dimensions);
this.inline = new Inline(imageData, imageData.dimensions);
this.root.push(this.inline);
} else if (mergedOptions.position === PlacementPosition.FLOATING) {
this.root.push(new Anchor(imageData.referenceId, imageData.dimensions, mergedOptions));
this.root.push(new Anchor(imageData, imageData.dimensions, mergedOptions));
}
}

View File

@ -1,11 +1,13 @@
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { GraphicDataAttributes } from "./graphic-data-attribute";
import { Pic } from "./pic";
export class GraphicData extends XmlComponent {
private readonly pic: Pic;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("a:graphicData");
this.root.push(
@ -14,7 +16,7 @@ export class GraphicData extends XmlComponent {
}),
);
this.pic = new Pic(referenceId, x, y);
this.pic = new Pic(mediaData, x, y);
this.root.push(this.pic);
}

View File

@ -1,12 +1,15 @@
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { Blip } from "./blip";
import { SourceRectangle } from "./source-rectangle";
import { Stretch } from "./stretch";
export class BlipFill extends XmlComponent {
constructor(referenceId: number) {
constructor(mediaData: IMediaData) {
super("pic:blipFill");
this.root.push(new Blip(referenceId));
this.root.push(new Blip(mediaData));
this.root.push(new SourceRectangle());
this.root.push(new Stretch());
}

View File

@ -1,3 +1,4 @@
import { IMediaData } from "file/media";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
interface IBlipProperties {
@ -13,11 +14,11 @@ class BlipAttributes extends XmlAttributeComponent<IBlipProperties> {
}
export class Blip extends XmlComponent {
constructor(referenceId: number) {
constructor(mediaData: IMediaData) {
super("a:blip");
this.root.push(
new BlipAttributes({
embed: `rId${referenceId}`,
embed: `rId{${mediaData.fileName}}`,
cstate: "none",
}),
);

View File

@ -1,5 +1,7 @@
// http://officeopenxml.com/drwPic.php
import { IMediaData } from "file/media";
import { XmlComponent } from "file/xml-components";
import { BlipFill } from "./blip/blip-fill";
import { NonVisualPicProperties } from "./non-visual-pic-properties/non-visual-pic-properties";
import { PicAttributes } from "./pic-attributes";
@ -8,7 +10,7 @@ import { ShapeProperties } from "./shape-properties/shape-properties";
export class Pic extends XmlComponent {
private readonly shapeProperties: ShapeProperties;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("pic:pic");
this.root.push(
@ -20,7 +22,7 @@ export class Pic extends XmlComponent {
this.shapeProperties = new ShapeProperties(x, y);
this.root.push(new NonVisualPicProperties());
this.root.push(new BlipFill(referenceId));
this.root.push(new BlipFill(mediaData));
this.root.push(new ShapeProperties(x, y));
}

View File

@ -1,4 +1,6 @@
import { IMediaData } from "file/media";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
import { GraphicData } from "./graphic-data";
interface IGraphicProperties {
@ -14,7 +16,7 @@ class GraphicAttributes extends XmlAttributeComponent<IGraphicProperties> {
export class Graphic extends XmlComponent {
private readonly data: GraphicData;
constructor(referenceId: number, x: number, y: number) {
constructor(mediaData: IMediaData, x: number, y: number) {
super("a:graphic");
this.root.push(
new GraphicAttributes({
@ -22,7 +24,7 @@ export class Graphic extends XmlComponent {
}),
);
this.data = new GraphicData(referenceId, x, y);
this.data = new GraphicData(mediaData, x, y);
this.root.push(this.data);
}

View File

@ -1,5 +1,5 @@
// http://officeopenxml.com/drwPicInline.php
import { IMediaDataDimensions } from "file/media";
import { IMediaData, IMediaDataDimensions } from "file/media";
import { XmlComponent } from "file/xml-components";
import { DocProperties } from "./../doc-properties/doc-properties";
import { EffectExtent } from "./../effect-extent/effect-extent";
@ -12,7 +12,7 @@ export class Inline extends XmlComponent {
private readonly extent: Extent;
private readonly graphic: Graphic;
constructor(referenceId: number, private readonly dimensions: IMediaDataDimensions) {
constructor(readonly mediaData: IMediaData, private readonly dimensions: IMediaDataDimensions) {
super("wp:inline");
this.root.push(
@ -25,7 +25,7 @@ export class Inline extends XmlComponent {
);
this.extent = new Extent(dimensions.emus.x, dimensions.emus.y);
this.graphic = new Graphic(referenceId, dimensions.emus.x, dimensions.emus.y);
this.graphic = new Graphic(mediaData, dimensions.emus.x, dimensions.emus.y);
this.root.push(this.extent);
this.root.push(new EffectExtent());

View File

@ -1,8 +1,11 @@
import { expect } from "chai";
import * as sinon from "sinon";
import { Formatter } from "export/formatter";
import { File } from "./file";
import { Paragraph } from "./paragraph";
import { Table } from "./table";
describe("File", () => {
describe("#constructor", () => {
@ -55,4 +58,24 @@ describe("File", () => {
expect(tree["w:body"][1]["w:sectPr"][9]["w:footerReference"][0]._attr["w:type"]).to.equal("even");
});
});
describe("#addParagraph", () => {
it("should call the underlying header's addParagraph", () => {
const file = new File();
const spy = sinon.spy(file.Document, "addParagraph");
file.addParagraph(new Paragraph());
expect(spy.called).to.equal(true);
});
});
describe("#addTable", () => {
it("should call the underlying header's addParagraph", () => {
const wrapper = new File();
const spy = sinon.spy(wrapper.Document, "addTable");
wrapper.addTable(new Table(1, 1));
expect(spy.called).to.equal(true);
});
});
});

View File

@ -17,6 +17,7 @@ import { Image, Media } from "./media";
import { Numbering } from "./numbering";
import { Bookmark, Hyperlink, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { TargetModeType } from "./relationships/relationship/relationship";
import { Settings } from "./settings";
import { Styles } from "./styles";
import { ExternalStylesFactory } from "./styles/external-styles-factory";
@ -147,7 +148,7 @@ export class File {
hyperlink.linkId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
link,
"External",
TargetModeType.EXTERNAL,
);
return hyperlink;
}

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { FooterReferenceType } from "./document";
import { Footer } from "./footer/footer";
import { Image, IMediaData, Media } from "./media";
import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -43,29 +43,8 @@ export class FooterWrapper {
this.footer.addChildElement(childElement);
}
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
return mediaData;
}
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
this.relationships.createRelationship(
refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
target,
targetMode,
);
}
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
const mediaData = this.media.addMedia(image, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}

View File

@ -9,9 +9,9 @@ import { Table } from "./table";
describe("HeaderWrapper", () => {
describe("#addParagraph", () => {
it("should call the underlying header's addParagraph", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addParagraph");
file.addParagraph(new Paragraph());
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "addParagraph");
wrapper.addParagraph(new Paragraph());
expect(spy.called).to.equal(true);
});
@ -19,9 +19,9 @@ describe("HeaderWrapper", () => {
describe("#addTable", () => {
it("should call the underlying header's addParagraph", () => {
const file = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(file.Header, "addTable");
file.addTable(new Table(1, 1));
const wrapper = new HeaderWrapper(new Media(), 1);
const spy = sinon.spy(wrapper.Header, "addTable");
wrapper.addTable(new Table(1, 1));
expect(spy.called).to.equal(true);
});

View File

@ -2,7 +2,7 @@ import { XmlComponent } from "file/xml-components";
import { HeaderReferenceType } from "./document";
import { Header } from "./header/header";
import { Image, IMediaData, Media } from "./media";
import { Image, Media } from "./media";
import { ImageParagraph, Paragraph } from "./paragraph";
import { Relationships } from "./relationships";
import { Table } from "./table";
@ -43,29 +43,8 @@ export class HeaderWrapper {
this.header.addChildElement(childElement);
}
public addImageRelationship(image: Buffer, refId: number, width?: number, height?: number): IMediaData {
const mediaData = this.media.addMedia(image, refId, width, height);
this.relationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
return mediaData;
}
public addHyperlinkRelationship(target: string, refId: number, targetMode?: "External" | undefined): void {
this.relationships.createRelationship(
refId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
target,
targetMode,
);
}
public createImage(image: Buffer | string | Uint8Array | ArrayBuffer, width?: number, height?: number): void {
// TODO
// tslint:disable-next-line:no-any
const mediaData = this.addImageRelationship(image as any, this.relationships.RelationshipCount, width, height);
const mediaData = this.media.addMedia(image, width, height);
this.addImage(new Image(new ImageParagraph(mediaData)));
}

View File

@ -10,7 +10,6 @@ export interface IMediaDataDimensions {
}
export interface IMediaData {
readonly referenceId: number;
readonly stream: Buffer | Uint8Array | ArrayBuffer;
readonly path?: string;
readonly fileName: string;

View File

@ -1,5 +1,6 @@
// tslint:disable:object-literal-key-quotes
import { expect } from "chai";
import { stub } from "sinon";
import { Formatter } from "export/formatter";
@ -20,16 +21,18 @@ describe("Media", () => {
});
it("should ensure the correct relationship id is used when adding image", () => {
// tslint:disable-next-line:no-any
stub(Media as any, "generateId").callsFake(() => "testId");
const file = new File();
const image1 = Media.addImage(file, "test");
const tree = new Formatter().format(image1.Paragraph);
const inlineElements = tree["w:p"][1]["w:r"][1]["w:drawing"][0]["wp:inline"];
const graphicData = inlineElements.find((x) => x["a:graphic"]);
expect(graphicData["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
_attr: {
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
"r:embed": `rId{testId.png}`,
cstate: "none",
},
});
@ -41,7 +44,7 @@ describe("Media", () => {
expect(graphicData2["a:graphic"][1]["a:graphicData"][1]["pic:pic"][2]["pic:blipFill"][0]["a:blip"][0]).to.deep.equal({
_attr: {
"r:embed": `rId${file.DocumentRelationships.RelationshipCount}`,
"r:embed": `rId{testId.png}`,
cstate: "none",
},
});
@ -53,9 +56,8 @@ describe("Media", () => {
// tslint:disable-next-line:no-any
(Media as any).generateId = () => "test";
const image = new Media().addMedia("", 1);
const image = new Media().addMedia("");
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,
@ -74,7 +76,7 @@ describe("Media", () => {
// tslint:disable-next-line:no-any
(Media as any).generateId = () => "test";
const image = new Media().addMedia("", 1);
const image = new Media().addMedia("");
expect(image.stream).to.be.an.instanceof(Uint8Array);
});
});
@ -85,12 +87,11 @@ describe("Media", () => {
(Media as any).generateId = () => "test";
const media = new Media();
media.addMedia("", 1);
media.addMedia("");
const image = media.getMedia("test.png");
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,
@ -116,7 +117,7 @@ describe("Media", () => {
(Media as any).generateId = () => "test";
const media = new Media();
media.addMedia("", 1);
media.addMedia("");
const array = media.Array;
expect(array).to.be.an.instanceof(Array);
@ -124,7 +125,6 @@ describe("Media", () => {
const image = array[0];
expect(image.fileName).to.equal("test.png");
expect(image.referenceId).to.equal(1);
expect(image.dimensions).to.deep.equal({
pixels: {
x: 100,

View File

@ -4,11 +4,6 @@ import { ImageParagraph } from "../paragraph";
import { IMediaData } from "./data";
import { Image } from "./image";
interface IHackedFile {
// tslint:disable-next-line:readonly-keyword
currentRelationshipId: number;
}
export class Media {
public static addImage(
file: File,
@ -18,14 +13,7 @@ export class Media {
drawingOptions?: IDrawingOptions,
): Image {
// Workaround to expose id without exposing to API
const exposedFile = (file as {}) as IHackedFile;
const mediaData = file.Media.addMedia(buffer, exposedFile.currentRelationshipId++, width, height);
file.DocumentRelationships.createRelationship(
mediaData.referenceId,
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
`media/${mediaData.fileName}`,
);
const mediaData = file.Media.addMedia(buffer, width, height);
return new Image(new ImageParagraph(mediaData, drawingOptions));
}
@ -57,17 +45,11 @@ export class Media {
return data;
}
public addMedia(
buffer: Buffer | string | Uint8Array | ArrayBuffer,
referenceId: number,
width: number = 100,
height: number = 100,
): IMediaData {
public addMedia(buffer: Buffer | string | Uint8Array | ArrayBuffer, width: number = 100, height: number = 100): IMediaData {
const key = `${Media.generateId()}.png`;
return this.createMedia(
key,
referenceId,
{
width: width,
height: height,
@ -78,7 +60,6 @@ export class Media {
private createMedia(
key: string,
relationshipsCount: number,
dimensions: { readonly width: number; readonly height: number },
data: Buffer | string | Uint8Array | ArrayBuffer,
filePath?: string,
@ -86,7 +67,6 @@ export class Media {
const newData = typeof data === "string" ? this.convertDataURIToBinary(data) : data;
const imageData: IMediaData = {
referenceId: relationshipsCount,
stream: newData,
path: filePath,
fileName: key,

View File

@ -10,10 +10,9 @@ describe("Image", () => {
beforeEach(() => {
image = new ImageParagraph({
referenceId: 0,
stream: new Buffer(""),
path: "",
fileName: "",
fileName: "test.png",
dimensions: {
pixels: {
x: 10,
@ -171,7 +170,7 @@ describe("Image", () => {
{
_attr: {
cstate: "none",
"r:embed": "rId0",
"r:embed": "rId{test.png}",
},
},
],

View File

@ -17,7 +17,9 @@ export type RelationshipType =
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
| "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes";
export type TargetModeType = "External";
export enum TargetModeType {
EXTERNAL = "External",
}
export class Relationship extends XmlComponent {
constructor(id: string, type: RelationshipType, target: string, targetMode?: TargetModeType) {