iText7高级教程之html2pdf——3.基于媒体查询生成PDF
Posted CuteXiaoKe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iText7高级教程之html2pdf——3.基于媒体查询生成PDF相关的知识,希望对你有一定的参考价值。
作者:CuteXiaoKe
微信公众号:CuteXiaoKe
1. 检阅样例html文件
如图3.1显示了一个HTML页面,该页面介绍了德克萨斯州(Texas)奥斯汀市(Austin)每年举行的一次活动——“South by South West(SXSW)”。此页面分布在三个浏览器窗口上;sxsw.html和sxsw.css是用于创建此页面的源文件。这个HTML页面的布局灵感来自于w3schools.com学校提供的一种设计。
我们使用基本的convertToPdf()/ConvertToPdf()
方法将此页面转换为PDF:
public void createPdf(String html, String dest) throws IOException
HtmlConverter.convertToPdf(html, new FileOutputStream(dest));
我们比较一下图3.2和图3.1,我们发现HTML转换成PDF转换的很成功。
本页面的颜色在CSS文件里面定义,代码片段如下:
h2
color:#F9C227;
margin: 0px;
.header
background-color: #F9C227;
color: #FFFFFF;
margin-bottom:10px;
这个CSS代码段定义了标题块的颜色和一些指标,并在html代码中使用:
<h2 class="header">SXSW Conference and festivals</h2>
当我们在屏幕上查看页面时,这些颜色很好,但是当我们打印页面时,可能希望避免使用太多颜色。打印机很便宜;但墨盒很贵。如果有一种方法可以在打印文档时节省彩色墨水,该怎么办?
2. 创建用于打印的PDF文件
sxsw.html的<head>
部分有点奇怪。它包含指向两个不同CSS文件的链接:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="css/sxsw.css"/>
<link rel="stylesheet" media="print only" href="css/sxsw_print.css">
</head>
当我们在屏幕上的浏览器中查看HTML页面时,使用sxsw.css样式表。然而,还有第二个样式表:sxsw.css,并且引用此样式表的标记具有一个media
属性,其值为“print-only”
。这是打印HTML页面时将使用的样式表,该样式表中的颜色值与我们以前的颜色值略有不同。
例如,h2
标签和header
类的css 代码如下所示:
h2
color:#FFFFFF;
margin: 0px;
font-size:18pt
.header
background-color: #000000;
color: #FFFFFF;
margin-bottom:10px;
sxsw.css中使用的颜色改为黑色(#000000)和白色(#FFFFFF)。
默认情况下,pdfHTML插件假定您要创建一个将在屏幕上查看的PDF,但如果您打算创建一个用于打印的PDF文档,则可以通过转换器属性将“打印”定义为媒体设备描述。代码如下:
public void createPdf(String baseUri, String src, String dest) throws IOException
ConverterProperties properties = new ConverterProperties();
properties.setBaseUri(baseUri);
MediaDeviceDescription mediaDeviceDescription =
new MediaDeviceDescription(MediaType.PRINT);
properties.setMediaDeviceDescription(mediaDeviceDescription);
HtmlConverter.convertToPdf(
new FileInputStream(src), new FileOutputStream(dest), properties);
在本例中,我们创建了一个MediaDeviceDescription
,其中PRINT
作为MediaType
,我们使用这个MediaDeviceDescription
来设置转换过程的配置属性ConverterProperties
。生成的PDF文档如图3.3所示:
请注意,本文档的打印布局与图3.2所示的PDF布局略有不同。这也是由样式表中的差异引起的。
如果我们仔细看看sxsw.css文件,我们看到它包含不同的部分:根据屏幕的宽调整内容。sxsw.html页面是一个响应性网页,它可以根据屏幕大小进行调整。
3. HTML中的响应
在图3.1中,屏幕相当窄,这是智能手机上的常见情况。在这种情况下,以下(默认)CSS定义用于p
、h1
和h2
:
@media only screen
p
font-size: 24pt;
h1
font-size: 36pt;
h2
font-size:28pt
在上一章中,我们已经使用@page
规则创建了页脚。在本例中,我们使用@media
来定义一些仅在页面显示在彩色屏幕上时使用的样式。
但是,CSS文件中有多个@media
规则。我们还有:
@media only screen and (min-width: 600px)
.col-1-m width: 24.9%;
.col-2-m width: 49.9%;
.col-3-m width:74%
.col-4-m width: 99%;
p
font-size: 16pt;
h1
font-size: 24pt;
h2
font-size:18pt
当我们调整浏览器窗口的大小,使其最小宽度为600像素时,如图3.4所示,我们得到了不同的布局。
字体大小较小,但也有定义不同元素宽度的类。如果我们查看HTML文件,我们会发现其中一些类用于页面的上部:
<div class="one col-1-m col-1 menu">
<ul>
<li>Interactive</li>
<li>Film</li>
<li>Music</li>
<li>Comedy</li>
</ul>
</div>
<div class="two col-3-m col-3 ">
<h1>South by Southwest</h1>
<p>The South by Southwest® (SXSW®) Conference &
Festivals in Austin (TX) celebrate the convergence of the interactive,
film, and music industries. Fostering creative and professional growth
alike, SXSW® is the premier destination for discovery.</p>
</div>
<div class="three col-4-m col-1">
<div class="aside">
<h2>Conference</h2>
<p>The SXSW conference is an amazing conglomeration of
people, places, brands, music, tech and film.</p>
<h2>Festivals</h2>
<p>The different SXSW festivals take place for 10 days in
mid-March Every year, with SXSW Interactive lasting for 5 days,
Music for 6 days, and Film & Comedy running concurrently
for 9 days.</p>
<h2>Exhibitions</h2>
<p>Trade Show, Flatstock, SXSW Marketplace, Job Market,
SXSW Create, Gaming Expo, Southbites Trailer Park, Spotlights.</p>
</div>
</div>
我们区分了三个<div>
元素,它们可以呈现为三列(one
、two
和three
)。
当我们查看图3.4时,我们看到列one
和列two
彼此相邻,而列three
显示在这两列下面。这种行为是由于在CSS文件中定义类col-1-m
、col-3-m
和col-4-m
的方式造成的。列one
的宽度为24.9%(col-1-m
),列two
的宽度是74%(col-3-m
)。24.9%+74%=98.9%,这意味着有足够的空间将两列并排放置,但列three
没有剩余空间。该列需要总宽度的99%(col-4-m
)。
当页面以600像素的最小宽度显示时,使用col-1-m
、col-2-m
和col-4-m
类,但当宽度达到768像素时,这些类的值将被覆盖,代码如下:
@media only screen and (min-width: 768px )
.col-1 width:24.9%;
.col-2 width: 33.32%;
.col-3 width: 49%;
.col-4 width: 99%;
p
font-size: 12pt;
h1
font-size: 20pt;
h2
font-size:16pt
我们有相同的三列,列one
的宽度仍为24.9%(col-1
),但列two
的宽度现在为49%(col-3
),第三列的宽度已降至24.9%(col-1
)。如果我们将所有这些百分比加起来,总宽度小于100%,这意味着三列之间非常接近,如图3.5所示。
使用@media
使网页响应是HTML的标准做法,但PDF呢?
HTML和PDF在概念上有很大的区别:
- HTML旨在传达更高级别的信息,如段落和表格。尽管有一些方法可以控制它们的布局,但最终还是要由浏览器来绘制这些更高级的概念。想象一下,一个HTML文档的段落宽度为100%。根据浏览器窗口的宽度,这段文字可能需要2行或10行,打印时可能需要7行,在手机上查看时可能需要20行。
- PDF旨在传达文档,这些文档无论在哪里呈现都必须保持相同。PDF文件必须独立于渲染设备。无论屏幕大小如何,它必须始终在同一布局中呈现完全相同的内容。由于这些必要性,PDF创建过程通常不支持诸如“表格”或“段落”之类的抽象事物(请记住这句话中的通常情况;我们将在本章最后一节讨论带标签PDF时进一步讨论这一点)。基本PDF语法支持三种基本内容:文本、线条/形状和图像。在PDF中,你不会说 “有一段落,PDF查看器:做你的事!” 相反,你会说:“用这个精确的字体在这个精确的X,Y位置绘制这个文本,别担心,我之前已经计算过文本的宽度,所以我知道它会适合这一行。” 你也不会说 “这是一张表格!” 相反,你说:“在这个精确的位置画这个文本,然后在我之前计算过的另一个精确位置画一个矩形,这样我就知道它会出现在文本周围。”
iText的布局引擎完成了计算一个段落在一个页面上适合多少行、如何在一个或多个页面上分配表格的单元格以及在哪里绘制文本和这些单元格的边框等所有困难的工作;pdfHTML将HTML标签转换为iText可以理解的指令,一旦创建了PDF,该布局就被固定了。更改PDF查看器窗口的大小不会像调整浏览器窗口大小时更改HTML页面布局的方式那样更改布局。
有计划开发一种新格式,目前代号为“下一代PDF”,它可以将同一文档的不同视图绑定到一个文件中。然后,观众将根据媒体查询显示一个特定视图。例如:下一代PDF文件可以包含图3.2所示的文件以及图3.3所示的。当在屏幕上的PDF查看器中打开时,您会看到图3.2中所示的内容。当打印文件时,您将看到图3.3中所示内容。
通过pdfHTML,我们正在积极准备支持这种新的文档格式。在接下来的三个示例中,我们将把同一个SXSW HTML页面转换为三个PDF,一个最适合桌面,一个适合平板电脑,一个可以在智能手机上轻松查看。在下一代PDF文档中,同一文档的这三个不同版本可以绑定到一个文件中。
4. 为桌面设备创建PDF
假设我们想使用CSS创建PDF,该CSS是使用@media
规则,定义了only screen and (min-width: 768px )
。在这种情况下,我们需要使用MediaType.SCREEN
而不是MediaType.PRINT
,我们需要至少768px的页面宽度。
在下面的代码中,我们将HTML转换为PDF文件,其中页面为横向A4。这样的页面大小842 x 595个用户单位。
public void createPdf(String baseUri, String src, String dest) throws IOException
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = PageSize.A4.rotate();
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
properties.setBaseUri(baseUri);
MediaDeviceDescription mediaDeviceDescription
= new MediaDeviceDescription(MediaType.SCREEN);
mediaDeviceDescription.setWidth(pageSize.getWidth());
properties.setMediaDeviceDescription(mediaDeviceDescription);
HtmlConverter.convertToPdf(new FileInputStream(src), pdf, properties);
我们在第5行和第6行定义页面大小。在第9行和第10行创建了MediaDeviceDescription
,并将类型设置为MediaType.SCREEN
。然后在第11行将MediaDeviceDescription
的宽度设置为页面宽度。
5. pt和px之间的关系是什么?
在我们的CSS中,页面大小以像素表示:600px和768px。一英寸有96个像素。在PDF中,测量单位以用户单位表示。默认情况下,一个用户单位对应一个点。一英寸有72个点。参见itext常见问题解答条目——HTML中的测量系统如何与PDF中的测量体系相关?以获得更深入的答案。
在我们示例的CSS文件中定义的边界上下文中,我们可以计算以下宽度:
- 600px(像素)=6.25inch(英寸)=450pt(磅)
- 768px(像素)=8inch(英寸)=576pt(磅)
在定义页面大小时,我们要使用450pt和579pt的值。
如图3.6所展示的是使用宽度大于576pt的媒体查询将SXSW HTML页面转换为PDF时生成的PDF。
这个PDF看起来非常类似于图3.5所示的HTML页面的“宽视图”。
现在让我们创建一个页面宽度较小的PDF。
6. 为平板电脑创建PDF
在下面的代码中,我们定义的自定义页面大小小于576,但大于450。
public void createPdf(String baseUri, String src, String dest) throws IOException
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = new PageSize(575, 1500);
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
properties.setBaseUri(baseUri);
MediaDeviceDescription mediaDeviceDescription
= new MediaDeviceDescription(MediaType.SCREEN);
mediaDeviceDescription.setWidth(pageSize.getWidth());
properties.setMediaDeviceDescription(mediaDeviceDescription);
HtmlConverter.convertToPdf(new FileInputStream(src), pdf, properties);
这个代码段与前一个代码段之间的唯一区别可以在第5行找到。我们现在使用575pt×1500pt的自定义页面大小,而不是横向的A4页面。
如图3.7所示的最终PDF看起来非常像我们在图3.4中看到的HTML。
最后,让我们再次更改页面大小,现在宽度小于450pt。
7. 为智能手机创建PDF
让我们创建一个页面大小为440*2000用户单位的PDF。代码如下所示:
public void createPdf(String baseUri, String src, String dest) throws IOException
PdfWriter writer = new PdfWriter(dest);
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = new PageSize(440, 2000);
pdf.setDefaultPageSize(pageSize);
ConverterProperties properties = new ConverterProperties();
properties.setBaseUri(baseUri);
MediaDeviceDescription mediaDeviceDescription
= new MediaDeviceDescription(MediaType.SCREEN);
mediaDeviceDescription.setWidth(pageSize.getWidth());
properties.setMediaDeviceDescription(mediaDeviceDescription);
HtmlConverter.convertToPdf(new FileInputStream(src), pdf, properties);
现在,我们在两个长而窄的PDF页面上看到了所有内容,字体大小远远大于于以前。
这种PDF在智能手机上比在横向A4页面上呈现的PDF更容易使用。
当查看最后三个示例的代码时,你可能会好奇一行奇怪的代码:在第4行,我们告诉iText创建一个带标签的PDF。
由于这一额外的代码行,iText还将把文档的语义结构添加到PDF中。
8.文档的语义结构
之前我们已经解释了HTML和PDF之间存在巨大的概念差异。在HTML中,可以有标题(<h1>
、<h2>
、…)、段落(<p>
)、列表和列表项(<ul>
、<ol>
、<li>
)等结构。浏览器会查看这些结构并实时呈现页面。
创建PDF文件时,pdfHTML会解释这些结构,将其转换为iText对象,然后iText通过在画布上的绝对位置绘制文本、线条和形状将这些对象渲染到页面上。如果不使用setTagged()
/setTagged()
方法,所有结构都将在该过程中丢失。PDF页面上的一行文本不知道它是章节的标题,是段落的一部分,还是列表项。这些信息不见了。一页上只有一堆文字。
然而,当你使用setTagged()
/setTagged()
方法时,会向PDF中添加一个结构树。在图3.9中,我们在Adobe Acrobat中看到了该结构树的一部分。你可以识别<Div>
、<P>
、<L>
和<LI>
元素。
在第1章的图1.5中,我们还发现了<Span>
和<figure>
等元素。这些是PDF文档的标签,存储在PDF文件的单独结构树中。该结构树指的是不同页面上的标记内容。当辅助技术(Assistive Technology,AT)使用PDF时,此结构非常重要。当这种结构存在时,盲人或视障人士可以读懂文档。
辅助技术是一个总称,包括残疾人的辅助、适应和康复设备,还包括选择、定位和使用这些设备的过程。辅助技术通过增强或改变完成这些任务所需的技术的交互方法,使人们能够完成以前无法完成或难以完成的任务,从而促进了更大的自身独立性。
带标签的PDF在下一代PDF中也很重要。下一代PDF规范将定义一种派生算法(derivation algorithm ),允许PDF处理器将PDF文档转换为响应HTML文件。通过提供创建正确标签的PDF的功能,我们准备在规范完成后支持下一代PDF。一旦实现,我们就可以将HTML转换为PDF,然后再转换回来!
我们将在下一章讨论PDF/A时重温带标签的PDF。
9. 总结
在本章中,我们创建了一个响应的HTML文件,并通过定义不同的媒体查询将该文件转换为不同的PDF文档。我们创建了一个PDF,它使用了一个专门为打印HTML文件而编写的CSS文件。我们还使用了媒体查询,使内容适应屏幕宽度。在下一章中,我们将了解使用pdfHTML作为报告引擎时的一些示例和最佳实践。
本章代码资源下载地址:
- 关注我的微信公众号CuteXiaoKe,点击代码资源-iText官网代码即可
- 或者直接点击微信文章
以上是关于iText7高级教程之html2pdf——3.基于媒体查询生成PDF的主要内容,如果未能解决你的问题,请参考以下文章
iText7高级教程之html2pdf——2.使用CSS定义样式
iText7高级教程之html2pdf——2.使用CSS定义样式
iText7高级教程之html2pdf——2.使用CSS定义样式