Related
I created a pdf using the following example:
https://developers.itextpdf.com/examples/actions-and-annotations/clone-creating-and-adding-annotations#2260-addstamp.java
#Category(SampleTest.class)
public class AddStamp extends GenericTest {
public static final String DEST = "./target/test/resources/sandbox/annotations/add_stamp.pdf";
public static final String IMG = "./src/test/resources/img/itext.png";
public static final String SRC = "./src/test/resources/pdfs/hello.pdf";
public static void main(String[] args) throws Exception {
File file = new File(DEST);
file.getParentFile().mkdirs();
new AddStamp().manipulatePdf(DEST);
}
#Override
protected void manipulatePdf(String dest) throws Exception {
PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC), new PdfWriter(DEST));
ImageData img = ImageDataFactory.create(IMG);
float w = img.getWidth();
float h = img.getHeight();
Rectangle location = new Rectangle(36, 770 - h, w, h);
PdfStampAnnotation stamp = new PdfStampAnnotation(location)
.setStampName(new PdfName("ITEXT"));
PdfFormXObject xObj = new PdfFormXObject(new Rectangle(w, h));
PdfCanvas canvas = new PdfCanvas(xObj, pdfDoc);
canvas.addImage(img, 0, 0, false);
stamp.setNormalAppearance(xObj.getPdfObject());
stamp.setFlags(PdfAnnotation.PRINT);
pdfDoc.getFirstPage().addAnnotation(stamp);
pdfDoc.close();
}
}
The pdf is properly created and contains the stamp annotation
I can get the annotation using:
...
PdfStampAnnotation s = (PdfStampAnnotation) pdfDoc.getFirstPage().getAnnotations().get(0);
s.?????
How can I get back the image (itext.png) of the stamp (eg: byte[]) ?
I'm really new to itext and after hours of research I'm stuck at this point...
First of all, you won't get the original image back. PDF support only very few bitmap image formats as they are: JPEG, JPEG2000, certain fax formats, but definitively not PNG. PNGs are converted into the PDF internal bitmap format, and upon extraction can best be converted back to a PNG.
Furthermore, the reason why there is no simple getImage method in the PdfStampAnnotation class is that the appearance of a stamp may be constructed like the contents of a regular page, it may contain text, it may contain vector graphics, it may contain bitmap images, it may contain an arbitrary mixture of those elements. Thus, all you can retrieve from an annotation is its appearance.
If you are sure an annotation contains only an image (or you at least are not interested in anything but the image), you can extract that image using the iText parser framework, e.g. like this:
Map<byte[], String> extractAnnotationImages(PdfStream xObject) {
final Map<byte[], String> result = new HashMap<>();
IEventListener renderListener = new IEventListener() {
#Override
public Set<EventType> getSupportedEvents() {
return Collections.singleton(RENDER_IMAGE);
}
#Override
public void eventOccurred(IEventData data, EventType type) {
if (data instanceof ImageRenderInfo) {
ImageRenderInfo imageRenderInfo = (ImageRenderInfo) data;
byte[] bytes = imageRenderInfo.getImage().getImageBytes();
String extension = imageRenderInfo.getImage().identifyImageFileExtension();
result.put(bytes, extension);
}
}
};
PdfCanvasProcessor processor = new PdfCanvasProcessor(renderListener, Collections.emptyMap());
processor.processContent(xObject.getBytes(), new PdfResources(xObject.getAsDictionary(PdfName.Resources)));
return result;
}
(ExtractAnnotationImage method)
which returns a mapping from image byte arrays to file extension to use.
I used it in this helper method:
void saveAnnotationImages(PdfDocument pdfDocument, String prefix) throws IOException {
for (int pageNumber = 1; pageNumber <= pdfDocument.getNumberOfPages(); pageNumber++) {
PdfPage page = pdfDocument.getPage(pageNumber);
int index = 0;
for (PdfAnnotation annotation : page.getAnnotations()) {
PdfDictionary normal = annotation.getAppearanceObject(PdfName.N);
if (normal instanceof PdfStream) {
Map<byte[], String> images = extractAnnotationImages((PdfStream)normal);
for (Map.Entry<byte[], String> entry : images.entrySet()) {
Files.write(new File(String.format("%s-%s-%s.%s", prefix, pageNumber, index++, entry.getValue())).toPath(), entry.getKey());
}
}
}
}
}
(ExtractAnnotationImage helper method)
to extract all images from annotations from the output of the iText example AddStamp you reference and got one image:
By the way, you'll recognize here that transparency is missing. Transparency in the PDF is modeled via a second image, a mask image, which effectively represents something like an alpha channel. One can retrieve this mask from the ImageRenderInfo.getImage() object.
I have a Product entity, which has a imageUrl String field.
Products images after obtaining from user will be saved in directory:
System.getProperty("user.home") + "shop/data/product/"
And when user wants to see some Product I need to get this image from "user.home"+... to JSP page.
I've tried to read the image into the byte array, convert it to Base64 encoding, and then refer in JSP like this:
<img alt="image from user home" src="data:image/png, base64;${requestScope.image}">
But this solution is not working, and as far as I understand, it has a limitation on image size.
Could you suggest me a way how to do such thing?
Try this ( i think you have some typo )
<img alt="image from user home" src="data:image/png;base64,${requestScope.image}">
Also use this site: http://www.askapache.com/online-tools/base64-image-converter/ to make sure that your output Base64 code is correct.
There's an example of ImageAction that serves image from the file system. It's called
Struts 2 dynamic image example.
Instead of base64 encoding/decoding which increases the content length two times and slows down page loading you can use the action that returnes image bytes from the file. It could be a database, in this way it should fetch bytes from Blob.
In your <img> tag that is using src attribute can contain the URL to the action that returns response with a header Content-Type: image/jpeg and bytes written to the body.
This is the code of the ImageAction:
#Result(type = "stream", params = {"contentType", "${type}"})
public class ImageAction extends ActionSupport implements ServletRequestAware {
byte[] imageInByte = null;
String imageId;
private HttpServletRequest servletRequest;
private final static String type = "image/jpeg";
public getInputStream() { return new ByteArrayInputStream(getCustomImageInBytes()); }
public String getType() { return type; }
private String getFilename() {
return this.filename;
}
public String getImageId() {
return imageId;
}
public void setImageId(String imageId) {
this.imageId = imageId;
}
public ImageAction() {
System.out.println("ImageAction");
}
public byte[] getCustomImageInBytes() {
System.out.println("imageId" + imageId);
BufferedImage originalImage;
try {
originalImage = ImageIO.read(getImageFile(this.imageId));
// convert BufferedImage to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpeg", baos);
baos.flush();
imageInByte = baos.toByteArray();
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return imageInByte;
}
private File getImageFile(String imageId) {
String filePath = servletRequest.getSession().getServletContext().getRealPath("/");
File file = new File(filePath + "/Image/", imageId);
System.out.println(file.toString());
return file;
}
#Override
public void setServletRequest(HttpServletRequest request) {
this.servletRequest = request;
}
}
This action supposed to have configuration created by convention-plugin. So it could be used in HTML like this
<img src="<s:url action='Image?imageId=darksouls.jpg' />" alt=""/>
So Alireza Fattahi was right that I had mistakes in my code. The first one is typo in img tag (see answer by Alireza Fattahi), the second one is after reading image to bytes array
byte[] image = ...;
I used
Base64.getEncoder().encode(image);
instead of
Base64.getEncoder().encodeToString(image));
So eventually this method with returning Base64 encoded image works. If there is a better choices - please left comments and answers.
I have a requirement to display the image as part of the FieldGroup. This is for the functionality where the Image appears as normal on a web page, when in edit mode I need to edit this image value by providing an 'upload' option.
I have a Pojo with a property of type com.vaadin.ui.Image along with the other String and Integer values.
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
I need to work this Image as a normal form element, for example when I edit a form, I have an editable TextField to type in the String value and change it, the same way I intend to display an upload button where I would have an option to replace the existing image.
I have tried using EasyUploads addon for this purpose by intercepting the build method of FieldGroup and specifying the Image field as of type org.vaadin.easyuploads.UploadField
Like this;
#Override
#SuppressWarnings({ "rawtypes", "unchecked" })
protected <T extends Field> T build(String caption, Class<?> dataType,
Class<T> fieldType) throws BindException {
T field = super.build(caption, dataType, fieldType);
if (caption.equalsIgnoreCase("image")) {
final UploadField imageField = new UploadField() {
#Override
protected void updateDisplay() {
final byte[] pngData = (byte[]) getValue();
String filename = getLastFileName();
String mimeType = getLastMimeType();
long filesize = getLastFileSize();
if (mimeType.equals("image/jpeg")) {
StreamSource imagesource = new ImageSource(pngData);
StreamResource resource = new StreamResource(
imagesource, "Uploaded File");
Embedded embedded = new Embedded("Image:" + filename
+ "(" + filesize + " bytes)", resource);
getRootLayout().addComponent(embedded);
} else {
super.updateDisplay();
}
}
};
imageField.setFieldType(FieldType.BYTE_ARRAY);
...
This however fails to display the already available image, errors out with the stacktrace:
Caused by: java.lang.IllegalArgumentException: Property type class com.vaadin.ui.Image is not compatible with UploadField
at org.vaadin.easyuploads.UploadField.setPropertyDataSource(UploadField.java:1021)
at com.vaadin.data.fieldgroup.FieldGroup.bind(FieldGroup.java:265)
at com.vaadin.data.fieldgroup.BeanFieldGroup.bind(BeanFieldGroup.java:167)
at com.vaadin.data.fieldgroup.FieldGroup.setItemDataSource(FieldGroup.java:106)
at com.vaadin.data.fieldgroup.BeanFieldGroup.setItemDataSource(BeanFieldGroup.java:142)
Is there a cleaner way of using an Image as part of the FieldGroup in vaadin 7?
I would suggest replacing in your Pojo the Image instance with a simple byte[], because looking through the UploadField code, I can't see any natural way of converting the result from the upload (which is either a byte[] or a File instance) into something else, using the FieldGroup like you asked.
If you look inside AbstractField.getValue(), you will see that the model value is eventually passed through a settable converter which would have normally helped you in this case (see com.vaadin.data.util.converter.Converter). But I think you are pretty much forced to use byte[] if you want to bind a image bean to the FieldGroup.
Anyway, if you DO replace with byte[] the following steps will help you:
Create a custom FieldGroupFieldFactory that will create an UploadField if you want to bind to a byte[] property + passes a ValueChangeListener for the UploadField is done uploading:
public class ImageEnhancedFieldFactory extends DefaultFieldGroupFieldFactory {
private Property.ValueChangeListener fileUploadedListener;
private ImageEnhancedFieldFactory(Property.ValueChangeListener fileUploadedListener) {
this.fileUploadedListener = fileUploadedListener;
}
#Override
public <T extends Field> T createField(Class<?> type, Class<T> fieldType) {
if (byte[].class.equals(type)) {
UploadField uploadField = new UploadField(UploadField.StorageMode.MEMORY);
uploadField.setFieldType(UploadField.FieldType.BYTE_ARRAY);
uploadField.setButtonCaption("Change image");
uploadField.addListener(fileUploadedListener);
return (T) uploadField;
}
return super.createField(type, fieldType);
}
}
Create an Image instance that shows the content of the byte[] from the pojo:
final ImagePojo imagePojo = new ImagePojo();
imagePojo.setName("superman");
imagePojo.setImageContent(new byte[0]);
BeanItem<ImagePojo> item = new BeanItem<ImagePojo>(imagePojo);
final StreamResource imageResource = new StreamResource(new StreamResource.StreamSource() {
#Override
public InputStream getStream() {
return new ByteArrayInputStream(imagePojo.getImageContent());
}
}, "myimage");
imageResource.setCacheTime(0);
final Image image = new Image("Image", imageResource);
addComponent(image);
NOTE: its necessary to set the cache time to 0 in order to prevent the browser from caching the resource( see https://vaadin.com/book/vaadin7/-/page/components.embedded.html in the section Generating and Reloading Images)
3.Create the FieldGroup (with the new FieldGroupFieldFactory set) and bind to the properties of the pojo, including the one that contains the image content (the byte[]):
FieldGroup fieldGroup = new FieldGroup(item);
fieldGroup.setFieldFactory(new ImageEnhancedFieldFactory(new Property.ValueChangeListener() {
#Override
public void valueChange(Property.ValueChangeEvent event) {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String filename = "myfilename-" + df.format(new Date()) + ".jpg";
imagePojo.setImageContent((byte[])event.getProperty().getValue());
image.markAsDirty();
imageResource.setFilename(filename);
}
}));
addComponent(fieldGroup.buildAndBind("Image name", "name"));
addComponent(fieldGroup.buildAndBind("Image content", "imageContent"));
I left a snippet on Gist of a component that you can paste in you UI and play around if you need (https://gist.github.com/gabrielruiu/9953279)
So my friend crated this enum and i want to shorten it. I thought to shorting each of the file paths in strings and refrencing it String pokemonPath = "Pictures/Menu/MainMenu/"; and then being like pokePath +" Cosmet.png but I'm not sure on how to implement it. Also if anyone has anyideas on how to shorten it more, i would love to hear it too. ALos its in its own class file so putting constants at the top will not work properly, it give me an error.
I don't really work with enums so i dont know how to use them efficiently.
This is his code
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public enum Images {
// Menu stuff. I don't know how to organize it. You do it.
WelcomeBG("Pictures/Menu/MainMenu/WelcomeBG.png"), PokemonTitleMenuBG(
"Pictures/Menu/MainMenu/PokemonTitleMenuBG.png"), PokemonMenuAll(
"Pictures/Menu/PokemonMenu/PokemonMenuAll.png"), PokemonMenuContinue(
"Pictures/Menu/PokemonMenu/PokemonMenuContinue.png"), PokemonMenuContinueSelected(
"Pictures/Menu/PokemonMenu/PokemonMenuContinueSelected.png"), PokemonMenuAllSelected(
"Pictures/Menu/PokemonMenu/PokemonMenuAllSelected.png"), TitleScreen(
"Pictures/Menu/MainMenu/TitleScreen.png"),
Professor("Pictures/Menu/NPC/Professor.png"), ProfessorFlip(
"Pictures/Menu/NPC/ProfessorFlip.png"),
FriendGirl("Pictures/Menu/PlayerPictures/FriendGirl.png"), FriendBoy(
"Pictures/Menu/PlayerPictures/FriendBoy.png"), PlayerBoy(
"Pictures/Menu/PlayerPictures/PlayerBoy.png"), PlayerGirl(
"Pictures/Menu/PlayerPictures/PlayerGirl.png"), Male(
"Pictures/Menu/PlayerPictures/Male.png"), Female(
"Pictures/Menu/PlayerPictures/Female.png"), Credits(
"Pictures/Menu/MainMenu/Credits.png"), Gender(
"Pictures/Menu/PlayerPictures/Gender.png"),
// Player sprites
PlayerDown("Pictures/Sprites/Player/"), PlayerDown1(
"Pictures/Sprites/Player/Down1.png"), PlayerDown2(
"Pictures/Sprites/Player/Down2.png"), PlayerLeft(
"Pictures/Sprites/Player/Left.png"), PlayerLeft1(
"Pictures/Sprites/Player/Left1.png"), PlayerLeft2(
"Pictures/Sprites/Player/Left2.png"), PlayerRight(
"Pictures/Sprites/Player/Right.png"), PlayerRight1(
"Pictures/Sprites/Player/Right1.png"), PlayerRight2(
"Pictures/Sprites/Player/Right2.png"), PlayerUp(
"Pictures/Sprites/Player/Up.png"), PlayerUp1(
"Pictures/Sprites/Player/Up1.png"), PlayerUp2(
"Pictures/Sprites/Player/Up2.png"),
// NPC Sprites
DadDown("Pictures/Sprites/NPC/Dad/DadDown.png"), DadRight(
"Pictures/Sprites/NPC/Dad/DadRight.png"), BoyLeft(
"Pictures/Sprites/NPC/Boy/BoyLeft.png"), BoyRight(
"Pictures/Sprites/NPC/Boy/BoyRight.png"), BoyRight1(
"Pictures/Sprites/NPC/Boy/BoyRight1.png"), BoyRight2(
"Pictures/Sprites/NPC/Boy/BoyRight2.png"), BoyUp(
"Pictures/Sprites/NPC/Boy/BoyUp.png"), BoyUp1(
"Pictures/Sprites/NPC/Boy/BoyUp1.png"), BoyUp2(
"Pictures/Sprites/NPC/Boy/BoyUp2.png"), DadUp(
"Pictures/Sprites/NPC/Dad/DadUp.png"), DadLeft(
"Pictures/Sprites/NPC/Dad/DadLeft.png"),
GirlDown("Pictures/Sprites/NPC/Girl/GirlDown.png"), GirlRight(
"Pictures/Sprites/NPC/Girl/GirlRight.png"), GirlLeft(
"Pictures/Sprites/NPC/Girl/GirlLeft.png"), GateDown(
"Pictures/Sprites/NPC/Gate/GateDown.png"), GateLeft(
"Pictures/Sprites/NPC/Gate/GateLeft.png"), GateLeft1(
"Pictures/Sprites/NPC/Gate/GateLeft1.png"), GateLeft2(
"Pictures/Sprites/NPC/Gate/GateLeft2.png"), GateRight(
"Pictures/Sprites/NPC/Gate/GateRight.png"), GateRight1(
"Pictures/Sprites/NPC/Gate/GateRight1.png"), GateRight2(
"Pictures/Sprites/NPC/Gate/GateRight2.png"), BoyDown(
"Pictures/Sprites/NPC/Boy/BoyDown.png"),
// NPC Effects
Exclamation("Pictures/Sprites/Misc/Exclamation.png"),
// Maps (the background, base thing)
Exitium("Pictures/Maps/Exitium.png"), Route1("Pictures/Maps/Route1.png"), House1Top(
"Pictures/Maps/House1Top.png"), House1Bot(
"Pictures/Maps/House1Bot.png"), MiracleForest1(
"Pictures/Maps/MiracleForest1.png"), MiracleForest2(
"Pictures/Maps/MiracleForest2.png"),
// Map decorations (the stuff you code on top of the map!)
House1Overlay("Pictures/Sprites/Structures/House1Overlay.png"), House2Overlay(
"Pictures/Sprites/Structures/House2Overlay.png"), House3Overlay(
"Pictures/Sprites/Structures/House3Overlay.png"), House4Overlay(
"Pictures/Sprites/Structures/House4Overlay.png"), Lamppost(
"Pictures/Sprites/Structures/Lamppost.png"), MailBox(
"Pictures/Sprites/Structures/MailBox.png"), MailBox2(
"Pictures/Sprites/Structures/MailBox2.png"), ColumnTrees(
"Pictures/Sprites/Structures/ColumnTrees.png"), Sign(
"Pictures/Sprites/Misc/Sign.png"), GreenColumnTrees(
"Pictures/Sprites/Structures/GreenColumnTrees.png"), GreenColumnTrees2(
"Pictures/Sprites/Structures/GreenColumnTrees2.png"), GreenColumnTrees3(
"Pictures/Sprites/Structures/GreenColumnTrees3.png"), ForestEntrance(
"Pictures/Sprites/Structures/ForestEntrance.png"), GreenTreeOverlay(
"Pictures/Sprites/Structures/GreenTreeOverlay.png"), LeftEntrance(
"Pictures/Sprites/Structures/LeftEntrance.png"), RightEntrance(
"Pictures/Sprites/Structures/RightEntrance.png"),
// GUI Things???
Arrow("Pictures/Sprites/Misc/Arrow.png"), Hand(
"Pictures/Sprites/Misc/Hand.png"), DialogBox(
"Pictures/Sprites/Misc/DialogBox.png"), DialogBox2(
"Pictures/Sprites/Misc/DialogBox2.png"), DialogBox3(
"Pictures/Sprites/Misc/DialogBox3.png"), DialogBox4(
"Pictures/Sprites/Misc/DialogBox4.png"),
// I dont even know
MainBattleBack("Pictures/Sprites/Battle/BattleStart/MainBattleBack.png"), Vs2(
"Pictures/Sprites/Battle/BattleStart/Vs2.png"), MainHead(
"Pictures/Sprites/Battle/BattleStart/MainHead.png"),
// Battle Menu GUI things??
GreenBackground("Pictures/Sprites/Battle/BattleMenu/GreenBackground.png"), BattleMenu2(
"Pictures/Sprites/Battle/BattleMenu/BattleMenu2.png"), HpBarAlly(
"Pictures/Sprites/Battle/BattleMenu/HpBarAlly.png"), HpBarEnemy(
"Pictures/Sprites/Battle/BattleMenu/HpBarEnemy.png"), MoveMenu2(
"Pictures/Sprites/Battle/BattleMenu/MoveMenu2.png"),
// Natalie
Natalie("Pictures/Menu/NPC/Natalie.png"), NatalieHead(
"Pictures/Sprites/Battle/BattleStart/NatalieHead.png"), NatalieBattleBack(
"Pictures/Sprites/Battle/BattleStart/NatalieBattleBack.png"),
// Flashing
PlayerFlashNormal(
"Pictures/Sprites/Battle/BattleStart/Player_flash_normal.gif"), OpponentFlashNormal(
"Pictures/Sprites/Battle/BattleStart/Opponent_flash_normal.gif"),
// Throwing Animation
MainThrow("Pictures/Sprites/Player/MainThrow.png"), MainThrow1(
"Pictures/Sprites/Player/MainThrow1.png"), MainThrow2(
"Pictures/Sprites/Player/MainThrow2.png"),
// Attack Effects
Tackle("Pictures/Sprites/Battle/Attacks/Tackle.png"), BlackBack(
"Pictures/Sprites/Battle/Attacks/BlackBack.png"), Growl(
"Pictures/Sprites/Battle/Attacks/Growl.png"), Leer(
"Pictures/Sprites/Battle/Attacks/Leer.png"),
// POKEMONS
Cosmet("Pictures/Sprites/Pokemon/Cosmet.png"), CosmetB(
"Pictures/Sprites/PokemonB/CosmetB.png"), CosmetStart(
"Pictures/Menu/Starters/CosmetStart.png"),
Tykepol("Pictures/Sprites/Pokemon/Tykepol.png"), TykepolB(
"Pictures/Sprites/PokemonB/TykepolB.png"), TykepolStart(
"Pictures/Menu/Starters/TykepolStart.png"),
Embite("Pictures/Sprites/Pokemon/Embite.png"), EmbiteB(
"Pictures/Sprites/PokemonB/EmbiteB.png"), EmbiteStart(
"Pictures/Menu/Starters/EmbiteStart.png"),
// Balls
Pokeball("Pictures/Sprites/Misc/Pokeball.png"), Pokeball4(
"Pictures/Sprites/Misc/Pokeball4.gif"),
// Outlines
Outline1("Pictures/Sprites/Battle/BattleMenu/Outline1.png"), Outline2(
"Pictures/Sprites/Battle/BattleMenu/Outline2.png"), Outline3(
"Pictures/Sprites/Battle/BattleMenu/Outline3.png"), Outline4(
"Pictures/Sprites/Battle/BattleMenu/Outline4.png");
private String filePath;
private Image image;
private boolean imageLoaded;
private Images(String imagePath) {
filePath = imagePath;
imageLoaded = false;
}
public Image getImage() {
if (!imageLoaded) {
loadImage();
}
return image;
}
public void loadImage() {
try {
image = ImageIO.read(new File(filePath));
} catch (IOException e) {
System.err.println("Failed to load image!");
e.printStackTrace();
}
imageLoaded = true;
}
How you do will depend on what it is you want to achieve.
If you want to prevent the need to replace the path in multiple places should it change, then using String constants is a reasonable idea.
Something like...
public interface PathConstants {
protected static final String SPRITES_PATH = "Pictures/Sprites";
protected static final String SPRITES_MISC_PATH = SPRITES_PATH + "/Misc";
protected static final String POKEMON_SPRITES_PATH = SPRITES_PATH + "/Pokemon";
protected static final String POKEMONB_SPRITES_PATH = SPRITES_PATH + "/PokemonB";
protected static final String STARTERS_MENU_PATH = "Pictures/Menu/Starters";
protected static final String SPRITES_BATTLE_PATH = SPRITES_PATH + "/Battle";
protected static final String MENU_BATTLE_PATH = SPRITES_BATTLE_PATH + "/BattleMenu";
}
Then you should be able to do something like...
public enum Images {
Cosmet(PathConstants.SPRITES_PATH + "/Cosmet.png"), CosmetB(
PathConstants.POKEMONB_SPRITES_PATH + "/CosmetB.png"), CosmetStart(
PathConstants.STARTERS_MENU_PATH + "/CosmetStart.png"),
Tykepol(PathConstants.POKEMON_SPRITES_PATH + "/Tykepol.png"), TykepolB(
PathConstants.POKEMONB_SPRITES_PATH + "/TykepolB.png"), TykepolStart(
PathConstants.STARTERS_MENU_PATH + "/TykepolStart.png"),
Embite(PathConstants.POKEMON_SPRITES_PATH + "/Embite.png"), EmbiteB(
PathConstants.POKEMONB_SPRITES_PATH + "/EmbiteB.png"), EmbiteStart(
PathConstants.STARTERS_MENU_PATH + "/EmbiteStart.png"),
// Balls
Pokeball(PathConstants.SPRITES_MISC_PATH + "/Pokeball.png"), Pokeball4(
PathConstants.SPRITES_MISC_PATH + "/Pokeball4.gif"),
// Outlines
Outline1(PathConstants.MENU_BATTLE_PATH + "/Outline1.png"), Outline2(
PathConstants.MENU_BATTLE_PATH + "/Outline2.png"), Outline3(
PathConstants.MENU_BATTLE_PATH + "/Outline3.png"), Outline4(
PathConstants.MENU_BATTLE_PATH + "/Outline4.png");
For example.
This means that if the SPRITES_PATH changes, you only need to change it one place and recompile.
I get the following problem when trying to display a list of items. For each item, I have to display an image which is dynamically loaded via a Wicket WebResource. The items are loaded step by step — 50 at a time — upon user scrolling, using an Ajax scroll.
[ERROR] 2011-04-19 09:58:18,000 btpool0-1 org.apache.wicket.RequestCycle.logRuntimeException (host=, request=, site=):
org.apache.wicket.WicketRuntimeException: component documentList:scroller:batchElem:666:content:item:3:batchItemContent:linkToPreview:imageThumbnail not found on page com.webapp.document.pages.DocumentListPage[id = 1]
listener interface = [RequestListenerInterface name=IResourceListener, method=public abstract void org.apache.wicket.IResourceListener.onResourceRequested()]
org.apache.wicket.protocol.http.request.InvalidUrlException: org.apache.wicket.WicketRuntimeException: component documentList:scroller:batchElem:666:content:item:3:batchItemContent:linkToPreview:imageThumbnail
not found on page com.webapp.document.pages.DocumentListPage[id = 1] listener interface = [RequestListenerInterface name=IResourceListener, method=public abstract void org.apache.wicket.IResourceListener.onResourceRequested()]
at org.apache.wicket.protocol.http.WebRequestCycleProcessor.resolve(WebRequestCycleProcessor.java:262)
at org.apache.wicket.RequestCycle.step(RequestCycle.java:1310)
at org.apache.wicket.RequestCycle.steps(RequestCycle.java:1428)
at org.apache.wicket.RequestCycle.request(RequestCycle.java:545)
at org.apache.wicket.protocol.http.WicketFilter.doGet(WicketFilter.java:479)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.CGLIB$doGet$6()
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816$$FastClassByGuice$$6d42bf5d.invoke()
at com.google.inject.internal.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:64)
at com.freiheit.monitoring.PerformanceMonitoringMethodInterceptor.invoke(PerformanceMonitoringMethodInterceptor.java:115)
at com.google.inject.internal.InterceptorStackCallback$InterceptedMethodInvocation.proceed(InterceptorStackCallback.java:64)
at com.google.inject.internal.InterceptorStackCallback.intercept(InterceptorStackCallback.java:44)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.doGet()
at org.apache.wicket.protocol.http.WicketFilter.doFilter(WicketFilter.java:312)
at org.apache.wicket.protocol.http.WicketFilter$$EnhancerByGuice$$51619816.CGLIB$doFilter$4()
How can this problem be solved?
Here is the part of the code responsible for adding the image:
previewLink.add(createThumbnailSmall("imageThumbnail", documentModel));
in
createThumbnailSmall(final String id, final IModel<BaseDocument> documentModel) {
// thumbnailResource is an object that contains the path of the image
if (thumbnailResource != null) {
final WebResource resource = getWebResource(thumbnailResource);
final Image image = new Image(id, resource);
return image;
}
return new InvisibleContainer(id);
}
WebResource getWebResource(final DocumentResource documentResource) {
return new WebResource() {
private static final long serialVersionUID = 1L;
#Override
public IResourceStream getResourceStream() {
return new BaseStreamResource(documentResource);
}
};
}
where BaseStreamResource is the following:
public class BaseStreamResource extends AbstractResourceStream {
private InputStream _fileInputStream = null;
private DocumentResource _resource = null;
public BaseStreamResource(final DocumentResource documentResource) {
_resource = documentResource;
}
#Override
public InputStream getInputStream() throws ResourceStreamNotFoundException {
if (_fileInputStream == null) {
try {
if (_resource == null) {
throw new ResourceStreamNotFoundException("Resource was null");
}
_fileInputStream = _resource.getFileInputStream();
} catch (final ResourceNotAvailableException ex) {
throw new ResourceStreamNotFoundException(ex);
}
}
return _fileInputStream;
}
In HTML:
<a wicket:id="linkToPreview" href="#">
<img wicket:id="imageThumbnail" alt="Attachment"></img></a>
The code added hasn't really added any clues for me, but maybe I can help narrow it down a bit anyway.
The stacktrace includes a reference to com.webapp.document.pages.DocumentListPage, which is likely calling some of the code you've posted. The error indicates a bad url, so debugging into that class, adding debug prints, and looking at the values of any field containing a url might be worthwhile.
It might even help to modify the code in DocumentListPage (maybe temporarily for debugging) to catch org.apache.wicket.protocol.http.request.InvalidUrlException and adding debugging prints specifically when the exception is caught.
This isn't really an answer, but it's too big for a comment, and maybe it'll help you get closer to an answer.
The following solution solved the problem:
- extend WebResource class
- add extended class as a resource to application shared resources
Ex:
public class MyWebResource extends WebResource {
final ValueMap map = new ValueMap();
#Override
public IResourceStream getResourceStream() {
String fileName = getFileName();
File file = new File(basePath, fileName);
if (!file.exists()) {
LOG.error("File does not exist: " + file);
throw new IllegalStateException("File does not exist: " + file);
}
return new FileResourceStream(file);
}
public final void addResource() {
Application.get().getSharedResources().add(getClass().getName(), this);
}
protected String getFileName() {
return getParameters().getString("id");
}
public final String urlFor(final String fileName) {
final ResourceReference resourceReference = new ResourceReference(getClass().getName());
final String encodedValue = WicketURLEncoder.QUERY_INSTANCE.encode(fileName);
map.add("id", encodedValue);
final CharSequence result = RequestCycle.get().urlFor(resourceReference, map);
if (result == null) {
throw new IllegalStateException("The resource was not added! "
+ "In your Application class add the following line:"
+ "MyConcreteResource.INSTANCE.addResource()");
}
String absoluteUrl = RequestUtils.toAbsolutePath(result.toString());
return absoluteUrl;
}
}
In Application class, in init(), I have added MyWebResource to shared resources:
public void init() {
...
new MyWebResource().addResource();
...
}