65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Aspose.PDF for .NET 实现文档可访问性

2019年12月9日

CPOL
viewsIcon

6677

在本文中,我们将为您提供有关法律、标准和技术的一些关键信息;向您展示如何将 Aspose.PDF for .NET 集成到应用程序中;以及如何利用它来创建可访问的 PDF 文档。

数字出版可以增加信息的可访问性,但如果疏忽大意,也可能成为障碍。例如,如果我们发布了一个仅包含图像的 PDF(因为它使用光学字符识别 (OCR) 扫描),屏幕阅读器将无法读取文本。

为残障人士提供信息访问是许多关于数字出版物可访问性法律和标准背后的主要驱动力之一,这些法律和标准可以帮助我们创建可访问的文档。

然而,其益处远不止于符合法律规定。例如:

  • 扫描的 PDF 不支持“重排”,否则可以以单列方式呈现内容,以便在智能手机上更轻松地查看。
  • 纯图像 PDF 对搜索引擎不可见,而搜索引擎需要读取文本。
  • 无法从纯图像 PDF 复制和粘贴文本。

有许多来源管理着出版物可访问性——并经常为创建它们提供有用的指导。最常参考的法律是:

  • 《康复法》第 508 条(美国)
  • 《欧洲无障碍法》(欧洲)
  • 《联合国残疾人权利公约》第 9 条(国际)

上述法律定义通常依赖于其他标准来判断可访问性。这些标准包括:

  • 万维网联盟 Web 内容可访问性指南 (W3C WCAG)
  • PDF/UA,即国际标准 ISO 14289 的易记名称

有趣的事实:PDF/UA 是第一个以符合 PDF/UA 的格式发布的 ISO 标准!

然而,如果您不习惯创建可访问的 PDF,这可能会显得令人生畏。

在本文中,我们将为您提供有关法律、标准和技术的一些关键信息;向您展示如何将 Aspose.PDF for .NET 集成到应用程序中;以及如何利用它来创建可访问的 PDF 文档。

可访问 PDF 文档的结构

可访问 PDF 有许多方面。可访问 PDF 的主要要求是其已标记。

与 HTML 类似,标签有助于表示文档的结构。HTML 和 PDF 最大的区别在于,在 PDF 中,这种逻辑结构是与内容分开存储的,因此顺序独立于显示的内容。屏幕阅读器等应用程序需要此结构层次(或结构树)来了解遍历和读取文档的顺序。

我们需要标记什么?

  • 页面上的所有标准内容
  • 注解
  • 多媒体对象
  • 表单字段
  • 表格
  • 列表

这包括在适当的情况下向标签添加实际文本、替代文本、扩展文本或不同语言。

使用 Aspose.PDF for .NET 

Aspose.PDF 是一系列用于创建、编辑、转换和查看 PDF 文档的开发产品。在本教程中,我们将使用 Aspose.PDF for .NET 在 C# 应用程序中演示一些可访问 PDF 的创建技术。

Aspose.PDF for .NET 捆绑在一个 NuGet 包中,您可以将其添加到任何新的或现有的 .NET Framework 2.0 至 4.7.2 和 .NET Standard 2.0、Core 2.0 和 Core 2.1 项目中。(有关更多信息,请参阅完整的系统要求列表)。

在 Visual Studio 中,通过点击“工具”>“NuGet 包管理器”>“程序包管理器控制台”在包管理器中安装 Aspose.PDF。

在包管理器中,执行以下命令:

Install-Package Aspose.PDF

您也可以通过右键单击项目并选择“管理 NuGet 包”来通过 GUI 安装该程序包。

通过代码创建新的 PDF

这是一个在代码中生成 PDF 的非常简单的示例,它添加了一个页面、一个页眉和一些文本。Aspose.PDF .NET 能够构建更广泛的组件,包括表单、图表、图像、链接、水印、表格、书签、注释、附件以及数字签名等安全功能。

在下面的代码中,我们执行了以下步骤。如您所见,语法非常简单。

  1. 初始化一个 Document 对象。
  2. 添加一页。
  3. 创建标题和文本元素。
  4. 按所需顺序将元素添加到页面段落集合中。
  5. 保存创建的 PDF。
Document document = new Document();
      Page page = document.Pages.Add();

      Heading h1 = new Heading(1);
      h1.Text = "Heading 1";
      h1.TextState.FontSize = 20;
      TextFragment text = new TextFragment("Some text here...");
      text.TextState.LineSpacing = 15f;

      page.Paragraphs.Add(h1);
      page.Paragraphs.Add(text);

      document.Save(@"CreateExample.pdf");

此代码创建以下 PDF 输出:

只需两行代码,我们就可以将 markdown 文件转换为 PDF。

  1. 使用 MD 选项将 markdown 文件加载到新的 Document 对象中。
  2. 另存为 PDF。
var doc = new Document("sample.md", , new MdLoadOptions());
            doc.Save("MarkdownToPDF.pdf");

我们也可以将 PDF 转换为另一种文档格式。同样,只需要两行代码即可将 PDF 转换为 Microsoft Word DOCX 格式。

  1. 将 PDF 文件加载到新的 Document 对象中。
  2. 使用 SaveFormat.DocX 保存。
            Document pdfDocument = new Document("PDFToDOCX.pdf");
            pdfDocument.Save("PDFToDOCX_out.docx", SaveFormat.DocX);

可以通过 DocSaveOptions 对象进一步控制转换。

  1. 将 PDF 文件加载到新的 Document 对象中。
  2. 初始化一个新的 DocSaveOptions 对象。
  3. 将输出格式指定为 DOCX。
  4. 启用项目符号识别。
  5. 切换到 Flow 模式(该模式优先考虑可编辑性而非保留外观)。
Document pdfDocument = new Document("PDFToDOC.pdf");
            var saveOptions = new DocSaveOptions
{
    Format = DocSaveOptions.DocFormat.DocX,
    RecognizeBullets = true,
    Mode = DocSaveOptions.RecognitionMode.Flow
};

            pdfDocument.Save("ConvertToDOCX_out.docx", saveOptions);

文档显示了 Aspose.PDF 可以处理的所有格式,包括导入和导出。

Aspose.PDF for .NET 如何帮助提高可访问性?

PDF 通常在字处理器等应用程序中手动创建,然后导出为 PDF。可访问性设计应始终从创建开始。这种方法减少了测试和修订 PDF 以获得可访问结果所需的工作量。

但是,如果您没有原始介质或希望在代码中创建新的可访问 PDF(也许是因为您需要按需创建定制化的 PDF),那么 Aspose.PDF .NET 可以通过其标记的 PDF 文档功能提供帮助。

Aspose.PDF NET 包含一个函数库,用于创建符合 PDF/UA 要求的可访问文档,包括逻辑结构树、元数据和附加描述性文本(替代、扩展和实际)。

它还可以用于通过单条命令验证合规性。

using (var doc = new Document("input.pdf"))
     {
         bool isValid = doc.Validate("validation-log.xml", Aspose.Pdf.PdfFormat.PDF_UA_1);
     }

让我们来看一个示例场景:为一批文档实现 PDF/UA 合规性自动化。

Aspose.PDF .NET 能够从各种格式导入,创建标记的 PDF 文档,并验证 PDF/UA 合规性,从而使我们能够轻松地自动执行批量修复。

在此示例中,我们:

  1. 创建一个不符合 PDF/UA 标准的普通 PDF。
    我们编写了一些辅助方法来创建此文件(使用 Aspose.PDF .NET)。为简洁起见,我们将其从本文档中省略了,但您可以在 GitHub 上的完整代码示例中找到它们。
  2. 对该 PDF 运行验证器并考虑结果。
  3. 再次在代码中打开 PDF 并提取内容。
  4. 创建一个新的标记 PDF 文档并添加元数据。
  5. 将从原始 PDF 中提取的内容插入到逻辑结构中。

即使在这个非常简单的 PDF 中,也有相当多的错误和一个警告(如果我们有更广泛的组件类型,我们预计会看到更多错误)。

  • 文档未标记为已标记
  • 文档未识别为符合 PDF/UA 标准
  • 图像和文本未标记
  • 元数据中缺少标题,因此也未显示
  • 元数据中缺少语言
        <General>
            <Problem Severity="Warning" Clause="7.1" ObjectID="" Page="" Convertable="True" Code="7.1:7.1(12.2)">'ViewerPreferences' dictionary missing</Problem>
            <Problem Severity="Error" Clause="7.1" ObjectID="" Page="" Convertable="True" Code="7.1:7.2(12.2)">'DisplayDocTitle' entry is not set</Problem>
            <Problem Severity="Error" Clause="7.1" ObjectID="" Page="" Convertable="True" Code="7.1:1.1(14.8.1)">Document is not marked as tagged</Problem>
            <Problem Severity="Error" Clause="7.1" ObjectID="" Page="1" Convertable="False" Code="7.1:1.1(14.8)">XObject object not tagged</Problem>
            <Problem Severity="Error" Clause="7.1" ObjectID="" Page="1" Convertable="False" Code="7.1:1.1(14.8)">Text object not tagged</Problem>
            <Problem Severity="Warning" Clause="7.1" ObjectID="" Page="" Convertable="False" Code="7.1:2.1">Structure tree missing</Problem>
            <Problem Severity="Error" Clause="7.1" ObjectID="" Page="" Convertable="True" Code="7.1:6.2">Title missing in document's XMP metadata</Problem>
        </General>
        <Text>
            <Problem Severity="Error" Clause="7.2" ObjectID="" Page="1" Convertable="False" Code="7.2:3.1(14.9.2.2)">Natural language for text object cannot be determined</Problem>
        </Text>
        <VersionIdentification>
            <Problem Severity="Error" Clause="5" ObjectID="" Page="" Convertable="True" Code="5:1">PDF/UA identifier missing</Problem>
        </VersionIdentification>

打开原始不兼容的 PDF。我们将需要一个引用包含我们希望提取的内容的页面的链接。请注意,Aspose.PDF .NET 的索引从 1 开始,而不是 0。

var originalDocument = new Document(inputFileName);
var pageOne = originalDocument.Pages[1];

创建一个具有最少必需元数据的新标记 PDF。

  1. 初始化标记 PDF 的新 Document 对象。
  2. 创建一个引用标记内容根元素的引用,准备好构建屏幕阅读器可访问的逻辑结构。
  3. 设置文档标题元数据,该元数据将在 Aspose.PDF 中默认显示在标题栏中。这修复了验证错误以及关于缺少元数据的错误。
  4. 设置文档语言元数据。这修复了与语言相关的验证错误以及关于缺少元数据的错误。
   var taggedDocument = new Document();
   ITaggedContent taggedContent = taggedDocument.TaggedContent;
   StructureElement rootElement = taggedContent.RootElement;
  
   taggedContent.SetTitle("Our compliant document.");
   taggedContent.SetLanguage("en-US");

接下来,我们将提取并将一些文本转换为标题结构元素。

Aspose.pdf.LogicalStructure 命名空间提供了多种类型,用于在语义上表示标准元素,并自动进行标记以包含在文档结构层次的阅读顺序中。

在下面的示例中,我们从原始文档中复制了一些文本和图像,但我们也可以在代码中用新内容创建标记的 PDF 文档。

  1. 在页面上接受 TextFragmentAbsorber。
  2. 将现有标题提取为页面上的第一个 TextFragment。
  3. 创建一个新的 Aspose.Pdf.LogicalStructure.HeaderElement 对象。
  4. 将原始文本中的文本和字体(嵌入此)复制到新元素中。
TextFragmentAbsorber textFragmentAbsorber = new TextFragmentAbsorber();
page.Accept(textFragmentAbsorber);
TextFragment originalHeaderText = textFragmentAbsorber.TextFragments[textIndex];
 
HeaderElement h1 = taggedContent.CreateHeaderElement(headerLevel);
h1.StructureTextState.ForegroundColor = originalHeaderText.TextState.ForegroundColor;
Font headerFont = FontRepository.FindFont(originalHeaderText.TextState.Font.FontName);
headerFont.IsEmbedded = true;
h1.StructureTextState.Font = headerFont;
h1.SetText(originalHeaderText.Text);

接下来,提取并将一些文本转换为段落结构元素。过程与创建标题类似。

  1. 将文本提取为页面上的第二个 TextFragment。
  2. 创建一个新的 Aspose.Pdf.LogicalStructure.ParagraphElement 对象。
  3. 将原始文本中的文本、颜色和字体(嵌入此)复制过来。
TextFragmentAbsorber textFragmentAbsorber = new TextFragmentAbsorber();
page.Accept(textFragmentAbsorber);
 
TextFragment originalText = textFragmentAbsorber.TextFragments[textIndex];
 
ParagraphElement p = taggedContent.CreateParagraphElement();
p.StructureTextState.ForegroundColor = originalText.TextState.ForegroundColor;
Font paraFont = FontRepository.FindFont(originalText.TextState.Font.FontName);
paraFont.IsEmbedded = true;
p.StructureTextState.Font = paraFont;
p.SetText(originalText.Text);

也许我们想在导入的文本末尾添加缩写 W3C。在 Aspose.Pdf.LogicalStructure 中,这是一个直接的任务。

在这里,我们将使用内联 SpanElement(您可能从 HTML 中熟悉它)来添加我们刚刚创建的段落元素的子元素。

既然我们正在处理,我们最好为这个新元素添加扩展文本(请记住,PDF/UA 要求我们使用扩展文本来描述缩写和术语)。

  1. 创建一个新的 span 元素。
  2. 将 span 的文本设置为“W3C”。
  3. 将 span 的扩展文本设置为“World Wide Web Consortium”。
  4. 将 span 追加到段落的末尾。
SpanElement w3cSpan = taggedContent.CreateSpanElement();
            w3cSpan.SetText("W3C");
w3cSpan.ExpansionText = "World Wide Web Consortium"
p.AppendChild(w3cSpan);

我们还可以将图像添加到 figure 结构元素中。对于图像,我们还需要添加替代文本,以便使用辅助技术的读者知道它是 Aspose logo。

  1. 将现有图像(页面上的第一个图像)提取到一个文件中。
  2. 从该文件创建一个新的 Aspose.Pdf.LogicalStructure.FigureElement 对象。
  3. 设置替代文本。
ImagePlacementAbsorber imagePlacementAbsorber = new ImagePlacementAbsorber();
page.Accept(imagePlacementAbsorber);
XImage xImage = imagePlacementAbsorber.ImagePlacements[imageIndex].Image;
 
FileStream outputImage = new FileStream("temp-image.png", FileMode.Create);
xImage.Save(outputImage, ImageFormat.Png);
outputImage.Close();
 
FigureElement figureElement = taggedContent.CreateFigureElement();
figureElement.SetImage("temp-image.png");
figureElement.AlternativeText = "Aspose logo";

现在我们将新元素追加到新的标记 PDF 中。从根元素开始,我们将按必需的逻辑顺序追加三个新元素。

Aspose.PDF 将负责构建具有正确标签名称和我们添加的任何附加文本的结构树。

rootElement.AppendChild(h1);
rootElement.AppendChild(figureElement);
rootElement.AppendChild(p);

最后,我们保存标记的文档并将其验证为 PDF/UA。

taggedDocument.Save(outputFileName);
 
 using (var d = new Document(outputFileName))
 {
     bool isValid = d.Validate("compliant-validation-log.xml", Aspose.Pdf.PdfFormat.PDF_UA_1);
 }

后续步骤

正如您所见,Aspose.PDF 使创建可访问的 PDF 变得相当简单,甚至还可以测试您以编程方式创建的文档的可访问性。

了解 PDF/UA 的两个好起点是:PDF Association 的PDF/UA 简介和 AIIM 的使用 PDF/UA 实现 WCAG 2.0(其中包含 WCAG 到 PDF/UA 的映射)。

下载Aspose.PDF for .NET 并亲自尝试。如果您想进一步试验本文中的示例,可以在 GitHub 上找到完整的代码示例,或者查看Aspose.PDF 文档和 PDF 编辑器示例。

© . All rights reserved.