I am trying to center a minus sign on the screen using the following code:
text = "-";
textPosition = new Point();
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
paint.setTextAlign(Paint.Align.CENTER);
textPosition.x = (int) (myArea.left + myArea.width() / 2);
textPosition.y = (int) (myArea.top + myArea.height() / 2 + bounds.height() / 2);
canvas.drawText(text, textPosition.x, textPosition.y, paint);
This code works like a charm when "text" is something else (for example a plus sign), but in this case the minus sign is positioned too high on the screen.
EDIT:
Here is the result for four different operators.
And... the funny thing is, that I created my own font to display these operators. So I know, that it should be centered correctly.
PS: Now that I look at it, all off them seem a little off, though the minus-sign is the worst...
Okay, I fixed it. Not by changing the code, but by changing the font. I have now aligned all characters on the baseline and that seems to work.
Related
I am trying to draw a string exactly in the center of a rectangle using JavaFX GraphicsContext2D.
I don't want to use JavaFX components so please do not recommend them.
For example, I stroke a rectangle with attributes: x = 10, y = 10, width = 100, height = 100. Now I want to stroke a text in a way that it comes exactly in the center(horizontally and vertically) of the rectangle. How can I do it?
As #James_D comments, you can use the GraphicsContext methods setTextAlign() and setTextBaseline() to center the fillText() in an arbitrary Rectangle. Starting from this example, I added the following lines in the tooltips loop in order to produce the image shown:
gc.setTextAlign(TextAlignment.CENTER);
gc.setTextBaseline(VPos.CENTER);
gc.setFill(Color.BLACK);
gc.fillText(color.toString(),
bounds.getX() + bounds.getWidth() / 2,
bounds.getY() + bounds.getHeight() / 2);
I am trying to add tiled diagonal watermarks to the pdf, but it seems that pattern fills in iText are always tiled from the bottom left of the page, meaning that the tiles at the top and right side of the page can be cut abruptly. Is there an option to tile from the top left or with an offset instead?
Here is a sample of the code:
List<String> watermarkLines = getWatermarkLines();
Rectangle watermarkRect = getWatermarkRect();
PdfContentByte over = stamper.getOverContent(1);
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight();
for (int x = 0; x < watermarkLines.size(); x++) {
AffineTransform trans = getWatermarkTransform(watermarkLines, x);
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
}
over.setColorFill(new PatternColor(painter));
over.rectangle(0, 0, pageSize.getWidth(), pageSize.getHeight());
over.fill();
I tried changing the x and y of the rectangle function to negative or positive values, but it seems that the watermark is still stamped in the pattern as if it was tiled from the bottom left, cutting it in the same place as before.
First of, I cannot fathom which iText version you are using,
List<String> watermarkLines = getWatermarkLines();
...
ColumnText.showTextAligned(painter, 0, watermarkLines.get(x), (float) trans.getTranslateX(), (float) trans.getTranslateY(), 45f);
implies that the third parameter of the ColumnText.showTextAligned method you use is typed as String or Object. The iText 5 version I have at hand, though, requires a Phrase there. Below I'll show how to apply an offset with the current iText 5.5.13. You'll have to check whether it also works for your version.
Yes, you can apply an offset... in the pattern definition!
If instead of
PdfPatternPainter painter = over.createPattern(watermarkRect.getWidth(), watermarkRect.getHeight());
you create the pattern like this
PdfPatternPainter painter = over.createPattern(2 * watermarkRect.getWidth(), 2 * watermarkRect.getHeight(),
watermarkRect.getWidth(), watermarkRect.getHeight());
you have the same step size of pattern application (watermarkRect.getWidth(), watermarkRect.getHeight()) but a canvas twice that width and twice that height to position you text on. By positioning the text with an offset, you effectively move the whole pattern by that offset.
E.g. if you calculate the offsets as
Rectangle pageSize = pdfReader.getCropBox(1);
float xOff = pageSize.getLeft();
float yOff = pageSize.getBottom() + ((int)pageSize.getHeight()) % ((int)watermarkRect.getHeight());
and draw the text using
ColumnText.showTextAligned(painter, 0, new Phrase(watermarkLines.get(x)), (float) trans.getTranslateX() + xOff, (float) trans.getTranslateY() + yOff, 45f);
the pattern should fill the page as if starting at the top left corner of the visible page.
You haven't supplied getWatermarkLines, getWatermarkRect, and getWatermarkTransform. If I use
static AffineTransform getWatermarkTransform(List<String> watermarkLines, int x) {
return AffineTransform.getTranslateInstance(6 + 15*x, 6);
}
static Rectangle getWatermarkRect() {
return new Rectangle(65, 50);
}
static List<String> getWatermarkLines() {
return Arrays.asList("Test line 1", "Test line 2");
}
your original code for me creates a top left corner like this
and the code with the above offset creates one like this
I use the class org.apache.pdfbox.pdmodel.PDPageContentStream for write text into PDF file.
I need to scale the text horizontally.
How can it be done?
do this before drawing your text:
contentStream.transform(Matrix.getScaleInstance(2, 1));
this will enlarge on the x axis. If this effect is to be temporary, don't forget to put your draw commands within saveGraphicsState() and restoreGraphicsState().
Alternatively, use setTextMatrix() with the same parameter. The later one has the advantage that one call replaces the previous.
THIS IS CORRECT CODE:
contentStream.saveGraphicsState();
contentStream.beginText();
contentStream.setFont(currentBaseFont, currentFontSize);
//X HORIZONTAL SCALING
Matrix scaleInstance = Matrix.getScaleInstance(scalaRiga / 100f, 1);
contentStream.transform(scaleInstance);
float rot = (float)((rotazione == 90)?(Math.PI / 2):(Math.PI * 2));
contentStream.setTextMatrix(Matrix.getRotateInstance(rot, x, y));
contentStream.showText(txpDati.getDati());
contentStream.endText();
contentStream.restoreGraphicsState();
THANK YOU TILMAN
FR
I'm using libGDX ProgressBar. I create it with the next code:
style = new ProgressBar.ProgressBarStyle();
style.background = new SpriteDrawable(backgroundImage);
NinePatch fillImage = new NinePatch(new TextureRegion(img2, 1, 1, img2.getWidth(), img2.getHeight()), 25, 26, 30, 30);
style.knobBefore = new NinePatchDrawable(fillImage);
style.knobBefore.setMinWidth(0);
style.knobBefore.setRightWidth(0);
style.knobBefore.setLeftWidth(0);
progress = new ProgressBar(0, 30, 0.1f, false, style);
When I set a value to a big number, for example 15 this is how it looks:[1]
but when I set a small value 1/30 (smaller the the basic 9 patch image) it looks bad:
Any ideas how to handle this problem?
You are setting your NinePatch width to be less than the minimum. The left and right do not change, so the minimum width your NinePatch can be is 25 + 26 = 51px. Any less than this you will get erroneous results. I believe the reason it looks like that is because your sides are actually pushing through each other, creating a negative width for the centre, which is still completely acceptable to draw with. (You can see in your image that the right is at the very far left, the left is most likely at the very far right but behind the reversed middle) The simplest solution would just be to limit the progress bar width to the proper minimum with something like this (if progress value is between 0 and 1);
//convert min width to a ratio
float minProgress = style.knobBefore.getMinWidth() / progressBarLength;
float progress = Math.max(progress, minProgress);
getMinWidth() would probably tell you it is 51/52px. You also shouldn't force setMinWidth(), setLeftWidht(), setRightWidth().
I'm trying to draw a NinePatch using a transform matrix so it can be scaled, rotated, moved etc. So I created a class that inherits from LibGDX's NinePatch class and which is responsible of the matrix.
This is how I compute my transform matrix (I update it each time one of the following values changes) :
this.transform
.idt()
.translate(originX, originY, 0)
.rotate(0, 0, 1, rotation)
.scale(scale, scale, 1)
.translate(-originX, -originY, 0)
;
and how I render my custom NinePatch class :
drawConfig.begin(Mode.BATCH);
this.oldTransform.set(drawConfig.getTransformMatrix());
drawConfig.setTransformMatrix(this.transform);
this.draw(drawConfig.getBatch(), this.x, this.y, this.width, this.height); // Libgdx's NinePatch#draw()
drawConfig.setTransformMatrix(this.oldTransform);
Case 1
Here's what I get when I render 4 nine patches with :
Position = 0,0 / Origin = 0,0 / Scale = 0.002 / Rotation = different for each 9patch
I get what I expect to.
Case 2
Now the same 4 nine patches with :
Position = 0,0 / Origin = 0.5,0.5 / Scale = same / Rotation = same
You can see that my 9 patches aren't draw at 0,0 (their position) but at 0.5,0.5 (their origin), like if I had no .translate(-originX, -originY, 0) when computing the transform matrix. Just to be sure, I commented this instruction and I indeed get the same result. So why is my 2nd translation apparently not taken into account?
The problem is probably your scaling. Because it also scales down the translation, your seccond translate actually translates (-originX*scale, -originY*scale, 0) since scale=0.002, it looks like there is no translate at all. For instance for the x coordinate, you compute :
x_final = originX + scale * (-originX + x_initial)
I had to change the code computing my transform matrix to take the scale into account when translating back as pointed by Guillaume G. except my code is different from his :
this.transform
.idt()
.translate(originX, originY, 0)
.rotate(0, 0, 1, rotation)
.scale(scale, scale, 1)
.translate(-originX / scale, -originY / scale, 0);
;