TF-3622 Fix deformed inlined image
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -27,24 +27,11 @@ class ImageTransformer extends DomTransformer {
|
|||||||
if (imageElements.isEmpty) return;
|
if (imageElements.isEmpty) return;
|
||||||
|
|
||||||
await Future.wait(imageElements.map((imageElement) async {
|
await Future.wait(imageElements.map((imageElement) async {
|
||||||
var exStyle = imageElement.attributes['style'];
|
|
||||||
if (exStyle != null) {
|
|
||||||
if (!exStyle.trim().endsWith(';')) {
|
|
||||||
exStyle = '$exStyle;';
|
|
||||||
}
|
|
||||||
if (!exStyle.contains('display')) {
|
|
||||||
exStyle = '$exStyle display:inline;';
|
|
||||||
}
|
|
||||||
if (!exStyle.contains('max-width')) {
|
|
||||||
exStyle = '$exStyle max-width:100%;';
|
|
||||||
}
|
|
||||||
imageElement.attributes['style'] = exStyle;
|
|
||||||
} else {
|
|
||||||
imageElement.attributes['style'] = 'display:inline;max-width:100%;';
|
|
||||||
}
|
|
||||||
final src = imageElement.attributes['src'];
|
final src = imageElement.attributes['src'];
|
||||||
|
|
||||||
if (src == null) return;
|
if (src == null) return;
|
||||||
|
|
||||||
|
final id = imageElement.attributes['id'] ?? '';
|
||||||
final mimeType = imageElement.attributes['data-mimetype'];
|
final mimeType = imageElement.attributes['data-mimetype'];
|
||||||
if (src.startsWith('cid:') && mapUrlDownloadCID != null) {
|
if (src.startsWith('cid:') && mapUrlDownloadCID != null) {
|
||||||
final imageBase64 = await _convertCidToBase64Image(
|
final imageBase64 = await _convertCidToBase64Image(
|
||||||
@@ -54,7 +41,10 @@ class ImageTransformer extends DomTransformer {
|
|||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
);
|
);
|
||||||
imageElement.attributes['src'] = imageBase64 ?? src;
|
imageElement.attributes['src'] = imageBase64 ?? src;
|
||||||
imageElement.attributes['id'] ??= src;
|
|
||||||
|
if (!id.startsWith('cid:')) {
|
||||||
|
imageElement.attributes['id'] = src;
|
||||||
|
}
|
||||||
} else if (src.startsWith('https://') || src.startsWith('http://')) {
|
} else if (src.startsWith('https://') || src.startsWith('http://')) {
|
||||||
if (!imageElement.attributes.containsKey('loading')) {
|
if (!imageElement.attributes.containsKey('loading')) {
|
||||||
imageElement.attributes['loading'] = 'lazy';
|
imageElement.attributes['loading'] = 'lazy';
|
||||||
|
|||||||
@@ -267,7 +267,10 @@ class _HtmlContentViewerOnWebState extends State<HtmlContentViewerOnWeb> {
|
|||||||
minHeight: minHeight,
|
minHeight: minHeight,
|
||||||
minWidth: _minWidth,
|
minWidth: _minWidth,
|
||||||
styleCSS: HtmlTemplate.tooltipLinkCss,
|
styleCSS: HtmlTemplate.tooltipLinkCss,
|
||||||
javaScripts: webViewActionScripts + scriptsDisableZoom + HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage,
|
javaScripts: webViewActionScripts
|
||||||
|
+ scriptsDisableZoom
|
||||||
|
+ HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage
|
||||||
|
+ HtmlInteraction.generateNormalizeImageScript(widget.widthContent),
|
||||||
direction: widget.direction,
|
direction: widget.direction,
|
||||||
contentPadding: widget.contentPadding,
|
contentPadding: widget.contentPadding,
|
||||||
useDefaultFont: widget.useDefaultFont,
|
useDefaultFont: widget.useDefaultFont,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
late InAppWebViewController _webViewController;
|
late InAppWebViewController _webViewController;
|
||||||
late double _actualHeight;
|
late double _actualHeight;
|
||||||
late Set<Factory<OneSequenceGestureRecognizer>> _gestureRecognizers;
|
late Set<Factory<OneSequenceGestureRecognizer>> _gestureRecognizers;
|
||||||
late String _customScripts;
|
late StringBuffer _customScriptsBuilder;
|
||||||
|
|
||||||
final _loadingBarNotifier = ValueNotifier(true);
|
final _loadingBarNotifier = ValueNotifier(true);
|
||||||
|
|
||||||
@@ -92,10 +92,13 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer(duration: _longPressGestureDurationIOS)),
|
Factory<LongPressGestureRecognizer>(() => LongPressGestureRecognizer(duration: _longPressGestureDurationIOS)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
_customScriptsBuilder = StringBuffer();
|
||||||
|
_customScriptsBuilder.write(HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage);
|
||||||
|
if (widget.initialWidth != null) {
|
||||||
|
_customScriptsBuilder.write(HtmlInteraction.generateNormalizeImageScript(widget.initialWidth!));
|
||||||
|
}
|
||||||
if (PlatformInfo.isAndroid) {
|
if (PlatformInfo.isAndroid) {
|
||||||
_customScripts = HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage + HtmlInteraction.scriptsHandleContentSizeChanged;
|
_customScriptsBuilder.write(HtmlInteraction.scriptsHandleContentSizeChanged);
|
||||||
} else {
|
|
||||||
_customScripts = HtmlInteraction.scriptsHandleLazyLoadingBackgroundImage;
|
|
||||||
}
|
}
|
||||||
_initialData();
|
_initialData();
|
||||||
}
|
}
|
||||||
@@ -115,7 +118,7 @@ class _HtmlContentViewState extends State<HtmlContentViewer> {
|
|||||||
_htmlData = HtmlUtils.generateHtmlDocument(
|
_htmlData = HtmlUtils.generateHtmlDocument(
|
||||||
content: widget.contentHtml,
|
content: widget.contentHtml,
|
||||||
direction: widget.direction,
|
direction: widget.direction,
|
||||||
javaScripts: _customScripts,
|
javaScripts: _customScriptsBuilder.toString(),
|
||||||
contentPadding: widget.contentPadding,
|
contentPadding: widget.contentPadding,
|
||||||
useDefaultFont: widget.useDefaultFont,
|
useDefaultFont: widget.useDefaultFont,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -99,4 +99,144 @@ class HtmlInteraction {
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
''';
|
''';
|
||||||
|
|
||||||
|
static String generateNormalizeImageScript(double displayWidth) {
|
||||||
|
return '''
|
||||||
|
<script type="text/javascript">
|
||||||
|
const displayWidth = $displayWidth;
|
||||||
|
|
||||||
|
const sizeUnits = ['px', 'in', 'cm', 'mm', 'pt', 'pc'];
|
||||||
|
|
||||||
|
function convertToPx(value, unit) {
|
||||||
|
switch (unit.toLowerCase()) {
|
||||||
|
case 'px': return value;
|
||||||
|
case 'in': return value * 96;
|
||||||
|
case 'cm': return value * 37.8;
|
||||||
|
case 'mm': return value * 3.78;
|
||||||
|
case 'pt': return value * (96 / 72);
|
||||||
|
case 'pc': return value * (96 / 6);
|
||||||
|
default: return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeWidthHeightFromStyle(style) {
|
||||||
|
style = style.replace(/width\\s*:\\s*[\\d.]+[a-zA-Z%]+\\s*;?/gi, '');
|
||||||
|
style = style.replace(/height\\s*:\\s*[\\d.]+[a-zA-Z%]+\\s*;?/gi, '');
|
||||||
|
style = style.trim();
|
||||||
|
if (style.length && !style.endsWith(';')) {
|
||||||
|
style += ';';
|
||||||
|
}
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractWidthHeightFromStyle(style) {
|
||||||
|
const result = {};
|
||||||
|
const widthMatch = style.match(/width\\s*:\\s*([\\d.]+)([a-zA-Z%]+)\\s*;?/);
|
||||||
|
const heightMatch = style.match(/height\\s*:\\s*([\\d.]+)([a-zA-Z%]+)\\s*;?/);
|
||||||
|
|
||||||
|
if (widthMatch) {
|
||||||
|
const value = parseFloat(widthMatch[1]);
|
||||||
|
const unit = widthMatch[2];
|
||||||
|
if (!isNaN(value) && unit) {
|
||||||
|
result['width'] = { value, unit };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heightMatch) {
|
||||||
|
const value = parseFloat(heightMatch[1]);
|
||||||
|
const unit = heightMatch[2];
|
||||||
|
if (!isNaN(value) && unit) {
|
||||||
|
result['height'] = { value, unit };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeStyleAttribute(attrs) {
|
||||||
|
let style = attrs['style'];
|
||||||
|
if (!style) {
|
||||||
|
attrs['style'] = 'display:inline;max-width:100%;';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
style = style.trim();
|
||||||
|
const dimensions = extractWidthHeightFromStyle(style);
|
||||||
|
const hasWidth = dimensions.hasOwnProperty('width');
|
||||||
|
|
||||||
|
if (hasWidth) {
|
||||||
|
const widthData = dimensions['width'];
|
||||||
|
const widthPx = convertToPx(widthData.value, widthData.unit);
|
||||||
|
|
||||||
|
if (displayWidth !== undefined &&
|
||||||
|
widthPx > displayWidth &&
|
||||||
|
sizeUnits.includes(widthData.unit)) {
|
||||||
|
style = removeWidthHeightFromStyle(style).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style.length && !style.endsWith(';')) {
|
||||||
|
style += ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!style.includes('max-width')) {
|
||||||
|
style += 'max-width:100%;height:auto;';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!style.includes('display')) {
|
||||||
|
style += 'display:inline;';
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs['style'] = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeWidthHeightAttribute(attrs) {
|
||||||
|
const widthStr = attrs['width'];
|
||||||
|
const heightStr = attrs['height'];
|
||||||
|
|
||||||
|
if (!widthStr || displayWidth === undefined) return;
|
||||||
|
|
||||||
|
const widthValue = parseFloat(widthStr);
|
||||||
|
if (isNaN(widthValue) || widthValue <= displayWidth) return;
|
||||||
|
|
||||||
|
delete attrs['width'];
|
||||||
|
delete attrs['height'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeImageSize(attrs) {
|
||||||
|
normalizeStyleAttribute(attrs);
|
||||||
|
normalizeWidthHeightAttribute(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyImageNormalization() {
|
||||||
|
document.querySelectorAll('img').forEach(img => {
|
||||||
|
const attrs = {
|
||||||
|
style: img.getAttribute('style'),
|
||||||
|
width: img.getAttribute('width'),
|
||||||
|
height: img.getAttribute('height')
|
||||||
|
};
|
||||||
|
|
||||||
|
normalizeImageSize(attrs);
|
||||||
|
|
||||||
|
if (attrs.style != null) img.setAttribute('style', attrs.style);
|
||||||
|
if ('width' in attrs) img.setAttribute('width', attrs.width);
|
||||||
|
else img.removeAttribute('width');
|
||||||
|
|
||||||
|
if ('height' in attrs) img.setAttribute('height', attrs.height);
|
||||||
|
else img.removeAttribute('height');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeApplyImageNormalization() {
|
||||||
|
try {
|
||||||
|
applyImageNormalization();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Image normalization failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = safeApplyImageNormalization;
|
||||||
|
</script>
|
||||||
|
''';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user