Apache Poi Apply Gradient Color to Cell - java

i have been searching the web and found no real good example for applying a gradient color to an excel sheet cell using Apache Poi.
The example I found are pretty old and the classes not really exist anymore in the current Apache Poi version. I'm currently using Apache Poi version 3.16.
Can somebody point out the steps which are needed to apply a gradient color to excel sheet using the poi library. All hints are appreciated.

There is always still not a possibility to set gradient cell fills using the default actual apache poi versions.
So I suspect that the code you found was for XSSF (*.xlsx) and for the code you found it was just not mentioned that this code needs the full jar of all of the schemas ooxml-schemas-*.jar or poi-ooxml-full-*.jar in the class path as mentioned in faq-N10025.
The following example works but also needs the full jar of all of the schemas in the class path as mentioned in faq-N10025.
It first sets pattern fill settings to the CellStyle only to have some fill to get the fill index from it. Then it gets the low level CTFill used in this CellStyle. An then it unsets the pattern fill and then it sets the gradient fill.
To get informations about how to use CTFill one needs download the sources of ooxml-schemas and do javadoc. There is no API documentation for ooxml-schemas public available.
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTFill;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTGradientFill;
public class CreateExcelCellGradientFillColor {
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
XSSFCellStyle cellstyle = workbook.createCellStyle();
//set pattern fill settings only to have some fill to get the fill index from it
cellstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//get fill index used in this CellStyle
int fillidx = (int)cellstyle.getCoreXf().getFillId();
//get the low level CTFill used in this CellStyle
CTFill ctfill = workbook.getStylesSource().getFillAt(fillidx).getCTFill();
System.out.println(ctfill);
//unset the pattern fill
ctfill.unsetPatternFill();
//now low level set the gradient fill
byte[] rgb1 = new byte[3];
rgb1[0] = (byte) 0; // red
rgb1[1] = (byte) 0; // green
rgb1[2] = (byte) 255; // blue
byte[] rgb2 = new byte[3];
rgb2[0] = (byte) 255; // red
rgb2[1] = (byte) 255; // green
rgb2[2] = (byte) 255; // blue
CTGradientFill ctgradientfill = ctfill.addNewGradientFill();
ctgradientfill.setDegree(90.0);
ctgradientfill.addNewStop().setPosition(0.0);
ctgradientfill.getStopArray(0).addNewColor().setRgb(rgb1);
ctgradientfill.addNewStop().setPosition(0.5);
ctgradientfill.getStopArray(1).addNewColor().setRgb(rgb2);
ctgradientfill.addNewStop().setPosition(1.0);
ctgradientfill.getStopArray(2).addNewColor().setRgb(rgb1);
System.out.println(ctfill);
Cell cell = row.createCell(0);
cell.setCellValue("");
cell.setCellStyle(cellstyle);
FileOutputStream out = new FileOutputStream("CreateExcelCellGradientFillColor.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}

Related

Apache Poi setActiveCell() for multiple cells

I'm trying to use the method sheet.setActiveCell(CellAddress addr) to set a range of multiple cells active at the same time. I've tryed with multiple versions of Apache poi-ooxml library and now i'm using 3.16 which also supports the method sheet.setActiveCell(String addr)(I know 3.16 is old but the issue stays the same also with the latest version).
Following the suggestions on this question: Is it possible to set the active range with Apache POI XSSF?
I've managed to get it to work, both with the custom CellAddress and the String in the format "A1:B5".
The problem is that every time I try to open an xlsx in which a range of cells has been set to active using apache poi, I get an error message from Excel saying that the file is damaged and need to be recovered. If I do, the recovery completes correctly, but this error is annoying since I have to open a great number of these files each day.
Is there a way to avoid this error from excel (maybe modifying the creation of the xlsx or changing some setting in Excel)?
Only one cell can be the active cell. And Sheet.setActiveCell only sets that one active cell. So sheet.setActiveCell("A1:B5") will work if setActiveCell(String addr) is available but it leads to a corrupted sheet. That's why it was removed.
Multiple cells can be selected. But there are no methods to set the selected cells in apache poi's high level classes. So the underlying low level classes needs to be used. Doing this one needs differentiate between XSSF and HSSF because different low level classes needs to be used.
Following complete example sets active cell to B2. This also sets sheet view having selection and active cell to that one given cell B2. Then it uses low level methods of XSSF and HSSF to set the selection to B2:E5.
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFSheet;
class CreateExcelSelectMultipleCells {
public static void main(String[] args) throws Exception {
try (Workbook workbook = new XSSFWorkbook(); FileOutputStream out = new FileOutputStream("Excel.xlsx") ) {
//try (Workbook workbook = new HSSFWorkbook(); FileOutputStream out = new FileOutputStream("Excel.xls") ) {
Sheet sheet = workbook.createSheet();
Row row;
Cell cell;
for (int r = 0; r < 6; r++) {
row = sheet.createRow(r);
for (int c = 0; c < 6; c++) {
cell = row.createCell(c);
cell.setCellValue("R" + (r+1) + "C" + (c+1));
}
}
// set active cell; this also sets sheet view having selection and active cell to one given cell
sheet.setActiveCell(new CellAddress("B2"));
// set selected cells
if (sheet instanceof XSSFSheet) {
XSSFSheet xssfSheet = (XSSFSheet) sheet;
xssfSheet.getCTWorksheet().getSheetViews().getSheetViewArray(0).getSelectionArray(0).setSqref(
java.util.Arrays.asList("B2:E5"));
} else if (sheet instanceof HSSFSheet) {
HSSFSheet hssfSheet = (HSSFSheet) sheet;
org.apache.poi.hssf.record.SelectionRecord selectionRecord = hssfSheet.getSheet().getSelection();
java.lang.reflect.Field field_6_refs = org.apache.poi.hssf.record.SelectionRecord.class.getDeclaredField("field_6_refs");
field_6_refs.setAccessible(true);
field_6_refs.set(
selectionRecord,
new org.apache.poi.hssf.util.CellRangeAddress8Bit[] { new org.apache.poi.hssf.util.CellRangeAddress8Bit(1,4,1,4) }
);
}
workbook.write(out);
}
}
}

How to insert a background image for an Excel Comment in .xlsx formats (Apache POI XSSF)?

There is a question which solves how to add a background image for an Excel Comment in versions previous to 2007 (format .xsl), with HSSF Apache POI.
apache poi insert comment with picture
But looking the doc, I cannot locate an equivalent method for XSSF Apache POI (.xslx formats).
It seems this key method was removed when moving from HSSF to XSSF:
HSSFComment comment;
...
comment.setBackgroundImage(picIndex); // set picture as background image
It is not supported using a method of XSSFComment. But if one knows what needs to be created, then it is not impossible.
First we need creating a default comment as shown in Quick-Quide CellComments.
Then we need adding picture data to this workbook as shown in Quick-Guide Images. We need the XSSFPictureData for adding references later.
Then we need getting the VML drawing. XSSFComments are stored in VML drawings and not in default XSSFDrawings. This is not public provided, so we need using reflection to do so.
Now we need setting the relation to the picture data in VML drawing.
At last we need getting the comment shape out of the VML drawing to set the fill of that comment shape to show the picture. There are no high level methods for this. So we need using methods of low level com.microsoft.schemas.vml.* classes.
The following example needs the full jar of all of the schemas ooxml-schemas-1.4.jar as mentioned in FAQ. It is tested using apache poi 4.1.1.
Complete example:
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.util.IOUtils;
class CreateXSSFCommentWithPicture {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) {
// First we create a default XSSFComment:
XSSFCreationHelper factory = workbook.getCreationHelper();
XSSFSheet sheet = workbook.createSheet("Sheet");
XSSFRow row = sheet.createRow(3);
XSSFCell cell = row.createCell(5);
cell.setCellValue("F4");
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = factory.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex()+2);
anchor.setRow1(row.getRowNum());
anchor.setRow2(row.getRowNum()+5);
XSSFComment comment = drawing.createCellComment(anchor);
XSSFRichTextString str = factory.createRichTextString("Hello, World!");
comment.setString(str);
comment.setAuthor("Apache POI");
// assign the comment to the cell
cell.setCellComment(comment);
// Now we put the image as fill of the comment's shape:
// add picture data to this workbook
InputStream is = new FileInputStream("samplePict.jpeg");
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = workbook.addPicture(bytes, XSSFWorkbook.PICTURE_TYPE_JPEG);
is.close();
// get picture data
XSSFPictureData pictureData = workbook.getAllPictures().get(pictureIdx);
// get VML drawing
java.lang.reflect.Method getVMLDrawing = XSSFSheet.class.getDeclaredMethod("getVMLDrawing", boolean.class);
getVMLDrawing.setAccessible(true);
XSSFVMLDrawing vml = (XSSFVMLDrawing)getVMLDrawing.invoke(sheet, true);
// set relation to the picture data in VML drawing
org.apache.poi.ooxml.POIXMLDocumentPart.RelationPart rp = vml.addRelation(null, XSSFRelation.IMAGES, pictureData);
// get comment shape
com.microsoft.schemas.vml.CTShape commentShape = vml.findCommentShape(cell.getRow().getRowNum(), cell.getColumnIndex());
// get fill of comment shape
com.microsoft.schemas.vml.CTFill fill = commentShape.getFillArray(0);
// already set color needs to be color2 now
fill.setColor2(fill.getColor());
fill.unsetColor();
// set relation Id of the picture
fill.setRelid(rp.getRelationship().getId());
// set some other properties
fill.setTitle("samplePict");
fill.setRecolor(com.microsoft.schemas.vml.STTrueFalse.T);
fill.setRotate(com.microsoft.schemas.vml.STTrueFalse.T);
fill.setType(com.microsoft.schemas.vml.STFillType.FRAME);
workbook.write(fileout);
}
}
}
According to this, adding image to comments was only added for HSSF.
I suppose you'll have to use another approach, like in apache poi guide.

Apache POI get Font Metrics

I want to auto-size columns in excel, but without spending too much performance.
The built-in auto-size of Apache POI is very slow (didn't finish after couple hours with 1 million rows).
To save performance I just want to approximate the cell width, but for that I need the Font Metrics.
Apache POI has a class called FontDetails, but it does not work on its own.
The class StaticFontMetrics seems to be the one actually loading the metrics, but it is not public.
But even with copying the protected code to my workspace and making it accessible it fails to load the Font Metrics.
How can I get those Metrics? Will java.awt.FontMetrics always return an accurate result?
EDIT: The stacktrace I get when trying to get the Metrics of a Font:
Caused by: java.lang.IllegalArgumentException: The supplied FontMetrics doesn't know about the font 'Calibri', so we can't use it. Please add it to your font metrics file (see StaticFontMetrics.getFontDetails
at ourpackagestructure.apachepoi.FontDetails.create(FontDetails.java:106)
at ourpackagestructure.apachepoi.StaticFontMetrics.getFontDetails(StaticFontMetrics.java:94)
Apache poi uses AttributedString and TextLayout to get the bounds out of a text in a special font.
So, as long as the whole column is in same font, best approach would be first get the longest string which shall be stored in that column. Then get the width of that string in that font using java.awt.font.TextLayout. Then set this as the column width.
Note, the column width in Excel is set in in units of 1/256th of a default character width. So in addition to the width of the string in that font in pixels, you need the width of a default character to calculate the Excel column width.
Example:
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import java.awt.geom.Rectangle2D;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.SheetUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ApachePoiGetStringWidth {
public static void main(String args[]) throws Exception {
String testString = "Lorem ipsum semit dolor";
String fontName = "Calibri";
short fontSize = 24;
boolean italic = true;
boolean bold = false;
Workbook workbook = new XSSFWorkbook();
Font font = workbook.createFont();
font.setFontHeightInPoints(fontSize);
font.setFontName(fontName);
font.setItalic(italic);
font.setBold(bold);
CellStyle style = workbook.createCellStyle();
style.setFont(font);
AttributedString attributedString = new AttributedString(testString);
attributedString.addAttribute(TextAttribute.FAMILY, font.getFontName(), 0, testString.length());
attributedString.addAttribute(TextAttribute.SIZE, (float)font.getFontHeightInPoints());
if (font.getBold()) attributedString.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, 0, testString.length());
if (font.getItalic()) attributedString.addAttribute(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE, 0, testString.length());
FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);
TextLayout layout = new TextLayout(attributedString.getIterator(), fontRenderContext);
Rectangle2D bounds = layout.getBounds();
double frameWidth = bounds.getX() + bounds.getWidth();
System.out.println(frameWidth);
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(2);
Cell cell = row.createCell(2);
cell.setCellValue(testString);
cell.setCellStyle(style);
int defaultCharWidth = SheetUtil.getDefaultCharWidth(workbook);
sheet.setColumnWidth(2, (int)Math.round(frameWidth / defaultCharWidth * 256));
try (java.io.FileOutputStream out = new java.io.FileOutputStream("Excel.xlsx")) {
workbook.write(out);
}
workbook.close();
}
}

Adding Custom colours to Excel sheet using Apache POI

Can anyone explain how to add Custom colours either using (rgb values or hex values ) to an excelsheet sheet(either in foreground or background) using Cellstyle in Apche poi to a Excelsheet(XSSF Workbook)?
Setting custom colors depends on the kind of Excel file (Office Open XML format *.xlsx vs. BIFF format *.xls). And it might be different using different versions of apache poi because of deprecation.
Using Office Open XML format *.xlsx we can simply set new colors using constructor of XSSFColor. In apache poi 4.0.0 XSSFColor(byte[] rgb, IndexedColorMap colorMap) can be used. IndexedColorMap can be null if no additional color map shall be used instead of the default one.
Using BIFF format *.xls only indexed colors are usable. But temporary overwriting some of the indexed colors is possible.
Following code shows both used for setting a cells's fill color. The used custom color is RGB(112,134,156). Using HSSF(BIFF format *.xls) the indexed color HSSFColor.HSSFColorPredefined.LIME will be temporary overwritten.
Note, the following is tested and works using apache poi 4.0.0. No guarantee using other versions.
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
public class CreateExcelCustomColor {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
//Workbook workbook = new HSSFWorkbook();
CellStyle cellcolorstyle = workbook.createCellStyle();
cellcolorstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
byte[] rgb = new byte[]{(byte)112, (byte)134, (byte)156};
if (cellcolorstyle instanceof XSSFCellStyle) {
XSSFCellStyle xssfcellcolorstyle = (XSSFCellStyle)cellcolorstyle;
xssfcellcolorstyle.setFillForegroundColor(new XSSFColor(rgb, null));
} else if (cellcolorstyle instanceof HSSFCellStyle) {
cellcolorstyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIME.getIndex());
HSSFWorkbook hssfworkbook = (HSSFWorkbook)workbook;
HSSFPalette palette = hssfworkbook.getCustomPalette();
palette.setColorAtIndex(HSSFColor.HSSFColorPredefined.LIME.getIndex(), rgb[0], rgb[1], rgb[2]);
}
Sheet sheet = workbook.createSheet();
Cell cell = sheet.createRow(0).createCell(0);
cell.setCellStyle(cellcolorstyle);
FileOutputStream out = null;
if (workbook instanceof XSSFWorkbook) {
out = new FileOutputStream("CreateExcelCustomColor.xlsx");
} else if (workbook instanceof HSSFWorkbook) {
out = new FileOutputStream("CreateExcelCustomColor.xls");
}
workbook.write(out);
out.close();
workbook.close();
}
}

Not able to set custom color in XSSFCell Apache POI

I am trying to set some custom(from hexcode or rgb value) color to a xssfcell.But the color of the cell is becoming black even though I am giving some other color.I have tried doing this by the following ways :
File xlSheet = new File("C:\\Users\\IBM_ADMIN\\Downloads\\Excel Test\\Something3.xlsx");
System.out.println(xlSheet.createNewFile());
FileOutputStream fileOutISPR = new FileOutputStream("C:\\Users\\IBM_ADMIN\\Downloads\\Excel Test\\Something3.xlsx");
XSSFWorkbook isprWorkbook = new XSSFWorkbook();
XSSFSheet sheet = isprWorkbook.createSheet("TEST");
XSSFRow row = sheet.createRow(0);
XSSFCellStyle cellStyle = isprWorkbook.createCellStyle();
byte[] rgb = new byte[3];
rgb[0] = (byte) 24; // red
rgb[1] = (byte) 22; // green
rgb[2] = (byte) 219; // blue
XSSFColor myColor = new XSSFColor(rbg);
cellStyle.setFillForegroundColor(myColor);
cellStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
cellStyle.setAlignment(HorizontalAlignment.CENTER);
XSSFCell cell = row.createCell(0);
cell.setCellValue("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has");
cell.setCellStyle(cellStyle);
CellRangeAddress rangeAddress = new CellRangeAddress(0, 0, 0, 2);
sheet.addMergedRegion(rangeAddress);
int width = ((int)(90 * 0.73)) * 256;
sheet.setColumnWidth(cell.getColumnIndex(), width);
//sheet.autoSizeColumn(cell.getColumnIndex());
RegionUtil.setBorderBottom(XSSFCellStyle.BORDER_MEDIUM, rangeAddress, sheet, isprWorkbook);
RegionUtil.setBottomBorderColor(IndexedColors.RED.getIndex(), rangeAddress, sheet, isprWorkbook);
XSSFCell cell2 = row.createCell(11);
cell2.setCellValue("222222222222222");
isprWorkbook.write(fileOutISPR);
//END of the program
XSSFCellStyle cellStyle = isprWorkbook.createCellStyle();
byte[] rgb = new byte[3];
rgb[0] = (byte) 24; // red
rgb[1] = (byte) 22; // green
rgb[2] = (byte) 219; // blue
XSSFColor myColor = new XSSFColor(rgb);
cellStyle.setFillForegroundColor(myColor);//1st method
//cellStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(128, 0, 128)));//2nd method
//XSSFColor myColor = new XSSFColor(Color.decode("0XFFFFFF"));
cellStyle.setFillForegroundColor(myColor);//3rd Method
I tried many other ways mentioned in answers to related questions but none of those solved my problem.
Please help me out.
This is caused by an incompleteness of Package org.apache.poi.ss.util.
PropertyTemplate as well as CellUtil and RegionUtilare be based on ss.usermodel level only and not on xssf.usermodel level. But org.apache.poi.ss.usermodel.CellStyle does not know something about a setFillForegroundColor(Color color) until now. It only knows setFillForegroundColor(short bg). So ss.usermodel level simply cannot set a Color as fill foreground color until now. Only a short (a color index) is possible.
If it comes to the question why setting the color is necessary when only the border shall be set using org.apache.poi.ss.util then the answer is, it is necessary because both, color and border, are in the same CellStyle. Thats why when adding the border settings to the CellStyle, the color settings must be maintain and finally be set new.
So in conclusion, there is not a way out of this dilemma. If you need using org.apache.poi.ss.util then you cannot use setFillForegroundColor(XSSFColor color) the same time. The only hope is setFillForegroundColor(Color color) will be added to org.apache.poi.ss.usermodel.CellStyle in later versions of apache poi.
As a workaround you can use conditional formatting to set custom colors after you've set all other format options (alignment, borders...) with cell style.
Here is a working (Kotlin) example that defines a custom color to distinguish between even and odd rows:
private fun setEvenOddColorFormatting(sheet: XSSFSheet) {
val sheetConditionalFormatting = sheet.sheetConditionalFormatting
val rule = sheetConditionalFormatting.createConditionalFormattingRule("MOD(ROW(), 2) = 0")
val formatForRule = rule.createPatternFormatting()
formatForRule.setFillBackgroundColor(XSSFColor(byteArrayOf(221.toByte(), 235.toByte(), 247.toByte())))
formatForRule.fillPattern = PatternFormatting.SOLID_FOREGROUND
val region = arrayOf(CellRangeAddress(0, sheet.lastRowNum,0,10))
sheetConditionalFormatting.addConditionalFormatting(region, rule)
}
A drawback is, that you have to write the rule as excel function. But you should be able to use an always true function and only set the region.

Categories

Resources