PDF exporter: Hebrew text reversed

Community Driven Extensions Project
Post Reply
rosière
Posts: 80
Joined: 28 Nov 2012, 15:55
Contact:

14 Sep 2017, 17:57

Hello everyone,
it's good to be back here.

I am using Primefaces Extension's exporter to export data table to PDF, in which some cells contain Hebrew text while others contain English text.

However at last the Hebrew text is always reversed. Even by adding dir="rtl" to the data table, it is never exported in the same order as its display in web page or in Excel file.
And I still have to use iText's free version (lowagie) because of budget constraint.
So do I have to create a specific method to reverse the text in Java if the cell is in Hebrew?

In order to integrate arial unicode font, I developped an exporter:

Code: Select all

public class PDFUTFExporter extends PDFExporter {

    @Override
    protected void createCustomFonts(String encoding) {
        //In this method we shall enforce the use of UTF font.
        
        Font f = FontFactory.getFont(
                "fonts/ARIALUNI.TTF",
                "Identity-H",
                true,   11.0f,   0,  null);
        try { //cellFont has private access so it may only be modified by reflection.
            Field fCellFont = getClass().getSuperclass().getDeclaredField("cellFont");
            fCellFont.setAccessible(true);
            fCellFont.set(this, f);            
        } catch (NoSuchFieldException nsfe) {
            nsfe.printStackTrace(System.err);
        } catch(IllegalAccessException iae){
            iae.printStackTrace(System.err);
        }

        try { //facetFont has private access so it may only be modified by reflection.
            Field fFacetFont = getClass().getSuperclass().getDeclaredField("facetFont");
            fFacetFont.setAccessible(true);
            fFacetFont.set(this, f);            
        } catch (NoSuchFieldException nsfe) {
            nsfe.printStackTrace(System.err);
        } catch(IllegalAccessException iae){
            iae.printStackTrace(System.err);
        }
        
          try { //fontName has private access so it may only be modified by reflection. Thus we no longer use Helvetica.
            Field fFontName = getClass().getSuperclass().getDeclaredField("fontName");
            fFontName.setAccessible(true);
            fFontName.set(this, "ArialUnicodeMS");            
        } catch (NoSuchFieldException nsfe) {
            nsfe.printStackTrace(System.err);
        } catch(IllegalAccessException iae){
            iae.printStackTrace(System.err);
        }
    }
}
JDK: 1.8
Operating system: Windows 7 Professional
server: payara 4.1.1
IDE: NetBeans 8.1
primefaces : version 5.3.13
primefaces extensions : 4.0.0

User avatar
Melloware
Posts: 390
Joined: 22 Apr 2013, 15:48

14 Sep 2017, 18:16

Wow that is a good question. Unfortunately I don't know too much about that so you might have step through that to figure it out.

or using the Primefaces test project https://github.com/primefaces/primefaces-test create us a fully working example for us to try and run and debug and figure out the issue.
PrimeFaces Extensions Developer
GitHub Profile: https://github.com/melloware
PrimeFaces Elite 6.1.X / PF Extensions 6.1.5

rosière
Posts: 80
Joined: 28 Nov 2012, 15:55
Contact:

14 Nov 2017, 11:34

Hi,
Actually it is not necessary to submit an app.
If you want to test Hebrew, you can store Hebrew text in your database or showcase' in-memory data set, then export it and see the text.

In my judgement, the Hebrew text is reversed because of the font Helvetica.
Because if the pdf contains only ArialUnicodeMS, the Hebrew text is preserved, not reversed.

However, I can't figure out why it uses Helvetica because I never declared it.

I also read Primefaces Extensions' PDFExporter source code from github. But I never found any reference to "Helvetica".
https://github.com/primefaces-extension ... orter.java
JDK: 1.8
Operating system: Windows 7 Professional
server: payara 4.1.1
IDE: NetBeans 8.1
primefaces : version 5.3.13
primefaces extensions : 4.0.0

User avatar
Melloware
Posts: 390
Joined: 22 Apr 2013, 15:48

14 Nov 2017, 14:22

That is weird. The only code I see about fonts is here:

Code: Select all

if (fontName != null && FontFactory.getFont(fontName).getBaseFont() != null) {
            this.cellFont = FontFactory.getFont(fontName, encoding);
            this.facetFont = FontFactory.getFont(fontName, encoding, Font.DEFAULTSIZE, Font.BOLD);
        }
Maybe when its doing "getBaseFont()" its returning Helvetica as the closest match?

Also I wonder if you have the same issue as this person who needs to be able to declare the font: https://github.com/primefaces/primefaces/issues/2363
PrimeFaces Extensions Developer
GitHub Profile: https://github.com/melloware
PrimeFaces Elite 6.1.X / PF Extensions 6.1.5

rosière
Posts: 80
Joined: 28 Nov 2012, 15:55
Contact:

14 Nov 2017, 15:21

That person's issue is a UTF-8 based font. I solved the issue by embedding ArialUnicodeMS with my customized PDFExporter.
My issue is that an extra font Helvetica or ArialMT makes RTF text reversed. And it seems impossible to remove it.

I downloaded Primefaces Extensions 4.0.0's source code from Github.
Then I searched for Helvetica. I found it in a Javascript file but never in Java:

Code: Select all

var stdFontMap = {
  'ArialNarrow': 'Helvetica',
  'ArialNarrow-Bold': 'Helvetica-Bold',
  'ArialNarrow-BoldItalic': 'Helvetica-BoldOblique',
  'ArialNarrow-Italic': 'Helvetica-Oblique',
  'ArialBlack': 'Helvetica',
  'ArialBlack-Bold': 'Helvetica-Bold',
  'ArialBlack-BoldItalic': 'Helvetica-BoldOblique',
  'ArialBlack-Italic': 'Helvetica-Oblique',
  'Arial': 'Helvetica',
  'Arial-Bold': 'Helvetica-Bold',
  'Arial-BoldItalic': 'Helvetica-BoldOblique',
  'Arial-Italic': 'Helvetica-Oblique',
  'Arial-BoldItalicMT': 'Helvetica-BoldOblique',
  'Arial-BoldMT': 'Helvetica-Bold',
  'Arial-ItalicMT': 'Helvetica-Oblique',
  'ArialMT': 'Helvetica',
...}
Then in org.primefaces.extensions.component.documentviewer.DocumentViewerRenderer I found reference to the file "documentviewer/viewer.html"

Code: Select all

  <script type="text/javascript">
        //Adding l10n file
        var l10nNodeUrl = window.parent.PrimeFacesExt.getFacesResource('documentviewer/locale/locales.txt','primefaces-extensions-uncompressed','${project.version}');
        var l10nNode = window.parent.jQuery("<link rel='resource' type='application/l10n' href='" + l10nNodeUrl + "'/>").get(0);
        document.getElementsByTagName('head')[0].appendChild(l10nNode);

        var scriptUrl = window.parent.PrimeFacesExt.getFacesResource('documentviewer/app.js','primefaces-extensions-uncompressed','${project.version}');

Last edited by rosière on 14 Nov 2017, 15:29, edited 3 times in total.
JDK: 1.8
Operating system: Windows 7 Professional
server: payara 4.1.1
IDE: NetBeans 8.1
primefaces : version 5.3.13
primefaces extensions : 4.0.0

User avatar
Melloware
Posts: 390
Joined: 22 Apr 2013, 15:48

14 Nov 2017, 15:24

Yeah that JS you found is for DocumentViewer component not for PDF Exporter.

https://www.primefaces.org/showcase-ext ... /basic.jsf
PrimeFaces Extensions Developer
GitHub Profile: https://github.com/melloware
PrimeFaces Elite 6.1.X / PF Extensions 6.1.5

rosière
Posts: 80
Joined: 28 Nov 2012, 15:55
Contact:

16 Nov 2017, 16:46

It is finally an issue of lowagie.
I tried to display the cell content by reloading addColumnValue() and I could still read the Hebrew text in its original order.

So I applied RTL with lowagie's API in the method:

Code: Select all

  @Override
    protected void addColumnValue(PdfPTable pdfTable, List<UIComponent> components, Font font, String columnType) {
        StringBuilder builder = new StringBuilder();

        for (UIComponent component : components) {
            if (component.isRendered()) {
                String value = exportValue(FacesContext.getCurrentInstance(), component);
                if (value != null) {
                    builder.append(value);
                }
            }
        }
        PdfPCell cell = new PdfPCell(new Paragraph(builder.toString(), font));
        //Enforce RTL, otherwise Hebrew text will be reversed.
[u]        cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
                [/u]
        for (UIComponent component : components) {
            cell = addColumnAlignments(component, cell);
        }
        if (columnType.equalsIgnoreCase("header")) {
            for (UIComponent component : components) {
                cell = addFacetAlignments(component, cell);
            }
        }
        pdfTable.addCell(cell);
    }

JDK: 1.8
Operating system: Windows 7 Professional
server: payara 4.1.1
IDE: NetBeans 8.1
primefaces : version 5.3.13
primefaces extensions : 4.0.0

User avatar
Melloware
Posts: 390
Joined: 22 Apr 2013, 15:48

16 Nov 2017, 16:55

Nice detective work!

I wonder if i should add something to the exporter like dir="rtl" like other components have and it was set the PDFCell's in the base PDF Exporter to reversed?
PrimeFaces Extensions Developer
GitHub Profile: https://github.com/melloware
PrimeFaces Elite 6.1.X / PF Extensions 6.1.5

rosière
Posts: 80
Joined: 28 Nov 2012, 15:55
Contact:

16 Nov 2017, 17:00

It is not a perfect solution, unfortunately.
If datatable cell contains both RTL and LTR characters, it will be completely messed.

To solve it completely, it is necessary to study JasperReports' source code.
However I can't do it now.
Last edited by rosière on 16 Nov 2017, 17:44, edited 1 time in total.
JDK: 1.8
Operating system: Windows 7 Professional
server: payara 4.1.1
IDE: NetBeans 8.1
primefaces : version 5.3.13
primefaces extensions : 4.0.0

User avatar
Melloware
Posts: 390
Joined: 22 Apr 2013, 15:48

16 Nov 2017, 17:16

Ahhh that would be a problem. I thought you were making all text in Hebrew and thus want all of it reversed.

Not some in English and some in Hebrew.
PrimeFaces Extensions Developer
GitHub Profile: https://github.com/melloware
PrimeFaces Elite 6.1.X / PF Extensions 6.1.5

Post Reply
  • Information
  • Who is online

    Users browsing this forum: No registered users and 4 guests