From d6720fa48d78cc313d6418acd8cbdaeda965285c Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 25 Apr 2024 17:15:20 -0400 Subject: changed marqueeAnnotator to save inline annotations as text strings instead of Docs. enabled making image crops of text selections on PDFs. cleaned up webboxrendered lint promses, and Annotation render --- src/client/views/MarqueeAnnotator.tsx | 27 +++--- src/client/views/StyleProvider.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 + src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/nodes/WebBoxRenderer.js | 133 ++++++++++++-------------- src/client/views/pdf/Annotation.tsx | 157 +++++++++++++++---------------- src/client/views/pdf/PDFViewer.tsx | 6 +- 7 files changed, 157 insertions(+), 174 deletions(-) (limited to 'src') diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index c4c00e0c3..ea73a53a9 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -100,22 +100,19 @@ export class MarqueeAnnotator extends ObservableReactComponent value.forEach(anno => { - const textRegion = new Doc(); - textRegion.x = parseInt(anno.style.left ?? '0'); - textRegion.y = parseInt(anno.style.top ?? '0'); - textRegion._height = parseInt(anno.style.height ?? '0'); - textRegion._width = parseInt(anno.style.width ?? '0'); - textRegion.embedContainer = textRegionAnnoProto; - textRegion.backgroundColor = color; - annoDocs.push(textRegion); + const x = parseInt(anno.style.left ?? '0'); + const y = parseInt(anno.style.top ?? '0'); + const height = parseInt(anno.style.height ?? '0'); + const width = parseInt(anno.style.width ?? '0'); + annoRects.push(`${x}:${y}:${width}:${height}`); anno.remove(); - minY = Math.min(NumCast(textRegion.y), minY); - minX = Math.min(NumCast(textRegion.x), minX); - maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY); - maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX); + minY = Math.min(NumCast(y), minY); + minX = Math.min(NumCast(x), minX); + maxY = Math.max(NumCast(y) + NumCast(height), maxY); + maxX = Math.max(NumCast(x) + NumCast(width), maxX); }) ); @@ -123,8 +120,10 @@ export class MarqueeAnnotator extends ObservableReactComponent(annoDocs); + textRegionAnnoProto.text_inlineAnnotations = new List(annoRects); + textRegionAnnoProto.opacity = 0; textRegionAnnoProto.layout_unrendered = true; savedAnnoMap.clear(); return textRegionAnno; diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3697aa010..6d4f69e6e 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -177,7 +177,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt() implem crop = (region: Doc | undefined, addCrop?: boolean) => { if (!region) return undefined; const cropping = Doc.MakeCopy(region, true); + cropping.layout_unrendered = false; // text selection have this + cropping.text_inlineAnnotations = undefined; // text selections have this -- it causes them not to be rendered. + cropping.backgroundColor = undefined; // text selections have this -- it causes images to be fully transparent + cropping.opacity = undefined; // text selections have this -- it causes images to be fully transparent const regionData = region[DocData]; regionData.lockedPosition = true; regionData.title = 'region:' + this.Document.title; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index fc2e4bf61..c0fffd66c 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1030,7 +1030,7 @@ export class WebBox extends ViewBoxAnnotatableComponent() implem .sort((a, b) => NumCast(a.y) - NumCast(b.y)) .map(anno => ( // eslint-disable-next-line react/jsx-props-no-spreading - + ))} ); diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js index 914adb404..6fb8f4957 100644 --- a/src/client/views/nodes/WebBoxRenderer.js +++ b/src/client/views/nodes/WebBoxRenderer.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ /** * * @param {StyleSheetList} styleSheets @@ -9,15 +10,14 @@ const ForeignHtmlRenderer = function (styleSheets) { * * @param {String} binStr */ - const binaryStringToBase64 = function (binStr) { - return new Promise(resolve => { + const binaryStringToBase64 = binStr => + new Promise(resolve => { const reader = new FileReader(); reader.readAsDataURL(binStr); reader.onloadend = function () { resolve(reader.result); }; }); - }; function prepend(extension) { return window.location.origin + extension; @@ -30,8 +30,8 @@ const ForeignHtmlRenderer = function (styleSheets) { * @param {String} url * @returns {Promise} */ - const getResourceAsBase64 = function (webUrl, inurl) { - return new Promise((resolve, reject) => { + const getResourceAsBase64 = (webUrl, inurl) => + new Promise(resolve => { const xhr = new XMLHttpRequest(); // const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl; // const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl; @@ -67,16 +67,15 @@ const ForeignHtmlRenderer = function (styleSheets) { xhr.send(null); }); - }; /** * * @param {String[]} urls * @returns {Promise} */ - const getMultipleResourcesAsBase64 = function (webUrl, urls) { + const getMultipleResourcesAsBase64 = (webUrl, urls) => { const promises = []; - for (let i = 0; i < urls.length; i += 1) { + for (let i = 0; webUrl && i < urls.length; i += 1) { promises.push(getResourceAsBase64(webUrl, urls[i])); } return Promise.all(promises); @@ -130,6 +129,7 @@ const ForeignHtmlRenderer = function (styleSheets) { const urlsFound = []; let searchStartIndex = 0; + // eslint-disable-next-line no-constant-condition while (true) { const url = parseValue(cssRuleStr, searchStartIndex, selector, delimiters); if (url === null) { @@ -155,9 +155,6 @@ const ForeignHtmlRenderer = function (styleSheets) { const getImageUrlsFromFromHtml = function (html) { return getUrlsFromCssString(html, 'src=', [' ', '>', '\t'], true); }; - const getSourceUrlsFromFromHtml = function (html) { - return getUrlsFromCssString(html, 'source=', [' ', '>', '\t'], true); - }; const escapeRegExp = function (string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string @@ -171,46 +168,45 @@ const ForeignHtmlRenderer = function (styleSheets) { * * @returns {Promise} */ - const buildSvgDataUri = async function (webUrl, inputContentHtml, width, height, scroll, xoff) { - return new Promise(async (resolve, reject) => { - /* !! The problems !! - * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with) - * 2. Platform won't wait for external assets to load (fonts, images, etc.) - */ - - // copy styles - let cssStyles = ''; - const urlsFoundInCss = []; - - for (let i = 0; i < styleSheets.length; i += 1) { - try { - const rules = styleSheets[i].cssRules; - for (let j = 0; j < rules.length; j += 1) { - const cssRuleStr = rules[j].cssText; - urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr)); - cssStyles += cssRuleStr; - } - } catch (e) { - /* empty */ + const buildSvgDataUri = (webUrl, inputContentHtml, width, height, scroll, xoff) => { + /* !! The problems !! + * 1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with) + * 2. Platform won't wait for external assets to load (fonts, images, etc.) + */ + + // copy styles + let cssStyles = ''; + const urlsFoundInCss = []; + + for (let i = 0; i < styleSheets.length; i += 1) { + try { + const rules = styleSheets[i].cssRules; + for (let j = 0; j < rules.length; j += 1) { + const cssRuleStr = rules[j].cssText; + urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr)); + cssStyles += cssRuleStr; } + } catch (e) { + /* empty */ } + } - // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss); - // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) { - // const r = fetchedResourcesFromStylesheets[i]; - // if (r.resourceUrl) { - // cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64); - // } - // } - - let contentHtml = inputContentHtml - .replace(/]*>/g, '') // tags have a which has a srcset field of image refs. instead of converting each, just use the default of the picture - .replace(/noscript/g, 'div') - .replace(/
<\/div>/g, '') // when scripting isn't available (ie, rendering web pages here),