1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-22 08:08:07 +02:00

Merge pull request #13171 from brendandahl/struct-tree

[api-minor] Add support for basic structure tree for accessibility.
This commit is contained in:
Tim van der Meij 2021-04-09 21:32:44 +02:00 committed by GitHub
commit 03c8c89002
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 911 additions and 14 deletions

View file

@ -572,6 +572,7 @@ var Driver = (function DriverClosure() {
initPromise = page
.getTextContent({
normalizeWhitespace: true,
includeMarkedContent: true,
})
.then(function (textContent) {
return rasterizeTextLayer(

View file

@ -24,7 +24,11 @@ async function runTests(results) {
jasmine.loadConfig({
random: false,
spec_dir: "integration",
spec_files: ["scripting_spec.js", "annotation_spec.js"],
spec_files: [
"scripting_spec.js",
"annotation_spec.js",
"accessibility_spec.js",
],
});
jasmine.addReporter({

View file

@ -0,0 +1,69 @@
/* Copyright 2021 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { closePages, loadAndWait } = require("./test_utils.js");
describe("accessibility", () => {
describe("structure tree", () => {
let pages;
beforeAll(async () => {
pages = await loadAndWait("structure_simple.pdf", ".structTree");
});
afterAll(async () => {
await closePages(pages);
});
it("must build structure that maps to text layer", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.waitForSelector(".structTree");
// Check the headings match up.
const head1 = await page.$eval(
".structTree [role='heading'][aria-level='1'] span",
el =>
document.getElementById(el.getAttribute("aria-owns")).textContent
);
expect(head1).withContext(`In ${browserName}`).toEqual("Heading 1");
const head2 = await page.$eval(
".structTree [role='heading'][aria-level='2'] span",
el =>
document.getElementById(el.getAttribute("aria-owns")).textContent
);
expect(head2).withContext(`In ${browserName}`).toEqual("Heading 2");
// Check the order of the content.
const texts = await page.$$eval(".structTree [aria-owns]", nodes =>
nodes.map(
el =>
document.getElementById(el.getAttribute("aria-owns"))
.textContent
)
);
expect(texts)
.withContext(`In ${browserName}`)
.toEqual([
"Heading 1",
"This paragraph 1.",
"Heading 2",
"This paragraph 2.",
]);
})
);
});
});
});

View file

@ -71,6 +71,7 @@
!issue8570.pdf
!issue8697.pdf
!issue8702.pdf
!structure_simple.pdf
!issue12823.pdf
!issue8707.pdf
!issue8798r.pdf

Binary file not shown.

View file

@ -23,7 +23,7 @@
bottom: 0;
line-height: 1;
}
.textLayer > span {
.textLayer span {
position: absolute;
white-space: pre;
-webkit-transform-origin: 0% 0%;
@ -37,3 +37,8 @@
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.textLayer .markedContent {
border: none;
background-color: transparent;
}

View file

@ -34,6 +34,7 @@
"pdf_history_spec.js",
"primitives_spec.js",
"stream_spec.js",
"struct_tree_spec.js",
"type1_parser_spec.js",
"ui_utils_spec.js",
"unicode_spec.js",

View file

@ -80,6 +80,7 @@ async function initializePDFJS(callback) {
"pdfjs-test/unit/primitives_spec.js",
"pdfjs-test/unit/scripting_spec.js",
"pdfjs-test/unit/stream_spec.js",
"pdfjs-test/unit/struct_tree_spec.js",
"pdfjs-test/unit/type1_parser_spec.js",
"pdfjs-test/unit/ui_utils_spec.js",
"pdfjs-test/unit/unicode_spec.js",

View file

@ -0,0 +1,108 @@
/* Copyright 2021 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { buildGetDocumentParams } from "./test_utils.js";
import { getDocument } from "../../src/display/api.js";
function equalTrees(rootA, rootB) {
function walk(a, b) {
expect(a.role).toEqual(b.role);
expect(a.type).toEqual(b.type);
expect("children" in a).toEqual("children" in b);
if (!a.children) {
return;
}
expect(a.children.length).toEqual(b.children.length);
for (let i = 0; i < rootA.children.length; i++) {
walk(a.children[i], b.children[i]);
}
}
return walk(rootA, rootB);
}
describe("struct tree", function () {
describe("getStructTree", function () {
it("parses basic structure", async function () {
const filename = "structure_simple.pdf";
const params = buildGetDocumentParams(filename);
const loadingTask = getDocument(params);
const doc = await loadingTask.promise;
const page = await doc.getPage(1);
const struct = await page.getStructTree();
equalTrees(
{
role: "Root",
children: [
{
role: "Document",
children: [
{
role: "H1",
children: [
{ role: "NonStruct", children: [{ type: "content" }] },
],
},
{
role: "P",
children: [
{ role: "NonStruct", children: [{ type: "content" }] },
],
},
{
role: "H2",
children: [
{ role: "NonStruct", children: [{ type: "content" }] },
],
},
{
role: "P",
children: [
{ role: "NonStruct", children: [{ type: "content" }] },
],
},
],
},
],
},
struct
);
await loadingTask.destroy();
});
it("parses structure with marked content reference", async function () {
const filename = "issue6782.pdf";
const params = buildGetDocumentParams(filename);
const loadingTask = getDocument(params);
const doc = await loadingTask.promise;
const page = await doc.getPage(1);
const struct = await page.getStructTree();
equalTrees(
{
role: "Root",
children: [
{
role: "Part",
children: [
{ role: "P", children: Array(27).fill({ type: "content" }) },
],
},
],
},
struct
);
await loadingTask.destroy();
});
});
});