mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
fix: detect opaque SVG backgrounds without a viewBox
Opaque background detection only read canvas dimensions from the viewBox, so an SVG that declares width and height on the root element but omits the viewBox slipped through and was tinted into a solid block. Fall back to the root svg width and height when no viewBox is present, and match attribute names exactly so stroke-width is not mistaken for the canvas width. Add tests for the no-viewBox cases.
This commit is contained in:
parent
85260671fc
commit
85163e4cbb
2 changed files with 38 additions and 4 deletions
|
|
@ -118,6 +118,24 @@ describe('isMonochromeSvg', () => {
|
|||
'<svg viewBox="0 0 24 24"><rect width="24" height="24" fill="none" /><path fill="#333" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects a background rect using root width/height when no viewBox is present', () => {
|
||||
const svg =
|
||||
'<svg width="24" height="24"><rect width="24" height="24" fill="#fff" /><path fill="#000" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects a background rect when root dimensions carry units', () => {
|
||||
const svg =
|
||||
'<svg width="48px" height="48px"><rect width="48" height="48" fill="black" /><path fill="white" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(false);
|
||||
});
|
||||
|
||||
it('does not confuse stroke-width with the canvas width', () => {
|
||||
const svg =
|
||||
'<svg width="24" height="24" stroke-width="2"><rect width="24" height="24" fill="#eee" /><path fill="#222" /></svg>';
|
||||
expect(isMonochromeSvg(svg)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -104,13 +104,27 @@ function extractColors(svg: string): string[] {
|
|||
}
|
||||
|
||||
const VIEWBOX_REGEX = /viewBox\s*=\s*["']\s*[-\d.]+\s+[-\d.]+\s+([\d.]+)\s+([\d.]+)/i;
|
||||
const SVG_TAG_REGEX = /<svg\b[^>]*>/i;
|
||||
const RECT_REGEX = /<rect\b[^>]*>/gi;
|
||||
|
||||
function getAttr(tag: string, name: string): string | null {
|
||||
const match = tag.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, 'i'));
|
||||
const match = tag.match(new RegExp(`(?<![-\\w])${name}\\s*=\\s*["']([^"']*)["']`, 'i'));
|
||||
return match ? match[1].trim() : null;
|
||||
}
|
||||
|
||||
function rootDimension(svg: string, name: 'width' | 'height'): number | null {
|
||||
const tag = svg.match(SVG_TAG_REGEX);
|
||||
if (!tag) {
|
||||
return null;
|
||||
}
|
||||
const value = getAttr(tag[0], name);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
const size = parseFloat(value);
|
||||
return Number.isNaN(size) ? null : size;
|
||||
}
|
||||
|
||||
function coversCanvas(value: string | null, canvas: number | null): boolean {
|
||||
if (value == null) {
|
||||
return false;
|
||||
|
|
@ -127,13 +141,15 @@ function coversCanvas(value: string | null, canvas: number | null): boolean {
|
|||
|
||||
/**
|
||||
* Detects a full-canvas opaque background (a `<rect>` at the origin spanning the
|
||||
* viewBox). Such SVGs cannot be tinted via a CSS mask, since the opaque
|
||||
* canvas). Such SVGs cannot be tinted via a CSS mask, since the opaque
|
||||
* background fills the whole area with the tint color instead of the glyph.
|
||||
* Canvas dimensions come from the viewBox, falling back to the root `<svg>`
|
||||
* width and height when no viewBox is present.
|
||||
*/
|
||||
function hasOpaqueBackground(svg: string): boolean {
|
||||
const viewBox = svg.match(VIEWBOX_REGEX);
|
||||
const width = viewBox ? parseFloat(viewBox[1]) : null;
|
||||
const height = viewBox ? parseFloat(viewBox[2]) : null;
|
||||
const width = viewBox ? parseFloat(viewBox[1]) : rootDimension(svg, 'width');
|
||||
const height = viewBox ? parseFloat(viewBox[2]) : rootDimension(svg, 'height');
|
||||
|
||||
const rects = svg.match(RECT_REGEX) ?? [];
|
||||
for (const rect of rects) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue