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 { StringStream } from "./stream.js";
|
||||
import { writeDict } from "./writer.js";
|
||||
import { XFAFactory } from "./xfa/factory.js";
|
||||
|
||||
class AnnotationFactory {
|
||||
/**
|
||||
@ -1098,6 +1099,10 @@ class MarkupAnnotation extends Annotation {
|
||||
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.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 {
|
||||
constructor() {
|
||||
constructor(rootNameSpace = null) {
|
||||
this._namespaceStack = [];
|
||||
this._nsAgnosticLevel = 0;
|
||||
|
||||
@ -76,7 +76,8 @@ class Builder {
|
||||
this._nextNsId = Math.max(
|
||||
...Object.values(NamespaceIds).map(({ id }) => id)
|
||||
);
|
||||
this._currentNamespace = new UnknownNamespace(++this._nextNsId);
|
||||
this._currentNamespace =
|
||||
rootNameSpace || new UnknownNamespace(++this._nextNsId);
|
||||
}
|
||||
|
||||
buildRoot(ids) {
|
||||
|
@ -13,13 +13,20 @@
|
||||
* 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 { DataHandler } from "./data.js";
|
||||
import { FontFinder } from "./fonts.js";
|
||||
import { stripQuotes } from "./utils.js";
|
||||
import { warn } from "../../shared/util.js";
|
||||
import { XFAParser } from "./parser.js";
|
||||
import { XhtmlNamespace } from "./xhtml.js";
|
||||
|
||||
class XFAFactory {
|
||||
constructor(data) {
|
||||
@ -106,6 +113,43 @@ class XFAFactory {
|
||||
}
|
||||
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 };
|
||||
|
@ -606,10 +606,16 @@ function setPara(node, nodeStyle, value) {
|
||||
}
|
||||
|
||||
function setFontFamily(xfaFont, node, fontFinder, style) {
|
||||
const name = stripQuotes(xfaFont.typeface);
|
||||
const typeface = fontFinder.find(name);
|
||||
if (!fontFinder) {
|
||||
// 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}"`;
|
||||
|
||||
const typeface = fontFinder.find(name);
|
||||
if (typeface) {
|
||||
const { fontFamily } = typeface.regular.cssFontInfo;
|
||||
if (fontFamily !== name) {
|
||||
|
@ -30,9 +30,9 @@ import { Builder } from "./builder.js";
|
||||
import { warn } from "../../shared/util.js";
|
||||
|
||||
class XFAParser extends XMLParserBase {
|
||||
constructor() {
|
||||
constructor(rootNameSpace = null, richText = false) {
|
||||
super();
|
||||
this._builder = new Builder();
|
||||
this._builder = new Builder(rootNameSpace);
|
||||
this._stack = [];
|
||||
this._globalData = {
|
||||
usedTypefaces: new Set(),
|
||||
@ -42,6 +42,7 @@ class XFAParser extends XMLParserBase {
|
||||
this._errorCode = XMLParserErrorCode.NoError;
|
||||
this._whiteRegex = /^\s+$/;
|
||||
this._nbsps = /\xa0+/g;
|
||||
this._richText = richText;
|
||||
}
|
||||
|
||||
parse(data) {
|
||||
@ -60,8 +61,8 @@ class XFAParser extends XMLParserBase {
|
||||
// Normally by definition a   is unbreakable
|
||||
// but in real life Acrobat can break strings on  .
|
||||
text = text.replace(this._nbsps, match => match.slice(1) + " ");
|
||||
if (this._current[$acceptWhitespace]()) {
|
||||
this._current[$onText](text);
|
||||
if (this._richText || this._current[$acceptWhitespace]()) {
|
||||
this._current[$onText](text, this._richText);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
$content,
|
||||
$extra,
|
||||
$getChildren,
|
||||
$getParent,
|
||||
$globalData,
|
||||
$nodeName,
|
||||
$onText,
|
||||
@ -38,6 +39,7 @@ import {
|
||||
import { getMeasurement, HTMLResult, stripQuotes } from "./utils.js";
|
||||
|
||||
const XHTML_NS_ID = NamespaceIds.xhtml.id;
|
||||
const $richText = Symbol();
|
||||
|
||||
const VALID_STYLES = new Set([
|
||||
"color",
|
||||
@ -109,6 +111,7 @@ const StyleMapping = new Map([
|
||||
|
||||
const spacesRegExp = /\s+/g;
|
||||
const crlfRegExp = /[\r\n]+/g;
|
||||
const crlfForRichTextRegExp = /\r\n?/g;
|
||||
|
||||
function mapStyle(styleStr, node) {
|
||||
const style = Object.create(null);
|
||||
@ -185,6 +188,7 @@ const NoWhites = new Set(["body", "html"]);
|
||||
class XhtmlObject extends XmlObject {
|
||||
constructor(attributes, name) {
|
||||
super(XHTML_NS_ID, name);
|
||||
this[$richText] = false;
|
||||
this.style = attributes.style || "";
|
||||
}
|
||||
|
||||
@ -197,11 +201,16 @@ class XhtmlObject extends XmlObject {
|
||||
return !NoWhites.has(this[$nodeName]);
|
||||
}
|
||||
|
||||
[$onText](str) {
|
||||
str = str.replace(crlfRegExp, "");
|
||||
if (!this.style.includes("xfa-spacerun:yes")) {
|
||||
str = str.replace(spacesRegExp, " ");
|
||||
[$onText](str, richText = false) {
|
||||
if (!richText) {
|
||||
str = str.replace(crlfRegExp, "");
|
||||
if (!this.style.includes("xfa-spacerun:yes")) {
|
||||
str = str.replace(spacesRegExp, " ");
|
||||
}
|
||||
} else {
|
||||
this[$richText] = true;
|
||||
}
|
||||
|
||||
if (str) {
|
||||
this[$content] += str;
|
||||
}
|
||||
@ -311,6 +320,15 @@ class XhtmlObject extends XmlObject {
|
||||
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({
|
||||
name: this[$nodeName],
|
||||
attributes: {
|
||||
@ -318,7 +336,7 @@ class XhtmlObject extends XmlObject {
|
||||
style: mapStyle(this.style, this),
|
||||
},
|
||||
children,
|
||||
value: this[$content] || "",
|
||||
value,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -457,6 +475,10 @@ class P extends XhtmlObject {
|
||||
}
|
||||
|
||||
[$text]() {
|
||||
const siblings = this[$getParent]()[$getChildren]();
|
||||
if (siblings[siblings.length - 1] === this) {
|
||||
return super[$text]();
|
||||
}
|
||||
return super[$text]() + "\n";
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
} from "./display_utils.js";
|
||||
import { AnnotationStorage } from "./annotation_storage.js";
|
||||
import { ColorConverters } from "../shared/scripting_utils.js";
|
||||
import { XfaLayer } from "./xfa_layer.js";
|
||||
|
||||
const DEFAULT_TAB_INDEX = 1000;
|
||||
const GetElementsByNameSet = new WeakSet();
|
||||
@ -322,6 +323,7 @@ class AnnotationElement {
|
||||
titleObj: data.titleObj,
|
||||
modificationDate: data.modificationDate,
|
||||
contentsObj: data.contentsObj,
|
||||
richText: data.richText,
|
||||
hideWrapper: true,
|
||||
});
|
||||
const popup = popupElement.render();
|
||||
@ -676,7 +678,8 @@ class TextAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable });
|
||||
}
|
||||
@ -1546,7 +1549,9 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement {
|
||||
class PopupAnnotationElement extends AnnotationElement {
|
||||
constructor(parameters) {
|
||||
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 });
|
||||
}
|
||||
@ -1582,6 +1587,7 @@ class PopupAnnotationElement extends AnnotationElement {
|
||||
titleObj: this.data.titleObj,
|
||||
modificationDate: this.data.modificationDate,
|
||||
contentsObj: this.data.contentsObj,
|
||||
richText: this.data.richText,
|
||||
});
|
||||
|
||||
// Position the popup next to the parent annotation's container.
|
||||
@ -1614,6 +1620,7 @@ class PopupElement {
|
||||
this.titleObj = parameters.titleObj;
|
||||
this.modificationDate = parameters.modificationDate;
|
||||
this.contentsObj = parameters.contentsObj;
|
||||
this.richText = parameters.richText;
|
||||
this.hideWrapper = parameters.hideWrapper || false;
|
||||
|
||||
this.pinned = false;
|
||||
@ -1655,6 +1662,7 @@ class PopupElement {
|
||||
const dateObject = PDFDateString.toDateObject(this.modificationDate);
|
||||
if (dateObject) {
|
||||
const modificationDate = document.createElement("span");
|
||||
modificationDate.className = "popupDate";
|
||||
modificationDate.textContent = "{{date}}, {{time}}";
|
||||
modificationDate.dataset.l10nId = "annotation_date_string";
|
||||
modificationDate.dataset.l10nArgs = JSON.stringify({
|
||||
@ -1664,8 +1672,20 @@ class PopupElement {
|
||||
popup.appendChild(modificationDate);
|
||||
}
|
||||
|
||||
const contents = this._formatContents(this.contentsObj);
|
||||
popup.appendChild(contents);
|
||||
if (
|
||||
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)) {
|
||||
this.trigger = [this.trigger];
|
||||
@ -1693,6 +1713,7 @@ class PopupElement {
|
||||
*/
|
||||
_formatContents({ str, dir }) {
|
||||
const p = document.createElement("p");
|
||||
p.className = "popupContent";
|
||||
p.dir = dir;
|
||||
const lines = str.split(/(?:\r\n?|\n)/);
|
||||
for (let i = 0, ii = lines.length; i < ii; ++i) {
|
||||
@ -1759,7 +1780,8 @@ class FreeTextAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -1779,7 +1801,8 @@ class LineAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -1824,7 +1847,8 @@ class SquareAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -1871,7 +1895,8 @@ class CircleAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -1918,7 +1943,8 @@ class PolylineAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
|
||||
@ -1983,7 +2009,8 @@ class CaretAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -2003,7 +2030,8 @@ class InkAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
|
||||
@ -2062,7 +2090,8 @@ class HighlightAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, {
|
||||
isRenderable,
|
||||
@ -2090,7 +2119,8 @@ class UnderlineAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, {
|
||||
isRenderable,
|
||||
@ -2118,7 +2148,8 @@ class SquigglyAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, {
|
||||
isRenderable,
|
||||
@ -2146,7 +2177,8 @@ class StrikeOutAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, {
|
||||
isRenderable,
|
||||
@ -2174,7 +2206,8 @@ class StampAnnotationElement extends AnnotationElement {
|
||||
const isRenderable = !!(
|
||||
parameters.data.hasPopup ||
|
||||
parameters.data.titleObj?.str ||
|
||||
parameters.data.contentsObj?.str
|
||||
parameters.data.contentsObj?.str ||
|
||||
parameters.data.richText?.str
|
||||
);
|
||||
super(parameters, { isRenderable, ignoreBorder: true });
|
||||
}
|
||||
@ -2215,7 +2248,9 @@ class FileAttachmentAnnotationElement extends AnnotationElement {
|
||||
|
||||
if (
|
||||
!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);
|
||||
}
|
||||
|
@ -106,7 +106,9 @@ class XfaLayer {
|
||||
if (key === "textContent") {
|
||||
html.textContent = value;
|
||||
} else if (key === "class") {
|
||||
html.setAttribute(key, value.join(" "));
|
||||
if (value.length) {
|
||||
html.setAttribute(key, value.join(" "));
|
||||
}
|
||||
} else {
|
||||
if (isHTMLAnchorElement && (key === "href" || key === "newWindow")) {
|
||||
continue; // Handled below.
|
||||
@ -159,11 +161,16 @@ class XfaLayer {
|
||||
|
||||
const rootDiv = parameters.div;
|
||||
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.
|
||||
rootDiv.setAttribute("class", "xfaLayer xfaFont");
|
||||
if (intent !== "richText") {
|
||||
rootDiv.setAttribute("class", "xfaLayer xfaFont");
|
||||
}
|
||||
|
||||
// Text nodes used for the text highlighter.
|
||||
const textDivs = [];
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -479,3 +479,4 @@
|
||||
!pr12564.pdf
|
||||
!pr12828.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",
|
||||
"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",
|
||||
"file": "pdfs/bug946506.pdf",
|
||||
"md5": "c28911b5c31bdc337c2ce404c5971cfc",
|
||||
|
@ -414,7 +414,7 @@ describe("XFAParser", function () {
|
||||
[
|
||||
" The first line of this paragraph is indented a half-inch.\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("")
|
||||
);
|
||||
});
|
||||
|
@ -192,17 +192,21 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.annotationLayer .popup span {
|
||||
.annotationLayer .popupDate {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.annotationLayer .popup p {
|
||||
.annotationLayer .popupContent {
|
||||
border-top: 1px solid rgba(51, 51, 51, 1);
|
||||
margin-top: 2px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.annotationLayer .richText > * {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.annotationLayer .highlightAnnotation,
|
||||
.annotationLayer .underlineAnnotation,
|
||||
.annotationLayer .squigglyAnnotation,
|
||||
|
Loading…
Reference in New Issue
Block a user