Skip to content

Commit 779752b

Browse files
authored
Merge pull request #35 from zurmokeeper/feature/add_shape_text_func
add shape and text
2 parents a18b3ed + 638b069 commit 779752b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1947
-67
lines changed

README.md

+26
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ To be clear, all contributions added to this library will be included in the lib
217217
<li><a href="#conditional-formatting">Conditional Formatting</a></li>
218218
<li><a href="#outline-levels">Outline Levels</a></li>
219219
<li><a href="#images">Images</a></li>
220+
<li><a href="#shape">Shape</a></li>
220221
<li><a href="#sheet-protection">Sheet Protection</a></li>
221222
<li><a href="#file-io">File I/O</a>
222223
<ul>
@@ -2236,6 +2237,31 @@ worksheet.addImage(imageId2, {
22362237
});
22372238
```
22382239

2240+
## Shape
2241+
2242+
### Add shape to worksheet[](#contents)
2243+
2244+
```javascript
2245+
worksheet.addShape({
2246+
type: 'roundRect',
2247+
rotation: 0,
2248+
fill: { type: 'solid', color: { rgb: '4499FF' } },
2249+
outline: { weight: 2, color: { rgb: '446699' }, dash: 'sysDash' },
2250+
textBody: {
2251+
vertAlign: 'ctr',
2252+
paragraphs: [
2253+
{ alignment: 'l', runs: ["Lorem ipsum dolor sit amet, consectetur adipiscing elit."] },
2254+
{ alignment: 'r', runs: [
2255+
{ text: "Nulla eget odio sed libero ultrices vehicula.", font: { bold: true, color: { rgb: 'FF0000' } } },
2256+
] },
2257+
],
2258+
},
2259+
}, 'B2:H8', {
2260+
hyperlink: 'https://www.example.com',
2261+
tooltip: 'Example Link',
2262+
});
2263+
```
2264+
22392265
## Sheet Protection[](#contents)<!-- Link generated with jump2header -->
22402266

22412267
Worksheets can be protected from modification by adding a password.

README_zh.md

+26
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ ws1.getCell('A1').value = { text: 'Sheet2', hyperlink: '#A1:B1' };
177177
<li><a href="#条件格式化">条件格式化</a></li>
178178
<li><a href="#大纲级别">大纲级别</a></li>
179179
<li><a href="#图片">图片</a></li>
180+
<li><a href="#形状">形状Shape</a></li>
180181
<li><a href="#工作表保护">工作表保护</a></li>
181182
<li><a href="#文件-io">文件 I/O</a>
182183
<ul>
@@ -2134,6 +2135,31 @@ worksheet.addImage(imageId2, {
21342135
});
21352136
```
21362137

2138+
## 形状(Shape)
2139+
2140+
### 新增形状(Shape)到工作表[](#contents)
2141+
2142+
```javascript
2143+
worksheet.addShape({
2144+
type: 'roundRect',
2145+
rotation: 0,
2146+
fill: { type: 'solid', color: { rgb: '4499FF' } },
2147+
outline: { weight: 2, color: { rgb: '446699' }, dash: 'sysDash' },
2148+
textBody: {
2149+
vertAlign: 'ctr',
2150+
paragraphs: [
2151+
{ alignment: 'l', runs: ["Lorem ipsum dolor sit amet, consectetur adipiscing elit."] },
2152+
{ alignment: 'r', runs: [
2153+
{ text: "Nulla eget odio sed libero ultrices vehicula.", font: { bold: true, color: { rgb: 'FF0000' } } },
2154+
] },
2155+
],
2156+
},
2157+
}, 'B2:H8', {
2158+
hyperlink: 'https://www.example.com',
2159+
tooltip: 'Example Link',
2160+
});
2161+
```
2162+
21372163
## 工作表保护[](#目录)<!-- Link generated with jump2header -->
21382164

21392165
可以通过添加密码来保护工作表免受修改。

index.d.ts

+71-6
Original file line numberDiff line numberDiff line change
@@ -1004,21 +1004,79 @@ export class Anchor implements IAnchor {
10041004

10051005
constructor(model?: IAnchor | object);
10061006
}
1007-
export interface ImageRange {
1007+
export interface DrawingRange {
10081008
tl: Anchor;
10091009
br: Anchor;
10101010
}
10111011

1012-
export interface ImagePosition {
1012+
export interface DrawingPosition {
10131013
tl: { col: number; row: number };
10141014
ext: { width: number; height: number };
10151015
}
10161016

1017-
export interface ImageHyperlinkValue {
1017+
export interface DrawingHyperlinkValue {
10181018
hyperlink: string;
10191019
tooltip?: string;
10201020
}
10211021

1022+
export interface ShapeProps {
1023+
/**
1024+
* Defined as DocumentFormat.OpenXml.Drawing.ShapeTypeValues in Open API Spec.
1025+
* See https://learn.microsoft.com/ja-jp/dotnet/api/documentformat.openxml.drawing.shapetypevalues
1026+
*/
1027+
type: string;
1028+
rotation?: number;
1029+
horizontalFlip?: boolean;
1030+
verticalFlip?: boolean;
1031+
fill?: ShapeFill;
1032+
outline?: ShapeOutline;
1033+
textBody?: ShapeTextBody;
1034+
}
1035+
1036+
export type ShapeFill = {
1037+
type: 'solid',
1038+
color: { theme?: string, rgb?: string }
1039+
}
1040+
1041+
export interface ShapeArrowEnd {
1042+
type?: 'triangle' | 'arrow' | 'stealth' | 'diamond' | 'oval';
1043+
length?: 'lg' | 'med' | 'sm';
1044+
width?: 'lg' | 'med' | 'sm';
1045+
}
1046+
1047+
export type ShapeOutline = {
1048+
weight?: number,
1049+
color?: { theme?: string, rgb?: string },
1050+
dash?: 'solid' | 'sysDot' | 'sysDash' | 'dash' | 'dashDot' | 'lgDash' | 'lgDashDot' | 'lgDashDotDot',
1051+
arrow?: {
1052+
head?: ShapeArrowEnd,
1053+
tail?: ShapeArrowEnd
1054+
}
1055+
}
1056+
1057+
export type ShapeTextBody = {
1058+
paragraphs: ShapeParagraph[],
1059+
vertAlign?: 't' | 'ctr' | 'b',
1060+
}
1061+
1062+
export type ShapeParagraph = {
1063+
runs: ShapeRun[],
1064+
alignment?: 'l' | 'ctr' | 'r',
1065+
}
1066+
1067+
export type ShapeRun = {
1068+
text: string,
1069+
font?: Partial<ShapeRunFont>,
1070+
}
1071+
1072+
export type ShapeRunFont = {
1073+
size: number,
1074+
color: { theme?: string, rgb?: string },
1075+
bold: boolean,
1076+
italic: boolean,
1077+
underline: 'sng' | 'dbl' | 'none',
1078+
}
1079+
10221080
export interface Range extends Location {
10231081
sheetName: string;
10241082

@@ -1466,14 +1524,21 @@ export interface Worksheet {
14661524
* Using the image id from `Workbook.addImage`,
14671525
* embed an image within the worksheet to cover a range
14681526
*/
1469-
addImage(imageId: number, range: string | { editAs?: string; } & ImageRange & { hyperlinks?: ImageHyperlinkValue } | { editAs?: string; } & ImagePosition & { hyperlinks?: ImageHyperlinkValue }): void;
1527+
addImage(imageId: number, range: string | { editAs?: string; } & DrawingRange & { hyperlinks?: DrawingHyperlinkValue } | { editAs?: string; } & DrawingPosition & { hyperlinks?: DrawingHyperlinkValue }): void;
14701528

14711529
getImages(): Array<{
14721530
type: 'image',
1473-
imageId: string;
1474-
range: ImageRange;
1531+
imageId: string,
1532+
range: DrawingRange,
14751533
}>;
14761534

1535+
addShape(props: ShapeProps, range: string | { editAs?: string; } & DrawingRange | { editAs?: string; } & DrawingPosition, hyperlinks?: DrawingHyperlinkValue ): void;
1536+
1537+
getShapes(): Array<{
1538+
props: ShapeProps,
1539+
range: DrawingRange,
1540+
}>
1541+
14771542
commit(): void;
14781543

14791544
model: WorksheetModel;

lib/doc/drawing-range.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const colCache = require('../utils/col-cache');
2+
const Anchor = require('./anchor');
3+
4+
module.exports = {
5+
parseRange: (range, hyperlinks, worksheet) => {
6+
if (typeof range === 'string') {
7+
const decoded = colCache.decode(range);
8+
return {
9+
tl: new Anchor(worksheet, {col: decoded.left, row: decoded.top}, -1),
10+
br: new Anchor(worksheet, {col: decoded.right, row: decoded.bottom}, 0),
11+
editAs: 'oneCell',
12+
};
13+
}
14+
return {
15+
tl: new Anchor(worksheet, range.tl, 0),
16+
br: range.br && new Anchor(worksheet, range.br, 0),
17+
ext: range.ext,
18+
editAs: range.editAs,
19+
hyperlinks: hyperlinks || range.hyperlinks,
20+
};
21+
},
22+
};

lib/doc/image.js

+3-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
const colCache = require('../utils/col-cache');
2-
const Anchor = require('./anchor');
1+
const {parseRange} = require('./drawing-range');
32

43
class Image {
54
constructor(worksheet, model) {
@@ -36,24 +35,9 @@ class Image {
3635
this.imageId = imageId;
3736

3837
if (type === 'image') {
39-
if (typeof range === 'string') {
40-
const decoded = colCache.decode(range);
41-
this.range = {
42-
tl: new Anchor(this.worksheet, {col: decoded.left, row: decoded.top}, -1),
43-
br: new Anchor(this.worksheet, {col: decoded.right, row: decoded.bottom}, 0),
44-
editAs: 'oneCell',
45-
};
46-
} else {
47-
this.range = {
48-
tl: new Anchor(this.worksheet, range.tl, 0),
49-
br: range.br && new Anchor(this.worksheet, range.br, 0),
50-
ext: range.ext,
51-
editAs: range.editAs,
52-
hyperlinks: hyperlinks || range.hyperlinks,
53-
};
54-
}
38+
this.range = parseRange(range, hyperlinks, this.worksheet);
5539
}
5640
}
5741
}
5842

59-
module.exports = Image;
43+
module.exports = Image;

lib/doc/shape.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
const {parseRange} = require('./drawing-range');
2+
3+
class Shape {
4+
constructor(worksheet, model) {
5+
this.worksheet = worksheet;
6+
this.model = model;
7+
}
8+
9+
get model() {
10+
return {
11+
props: {
12+
type: this.props.type,
13+
rotation: this.props.rotation,
14+
horizontalFlip: this.props.horizontalFlip,
15+
verticalFlip: this.props.verticalFlip,
16+
fill: this.props.fill,
17+
outline: this.props.outline,
18+
textBody: this.props.textBody,
19+
},
20+
range: {
21+
tl: this.range.tl.model,
22+
br: this.range.br && this.range.br.model,
23+
ext: this.range.ext,
24+
editAs: this.range.editAs,
25+
},
26+
hyperlinks: this.hyperlinks,
27+
};
28+
}
29+
30+
set model({props, range, hyperlinks}) {
31+
this.props = {type: props.type};
32+
if (props.rotation) {
33+
this.props.rotation = props.rotation;
34+
}
35+
if (props.horizontalFlip) {
36+
this.props.horizontalFlip = props.horizontalFlip;
37+
}
38+
if (props.verticalFlip) {
39+
this.props.verticalFlip = props.verticalFlip;
40+
}
41+
if (props.fill) {
42+
this.props.fill = props.fill;
43+
}
44+
if (props.outline) {
45+
this.props.outline = props.outline;
46+
}
47+
if (props.textBody) {
48+
this.props.textBody = parseAsTextBody(props.textBody);
49+
}
50+
this.range = parseRange(range, undefined, this.worksheet);
51+
this.hyperlinks = hyperlinks;
52+
}
53+
}
54+
55+
function parseAsTextBody(input) {
56+
if (typeof input === 'string') {
57+
return {
58+
paragraphs: [parseAsParagraph(input)],
59+
};
60+
}
61+
if (Array.isArray(input)) {
62+
return {
63+
paragraphs: input.map(parseAsParagraph),
64+
};
65+
}
66+
const model = {
67+
paragraphs: input.paragraphs.map(parseAsParagraph),
68+
};
69+
if (input.vertAlign) {
70+
model.vertAlign = input.vertAlign;
71+
}
72+
return model;
73+
}
74+
75+
function parseAsParagraph(input) {
76+
if (typeof input === 'string') {
77+
return {
78+
runs: [parseAsRun(input)],
79+
};
80+
}
81+
if (Array.isArray(input)) {
82+
return {
83+
runs: input.map(parseAsRun),
84+
};
85+
}
86+
const model = {
87+
runs: input.runs.map(parseAsRun),
88+
};
89+
if (input.alignment) {
90+
model.alignment = input.alignment;
91+
}
92+
return model;
93+
}
94+
95+
function parseAsRun(input) {
96+
if (typeof input === 'string') {
97+
return {
98+
text: input,
99+
};
100+
}
101+
const model = {
102+
text: input.text,
103+
};
104+
if (input.font) {
105+
model.font = input.font;
106+
}
107+
return model;
108+
}
109+
110+
module.exports = Shape;

0 commit comments

Comments
 (0)