aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/WebBoxRenderer.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/WebBoxRenderer.js')
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index ef465c453..31e0ef5e4 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -146,6 +146,29 @@ const ForeignHtmlRenderer = function (styleSheets) {
};
/**
+ * Extracts font-face URLs from CSS rules
+ * @param {String} cssRuleStr
+ * @returns {String[]}
+ */
+ const getFontFaceUrlsFromCss = function (cssRuleStr) {
+ const fontFaceUrls = [];
+ // Find @font-face blocks
+ const fontFaceBlocks = cssRuleStr.match(/@font-face\s*{[^}]*}/g) || [];
+
+ fontFaceBlocks.forEach(block => {
+ // Extract URLs from src properties
+ const urls = block.match(/src\s*:\s*[^;]*/g) || [];
+ urls.forEach(srcDeclaration => {
+ // Find all url() references in the src declaration
+ const fontUrls = getUrlsFromCssString(srcDeclaration);
+ fontFaceUrls.push(...fontUrls);
+ });
+ });
+
+ return fontFaceUrls;
+ };
+
+ /**
*
* @param {String} html
* @returns {String[]}
@@ -159,6 +182,61 @@ const ForeignHtmlRenderer = function (styleSheets) {
};
/**
+ * Create a fallback font-face rule for handling CORS errors
+ * @returns {String}
+ */
+ const createFallbackFontFaceRules = function () {
+ return `
+ @font-face {
+ font-family: 'CORS-fallback-serif';
+ src: local('Times New Roman'), local('Georgia'), serif;
+ }
+ @font-face {
+ font-family: 'CORS-fallback-sans';
+ src: local('Arial'), local('Helvetica'), sans-serif;
+ }
+ /* Add fallback font handling */
+ [data-font-error] {
+ font-family: 'CORS-fallback-sans', sans-serif !important;
+ }
+ [data-font-error="serif"] {
+ font-family: 'CORS-fallback-serif', serif !important;
+ }
+ `;
+ };
+
+ /**
+ * Clean up and optimize CSS for better rendering
+ * @param {String} cssStyles
+ * @returns {String}
+ */
+ const optimizeCssForRendering = function (cssStyles) {
+ // Add fallback font-face rules
+ const enhanced = cssStyles + createFallbackFontFaceRules();
+
+ // Replace problematic font-face declarations with proxied versions
+ let optimized = enhanced.replace(/(url\(['"]?)(https?:\/\/[^)'"]+)(['"]?\))/gi, (match, prefix, url, suffix) => {
+ // If it's a font file, proxy it
+ if (url.match(/\.(woff2?|ttf|eot|otf)(\?.*)?$/i)) {
+ return `${prefix}${CorsProxy(url)}${suffix}`;
+ }
+ return match;
+ });
+
+ // Add error handling for fonts
+ optimized += `
+ /* Suppress font CORS errors in console */
+ @supports (font-display: swap) {
+ @font-face {
+ font-display: swap !important;
+ }
+ }
+ `;
+
+ return optimized;
+ };
+
+ /**
*
* @param {String} contentHtml
* @param {Number} width
@@ -175,6 +253,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
// copy styles
let cssStyles = '';
const urlsFoundInCss = [];
+ const fontUrlsInCss = [];
for (let i = 0; i < styleSheets.length; i += 1) {
try {
@@ -182,6 +261,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
for (let j = 0; j < rules.length; j += 1) {
const cssRuleStr = rules[j].cssText;
urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
+ fontUrlsInCss.push(...getFontFaceUrlsFromCss(cssRuleStr));
cssStyles += cssRuleStr;
}
} catch (e) {
@@ -189,6 +269,9 @@ const ForeignHtmlRenderer = function (styleSheets) {
}
}
+ // Optimize and enhance CSS
+ cssStyles = optimizeCssForRendering(cssStyles);
+
// const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
// for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) {
// const r = fetchedResourcesFromStylesheets[i];
@@ -203,6 +286,26 @@ const ForeignHtmlRenderer = function (styleSheets) {
.replace(/<div class="mediaset"><\/div>/g, '') // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s. But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
.replace(/<link[^>]*>/g, '') // don't need to keep any linked style sheets because we've already processed all style sheets above
.replace(/srcset="([^ "]*)[^"]*"/g, 'src="$1"'); // instead of converting each item in the srcset to a data url, just convert the first one and use that
+
+ // Add script to handle font loading errors
+ contentHtml += `
+ <script>
+ // Handle font loading errors with fallbacks
+ document.addEventListener('DOMContentLoaded', function() {
+ // Mark elements with font issues
+ document.querySelectorAll('*').forEach(function(el) {
+ const style = window.getComputedStyle(el);
+ const fontFamily = style.getPropertyValue('font-family');
+ if (fontFamily && !fontFamily.includes('serif') && !fontFamily.includes('sans')) {
+ el.setAttribute('data-font-error', 'sans');
+ } else if (fontFamily && fontFamily.includes('serif')) {
+ el.setAttribute('data-font-error', 'serif');
+ }
+ });
+ });
+ </script>
+ `;
+
const urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith('data:'));
return getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml).then(fetchedResources => {
for (let i = 0; i < fetchedResources.length; i += 1) {