From e47e969aeccc5e817fed179291d620175c654f57 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Tue, 20 Feb 2018 17:35:23 +0000 Subject: [PATCH] fpspreadsheet: Add WorkbookOption boAbortReadOnFormulaError. Off by default, i.e. Reader continues after finding an unknown formula. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6207 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../read_write/opendocdemo/opendocread.lpi | 12 ++- .../source/common/fpsopendocument.pas | 23 +++++- .../source/common/fpspreadsheet.pas | 19 +++-- .../fpspreadsheet/source/common/xlsbiff8.pas | 3 - .../fpspreadsheet/source/common/xlscommon.pas | 13 +++- .../fpspreadsheet/source/common/xlsxooxml.pas | 76 +++++++++++-------- 6 files changed, 95 insertions(+), 51 deletions(-) diff --git a/components/fpspreadsheet/examples/read_write/opendocdemo/opendocread.lpi b/components/fpspreadsheet/examples/read_write/opendocdemo/opendocread.lpi index 4b5dd6507..b0b744150 100644 --- a/components/fpspreadsheet/examples/read_write/opendocdemo/opendocread.lpi +++ b/components/fpspreadsheet/examples/read_write/opendocdemo/opendocread.lpi @@ -1,7 +1,7 @@ - + @@ -23,9 +23,16 @@ - + + + + + + + + @@ -46,6 +53,7 @@ + diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index c62c5ccfd..b900ec83e 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -2357,10 +2357,25 @@ begin // ... convert to Excel "A1" dialect used by fps by defailt parser := TsSpreadsheetParser.Create(FWorksheet); try - parser.Dialect := fdOpenDocument; - parser.LocalizedExpression[FPointSeparatorSettings] := formula; - parser.Dialect := fdExcelA1; - formula := parser.Expression; + try + parser.Dialect := fdOpenDocument; + parser.LocalizedExpression[FPointSeparatorSettings] := formula; + parser.Dialect := fdExcelA1; + formula := parser.Expression; + except + on E:EExprParser do + begin + Workbook.AddErrorMsg(E.Message); + formula := ''; + if (boAbortReadOnFormulaError in Workbook.Options) then raise; + end; + on E:ECalcEngine do + begin + Workbook.AddErrorMsg(E.Message); + formula := ''; + if (boAbortReadOnFormulaError in Workbook.Options) then raise; + end; + end; finally parser.Free; end; diff --git a/components/fpspreadsheet/source/common/fpspreadsheet.pas b/components/fpspreadsheet/source/common/fpspreadsheet.pas index 688dcea39..d14109524 100644 --- a/components/fpspreadsheet/source/common/fpspreadsheet.pas +++ b/components/fpspreadsheet/source/common/fpspreadsheet.pas @@ -654,8 +654,8 @@ type @param boVirtualMode If in virtual mode date are not taken from cells when a spreadsheet is written to file, but are provided by means of the event OnWriteCellData. - Similarly, when data are read they are not added as - cells but passed the the event OnReadCellData; + Similarly, when data are read they are not added + as cells but passed the the event OnReadCellData; @param boBufStream When this option is set a buffered stream is used for writing (a memory stream swapping to disk) or reading (a file stream pre-reading chunks of data @@ -663,19 +663,22 @@ type @param boFileStream Uses file streams and temporary files during reading and writing. Lowest memory consumptions, but slow. - @param boAutoCalc Automatically recalculate rpn formulas whenever - a cell value changes. + @param boAutoCalc Automatically recalculate formulas whenever a + cell value changes. @param boCalcBeforeSaving Calculates formulas before saving the file. Otherwise there are no results when the file is loaded back by fpspreadsheet. - @param boReadFormulas Allows to turn off reading of rpn formulas; this is - a precaution since formulas not correctly + @param boReadFormulas Allows to turn off reading of rpn formulas; this + is a precaution since formulas not correctly implemented by fpspreadsheet could crash the reading operation. @param boWriteZoomfactor Instructs the writer to write the current zoom - factors of the worksheets to file. } + factors of the worksheets to file. + @param boAbortReadOnFormulaError Aborts reading if a formula error is + encountered } TsWorkbookOption = (boVirtualMode, boBufStream, boFileStream, - boAutoCalc, boCalcBeforeSaving, boReadFormulas, boWriteZoomFactor); + boAutoCalc, boCalcBeforeSaving, boReadFormulas, boWriteZoomFactor, + boAbortReadOnFormulaError); {@@ Set of option flags for the workbook } TsWorkbookOptions = set of TsWorkbookOption; diff --git a/components/fpspreadsheet/source/common/xlsbiff8.pas b/components/fpspreadsheet/source/common/xlsbiff8.pas index 8433ff8a2..ea5a70fb7 100644 --- a/components/fpspreadsheet/source/common/xlsbiff8.pas +++ b/components/fpspreadsheet/source/common/xlsbiff8.pas @@ -1015,9 +1015,6 @@ begin finally OLEStorage.Free; end; - -// InternalReadFromStream(OLEStream); // wp: moved up - finally OLEStream.Free; end; diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas index 9cf2445f0..81f2c1eb2 100644 --- a/components/fpspreadsheet/source/common/xlscommon.pas +++ b/components/fpspreadsheet/source/common/xlscommon.pas @@ -1662,6 +1662,7 @@ var err: TsErrorValue; ok: Boolean; cell: PCell; + msg: String; begin { Index to XF Record } ReadRowColXF(AStream, ARow, ACol, XF); @@ -1720,7 +1721,17 @@ begin begin ok := ReadRPNTokenArray(AStream, cell); if not ok then - FWorksheet.WriteErrorValue(cell, errFormulaNotSupported); + begin + msg := Format(rsFormulaNotSupported, [ + GetCellString(ARow, ACol), '.xls' + ]); + if (boAbortReadOnFormulaError in Workbook.Options) then + raise Exception.Create(msg) + else begin + FWorksheet.WriteErrorValue(cell, errFormulaNotSupported); + FWorkbook.AddErrorMsg(msg); + end; + end; end; {Add attributes} diff --git a/components/fpspreadsheet/source/common/xlsxooxml.pas b/components/fpspreadsheet/source/common/xlsxooxml.pas index eb4beb8f4..bb0cbdd22 100644 --- a/components/fpspreadsheet/source/common/xlsxooxml.pas +++ b/components/fpspreadsheet/source/common/xlsxooxml.pas @@ -236,7 +236,7 @@ implementation uses variants, strutils, math, lazutf8, LazFileUtils, uriparser, - {%H-}fpsPatches, fpsCrypto, + {%H-}fpsPatches, fpsCrypto, fpsExprParser, fpsStrings, fpsStreams, fpsClasses, fpsImages; const @@ -696,42 +696,52 @@ begin begin // Formula to cell formulaStr := GetNodeValue(datanode); - - s := GetAttrValue(datanode, 't'); - if s = 'shared' then - begin - // Shared formula - s := GetAttrValue(datanode, 'ref'); - if (s <> '') then // This defines the shared formula range + try + s := GetAttrValue(datanode, 't'); + if s = 'shared' then begin - AWorksheet.WriteFormula(cell, formulaStr); - // We store the shared formula base in the SharedFormulaBaseList. - // The list index is identical with the 'si' attribute of the node. - sharedformulabase := TSharedFormulaData.Create; - sharedformulabase.Worksheet := FWorksheet; - sharedformulabase.Row := rowindex; - sharedformulabase.Col := colindex; - sharedformulabase.Formula := formulaStr; - FSharedFormulaBaseList.Add(sharedformulabase); - end else - begin - // Get index into the SharedFormulaBaseList... - s := GetAttrValue(datanode, 'si'); - if s <> '' then + // Shared formula + s := GetAttrValue(datanode, 'ref'); + if (s <> '') then // This defines the shared formula range begin - sharedformulabase := TSharedFormulaData(FSharedFormulaBaseList[StrToInt(s)]); - // ... and copy shared formula to destination cell - InitCell(FWorksheet, sharedformulabase.Row, sharedformulabase.Col, lCell); - lCell.Formulavalue := sharedformulabase.Formula; - lCell.Worksheet := sharedformulabase.Worksheet; - FWorksheet.CopyFormula(@lCell, cell); - cell^.ContentType := cctFormula; + AWorksheet.WriteFormula(cell, formulaStr); + // We store the shared formula base in the SharedFormulaBaseList. + // The list index is identical with the 'si' attribute of the node. + sharedformulabase := TSharedFormulaData.Create; + sharedformulabase.Worksheet := FWorksheet; + sharedformulabase.Row := rowindex; + sharedformulabase.Col := colindex; + sharedformulabase.Formula := formulaStr; + FSharedFormulaBaseList.Add(sharedformulabase); + end else + begin + // Get index into the SharedFormulaBaseList... + s := GetAttrValue(datanode, 'si'); + if s <> '' then + begin + sharedformulabase := TSharedFormulaData(FSharedFormulaBaseList[StrToInt(s)]); + // ... and copy shared formula to destination cell + InitCell(FWorksheet, sharedformulabase.Row, sharedformulabase.Col, lCell); + lCell.Formulavalue := sharedformulabase.Formula; + lCell.Worksheet := sharedformulabase.Worksheet; + FWorksheet.CopyFormula(@lCell, cell); + cell^.ContentType := cctFormula; + end; end; + end + else + // "Normal" formula + AWorksheet.WriteFormula(cell, formulaStr); + except + on E:EExprParser do begin + FWorkbook.AddErrorMsg(E.Message); + if (boAbortReadOnFormulaError in Workbook.Options) then raise; end; - end - else - // "Normal" formula - AWorksheet.WriteFormula(cell, formulaStr); + on E:ECalcEngine do begin + FWorkbook.AddErrorMsg(E.Message); + if (boAbortReadOnFormulaError in Workbook.Options) then raise; + end; + end; end; datanode := datanode.NextSibling; end;