iText find position of text in pdf - java

I am creating a utility that will add a multi-line text field just below the last line of text in an existing PDF document. This will be used for people who want to add comments to a report that is generated from another system.
I followed the examples from the iText book, and also looked at this SO question: Get the exact Stringposition in PDF
So now I've got a method that parses the last page of the document and a custom listener that finds the coordinates of the text I'm looking for.
Here is my code parsing method:
private void parsePdfPage2(String src, int pageNum) throws IOException {
PdfReader reader = new PdfReader(src);
RenderListener listener = new MyTextRenderListener();
PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener);
PdfDictionary pageDic = reader.getPageN(pageNum);
PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
processor.processContent(ContentByteUtils.getContentBytesForPage(reader, pageNum), resourcesDic);
}
And here is the listener:
public class MyTextRenderListener implements RenderListener {
#Override
public void beginTextBlock() {}
#Override
public void endTextBlock() {}
#Override
public void renderImage(ImageRenderInfo renderInfo) {}
#Override
public void renderText(TextRenderInfo renderInfo) {
// Check if this is the text marker
String text = renderInfo.getText();
if (text.equalsIgnoreCase("Comments")) {
// Found it
LineSegment ls = renderInfo.getBaseline();
System.out.println("Found at X: " + ls.getBoundingRectange().getX() +
", Y: " + ls.getBoundingRectange().getY());
}
}
}
However, now I need to send the found LineSegment object (or the individual coordinates) back to the original parsing method. Of course I could write the values to disk and read it in the parsing method, but that seems horrible. I'm pretty sure there is a more elegant way to achieve this and would appreciate pointers.

This is an old question but still:
You could save the results of the listener into a private list (in the class).
After that what's left to add is a public method (getResults()) which returns the list.
Simply call getResults after the processContent call since the synchronous nature of the processContent method guarentees the list to be filled correctly.
The only problem with this solution is that listeners can't be reused.

Related

Wicket - Set Model from another panel

I am quite new to Wicket. I am adding a model to a sub-panel(ChartPanel) from a main panel (MainPanel) on a button click.
MainPanel.java
On button click, I am re-adding the chartPanel after I change its model. Following is the code I am using in the buttonClick of the MainPanel. Here the onRenderAnnotations event is generated on some click in the UI.
#OnEvent
public void onRenderAnnotations(RenderAnnotationsEvent aEvent)
{
LOG.trace("clicked on the annotation");
renderChart( aEvent.getRequestHandler());
}
private void renderChart(IPartialPageRequestHandler aRequestHandler)
{
MultiValuedMap<String, Double> recommenderScoreMap = getLatestScores(aRequestHandler);
Map<String,String> curveData = new HashMap<String,String>();
LearningCurve learningCurve = new LearningCurve();
for (String recommenderName : recommenderScoreMap.keySet()) {
String data = recommenderScoreMap.get(recommenderName).stream().map(Object::toString)
.collect(Collectors.joining(", "));
curveData.put(recommenderName,data);
learningCurve.setCurveData(curveData);
learningCurve.setMaximumPointsToPlot(MAX_POINTS_TO_PLOT);
}
chartPanel.setDefaultModel(Model.of(learningCurve));
// to avoid the error, A partial update of the page is being rendered
try {
aRequestHandler.add(chartPanel);
}
catch (IllegalStateException e) {
LOG.warn("Not updating the chart. " + e.toString());
setResponsePage(getPage());
}
}
ChartPanel.java
After this in the chartPanel, I want to use the updated model to add component inside the chartpanel. What would be the best way to do that?
I want to do something like this in the class ChartPanel:
#Override
protected void onRender()
{
super.onModelChanged();
LearningCurve newLearningCurve = getModel().getObject();
requestTarget = ???
String js = createJavascript(newLearningCurve);
requestTarget.prependJavascript(js);
}
My question is, in the above code how to get the request target since it is not an ajax request neither do I get it in the arguments. Should I use some other function where I also get a requestTarget. But I want it to be called every time the model of ChartPanel is updated from anywhere.
Pardon my ignorance. I have been trying for a few days but I am still stuck. I tried to explain it enough but if any information is missing, please comment and I will add it right away.
Thanks.
You should override renderHead() instead:
#Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
response.render(OnLoadHeaderItem.forScript(
createJavascript(newLearningCurve)));
}
This way your chart will be shown correctly regardless whether it was added due to an AjaxRequest or simply when the page is rerendered.

Is it normal that sampling tweets using TwitterStream as in Twitter4J code example, I get just mainly question marks as user name and status?

I used the code as in the section "code example" in Twitter4j:
public static void main(String[] args) throws TwitterException, IOException{
StatusListener listener = new StatusListener(){
public void onStatus(Status status) {
System.out.println(status.getUser().getName() + " : " + status.getText());
}
public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) {}
public void onTrackLimitationNotice(int numberOfLimitedStatuses) {}
public void onException(Exception ex) {
ex.printStackTrace();
}
};
TwitterStream twitterStream = new TwitterStreamFactory().getInstance();
twitterStream.addListener(listener);
// sample() method internally creates a thread which manipulates TwitterStream and calls these adequate listener methods continuously.
twitterStream.sample();
}
As you can see, there's a println in the code above, inside the method "onStatus". The following photo shows what I get mainly from that code. Is it normal?
question marks...question marks everywhere
Indeed, i I filter just statuses whose user hasn't got a question mark in his user name, I got almost nothing. Moreover, I should also filter users whose location is public. With regards to that I also ask what is the difference between:
user.isGeoEnabled()
and
user.getLocation() != ""
The responses you will get back are UTF-8 encoded https://dev.twitter.com/tags/utf-8
If you look at some of the accounts in the output they include non-western european characters https://twitter.com/tomokichi_koyo. These are breaking the output.
Try writing to a file instead and opening with a UTF-8 aware editor. There are various answers about setting up java and your OS to default to UTF-8 but you will need to look for you specific combination https://stackoverflow.com/search?q=windows+console+java+utf-8

implement Undo Redo in form based editor

I am developing a multipage Form Editor to edit/create a customized XML file in Eclipse.
structure is looks like:
Implementation class is MyXMLFormEditor which extends FormEditor.
Each page of FormEditor extends FormPage (i.e. MyXMLFormPage extends FormPage).
Between FormEditor and actual XML file I am maintaining JDOM model.
Also I implemented dirty flag handling. So user’s inputs into form editor gets saved into JDOM till the time user presses Save button. When user presses save button JDOM is written/serialized into XML file.
In an editor with above functionality I would like to implement undo/redo functionality as follow:
When editor is dirty (user changed something into form editor and it is not saved) undo operation should revert back the changes in form editor as well as JDOM to its original state (i.e. the state when editor was non-dirty) and redo operation should again bring back the changes into FormEditor as well as JDOM and editor should become dirty.
Following is my code snippet
MyXMLFormEditor.java
public class MyXMLFormEditor extends FormEditor {
MyXMLFormEditor(){
super();
}
#Override
protected FormToolkit createToolkit(Display display) {
// Create a toolkit that shares colors between editors.
return new FormToolkit(Activator.getDefault().getFormColors(display));
}
#Override
public void init(IEditorSite site, IEditorInput editorInput) {
setSite(site);
mSite = site;
setInput(editorInput);
try {
super.init(site, editorInput);
} catch (PartInitException e1) {
e1.printStackTrace();
}
if (!(editorInput instanceof IFileEditorInput))
try {
throw new PartInitException("Invalid Input: Must be IFileEditorInput");
} catch (PartInitException e) {
e.printStackTrace();
}
setPartName(fileName);
}
public void setUpProgFile(IEditorSite site, IEditorInput editorInput){
IFileEditorInput fileInput = ((IFileEditorInput) editorInput);
//create document builder and prepare JDom model for xml file.
}
#Override
protected void addPages() {
try {
//add 'Main' page
objMyXMLFormPage = new MyXMLFormPage (this, "FirstPage","Main");
//set rootNode of MyXMLFormPage
objMyXMLFormPage.rootNode = getRootNode();
objMyXMLFormPage.filePath = filePath;
objMyXMLFormPage.document = document;
addPage(objMyXMLFormPage);
} catch (PartInitException e) {
e.printStackTrace();
}
}
#Override
public void doSave(IProgressMonitor monitor) {
System.out.println("MyXMLFormEditor: doSave");
//logic to write jdom contents into xml file.
objMyXMLFormPage.setDirty(false);
}
#Override
public void doSaveAs() {
System.out.println("MyXMLFormEditor: doSaveAs");
}
#Override
public boolean isSaveAsAllowed() {
System.out.println("MyXMLFormEditor: isSaveAsAllowed");
return true;
}
}
MyXMLFormPage .java
public class MyXMLFormPage extends FormPage{
//private members declaration.
public MyXMLFormPage (MyXMLFormEditor editor,String title, String id) {
// initialize the editor and set its title and name.
super(editor,title,id );
}
#Override
public void createFormContent(IManagedForm managedForm) {
// Set page title
super.createFormContent(managedForm);
FormToolkit mMyXMLFormPage Toolkit = managedForm.getToolkit();
//Logic to creat UI and populating its contents from JDom
}
private void makeEditorDirty() {
updateJdom = true;
setDirty(true);
}
private void updateJDom() {
if(updateJdom){
System.out.println("*** Jdom updated ***");
updateJdom = false;
}
}
#Override
public boolean isDirty() {
return isDirtyFlag;
}
protected void setDirty(boolean value) {
isDirtyFlag = value;
dirtyStateChanged();
}
public void dirtyStateChanged() {
getEditor().editorDirtyStateChanged();
}
#Override
public boolean isSaveAsAllowed() {
System.out.println("MyXMLFormPage .isSaveAsAllowed");
return false;
}
#Override
public void doSave(IProgressMonitor monitor) {
System.out.println("MyXMLFormPage .doSave");
}
}
Can anyone provide me pointer/samples on how to implement undo/redo functionality into FormEditor? It would be good if the approach make use of existing undo/redo framework of Eclipse PDE or workbench.
There are a couple of important points to make about the pattern used by multi-page editor implementations in Eclipse. There may be other ways of doing it, but the editors in Eclipse seem to adhere to these points:
Maintain a model of the data that's shared by the pages in the editor (you're doing this).
Only refresh a page with the model data when the page is about to be displayed. Don't try to keep non-displayed pages in sync with the model.
When you perform an undo or redo, make the appropriate changes to the model (per #Scorpion's comment) and then refresh the current page.
Each page should have a its-ok-to-leave-this-page method (I don't remember the name) which is called to make sure that the displayed data is error free enough to allow the page to be changed (you don't want errors on non-displayed data).
Pages have an about-to-leave-this-page method which is called to save any changes to the model before switching pages. Most pages don't do anything here because the model is modified sometimes by every keystroke, but the source page would use this method to replace the model completely with the parse results on the text editor contents.
What this means is that your forms don't have to perform the undo/redo themselves. Rather, the classes representing the multipage editor pages will interact with the model as it's changed and then pass the correct data to be displayed to the forms.
The forms will need to listen for the undo/redo key events and pass those along to the model via the command pattern.

java gwt richtextarea change font-family

The Object richtextarea of java gwt has as default the font-family 'times new roman' is it somehow possible to change the family to 'Arial'?
You have several options. You can create a CSS class and set it on the body element of the document inside RichTextArea, or you can set the style attribute on it directly. If you want this change to be consistent throughout your app, I recommend creating a new class and adding an InitializeHandler to it.
public class MyRichTextArea extends RichTextArea {
public MyRichTextArea() {
addInitializeHandler(new InitializeHandler() {
#Override
public void onInitialize(InitializeEvent ie) {
Document document = IFrameElement.as(getElement()).getContentDocument();
BodyElement body = document.getBody();
body.setAttribute("style", "font-family: Arial Unicode MS,Arial,sans-serif;");
});
}
}
}
It is better to use the provided functionality.
If you want to change it at creation time, you will need the initializeHandler as mentioned in answer 1:
RichTextArea rta = new RichTextArea();
rta.addInitializeHandler(new InitializeHandler() {
public void onInitialize(InitializeEvent event) {
rta.getFormatter().setFontName("\"Trebuchet MS\",Trebuchet,Arial");
rta.getFormatter().setFontSize(FontSize.SMALL);
rta.getFormatter().setForeColor("#FF0000");
}
});
PS: you need to do this before you add any content to the textarea, or it will only be applied to new input.
I've found more common solution:
addInitializeHandler(new InitializeHandler() {
public void onInitialize(InitializeEvent ie) {
Document document = IFrameElement.as(getElement()).getContentDocument();
BodyElement body = document.getBody();
HeadElement head = HeadElement.as(Element.as(body.getPreviousSibling()));
LinkElement styleLink = document.createLinkElement();
styleLink.setType("text/css");
styleLink.setRel("stylesheet");
styleLink.setHref("richtextarea.css");
head.appendChild(styleLink);
}
});
After adding initialization handler, it's easy to change styles in richtextarea.css file

How to set displayed text for a HTML anchor tag using Wicket?

I would like to dynamically change the text displayed for a HTML anchor tag. So, for example if I have the following in my markup -
<a class="point" style="font-family:courier" wicket:id="link">[+]</a>
I want to change the '[+]' to something else. Currently the code fragment looks like this:
equipmentFamilyName.add(new Link<String>("link") {
#Override
protected void onComponentTag(ComponentTag tag) {
String id = "link" + equipmentFamilyName.getModelObject();
tag.put("onclick", "toggle('" + collapsibleId + "','" + id + "')");
tag.put("id", id);
}
#Override
public void onClick() {
}
});
Which just adds various attributes. I tried using a model associated with the Link object like this
IModel<String> linkModel = new Model<String>("-");
equipmentFamilyName.add(new Link<String>("link", linkModel) {
...
But that had no effect on the displayed text i.e. I still get '[+]' shown on my web page.
Any suggestions or code examples clarifying how to do this would be much appreciated.
Edit: Following the pointers in the comments, I added a method to override onComponentTagBody(). I now have a solution to this for our current version of Wicket (1.4.17).
#Override
protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
replaceComponentTagBody(markupStream, openTag, "[-]");
}
If you use Wicket 1.5 then this is quite easy: link.setBody(IModel).
The model's object will be used as link's body.

Categories

Resources