diff options
Diffstat (limited to 'src/client/views/nodes/WebBoxRenderer.js')
-rw-r--r-- | src/client/views/nodes/WebBoxRenderer.js | 103 |
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) { |