Fix various ImageDiff/SVG bugs (#23312)
Replace #23310, Close #19733 And fix various UI problems, including regressions from #22959 #22950 and more. ## SVG Detection The old regexp may mismatch non-SVG files. This PR adds new tests for those cases. ## UI Changes ### Before ![image](https://user-images.githubusercontent.com/2114189/222967716-f6ad8721-f46a-4a3f-9eb0-a89e488d3436.png) ![image](https://user-images.githubusercontent.com/2114189/222967780-8af8981a-e69d-4304-9dc4-0235582fa4f4.png) ### After ![image](https://user-images.githubusercontent.com/2114189/222967575-c21c23d4-0200-4e09-aac3-57895e853000.png) ![image](https://user-images.githubusercontent.com/2114189/222967585-8b8da262-bc96-441a-9851-8d3845f2659d.png) ![image](https://user-images.githubusercontent.com/2114189/222967595-58d9bea5-6df4-41fa-bf8a-86704117959d.png) ![image](https://user-images.githubusercontent.com/2114189/222967608-38757c1a-b8bd-4ebf-b7a8-3b30edb7f303.png) ![image](https://user-images.githubusercontent.com/2114189/222967623-9849a339-6fae-4484-8fa5-939e2fdacbf5.png) ![image](https://user-images.githubusercontent.com/2114189/222967633-4383d7dd-62ba-47a3-8c10-86f7ca7757ae.png) --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
parent
c84238800b
commit
4c59c8c768
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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']");
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue