Is it possible to draw a line in java poi at mid-angle within a cell? - apache-poi

I'm trying to draw underline and topline by drawing a line in a cell, but I don't know how. I need to draw two straight lines to implement it.
Sheet sheet = workbook.getSheetAt(2);
XSSFDrawing patriarch = (XSSFDrawing) sheet.createDrawingPatriarch();
XSSFClientAnchor regionr = patriarch.createAnchor(0, 0, 1, 1, 5, 5, 6, 6);
XSSFSimpleShape region1Shapevr = patriarch.createSimpleShape(regionr);
region1Shapevr.setLineStyleColor(10, 10, 10);
region1Shapevr.setFillColor(90, 10, 200);
region1Shapevr.setLineWidth(1);
region1Shapevr.setLineStyle(0);
region1Shapevr.setShapeType(ShapeTypes.LINE);
The current code gives the following result
Here is the desired result

As commented already main suggestion to you is to use cell border lines instead of line shapes. Cell border lines are one of the main features of a spreadsheet. Shapes are not. That's why shapes are much more complicated to use than cell border lines.
But of course drawing line shapes is possible.
The position and size of the shapes is determined by the anchor. One can think about shapes floating in the drawing layer over the cells. The anchor anchors them on the cell edges. According to the positions of the cells they are anchored to, shapes also will stretched or compressed. So also the size is determined by the anchor.
A ClientAnchor has following properties:
Col1 = top left edge of the shape is ancored on left edge of that column
Row1 = top left edge of the shape is ancored on top edge of that row
Col2 = bottom right edge of the shape is ancored on left edge of that column
Row2 = bottom right edge of the shape is ancored on top edge of that row
Dx1 = delta x to shift top left edge of the shape away from the left edge of the Col1
Dy1 = delta y to shift top left edge of the shape away from the top edge of the Row1
Dx2 = delta x to shift bottom right edge of the shape away from the left edge of the Col2
Dy2 = delta y to shift bottom right edge of the shape away from the top edge of the Row2
Note, measurement unit for dx and dy is EMU (English Metric Unit). There is Units to handle those strange measurement units properly.
Complete example:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.util.Units;
import java.io.FileOutputStream;
class CreateExcelLineShapesParallel {
static void drawLine(XSSFDrawing xssfdrawing, XSSFClientAnchor xssfanchor) {
XSSFSimpleShape xssfshape = xssfdrawing.createSimpleShape(xssfanchor);
xssfshape.setShapeType(ShapeTypes.LINE);
xssfshape.setLineWidth(1);
xssfshape.setLineStyle(0);
xssfshape.setLineStyleColor(0, 0, 0);
}
static ClientAnchor getAnchorHorizontalFromCell(CreationHelper helper, Cell cell) {
//anchor determines the size of the line shape to be from
//upper left edge of cell to upper left edge of next cell in row
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setRow1(cell.getRowIndex());
anchor.setCol2(cell.getColumnIndex()+1);
anchor.setRow2(cell.getRowIndex());
//dx and dy in anchor to shift it away from the edges of the cell
//all initialized to 0
anchor.setDx1(0);
anchor.setDx2(0);
anchor.setDy1(0);
anchor.setDy2(0);
return anchor;
}
public static void main(String[] args) throws Exception{
Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateExcelLineShapesParallel.xlsx";
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
Sheet sheet = workbook.createSheet("Sheet1");
int rowHeightInPt = 30;
int lineMarginTopAndBottomInPt = 5;
Row row = sheet.createRow(5);
row.setHeightInPoints(rowHeightInPt);
Cell cell = row.createCell(5);
cell.setCellValue("Hello");
cell.setCellStyle(style);
CreationHelper helper = workbook.getCreationHelper();
Drawing drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = getAnchorHorizontalFromCell(helper, cell);
//dx and dy in anchor to shift it away from the edges of the cell
//measurement unit for dx and dy is EMU (English Metric Unit)
anchor.setDy1(Units.toEMU(lineMarginTopAndBottomInPt));
anchor.setDy2(Units.toEMU(lineMarginTopAndBottomInPt));
//draw a line positioned by the anchor.
drawLine((XSSFDrawing)drawing, (XSSFClientAnchor)anchor);
anchor = getAnchorHorizontalFromCell(helper, cell);
anchor.setDy1(Units.toEMU(rowHeightInPt-lineMarginTopAndBottomInPt));
anchor.setDy2(Units.toEMU(rowHeightInPt-lineMarginTopAndBottomInPt));
drawLine((XSSFDrawing)drawing, (XSSFClientAnchor)anchor);
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close();
workbook.close();
}
}

Related

Horizontally center an image in merged cells using apache poi

The situation is , I merged all the five cells of the first row and inserted an image to the first cell of the first row. My requirements is to make the image horizontally centered for the first row.
I've tried cellStyle.setAlignment(CellStyle.ALIGN_CENTER); but it centers text not images.
Can anyone help?
Note: all the cells have different widths.
Positioning a image in an Excel sheet is a tricky thing since the picture is anchored on two cells. The upper left anchor cell plus a delta-x and a delta-y to this determines the position of the upper left edge of the picture. The bottom right anchor cell plus a delta-x and a delta-y to this determines the size.
Whether cells are merged or not is not important for this process.
So for centering horizontally we need to calculate which is the upper left anchor cell plus a delta-x to this. Fortunately the bottom right anchor cell plus a delta-x and a delta-y to this can be determined automatically through resizing the image to its original size after the upper left anchor cell is set. Of course only if the picture shall appear in its original size.
Example with comments:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Units;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
class CenterImageTest {
public static void main(String[] args) {
try {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
//create the cells A1:F1 with different widths
Cell cell = null;
Row row = sheet.createRow(0);
for (int col = 0; col < 6; col++) {
cell = row.createCell(col);
sheet.setColumnWidth(col, (col+1)*5*256);
}
//merge A1:F1
sheet.addMergedRegion(new CellRangeAddress(0,0,0,5));
//load the picture
InputStream inputStream = new FileInputStream("/home/axel/Bilder/Unbenannt.png");
byte[] bytes = IOUtils.toByteArray(inputStream);
int pictureIdx = wb.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
inputStream.close();
//create an anchor with upper left cell A1
CreationHelper helper = wb.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(0); //Column A
anchor.setRow1(0); //Row 1
//create a picture anchored to A1
Drawing drawing = sheet.createDrawingPatriarch();
Picture pict = drawing.createPicture(anchor, pictureIdx);
//resize the pictutre to original size
pict.resize();
//get the picture width
int pictWidthPx = pict.getImageDimension().width;
System.out.println(pictWidthPx);
//get the cell width A1:F1
float cellWidthPx = 0f;
for (int col = 0; col < 6; col++) {
cellWidthPx += sheet.getColumnWidthInPixels(col);
}
System.out.println(cellWidthPx);
//calculate the center position
int centerPosPx = Math.round(cellWidthPx/2f - (float)pictWidthPx/2f);
System.out.println(centerPosPx);
//determine the new first anchor column dependent of the center position
//and the remaining pixels as Dx
int anchorCol1 = 0;
for (int col = 0; col < 6; col++) {
if (Math.round(sheet.getColumnWidthInPixels(col)) < centerPosPx) {
centerPosPx -= Math.round(sheet.getColumnWidthInPixels(col));
anchorCol1 = col + 1;
} else {
break;
}
}
System.out.println(anchorCol1);
System.out.println(centerPosPx);
//set the new upper left anchor position
anchor.setCol1(anchorCol1);
//set the remaining pixels up to the center position as Dx in unit EMU
anchor.setDx1( centerPosPx * Units.EMU_PER_PIXEL);
//resize the pictutre to original size again
//this will determine the new bottom rigth anchor position
pict.resize();
FileOutputStream fileOut = new FileOutputStream("CenterImageTest.xlsx");
wb.write(fileOut);
fileOut.close();
} catch (IOException ioex) {
}
}
}
For a scaled picture have a scale factor:
double scaleFactor = 0.25d;
and then:
//calculate the center position
int centerPosPx = Math.round(cellWidthPx/2f - (float)pictWidthPx/2f*(float)scaleFactor);
and:
//resize the pictutre to original size again
//this will determine the new bottom rigth anchor position with original size
pict.resize();
//resize the pictutre to scaled size
//this will determine the new bottom rigth anchor position with scaled size
pict.resize(scaleFactor);
Note: Resize to original size first. So determine bottom right anchor position with original size. Then resize scaled. So get bottom right anchor position with scaled size.

set coordinates as center of imageview Android programmatically

I have coordinates to align imageviews on these, programmatically. And right now images are being aligned but the image starts from this coordinate or I should say that the coordinate becomes its top left corner of my imageview and I want to make it center of my imageview. how to make coordinate center of imageview? Right now I'm doing this:
ImageView iv = new ImageView(this);
float x_coordinate = 256;
float y_coordinate = 350;
iv.setX(x_coordinate);
iv.setY(y_coordinate);
iv.setImageResource(R.drawable.myimage);
iv.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mylayout.addView(iv);
I didn't find the simple one line solution but i solved my problem doing the following procedure. Here Place is my model class of which getX_Cord() and getY_Cord() returns the x and y coordinate of image, And you have to save an image in your drawable folder of which size you want to set on your screen in my case this is R.drawable.placeImage.
// display parameters
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
width = size.x;
height = size.y;
final Place place = roomPlace.get(i);
Drawable d = getResources().getDrawable(R.drawable.placeImage);
int y = d.getIntrinsicHeight() / 2;
int x = d.getIntrinsicWidth() / 2;
placeImage.setX((place.getX_Cord() * width) - x);
placeImage.setY((place.getY_Cord() * height) - y);
May be someone gets with more appropriate solution but i solved my problem doing this.

Trying to draw Rotated text with CGAffineTransform and MakeRotation appears at wrong location

I'm trying to draw some rotated texts by using the CGAffineTransform.MakeRotation method at specifc location. I also make use of the TranslateCTM, but something must be wrong as rotated texts do not appear aligned and at the correct x, y position where they should appear, here is simple the code I'm using, anyone know where the problem is? :
public override void Draw (RectangleF rect)
{
DrawTextRotated("Hello1",10,100,30);
DrawTextRotated("Hello2",50,100,60);
SetNeedsDisplay();
}
static public float DegreesToRadians(float x)
{
return (float) (Math.PI * x / 180.0);
}
public void DrawTextRotated(string text,int x, int y, int rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
c.TextMatrix = CGAffineTransform.MakeRotation((float)DegreesToRadians((float)(-rotDegree)));
c.ConcatCTM(c.TextMatrix);
float xxx = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*y);
float yyy = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*x);
// Move the context back into the view
c.TranslateCTM(-xxx,yyy);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetShouldSmoothFonts(true);
MonoTouch.Foundation.NSString str = new MonoTouch.Foundation.NSString(text);
SizeF strSize = new SizeF();
strSize = str.StringSize(UIFont.SystemFontOfSize(12));
RectangleF tmpR = new RectangleF(x,y,strSize.Width,strSize.Height);
str.DrawString(tmpR,UIFont.SystemFontOfSize(12),UILineBreakMode.WordWrap,UITextAlignment.Right);
c.RestoreState();
}
Thanks !
Here's some code that will draw text rotated properly about the top-left corner of the text. For the moment, I'm disregarding your use of text alignment.
First, a utility method to draw a marker where we expect the text to show up:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}
And the code to draw the text (note I've replaced all int rotations with float, and you may want negate your rotation):
public void DrawTextRotated(string text, float x, float y, float rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
DrawMarker(x,y);
// Proper rotation about a point
var m = CGAffineTransform.MakeTranslation(-x,-y);
m.Multiply( CGAffineTransform.MakeRotation(DegreesToRadians(rotDegree)));
m.Multiply( CGAffineTransform.MakeTranslation(x,y));
c.ConcatCTM( m );
// Draws text UNDER the point
// "This point represents the top-left corner of the string’s bounding box."
//http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
NSString ns = new NSString(text);
UIFont font = UIFont.SystemFontOfSize(12);
SizeF sz = ns.StringSize(font);
RectangleF rect = new RectangleF(x,y,sz.Width,sz.Height);
ns.DrawString( rect, font);
c.RestoreState();
}
Rotation about a point requires translation of the point to the origin followed by rotation, followed by rotation back to the original point. CGContext.TextMatrix has no effect on NSString.DrawString so you can just use ConcatCTM.
The alignment and line break modes don't have any effect. Since you're using NSString.StringSize, the bounding rectangle fits the entirety of the text, snug up against the left and right edges. If you make the width of the bounding rectangle wider and use UITextAlignment.Right, you'll get proper right alignment, but the text will still rotate around the top left corner of the entire bounding rectangle. Which is not, I'm guessing, what you're expecting.
If you want the text to rotate around the top right corner, let me know and I'll adjust the code accordingly.
Here's the code I used in my test:
DrawTextRotated("Hello 0",100, 50, 0);
DrawTextRotated("Hello 30",100,100,30);
DrawTextRotated("Hello 60",100,150,60);
DrawTextRotated("Hello 90",100,200,90);
Cheers.

Find scene position of Shape in Group

I have a group of shapes like this:
Group g = new Group();
Rectangle r = new Rectangle( 100, 100, 50, 50 );
Circle c = new Circle( 125, 125, 10 );
g.getChildren().addAll( r, c );
and a Line, which is not part of the group, that ends at the centre of the circle.
Line l = new Line( 0, 0, 125, 125 );
I have made it so that the group can be dragged around as per the Mouse Events demo in Ensemble (just a couple of mouse listeners that update the x and y translation property). What I would like though is for the end of the line to follow the centre of the circle. That means updating the endX and endY properties of the line.
The problem I'm having is that endX and endY are in scene local space (I think). When I drag the group around the screen the centerX and centerY properties of the circle don't get updated. All I seem to get is changes to translationX and translationY on the group. I can't figure out how I find the new centre of the circle after (and during) the drag operation.
Pointers very gratefully received as this has got me tearing my hair out!
The best solution I've come up with is to put a translation listener on the group, get the centre position of the circle and then add the translation of the group but the results aren't exactly great the end of the line roughly follows the circle but jumps about and drifts away. Something like this:
translateXProperty().addListener( new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) {
Connector c = getInputConnectors().get( 0 );
double x = circle.getCenterX();
double y = circle.getCenterY();
line.setEndX( x + newValue.doubleValue());
}
});
Use binding:
l.endXProperty().bind(g.translateXProperty().add(c.centerXProperty()));
l.endYProperty().bind(g.translateYProperty().add(c.centerYProperty()));
P.S.: your way can work too, but note you should have listeners for both x and y property.

Rotating an Image in Silverlight without cropping

I am currently working on a simple Silverlight app that will allow people to upload an image, crop, resize and rotate it and then load it via a webservice to a CMS.
Cropping and resizing is done, however rotation is causing some problems. The image gets cropped and is off centre after the rotation.
WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;
wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;
message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();
//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;
The code above only needs to rotate by 90° at this time so I'm just setting destWidth and destHeight to the height and width of the original image.
It looks like your target image is the same size as your source image. If you want to rotate over 90 degrees, your width and height should be exchanged:
WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);
Also, if you rotate about the centre of the original image, part of it will end up outside the boundaries. You could either include some translation transforms, or simply rotate the image about a different point:
rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);
Try it with a piece of rectangular paper to see why that makes sense.
Many thanks to those above.. they helped a lot. I include here a simple example which includes the additional transform necessary to move the rotated image back to the top left corner of the result.
int width = currentImage.PixelWidth;
int height = currentImage.PixelHeight;
int full = Math.Max(width, height);
Image tempImage2 = new Image();
tempImage2.Width = full;
tempImage2.Height = full;
tempImage2.Source = currentImage;
// New bitmap has swapped width/height
WriteableBitmap wb1 = new WriteableBitmap(height,width);
TransformGroup transformGroup = new TransformGroup();
// Rotate around centre
RotateTransform rotate = new RotateTransform();
rotate.Angle = 90;
rotate.CenterX = full/2;
rotate.CenterY = full/2;
transformGroup.Children.Add(rotate);
// and transform back to top left corner of new image
TranslateTransform translate = new TranslateTransform();
translate.X = -(full - height) / 2;
translate.Y = -(full - width) / 2;
transformGroup.Children.Add(translate);
wb1.Render(tempImage2, transformGroup);
wb1.Invalidate();
If the image isn't square you will get cropping.
I know this won't give you exactly the right result, you'll need to crop it afterwards, but it will create a bitmap big enough in each direction to take the rotated image.
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = Math.Max(width, height);
tempImage2.Height = Math.Max(width, height);
tempImage2.Source = rawImage;
You need to calculate the scaling based on the rotation of the corners relative to the centre.
If the image is a square only one corner is needed, but for a rectangle you need to check 2 corners in order to see if a vertical or horizontal edge is overlapped. This check is a linear comparison of how much the rectangle's height and width are exceeded.
Click here for the working testbed app created for this answer (image below):
double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
The pseudo-code is as follows (actual C# code at the end):
Convert rotation angle into Radians
Calculate the "radius" from the rectangle centre to a corner
Convert BR corner position to polar coordinates
Convert BL corner position to polar coordinates
Apply the rotation to both polar coordinates
Convert the new positions back to Cartesian coordinates (ABS value)
Find the largest of the 2 horizontal positions
Find the largest of the 2 vertical positions
Calculate the delta change for horizontal size
Calculate the delta change for vertical size
Return width/2 / x if horizontal change is greater
Return height/2 / y if vertical change is greater
The result is a multiplier that will scale the image down to fit the original rectangle regardless of rotation.
**Note: While it is possible to do much of the maths using matrix operations, there are not enough calculations to warrant that. I also thought it would make a better example from first-principles.*
C# Code:
/// <summary>
/// Calculate the scaling required to fit a rectangle into a rotation of that same rectangle
/// </summary>
/// <param name="rotation">Rotation in degrees</param>
/// <param name="pixelWidth">Width in pixels</param>
/// <param name="pixelHeight">Height in pixels</param>
/// <returns>A scaling value between 1 and 0</returns>
/// <remarks>Released to the public domain 2011 - David Johnston (HiTech Magic Ltd)</remarks>
private double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
{
// Convert angle to radians for the math lib
double rotationRadians = rotation * PiDiv180;
// Centre is half the width and height
double width = pixelWidth / 2.0;
double height = pixelHeight / 2.0;
double radius = Math.Sqrt(width * width + height * height);
// Convert BR corner into polar coordinates
double angle = Math.Atan(height / width);
// Now create the matching BL corner in polar coordinates
double angle2 = Math.Atan(height / -width);
// Apply the rotation to the points
angle += rotationRadians;
angle2 += rotationRadians;
// Convert back to rectangular coordinate
double x = Math.Abs(radius * Math.Cos(angle));
double y = Math.Abs(radius * Math.Sin(angle));
double x2 = Math.Abs(radius * Math.Cos(angle2));
double y2 = Math.Abs(radius * Math.Sin(angle2));
// Find the largest extents in X & Y
x = Math.Max(x, x2);
y = Math.Max(y, y2);
// Find the largest change (pixel, not ratio)
double deltaX = x - width;
double deltaY = y - height;
// Return the ratio that will bring the largest change into the region
return (deltaX > deltaY) ? width / x : height / y;
}
Example of use:
private WriteableBitmap GenerateConstrainedBitmap(BitmapImage sourceImage, int pixelWidth, int pixelHeight, double rotation)
{
double scale = CalculateConstraintScale(rotation, pixelWidth, pixelHeight);
// Create a transform to render the image rotated and scaled
var transform = new TransformGroup();
var rt = new RotateTransform()
{
Angle = rotation,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(rt);
var st = new ScaleTransform()
{
ScaleX = scale,
ScaleY = scale,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(st);
// Resize to specified target size
var tempImage = new Image()
{
Stretch = Stretch.Fill,
Width = pixelWidth,
Height = pixelHeight,
Source = sourceImage,
};
tempImage.UpdateLayout();
// Render to a writeable bitmap
var writeableBitmap = new WriteableBitmap(pixelWidth, pixelHeight);
writeableBitmap.Render(tempImage, transform);
writeableBitmap.Invalidate();
return writeableBitmap;
}
I released a Test-bed of the code on my website so you can try it for real - click to try it
P.S. Yes this is my answer from another question, duplicated exactly, but the question does require the same answer as that one to be complete.

Resources