Skip to content

Commit cf4c465

Browse files
rajveermalviyagnprice
authored andcommitted
content: Parse original image dimensions
1 parent c17dd0e commit cf4c465

File tree

2 files changed

+102
-22
lines changed

2 files changed

+102
-22
lines changed

lib/model/content.dart

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ class ImageNode extends BlockContentNode {
356356
required this.srcUrl,
357357
required this.thumbnailUrl,
358358
required this.loading,
359+
required this.originalWidth,
360+
required this.originalHeight,
359361
});
360362

361363
/// The canonical source URL of the image.
@@ -379,23 +381,34 @@ class ImageNode extends BlockContentNode {
379381
/// Typically it will be `true` while Server is generating thumbnails.
380382
final bool loading;
381383

384+
/// The width of the canonical image.
385+
final double? originalWidth;
386+
387+
/// The height of the canonical image.
388+
final double? originalHeight;
389+
382390
@override
383391
bool operator ==(Object other) {
384392
return other is ImageNode
385393
&& other.srcUrl == srcUrl
386394
&& other.thumbnailUrl == thumbnailUrl
387-
&& other.loading == loading;
395+
&& other.loading == loading
396+
&& other.originalWidth == originalWidth
397+
&& other.originalHeight == originalHeight;
388398
}
389399

390400
@override
391-
int get hashCode => Object.hash('ImageNode', srcUrl, thumbnailUrl, loading);
401+
int get hashCode => Object.hash('ImageNode',
402+
srcUrl, thumbnailUrl, loading, originalWidth, originalHeight);
392403

393404
@override
394405
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
395406
super.debugFillProperties(properties);
396407
properties.add(StringProperty('srcUrl', srcUrl));
397408
properties.add(StringProperty('thumbnailUrl', thumbnailUrl));
398409
properties.add(FlagProperty('loading', value: loading, ifTrue: "is loading"));
410+
properties.add(DoubleProperty('originalWidth', originalWidth));
411+
properties.add(DoubleProperty('originalHeight', originalHeight));
399412
}
400413
}
401414

@@ -1037,6 +1050,8 @@ class _ZulipContentParser {
10371050
return CodeBlockNode(spans, debugHtmlNode: debugHtmlNode);
10381051
}
10391052

1053+
static final _imageDimensionsRegExp = RegExp(r'^(\d+)x(\d+)$');
1054+
10401055
BlockContentNode parseImageNode(dom.Element divElement) {
10411056
assert(_debugParserContext == _ParserContext.block);
10421057
final elements = () {
@@ -1071,6 +1086,8 @@ class _ZulipContentParser {
10711086
srcUrl: href,
10721087
thumbnailUrl: null,
10731088
loading: true,
1089+
originalWidth: null,
1090+
originalHeight: null,
10741091
debugHtmlNode: debugHtmlNode);
10751092
}
10761093
final src = imgElement.attributes['src'];
@@ -1093,10 +1110,32 @@ class _ZulipContentParser {
10931110
} else {
10941111
return UnimplementedBlockContentNode(htmlNode: divElement);
10951112
}
1113+
1114+
double? originalWidth, originalHeight;
1115+
final originalDimensions = imgElement.attributes['data-original-dimensions'];
1116+
if (originalDimensions != null) {
1117+
// Server encodes this string as "{width}x{height}" (eg. "300x400")
1118+
final match = _imageDimensionsRegExp.firstMatch(originalDimensions);
1119+
if (match != null) {
1120+
final width = int.tryParse(match.group(1)!, radix: 10);
1121+
final height = int.tryParse(match.group(2)!, radix: 10);
1122+
if (width != null && height != null) {
1123+
originalWidth = width.toDouble();
1124+
originalHeight = height.toDouble();
1125+
}
1126+
}
1127+
1128+
if (originalWidth == null || originalHeight == null) {
1129+
return UnimplementedBlockContentNode(htmlNode: divElement);
1130+
}
1131+
}
1132+
10961133
return ImageNode(
10971134
srcUrl: srcUrl,
10981135
thumbnailUrl: thumbnailUrl,
10991136
loading: false,
1137+
originalWidth: originalWidth,
1138+
originalHeight: originalHeight,
11001139
debugHtmlNode: debugHtmlNode);
11011140
}
11021141

test/model/content_test.dart

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ class ContentExample {
263263
]),
264264
ImageNodeList([
265265
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png?version=3',
266-
thumbnailUrl: null, loading: false),
266+
thumbnailUrl: null, loading: false,
267+
originalWidth: null, originalHeight: null),
267268
]),
268269
],
269270
content: [ParagraphNode(links: null, nodes: [TextNode('hello world')])],
@@ -421,6 +422,22 @@ class ContentExample {
421422

422423
static const imageSingle = ContentExample(
423424
'single image',
425+
// https://chat.zulip.org/#narrow/stream/7-test-here/topic/Thumbnails/near/1900103
426+
"[image.jpg](/user_uploads/2/ce/nvoNL2LaZOciwGZ-FYagddtK/image.jpg)",
427+
'<div class="message_inline_image">'
428+
'<a href="/user_uploads/2/ce/nvoNL2LaZOciwGZ-FYagddtK/image.jpg" title="image.jpg">'
429+
'<img data-original-dimensions="6000x4000" src="/user_uploads/thumbnail/2/ce/nvoNL2LaZOciwGZ-FYagddtK/image.jpg/840x560.webp"></a></div>', [
430+
ImageNodeList([
431+
ImageNode(srcUrl: '/user_uploads/2/ce/nvoNL2LaZOciwGZ-FYagddtK/image.jpg',
432+
thumbnailUrl: '/user_uploads/thumbnail/2/ce/nvoNL2LaZOciwGZ-FYagddtK/image.jpg/840x560.webp',
433+
loading: false,
434+
originalWidth: 6000,
435+
originalHeight: 4000),
436+
]),
437+
]);
438+
439+
static const imageSingleNoDimensions = ContentExample(
440+
'single image no dimensions',
424441
// https://chat.zulip.org/#narrow/stream/7-test-here/topic/Thumbnails/near/1893590
425442
"[image.jpg](/user_uploads/2/c3/wb9FXk8Ej6qIc28aWKcqUogD/image.jpg)",
426443
'<div class="message_inline_image">'
@@ -429,7 +446,9 @@ class ContentExample {
429446
ImageNodeList([
430447
ImageNode(srcUrl: '/user_uploads/2/c3/wb9FXk8Ej6qIc28aWKcqUogD/image.jpg',
431448
thumbnailUrl: '/user_uploads/thumbnail/2/c3/wb9FXk8Ej6qIc28aWKcqUogD/image.jpg/840x560.webp',
432-
loading: false),
449+
loading: false,
450+
originalWidth: null,
451+
originalHeight: null),
433452
]),
434453
]);
435454

@@ -441,7 +460,8 @@ class ContentExample {
441460
'<img src="https://chat.zulip.org/user_avatars/2/realm/icon.png?version=3"></a></div>', [
442461
ImageNodeList([
443462
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png?version=3',
444-
thumbnailUrl: null, loading: false),
463+
thumbnailUrl: null, loading: false,
464+
originalWidth: null, originalHeight: null),
445465
]),
446466
]);
447467

@@ -454,7 +474,8 @@ class ContentExample {
454474
'<img class="image-loading-placeholder" src="/static/images/loading/loader-black.svg"></a></div>', [
455475
ImageNodeList([
456476
ImageNode(srcUrl: '/user_uploads/2/c3/wb9FXk8Ej6qIc28aWKcqUogD/image.jpg',
457-
thumbnailUrl: null, loading: true),
477+
thumbnailUrl: null, loading: true,
478+
originalWidth: null, originalHeight: null),
458479
]),
459480
]);
460481

@@ -467,7 +488,8 @@ class ContentExample {
467488
'<img src="/external_content/de28eb3abf4b7786de4545023dc42d434a2ea0c2/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f372f37382f566572726567656e64655f626c6f656d5f76616e5f65656e5f48656c656e69756d5f253237456c5f446f7261646f2532372e5f32322d30372d323032332e5f253238642e6a2e622532392e6a7067"></a></div>', [
468489
ImageNodeList([
469490
ImageNode(srcUrl: '/external_content/de28eb3abf4b7786de4545023dc42d434a2ea0c2/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f372f37382f566572726567656e64655f626c6f656d5f76616e5f65656e5f48656c656e69756d5f253237456c5f446f7261646f2532372e5f32322d30372d323032332e5f253238642e6a2e622532392e6a7067',
470-
thumbnailUrl: null, loading: false),
491+
thumbnailUrl: null, loading: false,
492+
originalWidth: null, originalHeight: null),
471493
]),
472494
]);
473495

@@ -478,7 +500,9 @@ class ContentExample {
478500
'<a href="::not a URL::">'
479501
'<img src="::not a URL::"></a></div>', [
480502
ImageNodeList([
481-
ImageNode(srcUrl: '::not a URL::', thumbnailUrl: null, loading: false),
503+
ImageNode(srcUrl: '::not a URL::',
504+
thumbnailUrl: null, loading: false,
505+
originalWidth: null, originalHeight: null),
482506
]),
483507
]);
484508

@@ -504,10 +528,14 @@ class ContentExample {
504528
ImageNodeList([
505529
ImageNode(srcUrl: '/user_uploads/2/9b/WkDt2Qsy79iwf3sM9EMp9fYL/image.jpg',
506530
thumbnailUrl: '/user_uploads/thumbnail/2/9b/WkDt2Qsy79iwf3sM9EMp9fYL/image.jpg/840x560.webp',
507-
loading: false),
531+
loading: false,
532+
originalWidth: null,
533+
originalHeight: null),
508534
ImageNode(srcUrl: '/user_uploads/2/70/pVeI52TwFUEoFE2qT_u9AMCO/image2.jpg',
509535
thumbnailUrl: '/user_uploads/thumbnail/2/70/pVeI52TwFUEoFE2qT_u9AMCO/image2.jpg/840x560.webp',
510-
loading: false),
536+
loading: false,
537+
originalWidth: null,
538+
originalHeight: null),
511539
]),
512540
]);
513541

@@ -531,9 +559,11 @@ class ContentExample {
531559
]),
532560
ImageNodeList([
533561
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/f535ba07f95b99a83aa48e44fd62bbb6c6cf6615/68747470733a2f2f636861742e7a756c69702e6f72672f757365725f617661746172732f322f7265616c6d2f69636f6e2e706e673f76657273696f6e3d33',
534-
thumbnailUrl: null, loading: false),
562+
thumbnailUrl: null, loading: false,
563+
originalWidth: null, originalHeight: null),
535564
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/8f63bc2632a0e41be3f457d86c077e61b4a03e7e/68747470733a2f2f636861742e7a756c69702e6f72672f757365725f617661746172732f322f7265616c6d2f69636f6e2e706e673f76657273696f6e3d34',
536-
thumbnailUrl: null, loading: false),
565+
thumbnailUrl: null, loading: false,
566+
originalWidth: null, originalHeight: null),
537567
]),
538568
]);
539569

@@ -558,9 +588,11 @@ class ContentExample {
558588
]),
559589
ImageNodeList([
560590
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png',
561-
thumbnailUrl: null, loading: false),
591+
thumbnailUrl: null, loading: false,
592+
originalWidth: null, originalHeight: null),
562593
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png?version=2',
563-
thumbnailUrl: null, loading: false),
594+
thumbnailUrl: null, loading: false,
595+
originalWidth: null, originalHeight: null),
564596
]),
565597
ParagraphNode(links: null, nodes: [
566598
TextNode('more content'),
@@ -596,9 +628,11 @@ class ContentExample {
596628
]),
597629
ImageNodeList([
598630
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/34b2695ca83af76204b0b25a8f2019ee35ec38fa/68747470733a2f2f656e2e77696b6970656469612e6f72672f7374617469632f696d616765732f69636f6e732f77696b6970656469612e706e67',
599-
thumbnailUrl: null, loading: false),
631+
thumbnailUrl: null, loading: false,
632+
originalWidth: null, originalHeight: null),
600633
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/d200fb112aaccbff9df767373a201fa59601f362/68747470733a2f2f656e2e77696b6970656469612e6f72672f7374617469632f696d616765732f69636f6e732f77696b6970656469612e706e673f763d31',
601-
thumbnailUrl: null, loading: false),
634+
thumbnailUrl: null, loading: false,
635+
originalWidth: null, originalHeight: null),
602636
]),
603637
ParagraphNode(links: null, nodes: [
604638
TextNode('Test'),
@@ -611,9 +645,11 @@ class ContentExample {
611645
]),
612646
ImageNodeList([
613647
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/c4db87e81348dac94eacaa966b46d968b34029cc/68747470733a2f2f656e2e77696b6970656469612e6f72672f7374617469632f696d616765732f69636f6e732f77696b6970656469612e706e673f763d32',
614-
thumbnailUrl: null, loading: false),
648+
thumbnailUrl: null, loading: false,
649+
originalWidth: null, originalHeight: null),
615650
ImageNode(srcUrl: 'https://uploads.zulipusercontent.net/51b70540cf6a5b3c8a0b919c893b8abddd447e88/68747470733a2f2f656e2e77696b6970656469612e6f72672f7374617469632f696d616765732f69636f6e732f77696b6970656469612e706e673f763d33',
616-
thumbnailUrl: null, loading: false),
651+
thumbnailUrl: null, loading: false,
652+
originalWidth: null, originalHeight: null),
617653
]),
618654
]);
619655

@@ -628,7 +664,8 @@ class ContentExample {
628664
ListNode(ListStyle.unordered, [[
629665
ImageNodeList([
630666
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png',
631-
thumbnailUrl: null, loading: false),
667+
thumbnailUrl: null, loading: false,
668+
originalWidth: null, originalHeight: null),
632669
]),
633670
]]),
634671
]);
@@ -654,9 +691,11 @@ class ContentExample {
654691
]),
655692
ImageNodeList([
656693
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png',
657-
thumbnailUrl: null, loading: false),
694+
thumbnailUrl: null, loading: false,
695+
originalWidth: null, originalHeight: null),
658696
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png?version=2',
659-
thumbnailUrl: null, loading: false),
697+
thumbnailUrl: null, loading: false,
698+
originalWidth: null, originalHeight: null),
660699
]),
661700
]]),
662701
]);
@@ -680,7 +719,8 @@ class ContentExample {
680719
]),
681720
const ImageNodeList([
682721
ImageNode(srcUrl: 'https://chat.zulip.org/user_avatars/2/realm/icon.png',
683-
thumbnailUrl: null, loading: false),
722+
thumbnailUrl: null, loading: false,
723+
originalWidth: null, originalHeight: null),
684724
]),
685725
blockUnimplemented('more text'),
686726
]]),
@@ -1117,6 +1157,7 @@ void main() {
11171157
testParseExample(ContentExample.mathBlockInQuote);
11181158

11191159
testParseExample(ContentExample.imageSingle);
1160+
testParseExample(ContentExample.imageSingleNoDimensions);
11201161
testParseExample(ContentExample.imageSingleNoThumbnail);
11211162
testParseExample(ContentExample.imageSingleLoadingPlaceholder);
11221163
testParseExample(ContentExample.imageSingleExternal);

0 commit comments

Comments
 (0)