Support rich content in markup annotation
- use the xfa parser but in the xhtml namespace.
This commit is contained in:
parent
0e7614df7f
commit
cf8dc750d6
@ -55,6 +55,7 @@ import { ObjectLoader } from "./object_loader.js";
|
|||||||
import { OperatorList } from "./operator_list.js";
|
import { OperatorList } from "./operator_list.js";
|
||||||
import { StringStream } from "./stream.js";
|
import { StringStream } from "./stream.js";
|
||||||
import { writeDict } from "./writer.js";
|
import { writeDict } from "./writer.js";
|
||||||
|
import { XFAFactory } from "./xfa/factory.js";
|
||||||
|
|
||||||
class AnnotationFactory {
|
class AnnotationFactory {
|
||||||
/**
|
/**
|
||||||
@ -1098,6 +1099,10 @@ class MarkupAnnotation extends Annotation {
|
|||||||
this.data.color = null;
|
this.data.color = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dict.has("RC")) {
|
||||||
|
this.data.richText = XFAFactory.getRichTextAsHtml(dict.get("RC"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2545,6 +2550,10 @@ class PopupAnnotation extends Annotation {
|
|||||||
|
|
||||||
this.setContents(parentItem.get("Contents"));
|
this.setContents(parentItem.get("Contents"));
|
||||||
this.data.contentsObj = this._contents;
|
this.data.contentsObj = this._contents;
|
||||||
|
|
||||||
|
if (parentItem.has("RC")) {
|
||||||
|
this.data.richText = XFAFactory.getRichTextAsHtml(parentItem.get("RC"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class Empty extends XFAObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Builder {
|
class Builder {
|
||||||
constructor() {
|
constructor(rootNameSpace = null) {
|
||||||
this._namespaceStack = [];
|
this._namespaceStack = [];
|
||||||
this._nsAgnosticLevel = 0;
|
this._nsAgnosticLevel = 0;
|
||||||
|
|
||||||
@ -76,7 +76,8 @@ class Builder {
|
|||||||
this._nextNsId = Math.max(
|
this._nextNsId = Math.max(
|
||||||
...Object.values(NamespaceIds).map(({ id }) => id)
|
...Object.values(NamespaceIds).map(({ id }) => id)
|
||||||
);
|
);
|
||||||
this._currentNamespace = new UnknownNamespace(++this._nextNsId);
|
this._currentNamespace =
|
||||||
|
rootNameSpace || new UnknownNamespace(++this._nextNsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRoot(ids) {
|
buildRoot(ids) {
|
||||||
|
@ -13,13 +13,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { $globalData, $toHTML } from "./xfa_object.js";
|
import {
|
||||||
|
$appendChild,
|
||||||
|
$globalData,
|
||||||
|
$nodeName,
|
||||||
|
$text,
|
||||||
|
$toHTML,
|
||||||
|
} from "./xfa_object.js";
|
||||||
import { Binder } from "./bind.js";
|
import { Binder } from "./bind.js";
|
||||||
import { DataHandler } from "./data.js";
|
import { DataHandler } from "./data.js";
|
||||||
import { FontFinder } from "./fonts.js";
|
import { FontFinder } from "./fonts.js";
|
||||||
import { stripQuotes } from "./utils.js";
|
import { stripQuotes } from "./utils.js";
|
||||||
import { warn } from "../../shared/util.js";
|
import { warn } from "../../shared/util.js";
|
||||||
import { XFAParser } from "./parser.js";
|
import { XFAParser } from "./parser.js";
|
||||||
|
import { XhtmlNamespace } from "./xhtml.js";
|
||||||
|
|
||||||
class XFAFactory {
|
class XFAFactory {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
@ -106,6 +113,43 @@ class XFAFactory {
|
|||||||
}
|
}
|
||||||
return Object.values(data).join("");
|
return Object.values(data).join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getRichTextAsHtml(rc) {
|
||||||
|
if (!rc || typeof rc !== "string") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let root = new XFAParser(XhtmlNamespace, /* richText */ true).parse(rc);
|
||||||
|
if (!["body", "xhtml"].includes(root[$nodeName])) {
|
||||||
|
// No body, so create one.
|
||||||
|
const newRoot = XhtmlNamespace.body({});
|
||||||
|
newRoot[$appendChild](root);
|
||||||
|
root = newRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = root[$toHTML]();
|
||||||
|
if (!result.success) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { html } = result;
|
||||||
|
const { attributes } = html;
|
||||||
|
if (attributes) {
|
||||||
|
if (attributes.class) {
|
||||||
|
attributes.class = attributes.class.filter(
|
||||||
|
attr => !attr.startsWith("xfa")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
attributes.dir = "auto";
|
||||||
|
}
|
||||||
|
|
||||||
|
return { html, str: root[$text]() };
|
||||||
|
} catch (e) {
|
||||||
|
warn(`XFA - an error occurred during parsing of rich text: ${e}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { XFAFactory };
|
export { XFAFactory };
|
||||||
|
@ -606,10 +606,16 @@ function setPara(node, nodeStyle, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setFontFamily(xfaFont, node, fontFinder, style) {
|
function setFontFamily(xfaFont, node, fontFinder, style) {
|
||||||
const name = stripQuotes(xfaFont.typeface);
|
if (!fontFinder) {
|
||||||
const typeface = fontFinder.find(name);
|
// The font cannot be found in the pdf so use the default one.
|
||||||
|
delete style.fontFamily;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = stripQuotes(xfaFont.typeface);
|
||||||
style.fontFamily = `"${name}"`;
|
style.fontFamily = `"${name}"`;
|
||||||
|
|
||||||
|
const typeface = fontFinder.find(name);
|
||||||
if (typeface) {
|
if (typeface) {
|
||||||
const { fontFamily } = typeface.regular.cssFontInfo;
|
const { fontFamily } = typeface.regular.cssFontInfo;
|
||||||
if (fontFamily !== name) {
|
if (fontFamily !== name) {
|
||||||
|
@ -30,9 +30,9 @@ import { Builder } from "./builder.js";
|
|||||||
import { warn } from "../../shared/util.js";
|
import { warn } from "../../shared/util.js";
|
||||||
|
|
||||||
class XFAParser extends XMLParserBase {
|
class XFAParser extends XMLParserBase {
|
||||||
constructor() {
|
constructor(rootNameSpace = null, richText = false) {
|
||||||
super();
|
super();
|
||||||
this._builder = new Builder();
|
this._builder = new Builder(rootNameSpace);
|
||||||
this._stack = [];
|
this._stack = [];
|
||||||
this._globalData = {
|
this._globalData = {
|
||||||
usedTypefaces: new Set(),
|
usedTypefaces: new Set(),
|
||||||
@ -42,6 +42,7 @@ class XFAParser extends XMLParserBase {
|
|||||||
this._errorCode = XMLParserErrorCode.NoError;
|
this._errorCode = XMLParserErrorCode.NoError;
|
||||||
this._whiteRegex = /^\s+$/;
|
this._whiteRegex = /^\s+$/;
|
||||||
this._nbsps = /\xa0+/g;
|
this._nbsps = /\xa0+/g;
|
||||||
|
this._richText = richText;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(data) {
|
parse(data) {
|
||||||
@ -60,8 +61,8 @@ class XFAParser extends XMLParserBase {
|
|||||||
// Normally by definition a   is unbreakable
|
// Normally by definition a   is unbreakable
|
||||||
// but in real life Acrobat can break strings on  .
|
// but in real life Acrobat can break strings on  .
|
||||||
text = text.replace(this._nbsps, match => match.slice(1) + " ");
|
text = text.replace(this._nbsps, match => match.slice(1) + " ");
|
||||||
if (this._current[$acceptWhitespace]()) {
|
if (this._richText || this._current[$acceptWhitespace]()) {
|
||||||
this._current[$onText](text);
|
this._current[$onText](text, this._richText);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
$content,
|
$content,
|
||||||
$extra,
|
$extra,
|
||||||
$getChildren,
|
$getChildren,
|
||||||
|
$getParent,
|
||||||
$globalData,
|
$globalData,
|
||||||
$nodeName,
|
$nodeName,
|
||||||
$onText,
|
$onText,
|
||||||
@ -38,6 +39,7 @@ import {
|
|||||||
import { getMeasurement, HTMLResult, stripQuotes } from "./utils.js";
|
import { getMeasurement, HTMLResult, stripQuotes } from "./utils.js";
|
||||||
|
|
||||||
const XHTML_NS_ID = NamespaceIds.xhtml.id;
|
const XHTML_NS_ID = NamespaceIds.xhtml.id;
|
||||||
|
const $richText = Symbol();
|
||||||
|
|
||||||
const VALID_STYLES = new Set([
|
const VALID_STYLES = new Set([
|
||||||
"color",
|
"color",
|
||||||
@ -109,6 +111,7 @@ const StyleMapping = new Map([
|
|||||||
|
|
||||||
const spacesRegExp = /\s+/g;
|
const spacesRegExp = /\s+/g;
|
||||||
const crlfRegExp = /[\r\n]+/g;
|
const crlfRegExp = /[\r\n]+/g;
|
||||||
|
const crlfForRichTextRegExp = /\r\n?/g;
|
||||||
|
|
||||||
function mapStyle(styleStr, node) {
|
function mapStyle(styleStr, node) {
|
||||||
const style = Object.create(null);
|
const style = Object.create(null);
|
||||||
@ -185,6 +188,7 @@ const NoWhites = new Set(["body", "html"]);
|
|||||||
class XhtmlObject extends XmlObject {
|
class XhtmlObject extends XmlObject {
|
||||||
constructor(attributes, name) {
|
constructor(attributes, name) {
|
||||||
super(XHTML_NS_ID, name);
|
super(XHTML_NS_ID, name);
|
||||||
|
this[$richText] = false;
|
||||||
this.style = attributes.style || "";
|
this.style = attributes.style || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,11 +201,16 @@ class XhtmlObject extends XmlObject {
|
|||||||
return !NoWhites.has(this[$nodeName]);
|
return !NoWhites.has(this[$nodeName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[$onText](str) {
|
[$onText](str, richText = false) {
|
||||||
str = str.replace(crlfRegExp, "");
|
if (!richText) {
|
||||||
if (!this.style.includes("xfa-spacerun:yes")) {
|
str = str.replace(crlfRegExp, "");
|
||||||
str = str.replace(spacesRegExp, " ");
|
if (!this.style.includes("xfa-spacerun:yes")) {
|
||||||
|
str = str.replace(spacesRegExp, " ");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this[$richText] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str) {
|
if (str) {
|
||||||
this[$content] += str;
|
this[$content] += str;
|
||||||
}
|
}
|
||||||
@ -311,6 +320,15 @@ class XhtmlObject extends XmlObject {
|
|||||||
return HTMLResult.EMPTY;
|
return HTMLResult.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let value;
|
||||||
|
if (this[$richText]) {
|
||||||
|
value = this[$content]
|
||||||
|
? this[$content].replace(crlfForRichTextRegExp, "\n")
|
||||||
|
: undefined;
|
||||||
|
} else {
|
||||||
|
value = this[$content] || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
return HTMLResult.success({
|
return HTMLResult.success({
|
||||||
name: this[$nodeName],
|
name: this[$nodeName],
|
||||||
attributes: {
|
attributes: {
|
||||||
@ -318,7 +336,7 @@ class XhtmlObject extends XmlObject {
|
|||||||
style: mapStyle(this.style, this),
|
style: mapStyle(this.style, this),
|
||||||
},
|
},
|
||||||
children,
|
children,
|
||||||
value: this[$content] || "",
|
value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,6 +475,10 @@ class P extends XhtmlObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[$text]() {
|
[$text]() {
|
||||||
|
const siblings = this[$getParent]()[$getChildren]();
|
||||||
|
if (siblings[siblings.length - 1] === this) {
|
||||||
|
return super[$text]();
|
||||||
|
}
|
||||||
return super[$text]() + "\n";
|
return super[$text]() + "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
} from "./display_utils.js";
|
} from "./display_utils.js";
|
||||||
import { AnnotationStorage } from "./annotation_storage.js";
|
import { AnnotationStorage } from "./annotation_storage.js";
|
||||||
import { ColorConverters } from "../shared/scripting_utils.js";
|
import { ColorConverters } from "../shared/scripting_utils.js";
|
||||||
|
import { XfaLayer } from "./xfa_layer.js";
|
||||||
|
|
||||||
const DEFAULT_TAB_INDEX = 1000;
|
const DEFAULT_TAB_INDEX = 1000;
|
||||||
const GetElementsByNameSet = new WeakSet();
|
const GetElementsByNameSet = new WeakSet();
|
||||||
@ -322,6 +323,7 @@ class AnnotationElement {
|
|||||||
titleObj: data.titleObj,
|
titleObj: data.titleObj,
|
||||||
modificationDate: data.modificationDate,
|
modificationDate: data.modificationDate,
|
||||||
contentsObj: data.contentsObj,
|
contentsObj: data.contentsObj,
|
||||||
|
richText: data.richText,
|
||||||
hideWrapper: true,
|
hideWrapper: true,
|
||||||
});
|
});
|
||||||
const popup = popupElement.render();
|
const popup = popupElement.render();
|
||||||
@ -676,7 +678,8 @@ class TextAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable });
|
super(parameters, { isRenderable });
|
||||||
}
|
}
|
||||||
@ -1546,7 +1549,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
|||||||
class PopupAnnotationElement extends AnnotationElement {
|
class PopupAnnotationElement extends AnnotationElement {
|
||||||
constructor(parameters) {
|
constructor(parameters) {
|
||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.titleObj?.str || parameters.data.contentsObj?.str
|
parameters.data.titleObj?.str ||
|
||||||
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable });
|
super(parameters, { isRenderable });
|
||||||
}
|
}
|
||||||
@ -1582,6 +1587,7 @@ class PopupAnnotationElement extends AnnotationElement {
|
|||||||
titleObj: this.data.titleObj,
|
titleObj: this.data.titleObj,
|
||||||
modificationDate: this.data.modificationDate,
|
modificationDate: this.data.modificationDate,
|
||||||
contentsObj: this.data.contentsObj,
|
contentsObj: this.data.contentsObj,
|
||||||
|
richText: this.data.richText,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Position the popup next to the parent annotation's container.
|
// Position the popup next to the parent annotation's container.
|
||||||
@ -1614,6 +1620,7 @@ class PopupElement {
|
|||||||
this.titleObj = parameters.titleObj;
|
this.titleObj = parameters.titleObj;
|
||||||
this.modificationDate = parameters.modificationDate;
|
this.modificationDate = parameters.modificationDate;
|
||||||
this.contentsObj = parameters.contentsObj;
|
this.contentsObj = parameters.contentsObj;
|
||||||
|
this.richText = parameters.richText;
|
||||||
this.hideWrapper = parameters.hideWrapper || false;
|
this.hideWrapper = parameters.hideWrapper || false;
|
||||||
|
|
||||||
this.pinned = false;
|
this.pinned = false;
|
||||||
@ -1655,6 +1662,7 @@ class PopupElement {
|
|||||||
const dateObject = PDFDateString.toDateObject(this.modificationDate);
|
const dateObject = PDFDateString.toDateObject(this.modificationDate);
|
||||||
if (dateObject) {
|
if (dateObject) {
|
||||||
const modificationDate = document.createElement("span");
|
const modificationDate = document.createElement("span");
|
||||||
|
modificationDate.className = "popupDate";
|
||||||
modificationDate.textContent = "{{date}}, {{time}}";
|
modificationDate.textContent = "{{date}}, {{time}}";
|
||||||
modificationDate.dataset.l10nId = "annotation_date_string";
|
modificationDate.dataset.l10nId = "annotation_date_string";
|
||||||
modificationDate.dataset.l10nArgs = JSON.stringify({
|
modificationDate.dataset.l10nArgs = JSON.stringify({
|
||||||
@ -1664,8 +1672,20 @@ class PopupElement {
|
|||||||
popup.appendChild(modificationDate);
|
popup.appendChild(modificationDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
const contents = this._formatContents(this.contentsObj);
|
if (
|
||||||
popup.appendChild(contents);
|
this.richText?.str &&
|
||||||
|
(!this.contentsObj?.str || this.contentsObj.str === this.richText.str)
|
||||||
|
) {
|
||||||
|
XfaLayer.render({
|
||||||
|
xfa: this.richText.html,
|
||||||
|
intent: "richText",
|
||||||
|
div: popup,
|
||||||
|
});
|
||||||
|
popup.lastChild.className = "richText popupContent";
|
||||||
|
} else {
|
||||||
|
const contents = this._formatContents(this.contentsObj);
|
||||||
|
popup.appendChild(contents);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Array.isArray(this.trigger)) {
|
if (!Array.isArray(this.trigger)) {
|
||||||
this.trigger = [this.trigger];
|
this.trigger = [this.trigger];
|
||||||
@ -1693,6 +1713,7 @@ class PopupElement {
|
|||||||
*/
|
*/
|
||||||
_formatContents({ str, dir }) {
|
_formatContents({ str, dir }) {
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
|
p.className = "popupContent";
|
||||||
p.dir = dir;
|
p.dir = dir;
|
||||||
const lines = str.split(/(?:\r\n?|\n)/);
|
const lines = str.split(/(?:\r\n?|\n)/);
|
||||||
for (let i = 0, ii = lines.length; i < ii; ++i) {
|
for (let i = 0, ii = lines.length; i < ii; ++i) {
|
||||||
@ -1759,7 +1780,8 @@ class FreeTextAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -1779,7 +1801,8 @@ class LineAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -1824,7 +1847,8 @@ class SquareAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -1871,7 +1895,8 @@ class CircleAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -1918,7 +1943,8 @@ class PolylineAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
|
|
||||||
@ -1983,7 +2009,8 @@ class CaretAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -2003,7 +2030,8 @@ class InkAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
|
|
||||||
@ -2062,7 +2090,8 @@ class HighlightAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, {
|
super(parameters, {
|
||||||
isRenderable,
|
isRenderable,
|
||||||
@ -2090,7 +2119,8 @@ class UnderlineAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, {
|
super(parameters, {
|
||||||
isRenderable,
|
isRenderable,
|
||||||
@ -2118,7 +2148,8 @@ class SquigglyAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, {
|
super(parameters, {
|
||||||
isRenderable,
|
isRenderable,
|
||||||
@ -2146,7 +2177,8 @@ class StrikeOutAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, {
|
super(parameters, {
|
||||||
isRenderable,
|
isRenderable,
|
||||||
@ -2174,7 +2206,8 @@ class StampAnnotationElement extends AnnotationElement {
|
|||||||
const isRenderable = !!(
|
const isRenderable = !!(
|
||||||
parameters.data.hasPopup ||
|
parameters.data.hasPopup ||
|
||||||
parameters.data.titleObj?.str ||
|
parameters.data.titleObj?.str ||
|
||||||
parameters.data.contentsObj?.str
|
parameters.data.contentsObj?.str ||
|
||||||
|
parameters.data.richText?.str
|
||||||
);
|
);
|
||||||
super(parameters, { isRenderable, ignoreBorder: true });
|
super(parameters, { isRenderable, ignoreBorder: true });
|
||||||
}
|
}
|
||||||
@ -2215,7 +2248,9 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
!this.data.hasPopup &&
|
!this.data.hasPopup &&
|
||||||
(this.data.titleObj?.str || this.data.contentsObj?.str)
|
(this.data.titleObj?.str ||
|
||||||
|
this.data.contentsObj?.str ||
|
||||||
|
this.data.richText)
|
||||||
) {
|
) {
|
||||||
this._createPopup(trigger, this.data);
|
this._createPopup(trigger, this.data);
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,9 @@ class XfaLayer {
|
|||||||
if (key === "textContent") {
|
if (key === "textContent") {
|
||||||
html.textContent = value;
|
html.textContent = value;
|
||||||
} else if (key === "class") {
|
} else if (key === "class") {
|
||||||
html.setAttribute(key, value.join(" "));
|
if (value.length) {
|
||||||
|
html.setAttribute(key, value.join(" "));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
|
if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
|
||||||
continue; // Handled below.
|
continue; // Handled below.
|
||||||
@ -159,11 +161,16 @@ class XfaLayer {
|
|||||||
|
|
||||||
const rootDiv = parameters.div;
|
const rootDiv = parameters.div;
|
||||||
rootDiv.appendChild(rootHtml);
|
rootDiv.appendChild(rootHtml);
|
||||||
const transform = `matrix(${parameters.viewport.transform.join(",")})`;
|
|
||||||
rootDiv.style.transform = transform;
|
if (parameters.viewport) {
|
||||||
|
const transform = `matrix(${parameters.viewport.transform.join(",")})`;
|
||||||
|
rootDiv.style.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
// Set defaults.
|
// Set defaults.
|
||||||
rootDiv.setAttribute("class", "xfaLayer xfaFont");
|
if (intent !== "richText") {
|
||||||
|
rootDiv.setAttribute("class", "xfaLayer xfaFont");
|
||||||
|
}
|
||||||
|
|
||||||
// Text nodes used for the text highlighter.
|
// Text nodes used for the text highlighter.
|
||||||
const textDivs = [];
|
const textDivs = [];
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -479,3 +479,4 @@
|
|||||||
!pr12564.pdf
|
!pr12564.pdf
|
||||||
!pr12828.pdf
|
!pr12828.pdf
|
||||||
!secHandler.pdf
|
!secHandler.pdf
|
||||||
|
!rc_annotation.pdf
|
||||||
|
1
test/pdfs/issue13915.pdf.link
Normal file
1
test/pdfs/issue13915.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://github.com/mozilla/pdf.js/files/7024938/2102.12353.pdf
|
BIN
test/pdfs/rc_annotation.pdf
Normal file
BIN
test/pdfs/rc_annotation.pdf
Normal file
Binary file not shown.
@ -137,6 +137,24 @@
|
|||||||
"type": "eq",
|
"type": "eq",
|
||||||
"annotations": true
|
"annotations": true
|
||||||
},
|
},
|
||||||
|
{ "id": "issue2966",
|
||||||
|
"file": "pdfs/rc_annotation.pdf",
|
||||||
|
"md5": "7b978a8c2871b8902656adb67f7bd117",
|
||||||
|
"rounds": 1,
|
||||||
|
"lastPage": 1,
|
||||||
|
"type": "eq",
|
||||||
|
"annotations": true
|
||||||
|
},
|
||||||
|
{ "id": "issue13915",
|
||||||
|
"file": "pdfs/issue13915.pdf",
|
||||||
|
"md5": "fef3108733bbf80ea8551feedb427b1c",
|
||||||
|
"rounds": 1,
|
||||||
|
"firstPage": 51,
|
||||||
|
"lastPage": 51,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq",
|
||||||
|
"annotations": true
|
||||||
|
},
|
||||||
{ "id": "bug946506",
|
{ "id": "bug946506",
|
||||||
"file": "pdfs/bug946506.pdf",
|
"file": "pdfs/bug946506.pdf",
|
||||||
"md5": "c28911b5c31bdc337c2ce404c5971cfc",
|
"md5": "c28911b5c31bdc337c2ce404c5971cfc",
|
||||||
|
@ -46,7 +46,7 @@ describe("XFAParser", function () {
|
|||||||
forbidden
|
forbidden
|
||||||
</dynamicRender>
|
</dynamicRender>
|
||||||
</acrobat7>
|
</acrobat7>
|
||||||
<autoSave>enabled</autoSave>
|
<autoSave>enabled</autoSave>
|
||||||
<submitUrl>
|
<submitUrl>
|
||||||
http://d.e.f
|
http://d.e.f
|
||||||
</submitUrl>
|
</submitUrl>
|
||||||
@ -414,7 +414,7 @@ describe("XFAParser", function () {
|
|||||||
[
|
[
|
||||||
" The first line of this paragraph is indented a half-inch.\n",
|
" The first line of this paragraph is indented a half-inch.\n",
|
||||||
" Successive lines are not indented.\n",
|
" Successive lines are not indented.\n",
|
||||||
" This is the last line of the paragraph.\n \n",
|
" This is the last line of the paragraph.\n ",
|
||||||
].join("")
|
].join("")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -192,17 +192,21 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationLayer .popup span {
|
.annotationLayer .popupDate {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotationLayer .popup p {
|
.annotationLayer .popupContent {
|
||||||
border-top: 1px solid rgba(51, 51, 51, 1);
|
border-top: 1px solid rgba(51, 51, 51, 1);
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotationLayer .richText > * {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.annotationLayer .highlightAnnotation,
|
.annotationLayer .highlightAnnotation,
|
||||||
.annotationLayer .underlineAnnotation,
|
.annotationLayer .underlineAnnotation,
|
||||||
.annotationLayer .squigglyAnnotation,
|
.annotationLayer .squigglyAnnotation,
|
||||||
|
Loading…
Reference in New Issue
Block a user