I want to keep track of the y-coordinate when generating pdf.
This is how I am currently doing it.
PDRectangle mediabox = page.findMediaBox();
float margin = 15;
float y = mediabox.getUpperRightY() - margin;
float fontSize = 10f;
PDType1Font font = PDType1Font.HELVETICA;
contentStream.showText("Hello");
y = y - fontSize; //decrease y-coordinate
contentStream.newLine(); //go to new line
contentStream.showText("World!");
y = y - fontSize; //decrease y-coordinate
What is the height of new line so that I can precisely keep track of the y-coordinate?
I need something like this.
contentStream.showText("Hello");
y = y - fontSize; //decrease y-coordinate
contentStream.newLine(); //go to new line
y = y - newLineSize; <---- require the height of new line.
contentStream.showText("World!");
y = y - fontSize; //decrease y-coordinate
Thank you.
The operator created by newLine() starts a new line taking the start of the current line and subtracting the leading from the y coordinate, a value you can set using setLeading.
Related
I'm currently working on a raycaster in Java, and so far, I have the floor correctly textured. The problem, however, is that the floor doesn't scroll. In other words, when I move the camera in the projection, the floor stays the same, yet the walls move as expected. I'm really not sure what I'm doing wrong. I took almost all the code from this reference. Note that I took some liberties when pasting the code in that I used some pseudocode.
I tried applying a player offset to the tileX and tileY variables, e.g., tileX += player.x, and all I got was a floor that scrolls far too quickly and incorrectly.
for every ray:
... // other stuff relating to the walls above here.
int start = (int)(wallY + wallHeight + 1);
double directionCos = cos(rad(ray.getAngle()));
double directionSin = sin(rad(ray.getAngle()));
int textureDim = 16;
for (int y = start; y < screenHeight; y++) {
double distance = screenHeight / (2.f * y - screenHeight);
distance /= cos(rad(player.getAngle()) - rad(ray.getAngle()));
// The source I grabbed the code from actually appends the player's x and y to the tileX and tileY variables, but this completely messes up the textures when I try to.
double tileX = distance * directionCos;
double tileY = distance * directionSin;
int textureX = Math.floorMod((int)(tileX * textureDim), textureDim);
int textureY = Math.floorMod((int)(tileY * textureDim), textureDim);
int rgb = floorTexture.getRGB(textureX, textureY);
projectionFloor.setRGB((int)wallX, y, rgb);
}
Below is an image of the floor.
Below is an animation visualizing the problem.
Below is an animation visualizing what happens if I try to apply a player position offset:
Fixed it on my own. Turns out that, yes, you do have to account for the player's position (shocker!); the source I got the code from just didn't do it correctly.
DTPP = distance to projection plane.
for every pixel y from wallY + wallHeight + 1 to projectionHeight:
double r = y - this.getPreferredSize().height / 2.f;
double d = (CAMERA_HEIGHT * DTPP / r) / ANGLE;
double tileX = CAMERA_X + d * RAY_COSANGLE;
double tileY = CAMERA_Y + d * RAY_SINANGLE;
int textureX = Math.floorMod((int) (tileX * TEXTURE_SIZE /
TEXTURE_SCALE), TEXTURE_SIZE);
int textureY = Math.floorMod((int) (tileY * TEXTURE_SIZE /
TEXTURE_SCALE), TEXTURE_SIZE);
... (drawing occurs here)
How do you find out the height and width of a PFont string in Processing or Java?
The best thing you can do when you have a question like this is to read through the Processing reference.
Specifically you're probably looking for the textWidth(), textAscent(), and textDescent() functions.
size(400, 400);
textSize(36);
String str = "Hello world";
float x = 100;
float y = 100;
float strWidth = textWidth(str);
float strAscent = textAscent();
float strDescent = textDescent();
float strHeight = strAscent + strDescent;
rect(x, y - strAscent, strWidth, strHeight);
fill(0);
text(str, x, y);
Using the inbuilt functions textWidth(), textAscent(), and textDescent() are an easy way to get a good approximate result for the height and width of a string, but they are not exact.
Why?
textAscent() returns text height above the line based on the letter 'd'
textDescent() returns text height below the line based on the letter 'p'.
textWidth() includes glyph whitespace (aka padding; ideally we want to ignore this for the first and last characters)
textAscent() + textDescent() therefore measures the maximum height of a string in a given font and font size, and not the height of a specific string. In other words, if your text doesn't include both 'd' and 'p' characters, then using these methods to determine text height will overestimate the result (as we see in Kevin's screenshot).
Getting the exact height
We can use this approach to get an exact result for height:
Get a vector representation of each character
Iterate over the vector's vertices, finding:
The vertex with highest Y position
The vertex with lowest Y position
Subtract the highest Y position from the lowest Y position to determine the exact string height
Code Example
Note you'll need to explicitly create a PFont for this.
String string = "Hello world";
PFont font = createFont("Arial", 96, true); // arial, size 96
textFont(font);
float minY = Float.MAX_VALUE;
float maxY = Float.NEGATIVE_INFINITY;
for (Character c : string.toCharArray()) {
PShape character = font.getShape(c); // create character vector
for (int i = 0; i < character.getVertexCount(); i++) {
minY = min(character.getVertex(i).y, minY);
maxY = max(character.getVertex(i).y, maxY);
}
}
final float textHeight = maxY - minY;
Result
(Note we're still using textWidth() for width here)
text(string, mouseX, mouseY);
rect(mouseX, mouseY, textWidth("Hello world"), -textHeight);
Getting the exact width
Code Example
String string = "Hello world";
PFont font = createFont("Arial", 96, true); // arial, size 96
textFont(font);
float textWidth = textWidth(string); // call Processing method
float whitespace = (font.width(string.charAt(string.length() - 1)) * font.getSize()
- font.getGlyph(string.charAt(string.length() - 1)).width) / 2;
textWidth -= whitespace; // subtract whitespace of last character
whitespace = (font.width(string.charAt(0)) * font.getSize() - font.getGlyph(string.charAt(0)).width) / 2;
textWidth -= whitespace; // subtract whitespace of first character
Result
(Putting the two together...)
text(string, mouseX, mouseY);
rect(mouseX + whitespace, mouseY, textWidth, -textHeight);
Y-Axis Alignment
A rectangle drawn around "Hello world" happens to be aligned because none of the glyphs descend below the baseline.
With a string like ##'pdXW\, both # and p descend below the baseline such that the rectangle, although it is the correct height, is out of alignment with the string on the y-axis, as below:
A programmatic way to determine the y-offset would be to find the Y-coordinate of the lowest (although remember Processing's y-axis extends downwards so we're actually looking for the highest value) vertex . Fortunately, this was calculated as part of finding the exact height.
We can simply use the maxY value that was calculated there to offset the text bounding box.
Result
text(string, mouseX, mouseY);
rect(mouseX + whitespace, mouseY + maxY, textWidth, -textHeight);
I am creating a Cartesian Coordinate graph using NumberAxis to create an x and y axis in a LineChart, and use getDisplayPosition to find the distance between two points along the x axis and two points along the y axis. The differences do not equal each other.
I set upper/lower bounds of the x and y axis to be the same with the same tickUnit setting. Visually, the grid looks square and equal on all sides but I cannot seem to make them exactly equal.
Here is how I set NumberAxis for x and y within my linechart:
xAxis.setLowerBound(-2.4);
xAxis.setUpperBound(2.4);
xAxis.setTickUnit(.1);
xAxis.setSide(Side.BOTTOM);
xAxis.setAutoRanging(false);
yAxis.setLowerBound(-2.4);
yAxis.setUpperBound(2.4);
yAxis.setTickUnit(.1);
yAxis.setSide(Side.LEFT);
yAxis.setAutoRanging(false);
LineChart linechart = new LineChart<Number,Number>(xAxis, yAxis);
Here is how I convert inputs for a rectangle to be displayed properly
private Rectangle calcRectangle(SectionRectangle rectangle){
double xPosition = xAxis.getDisplayPosition(rectangle.getXorigin());
double yPosition = yAxis.getDisplayPosition(rectangle.getYorigin());
double xCoord = xPosition + chartZeroX;//x origin of rectangle
double yCoord = yPosition + chartZeroY;//y origin of rectangle
double width = rectangle.getXorigin() + rectangle.getWidth();
double widthPosition = xAxis.getDisplayPosition(width);
double heightPosition = yAxis.getDisplayPosition(rectangle.getYorigin() + rectangle.getHeight());
Rectangle calculatedRectangle = new Rectangle(xCoord, yCoord - (yPosition - heightPosition), widthPosition - xPosition, yPosition - heightPosition);
calculatedRectangle.setFill(Color.TRANSPARENT);
Rotate rotate = new Rotate();
rotate.setAngle(360 - rectangle.getRotation());
rotate.setPivotX(xPosition + chartZeroX);
rotate.setPivotY(yPosition + chartZeroY);
calculatedRectangle.getTransforms().add(rotate);
calculatedRectangle.setStroke(Color.BLACK);
return calculatedRectangle;
And here is how I add the shape onto the graph
Pane chartContent = (Pane) linechart.lookup(".chart-content");
Rectangle rectangle = calcRectangle(rectangleData);
chartContent.getChildren().add(rectangle);
Without rotating, the rectangle will be displayed to scale as expected, but because of the slight variance in pixel width vs pixel height, rotating 90 degrees causes the rectangle to measure taller than it had measured wide.
Here is a more precise example of the issue
double xdiff = xAxis.getDisplayPosition(1) - xAxis.getDisplayPosition(0);
double ydiff = yAxis.getDisplayPosition(0) - yAxis.getDisplayPosition(1);
here xdiff = 195.208 and ydiff = 191.041
Hoping for some way to force the x and y axis to maintain exact equal distance.
I solved my own problem by controlling the height and width of the linechart:
linegraph.setPrefHeight(850);
linegraph.setMinHeight(850);
linegraph.setMaxHeight(850);
linegraph.setMinWidth(853);
linegraph.setMaxWidth(853);
linegraph.setPrefWidth(853);
linegraph.setPrefSize(853, 850);
linegraph.setMinSize(853, 850);
linegraph.setMaxSize(853, 850);
I did a check of the distance between x=0, x=1 and y=0, y=1 using:
double xlength = xAxis.getDisplayPosition(1) - xAxis.getDisplayPosition(0);
double ylength = yAxis.getDisplayPosition(0) - yAxis.getDisplayPosition(1);
I slowly incremented my width until both lengths were equal.
im trying to draw multiple diagonal lines across the whole image (leaving a space between them) i've used this code to draw horizontal and vertical lines:
for (int z = 1; z < partToCrop; z++) {
Shape hLines = new Line2D.Float(0, cropInPartWidth*z, chunkWidth, cropInPartWidth*z);
Shape vLines = new Line2D.Float(cropInPartHeight*z, 0, cropInPartHeight*z, chunkHeight);
gr.draw(hLines); //gr is a BufferedImage
gr.draw(vLines);
}
where
int partToCrop = 5;
float cropInPartWidth = imgWidth / partToCrop;
float cropInPartHeight = imgHeight / partToCrop;
and works good. Now i need to draw multiple diagonal lines (i.e 4 diagonal lines) with 45° and -45° degrees of inclination across the whole image, hope you will help me.
Thanks.
Shape firstLine = new Line2D.Float(0, imgHeight, imgWidth, 0); // this line is from bottom left to top right
Shape secondLine = new Line2D.Float(0, 0, imgWidth, imgHeight); // this line is from top left to bot right
Actually this seems easier to me.
Assuming imgDim = imgHeight = imgWidth:
int spacing = 2;
for (int z = 1; z < imgDim; z = z + spacing)
{
Shape dLines = new Line2D.Float(0, z, z, 0);
gr.draw(dLines);
}
So, I'm using the code in Java tutorial to draw a piece of text, but I don't know how to align text to the right margin.
I just included attstring.addAttribute(TextAttribute.RUN_DIRECTION, TextAttribute.RUN_DIRECTION_RTL); in the code for that case but it doesn't work.
protected float drawParagraph (Graphics2D g2, String text, float width, float x, float y, Boolean dir){
AttributedString attstring = new AttributedString(text);
attstring.addAttribute(TextAttribute.FONT, font);
if (dir == TextAttribute.RUN_DIRECTION_RTL){
attstring.addAttribute(TextAttribute.RUN_DIRECTION, TextAttribute.RUN_DIRECTION_RTL);
}
AttributedCharacterIterator paragraph = attstring.getIterator();
int paragraphStart = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = y;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position. If the paragraph is right-to-left we
// will align the TextLayouts to the right edge of the panel.
// Note: drawPosX is always where the LEFT of the text is placed.
float drawPosX = (float) (layout.isLeftToRight()
? x : breakWidth - layout.getAdvance());
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
return drawPosY;
}
Give a hand please, I'm lost ^^
The error was in the calculation of drawPosX. The working formula is drawPosX = (float) x + breakWidth - layout.getAdvance();
I ended up doing a little fix to support center alignment and here is the code:
public abstract class MyClass extends JPanel implements Printable{
[...]
public static enum Alignment {RIGHT, LEFT, CENTER};
[...]
/**
* Draw paragraph.
* Pinta un parrafo segun las localizaciones pasadas como parametros.
*
* #param g2 Drawing graphic.
* #param text String to draw.
* #param width Paragraph's desired width.
* #param x Start paragraph's X-Position.
* #param y Start paragraph's Y-Position.
* #param dir Paragraph's alignment.
* #return Next line Y-position to write to.
*/
protected float drawParagraph (Graphics2D g2, String text, float width, float x, float y, Alignment alignment){
AttributedString attstring = new AttributedString(text);
attstring.addAttribute(TextAttribute.FONT, font);
AttributedCharacterIterator paragraph = attstring.getIterator();
int paragraphStart = paragraph.getBeginIndex();
int paragraphEnd = paragraph.getEndIndex();
FontRenderContext frc = g2.getFontRenderContext();
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);
// Set break width to width of Component.
float breakWidth = width;
float drawPosY = y;
// Set position to the index of the first character in the paragraph.
lineMeasurer.setPosition(paragraphStart);
// Get lines until the entire paragraph has been displayed.
while (lineMeasurer.getPosition() < paragraphEnd) {
// Retrieve next layout. A cleverer program would also cache
// these layouts until the component is re-sized.
TextLayout layout = lineMeasurer.nextLayout(breakWidth);
// Compute pen x position.
float drawPosX;
switch (alignment){
case RIGHT:
drawPosX = (float) x + breakWidth - layout.getAdvance();
break;
case CENTER:
drawPosX = (float) x + (breakWidth - layout.getAdvance())/2;
break;
default:
drawPosX = (float) x;
}
// Move y-coordinate by the ascent of the layout.
drawPosY += layout.getAscent();
// Draw the TextLayout at (drawPosX, drawPosY).
layout.draw(g2, drawPosX, drawPosY);
// Move y-coordinate in preparation for next layout.
drawPosY += layout.getDescent() + layout.getLeading();
}
return drawPosY;
}
}