Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add shape and text #35

Merged
merged 2 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ To be clear, all contributions added to this library will be included in the lib
<li><a href="#conditional-formatting">Conditional Formatting</a></li>
<li><a href="#outline-levels">Outline Levels</a></li>
<li><a href="#images">Images</a></li>
<li><a href="#shape">Shape</a></li>
<li><a href="#sheet-protection">Sheet Protection</a></li>
<li><a href="#file-io">File I/O</a>
<ul>
Expand Down Expand Up @@ -2209,6 +2210,31 @@ worksheet.addImage(imageId2, {
});
```

## Shape

### Add shape to worksheet[⬆](#contents)

```javascript
worksheet.addShape({
type: 'roundRect',
rotation: 0,
fill: { type: 'solid', color: { rgb: '4499FF' } },
outline: { weight: 2, color: { rgb: '446699' }, dash: 'sysDash' },
textBody: {
vertAlign: 'ctr',
paragraphs: [
{ alignment: 'l', runs: ["Lorem ipsum dolor sit amet, consectetur adipiscing elit."] },
{ alignment: 'r', runs: [
{ text: "Nulla eget odio sed libero ultrices vehicula.", font: { bold: true, color: { rgb: 'FF0000' } } },
] },
],
},
}, 'B2:H8', {
hyperlink: 'https://www.example.com',
tooltip: 'Example Link',
});
```

## Sheet Protection[⬆](#contents)<!-- Link generated with jump2header -->

Worksheets can be protected from modification by adding a password.
Expand Down
26 changes: 26 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ ws1.getCell('A1').value = { text: 'Sheet2', hyperlink: '#A1:B1' };
<li><a href="#条件格式化">条件格式化</a></li>
<li><a href="#大纲级别">大纲级别</a></li>
<li><a href="#图片">图片</a></li>
<li><a href="#形状">形状Shape</a></li>
<li><a href="#工作表保护">工作表保护</a></li>
<li><a href="#文件-io">文件 I/O</a>
<ul>
Expand Down Expand Up @@ -2106,6 +2107,31 @@ worksheet.addImage(imageId2, {
});
```

## 形状(Shape)

### 新增形状(Shape)到工作表[⬆](#contents)

```javascript
worksheet.addShape({
type: 'roundRect',
rotation: 0,
fill: { type: 'solid', color: { rgb: '4499FF' } },
outline: { weight: 2, color: { rgb: '446699' }, dash: 'sysDash' },
textBody: {
vertAlign: 'ctr',
paragraphs: [
{ alignment: 'l', runs: ["Lorem ipsum dolor sit amet, consectetur adipiscing elit."] },
{ alignment: 'r', runs: [
{ text: "Nulla eget odio sed libero ultrices vehicula.", font: { bold: true, color: { rgb: 'FF0000' } } },
] },
],
},
}, 'B2:H8', {
hyperlink: 'https://www.example.com',
tooltip: 'Example Link',
});
```

## 工作表保护[⬆](#目录)<!-- Link generated with jump2header -->

可以通过添加密码来保护工作表免受修改。
Expand Down
77 changes: 71 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1004,21 +1004,79 @@ export class Anchor implements IAnchor {

constructor(model?: IAnchor | object);
}
export interface ImageRange {
export interface DrawingRange {
tl: Anchor;
br: Anchor;
}

export interface ImagePosition {
export interface DrawingPosition {
tl: { col: number; row: number };
ext: { width: number; height: number };
}

export interface ImageHyperlinkValue {
export interface DrawingHyperlinkValue {
hyperlink: string;
tooltip?: string;
}

export interface ShapeProps {
/**
* Defined as DocumentFormat.OpenXml.Drawing.ShapeTypeValues in Open API Spec.
* See https://learn.microsoft.com/ja-jp/dotnet/api/documentformat.openxml.drawing.shapetypevalues
*/
type: string;
rotation?: number;
horizontalFlip?: boolean;
verticalFlip?: boolean;
fill?: ShapeFill;
outline?: ShapeOutline;
textBody?: ShapeTextBody;
}

export type ShapeFill = {
type: 'solid',
color: { theme?: string, rgb?: string }
}

export interface ShapeArrowEnd {
type?: 'triangle' | 'arrow' | 'stealth' | 'diamond' | 'oval';
length?: 'lg' | 'med' | 'sm';
width?: 'lg' | 'med' | 'sm';
}

export type ShapeOutline = {
weight?: number,
color?: { theme?: string, rgb?: string },
dash?: 'solid' | 'sysDot' | 'sysDash' | 'dash' | 'dashDot' | 'lgDash' | 'lgDashDot' | 'lgDashDotDot',
arrow?: {
head?: ShapeArrowEnd,
tail?: ShapeArrowEnd
}
}

export type ShapeTextBody = {
paragraphs: ShapeParagraph[],
vertAlign?: 't' | 'ctr' | 'b',
}

export type ShapeParagraph = {
runs: ShapeRun[],
alignment?: 'l' | 'ctr' | 'r',
}

export type ShapeRun = {
text: string,
font?: Partial<ShapeRunFont>,
}

export type ShapeRunFont = {
size: number,
color: { theme?: string, rgb?: string },
bold: boolean,
italic: boolean,
underline: 'sng' | 'dbl' | 'none',
}

export interface Range extends Location {
sheetName: string;

Expand Down Expand Up @@ -1458,14 +1516,21 @@ export interface Worksheet {
* Using the image id from `Workbook.addImage`,
* embed an image within the worksheet to cover a range
*/
addImage(imageId: number, range: string | { editAs?: string; } & ImageRange & { hyperlinks?: ImageHyperlinkValue } | { editAs?: string; } & ImagePosition & { hyperlinks?: ImageHyperlinkValue }): void;
addImage(imageId: number, range: string | { editAs?: string; } & DrawingRange & { hyperlinks?: DrawingHyperlinkValue } | { editAs?: string; } & DrawingPosition & { hyperlinks?: DrawingHyperlinkValue }): void;

getImages(): Array<{
type: 'image',
imageId: string;
range: ImageRange;
imageId: string,
range: DrawingRange,
}>;

addShape(props: ShapeProps, range: string | { editAs?: string; } & DrawingRange | { editAs?: string; } & DrawingPosition, hyperlinks?: DrawingHyperlinkValue ): void;

getShapes(): Array<{
props: ShapeProps,
range: DrawingRange,
}>

commit(): void;

model: WorksheetModel;
Expand Down
22 changes: 22 additions & 0 deletions lib/doc/drawing-range.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const colCache = require('../utils/col-cache');
const Anchor = require('./anchor');

module.exports = {
parseRange: (range, hyperlinks, worksheet) => {
if (typeof range === 'string') {
const decoded = colCache.decode(range);
return {
tl: new Anchor(worksheet, {col: decoded.left, row: decoded.top}, -1),
br: new Anchor(worksheet, {col: decoded.right, row: decoded.bottom}, 0),
editAs: 'oneCell',
};
}
return {
tl: new Anchor(worksheet, range.tl, 0),
br: range.br && new Anchor(worksheet, range.br, 0),
ext: range.ext,
editAs: range.editAs,
hyperlinks: hyperlinks || range.hyperlinks,
};
},
};
22 changes: 3 additions & 19 deletions lib/doc/image.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const colCache = require('../utils/col-cache');
const Anchor = require('./anchor');
const {parseRange} = require('./drawing-range');

class Image {
constructor(worksheet, model) {
Expand Down Expand Up @@ -36,24 +35,9 @@ class Image {
this.imageId = imageId;

if (type === 'image') {
if (typeof range === 'string') {
const decoded = colCache.decode(range);
this.range = {
tl: new Anchor(this.worksheet, {col: decoded.left, row: decoded.top}, -1),
br: new Anchor(this.worksheet, {col: decoded.right, row: decoded.bottom}, 0),
editAs: 'oneCell',
};
} else {
this.range = {
tl: new Anchor(this.worksheet, range.tl, 0),
br: range.br && new Anchor(this.worksheet, range.br, 0),
ext: range.ext,
editAs: range.editAs,
hyperlinks: hyperlinks || range.hyperlinks,
};
}
this.range = parseRange(range, hyperlinks, this.worksheet);
}
}
}

module.exports = Image;
module.exports = Image;
110 changes: 110 additions & 0 deletions lib/doc/shape.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const {parseRange} = require('./drawing-range');

class Shape {
constructor(worksheet, model) {
this.worksheet = worksheet;
this.model = model;
}

get model() {
return {
props: {
type: this.props.type,
rotation: this.props.rotation,
horizontalFlip: this.props.horizontalFlip,
verticalFlip: this.props.verticalFlip,
fill: this.props.fill,
outline: this.props.outline,
textBody: this.props.textBody,
},
range: {
tl: this.range.tl.model,
br: this.range.br && this.range.br.model,
ext: this.range.ext,
editAs: this.range.editAs,
},
hyperlinks: this.hyperlinks,
};
}

set model({props, range, hyperlinks}) {
this.props = {type: props.type};
if (props.rotation) {
this.props.rotation = props.rotation;
}
if (props.horizontalFlip) {
this.props.horizontalFlip = props.horizontalFlip;
}
if (props.verticalFlip) {
this.props.verticalFlip = props.verticalFlip;
}
if (props.fill) {
this.props.fill = props.fill;
}
if (props.outline) {
this.props.outline = props.outline;
}
if (props.textBody) {
this.props.textBody = parseAsTextBody(props.textBody);
}
this.range = parseRange(range, undefined, this.worksheet);
this.hyperlinks = hyperlinks;
}
}

function parseAsTextBody(input) {
if (typeof input === 'string') {
return {
paragraphs: [parseAsParagraph(input)],
};
}
if (Array.isArray(input)) {
return {
paragraphs: input.map(parseAsParagraph),
};
}
const model = {
paragraphs: input.paragraphs.map(parseAsParagraph),
};
if (input.vertAlign) {
model.vertAlign = input.vertAlign;
}
return model;
}

function parseAsParagraph(input) {
if (typeof input === 'string') {
return {
runs: [parseAsRun(input)],
};
}
if (Array.isArray(input)) {
return {
runs: input.map(parseAsRun),
};
}
const model = {
runs: input.runs.map(parseAsRun),
};
if (input.alignment) {
model.alignment = input.alignment;
}
return model;
}

function parseAsRun(input) {
if (typeof input === 'string') {
return {
text: input,
};
}
const model = {
text: input.text,
};
if (input.font) {
model.font = input.font;
}
return model;
}

module.exports = Shape;
Loading
Loading