iText : linked textField can only be change on first page - java

I'm new to itext, and I need to create a pdf, with the same field on every page so that when I change the value of one of them they all change and have the same value.
So far I'm creating a TextField on each page, use it to get a PdfFormField and then I add it to a global parent PdfFormField. I add annotation to the parent and to the children and it's almost working the way I want except for one thing :
When I want to select the field with the mouse, I can only do it on the first page, on the others, the cursor rotate 90° and I can't select the text in the field. I can get to the needed field with tab, and changing it change all the value in the pdf as I want it to, but I can't access it with the cursor.
I checked the itext doc and search for examples/tutos, didn't find anything that helped so far
Does anyone have an idea why it does that ? Is there problem with the way I'm doing it ? At the beginning of the document I rotate it (need to be landscape) can it cause this problem ?
Thx in advance,
Edit :
private void addChangeableField(String fieldName, TextField textField, PdfFormField parent) throws IOException,
DocumentException {
PdfFormField datafield = textField.getTextField();
datafield.setFieldName(fieldName);
datafield.setFieldFlags(PdfFormField.FF_EDIT | PdfFormField.FF_DONOTSCROLL);
datafield.setMKTextPosition(PdfFormField.Q_CENTER);
parent.addKid(datafield);
writer.addAnnotation(parent);
writer.addAnnotation(datafield);
}
Here is how I add a field that can be change and modify other fields
And here is the example http://docdro.id/To19NKQ

Related

How to use (browser)autocomplete with Vaadin 10 TextField

I try to build an reservation Form with vaadin 10 while building it i encounterd the problem, that the autocompletion we know from every form on te web doesn't work. I put in an name field the name press submit an the next time i want to re-enter the name i need to write it out again.
My code looks like that (shortend):
TextField name = new TextField();
Button save = new Button("submit");
save.addClickListener(event -> save());
name.setAutocomplete(Autocomplete.ON);
add(name);
add(save);
i had the hopes that Autocomplete.On does the magic for me but it seems not to work. Maybe the way the save methode works screw things up?
the methode is rather big i just simplify it
private void save() {
--save everything to db
--remove all fields
--replace the fields with text saying reservation done
}
found out that someone created issue https://github.com/vaadin/vaadin-text-field/issues/156
seems to be an Shadow DOM limitation
Related issues:
https://bugs.chromium.org/p/chromium/issues/detail?id=746593
https://bugs.webkit.org/show_bug.cgi?id=172567
Edit:
for auto completion for my loginForm i got it working with adding
class xyz extends Div implements PageConfigurator{
...
#Override
public void configurePage(InitialPageSettings settings) {
settings.addInlineWithContents(
InitialPageSettings.Position.PREPEND,
"window.customElements=window.customElements||{};
window.customElements.forcePolyfill=true;
window.ShadyDOM={force:true};",
InitialPageSettings.WrapMode.JAVASCRIPT);
}
I came across this issue recently with Vaadin 14 and a custom login form.
Chrome only offers auto-filling fields (and also save login details) if it can see the input tags with name attributes in the Light DOM, but Vaadin creates TextFields with all of its elements inside of a Shadow DOM hidden.
Solution is to create a reference with <input slot="input"> inside the parent <vaadin-text-field> as part of Light DOM. All the styles and everything will still be in the Shadow DOM but Chrome now can see the input fields for auto-completion.
Kotlin code:
val username = TextField().apply {
element.setAttribute("name", "username")
element.appendChild(Element("input").setAttribute("slot", "input"))
}
val password = PasswordField().apply {
element.setAttribute("name", "password")
element.appendChild(Element("input").setAttribute("slot", "input"))
}
add(username, password)

Remove page reference from annotation

I added a textfield with several kids similar as described here. Did that to use the autofill functionality of PDF...
Now my question is how am I able to remove the page reference from the parent element? The data field should not contain a parent reference since it is not related to any page. The widgets should contain those (which I added there however I can't remove the parent /P page reference)
I tried
PdfFormField parent = PdfFormField.createTextField(stamper.getWriter(), false, false, 0);
parent.setFieldName(fieldName);
for (int page = 1; page <= pages; page++) {
TextField textField = new TextField(stamper.getWriter(), new Rectangle(560, 600, 590, 800), null);
PdfFormField pff = textField.getTextField();
parent.addKid(pff);
// add widget to each page
pff.setPlaceInPage(page);
//iText workarounds
field.put(PdfName.P, stamper.getWriter().getPageReference(page));
field.remove(PdfName.FF);
field.remove(PdfName.FT);
}
//in addAnnotation() the page reference is written
stamper.addAnnotation(parent, 1);
//does not work
parent.remove(PdfName.P);
however it didn't work since I guess the page reference is already written. Is there a way to remove it afterwards?
Fact 1:
The PdfFormField class extends the PdfAnnotation class, because most of the times, it is possible to merge a field dictionary with an annotation dictionary.
In your case, you have a PdfFormField that is used as a hierarchical element and although that element is also an instance of PdfAnnotation, it isn't. You can check this by using the isAnnotation() method. It will return false.
Fact 2:
When you add an annotation to an existing PDF, you have to use PdfStamper's addAnnotation() method that not only accepts an annotation object, but also a page number. If you didn't add a page number, PdfStamper wouldn't know where to add the annotation.
When you add a real annotation, PdfStamper will add a /P key that refers to the page where the annotations is visualized.
Fact 3:
There is no other way than using addAnnotation() with an annotation and a page number when working with PdfStamper. When you add a PdfFormField object that is not a real annotation, it won't be treated differently, hence a /P entry will be added, although that doesn't really make sense (as you rightly point out in your question).
Fact 4:
The /P entry is optional. For the widget annotations to be displayed correctly, it is sufficient that they appear in the /Annots of a page.
Conclusion:
iText shouldn't add a /P entry in case you add a PdfFormField that is not a real annotation. Hence I have committed the following change: revision 6756
void addAnnotation(PdfAnnotation annot, int page) {
- annot.setPage(page);
+ if (annot.isAnnotation())
+ annot.setPage(page);
addAnnotation(annot, reader.getPageN(page));
}
I have tested this with the AddFieldAndKids example and it seems to work: the /P entry is no longer added.
This solves your problem in a more robust way. You shouldn't try to remove something that shouldn't have been added in the first place.

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.

Prevent typing in the same style in JTextPane

forgive me for a possible misleading title, but the problem is a bit hard to describe.
I'm currently trying to create a basic texteditor using a JTextPane in Java and I've run into an issue.
As you know in most texteditors you can put your caret/cursor behind a piece of styled text (for example text which is bold) and then you can continue typing in that same style (Eg. append more bold characters).
Luckely this is present by default in the JTextPane, but I want to disable it for a certain style. Mainly the URL-style I coded (basicly this one just sets the HTML.Attribute.HREF attribute in the style to an URL).
So if I would put my caret behind a word (or piece of text) which is an URL, I want to ensure that the next characters which will be added, will not be in the URL-style.
Eg. I think tinymce has this behaviour:
You select text
Click on the Insert URL button
Insert the URL
Place the cursor right after the URL and start typing again in normal style
Is there a way to enforce this behaviour in a JTextPane?
I was thinking about something like this:
Adding a listener for content changes in the document
Check if the added characters were placed right behind a piece of text with the URLstyle
If that was the case => remove the "href" attribute from the style of those characters
The code i use for setting the URL-style to the selected text can be found below. "dot" and "mark" are retrieved from the caret.
SimpleAttributeSet attr = new SimpleAttributeSet(doc.getCharacterElement(dot).getAttributes());
StyleConstants.setUnderline(attr, true);
StyleConstants.setForeground(attr, Color.BLUE);
attr.addAttribute(HTML.Attribute.HREF, url);
doc.setCharacterAttributes((dot < mark) ? dot : mark, length, attr, true);
(Note: To be able to tell the difference between normal "blue underlined" text and an URL, the HREF attribute is used for an URL.)
PS: This is my first question here, so hopefully I gave enough information. ;)
Language: Java, JDK 1.7
Thanks in advance.
Add a CaretListener to detect move and check whether current caret position needs the style reset. If it's detected use
StyledEditorKit's method
public MutableAttributeSet getInputAttributes()
Here just remove the attributes you don't need (URL, blue, underline).
I thought I'd share my solution to the problem (found with the help of StanislavL's answer - thanks again for putting me on the right track).
The following method is called from within a caretlistener, passing the attributes found via the "getInputAttributes"-function and the dot and mark of the caret.
private void blockURLTyping(MutableAttributeSet inputAttr, int dot, int mark)
{
StyledDocument doc = getStyledDocument();
int begin = (dot < mark) ? dot - 1 : mark - 1;
if(begin >= 0)
{
Element dotEl = doc.getCharacterElement(begin);
Element markEl = doc.getCharacterElement((dot < mark) ? mark : dot);
AttributeSet dotAttr = dotEl.getAttributes();
AttributeSet markAttr = markEl.getAttributes();
if(dotAttr.isDefined(HTML.Attribute.HREF)) // Ensure atleast one of them isn't null
{
if(dotAttr.getAttribute(HTML.Attribute.HREF) == markAttr.getAttribute(HTML.Attribute.HREF))
{
inputAttr.addAttribute(HTML.Attribute.HREF, dotAttr.getAttribute(HTML.Attribute.HREF));
inputAttr.addAttribute(StyleConstants.Foreground, Color.BLUE);
inputAttr.addAttribute(StyleConstants.Underline, true);
return;
}
}
}
if(inputAttr.isDefined(HTML.Attribute.HREF)) // In all other cases => remove
{
inputAttr.removeAttribute(HTML.Attribute.HREF);
inputAttr.removeAttribute(StyleConstants.Foreground);
inputAttr.removeAttribute(StyleConstants.Underline);
}
}
Important note; The inputAttributes do not update when the caretposition changes but stays within the same element.
So: when the caret is positioned at the end of the URL, behind the last character => you remove the three attributes you can see in the code above => However when the caret is moved to another position within the URL, the attribute stays removed because the set does not update.
So in practice this means that when you remove attributes from the attributeset, they will stay removed untill the StyledEditorKit updates the inputattributes.
To work around this problem I decided to add the attributes again if the caret is in the middle of an URL, allowing you to insert characters in the middle of the URL - but not append or prepend characters (like I wanted).
The code can probably be optimized a bit more because in most cases dot==mark, but I wanted to share this solution.
PS: The comparison of the HREF-attributes is to deal with the situation where two different URLs are positioned next to eachother in a text. It basicly should check if they are both different instances of a certain object even if the URL itself might be the same.
Code that calls this function:
#Override
protected void fireCaretUpdate(CaretEvent e)
{
super.fireCaretUpdate(e);
MutableAttributeSet attr = getStyledEditorKit().getInputAttributes();
int dot = e.getDot();
int mark = e.getMark();
blockURLTyping(attr, dot, mark);
...
}

Items decorations in a TreeViewer

I have the following problem:
I'm preparing an editor in Eclipse and one of the tab contains TreeViewer to show items in the tree. Each item has a name and a value, which is editable.
The problem I need to indicate to user that value is incorrect (e.g. exceeds a given range). My idea is to decorate incorrect cells with a warning or error icon which will be shown also after editing is complete.
Does anybody have an idea how to decorate items in the tree? I was experimenting with ControlDecoration class but without success.
Thanks in advance,
Marcin
PS. I'm limited to Eclipse 3.4
There are two ways that this can be done. If your TreeViewer displays objects that are instances of EObject (generated by EMF. If your don't understand this part, skip to the next paragraph :)), you can change these EObject's "XyzItemProvider" so that their "getImage" method return a decorated image instead of the "plain" image... and that's it for EMF objects, nothing else needs to be changed.
If you're displaying "classic" Java Objects, you'll have to change your TreeViewer's LabelProvider in order to decorate the Image. This is done through the TreeViewer#setLabelProvider() method.
What you will need then is "how to decorate an Image", which is done through code such as this :
public class MyLabelProvider extends DecoratingLabelProvider {
public Image getImage(Object element) {
Image image = super.getImage(element);
List<Object> images = new ArrayList<Object>(2);
images.add(image);
images.add(<Image of the decorator>);
labelImage = new ComposedImage(images); // This will put the second of the "images" list (the decorator) above the first (the element's image)
return decoratedImage;
}
[...]
}
You then need to give your tree viewer this label provider :
TreeViewer treeViewer = new TreeViewer(...);
treeViewer.setLabelProvider(new MyLabelProvider(new LabelProvider()); // new LabelProvider()... or your previous label provider if you have one.

Categories