diff --git a/reviewboard/static/rb/css/pages/image-review-ui.less b/reviewboard/static/rb/css/pages/image-review-ui.less
index 7437c0a85e5faed766eef8be09d42a06e834db7f..d6a0b6aab1c10154192de5e012a06b83216a0b84 100644
--- a/reviewboard/static/rb/css/pages/image-review-ui.less
+++ b/reviewboard/static/rb/css/pages/image-review-ui.less
@@ -50,6 +50,12 @@
     padding: 0;
   }
 
+  .svg-container {
+    display: block;
+    margin: 0 auto;
+    padding: 0;
+  }
+
   .caption {
     margin-bottom: 1em;
     text-align: center;
@@ -155,7 +161,7 @@
       }
 
       > a {
-        color: #444;
+        color: #rb-ns-ui.colors[@grey-20];
         display: block;
         margin: 1em;
       }
@@ -244,7 +250,7 @@
 
   .menu-item {
     cursor: pointer;
-    color: #444;
+    color: #rb-ns-ui.colors[@grey-20];
     padding: 8px 10px;
 
     &:last-child {
@@ -253,6 +259,17 @@
   }
 }
 
+.svg-control-menu {
+  float: left;
+  margin-left: 1em;
+
+  .dragging-mode-toggle, .wheel-mode-toggle {
+    margin-right: 5px;
+    font-size: 14px;
+  }
+
+}
+
 // Version of the menu when there's a diff
 .image-diff-modes .image-resolution-menu {
   .menu-header {
@@ -274,7 +291,8 @@
   text-align: center;
 
   .caption,
-  .image-resolution-menu {
+  .image-resolution-menu,
+  .svg-control-menu {
     display: inline-block;
   }
 
@@ -288,7 +306,7 @@
     }
 
     .menu-header {
-      color: #444;
+      color: #rb-ns-ui.colors[@grey-20];
       line-height: 16px;
     }
 
@@ -296,4 +314,13 @@
       background-color: darken(@review-ui-header-bg, 10%);
     }
   }
+
+  .svg-control-menu {
+    padding: 8px 0;
+
+    #dragging-mode-icon, #wheel-mode-icon {
+      color: #rb-ns-ui.colors[@grey-20];
+      line-height: 16px;
+    }
+  }
 }
diff --git a/reviewboard/static/rb/js/views/imageReviewableView.es6.js b/reviewboard/static/rb/js/views/imageReviewableView.es6.js
index f62c53003b1ff476402964235136f156b981f4c7..1bae30414412ce87a1968bb79a26dc92e5577b9c 100644
--- a/reviewboard/static/rb/js/views/imageReviewableView.es6.js
+++ b/reviewboard/static/rb/js/views/imageReviewableView.es6.js
@@ -266,6 +266,419 @@ const ImageAttachmentView = BaseImageView.extend({
 });
 
 
+/**
+ * Displays a single SVG image.
+ *
+ * This view is uesd for standalone SVG images, without diffs.
+ * It display the SVG and allows it to be zoomed in, spanned
+ * and commented.
+ */
+const SvgAttachmentView = BaseImageView.extend({
+    mode: 'attachment',
+    className: 'svg-container',
+
+    /**
+     * Initialize the view.
+     */
+    initialize() {
+        _super(this).initialize.call(this);
+
+        this.svgPromise = this.fetchSvg();
+
+        this.on('viewBoxChanged', this._onViewBoxChanged, this);
+        this.on('svgMouseDown', this._onSvgMouseDown, this);
+        this.on('svgMouseUp', this._onSvgMouseUp, this);
+        this.on('svgMouseMove', this._onSvgMouseMove, this);
+    },
+
+    /**
+     * Render the view.
+     *
+     * Returns:
+     *     SvgAttachmentView:
+     *     This object, for chaining.
+     */
+    async render() {
+        const svg = await this.svgPromise;
+        this.svg = svg;
+        this.viewBox = this.svg.viewBox.baseVal;
+        this.$el.html(this.svg);
+
+        this.$commentRegion = this.$el;
+
+        // Prepare all SVGElementApi variables for zoom in and spanning
+        this.point = this.svg.createSVGPoint();
+        this.startPoint = this.svg.createSVGPoint();
+        this.endPoint = this.svg.createSVGPoint();
+        this.viewBox = this.svg.viewBox.baseVal;
+        this.svgSize = {width: this.viewBox.width,
+                        height: this.viewBox.height
+        };
+        this.viewBoxScale = 1;
+        this.scale = 1;
+        this.isPanning = false;
+
+        this.loadImages(this.$el);
+        return this;
+
+    },
+
+    /**
+     * Inlines SVG by fetching the image
+     *
+     * Returns:
+     *     Promise:
+     *     Resolves with the inline SVG. If any problems
+     *     with fetching occurs, the promise rejects.
+     */
+    fetchSvg() {
+        return fetch(this.model.get('imageURL'))
+            .then(res => res.text())
+            .then(data => {
+                const parser = new DOMParser();
+                return parser.parseFromString(data, 'image/svg+xml')
+                    .querySelector('svg');
+            });
+    },
+
+    /**
+     * Load a list of images.
+     *
+     * Once each SVG image is loaded, the view's onImagesLoaded
+     * function will be called.
+     *
+     * Args:
+     *     $images (jQuery):
+     *         The image elements to load.
+     */
+    loadImages($images) {
+        const scale = this.model.get('scale');
+
+        let loadsRemaining = $images.length;
+
+        this._$images = $images;
+
+        $images.each((ix, image) => {
+            const $image = $(image);
+
+            if ($image.data('initial-width') === undefined) {
+                loadsRemaining--;
+                console.assert(loadsRemaining >= 0);
+
+                $image
+                    .data({
+                        'initial-width': this.viewBox.width,
+                        'initial-height': this.viewBox.height,
+                    })
+                    .css({
+                        width: this.viewBox.width * scale,
+                        height: this.viewBox.height * scale,
+                    });
+
+                if (loadsRemaining === 0) {
+                    this.onImagesLoaded();
+                    this.trigger('regionChanged');
+                }
+            } else {
+                loadsRemaining--;
+
+                if (loadsRemaining === 0) {
+                    this.onImagesLoaded();
+                    this.trigger('regionChanged');
+                }
+            }
+        });
+    },
+
+    /**
+     * Callback handler for when the SVG images on the view are loaded.
+     */
+    onImagesLoaded() {
+        let scale = null;
+
+        /*
+         * If the image is obviously a 2x or 3x pixel ratio, pre-select the
+         * right scaling factor.
+         *
+         * Otherwise, we select the largest scaling factor that allows the
+         * entire image to be shown (or the smallest scaling factor if the
+         * scaled image is still too large).
+         */
+        const filename = this.model.get('filename');
+
+        /*
+         * The `filename` attribute does not exist for screenshots, so we need
+         * to check it.
+         */
+        if (filename) {
+            if (filename.includes('@2x.')) {
+                scale = 0.5;
+            } else if (filename.includes('@3x.')) {
+                scale = 0.33;
+            }
+        }
+
+        if (scale === null) {
+            const {width} = this.getInitialSize();
+            const maxWidth = this.$el.closest('.image-content').width();
+            const scales = Array
+                .from(scalingFactors.keys())
+                .filter(f => (f <= 1));
+
+            for (let i = scales.length - 1; i >= 0; i--) {
+                scale = scales[i];
+
+                if (width * scale <= maxWidth) {
+                    break;
+                }
+            }
+        }
+
+        this.model.set('scale', scale);
+    },
+
+    /**
+     * Handler for when the parent view's wheel event on SVG is triggered.
+     *
+     * Calculates new viewBox value of the SVG based on event location
+     * and wheel direction. Once the new viewBox is calculated, the
+     * viewBox of the SVG is updated.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onViewBoxChanged(e) {
+        e.preventDefault();
+        let w = this.viewBox.width;
+        let h = this.viewBox.height;
+        this.point.x = e.clientX - 1;
+        this.point.y = e.clientY - 1;
+        // Translates the relative position of mouse to
+        // position in SVG coordinate system
+        const svgMatrix = this.svg.getScreenCTM().inverse();
+        const svgPoint = this.point.matrixTransform(svgMatrix);
+        let mx = svgPoint.x;
+        let my = svgPoint.y;
+
+        // calculate by how much we need to decrease width/height of the viewBox
+        // Note: decreasing width/height --> zoom in;
+        //       increasing width/height --> zoom out
+        let dw = w * Math.sign(e.originalEvent.deltaY) * 0.05;
+        let dh = h * Math.sign(e.originalEvent.deltaY) * 0.05;
+
+        // calculate by how much we need to shift the viewBox so the mouse is still centered
+        let dx = dw * mx/this.svgSize.width;
+        let dy = dh * my/this.svgSize.height;
+
+        // TODO: figure out a minimum values, since view breaks if values are too big/small.
+        this.viewBox = {x: this.viewBox.x + dx,
+                        y: this.viewBox.y + dy,
+                        width: this.viewBox.width - dw,
+                        height: this.viewBox.height - dh,
+        };
+        this.viewBoxScale = this.svgSize.width/this.viewBox.width;
+        this.svg.setAttribute('viewBox', this.stringifyViewBox(this.viewBox));
+    },
+
+    /**
+     * Handle the image scale being changed.
+     *
+     * Args:
+     *     scale (number):
+     *         The new image scaling factor (where 1.0 is 100%, 0.5 is 50%,
+     *         etc).
+     */
+    _onScaleChanged(scale) {
+        const scaledWidth = this.svgSize.width * scale;
+        const scaledHeight = this.svgSize.height * scale;
+        this.$el.css({
+            width: scaledWidth + 'px',
+            height: scaledHeight + 'px',
+        });
+        this.svg.setAttribute('width', scaledWidth + 'px');
+        this.svg.setAttribute('height', scaledHeight + 'px');
+        this.svg.setAttribute('viewBox', `0 0 ${this.svgSize.width} ${this.svgSize.height}`);
+        this.viewBox = {x: 0,
+                        y: 0,
+                        width: this.svgSize.width,
+                        height: this.svgSize.height,
+        };
+        this.scale = scale;
+        this.viewBoxScale = scale;
+    },
+
+    /**
+     * Handle a mousedown on the SVG.
+     *
+     * This will store the starting point of the mouse, which will
+     * later be used for calculating new viewBox values.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onSvgMouseDown(e) {
+        e.preventDefault();
+        this.isPanning = true;
+        this.startPoint.x = e.clientX - 1;
+        this.startPoint.y = e.clientY - 1;
+    },
+
+    /**
+     * Handle a mouseup on the SVG.
+     *
+     * This will register as the end point of the mouse, and hence the new
+     * value of viewBox will be calculated based on the starting point.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onSvgMouseUp(e) {
+        if (this.isPanning) {
+            this.endPoint.x = e.clientX-1;
+            this.endPoint.y = e.clientY-1;
+            // get SVG coordinates of endPoint and startPoint
+            const svgStartPoint = this.startPoint.matrixTransform(this.svg.getScreenCTM().inverse());
+            const svgEndPoint = this.endPoint.matrixTransform(this.svg.getScreenCTM().inverse());
+            // shift viewBox by the difference in x and y of startPoint and endPoint
+            const dx = (svgStartPoint.x - svgEndPoint.x) / this.scale;
+            const dy = (svgStartPoint.y - svgEndPoint.y) / this.scale;
+            this.viewBox = {x: this.viewBox.x + dx,
+                            y: this.viewBox.y + dy,
+                            width: this.viewBox.width,
+                            height: this.viewBox.height,
+            };
+            this.svg.setAttribute('viewBox', this.stringifyViewBox(this.viewBox));
+            this.isPanning = false;
+        }
+    },
+
+    /**
+     * Handle a mousemove on the SVG.
+     *
+     * This will register as the end point of the mouse, and hence the new
+     * value of viewBox will be calculated based on the starting point.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onSvgMouseMove(e) {
+        if (this.isPanning){
+            this.endPoint.x = e.clientX-1;
+            this.endPoint.y = e.clientY-1;
+            // get SVG coordinates of endPoint and startPoint
+            const svgMatrix = this.svg.getScreenCTM().inverse();
+            const svgStartPoint = this.startPoint.matrixTransform(svgMatrix);
+            const svgEndPoint = this.endPoint.matrixTransform(svgMatrix);
+            // shift viewBox by the difference in x and y of startPoint and endPoint
+            const dx = (svgStartPoint.x - svgEndPoint.x) / this.scale;
+            const dy = (svgStartPoint.y - svgEndPoint.y) / this.scale;
+            this.movedViewBox = {x: this.viewBox.x + dx,
+                                y: this.viewBox.y + dy,
+                                width: this.viewBox.width,
+                                height: this.viewBox.height,
+            };
+            this.svg.setAttribute('viewBox', this.stringifyViewBox(this.movedViewBox));
+        }
+    },
+
+    /**
+     * Return the initial size of the image.
+     *
+     * Returns:
+     *     object:
+     *     An object containing the initial height and width of the image.
+     */
+    getInitialSize() {
+        const $img = this._$images.eq(0);
+        this.viewBox.width =  $img.data('initial-width');
+        this.viewBox.height = $img.data('initial-height');
+        this.svgSize.width = $img.data('initial-width');
+        this.svgSize.height = $img.data('initial-height');
+
+        return {
+            width: $img.data('initial-width'),
+            height: $img.height('initial-height'),
+        };
+    },
+
+    /**
+     * Return the scale based on the viewBox.
+     *
+     * Returns:
+     *     integer:
+     *     An integer representing the scale based on the viewBox.
+     */
+    getViewBoxScale() {
+        return (this.svgSize.width*this.scale)/this.viewBox.width;
+    },
+
+    /**
+     * Return viewBox of SVG.
+     *
+     * Returns:
+     *     Object:
+     *     An object with ``x``, ``y``, ``width`` and ``height`` keys.
+     */
+    getViewBox() {
+        return this.svg.viewBox.baseVal;
+    },
+
+    /**
+     * Return viewBox as a string where values are seperated by space.
+     *
+     * Args:
+     *     viewBox (object):
+     *         object representing viewBox.
+     *         Must have keys: x, y, width and height.
+     *
+     * Returns:
+     *     String:
+     *     Represents viewBox value
+     */
+    stringifyViewBox(viewBox) {
+        return `${viewBox.x} ${viewBox.y} ${viewBox.width} ${viewBox.height}`;
+    },
+
+    /**
+     * Return the region of the view where commenting can take place.
+     *
+     * The region is based on the $commentRegion member variable,
+     * which must be set by a subclass.
+     *
+     * Returns:
+     *     Promise:
+     *     Promise resolves with an object with
+     *     ``left``, ``top``, ``width``, and ``height`` keys.
+     */
+    getSelectionRegion() {
+        return this.svgPromise.then((res) => {
+                const $region = this.$commentRegion;
+                const offset = $region.position();
+
+                /*
+                * The margin: 0 auto means that position.left() will return
+                * the left-most part of the entire block, rather than the actual
+                * position of the region on Chrome. Every other browser returns 0
+                * for this margin, as we'd expect. So, just play it safe and
+                * offset by the margin-left. (Bug #1050)
+                */
+                offset.left += $region.getExtents('m', 'l');
+
+                return {
+                    left: offset.left,
+                    top: offset.top,
+                    width: $region.width(),
+                    height: $region.height()
+                };
+            });
+    },
+});
+
+
 /**
  * Displays a color difference view of two images.
  *
@@ -804,12 +1217,17 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
 
     commentBlockView: RB.RegionCommentBlockView,
 
+    enableCommenting: true,
+
     events: {
         'click .image-diff-mode': '_onImageModeClicked',
         'click .image-resolution-menu .menu-item': '_onImageZoomLevelClicked',
         'mousedown .selection-container': '_onMouseDown',
         'mouseup .selection-container': '_onMouseUp',
-        'mousemove .selection-container': '_onMouseMove'
+        'mousemove .selection-container': '_onMouseMove',
+        'wheel .selection-container': '_onWheel',
+        'click .dragging-mode-toggle': '_onDraggingModeClicked',
+        'click .wheel-mode-toggle': '_onWheelModeClicked'
     },
 
     modeItemTemplate: _.template(
@@ -840,7 +1258,7 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
     /**
      * Initialize the view.
      */
-    initialize() {
+    async initialize() {
         RB.FileAttachmentReviewableView.prototype.initialize.apply(
             this, arguments);
 
@@ -857,7 +1275,7 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
          */
         this.on('commentBlockViewAdded', commentBlockView => {
             commentBlockView.setSelectionRegionSizeFunc(
-                () => _.pick(this._imageView.getSelectionRegion(),
+                async () => _.pick(await this._imageView.getSelectionRegion(),
                              'width', 'height'));
             commentBlockView.setScale(this.model.get('scale'));
 
@@ -899,6 +1317,7 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
      */
     renderContent() {
         const hasDiff = !!this.model.get('diffAgainstFileAttachmentID');
+        const isSVG = this.model.get('filename').endsWith('.svg');
 
         this._$selectionArea = $('<div/>')
             .addClass('selection-container')
@@ -951,6 +1370,18 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
                 .appendTo(this.$el);
 
             this._setDiffMode(ImageTwoUpDiffView.prototype.mode);
+        } else if (isSVG) {
+            this._imageView = new SvgAttachmentView({
+                model: this.model,
+            });
+
+            this._imageView.svgPromise.then(res => this._adjustPos());
+            this.enableCommenting = true;
+            this.enableZoomOnWheel = true;
+            $wrapper
+                .append(this._imageView.$el)
+                .appendTo(this.$el);
+            this._imageView.render();
         } else {
             this._imageView = new ImageAttachmentView({
                 model: this.model
@@ -1071,6 +1502,20 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
         } else {
             this.$('.caption').after($resolutionMenu);
         }
+
+        if (isSVG) {
+            const $svgControlMenu = $([
+              '<li class="svg-control-menu">',
+              ' <a href="#", class="wheel-mode-toggle">',
+              '  <span class="fa fa-arrows-alt" id="wheel-mode-icon"></span>',
+              ' </a>',
+              ' <a href="#", class="dragging-mode-toggle">',
+              '  <span class="fa fa-comment" id="dragging-mode-icon"></span>',
+              ' </a>',
+              '</li>',
+            ].join(''));
+            this.$('.caption').after($svgControlMenu);
+        }
     },
 
     /**
@@ -1247,6 +1692,90 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
         this._setDiffMode($(e.target).data('mode'));
     },
 
+    /**
+     * Handler for when a dragging mode in the header clicked.
+     *
+     * Sets the mode to spanning SVG from commenting, and vice versa.
+     * It wills also change the icon of the button to indicate
+     * the current mode.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+     _onDraggingModeClicked(e) {
+        e.preventDefault();
+        e.stopPropagation();
+
+        if (this.enableCommenting) {
+            this.$('#dragging-mode-icon')
+            .attr({
+                class: 'fa fa-arrows'
+            });
+            this.enableCommenting = false;
+        } else {
+            this.$('#dragging-mode-icon')
+            .attr({
+                class: 'fa fa-comment'
+            });
+            this.enableCommenting = true;
+        }
+    },
+
+    /**
+     * Handler for when a wheel mode in the header clicked.
+     *
+     * Sets the mode to being able to zoom into svg on mouse wheel
+     * if it is not enabled already. If it is enabled already,
+     * mouse wheel action comes back to default scrolling.
+     *
+     * It wills also change the icon of the button to indicate
+     * the current mode.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onWheelModeClicked(e) {
+        e.preventDefault();
+        e.stopPropagation();
+
+        if (this.enableZoomOnWheel) {
+            this.$('#wheel-mode-icon')
+            .attr({
+                class: 'fa fa-arrows-v'
+            });
+            this.enableZoomOnWheel = false;
+        } else {
+            this.$('#wheel-mode-icon')
+            .attr({
+                class: 'fa fa-arrows-alt'
+            });
+            this.enableZoomOnWheel = true;
+        }
+    },
+
+    /**
+     * Handler for wheel event.
+     *
+     * If the zooming in/out is enabled and the image is SVG, then
+     * it will trigger the corresponding handler in SvgAttachmentView.
+     * After the SVG is scaled appropriately, comment boxes positions
+     * are adjusted.
+     *
+     * Args:
+     *     e (Event):
+     *         The event which triggered the callback.
+     */
+    _onWheel(e) {
+        if (this.enableZoomOnWheel) {
+            this._imageView.trigger('viewBoxChanged', e);
+            const viewBox = this._imageView.getViewBox();
+            const scale = this._imageView.getViewBoxScale();
+            this._commentBlockViews.forEach(view => view.adjustPositionOnViewBox(viewBox, scale));
+        }
+    },
+
     /**
      * Handler for when a zoom level is clicked.
      *
@@ -1263,76 +1792,115 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
     /**
      * Handle a mousedown on the selection area.
      *
-     * If this is the first mouse button, and it's not being placed on
-     * an existing comment block, then this will begin the creation of a new
-     * comment block starting at the mousedown coordinates.
+     * If commenting is enabled and if this is the first mouse button
+     * and it's not being placed on an existing comment block, then this
+     * will begin the creation of a new comment block starting at the
+     * mousedown coordinates.
+     *
+     * If commenting is not enabled and the image is SVG, then
+     * it will trigger corresponding handler in the SvgAttachmentView.
      *
      * Args:
      *     e (Event):
      *         The event which triggered the callback.
      */
     _onMouseDown(e) {
-        if (e.which === 1 &&
-            !this.commentDlg &&
-            !$(e.target).hasClass('selection-flag')) {
-            const offset = this._$selectionArea.offset();
-
-            this._activeSelection.beginX =
-                e.pageX - Math.floor(offset.left) - 1;
-            this._activeSelection.beginY =
-                e.pageY - Math.floor(offset.top) - 1;
-
-            const updateData = {
-                left: this._activeSelection.beginX,
-                top: this._activeSelection.beginY,
-                width: 1,
-                height: 1
-            };
+        if (!this.enableCommenting) {
+            this._imageView.trigger('svgMouseDown', e);
+        } else {
+            if (e.which === 1 &&
+                !this.commentDlg &&
+                !$(e.target).hasClass('selection-flag')) {
+                const offset = this._$selectionArea.offset();
+
+                this._activeSelection.beginX =
+                    e.pageX - Math.floor(offset.left) - 1;
+                this._activeSelection.beginY =
+                    e.pageY - Math.floor(offset.top) - 1;
+
+                const updateData = {
+                    left: this._activeSelection.beginX,
+                    top: this._activeSelection.beginY,
+                    width: 1,
+                    height: 1
+                };
 
-            this._$selectionRect
-                .css(updateData)
-                .data(updateData)
-                .show();
+                this._$selectionRect
+                    .css(updateData)
+                    .data(updateData)
+                    .show();
 
-            if (this._$selectionRect.is(':hidden')) {
-                this.commentDlg.close();
-            }
+                if (this._$selectionRect.is(':hidden')) {
+                    this.commentDlg.close();
+                }
 
-            return false;
+                return false;
+            }
         }
     },
 
     /**
      * Handle a mouseup on the selection area.
      *
-     * This will finalize the creation of a comment block and pop up the
-     * comment dialog.
+     * If commenting is enabled then this will finalize the creation
+     * of a comment block and pop up the comment dialog.
      *
+     * If commenting is not enabled and the image is SVG, then
+     * it will trigger corresponding handler in the SvgAttachmentView.
      * Args:
      *     e (Event):
      *         The event which triggered the callback.
      */
     _onMouseUp(e) {
-        if (!this.commentDlg &&
-            this._$selectionRect.is(':visible')) {
-            e.stopPropagation();
-            this._$selectionRect.hide();
-
-            /*
-             * If we don't pass an arbitrary minimum size threshold,
-             * don't do anything. This helps avoid making people mad
-             * if they accidentally click on the image.
-             */
-            const position = this._$selectionRect.data();
-            const scale = this.model.get('scale');
-
-            if (position.width > 5 && position.height > 5) {
-                this.createAndEditCommentBlock({
-                    x: Math.floor(position.left / scale),
-                    y: Math.floor(position.top / scale),
-                    width: Math.floor(position.width / scale),
-                    height: Math.floor(position.height / scale),
-                });
+        if (!this.enableCommenting) {
+            this._imageView.trigger('svgMouseUp', e);
+            const scale = this._imageView.getViewBoxScale();
+            const viewBox = this._imageView.getViewBox();
+            this._commentBlockViews.forEach(view => view.adjustPositionOnViewBox(viewBox, scale));
+        } else {
+            if (!this.commentDlg &&
+                this._$selectionRect.is(':visible')) {
+                e.stopPropagation();
+                this._$selectionRect.hide();
+
+                /*
+                 * If we don't pass an arbitrary minimum size threshold,
+                 * don't do anything. This helps avoid making people mad
+                 * if they accidentally click on the image.
+                 */
+                const position = this._$selectionRect.data();
+                const scale = this.model.get('scale');
+
+                if (position.width > 5 && position.height > 5) {
+                    /*
+                     * If the image is SVG, we need to scale the coordinates
+                     * and size according to the viewBox.
+                     */
+                    if (this._imageView.getViewBox()) {
+                        const viewBox = this._imageView.getViewBox();
+                        const viewBoxScale = this._imageView.getViewBoxScale();
+                        this.createAndEditCommentBlock({
+                            x: Math.floor((position.left / viewBoxScale) + viewBox.x),
+                            y: Math.floor((position.top / viewBoxScale) + viewBox.y),
+                            width: Math.floor(position.width / viewBoxScale),
+                            height: Math.floor(position.height / viewBoxScale),
+                        });
+                        /*
+                         * We need to readjust the position, otherwise they will be
+                         * displayed based on the original scale and not the viewBox scale.
+                         */
+                        this._commentBlockViews.forEach(view => {
+                            view.adjustPositionOnViewBox(viewBox, viewBoxScale);
+                        });
+                    } else {
+                        this.createAndEditCommentBlock({
+                            x: Math.floor(position.left / scale),
+                            y: Math.floor(position.top / scale),
+                            width: Math.floor(position.width / scale),
+                            height: Math.floor(position.height / scale),
+                        });
+                    }
+                }
             }
         }
     },
@@ -1340,49 +1908,59 @@ RB.ImageReviewableView = RB.FileAttachmentReviewableView.extend({
     /**
      * Handle a mousemove on the selection area.
      *
-     * If we're creating a comment block, this will update the
-     * size/position of the block.
+     * If commenting is enabled and if we're creating a comment block,
+     * this will update the size/position of the block.
+     *
+     * If commenting is not enabled and the image is SVG, then
+     * it will trigger corresponding handler in the SvgAttachmentView.
      *
      * Args:
      *     e (Event):
      *         The event which triggered the callback.
      */
     _onMouseMove(e) {
-        if (!this.commentDlg && this._$selectionRect.is(':visible')) {
-            const offset = this._$selectionArea.offset();
-            const x = e.pageX - Math.floor(offset.left) - 1;
-            const y = e.pageY - Math.floor(offset.top) - 1;
-            const updateData = {};
-
-            if (this._activeSelection.beginX <= x) {
-                updateData.left = this._activeSelection.beginX;
-                updateData.width = x - this._activeSelection.beginX;
-            } else {
-                updateData.left = x;
-                updateData.width = this._activeSelection.beginX - x;
-            }
+        if (!this.enableCommenting) {
+            this._imageView.trigger('svgMouseMove', e);
+            const scale = this._imageView.getViewBoxScale();
+            const movedViewBox = this._imageView.getViewBox();
+            this._commentBlockViews.forEach(view => view.adjustPositionOnViewBox(movedViewBox, scale));
+        } else {
+            if (!this.commentDlg && this._$selectionRect.is(':visible')) {
+                const offset = this._$selectionArea.offset();
+                const x = e.pageX - Math.floor(offset.left) - 1;
+                const y = e.pageY - Math.floor(offset.top) - 1;
+                const updateData = {};
+
+                if (this._activeSelection.beginX <= x) {
+                    updateData.left = this._activeSelection.beginX;
+                    updateData.width = x - this._activeSelection.beginX;
+                } else {
+                    updateData.left = x;
+                    updateData.width = this._activeSelection.beginX - x;
+                }
 
-            if (this._activeSelection.beginY <= y) {
-                updateData.top = this._activeSelection.beginY;
-                updateData.height = y - this._activeSelection.beginY;
-            } else {
-                updateData.top = y;
-                updateData.height = this._activeSelection.beginY - y;
-            }
+                if (this._activeSelection.beginY <= y) {
+                    updateData.top = this._activeSelection.beginY;
+                    updateData.height = y - this._activeSelection.beginY;
+                } else {
+                    updateData.top = y;
+                    updateData.height = this._activeSelection.beginY - y;
+                }
 
-            this._$selectionRect
-                .css(updateData)
-                .data(updateData);
+                this._$selectionRect
+                    .css(updateData)
+                    .data(updateData);
 
-            return false;
+                return false;
+            }
         }
     },
 
     /**
      * Reposition the selection area to the right locations.
      */
-    _adjustPos() {
-        const region = this._imageView.getSelectionRegion();
+    async _adjustPos() {
+        const region = await this._imageView.getSelectionRegion();
 
         this._$selectionArea
             .width(region.width)
diff --git a/reviewboard/static/rb/js/views/regionCommentBlockView.es6.js b/reviewboard/static/rb/js/views/regionCommentBlockView.es6.js
index e15716e7ba77619fe97547f601c4939ef426cdcd..c5bbb667ad3f209f5db2c62be6b4a8a2d780a670 100644
--- a/reviewboard/static/rb/js/views/regionCommentBlockView.es6.js
+++ b/reviewboard/static/rb/js/views/regionCommentBlockView.es6.js
@@ -333,5 +333,28 @@ RB.RegionCommentBlockView = RB.AbstractCommentBlockView.extend({
     setScale(scale) {
         this._scale = scale;
         this._updateBounds();
+    },
+
+    /**
+     * Adjust position of comment box based the current viewBox
+     *
+     * Args:
+     *     viewBox (object):
+     *         object representing viewBox.
+     *         Must have keys: x, y, width and height.
+     *
+     *     viewBoxScale (number):
+     *         A scaling factor based on the viewBox width and SVG width.
+     *         1.0 is a 1:1 pixel ratio, 0.5 is displayed
+     *         at half size, etc.
+     */
+    adjustPositionOnViewBox(viewBox, viewBoxScale) {
+        this._scale = viewBoxScale;
+        this.$el
+        .move((this.model.get('x') - viewBox.x) * viewBoxScale,
+              (this.model.get('y') - viewBox.y) * viewBoxScale,
+              'absolute')
+        .width(this.model.get('width') * viewBoxScale)
+        .height(this.model.get('height') * viewBoxScale);
     }
 });
