@odoo/o-spreadsheet 16.4.0-alpha.6 → 16.4.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12596,6 +12596,18 @@ const AND = {
12596
12596
  isExported: true,
12597
12597
  };
12598
12598
  // -----------------------------------------------------------------------------
12599
+ // FALSE
12600
+ // -----------------------------------------------------------------------------
12601
+ const FALSE = {
12602
+ description: _lt("Logical value `false`."),
12603
+ args: [],
12604
+ returns: ["BOOLEAN"],
12605
+ compute: function () {
12606
+ return false;
12607
+ },
12608
+ isExported: true,
12609
+ };
12610
+ // -----------------------------------------------------------------------------
12599
12611
  // IF
12600
12612
  // -----------------------------------------------------------------------------
12601
12613
  const IF = {
@@ -12731,6 +12743,18 @@ const OR = {
12731
12743
  isExported: true,
12732
12744
  };
12733
12745
  // -----------------------------------------------------------------------------
12746
+ // TRUE
12747
+ // -----------------------------------------------------------------------------
12748
+ const TRUE = {
12749
+ description: _lt("Logical value `true`."),
12750
+ args: [],
12751
+ returns: ["BOOLEAN"],
12752
+ compute: function () {
12753
+ return true;
12754
+ },
12755
+ isExported: true,
12756
+ };
12757
+ // -----------------------------------------------------------------------------
12734
12758
  // XOR
12735
12759
  // -----------------------------------------------------------------------------
12736
12760
  const XOR = {
@@ -12757,12 +12781,14 @@ const XOR = {
12757
12781
  var logical = /*#__PURE__*/Object.freeze({
12758
12782
  __proto__: null,
12759
12783
  AND: AND,
12784
+ FALSE: FALSE,
12760
12785
  IF: IF,
12761
12786
  IFERROR: IFERROR,
12762
12787
  IFNA: IFNA,
12763
12788
  IFS: IFS,
12764
12789
  NOT: NOT,
12765
12790
  OR: OR,
12791
+ TRUE: TRUE,
12766
12792
  XOR: XOR
12767
12793
  });
12768
12794
 
@@ -13949,7 +13975,6 @@ function indentCode(code) {
13949
13975
  * is useful for the composer, which needs to be able to work with incomplete
13950
13976
  * formulas.
13951
13977
  */
13952
- const functions$3 = functionRegistry.content;
13953
13978
  const POSTFIX_UNARY_OPERATORS = ["%"];
13954
13979
  const OPERATORS = "+,-,*,/,:,=,<>,>=,>,<=,<,^,&".split(",").concat(POSTFIX_UNARY_OPERATORS);
13955
13980
  function tokenize(str, locale = DEFAULT_LOCALE) {
@@ -14080,17 +14105,11 @@ function tokenizeSymbol(chars) {
14080
14105
  }
14081
14106
  if (result.length) {
14082
14107
  const value = result;
14083
- const isFunction = value.toUpperCase() in functions$3;
14084
- if (isFunction) {
14085
- return { type: "FUNCTION", value };
14086
- }
14087
14108
  const isReference = rangeReference.test(value);
14088
14109
  if (isReference) {
14089
14110
  return { type: "REFERENCE", value };
14090
14111
  }
14091
- else {
14092
- return { type: "SYMBOL", value };
14093
- }
14112
+ return { type: "SYMBOL", value };
14094
14113
  }
14095
14114
  return null;
14096
14115
  }
@@ -14190,9 +14209,6 @@ function parseOperand(tokens) {
14190
14209
  return { type: "NUMBER", value: parseNumber(current.value, DEFAULT_LOCALE) };
14191
14210
  case "STRING":
14192
14211
  return { type: "STRING", value: removeStringQuotes(current.value) };
14193
- case "FUNCTION":
14194
- const args = parseFunctionArgs(tokens);
14195
- return { type: "FUNCALL", value: current.value, args };
14196
14212
  case "INVALID_REFERENCE":
14197
14213
  throw new InvalidReferenceError();
14198
14214
  case "REFERENCE":
@@ -14209,13 +14225,15 @@ function parseOperand(tokens) {
14209
14225
  value: current.value,
14210
14226
  };
14211
14227
  case "SYMBOL":
14212
- if (["TRUE", "FALSE"].includes(current.value.toUpperCase())) {
14213
- return { type: "BOOLEAN", value: current.value.toUpperCase() === "TRUE" };
14228
+ const value = current.value;
14229
+ const nextToken = tokens[0];
14230
+ if (nextToken?.type === "LEFT_PAREN" && functionRegex.test(current.value)) {
14231
+ const args = parseFunctionArgs(tokens);
14232
+ return { type: "FUNCALL", value: value, args };
14214
14233
  }
14215
- if (current.value) {
14216
- if (functionRegex.test(current.value) && tokens[0]?.type === "LEFT_PAREN") {
14217
- throw new UnknownFunctionError(current.value);
14218
- }
14234
+ const upperCaseValue = value.toUpperCase();
14235
+ if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
14236
+ return { type: "BOOLEAN", value: upperCaseValue === "TRUE" };
14219
14237
  }
14220
14238
  throw new BadExpressionError(_lt("Invalid formula"));
14221
14239
  case "LEFT_PAREN":
@@ -14317,36 +14335,60 @@ function parseTokens(tokens) {
14317
14335
  * to nodes of a specific type.
14318
14336
  * Useful if you want to convert some part of a formula.
14319
14337
  *
14320
- * e.g.
14321
- * ```ts
14338
+ * @example
14322
14339
  * convertAstNodes(ast, "FUNCALL", convertFormulaToExcel)
14323
14340
  *
14324
14341
  * function convertFormulaToExcel(ast: ASTFuncall) {
14325
14342
  * // ...
14326
14343
  * return modifiedAst
14327
14344
  * }
14328
- * ```
14329
14345
  */
14330
14346
  function convertAstNodes(ast, type, fn) {
14331
- if (type === ast.type) {
14332
- ast = fn(ast);
14347
+ return mapAst(ast, (ast) => {
14348
+ if (ast.type === type) {
14349
+ return fn(ast);
14350
+ }
14351
+ return ast;
14352
+ });
14353
+ }
14354
+ function iterateAstNodes(ast) {
14355
+ return Array.from(astIterator(ast));
14356
+ }
14357
+ function* astIterator(ast) {
14358
+ yield ast;
14359
+ switch (ast.type) {
14360
+ case "FUNCALL":
14361
+ for (const arg of ast.args) {
14362
+ yield* astIterator(arg);
14363
+ }
14364
+ break;
14365
+ case "UNARY_OPERATION":
14366
+ yield* astIterator(ast.operand);
14367
+ break;
14368
+ case "BIN_OPERATION":
14369
+ yield* astIterator(ast.left);
14370
+ yield* astIterator(ast.right);
14371
+ break;
14333
14372
  }
14373
+ }
14374
+ function mapAst(ast, fn) {
14375
+ ast = fn(ast);
14334
14376
  switch (ast.type) {
14335
14377
  case "FUNCALL":
14336
14378
  return {
14337
14379
  ...ast,
14338
- args: ast.args.map((child) => convertAstNodes(child, type, fn)),
14380
+ args: ast.args.map((child) => mapAst(child, fn)),
14339
14381
  };
14340
14382
  case "UNARY_OPERATION":
14341
14383
  return {
14342
14384
  ...ast,
14343
- operand: convertAstNodes(ast.operand, type, fn),
14385
+ operand: mapAst(ast.operand, fn),
14344
14386
  };
14345
14387
  case "BIN_OPERATION":
14346
14388
  return {
14347
14389
  ...ast,
14348
- right: convertAstNodes(ast.right, type, fn),
14349
- left: convertAstNodes(ast.left, type, fn),
14390
+ right: mapAst(ast.right, fn),
14391
+ left: mapAst(ast.left, fn),
14350
14392
  };
14351
14393
  default:
14352
14394
  return ast;
@@ -14616,6 +14658,9 @@ function compile(formula) {
14616
14658
  const { args } = ast;
14617
14659
  const functionName = ast.value.toUpperCase();
14618
14660
  const functionDefinition = functions$2[functionName];
14661
+ if (!functionDefinition) {
14662
+ throw new UnknownFunctionError(ast.value);
14663
+ }
14619
14664
  assertEnoughArgs(ast);
14620
14665
  const compiledArgs = [];
14621
14666
  for (let i = 0; i < args.length; i++) {
@@ -14874,7 +14919,7 @@ function mapParentFunction(tokens) {
14874
14919
  functionStarted = "";
14875
14920
  }
14876
14921
  switch (token.type) {
14877
- case "FUNCTION":
14922
+ case "SYMBOL":
14878
14923
  functionStarted = token.value;
14879
14924
  break;
14880
14925
  case "LEFT_PAREN":
@@ -14911,6 +14956,17 @@ function composerTokenize(formula, locale) {
14911
14956
  return mapParentFunction(mapParenthesis(enrichTokens(tokens)));
14912
14957
  }
14913
14958
 
14959
+ const functions$1 = functionRegistry.content;
14960
+ function isExportableToExcel(tokens) {
14961
+ try {
14962
+ const nonExportableFunctions = iterateAstNodes(parseTokens(tokens)).filter((ast) => ast.type === "FUNCALL" && !functions$1[ast.value.toUpperCase()]?.isExported);
14963
+ return nonExportableFunctions.length === 0;
14964
+ }
14965
+ catch (error) {
14966
+ return false;
14967
+ }
14968
+ }
14969
+
14914
14970
  function isValidLocale(locale) {
14915
14971
  if (!(locale &&
14916
14972
  typeof locale === "object" &&
@@ -17483,8 +17539,14 @@ function getImageMenuRegistry(figureId, onFigureDeleted, env) {
17483
17539
  id: "reset_size",
17484
17540
  name: _lt("Reset size"),
17485
17541
  sequence: 4,
17486
- execute: () => {
17487
- const size = env.model.getters.getImageSize(figureId);
17542
+ execute: async () => {
17543
+ const imagePath = env.model.getters.getImagePath(figureId);
17544
+ const size = env.model.getters.getImageSize(figureId) ??
17545
+ (await env.imageProvider?.getImageOriginalSize(imagePath));
17546
+ if (!env.model.getters.getImageSize(figureId)) {
17547
+ const image = env.model.getters.getImage(figureId);
17548
+ image.size = size;
17549
+ }
17488
17550
  const { height, width } = getMaxFigureSize(env.model.getters, size);
17489
17551
  env.model.dispatch("UPDATE_FIGURE", {
17490
17552
  sheetId: env.model.getters.getActiveSheetId(),
@@ -23537,7 +23599,7 @@ FunctionDescriptionProvider.props = {
23537
23599
  argToFocus: Number,
23538
23600
  };
23539
23601
 
23540
- const functions$1 = functionRegistry.content;
23602
+ const functions = functionRegistry.content;
23541
23603
  const ASSISTANT_WIDTH = 300;
23542
23604
  const selectionIndicatorClass = "selector-flag";
23543
23605
  const selectionIndicatorColor = "#a9a9a9";
@@ -23571,10 +23633,6 @@ css /* scss */ `
23571
23633
  padding-right: 3px;
23572
23634
  outline: none;
23573
23635
 
23574
- &.unfocusable {
23575
- pointer-events: none;
23576
- }
23577
-
23578
23636
  p {
23579
23637
  margin-bottom: 0px;
23580
23638
 
@@ -23822,7 +23880,10 @@ class Composer extends owl.Component {
23822
23880
  }
23823
23881
  }
23824
23882
  showAutocomplete(searchTerm) {
23825
- if (!this.env.model.getters.getCurrentContent().startsWith("=")) {
23883
+ const searchTermUpperCase = searchTerm.toUpperCase();
23884
+ if (!this.env.model.getters.getCurrentContent().startsWith("=") ||
23885
+ searchTermUpperCase === "TRUE" ||
23886
+ searchTermUpperCase === "FALSE") {
23826
23887
  return;
23827
23888
  }
23828
23889
  this.autoCompleteState.showProvider = true;
@@ -23940,7 +24001,6 @@ class Composer extends owl.Component {
23940
24001
  switch (token.type) {
23941
24002
  case "OPERATOR":
23942
24003
  case "NUMBER":
23943
- case "FUNCTION":
23944
24004
  case "ARG_SEPARATOR":
23945
24005
  case "STRING":
23946
24006
  result.push({ value: token.value, color: tokenColors[token.type] || "#000" });
@@ -23950,10 +24010,14 @@ class Composer extends owl.Component {
23950
24010
  result.push({ value: token.value, color: this.rangeColor(xc, sheetName) || "#000" });
23951
24011
  break;
23952
24012
  case "SYMBOL":
23953
- let value = token.value;
23954
- if (["TRUE", "FALSE"].includes(value.toUpperCase())) {
24013
+ const value = token.value;
24014
+ const upperCaseValue = value.toUpperCase();
24015
+ if (upperCaseValue === "TRUE" || upperCaseValue === "FALSE") {
23955
24016
  result.push({ value: token.value, color: tokenColors.NUMBER });
23956
24017
  }
24018
+ else if (upperCaseValue in functionRegistry.content) {
24019
+ result.push({ value: token.value, color: tokenColors.FUNCTION });
24020
+ }
23957
24021
  else {
23958
24022
  result.push({ value: token.value, color: "#000" });
23959
24023
  }
@@ -24049,25 +24113,28 @@ class Composer extends owl.Component {
24049
24113
  this.autoCompleteState.showProvider = false;
24050
24114
  this.functionDescriptionState.showDescription = false;
24051
24115
  if (content.startsWith("=")) {
24052
- const tokenAtCursor = this.env.model.getters.getTokenAtCursor();
24053
- if (tokenAtCursor) {
24054
- const { xc } = splitReference(tokenAtCursor.value);
24055
- if (tokenAtCursor.type === "FUNCTION" ||
24056
- (tokenAtCursor.type === "SYMBOL" && !rangeReference.test(xc))) {
24057
- // initialize Autocomplete Dropdown
24058
- this.showAutocomplete(tokenAtCursor.value);
24059
- }
24060
- else if (tokenAtCursor.functionContext && tokenAtCursor.type !== "UNKNOWN") {
24061
- // initialize Formula Assistant
24062
- const tokenContext = tokenAtCursor.functionContext;
24063
- const parentFunction = tokenContext.parent.toUpperCase();
24064
- const description = functions$1[parentFunction];
24065
- const argPosition = tokenContext.argPosition;
24066
- this.functionDescriptionState.functionName = parentFunction;
24067
- this.functionDescriptionState.functionDescription = description;
24068
- this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
24069
- this.functionDescriptionState.showDescription = true;
24070
- }
24116
+ const token = this.env.model.getters.getTokenAtCursor();
24117
+ if (!token) {
24118
+ return;
24119
+ }
24120
+ if (token.type === "SYMBOL") {
24121
+ // initialize Autocomplete Dropdown
24122
+ this.showAutocomplete(token.value);
24123
+ return;
24124
+ }
24125
+ const tokenContext = token.functionContext;
24126
+ const parentFunction = tokenContext?.parent.toUpperCase();
24127
+ if (tokenContext &&
24128
+ parentFunction &&
24129
+ parentFunction in functions &&
24130
+ token.type !== "UNKNOWN") {
24131
+ // initialize Formula Assistant
24132
+ const description = functions[parentFunction];
24133
+ const argPosition = tokenContext.argPosition;
24134
+ this.functionDescriptionState.functionName = parentFunction;
24135
+ this.functionDescriptionState.functionDescription = description;
24136
+ this.functionDescriptionState.argToFocus = description.getArgToFocus(argPosition + 1) - 1;
24137
+ this.functionDescriptionState.showDescription = true;
24071
24138
  }
24072
24139
  }
24073
24140
  }
@@ -26767,7 +26834,12 @@ const XLSX_RELATION_TYPE = {
26767
26834
  };
26768
26835
  const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
26769
26836
  const HEIGHT_FACTOR = 0.75; // 100px => 75 u
26770
- const WIDTH_FACTOR = 0.1317; // 100px => 13.17 u
26837
+ /**
26838
+ * Excel says its default column width is 8.43 characters (64px)
26839
+ * which makes WIDTH_FACTOR = 0.1317, but it doesn't work well
26840
+ * 0.143 is a value from dev's experiments.
26841
+ */
26842
+ const WIDTH_FACTOR = 0.143;
26771
26843
  /** unit : maximum number of characters a column can hold at the standard font size. What. */
26772
26844
  const EXCEL_DEFAULT_COL_WIDTH = 8.43;
26773
26845
  /** unit : points */
@@ -27284,7 +27356,7 @@ const XLSX_INDEXED_COLORS = {
27284
27356
  64: "000000",
27285
27357
  65: "FFFFFF", // system background
27286
27358
  };
27287
- const IMAGE_MIMETYPE_EXTENSION_MAPPING = {
27359
+ const IMAGE_MIMETYPE_TO_EXTENSION_MAPPING = {
27288
27360
  "image/avif": "avif",
27289
27361
  "image/bmp": "bmp",
27290
27362
  "image/gif": "gif",
@@ -27293,6 +27365,17 @@ const IMAGE_MIMETYPE_EXTENSION_MAPPING = {
27293
27365
  "image/png": "png",
27294
27366
  "image/tiff": "tiff",
27295
27367
  "image/webp": "webp",
27368
+ };
27369
+ const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
27370
+ avif: "image/avif",
27371
+ bmp: "image/bmp",
27372
+ gif: "image/gif",
27373
+ ico: "image/vnd.microsoft.icon",
27374
+ jpeg: "image/jpeg",
27375
+ png: "image/png",
27376
+ tiff: "image/tiff",
27377
+ webp: "image/webp",
27378
+ jpg: "image/jpeg",
27296
27379
  };
27297
27380
 
27298
27381
  /**
@@ -28096,20 +28179,35 @@ function convertFigure(figure, id, sheetData) {
28096
28179
  convertEMUToDotValue(figure.anchors[0].rowOffset);
28097
28180
  const y2 = getRowPosition(figure.anchors[1].row, sheetData) +
28098
28181
  convertEMUToDotValue(figure.anchors[1].rowOffset);
28099
- const width = x2 - x1;
28100
- const height = y2 - y1;
28101
- const chartData = convertChartData(figure.data);
28102
- if (!chartData)
28103
- return undefined;
28104
- return {
28105
- id: id,
28106
- x: x1,
28107
- y: y1,
28108
- width: width,
28109
- height: height,
28110
- tag: "chart",
28111
- data: convertChartData(figure.data),
28112
- };
28182
+ const figureData = { id, x: x1, y: y1 };
28183
+ if (isChartData(figure.data)) {
28184
+ return {
28185
+ ...figureData,
28186
+ width: x2 - x1,
28187
+ height: y2 - y1,
28188
+ tag: "chart",
28189
+ data: convertChartData(figure.data),
28190
+ };
28191
+ }
28192
+ else if (isImageData(figure.data)) {
28193
+ return {
28194
+ ...figureData,
28195
+ width: convertEMUToDotValue(figure.data.size.cx),
28196
+ height: convertEMUToDotValue(figure.data.size.cy),
28197
+ tag: "image",
28198
+ data: {
28199
+ path: figure.data.imageSrc,
28200
+ mimetype: figure.data.mimetype,
28201
+ },
28202
+ };
28203
+ }
28204
+ return undefined;
28205
+ }
28206
+ function isChartData(data) {
28207
+ return "dataSets" in data;
28208
+ }
28209
+ function isImageData(data) {
28210
+ return "imageSrc" in data;
28113
28211
  }
28114
28212
  function convertChartData(chartData) {
28115
28213
  const dataSetsHaveTitle = chartData.dataSets[0].label !== undefined;
@@ -28816,9 +28914,13 @@ class XlsxBaseExtractor {
28816
28914
  /**
28817
28915
  * Get the list of all the XLSX files in the XLSX file structure
28818
28916
  */
28819
- getListOfFiles() {
28820
- const files = Object.values(this.xlsxFileStructure).flat().filter(isDefined$1);
28821
- return files;
28917
+ getListOfXMLFiles() {
28918
+ const XMLFiles = Object.entries(this.xlsxFileStructure)
28919
+ .filter(([key]) => key !== "images")
28920
+ .map(([_, value]) => value)
28921
+ .flat()
28922
+ .filter(isDefined$1);
28923
+ return XMLFiles;
28822
28924
  }
28823
28925
  /**
28824
28926
  * Return an array containing the return value of the given function applied to all the XML elements
@@ -28990,20 +29092,31 @@ class XlsxBaseExtractor {
28990
29092
  return color;
28991
29093
  }
28992
29094
  /**
28993
- * Returns the xlsx file targeted by a relationship.
29095
+ * Returns the xml file targeted by a relationship.
28994
29096
  */
28995
29097
  getTargetXmlFile(relationship) {
28996
29098
  if (!relationship)
28997
29099
  throw new Error("Undefined target file");
28998
- let target = relationship.target;
28999
- target = target.replace("../", "");
29000
- target = target.replace("./", "");
29100
+ const target = this.processRelationshipTargetName(relationship.target);
29001
29101
  // Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
29002
- const f = this.getListOfFiles().find((f) => f.file.fileName.endsWith(target));
29102
+ const f = this.getListOfXMLFiles().find((f) => f.file.fileName.endsWith(target));
29003
29103
  if (!f || !f.file)
29004
29104
  throw new Error("Cannot find target file");
29005
29105
  return f;
29006
29106
  }
29107
+ /**
29108
+ * Returns the image parameters targeted by a relationship.
29109
+ */
29110
+ getTargetImageFile(relationship) {
29111
+ if (!relationship)
29112
+ throw new Error("Undefined target file");
29113
+ const target = this.processRelationshipTargetName(relationship.target);
29114
+ // Use "endsWith" because targets are relative paths, and we know the files by their absolute path.
29115
+ const f = this.xlsxFileStructure.images.find((f) => f.fileName.endsWith(target));
29116
+ if (!f)
29117
+ throw new Error("Cannot find target file");
29118
+ return f;
29119
+ }
29007
29120
  /**
29008
29121
  * Wrapper of querySelector, but we'll remove the namespaces from the query if areNamespacesIgnored is true.
29009
29122
  *
@@ -29050,6 +29163,10 @@ class XlsxBaseExtractor {
29050
29163
  return clrScheme[colorId].value;
29051
29164
  }
29052
29165
  }
29166
+ /** Remove signs of relative path. */
29167
+ processRelationshipTargetName(targetName) {
29168
+ return targetName.replace(/\.+\//, "");
29169
+ }
29053
29170
  }
29054
29171
 
29055
29172
  /**
@@ -29279,15 +29396,16 @@ class XlsxFigureExtractor extends XlsxBaseExtractor {
29279
29396
  throw new Error("Only twoCellAnchor are supported for xlsx drawings.");
29280
29397
  }
29281
29398
  const chartElement = this.querySelector(figureElement, "c:chart");
29282
- if (!chartElement) {
29283
- throw new Error("Only chart figures are currently supported.");
29399
+ const imageElement = this.querySelector(figureElement, "a:blip");
29400
+ if (!chartElement && !imageElement) {
29401
+ throw new Error("Only chart and image figures are currently supported.");
29284
29402
  }
29285
29403
  return {
29286
29404
  anchors: [
29287
29405
  this.extractFigureAnchor("xdr:from", figureElement),
29288
29406
  this.extractFigureAnchor("xdr:to", figureElement),
29289
29407
  ],
29290
- data: this.extractChart(chartElement),
29408
+ data: chartElement ? this.extractChart(chartElement) : this.extractImage(figureElement),
29291
29409
  };
29292
29410
  });
29293
29411
  }
@@ -29312,6 +29430,24 @@ class XlsxFigureExtractor extends XlsxBaseExtractor {
29312
29430
  }
29313
29431
  return chartDefinition;
29314
29432
  }
29433
+ extractImage(figureElement) {
29434
+ const imageElement = this.querySelector(figureElement, "a:blip");
29435
+ const imageId = this.extractAttr(imageElement, "r:embed", { required: true }).asString();
29436
+ const image = this.getTargetImageFile(this.relationships[imageId]);
29437
+ if (!image) {
29438
+ throw new Error("Unable to extract image");
29439
+ }
29440
+ const shapePropertyElement = this.querySelector(figureElement, "a:xfrm");
29441
+ const extension = image.fileName.split(".").at(-1);
29442
+ return {
29443
+ imageSrc: image.imageSrc,
29444
+ mimetype: extension ? IMAGE_EXTENSION_TO_MIMETYPE_MAPPING[extension] : undefined,
29445
+ size: {
29446
+ cx: this.extractChildAttr(shapePropertyElement, "a:ext", "cx", { required: true }).asNum(),
29447
+ cy: this.extractChildAttr(shapePropertyElement, "a:ext", "cy", { required: true }).asNum(),
29448
+ },
29449
+ };
29450
+ }
29315
29451
  }
29316
29452
 
29317
29453
  /**
@@ -29881,14 +30017,22 @@ const EXCEL_IMPORT_VERSION = 12;
29881
30017
  class XlsxReader {
29882
30018
  warningManager;
29883
30019
  xmls;
30020
+ images;
29884
30021
  constructor(files) {
29885
30022
  this.warningManager = new XLSXImportWarningManager();
29886
30023
  this.xmls = {};
30024
+ this.images = [];
29887
30025
  for (let key of Object.keys(files)) {
29888
30026
  // Random files can be in xlsx (like a bin file for printer settings)
29889
30027
  if (key.endsWith(".xml") || key.endsWith(".rels")) {
29890
30028
  this.xmls[key] = parseXML(new XMLString(files[key]));
29891
30029
  }
30030
+ else if (key.includes("media/image")) {
30031
+ this.images.push({
30032
+ fileName: key,
30033
+ imageSrc: files[key]["imageSrc"],
30034
+ });
30035
+ }
29892
30036
  }
29893
30037
  }
29894
30038
  convertXlsx() {
@@ -29941,6 +30085,7 @@ class XlsxReader {
29941
30085
  tables: getXLSXFilesOfType(CONTENT_TYPES.table, this.xmls),
29942
30086
  pivots: getXLSXFilesOfType(CONTENT_TYPES.pivot, this.xmls),
29943
30087
  externalLinks: getXLSXFilesOfType(CONTENT_TYPES.externalLink, this.xmls),
30088
+ images: this.images,
29944
30089
  };
29945
30090
  if (!xlsxFileStructure.workbook.rels) {
29946
30091
  throw Error(_lt("Cannot find workbook relations file"));
@@ -33091,7 +33236,7 @@ class ImagePlugin extends CorePlugin {
33091
33236
  handle(cmd) {
33092
33237
  switch (cmd.type) {
33093
33238
  case "CREATE_IMAGE":
33094
- this.addFigure(cmd.figureId, cmd.sheetId, cmd.position, cmd.size);
33239
+ this.addImage(cmd.figureId, cmd.sheetId, cmd.position, cmd.size);
33095
33240
  this.history.update("images", cmd.sheetId, cmd.figureId, cmd.definition);
33096
33241
  this.syncedImages.add(cmd.definition.path);
33097
33242
  break;
@@ -33155,7 +33300,7 @@ class ImagePlugin extends CorePlugin {
33155
33300
  // ---------------------------------------------------------------------------
33156
33301
  // Private
33157
33302
  // ---------------------------------------------------------------------------
33158
- addFigure(id, sheetId, position, size) {
33303
+ addImage(id, sheetId, position, size) {
33159
33304
  const figure = {
33160
33305
  id,
33161
33306
  x: position.x,
@@ -35662,7 +35807,6 @@ class PositionBitsEncoder {
35662
35807
  }
35663
35808
  }
35664
35809
 
35665
- const functions = functionRegistry.content;
35666
35810
  //#region
35667
35811
  // ---------------------------------------------------------------------------
35668
35812
  // INTRODUCTION
@@ -35884,9 +36028,7 @@ class EvaluationPlugin extends UIPlugin {
35884
36028
  let isExported = true;
35885
36029
  const formulaCell = this.getCorrespondingFormulaCell(position);
35886
36030
  if (formulaCell) {
35887
- isExported = formulaCell.compiledFormula.tokens
35888
- .filter((tk) => tk.type === "FUNCTION")
35889
- .every((tk) => functions[tk.value.toUpperCase()].isExported);
36031
+ isExported = isExportableToExcel(formulaCell.compiledFormula.tokens);
35890
36032
  isFormula = isExported;
35891
36033
  if (!isExported) {
35892
36034
  newContent = value.toString();
@@ -44292,7 +44434,7 @@ class ImageProvider {
44292
44434
  async requestImage() {
44293
44435
  const file = await this.getImageFromUser();
44294
44436
  const path = await this.fileStore.upload(file);
44295
- const size = await this.getImageSize(path);
44437
+ const size = await this.getImageOriginalSize(path);
44296
44438
  return { path, size, mimetype: file.type };
44297
44439
  }
44298
44440
  getImageFromUser() {
@@ -44311,7 +44453,7 @@ class ImageProvider {
44311
44453
  input.click();
44312
44454
  });
44313
44455
  }
44314
- getImageSize(path) {
44456
+ getImageOriginalSize(path) {
44315
44457
  return new Promise((resolve, reject) => {
44316
44458
  const image = new Image();
44317
44459
  image.src = path;
@@ -45796,6 +45938,10 @@ css /* scss */ `
45796
45938
  top: 20%;
45797
45939
  }
45798
45940
  }
45941
+
45942
+ .user-select-text {
45943
+ user-select: text;
45944
+ }
45799
45945
  `;
45800
45946
  class TopBarComposer extends owl.Component {
45801
45947
  static template = "o-spreadsheet-TopBarComposer";
@@ -48834,6 +48980,8 @@ function createImageDrawing(figure, sheet, imageRelId) {
48834
48980
  ["name", `Image ${imageId}`],
48835
48981
  ["title", "Image"],
48836
48982
  ];
48983
+ const cx = convertDotValueToEMU(figure.width);
48984
+ const cy = convertDotValueToEMU(figure.height);
48837
48985
  return escapeXml /*xml*/ `
48838
48986
  <xdr:twoCellAnchor editAs="oneCell">
48839
48987
  <xdr:from>
@@ -48860,6 +49008,9 @@ function createImageDrawing(figure, sheet, imageRelId) {
48860
49008
  </a:stretch>
48861
49009
  </xdr:blipFill>
48862
49010
  <xdr:spPr>
49011
+ <a:xfrm>
49012
+ <a:ext cx="${cx}" cy="${cy}" />
49013
+ </a:xfrm>
48863
49014
  <a:prstGeom prst="rect">
48864
49015
  <a:avLst/>
48865
49016
  </a:prstGeom>
@@ -49379,7 +49530,7 @@ function createWorksheets(data, construct) {
49379
49530
  const mimeType = image.data.mimetype;
49380
49531
  if (mimeType === undefined)
49381
49532
  continue;
49382
- const extension = IMAGE_MIMETYPE_EXTENSION_MAPPING[mimeType];
49533
+ const extension = IMAGE_MIMETYPE_TO_EXTENSION_MAPPING[mimeType];
49383
49534
  // only support exporting images with mimetypes specified in the mapping
49384
49535
  if (extension === undefined)
49385
49536
  continue;
@@ -49515,7 +49666,7 @@ function createRelsFiles(relsFiles) {
49515
49666
  function createContentTypes(files) {
49516
49667
  const overrideNodes = [];
49517
49668
  // hard-code supported image mimetypes
49518
- const imageDefaultNodes = Object.entries(IMAGE_MIMETYPE_EXTENSION_MAPPING).map(([mimetype, extension]) => createDefaultXMLElement(extension, mimetype));
49669
+ const imageDefaultNodes = Object.entries(IMAGE_MIMETYPE_TO_EXTENSION_MAPPING).map(([mimetype, extension]) => createDefaultXMLElement(extension, mimetype));
49519
49670
  for (const file of files) {
49520
49671
  if ("contentType" in file && file.contentType) {
49521
49672
  overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
@@ -50145,6 +50296,7 @@ exports.helpers = helpers;
50145
50296
  exports.invalidateCFEvaluationCommands = invalidateCFEvaluationCommands;
50146
50297
  exports.invalidateDependenciesCommands = invalidateDependenciesCommands;
50147
50298
  exports.invalidateEvaluationCommands = invalidateEvaluationCommands;
50299
+ exports.iterateAstNodes = iterateAstNodes;
50148
50300
  exports.links = links;
50149
50301
  exports.load = load;
50150
50302
  exports.parse = parse;
@@ -50155,6 +50307,6 @@ exports.setTranslationMethod = setTranslationMethod;
50155
50307
  exports.tokenize = tokenize;
50156
50308
 
50157
50309
 
50158
- __info__.version = '16.4.0-alpha.6';
50159
- __info__.date = '2023-06-28T11:40:12.377Z';
50160
- __info__.hash = '7d15d3c';
50310
+ __info__.version = '16.4.0-alpha.7';
50311
+ __info__.date = '2023-07-04T13:46:24.130Z';
50312
+ __info__.hash = 'b04cf78';