I need to remove property in Text (setRise) , if t.setRise(+-) gets out of fields paper.
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
Document doc = new Document(pdfDoc, PageSize.A5);
doc.setMargins(0,0,0,36);
for (int i = 0; i <50 ; i++) {
Text t = new Text("hello " + i);
if(i ==0){
t.setTextRise(7);
}
if(i==31){
t.setTextRise(-35);
}
Paragraph p = new Paragraph(t);
p.setNextRenderer(new ParagraphRen(p,doc));
p.setFixedLeading(fixedLeading);
doc.add(p);
}
doc.close();
}
class ParagraphRen extends ParagraphRenderer{
private float heightDoc;
private float marginTop;
private float marginBot;
public ParagraphRen(Paragraph modelElement, Document doc) {
super(modelElement);
this.heightDoc =doc.getPdfDocument().getDefaultPageSize().getHeight();
this.marginTop = doc.getTopMargin();
this.marginBot = doc.getBottomMargin();
}
#Override
public void drawChildren(DrawContext drawContext) {
super.drawChildren(drawContext);
Rectangle rect = this.getOccupiedAreaBBox();
List<IRenderer> childRenderers = this.getChildRenderers();
//check first line
if(rect.getTop()<=heightDoc- marginTop) {
for (IRenderer iRenderer : childRenderers) {
if (iRenderer.getModelElement().hasProperty(72)) {
Object property = iRenderer.getModelElement().getProperty(72);
float v = (Float) property + rect.getTop();
//check text more AreaPage
if(v >heightDoc){
iRenderer.getModelElement().deleteOwnProperty(72);
}
}
}
}
//check last line
if(rect.getBottom()-marginBot-rect.getHeight()*2<0){
for (IRenderer iRenderer : childRenderers) {
if (iRenderer.getModelElement().hasProperty(72)) {
Object property = iRenderer.getModelElement().getProperty(72);
//if setRise(-..) more margin bottom setRise remove
if(rect.getBottom()-marginBot-rect.getHeight()+(Float) property<0)
iRenderer.getModelElement().deleteOwnProperty(72);
}
}
}
}
}
Here i check if first lines with setRise more the paper area I remove setRise property.
And if last lines with serRise(-35) more then margin bottom I remove it.
But it doesn't work. Properties don't remove.
Your problem is as follows: drawChildren method gets called after rendering has been done. At this stage iText usually doesn't consider properties of any elements: it just places the element in its occupied area, which has been calculated before, at layout() stage.
You can overcome it with layout emulation.
Let's add all your paragraphs to a div rather than directly to the document. Then emulate adding this div to the document:
LayoutResult result = div.createRendererSubTree().setParent(doc.getRenderer()).layout(new LayoutContext(new LayoutArea(0, PageSize.A5)));
In the snippet above I've tried to layout our div on a A5-sized document.
Now you can consider the result of layout and change some elements, which will be then processed for real with Document#add. For example, to get the 30th layouted paragraph one can use:
((DivRenderer)result.getSplitRenderer()).getChildRenderers().get(30);
Some more tips:
split renderer represent the part of the content which iText can place on the area, overflow - the content which overflows.
I am trying to set the registration point of an image to the bottom centre so it can rotate around that point. So far my code looks like this:
var img = new createjs.Bitmap("img.png");
img.x = 200;
img.y = 180;
img.scaleX = 0.35;
img.scaleY = 0.35;
img.regX = img.width/2;
img.regY = 0;
img.rotation = 15;
canvas.addChild(img);
I tried changing the numbers of img.regY but I can't seem to get it correct.
What is redBalloon.width?
The best approach is to wait until the image is loaded, and then use its natural width.
img.image.onload = function() {
img.regX = img.image.naturalWidth;
img.regY = img.image.naturalHeight;
}
Hope that helps.
My app features an InfiniteScrollAdapter populated with images through URLImage and URLImage.ImageAdapter.
In the simulator (Iphone3GS or Xoom or GoogleNexus7), and NPE is shown the first time the InfiniteScrollAdapter appears, although the file does exist on the server.
Please note : In this test there was only one entry in the database. So on the image below what you should see is the same row (image + text) duplicated 3 times.
Please note that the order in the undisplayed icon can differ
The code I used to download the image is :
Image tempPlaceholder = Image.createImage(
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.accentColor);
Graphics gr = tempPlaceholder.getGraphics();
gr.setAntiAliased(true);
gr.setColor(ParametresGeneraux.accentColor);
gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360);
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
And here is the overridden imageAdapter method :
public final static URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
final Image[] tmp = new Image[1];
if (!Display.getInstance().isEdt()) {
// The image scaling has to be called from EDT
Display.getInstance().callSeriallyAndWait(() -> {
tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp[0].getWidth() > placeholderImage.getWidth()) {
int diff = tmp[0].getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp[0].getHeight() > placeholderImage.getHeight()) {
int diff = tmp[0].getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()),
Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true);
}
});
} else {
tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp[0].getWidth() > placeholderImage.getWidth()) {
int diff = tmp[0].getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp[0].getHeight() > placeholderImage.getHeight()) {
int diff = tmp[0].getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()),
Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true);
}
}
EncodedImage[] image2Return = new EncodedImage[1];
if (!Display.getInstance().isEdt()) {
// The image scaling has to be called from EDT
Display.getInstance().callSeriallyAndWait(() -> {
Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp[0] = tmp[0].applyMask(mask);
image2Return[0] = EncodedImage.createFromImage(tmp[0], false);
});
} else {
Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp[0] = tmp[0].applyMask(mask);
image2Return[0] = EncodedImage.createFromImage(tmp[0], false);
}
return image2Return[0];
}
In the stacktrace, the NPE seems to stem from the overridden URLImage.ImageAdapter :
java.lang.IllegalArgumentException: create image failed for the given
image data of length: 0 at
com.codename1.ui.Image.createImage(Image.java:654) at
com.codename1.ui.EncodedImage.getInternal(EncodedImage.java:365) at
com.codename1.ui.EncodedImage.getInternalImpl(EncodedImage.java:340)
at com.codename1.ui.EncodedImage.getHeight(EncodedImage.java:522) at
com.codename1.ui.Image.scaledLargerRatio(Image.java:899) at
com.my.application.ParametresGeneraux$1.lambda$adaptImage$0(ParametresGeneraux.java:564)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:95) at
com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1207) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1244) at
com.codename1.ui.URLImage$DownloadCompleted.actionPerformed(URLImage.java:233)
at com.codename1.ui.URLImage$4.onSucess(URLImage.java:301) at
com.codename1.ui.URLImage$4.onSucess(URLImage.java:297) at
com.codename1.util.CallbackDispatcher.run(CallbackDispatcher.java:53)
at com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.mainEDTLoop(Display.java:999) at
com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120) at
com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
[EDT] 0:0:0,1 - Codename One revisions:
e5c43877074c18b4b5c7748d000e5cfac75ab749 2318
[EDT] 0:0:0,1 - Exception: java.lang.NullPointerException - null
java.lang.NullPointerException at
com.codename1.impl.javase.JavaSEPort.scale(JavaSEPort.java:3996) at
com.codename1.ui.Image.scale(Image.java:1007) at
com.codename1.ui.Image.scaledImpl(Image.java:953) at
com.codename1.ui.Image.scaled(Image.java:918) at
com.codename1.impl.javase.JavaSEPort$71.save(JavaSEPort.java:7659) at
com.codename1.ui.EncodedImage.scaledEncoded(EncodedImage.java:626) at
com.codename1.ui.EncodedImage.scaled(EncodedImage.java:653) at
com.codename1.ui.Image.scaledLargerRatio(Image.java:904) at
com.my.application.ParametresGeneraux$1.lambda$adaptImage$0(ParametresGeneraux.java:564)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:95) at
com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1207) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1244) at
com.codename1.ui.URLImage$DownloadCompleted.actionPerformed(URLImage.java:233)
at com.codename1.ui.URLImage$4.onSucess(URLImage.java:301) at
com.codename1.ui.URLImage$4.onSucess(URLImage.java:297) at
com.codename1.util.CallbackDispatcher.run(CallbackDispatcher.java:53)
at com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.mainEDTLoop(Display.java:999) at
com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120) at
com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
Moreover, a glance in the .cn1 directory shows the URLImage storage file name with the suffix "ImageURLTMP" which does not appear when everything works without NPE.
Finally, if I come back to this form later, everything works as expected (images were shown, no NPE). I tried to test for downloadedImage nullness in imageAdapter but the EncodedImage is not null.
How can I avoid this NPE?
Edit March 1st 2017
Following the answers from #Diamond and #Shai, I believe the NPE occurs because the InfiniteScrollAdapter wants to fill in the screen with rows and consequently launches the download of the same image simultaneously (because it is not in cache). So a solution could be to prevent the InfiniteScrollAdapter to loop (so it becomes finite). How can I do that ?
Please also note that there is not 404 error, the Network monitor shows response code 200 as depicted below. However the image should not be downloaded 3 times, should it ?
Change your ImageAdapter to the following:
public static final URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
Image tmp = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp.getWidth() > placeholderImage.getWidth()) {
int diff = tmp.getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp = tmp.subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp.getHeight() > placeholderImage.getHeight()) {
int diff = tmp.getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp = tmp.subImage(0, y, Math.min(placeholderImage.getWidth(), tmp.getWidth()),
Math.min(placeholderImage.getHeight(), tmp.getHeight()), true);
}
Image roundMask = Image.createImage(tmp.getWidth(), tmp.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp.getWidth(), tmp.getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp = tmp.applyMask(mask);
return EncodedImage.createFromImage(tmp, false);
}
#Override
public boolean isAsyncAdapter() {
return true;
}
};
No need to check EDT.
Make sure your tempPlaceholder image is applied to your component first and at the end of your logic, call your URLImage in a callSerially() method:
Image tempPlaceholder = Image.createImage(
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.accentColor);
Graphics gr = tempPlaceholder.getGraphics();
gr.setAntiAliased(true);
gr.setColor(ParametresGeneraux.accentColor);
gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360);
myComponent.setIcon(tempPlaceholder);
...
//Then call this at the end of everything
Display.getInstance().callSerially(() -> {
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
myComponent.setIcon(reportImage);
myComponent.getComponentForm().repaint();
});
Edit:
Based on #Shai's answer, you could check if you are currently downloading the same image and prevent another one from being pulled. Because this usually causes a conflict:
//Declare this at the top of your class
final static private Map<String, Image> LOADED_URLS = new HashMap<>();
//Then change the URLImage image method to this
Display.getInstance().callSerially(() -> {
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = LOADED_URLS.containsKey(photoFilenameInStorage) ? LOADED_URLS.get(photoFilenameInStorage)
: URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
LOADED_URLS.put(photoFilenameInStorage, reportImage);
myComponent.setIcon(reportImage);
myComponent.getComponentForm().repaint();
});
In your adapter check if downloadedImage.getData() is null. I assume it's not and it's a 404 error page or something similar to that.
In that case your adapter can catch the exception and just return a fallback that matches what you expect to see when no image exists.
This works the second time around since the system sees the tmp file and assumes a download is in progress so it doesn't invoke the download code again. The tmp file is later renamed to the final downloadable file.
I have been trying to figure out a way to center text on a button, but can't find an easy, multi-purpose way to. I can do it, but it will only work for a certain string, not for any string. i would like to know if there is a way to center any string on a button. My button in this case is 185x50.
I have been able to center this button on the screen, like so:
buttonX = WIDTH / 2 - (screen.getRegionWidth() / 2);
buttonY = HEIGHT / 2 - (screen.getRegionHeight() / 2);
Any help would be much appreciated. :)
Updated the answer to libgdx version 1.7.1-SNAPSHOT:
The easiest way to do this, is to use the TextButton class from libgdx. The text from a TextButton is centered by default. This still works!
Updated example:
final BitmapFont font = new BitmapFont();
final String text = "Test";
final GlyphLayout layout = new GlyphLayout(font, text);
// or for non final texts: layout.setText(font, text);
final float fontX = objectX + (objectWidth - layout.width) / 2;
final float fontY = objectY + (objectHeight + layout.height) / 2;
font.draw(batch, layout, fontX, fontY);
Outdated example:
This no longer works!
If you do not want to use it, you can get the font width and height with:
font.getBounds("Test text");
So you can do something like this:
String fontText = "";
fontX = buttonX + buttonWidth/2 - font.getBounds(fontText).width/2;
fontY = buttonY + buttonHeight/2 + font.getBounds(fontText).height/2;
For the newer version of libgdx the function BitMapFont.getBounds() isn't there in api anymore. You can use GlyphLayout to get bounds.For example,
BitmapFont font;
SpriteBatch spriteBatch;
//... Load any font of your choice first
FreeTypeFontGenerator fontGenerator = new FreeTypeFontGenerator(
Gdx.files.internal("myFont.ttf")
);
FreeTypeFontGenerator.FreeTypeFontParameter freeTypeFontParameter =
new FreeTypeFontGenerator.FreeTypeFontParameter();
freeTypeFontParameter.size = size;
fontGenerator.generateData(freeTypeFontParameter);
font = fontGenerator.generateFont(freeTypeFontParameter);
//Initialize the spriteBatch as requirement
GlyphLayout glyphLayout = new GlyphLayout();
String item = "Example";
glyphLayout.setText(font,item);
float w = glyphLayout.width;
font.draw(spriteBatch, glyphLayout, (Game.SCREEN_WIDTH - w)/2, y);
Try the following:
label.setPosition(Gdx.graphics.getWidth()/2-(label.getPrefWidth()/2),Gdx.graphics.getHeight()-(label.getPrefHeight()/2));
I have textpane getting generated. i need to supercript the text when the text is selected and during click of superscript button, i need to superscript the text. if the text is already superscripted, it needs to unsuperscript the text. My problem is i am able to superscript the text, but unable to restore back. I am checking for the isSuperscript condition, but then every time it returns the value as true and sets the text as superscript. below is the code i am using, can anyone tell me how i can reset the superscripted text.
SimpleAttributeSet sasText = new SimpleAttributeSet(parentTextPane.getCharacterAttributes());
System.out.println("character set 1 " + sasText.toString());
if ( StyleConstants.isSuperscript(sasText) ){
System.out.println("already super");
StyleConstants.setSuperscript(sasText, false);
} else {
System.out.println("needs super");
StyleConstants.setSuperscript(sasText, true);
}
int caretOffset = parentTextPane.getSelectionStart();
parentTextPane.select(caretOffset, caretOffset + textLength);
HTMLDoc.setCharacterAttributes(selStart,textLength,sasText, false);
parentEkit.refreshOnUpdate();
The problem is that parentTextPane.getCharacterAttributes() will return the character attributes for the character after the current caret position. As your selection encompasses your superscript text, the following character is normal. It is the attributes for that following char that you are testing, and the result will be false. You have the option of doing what getCharacterAttributes() (from JTextPane):
public AttributeSet getCharacterAttributes() {
StyledDocument doc = getStyledDocument();
Element run = doc.getCharacterElement(getCaretPosition());
if (run != null) {
return run.getAttributes();
}
return null;
}
except that you want to return the start of your selection:
public AttributeSet getMyCharacterAttributes() {
StyledDocument doc = parentTextPane.getStyledDocument();
Element run = doc.getCharacterElement(parentTextPane.getSelectionStart());
if (run != null) {
return run.getAttributes();
}
return null;
}
Your code would then change to do something like the following:
SimpleAttributeSet sasText = new SimpleAttributeSet(getMyCharacterAttributes());
//... the rest of your code
It works fine for me. I do a quick test with code like:
SimpleAttributeSet green = new SimpleAttributeSet();
System.out.println( StyleConstants.isSuperscript(green) );
StyleConstants.setForeground(green, Color.GREEN);
StyleConstants.setSuperscript(green, true);
System.out.println( StyleConstants.isSuperscript(green) );
StyleConstants.setSuperscript(green, false);
System.out.println( StyleConstants.isSuperscript(green) );
and get the output:
false
true
false
which proves that the attribute is being reset properly. The text is also displayed properly.
If your "sasText" always returns true when you test for the superscript attribute then you must be resetting that attribute somewhere else in your code.
If you need more help post your SSCCE showing the problem.