acroform field.setRichTextValue is not working - pdfbox

I have a field from acroform and I see field.setValue() and field.setRichTextValue(...). The first one set the correct value, but second one seems not working, rich text value is not display.
Here is code im using :
PDDocument pdfDocument = PDDocument.load(new File(SRC));
pdfDocument.getDocument().setIsXRefStream(true);
PDAcroForm acroForm = pdfDocument.getDocumentCatalog().getAcroForm();
acroForm.setNeedAppearances(false);
acroForm.getField("tenantDataValue").setValue("Deuxième texte");
acroForm.getField("tradingAddressValue").setValue("Text replacé");
acroForm.getField("buildingDataValue").setValue("Deuxième texte");
acroForm.getField("oldRentValue").setValue("750");
acroForm.getField("oldChargesValue").setValue("655");
acroForm.getField("newRentValue").setValue("415");
acroForm.getField("newChargesValue").setValue("358");
acroForm.getField("increaseEffectiveDateValue").setValue("Texte 3eme contenu");
// THIS RICH TEXT NOT SHOW ANYTHING
PDTextField field = (PDTextField) acroForm.getField("tableData");
field.setRichText(true);
String val = "\\rtpara[size=12]{para1}{This is 12pt font, while \\span{size=8}{this is 8pt font.} OK?}";
field.setRichTextValue(val);
I expect field named "tableData" to be setted with rich text value!
You can download the PDF form I am using with this code : download pdf form
and you can download the output after runn this code and flatten form data download output here

To sum up what has been said in the comments to the question plus some studies of the working version...
Wrong rich text format
The OP in his original code used this as rich text
String val = "\\rtpara[size=12]{para1}{This is 12pt font, while \\span{size=8}{this is 8pt font.} OK?}";
which he took from this document. But that document is the manual for the LaTeX richtext package which provides commands and documentation needed to “easily” produce such rich strings. I.e. the \rtpara... above is not PDF rich text but instead a LaTeX command that produces PDF rich text (if executed in a LaTeX context).
The document actually even demonstrates this using the example
\rtpara[indent=first]{para1}{Now is the time for
\span{style={bold,italic,strikeit},color=ff0000}{J\374rgen}
and all good men to come to the aid of \it{their}
\bf{country}. Now is the time for \span{style=italic}
{all good} women to do the same.}
for which the instruction generates two values, a rich text value and a plain text value:
\useRV{para1}: <p dir="ltr" style="text-indent:12pt;
margin-top:0pt;margin-bottom:0pt;">Now is the time
for <span style="text-decoration:line-through;
font-weight:bold;font-style:italic;color:#ff0000;
">J\374rgen</span> and all good men to come to the
aid of <i>their</i> <b>country</b>. Now is the
time for <span style="font-style:italic;">all
good</span> women to do the same.</p>
\useV{para1}: Now is the time for J\374rgen and all
good men to come to the aid of their country. Now
is the time for all good women to do the same.
As one can see in the \useRV{para1} result, PDF rich text uses (cut down) HTML markup for rich text.
For more details please lookup the PDF specification, e.g. section 12.7.3.4 "Rich Text Strings" in the copy of ISO 32000-1 published by Adobe here
PDFBox does not create rich text appearances
The OP in his original code uses
acroForm.setNeedAppearances(false);
This sets a flag that claims that all form fields have appearance streams (in which the visual appearance of the respective form field plus its content are elaborated) and that these streams represent the current value of the field, so it effectively tells the next processor of the PDF that it can use these appearance streams as-is and does not need to generate them itself.
As #Tilman quoted from the JavaDocs, though,
/**
* Set the fields rich text value.
*
* <p>
* Setting the rich text value will not generate the appearance
* for the field.
* <br>
* You can set {#link PDAcroForm#setNeedAppearances(Boolean)} to
* signal a conforming reader to generate the appearance stream.
* </p>
*
* Providing null as the value will remove the default style string.
*
* #param richTextValue a rich text string
*/
public void setRichTextValue(String richTextValue)
So setRichTextValue does not create an appropriate appearance stream for the field. To signal the next processor of the PDF (in particular a viewer or form flattener) that it has to generate appearances, therefore, one needs to use
acroForm.setNeedAppearances(true);
Making Adobe Acrobat (Reader) generate the appearance from rich text
When asked to generate field appearances for a rich text field, Adobe Acrobat has the choice to do so either based on the rich text value RV or the flat text value V. I did some quick checks and Adobe Acrobat appears to use these strategies:
If RV is set and the value of V equals the value of RV without the rich text markup, Adobe Acrobat assumes the value of RV to be up-to-date and generates an appearance from this rich text string according to the PDF specification. Else the value of RV (if present at all) is assumed to be outdated and ignored!
Otherwise, if the V value contains rich text markup, Adobe Acrobat assumes this value to be rich text and creates the appearance according to this styling.
This is not according to the PDF specification.
Probably some software products used to falsely put the rich text into the V value and Adobe Acrobat started to support this misuse for larger compatibility.
Otherwise the V value is used as a plain string and an appearance is generated accordingly.
This explains why the OP's original approach using only
field.setRichTextValue(val);
showed no change - the rich text value was ignored by Adobe Acrobat.
And it also explains his observation
then instead of setRichTextValue simply using field.setValue("<body xmlns=\"http://www.w3.org/1999/xhtml\"><p style=\"color:#FF0000;\">Red
</p><p style=\"color:#1E487C;\">Blue
</p></body>") works ! in acrobat reader (without flatten) the field is correctly formatted
Be aware, though, that this is beyond the PDF specification. If you want to generate valid PDF, you have to set both RV and V and have the latter contain the plain version of the rich text of the former.
For example use
String val = "<?xml version=\"1.0\"?>"
+ "<body xfa:APIVersion=\"Acroform:2.7.0.0\" xfa:spec=\"2.1\" xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:xfa=\"http://www.xfa.org/schema/xfa-data/1.0/\">"
+ "<p dir=\"ltr\" style=\"margin-top:0pt;margin-bottom:0pt;font-family:Helvetica;font-size:12pt\">"
+ "This is 12pt font, while "
+ "<span style=\"font-size:8pt\">this is 8pt font.</span>"
+ " OK?"
+ "</p>"
+ "</body>";
String valClean = "This is 12pt font, while this is 8pt font. OK?";
field.setValue(valClean);
field.setRichTextValue(val);
or
String val = "<body xmlns=\"http://www.w3.org/1999/xhtml\"><p style=\"color:#FF0000;\">Red
</p><p style=\"color:#1E487C;\">Blue
</p></body>";
String valClean = "Red\rBlue\r";
field.setValue(valClean);
field.setRichTextValue(val);

Related

Adding Arial Unicode MS to CKEditor

My web application allows user to write rich text inside CKEditor, then export the result as PDF with the Flying Saucer library.
As they need to write Greek characters, I chose to add Arial Unicode MS to the available fonts, by doing the following :
config.font_names = "*several fonts...*; Arial Unicode MS/Arial Unicode MS, serif";
This font is now displayed correctly in the CKEditor menu, but when I apply this font to any element, I get the following result :
<span style="font-family:arial unicode ms,serif;"> some text </span>
As you can notice, I lost the UpperCase characters. This has pretty bad effect during PDF export, as then Flying Saucer doesn't recognise the font and so uses Helvetica which does not support Unicode characters, so the greek characters are not displayed in the PDF.
If I change manually from code source
<span style="font-family:arial unicode ms,serif;"> some text </span>
to
<span style="font-family:Arial Unicode MS,serif;"> some text </span>
then it is working as expected, greek characters are displayed.
Has anyone met this problem before? Is there a way to avoid UpperCase characters to be changed to LowerCase?
I really want to avoid doing any kind of post-processing like :
htmlString = htmlString.replace("arial unicode ms", "Arial Unicode MS");
I agree with you regarding resolving this issue aside from Flying Saucer R8.
Although depending upon your workflow, would it be more efficient to allow CKEditor to preprocess and validate a completed HTML encoded file (render the entire document to HTML first)?
None of the CKEditor support tickets specify the true source of the issue, so I recommend confirming for yourself whether it is (A) a styling issue, or (B) a CSS processing issue, or (C) a peculiar CKEditor parsing issue.
A possible workaround:
Make a copy of the desired unicode font and import it into Type 3.2 (works on both Mac and Windows).
http://www.cr8software.net/type.html
rename the duplicate font set into something all lowercase.
Limit your font selection
config.font_names = "customfontnamehere";
Apply the style separately (unicode typeface greatvibes below) and see if that gives you the desired result:
var s = CKEDITOR.document.$.createElement( 'style' );
s.type = 'text/css';
cardElement.$.appendChild( s );
s.styleSheet.cssText =
'#font-face {' +
'font-family: \'GreatVibes\';' +
'src: url(\'' + path +'fonts/GreatVibes-Regular.eot\');' +
'}' +
style;
If the above does not work, you can try to modify the xmas plugin.js (also uses the unicode typeface greatvibes and does all sorts of cool manipulations before output), so it might be worth trying to modify it rather than start from scratch:
'<style type="text/css">' +
'#font-face {' +
'font-family: "GreatVibes";' +
'src: url("' + path +'fonts/GreatVibes-Regular.ttf");' +
'}' +
style +
'</style>' )
Whichever approach you try, the goal is to test various styling and see if CKEditor defaults back to Helvetica again.
Lastly, the CKEditor SDK has excellent support, so if you have the time and energy, you could write a plugin. Sounds daunting, I know, but notice how the plugin.js within the /plugins/font directory has priority for size attributes.
If you are not interested in producing your own plugin, I recommend contacting the prolific ckeditor plugin writer
doksoft
(listed both on their website and on his own website) and ask for a demo of his commercial plugin "CKEditor Special Symbols" which has broad unicode capability.
Hope that helps,
ClaireW
I didn't find any way to do it with Flying Saucer R8, but you can make it work using Flying Saucer R9.
The method
ITextResolver.addFont(String path, String fontFamilyNameOverride, String encoding, boolean embedded, String pathToPFB) allow you to add the fond with a specific name.
Code sample:
ITextRenderer renderer = new ITextRenderer();
// Adding fonts
renderer.getFontResolver().addFont("fonts/ARIALUNI.TTF", "arial unicode ms", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, null);
renderer.getFontResolver().addFont("fonts/ARIALUNI.TTF", "Arial Unicode MS", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, null);
String inputFile = "test.html";
renderer.setDocument(new File(inputFile));
renderer.layout();
String outputFile = "test.pdf";
OutputStream os = new FileOutputStream(outputFile);
renderer.createPDF(os);
os.close();
You can find Flying Saucer R9 on Maven.
The simplest solution (until CKEditor fixes that bug) is to do that post-processing.
You can do it on the server (really simple, you already have the code) or with a little CKEditor plugin, but that will give you the solution that you want and unless you need to add more fonts it will work without any further changes.

Docx4J: Vertical text frame not exported to PDF

I'm using Docx4J to make an invoice model.
In the left-side of the page, it's usual to show a legal sentence as: Registered company in ... Book ... Page ...
I have inserted this in my template with a Word text frame.
Well, my issue is: when exporting to .docx, this legal text is shown perfect, but when exporting to .pdf, it's shown as an horizontal table under the other data.
The code to export to PDF is:
FOSettings foSettings = Docx4J.createFOSettings();
foSettings.setFoDumpFile(foDumpFile);
foSettings.setWmlPackage(template);
fos = new FileOutputStream(new File("/C:/mypath/prueba_OUT.pdf"));
Docx4J.toFO(foSettings, fos, Docx4J.FLAG_EXPORT_PREFER_XSL);
Any help would be very appreciated.
Thanks.
You'd need to extend the PDF via FO code; see further How to correctly position a header image with docx4j?
Float left may or may not be easy; similarly the rotated text.
In general, the way to work on this is to take the FO generated by docx4j, then hand edit it to something which FOP can convert to a PDF you are happy with. If you can do that, then its a matter of modifying docx4j to generate that FO.

Tika - how to extract text from PDF text: underlined, highlighted, crossed out

I'm using Tika* to parse a PDF file.
There are no problems to retrieve the document's text, but I don't figure out how to extract text:
underlined
highlighted
crossed out
Adobe Writer gives you different text edit options, but I'm not able to see where they are "hidden".
Is there a solution to extract these metadata information? (underline, highligh ...)
Do you know if Tika is able to extract this data?
*http://tika.apache.org/
Wow. 4 years is a long time to wait for an answer, and I figure you have found a solution by now. Anyways, for the sake of those who would visit this link, the answer is Yes. Apache Tika can extract not just text in a document, but also the formatting as well (e.g. bold, italicized). This was my Scenario:
//inputStream is the document you wish to parse from.
AutoDetectParser parser = new AutoDetectParser();
ContentHandler handler = new BodyContentHandler(new ToXMLContentHandler());
Metadata metadata = new Metadata();
parser.parse(inputStream,handler,metadata);
System.out.println(handler.toString());
The print statement prints an XML of your document. With a little work of cleaning up the XML (really HTML tags), you would be left with tags like < b >text< /b> for bold text and < i >text < / i > for italicized text. Then you could find a way to render it. Good luck.

How to find x,y location of a text in pdf

Is there any tool to find the X-Y location on a text content in a pdf file ?
Docotic.Pdf Library can do it. See C# sample below:
using (PdfDocument doc = new PdfDocument("your_pdf.pdf"))
{
foreach (PdfTextData textData in doc.Pages[0].Canvas.GetTextData())
Console.WriteLine(textData.Position + " " + textData.Text);
}
Try running "Preflight..." in Acrobat and choosing PDF Analysis -> List page objects, grouped by type of object.
If you locate the text objects within the results list, you will notice there is a position value (in points) within the Text Properties -> * Font section.
TET, the Text Extraction Toolkit from the pdflib family of products can do that. TET has a commandline interface, and it's the most powerful of all text extraction tools I'm aware of. (It can even handle ligatures...)
Geometry
TET provides precise metrics for the text, such as the position on the page, glyph widths, and text direction. Specific areas on the page can be excluded or included in the text extraction, e.g. to ignore headers and footers or margins.

Is there a way to add "alt text" to links in PDFs in Adobe Acrobat?

In Adobe Acrobat Pro, it's not that difficult to add links to a page, but I'm wondering if there's also a way to add "alt text" (sometimes called "title text") to links as well. In HTML, this would be done as such:
link
Then when the mouse is hovering over the link, the text appears as a little tooltip. Is there an equivalent for PDFs? And if so, how do you add it?
Actually PDF does support alternate text. It's part of Logical Structure documented PDF Reference 1.7 section 10.8.2 "Alternate Descriptions"
/Span << /Lang (en-us) /Alt (six-point star) >> BDC (✡) Tj EMC
In PDF syntax, Link annotations support a Contents entry to serve as an alternate description:
/Annots [
<<
/Type /Annot
/Subtype /Link
/Border [1 1 1]
/Dest [4 0 R /XYZ 0 0 null]
/Rect [ 50 50 80 60 ]
/Contents (Link)
>>
]
Quoting "PDF Reference - 6th edition - Adobe® Portable Document Format - Version 1.7 - November 2006" :
Contents text string (Optional) Text to be displayed for the annotation or, if this type of annotation does not display text, an alternate description of the annotation’s contents in human-readable form. In either case, this text is useful when extracting the document’s contents in support of accessibility to users with disabilities or for other purposes
And later on:
For all other annotation types (Link , Movie , Widget , PrinterMark , and TrapNet), the Contents entry provides an alternate representation of the annotation’s contents in human-readable form
This is displayed well with Sumatra PDF v3.1.2, when a border is present:
However this is not widely supported by other PDF readers.
The W3C, in its PDF Techniques for WCAG 2.0 recommend another ways to display alternative texts on links for accessibility purposes:
PDF11: Providing links and link text using the Link annotation and the /Link structure element in PDF documents
PDF13: Providing replacement text using the /Alt entry for links in PDF documents
No, it's not possible to add alt text to a link in a PDF. There's no provision for this in the PDF reference.
On a related note, links in PDFs and links in HTML documents are handled quite differently. A link in a PDF is actually a type of annotation, which sits on top of the page at specified co-ordinates, and has no technical relationship to the text or image in the document. Where as links in HTML documents bare a direct relationpship to the elements which they hyperlink.
Alt text, or alternate text, is not the same as title text. Title text is meta data intended for human consumption. Alt text is alternate text content upon media in case the media fails to load.
There is also a trick using an invisible form button that doesn't do anything but allows a small popup tooltip text to be added when the mouse hovers over it.
Officially, per PDF 1.7 as defined in ISO 32000-1 14.9.3 (see Adobe website for a free download of a document that is equivaent to the ISO standard for PDF 1.7), one would provide alternate text for an annotation - like for example a Link annotation - by adding a key "Contents" to its data structure and provide the alt text as a text string value for that key.
Unfortunately Acrobat does not seem to provide a user interface to add or edit this "Contents" text string for Link annotations, and even if it is present it will not be used for the tool tip. Instead the tool tip always seems to be the target of the Link annotation, at least if it points to a URL.
So on a visual level one could hack around this by adding some other invisible elements on top of the area of the Link annotation that have the desired behavior. Not a very nice hack, at least for my taste. In addition it does not help with accessibility of the PDF, as it introduces yet another stray object...
Facing the same problem I used the JS lib "pdf-lib" (https://pdf-lib.js.org/docs/api/classes/pdfdocument) to edit the content of the pdf file and add the missing attributes on annotations.
const pdfLib = require('pdf-lib');
const fs = require('fs');
function getNewMap(pdfDoc, str){
return pdfDoc.context.obj(
{
Alt: new pdfLib.PDFString(str),
Contents: new pdfLib.PDFString(str)
}).dict;
}
const pdfData = await fs.readFile('your-pdf-document.pdf');
const pdfDoc = await pdfLib.PDFDocument.load(pdfData);
pdfDoc.context.enumerateIndirectObjects().forEach(_o => {
const pdfRef = _o[0];
const pdfObject = _o[1];
if (typeof pdfObject?.lookup === "function"){
if (pdfObject.lookup(pdfLib.PDFName.of('Type'))?.encodedName === "/Annot"){
// We use the link URI to implement annotation "Alt" & "Contents" attributes
const annotLinkUri = pdfObject.lookup(pdfLib.PDFName.of('A')).get(pdfLib.PDFName.of('URI')).value;
const titleFromUri = annotLinkUri.replace(/(http:|)(^|\/\/)(.*?\/)/g, "").replace(/\//g, "").trim();
// We build the new map with "Alt" and "Contents" attributes and assign it to the "Annot" object dictionnary
const newMap = getNewMap(pdfDoc, titleFromUri);
const newdict = new Map([...pdfObject.dict, ...newMap]);
pdfObject.dict = newdict;
}
}
})
// We save the file
const pdfBytes = await pdfDoc.save();
await fs.promises.writeFile("your-pdf-document-accessible.pdf", pdfBytes);