From 9b1fe48c8315d4d9b227ccbb51d76c87aed35526 Mon Sep 17 00:00:00 2001 From: wp_xxyyzz Date: Thu, 22 May 2025 17:07:19 +0000 Subject: [PATCH] fpspreadsheet: Support reading of rotated images from ods files. git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@9753 8e941d3f-bd1b-0410-a28a-d453659cc2b4 --- .../source/common/fpsopendocument.pas | 65 ++++++++++++++++--- .../source/common/fpsopendocumentchart.pas | 37 ----------- .../source/common/fpsxmlcommon.pas | 47 ++++++++++++++ .../fpspreadsheet/source/common/xlscommon.pas | 23 ++++--- 4 files changed, 114 insertions(+), 58 deletions(-) diff --git a/components/fpspreadsheet/source/common/fpsopendocument.pas b/components/fpspreadsheet/source/common/fpsopendocument.pas index c6f136a00..2ed461d10 100644 --- a/components/fpspreadsheet/source/common/fpsopendocument.pas +++ b/components/fpspreadsheet/source/common/fpsopendocument.pas @@ -5111,16 +5111,59 @@ procedure TsSpreadOpenDocReader.ReadShape(ANode: TDOMNode; ARow: Cardinal = UNASSIGNED_ROW_COL_INDEX; ACol: Cardinal = UNASSIGNED_ROW_COL_INDEX); + // Example for s: skewX (7.75940943781828E-017) rotate (1.0471975511966) translate (0.014cm -0.023cm) + procedure AnalyzeTransform(s: String; var SkewX, RotAngle, dX, dY: Double); + var + sa: TStringArray; + i: Integer; + valueStr: String; + relative: Boolean; + begin + SkewX := 0.0; + RotAngle := 0.0; + dX := 0.0; + dy := 0.0; + sa := s.Split(' '); + for i := 0 to High(sa) do + case sa[i] of + 'skewX': + begin + valueStr := Copy(sa[i+1], 2, Length(sa[+1])-2); + SkewX := StrToFloatDef(valueStr, 0.0, FPointSeparatorSettings); + end; + 'rotate': + begin + valueStr := Copy(sa[i+1], 2, Length(sa[+1])-2); + RotAngle := RadToDeg(StrToFloatDef(valueStr, 0.0, FPointSeparatorSettings)); + end; + 'translate': + begin + valueStr := Copy(sa[i+1], 2, Length(sa[+1])-2); + EvalLengthStr(valueStr, dX, relative); + valueStr := Copy(sa[i+2], 2, Length(sa[+2])-2); + EvalLengthStr(valueStr, dX, relative); + end; + end; + end; + procedure ReadDrawFrame(ANode: TDOMNode; AHLink: String); var r, c: Cardinal; - x, y, w, h: Double; - nodeName: String; + x: Double = 0.0; + y: Double = 0.0; + w: Double = 0.0; + h: Double = 0.0; dx: Double = 0.0; dy: Double = 0.0; sx: Double = 1.0; sy: Double = 1.0; + attr: String = ''; + transfSkew: Double = 0.0; + transfAngle: Double = 0.0; + transfDX: Double = 0.0; + transfDY: Double = 0.0; childNode: TDOMNode; + nodeName: String; i, idx: Integer; href: String; img: PsImage; @@ -5131,10 +5174,14 @@ procedure TsSpreadOpenDocReader.ReadShape(ANode: TDOMNode; {$ENDIF} begin nodeName := ANode.NodeName; - x := PtsToMM(HTMLLengthStrToPts(GetAttrValue(ANode, 'svg:x'))); - y := PtsToMM(HTMLLengthStrToPts(GetAttrValue(ANode, 'svg:y'))); - w := PtsToMM(HTMLLengthStrToPts(GetAttrValue(ANode, 'svg:width'))); - h := PtsToMM(HTMLLengthStrToPts(GetAttrValue(ANode, 'svg:height'))); + attr := GetAttrValue(ANode, 'svg:x'); + if attr <> '' then x := PtsToMM(HTMLLengthStrToPts(attr)); + attr := GetAttrValue(ANode, 'svg:y'); + if attr <> '' then y := PtsToMM(HTMLLengthStrToPts(attr)); + attr := GetAttrValue(ANode, 'svg:width'); + if attr <> '' then h := PtsToMM(HTMLLengthStrToPts(attr)); + attr := GetAttrValue(ANode, 'draw:transform'); + if attr <> '' then AnalyzeTransform(attr, transfSkew, transfAngle, transfDX, transfDY); childNode := ANode.FirstChild; while Assigned(childNode) do begin @@ -5185,10 +5232,10 @@ procedure TsSpreadOpenDocReader.ReadShape(ANode: TDOMNode; dx := x; end; idx := WriteImage(r, c, idx, dx, dy, sx, sy); - if AHLink <> '' then begin - img := GetPointerToImage(idx); + img := GetPointerToImage(idx); + img^.RotationAngle := transfAngle; + if AHLink <> '' then img^.HyperlinkTarget := AHLink; - end; end; end; end; diff --git a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas index 37004a091..4a3582795 100644 --- a/components/fpspreadsheet/source/common/fpsopendocumentchart.pas +++ b/components/fpspreadsheet/source/common/fpsopendocumentchart.pas @@ -267,43 +267,6 @@ begin end; end; -{ Extracts the length from an ods length string, e.g. "3.5cm" or "300%". In the - former case AValue become 35 (in millimeters), in the latter case AValue is - 300 and Relative becomes true } -function EvalLengthStr(AText: String; out AValue: Double; out Relative: Boolean): Boolean; -var - i: Integer; - res: Integer; - units: String; -begin - Result := false; - - if AText = '' then - exit; - - units := ''; - for i := Length(AText) downto 1 do - if AText[i] in ['%', 'm', 'c', 'p', 't', 'i', 'n'] then - begin - units := AText[i] + units; - Delete(AText, i, 1); - end; - Val(AText, AValue, res); - Result := (res = 0); - if res = 0 then - begin - Relative := false; - case units of - '%': Relative := true; - 'mm': ; - 'cm': AValue := AValue * 10; - 'pt': AValue := PtsToMM(AValue); - 'in': AValue := InToMM(AValue); - else Result := false; - end; - end; -end; - function ModifyColor(AColor: TsChartColor; AIntensity: double): TsChartColor; begin Result.Color := LumModOff(AColor.Color, AIntensity, 0.0); diff --git a/components/fpspreadsheet/source/common/fpsxmlcommon.pas b/components/fpspreadsheet/source/common/fpsxmlcommon.pas index 50b57f211..910ca2932 100644 --- a/components/fpspreadsheet/source/common/fpsxmlcommon.pas +++ b/components/fpspreadsheet/source/common/fpsxmlcommon.pas @@ -62,6 +62,8 @@ function CreateTempStream(AWorkbook: TsBasicWorkbook; AFileNameBase: String): TStream; procedure DestroyTempStream(AStream: TStream); +function EvalLengthStr(AText: String; out AValue: Double; out Relative: Boolean): Boolean; + implementation @@ -231,6 +233,51 @@ begin else Result := ''; end; + +{@@ ---------------------------------------------------------------------------- + Extracts the length from an ods length string, e.g. "3.5cm" or "300%". In the + ormer case AValue become 35 (in millimeters), in the latter case AValue is + 00 and Relative becomes true + + @param AText Length string to be analyzed, e.g. "3.5cm" + @param AValue Numerical value, in cm, or as integer percentage + @param Relative If true the parameter AValue is a percentage, otherwise a length in millimeters +-------------------------------------------------------------------------------} +function EvalLengthStr(AText: String; out AValue: Double; out Relative: Boolean): Boolean; +var + i: Integer; + res: Integer; + units: String; +begin + Result := false; + + if AText = '' then + exit; + + units := ''; + for i := Length(AText) downto 1 do + if AText[i] in ['%', 'm', 'c', 'p', 't', 'i', 'n'] then + begin + units := AText[i] + units; + Delete(AText, i, 1); + end; + Val(AText, AValue, res); + Result := (res = 0); + if res = 0 then + begin + Relative := false; + case units of + '%': Relative := true; + 'mm': ; + 'cm': AValue := AValue * 10; + 'pt': AValue := PtsToMM(AValue); + 'in': AValue := InToMM(AValue); + else Result := false; + end; + end; +end; + + {------------------------------------------------------------------------------} { Unzipping } {------------------------------------------------------------------------------} diff --git a/components/fpspreadsheet/source/common/xlscommon.pas b/components/fpspreadsheet/source/common/xlscommon.pas index 4ab3bfe55..26b92e5bb 100644 --- a/components/fpspreadsheet/source/common/xlscommon.pas +++ b/components/fpspreadsheet/source/common/xlscommon.pas @@ -342,20 +342,20 @@ type TDateMode = (dm1900, dm1904); //DATEMODE values, 5.28 - // Adjusts Excel float (date, date/time, time) with the file's base date to get a TDateTime - function ConvertExcelDateTimeToDateTime - (const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime; +// Adjusts Excel float (date, date/time, time) with the file's base date to get a TDateTime +function ConvertExcelDateTimeToDateTime + (const AExcelDateNum: Double; ADateMode: TDateMode): TDateTime; - // Adjusts TDateTime with the file's base date to get - // an Excel float value representing a time/date/datetime - function ConvertDateTimeToExcelDateTime - (const ADateTime: TDateTime; ADateMode: TDateMode): Double; +// Adjusts TDateTime with the file's base date to get +// an Excel float value representing a time/date/datetime +function ConvertDateTimeToExcelDateTime + (const ADateTime: TDateTime; ADateMode: TDateMode): Double; - // Converts the error byte read from cells or formulas to fps error value - function ConvertFromExcelError(AValue: Byte): TsErrorValue; +// Converts the error byte read from cells or formulas to fps error value +function ConvertFromExcelError(AValue: Byte): TsErrorValue; - // Converts an fps error value to the byte code needed in xls files - function ConvertToExcelError(AValue: TsErrorValue): byte; +// Converts an fps error value to the byte code needed in xls files +function ConvertToExcelError(AValue: TsErrorValue): byte; type { TsSheetData } @@ -5984,6 +5984,5 @@ begin WriteXF(AStream, TsWorkbook(Workbook).GetPointerToCellFormat(i), 0); end; - end.