/// import * as SVG from "../interfaces/svg" import * as CSS from "../html/css" import { VBATranslateFunctions } from "../common/vba_functions" import * as AttributeNames from "../common/attribute_names" import * as StyleNames from "../common/style_names" import * as DefaultClassNames from "../common/default_class_names" import { ShapeObjectType, msoDashStyle, HorizontalAnchor, VerticalAnchor, DataName, ShapeToFitType } from "../common/enums"; import { Rectangle, PositionType, round100, nearlyEqual } from "../common/vline" import * as HTMLFunctions from "../html/html_functions" import * as SVGTextBox from "../interfaces/svg_textbox" import { ZObject } from "./z_object" import { ZVertex } from "./z_vertex" import * as Extensions from "../interfaces/extensions" import * as GOptions from "./z_options" import { AutoSizeShapeToFitText } from "../common/enums" import { updateAppropriateDashArray, getUpdateFlagAppropriateDashArray } from "../html/enum_extension"; import * as ElementExtension from "../interfaces/element_extension" import * as SVGElementExtension from "../interfaces/svg_element_extension" import * as SVGTextExtension from "../interfaces/svg_text_extension" import { UndefinedError } from "../common/exceptions" import { getVirtualRegion } from "../interfaces/virtual_text" import { createSVGText } from "./element_builder" import { Debug } from ".." import { Debugger } from "../common/debugger" //namespace GraphTableSVG { export class ZTextBox extends ZVertex { private _svgText: SVGTextElement; //private isFixTextSize: boolean = false; protected surfaceAttributes: string[] = []; private _textObserver: MutationObserver; private static updateTextAttributes = ["style"] //protected _isSpecialTextBox: boolean = false; protected _minimumWidth: number = 10; protected _minimumHeight: number = 10; public constructor(svgbox: SVGElement | string) { super(svgbox) this._svgText = createSVGText(undefined, undefined, DataName.Text); this.svgGroup.appendChild(this.svgText); this._textObserver = new MutationObserver(this.textObserverFunc); const option2: MutationObserverInit = { childList: true, attributes: true, subtree: true }; this._textObserver.observe(this.svgText, option2); if (this.type == ShapeObjectType.Object) this.firstFunctionAfterInitialized(); /* this.svgText!.onclick = (e) => { const textRect = this.getVirtualRegion(); this.svgText!.style.border = "black" } */ } /* protected setBasicOption(option: GOptions.ZTextBoxAttributes) { super.setBasicOption(option) const textClass = CSS.createCSSClass(option.textClass); const styleClass = CSS.createCSSClass(option.textStyle); GOptions.setClassAndStyle(this.svgText, textClass, styleClass); if (typeof option.text == "string") { SVGTextExtension.setTextContent(this.svgText, option.text); } else if (Array.isArray(option.text)) { SVGTextBox.constructSVGTextByHTMLElements(this.svgText, option.text, false); SVGTextBox.sortText(this.svgText, this.horizontalAnchor, false); } else { } const b = ElementExtension.getPropertyStyleValue(this.svgGroup, StyleNames.autoSizeShapeToFitText); if (b === undefined && typeof (option.style) == "object") { const style: GOptions.ZTextBoxCSS = option.style; if (style.autoSizeShapeToFitText !== undefined) { this.isAutoSizeShapeToFitText = style.autoSizeShapeToFitText; } } } public setOption(option: GOptions.ZTextBoxAttributes) { super.setOption(option); //this.setBasicOption(option); } */ public initializeSetBasicOption(source: SVGElement) { super.initializeSetBasicOption(source); ZTextBox.importTextFromSource(this.svgText, this.horizontalAnchor, source); if (this.svgText != null) { ZObject.setSubAttributes(this.svgText, source); } } public static importTextFromSource(svgText: SVGTextElement | SVGTextPathElement, horizontalAnchor: HorizontalAnchor | null, source: SVGElement) { if (source.children.length > 0) { const tNodes = HTMLFunctions.getTNodes(source); if (tNodes != null) { tNodes.forEach((v) => v.remove()) SVGTextBox.constructSVGTextByHTMLElements(svgText, tNodes, false); if(horizontalAnchor != null && svgText instanceof SVGTextElement) { SVGTextBox.sortText(svgText, horizontalAnchor, false); } } } else if (source.innerHTML.length > 0) { if(svgText instanceof SVGTextElement){ SVGTextExtension.setTextContent(svgText, source.textContent!); }else{ Extensions.setTextContent(svgText, source.textContent!); } source.innerHTML = ""; } } /* static constructAttributes(e: Element, removeAttributes: boolean = false, output: GOptions.ZTextBoxAttributes = {}): GOptions.ZTextBoxAttributes { ZObject.constructAttributes(e, removeAttributes, output, "center"); //output.isAutoSizeShapeToFitText = e.gtGetStyleBooleanWithUndefined(AttributeNames.Style.autoSizeShapeToFitText); //const textChild = HTMLFunctions.getChildByNodeName(e, AttributeNames.text); output.textClass = ElementExtension.gtGetInheritedAttributeString(e, AttributeNames.textClass); output.textStyle = ElementExtension.gtGetInheritedAttributeString(e, AttributeNames.textStyle); if (e.children.length > 0) { const tNodes = HTMLFunctions.getTNodes(e); if (tNodes != null) { tNodes.forEach((v) => v.remove()) output.text = tNodes; } } else if (e.innerHTML.length > 0) { output.text = e.innerHTML; } if (removeAttributes) { //e.removeAttribute(AttributeNames.text); e.removeAttribute(AttributeNames.className); e.removeAttribute(AttributeNames.textStyle); (e).style.removeProperty(StyleNames.autoSizeShapeToFitText); } return output; } */ public get svgText(): SVGTextElement { return this._svgText; } protected textObserverFunc: MutationCallback = (x: MutationRecord[]) => { if (!this.isLocated) return; let b = false; for (let i = 0; i < x.length; i++) { const p = x[i]; if (ZTextBox.updateTextAttributes.some((v) => v == p.attributeName)) { b = true; } if (p.attributeName == null) { b = true; } } if (b) { //this.resetUnstableCounter(); //this.update(); } }; get horizontalAnchor(): HorizontalAnchor { const b = ElementExtension.getPropertyStyleValueWithDefault(this.svgGroup, StyleNames.horizontalAnchor, "center"); return HorizontalAnchor.toHorizontalAnchor(b); } /** テキストの水平方向の配置設定を設定します。 */ set horizontalAnchor(value: HorizontalAnchor) { if (this.horizontalAnchor != value) ElementExtension.setPropertyStyleValue(this.svgGroup, StyleNames.horizontalAnchor, value); } /** テキストの垂直方向の配置設定を返します。 */ get verticalAnchor(): VerticalAnchor { const b = ElementExtension.getPropertyStyleValueWithDefault(this.svgGroup, StyleNames.verticalAnchor, "middle"); return VerticalAnchor.toVerticalAnchor(b); } /** テキストの垂直方向の配置設定を設定します。 */ set verticalAnchor(value: VerticalAnchor) { if (this.verticalAnchor != value) ElementExtension.setPropertyStyleValue(this.svgGroup, StyleNames.verticalAnchor, value); } /** * このVertexがテキストに合わせてサイズを変える場合Trueを返します。 */ get isAutoSizeShapeToFitText(): AutoSizeShapeToFitText { const b = ElementExtension.getPropertyStyleValueWithDefault(this.svgGroup, StyleNames.autoSizeShapeToFitText, ShapeToFitType.SemiAuto ); if (b == ShapeToFitType.Auto) { return ShapeToFitType.Auto; } else if (b == ShapeToFitType.SemiAuto) { return ShapeToFitType.SemiAuto; } else { return ShapeToFitType.None; } /* if (b == undefined) { return false; } else { return b; } */ } set isAutoSizeShapeToFitText(value: AutoSizeShapeToFitText) { ElementExtension.setPropertyStyleValue(this.svgGroup, StyleNames.autoSizeShapeToFitText, value); //this.svgGroup.setPropertyStyleValue(AttributeNames.Style.autoSizeShapeToFitText, value ? "true" : "false"); } protected updateStyleWithUpdateFlag(updateFlag: boolean): boolean { let b = false; const dashStyle = this.msoDashStyle; if (dashStyle != null && this.svgSurface != null) { if (updateFlag) { this.hasConnectedObserverFunction = false; b = updateAppropriateDashArray(this.svgSurface); this.hasConnectedObserverFunction = true; } else { b = getUpdateFlagAppropriateDashArray(this.svgSurface); } } return b; } /* protected getUpdateFlagOfStyle(): boolean { return this.updateStyleWithUpdateFlag(false); } protected updateStyle() : boolean { return this.updateStyleWithUpdateFlag(true); } */ protected updateSurfaceSizeWithUpdateFlag(withUpdate: boolean) { const region = this.getVirtualRegion(); let b = false; const widthRound100 = round100(region.width); if (!nearlyEqual(this.width, widthRound100)) { if (withUpdate) { this.width = widthRound100; } b = true; } const heightRound100 = round100(region.height); if (!nearlyEqual(this.height, heightRound100)) { if (withUpdate) { this.height = heightRound100; } //this.height = region.height; b = true; } return b; } /* protected updateTextLocationOrGetUpdateFlag(executeUpdate : boolean): boolean { return SVGTextExtension.up(this.svgText, this.getVirtualTextLocationRegion(), this.verticalAnchor, this.horizontalAnchor, this.isAutoSizeShapeToFitText); } */ protected updateTextLocation(): boolean { return SVGTextExtension.updateLocation(this.svgText, this.getVirtualTextLocationRegion(), this.verticalAnchor, this.horizontalAnchor, this.isAutoSizeShapeToFitText); } protected getUpdateFlagOfTextLocation(): boolean { return SVGTextExtension.getUpdateFlagOfLocation(this.svgText, this.getVirtualTextLocationRegion(), this.verticalAnchor, this.horizontalAnchor, this.isAutoSizeShapeToFitText); } protected updateSurfaceLocation(): boolean { return false; } protected getUpdateFlagOfSurfaceLocation(): boolean { return false; } op: number = 0; public getUpdateFlag(): boolean { const b1 = super.getUpdateFlag(); if (b1) { Debugger.updateFlagLog(this, this.getUpdateFlag, `${super.getUpdateFlag.name}`) return b1; } if (!this.isShown) return b1; if (this.svgText == null) { throw new TypeError("svgText is null"); } const b2: boolean = this.updateStyleWithUpdateFlag(false); if (b2) { Debugger.updateFlagLog(this, this.getUpdateFlag, `${this.updateStyleWithUpdateFlag.name}`) return b2; } const b3: boolean = this.getUpdateFlagOfTextLocation(); if (b3) { Debugger.updateFlagLog(this, this.getUpdateFlag, `${this.getUpdateFlagOfTextLocation.name}`) return b3; } const b4: boolean = this.updateSurfaceSizeWithUpdateFlag(false); if (b4) { Debugger.updateFlagLog(this, this.getUpdateFlag, `${this.updateSurfaceSizeWithUpdateFlag.name}`) return b3; } const b5: boolean = this.getUpdateFlagOfSurfaceLocation(); if (b4) { Debugger.updateFlagLog(this, this.getUpdateFlag, `${this.getUpdateFlagOfSurfaceLocation.name}`) return b3; } const b = b1 || b2 || b3 || b4 || b5; return b; } private updateSub(): void { super.update(); this._isUpdating = true; if (!this.isShown) return; //this._observer.disconnect(); this.hasConnectedObserverFunction = false; if (this.svgText == null) { throw new TypeError("svgText is null"); } this.updateStyleWithUpdateFlag(true); this.updateTextLocation(); this.updateSurfaceSizeWithUpdateFlag(true); this.updateSurfaceLocation(); this.updateObjectLocation(); //this.joint(); this._isUpdating = false; this.hasConnectedObserverFunction = true; } public update() { //let counter = 1; this.updateSub(); /* while(this.getUpdateFlag()){ if(counter > 10){ throw new Error("Update-Loop Error!"); } counter++; } */ //return counter > 1; } /* protected updateToFitText(isWidth: boolean) { //this.isFixTextSize = true; //const box = this.svgText.getBBox(); const textRect = SVGTextExtension.getSize(this.svgText); const textWidth = textRect.width < this._minimumWidth ? this._minimumWidth : textRect.width; const textHeight = textRect.height < this._minimumHeight ? this._minimumHeight : textRect.height; if (isWidth) { this.width = textWidth + this.marginPaddingLeft + this.marginPaddingRight; } else { this.height = textHeight + this.marginPaddingTop + this.marginPaddingBottom; } } */ get marginPaddingTop() { return SVGTextExtension.getMarginTop(this.svgText) + SVGElementExtension.getPaddingTop(this.svgGroup); } get marginPaddingLeft() { return SVGTextExtension.getMarginLeft(this.svgText) + SVGElementExtension.getPaddingLeft(this.svgGroup); } get marginPaddingRight() { return SVGTextExtension.getMarginRight(this.svgText) + SVGElementExtension.getPaddingRight(this.svgGroup); } get marginPaddingBottom() { return SVGTextExtension.getMarginBottom(this.svgText) + SVGElementExtension.getPaddingBottom(this.svgGroup); } get paddingTop(): number { return SVGElementExtension.getPaddingTop(this.svgGroup); } set paddingTop(value: number) { SVGElementExtension.setPaddingTop(this.svgGroup, value); } get paddingLeft(): number { return SVGElementExtension.getPaddingLeft(this.svgGroup); } set paddingLeft(value: number) { SVGElementExtension.setPaddingLeft(this.svgGroup, value); } get paddingRight(): number { return SVGElementExtension.getPaddingRight(this.svgGroup); } set paddingRight(value: number) { SVGElementExtension.setPaddingRight(this.svgGroup, value); } get paddingBottom(): number { return SVGElementExtension.getPaddingBottom(this.svgGroup); } set paddingBottom(value: number) { SVGElementExtension.setPaddingBottom(this.svgGroup, value); } get marginTop(): number { return SVGTextExtension.getMarginTop(this.svgText); } set marginTop(value: number) { SVGTextExtension.setMarginTop(this.svgText, value); } get marginLeft(): number { return SVGTextExtension.getMarginLeft(this.svgText); } set marginLeft(value: number) { SVGTextExtension.setMarginLeft(this.svgText, value); } get marginRight(): number { return SVGTextExtension.getMarginRight(this.svgText); } set marginRight(value: number) { SVGTextExtension.setMarginRight(this.svgText, value); } get marginBottom(): number { return SVGTextExtension.getMarginBottom(this.svgText); } set marginBottom(value: number) { SVGTextExtension.setMarginBottom(this.svgText, value); } /* private get innerRectangleWithoutMargin(): Rectangle { const rect = this.innerRectangle; rect.width = rect.width - this.marginPaddingLeft - this.marginPaddingRight; rect.height = rect.height - this.marginPaddingTop - this.marginPaddingBottom; rect.x = rect.x + this.marginPaddingLeft; rect.y = rect.y + this.marginPaddingTop; return rect; } */ /* get marginLeft(): number { return this.svgText.getPropertyStyleNumberValue("--margin-left", 0); } set marginLeft(value: number) { this.svgText.setPropertyStyleValue("--margin-left", value.toString()); } get marginTop(): number { return this.svgText.getPropertyStyleNumberValue("--margin-top", 0); } set marginTop(value: number) { this.svgText.setPropertyStyleValue("--margin-top", value.toString()); } */ public get svgElements(): SVGElement[] { const r: SVGElement[] = []; r.push(this.svgGroup); r.push(this.svgText); return r; } public hasDescendant(obj: SVGElement): boolean { const ids = this.svgElements.map((v) => v.getAttribute(AttributeNames.objectIDName)).filter((v) => v != null); const id = obj.getAttribute(AttributeNames.objectIDName); return ids.some((v) => v == id); } public get hasSize(): boolean { return true; } public get msoDashStyle(): msoDashStyle | null { if (this.svgSurface != null) { const dashStyle = ElementExtension.getPropertyStyleValue(this.svgSurface, StyleNames.msoDashStyleName); if (dashStyle != null) { return msoDashStyle.toMSODashStyle(dashStyle); } else { return null; } } else { return null; } } public set msoDashStyle(value: msoDashStyle | null) { if (this.svgSurface != null) { if (msoDashStyle == null) { this.svgSurface.style.removeProperty(StyleNames.msoDashStyleName); } else { ElementExtension.setPropertyStyleValue(this.svgSurface, StyleNames.msoDashStyleName, value); } } } public createVBACode(id: number): string[] { const lines: string[] = []; const backColor = VBATranslateFunctions.colorToVBA(ElementExtension.getPropertyStyleValueWithDefault(this.svgSurface!, "fill", "gray")); const visible = ElementExtension.getPropertyStyleValueWithDefault(this.svgSurface!, "visibility", "visible") == "visible" ? "msoTrue" : "msoFalse"; const vAnchor = VBATranslateFunctions.ToVerticalAnchor(this.verticalAnchor); const hAnchor = VBATranslateFunctions.ToHorizontalAnchor(this.horizontalAnchor); lines.push(`Sub create${id}(createdSlide As slide)`); lines.push(` Dim shapes_ As Shapes : Set shapes_ = createdSlide.Shapes`); lines.push(` Dim obj As Shape`); lines.push(` Set obj = shapes_.AddShape(${this.shape}, ${this.globalX}, ${this.globalY}, ${this.width}, ${this.height})`); lines.push(` Call EditTextFrame(obj.TextFrame, ${this.marginPaddingTop}, ${this.marginPaddingBottom}, ${this.marginPaddingLeft}, ${this.marginPaddingRight}, false, ppAutoSizeNone)`); lines.push(` Call EditAnchor(obj.TextFrame, ${vAnchor}, ${hAnchor})`); VBATranslateFunctions.TranslateSVGTextElement2(this.svgText, `obj.TextFrame.TextRange`).forEach((v) => lines.push(v)); lines.push(this.getVBAEditLine()); lines.push(` Call EditCallOut(obj, "${this.objectID}", ${visible}, ${backColor})`) this.VBAAdjustments.forEach((v, i) => { lines.push(` obj.Adjustments.Item(${i + 1}) = ${v}`); }) lines.push(`End Sub`); return lines; } public getVirtualWidth(): number { return this.getVirtualRegion().width; } public getVirtualHeight(): number { return this.getVirtualRegion().height; } getVirtualExtraRegion(): Rectangle { const textRect = getVirtualRegion(this.svgText); //const textRect = SVGTextExtension.getRegion(this.svgText); const w = textRect.width + this.leftExtraLength + this.rightExtraLength; const h = textRect.height + this.topExtraLength + this.bottomExtraLength; const x = -w / 2; const y = -h / 2; return new Rectangle(x, y, w, h); } getVirtualTextLocationRegion(): Rectangle { const rect = this.getVirtualRegion(); rect.x += this.leftExtraLength; rect.y += this.topExtraLength; rect.width -= this.leftExtraLength + this.rightExtraLength; rect.height -= this.topExtraLength + this.bottomExtraLength; return rect; /* const rect = this.ExtraRegion; const w = rect.width - this.leftExtraLength - this.rightExtraLength; const h = rect.height - this.topExtraLength - this.bottomExtraLength; const x =rect.x + this.leftExtraLength; const y = rect.y + this.topExtraLength; return new Rectangle(x, y, w, h); */ } public getVirtualRegion(): Rectangle { if (this.svgText === undefined) { throw new UndefinedError(); //return new Rectangle(this.cx, this.cy, 0, 0); } const marginRect = this.getVirtualExtraRegion(); if (this.isAutoSizeShapeToFitText == AutoSizeShapeToFitText.Auto) { return marginRect; } else if (this.isAutoSizeShapeToFitText == AutoSizeShapeToFitText.SemiAuto) { let [x, y] = [0, 0] let [newWidth, newHeight] = [0, 0] if (this.width < marginRect.width) { newWidth = marginRect.width; x = marginRect.x; } else { //const surface_x = this.svgSurface != null ? SVGElementExtension.getX(this.svgSurface) : 0; newWidth = this.width; x = -this.width / 2; } if (this.height < marginRect.height) { newHeight = marginRect.height; y = marginRect.y; } else { //const surface_y = this.svgSurface != null ? SVGElementExtension.getY(this.svgSurface) : 0; newHeight = this.height; y = -this.height / 2; } //const newWidth = this.width < width ? width : this.width; //const newHeigth = this.height < height ? height : this.height; return new Rectangle(round100(x), round100(y), round100(newWidth), round100(newHeight)); } else { return new Rectangle(- (this.width / 2), - (this.height / 2), this.width, this.height); //return new Rectangle(this.x, this.y, this.width, this.height); } } get topExtraLength(): number { return this.marginPaddingTop; } get leftExtraLength(): number { return this.marginPaddingLeft; } get rightExtraLength(): number { return this.marginPaddingRight; } get bottomExtraLength(): number { return this.marginPaddingBottom; } /* public get upperHeight() : number{ return this.topExtraLength - this.textRegion.y; } public get leftWidth() : number{ return this.leftExtraLength - this.textRegion.x; } */ } //}