diff --git a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr index cb054ec6f..13db944ae 100644 --- a/components/fpspreadsheet/examples/excel5demo/excel5write.lpr +++ b/components/fpspreadsheet/examples/excel5demo/excel5write.lpr @@ -89,6 +89,28 @@ begin MyWorksheet.WriteDateTime(5, 0, now); MyWorksheet.WriteFont(5, 0, 'Courier New', 20, [fssBold, fssItalic, fssUnderline], scBlue); + // F6 empty cell, only all thin borders + MyWorksheet.WriteBorders(5, 5, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderLineStyle(5, 5, cbSouth, lsDotted); + MyWorksheet.WriteBorderColor(5, 5, cbSouth, scRed); + MyWorksheet.WriteBorderLineStyle(5, 5, cbNorth, lsThick); + + // H6 empty cell, only all medium borders + MyWorksheet.WriteBorders(5, 7, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderColor(5, 7, cbSouth, scBlack); + MyWorksheet.WriteBorderLineStyle(5, 7, cbSouth, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbEast, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbWest, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbNorth, lsMedium); + + // J6 empty cell, only all thick borders + MyWorksheet.WriteBorders(5, 9, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderLineStyle(5, 9, cbSouth, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbEast, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbWest, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbNorth, lsThick); + + { Uncomment this to test large XLS files for i := 2 to 20 do begin diff --git a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr index dea9d9660..187f2dc3c 100644 --- a/components/fpspreadsheet/examples/excel8demo/excel8write.lpr +++ b/components/fpspreadsheet/examples/excel8demo/excel8write.lpr @@ -62,8 +62,43 @@ begin // E6 empty cell, only background color MyWorksheet.WriteBackgroundColor(5, 4, scYellow); - // E7 empty cell, only all borders + // F6 empty cell, all borders MyWorksheet.WriteBorders(5, 5, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderStyle(5, 5, cbSouth, lsDotted, scRed); + MyWorksheet.WriteBorderLineStyle(5, 5, cbNorth, lsThick); + + // H6 empty cell, all medium borders + MyWorksheet.WriteBorders(5, 7, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderColor(5, 7, cbSouth, scBlack); + MyWorksheet.WriteBorderLineStyle(5, 7, cbSouth, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbEast, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbWest, lsMedium); + MyWorksheet.WriteBorderLineStyle(5, 7, cbNorth, lsMedium); + + // J6 empty cell, all thick borders + MyWorksheet.WriteBorders(5, 9, [cbNorth, cbEast, cbSouth, cbWest]); + MyWorksheet.WriteBorderLineStyle(5, 9, cbSouth, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbEast, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbWest, lsThick); + MyWorksheet.WriteBorderLineStyle(5, 9, cbNorth, lsThick); + + // K6 empty cell, top border thick + MyWorksheet.WriteBorders(5, 11, [cbNorth]); + MyWorksheet.WriteBorderLineStyle(5, 11, cbNorth, lsThick); + + // L6 empty cell, bottom border medium + MyWorksheet.WriteBorders(5, 12, [cbSouth]); + MyWorksheet.WriteBorderLineStyle(5, 12, cbSouth, lsMedium); + + // M6 empty cell, top & bottom border dashed and dotted + MyWorksheet.WriteBorders(5, 13, [cbNorth, cbSouth]); + MyWorksheet.WriteBorderLineStyle(5, 13, cbNorth, lsDashed); + MyWorksheet.WriteBorderLineStyle(5, 13, cbSouth, lsDotted); + + // N6 empty cell, left border: double +// MyWorksheet.WriteBlank(5, 14); + MyWorksheet.WriteBorders(5, 14, [cbWest]); + MyWorksheet.WriteBorderLineStyle(5, 14, cbWest, lsDouble); // Word-wrapped long text in D7 MyWorksheet.WriteUTF8Text(6, 3, 'This is a very, very, very, very long wrapped text.'); diff --git a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi index 10b127a17..0d1d92593 100644 --- a/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi +++ b/components/fpspreadsheet/examples/fpsgrid/fpsgrid.lpi @@ -108,7 +108,7 @@ - + @@ -117,7 +117,7 @@ - + @@ -131,28 +131,28 @@ - + - - + - - + + + - - + + @@ -247,10 +247,12 @@ + - - + + + @@ -262,10 +264,10 @@ - + - - + + @@ -287,7 +289,7 @@ - + @@ -297,17 +299,17 @@ - + - - + + - + @@ -328,10 +330,12 @@ + - - + + + @@ -351,135 +355,140 @@ - + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - diff --git a/components/fpspreadsheet/fpspreadsheet.pas b/components/fpspreadsheet/fpspreadsheet.pas index 618e5f060..9cc46fda1 100755 --- a/components/fpspreadsheet/fpspreadsheet.pas +++ b/components/fpspreadsheet/fpspreadsheet.pas @@ -121,29 +121,23 @@ type {@@ RPN formula. Similar to the expanded formula, but in RPN notation. Simplifies the task of format writers which need RPN } - TsRPNFormula = array of TsFormulaElement; {@@ Describes the type of content of a cell on a TsWorksheet } - TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber, cctUTF8String, cctDateTime); {@@ List of possible formatting fields } - TsUsedFormattingField = (uffTextRotation, uffFont, uffBold, uffBorder, uffBackgroundColor, uffNumberFormat, uffWordWrap, uffHorAlign, uffVertAlign ); {@@ Describes which formatting fields are active } - TsUsedFormattingFields = set of TsUsedFormattingField; {@@ Number/cell formatting. Only uses a subset of the default formats, - enough to be able to read/write date values. - } - + enough to be able to read/write date values. } TsNumberFormat = (nfGeneral, nfFixed, nfFixedTh, nfExp, nfSci, nfPercentage, nfShortDateTime, nfFmtDateTime, nfShortDate, nfShortTime, nfLongTime, nfShortTimeAM, nfLongTimeAM, nfTimeInterval); @@ -163,18 +157,9 @@ type | B | A } - TsTextRotation = (trHorizontal, rt90DegreeClockwiseRotation, rt90DegreeCounterClockwiseRotation, rtStacked); - {@@ Indicates the border for a cell } - - TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth); - - {@@ Indicates the border for a cell } - - TsCellBorders = set of TsCellBorder; - {@@ Indicates horizontal and vertical text alignment in cells } TsHorAlignment = (haDefault, haLeft, haCenter, haRight); TsVertAlignment = (vaDefault, vaTop, vaCenter, vaBottom); @@ -183,8 +168,7 @@ type Colors in fpspreadsheet are given as indices into a palette. Use the workbook's GetPaletteColor to determine the color rgb value as little-endian (with "r" being the low-value byte, in agreement with TColor). - The data type for rgb values is TsColorValue. - } + The data type for rgb values is TsColorValue. } TsColor = Word; {@@ @@ -241,6 +225,32 @@ type Color: TsColor; end; + {@@ Indicates the border for a cell } + TsCellBorder = (cbNorth, cbWest, cbEast, cbSouth); + + {@@ Indicates the border for a cell } + TsCellBorders = set of TsCellBorder; + + {@@ Line style (for cell borders) } + TsLineStyle = (lsThin, lsMedium, lsDashed, lsDotted, lsThick, lsDouble); + + {@@ Cell border style } + TsCellBorderStyle = record + LineStyle: TsLineStyle; + Color: TsColor; + end; + + TsCellBorderStyles = array[TsCellBorder] of TsCellBorderStyle; + +const + DEFAULT_BORDERSTYLES: TsCellBorderStyles = ( + (LineStyle: lsThin; Color: scBlack), + (LineStyle: lsThin; Color: scBlack), + (LineStyle: lsThin; Color: scBlack), + (LineStyle: lsThin; Color: scBlack) + ); + +type {@@ Cell structure for TsWorksheet Never suppose that all *Value fields are valid, @@ -267,6 +277,7 @@ type HorAlignment: TsHorAlignment; VertAlignment: TsVertAlignment; Border: TsCellBorders; + BorderStyles: TsCelLBorderStyles; BackgroundColor: TsColor; NumberFormat: TsNumberFormat; NumberFormatStr: String; @@ -327,6 +338,7 @@ type function ReadUsedFormatting(ARow, ACol: Cardinal): TsUsedFormattingFields; function ReadBackgroundColor(ARow, ACol: Cardinal): TsColor; procedure RemoveAllCells; + { Writing of values } procedure WriteUTF8Text(ARow, ACol: Cardinal; AText: ansistring); procedure WriteNumber(ARow, ACol: Cardinal; ANumber: double; AFormat: TsNumberFormat = nfGeneral; ADecimals: Word = 2); @@ -334,8 +346,9 @@ type procedure WriteDateTime(ARow, ACol: Cardinal; AValue: TDateTime; AFormat: TsNumberFormat = nfShortDateTime; AFormatStr: String = ''); procedure WriteFormula(ARow, ACol: Cardinal; AFormula: TsFormula); - procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat); procedure WriteRPNFormula(ARow, ACol: Cardinal; AFormula: TsRPNFormula); + { Writing of cell attributes } + procedure WriteNumberFormat(ARow, ACol: Cardinal; ANumberFormat: TsNumberFormat); function WriteFont(ARow, ACol: Cardinal; const AFontName: String; AFontSize: Single; AFontStyle: TsFontStyles; AFontColor: TsColor): Integer; overload; procedure WriteFont(ARow, ACol: Cardinal; AFontIndex: Integer); overload; @@ -344,7 +357,15 @@ type procedure WriteTextRotation(ARow, ACol: Cardinal; ARotation: TsTextRotation); procedure WriteUsedFormatting(ARow, ACol: Cardinal; AUsedFormatting: TsUsedFormattingFields); procedure WriteBackgroundColor(ARow, ACol: Cardinal; AColor: TsColor); + procedure WriteBorderColor(ARow, ACol: Cardinal; ABorder: TsCellBorder; AColor: TsColor); + procedure WriteBorderLineStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; + ALineStyle: TsLineStyle); procedure WriteBorders(ARow, ACol: Cardinal; ABorders: TsCellBorders); + procedure WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; + AStyle: TsCellBorderStyle); overload; + procedure WriteBorderStyle(ARow, ACol: Cardinal; ABorder: TsCellBorder; + ALineStyle: TsLineStyle; AColor: TsColor); overload; + procedure WriteBorderStyles(ARow, ACol: Cardinal; const AStyles: TsCellBorderStyles); procedure WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment); procedure WriteVertAlignment(ARow, ACol: Cardinal; AValue: TsVertAlignment); procedure WriteWordwrap(ARow, ACol: Cardinal; AValue: boolean); @@ -886,6 +907,7 @@ begin Result^.Row := ARow; Result^.Col := ACol; + Result^.BorderStyles := DEFAULT_BORDERSTYLES; Cells.Add(Result); end; @@ -1451,6 +1473,30 @@ begin ACell^.BackgroundColor := AColor; end; +{ Sets the color of a cell border line. + Note: the border must be included in Borders set in order to be shown! } +procedure TsWorksheet.WriteBorderColor(ARow, ACol: Cardinal; + ABorder: TsCellBorder; AColor: TsColor); +var + lCell: PCell; +begin + lCell := GetCell(ARow, ACol); + lCell^.BorderStyles[ABorder].Color := AColor; +end; + +{ Sets the linestyle of a cell border. + Note: the border must be included in the "Borders" set in order to be shown! } +procedure TsWorksheet.WriteBorderLineStyle(ARow, ACol: Cardinal; + ABorder: TsCellBorder; ALineStyle: TsLineStyle); +var + lCell: PCell; +begin + lCell := GetCell(ARow, ACol); + lCell^.BorderStyles[ABorder].LineStyle := ALineStyle; +end; + +{ Shows the cell borders included in the set ABorders. The borders are drawn + using the "BorderStyles" assigned to the cell. } procedure TsWorksheet.WriteBorders(ARow, ACol: Cardinal; ABorders: TsCellBorders); var lCell: PCell; @@ -1460,6 +1506,41 @@ begin lCell^.Border := ABorders; end; +{ Sets the style of a cell border, i.e. line style and line color. + Note: the border must be included in the "Borders" set in order to be shown! } +procedure TsWorksheet.WriteBorderStyle(ARow, ACol: Cardinal; + ABorder: TsCellBorder; AStyle: TsCellBorderStyle); +var + lCell: PCell; +begin + lCell := GetCell(ARow, ACol); + lCell^.BorderStyles[ABorder] := AStyle; +end; + +{ Sets line style and line color of a cell border. + Note: the border must be included in the "Borders" set in order to be shown! } +procedure TsWorksheet.WriteBorderStyle(ARow, ACol: Cardinal; + ABorder: TsCellBorder; ALineStyle: TsLinestyle; AColor: TsColor); +var + lCell: PCell; +begin + lCell := GetCell(ARow, ACol); + lCell^.BorderStyles[ABorder].LineStyle := ALineStyle; + lCell^.BorderStyles[ABorder].Color := AColor; +end; + +{ Sets the style of all cell border of a cell, i.e. line style and line color. + Note: Only those borders included in the "Borders" set are shown! } +procedure TsWorksheet.WriteBorderStyles(ARow, ACol: Cardinal; + const AStyles: TsCellBorderStyles); +var + b: TsCellBorder; + cell: PCell; +begin + cell := GetCell(ARow, ACol); + for b in TsCellBorder do cell^.BorderStyles[b] := AStyles[b]; +end; + procedure TsWorksheet.WriteHorAlignment(ARow, ACol: Cardinal; AValue: TsHorAlignment); var lCell: PCell; @@ -2280,12 +2361,14 @@ begin end; {@@ - Checks if the style of a cell is in the list FFormattingStyles and returns the index - or -1 if it isn't + Checks if the style of a cell is in the list of manually added FFormattingStyles + and returns the index or -1 if it isn't } function TsCustomSpreadWriter.FindFormattingInList(AFormat: PCell): Integer; var i: Integer; + b: TsCellBorder; + equ: Boolean; begin Result := -1; @@ -2302,8 +2385,23 @@ begin if uffTextRotation in AFormat^.UsedFormattingFields then if (FFormattingStyles[i].TextRotation <> AFormat^.TextRotation) then Continue; - if uffBorder in AFormat^.UsedFormattingFields then + if uffBorder in AFormat^.UsedFormattingFields then begin if (FFormattingStyles[i].Border <> AFormat^.Border) then Continue; + equ := true; + for b in TsCellBorder do begin + if FFormattingStyles[i].BorderStyles[b].LineStyle <> AFormat^.BorderStyles[b].LineStyle + then begin + equ := false; + Break; + end; + if FFormattingStyles[i].BorderStyles[b].Color <> AFormat^.BorderStyles[b].Color + then begin + equ := false; + Break; + end; + end; + if not equ then Continue; + end; if uffBackgroundColor in AFormat^.UsedFormattingFields then if (FFormattingStyles[i].BackgroundColor <> AFormat^.BackgroundColor) then Continue; diff --git a/components/fpspreadsheet/fpspreadsheetgrid.pas b/components/fpspreadsheet/fpspreadsheetgrid.pas index e57237c5f..1fa2b850a 100644 --- a/components/fpspreadsheet/fpspreadsheetgrid.pas +++ b/components/fpspreadsheet/fpspreadsheetgrid.pas @@ -380,6 +380,54 @@ var cell: PCell; c, r: Integer; rect: TRect; + + procedure DrawBorderLine(ACell: PCell; ARect: TRect; ABorder: TsCellBorder; + ALineStyle: TsLineStyle); + const + // TsLineStyle = (lsThin, lsMedium, lsDashed, lsDotted, lsThick, lsDouble); + PEN_WIDTHS: array[TsLineStyle] of Byte = + (1, 2, 1, 1, 3, 1); + PEN_STYLES: array[TsLineStyle] of TPenStyle = + (psSolid, psSolid, psDash, psDot, psSolid, psSolid); +// (psSolid, psSolid, psPattern, psPattern, psSolid, psSolid); + PEN_PATTERNS: array[TsLineStyle] of TPenPattern = + ($FFFFFFFF, $FFFFFFFF, $07070707, $AAAAAAAA, $FFFFFFFF, $FFFFFFFF); + var + w: Integer; + begin + if ALineStyle = lsDouble then + case ABorder of + cbEast, cbWest: + begin + InflateRect(ARect, -1, 0); + DrawBorderLine(ACell, ARect, ABorder, lsThin); + InflateRect(ARect, +2, 0); + DrawBorderLine(ACell, ARect, ABorder, lsThin); + end; + cbNorth, cbSouth: + begin + InflateRect(ARect, 0, -1); + DrawBorderLine(ACell, ARect, ABorder, lsThin); + InflateRect(ARect, 0, +2); + DrawBorderLine(ACell, ARect, ABorder, lsThin) + end; + end + else begin + w := PEN_WIDTHS[ACell^.BorderStyles[ABorder].LineStyle] div 2; + Canvas.Pen.Style := PEN_STYLES[ACell^.BorderStyles[ABorder].LineStyle]; + Canvas.Pen.Width := PEN_WIDTHS[ACell^.BorderStyles[ABorder].LineStyle]; + Canvas.Pen.Color := FWorkBook.GetPaletteColor(ACell^.BorderStyles[ABorder].Color); + //Canvas.Pen.Pattern := PEN_PATTERNS[ACell^.BorderStyles[ABorder].LineStyle]; + //Canvas.Pen.EndCap := pecSquare; + case ABorder of + cbEast : Canvas.Line(ARect.Right-1, ARect.Top, ARect.Right-1, ARect.Bottom-w); + cbSouth: Canvas.Line(ARect.Left-1, ARect.Bottom-1, ARect.Right-w, ARect.Bottom-1); + cbWest : Canvas.Line(ARect.Left-1, ARect.Top, ARect.Left-1, ARect.Bottom-w); + cbNorth: Canvas.Line(ARect.Left-1, ARect.Top-1, ARect.Right-w, ARect.Top-1); + end; + end; + end; + begin inherited; if FWorksheet = nil then exit; @@ -390,16 +438,14 @@ begin c := cell^.Col + FixedCols; r := cell^.Row + FixedRows; rect := CellRect(c, r); - Canvas.Pen.Style := psSolid; - Canvas.Pen.Color := clBlack; if (cbNorth in cell^.Border) then - Canvas.Line(rect.Left, rect.Top-1, rect.Right, rect.Top-1); - if (cbWest in cell^.Border) then - Canvas.Line(rect.Left-1, rect.Top, rect.Left-1, rect.Bottom); - if (cbEast in cell^.Border) then - Canvas.Line(rect.Right-1, rect.Top, rect.Right-1, rect.Bottom); - if (cbSouth in cell^.Border) then - Canvas.Line(rect.Left, rect.Bottom-1, rect.Right, rect.Bottom-1); + DrawBorderLine(cell, rect, cbNorth, cell^.BorderStyles[cbNorth].LineStyle); + if cbEast in cell^.Border then + DrawBorderLine(cell, rect, cbEast, cell^.BorderStyles[cbEast].LineStyle); + if cbSouth in cell^.Border then + DrawBorderLine(cell, rect, cbSouth, cell^.BorderStyles[cbSouth].LineStyle); + if cbWest in cell^.Border then + DrawBorderLine(cell, rect, cbWest, cell^.BorderStyles[cbWest].LineStyle); end; cell := FWorksheet.GetNextCell; end; diff --git a/components/fpspreadsheet/tests/formattests.pas b/components/fpspreadsheet/tests/formattests.pas index 4a614e51c..f14d1bbaa 100644 --- a/components/fpspreadsheet/tests/formattests.pas +++ b/components/fpspreadsheet/tests/formattests.pas @@ -30,6 +30,8 @@ var SollColWidths: array[0..1] of Single; SollBorders: array[0..15] of TsCellBorders; + SollBorderLineStyles: array[0..6] of TsLineStyle; + SollBorderColors: array[0..5] of TsColor; procedure InitSollFmtData; @@ -47,6 +49,8 @@ type procedure TestWriteReadAlignment(AFormat: TsSpreadsheetFormat); // Test border procedure TestWriteReadBorder(AFormat: TsSpreadsheetFormat); + // Test border styles + procedure TestWriteReadBorderStyles(AFormat: TsSpreadsheetFormat); // Test column widths procedure TestWriteReadColWidths(AFormat: TsSpreadsheetFormat); // Test text rotation @@ -63,12 +67,14 @@ type procedure TestWriteReadBIFF2_Border; procedure TestWriteReadBIFF2_ColWidths; // These features are not supported by Excel2 --> no test cases required! + // - BorderStyle // - TextRotation // - Wordwrap { BIFF5 Tests } procedure TestWriteReadBIFF5_Alignment; procedure TestWriteReadBIFF5_Border; + procedure TestWriteReadBIFF5_BorderStyles; procedure TestWriteReadBIFF5_ColWidths; procedure TestWriteReadBIFF5_TextRotation; procedure TestWriteReadBIFF5_WordWrap; @@ -76,6 +82,7 @@ type { BIFF8 Tests } procedure TestWriteReadBIFF8_Alignment; procedure TestWriteReadBIFF8_Border; + procedure TestWriteReadBIFF8_BorderStyles; procedure TestWriteReadBIFF8_ColWidths; procedure TestWriteReadBIFF8_TextRotation; procedure TestWriteReadBIFF8_WordWrap; @@ -192,6 +199,21 @@ begin SollBorders[13] := [cbSouth, cbWest, cbNorth]; SollBorders[14] := [cbWest, cbNorth, cbEast]; SollBorders[15] := [cbEast, cbSouth, cbWest, cbNorth]; + + SollBorderLineStyles[0] := lsThin; + SollBorderLineStyles[1] := lsMedium; + SollBorderLineStyles[2] := lsThick; + SollBorderLineStyles[3] := lsThick; + SollBorderLineStyles[4] := lsDashed; + SollBorderLineStyles[5] := lsDotted; + SollBorderLineStyles[6] := lsDouble; + + SollBorderColors[0] := scBlue; + SollBorderColors[1] := scRed; + SollBorderColors[2] := scBlue; + SollBorderColors[3] := scGray; + SollBorderColors[4] := scSilver; + SollBorderColors[5] := scMagenta; end; { TSpreadWriteReadFormatTests } @@ -459,6 +481,107 @@ begin TestWriteReadBorder(sfExcel8); end; +procedure TSpreadWriteReadFormatTests.TestWriteReadBorderStyles(AFormat: TsSpreadsheetFormat); +{ This test paints 10x10 cells with all borders, each separated by an empty + column and an empty row. The border style varies from border to border + according to the line styles defined in SollBorderStyles. At first, all border + lines use the first color in SollBorderColors. When all BorderStyles are used + the next color is taken, etc. } +var + MyWorksheet: TsWorksheet; + MyWorkbook: TsWorkbook; + MyCell: PCell; + ActualColWidth: Single; + row, col: Integer; + b: TsCellBorder; + expected: Integer; + current: Integer; + TempFile: string; //write xls/xml to this file and read back from it + c, ls: Integer; +begin + TempFile:=GetTempFileName; + {// Not needed: use workbook.writetofile with overwrite=true + if fileexists(TempFile) then + DeleteFile(TempFile); + } + // Write out all test values + MyWorkbook := TsWorkbook.Create; + MyWorkSheet:= MyWorkBook.AddWorksheet(BordersSheet); + + c := 0; + ls := 0; + for row := 1 to 10 do begin + for col := 1 to 10 do begin + MyWorksheet.WriteBorders(row*2, col*2, [cbNorth, cbSouth, cbEast, cbWest]); + for b in TsCellBorders do begin + MyWorksheet.WriteBorderLineStyle(row*2, col*2, b, SollBorderLineStyles[ls]); + MyWorksheet.WriteBorderColor(row*2, col*2, b, SollBorderColors[c]); + inc(ls); + if ls > High(SollBorderLineStyles) then begin + ls := 0; + inc(c); + if c > High(SollBorderColors) then + c := 0; + end; + end; + end; + end; + + MyWorkBook.WriteToFile(TempFile, AFormat, true); + MyWorkbook.Free; + + // Open the spreadsheet + MyWorkbook := TsWorkbook.Create; + MyWorkbook.ReadFromFile(TempFile, AFormat); + if AFormat = sfExcel2 then + MyWorksheet := MyWorkbook.GetFirstWorksheet + else + MyWorksheet := GetWorksheetByName(MyWorkBook, BordersSheet); + if MyWorksheet=nil then + fail('Error in test code. Failed to get named worksheet'); + c := 0; + ls := 0; + for row := 1 to 10 do begin + for col := 1 to 10 do begin + MyCell := MyWorksheet.FindCell(row*2, col*2); + if myCell = nil then + fail('Error in test code. Failed to get cell.'); + for b in TsCellBorder do begin + current := ord(MyCell^.BorderStyles[b].LineStyle); + expected := ord(SollBorderLineStyles[ls]); + CheckEquals(current, expected, + 'Test saved border line style mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2)); + current := MyCell^.BorderStyles[b].Color; + expected := SollBorderColors[c]; + CheckEquals(current, expected, + 'Test saved border color mismatch, cell ' + CellNotation(MyWorksheet, row*2, col*2)); + inc(ls); + if ls > High(SollBorderLineStyles) then begin + ls := 0; + inc(c); + if c > High(SollBorderColors) then + c := 0; + end; + end; + end; + end; + + // Finalization + MyWorkbook.Free; + + DeleteFile(TempFile); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF5_BorderStyles; +begin + TestWriteReadBorderStyles(sfExcel5); +end; + +procedure TSpreadWriteReadFormatTests.TestWriteReadBIFF8_BorderStyles; +begin + TestWriteReadBorderStyles(sfExcel8); +end; + procedure TSpreadWriteReadFormatTests.TestWriteReadColWidths(AFormat: TsSpreadsheetFormat); var MyWorksheet: TsWorksheet; diff --git a/components/fpspreadsheet/xlsbiff5.pas b/components/fpspreadsheet/xlsbiff5.pas index 4d6b11e88..b35917ff5 100755 --- a/components/fpspreadsheet/xlsbiff5.pas +++ b/components/fpspreadsheet/xlsbiff5.pas @@ -129,9 +129,9 @@ type procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean); procedure WriteXF(AStream: TStream; AFontIndex: Word; AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders; - AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault; - AWordWrap: Boolean = false; AddBackground: Boolean = false; - ABackgroundColor: TsColor = scSilver); + const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault; + AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false; + AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver); procedure WriteXFFieldsForFormattingStyles(AStream: TStream); procedure WriteXFRecords(AStream: TStream); public @@ -318,10 +318,16 @@ const MASK_XF_BORDER_TOP = $00000007; MASK_XF_BORDER_BOTTOM = $01C00000; + { XF CELL BORDER COLORS } + MASK_XF_BORDER_LEFT_COLOR = $007F0000; + MASK_XF_BORDER_RIGHT_COLOR = $3F800000; + MASK_XF_BORDER_TOP_COLOR = $0000FE00; + MASK_XF_BORDER_BOTTOM_COLOR = $FE000000; + { XF CELL BACKGROUND } - MASK_XF_BKGR_PATTERN_COLOR = $0000007F; - MASK_XF_BKGR_BACKGROUND_COLOR = $00003F80; - MASK_XF_BKGR_FILLPATTERN = $003F0000; + MASK_XF_BKGR_PATTERN_COLOR = $0000007F; + MASK_XF_BKGR_BACKGROUND_COLOR = $00003F80; + MASK_XF_BKGR_FILLPATTERN = $003F0000; { TsSpreadBIFF5Writer } @@ -1049,13 +1055,11 @@ end; *******************************************************************} procedure TsSpreadBIFF5Writer.WriteXF(AStream: TStream; AFontIndex: Word; AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders; - AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault; - AWordWrap: Boolean = false; AddBackground: Boolean = false; - ABackgroundColor: TsColor = scSilver); + const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault; + AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false; + AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver); const FILL_PATTERN = 1; // solid fill - BORDER_LINE_STYLE = 1; // thin solid line - BORDER_COLOR = scBLACK; var optns: Word; b: Byte; @@ -1111,14 +1115,14 @@ begin end; // Border lines if cbSouth in ABorders then - dw1 := dw1 or (BORDER_LINE_STYLE shl 22); - dw1 := dw1 or (BORDER_COLOR shl 25); // Bottom line color - dw2 := (BORDER_COLOR shl 9) or // Top line color - (BORDER_COLOR shl 16) or // Left line color - (BORDER_COLOR shl 23); // Right line color - if cbNorth in ABorders then dw2 := dw2 or BORDER_LINE_STYLE; - if cbWest in ABorders then dw2 := dw2 or (BORDER_LINE_STYLE shl 3); - if cbEast in ABorders then dw2 := dw2 or (BORDER_LINE_STYLE shl 6); + dw1 := dw1 or ((ord(ABorderStyles[cbSouth].LineStyle)+1) shl 22); + dw1 := dw1 or (ABorderStyles[cbSouth].Color shl 25); // Bottom line color + dw2 := (ABorderStyles[cbNorth].Color shl 9) or // Top line color + (ABorderStyles[cbWest].Color shl 16) or // Left line color + (ABorderStyles[cbEast].Color shl 23); // Right line color + if cbNorth in ABorders then dw2 := dw2 or (ord(ABorderStyles[cbNorth].LineStyle)+1); + if cbWest in ABorders then dw2 := dw2 or ((ord(ABorderStyles[cbWest].LineStyle)+1) shl 3); + if cbEast in ABorders then dw2 := dw2 or ((ord(ABorderStyles[cbEast].LineStyle)+1) shl 6); AStream.WriteDWord(DWordToLE(dw1)); AStream.WriteDWord(DWordToLE(dw2)); end; @@ -1130,6 +1134,7 @@ var lFormatIndex: Word; //number format lTextRotation: Byte; lBorders: TsCellBorders; + lBorderStyles: TsCellBorderStyles; lAddBackground: Boolean; lBackgroundColor: TsColor; lHorAlign: TsHorAlignment; @@ -1144,6 +1149,7 @@ begin lFormatIndex := 0; //General format (one of the built-in number formats) lTextRotation := XF_ROTATION_HORIZONTAL; lBorders := []; + lBorderStyles := FFormattingStyles[i].BorderStyles; lHorAlign := FFormattingStyles[i].HorAlignment; lVertAlign := FFormattingStyles[i].VertAlignment; lBackgroundColor := FFormattingStyles[i].BackgroundColor; @@ -1233,44 +1239,45 @@ begin // And finally write the style WriteXF(AStream, lFontIndex, lFormatIndex, 0, lTextRotation, lBorders, - lHorAlign, lVertAlign, lWordwrap, lAddBackground, lBackgroundColor); + lBorderStyles, lHorAlign, lVertAlign, lWordwrap, lAddBackground, + lBackgroundColor); end; end; procedure TsSpreadBIFF5Writer.WriteXFRecords(AStream: TStream); begin // XF0 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF1 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF2 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF3 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF4 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF5 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF6 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF7 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF8 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF9 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF10 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF11 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF12 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF13 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF14 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF15 - Default, no formatting - WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // Add all further non-standard/built-in formatting styles ListAllFormattingStyles; @@ -1594,6 +1601,7 @@ var lData: TXFListData; xf: TXFRecord; b: Byte; + dw: DWord; begin AStream.ReadBuffer(xf, SizeOf(xf)); @@ -1634,16 +1642,35 @@ begin xf.Border_Background_1 := DWordLEToN(xf.Border_Background_1); xf.Border_Background_2 := DWordLEToN(xf.Border_Background_2); lData.Borders := []; - // the 4 masked bits encode the line style of the border line. 0 = no line - // We ignore the line style here. --> check against "no line" - if xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM <> 0 then + // The 4 masked bits encode the line style of the border line. 0 = no line. + // The case of "no line" is not included in the TsLineStyle enumeration. + // --> correct by subtracting 1! + dw := xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM; + if dw <> 0 then begin Include(lData.Borders, cbSouth); - if xf.Border_Background_2 and MASK_XF_BORDER_LEFT <> 0 then + lData.BorderStyles[cbSouth].LineStyle := TsLineStyle(dw shr 22 - 1); + end; + dw := xf.Border_Background_2 and MASK_XF_BORDER_LEFT; + if dw <> 0 then begin Include(lData.Borders, cbWest); - if xf.Border_Background_2 and MASK_XF_BORDER_RIGHT <> 0 then + lData.BorderStyles[cbWest].LineStyle := TsLineStyle(dw shr 3 - 1); + end; + dw := xf.Border_Background_2 and MASK_XF_BORDER_RIGHT; + if dw <> 0 then begin Include(lData.Borders, cbEast); - if xf.Border_Background_2 and MASK_XF_BORDER_TOP <> 0 then + lData.BorderStyles[cbEast].LineStyle := TsLineStyle(dw shr 6 - 1); + end; + dw := xf.Border_Background_2 and MASK_XF_BORDER_TOP; + if dw <> 0 then begin Include(lData.Borders, cbNorth); + lData.BorderStyles[cbNorth].LineStyle := TsLineStyle(dw - 1); + end; + + // Border line colors + lData.BorderStyles[cbWest].Color := (xf.Border_Background_2 and MASK_XF_BORDER_LEFT_COLOR) shr 16; + lData.BorderStyles[cbEast].Color := (xf.Border_Background_2 and MASK_XF_BORDER_RIGHT_COLOR) shr 23; + lData.BorderStyles[cbNorth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_TOP_COLOR) shr 9; + lData.BorderStyles[cbSouth].Color := (xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM_COLOR) shr 25; // Background color lData.BackgroundColor := xf.Border_Background_1 AND MASK_XF_BKGR_PATTERN_COLOR; diff --git a/components/fpspreadsheet/xlsbiff8.pas b/components/fpspreadsheet/xlsbiff8.pas index 8b5bb55ff..764c73585 100755 --- a/components/fpspreadsheet/xlsbiff8.pas +++ b/components/fpspreadsheet/xlsbiff8.pas @@ -107,7 +107,6 @@ type TsSpreadBIFF8Writer = class(TsSpreadBIFFWriter) private // Writes index to XF record according to cell's formatting - //procedure WriteXFIndex(AStream: TStream; ACell: PCell); procedure WriteXFFieldsForFormattingStyles(AStream: TStream); protected { Record writing methods } @@ -131,9 +130,9 @@ type procedure WriteWindow2(AStream: TStream; ASheetSelected: Boolean); procedure WriteXF(AStream: TStream; AFontIndex: Word; AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders; - AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault; - AWordWrap: Boolean = false; AddBackground: Boolean = false; - ABackgroundColor: TsColor = scSilver); + const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault; + AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false; + AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver); procedure WriteXFRecords(AStream: TStream); public { General writing methods } @@ -292,12 +291,18 @@ const XF_ROTATION_90DEG_CW = 180; XF_ROTATION_STACKED = 255; // Letters stacked top to bottom, but not rotated - { XF CELL BORDER } + { XF CELL BORDER LINE STYLES } MASK_XF_BORDER_LEFT = $0000000F; MASK_XF_BORDER_RIGHT = $000000F0; MASK_XF_BORDER_TOP = $00000F00; MASK_XF_BORDER_BOTTOM = $0000F000; + { XF CELL BORDER COLORS } + MASK_XF_BORDER_LEFT_COLOR = $007F0000; + MASK_XF_BORDER_RIGHT_COLOR = $3F800000; + MASK_XF_BORDER_TOP_COLOR = $0000007F; + MASK_XF_BORDER_BOTTOM_COLOR = $00003F80; + { TsSpreadBIFF8Writer } @@ -308,6 +313,7 @@ var lFormatIndex: Word; //number format lTextRotation: Byte; lBorders: TsCellBorders; + lBorderStyles: TsCellBorderStyles; lAddBackground: Boolean; lBackgroundColor: TsColor; lHorAlign: TsHorAlignment; @@ -322,6 +328,7 @@ begin lFormatIndex := 0; //General format (one of the built-in number formats) lTextRotation := XF_ROTATION_HORIZONTAL; lBorders := []; + lBorderStyles := FFormattingStyles[i].BorderStyles; lHorAlign := FFormattingStyles[i].HorAlignment; lVertAlign := FFormattingStyles[i].VertAlignment; lBackgroundColor := FFormattingStyles[i].BackgroundColor; @@ -410,7 +417,8 @@ begin // And finally write the style WriteXF(AStream, lFontIndex, lFormatIndex, 0, lTextRotation, lBorders, - lHorAlign, lVertAlign, lWordwrap, lAddBackground, lBackgroundColor); + lBorderStyles, lHorAlign, lVertAlign, lWordwrap, lAddBackground, + lBackgroundColor); end; end; @@ -1229,9 +1237,9 @@ end; *******************************************************************} procedure TsSpreadBIFF8Writer.WriteXF(AStream: TStream; AFontIndex: Word; AFormatIndex: Word; AXF_TYPE_PROT, ATextRotation: Byte; ABorders: TsCellBorders; - AHorAlignment: TsHorAlignment = haDefault; AVertAlignment: TsVertAlignment = vaDefault; - AWordWrap: Boolean = false; AddBackground: Boolean = false; - ABackgroundColor: TsColor = scSilver); + const ABorderStyles: TsCellBorderStyles; AHorAlignment: TsHorAlignment = haDefault; + AVertAlignment: TsVertAlignment = vaDefault; AWordWrap: Boolean = false; + AddBackground: Boolean = false; ABackgroundColor: TsColor = scSilver); var XFOptions: Word; XFAlignment, XFOrientationAttrib: Byte; @@ -1292,18 +1300,33 @@ begin { Cell border lines and background area } - // Left and Right line colors, use black + // Left and Right line colors + XFBorderDWord1 := ABorderStyles[cbWest].Color shl 16 + + ABorderStyles[cbEast].Color shl 23; + + // Border line styles + if cbWest in ABorders then + XFBorderDWord1 := XFBorderDWord1 or (ord(ABorderStyles[cbWest].LineStyle)+1); + if cbEast in ABorders then + XFBorderDWord1 := XFBorderDWord1 or ((ord(ABorderStyles[cbEast].LineStyle)+1) shl 4); + if cbNorth in ABorders then + XFBorderDWord1 := XFBorderDWord1 or ((ord(ABorderStyles[cbNorth].LineStyle)+1) shl 8); + if cbSouth in ABorders then + XFBorderDWord1 := XFBorderDWord1 or ((ord(ABorderStyles[cbSouth].LineStyle)+1) shl 12); + (* XFBorderDWord1 := 8 * $10000 {left line - black} + 8 * $800000 {right line - black}; if cbNorth in ABorders then XFBorderDWord1 := XFBorderDWord1 or $100; if cbWest in ABorders then XFBorderDWord1 := XFBorderDWord1 or $1; if cbEast in ABorders then XFBorderDWord1 := XFBorderDWord1 or $10; if cbSouth in ABorders then XFBorderDWord1 := XFBorderDWord1 or $1000; - + *) AStream.WriteDWord(DWordToLE(XFBorderDWord1)); - // Top and Bottom line colors, use black - XFBorderDWord2 := 8 {top line - black} + 8 * $80 {bottom line - black}; + // Top and Bottom line colors + XFBorderDWord2 := ABorderStyles[cbNorth].Color + ABorderStyles[cbSouth].Color shl 7; +// XFBorderDWord2 := 8 {top line - black} + 8 * $80 {bottom line - black}; + // Add a background, if desired if AddBackground then XFBorderDWord2 := XFBorderDWord2 or $4000000; AStream.WriteDWord(DWordToLE(XFBorderDWord2)); @@ -1318,37 +1341,37 @@ end; procedure TsSpreadBIFF8Writer.WriteXFRecords(AStream: TStream); begin // XF0 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF1 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF2 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF3 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF4 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF5 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF6 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF7 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF8 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF9 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF10 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF11 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF12 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF13 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF14 - WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, MASK_XF_TYPE_PROT_STYLE_XF, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // XF15 - Default, no formatting - WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, []); + WriteXF(AStream, 0, 0, 0, XF_ROTATION_HORIZONTAL, [], DEFAULT_BORDERSTYLES); // Add all further non-standard/built-in formatting styles ListAllFormattingStyles; @@ -1971,6 +1994,7 @@ var lData: TXFListData; xf: TXFRecord; b: Byte; + dw: DWord; begin AStream.ReadBuffer(xf, SizeOf(xf)); @@ -2010,16 +2034,35 @@ begin // Cell borders xf.Border_Background_1 := DWordLEToN(xf.Border_Background_1); lData.Borders := []; + lData.BorderStyles := DEFAULT_BORDERSTYLES; + // the 4 masked bits encode the line style of the border line. 0 = no line - // We ignore the line style here. --> check against "no line" - if xf.Border_Background_1 and MASK_XF_BORDER_LEFT <> 0 then + dw := xf.Border_Background_1 and MASK_XF_BORDER_LEFT; + if dw <> 0 then begin Include(lData.Borders, cbWest); - if xf.Border_Background_1 and MASK_XF_BORDER_RIGHT <> 0 then + lData.BorderStyles[cbWest].LineStyle := TsLineStyle(dw - 1); + end; + dw := xf.Border_Background_1 and MASK_XF_BORDER_RIGHT; + if dw <> 0 then begin Include(lData.Borders, cbEast); - if xf.Border_Background_1 and MASK_XF_BORDER_TOP <> 0 then + lData.BorderStyles[cbEast].LineStyle := TsLineStyle(dw shr 4 - 1); + end; + dw := xf.Border_Background_1 and MASK_XF_BORDER_TOP; + if dw <> 0 then begin Include(lData.Borders, cbNorth); - if xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM <> 0 then + lData.BorderStyles[cbNorth].LineStyle := TsLineStyle(dw shr 8 - 1); + end; + dw := xf.Border_Background_1 and MASK_XF_BORDER_BOTTOM; + if dw <> 0 then begin Include(lData.Borders, cbSouth); + lData.BorderStyles[cbSouth].LineStyle := TsLineStyle(dw shr 12 - 1); + end; + + // Border line colors + lData.BorderStyles[cbWest].Color := (xf.Border_Background_1 and MASK_XF_BORDER_LEFT_COLOR) shr 16; + lData.BorderStyles[cbEast].Color := (xf.Border_Background_1 and MASK_XF_BORDER_RIGHT_COLOR) shr 23; + lData.BorderStyles[cbNorth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_TOP_COLOR); + lData.BorderStyles[cbSouth].Color := (xf.Border_Background_2 and MASK_XF_BORDER_BOTTOM_COLOR) shr 7; // Background color; xf.Border_Background_3 := DWordLEToN(xf.Border_Background_3); diff --git a/components/fpspreadsheet/xlscommon.pas b/components/fpspreadsheet/xlscommon.pas index 3e99c7d3f..9ba18681a 100644 --- a/components/fpspreadsheet/xlscommon.pas +++ b/components/fpspreadsheet/xlscommon.pas @@ -312,6 +312,7 @@ type WordWrap: Boolean; TextRotation: TsTextRotation; Borders: TsCellBorders; + BorderStyles: TsCellBorderStyles; BackgroundColor: TsColor; end; @@ -523,6 +524,7 @@ begin lCell^.TextRotation := XFData.TextRotation; // Borders + lCell^.BorderStyles := XFData.BorderStyles; if XFData.Borders <> [] then begin Include(lCell^.UsedFormattingFields, uffBorder); lCell^.Border := XFData.Borders; @@ -887,13 +889,13 @@ end; } procedure TsSpreadBIFFWriter.AddDefaultFormats(); begin - SetLength(FFormattingStyles, 1); - // XF0..XF14: Normal style, Row Outline level 1..7, // Column Outline level 1..7. // XF15 - Default cell format, no formatting (4.6.2) + SetLength(FFormattingStyles, 1); FFormattingStyles[0].UsedFormattingFields := []; + FFormattingStyles[0].BorderStyles := DEFAULT_BORDERSTYLES; FFormattingStyles[0].Row := 15; NextXFIndex := 15 + Length(FFormattingStyles);