{ Included by fpspreadsheet.pas } { Code for embedded objects (images) } {==============================================================================} { TsWorksheet code for embedded objects } {==============================================================================} {@@ ---------------------------------------------------------------------------- Calculates the position of the image with given index relative to the cell containing the top/left corner of the image. @param x worksheet-relative coordinate of the left image edge, in workbook units @param y worksheet-relative coordinate of the top image edge, in workbook units @param ARow Index of the row containing the top/left corner of the image @param ACol Index of the column containing the top/left corner of the image @param ARowOffset Distance, in workbook units, between top cell and image borders @param AColOffset Distance, in workbook units, between left cell and image borders @param AScaleX Scaling factor for the image width @param AScaleY Scaling factor for the image height -------------------------------------------------------------------------------} procedure TsWorksheet.CalcImageCell(AIndex: Integer; x, y, AWidth, AHeight: Double; out ARow, ACol: Cardinal; out ARowOffs, AColOffs, AScaleX, AScaleY: Double); // All lengths are in workbook units! var embObj: TsEmbeddedObj; begin CalcObjectCell(x, y, AWidth, AHeight, ARow, ACol, ARowOffs, AColOffs); embObj := FWorkbook.GetEmbeddedObj(AIndex); AScaleX := AWidth / embObj.ImageWidth; AScaleY := AHeight / embObj.ImageHeight; end; { Calculates the row/col indices of the top/left corner of an embedded object or chart (...) for which the x and y coordinates are given in millimeters. ARow, ACol are the row and column indices found, and ARowOffs and AColOffs denote the distance to the top/left corner of this particular cell (in mm). } procedure TsWorksheet.CalcObjectCell(x, y, AWidth, AHeight: Double; out ARow, ACol: Cardinal; out ARowOffs, AColOffs: Double); var colW, rowH, sum: Double; begin ACol := 0; sum := 0; colW := GetColWidth(0, FWorkbook.Units); while (sum + colW < x) do begin sum := sum + colW; inc(ACol); colW := GetColWidth(ACol, FWorkbook.Units); end; AColOffs := x - sum; ARow := 0; sum := 0; rowH := CalcRowHeight(0); while (sum + rowH < y) do begin sum := sum + rowH; inc(ARow); rowH := CalcRowHeight(ARow); end; ARowOffs := y - sum; end; {@@ ---------------------------------------------------------------------------- Calculates image extent @param AIndex Index of the image into the worksheet's image list @param UsePixels If @TRUE then pixels are used for calculation - this improves the display of the images in Excel @param ARow1 Index of the row containing the top edge of the image @param ACol1 Index of the column containing the left edege of the image @param ARow2 Index of the row containing the right edge of the image @param ACol2 Index of the column containing the bottom edge of the image @param ARowOffs1 Distance between the top edge of image and row 1 @param AColOffs1 Distance between the left edge of image and column 1 @param ARowOffs2 Distance between the bottom edge of image and top of row 2 @param AColOffs2 Distance between the right edge of image and left of col 2 @param x Absolute coordinate of left edge of image @param y Absolute coordinate of top edge of image @param AWidth Width of the image @param AHeight Height of the image All dimensions are in workbook units -------------------------------------------------------------------------------} procedure TsWorksheet.CalcImageExtent(AIndex: Integer;// UsePixels: Boolean; out ARow1, ACol1, ARow2, ACol2: Cardinal; out ARowOffs1, AColOffs1, ARowOffs2, AColOffs2: Double; out x,y, AWidth, AHeight: Double); var img: TsImage; obj: TsEmbeddedObj; begin img := GetImage(AIndex); ARow1 := img.Row; ACol1 := img.Col; AColOffs1 := img.OffsetX; // in workbook units ARowOffs1 := img.OffsetY; // in workbook units obj := FWorkbook.GetEmbeddedObj(img.Index); AWidth := obj.ImageWidth * img.ScaleX; // in workbook units AHeight := obj.ImageHeight * img.ScaleY; // in workbook units CalcDrawingExtent(AWidth, AHeight, ARow1, ACol1, ARow2, ACol2, ARowOffs1, AColOffs1, ARowOffs2, AColOffs2, x, y); end; {@@ ---------------------------------------------------------------------------- Returns the parameters of the image stored in the internal image list at the specified index. @param AIndex Index of the image to be retrieved @returns @link(TsImage) record with all image parameters. -------------------------------------------------------------------------------} function TsWorksheet.GetImage(AIndex: Integer): TsImage; var img: PsImage; begin img := PsImage(FImages[AIndex]); Result := img^; end; {@@ ---------------------------------------------------------------------------- Returns the count of images that are embedded into this sheet. -------------------------------------------------------------------------------} function TsWorksheet.GetImageCount: Integer; begin Result := FImages.Count; end; {@@ ---------------------------------------------------------------------------- Returns a pointer to the image record at the specified index. @param AIndex Index of the image to be considered. -------------------------------------------------------------------------------} function TsWorksheet.GetPointerToImage(AIndex: Integer): PsImage; begin Result := PsImage(FImages[AIndex]); end; {@@ ---------------------------------------------------------------------------- Removes all images from the internal image list. The image streams (stored by the workbook), however, are retained because images may also be used as header/footer images. -------------------------------------------------------------------------------} procedure TsWorksheet.RemoveAllImages; var i: Integer; begin for i := FImages.Count-1 downto 0 do RemoveImage(i); end; {@@ ---------------------------------------------------------------------------- Removes an image from the internal image list. The image is identified by its index. The image stream (stored by the workbook) is retained. -------------------------------------------------------------------------------} procedure TsWorksheet.RemoveImage(AIndex: Integer); var img: PsImage; begin img := PsImage(FImages[AIndex]); if (img <> nil) then begin if (img^.Picture <> nil) then img^.Picture.Free; img^.HyperlinkTarget := ''; img^.HyperlinkToolTip := ''; end; Dispose(img); FImages.Delete(AIndex); end; {@@ ---------------------------------------------------------------------------- Adds an embedded image to the worksheet @param ARow Index of the row at which the image begins (top edge) @param ACol Index of the column at which the image begins (left edge) @param AFileName Name of the image file @param AOffsetX The image is offset horizontally from the left edge of the anchor cell. May reach into another cell. Value is in workbook units. @param AOffsetY The image is offset vertically from the top edge of the anchor cell. May reach into another cell. Value is in workbook units. @param AScaleX Horizontal scaling factor of the image @param AScaleY Vertical scaling factor of the image @returns Index into the internal image list. -------------------------------------------------------------------------------} function TsWorksheet.WriteImage(ARow, ACol: Cardinal; AFileName: String; AOffsetX: Double = 0.0; AOffsetY: Double = 0.0; AScaleX: Double = 1.0; AScaleY: Double = 1.0): Integer; var idx: Integer; begin // Does the image already exist? idx := Workbook.FindEmbeddedObj(AFileName); // No? Open and store in embedded object list. if idx = -1 then idx := Workbook.AddEmbeddedObj(AFileName); // An error has occured? Error is already logged. Just exit. if idx = -1 then exit; // Everything ok here... Result := WriteImage(ARow, ACol, idx, AOffsetX, AOffsetY, AScaleX, AScaleY); end; {@@ ---------------------------------------------------------------------------- Adds an embedded image to the worksheet. The image is passed in a stream. @param ARow Index of the row at which the image begins (top edge) @param ACol Index of the column at which the image begins (left edge) @param AStream Stream which contains the image data @param AOffsetX The image is offset horizontally from the left edge of the anchor cell. May reach into another cell. Value is in workbook units. @param AOffsetY The image is offset vertically from the top edge of the anchor cell. May reach into another cell. Value is in workbook units. @param AScaleX Horizontal scaling factor of the image @param AScaleY Vertical scaling factor of the image @param ASize Number of bytes to be read from the input stream. @returns Index into the internal image list. -------------------------------------------------------------------------------} function TsWorksheet.WriteImage(ARow, ACol: Cardinal; AStream: TStream; AOffsetX: Double = 0.0; AOffsetY: Double = 0.0; AScaleX: Double = 1.0; AScaleY: Double = 1.0; ASize: Int64 = -1): Integer; var idx: Integer; begin // Copy the stream to a new item in embedded object list. idx := Workbook.AddEmbeddedObj(AStream, '', ASize); // An error has occured? Error is already logged. Just exit. if idx = -1 then exit; // Everything ok here... Result := WriteImage(ARow, ACol, idx, AOffsetX, AOffsetY, AScaleX, AScaleY); end; function TsWorksheet.WriteImage(ARow, ACol: Cardinal; AImageIndex: Integer; AOffsetX: Double = 0.0; AOffsetY: Double = 0.0; AScaleX: Double = 1.0; AScaleY: Double = 1.0; ARotationAngle: Double = 0.0): Integer; var img: PsImage; begin New(img); InitImageRecord(img^, ARow, ACol, AOffsetX, AOffsetY, AScaleX, AScaleY, ARotationAngle); img^.Index := AImageIndex; Result := FImages.Add(img); end; {==============================================================================} { TsWorkbook code for embedded objects } {==============================================================================} {@@ ---------------------------------------------------------------------------- Creates a new "embedded" stream and loads the specified file. Returns the index of the embedded file item. Image dimensions are converted to workbook units. -------------------------------------------------------------------------------} function TsWorkbook.AddEmbeddedObj(const AFileName: String): Integer; var obj: TsEmbeddedObj = nil; begin if not FileExists(AFileName) then begin AddErrorMsg(rsFileNotFound, [AFileName]); Result := -1; exit; end; obj := TsEmbeddedObj.Create; if obj.LoadFromFile(AFileName) then begin obj.ImageWidth := ConvertUnits(obj.ImageWidth, suInches, FUnits); obj.ImageHeight := ConvertUnits(obj.ImageHeight, suInches, FUnits); Result := FEmbeddedObjList.Add(obj); end else begin AddErrorMsg(rsFileFormatNotSupported, [AFileName]); obj.Free; Result := -1; end; end; {@@ ---------------------------------------------------------------------------- Creates a new "embedded" stream and copies the specified stream to it. Returns the index of the embedded object. -------------------------------------------------------------------------------} function TsWorkbook.AddEmbeddedObj(AStream: TStream; const AName: String = ''; ASize: Int64 = -1): Integer; var obj: TsEmbeddedObj = nil; begin obj := TsEmbeddedObj.Create; if obj.LoadFromStream(AStream, AName, ASize) then begin obj.ImageWidth := ConvertUnits(obj.ImageWidth, suInches, FUnits); obj.ImageHeight := ConvertUnits(obj.ImageHeight, suInches, FUnits); obj.Stream.Position := 0; Result := FEmbeddedObjList.Add(obj) end else begin AddErrorMsg(rsImageFormatNotSupported); obj.Free; Result := -1; end; end; {@@ ---------------------------------------------------------------------------- Checks whether an embedded object with the specified file name already exists. If yes, returns its index in the object list, or -1 if no. -------------------------------------------------------------------------------} function TsWorkbook.FindEmbeddedObj(const AFileName: String): Integer; var obj: TsEmbeddedObj; begin for Result:=0 to FEmbeddedObjList.Count-1 do begin obj := TsEmbeddedObj(FEmbeddedObjList[Result]); if obj.FileName = AFileName then exit; end; Result := -1; end; {@@ ---------------------------------------------------------------------------- Returns the embedded object stored in the embedded object list at the specified index. -------------------------------------------------------------------------------} function TsWorkbook.GetEmbeddedObj(AIndex: Integer): TsEmbeddedObj; begin Result := TsEmbeddedObj(FEmbeddedObjList[AIndex]); end; {@@ ---------------------------------------------------------------------------- Returns the count of embedded objects -------------------------------------------------------------------------------} function TsWorkbook.GetEmbeddedObjCount: Integer; begin Result := FEmbeddedObjList.Count; end; {@@ ---------------------------------------------------------------------------- Returns @true if there is at least one worksheet with an embedded images. -------------------------------------------------------------------------------} function TsWorkbook.HasEmbeddedSheetImages: Boolean; var i: Integer; sheet: TsWorksheet; begin Result := true; for i:=0 to FWorksheets.Count-1 do begin sheet := TsWorksheet(FWorksheets.Items[i]); if sheet.GetImageCount > 0 then exit; end; Result := false; end; {@@ ---------------------------------------------------------------------------- Removes all embedded objects -------------------------------------------------------------------------------} procedure TsWorkbook.RemoveAllEmbeddedObj; var i: Integer; begin for i:= 0 to FEmbeddedObjList.Count-1 do TsEmbeddedObj(FEmbeddedObjList[i]).Free; FEmbeddedObjList.Clear; end;