From de769778c04266ed08c5721a01670cdbf0992d2b Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Sat, 9 Sep 2023 10:07:43 +0800 Subject: [PATCH] feat: custom letter class #279 --- docs/en/guide/option.md | 9 +++++---- docs/guide/option.md | 9 +++++---- src/editor/core/draw/Draw.ts | 14 ++++++++++++-- src/editor/core/draw/particle/TextParticle.ts | 4 +++- src/editor/core/event/handlers/click.ts | 3 ++- src/editor/dataset/constant/Common.ts | 13 +++++++++++++ src/editor/dataset/constant/Regular.ts | 2 -- src/editor/index.ts | 5 ++++- src/editor/interface/Editor.ts | 1 + 9 files changed, 45 insertions(+), 15 deletions(-) diff --git a/docs/en/guide/option.md b/docs/en/guide/option.md index 344c38f..6d5c7fa 100644 --- a/docs/en/guide/option.md +++ b/docs/en/guide/option.md @@ -51,10 +51,11 @@ interface IEditorOption { pageNumber?: IPageNumber // Page number information. {bottom:number; size:number; font:string; color:string; rowFlex:RowFlex; format:string; numberType:NumberType;} paperDirection?: PaperDirection // Paper orientation: portrait, landscape inactiveAlpha?: number // When the body content is out of focus, transparency. default: 0.6 - historyMaxRecordCount: number // History (undo redo) maximum number of records. default: 100 - printPixelRatio: number // Print the pixel ratio (larger values are clearer, but larger sizes). default: 3 - maskMargin: IMargin // Masking margins above the editor(for example: menu bar, bottom toolbar)。default: [0, 0, 0, 0] - wordBreak: WordBreak // Word and punctuation breaks: No punctuation in the first line of the BREAK_WORD &The word is not split, and the line is folded after BREAK_ALL full according to the width of the character. default: BREAK_WORD + historyMaxRecordCount?: number // History (undo redo) maximum number of records. default: 100 + printPixelRatio?: number // Print the pixel ratio (larger values are clearer, but larger sizes). default: 3 + maskMargin?: IMargin // Masking margins above the editor(for example: menu bar, bottom toolbar)。default: [0, 0, 0, 0] + letterClass? string[] // Alphabet class supported by typesetting. default: a-zA-Z. Built-in alternative alphabet class: LETTER_CLASS + wordBreak?: WordBreak // Word and punctuation breaks: No punctuation in the first line of the BREAK_WORD &The word is not split, and the line is folded after BREAK_ALL full according to the width of the character. default: BREAK_WORD watermark?: IWatermark // Watermark{data:string; color?:string; opacity?:number; size?:number; font?:string;} control?: IControlOption // Control {placeholderColor?:string; bracketColor?:string; prefix?:string; postfix?:string;} checkbox?: ICheckboxOption // Checkbox {width?:number; height?:number; gap?:number; lineWidth?:number; fillStyle?:string; fontStyle?: string;} diff --git a/docs/guide/option.md b/docs/guide/option.md index 68de8b4..9fcdfe4 100644 --- a/docs/guide/option.md +++ b/docs/guide/option.md @@ -51,10 +51,11 @@ interface IEditorOption { pageNumber?: IPageNumber // 页码信息。{bottom:number; size:number; font:string; color:string; rowFlex:RowFlex; format:string; numberType:NumberType;} paperDirection?: PaperDirection // 纸张方向:纵向、横向 inactiveAlpha?: number // 正文内容失焦时透明度。默认值:0.6 - historyMaxRecordCount: number // 历史(撤销重做)最大记录次数。默认:100次 - printPixelRatio: number // 打印像素比率(值越大越清晰,但尺寸越大)。默认:3 - maskMargin: IMargin // 编辑器上的遮盖边距(如悬浮到编辑器上的菜单栏、底部工具栏)。默认:[0, 0, 0, 0] - wordBreak: WordBreak // 单词与标点断行:BREAK_WORD首行不出现标点&单词不拆分、BREAK_ALL按字符宽度撑满后折行。默认:BREAK_WORD + historyMaxRecordCount?: number // 历史(撤销重做)最大记录次数。默认:100次 + printPixelRatio?: number // 打印像素比率(值越大越清晰,但尺寸越大)。默认:3 + maskMargin?: IMargin // 编辑器上的遮盖边距(如悬浮到编辑器上的菜单栏、底部工具栏)。默认:[0, 0, 0, 0] + letterClass? string[] // 排版支持的字母类。默认:a-zA-Z。内置可选择的字母表类:LETTER_CLASS + 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 e443840..3e39ba2 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -79,7 +79,6 @@ 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' import { EventBus } from '../event/eventbus/EventBus' import { EventBusMap } from '../../interface/EventBus' import { Group } from './interactive/Group' @@ -140,6 +139,8 @@ export class Draw { private selectionObserver: SelectionObserver private imageObserver: ImageObserver + private LETTER_REG: RegExp + private WORD_LIKE_REG: RegExp private rowList: IRow[] private pageRowList: IRow[][] private painterStyle: IElementStyle | null @@ -219,6 +220,11 @@ export class Draw { this.workerManager = new WorkerManager(this) + const { letterClass } = options + this.LETTER_REG = new RegExp(`[${letterClass.join('')}]`) + this.WORD_LIKE_REG = new RegExp( + `${letterClass.map(letter => `[^${letter}][${letter}]`).join('|')}` + ) this.rowList = [] this.pageRowList = [] this.painterStyle = null @@ -234,6 +240,10 @@ export class Draw { }) } + public getLetterReg(): RegExp { + return this.LETTER_REG + } + public getMode(): EditorMode { return this.mode } @@ -1346,7 +1356,7 @@ export class Draw { ) { // 英文单词 const word = `${preElement?.value || ''}${element.value}` - if (WORD_LIKE_REG.test(word)) { + if (this.WORD_LIKE_REG.test(word)) { const { width, endElement } = this.textParticle.measureWord( ctx, elementList, diff --git a/src/editor/core/draw/particle/TextParticle.ts b/src/editor/core/draw/particle/TextParticle.ts index 25c85db..0e3dbf2 100644 --- a/src/editor/core/draw/particle/TextParticle.ts +++ b/src/editor/core/draw/particle/TextParticle.ts @@ -1,6 +1,5 @@ import { ElementType, 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' @@ -10,6 +9,7 @@ export interface IMeasureWordResult { } export class TextParticle { + private draw: Draw private ctx: CanvasRenderingContext2D private curX: number private curY: number @@ -19,6 +19,7 @@ export class TextParticle { public cacheMeasureText: Map constructor(draw: Draw) { + this.draw = draw this.ctx = draw.getCtx() this.curX = -1 this.curY = -1 @@ -32,6 +33,7 @@ export class TextParticle { elementList: IElement[], curIndex: number ): IMeasureWordResult { + const LETTER_REG = this.draw.getLetterReg() let width = 0 let endElement: IElement = elementList[curIndex] let i = curIndex diff --git a/src/editor/core/event/handlers/click.ts b/src/editor/core/event/handlers/click.ts index c017069..6bfcba4 100644 --- a/src/editor/core/event/handlers/click.ts +++ b/src/editor/core/event/handlers/click.ts @@ -1,9 +1,10 @@ import { ZERO } from '../../../dataset/constant/Common' -import { LETTER_REG, NUMBER_LIKE_REG } from '../../../dataset/constant/Regular' +import { NUMBER_LIKE_REG } from '../../../dataset/constant/Regular' import { CanvasEvent } from '../CanvasEvent' function dblclick(host: CanvasEvent, evt: MouseEvent) { const draw = host.getDraw() + const LETTER_REG = draw.getLetterReg() const position = draw.getPosition() // 切换区域 if (draw.getIsPagingMode()) { diff --git a/src/editor/dataset/constant/Common.ts b/src/editor/dataset/constant/Common.ts index 27e6d16..3e90650 100644 --- a/src/editor/dataset/constant/Common.ts +++ b/src/editor/dataset/constant/Common.ts @@ -27,3 +27,16 @@ export const maxHeightRadioMapping: Record = { [MaxHeightRatio.ONE_THIRD]: 1 / 3, [MaxHeightRatio.QUARTER]: 1 / 4 } + +export const LETTER_CLASS = { + ENGLISH: 'A-Za-z', + SPANISH: 'A-Za-zÁÉÍÓÚáéíóúÑñÜü', + FRENCH: 'A-Za-zÀÂÇàâçÉéÈèÊêËëÎîÏïÔôÙùÛûŸÿ', + GERMAN: 'A-Za-zÄäÖöÜüß', + RUSSIAN: 'А-Яа-яЁё', + PORTUGUESE: 'A-Za-zÁÉÍÓÚáéíóúÃÕãõÇç', + ITALIAN: 'A-Za-zÀàÈèÉéÌìÍíÎîÓóÒòÙù', + DUTCH: 'A-Za-zÀàÁáÂâÄäÈèÉéÊêËëÌìÍíÎîÏïÓóÒòÔôÖöÙùÛûÜü', + SWEDISH: 'A-Za-zÅåÄäÖö', + GREEK: 'ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω' +} diff --git a/src/editor/dataset/constant/Regular.ts b/src/editor/dataset/constant/Regular.ts index 2c972d8..d51c6ee 100644 --- a/src/editor/dataset/constant/Regular.ts +++ b/src/editor/dataset/constant/Regular.ts @@ -1,8 +1,6 @@ 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]/ -export const WORD_LIKE_REG = /[^a-zA-Z][a-zA-Z]/ export const SURROGATE_PAIR_REG = /[\uD800-\uDBFF][\uDC00-\uDFFF]/ // unicode代理对(surrogate pair) // https://github.com/mathiasbynens/emoji-test-regex-pattern export const EMOJI_REG = diff --git a/src/editor/index.ts b/src/editor/index.ts index bb0fe19..6e57bcd 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -65,6 +65,7 @@ import { IRangeStyle } from './interface/Listener' import { Override } from './core/override/Override' import { defaultPageBreakOption } from './dataset/constant/PageBreak' import { IPageBreak } from './interface/PageBreak' +import { LETTER_CLASS } from './dataset/constant/Common' export default class Editor { public command: Command @@ -164,6 +165,7 @@ export default class Editor { wordBreak: WordBreak.BREAK_WORD, printPixelRatio: 3, maskMargin: [0, 0, 0, 0], + letterClass: [LETTER_CLASS.ENGLISH], ...options, header: headerOptions, footer: footerOptions, @@ -243,6 +245,8 @@ export default class Editor { // 对外对象 export { + EDITOR_COMPONENT, + LETTER_CLASS, Editor, RowFlex, VerticalAlign, @@ -251,7 +255,6 @@ export { ElementType, ControlType, EditorComponent, - EDITOR_COMPONENT, PageMode, ImageDisplay, Command, diff --git a/src/editor/interface/Editor.ts b/src/editor/interface/Editor.ts index 16c082c..9acd290 100644 --- a/src/editor/interface/Editor.ts +++ b/src/editor/interface/Editor.ts @@ -63,6 +63,7 @@ export interface IEditorOption { historyMaxRecordCount?: number printPixelRatio?: number maskMargin?: IMargin + letterClass?: string[] wordBreak?: WordBreak header?: IHeader footer?: IFooter