Merge pull request #20231 from calixteman/xfa_render_richtext
Add a new function renderRichText to be used in the annotation layer
This commit is contained in:
commit
2a93ade197
@ -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