diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js
index 958a5cda33f9f..671ac870ef157 100644
--- a/src/display/annotation_layer.js
+++ b/src/display/annotation_layer.js
@@ -49,14 +49,11 @@ import {
Util,
warn,
} from "../shared/util.js";
-import {
- PDFDateString,
- renderRichText,
- setLayerDimensions,
-} from "./display_utils.js";
+import { PDFDateString, setLayerDimensions } from "./display_utils.js";
import { AnnotationStorage } from "./annotation_storage.js";
import { ColorConverters } from "../shared/scripting_utils.js";
import { DOMSVGFactory } from "./svg_factory.js";
+import { renderRichText } from "./annotation_layer_utils.js";
const DEFAULT_FONT_SIZE = 9;
const GetElementsByNameSet = new WeakSet();
diff --git a/src/display/annotation_layer_utils.js b/src/display/annotation_layer_utils.js
new file mode 100644
index 0000000000000..c2cc4665091cb
--- /dev/null
+++ b/src/display/annotation_layer_utils.js
@@ -0,0 +1,43 @@
+/* Copyright 2015 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 { XfaLayer } from "./xfa_layer.js";
+
+function renderRichText({ html, dir, className }, container) {
+ const fragment = document.createDocumentFragment();
+ if (typeof html === "string") {
+ const p = document.createElement("p");
+ p.dir = dir || "auto";
+ const lines = html.split(/\r\n?|\n/);
+ for (let i = 0, ii = lines.length; i < ii; ++i) {
+ const line = lines[i];
+ p.append(document.createTextNode(line));
+ if (i < ii - 1) {
+ p.append(document.createElement("br"));
+ }
+ }
+ fragment.append(p);
+ } else {
+ XfaLayer.render({
+ xfaHtml: html,
+ div: fragment,
+ intent: "richText",
+ });
+ }
+ fragment.firstElementChild.classList.add("richText", className);
+ container.append(fragment);
+}
+
+export { renderRichText };
diff --git a/src/display/display_utils.js b/src/display/display_utils.js
index 989568379cfd3..33baf54f3c687 100644
--- a/src/display/display_utils.js
+++ b/src/display/display_utils.js
@@ -23,7 +23,6 @@ import {
} from "../shared/util.js";
import { MathClamp } from "../shared/math_clamp.js";
import { PageViewport } from "./page_viewport.js";
-import { XfaLayer } from "./xfa_layer.js";
class PixelsPerInch {
static CSS = 96.0;
@@ -762,31 +761,6 @@ function findContrastColor(baseColor, fixedColor) {
return cachedValue;
}
-function renderRichText({ html, dir, className }, container) {
- const fragment = document.createDocumentFragment();
- if (typeof html === "string") {
- const p = document.createElement("p");
- p.dir = dir || "auto";
- const lines = html.split(/\r\n?|\n/);
- for (let i = 0, ii = lines.length; i < ii; ++i) {
- const line = lines[i];
- p.append(document.createTextNode(line));
- if (i < ii - 1) {
- p.append(document.createElement("br"));
- }
- }
- fragment.append(p);
- } else {
- XfaLayer.render({
- xfaHtml: html,
- div: fragment,
- intent: "richText",
- });
- }
- fragment.firstElementChild.classList.add("richText", className);
- container.append(fragment);
-}
-
function makePathFromDrawOPS(data) {
// Using a SVG string is slightly slower than using the following loop.
const path = new Path2D();
@@ -849,7 +823,6 @@ export {
PDFDateString,
PixelsPerInch,
RenderingCancelledException,
- renderRichText,
setLayerDimensions,
StatTimer,
stopEvent,
diff --git a/src/pdf.js b/src/pdf.js
index 6be3e8a62e761..9a51dd210d4e9 100644
--- a/src/pdf.js
+++ b/src/pdf.js
@@ -64,7 +64,6 @@ import {
PDFDateString,
PixelsPerInch,
RenderingCancelledException,
- renderRichText,
setLayerDimensions,
stopEvent,
SupportedImageMimeTypes,
@@ -86,6 +85,7 @@ import { GlobalWorkerOptions } from "./display/worker_options.js";
import { HighlightOutliner } from "./display/editor/drawers/highlight.js";
import { isValidExplicitDest } from "./display/api_utils.js";
import { MathClamp } from "./shared/math_clamp.js";
+import { renderRichText } from "./display/annotation_layer_utils.js";
import { SignatureExtractor } from "./display/editor/drawers/signaturedraw.js";
import { TextLayer } from "./display/text_layer.js";
import { TextLayerImages } from "./display/text_layer_images.js";
diff --git a/test/unit/annotation_layer_utils_spec.js b/test/unit/annotation_layer_utils_spec.js
new file mode 100644
index 0000000000000..f827027c332c2
--- /dev/null
+++ b/test/unit/annotation_layer_utils_spec.js
@@ -0,0 +1,159 @@
+/* Copyright 2017 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 { isNodeJS } from "../../src/shared/util.js";
+import { renderRichText } from "../../src/display/annotation_layer_utils.js";
+
+describe("annotation_layer_utils", function () {
+ describe("renderRichText", function () {
+ // Unlike other tests we cannot simply compare the HTML-strings since
+ // Chrome and Firefox produce different results. Instead we compare sets
+ // containing the individual parts of the HTML-strings.
+ const splitParts = s => new Set(s.split(/[<>/ ]+/).filter(Boolean));
+
+ it("should render plain text", function () {
+ if (isNodeJS) {
+ pending("DOM is not supported in Node.js.");
+ }
+ const container = document.createElement("div");
+ renderRichText(
+ {
+ html: "Hello world!\nThis is a test.",
+ dir: "ltr",
+ className: "foo",
+ },
+ container
+ );
+ expect(splitParts(container.innerHTML)).toEqual(
+ splitParts(
+ '
Hello world!
This is a test.
'
+ )
+ );
+ });
+
+ it("should render XFA rich text", function () {
+ if (isNodeJS) {
+ pending("DOM is not supported in Node.js.");
+ }
+ const container = document.createElement("div");
+ const xfaHtml = {
+ name: "div",
+ attributes: { style: { color: "red" } },
+ children: [
+ {
+ name: "p",
+ attributes: { style: { fontSize: "20px" } },
+ children: [
+ {
+ name: "span",
+ attributes: { style: { fontWeight: "bold" } },
+ value: "Hello",
+ },
+ { name: "#text", value: " world!" },
+ ],
+ },
+ ],
+ };
+ renderRichText(
+ { html: xfaHtml, dir: "ltr", className: "foo" },
+ container
+ );
+ expect(splitParts(container.innerHTML)).toEqual(
+ splitParts(
+ '' +
+ '
' +
+ 'Hello world!
'
+ )
+ );
+ });
+
+ it("should only keep the supported rich text elements", function () {
+ if (isNodeJS) {
+ pending("DOM is not supported in Node.js.");
+ }
+ const container = document.createElement("div");
+ const xfaHtml = {
+ name: "div",
+ children: [
+ { name: "p", value: "kept" },
+ {
+ name: "section",
+ children: [{ name: "span", value: "removed" }],
+ },
+ ],
+ };
+ renderRichText(
+ { html: xfaHtml, dir: "ltr", className: "foo" },
+ container
+ );
+
+ expect(container.querySelector("p")).not.toBeNull();
+ expect(container.querySelector("section")).toBeNull();
+ expect(container.querySelector("span")).toBeNull();
+ expect(container.textContent).toEqual("kept");
+ });
+
+ it("should only keep the supported rich text attributes", function () {
+ if (isNodeJS) {
+ pending("DOM is not supported in Node.js.");
+ }
+ const container = document.createElement("div");
+ const xfaHtml = {
+ name: "div",
+ children: [
+ {
+ name: "p",
+ attributes: { class: ["bar"], dir: "rtl", title: "unsupported" },
+ value: "text",
+ },
+ ],
+ };
+ renderRichText(
+ { html: xfaHtml, dir: "ltr", className: "foo" },
+ container
+ );
+ const p = container.querySelector("p");
+
+ expect(p.getAttribute("class")).toEqual("bar");
+ expect(p.getAttribute("dir")).toEqual("rtl");
+ expect(p.hasAttribute("title")).toEqual(false);
+ });
+
+ it("should only apply the supported rich text style properties", function () {
+ if (isNodeJS) {
+ pending("DOM is not supported in Node.js.");
+ }
+ const container = document.createElement("div");
+ const xfaHtml = {
+ name: "div",
+ children: [
+ {
+ name: "span",
+ attributes: { style: { color: "green", width: "100px" } },
+ value: "text",
+ },
+ ],
+ };
+ renderRichText(
+ { html: xfaHtml, dir: "ltr", className: "foo" },
+ container
+ );
+ const span = container.querySelector("span");
+
+ expect(span.style.color).toEqual("green");
+ expect(span.style.width).toEqual("");
+ });
+ });
+});
diff --git a/test/unit/clitests.json b/test/unit/clitests.json
index c98345d3c34b6..3872ccf318cfa 100644
--- a/test/unit/clitests.json
+++ b/test/unit/clitests.json
@@ -4,6 +4,7 @@
"helpers": ["clitests_helper.js"],
"spec_files": [
+ "annotation_layer_utils_spec.js",
"annotation_spec.js",
"annotation_storage_spec.js",
"api_spec.js",
diff --git a/test/unit/display_utils_spec.js b/test/unit/display_utils_spec.js
index e5c2344a1fc15..f7824dcb64d9a 100644
--- a/test/unit/display_utils_spec.js
+++ b/test/unit/display_utils_spec.js
@@ -22,7 +22,6 @@ import {
getRGBA,
isValidFetchUrl,
PDFDateString,
- renderRichText,
} from "../../src/display/display_utils.js";
import { isNodeJS } from "../../src/shared/util.js";
@@ -402,144 +401,4 @@ describe("display_utils", function () {
expect(applyOpacity([123, 45, 67], ctx.globalAlpha)).toEqual([r, g, b]);
});
});
-
- describe("renderRichText", function () {
- // Unlike other tests we cannot simply compare the HTML-strings since
- // Chrome and Firefox produce different results. Instead we compare sets
- // containing the individual parts of the HTML-strings.
- const splitParts = s => new Set(s.split(/[<>/ ]+/).filter(Boolean));
-
- it("should render plain text", function () {
- if (isNodeJS) {
- pending("DOM is not supported in Node.js.");
- }
- const container = document.createElement("div");
- renderRichText(
- {
- html: "Hello world!\nThis is a test.",
- dir: "ltr",
- className: "foo",
- },
- container
- );
- expect(splitParts(container.innerHTML)).toEqual(
- splitParts(
- 'Hello world!
This is a test.
'
- )
- );
- });
-
- it("should render XFA rich text", function () {
- if (isNodeJS) {
- pending("DOM is not supported in Node.js.");
- }
- const container = document.createElement("div");
- const xfaHtml = {
- name: "div",
- attributes: { style: { color: "red" } },
- children: [
- {
- name: "p",
- attributes: { style: { fontSize: "20px" } },
- children: [
- {
- name: "span",
- attributes: { style: { fontWeight: "bold" } },
- value: "Hello",
- },
- { name: "#text", value: " world!" },
- ],
- },
- ],
- };
- renderRichText(
- { html: xfaHtml, dir: "ltr", className: "foo" },
- container
- );
- expect(splitParts(container.innerHTML)).toEqual(
- splitParts(
- '' +
- '
' +
- 'Hello world!
'
- )
- );
- });
-
- it("should only keep the supported rich text elements", function () {
- if (isNodeJS) {
- pending("DOM is not supported in Node.js.");
- }
- const container = document.createElement("div");
- const xfaHtml = {
- name: "div",
- children: [
- { name: "p", value: "kept" },
- {
- name: "section",
- children: [{ name: "span", value: "removed" }],
- },
- ],
- };
- renderRichText(
- { html: xfaHtml, dir: "ltr", className: "foo" },
- container
- );
-
- expect(container.querySelector("p")).not.toBeNull();
- expect(container.querySelector("section")).toBeNull();
- expect(container.querySelector("span")).toBeNull();
- expect(container.textContent).toEqual("kept");
- });
-
- it("should only keep the supported rich text attributes", function () {
- if (isNodeJS) {
- pending("DOM is not supported in Node.js.");
- }
- const container = document.createElement("div");
- const xfaHtml = {
- name: "div",
- children: [
- {
- name: "p",
- attributes: { class: ["bar"], dir: "rtl", title: "unsupported" },
- value: "text",
- },
- ],
- };
- renderRichText(
- { html: xfaHtml, dir: "ltr", className: "foo" },
- container
- );
- const p = container.querySelector("p");
-
- expect(p.getAttribute("class")).toEqual("bar");
- expect(p.getAttribute("dir")).toEqual("rtl");
- expect(p.hasAttribute("title")).toEqual(false);
- });
-
- it("should only apply the supported rich text style properties", function () {
- if (isNodeJS) {
- pending("DOM is not supported in Node.js.");
- }
- const container = document.createElement("div");
- const xfaHtml = {
- name: "div",
- children: [
- {
- name: "span",
- attributes: { style: { color: "green", width: "100px" } },
- value: "text",
- },
- ],
- };
- renderRichText(
- { html: xfaHtml, dir: "ltr", className: "foo" },
- container
- );
- const span = container.querySelector("span");
-
- expect(span.style.color).toEqual("green");
- expect(span.style.width).toEqual("");
- });
- });
});
diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js
index e6605e65ad1aa..9d61827e31053 100644
--- a/test/unit/jasmine-boot.js
+++ b/test/unit/jasmine-boot.js
@@ -49,6 +49,7 @@ import { TestReporter } from "../reporter.js";
async function initializePDFJS(callback) {
await Promise.all(
[
+ "pdfjs-test/unit/annotation_layer_utils_spec.js",
"pdfjs-test/unit/annotation_spec.js",
"pdfjs-test/unit/annotation_storage_spec.js",
"pdfjs-test/unit/api_spec.js",
diff --git a/test/unit/pdf_spec.js b/test/unit/pdf_spec.js
index 57295541c9b79..80d5d45c3664e 100644
--- a/test/unit/pdf_spec.js
+++ b/test/unit/pdf_spec.js
@@ -55,7 +55,6 @@ import {
PDFDateString,
PixelsPerInch,
RenderingCancelledException,
- renderRichText,
setLayerDimensions,
stopEvent,
SupportedImageMimeTypes,
@@ -76,6 +75,7 @@ import { DrawLayer } from "../../src/display/draw_layer.js";
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
import { isValidExplicitDest } from "../../src/display/api_utils.js";
import { MathClamp } from "../../src/shared/math_clamp.js";
+import { renderRichText } from "../../src/display/annotation_layer_utils.js";
import { SignatureExtractor } from "../../src/display/editor/drawers/signaturedraw.js";
import { TextLayer } from "../../src/display/text_layer.js";
import { TextLayerImages } from "../../src/display/text_layer_images.js";