двунаправленный с текстовым документом с использованием Aphace POI

Я пытаюсь добавить текст на иврите в текстовый документ, и он работает нормально, но когда я добавляю знаки препинания, он становится беспорядочным.

Это код, который я запускаю:

public static void main(String[] args) throws Exception {

    XWPFDocument document = new XWPFDocument();
    XWPFParagraph paragraph = document.createParagraph();

    paragraph.setAlignment(ParagraphAlignment.LEFT);

    // make RTL direction
    CTP ctp = paragraph.getCTP();
    CTPPr ctppr;
    if ((ctppr = ctp.getPPr()) == null) {
        ctppr = ctp.addNewPPr();
    }
    ctppr.addNewBidi().setVal(STOnOff.ON);

    XWPFRun run = paragraph.createRun();
    run.setText("שלום עולם !");

    // create the document in the specific path by giving it a name
    File newFile = new File("helloWorld.docx");

    // insert document to newFile
    try {
        FileOutputStream output = new FileOutputStream(newFile);
        document.write(output);
        output.close();
        document.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Это "helloWorld.docx", который я получаю:

скриншот

И вот как это должно быть:

скриншот

Более того, я хочу, чтобы весь документ был RTL (даже с двунаправленным), а не только конкретный абзац.

Спасибо за помощь !


person Nadav Rosenberg    schedule 12.04.2020    source источник


Ответы (1)


Это хорошо известная проблема с использованием двунаправленного текста. Восклицательный знак, а также пробел сами по себе не являются символами с написанием справа налево. Поэтому нам нужно пометить их как таковые, если это необходимо. RIGHT-TO-LEFT MARK (RLM) это U+200F. См. https://en.wikipedia.org/wiki/Bidirectional_text#Table_of_possible_BiDi_character_types.

Следующий код работает для меня:

import java.io.FileOutputStream;

import org.apache.poi.xwpf.usermodel.*;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;

public class CreateWordRTLParagraph {

 public static void main(String[] args) throws Exception {

  XWPFDocument doc= new XWPFDocument();

  XWPFParagraph paragraph = doc.createParagraph();
  CTP ctp = paragraph.getCTP();
  CTPPr ctppr;
  if ((ctppr = ctp.getPPr()) == null) ctppr = ctp.addNewPPr();
  ctppr.addNewBidi().setVal(STOnOff.ON);

  XWPFRun run = paragraph.createRun();
  run.setText("שלום עולם \u200F!\u200F");

  FileOutputStream out = new FileOutputStream("WordDocument.docx");
  doc.write(out);
  out.close();
  doc.close();

 }
}

Обратите внимание на знак \u200F после пробела и восклицательный знак.

Если текстовые строки берутся из файла, то выделение отдельных символов не рекомендуется. Затем вся текстовая строка должна быть помечена как текст с написанием справа налево. Для этого мы можем вставить текстовые строки в U+202E RIGHT-TO-LEFT OVERRIDE (RLO), за которым следует U+202C POP DIRECTIONAL FORMATTING (PDF).

Пример:

import java.io.File;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

import org.apache.poi.xwpf.usermodel.*;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STOnOff;

import java.util.List;

public class CreateWordRTLParagraphsFromFile {

 public static void main(String[] args) throws Exception {

  List<String> lines = Files.readAllLines(new File("HebrewTextFile.txt").toPath(), StandardCharsets.UTF_8);

  XWPFDocument doc= new XWPFDocument();

  for (String line : lines) {

   XWPFParagraph paragraph = doc.createParagraph();
   CTP ctp = paragraph.getCTP();
   CTPPr ctppr = ctp.getPPr();
   if (ctppr == null) ctppr = ctp.addNewPPr();
   ctppr.addNewBidi().setVal(STOnOff.ON);

   XWPFRun run = paragraph.createRun();
   run.setText("\u202E" + line + "\u202C");

  }

  FileOutputStream out = new FileOutputStream("WordDocument.docx");
  doc.write(out);
  out.close();
  doc.close();

 }
}

Использование apache poi 5.0.0 для Bidi .setVal(STOnOff.ON) больше не возможно, но .setVal(true) можно использовать:

  //ctppr.addNewBidi().setVal(STOnOff.ON); // up to apache poi 4.1.2
  ctppr.addNewBidi().setVal(true); // from apache poi 5.0.0 on
person Axel Richter    schedule 12.04.2020