Merge pull request #18 from Hufe921/feature/page-break
feat:add page break
This commit is contained in:
@@ -157,6 +157,9 @@
|
|||||||
<div class="menu-item__codeblock">
|
<div class="menu-item__codeblock">
|
||||||
<i></i>
|
<i></i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="menu-item__page-break">
|
||||||
|
<i></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-divider"></div>
|
<div class="menu-divider"></div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 16 16" xml:space="preserve"><style>.st0{fill:none;stroke:#3d4757}.st1{fill:#3d4757}</style><g id="_x30_1-文字_x2F_02插入_x2F_分页-16px"><path id="矩形" class="st0" d="M5.5 2.5h7c.6 0 1 .4 1 1v3c0 .6-.4 1-1 1h-7c-.6 0-1-.4-1-1v-3c0-.6.4-1 1-1z"/><path id="三角形" class="st1" d="M4 8.5L1 6v5z"/><path id="合并形状" class="st0" d="M10.8 9.5H5c-.3 0-.5.2-.5.5v4c0 .3.2.5.5.5h8c.3 0 .5-.2.5-.5v-1.8l-2.7-2.7z"/><path id="矩形_1_" class="st1" d="M10 10h1v3h-1z"/><path id="矩形-copy-2" class="st1" d="M10 12h3v1h-3z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 635 B |
@@ -42,6 +42,7 @@ export class Command {
|
|||||||
private static image: Function
|
private static image: Function
|
||||||
private static hyperlink: Function
|
private static hyperlink: Function
|
||||||
private static separator: Function
|
private static separator: Function
|
||||||
|
private static pageBreak: Function
|
||||||
private static addWatermark: Function
|
private static addWatermark: Function
|
||||||
private static deleteWatermark: Function
|
private static deleteWatermark: Function
|
||||||
private static search: Function
|
private static search: Function
|
||||||
@@ -88,6 +89,7 @@ export class Command {
|
|||||||
Command.image = adapt.image.bind(adapt)
|
Command.image = adapt.image.bind(adapt)
|
||||||
Command.hyperlink = adapt.hyperlink.bind(adapt)
|
Command.hyperlink = adapt.hyperlink.bind(adapt)
|
||||||
Command.separator = adapt.separator.bind(adapt)
|
Command.separator = adapt.separator.bind(adapt)
|
||||||
|
Command.pageBreak = adapt.pageBreak.bind(adapt)
|
||||||
Command.addWatermark = adapt.addWatermark.bind(adapt)
|
Command.addWatermark = adapt.addWatermark.bind(adapt)
|
||||||
Command.deleteWatermark = adapt.deleteWatermark.bind(adapt)
|
Command.deleteWatermark = adapt.deleteWatermark.bind(adapt)
|
||||||
Command.search = adapt.search.bind(adapt)
|
Command.search = adapt.search.bind(adapt)
|
||||||
@@ -246,6 +248,10 @@ export class Command {
|
|||||||
return Command.separator(payload)
|
return Command.separator(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public executePageBreak() {
|
||||||
|
return Command.pageBreak()
|
||||||
|
}
|
||||||
|
|
||||||
public executeAddWatermark(payload: IWatermark) {
|
public executeAddWatermark(payload: IWatermark) {
|
||||||
return Command.addWatermark(payload)
|
return Command.addWatermark(payload)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ZERO } from "../../dataset/constant/Common"
|
import { WRAP, ZERO } from "../../dataset/constant/Common"
|
||||||
import { EDITOR_ELEMENT_STYLE_ATTR, TEXTLIKE_ELEMENT_TYPE } from "../../dataset/constant/Element"
|
import { EDITOR_ELEMENT_STYLE_ATTR, TEXTLIKE_ELEMENT_TYPE } from "../../dataset/constant/Element"
|
||||||
import { defaultWatermarkOption } from "../../dataset/constant/Watermark"
|
import { defaultWatermarkOption } from "../../dataset/constant/Watermark"
|
||||||
import { EditorContext } from "../../dataset/enum/Editor"
|
import { EditorContext } from "../../dataset/enum/Editor"
|
||||||
@@ -905,7 +905,7 @@ export class CommandAdapt {
|
|||||||
endElement.dashArray = payload
|
endElement.dashArray = payload
|
||||||
} else {
|
} else {
|
||||||
const newElement: IElement = {
|
const newElement: IElement = {
|
||||||
value: '\n',
|
value: WRAP,
|
||||||
type: ElementType.SEPARATOR,
|
type: ElementType.SEPARATOR,
|
||||||
dashArray: payload
|
dashArray: payload
|
||||||
}
|
}
|
||||||
@@ -922,6 +922,13 @@ export class CommandAdapt {
|
|||||||
this.draw.render({ curIndex })
|
this.draw.render({ curIndex })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public pageBreak() {
|
||||||
|
this.insertElementList([{
|
||||||
|
type: ElementType.PAGE_BREAK,
|
||||||
|
value: WRAP
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
public addWatermark(payload: IWatermark) {
|
public addWatermark(payload: IWatermark) {
|
||||||
const options = this.draw.getOptions()
|
const options = this.draw.getOptions()
|
||||||
const { color, size, opacity, font } = defaultWatermarkOption
|
const { color, size, opacity, font } = defaultWatermarkOption
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import { Header } from "./frame/Header"
|
|||||||
import { SuperscriptParticle } from "./particle/Superscript"
|
import { SuperscriptParticle } from "./particle/Superscript"
|
||||||
import { SubscriptParticle } from "./particle/Subscript"
|
import { SubscriptParticle } from "./particle/Subscript"
|
||||||
import { SeparatorParticle } from "./particle/Separator"
|
import { SeparatorParticle } from "./particle/Separator"
|
||||||
|
import { PageBreakParticle } from "./particle/PageBreak"
|
||||||
import { Watermark } from "./frame/Watermark"
|
import { Watermark } from "./frame/Watermark"
|
||||||
|
|
||||||
export class Draw {
|
export class Draw {
|
||||||
@@ -64,6 +65,7 @@ export class Draw {
|
|||||||
private header: Header
|
private header: Header
|
||||||
private hyperlinkParticle: HyperlinkParticle
|
private hyperlinkParticle: HyperlinkParticle
|
||||||
private separatorParticle: SeparatorParticle
|
private separatorParticle: SeparatorParticle
|
||||||
|
private pageBreakParticle: PageBreakParticle
|
||||||
private superscriptParticle: SuperscriptParticle
|
private superscriptParticle: SuperscriptParticle
|
||||||
private subscriptParticle: SubscriptParticle
|
private subscriptParticle: SubscriptParticle
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ export class Draw {
|
|||||||
this.header = new Header(this)
|
this.header = new Header(this)
|
||||||
this.hyperlinkParticle = new HyperlinkParticle(this)
|
this.hyperlinkParticle = new HyperlinkParticle(this)
|
||||||
this.separatorParticle = new SeparatorParticle()
|
this.separatorParticle = new SeparatorParticle()
|
||||||
|
this.pageBreakParticle = new PageBreakParticle(this)
|
||||||
this.superscriptParticle = new SuperscriptParticle()
|
this.superscriptParticle = new SuperscriptParticle()
|
||||||
this.subscriptParticle = new SubscriptParticle()
|
this.subscriptParticle = new SubscriptParticle()
|
||||||
|
|
||||||
@@ -512,6 +515,10 @@ export class Draw {
|
|||||||
metrics.height = this.options.defaultSize
|
metrics.height = this.options.defaultSize
|
||||||
metrics.boundingBoxAscent = -rowMargin
|
metrics.boundingBoxAscent = -rowMargin
|
||||||
metrics.boundingBoxDescent = -rowMargin
|
metrics.boundingBoxDescent = -rowMargin
|
||||||
|
} else if (element.type === ElementType.PAGE_BREAK) {
|
||||||
|
element.width = innerWidth
|
||||||
|
metrics.width = innerWidth
|
||||||
|
metrics.height = this.options.defaultSize
|
||||||
} else {
|
} else {
|
||||||
// 设置上下标真实字体尺寸
|
// 设置上下标真实字体尺寸
|
||||||
const size = element.size || this.options.defaultSize
|
const size = element.size || this.options.defaultSize
|
||||||
@@ -549,7 +556,8 @@ export class Draw {
|
|||||||
height,
|
height,
|
||||||
elementList: [rowElement],
|
elementList: [rowElement],
|
||||||
ascent,
|
ascent,
|
||||||
rowFlex: rowElement.rowFlex
|
rowFlex: rowElement.rowFlex,
|
||||||
|
isPageBreak: element.type === ElementType.PAGE_BREAK
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
curRow.width += metrics.width
|
curRow.width += metrics.width
|
||||||
@@ -641,6 +649,8 @@ export class Draw {
|
|||||||
this.subscriptParticle.render(ctx, element, x, y + offsetY)
|
this.subscriptParticle.render(ctx, element, x, y + offsetY)
|
||||||
} else if (element.type === ElementType.SEPARATOR) {
|
} else if (element.type === ElementType.SEPARATOR) {
|
||||||
this.separatorParticle.render(ctx, element, x, y)
|
this.separatorParticle.render(ctx, element, x, y)
|
||||||
|
} else if (element.type === ElementType.PAGE_BREAK) {
|
||||||
|
this.pageBreakParticle.render(ctx, element, x, y)
|
||||||
} else {
|
} else {
|
||||||
this.textParticle.record(ctx, element, x, y + offsetY)
|
this.textParticle.record(ctx, element, x, y + offsetY)
|
||||||
}
|
}
|
||||||
@@ -788,7 +798,7 @@ export class Draw {
|
|||||||
let pageRowList: IRow[][] = [[]]
|
let pageRowList: IRow[][] = [[]]
|
||||||
for (let i = 0; i < this.rowList.length; i++) {
|
for (let i = 0; i < this.rowList.length; i++) {
|
||||||
const row = this.rowList[i]
|
const row = this.rowList[i]
|
||||||
if (row.height + pageHeight > height) {
|
if (row.height + pageHeight > height || this.rowList[i - 1]?.isPageBreak) {
|
||||||
pageHeight = marginHeight + row.height
|
pageHeight = marginHeight + row.height
|
||||||
pageRowList.push([row])
|
pageRowList.push([row])
|
||||||
pageNo++
|
pageNo++
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { IEditorOption } from "../../../interface/Editor"
|
||||||
|
import { IRowElement } from "../../../interface/Row"
|
||||||
|
import { Draw } from "../Draw"
|
||||||
|
|
||||||
|
export class PageBreakParticle {
|
||||||
|
|
||||||
|
static readonly font: string = 'Yahei'
|
||||||
|
static readonly fontSize: number = 12
|
||||||
|
static readonly displayName: string = '分页符'
|
||||||
|
static readonly lineDash: number[] = [3, 1]
|
||||||
|
|
||||||
|
private draw: Draw
|
||||||
|
private options: Required<IEditorOption>
|
||||||
|
|
||||||
|
constructor(draw: Draw) {
|
||||||
|
this.draw = draw
|
||||||
|
this.options = draw.getOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(ctx: CanvasRenderingContext2D, element: IRowElement, x: number, y: number) {
|
||||||
|
const { font, fontSize, displayName, lineDash } = PageBreakParticle
|
||||||
|
const { scale, defaultRowMargin } = this.options
|
||||||
|
const size = fontSize * scale
|
||||||
|
const elementWidth = element.width!
|
||||||
|
const offsetY = this.draw.getDefaultBasicRowMarginHeight() * defaultRowMargin
|
||||||
|
ctx.save()
|
||||||
|
ctx.font = `${size}px ${font}`
|
||||||
|
const textMeasure = ctx.measureText(displayName)
|
||||||
|
const halfX = (elementWidth - textMeasure.width) / 2
|
||||||
|
// 线段
|
||||||
|
ctx.setLineDash(lineDash)
|
||||||
|
ctx.translate(0, 0.5 + offsetY)
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(x, y)
|
||||||
|
ctx.lineTo(x + halfX, y)
|
||||||
|
ctx.moveTo(x + halfX + textMeasure.width, y)
|
||||||
|
ctx.lineTo(x + elementWidth, y)
|
||||||
|
ctx.stroke()
|
||||||
|
// 文字
|
||||||
|
ctx.fillText(displayName, x + halfX, y + textMeasure.actualBoundingBoxAscent - size / 2)
|
||||||
|
ctx.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,5 +5,6 @@ export enum ElementType {
|
|||||||
HYPERLINK = 'hyperlink',
|
HYPERLINK = 'hyperlink',
|
||||||
SUPERSCRIPT = 'superscript',
|
SUPERSCRIPT = 'superscript',
|
||||||
SUBSCRIPT = 'subscript',
|
SUBSCRIPT = 'subscript',
|
||||||
SEPARATOR = 'separator'
|
SEPARATOR = 'separator',
|
||||||
|
PAGE_BREAK = 'pageBreak'
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ export interface IRow {
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
ascent: number;
|
ascent: number;
|
||||||
rowFlex?: RowFlex
|
rowFlex?: RowFlex;
|
||||||
|
isPageBreak?: boolean;
|
||||||
elementList: IRowElement[];
|
elementList: IRowElement[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -488,6 +488,11 @@ window.onload = function () {
|
|||||||
}
|
}
|
||||||
instance.command.executeSeparator(payload)
|
instance.command.executeSeparator(payload)
|
||||||
}
|
}
|
||||||
|
const pageBreakDom = document.querySelector<HTMLDivElement>('.menu-item__page-break')!
|
||||||
|
pageBreakDom.onclick = function () {
|
||||||
|
console.log('pageBreak')
|
||||||
|
instance.command.executePageBreak()
|
||||||
|
}
|
||||||
const watermarkDom = document.querySelector<HTMLDivElement>('.menu-item__watermark')!
|
const watermarkDom = document.querySelector<HTMLDivElement>('.menu-item__watermark')!
|
||||||
const watermarkOptionDom = watermarkDom.querySelector<HTMLDivElement>('.options')!
|
const watermarkOptionDom = watermarkDom.querySelector<HTMLDivElement>('.options')!
|
||||||
watermarkDom.onclick = function () {
|
watermarkDom.onclick = function () {
|
||||||
|
|||||||
@@ -425,6 +425,10 @@ ul {
|
|||||||
background-image: url('./assets/images/codeblock.svg');
|
background-image: url('./assets/images/codeblock.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-item__page-break i {
|
||||||
|
background-image: url('./assets/images/page-break.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.menu-item__search {
|
.menu-item__search {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user