提问人:eugeneagyeman 提问时间:11/7/2023 最后编辑:eugeneagyeman 更新时间:11/7/2023 访问量:34
如何有效地使用 PDFBox 3.0 标记 PDF 文档是否符合可访问性,并确保其符合 WCAG 和 PDF/UA 标准?
How can I effectively use PDFBox 3.0 to tag a PDF document for accessibility compliance and ensure it meets WCAG and PDF/UA standards?
问:
我目前正在做一个项目,我需要让残障人士可以访问 PDF 文档。我正在使用 PDFBox 3.0 创建这些 PDF,但我在正确标记 PDF 以满足可访问性合规性方面面临挑战,我已经做了其他所有事情来使它们符合要求(XMP 元数据、标记字典等)。
我按照已经发布在堆栈溢出上的这两个指南进行操作,但没有结果。我预计这些标签将反映在文档中,但是在Acrobat中测试时,仅反映逻辑结构下的顶级标签
这是目前的代码,请注意,为了保持简洁,我已经剥离了一些布局代码。
object GeneratorImpl {
protected class PDFContext {
val document: PDDocument = new PDDocument()
var page: PDPage = new PDPage(PDRectangle.A4)
page.getCOSObject.setItem(COSName.STRUCT_PARENTS, COSInteger.get(0))
var contentStream: PDPageContentStream = new PDPageContentStream(document, page)
var currentY: Float = 0
val MARGIN = 50f
var unsupportedCharsFound = false
page.setMediaBox(PDRectangle.A4)
document.addPage(page)
//Set Document Language
document.getDocumentCatalog.setLanguage("en")
document.getDocument.setVersion(1.7F)
//Set Document Viewer Preferences and add metadata
document.getDocumentCatalog.setViewerPreferences(new PDViewerPreferences(new COSDictionary()));
document.getDocumentCatalog.getViewerPreferences.setDisplayDocTitle(true);
//Mark the PDF as Tagged
private val markInfo = new PDMarkInfo()
markInfo.setMarked(true)
document.getDocumentCatalog.setMarkInfo(markInfo)
addXMPMetadata(document)
val structureTreeRoot = new PDStructureTreeRoot()
document.getDocumentCatalog.setStructureTreeRoot(structureTreeRoot)
val root = new PDStructureElement(StandardStructureTypes.DOCUMENT, structureTreeRoot)
val dictionary = new COSDictionary()
dictionary.setItem(COSName.STRUCT_PARENTS, COSInteger.get(0))
var pageStructureElement = new PDStructureElement(dictionary)
root.appendKid(pageStructureElement)
var currentMCID = 0
var currentMarkedDictionary = new COSDictionary()
currentMarkedDictionary.setInt(COSName.MCID, currentMCID)
var nums = new COSArray()
var numDictionaries = new COSArray()
}
def generateCertificatePDF(fields: Seq[Field]): Unit = {
var pdfContext = createContext()
if (fields.isEmpty) throw NonRetryableException("No fields supplied to PDF generator")
for (field <- fields) {
field match {
case header: Header => {
pdfContext = addTitle(header.contents, pdfContext.ROBOTO_BOLD, 20)(pdfContext)
}
// Stripped out the other cases for simplicity
case _ => throw NonRetryableException("Unknown field type in list. Cannot generate PDF without matching generation logic.")
}
}
pdfContext.contentStream.close()
addParentTree(pdfContext)
pdfContext.document.save(new File("./test.pdf"))
val outputStream = new ByteArrayOutputStream()
pdfContext.document.save(outputStream)
outputStream.close()
pdfContext.document.close()
}
private def addTitle(text: String, font: PDType0Font, fontSize: Float)(implicit context: PDFContext): PDFContext = {
if (text.nonEmpty) {
setDocumentTitle(text)
addContentToParent(COSName.P, StandardStructureTypes.H, context.page, context.pageStructureElement)
addCenteredTextBlock(text.toUpperCase, fontSize, font)
}
context
}
private def addCenteredTextBlock(text: String, fontSize: Float, font: PDType0Font)(implicit context: PDFContext): PDFContext = {
var localContext = context
localContext.contentStream.setFont(font, fontSize)
// 2 * context.MARGIN for left and right margin * 2 for extra padding
val maxCentreWidth = localContext.page.getMediaBox.getWidth - (2 * localContext.MARGIN * 2)
localContext.currentY = localContext.page.getMediaBox.getHeight - (2 * localContext.MARGIN)
val startX = (localContext.page.getMediaBox.getWidth - maxCentreWidth) / 2f
val startY = localContext.currentY
var currentY = startY
val lines = wrapText(text, font, fontSize, maxCentreWidth)
for (line <- lines) {
if (localContext.currentY - fontSize <= localContext.MARGIN) {
localContext = addPage(fontSize, font)
}
val textWidth = fontSize * font.getStringWidth(line) / 1000F
val currentX = startX + (maxCentreWidth - textWidth) / 2f
localContext.contentStream.beginText()
localContext.contentStream.newLineAtOffset(currentX, currentY)
localContext.contentStream.beginMarkedContent(COSName.P, PDPropertyList.create(context.currentMarkedDictionary))
localContext.contentStream.showText(line)
localContext.contentStream.endMarkedContent()
setNextMarkedDictionary
if (line.contains("\n") || line.contains("\\n") || line.equals("")) {
currentY -= fontSize
localContext.contentStream.beginMarkedContent(COSName.ARTIFACT, PDPropertyList.create(context.currentMarkedDictionary))
localContext.contentStream.newLineAtOffset(currentX, currentY)
localContext.contentStream.endMarkedContent()
setNextMarkedDictionary
}
localContext.contentStream.endText()
currentY -= fontSize
localContext.currentY = currentY
}
localContext
}
private def setNextMarkedDictionary(implicit context: PDFContext): Unit = {
context.currentMCID += 1
context.currentMarkedDictionary = new COSDictionary()
context.currentMarkedDictionary.setInt(COSName.MCID, context.currentMCID)
}
//Add a structure element to a parent structure element with optional marked content given a non-null name param.//Add a structure element to a parent structure element with optional marked content given a non-null name param.
private def addContentToParent(name: COSName, structureType: String, currentPage: PDPage, parent: PDStructureElement)(implicit context: PDFContext) = {
//Create a structure element and add it to the current section.
var structureElement: PDStructureElement = null
if (structureType != null) {
structureElement = new PDStructureElement(structureType, parent)
structureElement.setPage(currentPage)
}
//If COSName is not null then there is marked content.
if (name != null) {
//numDict for parent tree
val numDict = new COSDictionary()
numDict.setInt(COSName.K, context.currentMCID)
numDict.setItem(COSName.PG, currentPage.getCOSObject)
if (structureElement != null) {
if (!COSName.ARTIFACT.equals(name)) structureElement.appendKid(new PDMarkedContent(name, context.currentMarkedDictionary))
else structureElement.appendKid(new PDArtifactMarkedContent(context.currentMarkedDictionary))
numDict.setItem(COSName.P, structureElement.getCOSObject)
}
else {
if (!COSName.ARTIFACT.equals(name)) parent.appendKid(new PDMarkedContent(name, context.currentMarkedDictionary))
else parent.appendKid(new PDArtifactMarkedContent(context.currentMarkedDictionary))
numDict.setItem(COSName.P, parent.getCOSObject)
}
numDict.setName(COSName.S, name.getName)
context.numDictionaries.add(numDict)
}
if (structureElement != null) parent.appendKid(structureElement)
structureElement
}
//Adds the parent tree to root struct element to identify tagged content//Adds the parent tree to root struct element to identify tagged content
private def addParentTree(implicit context: PDFContext): Unit = {
val dict = new COSDictionary()
context.nums.add(context.numDictionaries)
var i = 1
while (i < 1) {
context.nums.add(COSInteger.get(i))
i += 1
}
dict.setItem(COSName.NUMS, context.nums)
val numberTreeNode = new PDNumberTreeNode(dict, dict.getClass)
context.document.getDocumentCatalog.getStructureTreeRoot.setParentTree(numberTreeNode)
context.document.getDocumentCatalog.getStructureTreeRoot.appendKid(context.root)
}
}
答: 暂无答案
评论