使用 iText 从 html 内容到 pdf 的阿拉伯字符
Arabic characters from html content to pdf using iText
我无法在 PDF 生成中将来自 HTML 内容的阿拉伯字符显示为“?”
我能够显示来自 String 变量的阿拉伯语文本。同时,我无法从 HTML 字符串生成阿拉伯文本。
我想用两栏显示 PDF,左侧是英语,右侧是阿拉伯语文本。
当我使用以下程序转换成pdf时。请在这方面帮助我。
try
{
Document document = new Document(PageSize.A4, 50, 50, 50, 50);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, out);
BaseFont bf = BaseFont.createFont("C:\arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
Font font = new Font(bf, 8);
document.open();
BufferedReader br = new BufferedReader(new FileReader("C:\style.css"));
StringBuffer fileContents = new StringBuffer();
String line = br.readLine();
while (line != null)
{
fileContents.append(line);
line = br.readLine();
}
br.close();
String styles = fileContents.toString(); //"p { font-family: Arial;}";
Paragraph cirNoEn = null;
Paragraph cirNoAr = null;
String htmlContentEn = null;
String htmlContentAr = null;
PdfPCell contentEnCell = new PdfPCell();
PdfPCell contentArCell = new PdfPCell();
cirNoEn = new Paragraph("Circular No. (" + cirEnNo + ")", new Font(bf, 14, Font.BOLD | Font.UNDERLINE));
cirNoAr = new Paragraph("رقم التعميم (" + cirArNo + ")", new Font(bf, 14, Font.BOLD | Font.UNDERLINE));
htmlContentEn = “< p >< span > Dear….</ span ></ p >”;
htmlContentAr = “< p >< span > رقم التعميم رقم التعميم </ p >< p > رقم التعميم ….</ span ></ p >”;
for (Element e : XMLWorkerHelper.parseToElementList(htmlContentEn, styles))
{
for (Chunk c : e.getChunks())
{
c.setFont(new Font(bf));
}
contentEnCell.addElement(e);
}
for (Element e : XMLWorkerHelper.parseToElementList(htmlContentAr, styles))
{
for (Chunk c:e.getChunks())
{
c.setFont(new Font(bf));
}
contentArCell.addElement(e);
}
PdfPCell emptyCell = new PdfPCell();
PdfPCell cirNoEnCell = new PdfPCell(cirNoEn);
PdfPCell cirNoArCell = new PdfPCell(cirNoAr);
cirNoEnCell.setHorizontalAlignment(Element.ALIGN_CENTER);
cirNoArCell.setHorizontalAlignment(Element.ALIGN_CENTER);
emptyCell.setBorder(Rectangle.NO_BORDER);
emptyCell.setFixedHeight(15);
cirNoEnCell.setBorder(Rectangle.NO_BORDER);
cirNoArCell.setBorder(Rectangle.NO_BORDER);
contentEnCell.setBorder(Rectangle.NO_BORDER);
contentArCell.setBorder(Rectangle.NO_BORDER);
cirNoArCell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
contentArCell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
contentEnCell.setNoWrap(false);
contentArCell.setNoWrap(false);
PdfPTable circularInfoTable = null;
emptyCell.setColspan(2);
circularInfoTable = new PdfPTable(2);
circularInfoTable.addCell(cirNoEnCell);
circularInfoTable.addCell(cirNoArCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(contentEnCell);
circularInfoTable.addCell(contentArCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
circularInfoTable.setWidthPercentage(100);
document.add(circularInfoTable);
document.close();
}
catch (Exception e)
{
}
请查看 ParseHtml7 and ParseHtml8 示例。他们使用阿拉伯字符输入 HTML,然后创建具有相同阿拉伯文本的 PDF:
在我们看代码之前,请允许我解释一下在源代码中使用非 ASCII 字符不是一个好主意。例如:这还没有完成:
htmlContentAr = “<p><span> رقم التعميم رقم التعميم</p><p>رقم التعميم ….</span></p>”;
您永远不知道包含这些字形的 Java 文件将如何存储。如果它没有存储为 UTF-8,字符最终可能看起来完全不同。众所周知,版本控制系统在处理非 ASCII 字符时存在问题,甚至编译器也会出现编码错误。如果您真的想在代码中存储硬编码的 String
值,请使用 UNICODE 表示法。您的部分问题是编码问题,您可以在此处阅读更多相关信息:Can't get Czech characters while generating a PDF
对于屏幕截图中显示的示例,我使用 UTF-8 编码保存了以下文件:
这是您将在文件中找到的内容 arabic.html
:
<html>
<body style="font-family: Noto Naskh Arabic">
<p>رقم التعميم رقم التعميم</p>
<p>رقم التعميم</p>
</body>
</html>
这是您将在文件中找到的内容 arabic2.html
:
<html>
<body style="font-family: Noto Naskh Arabic">
<table>
<tr>
<td dir="rtl">رقم التعميم رقم التعميم</td>
<td dir="rtl">رقم التعميم</td>
</tr>
</table>
</body>
</html>
问题的第二部分与字体有关。使用知道如何绘制阿拉伯字形的字体很重要。很难相信 arial.ttf
就在 C:
驱动器的根目录下。那不是一个好主意。我希望您使用 C:/windows/fonts/arialuni.ttf
,它当然知道阿拉伯字形。
选择字体是不够的。您的 HTML 需要知道要使用的字体系列。因为文档中的大多数示例都使用 Arial,所以我决定使用 NOTO 字体。我通过阅读这个问题发现了这些字体:。我真的很喜欢这些字体,因为它们很好,而且(几乎)支持所有语言。例如,我使用 NotoNaskhArabic-Regular.ttf
这意味着我需要像这样定义字体系列:
style="font-family: Noto Naskh Arabic"
我在 XML 的 body 标签中定义了样式,显然您可以选择在哪里定义它:在外部 CSS 文件中,在 <head>
,在 <td>
标签级别,...这个选择完全由您决定,但您必须在某处定义要使用的字体。
当然:当XML Worker 遇到font-family: Noto Naskh Arabic
时,iText 不知道在哪里可以找到对应的NotoNaskhArabic-Regular.ttf
,除非我们注册那个字体。我们可以通过创建 FontProvider
接口的实例来做到这一点。我选择使用 XMLWorkerFontProvider
,但您可以自由编写自己的 FontProvider
实现:
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
还有一个障碍需要克服:阿拉伯语是从右向左书写的。我看到您想在 PdfPCell
级别定义 运行 方向,并使用 ElementList
将 HTML 内容添加到此单元格。这就是为什么我首先写了一个类似的例子,命名为 ParseHtml7:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
// Styles
CSSResolver cssResolver = new StyleAttrCSSResolver();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
// HTML
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline pdf = new ElementHandlerPipeline(elements, null);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML), Charset.forName("UTF-8"));
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell();
cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
for (Element e : elements) {
cell.addElement(e);
}
table.addCell(cell);
document.add(table);
// step 5
document.close();
}
HTML中没有table,但是我们自己创建PdfPTable
,我们把HTML中的内容添加到一个PdfPCell
中使用 运行 方向 LTR,我们将此单元格添加到 table,并将 table 添加到文档。
也许这是你的实际需求,但你为什么要以如此复杂的方式做到这一点?如果您需要 table,为什么不在 HTML 中创建 table 并定义一些单元格是这样的 RTL:
<td dir="rtl">...</td>
这样,您不必创建 ElementList
,您只需像 ParseHtml8 示例中那样将 HTML 解析为 PDF:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
// Styles
CSSResolver cssResolver = new StyleAttrCSSResolver();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML), Charset.forName("UTF-8"));;
// step 5
document.close();
}
这个例子中需要的代码较少,当你想改变布局时,改变HTML就足够了。您无需更改 Java 代码。
再举一个例子:在 ParseHtml9 中,我创建了一个 table,其中一列是英文名称 ("Lawrence of Arabia"),另一列是阿拉伯语翻译 ("لورانس العرب ”)。因为我需要英文和阿拉伯文的不同字体,所以我在<td>
级别定义了字体:
<table>
<tr>
<td>Lawrence of Arabia</td>
<td dir="rtl" style="font-family: Noto Naskh Arabic">لورانس العرب</td>
</tr>
</table>
第一列使用默认字体,从左到右书写,无需特殊设置。对于第二列,我定义了一种阿拉伯字体并将 运行 方向设置为 "rtl"
.
结果如下所示:
这比您在代码中尝试做的要容易得多。
我无法在 PDF 生成中将来自 HTML 内容的阿拉伯字符显示为“?”
我能够显示来自 String 变量的阿拉伯语文本。同时,我无法从 HTML 字符串生成阿拉伯文本。
我想用两栏显示 PDF,左侧是英语,右侧是阿拉伯语文本。
当我使用以下程序转换成pdf时。请在这方面帮助我。
try
{
Document document = new Document(PageSize.A4, 50, 50, 50, 50);
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, out);
BaseFont bf = BaseFont.createFont("C:\arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
Font font = new Font(bf, 8);
document.open();
BufferedReader br = new BufferedReader(new FileReader("C:\style.css"));
StringBuffer fileContents = new StringBuffer();
String line = br.readLine();
while (line != null)
{
fileContents.append(line);
line = br.readLine();
}
br.close();
String styles = fileContents.toString(); //"p { font-family: Arial;}";
Paragraph cirNoEn = null;
Paragraph cirNoAr = null;
String htmlContentEn = null;
String htmlContentAr = null;
PdfPCell contentEnCell = new PdfPCell();
PdfPCell contentArCell = new PdfPCell();
cirNoEn = new Paragraph("Circular No. (" + cirEnNo + ")", new Font(bf, 14, Font.BOLD | Font.UNDERLINE));
cirNoAr = new Paragraph("رقم التعميم (" + cirArNo + ")", new Font(bf, 14, Font.BOLD | Font.UNDERLINE));
htmlContentEn = “< p >< span > Dear….</ span ></ p >”;
htmlContentAr = “< p >< span > رقم التعميم رقم التعميم </ p >< p > رقم التعميم ….</ span ></ p >”;
for (Element e : XMLWorkerHelper.parseToElementList(htmlContentEn, styles))
{
for (Chunk c : e.getChunks())
{
c.setFont(new Font(bf));
}
contentEnCell.addElement(e);
}
for (Element e : XMLWorkerHelper.parseToElementList(htmlContentAr, styles))
{
for (Chunk c:e.getChunks())
{
c.setFont(new Font(bf));
}
contentArCell.addElement(e);
}
PdfPCell emptyCell = new PdfPCell();
PdfPCell cirNoEnCell = new PdfPCell(cirNoEn);
PdfPCell cirNoArCell = new PdfPCell(cirNoAr);
cirNoEnCell.setHorizontalAlignment(Element.ALIGN_CENTER);
cirNoArCell.setHorizontalAlignment(Element.ALIGN_CENTER);
emptyCell.setBorder(Rectangle.NO_BORDER);
emptyCell.setFixedHeight(15);
cirNoEnCell.setBorder(Rectangle.NO_BORDER);
cirNoArCell.setBorder(Rectangle.NO_BORDER);
contentEnCell.setBorder(Rectangle.NO_BORDER);
contentArCell.setBorder(Rectangle.NO_BORDER);
cirNoArCell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
contentArCell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
contentEnCell.setNoWrap(false);
contentArCell.setNoWrap(false);
PdfPTable circularInfoTable = null;
emptyCell.setColspan(2);
circularInfoTable = new PdfPTable(2);
circularInfoTable.addCell(cirNoEnCell);
circularInfoTable.addCell(cirNoArCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.addCell(contentEnCell);
circularInfoTable.addCell(contentArCell);
circularInfoTable.addCell(emptyCell);
circularInfoTable.getDefaultCell().setBorder(PdfPCell.NO_BORDER);
circularInfoTable.setWidthPercentage(100);
document.add(circularInfoTable);
document.close();
}
catch (Exception e)
{
}
请查看 ParseHtml7 and ParseHtml8 示例。他们使用阿拉伯字符输入 HTML,然后创建具有相同阿拉伯文本的 PDF:
在我们看代码之前,请允许我解释一下在源代码中使用非 ASCII 字符不是一个好主意。例如:这还没有完成:
htmlContentAr = “<p><span> رقم التعميم رقم التعميم</p><p>رقم التعميم ….</span></p>”;
您永远不知道包含这些字形的 Java 文件将如何存储。如果它没有存储为 UTF-8,字符最终可能看起来完全不同。众所周知,版本控制系统在处理非 ASCII 字符时存在问题,甚至编译器也会出现编码错误。如果您真的想在代码中存储硬编码的 String
值,请使用 UNICODE 表示法。您的部分问题是编码问题,您可以在此处阅读更多相关信息:Can't get Czech characters while generating a PDF
对于屏幕截图中显示的示例,我使用 UTF-8 编码保存了以下文件:
这是您将在文件中找到的内容 arabic.html
:
<html>
<body style="font-family: Noto Naskh Arabic">
<p>رقم التعميم رقم التعميم</p>
<p>رقم التعميم</p>
</body>
</html>
这是您将在文件中找到的内容 arabic2.html
:
<html>
<body style="font-family: Noto Naskh Arabic">
<table>
<tr>
<td dir="rtl">رقم التعميم رقم التعميم</td>
<td dir="rtl">رقم التعميم</td>
</tr>
</table>
</body>
</html>
问题的第二部分与字体有关。使用知道如何绘制阿拉伯字形的字体很重要。很难相信 arial.ttf
就在 C:
驱动器的根目录下。那不是一个好主意。我希望您使用 C:/windows/fonts/arialuni.ttf
,它当然知道阿拉伯字形。
选择字体是不够的。您的 HTML 需要知道要使用的字体系列。因为文档中的大多数示例都使用 Arial,所以我决定使用 NOTO 字体。我通过阅读这个问题发现了这些字体:NotoNaskhArabic-Regular.ttf
这意味着我需要像这样定义字体系列:
style="font-family: Noto Naskh Arabic"
我在 XML 的 body 标签中定义了样式,显然您可以选择在哪里定义它:在外部 CSS 文件中,在 <head>
,在 <td>
标签级别,...这个选择完全由您决定,但您必须在某处定义要使用的字体。
当然:当XML Worker 遇到font-family: Noto Naskh Arabic
时,iText 不知道在哪里可以找到对应的NotoNaskhArabic-Regular.ttf
,除非我们注册那个字体。我们可以通过创建 FontProvider
接口的实例来做到这一点。我选择使用 XMLWorkerFontProvider
,但您可以自由编写自己的 FontProvider
实现:
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
还有一个障碍需要克服:阿拉伯语是从右向左书写的。我看到您想在 PdfPCell
级别定义 运行 方向,并使用 ElementList
将 HTML 内容添加到此单元格。这就是为什么我首先写了一个类似的例子,命名为 ParseHtml7:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
// Styles
CSSResolver cssResolver = new StyleAttrCSSResolver();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
// HTML
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
ElementList elements = new ElementList();
ElementHandlerPipeline pdf = new ElementHandlerPipeline(elements, null);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML), Charset.forName("UTF-8"));
PdfPTable table = new PdfPTable(1);
PdfPCell cell = new PdfPCell();
cell.setRunDirection(PdfWriter.RUN_DIRECTION_RTL);
for (Element e : elements) {
cell.addElement(e);
}
table.addCell(cell);
document.add(table);
// step 5
document.close();
}
HTML中没有table,但是我们自己创建PdfPTable
,我们把HTML中的内容添加到一个PdfPCell
中使用 运行 方向 LTR,我们将此单元格添加到 table,并将 table 添加到文档。
也许这是你的实际需求,但你为什么要以如此复杂的方式做到这一点?如果您需要 table,为什么不在 HTML 中创建 table 并定义一些单元格是这样的 RTL:
<td dir="rtl">...</td>
这样,您不必创建 ElementList
,您只需像 ParseHtml8 示例中那样将 HTML 解析为 PDF:
public void createPdf(String file) throws IOException, DocumentException {
// step 1
Document document = new Document();
// step 2
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
// Styles
CSSResolver cssResolver = new StyleAttrCSSResolver();
XMLWorkerFontProvider fontProvider = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS);
fontProvider.register("resources/fonts/NotoNaskhArabic-Regular.ttf");
CssAppliers cssAppliers = new CssAppliersImpl(fontProvider);
HtmlPipelineContext htmlContext = new HtmlPipelineContext(cssAppliers);
htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
// Pipelines
PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);
// XML Worker
XMLWorker worker = new XMLWorker(css, true);
XMLParser p = new XMLParser(worker);
p.parse(new FileInputStream(HTML), Charset.forName("UTF-8"));;
// step 5
document.close();
}
这个例子中需要的代码较少,当你想改变布局时,改变HTML就足够了。您无需更改 Java 代码。
再举一个例子:在 ParseHtml9 中,我创建了一个 table,其中一列是英文名称 ("Lawrence of Arabia"),另一列是阿拉伯语翻译 ("لورانس العرب ”)。因为我需要英文和阿拉伯文的不同字体,所以我在<td>
级别定义了字体:
<table>
<tr>
<td>Lawrence of Arabia</td>
<td dir="rtl" style="font-family: Noto Naskh Arabic">لورانس العرب</td>
</tr>
</table>
第一列使用默认字体,从左到右书写,无需特殊设置。对于第二列,我定义了一种阿拉伯字体并将 运行 方向设置为 "rtl"
.
结果如下所示:
这比您在代码中尝试做的要容易得多。