Is it possible to extract chart information from an Office 2007 (xlsx / OpenXML) spreadsheet using Apache POI? I've managed to read in the spreadsheet and even get the part that refers to the chart but not sure how I can retrieve any info from this part e.g. Type of chart, chart data etc.
XSSFWorkbook xwb = new XSSFWorkbook("charts_lines.xlsx");
XSSFSheet sheet = xwb.getSheetAt(0);
I can also iterate through the package parts to retrieve the chart part, but I don't see how I then go on to retrieve any info about the chart?
Note, I'm not interested in creating charts using POI, just read as much chart info as is possible to do...I'm also not saving an xlsx. I simply wish to extract line colours, labels, data, chart type (pie, line, bar etc.)
There isn't a high level representation at the moment, so you'll need to drop down into the xmlbeans level and work with the low level CT* objects.
For Chart Sheets, there's XSSFChartSheet which will give you a CTChartsheet object, which has a little bit of info.
For both XSSFChart and XSSFChartSheet (regular and chart sheets), you'll need to go via the drawings to get the charts. Each sheet with charts on it should have one Drawing, and the charts get linked from the drawing, rather than the sheet itself.
As of r1090442 (so POI 3.8 or newer), there's a method on XSSFDrawing to give you all the XSSFChart objects (which are wrappers around the /charts/chart#.xml part). If you're on a really really old version of POI, use the CTDrawing to get the details of the chart, grab the /charts/chart#.xml part that corresponts, and then have xmlbeans give you the CT objects for it. Either way that'll let you get the titles, types, data ranges etc.
It is a bit fiddly though, so do please consider sending in a patch to POI if you get something good worked out for working with the CTChart objects!
you can read chart data as XML using XSSFDrawing
like
XSSFDrawing drawing = ((XSSFSheet)sheet).createDrawingPatriarch();
System.out.println(drawing.getCTDrawing().toString());
will print whole chart as XMl and also using
drawing.getCharts();
you can add Iterator to it to browse chart
I don't know the exact answer to your question, but the OpenXML SDK 2.0 comes with a DocumentReflector.exe tool that will show you exactly how the chart is defined (including all relationships between the SpreadsheetML and the DrawingML packages). There is some more info on this tool in this article.
Yes, It is possible to read any type of chart using Apache POI. But Before reading any chart information you need to know what XML string you are receiving because this could be different based on the chart type i.e. pie, line, bar, scatter or mixed (a combination of two or more) charts, etc. Therefore you approach will be different for different type of chart.
For a simple bar chart like this:
Your XML will look something like this:
<xml-fragment ...>
<c:title>
<c:tx>
<c:rich>
...
<a:p>
...
<a:r>
...
<a:t>Employee Salary</a:t>
</a:r>
</a:p>
</c:rich>
</c:tx>
...
</c:title>
...
<c:plotArea>
...
<c:barChart>
...
<c:ser>
...
<c:cat>
<c:strRef>
...
<c:strCache>
<c:ptCount val="5"/>
<c:pt idx="0">
<c:v>Tom</c:v>
</c:pt>
<c:pt idx="1">
<c:v>John</c:v>
</c:pt>
<c:pt idx="2">
<c:v>Harry</c:v>
</c:pt>
<c:pt idx="3">
<c:v>Sam</c:v>
</c:pt>
<c:pt idx="4">
<c:v>Richa</c:v>
</c:pt>
</c:strCache>
</c:strRef>
</c:cat>
<c:val>
<c:numRef>
...
<c:numCache>
<c:formatCode>"$"#,##0</c:formatCode>
<c:ptCount val="5"/>
<c:pt idx="0">
<c:v>1000</c:v>
</c:pt>
<c:pt idx="1">
<c:v>700</c:v>
</c:pt>
<c:pt idx="2">
<c:v>300</c:v>
</c:pt>
<c:pt idx="3">
<c:v>900</c:v>
</c:pt>
<c:pt idx="4">
<c:v>800</c:v>
</c:pt>
</c:numCache>
</c:numRef>
</c:val>
...
</c:ser>
...
</c:barChart>
...
</c:plotArea>
...
</xml-fragment>
Now based on the above XML String we can use CT* classes and its various methods to traverse through the whole XML using Apache POI. Let's see how to read Chart Title, Labels(Employee names) and Series(Employee salaries) using POI:
Workbook workbook = new XSSFWorkbook(new File(PATH));
Sheet sheet = workbook.getSheet("GraphSheet");
XSSFSheet xsheet = (XSSFSheet) sheet;
XSSFDrawing drawing = xsheet.getDrawingPatriarch();
if (drawing != null) {
List<XSSFChart> charts = drawing.getCharts();
for (int chartIndex = 0; charts != null && chartIndex < (charts.size()); chartIndex++) {
XSSFChart chart = charts.get(chartIndex);
CTChart chart2 = chart.getCTChart();
CTPlotArea plot = chart2.getPlotArea();
System.out.println("Chart Title :" + chart2.getTitle().getTx().getRich().getPArray(0).getRArray(0).getT());
CTBarSer[] ctScaSerList = plot.getBarChartArray(0).getSerArray();
for (CTBarSer ctLineSer : ctScaSerList) {
CTStrVal[] ctStrVals = ctLineSer.getCat().getStrRef().getStrCache().getPtArray();
for (int i = 0; i < ctStrVals.length; i++) {
System.out.print(ctStrVals[i].getV() + ",");
}
System.out.println();
CTNumVal[] ctXNumVal = ctLineSer.getVal().getNumRef().getNumCache().getPtArray();
for (int i = 0; i < ctXNumVal.length; i++) {
System.out.print(ctXNumVal[i].getV() + ",");
}
}
}
}
Console:
Chart Title :Employee Salary
Tom,John,Harry,Sam,Richa,
1000,700,300,900,800,
Note: Here, the idea is to first read the XML String(because could be different based on your graph type) and then traverse the whole XML accordingly.
Related
Is there a Java or Nodejs library that can move existing text in a PDF file?
I'd like to extract all the text nodes, then move some of them to a new location based on some conditions.
I tried PdfClown, galkahana/HummusJS, Hopding/pdf-lib, but seems they don't have exactly what I need.
can anyone help? thanks
After inspecting the variables, I figured out how to move text, here is the code
PrimitiveComposer composer = new PrimitiveComposer(page);
ContentScanner scanner = composer.getScanner();
tranverse(scanner);
composer.flush();
...
while (level.moveNext()){
ContentObject content = level.getCurrent();
if (content instanceof Text){
...
List<ContentObject> objects = text.getBaseDataObject().getObjects();
for(ContentObject co: objects){
if(co instanceof SetTextMatrix){
List<PdfDirectObject> operands = ((SetTextMatrix)co).getOperands();
PdfInteger y = (PdfInteger)operands.get(5);
operands.set(5, new PdfInteger(y.getIntValue()-100));
}
}
I'm using C++ to control LibreOffice/OpenOffice from another application, but I guess you can help me if you know the java-bridge as well. So basically I want to load a document (works), set the text of a cell (works) and set a table-cell to horizontal align right (which I got no idea how to do it):
I do:
// Load Document
Reference <XInterface> rDoc = myLoader->loadComponentFromURL(...);
// Get Table
Reference <XTextTablesSupplier> rTablesSuppl(rDocument, UNO_QUERY);
Any any = rTablesSuppl->getTextTables()->getByName("Table1");
Reference<XTextTable> rTable(any, UNO_QUERY);
// Set Text in cell
Reference<XCellRange> rRange (rTable, UNO_QUERY);
Reference<XCell> rCell = rRange->getCellByPosition(x, y);
Reference<XTextRange> rTextRange(rCell, UNO_QUERY);
rTextRange->setString("MyNewText");
// Align "MyNewText" right
????
Any idea how to continue?
Caveat... While I have experience with C++, I use Java for LO API programming, so the following may be a bit off. You'll probably have to tweak a bit to get things going.
In Java and using the cell name to get the cell, I right-justified text in a cell like this:
XCell xCell = xTextTable.getCellByName(cellname);
XText xText = UnoRuntime.queryInterface(XText.class, xCell);
XPropertySet xPropertySet = UnoRuntime.queryInterface(XPropertySet.class, xText.getStart());
xPropertySet.setPropertyValue("ParaAdjust", com.sun.star.style.ParagraphAdjust.RIGHT);
In C++ and using the cell position to get the cell, I'm thinking that a rough translation would be:
Reference<XCell> rCell = rRange->getCellByPosition(x, y);
Reference<XText> rText(rCell, UNO_QUERY);
Reference< XPropertySet > xPropSet( rText->getStart(), UNO_QUERY );
xPropSet->getPropertyValue("ParaAdjust") >>= com::sun::star::style::ParagraphAdjust.RIGHT;
Given what you've already got, it looks like you might be able to just replace your ???? with something like this:
Reference< XPropertySet > xPropSet( rTextRange, UNO_QUERY );
xPropSet->getPropertyValue("ParaAdjust") >>= com::sun::star::style::ParagraphAdjust.RIGHT;
I want to append data to a TestData.xls.
TestData.xls already contains data for my scripts. I am reading this data in program, processing it and result-Pass/Fail is decided on this processing.
I want to write back this results to next column in TestData.xls
I have tried like below,
for(int i=0;i<rows-1;i++)
{
WritableWorkbook wkb = Workbook.createWorkbook(new File("E://Testing//testing Tools//Selenium//TestData//TestData.xls"));
WritableSheet sh = wkb.createSheet("Agent",0);
WritableFont f = new WritableFont(WritableFont.ARIAL);
WritableCellFormat cf = new WritableCellFormat(f);
sh.addCell(new Label(0,i+1,"Pass",cf));
wkb.write();
wkb.close();
}
But it deletes all data previously available in TestData.xls
Then i tried as TestData1.xls but data is not written at correct position and it is partial.
Now I want to append data to next available column of TestData.xls without erasing previous data.
Please tell me how to append data through Selenium Webdriver
For a Starter to this question : This has nothing to do with Selenium WebDriver.
You are trying to insert value to a particular location in xls.
Try below code may be it it useful to you.
`public static void writeReport(String readFileName, String readSheetName, String result, int row,int col) throws BiffException, IOException, RowsExceededException, WriteException
{
File fileWrite = new File(readFileName);
Workbook in = Workbook.getWorkbook(fileWrite);
WritableWorkbook writable_workbook = Workbook.createWorkbook(fileWrite, in);
WritableSheet writable_sheet = writable_workbook.getSheet(readSheetName);
writable_sheet.addCell(new Label(col, row , result));
writable_workbook.write();
writable_workbook.close();
}`
I am trying to execute the following code to convert a xlsm file to csv :
//Workbook wbk = new HSSFWorkbook(new FileInputStream(new File("myFile.xls")));
Workbook wbk = new XSSFWorkbook(new FileInputStream(new File("myFile.xlsm")));
for (int i = 0; i < wbk.getNumberOfNames(); i++) {
if (wbk.getNameAt(i).getNameName().startsWith("START\\")) {
// Get SheetName
sheetName = wbk.getNameAt(i).getSheetName();
// Get csv Filename
csvFilename = generateFileName(wbk.getNameAt(i).getNameName(), currentDate);
// Starting row index for this sheet
startingRowIndex = getStartingRowIndex(wbk, i);
// Max column index for this sheet
maxColumnIndex = getMaxColumnIndex(wbk, wbk.getSheet(sheetName));
// Convert sheet to csv
toCSV(csvFilename, startingRowIndex, maxColumnIndex, wbk, sheetName);
}
}
-Xmx argument is setted to 1024 and i use a xslm file.
This file is 15 Mo.
I get this error "java.lang.OutOfMemoryError: Java heap space" on the first line.
With the same file in xls format (50 Mo), it works great.
I can't change the Xmx argument and I can't use other API than POI.
I read in others messages that the better way is to use the SAX API for this kind of memory problems.
However, in my file, all sheets and all rows don't need to be extracted in CSV.
That is why I use "wbk.getNumberOfNames()" to get all the defined names (in the name manager) and to know the sheets to convert.
Do you know how i can access these properties using SAX API ?
Thanks.
Regards.
The following Apache POI code example uses SAX parser to convert XLSX file to CSV.
http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/eventusermodel/XLSX2CSV.java
How do I get an outline view in sublime text editor for Windows?
The minimap is helpful but I miss a traditional outline (a klickable list of all the functions in my code in the order they appear for quick navigation and orientation)
Maybe there is a plugin, addon or similar? It would also be nice if you can shortly name which steps are neccesary to make it work.
There is a duplicate of this question on the sublime text forums.
Hit CTRL+R, or CMD+R for Mac, for the function list. This works in Sublime Text 1.3 or above.
A plugin named Outline is available in package control, try it!
https://packagecontrol.io/packages/Outline
Note: it does not work in multi rows/columns mode.
For multiple rows/columns work use this fork:
https://github.com/vlad-wonderkidstudio/SublimeOutline
I use the fold all action. It will minimize everything to the declaration, I can see all the methods/functions, and then expand the one I'm interested in.
I briefly look at SublimeText 3 api and view.find_by_selector(selector) seems to be able to return a list of regions.
So I guess that a plugin that would display the outline/structure of your file is possible.
A plugin that would display something like this:
Note: the function name display plugin could be used as an inspiration to extract the class/methods names or ClassHierarchy to extract the outline structure
If you want to be able to printout or save the outline the ctr / command + r is not very useful.
One can do a simple find all on the following grep ^[^\n]*function[^{]+{ or some variant of it to suit the language and situation you are working in.
Once you do the find all you can copy and paste the result to a new document and depending on the number of functions should not take long to tidy up.
The answer is far from perfect, particularly for cases when the comments have the word function (or it's equivalent) in them, but I do think it's a helpful answer.
With a very quick edit this is the result I got on what I'm working on now.
PathMaker.prototype.start = PathMaker.prototype.initiate = function(point){};
PathMaker.prototype.path = function(thePath){};
PathMaker.prototype.add = function(point){};
PathMaker.prototype.addPath = function(path){};
PathMaker.prototype.go = function(distance, angle){};
PathMaker.prototype.goE = function(distance, angle){};
PathMaker.prototype.turn = function(angle, distance){};
PathMaker.prototype.continue = function(distance, a){};
PathMaker.prototype.curve = function(angle, radiusX, radiusY){};
PathMaker.prototype.up = PathMaker.prototype.north = function(distance){};
PathMaker.prototype.down = PathMaker.prototype.south = function(distance){};
PathMaker.prototype.east = function(distance){};
PathMaker.prototype.west = function(distance){};
PathMaker.prototype.getAngle = function(point){};
PathMaker.prototype.toBezierPoints = function(PathMakerPoints, toSource){};
PathMaker.prototype.extremities = function(points){};
PathMaker.prototype.bounds = function(path){};
PathMaker.prototype.tangent = function(t, points){};
PathMaker.prototype.roundErrors = function(n, acurracy){};
PathMaker.prototype.bezierTangent = function(path, t){};
PathMaker.prototype.splitBezier = function(points, t){};
PathMaker.prototype.arc = function(start, end){};
PathMaker.prototype.getKappa = function(angle, start){};
PathMaker.prototype.circle = function(radius, start, end, x, y, reverse){};
PathMaker.prototype.ellipse = function(radiusX, radiusY, start, end, x, y , reverse/*, anchorPoint, reverse*/ ){};
PathMaker.prototype.rotateArc = function(path /*array*/ , angle){};
PathMaker.prototype.rotatePoint = function(point, origin, r){};
PathMaker.prototype.roundErrors = function(n, acurracy){};
PathMaker.prototype.rotate = function(path /*object or array*/ , R){};
PathMaker.prototype.moveTo = function(path /*object or array*/ , x, y){};
PathMaker.prototype.scale = function(path, x, y /* number X scale i.e. 1.2 for 120% */ ){};
PathMaker.prototype.reverse = function(path){};
PathMaker.prototype.pathItemPath = function(pathItem, toSource){};
PathMaker.prototype.merge = function(path){};
PathMaker.prototype.draw = function(item, properties){};