#269 Text Frame (Text Box)

This commit is contained in:
Dolan
2021-03-14 17:00:42 +00:00
parent 2fa8df7cf4
commit 7f86385b09
17 changed files with 354 additions and 25 deletions

8
.nycrc
View File

@ -1,9 +1,9 @@
{
"check-coverage": true,
"lines": 98.66,
"functions": 97.06,
"branches": 95.51,
"statements": 98.67,
"lines": 98.67,
"functions": 97.08,
"branches": 95.6,
"statements": 98.68,
"include": [
"src/**/*.ts"
],

71
demo/60-text-frame.ts Normal file
View File

@ -0,0 +1,71 @@
// Text Frame (Text Box) example
// Import from 'docx' rather than '../build' if you install from npm
import * as fs from "fs";
import { Document, FrameAnchorType, HorizontalPositionAlign, Packer, Paragraph, TextRun, VerticalPositionAlign } from "../build";
const doc = new Document();
doc.addSection({
properties: {},
children: [
new Paragraph({
frame: {
position: {
x: 1000,
y: 3000,
},
width: 4000,
height: 1000,
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
alignment: {
x: HorizontalPositionAlign.CENTER,
y: VerticalPositionAlign.TOP,
},
},
border: {
top: {
color: "auto",
space: 1,
value: "single",
size: 6,
},
bottom: {
color: "auto",
space: 1,
value: "single",
size: 6,
},
left: {
color: "auto",
space: 1,
value: "single",
size: 6,
},
right: {
color: "auto",
space: 1,
value: "single",
size: 6,
},
},
children: [
new TextRun("Hello World"),
new TextRun({
text: "Foo Bar",
bold: true,
}),
new TextRun({
text: "\tGithub is the best",
bold: true,
}),
],
}),
],
});
Packer.toBuffer(doc).then((buffer) => {
fs.writeFileSync("My Document.docx", buffer);
});

View File

@ -4,7 +4,7 @@
- Simple, declarative API
- 50+ usage examples
- Battle tested, mature, 97%+ coverage
- Battle tested, mature, 98%+ coverage
[GitHub](https://github.com/dolanmiu/docx)
[Get Started](#Welcome)

View File

@ -1,9 +1,9 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { VerticalPositionAlign } from "file/shared/alignment";
import { Align } from "./align";
import { VerticalPositionAlign } from "./floating-position";
describe("Align", () => {
describe("#constructor()", () => {

View File

@ -1,6 +1,6 @@
// http://officeopenxml.com/drwPicFloating-position.php
import { HorizontalPositionAlign, VerticalPositionAlign } from "file/shared/alignment";
import { XmlComponent } from "file/xml-components";
import { HorizontalPositionAlign, VerticalPositionAlign } from "./floating-position";
export class Align extends XmlComponent {
constructor(value: HorizontalPositionAlign | VerticalPositionAlign) {

View File

@ -1,5 +1,7 @@
// http://officeopenxml.com/drwPicFloating-position.php
// http://officeopenxml.com/drwPicFloating.php
import { HorizontalPositionAlign, VerticalPositionAlign } from "file/shared/alignment";
import { ITextWrapping } from "../text-wrap";
export enum HorizontalPositionRelativeFrom {
@ -24,22 +26,6 @@ export enum VerticalPositionRelativeFrom {
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 {
readonly relative?: HorizontalPositionRelativeFrom;
readonly align?: HorizontalPositionAlign;

View File

@ -1,8 +1,9 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { HorizontalPositionAlign } from "file/shared/alignment";
import { HorizontalPositionAlign, HorizontalPositionRelativeFrom } from "./floating-position";
import { HorizontalPositionRelativeFrom } from "./floating-position";
import { HorizontalPosition } from "./horizontal-position";
describe("HorizontalPosition", () => {

View File

@ -1,8 +1,9 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { VerticalPositionAlign } from "file/shared/alignment";
import { VerticalPositionAlign, VerticalPositionRelativeFrom } from "./floating-position";
import { VerticalPositionRelativeFrom } from "./floating-position";
import { VerticalPosition } from "./vertical-position";
describe("VerticalPosition", () => {

View File

@ -14,3 +14,4 @@ export * from "./footer-wrapper";
export * from "./header";
export * from "./footnotes";
export * from "./track-revision";
export * from "./shared";

View File

@ -0,0 +1,86 @@
import { expect } from "chai";
import { Formatter } from "export/formatter";
import { HorizontalPositionAlign, VerticalPositionAlign } from "file/shared";
import { FrameAnchorType, FrameProperties } from "./frame-properties";
describe("FrameProperties", () => {
describe("#constructor()", () => {
it("should create", () => {
const currentFrameProperties = new FrameProperties({
position: {
x: 1000,
y: 3000,
},
width: 4000,
height: 1000,
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
alignment: {
x: HorizontalPositionAlign.CENTER,
y: VerticalPositionAlign.TOP,
},
});
const tree = new Formatter().format(currentFrameProperties);
expect(tree).to.deep.equal({
"w:framePr": {
_attr: {
"w:h": 1000,
"w:hAnchor": "margin",
"w:vAnchor": "margin",
"w:w": 4000,
"w:x": 1000,
"w:xAlign": "center",
"w:y": 3000,
"w:yAlign": "top",
},
},
});
});
it("should create with the space attribute", () => {
const currentFrameProperties = new FrameProperties({
position: {
x: 1000,
y: 3000,
},
width: 4000,
height: 1000,
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
alignment: {
x: HorizontalPositionAlign.CENTER,
y: VerticalPositionAlign.TOP,
},
space: {
horizontal: 100,
vertical: 200,
},
});
const tree = new Formatter().format(currentFrameProperties);
expect(tree).to.deep.equal({
"w:framePr": {
_attr: {
"w:h": 1000,
"w:hAnchor": "margin",
"w:vAnchor": "margin",
"w:w": 4000,
"w:x": 1000,
"w:xAlign": "center",
"w:y": 3000,
"w:yAlign": "top",
"w:hSpace": 100,
"w:vSpace": 200,
},
},
});
});
});
});

View File

@ -0,0 +1,111 @@
// http://officeopenxml.com/WPparagraph-textFrames.php
import { HorizontalPositionAlign, VerticalPositionAlign } from "file/shared/alignment";
import { XmlAttributeComponent, XmlComponent } from "file/xml-components";
export enum DropCapType {
NONE = "none",
DROP = "drop",
MARGIN = "margin",
}
export enum FrameAnchorType {
MARGIN = "margin",
PAGE = "page",
TEXT = "text",
}
export enum FrameWrap {
AROUND = "around",
AUTO = "auto",
NONE = "none",
NOT_BESIDE = "notBeside",
THROUGH = "through",
TIGHT = "tight",
}
export interface IFrameOptions {
readonly anchorLock?: boolean;
readonly dropCap?: DropCapType;
readonly width: number;
readonly height: number;
readonly position: {
readonly x: number;
readonly y: number;
};
readonly wrap?: FrameWrap;
readonly lines?: number;
readonly anchor: {
readonly horizontal: FrameAnchorType;
readonly vertical: FrameAnchorType;
};
readonly space?: {
readonly horizontal: number;
readonly vertical: number;
};
readonly rule?: number;
readonly alignment: {
readonly x: HorizontalPositionAlign;
readonly y: VerticalPositionAlign;
};
}
export class FramePropertiesAttributes extends XmlAttributeComponent<{
readonly anchorLock?: boolean;
readonly dropCap?: DropCapType;
readonly width: number;
readonly height: number;
readonly x: number;
readonly y: number;
readonly wrap?: FrameWrap;
readonly lines?: number;
readonly anchorHorizontal?: FrameAnchorType;
readonly anchorVertical?: FrameAnchorType;
readonly spaceHorizontal?: number;
readonly spaceVertical?: number;
readonly rule?: number;
readonly alignmentX?: HorizontalPositionAlign;
readonly alignmentY?: VerticalPositionAlign;
}> {
protected readonly xmlKeys = {
anchorLock: "w:anchorLock",
dropCap: "w:dropCap",
width: "w:w",
height: "w:h",
x: "w:x",
y: "w:y",
anchorHorizontal: "w:hAnchor",
anchorVertical: "w:vAnchor",
spaceHorizontal: "w:hSpace",
spaceVertical: "w:vSpace",
rule: "w:hRule",
alignmentX: "w:xAlign",
alignmentY: "w:yAlign",
lines: "w:lines",
wrap: "w:wrap",
};
}
export class FrameProperties extends XmlComponent {
constructor(options: IFrameOptions) {
super("w:framePr");
this.root.push(
new FramePropertiesAttributes({
anchorLock: options.anchorLock,
dropCap: options.dropCap,
width: options.width,
height: options.height,
x: options.position.x,
y: options.position.y,
anchorHorizontal: options.anchor.horizontal,
anchorVertical: options.anchor.vertical,
spaceHorizontal: options.space?.horizontal,
spaceVertical: options.space?.vertical,
rule: options.rule,
alignmentX: options.alignment.x,
alignmentY: options.alignment.y,
lines: options.lines,
wrap: options.wrap,
}),
);
}
}

View File

@ -0,0 +1 @@
export * from "./frame-properties";

View File

@ -4,3 +4,4 @@ export * from "./properties";
export * from "./run";
export * from "./links";
export * from "./math";
export * from "./frame";

View File

@ -7,8 +7,10 @@ import { EMPTY_OBJECT } from "file/xml-components";
import { IViewWrapper } from "../document-wrapper";
import { File } from "../file";
import { HorizontalPositionAlign, VerticalPositionAlign } from "../shared";
import { ShadingType } from "../table/shading";
import { AlignmentType, HeadingLevel, LeaderType, PageBreak, TabStopPosition, TabStopType } from "./formatting";
import { FrameAnchorType } from "./frame";
import { Bookmark, ExternalHyperlink } from "./links";
import { Paragraph } from "./paragraph";
import { TextRun } from "./run";
@ -859,6 +861,52 @@ describe("Paragraph", () => {
});
});
describe("#frame", () => {
it("should set frame attribute", () => {
const paragraph = new Paragraph({
frame: {
position: {
x: 1000,
y: 3000,
},
width: 4000,
height: 1000,
anchor: {
horizontal: FrameAnchorType.MARGIN,
vertical: FrameAnchorType.MARGIN,
},
alignment: {
x: HorizontalPositionAlign.CENTER,
y: VerticalPositionAlign.TOP,
},
},
});
const tree = new Formatter().format(paragraph);
expect(tree).to.deep.equal({
"w:p": [
{
"w:pPr": [
{
"w:framePr": {
_attr: {
"w:h": 1000,
"w:hAnchor": "margin",
"w:vAnchor": "margin",
"w:w": 4000,
"w:x": 1000,
"w:xAlign": "center",
"w:y": 3000,
"w:yAlign": "top",
},
},
},
],
},
],
});
});
});
describe("#prepForXml", () => {
it("should set Internal Hyperlink", () => {
const paragraph = new Paragraph({

View File

@ -13,6 +13,7 @@ import { HeadingLevel, Style } from "./formatting/style";
import { LeaderType, TabStop, TabStopPosition, TabStopType } from "./formatting/tab-stop";
import { NumberProperties } from "./formatting/unordered-list";
import { WidowControl } from "./formatting/widow-control";
import { FrameProperties, IFrameOptions } from "./frame/frame-properties";
import { OutlineLevel } from "./links";
import { Shading } from "./run/formatting";
@ -55,6 +56,7 @@ export interface IParagraphPropertiesOptions extends IParagraphStylePropertiesOp
readonly color: string;
};
readonly widowControl?: boolean;
readonly frame?: IFrameOptions;
}
export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
@ -159,6 +161,10 @@ export class ParagraphProperties extends IgnoreIfEmptyXmlComponent {
if (options.widowControl) {
this.push(new WidowControl(options.widowControl));
}
if (options.frame) {
this.push(new FrameProperties(options.frame));
}
}
public push(item: XmlComponent): void {

View File

@ -0,0 +1,15 @@
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",
}

1
src/file/shared/index.ts Normal file
View File

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