I'm trying to draw curves with gradient stroke color not fill color using JAVA PDFbox, anyone can help me with how to do that?
For filling shapes I'm using:
contentStream.clip();
contentStream.shadingFill(PdfUtils.createGradientColor(gradientFactors));
public static PDShadingType2 createGradientColor(GradientFactors gradientFactors) throws IOException {
Color startColor = gradientFactors.getStartColor().getColor();
Color endColor = gradientFactors.getEndColor().getColor();
COSDictionary fdict = new COSDictionary();
fdict.setInt(COSName.FUNCTION_TYPE, 2);
COSArray domain = new COSArray();
domain.add(COSInteger.get(0));
domain.add(COSInteger.get(1));
COSArray c0 = new COSArray();
c0.add(new COSFloat(startColor.getRed() / 255f));
c0.add(new COSFloat(startColor.getGreen() / 255f));
c0.add(new COSFloat(startColor.getBlue() / 255f));
COSArray c1 = new COSArray();
c1.add(new COSFloat(endColor.getRed() / 255f));
c1.add(new COSFloat(endColor.getGreen() / 255f));
c1.add(new COSFloat(endColor.getBlue() / 255f));
fdict.setItem(COSName.DOMAIN, domain);
fdict.setItem(COSName.C0, c0);
fdict.setItem(COSName.C1, c1);
fdict.setInt(COSName.N, 1);
PDFunctionType2 func = new PDFunctionType2(fdict);
PDShadingType2 axialShading = new PDShadingType2(new COSDictionary());
axialShading.setColorSpace(PDDeviceRGB.INSTANCE);
axialShading.setShadingType(PDShading.SHADING_TYPE2);
COSArray coords1 = new COSArray();
coords1.add(new COSFloat(gradientFactors.getX1()));
coords1.add(new COSFloat(gradientFactors.getY1()));
coords1.add(new COSFloat(gradientFactors.getX2()));
coords1.add(new COSFloat(gradientFactors.getY2()));
axialShading.setCoords(coords1);
axialShading.setFunction(func);
return axialShading;
}
Could anyone please help me with this?
As already indicated by Tilman, you create a PDColor from your shading:
PDShadingType2 shading = createGradientColor(...);
PDShadingPattern pattern = new PDShadingPattern();
pattern.setShading(shading);
COSName name = page.getResources().add(pattern);
PDColor color = new PDColor(name, new PDPattern(null));
Then you draw with that color, e.g.:
try ( PDPageContentStream canvas = new PDPageContentStream(document, page)) {
canvas.setStrokingColor(color);
canvas.setLineWidth(5);
canvas.moveTo(0, 0);
canvas.lineTo(500, 500);
canvas.curveTo2(500, 250, 250, 250);
canvas.curveTo1(0, 250, 0, 0);
canvas.stroke();
}
Assuming that gradient goes from red to green from 0,0 to 500,500, the result looks like this:
(DrawGradient test testDrawWithGradientColor)
Related
I'm trying to perform a signing and want to generate the same multiple appearances. Is there a way to achieve a custom appearance as we can generate in normal cases?
PdfSignatureFormField sigField = (PdfSignatureFormField) acroForm.getField(fieldName);
sigField.addKid(
createSubField(pdfPage, 40, 700, 100, 40, "sig1", signatureDictionary, document));
sigField.addKid(
createSubField(pdfPage, 400, 600, 100, 40, "sig2", signatureDictionary, document));
The following is the method which is generating subfields.
public static PdfFormField createSubField(PdfPage page, float x, float y, float width,
float height, String name, PdfDictionary signatureDictionary, PdfDocument pdfDocument) {
PdfFormXObject appearance = new PdfFormXObject(new Rectangle(width, height));
PdfCanvas pdfCanvas = new PdfCanvas(appearance, pdfDocument);
try (Canvas canvas = new Canvas(pdfCanvas, new Rectangle(width, height))) {
canvas.showTextAligned(name, 2, 2, TextAlignment.LEFT);
}
PdfDictionary ap = new PdfDictionary();
ap.put(PdfName.N, appearance.getPdfObject());
PdfWidgetAnnotation widget = new PdfWidgetAnnotation(new Rectangle(x, y, width, height));
widget.setFlags(PdfAnnotation.PRINT | PdfAnnotation.LOCKED);
widget.setPage(page);
widget.put(PdfName.AP, ap);
PdfSignatureFormField sigField = PdfFormField.createSignature(pdfDocument);
sigField.setFieldName(name);
sigField.addKid(widget);
sigField.put(PdfName.V, signatureDictionary);
page.addAnnotation(widget);
return sigField;
}
I'm trying to rotate text using pdfbox by I couldn't achieve it. I tried to set the texMatrix but my text is not rotating as intended.
Does someone have an idea of how I could turn at 90 degrees my text?
This is my code :
contentStream.beginText();
float tx = titleWidth / 2;
float ty = titleHeight / 2;
contentStream.setTextMatrix(Matrix.getTranslateInstance(tx, ty));
contentStream.setTextMatrix(Matrix.getRotateInstance(Math.toRadians(90),tx,ty));
contentStream.setTextMatrix(Matrix.getTranslateInstance(-tx, -ty));
contentStream.newLineAtOffset(xPos, yPos);
contentStream.setFont(font, fontSize);
contentStream.showText("Tets");
contentStream.endText();
Thank You
Here's a solution that draws three pages, one with text unrotated, one with text rotated but keeping the coordinates as if planning landscape printing, and one that is what you wanted (rotated around the center of the text). My solution is close to that, it rotates around the bottom of the center of the text.
public static void main(String[] args) throws IOException
{
PDDocument doc = new PDDocument();
PDPage page1 = new PDPage();
doc.addPage(page1);
PDPage page2 = new PDPage();
doc.addPage(page2);
PDPage page3 = new PDPage();
doc.addPage(page3);
PDFont font = PDType1Font.HELVETICA;
float fontSize = 20;
int xPos = 100;
int yPos = 400;
float titleWidth = font.getStringWidth("Tets") / 1000;
float titleHeight = fontSize;
float tx = titleWidth / 2;
float ty = titleHeight / 2;
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page1))
{
contentStream.beginText();
contentStream.newLineAtOffset(xPos, yPos);
contentStream.setFont(font, fontSize);
contentStream.showText("Tets");
contentStream.endText();
}
// classic case of rotated page
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page2))
{
contentStream.beginText();
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(90), 0, 0);
matrix.translate(0, -page2.getMediaBox().getWidth());
contentStream.setTextMatrix(matrix);
contentStream.newLineAtOffset(xPos, yPos);
contentStream.setFont(font, fontSize);
contentStream.showText("Tets");
contentStream.endText();
}
// rotation around text
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page3))
{
contentStream.beginText();
Matrix matrix = Matrix.getRotateInstance(Math.toRadians(90), 0, 0);
matrix.translate(0, -page3.getMediaBox().getWidth());
contentStream.setTextMatrix(matrix);
contentStream.newLineAtOffset(yPos - titleWidth / 2 - fontSize, page3.getMediaBox().getWidth() - xPos - titleWidth / 2 - fontSize);
contentStream.setFont(font, fontSize);
contentStream.showText("Tets");
contentStream.endText();
}
doc.save("saved.pdf");
doc.close();
}
This example rotates around the left baseline of the text and uses the matrix translation to position the text at the specific point.
The showText() is always positioned at 0,0, which is the position before the rotation. The matrix translation then positions the text after the rotation.
If you want another rotation point of your text relocation the text rotation position in the contentStream.newLineAtOffset(0, 0)-line
float angle = 35;
double radians = Math.toRadians(angle);
for (int x : new int[] {50,85,125, 200})
for (int y : new int[] {40, 95, 160, 300}) {
contentStream.beginText();
// Notice the post rotation position
Matrix matrix = Matrix.getRotateInstance(radians,x,y);
contentStream.setTextMatrix(matrix);
// Notice the pre rotation position
contentStream.newLineAtOffset(0, 0);
contentStream.showText(".(" + x + "," + y + ")");
contentStream.endText();
}
To get the height and the width of the text you want to rotate use font.getBoundingBox().getHeight()/1000*fontSize and font.getStringWidth(text)/1000*fontSize.
I have 4 points. For example...
A(1;1)
B(2;5)
C(4;4)
D(3;2)
How I can change RGB parameters in this rectangle (for all pixels)?
Like this:
double[] data = mat.get(x, y);
data[0] = data[0]+30;
data[1] = data[1]+20;
data[2] = data[2]+10;
mat.put(x, y, data);
Try something like that for implementing approach described in Dan MaĊĦek comment:
...
Bitmap sourceBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.<your_image>);
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat maskMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC4);
Mat resultMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC4);
// create color, which added to sourceMat region (+100 - for red channel)
Scalar color = new Scalar(100, 0, 0, 255);
// or you can try Scalar color = new Scalar(10, 20, 30); as in your question
Point[] region = new Point[4];
// your coords multiplied by 50 for visualization convenience
region[0] = new Point(50, 50);
region[1] = new Point(100, 250);
region[2] = new Point(200, 200);
region[3] = new Point(150, 100);
List<MatOfPoint> contours = new ArrayList();
MatOfPoint regionMat = new MatOfPoint(region);
contours.add(regionMat);
// create mask
Imgproc.drawContours(maskMat, contours, 0, color, -1);
// apply mask to source
Core.add(maskMat, sourceMat, resultMat);
// just for visualisation
Bitmap bitmap = Bitmap.createBitmap(sourceMat.cols(), sourceMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(resultMat, bitmap);
<your_ImageView>.setImageBitmap(bitmap);
...
NB! This is just example of masking, not optimized.
Edited answer due to comment below:
This is OpenCV C++ code but you can easily port it to JAVA. Also my code assumes the points always represent a rectangle.
// read image
Mat image=imread("image.jpg",-1);
// region of interest, shape=rectangle
Point p1(50,50), p2(100,80);
Rect roi(p1.x,p1.y,p2.x,p2.y);
// vector hold channels
std::vector<Mat> channels(3);
// split original image to bgr channels
cv::split(image, channels);
// Mat to hold ROI
Mat extractedRoi;
//For channel B
extractedRoi = channels.at(0)(roi);
extractedRoi += 30;
//For channel G
extractedRoi = channels.at(1)(roi);
extractedRoi += 20;
//For channel R
extractedRoi = channels.at(2)(roi);
extractedRoi += 10;
// merge channels back together
cv::merge(channels, image);
Edit2: A faster approach.
Mat image=imread("/home/haseebullah/Pictures/S1.jpg",-1);
Point p1(50,50), p2(100,80);
Rect roi(p1.x,p1.y,p2.x,p2.y);
Mat extractedRoi;
extractedRoi = image(roi);
Scalar constants(30,20,10);
extractedRoi += constants
In the picture below, the application detected multiple "black" and drawn a bounding rectangle around them. Now I want to compare the rect3.tl().y values of each rectangle and only keep the lowest one, deleting the other bounding rectangles. But I'm not sure how to go about doing that.
Code:
Rect rectBlack = new Rect();
Bitmap roiBitmap = null;
Scalar green = new Scalar(0, 255, 0, 255);
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CvType.CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
Mat roiTmp = sourceMat.clone();
bitmapWidth = sourceBitmap.getWidth();
Log.e("bitmapWidth", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
final Mat hsvMat = new Mat();
sourceMat.copyTo(hsvMat);
// convert mat to HSV format for Core.inRange()
Imgproc.cvtColor(hsvMat, hsvMat, Imgproc.COLOR_RGB2HSV);
Scalar lowerb = new Scalar(85, 50, 40); // lower color border for BLUE
Scalar upperb = new Scalar(135, 255, 255); // upper color border for BLUE
Scalar lowerblack = new Scalar(0, 0, 0); // lower color border for BLACK
Scalar upperblack = new Scalar(180, 255, 40); // upper color border for BLACK
Scalar testRunL = new Scalar(60, 50, 40); // lower Green 83 100 51
Scalar testRunU = new Scalar(90, 255, 255); // upper Green
Core.inRange(hsvMat, lowerblack, upperblack, roiTmp); // select only blue pixels
// find contours
List<MatOfPoint> contours = new ArrayList<>();
List<RotatedRect> boundingRects = new ArrayList<>();
Imgproc.findContours(roiTmp, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
double rectangleArea = boundingRect.size.area();
// test min ROI area in pixels
if (rectangleArea > 1300 ) {
Point rotated_rect_points[] = new Point[4];
boundingRect.points(rotated_rect_points);
Rect rect3 = Imgproc.boundingRect(new MatOfPoint(rotated_rect_points));
// test horizontal ROI orientation
if (rect3.height > rect3.width) {
Log.e("w,h", String.valueOf(rect3.width)+ " h " + String.valueOf(rect3.height));
double w = rect3.width;
double h = rect3.height;
double ratio= h/w;
Log.e("h:w ratio", String.valueOf(ratio));
Log.e("Black Area", String.valueOf(rect3.area()));
Imgproc.rectangle(sourceMat, rect3.tl(), rect3.br(), green, 3);
rectBlack = rect3;
Log.e("blackArea", String.valueOf(rect3.area()));
xBlack = rect3.br().x;
xBlackCenter = (rect3.br().x + rect3.tl().x) / 2;
yBlack = rect3.br().y;//bottom
battHeight = (rect3.br().y - rect3.tl().y); //batt height in pixel
}
}
}
You can create list of rects:
List<Rect> rects = new ArrayList<>();
then in your for (MatOfPoint contour : contours) loop add each founded rectangle to that list:
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
...
// test horizontal ROI orientation
if (rect3.height > rect3.width) {
...
rects.add(rect3)
}
}
then use method to find bottom-most rectangle, like that:
public static Rect getBottomMostRect(List<Rect> rects) {
Rect bottomMostRect = null;
if (rects != null && rects.size() >= 1) {
Rect rect;
double minY;
int ixMinY = 0;
rect = rects.get(ixMinY);
minY = rect.tl().y;
for (int ix = 1; ix < rects.size(); ix++) {
rect = rects.get(ix);
if (rect.tl().y < minY) {
minY = rect.tl().y;
ixMinY = ix;
}
}
bottomMostRect = rects.get(ixMinY);
}
return bottomMostRect;
}
and call it this way:
Rect bottomMostRect = getBottomMostRect(rects)
Or add getBottomMostRect() implementation directly into your for (MatOfPoint contour : contours) loop.
I have this piece of code that make me somehow a latex formula in a image and display to a pdf.The first thing is i have a error when i open the pdf file 109. and the first page with the formulas is not displayed at all.
the next ones is ok.
Any ideas? Thanks
document.open();
Paragraph preface = new Paragraph();
// We add one empty line
addEmptyLine(preface, 1);
// Lets write a big header
preface.add(new Paragraph("Test Generat", catFont));
addEmptyLine(preface, 1);
document.add(preface);
// Start a new page
// document.newPage();
for (int i = 0; i < intrebari.size(); i++) {
TeXFormula formula = new TeXFormula("x=\\frac{-b \\pm \\sqrt {b^2-4ac}}{2a}");
TeXIcon icon = formula
.createTeXIcon(TeXConstants.STYLE_DISPLAY, 20);
// insert a border
icon.setInsets(new Insets(5, 5, 5, 5));
// now create an actual image of the rendered equation
BufferedImage image = new BufferedImage(icon.getIconWidth(),
icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, icon.getIconWidth(), icon.getIconHeight());
JLabel jl = new JLabel();
jl.setForeground(new Color(0, 0, 0));
icon.paintIcon(jl, g2, 0, 0);
document.setPageSize(PageSize.A4);
PdfContentByte pdfCB = new PdfContentByte(writer);
try {
Image image1 = Image.getInstance(pdfCB, image, 1);
document.newPage();
Phrase p = new Phrase("quick brown fox jumps over the lazy dog.");
p.add(new Chunk(image1, 0, 0, true));
p.add(" ");
p.add(new Chunk(image1, 0, 0, true));
ColumnText ct = new ColumnText(writer.getDirectContent());
ct.setSimpleColumn(new Rectangle(20, document.getPageSize().getWidth(), 400, 800));
ct.addText(p);
ct.go();
document.newPage();
} catch (BadElementException | IOException ex) {
Logger.getLogger(Utils.class.getName()).log(Level.SEVERE, null, ex);
}
}
document.newPage();
}