Itext : set zoom level of external hyper link in pdf - java

I am using following code to set external hyperlink using itext library in Java.
Chunk chunk = new Chunk("Click to Open File");
PdfAction action = new PdfAction("externalfile.pdf");
action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
action.put(PdfName.ZOOM, PdfName.FIT);
chunk.setAction(action);
I want to set zoom level of external hyper link: when I click on hyper link file should be open and FIT page.
I tried using action.put(PdfName.ZOOM, PdfName.FIT); but it's not working.

Please don't ever create PDF object manually without consulting ISO-32000-1.
You want to create a GoToR action. Such an action is expressed as a PDF dictionary that can contain the following keys:
There is no key named Zoom in that table, hence your code is wrong.
You need the D key and as you want to link to a page and define a zoom factor, you need to define a destination:
In other words, the destination needs to be a PdfArray! PdfName.FIT isn't sufficient!
(All screen shots are taken from the copy of ISO-32000-1 that is provided by Adobe on its web site.)
Update:
If you want to add a link to a remote page, you can also follow the example on page 197-198 of iText in Action - Second Edition: see the LinkActions example that uses the gotoRemotePage() method.
Internally, this method looks like this:
public static PdfAction gotoRemotePage(String filename, String dest, boolean isName, boolean newWindow) {
PdfAction action = new PdfAction();
action.put(PdfName.F, new PdfString(filename));
action.put(PdfName.S, PdfName.GOTOR);
if (isName)
action.put(PdfName.D, new PdfName(dest));
else
action.put(PdfName.D, new PdfString(dest, PdfObject.TEXT_UNICODE));
if (newWindow)
action.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
return action;
}
Note that this assumes that you have a named destination in the target file.
I think that you'd rather want to use the constructor that takes a page number:
public PdfAction(String filename, int page) {
put(PdfName.S, PdfName.GOTOR);
put(PdfName.F, new PdfString(filename));
put(PdfName.D, new PdfLiteral("[" + (page - 1) + " /FitH 10000]"));
}
Of course, this doesn't use PdfName.FIT. If you really want to define the destination yourself, you need a line that looks like this:
put(PdfName.D, new PdfLiteral("[" + (page - 1) + " /Fit]"));

Related

JasperReports export to Excel uses only last set background color

Im pretty pretty new to Dynamic-Jasper, but due to work i had to add a new feature to our already implemented solution.
My Problem
The Goal is to add a Column to a report that consists only out of a background-color based on some Information. I managed to do that, but while testing I stumbled upon a Problem. While all my Columns in the html and pdf view had the right color, the Excel one only colored the fields in the last Color.
While debugging i noticed, that the same colored Fields had the same templateId, but while all Views run through mostly the same Code the Excel one showed different behavior and had the same ID in all fields.
My Code where I manipulate the template
for(JRPrintElement elemt : jasperPrint.getPages().get(0).getElements()) {
if(elemt instanceof JRTemplatePrintText) {
JRTemplatePrintText text = (JRTemplatePrintText) elemt;
(...)
if (text.getFullText().startsWith("COLOR_IDENTIFIER")) {
String marker = text.getFullText().substring(text.getFullText().indexOf('#') + 1);
text.setText("ID = " + ((JRTemplatePrintText) elemt).getTemplate().getId());
int rgb = TypeConverter.string2int(Integer.parseInt(marker, 16) + "", 0);
((JRTemplatePrintText) elemt).getTemplate().setBackcolor(new Color(rgb));
}
}
}
The html view
The Excel view
Temporary Conclusion
The same styles uses the same Objects in the background and the JR-Excel export messes something up by assigning the same Object to all the Fields that I manipulated there. If anyone knows of a misstake by me or potential Solutions to change something different to result the same thing please let me know.
Something different I tried earlier, was trying to set the field in an evaluate Method that was called by Jasper. In that method we assign the textvalue of each field. It contained a map with JRFillFields, but unfortunatelly the Map-Implementation denied access to them and just retuned the Value of those. The map was provided by dj and couldn't be switched with a different one.
Edit
We are using JasperReports 6.7.1
I found a Solution, where I replaced each template with a new one that was supposed to look exactly alike. That way every Field has its own ID guaranteed and its not up to chance, how JasperReports handles its Data internaly.
JRTemplateElement custom =
new JRTemplateText(((JRTemplatePrintText) elemt).getTemplate().getOrigin(),
((JRTemplatePrintText) elemt).getTemplate().getDefaultStyleProvider());
custom.setBackcolor(new Color(rgb));
custom.setStyle(((JRTemplatePrintText) elemt).getTemplate().getStyle());
((JRTemplatePrintText) elemt).setTemplate(custom);

Programmatically render template area in Magnolia CMS

I am using Magnolia CMS 5.4 and I want to build a module that will render some content of a page and expose it over REST API. The task is simple but not sure how to approach it and/or where to start.
I want my module to generate partial template or an area of a template for a given reference, let's say that is "header". I need to render the header template/area get the HTML and return that as a response to another system.
So questions are: is this possible at all and where to start?
OK after asking here and on Magnolia forum couldn't get answer I dug in the source code and found a way to do it.
First thing the rendering works based on different renderers and those could be JCR, plain text or Freemarker renderer. In Magnolia those are decided and used in RenderingEngine and the implementation: DefaultRenderingEngine. The rendering engine will allow you to render a whole page node which is one step closer to what I am trying to achieve. So let's see how could this be done:
I'll skip some steps but I've added command and made that work over REST so I could see what's happening when I send a request to the endpoint. The command extends BaseRepositoryCommand to allow access to the JCR repositories.
#Inject
public setDefaultRenderingEngine(
final RendererRegistry rendererRegistry,
final TemplateDefinitionAssignment templateDefinitionAssignment,
final RenderableVariationResolver variationResolver,
final Provider<RenderingContext> renderingContextProvider
) {
renderingEngine = new DefaultRenderingEngine(rendererRegistry, templateDefinitionAssignment,
variationResolver, renderingContextProvider);
}
This creates your rendering engine and from here you can start rendering nodes with few small gotchas. I've tried injecting the rendering engine directly but that didn't work as all of the internals were empty/null so decided to grab all construct properties and initialise my own version.
Next step is we want to render a page node. First of all the rendering engine works based on the idea it's rendering for a HttpServletResponse and ties to the request/response flow really well, though we need to put the generated markup in a variable so I've added a new implementation of the FilteringResponseOutputProvider:
public class AppendableFilteringResponseOutputProvider extends FilteringResponseOutputProvider {
private final FilteringAppendableWrapper appendable;
private OutputStream outputStream = new ByteArrayOutputStream();
public AppendableFilteringResponseOutputProvider(HttpServletResponse aResponse) {
super(aResponse);
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
appendable = Components.newInstance(FilteringAppendableWrapper.class);
appendable.setWrappedAppendable(writer);
}
#Override
public Appendable getAppendable() throws IOException {
return appendable;
}
#Override
public OutputStream getOutputStream() throws IOException {
((Writer) appendable.getWrappedAppendable()).flush();
return outputStream;
}
#Override
public void setWriteEnabled(boolean writeEnabled) {
super.setWriteEnabled(writeEnabled);
appendable.setWriteEnabled(writeEnabled);
}
}
So idea of the class is to expose the output stream and still preserve the FilteringAppendableWrapper that will allow us the filter the content we want to write. This is not needed in the general case, you can stick to using AppendableOnlyOutputProvider with StringBuilder appendable and easily retrieve the entire page markup.
// here I needed to create a fake HttpServletResponse
OutputProvider outputProvider = new AppendableFilteringResponseOutputProvider(new FakeResponse());
Once you have the output provider you need a page node and since you are faking it you need to set the Magnolia global env to be able to retrieve the JCR node:
// populate repository and root node as those are not set for commands
super.setRepository(RepositoryConstants.WEBSITE);
super.setPath(nodePath); // this can be any existing path like: "/home/page"
Node pageNode = getJCRNode(context);
Now we have the content provider and the node we want to render next thing is actually running the rendering engine:
renderingEngine.render(pageNode, outputProvider);
outputProvider.getOutputStream().toString();
And that's it, you should have your content rendered and you can use it as you wish.
Now we come to my special case where I want to render just an area of the whole page in this case this is the Header of the page. This is all handled by same renderingEngine though you need to add a rendering listener that overrides the writing process. First inject it in the command:
#Inject
public void setAreaFilteringListener(final AreaFilteringListener aAreaFilteringListener) {
areaFilteringListener = aAreaFilteringListener;
}
This is where the magic happens, the AreaFilteringListener will check if you are currently rendering the requested area and if you do it enables the output provider for writing otherwise keeps it locked and skips all unrelated areas. You need to add the listener to the rendering engine like so:
// add the area filtering listener that generates specific area HTML only
LinkedList<AbstractRenderingListener> listeners = new LinkedList<>();
listeners.add(areaFilteringListener);
renderingEngine.setListeners(listeners);
// we need to provide the exact same Response instance that the WebContext is using
// otherwise the voters against the AreaFilteringListener will skip the execution
renderingEngine.initListeners(outputProvider, MgnlContext.getWebContext().getResponse());
I hear you ask: "But where do we specify the area to be rendered?", aha here is comes:
// enable the area filtering listener through a global flag
MgnlContext.setAttribute(AreaFilteringListener.MGNL_AREA_PARAMETER, areaName);
MgnlContext.getAggregationState().setMainContentNode(pageNode);
The area filtering listener is checking for a specific Magnolia context property to be set: "mgnlArea" if that's found it will read its value and use it as an area name, check if that area exists in the node and then enable writing once we hit the area. This could be also used through URLs like: https://demopublic.magnolia-cms.com/~mgnlArea=footer~.html and this will give you just the footer area generated as an HTML page.
here is the full solution: http://yysource.com/2016/03/programatically-render-template-area-in-magnolia-cms/
Just use the path of the area and make a http request using that url, e.g. http://localhost:9080/magnoliaAuthor/travel/main/0.html
As far as I can see there is no need to go through everything programmatically as you did.
Direct component rendering

Is it possible to add 'Open a web link action' action to acroform field programmatically using iText?

In Adobe Acrobat there is a possibility to add 'Open a web link action' to acroform. Is it possible to do so with iText usind already existing acroform?
I was unable to find any mention about it at iText docs and therefore tried to create new acrofield programmatically and add this action to it, but without success. Here's my code:
PdfReader pdfReader = new PdfReader(templateStream);
PdfStamper stamper = new PdfStamper(pdfReader, new FileOutputStream("delivery.pdf"));
stamper.setFormFlattening(true);
stamper.getAcroFields().setField("package", packages);
stamper.getAcroFields().setField("purchase_id", purchaseId);
stamper.getAcroFields().setField("activation_code", activationCode);
if (partner != "") {
PdfFormField field = PdfFormField.createTextField(stamper.getWriter(), false,
false, 100);
field.setFieldName("partner");
PdfAction action = new PdfAction(partner);
field.setAction(action);
field.setColor(new BaseColor(0,0,255));
PdfAppearance appearance = stamper.getUnderContent(1).
createAppearance(200, 20);
appearance.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED), 12f);
appearance.setColorFill(BaseColor.BLUE);
field.setAppearance(PdfAnnotation.APPEARANCE_DOWN, appearance);
field.setDefaultAppearanceString(appearance);
stamper.getAcroFields().setField("partner", "Click here to show partner's web site");
}
The resulting PDF document is shown without partner field. Please point me to some docs or to mistake at my code.
You are trying to add interactivity to a form. However, you are also throwing away all interactivity by using this line:
stamper.setFormFlattening(true);
You also claim that you are adding an extra field. As far as I can see, that claim is false. You create a field name field (and you create it the hard way; I would expect you to use the TextField class instead). However, I don't see you adding that field anywhere. I miss the following line:
stamper.addAnnotation(field, 1);
Note that this line doesn't make sense:
stamper.getAcroFields().setField("partner", "Click here to show partner's web site");
Why would you create a field first (and possibly add a caption) and then change it immediately afterwards? Why not create the field correctly from the start?
Finally, it seems that you want to create a button that can be clicked by people. Then why are you creating a text field? Wouldn't it make more sense to create a push button?
This is an example of a question to which a machine would respond: Too many errors... Maybe you should consider reading the documentation before trying to fix your code.

How to load a Google maps static map using Picasso?

In my Android app I use Picasso to load images. This normally works perfectly well.
Today I tried loading a static image from the google maps api, but this doesn't seem to work. When I open the example link as provided on their info page, I get to see the static map image perfectly well. When I load it in my Android app using the line below, I get nothing at all.
Picasso.with(getContext()).load("http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=370x250&maptype=roadmap%20&markers=color:blue|label:S|40.702147,-74.015794&markers=color:green|label:G|40.711614,-74.012318%20&markers=color:red|color:red|label:C|40.718217,-73.998284&sensor=false").into(mapView);
I also tried to download the image and uploading it to my personal webspace, from which it loads perfectly well, but somehow, it doesn't seem to load directly from the direct google API url.
Does anybody know why this is so, and how I can solve it?
The only programmatic point-of-failure that comes to mind is in parsing the URI. Looking at the current Picasso code (https://github.com/square/picasso/blob/master/picasso/src/main/java/com/squareup/picasso/Picasso.java) I see the following:
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
So I'd first debug
Uri.parse("http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=370x250&maptype=roadmap%20&markers=color:blue|label:S|40.702147,-74.015794&markers=color:green|label:G|40.711614,-74.012318%20&markers=color:red|color:red|label:C|40.718217,-73.998284&sensor=false")
and see what that Object looks like. Does it drop or confuse any of your parameters?
If that doesn't lead you anwhere, try downloading the file manually using a HttpClient [or similar]. Then at least you can fully debug the request/response.
Also, I know Google maps has some limits -- are you sure you haven't reached them?
replace http with https
replace | with %7C
add api key
The .loadMap() function has many declared variables. This is the heart of the whole process.
So what is required for the static maps API to give us an image is that we make an http request with a given url, for which an image response (URL) is received. Let us run through the meaning and utility of these variables. Yes, all of them have a completely different meaning!
The mapUrlInitial variable is always the same while making an API call. It has a query of center ( ?center ) which specifies that we want the location to be centered in the map.
The mapUrlProperties variable contains a string where you control the actual zooming of the image response you will get, the size ofthe image and the color of the marker which will point out our place.
The mapUrlMapType variable is a string where you can actually determine the marker size you want and the type of the map. We are using a roadtype map in the app.
Finally latLong is a string which concatenates the latitude and the longitude of the place we want to pinpoint!
We then concatenate all of these strings to form a feasible Url. The Url is then loaded as we have seen above, in the Picasso code. One thing we can notice is that an event object is always required for all of this to happen, because we are able to fetch the position details using the event object! Final Code:-
fun loadMap(event: Event): String{
//location handling
val mapUrlInitial = “https://maps.googleapis.com/maps/api/staticmap?center=”
val mapUrlProperties = “&zoom=12&size=1200×390&markers=color:red%7C”
val mapUrlMapType = “&markers=size:mid&maptype=roadmap”
val latLong: String = “” +event.latitude + “,” + event.longitude
return mapUrlInitial + latLong + mapUrlProperties + latLong + mapUrlMapType
}
//load image
Picasso.get()
.load(loadMap(event))
.placeholder(R.drawable.ic_map_black_24dp)
.into(rootView.image_map)

Need help to open a subchart from a main chart?

I have the following chart:
Now my problem is I want to open a new chart containing the information for Linux OS when a user clicks on Linux portion of chart, shown in red. I have tried this:
//check if Linux OS is clicked on chart...
if("Linux".equals(chartMouseEvent.getEntity().getToolTipText()))
{
//open new chart having the information for Linux
}
But I think there may be some better alternate to do the same job.
So please help if you know how to achieve this.
ChartEntity is a good choice, but getToolTipText() is not the only resource available. The getURLText() method is also handy, using the information provided by the StandardPieURLGenerator. Alternatively, a custom implementation of PieURLGenerator can access the data set directly:
plot.setURLGenerator(new PieURLGenerator() {
public String generateURL(PieDataset dataset, Comparable key, int pieIndex) {
// access the dataset as required
}
});

Categories