Csdn作为开发者的专业社区,是最流行的技术学习总结和分享交流的平台。但有时候我们也需要将学习总结的技术做线下分享,因此我们可能希望将我们Csdn的博客,保存成线下文档。本文将介绍如何基于iText实现将Csdn的博客保存为PDF文档保存。
iText简介
iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。
获取博客内容
本文介绍基于Android平台的转换方法,其他运行Java的平台,包括Kotlin Multiplatform、JavaWeb等都可以使用该方法。
使用Okhttp3框架访问博客链接,并获取博客内容并将结果转换成InputStream。
val serverResponse =
OkHttpReqKit.requestServer("$ASSISTANT_SERVER$ASSISTANT_CONVERT_PATH?url=$url") // 借助服务器将内容将博客内容格式化
val result = serverResponse.body?.string()?.let {
JSON.parse(it)
}
val serverUri =
(result as? JSONObject)?.takeIf { it.getString("state") == "ok" }?.getString("path")
?: return null
val response = OkHttpReqKit.requestServer("$ASSISTANT_SERVER$serverUri")
val inputStream = response.toString().byteInputStream(charset = Charset.forName("utf-8") // 将博客内容转成inputStream备用
生成PDF文档
将iText功能封装成一个工具类HtmlToPdfKit。
object HtmlToPdfKit {
fun convertToPdf(
inputStream: InputStream?,
waterMark: String?,
outputStream: OutputStream?
) {
inputStream ?: return
val defaultFontProvider = DefaultFontProvider(false, false, true, "仿宋")
val properties = ConverterProperties().also {
it.setFontProvider(defaultFontProvider)
}
val pdfWriter = PdfWriter(outputStream)
val pdfDocument = PdfDocument(pdfWriter).also {
// 设置为A4大小
it.defaultPageSize = PageSize.A4
waterMark?.let { wm ->
it.addEventHandler(PdfDocumentEvent.INSERT_PAGE, WaterMarkEventHandler(wm)) // 添加水印
}
it.addEventHandler(PdfDocumentEvent.END_PAGE, PageEventHandler()) // 添加页脚
}
readInputStream(inputStream)?.use {
HtmlConverter.convertToPdf(it, pdfDocument, properties)
pdfWriter.close()
pdfDocument.close()
} ?: LogTool.e("转换失败")
}
private fun readInputStream(inputStream: InputStream): InputStream? = try {
var content: String?
ByteArrayOutputStream().use {
var len: Int
val buffer = ByteArray(1024)
while ((inputStream.read(buffer).also { len = it }) != -1) {
it.write(buffer, 0, len)
}
content = it.toString()
}
content?.let {
val regexSpecial = "&[a-zA-Z]{1,10};"
val replaceAll: String =
Pattern.compile(regexSpecial, Pattern.CASE_INSENSITIVE).matcher(it).replaceAll("")
.also { res ->
LogTool.i("replaced str = $res")
}
getStringStream(replaceAll)
}
} catch (e: Exception) {
LogTool.e("错误信息:${e.message}")
null
}
private fun getStringStream(string: String?): InputStream? = try {
string?.takeIf { it.trim().isNotEmpty() }?.toByteArray()?.let {
ByteArrayInputStream(it)
}
} catch (ioe: IOException) {
LogTool.e("错误信息:${ioe.message}")
null
}
}
在iText
的PdfDocument
中,可以通过添加EventHandler
来处理页眉、页脚、水印等。
class PageEventHandler : IEventHandler {
override fun handleEvent(event: Event) {
val documentEvent = event as PdfDocumentEvent
val document = documentEvent.document
val page = documentEvent.page
val pageSize: Rectangle = page.pageSize
val pdfFont: PdfFont? = try {
PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false)
} catch (e: IOException) {
e.printStackTrace()
null
}
val pdfCanvas = PdfCanvas(page.lastContentStream, page.resources, document)
val canvas = Canvas(pdfCanvas, pageSize)
val x: Float = (pageSize.left + pageSize.right) / 2
val y: Float = pageSize.bottom + 15
val paragraph: Paragraph =
Paragraph("第" + document.getPageNumber(page) + "页/共" + document.numberOfPages + "页")
.setFontSize(10f)
.setFont(pdfFont)
canvas.showTextAligned(paragraph, x, y, TextAlignment.CENTER)
canvas.close()
}
}
class WaterMarkEventHandler @JvmOverloads constructor(
// 水印内容
private val waterMarkContent: String,
//一页中有几列水印
private val waterMarkX: Int = 5,
// 一页中每列有多少水印
private val waterMarkY: Int = 5
) :
IEventHandler {
override fun handleEvent(event: Event) {
val documentEvent = event as PdfDocumentEvent
val document = documentEvent.document
val page = documentEvent.page
val pageSize: Rectangle = page.pageSize
val pdfFont: PdfFont? = try {
PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H", false)
} catch (e: IOException) {
e.printStackTrace()
null
}
val pdfCanvas = PdfCanvas(page.newContentStreamAfter(), page.resources, document)
val canvas: Canvas = Canvas(pdfCanvas, pageSize).also {
it.setFontColor(WebColors.getRGBColor("lightgray"))
it.setFontSize(16f)
it.setFont(pdfFont)
}
val waterMark: Paragraph = Paragraph(waterMarkContent).setOpacity(0.5f)
for (i in 0 until waterMarkX) {
for (j in 0 until waterMarkY) {
canvas.showTextAligned(
waterMark,
(150 + i * 300f),
(160 + j * 150f),
document.numberOfPages,
TextAlignment.CENTER,
VerticalAlignment.BOTTOM,
120f
)
}
}
canvas.close()
}
}
再将Html内容的inputStream传入工具类,即可将内容转换成PDF保存。
直接使用工具
但如果你不想自己实现该功能,也可以关注公众号”梦想周游世界的猿同学“,或扫码关注。
关注公众号后,如需要将这篇博客Gerrit使用和配置转成PDF保存,只需给公众号发送如下消息:
发送消息后稍作等待,按提示发送”我的文档“,即可获取pdf文档下载链接。
接下来,我们只需点击文档地址链接,即可下载pdf文档。
浏览器提示已阻止不安全的下载时,选择保留。 下载完成后,点击文档打开。