2964 lines
114 KiB
JavaScript
2964 lines
114 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2023
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
(function(window, document)
|
|
{
|
|
// Import
|
|
let Shape_Type = window['AscVisio'].Shape_Type;
|
|
|
|
let isInvertCoords = true;
|
|
|
|
function convertVsdxTextToPptxText(text){
|
|
// Replace LineSeparator
|
|
return text.replaceAll("\u2028", "\n");
|
|
}
|
|
|
|
/**
|
|
* get full flip using group flips
|
|
* @return {{flipV: (boolean|*), flipH: (boolean|*)}}
|
|
*/
|
|
AscFormat.CGraphicObjectBase.prototype.getFullFlipVSpPr = function ()
|
|
{
|
|
let group = this.group;
|
|
let flipV = this.spPr.xfrm.flipV;
|
|
while (group) {
|
|
flipV = group.spPr.xfrm.flipV ? !flipV : flipV;
|
|
group = group.group;
|
|
}
|
|
return flipV;
|
|
};
|
|
|
|
/**
|
|
* calculateShapeParamsAndConvertToCShape or CGroupShape which combines shape and text if Shape has text
|
|
* @memberof Shape_Type
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {Page_Type} pageInfo
|
|
* @param {Number} drawingPageScale
|
|
* @param {CGroupShape?} currentGroupHandling
|
|
* @return {(CShape | CGroupShape)} cShape or cGroupShape (if shape and text)
|
|
*/
|
|
Shape_Type.prototype.convertShape = function (visioDocument,
|
|
pageInfo, drawingPageScale, currentGroupHandling) {
|
|
|
|
|
|
// Method start
|
|
|
|
// Refact:
|
|
// 1) I guess any cell can be = THEMEVAL() so better to always
|
|
// use Cell_Type.calculateValue method
|
|
// consider sometimes = THEMEVAL() can be replaced not to Themed but
|
|
// to concrete value on save for Cell_Type.v
|
|
// 2) May be create methods on rows sections and shape -
|
|
// this.calculateCellValue("FillBkgnd",this, pageInfo,
|
|
// visioDocument.themes, themeValWasUsedFor, true);
|
|
// 3) May be bind arguments to calculateValue function
|
|
// 4) May be move getTextCShape to other file
|
|
|
|
let maxHeightScaledIn;
|
|
if (currentGroupHandling) {
|
|
let heightMM = currentGroupHandling.spPr.xfrm.extY;
|
|
maxHeightScaledIn = heightMM / g_dKoef_in_to_mm;
|
|
} else {
|
|
let pageIndex = visioDocument.pages.page.indexOf(pageInfo);
|
|
maxHeightScaledIn = visioDocument.GetHeightScaledMM(pageIndex) / g_dKoef_in_to_mm;
|
|
}
|
|
|
|
// there was case with shape type group with no PinX and PinY
|
|
// https://disk.yandex.ru/d/tl877cuzcRcZYg
|
|
let pinX_inch = this.getCellNumberValueWithScale("PinX", drawingPageScale);
|
|
let pinY_inch = this.getCellNumberValueWithScale("PinY", drawingPageScale);
|
|
|
|
/** @type {{ [key: string]: Cell_Type }} */
|
|
let layerProperties = this.getLayerProperties(pageInfo);
|
|
// only if all shape layers are invisible shape is invisible
|
|
let areShapeLayersInvisible = layerProperties["Visible"] !== undefined && layerProperties["Visible"].v === "0";
|
|
|
|
/**
|
|
* @type {CUniFill | undefined}
|
|
* if layerColor is applied fill is always white
|
|
*/
|
|
let layerColor;
|
|
|
|
/**
|
|
* @type {CUniFill}
|
|
* if layerColor is applied fill is always white
|
|
*/
|
|
let layerFill = AscFormat.CreateUnfilFromRGB(255, 255, 255);
|
|
if (layerProperties["Color"] !== undefined && layerProperties["Color"].v !== "255") {
|
|
let layerColorUniColor = layerProperties["Color"].calculateValue(this, pageInfo,
|
|
visioDocument.themes);
|
|
layerColor = AscFormat.CreateUnfilFromRGB(layerColorUniColor.color.RGBA.R,
|
|
layerColorUniColor.color.RGBA.G, layerColorUniColor.color.RGBA.B);
|
|
}
|
|
|
|
let isShapeDeleted = this.del === "1" || this.del === true;
|
|
if (isShapeDeleted) {
|
|
return createEmptyShape();
|
|
}
|
|
|
|
|
|
// also check for {}, undefined, NaN, null
|
|
if (isNaN(pinX_inch) || pinX_inch === null || isNaN(pinY_inch) || pinY_inch === null ||
|
|
areShapeLayersInvisible) {
|
|
// AscCommon.consoleLog('pinX_inch or pinY_inch is NaN for Shape or areShapeLayersInvisible. Its ok sometimes. ' +
|
|
// 'Empty CShape is returned. See original shape: ', this);
|
|
// let's use empty shape
|
|
return createEmptyShape();
|
|
}
|
|
|
|
let shapeAngle = this.getCellNumberValue("Angle");
|
|
let locPinX_inch = this.getCellNumberValueWithScale("LocPinX", drawingPageScale);
|
|
let locPinY_inch = this.getCellNumberValueWithScale("LocPinY", drawingPageScale);
|
|
let shapeWidth_inch = this.getCellNumberValueWithScale("Width", drawingPageScale);
|
|
let shapeHeight_inch = this.getCellNumberValueWithScale("Height", drawingPageScale);
|
|
|
|
if (isInvertCoords) {
|
|
pinY_inch = maxHeightScaledIn - pinY_inch;
|
|
shapeAngle *= -1;
|
|
locPinY_inch = shapeHeight_inch - locPinY_inch;
|
|
}
|
|
|
|
// to rotate around point we: 1) move shape to new cords 2) rotate shape around center using shape angle
|
|
let newCords = getCordsRotatedAroundPoint(pinX_inch,
|
|
pinY_inch, locPinX_inch,
|
|
locPinY_inch, shapeWidth_inch, shapeHeight_inch, shapeAngle);
|
|
let x_inch = newCords[0];
|
|
let y_inch = newCords[1];
|
|
|
|
/**
|
|
* Fill without gradient used for handleQuickStyleVariation function and for handleTextQuickStyleVariation.
|
|
* We need fill without pattern and gradient applied. Pattern applied can set NoSolidFill object without color,
|
|
* so we will not be able to calculate handleVariationColor function result
|
|
* @type CUniFill
|
|
*/
|
|
let uniFillForegndNoGradient = null;
|
|
|
|
/**
|
|
* Used for handleQuickStyleVariation function and for handleTextQuickStyleVariation.
|
|
* @type CUniFill */
|
|
let lineUniFillNoGradient = null;
|
|
|
|
/**
|
|
* Final uniFill with gradient and pattern applied.
|
|
* Used for result shape fill.
|
|
* @type CUniFill */
|
|
let uniFillForegndWithPattern = null;
|
|
|
|
/**
|
|
* lineUniFill after gradient applied.
|
|
* Used for result shape stroke.
|
|
* @type {CUniFill}
|
|
*/
|
|
let lineUniFill = null;
|
|
|
|
if (layerColor) {
|
|
uniFillForegndWithPattern = layerFill;
|
|
uniFillForegndNoGradient = layerFill;
|
|
lineUniFillNoGradient = layerColor;
|
|
lineUniFill = layerColor;
|
|
} else {
|
|
/**
|
|
* @type boolean
|
|
*/
|
|
let fillGradientEnabled;
|
|
|
|
/**
|
|
* Fill without pattern applied. But with gradient applied.
|
|
* @type CUniFill */
|
|
let uniFillForegnd = null;
|
|
|
|
/** @type CUniFill */
|
|
let uniFillBkgnd = null;
|
|
|
|
|
|
/**
|
|
* Let's memorize what color properties used themeVal because quickStyleVariation can change only those
|
|
* color props that used themeVal function.
|
|
* @type {{lineUniFill: boolean, uniFillForegnd: boolean}}
|
|
*/
|
|
let themeValWasUsedFor = {
|
|
lineUniFill : false,
|
|
uniFillForegnd: false
|
|
}
|
|
|
|
uniFillForegndNoGradient = getForegroundNoGradient(this, pageInfo, visioDocument, themeValWasUsedFor);
|
|
|
|
uniFillBkgnd = getFillBackground(this, pageInfo, visioDocument, themeValWasUsedFor);
|
|
|
|
lineUniFillNoGradient = getLineColorNoGradient(this, pageInfo, visioDocument, themeValWasUsedFor);
|
|
|
|
// calculate variation before pattern bcs pattern can make NoFillUniFill object without color
|
|
// use quickStyleVariation only if themes exist in file.
|
|
// Default theme which come to visioDocument.themes[0] should not be considered.
|
|
// See bug https://bugzilla.onlyoffice.com/show_bug.cgi?id=76044
|
|
if (this.calculateColorThemeIndex(pageInfo) !== 0) {
|
|
let newFills = handleQuickStyleVariation(lineUniFillNoGradient, uniFillForegndNoGradient,
|
|
this, themeValWasUsedFor, pageInfo, visioDocument.themes);
|
|
uniFillForegndNoGradient = newFills[0];
|
|
lineUniFillNoGradient = newFills[1];
|
|
}
|
|
|
|
// FillGradientDir and FillPattern can tell about gradient type
|
|
// if FillGradient Enabled
|
|
// FillGradientDir defines gradient type. If gradient is linear gradient type is complemented with angle.
|
|
// 13 FillGradientDir is path. path cant be set in interface. also like some radial gradient types witch cant be set in interface.
|
|
// FillGradientDir > 13 is linear like FillGradientDir = 0
|
|
//
|
|
// if FillGradientEnabled Disabled
|
|
// FillPattern defines gradient type and colors define. There linear types with different predefined angles, rectandulat and radial gradient types.
|
|
// Rectangular and radial gradient types differs. There are two colors when i set three colors for gradient. Also FillPattern gradients are not listed in
|
|
// interface. Only true patterns.
|
|
//
|
|
// Its better to convert linear FillPattern gradients there. But FillPattern radial gradients seems to be
|
|
// not like FillGradientDir radial gradients but with different colors
|
|
|
|
let fillGradientEnabledCell = this.getCell("FillGradientEnabled");
|
|
if (fillGradientEnabledCell !== undefined) {
|
|
fillGradientEnabled = fillGradientEnabledCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes, undefined, true);
|
|
} else {
|
|
fillGradientEnabled = false;
|
|
}
|
|
|
|
if (fillGradientEnabled) {
|
|
uniFillForegnd = calculateGradient(this, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor, true);
|
|
} else {
|
|
uniFillForegnd = uniFillForegndNoGradient;
|
|
}
|
|
|
|
let lineGradientEnabled;
|
|
let lineGradientEnabledCell = this.getCell("LineGradientEnabled");
|
|
if (lineGradientEnabledCell !== undefined) {
|
|
lineGradientEnabled = lineGradientEnabledCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes);
|
|
} else {
|
|
lineGradientEnabled = false;
|
|
}
|
|
|
|
if (lineGradientEnabled) {
|
|
// Line gradient uniFill is not supported for now so let's take middle color from gradient
|
|
let lineGradientFill = calculateGradient(this, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor, false);
|
|
let colorIndex = Math.floor(lineGradientFill.fill.colors.length / 2);
|
|
let cUniColor = lineGradientFill.fill.colors[colorIndex].color;
|
|
|
|
// lineUniFill = lineGradientFill;
|
|
lineUniFill = new AscFormat.CUniFill();
|
|
lineUniFill.setFill(new AscFormat.CSolidFill());
|
|
lineUniFill.fill.setColor(cUniColor);
|
|
} else {
|
|
lineUniFill = lineUniFillNoGradient;
|
|
}
|
|
|
|
uniFillForegndWithPattern = getUnifillForegroundWithPattern(this, pageInfo,visioDocument,
|
|
themeValWasUsedFor, uniFillBkgnd, uniFillForegnd, fillGradientEnabled);
|
|
|
|
|
|
// Block end
|
|
// Used functions:
|
|
/**
|
|
* Calculate FillForegnd without gradient for handleQuickStyleVariation
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @return {CUniFill} uniFillForegndNoGradient
|
|
*/
|
|
function getForegroundNoGradient(shape, pageInfo, visioDocument, themeValWasUsedFor) {
|
|
const fillForegndCell = shape.getCell("FillForegnd");
|
|
let res;
|
|
if (fillForegndCell) {
|
|
// AscCommon.consoleLog("FillForegnd was found:", fillForegndCell);
|
|
res = fillForegndCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor, false);
|
|
|
|
const fillForegndTransValue = shape.getCellNumberValue("FillForegndTrans");
|
|
if (!isNaN(fillForegndTransValue)) {
|
|
/** @type {CSolidFill} */
|
|
const fillObj = res.fill;
|
|
fillObj.color.color.RGBA.A = fillObj.color.color.RGBA.A * (1 - fillForegndTransValue);
|
|
} else {
|
|
// AscCommon.consoleLog("fillForegndTrans value is themed or something. Not calculated for", this);
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("fillForegnd cell not found for", shape);
|
|
// try to get from theme
|
|
// uniFillForegnd = AscVisio.themeval(null, this, pageInfo, visioDocument.themes, "FillColor",
|
|
// undefined, fillGradientEnabled);
|
|
// just use white
|
|
res = AscFormat.CreateUnfilFromRGB(255, 255, 255);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Calculate FillBkgnd with gradient
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @return {CUniFill} uniFillBkgnd
|
|
*/
|
|
function getFillBackground(shape, pageInfo, visioDocument, themeValWasUsedFor) {
|
|
const fillBkgndCell = shape.getCell("FillBkgnd");
|
|
let res;
|
|
if (fillBkgndCell) {
|
|
// AscCommon.consoleLog("FillBkgnd was found:", fillBkgndCell);
|
|
res = fillBkgndCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor);
|
|
|
|
if (!(res.fill.type === Asc.c_oAscFill.FILL_TYPE_GRAD)) {
|
|
const fillBkgndTransValue = shape.getCellNumberValue("FillBkgndTrans");
|
|
if (!isNaN(fillBkgndTransValue)) {
|
|
/** @type {CSolidFill} */
|
|
const fillObj = res.fill;
|
|
fillObj.color.color.RGBA.A = fillObj.color.color.RGBA.A * (1 - fillBkgndTransValue);
|
|
} else {
|
|
// AscCommon.consoleLog("fillBkgndTrans value is themed or something. Not calculated for", this);
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Calculate LineColor no gradient
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @return {CUniFill} lineUniFillNoGradient
|
|
*/
|
|
function getLineColorNoGradient(shape, pageInfo, visioDocument, themeValWasUsedFor) {
|
|
const lineColorCell = shape.getCell("LineColor");
|
|
let res;
|
|
if (lineColorCell) {
|
|
// AscCommon.consoleLog("LineColor was found for shape", lineColorCell);
|
|
res = lineColorCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor, false);
|
|
|
|
const lineTransValue = shape.getCellNumberValue("LineColorTrans");
|
|
if (!isNaN(lineTransValue)) {
|
|
// lineUniFillNoGradient.transparent is opacity in fact
|
|
// setting RGBA.A doesn't work
|
|
res.transparent = 255 - lineTransValue * 255;
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("LineColor cell for line stroke (border) was not found painting dark");
|
|
res = AscFormat.CreateUnfilFromRGB(0, 0, 0);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Using shape data and params for calculations
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @param {CUniFill} uniFillBkgnd
|
|
* @param {CUniFill} uniFillForegnd
|
|
* @param {boolean} fillGradientEnabled
|
|
* @return {CUniFill} fill foreground with pattern applied
|
|
*/
|
|
function getUnifillForegroundWithPattern(shape, pageInfo, visioDocument,
|
|
themeValWasUsedFor, uniFillBkgnd, uniFillForegnd, fillGradientEnabled) {
|
|
const fillPatternTypeCell = shape.getCell("FillPattern");
|
|
const fillPatternType = fillPatternTypeCell ? fillPatternTypeCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes) : 1;
|
|
let res;
|
|
|
|
if (!isNaN(fillPatternType) && uniFillBkgnd && uniFillForegnd) {
|
|
// https://learn.microsoft.com/ru-ru/office/client-developer/visio/fillpattern-cell-fill-format-section
|
|
let isfillPatternTypeGradient = fillPatternType >= 25 && fillPatternType <= 40;
|
|
if (fillGradientEnabled) {
|
|
res = uniFillForegnd;
|
|
} else if (fillPatternType === 0) {
|
|
res = AscFormat.CreateNoFillUniFill();
|
|
} else if (fillPatternType === 1) {
|
|
// convert fill to solid
|
|
//
|
|
// FILL_TYPE_NONE never comes
|
|
// FILL_TYPE_BLIP - images are handled separate from fill
|
|
// FILL_TYPE_NOFILL never comes
|
|
// FILL_TYPE_SOLID is handled
|
|
// FILL_TYPE_GRAD if gradient is not enabled uniFillNoGradient is in uniFillForegnd
|
|
// which is solid otherwise fillGradientEnabled is true and above code run
|
|
// FILL_TYPE_PATT is handled
|
|
// FILL_TYPE_GRP empty fill
|
|
if (uniFillForegnd.fill.type === Asc.c_oAscFill.FILL_TYPE_PATT) {
|
|
res = new AscFormat.CUniFill();
|
|
res.fill = new AscFormat.CSolidFill();
|
|
res.fill.color = uniFillForegnd.fill.fgClr;
|
|
} else if (uniFillForegnd.fill.type === Asc.c_oAscFill.FILL_TYPE_SOLID) {
|
|
res = uniFillForegnd;
|
|
} else {
|
|
res = uniFillForegnd;
|
|
AscCommon.consoleLog("Unknown fill type. Need to convert to solid");
|
|
}
|
|
} else if (isfillPatternTypeGradient) {
|
|
if (fillPatternType === 25) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillForegnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillBkgnd.fill.color;
|
|
let pos2 = 100000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, 0);
|
|
} else if (fillPatternType === 26) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillBkgnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillForegnd.fill.color;
|
|
let pos2 = 50000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
let colorStop3 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color3 = uniFillBkgnd.fill.color;
|
|
let pos3 = 100000;
|
|
colorStop3.setColor(color3);
|
|
colorStop3.setPos(pos3);
|
|
fillGradientStops.push({Gs : colorStop3});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, 0);
|
|
} else if (fillPatternType === 27) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillBkgnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillForegnd.fill.color;
|
|
let pos2 = 100000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, 0);
|
|
} else if (fillPatternType === 28) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillForegnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillBkgnd.fill.color;
|
|
let pos2 = 100000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, 90 * AscFormat.degToC);
|
|
} else if (fillPatternType === 29) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillBkgnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillForegnd.fill.color;
|
|
let pos2 = 50000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
let colorStop3 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color3 = uniFillBkgnd.fill.color;
|
|
let pos3 = 100000;
|
|
colorStop3.setColor(color3);
|
|
colorStop3.setPos(pos3);
|
|
fillGradientStops.push({Gs : colorStop3});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, 90 * AscFormat.degToC);
|
|
} else if (fillPatternType === 30) {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillForegnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillBkgnd.fill.color;
|
|
let pos2 = 100000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
res = AscFormat.builder_CreateLinearGradient(fillGradientStops, -90 * AscFormat.degToC);
|
|
} else {
|
|
let fillGradientStops = [];
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop1 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color1 = uniFillForegnd.fill.color;
|
|
let pos1 = 0;
|
|
colorStop1.setColor(color1);
|
|
colorStop1.setPos(pos1);
|
|
fillGradientStops.push({Gs : colorStop1});
|
|
|
|
let colorStop2 = new AscFormat.CGs();
|
|
// calculate color (AscFormat.CUniColor)
|
|
let color2 = uniFillBkgnd.fill.color;
|
|
let pos2 = 100000;
|
|
colorStop2.setColor(color2);
|
|
colorStop2.setPos(pos2);
|
|
fillGradientStops.push({Gs : colorStop2});
|
|
|
|
res = AscFormat.builder_CreateRadialGradient(fillGradientStops);
|
|
}
|
|
} else if (fillPatternType > 1) {
|
|
// create patt fill using foregnd and bkgnd colors
|
|
const ooxmlFillPatternType = mapVisioFillPatternToOOXML(fillPatternType);
|
|
res = AscFormat.CreatePatternFillUniFill(ooxmlFillPatternType,
|
|
uniFillBkgnd.fill.color, uniFillForegnd.fill.color);
|
|
|
|
function mapVisioFillPatternToOOXML(fillPatternType) {
|
|
// change down to up and up to down bcs of Global matrix inverted
|
|
let upSideDownPatterns = false;
|
|
switch (fillPatternType) {
|
|
case 2:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["dnDiag"] :
|
|
AscCommon.global_hatch_offsets["upDiag"];
|
|
case 3:
|
|
return AscCommon.global_hatch_offsets["cross"];
|
|
case 4:
|
|
return AscCommon.global_hatch_offsets["diagCross"];
|
|
case 5:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["upDiag"] :
|
|
AscCommon.global_hatch_offsets["dnDiag"];
|
|
case 6:
|
|
return AscCommon.global_hatch_offsets["horz"];
|
|
case 7:
|
|
return AscCommon.global_hatch_offsets["vert"];
|
|
case 8:
|
|
return AscCommon.global_hatch_offsets["pct60"];
|
|
case 9:
|
|
return AscCommon.global_hatch_offsets["pct40"];
|
|
case 10:
|
|
return AscCommon.global_hatch_offsets["pct25"];
|
|
case 11:
|
|
return AscCommon.global_hatch_offsets["pct20"];
|
|
case 12:
|
|
return AscCommon.global_hatch_offsets["pct10"];
|
|
case 13:
|
|
return AscCommon.global_hatch_offsets["dkHorz"];
|
|
case 14:
|
|
return AscCommon.global_hatch_offsets["dkVert"];
|
|
case 15:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["dkUpDiag"] :
|
|
AscCommon.global_hatch_offsets["dkDnDiag"];
|
|
case 16:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["dkDnDiag"] :
|
|
AscCommon.global_hatch_offsets["dkUpDiag"];
|
|
case 17:
|
|
return AscCommon.global_hatch_offsets["smCheck"];
|
|
case 18:
|
|
return AscCommon.global_hatch_offsets["trellis"];
|
|
case 19:
|
|
return AscCommon.global_hatch_offsets["ltHorz"];
|
|
case 20:
|
|
return AscCommon.global_hatch_offsets["ltVert"];
|
|
case 21:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["ltUpDiag"] :
|
|
AscCommon.global_hatch_offsets["ltDnDiag"];
|
|
case 22:
|
|
return upSideDownPatterns ? AscCommon.global_hatch_offsets["ltDnDiag"] :
|
|
AscCommon.global_hatch_offsets["ltUpDiag"];
|
|
case 23:
|
|
return AscCommon.global_hatch_offsets["smGrid"];
|
|
case 24:
|
|
return AscCommon.global_hatch_offsets["pct50"];
|
|
default:
|
|
AscCommon.consoleLog("patten fill unhandled");
|
|
return AscCommon.global_hatch_offsets["cross"];
|
|
}
|
|
}
|
|
}
|
|
} else if (uniFillForegnd) {
|
|
res = uniFillForegnd;
|
|
} else {
|
|
AscCommon.consoleLog("FillForegnd not found for shape", shape);
|
|
res = AscFormat.CreateNoFillUniFill();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Get proper shape data and calculate fill or line gradient
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CTheme[]} visioDocumentThemes
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @param {boolean} isFillGradient - if not isFillGradient calculates line gradient
|
|
* @return {CUniFill} gradient fill
|
|
*/
|
|
function calculateGradient(shape, pageInfo, visioDocumentThemes,
|
|
themeValWasUsedFor, isFillGradient) {
|
|
/**
|
|
* @type {CUniFill | undefined}
|
|
*/
|
|
let returnValue;
|
|
|
|
let gradientDirCellName = isFillGradient ? "FillGradientDir" : "LineGradientDir";
|
|
|
|
let gradientDir = shape.getCellNumberValue(gradientDirCellName);
|
|
|
|
let invertGradient = false;
|
|
// global matrix transform: invert Y axis causes 0 is bottom of gradient and 100000 is top
|
|
// let invertGradient = !isInvertCoords;
|
|
// if (gradientDir === 3) {
|
|
// // radial gradient seems to be handled in another way
|
|
// invertGradient = isInvertCoords;
|
|
// }
|
|
|
|
// now let's come through gradient stops
|
|
let gradientStopsSectionName = isFillGradient ? "FillGradient" : "LineGradient";
|
|
let fillGradientStopsSection = shape.getSection(gradientStopsSectionName);
|
|
let rows = fillGradientStopsSection.getElements();
|
|
let fillGradientStops = [];
|
|
let prevPos = invertGradient ? 100000 : 0;
|
|
for (const rowKey in rows) {
|
|
let row = rows[rowKey];
|
|
if (row.del) {
|
|
continue;
|
|
}
|
|
|
|
// has color (CUniColor) and pos from 0 to 100000
|
|
let colorStop = new AscFormat.CGs();
|
|
|
|
// calculate color (CUniColor)
|
|
let color = new AscFormat.CUniColor();
|
|
let gradientStopColorCell = row.getCell("GradientStopColor");
|
|
color = gradientStopColorCell.calculateValue(shape, pageInfo,
|
|
visioDocumentThemes, themeValWasUsedFor, true, rowKey);
|
|
|
|
let gradientStopColorTransCell = row.getCell("GradientStopColorTrans");
|
|
let gradientStopColorTransValue = gradientStopColorTransCell.calculateValue(shape, pageInfo,
|
|
visioDocumentThemes, themeValWasUsedFor, true, rowKey);
|
|
color.RGBA.A = color.RGBA.A * (1 - gradientStopColorTransValue);
|
|
|
|
// now let's get pos
|
|
let gradientStopPositionCell = row.getCell("GradientStopPosition");
|
|
let pos = gradientStopPositionCell.calculateValue(shape, pageInfo,
|
|
visioDocumentThemes, undefined, true, rowKey);
|
|
pos = invertGradient ? 100000 - pos : pos;
|
|
|
|
// if new pos < prevPos break
|
|
if (!invertGradient && pos < prevPos || invertGradient && pos > prevPos) {
|
|
break;
|
|
}
|
|
prevPos = pos;
|
|
|
|
colorStop.setColor(color);
|
|
colorStop.setPos(pos);
|
|
|
|
fillGradientStops.push({Gs : colorStop});
|
|
|
|
if ((pos === 100000 && !invertGradient) || (invertGradient && pos === 0)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// gradientDir 0 is linear. FillGradientDir from 1 to 13 including are
|
|
// unhandled so paint them as radial. FillGradientDir from 14 including as same as FillGradientDir 0
|
|
if (gradientDir && gradientDir !== 0 && gradientDir < 14) {
|
|
// radial
|
|
returnValue = AscFormat.builder_CreateRadialGradient(fillGradientStops);
|
|
} else {
|
|
let gradientAngleCellName = isFillGradient ? "FillGradientAngle" : "LineGradientAngle";
|
|
let gradientAngleCell = shape.getCell(gradientAngleCellName);
|
|
// TODO handle multiple gradient types
|
|
let gradientAngle = gradientAngleCell.calculateValue(shape, pageInfo,
|
|
visioDocumentThemes);
|
|
|
|
returnValue = AscFormat.builder_CreateLinearGradient(fillGradientStops, gradientAngle);
|
|
}
|
|
return returnValue;
|
|
}
|
|
|
|
/**
|
|
* handle QuickStyleVariation cell which can change color (but only if color is a result of ThemeVal)
|
|
* cant be separated for unifill and stroke
|
|
* @param {CUniFill} lineUniFill stroke
|
|
* @param {CUniFill} fillUniFill
|
|
* @param {Shape_Type} shape
|
|
* @param {{lineUniFill: boolean, uniFillForegnd: boolean}} themeValWasUsedFor
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CTheme[]} themes
|
|
* @return {[]} [newFillUnifill, newLineUniFill]
|
|
*/
|
|
function handleQuickStyleVariation(lineUniFill, fillUniFill, shape, themeValWasUsedFor, pageInfo, themes) {
|
|
// https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-vsdx/68bb0221-d8a1-476e-a132-8c60a49cea63?redirectedfrom=MSDN
|
|
// consider "QuickStyleVariation" cell
|
|
// https://visualsignals.typepad.co.uk/vislog/2013/05/visio-2013-themes-in-the-shapesheet-part-2.html
|
|
|
|
let backgroundColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
let lineColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
let fillColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
|
|
// in quick style variation we need to consider fill.color not fill.color.color because
|
|
// fill.color consider mods applied. And we need to store new color to fill.color.color because
|
|
// fill.color is calculated in recalculate function from fill.color.color
|
|
|
|
// Calculate fill.color if it is not calculated
|
|
let theme = shape.getTheme(pageInfo, themes);
|
|
fillUniFill.fill.color.Calculate(theme);
|
|
lineUniFill.fill.color.Calculate(theme);
|
|
|
|
let lineColorRGBA = lineUniFill.fill && lineUniFill.fill.color && lineUniFill.fill.color.RGBA;
|
|
let fillColorRGBA = fillUniFill.fill && fillUniFill.fill.color && fillUniFill.fill.color.RGBA;
|
|
// let lineColorNoMods = lineUniFill.fill && lineUniFill.fill.color && lineUniFill.fill.color.color
|
|
// && lineUniFill.fill.color.color.RGBA;
|
|
// let fillColorNoMods = fillUniFill.fill && fillUniFill.fill.color && fillUniFill.fill.color.color
|
|
// && fillUniFill.fill.color.color.RGBA;
|
|
|
|
let newLineUniFill = new AscFormat.CUniFill();
|
|
newLineUniFill.fill = new AscFormat.CSolidFill();
|
|
newLineUniFill.fill.color = new AscFormat.CUniColor();
|
|
newLineUniFill.fill.color.color = new AscFormat.CRGBColor();
|
|
let newLineColorNoMods = newLineUniFill.fill.color.color;
|
|
// set defaults for new color
|
|
newLineColorNoMods.setColor(lineColorRGBA.R, lineColorRGBA.G, lineColorRGBA.B);
|
|
newLineColorNoMods.RGBA.A = lineColorRGBA.A;
|
|
newLineUniFill.transparent = lineUniFill.transparent;
|
|
|
|
|
|
let newFillUniFill = new AscFormat.CUniFill();
|
|
newFillUniFill.fill = new AscFormat.CSolidFill();
|
|
newFillUniFill.fill.color = new AscFormat.CUniColor();
|
|
newFillUniFill.fill.color.color = new AscFormat.CRGBColor();
|
|
let newFillColorNoMods = newFillUniFill.fill.color.color;
|
|
// set defaults for new color
|
|
newFillColorNoMods.setColor(fillColorRGBA.R, fillColorRGBA.G, fillColorRGBA.B);
|
|
newFillColorNoMods.RGBA.A = fillColorRGBA.A;
|
|
newFillUniFill.transparent = fillUniFill.transparent;
|
|
|
|
|
|
if (lineColorRGBA !== undefined && fillColorRGBA !== undefined) {
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(255, 255, 255, backgroundColorHSL);
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(lineColorRGBA.R, lineColorRGBA.G, lineColorRGBA.B, lineColorHSL);
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(fillColorRGBA.R, fillColorRGBA.G, fillColorRGBA.B, fillColorHSL);
|
|
|
|
// covert L to percents
|
|
backgroundColorHSL.L = backgroundColorHSL.L / 255 * 100;
|
|
lineColorHSL.L = lineColorHSL.L / 255 * 100;
|
|
fillColorHSL.L = fillColorHSL.L / 255 * 100;
|
|
|
|
let quickStyleVariationCell = shape.getCell("QuickStyleVariation");
|
|
if (quickStyleVariationCell) {
|
|
let quickStyleVariationCellValue = Number(quickStyleVariationCell.v);
|
|
if ((quickStyleVariationCellValue & 4) === 4 && themeValWasUsedFor.lineUniFill) {
|
|
// line color variation enabled (bit mask used)
|
|
if (Math.abs(backgroundColorHSL.L - lineColorHSL.L) < 16.66) {
|
|
if (backgroundColorHSL.L <= 72.92) {
|
|
// if background is dark set stroke to white
|
|
newLineColorNoMods.setColor(255, 255, 255);
|
|
newLineColorNoMods.RGBA.A = 255;
|
|
newLineUniFill.transparent = 255; // transparent is opacity in fact
|
|
} else {
|
|
if (Math.abs(backgroundColorHSL.L - fillColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - lineColorHSL.L)) {
|
|
// evaluation = THEMEVAL("FillColor")
|
|
newLineColorNoMods.setColor(fillColorRGBA.R, fillColorRGBA.G, fillColorRGBA.B);
|
|
newLineColorNoMods.RGBA.A = fillColorRGBA.A;
|
|
// transparency should not be considered
|
|
// newLineUniFill.transparent = fillUniFill.transparent;
|
|
} else {
|
|
// evaluation = THEMEVAL("LineColor") or not affected I guess
|
|
// get theme line color despite cell
|
|
// lineUniFillNoGradient = AscVisio.themeval(this.theme, shape, null, "LineColor");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((quickStyleVariationCellValue & 8) === 8 && themeValWasUsedFor.uniFillForegnd) {
|
|
// fill color variation enabled (bit mask used)
|
|
if (Math.abs(backgroundColorHSL.L - fillColorHSL.L) < 16.66) {
|
|
if (backgroundColorHSL.L <= 72.92) {
|
|
// if background is dark set stroke to white
|
|
newFillColorNoMods.setColor(255, 255, 255);
|
|
newFillColorNoMods.RGBA.A = 255;
|
|
newFillUniFill.transparent = 255; // transparent is opacity in fact
|
|
} else {
|
|
if (Math.abs(backgroundColorHSL.L - lineColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - fillColorHSL.L)) {
|
|
// evaluation = THEMEVAL("LineColor")
|
|
newFillColorNoMods.setColor(lineColorRGBA.R, lineColorRGBA.G, lineColorRGBA.B);
|
|
newFillColorNoMods.RGBA.A = lineColorRGBA.A;
|
|
// transparency should not be considered
|
|
// newFillUniFill.transparent = lineUniFill.transparent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if ((quickStyleVariationCellValue & 2) === 2) {
|
|
// // text color variation enabled (bit mask used)
|
|
// // Text color variation is realized in getTextCShape function handleTextQuickStyleVariation
|
|
// }
|
|
}
|
|
}
|
|
return [newFillUniFill, newLineUniFill];
|
|
}
|
|
}
|
|
|
|
let lineWidthEmu = getLineWidth(this, pageInfo, visioDocument);
|
|
|
|
// not scaling lineWidth
|
|
/** * @type {CLn} */
|
|
let oStroke = AscFormat.builder_CreateLine(lineWidthEmu, {UniFill: lineUniFill});
|
|
|
|
// seems to be unsupported for now
|
|
// see [MS-VSDX]-220215 (1) - 2.4.4.170 LineCap
|
|
let lineCap = getLineCap(this, pageInfo, visioDocument);
|
|
oStroke.setCap(lineCap);
|
|
|
|
let linePattern = getPresetDash(this, pageInfo, visioDocument, lineCap);
|
|
if (linePattern === 11 && oStroke.Fill) {
|
|
// 11 type is vsdx transparent
|
|
//todo реализовать прозрачный тип через отдельную настройку или разделить fill для линий и наконечников
|
|
//в vsdx может быть прозрачная линия с видимыми наконечниками
|
|
oStroke.Fill.fill = new AscFormat.CNoFill();
|
|
} else {
|
|
oStroke.setPrstDash(linePattern);
|
|
}
|
|
|
|
// Each geometry section may have arrows
|
|
// Arrow are displayed only if that geometry section has NoFill cell equal to 1
|
|
// For now as we set arrow for all the shape let's check first geometry only
|
|
let firstGeometrySection = this.getSection("Geometry_0");
|
|
if (firstGeometrySection && firstGeometrySection.getCellNumberValue("NoFill") === 1) {
|
|
let endArrowTypeCell = this.getCell("EndArrow");
|
|
let endArrowSizeCell = this.getCell("EndArrowSize");
|
|
let endArrowType = endArrowTypeCell ? endArrowTypeCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes) : 0;
|
|
let endArrowSize = endArrowSizeCell ? endArrowSizeCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes) : 1;
|
|
let endArrow = getEndArrow(endArrowType, endArrowSize);
|
|
oStroke.setTailEnd(endArrow);
|
|
|
|
let beginArrowTypeCell = this.getCell("BeginArrow");
|
|
let beginArrowSizeCell = this.getCell("BeginArrowSize");
|
|
let beginArrowType = beginArrowTypeCell ? beginArrowTypeCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes) : 0;
|
|
let beginArrowSize = beginArrowSizeCell ? beginArrowSizeCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes) : 1;
|
|
let beginArrow = getEndArrow(beginArrowType, beginArrowSize);
|
|
oStroke.setHeadEnd(beginArrow);
|
|
}
|
|
|
|
|
|
// apply flip props consider flip point: locPinX_inch or locPinY_inch
|
|
let flipHorizontally = this.getCellNumberValue("FlipX") === 1;
|
|
if (flipHorizontally) {
|
|
x_inch = x_inch + 2 * (locPinX_inch - shapeWidth_inch / 2);
|
|
}
|
|
|
|
let flipVertically = this.getCellNumberValue("FlipY") === 1;
|
|
if (flipVertically) {
|
|
y_inch = y_inch + 2 * (locPinY_inch - shapeHeight_inch / 2);
|
|
}
|
|
|
|
let x_mm = x_inch * g_dKoef_in_to_mm;
|
|
let y_mm = y_inch * g_dKoef_in_to_mm;
|
|
|
|
let shapeWidth_mm = shapeWidth_inch * g_dKoef_in_to_mm;
|
|
let shapeHeight_mm = shapeHeight_inch * g_dKoef_in_to_mm;
|
|
|
|
|
|
let cShape = this.convertToCShapeUsingParamsObj({
|
|
x_mm: x_mm, y_mm: y_mm,
|
|
w_mm: shapeWidth_mm, h_mm: shapeHeight_mm,
|
|
rot: shapeAngle,
|
|
oFill: uniFillForegndWithPattern, oStroke: oStroke,
|
|
flipHorizontally: flipHorizontally, flipVertically: flipVertically,
|
|
pageInfo: pageInfo,
|
|
cVisioDocument: visioDocument,
|
|
drawingPageScale : drawingPageScale,
|
|
isInvertCoords: isInvertCoords,
|
|
isShapeDeleted: isShapeDeleted,
|
|
id: this.id
|
|
});
|
|
|
|
|
|
// set shadow
|
|
// check shadow pattern
|
|
let shadowPatternCell = this.getCell("ShdwPattern");
|
|
let shadowPattern = shadowPatternCell ? shadowPatternCell.calculateValue(this, pageInfo,
|
|
visioDocument.themes) : 0;
|
|
let isShadowVisible = shadowPattern === 1;
|
|
|
|
if (isShadowVisible) {
|
|
let shadows = getShadows(this, pageInfo, visioDocument);
|
|
let outerShadow = shadows[0];
|
|
let innerShadow = shadows[1];
|
|
|
|
// cShape.spPr.changeShadow(shadow);
|
|
cShape.spPr.effectProps = new AscFormat.CEffectProperties();
|
|
|
|
// EffectDag (effect container) doesn't work for now
|
|
// cShape.spPr.effectProps.EffectDag = new AscFormat.CEffectContainer();
|
|
// cShape.spPr.effectProps.EffectDag.name = "1container";
|
|
// // cShape.spPr.effectProps.EffectDag.type = AscFormat.effectcontainertypeTree;
|
|
// // cShape.spPr.effectProps.EffectDag.type = AscFormat.effectcontainertypeSib;
|
|
// cShape.spPr.effectProps.EffectDag.effectList.push(shadow);
|
|
|
|
cShape.spPr.effectProps.EffectLst = new AscFormat.CEffectLst();
|
|
cShape.spPr.effectProps.EffectLst.outerShdw = outerShadow;
|
|
cShape.spPr.effectProps.EffectLst.innerShdw = innerShadow;
|
|
|
|
/**
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @return {[COuterShdw,CInnerShdw]} outer shadow and inner
|
|
*/
|
|
function getShadows(shape, pageInfo, visioDocument) {
|
|
/**
|
|
* @type {COuterShdw}
|
|
*/
|
|
let shadow = new AscFormat.COuterShdw();
|
|
|
|
let shadowColor = getShadowColor(shape, pageInfo, visioDocument);
|
|
shadow.color = shadowColor;
|
|
|
|
|
|
let shadowTypeCell = shape.getCell("ShapeShdwType");
|
|
// TODO check themed type. shadowTypeCell.calculateValue return undefined on THEMEVAL
|
|
// because there is an issue with visio THEMEVAL it sometimes return 0 sometimes 1 on empty effectStyleLst
|
|
// where shadow data should be
|
|
// see files: offsets shadow properties themeval type 1.vsdx and offsets shadow properties themeval type 0.vsdx
|
|
// in https://bugzilla.onlyoffice.com/show_bug.cgi?id=75884
|
|
let shadowType = shadowTypeCell && shadowTypeCell.calculateValue(shape, pageInfo,visioDocument.themes);
|
|
|
|
let shadowOffsetX_inch;
|
|
let shadowOffsetY_inch;
|
|
let shadowScaleX;
|
|
let shadowScaleY;
|
|
if (shadowType !== undefined && shadowType === 0) {
|
|
shadowOffsetX_inch = 0.0625;
|
|
shadowOffsetY_inch = -0.0625;
|
|
shadowScaleX = 1;
|
|
shadowScaleY = 1;
|
|
} else {
|
|
let shapeShdwScaleFactorCell = shape.getCell("ShapeShdwScaleFactor");
|
|
let shapeShdwScaleFactor = shapeShdwScaleFactorCell && shapeShdwScaleFactorCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes);
|
|
shadowScaleX = shapeShdwScaleFactor;
|
|
shadowScaleY = shapeShdwScaleFactor;
|
|
|
|
// set offsets for shadow
|
|
let shadowOffsetXcell = shape.getCell("ShapeShdwOffsetX");
|
|
if (shadowOffsetXcell) {
|
|
shadowOffsetX_inch = shadowOffsetXcell.calculateValue(shape, pageInfo, visioDocument.themes);
|
|
} else {
|
|
shadowOffsetX_inch = 0.125;
|
|
}
|
|
|
|
let shadowOffsetYcell = shape.getCell("ShapeShdwOffsetY");
|
|
if (shadowOffsetYcell) {
|
|
shadowOffsetY_inch = shadowOffsetYcell.calculateValue(shape, pageInfo, visioDocument.themes);
|
|
} else {
|
|
shadowOffsetY_inch = -0.125;
|
|
}
|
|
}
|
|
|
|
let shadowSx = shadowScaleX * 100000;
|
|
let shadowSy = shadowScaleY * 100000;
|
|
shadow.sx = shadowSx;
|
|
shadow.sy = shadowSy;
|
|
|
|
let shadowOffsetX = shadowOffsetX_inch === undefined ? 0 : shadowOffsetX_inch * g_dKoef_in_to_mm;
|
|
let shadowOffsetY = shadowOffsetY_inch === undefined ? 0 : shadowOffsetY_inch * g_dKoef_in_to_mm;
|
|
let atan = Math.atan2(shadowOffsetY, shadowOffsetX);
|
|
let emuDist = Math.hypot(shadowOffsetX, shadowOffsetY) * g_dKoef_mm_to_emu;
|
|
shadow.dist = emuDist;
|
|
// if true move to cord system where y goes down
|
|
if (isInvertCoords) {
|
|
atan = -atan;
|
|
}
|
|
let dirC = atan * AscFormat.radToDeg * AscFormat.degToC;
|
|
shadow.dir = dirC;
|
|
|
|
shadow.rotWithShape = true;
|
|
|
|
/**
|
|
* @type {CInnerShdw}
|
|
*/
|
|
let shadowInner = new AscFormat.CInnerShdw();
|
|
if (shadowType && shadowType === 3) {
|
|
shadowInner = new AscFormat.CInnerShdw();
|
|
shadowInner.color = shadowColor;
|
|
shadowInner.sx = shadowSx;
|
|
shadowInner.sy = shadowSy;
|
|
shadowInner.dist = emuDist;
|
|
shadowInner.dir = dirC;
|
|
shadowInner.rotWithShape = true;
|
|
}
|
|
|
|
return [shadow, shadowInner];
|
|
|
|
/**
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @return {CUniColor} shadow color
|
|
*/
|
|
function getShadowColor(shape, pageInfo, visioDocument) {
|
|
let shadowForegndCell = shape.getCell("ShdwForegnd");
|
|
let shadowColor;
|
|
if (shadowForegndCell) {
|
|
// AscCommon.consoleLog("FillForegnd was found:", fillForegndCell);
|
|
shadowColor = shadowForegndCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes);
|
|
|
|
let mainFillAlphaCoef = 1;
|
|
let fillForegndTransValue = shape.getCellNumberValue("FillForegndTrans");
|
|
if (!isNaN(fillForegndTransValue)) {
|
|
mainFillAlphaCoef = 1 - fillForegndTransValue;
|
|
}
|
|
|
|
let shadowTransValue = 0;
|
|
let shadowTransCell = shape.getCell("ShdwForegndTrans");
|
|
// if themed alpha is included in shadowColor already
|
|
if (shadowTransCell.getStringValue() !== "Themed") {
|
|
shadowTransValue = shadowTransCell.getNumberValue("ShdwForegndTrans");
|
|
|
|
let shadowAlpha = (1 - shadowTransValue) * mainFillAlphaCoef;
|
|
if (shadowAlpha !== 1) {
|
|
let oMod = new AscFormat.CColorMod("alpha", shadowAlpha * 100 * 1000 + 0.5 >> 0);
|
|
shadowColor.addColorMod(oMod);
|
|
}
|
|
} else {
|
|
// check if alpha is set already
|
|
let alphaMod = shadowColor.Mods && shadowColor.Mods.Mods.find(function (mod) {
|
|
return mod.name === "alpha";
|
|
});
|
|
if (alphaMod) {
|
|
alphaMod.val = alphaMod.val * mainFillAlphaCoef;
|
|
} else {
|
|
let oMod = new AscFormat.CColorMod("alpha", mainFillAlphaCoef * 100 * 1000 + 0.5 >> 0);
|
|
shadowColor.addColorMod(oMod);
|
|
}
|
|
}
|
|
} else {
|
|
// AscCommon.consoleLog("shadow foreground cell not found for", this);
|
|
shadowColor = AscFormat.CreateUniColorRGB(0,0,0);
|
|
}
|
|
return shadowColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// not scaling fontSize
|
|
let textCShape = getTextCShape(visioDocument.themes[0], this, cShape,
|
|
lineUniFillNoGradient, uniFillForegndNoGradient, drawingPageScale, maxHeightScaledIn,
|
|
visioDocument.pageIndex, visioDocument.pages.page.length, pageInfo, layerColor);
|
|
|
|
if (textCShape !== null) {
|
|
if (isShapeDeleted) {
|
|
textCShape.setBDeleted(true);
|
|
}
|
|
}
|
|
|
|
if (this.type === AscVisio.SHAPE_TYPES_FOREIGN) {
|
|
// AscCommon.consoleLog("Shape has type Foreign and may not be displayed. " +
|
|
// "Check shape.elements --> ForeignData_Type obj. See shape:", this);
|
|
|
|
let foreignDataObject = this.getForeignDataObject();
|
|
if (foreignDataObject) {
|
|
if (this.cImageShape !== null) {
|
|
setCImageShapeParams(this.cImageShape, cShape, this, shapeWidth_mm, shapeHeight_mm);
|
|
|
|
this.cImageShape.setParent2(visioDocument);
|
|
cShape = this.cImageShape;
|
|
|
|
/**
|
|
* changing cImageShape to set its size, position and other props from cShape
|
|
* @param cImageShape
|
|
* @param cShape
|
|
* @param shapeType
|
|
* @param shapeWidth_mm
|
|
* @param shapeHeight_mm
|
|
*/
|
|
function setCImageShapeParams(cImageShape, cShape, shapeType, shapeWidth_mm, shapeHeight_mm) {
|
|
// move some properties from shape to image. Then we will return cImageShape instead of cShape
|
|
cImageShape.setLocks(0);
|
|
cImageShape.setBDeleted(false);
|
|
cImageShape.setSpPr(cShape.spPr.createDuplicate());
|
|
cImageShape.spPr.setParent(cImageShape);
|
|
|
|
let imgWidth_inch = shapeType.getCellNumberValueWithScale("ImgWidth", drawingPageScale);
|
|
let imgHeight_inch = shapeType.getCellNumberValueWithScale("ImgHeight", drawingPageScale);
|
|
let imgOffsetX_inch = shapeType.getCellNumberValueWithScale("ImgOffsetX", drawingPageScale);
|
|
let imgOffsetY_inch = shapeType.getCellNumberValueWithScale("ImgOffsetY", drawingPageScale);
|
|
|
|
let imgWidth_mm = imgWidth_inch * g_dKoef_in_to_mm;
|
|
let imgHeight_mm = imgHeight_inch * g_dKoef_in_to_mm;
|
|
|
|
cImageShape.blipFill.srcRect = new AscFormat.CSrcRect();
|
|
let rect = cImageShape.blipFill.srcRect;
|
|
|
|
// add scale
|
|
if (imgWidth_inch !== undefined && imgHeight_inch !== undefined) {
|
|
let widthScale = imgWidth_mm / shapeWidth_mm;
|
|
let heightScale = imgHeight_mm / shapeHeight_mm;
|
|
// coords in our class CSrcRect is srcRect relative i.e. relative to original image size
|
|
// isInvertCoords check?
|
|
rect.setLTRB(0, 100 - 1/heightScale * 100, 1/widthScale * 100, 100);
|
|
}
|
|
// add horizontal shift
|
|
if (imgOffsetX_inch !== undefined) {
|
|
let imgOffsetX_mm = imgOffsetX_inch * g_dKoef_in_to_mm;
|
|
let offsetX = imgOffsetX_mm / imgWidth_mm;
|
|
rect.setLTRB(rect.l - offsetX * 100, rect.t, rect.r - offsetX * 100, rect.b);
|
|
}
|
|
// add vertical shift
|
|
if (imgOffsetY_inch !== undefined) {
|
|
let imgOffsetY_mm = imgOffsetY_inch * g_dKoef_in_to_mm;
|
|
let offsetY = imgOffsetY_mm / imgHeight_mm;
|
|
rect.setLTRB(rect.l, rect.t + offsetY * 100, rect.r, rect.b + offsetY * 100);
|
|
}
|
|
|
|
|
|
cImageShape.rot = cShape.rot;
|
|
// cImageShape.brush = cShape.brush;
|
|
cImageShape.bounds = cShape.bounds;
|
|
cImageShape.flipH = cShape.flipH;
|
|
cImageShape.flipV = cShape.flipV;
|
|
cImageShape.localTransform = cShape.localTransform;
|
|
// cImageShape.pen = cShape.pen;
|
|
cImageShape.Id = cShape.Id;
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("Unknown error: cImageShape was not initialized on ooxml parse");
|
|
}
|
|
}
|
|
}
|
|
|
|
// combine textCShape and geometryCShape to group
|
|
if (textCShape !== null) {
|
|
let groupShape = new AscFormat.CGroupShape();
|
|
// this.graphicObjectsController = new AscFormat.DrawingObjectsController();
|
|
// let groupShape = AscFormat.builder_CreateGroup();
|
|
|
|
groupShape.setLocks(0);
|
|
|
|
groupShape.setBDeleted(false);
|
|
|
|
// Create CGroupShape with SpPr from cShape but with no fill and line
|
|
let noLineFillSpPr = cShape.spPr.createDuplicate();
|
|
noLineFillSpPr.setFill(AscFormat.CreateNoFillUniFill());
|
|
noLineFillSpPr.setLn(AscFormat.CreateNoFillLine());
|
|
groupShape.setSpPr(noLineFillSpPr);
|
|
groupShape.spPr.setParent(groupShape);
|
|
// these props came to group
|
|
cShape.spPr.xfrm.rot = 0;
|
|
cShape.spPr.xfrm.flipV = false;
|
|
cShape.spPr.xfrm.flipH = false;
|
|
|
|
|
|
groupShape.brush = cShape.brush;
|
|
groupShape.bounds = cShape.bounds;
|
|
groupShape.localTransform = cShape.localTransform;
|
|
groupShape.pen = cShape.pen;
|
|
groupShape.Id = cShape.Id + "ShapeAndText";
|
|
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShape);
|
|
groupShape.spTree[groupShape.spTree.length - 1].setGroup(groupShape);
|
|
cShape.spPr.xfrm.setOffX(0);
|
|
cShape.spPr.xfrm.setOffY(0);
|
|
|
|
|
|
groupShape.addToSpTree(groupShape.spTree.length, textCShape);
|
|
groupShape.spTree[groupShape.spTree.length - 1].setGroup(groupShape);
|
|
textCShape.spPr.xfrm.setOffX(textCShape.spPr.xfrm.offX - groupShape.spPr.xfrm.offX);
|
|
textCShape.spPr.xfrm.setOffY(textCShape.spPr.xfrm.offY - groupShape.spPr.xfrm.offY);
|
|
textCShape.spPr.xfrm.flipH = false;
|
|
textCShape.spPr.xfrm.flipV = false;
|
|
|
|
// In power point presentations on flipV text position is flipped + text
|
|
// is mirrored horizontally and vertically (https://disk.yandex.ru/d/Hi8OCMITgb730Q)
|
|
// below we remove text mirror. In visio text is never mirrored. (https://disk.yandex.ru/d/JjbNzzZLDIAEuQ)
|
|
// (on flipH in power point presentation text is not mirrored)
|
|
let currentFlip = groupShape.spPr.xfrm.flipV;
|
|
let groupFlip = currentGroupHandling && currentGroupHandling.getFullFlipVSpPr();
|
|
let flip = groupFlip ? !currentFlip : currentFlip;
|
|
if (flip) {
|
|
textCShape.spPr.xfrm.setRot(Math.PI + textCShape.spPr.xfrm.rot);
|
|
}
|
|
|
|
groupShape.setParent2(visioDocument);
|
|
|
|
return groupShape;
|
|
} else {
|
|
return cShape;
|
|
}
|
|
|
|
// Method end
|
|
// Used functions:
|
|
// TODO import
|
|
/**
|
|
* Calculates coordinates after rotation using rotation point
|
|
* @param {number} rotatePointX_global
|
|
* @param {number} rotatePointY_global
|
|
* @param {number} rotatePointX_local
|
|
* @param {number} rotatePointY_local
|
|
* @param {number} shapeWidth
|
|
* @param {number} shapeHeight
|
|
* @param {number} angle - radians rotate clockwise. E.g. 30 degrees goes down.
|
|
* @return {[x: number, y: number]} returns left bottom corner coordinates.
|
|
*/
|
|
function getCordsRotatedAroundPoint(rotatePointX_global, rotatePointY_global,
|
|
rotatePointX_local, rotatePointY_local, shapeWidth, shapeHeight, angle) {
|
|
// to rotate around point we 1) add one more offset 2) rotate around center
|
|
// could be refactored maybe
|
|
// https://www.figma.com/design/SJSKMY5dGoAvRg75YnHpdX/newRotateScheme?node-id=0-1&node-type=canvas&t=UTtoZyLRItzaQvS9-0
|
|
let redVector = {x: -(rotatePointX_local - shapeWidth/2), y: -(rotatePointY_local - shapeHeight/2)};
|
|
// rotate antiClockWise by shapeAngle
|
|
let purpleVector = rotatePointAroundCordsStartClockWise(redVector.x, redVector.y, -angle);
|
|
let rotatedCenter = {x: rotatePointX_global + purpleVector.x, y: rotatePointY_global + purpleVector.y};
|
|
let turquoiseVector = {x: -shapeWidth/2, y: -shapeHeight/2};
|
|
let x = rotatedCenter.x + turquoiseVector.x;
|
|
let y = rotatedCenter.y + turquoiseVector.y;
|
|
return [x, y];
|
|
}
|
|
|
|
//TODO import
|
|
/**
|
|
* afin rotate clockwise
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @param {number} radiansRotateAngle radians Rotate clockwise Angle. E.g. 30 degrees rotates does DOWN.
|
|
* @returns {{x: number, y: number}} point
|
|
*/
|
|
function rotatePointAroundCordsStartClockWise(x, y, radiansRotateAngle) {
|
|
let newX = x * Math.cos(radiansRotateAngle) + y * Math.sin(radiansRotateAngle);
|
|
let newY = x * (-1) * Math.sin(radiansRotateAngle) + y * Math.cos(radiansRotateAngle);
|
|
return {x : newX, y: newY};
|
|
}
|
|
|
|
/**
|
|
* @param {CTheme} theme
|
|
* @param {Shape_Type} shape
|
|
* @param {CShape} cShape
|
|
* @param {CUniFill} lineUniFill - without gradient to handle text quickStyleVariation
|
|
* @param {CUniFill} fillUniFill - without gradient to handle text quickStyleVariation
|
|
* @param {number} drawingPageScale
|
|
* @param {number} maxHeightScaledIn
|
|
* @param {number} currentPageIndex
|
|
* @param {number} pagesCount
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CUniFill?} layerColor
|
|
* @return {CShape | null} return textCShape or null if no text
|
|
*/
|
|
function getTextCShape(theme, shape, cShape, lineUniFill,
|
|
fillUniFill, drawingPageScale, maxHeightScaledIn, currentPageIndex, pagesCount, pageInfo, layerColor) {
|
|
// see 2.2.8 Text [MS-VSDX]-220215
|
|
/**
|
|
* handle QuickStyleVariation cell which can change color (but only if color is a result of ThemeVal)
|
|
* @param textUniColor
|
|
* @param lineUniFill
|
|
* @param fillUniFill
|
|
* @param {{fontColor:boolean}} themeValWasUsedFor - sets during calculateCellValue
|
|
*/
|
|
function handleTextQuickStyleVariation(textUniColor, lineUniFill, fillUniFill, themeValWasUsedFor) {
|
|
// https://learn.microsoft.com/en-us/openspecs/sharepoint_protocols/ms-vsdx/68bb0221-d8a1-476e-a132-8c60a49cea63?redirectedfrom=MSDN
|
|
// consider "QuickStyleVariation" cell
|
|
// https://visualsignals.typepad.co.uk/vislog/2013/05/visio-2013-themes-in-the-shapesheet-part-2.html
|
|
|
|
// line and fill QuickStyleVariation are handled in handleQuickStyleVariation
|
|
|
|
if (!themeValWasUsedFor.fontColor) {
|
|
return;
|
|
}
|
|
|
|
let backgroundColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
let textColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
let lineColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
let fillColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
|
|
let textColorRGBA = textUniColor.color && textUniColor.color.RGBA;
|
|
let lineColorRGBA = lineUniFill.fill && lineUniFill.fill.color && lineUniFill.fill.color.color.RGBA;
|
|
let fillColorRGBA = fillUniFill.fill && fillUniFill.fill.color && fillUniFill.fill.color.color.RGBA;
|
|
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(255, 255, 255, backgroundColorHSL);
|
|
let compareWithOneColor = lineColorRGBA === undefined || fillColorRGBA === undefined;
|
|
if (lineColorRGBA !== undefined && fillColorRGBA !== undefined && textColorRGBA !== undefined) {
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(lineColorRGBA.R, lineColorRGBA.G, lineColorRGBA.B, lineColorHSL);
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(fillColorRGBA.R, fillColorRGBA.G, fillColorRGBA.B, fillColorHSL);
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(textColorRGBA.R, textColorRGBA.G, textColorRGBA.B, textColorHSL);
|
|
|
|
// covert L to percents
|
|
backgroundColorHSL.L = backgroundColorHSL.L / 255 * 100;
|
|
lineColorHSL.L = lineColorHSL.L / 255 * 100;
|
|
fillColorHSL.L = fillColorHSL.L / 255 * 100;
|
|
textColorHSL.L = textColorHSL.L / 255 * 100;
|
|
|
|
|
|
let quickStyleVariationCell = shape.getCell("QuickStyleVariation");
|
|
if (quickStyleVariationCell) {
|
|
let quickStyleVariationCellValue = Number(quickStyleVariationCell.v);
|
|
|
|
if ((quickStyleVariationCellValue & 2) === 2) {
|
|
// text color variation enabled (bit mask used)
|
|
|
|
// let fillPattern = shape.getCellNumberValue("FillPattern");
|
|
// if (fillPattern !== 0) {
|
|
// AscCommon.consoleLog("TextQuickStyleVariation for shapes with FillPattern !== 0 is disabled");
|
|
// // consider example https://disk.yandex.ru/d/2fbgXRrCBThlCw
|
|
// return;
|
|
// }
|
|
|
|
if (Math.abs(backgroundColorHSL.L - textColorHSL.L) < 16.66) {
|
|
if (backgroundColorHSL.L <= 72.92) {
|
|
// if background is dark set stroke to white
|
|
textColorRGBA.R = 255;
|
|
textColorRGBA.G = 255;
|
|
textColorRGBA.B = 255;
|
|
} else {
|
|
// return the color with the largest absolute difference in luminance from the
|
|
// formula evaluation of the "TextColor", "FillColor", and "LineColor"
|
|
let fillDifferenceIsTheLargest =
|
|
Math.abs(backgroundColorHSL.L - fillColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - lineColorHSL.L) &&
|
|
Math.abs(backgroundColorHSL.L - fillColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - textColorHSL.L);
|
|
if (fillDifferenceIsTheLargest) {
|
|
textColorRGBA.R = fillColorRGBA.R;
|
|
textColorRGBA.G = fillColorRGBA.G;
|
|
textColorRGBA.B = fillColorRGBA.B;
|
|
} else {
|
|
if (Math.abs(backgroundColorHSL.L - lineColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - textColorHSL.L)) {
|
|
textColorRGBA.R = lineColorRGBA.R;
|
|
textColorRGBA.G = lineColorRGBA.G;
|
|
textColorRGBA.B = lineColorRGBA.B;
|
|
} // else leave text color
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (compareWithOneColor) {
|
|
let compareColorRGBA = lineColorRGBA || fillColorRGBA;
|
|
let compareColorHSL = {H: undefined, S: undefined, L: undefined};
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(compareColorRGBA.R, compareColorRGBA.G, compareColorRGBA.B, compareColorHSL);
|
|
AscFormat.CColorModifiers.prototype.RGB2HSL(textColorRGBA.R, textColorRGBA.G, textColorRGBA.B, textColorHSL);
|
|
|
|
// covert L to percents
|
|
backgroundColorHSL.L = backgroundColorHSL.L / 255 * 100;
|
|
compareColorHSL.L = compareColorHSL.L / 255 * 100;
|
|
textColorHSL.L = textColorHSL.L / 255 * 100;
|
|
|
|
|
|
let quickStyleVariationCell = shape.getCell("QuickStyleVariation");
|
|
if (quickStyleVariationCell) {
|
|
let quickStyleVariationCellValue = Number(quickStyleVariationCell.v);
|
|
|
|
if ((quickStyleVariationCellValue & 2) === 2) {
|
|
// text color variation enabled (bit mask used)
|
|
|
|
// let fillPattern = shape.getCellNumberValue("FillPattern");
|
|
// if (fillPattern !== 0) {
|
|
// AscCommon.consoleLog("TextQuickStyleVariation for shapes with FillPattern !== 0 is disabled");
|
|
// // consider example https://disk.yandex.ru/d/2fbgXRrCBThlCw
|
|
// return;
|
|
// }
|
|
|
|
if (Math.abs(backgroundColorHSL.L - textColorHSL.L) < 16.66) {
|
|
if (backgroundColorHSL.L <= 72.92) {
|
|
// if background is dark set stroke to white
|
|
textColorRGBA.R = 255;
|
|
textColorRGBA.G = 255;
|
|
textColorRGBA.B = 255;
|
|
} else {
|
|
// return the color with the largest absolute difference in luminance from the
|
|
// formula evaluation of the "TextColor" and "FillColor" or "LineColor" i.e. compareColor
|
|
if (Math.abs(backgroundColorHSL.L - compareColorHSL.L) >
|
|
Math.abs(backgroundColorHSL.L - textColorHSL.L)) {
|
|
textColorRGBA.R = compareColorRGBA.R;
|
|
textColorRGBA.G = compareColorRGBA.G;
|
|
textColorRGBA.B = compareColorRGBA.B;
|
|
} // else leave text color
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Searches for pp element after passed element in textElements
|
|
* @param {[]} textElements - elements with pp tp cp and text with \n
|
|
* @param {number} currentIndex
|
|
* @param {string?} afterDropText - afterDropText without \n
|
|
* @param {boolean?} isNextElementLineDrop
|
|
* @return {number | undefined} row num
|
|
*/
|
|
function searchForPP(textElements, currentIndex, afterDropText, isNextElementLineDrop) {
|
|
if (afterDropText === undefined) {
|
|
afterDropText = "";
|
|
}
|
|
let afterNext = textElements[currentIndex + 2];
|
|
let next = textElements[currentIndex + 1];
|
|
// if there is something after \n
|
|
// also check for isNextElementLineDrop - means new paragraph start
|
|
if (afterDropText.length > 0 || isNextElementLineDrop) {
|
|
return undefined;
|
|
} else if (afterNext && afterNext.kind === AscVisio.c_oVsdxTextKind.PP) {
|
|
return afterNext.ix;
|
|
} else if (next && next.kind === AscVisio.c_oVsdxTextKind.PP) {
|
|
return next.ix;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* @param propsRowNum
|
|
* @param {?Section_Type} paragraphPropsCommon
|
|
* @param textCShape
|
|
*/
|
|
function parseParagraphAndAddToShapeContent(propsRowNum, paragraphPropsCommon, textCShape) {
|
|
if (paragraphPropsCommon === null || paragraphPropsCommon === undefined) {
|
|
AscCommon.consoleLog("paragraphPropsCommon is null or undefined. Creating default paragraph");
|
|
// create new paragraph to hold new properties
|
|
let oContent = textCShape.getDocContent();
|
|
let paragraph = new Paragraph(textCShape.getDrawingDocument(), true);
|
|
// Set defaultParagraph justify/align text - center
|
|
paragraph.Pr.SetJc(AscCommon.align_Center);
|
|
oContent.Content.push(paragraph);
|
|
paragraph.SetParent(oContent);
|
|
return;
|
|
}
|
|
let paragraphPropsFinal = propsRowNum !== null && paragraphPropsCommon.getRow(propsRowNum);
|
|
|
|
// handle horizontal align
|
|
|
|
// 0 Specifies that the defaultParagraph is left aligned.
|
|
// 1 Specifies that the defaultParagraph is centered.
|
|
// 2 Specifies that the defaultParagraph is right aligned.
|
|
// 3 Specifies that the defaultParagraph is justified.
|
|
// 4 Specifies that the defaultParagraph is distributed.
|
|
let hAlignCell = paragraphPropsFinal && paragraphPropsFinal.getCell("HorzAlign");
|
|
|
|
let horizontalAlign = AscCommon.align_Left;
|
|
if (hAlignCell && hAlignCell.kind === AscVisio.c_oVsdxSheetStorageKind.Cell_Type) {
|
|
// omit calculateCellValue here
|
|
// let fontColor = calculateCellValue(theme, shape, characterColorCell);
|
|
let horAlignTryParse = Number(hAlignCell.v);
|
|
if (!isNaN(horAlignTryParse)) {
|
|
switch (horAlignTryParse) {
|
|
case 0:
|
|
horizontalAlign = AscCommon.align_Left;
|
|
break;
|
|
case 1:
|
|
horizontalAlign = AscCommon.align_Center;
|
|
break;
|
|
case 2:
|
|
horizontalAlign = AscCommon.align_Right;
|
|
break;
|
|
case 3:
|
|
horizontalAlign = AscCommon.align_Justify;
|
|
break;
|
|
case 4:
|
|
horizontalAlign = AscCommon.align_Distributed;
|
|
break;
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("horizontal align was not parsed so default is set (left)");
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("horizontal align cell was not found so default is set (left)");
|
|
}
|
|
|
|
// handle bullet list
|
|
let bulletTypeCell = paragraphPropsFinal && paragraphPropsFinal.getCell("Bullet");
|
|
let bulletType;
|
|
if (bulletTypeCell) {
|
|
bulletType = bulletTypeCell.getNumberValue();
|
|
}
|
|
|
|
let bulletChar;
|
|
let bulletFont;
|
|
|
|
if (bulletType === 0) {
|
|
// none
|
|
} else if (bulletType === 1) {
|
|
bulletChar = "•";
|
|
bulletFont = "Symbol";
|
|
} else if (bulletType === 2) {
|
|
bulletChar = "◆";
|
|
bulletFont = "Courier New";
|
|
} else if (bulletType === 3) {
|
|
bulletChar = "▪";
|
|
bulletFont = "Wingdings";
|
|
} else if (bulletType === 4) {
|
|
bulletChar = "□";
|
|
bulletFont = "Wingdings";
|
|
} else if (bulletType === 5) {
|
|
bulletChar = "❖";
|
|
bulletFont = "Wingdings";
|
|
} else if (bulletType === 6) {
|
|
bulletChar = "➢";
|
|
bulletFont = "Wingdings";
|
|
} else if (bulletType === 7) {
|
|
bulletChar = "✓";
|
|
bulletFont = "Wingdings";
|
|
}
|
|
|
|
// handle left indentation
|
|
let indentationLeftCell = paragraphPropsFinal && paragraphPropsFinal.getCell("IndLeft");
|
|
let indentationLeft;
|
|
if (indentationLeftCell) {
|
|
indentationLeft = indentationLeftCell.getNumberValue() * AscCommonWord.g_dKoef_in_to_mm;
|
|
}
|
|
|
|
// handle first line indentation
|
|
let indentationFirstLineCell = paragraphPropsFinal && paragraphPropsFinal.getCell("IndFirst");
|
|
let indentationFirstLine;
|
|
if (indentationLeftCell) {
|
|
indentationFirstLine = indentationFirstLineCell.getNumberValue() * AscCommonWord.g_dKoef_in_to_mm;
|
|
}
|
|
|
|
|
|
// create new paragraph to hold new properties
|
|
let oContent = textCShape.getDocContent();
|
|
let paragraph = new Paragraph(textCShape.getDrawingDocument(), true);
|
|
|
|
// https://learn.microsoft.com/en-us/office/client-developer/visio/spline-cell-paragraph-section
|
|
// const lineSpacingVsdx = paragraphPropsFinal &&
|
|
// paragraphPropsFinal.getCellNumberValue("SpLine");
|
|
// let lineSpacing;
|
|
// if (lineSpacingVsdx < 0) {
|
|
// paragraph.Pr.Spacing.LineRule = window['Asc'].linerule_Auto;
|
|
// lineSpacing = -lineSpacingVsdx;
|
|
// } else if (lineSpacingVsdx === 0) {
|
|
// paragraph.Pr.Spacing.LineRule = window['Asc'].linerule_Auto;
|
|
// lineSpacing = 1;
|
|
// } else {
|
|
// paragraph.Pr.Spacing.LineRule = window['Asc'].linerule_Exact;
|
|
// lineSpacing = lineSpacingVsdx;
|
|
// }
|
|
// paragraph.Pr.Spacing.Line = lineSpacing;
|
|
|
|
|
|
// Set defaultParagraph justify/align text - center
|
|
paragraph.Pr.SetJc(horizontalAlign);
|
|
|
|
// // CPresentationBullet
|
|
if (bulletChar) {
|
|
paragraph.Pr.Bullet = new AscFormat.CBullet();
|
|
// smth wrong with Symbol font see:
|
|
// https://disk.yandex.ru/d/uNQ2eMfNyVtUFQ // https://disk.yandex.ru/i/2a0drnBXaVxJNw
|
|
// paragraph.Pr.Bullet.fillBulletFromCharAndFont(bulletChar, bulletFont);
|
|
paragraph.Pr.Bullet.fillBulletFromCharAndFont(bulletChar, "Arial");
|
|
}
|
|
// paragraph.PresentationPr.Bullet.m_nType = AscFormat.numbering_presentationnumfrmt_Blip;
|
|
//
|
|
// let Bullet = new AscFormat.CBullet();
|
|
// Bullet.bulletType = new AscFormat.CBulletType();
|
|
// Bullet.bulletType.type = AscFormat.BULLET_TYPE_BULLET_AUTONUM;
|
|
// paragraph.Add_PresentationNumbering(Bullet);
|
|
|
|
paragraph.Pr.Ind.Left = indentationLeft;
|
|
paragraph.Pr.Ind.FirstLine = indentationFirstLine;
|
|
|
|
oContent.Content.push(paragraph);
|
|
paragraph.SetParent(oContent);
|
|
|
|
// paragraph.Pr.Spacing.Before = 0;
|
|
// paragraph.Pr.Spacing.After = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses run props and set them
|
|
* @param characterRowNum
|
|
* @param {?Section_Type} characterPropsCommon
|
|
* @param {ParaRun | AscCommonWord.CPresentationField} oRun
|
|
* @param lineUniFill
|
|
* @param fillUniFill
|
|
* @param theme
|
|
* @param shape
|
|
* @param visioDocument
|
|
* @param {Page_Type} pageInfo
|
|
*/
|
|
function setRunProps(characterRowNum, characterPropsCommon, oRun, lineUniFill,
|
|
fillUniFill, theme, shape, visioDocument, pageInfo) {
|
|
let characterPropsFinal = characterRowNum !== null && characterPropsCommon.getRow(characterRowNum);
|
|
|
|
/**
|
|
* Let's memorize what color properties used themeVal because quickStyleVariation can change only those
|
|
* color props that used themeVal function.
|
|
* @type {{fontColor:boolean}}
|
|
*/
|
|
let themeValWasUsedFor = {
|
|
fontColor: false
|
|
}
|
|
|
|
|
|
// handle Color
|
|
let textColor;
|
|
if (layerColor !== undefined && layerColor !== null) {
|
|
textColor = new CDocumentColor(layerColor.fill.color.color.RGBA.R, layerColor.fill.color.color.RGBA.G,
|
|
layerColor.fill.color.color.RGBA.B, false);
|
|
} else {
|
|
let characterColorCell = characterPropsFinal && characterPropsFinal.getCell("Color");
|
|
let fontColor;
|
|
if (characterColorCell && characterColorCell.kind === AscVisio.c_oVsdxSheetStorageKind.Cell_Type) {
|
|
fontColor = characterColorCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes, themeValWasUsedFor);
|
|
} else {
|
|
AscCommon.consoleLog("text color cell not found! set text color as themed");
|
|
fontColor = AscVisio.themeval(null, shape, pageInfo, visioDocument.themes, "TextColor");
|
|
themeValWasUsedFor.fontColor = true;
|
|
}
|
|
handleTextQuickStyleVariation(fontColor, lineUniFill, fillUniFill, themeValWasUsedFor);
|
|
textColor = new CDocumentColor(fontColor.color.RGBA.R, fontColor.color.RGBA.G,
|
|
fontColor.color.RGBA.B, false);
|
|
}
|
|
|
|
oRun.Set_Color(textColor);
|
|
|
|
// handle lang
|
|
let oNewLang = new CLang();
|
|
let languageCell = characterPropsFinal && characterPropsFinal.getCell("LangID");
|
|
let languageId = languageCell ? Asc.g_oLcidNameToIdMap[languageCell.v] : 1033;
|
|
// switch (languageCell.v) {
|
|
// case "ru-RU":
|
|
// languageId = 1049;
|
|
// break;
|
|
// default:
|
|
// languageId = 1033;
|
|
// break;
|
|
// }
|
|
oNewLang.Val = languageId;
|
|
oRun.Set_Lang(oNewLang);
|
|
|
|
|
|
let fontSizeCell = characterPropsFinal && characterPropsFinal.getCell("Size");
|
|
let fontSizePt;
|
|
if (fontSizeCell && fontSizeCell.kind === AscVisio.c_oVsdxSheetStorageKind.Cell_Type) {
|
|
// omit calculateCellValue here
|
|
// let fontColor = calculateCellValue(theme, shape, characterColorCell);
|
|
const fontSizeIn = Number(fontSizeCell.v);
|
|
if (!isNaN(fontSizeIn)) {
|
|
fontSizePt = fontSizeIn * 72; // convert from in to pt
|
|
} else {
|
|
AscCommon.consoleLog("font size was not parsed so default is set (9 pt)");
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("font size was not found so default is set (9 pt)");
|
|
}
|
|
oRun.SetFontSize(fontSizePt);
|
|
|
|
|
|
// handle font
|
|
|
|
/**
|
|
* returns CRFont object for fontName. Also checks if font is loaded and if it is not
|
|
* uses default font from stylesheet "NoStyle". If stylesheet "NoStyle" font is not available uses Calibri.
|
|
* @param fontName
|
|
* @param {CVisioDocument} visioDocument
|
|
* @return {CRFonts}
|
|
*/
|
|
function getRFonts(fontName, visioDocument) {
|
|
let cRFonts = new CRFonts();
|
|
|
|
// see file https://disk.yandex.ru/d/AjpLrcamAzDeKg
|
|
// if themed font is not available in user PC visio sets default font from stylesheets
|
|
|
|
let loadedFonts = visioDocument.loadedFonts;
|
|
if (!fontName || loadedFonts.findIndex(function (cFont) {
|
|
return cFont.name === fontName;
|
|
}) === -1) {
|
|
AscCommon.consoleLog("Tried to use font that is not loaded: " + fontName + ". Loading Stylesheets font.");
|
|
let styleSheetsFont = visioDocument.styleSheets[0].getSection("Character").getRow(0).getCellStringValue("Font")
|
|
if (!styleSheetsFont || loadedFonts.findIndex(function (cFont) {
|
|
return cFont.name === styleSheetsFont;
|
|
}) === -1) {
|
|
AscCommon.consoleLog("Failed to use styleSheetsFont. Loading Calibri.");
|
|
cRFonts.Ascii = {Name: "Calibri", Index: 1};
|
|
cRFonts.HAnsi = {Name: "Calibri", Index: 1};
|
|
cRFonts.CS = {Name: "Calibri", Index: 1};
|
|
cRFonts.EastAsia = {Name: "Calibri", Index: 1};
|
|
} else {
|
|
AscCommon.consoleLog("Loaded styleSheetsFont: " + styleSheetsFont);
|
|
cRFonts.Ascii = {Name: styleSheetsFont, Index: 1};
|
|
cRFonts.HAnsi = {Name: styleSheetsFont, Index: 1};
|
|
cRFonts.CS = {Name: styleSheetsFont, Index: 1};
|
|
cRFonts.EastAsia = {Name: styleSheetsFont, Index: 1};
|
|
}
|
|
} else {
|
|
cRFonts.Ascii = {Name: fontName, Index: 1};
|
|
cRFonts.HAnsi = {Name: fontName, Index: 1};
|
|
cRFonts.CS = {Name: fontName, Index: 1};
|
|
cRFonts.EastAsia = {Name: fontName, Index: 1};
|
|
}
|
|
|
|
return cRFonts;
|
|
}
|
|
|
|
let fontCell = characterPropsFinal && characterPropsFinal.getCell("Font");
|
|
let cRFonts = new CRFonts();
|
|
if (fontCell && fontCell.kind === AscVisio.c_oVsdxSheetStorageKind.Cell_Type) {
|
|
// all document fonts all loaded already in CVisioDocument.prototype.loadFonts
|
|
|
|
let fontName = fontCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes, undefined, true);
|
|
|
|
cRFonts = getRFonts(fontName, visioDocument);
|
|
} else {
|
|
AscCommon.consoleLog("fontCell was not found so default is set (Calibri). Check mb AsianFont or ScriptFont");
|
|
}
|
|
oRun.Set_RFonts(cRFonts);
|
|
|
|
|
|
// handle style (bold italic underline small caps)
|
|
const styleVsdx = characterPropsFinal && characterPropsFinal.getCellStringValue("Style");
|
|
if (styleVsdx === "Themed") {
|
|
AscCommon.consoleLog("Themed style text is unhandled");
|
|
} else {
|
|
oRun.Pr.Bold = Boolean(Number(styleVsdx) & 1);
|
|
oRun.Pr.Italic = Boolean(Number(styleVsdx) & 2);
|
|
oRun.Pr.Underline = Boolean(Number(styleVsdx) & 4);
|
|
oRun.Pr.SmallCaps = Boolean(Number(styleVsdx) & 8);
|
|
}
|
|
|
|
// handle Strikethru
|
|
const strikeVsdx = characterPropsFinal && characterPropsFinal.getCellStringValue("Strikethru");
|
|
oRun.Pr.Strikeout = strikeVsdx === "1";
|
|
|
|
// handle DoubleStrikethrough
|
|
const doubleStrikeVsdx = characterPropsFinal && characterPropsFinal.getCellStringValue("DoubleStrikethrough");
|
|
oRun.Pr.DStrikeout = doubleStrikeVsdx === "1";
|
|
|
|
// handle Caps
|
|
const caseVsdx = characterPropsFinal && characterPropsFinal.getCellStringValue("Case");
|
|
oRun.Pr.Caps = caseVsdx === "1";
|
|
|
|
// handle VertAlign (doesn't work I don't know why)
|
|
const posVsdx = characterPropsFinal && characterPropsFinal.getCellStringValue("Pos");
|
|
if (posVsdx === "1") {
|
|
oRun.Pr.VertAlign = AscCommon.vertalign_SuperScript;
|
|
} else if (posVsdx === "2") {
|
|
oRun.Pr.VertAlign = AscCommon.vertalign_SubScript;
|
|
} else {
|
|
oRun.Pr.VertAlign = AscCommon.vertalign_Baseline;
|
|
}
|
|
}
|
|
|
|
function initPresentationField(oFld, fieldRow, isTextInherited) {
|
|
const valueCell = fieldRow.getCell("Value");
|
|
oFld.SetFieldType(valueCell.f);
|
|
oFld.vsdxFieldValue = valueCell;
|
|
// inits new class variable
|
|
oFld.isTextInherited = isTextInherited;
|
|
|
|
// then format it according to Format cell
|
|
oFld.vsdxFieldFormat = fieldRow.getCell("Format");
|
|
}
|
|
|
|
/**
|
|
* Get row from section Field and transform it into text usings its cells.
|
|
* @param {Row_Type} fieldRow
|
|
* @param {string} fldTagText
|
|
* @param {number} currentPageIndex
|
|
* @param {number} pagesCount
|
|
* @return {string} text
|
|
*/
|
|
function parseTextFromFieldRow(fieldRow, fldTagText, currentPageIndex, pagesCount) {
|
|
/**
|
|
* format inches to feet and inches with decimal fraction.
|
|
* @param inchesTotal
|
|
* @return {string}
|
|
*/
|
|
function formatInches(inchesTotal) {
|
|
const feet = Math.floor(inchesTotal / 12); // Calculate whole feet
|
|
const inches = inchesTotal % 12; // Remaining inches
|
|
|
|
// Get the whole inch part and the fractional part
|
|
const formatedInchesStr = formatDecimalFraction(inches);
|
|
|
|
// Combine feet, whole inches, and fractional inches into the final format
|
|
// return `${feet}' ${wholeInches}${fractionStr}"`;
|
|
return feet + "\' " + formatedInchesStr + "\"";
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param number
|
|
* @return {string}
|
|
*/
|
|
function formatDecimalFraction(number) {
|
|
// Get the whole inch part and the fractional part
|
|
const wholePart = Math.floor(number);
|
|
const fractionPart = number - wholePart;
|
|
|
|
// Find the best fraction with a denominator up to 9
|
|
let bestNumerator = 0;
|
|
let bestDenominator = 1;
|
|
let minDifference = Infinity;
|
|
|
|
for (let denominator = 1; denominator <= 9; denominator++) {
|
|
const numerator = Math.round(fractionPart * denominator);
|
|
const difference = Math.abs(fractionPart - numerator / denominator);
|
|
if (difference < minDifference) {
|
|
bestNumerator = numerator;
|
|
bestDenominator = denominator;
|
|
minDifference = difference;
|
|
}
|
|
}
|
|
|
|
// Format the fraction part if it's not zero
|
|
let fractionStr;
|
|
let wholePartAfterFractionHandle = wholePart;
|
|
if (bestNumerator > 0 && bestNumerator !== bestDenominator) {
|
|
fractionStr = " " + bestNumerator + "/" + bestDenominator;
|
|
} else if (bestDenominator === 0) {
|
|
fractionStr = "";
|
|
} else if (bestNumerator === bestDenominator) {
|
|
fractionStr = "";
|
|
wholePartAfterFractionHandle += 1;
|
|
}
|
|
|
|
return String(wholePartAfterFractionHandle) + fractionStr;
|
|
}
|
|
|
|
/**
|
|
* Example usage:
|
|
* console.log(formatDate("2023-11-23T11:19:36")); // Output: "23.11.2023 11:19:36"
|
|
* @param {string} dateString
|
|
* @return {string}
|
|
*/
|
|
function formatDate(dateString) {
|
|
// Create a Date object from the input string
|
|
const date = new Date(dateString);
|
|
|
|
// Extract day, month, year, hours, minutes, and seconds
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-based
|
|
const year = date.getFullYear();
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
|
|
// Format to "dd.mm.yyyy hh:mm:ss"
|
|
return day + '.' + month + '.' + year + ' ' + hours + ':' + minutes + ':' + seconds;
|
|
}
|
|
|
|
/**
|
|
* For example, 41879 corresponds to 8/28/2014.
|
|
* @param {string} serial
|
|
* @return {string}
|
|
*/
|
|
function excelSerialToDate(serial) {
|
|
// Excel date serials start from 1900-01-01, so calculate the base date
|
|
const baseDate = new Date(1900, 0, 1); // January 1, 1900
|
|
const serialNum = Number(serial);
|
|
|
|
// Adjust for Excel's incorrect leap year handling (Excel includes 29th February 1900)
|
|
const adjustedSerial = serialNum - 1;
|
|
|
|
// Add the number of days represented by the serial number
|
|
baseDate.setDate(baseDate.getDate() + adjustedSerial);
|
|
|
|
// Format the date as "m/d/yyyy"
|
|
const month = baseDate.getMonth() + 1;
|
|
const day = baseDate.getDate();
|
|
const year = baseDate.getFullYear();
|
|
|
|
return month + '/' + day + '/' + year;
|
|
}
|
|
|
|
const valueCell = fieldRow.getCell("Value");
|
|
// const valueFunction = valueCell.f;
|
|
const valueV = valueCell.v;
|
|
const valueUnits = valueCell.u;
|
|
|
|
// let's not use formula (valueCell.f) for now
|
|
// first convert value (valueCell.v) which is inches by default to units set in valueCell.u
|
|
|
|
/**
|
|
* @type {(number|string)}
|
|
*/
|
|
let valueInProperUnits;
|
|
if (valueUnits === "CM") {
|
|
valueInProperUnits = Number(valueV) * g_dKoef_in_to_mm / 10;
|
|
} else if (valueUnits === "MM") {
|
|
valueInProperUnits = Number(valueV) * g_dKoef_in_to_mm;
|
|
} else if (valueUnits === "DATE") {
|
|
valueInProperUnits = formatDate(valueV);
|
|
} else {
|
|
valueInProperUnits = valueV;
|
|
}
|
|
|
|
// then format it according to Format cell
|
|
const formatCell = fieldRow.getCell("Format");
|
|
const formatValue = formatCell.v;
|
|
let formatedString;
|
|
if (formatValue === "esc(13)") {
|
|
// take original value in inches and return feet + inches
|
|
formatedString = formatInches(valueV);
|
|
} else if (formatValue === "esc(15)") {
|
|
// take original value despite of units convert fractional part to decimal fraction
|
|
formatedString = formatDecimalFraction(valueInProperUnits);
|
|
} else if (formatValue === "T") {
|
|
// take time only
|
|
// formatValue === "T" is N='Format' F='FIELDPICTURE(30)'
|
|
formatedString = valueInProperUnits.split(" ")[1];
|
|
} else if (formatValue === "ddddd") {
|
|
// take date only
|
|
// formatValue === "ddddd" is N='Format' F='FIELDPICTURE(20)'
|
|
formatedString = valueInProperUnits.split(" ")[0];
|
|
} else if (formatValue === "{{M/d/yyyy}}") {
|
|
formatedString = excelSerialToDate(valueInProperUnits);
|
|
} else {
|
|
formatedString = valueInProperUnits;
|
|
}
|
|
|
|
return formatedString;
|
|
|
|
|
|
// if (valueFunction === "PAGENUMBER()") {
|
|
// return String(currentPageIndex);
|
|
// } else if (valueFunction === "PAGECOUNT()") {
|
|
// return String(pagesCount);
|
|
// }
|
|
|
|
// return valueV ? valueV : fldTagText;
|
|
}
|
|
|
|
|
|
let textElement = shape.getTextElement();
|
|
if (!textElement) {
|
|
return null;
|
|
}
|
|
|
|
// see https://disk.yandex.ru/d/xy2yxhAQHlUHsA shape with number-text has HideText cell v=1 and when we
|
|
// open file and display all unit numbers
|
|
// like number-text in that file these numbers can overlap like there https://disk.yandex.ru/d/G_GaAB2yH9OMDg
|
|
if (shape.getCellNumberValue("HideText") === 1) {
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* text shape saves text only no fill and no line. it goes along with CShape with the same id + "Text".
|
|
* It has not local coordinates but the same cord system like shape.
|
|
* @type {CShape}
|
|
*/
|
|
let textCShape = new AscFormat.CShape();
|
|
textCShape.Id = cShape.Id + "Text";
|
|
textCShape.setParent(visioDocument);
|
|
|
|
// set default settings
|
|
// see sdkjs/common/Drawings/CommonController.js createTextArt: function (nStyle, bWord, wsModel, sStartString)
|
|
// for examples
|
|
// https://api.onlyoffice.com/docbuilder/textdocumentapi just some related info
|
|
let bWord = false;
|
|
textCShape.setWordShape(bWord);
|
|
textCShape.setBDeleted(false);
|
|
if (bWord) {
|
|
textCShape.createTextBoxContent();
|
|
} else {
|
|
textCShape.createTextBody();
|
|
}
|
|
textCShape.setVerticalAlign(1); // sets text vert align center. equal to anchor set to txBody bodyPr
|
|
textCShape.bSelectedText = false;
|
|
|
|
|
|
// instead of AscFormat.AddToContentFromString(oContent, sText);
|
|
// use https://api.onlyoffice.com/docbuilder/presentationapi/apishape api implementation code
|
|
// to work with text separated into ParaRuns to split properties use
|
|
|
|
// read propsCommonObjects
|
|
let characterPropsCommon = shape.getSection("Character");
|
|
let paragraphPropsCommon = shape.getSection("Paragraph");
|
|
let fieldPropsCommon = shape.getSection("Field");
|
|
|
|
// to store last entries of cp/pp/tp like
|
|
// <cp IX='0'/> or
|
|
// <tp IX='0'/>
|
|
// character properties are used until another element specifies new character properties.
|
|
// TODO tp_Type is not parsed?
|
|
let propsCP = null;
|
|
let propsTP = null;
|
|
let currentParagraphPropsRow;
|
|
let currentParagraph;
|
|
|
|
let oContent = textCShape.getDocContent();
|
|
oContent.Content = [];
|
|
|
|
/**
|
|
* if text is inherited so we consider that text fields in it have wrong values
|
|
* and we recalculate values them
|
|
*/
|
|
const isTextInherited = textElement.isInherited;
|
|
|
|
// UPD: now with binary file read \r\n in original is replaced with \n! Focus is made for binary \n
|
|
|
|
// read text:
|
|
// consider CRLF (\r\n) (UPD: \n for binary read) as new paragraph start.
|
|
// Right after CRLF visio searches for pp
|
|
// which will be properties for new paragraph.
|
|
// (Or if it is something after CRLF it doesn't search for pp. E.g. if pp comes in text, it ignores)
|
|
|
|
textElement.elements.forEach(function(textElementPart, i) {
|
|
// init currentParagraph: if there is text in first element use default paragraph with
|
|
// properties from 0 paragraph props row
|
|
// otherwise search for pp tag witch can set paragraph properties for future text
|
|
if (i === 0) {
|
|
if (typeof textElementPart === "string" || textElementPart.kind === AscVisio.c_oVsdxTextKind.FLD) {
|
|
currentParagraphPropsRow = 0;
|
|
} else {
|
|
currentParagraphPropsRow = searchForPP(textElement.elements, i);
|
|
currentParagraphPropsRow = currentParagraphPropsRow === undefined ? 0 : currentParagraphPropsRow;
|
|
}
|
|
parseParagraphAndAddToShapeContent(currentParagraphPropsRow,
|
|
paragraphPropsCommon, textCShape);
|
|
currentParagraph = oContent.Content.slice(-1)[0]; // last paragraph
|
|
}
|
|
|
|
// visio set extra \r\n at the end of each Text tag: see fix below.
|
|
// UPD: fix below works for both binary read with \n and xml with \r\n
|
|
if (i === textElement.elements.length - 1) {
|
|
if (typeof textElementPart === "string") {
|
|
if (textElementPart.endsWith("\r\n")) {
|
|
textElementPart = textElementPart.slice(0, textElementPart.length - 2);
|
|
} else if (textElementPart.endsWith("\n")) {
|
|
textElementPart = textElementPart.slice(0, textElementPart.length - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (typeof textElementPart === "string" || textElementPart.kind === AscVisio.c_oVsdxTextKind.FLD) {
|
|
if (typeof textElementPart === "string") {
|
|
// Split on CRLF, or LF
|
|
let textArr = textElementPart.split(/\r\n|\n/);
|
|
|
|
for (let j = 0; j < textArr.length; j++) {
|
|
let text = textArr[j];
|
|
|
|
// if j > 0 CR exists in textArr and should be handled as new paragraph start
|
|
if (j > 0) {
|
|
// instead of lineDrop we get "" after split();
|
|
let isNextElementLineDrop = textArr[j + 1] === "";
|
|
let nextPP = searchForPP(textElement.elements, i, text, isNextElementLineDrop);
|
|
currentParagraphPropsRow = nextPP ? nextPP : currentParagraphPropsRow;
|
|
|
|
parseParagraphAndAddToShapeContent(currentParagraphPropsRow,
|
|
paragraphPropsCommon, textCShape);
|
|
currentParagraph = oContent.Content.slice(-1)[0]; // last paragraph
|
|
}
|
|
|
|
// equal to ApiParagraph.prototype.AddText method
|
|
let oRun = new ParaRun(currentParagraph, false);
|
|
let textWithLineDrops = convertVsdxTextToPptxText(text);
|
|
oRun.AddText(textWithLineDrops);
|
|
|
|
// check character properties: get cp_Type object and in characterPropsCommon get needed Row
|
|
let characterRowNum = propsCP && propsCP.ix;
|
|
if (propsCP === null) {
|
|
characterRowNum = 0;
|
|
}
|
|
|
|
// setup Run props
|
|
setRunProps(characterRowNum, characterPropsCommon,
|
|
oRun, lineUniFill, fillUniFill, theme, shape,
|
|
visioDocument, pageInfo);
|
|
currentParagraph.Add_ToContent(currentParagraph.Content.length - 1, oRun);
|
|
}
|
|
} else if (textElementPart.kind === AscVisio.c_oVsdxTextKind.FLD) {
|
|
// text field
|
|
|
|
let oFld = new AscCommonWord.CPresentationField(currentParagraph);
|
|
let fieldRowNum = textElementPart.ix;
|
|
let fieldPropsFinal = fieldRowNum !== null && fieldPropsCommon.getRow(fieldRowNum);
|
|
initPresentationField(oFld, fieldPropsFinal, isTextInherited);
|
|
|
|
let fldTagText = textElementPart.value;
|
|
if (fldTagText) {
|
|
fldTagText = convertVsdxTextToPptxText(fldTagText);
|
|
}
|
|
oFld.CanAddToContent = true;
|
|
oFld.AddText(fldTagText, -1);
|
|
oFld.CanAddToContent = false;
|
|
|
|
// setup Run
|
|
// check character properties: get cp_Type object and in characterPropsCommon get needed Row
|
|
let characterRowNum = propsCP && propsCP.ix;
|
|
if (propsCP === null) {
|
|
characterRowNum = 0;
|
|
}
|
|
|
|
setRunProps(characterRowNum, characterPropsCommon,
|
|
oFld, lineUniFill, fillUniFill, theme, shape,
|
|
visioDocument, pageInfo);
|
|
|
|
currentParagraph.AddToContent(currentParagraph.Content.length - 1, new ParaRun(currentParagraph, false));
|
|
currentParagraph.AddToContent(currentParagraph.Content.length - 1, oFld);
|
|
currentParagraph.AddToContent(currentParagraph.Content.length - 1, new ParaRun(currentParagraph, false));
|
|
}
|
|
} else if (textElementPart.kind === AscVisio.c_oVsdxTextKind.PP) {
|
|
// search for pp only after CRLF and in the beginning of text element
|
|
|
|
// currentParagraphPropsRow = textElementPart.ix;
|
|
// parseParagraphAndAddToShapeContent(currentParagraphPropsRow, paragraphPropsCommon, textCShape);
|
|
|
|
} else if (textElementPart.kind === AscVisio.c_oVsdxTextKind.CP) {
|
|
propsCP = textElementPart;
|
|
} else if (textElementPart.kind === AscVisio.c_oVsdxTextKind.TP) {
|
|
propsTP = textElementPart;
|
|
} else {
|
|
AscCommon.consoleLog("unknown type in text tag");
|
|
}
|
|
});
|
|
|
|
// create defaultParagraph if no strings found
|
|
if (oContent.Content.length === 0) {
|
|
// create defaultParagraph
|
|
parseParagraphAndAddToShapeContent(0, paragraphPropsCommon, textCShape);
|
|
}
|
|
|
|
|
|
// handle horizontal align i. e. defaultParagraph align
|
|
|
|
// handle vertical align
|
|
let verticalAlignCell = shape.getCell("VerticalAlign");
|
|
if (verticalAlignCell) {
|
|
// 0 - top, 1 - middle, 2 - bottom
|
|
let verticalAlign = Number(verticalAlignCell.v);
|
|
if (!isNaN(verticalAlign)) {
|
|
// 0 - bottom, 1, 2, 3 - ctr, 4, - top
|
|
// but baseMatrix transformations changes values to
|
|
// 0 - top, 1, 2, 3 - center, 4 - bottom
|
|
// NO REVERT NOW TOP IS TOP BOTTOM IS BOTTOM
|
|
if (verticalAlign === 0) {
|
|
textCShape.setVerticalAlign(4); // sets text vert align center equal to anchor set to txBody bodyPr
|
|
} else if (verticalAlign === 2) {
|
|
textCShape.setVerticalAlign(0); // sets text vert align center equal to anchor set to txBody bodyPr
|
|
}
|
|
// else leave center align
|
|
} else {
|
|
AscCommon.consoleLog("vertical align cell was not parsed for shape. align set to center. Shape:", shape);
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("vertical align cell was not found for shape. align set to center. Shape:", shape);
|
|
}
|
|
|
|
|
|
// setup text properties
|
|
let oTextPr;
|
|
oTextPr = new CTextPr();
|
|
// i dont know why but when i set font size not for overall shape but for runs text shifts to the top
|
|
// oTextPr.FontSize = nFontSize;
|
|
// oTextPr.RFonts.Ascii = {Name: "Calibri", Index: -1};
|
|
// oTextPr.RFonts.HAnsi = {Name: "Calibri", Index: -1};
|
|
// oTextPr.RFonts.CS = {Name: "Calibri", Index: -1};
|
|
// oTextPr.RFonts.EastAsia = {Name: "Calibri", Index: -1};
|
|
|
|
oTextPr.VertAlign = AscCommon.vertalign_Baseline;
|
|
|
|
// apply text properties
|
|
oContent.SetApplyToAll(true);
|
|
oContent.AddToParagraph(new ParaTextPr(oTextPr));
|
|
// oContent.SetParagraphAlign(AscCommon.align_Center);
|
|
oContent.SetApplyToAll(false);
|
|
|
|
let oBodyPr = textCShape.getBodyPr().createDuplicate();
|
|
oBodyPr.rot = 0;
|
|
// oBodyPr.spcFirstLastPara = false;
|
|
// oBodyPr.vertOverflow = AscFormat.nVOTOverflow;
|
|
// oBodyPr.horzOverflow = AscFormat.nHOTOverflow;
|
|
// oBodyPr.vert = AscFormat.nVertTThorz; // default //( ( Horizontal ))
|
|
oBodyPr.wrap = AscFormat.nTWTSquare; // default
|
|
// oBodyPr.setDefaultInsets();
|
|
// oBodyPr.numCol = 1;
|
|
// oBodyPr.spcCol = 0;
|
|
// oBodyPr.rtlCol = 0;
|
|
// oBodyPr.fromWordArt = false;
|
|
// oBodyPr.anchor = 1; // 4 - bottom, 1,2,3 - center
|
|
// oBodyPr.anchorCtr = false;
|
|
// oBodyPr.forceAA = false;
|
|
// oBodyPr.compatLnSpc = true;
|
|
// // oBodyPr.prstTxWarp = AscFormat.CreatePrstTxWarpGeometry("textNoShape");
|
|
|
|
// cShape.bCheckAutoFitFlag = true;
|
|
// oBodyPr.textFit = new AscFormat.CTextFit();
|
|
// oBodyPr.textFit.type = AscFormat.text_fit_Auto;
|
|
|
|
// oBodyPr.upright = false; // default
|
|
|
|
let leftMarginInch = shape.getCellNumberValueWithScale("LeftMargin", 1);
|
|
let topMarginInch = shape.getCellNumberValueWithScale("TopMargin", 1);
|
|
let rightMarginInch = shape.getCellNumberValueWithScale("RightMargin", 1);
|
|
let bottomMarginInch = shape.getCellNumberValueWithScale("BottomMargin", 1);
|
|
|
|
|
|
// CHECKS SIGN but positive tIns gives bottom inset.
|
|
// Check https://disk.yandex.ru/d/IU1vdjzcF9p3IQ , https://disk.yandex.ru/d/0l7elFyX5INcXg
|
|
// it is may global graphics transform issue so set bottom inset as top and opposite
|
|
// NO REVERT NOW. TOP IS TOP BOTTOM IS BOTTOM
|
|
oBodyPr.tIns = topMarginInch * g_dKoef_in_to_mm;
|
|
oBodyPr.bIns = bottomMarginInch * g_dKoef_in_to_mm;
|
|
oBodyPr.lIns = leftMarginInch * g_dKoef_in_to_mm;
|
|
oBodyPr.rIns = rightMarginInch * g_dKoef_in_to_mm;
|
|
|
|
if (bWord) {
|
|
textCShape.setBodyPr(oBodyPr);
|
|
} else {
|
|
textCShape.txBody.setBodyPr(oBodyPr);
|
|
}
|
|
|
|
|
|
// handle cords
|
|
|
|
let shapeAngle = shape.getCellNumberValue("Angle");
|
|
let textAngle = shape.getCellNumberValue("TxtAngle");
|
|
|
|
if (isInvertCoords) {
|
|
shapeAngle = -shapeAngle;
|
|
textAngle = -textAngle;
|
|
}
|
|
|
|
|
|
// to rotate around point we 1) add one more offset 2) rotate around center
|
|
// https://www.figma.com/design/SJSKMY5dGoAvRg75YnHpdX/newRotateScheme?node-id=0-1&node-type=canvas&t=UTtoZyLRItzaQvS9-0
|
|
|
|
let txtPinX_inch = shape.getCellNumberValueWithScale("TxtPinX", drawingPageScale);
|
|
let txtPinY_inch = shape.getCellNumberValueWithScale("TxtPinY", drawingPageScale);
|
|
|
|
|
|
// consider https://disk.yandex.ru/d/2XzRaPTKzKHFjA
|
|
// where TxtHeight and TxtWidth get all shape height and width and txtPinX_inch and txtPinY_inch are not defined
|
|
// also check for {}, undefined, NaN, null
|
|
let oSpPr = new AscFormat.CSpPr();
|
|
let oXfrm = new AscFormat.CXfrm();
|
|
|
|
let globalXmm = cShape.spPr.xfrm.offX;
|
|
let globalYmm = cShape.spPr.xfrm.offY;
|
|
|
|
let shapeWidth = shape.getCellNumberValueWithScale("Width", drawingPageScale);
|
|
let shapeHeight =shape.getCellNumberValueWithScale("Height", drawingPageScale);
|
|
|
|
if (!(isNaN(txtPinX_inch) || txtPinX_inch === null) && !(isNaN(txtPinY_inch) || txtPinY_inch === null)) {
|
|
// https://www.figma.com/file/WiAC4sxQuJaq65h6xppMYC/cloudFare?type=design&node-id=0%3A1&mode=design&t=SZbio0yIyxq0YnMa-1s
|
|
|
|
let shapeLocPinX = shape.getCellNumberValueWithScale("LocPinX", drawingPageScale);
|
|
let shapeLocPinY = shape.getCellNumberValueWithScale("LocPinY", drawingPageScale);
|
|
let txtWidth_inch = shape.getCellNumberValueWithScale("TxtWidth", drawingPageScale);
|
|
let txtHeight_inch = shape.getCellNumberValueWithScale("TxtHeight", drawingPageScale);
|
|
let txtLocPinX_inch = shape.getCellNumberValueWithScale("TxtLocPinX", drawingPageScale);
|
|
let txtLocPinY_inch = shape.getCellNumberValueWithScale("TxtLocPinY", drawingPageScale);
|
|
|
|
// defaultParagraph.Pr.SetJc(AscCommon.align_Left);
|
|
let oBodyPr = textCShape.getBodyPr().createDuplicate();
|
|
// oBodyPr.anchor = 4; // 4 - bottom, 1,2,3 - center
|
|
|
|
let localXmm = (txtPinX_inch - txtLocPinX_inch) * g_dKoef_in_to_mm;
|
|
|
|
// back to MS coords
|
|
if (isInvertCoords) {
|
|
let topLeftCornerYNewCoords = maxHeightScaledIn * g_dKoef_in_to_mm - globalYmm;
|
|
// now it is bottom left corner y coord
|
|
globalYmm = topLeftCornerYNewCoords - cShape.spPr.xfrm.extY;
|
|
}
|
|
|
|
let localYmm;
|
|
|
|
// Don't recalculate text pos on flip manually, shape with text combined to group whose flipV is applied
|
|
// let flipYCell = shape.getCell("FlipY");
|
|
// let flipVertically = flipYCell ? flipYCell.v === "1" : false;
|
|
// if (flipVertically) {
|
|
// // if we flip figure we flip text pinY around shape pinY
|
|
// if (txtPinY_inch > 0) {
|
|
// // y cord of text block start. when cord system starts in left bottom corner on shape
|
|
// let blockCord = txtPinY_inch - txtLocPinY_inch;
|
|
// // (y part of vector) from shape center to txt block start
|
|
// let fromShapeCenterToBlockStart = blockCord - shapeLocPinY;
|
|
//
|
|
// // mirror distance fromBlock start ToShapeCenter then add text block height to it
|
|
// // + shapeLocPinY made shift from shape center to shape bottom bcs we calculate
|
|
// // localYmm starting from bottom of shape not from center
|
|
// localYmm = (-fromShapeCenterToBlockStart - txtHeight_inch + shapeLocPinY) * g_dKoef_in_to_mm;
|
|
// } else {
|
|
// // negative, y part of vector. y cord of text block start. when cord system starts in left bottom corner on shape
|
|
// let blockCord = txtPinY_inch + (txtHeight_inch - txtLocPinY_inch);
|
|
//
|
|
// // lets make it negative like y part of vector. It comes from top to bottom.
|
|
// // It is vector that comes from shape center to text block start.
|
|
// let fromBlockToShapeCenter = blockCord - shapeLocPinY;
|
|
//
|
|
// // Finally we mirror fromBlockToShapeCenter by multiplying by -1 and add shapeLocPinY to move its
|
|
// // start to bottom on shape
|
|
// localYmm = (-fromBlockToShapeCenter + shapeLocPinY) * g_dKoef_in_to_mm;
|
|
// }
|
|
// oXfrm.setRot(- textAngle);
|
|
// } else {
|
|
// // do calculations
|
|
// localYmm = (txtPinY_inch - txtLocPinY_inch) * g_dKoef_in_to_mm;
|
|
// oXfrm.setRot(textAngle);
|
|
// }
|
|
|
|
// do calculations
|
|
localYmm = (txtPinY_inch - txtLocPinY_inch) * g_dKoef_in_to_mm;
|
|
oXfrm.setRot(textAngle);
|
|
|
|
let offY = globalYmm + localYmm;
|
|
// back to presentation coords
|
|
if (isInvertCoords) {
|
|
let bottomCornerOffY = maxHeightScaledIn * g_dKoef_in_to_mm - offY;
|
|
let topCornerOffY = bottomCornerOffY - txtHeight_inch * g_dKoef_in_to_mm;
|
|
offY = topCornerOffY;
|
|
}
|
|
oXfrm.setOffX(globalXmm + localXmm); // mm
|
|
oXfrm.setOffY(shapeHeight < 0 ? offY + 2 * shapeHeight * g_dKoef_in_to_mm : offY);
|
|
oXfrm.setExtX(txtWidth_inch * g_dKoef_in_to_mm);
|
|
oXfrm.setExtY(txtHeight_inch * g_dKoef_in_to_mm);
|
|
} else {
|
|
// create text block with shape sizes
|
|
oXfrm.setOffX(globalXmm);
|
|
oXfrm.setOffY(shapeHeight < 0 ? globalYmm + 2 * shapeHeight * g_dKoef_in_to_mm : globalYmm);
|
|
oXfrm.setExtX(Math.abs(shapeWidth) * g_dKoef_in_to_mm);
|
|
oXfrm.setExtY(Math.abs(shapeHeight) * g_dKoef_in_to_mm);
|
|
oXfrm.setRot(0);
|
|
}
|
|
|
|
oSpPr.setXfrm(oXfrm);
|
|
oXfrm.setParent(oSpPr);
|
|
oSpPr.setFill(AscFormat.CreateNoFillUniFill());
|
|
oSpPr.setLn(AscFormat.CreateNoFillLine());
|
|
|
|
|
|
textCShape.setSpPr(oSpPr);
|
|
oSpPr.setParent(textCShape);
|
|
|
|
// just trash below
|
|
//
|
|
// // placeholder
|
|
// let oUniNvPr = new AscFormat.UniNvPr();
|
|
// oUniNvPr.nvPr.ph = Asc.VisioEditorApi.prototype.CreatePlaceholder("object");
|
|
//
|
|
// // cShape.txBody.content2 = cShape.txBody.content;
|
|
//
|
|
// cShape.setNvSpPr(oUniNvPr);
|
|
|
|
// copy settings from presentations debug
|
|
// cShape.txBody.content.CurPos.TableMove = 1;
|
|
// cShape.txBody.content.ReindexStartPos = 0;
|
|
// cShape.txBody.content.Content[0].Content[1].CollPrChangeMine = false;
|
|
// cShape.txBody.content.Content[0].Content[1].State.ContentPos = 10;
|
|
// cShape.txBody.content.Content[0].Index = -1;
|
|
// cShape.txBody.compiledBodyPr = null;
|
|
|
|
// Set Paragraph (the only one defaultParagraph exist) justify/align text - center
|
|
// textCShape.txBody.content.Content[0].Pr.SetJc(AscCommon.align_Left);
|
|
|
|
// use ParaRun.prototype.Set_Color
|
|
// cShape.txBody.content.Content[0].Content[1].Pr.Color = TextColor1;
|
|
// cShape.txBody.content.Content[0].Content[0].Pr.Color = TextColor1;
|
|
|
|
return textCShape;
|
|
}
|
|
|
|
/**
|
|
* endArrow can be beginning or ending
|
|
* @param {string} arrowType
|
|
* @param {number} arrowSize
|
|
* @return {AscFormat.EndArrow} endArrowObject
|
|
*/
|
|
function getEndArrow(arrowType, arrowSize) {
|
|
// 2.4.4.20 BeginArrow in MS-VSDX and 20.1.10.33 ST_LineEndType (Line End Type) in ECMA
|
|
let endArrow = new AscFormat.EndArrow();
|
|
|
|
switch (arrowType) {
|
|
case "0":
|
|
endArrow.type = AscFormat.LineEndType.vsdxNone;
|
|
break;
|
|
case "1":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpen90Arrow;
|
|
break;
|
|
case "2":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilled90Arrow;
|
|
break;
|
|
case "3":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenSharpArrow;
|
|
break;
|
|
case "4":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledSharpArrow;
|
|
break;
|
|
case "5":
|
|
endArrow.type = AscFormat.LineEndType.vsdxIndentedFilledArrow;
|
|
break;
|
|
case "6":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOutdentedFilledArrow;
|
|
break;
|
|
case "7":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenFletch;
|
|
break;
|
|
case "8":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledFletch;
|
|
break;
|
|
case "9":
|
|
endArrow.type = AscFormat.LineEndType.vsdxDimensionLine;
|
|
break;
|
|
case "10":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledDot;
|
|
break;
|
|
case "11":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledSquare;
|
|
break;
|
|
case "12":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenASMEArrow;
|
|
break;
|
|
case "13":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledASMEArrow;
|
|
break;
|
|
case "14":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedASMEArrow;
|
|
break;
|
|
case "15":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosed90Arrow;
|
|
break;
|
|
case "16":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedSharpArrow;
|
|
break;
|
|
case "17":
|
|
endArrow.type = AscFormat.LineEndType.vsdxIndentedClosedArrow;
|
|
break;
|
|
case "18":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOutdentedClosedArrow;
|
|
break;
|
|
case "19":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedFletch;
|
|
break;
|
|
case "20":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedDot;
|
|
break;
|
|
case "21":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedSquare;
|
|
break;
|
|
case "22":
|
|
endArrow.type = AscFormat.LineEndType.vsdxDiamond;
|
|
break;
|
|
case "23":
|
|
endArrow.type = AscFormat.LineEndType.vsdxBackslash;
|
|
break;
|
|
case "24":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenOneDash;
|
|
break;
|
|
case "25":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenTwoDash;
|
|
break;
|
|
case "26":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenThreeDash;
|
|
break;
|
|
case "27":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFork;
|
|
break;
|
|
case "28":
|
|
endArrow.type = AscFormat.LineEndType.vsdxDashFork;
|
|
break;
|
|
case "29":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedFork;
|
|
break;
|
|
case "30":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedPlus;
|
|
break;
|
|
case "31":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedOneDash;
|
|
break;
|
|
case "32":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedTwoDash;
|
|
break;
|
|
case "33":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedThreeDash;
|
|
break;
|
|
case "34":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedDiamond;
|
|
break;
|
|
case "35":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledOneDash;
|
|
break;
|
|
case "36":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledTwoDash;
|
|
break;
|
|
case "37":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledThreeDash;
|
|
break;
|
|
case "38":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledDiamond;
|
|
break;
|
|
case "39":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledDoubleArrow;
|
|
break;
|
|
case "40":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedDoubleArrow;
|
|
break;
|
|
case "41":
|
|
endArrow.type = AscFormat.LineEndType.vsdxClosedNoDash;
|
|
break;
|
|
case "42":
|
|
endArrow.type = AscFormat.LineEndType.vsdxFilledNoDash;
|
|
break;
|
|
case "43":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenDoubleArrow;
|
|
break;
|
|
case "44":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenArrowSingleDash;
|
|
break;
|
|
case "45":
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpenDoubleArrowSingleDash;
|
|
break;
|
|
case "Themed":
|
|
endArrow.type = AscFormat.LineEndType.vsdxNone;
|
|
break;
|
|
case !isNaN(Number(arrowType)) && arrowType:
|
|
// is unhandled number
|
|
endArrow.type = AscFormat.LineEndType.vsdxOpen90Arrow;
|
|
break;
|
|
default:
|
|
endArrow.type = AscFormat.LineEndType.vsdxNone;
|
|
}
|
|
|
|
if (arrowSize >= 0 && arrowSize <= 6 ) {
|
|
let sizeEnumVsdxShift = 3;
|
|
// see AscFormat.LineEndSize
|
|
endArrow.len = arrowSize + sizeEnumVsdxShift;
|
|
endArrow.w = arrowSize + sizeEnumVsdxShift;
|
|
} else {
|
|
AscCommon.consoleLog("arrowSize unknown:", arrowSize);
|
|
endArrow.len = AscFormat.LineEndSize.vsdxMedium;
|
|
endArrow.w = AscFormat.LineEndSize.vsdxMedium;
|
|
}
|
|
|
|
return endArrow;
|
|
}
|
|
|
|
function createEmptyShape() {
|
|
let emptyCShape = new AscFormat.CShape();
|
|
emptyCShape.setWordShape(false);
|
|
emptyCShape.setBDeleted(false);
|
|
|
|
var oSpPr = new AscFormat.CSpPr();
|
|
var oXfrm = new AscFormat.CXfrm();
|
|
|
|
oSpPr.setXfrm(oXfrm);
|
|
oXfrm.setParent(oSpPr);
|
|
|
|
emptyCShape.setSpPr(oSpPr);
|
|
oSpPr.setParent(emptyCShape);
|
|
emptyCShape.setParent2(visioDocument);
|
|
|
|
return emptyCShape;
|
|
}
|
|
|
|
/**
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @return {number} line width EMUs
|
|
*/
|
|
function getLineWidth(shape, pageInfo, visioDocument) {
|
|
let lineWidthEmu = null;
|
|
let lineWeightCell = shape.getCell("LineWeight");
|
|
if (lineWeightCell) {
|
|
// to cell.v visio always saves inches
|
|
// let lineWeightInches = Number(lineWeightCell.v);
|
|
let lineWeightInches = lineWeightCell.calculateValue(shape, pageInfo,
|
|
visioDocument.themes);
|
|
if (!isNaN(lineWeightInches)) {
|
|
lineWidthEmu = lineWeightInches * AscCommonWord.g_dKoef_in_to_mm * AscCommonWord.g_dKoef_mm_to_emu;
|
|
} else {
|
|
AscCommon.consoleLog("caught unknown error. line will be painted 9525 emus");
|
|
// 9255 emus = 0.01041666666666667 inches is document.xml StyleSheet ID=0 LineWeight e. g. default value
|
|
lineWidthEmu = 9525;
|
|
}
|
|
} else {
|
|
AscCommon.consoleLog("LineWeight cell was not calculated. line will be painted 9525 emus");
|
|
lineWidthEmu = 9525;
|
|
}
|
|
return lineWidthEmu;
|
|
}
|
|
|
|
/**
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @return {number} cap type
|
|
*/
|
|
function getLineCap(shape, pageInfo, visioDocument) {
|
|
let lineCapCell = shape.getCell("LineCap");
|
|
let lineCapNumber;
|
|
if (lineCapCell) {
|
|
lineCapNumber = lineCapCell.calculateValue(shape, pageInfo, visioDocument.themes);
|
|
if (isNaN(lineCapNumber)) {
|
|
lineCapNumber = 2;
|
|
}
|
|
} else {
|
|
lineCapNumber = 2;
|
|
}
|
|
return lineCapNumber;
|
|
}
|
|
|
|
/**
|
|
* Calculate line pattern. See some issues inside.
|
|
* @param {Shape_Type} shape
|
|
* @param {Page_Type} pageInfo
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {number} lineCap
|
|
* @return {number} cap type
|
|
*/
|
|
function getPresetDash(shape, pageInfo, visioDocument, lineCap) {
|
|
let linePattern = shape.getCell("LinePattern");
|
|
let prstDash;
|
|
if (linePattern) {
|
|
// see ECMA-376-1 - L.4.8.5.2 Line Dash Properties and [MS-VSDX]-220215 (1) - 2.4.4.180 LinePattern
|
|
let linePatternNumber = linePattern.calculateValue(shape, pageInfo, visioDocument.themes);
|
|
if (isNaN(linePatternNumber)) {
|
|
prstDash = AscFormat.CLn.prototype.GetDashCode("vsdxSolid");
|
|
} else {
|
|
const shift = 11;
|
|
const visioLineCode = linePatternNumber + shift;
|
|
let dashTypeName = AscFormat.CLn.prototype.GetDashByCode(visioLineCode);
|
|
if (dashTypeName !== null) {
|
|
if (lineCap !== 2) {
|
|
AscCommon.consoleLog("linePattern may be wrong. Because visio cap is not square" +
|
|
"Now only flat cap is supported in sdkjs but Line patterns were made " +
|
|
"for visio cap square looks correct" +
|
|
"So when visio cap is not square line pattern will not fit."
|
|
)
|
|
if ("vsdxHalfHalfDash" === dashTypeName) {
|
|
// vsdxHalfHalfDash looks like solid on visio cap square but if cap is not square in visio
|
|
// vsdxHalfHalfDash should be dotted
|
|
// set 10th visio pattern
|
|
return 10 + shift;
|
|
}
|
|
}
|
|
prstDash = visioLineCode;
|
|
} else {
|
|
prstDash = AscFormat.CLn.prototype.GetDashCode("vsdxDash");
|
|
}
|
|
}
|
|
} else {
|
|
prstDash = AscFormat.CLn.prototype.GetDashCode("vsdxSolid");
|
|
}
|
|
return prstDash;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* converts !Shape TypeGroup! To CGroupShape Recursively.
|
|
* Shape can only have subshapes if its Type='Group'.
|
|
* Can be called on shapes Type not 'Group' then shape just adds to currentGroupHandling
|
|
* @memberOf Shape_Type
|
|
* @param {CVisioDocument} visioDocument
|
|
* @param {Page_Type} pageInfo
|
|
* @param {Number} drawingPageScale
|
|
* @param {CGroupShape?} currentGroupHandling
|
|
* @param {number[]?} delMasterShapes - shapes with MasterShape included in array will not be converted
|
|
* @return {CGroupShape | undefined}
|
|
*/
|
|
Shape_Type.prototype.convertGroup = function (visioDocument, pageInfo,
|
|
drawingPageScale, currentGroupHandling, delMasterShapes) {
|
|
// if we need to create CGroupShape create CShape first then copy its properties to CGroupShape object
|
|
// so anyway create CShapes
|
|
let cShapeOrCGroupShape = this.convertShape(visioDocument, pageInfo, drawingPageScale, currentGroupHandling);
|
|
|
|
// handle ShapeShdwShow to hide shadow if shape has ShapeShdwShow 1 and shape is in group
|
|
let shapeShdwShowCell = this.getCell("ShapeShdwShow");
|
|
let shapeShdwShow = shapeShdwShowCell && shapeShdwShowCell.calculateValue(this, pageInfo, visioDocument.themes);
|
|
if (shapeShdwShow === 1 && currentGroupHandling) {
|
|
let shape;
|
|
if (cShapeOrCGroupShape.getObjectType() === AscDFH.historyitem_type_GroupShape) {
|
|
shape = cShapeOrCGroupShape.spTree[0];
|
|
} else {
|
|
shape = cShapeOrCGroupShape;
|
|
}
|
|
if (shape && shape.spPr.effectProps && shape.spPr.effectProps.EffectLst &&
|
|
shape.spPr.effectProps.EffectLst.outerShdw) {
|
|
// hide shadow
|
|
// TODO dont delete shadow object but create ShapeShdwShow property for CShape to handle it on draw
|
|
shape.spPr.effectProps.EffectLst.outerShdw = null;
|
|
shape.spPr.effectProps.EffectLst.innerShdw = null;
|
|
shape.spPr.effectProps.EffectLst.prstShdw = null;
|
|
}
|
|
|
|
}
|
|
|
|
// if it is group in vsdx
|
|
if (this.type === AscVisio.SHAPE_TYPES_GROUP) {
|
|
// CGroupShape cant support text. So cShape will represent everything related to Shape Type="Group".
|
|
// Let's push cShape into CGroupShape object.
|
|
if (cShapeOrCGroupShape) {
|
|
let groupShape = new AscFormat.CGroupShape();
|
|
// this.graphicObjectsController = new AscFormat.DrawingObjectsController();
|
|
// let groupShape = AscFormat.builder_CreateGroup();
|
|
|
|
groupShape.setLocks(0);
|
|
|
|
groupShape.setBDeleted(false);
|
|
|
|
// Create CGroupShape with SpPr from cShape but with no fill and line
|
|
let noLineFillSpPr = cShapeOrCGroupShape.spPr.createDuplicate();
|
|
noLineFillSpPr.setFill(AscFormat.CreateNoFillUniFill());
|
|
noLineFillSpPr.setLn(AscFormat.CreateNoFillLine());
|
|
groupShape.setSpPr(noLineFillSpPr);
|
|
groupShape.spPr.setParent(groupShape);
|
|
// these props came to group
|
|
cShapeOrCGroupShape.spPr.xfrm.rot = 0;
|
|
cShapeOrCGroupShape.spPr.xfrm.flipH = false;
|
|
cShapeOrCGroupShape.spPr.xfrm.flipV = false;
|
|
cShapeOrCGroupShape.spPr.xfrm.setOffX(0);
|
|
cShapeOrCGroupShape.spPr.xfrm.setOffY(0);
|
|
// cShapeOrCGroupShape.setLocks(1)?;
|
|
|
|
groupShape.brush = cShapeOrCGroupShape.brush;
|
|
groupShape.bounds = cShapeOrCGroupShape.bounds;
|
|
groupShape.localTransform = cShapeOrCGroupShape.localTransform;
|
|
groupShape.pen = cShapeOrCGroupShape.pen;
|
|
groupShape.Id = cShapeOrCGroupShape.Id + "_Group";
|
|
groupShape.setParent2(visioDocument);
|
|
|
|
|
|
// if DisplayMode is 1 add group geometry to bottom layer
|
|
if (this.getCellNumberValue("DisplayMode") === 1) {
|
|
// if it is group so there is geometry and text in it. We take geometry
|
|
if (cShapeOrCGroupShape instanceof CGroupShape) {
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShapeOrCGroupShape.spTree[0]);
|
|
} else {
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShapeOrCGroupShape);
|
|
}
|
|
groupShape.spTree[groupShape.spTree.length - 1].setGroup(groupShape);
|
|
}
|
|
|
|
// handle sub-shapes
|
|
let subShapes = this.getSubshapes();
|
|
|
|
/**
|
|
* see bug for Del attribute handle: https://bugzilla.onlyoffice.com/show_bug.cgi?id=76050
|
|
* let's collect dels first and then traverse through all the group again in groupShape.deleteShapes().
|
|
* Dels appear rarely so it is ok.
|
|
* @type {number[]}
|
|
*/
|
|
let delMasterShapesCurrent = [];
|
|
for (let i = 0; i < subShapes.length; i++) {
|
|
const subShape = subShapes[i];
|
|
if (subShape.del && subShape.masterShape !== null) {
|
|
delMasterShapesCurrent.push(subShape.masterShape);
|
|
}
|
|
}
|
|
if (delMasterShapes === undefined) {
|
|
delMasterShapes = delMasterShapesCurrent;
|
|
} else {
|
|
delMasterShapes = delMasterShapes.concat(delMasterShapesCurrent);
|
|
}
|
|
|
|
for (let i = 0; i < subShapes.length; i++) {
|
|
const subShape = subShapes[i];
|
|
// if group - remove
|
|
// if shape and fully inherited - remove
|
|
// if shape/group is not inherited check for masterShape
|
|
// if shape/group is inherited check for id
|
|
// TODO Optimization: use Set() because has() is faster than includes()
|
|
// TODO try changing shape Del='1' IDs and see what happens
|
|
const delIdCheck = this.inheritedShapes.includes(subShape) && delMasterShapes.includes(subShape.id)
|
|
|| delMasterShapes.includes(subShape.masterShape);
|
|
const isDeleted = delIdCheck && (subShape.type === AscVisio.SHAPE_TYPES_GROUP
|
|
|| subShape.type === AscVisio.SHAPE_TYPES_SHAPE && this.inheritedShapes.includes(subShape))
|
|
if (!isDeleted) {
|
|
subShape.convertGroup(visioDocument, pageInfo, drawingPageScale, groupShape, delMasterShapes);
|
|
}
|
|
}
|
|
|
|
// if group geometry should be on the top layer
|
|
if (this.getCellNumberValue("DisplayMode") === 2) {
|
|
if (cShapeOrCGroupShape instanceof CGroupShape) {
|
|
// if it is group so there is geometry and text in it. We take geometry
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShapeOrCGroupShape.spTree[0]);
|
|
} else {
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShapeOrCGroupShape);
|
|
}
|
|
groupShape.spTree[groupShape.spTree.length - 1].setGroup(groupShape);
|
|
}
|
|
|
|
// add group text to top
|
|
if (cShapeOrCGroupShape instanceof CGroupShape) {
|
|
groupShape.addToSpTree(groupShape.spTree.length, cShapeOrCGroupShape.spTree[1]);
|
|
groupShape.spTree[groupShape.spTree.length - 1].setGroup(groupShape);
|
|
}
|
|
|
|
if (currentGroupHandling) {
|
|
// insert group to currentGroupHandling
|
|
currentGroupHandling.addToSpTree(currentGroupHandling.spTree.length, groupShape);
|
|
currentGroupHandling.spTree[currentGroupHandling.spTree.length - 1].setGroup(currentGroupHandling);
|
|
}
|
|
|
|
return groupShape;
|
|
}
|
|
} else {
|
|
// if read cShape not CGroupShape
|
|
if (!currentGroupHandling) {
|
|
throw new Error("Group handler was called on simple shape");
|
|
} else {
|
|
// add shape and text (shapeAndTextGroup or shape) to currentGroupHandling
|
|
if (cShapeOrCGroupShape) {
|
|
currentGroupHandling.addToSpTree(currentGroupHandling.spTree.length, cShapeOrCGroupShape);
|
|
currentGroupHandling.spTree[currentGroupHandling.spTree.length-1].setGroup(currentGroupHandling);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @memberOf Shape_Type
|
|
* @param {{x_mm, y_mm, w_mm, h_mm, rot, oFill, oStroke, flipHorizontally, flipVertically, cVisioDocument,
|
|
* drawingPageScale, isInvertCoords, isShapeDeleted, id}} paramsObj
|
|
* @return {CShape} CShape
|
|
*/
|
|
Shape_Type.prototype.convertToCShapeUsingParamsObj = function(paramsObj) {
|
|
let x = paramsObj.x_mm;
|
|
let y = paramsObj.y_mm;
|
|
let w_mm = paramsObj.w_mm;
|
|
let h_mm = paramsObj.h_mm;
|
|
let rot = paramsObj.rot;
|
|
let oFill = paramsObj.oFill;
|
|
let oStroke = paramsObj.oStroke;
|
|
let cVisioDocument = paramsObj.cVisioDocument;
|
|
let flipHorizontally = paramsObj.flipHorizontally;
|
|
let flipVertically = paramsObj.flipVertically;
|
|
let drawingPageScale = paramsObj.drawingPageScale;
|
|
let isInvertCoords = paramsObj.isInvertCoords;
|
|
let isShapeDeleted = paramsObj.isShapeDeleted;
|
|
let id = paramsObj.id;
|
|
|
|
let shapeGeom = AscVisio.getGeometryFromShape(this, drawingPageScale, isInvertCoords);
|
|
|
|
let sType = "rect";
|
|
let nWidth_mm = w_mm;
|
|
let nHeight_mm = h_mm;
|
|
//let oDrawingDocument = new AscCommon.CDrawingDocument();
|
|
let shape = AscFormat.builder_CreateShape(sType, nWidth_mm, nHeight_mm,
|
|
oFill, oStroke, cVisioDocument, cVisioDocument.themes[0], null, false);
|
|
shape.spPr.xfrm.setOffX(x);
|
|
shape.spPr.xfrm.setOffY(y);
|
|
shape.spPr.xfrm.setRot(rot);
|
|
shape.spPr.xfrm.setFlipH(flipHorizontally);
|
|
shape.spPr.xfrm.setFlipV(flipVertically);
|
|
|
|
shape.spPr.setGeometry(shapeGeom);
|
|
|
|
if (isShapeDeleted) {
|
|
shape.setBDeleted(true);
|
|
}
|
|
|
|
shape.Id = String(id); // it was string in cShape
|
|
|
|
return shape;
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------export-------------------------------------------------
|
|
window['Asc'] = window['Asc'] || {};
|
|
window['AscCommon'] = window['AscCommon'] || {};
|
|
window['AscCommonWord'] = window['AscCommonWord'] || {};
|
|
window['AscCommonSlide'] = window['AscCommonSlide'] || {};
|
|
window['AscCommonExcel'] = window['AscCommonExcel'] || {};
|
|
window['AscVisio'] = window['AscVisio'] || {};
|
|
window['AscFormat'] = window['AscFormat'] || {};
|
|
window['AscWord'] = window['AscWord'] || {};
|
|
|
|
})(window, window.document);
|