Add a new function renderRichText to be used in the annotation layer
and which will be used in order to make the contents of the new popup used for comments.
This commit is contained in:
parent
7b87c220a5
commit
35c909843b
@ -41,12 +41,12 @@ import {
|
||||
applyOpacity,
|
||||
changeLightness,
|
||||
PDFDateString,
|
||||
renderRichText,
|
||||
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 { XfaLayer } from "./xfa_layer.js";
|
||||
|
||||
const DEFAULT_FONT_SIZE = 9;
|
||||
const GetElementsByNameSet = new WeakSet();
|
||||
@ -2491,18 +2491,15 @@ class PopupElement {
|
||||
header.append(modificationDate);
|
||||
}
|
||||
|
||||
const html = this.#html;
|
||||
if (html) {
|
||||
XfaLayer.render({
|
||||
xfaHtml: html,
|
||||
intent: "richText",
|
||||
div: popup,
|
||||
});
|
||||
popup.lastChild.classList.add("richText", "popupContent");
|
||||
} else {
|
||||
const contents = this._formatContents(this.#contentsObj);
|
||||
popup.append(contents);
|
||||
}
|
||||
renderRichText(
|
||||
{
|
||||
html: this.#html || this.#contentsObj.str,
|
||||
dir: this.#contentsObj.dir,
|
||||
className: "popupContent",
|
||||
},
|
||||
popup
|
||||
);
|
||||
|
||||
this.#container.append(popup);
|
||||
}
|
||||
|
||||
@ -2561,29 +2558,6 @@ class PopupElement {
|
||||
return popupContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the contents of the popup by adding newlines where necessary.
|
||||
*
|
||||
* @private
|
||||
* @param {Object<string, string>} contentsObj
|
||||
* @memberof PopupElement
|
||||
* @returns {HTMLParagraphElement}
|
||||
*/
|
||||
_formatContents({ str, dir }) {
|
||||
const p = document.createElement("p");
|
||||
p.classList.add("popupContent");
|
||||
p.dir = dir;
|
||||
const lines = str.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"));
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#keyDown(event) {
|
||||
if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
|
||||
return;
|
||||
|
||||
@ -20,6 +20,7 @@ import {
|
||||
Util,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { XfaLayer } from "./xfa_layer.js";
|
||||
|
||||
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||
|
||||
@ -829,6 +830,31 @@ function applyOpacity(r, g, b, opacity) {
|
||||
return [r, g, b];
|
||||
}
|
||||
|
||||
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.firstChild.classList.add("richText", className);
|
||||
container.append(fragment);
|
||||
}
|
||||
|
||||
export {
|
||||
applyOpacity,
|
||||
changeLightness,
|
||||
@ -851,6 +877,7 @@ export {
|
||||
PDFDateString,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
setLayerDimensions,
|
||||
StatTimer,
|
||||
stopEvent,
|
||||
|
||||
@ -59,6 +59,7 @@ import {
|
||||
PDFDateString,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
setLayerDimensions,
|
||||
stopEvent,
|
||||
SupportedImageMimeTypes,
|
||||
@ -132,6 +133,7 @@ globalThis.pdfjsLib = {
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
ResponseException,
|
||||
setLayerDimensions,
|
||||
shadow,
|
||||
@ -189,6 +191,7 @@ export {
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
ResponseException,
|
||||
setLayerDimensions,
|
||||
shadow,
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
getRGB,
|
||||
isValidFetchUrl,
|
||||
PDFDateString,
|
||||
renderRichText,
|
||||
} from "../../src/display/display_utils.js";
|
||||
import { isNodeJS, toBase64Util } from "../../src/shared/util.js";
|
||||
|
||||
@ -342,4 +343,67 @@ 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(x => x));
|
||||
|
||||
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(
|
||||
'<p dir="ltr" class="richText foo">Hello world!<br>This is a test.</p>'
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
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(
|
||||
'<div style="color: red;" class="richText foo">' +
|
||||
'<p style="font-size: 20px;">' +
|
||||
'<span style="font-weight: bold;">Hello</span> world!</p></div>'
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -50,6 +50,7 @@ import {
|
||||
PDFDateString,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
setLayerDimensions,
|
||||
stopEvent,
|
||||
SupportedImageMimeTypes,
|
||||
@ -116,6 +117,7 @@ const expectedAPI = Object.freeze({
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
ResponseException,
|
||||
setLayerDimensions,
|
||||
shadow,
|
||||
|
||||
@ -55,6 +55,7 @@ const {
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
ResponseException,
|
||||
setLayerDimensions,
|
||||
shadow,
|
||||
@ -112,6 +113,7 @@ export {
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderRichText,
|
||||
ResponseException,
|
||||
setLayerDimensions,
|
||||
shadow,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user