From d471165430e27e5322160e22da5a5f53fc969b0f Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Wed, 12 Jul 2023 20:40:58 +0800 Subject: [PATCH] feat: add word break option #212 --- docs/guide/option.md | 1 + src/editor/core/draw/Draw.ts | 20 ++++++++++++--- src/editor/core/draw/particle/TextParticle.ts | 25 +++++++++++++++++++ src/editor/dataset/constant/Regular.ts | 3 ++- src/editor/dataset/enum/Editor.ts | 5 ++++ src/editor/index.ts | 3 ++- src/editor/interface/Editor.ts | 3 ++- 7 files changed, 53 insertions(+), 7 deletions(-) diff --git a/docs/guide/option.md b/docs/guide/option.md index e200aa9..15ac131 100644 --- a/docs/guide/option.md +++ b/docs/guide/option.md @@ -51,6 +51,7 @@ interface IEditorOption { paperDirection?: PaperDirection; // 纸张方向:纵向、横向 inactiveAlpha?: number; // 正文内容失焦时透明度。默认值:0.6 historyMaxRecordCount: number; // 历史(撤销重做)最大记录次数。默认:100次 + wordBreak: WordBreak; // 单词与标点断行:BREAK_WORD首行不出现标点&单词不拆分、BREAK_ALL按字符宽度撑满后折行。默认:BREAK_WORD watermark?: IWatermark; // 水印信息。{data:string; color?:string; opacity?:number; size?:number; font?:string;} control?: IControlOption; // 控件信息。 {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string;} checkbox?: ICheckboxOption; // 复选框信息。{width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; fontStyle?: string;} diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 8e75b43..d0c543d 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -35,7 +35,7 @@ import { SubscriptParticle } from './particle/Subscript' import { SeparatorParticle } from './particle/Separator' import { PageBreakParticle } from './particle/PageBreak' import { Watermark } from './frame/Watermark' -import { EditorComponent, EditorMode, EditorZone, PageMode, PaperDirection } from '../../dataset/enum/Editor' +import { EditorComponent, EditorMode, EditorZone, PageMode, PaperDirection, WordBreak } from '../../dataset/enum/Editor' import { Control } from './control/Control' import { zipElementList } from '../../utils/element' import { CheckboxParticle } from './particle/CheckboxParticle' @@ -55,6 +55,7 @@ import { Footer } from './frame/Footer' import { INLINE_ELEMENT_TYPE } from '../../dataset/constant/Element' import { ListParticle } from './particle/ListParticle' import { Placeholder } from './frame/Placeholder' +import { WORD_LIKE_REG } from '../../dataset/constant/Regular' export class Draw { @@ -1107,9 +1108,20 @@ export class Draw { }) // 超过限定宽度 const preElement = elementList[i - 1] - const nextElement = elementList[i + 1] - // 累计行宽 + 当前元素宽度 + 后面标点符号宽度 - const curRowWidth = curRow.width + metrics.width + this.textParticle.measurePunctuationWidth(ctx, nextElement) + let nextElement = elementList[i + 1] + // 累计行宽 + 当前元素宽度 + 排版宽度(英文单词整体宽度 + 后面标点符号宽度) + let curRowWidth = curRow.width + metrics.width + if (this.options.wordBreak === WordBreak.BREAK_WORD) { + // 英文单词 + const word = `${preElement?.value || ''}${element.value}` + if (WORD_LIKE_REG.test(word)) { + const { width, endElement } = this.textParticle.measureWord(ctx, elementList, i) + curRowWidth += width + nextElement = endElement + } + // 标点符号 + curRowWidth += this.textParticle.measurePunctuationWidth(ctx, nextElement) + } // 列表信息 if (element.listId) { if (element.listId !== listId) { diff --git a/src/editor/core/draw/particle/TextParticle.ts b/src/editor/core/draw/particle/TextParticle.ts index 9fc1c95..1702b50 100644 --- a/src/editor/core/draw/particle/TextParticle.ts +++ b/src/editor/core/draw/particle/TextParticle.ts @@ -1,8 +1,14 @@ import { IElement } from '../../..' import { PUNCTUATION_LIST } from '../../../dataset/constant/Common' +import { LETTER_REG } from '../../../dataset/constant/Regular' import { IRowElement } from '../../../interface/Row' import { Draw } from '../Draw' +export interface IMeasureWordResult { + width: number; + endElement: IElement; +} + export class TextParticle { private ctx: CanvasRenderingContext2D @@ -22,6 +28,25 @@ export class TextParticle { this.cacheMeasureText = new Map() } + public measureWord(ctx: CanvasRenderingContext2D, elementList: IElement[], curIndex: number): IMeasureWordResult { + let width = 0 + let endElement: IElement = elementList[curIndex] + let i = curIndex + while (i < elementList.length) { + const element = elementList[i] + if (!LETTER_REG.test(element.value)) { + endElement = element + break + } + width += this.measureText(ctx, element).width + i++ + } + return { + width, + endElement + } + } + public measurePunctuationWidth(ctx: CanvasRenderingContext2D, element: IElement): number { if (!element || !PUNCTUATION_LIST.includes(element.value)) return 0 return this.measureText(ctx, element).width diff --git a/src/editor/dataset/constant/Regular.ts b/src/editor/dataset/constant/Regular.ts index 63be9dd..9d4e4b7 100644 --- a/src/editor/dataset/constant/Regular.ts +++ b/src/editor/dataset/constant/Regular.ts @@ -1,4 +1,5 @@ export const NUMBER_REG = /[0-9]/ export const LETTER_REG = /[a-zA-Z]/ export const NUMBER_LIKE_REG = /[0-9.]/ -export const CHINESE_REG = /[\u4e00-\u9fa5]/ \ No newline at end of file +export const CHINESE_REG = /[\u4e00-\u9fa5]/ +export const WORD_LIKE_REG = /[^a-zA-Z][a-zA-Z]/ \ No newline at end of file diff --git a/src/editor/dataset/enum/Editor.ts b/src/editor/dataset/enum/Editor.ts index 7d1823f..1c0f8f9 100644 --- a/src/editor/dataset/enum/Editor.ts +++ b/src/editor/dataset/enum/Editor.ts @@ -33,4 +33,9 @@ export enum PageMode { export enum PaperDirection { VERTICAL = 'vertical', HORIZONTAL = 'horizontal' +} + +export enum WordBreak { + BREAK_ALL = 'break-all', + BREAK_WORD = 'break-word' } \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index a92ce1f..f9e4235 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -11,7 +11,7 @@ import { formatElementList } from './utils/element' import { Register } from './core/register/Register' import { ContextMenu } from './core/contextmenu/ContextMenu' import { IContextMenuContext, IRegisterContextMenu } from './interface/contextmenu/ContextMenu' -import { EditorComponent, EditorZone, EditorMode, PageMode, PaperDirection } from './dataset/enum/Editor' +import { EditorComponent, EditorZone, EditorMode, PageMode, PaperDirection, WordBreak } from './dataset/enum/Editor' import { EDITOR_COMPONENT } from './dataset/constant/Editor' import { IHeader } from './interface/Header' import { IWatermark } from './interface/Watermark' @@ -129,6 +129,7 @@ export default class Editor { paperDirection: PaperDirection.VERTICAL, inactiveAlpha: 0.6, historyMaxRecordCount: 100, + wordBreak: WordBreak.BREAK_WORD, ...options, header: headerOptions, footer: footerOptions, diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index de2dc79..a540b2b 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -1,5 +1,5 @@ import { IElement } from '..' -import { EditorMode, PageMode, PaperDirection } from '../dataset/enum/Editor' +import { EditorMode, PageMode, PaperDirection, WordBreak } from '../dataset/enum/Editor' import { ICheckboxOption } from './Checkbox' import { IControlOption } from './Control' import { ICursorOption } from './Cursor' @@ -52,6 +52,7 @@ export interface IEditorOption { paperDirection?: PaperDirection; inactiveAlpha?: number; historyMaxRecordCount?: number; + wordBreak?: WordBreak; header?: IHeader; footer?: IFooter; pageNumber?: IPageNumber;