wxiaoguang 2023-03-07 20:11:24 +08:00 committed by GitHub
parent c84238800b
commit 4c59c8c768
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 17 deletions

View file

@ -4,6 +4,7 @@
package typesniffer package typesniffer
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@ -24,8 +25,9 @@ const (
) )
var ( var (
svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`) svgComment = regexp.MustCompile(`(?s)<!--.*?-->`)
svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!--.*?-->|<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg[\s>\/]`) svgTagRegex = regexp.MustCompile(`(?si)\A\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
svgTagInXMLRegex = regexp.MustCompile(`(?si)\A<\?xml\b.*?\?>\s*(?:(<!DOCTYPE\s+svg([\s:]+.*?>|>))\s*)*<svg\b`)
) )
// SniffedType contains information about a blobs type. // SniffedType contains information about a blobs type.
@ -91,11 +93,18 @@ func DetectContentType(data []byte) SniffedType {
data = data[:sniffLen] data = data[:sniffLen]
} }
if (strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) || // SVG is unsupported by http.DetectContentType, https://github.com/golang/go/issues/15888
strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data) {
// SVG is unsupported. https://github.com/golang/go/issues/15888 detectByHTML := strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")
detectByXML := strings.Contains(ct, "text/xml")
if detectByHTML || detectByXML {
dataProcessed := svgComment.ReplaceAll(data, nil)
dataProcessed = bytes.TrimSpace(dataProcessed)
if detectByHTML && svgTagRegex.Match(dataProcessed) ||
detectByXML && svgTagInXMLRegex.Match(dataProcessed) {
ct = SvgMimeType ct = SvgMimeType
} }
}
return SniffedType{ct} return SniffedType{ct}
} }

View file

@ -28,7 +28,6 @@ func TestIsSvgImage(t *testing.T) {
assert.True(t, DetectContentType([]byte("<svg></svg>")).IsSvgImage()) assert.True(t, DetectContentType([]byte("<svg></svg>")).IsSvgImage())
assert.True(t, DetectContentType([]byte(" <svg></svg>")).IsSvgImage()) assert.True(t, DetectContentType([]byte(" <svg></svg>")).IsSvgImage())
assert.True(t, DetectContentType([]byte(`<svg width="100"></svg>`)).IsSvgImage()) assert.True(t, DetectContentType([]byte(`<svg width="100"></svg>`)).IsSvgImage())
assert.True(t, DetectContentType([]byte("<svg/>")).IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`)).IsSvgImage()) assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`)).IsSvgImage())
assert.True(t, DetectContentType([]byte(`<!-- Comment --> assert.True(t, DetectContentType([]byte(`<!-- Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`)).IsSvgImage())
@ -57,6 +56,10 @@ func TestIsSvgImage(t *testing.T) {
<!-- Multline <!-- Multline
Comment --> Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`)).IsSvgImage())
// the DetectContentType should work for incomplete data, because only beginning bytes are used for detection
assert.True(t, DetectContentType([]byte(`<svg>....`)).IsSvgImage())
assert.False(t, DetectContentType([]byte{}).IsSvgImage()) assert.False(t, DetectContentType([]byte{}).IsSvgImage())
assert.False(t, DetectContentType([]byte("svg")).IsSvgImage()) assert.False(t, DetectContentType([]byte("svg")).IsSvgImage())
assert.False(t, DetectContentType([]byte("<svgfoo></svgfoo>")).IsSvgImage()) assert.False(t, DetectContentType([]byte("<svgfoo></svgfoo>")).IsSvgImage())
@ -68,6 +71,26 @@ func TestIsSvgImage(t *testing.T) {
assert.False(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.False(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!-- <svg></svg> inside comment --> <!-- <svg></svg> inside comment -->
<foo></foo>`)).IsSvgImage()) <foo></foo>`)).IsSvgImage())
assert.False(t, DetectContentType([]byte(`
<!-- comment1 -->
<div>
<!-- comment2 -->
<svg></svg>
</div>
`)).IsSvgImage())
assert.False(t, DetectContentType([]byte(`
<!-- comment1
-->
<div>
<!-- comment2
-->
<svg></svg>
</div>
`)).IsSvgImage())
assert.False(t, DetectContentType([]byte(`<html><body><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg></svg></body></html>`)).IsSvgImage())
assert.False(t, DetectContentType([]byte(`<html><body><?xml version="1.0" encoding="UTF-8"?><svg></svg></body></html>`)).IsSvgImage())
} }
func TestIsPDF(t *testing.T) { func TestIsPDF(t *testing.T) {

View file

@ -129,8 +129,8 @@ export function initImageDiff() {
initOverlay(createContext($imageAfter[2], $imageBefore[2])); initOverlay(createContext($imageAfter[2], $imageBefore[2]));
} }
hideElem($container.find('> .loader'));
$container.find('> .gt-hidden').removeClass('gt-hidden'); $container.find('> .gt-hidden').removeClass('gt-hidden');
hideElem($container.find('.ui.loader'));
} }
function initSideBySide(sizes) { function initSideBySide(sizes) {
@ -155,7 +155,7 @@ export function initImageDiff() {
height: sizes.size1.height * factor height: sizes.size1.height * factor
}); });
sizes.image1.parent().css({ sizes.image1.parent().css({
margin: `${sizes.ratio[1] * factor + 15}px ${sizes.ratio[0] * factor}px ${sizes.ratio[1] * factor}px`, margin: `10px auto`,
width: sizes.size1.width * factor + 2, width: sizes.size1.width * factor + 2,
height: sizes.size1.height * factor + 2 height: sizes.size1.height * factor + 2
}); });
@ -164,7 +164,7 @@ export function initImageDiff() {
height: sizes.size2.height * factor height: sizes.size2.height * factor
}); });
sizes.image2.parent().css({ sizes.image2.parent().css({
margin: `${sizes.ratio[3] * factor}px ${sizes.ratio[2] * factor}px`, margin: `10px auto`,
width: sizes.size2.width * factor + 2, width: sizes.size2.width * factor + 2,
height: sizes.size2.height * factor + 2 height: sizes.size2.height * factor + 2
}); });
@ -255,13 +255,12 @@ export function initImageDiff() {
width: sizes.size2.width * factor + 2, width: sizes.size2.width * factor + 2,
height: sizes.size2.height * factor + 2 height: sizes.size2.height * factor + 2
}); });
// some inner elements are `position: absolute`, so the container's height must be large enough
// the "css(width, height)" is somewhat hacky and not easy to understand, it could be improved in the future
sizes.image2.parent().parent().css({ sizes.image2.parent().parent().css({
width: sizes.max.width * factor + 2, width: sizes.max.width * factor + 2,
height: sizes.max.height * factor + 2 height: sizes.max.height * factor + 2 + 20 /* extra height for inner "position: absolute" elements */,
});
$container.find('.onion-skin').css({
width: sizes.max.width * factor + 2,
height: sizes.max.height * factor + 4
}); });
const $range = $container.find("input[type='range']"); const $range = $container.find("input[type='range']");

View file

@ -1,6 +1,6 @@
.image-diff-container { .image-diff-container {
text-align: center; text-align: center;
padding: 30px 0; padding: 1em 0;
img { img {
border: 1px solid var(--color-primary-light-7); border: 1px solid var(--color-primary-light-7);
@ -22,6 +22,7 @@
display: inline-block; display: inline-block;
line-height: 0; line-height: 0;
vertical-align: top; vertical-align: top;
margin: 0 1em;
.side-header { .side-header {
font-weight: bold; font-weight: bold;
@ -98,7 +99,7 @@
} }
input { input {
width: 300px; max-width: 300px;
} }
} }
} }