I have an Excel document where I need to change the value of a single character inside a cell but keeping the formatting.
Using setCellValue, it properly changes the value but completely removes formatting.
Looking at POI documentation, it seems like there is no method to do that out of the box.
As usual with Excel, not super easy but you need to :
Get the text of the cell
Keep track of the style on a per-range basis (not on a per-character basis, otherwise italic text look odd)
Change the text of the cell applying the previous style
// Copy the style of the different ranges
List<XSSFFont> fonts = new ArrayList<>();
List<Integer> ranges = new ArrayList<>();
String cellText = cell.getStringCellValue();
XSSFRichTextString cellRichText = cell.getRichStringCellValue();
for (int i = 0; i < cellText.length(); i++) {
XSSFFont font = cellRichText.getFontAtIndex(i);
fonts.add(font);
ranges.add(i);
while (font.equals(cellRichText.getFontAtIndex(i)))
i++;
i--;
}
ranges.add(cellText.length());
// Create the new content of the cell
XSSFRichTextString newCellRichText = new XSSFRichTextString(cellText.replace('A', 'B'));
// Apply the style to the new value - In this case, that's
// super easy, there is no need to change the indexes inside
// the `ranges` list as the string has the same size.
for (int i = 0; i < fonts.size(); i++) {
newCellRichText.applyFont(ranges.get(i), ranges.get(i+1), fonts.get(i));
}
cell.setCellValue(newCellRichText);
I have two text columns which look like this:
and now I need to make a bubble chart that looks like this:
Any idea how I can achieve this using excel 2016?
There is a way to do this by using Javascript. This langage has a lot of powerful libraries for data visualization and data processing. And there is a way to use it in Excel by using an Excel Add-in called funfun.
I have written a working code for you:
https://www.funfun.io/1/#/edit/5a7c4d5db8b2864030f9de15
I used an online editor with an embedded spreadsheet to build this chart. I use a Json file(short/full underneath Settings) to get the data from the spreadsheet to my javascript code:
{
"data": "=A1:B18"
}
I then store it in local variables in the script.js so I can use them correctly in the chart I will create:
var Approaches = []; // list of Approaches
var Contribution = []; // list of contribution
var tmpC = [];
/*
* Parse your spreadsheet to count how much approaches and contribution there are
*/
for (var x = 1; x < $internal.data.length; x++) {
if (Approaches.indexOf($internal.data[x][0]) <= -1)
Approaches.push($internal.data[x][0]);
if (tmpC.indexOf($internal.data[x][1]) <= -1)
tmpC.push($internal.data[x][1]);
}
/*
* sort the array so that other is at the end
* (remove if you want you don't care about the order, replace 'tmpC' by 'Contribution' above)
*/
for (var t = tmpC.length - 1; t >= 0; t--)
Contribution.push(tmpC[t]);
var techniquesIndex = new Array(Contribution.length); // how much of one contribution is made per approach
var total = 0; // total of contribution
var totalPerApproaches = new Array(Approaches.length); //total of contributions for one Approach
for (var z = 0; z < totalPerApproaches.length; z++) {
totalPerApproaches[z] = 0;
}
var data = []; // data for the chart
/*
* Parse your every approach
*/
for (var x = 0; x < Approaches.length; x++) {
for (var z = 0; z < techniquesIndex.length; z++) {
techniquesIndex[z] = 0;
}
/*
* Parse your spreadsheet to count the number of contribution in this approach
*/
for (var y = 0; y < $internal.data.length; y++) {
if (Approaches.indexOf($internal.data[y][0]) == x) {
total += 1;
techniquesIndex[Contribution.indexOf($internal.data[y][1])] += 1;
}
}
for (var c = 0; c < Contribution.length; c++) {
/*
* calculate the total of contribution on this approach
*/
totalPerApproaches[x] += techniquesIndex[c];
/*
* removes the values equals to zero off the chart
* (remove this condition if you want to show the zeros)
*/
if (techniquesIndex[c] == 0)
continue;
/*
* adds a bubble to the charts with the number of Contribution per Approach
*/
data.push(
{
x: x, // -> index of array Approach[x]
y: c, // -> index of array Contribution[c]
z: techniquesIndex[c], // number of contribution[c] in Approach[x]
name: techniquesIndex[c] // ..
});
}
}
The $Internal.data is the data from the spreadsheet accessible thanks to the Json file. The array data (at the end) will be used to create all the bubbles of the charts.
Once I have my data stored in the right format I create the chart in index.html using a data visualization library called Highcharts, it has lots of examples and good documentation for beginners. You can choose to add many options for your chart and at the end you pass your data to the chart as such:
series: [{
data: data // use the data from script.js
}]
Once you've build your chart you can open it in Excel by pasting the URL in the Funfun excel add-in. Here is how it looks like with my example:
You can as much lines as you want you just need to make sure that the range of data in the Json file is what you need.
you can then save the chart in many formats:
Hope this helps !
Disclosure : I’m a developer of funfun
I am usind pdfbox and trying to colour the header of the table; I had done the table drawing correctly using tutorial ' http://fahdshariff.blogspot.in/2010/10/creating-tables-with-pdfbox.html'
The code as follows
//draw the rows
float nexty = y ;
for (int i = 0; i <= rows; i++) {
contentStream.drawLine(margin, nexty, margin+tableWidth, nexty);
nexty-= rowHeight;
}
//draw the columns
float nextx = margin;
for (int i = 0; i <= cols; i++) {
contentStream.drawLine(nextx, y, nextx, y-tableHeight);
nextx += (colWidths != null) ? colWidths[i] : colWidth;
}
I got the code for colouring as follows:
contentStream.setNonStrokingColor( Color.RED );
contentStream.fillRect( 10, 10, 100, 100 );
I tried to put it but its not working as needed. I need to colour table header only. How can I achieve that. Please help me.
How to autofit content in cell using jxl api?
I know this is an old question at this point, but I was looking for the solution to this and thought I would post it in case someone else needs it.
CellView Auto-Size
I'm not sure why the FAQ doesn't mention this, because it very clearly exists in the docs.
My code looked like the following:
for(int x=0;x<c;x++)
{
cell=sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
c stores the number of columns created
cell is just a temporary place holder for the returned CellView object
sheet is my WriteableSheet object
The Api warns that this is a processor intensive function, so it's probably not ideal for large files. But for a small file like mine (<100 rows) it took no noticeable time.
Hope this helps someone.
The method is self explanatory and commented:
private void sheetAutoFitColumns(WritableSheet sheet) {
for (int i = 0; i < sheet.getColumns(); i++) {
Cell[] cells = sheet.getColumn(i);
int longestStrLen = -1;
if (cells.length == 0)
continue;
/* Find the widest cell in the column. */
for (int j = 0; j < cells.length; j++) {
if ( cells[j].getContents().length() > longestStrLen ) {
String str = cells[j].getContents();
if (str == null || str.isEmpty())
continue;
longestStrLen = str.trim().length();
}
}
/* If not found, skip the column. */
if (longestStrLen == -1)
continue;
/* If wider than the max width, crop width */
if (longestStrLen > 255)
longestStrLen = 255;
CellView cv = sheet.getColumnView(i);
cv.setSize(longestStrLen * 256 + 100); /* Every character is 256 units wide, so scale it. */
sheet.setColumnView(i, cv);
}
}
for(int x=0;x<c;x++)
{
cell=sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
It is fine, instead of scanning all the columns. Pass the column as a parameter.
void display(column)
{
Cell = sheet.getColumnView(column);
cell.setAutosize(true);
sheet.setColumnView(column, cell);
}
So when you wiill be displaying your text you can set the particular length. Can be helpfull for huge excel files.
From the JExcelApi FAQ
How do I do the equivilent of Excel's "Format/Column/Auto Fit Selection"?
There is no API function to do this for you. You'll need to write code that scans the cells in each column, calculates the maximum length, and then calls setColumnView() accordingly. This will get you close to what Excel does but not exactly. Since most fonts have variable width characters, to get the exact same value, you would need to use FontMetrics to calculate the maximum width of each string in the column. No one has posted code on how to do this yet. Feel free to post code to the Yahoo! group or send it directly to the FAQ author's listed at the bottom of this page.
FontMetrics presumably refers to java.awt.FontMetrics. You should be able to work something out with the getLineMetrics(String, Graphics) method I would have though.
CellView's autosize method doesn't work for me all the time. My way of doing this is by programatically set the size(width) of the column based on the highest length of data in the column. Then perform some mathematical operations.
CellView cv = excelSheet.getColumnView(0);
cv.setSize((highest + ((highest/2) + (highest/4))) * 256);
where highest is an int that holds the longest length of data in the column.
setAutosize() method WILL NOT WORK if your cell has over 255 characters. This is related to the Excel 2003 max column width specification: http://office.microsoft.com/en-us/excel-help/excel-specifications-and-limits-HP005199291.aspx
You will need to write your own autosize method to handle this case.
Try this exemple:
expandColumns(sheet, 3);
workbook.write();
workbook.close();
private void expandColumn(WritableSheet sheet, int amountOfColumns){
int c = amountOfColumns;
for(int x=0;x<c;x++)
{
CellView cell = sheet.getColumnView(x);
cell.setAutosize(true);
sheet.setColumnView(x, cell);
}
}
Kotlin's implementation
private fun sheetAutoFitColumns(sheet: WritableSheet, columnsIndexesForFit: Array<Int>? = null, startFromRowWithIndex: Int = 0, excludeLastRows : Int = 0) {
for (columnIndex in columnsIndexesForFit?.iterator() ?: IntProgression.fromClosedRange(0, sheet.columns, 1).iterator()) {
val cells = sheet.getColumn(columnIndex)
var longestStrLen = -1
if (cells.isEmpty()) continue
for (j in startFromRowWithIndex until cells.size - excludeLastRows) {
if (cells[j].contents.length > longestStrLen) {
val str = cells[j].contents
if (str == null || str.isEmpty()) continue
longestStrLen = str.trim().length
}
}
if (longestStrLen == -1) continue
val newWidth = if (longestStrLen > 255) 255 else longestStrLen
sheet.setColumnView(columnIndex, newWidth)
}
}
example for use
sheetAutoFitColumns(sheet) // fit all columns by all rows
sheetAutoFitColumns(sheet, arrayOf(0, 3))// fit A and D columns by all rows
sheetAutoFitColumns(sheet, arrayOf(0, 3), 5)// fit A and D columns by rows after 5
sheetAutoFitColumns(sheet, arrayOf(0, 3), 5, 2)// fit A and D columns by rows after 5 and ignore two last rows
I need to get the total of the item prices(which are in the same column in a list view in C#) without selecting the label called lblTotal.The item prices come from the databases and need to get the sum of them within the application itself. Can you please help me and send your ideas.
I have build this using WinForms and I have designed the following code
float lblTotal = 0F;
for (int i = 0; i < orderList.Items.Count; i++)
{
if (orderList.Items[i].Selected)
{
lblTotal = float.Parse(orderList.Items[i].SubItems[1].Text);
}
}
However this is not working for my application.
Thank you
I windows forms c# the following code will total up all the integer values in column 2,
change the 2 to whatever column your values are stored in.
int column = 0;
column = 2;
int total = 0;
total = 0;
ListViewItem item = default(ListViewItem);
foreach ( item in this.ListView1.Items) {
total += int.Parse((string)item.SubItems(2).Text);
}
It is best to somehow calculate the sum either by moving the calculation to the database (SUM(x)) or your business logic/data source on the client, instead of going through all the hoops of reading it from the UI and conversion/casting.