diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php
index 173b92b6c..0b08c7828 100644
--- a/src/applications/files/PhabricatorImageTransformer.php
+++ b/src/applications/files/PhabricatorImageTransformer.php
@@ -1,273 +1,295 @@
 <?php
 
 final class PhabricatorImageTransformer {
 
   public function executeMemeTransform(
     PhabricatorFile $file,
     $upper_text,
     $lower_text) {
     $image = $this->applyMemeTo($file, $upper_text, $lower_text);
     return PhabricatorFile::newFromFileData(
       $image,
       array(
         'name' => 'meme-'.$file->getName(),
       ));
   }
 
   public function executeThumbTransform(
     PhabricatorFile $file,
     $x,
     $y) {
 
     $image = $this->crudelyScaleTo($file, $x, $y);
 
     return PhabricatorFile::newFromFileData(
       $image,
       array(
         'name' => 'thumb-'.$file->getName(),
       ));
   }
 
   public function executeProfileTransform(
     PhabricatorFile $file,
     $x,
     $min_y,
     $max_y) {
 
     $image = $this->crudelyCropTo($file, $x, $min_y, $max_y);
 
     return PhabricatorFile::newFromFileData(
       $image,
       array(
         'name' => 'profile-'.$file->getName(),
       ));
   }
 
   public function executePreviewTransform(
     PhabricatorFile $file,
     $size) {
 
     $image = $this->generatePreview($file, $size);
 
     return PhabricatorFile::newFromFileData(
       $image,
       array(
         'name' => 'preview-'.$file->getName(),
       ));
   }
 
 
   private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) {
     $data = $file->loadFileData();
     $img = imagecreatefromstring($data);
     $sx = imagesx($img);
     $sy = imagesy($img);
 
     $scaled_y = ($x / $sx) * $sy;
     if ($scaled_y > $max_y) {
       // This image is very tall and thin.
       $scaled_y = $max_y;
     } else if ($scaled_y < $min_y) {
       // This image is very short and wide.
       $scaled_y = $min_y;
     }
 
     $img = $this->applyScaleTo(
       $img,
       $x,
       $scaled_y);
 
     return $this->saveImageDataInAnyFormat($img, $file->getMimeType());
   }
 
   /**
    * Very crudely scale an image up or down to an exact size.
    */
   private function crudelyScaleTo(PhabricatorFile $file, $dx, $dy) {
     $data = $file->loadFileData();
     $src = imagecreatefromstring($data);
 
     $dst = $this->applyScaleTo($src, $dx, $dy);
 
     return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
   }
 
   private function applyScaleTo($src, $dx, $dy) {
     $x = imagesx($src);
     $y = imagesy($src);
 
     $scale = min(($dx / $x), ($dy / $y), 1);
 
     $dst = imagecreatetruecolor($dx, $dy);
     imagesavealpha($dst, true);
     imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
 
     $sdx = $scale * $x;
     $sdy = $scale * $y;
 
     imagecopyresampled(
       $dst,
       $src,
       ($dx - $sdx) / 2,  ($dy - $sdy) / 2,
       0, 0,
       $sdx, $sdy,
       $x, $y);
 
     return $dst;
   }
 
-  private function generatePreview(PhabricatorFile $file, $size) {
+  public static function getPreviewDimensions(PhabricatorFile $file, $size) {
     $data = $file->loadFileData();
     $src = imagecreatefromstring($data);
 
     $x = imagesx($src);
     $y = imagesy($src);
 
     $scale = min($size / $x, $size / $y, 1);
 
     $dx = max($size / 4, $scale * $x);
     $dy = max($size / 4, $scale * $y);
 
+    $sdx = $scale * $x;
+    $sdy = $scale * $y;
+
+    return array(
+      'x' => $x,
+      'y' => $y,
+      'dx' => $dx,
+      'dy' => $dy,
+      'sdx' => $sdx,
+      'sdy' => $sdy
+    );
+  }
+
+  private function generatePreview(PhabricatorFile $file, $size) {
+    $data = $file->loadFileData();
+    $src = imagecreatefromstring($data);
+
+    $dimensions = self::getPreviewDimensions($file, $size);
+    $x = $dimensions['x'];
+    $y = $dimensions['y'];
+    $dx = $dimensions['dx'];
+    $dy = $dimensions['dy'];
+    $sdx = $dimensions['sdx'];
+    $sdy = $dimensions['sdy'];
+
     $dst = imagecreatetruecolor($dx, $dy);
     imagesavealpha($dst, true);
     imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
 
-    $sdx = $scale * $x;
-    $sdy = $scale * $y;
-
     imagecopyresampled(
       $dst,
       $src,
       ($dx - $sdx) / 2, ($dy - $sdy) / 2,
       0, 0,
       $sdx, $sdy,
       $x, $y);
 
     return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
   }
 
   private function applyMemeTo(
     PhabricatorFile $file,
     $upper_text,
     $lower_text) {
     $data = $file->loadFileData();
     $img = imagecreatefromstring($data);
     $phabricator_root = dirname(phutil_get_library_root('phabricator'));
     $font_path = $phabricator_root.'/resources/font/tuffy.ttf';
     $white = imagecolorallocate($img, 255, 255, 255);
     $black = imagecolorallocate($img, 0, 0, 0);
     $border_width = 3;
     $font_max = 200;
     $font_min = 5;
     for ($i = $font_max; $i > $font_min; $i--) {
       $fit = $this->doesTextBoundingBoxFitInImage(
         $img,
         $upper_text,
         $i,
         $font_path);
       if ($fit['doesfit']) {
         $x = ($fit['imgwidth'] - $fit['txtwidth']) / 2;
         $y = $fit['txtheight'] + 10;
         $this->makeImageWithTextBorder($img,
           $i,
           $x,
           $y,
           $white,
           $black,
           $border_width,
           $font_path,
           $upper_text);
         break;
       }
     }
     for ($i = $font_max; $i > $font_min; $i--) {
       $fit = $this->doesTextBoundingBoxFitInImage($img,
         $lower_text, $i, $font_path);
       if ($fit['doesfit']) {
         $x = ($fit['imgwidth'] - $fit['txtwidth']) / 2;
         $y = $fit['imgheight'] - 10;
         $this->makeImageWithTextBorder(
           $img,
           $i,
           $x,
           $y,
           $white,
           $black,
           $border_width,
           $font_path,
           $lower_text);
         break;
       }
     }
     return $this->saveImageDataInAnyFormat($img, $file->getMimeType());
   }
 
   private function makeImageWithTextBorder($img, $font_size, $x, $y,
     $color, $stroke_color, $bw, $font, $text) {
     $angle = 0;
     $bw = abs($bw);
     for ($c1 = $x - $bw; $c1 <= $x + $bw; $c1++) {
       for ($c2 = $y - $bw; $c2 <= $y + $bw; $c2++) {
         if (!(($c1 == $x - $bw || $x + $bw) &&
           $c2 == $y - $bw || $c2 == $y + $bw)) {
           $bg = imagettftext($img, $font_size,
             $angle, $c1, $c2, $stroke_color, $font, $text);
           }
         }
       }
     imagettftext($img, $font_size, $angle,
             $x , $y, $color , $font, $text);
   }
 
   private function doesTextBoundingBoxFitInImage($img,
     $text, $font_size, $font_path) {
     // Default Angle = 0
     $angle = 0;
 
     $bbox = imagettfbbox($font_size, $angle, $font_path, $text);
     $text_height = abs($bbox[3] - $bbox[5]);
     $text_width = abs($bbox[0] - $bbox[2]);
     return array(
       "doesfit" => ($text_height * 1.05 <= imagesy($img) / 2
         && $text_width * 1.05 <= imagesx($img)),
       "txtwidth" => $text_width,
       "txtheight" => $text_height,
       "imgwidth" => imagesx($img),
       "imgheight" => imagesy($img),
     );
   }
 
   private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
     switch ($preferred_mime) {
       case 'image/gif': // GIF doesn't support true color.
       case 'image/png':
         if (function_exists('imagepng')) {
           ob_start();
           imagepng($data);
           return ob_get_clean();
         }
         break;
     }
 
     $img = null;
 
     if (function_exists('imagejpeg')) {
       ob_start();
       imagejpeg($data);
       $img = ob_get_clean();
     } else if (function_exists('imagepng')) {
       ob_start();
       imagepng($data);
       $img = ob_get_clean();
     } else if (function_exists('imagegif')) {
       ob_start();
       imagegif($data);
       $img = ob_get_clean();
     } else {
       throw new Exception("No image generation functions exist!");
     }
 
     return $img;
   }
 
 }
diff --git a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleEmbedFile.php b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleEmbedFile.php
index 4aa893861..31ec888b2 100644
--- a/src/infrastructure/markup/rule/PhabricatorRemarkupRuleEmbedFile.php
+++ b/src/infrastructure/markup/rule/PhabricatorRemarkupRuleEmbedFile.php
@@ -1,180 +1,183 @@
 <?php
 
 /**
  * @group markup
  */
 final class PhabricatorRemarkupRuleEmbedFile
   extends PhutilRemarkupRule {
 
   const KEY_RULE_EMBED_FILE = 'rule.embed.file';
   const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
 
   public function apply($text) {
     return preg_replace_callback(
       "@{F(\d+)([^}]+?)?}@",
       array($this, 'markupEmbedFile'),
       $text);
   }
 
   public function markupEmbedFile($matches) {
 
     $file = null;
     if ($matches[1]) {
       // TODO: This is pretty inefficient if there are a bunch of files.
       $file = id(new PhabricatorFile())->load($matches[1]);
     }
 
     if (!$file) {
       return $matches[0];
     }
     $phid = $file->getPHID();
 
     $engine = $this->getEngine();
     $token = $engine->storeText('');
     $metadata_key = self::KEY_RULE_EMBED_FILE;
     $metadata = $engine->getTextMetadata($metadata_key, array());
     $bundle = array('token' => $token);
 
     $options = array(
       'size'    => 'thumb',
       'layout'  => 'left',
       'float'   => false,
       'name'    => null,
     );
 
     if (!empty($matches[2])) {
       $matches[2] = trim($matches[2], ', ');
       $parser = new PhutilSimpleOptions();
       $options = $parser->parse($matches[2]) + $options;
     }
     $file_name = coalesce($options['name'], $file->getName());
     $options['name'] = $file_name;
 
     $attrs = array();
     switch ((string)$options['size']) {
       case 'full':
         $attrs['src'] = $file->getBestURI();
         $options['image_class'] = null;
         $file_data = $file->getMetadata();
         $height = idx($file_data, PhabricatorFile::METADATA_IMAGE_HEIGHT);
         if ($height) {
           $attrs['height'] = $height;
         }
         $width = idx($file_data, PhabricatorFile::METADATA_IMAGE_WIDTH);
         if ($width) {
           $attrs['width'] = $width;
         }
         break;
       case 'thumb':
       default:
         $attrs['src'] = $file->getPreview220URI();
-        $attrs['width'] = '220';
+        $dimensions =
+          PhabricatorImageTransformer::getPreviewDimensions($file, 220);
+        $attrs['width'] = $dimensions['sdx'];
+        $attrs['height'] = $dimensions['sdy'];
         $options['image_class'] = 'phabricator-remarkup-embed-image';
         break;
     }
     $bundle['attrs'] = $attrs;
     $bundle['options'] = $options;
 
     $bundle['meta'] = array(
       'phid'     => $file->getPHID(),
       'viewable' => $file->isViewableImage(),
       'uri'      => $file->getBestURI(),
       'dUri'     => $file->getDownloadURI(),
       'name'     => $options['name'],
     );
     $metadata[$phid][] = $bundle;
     $engine->setTextMetadata($metadata_key, $metadata);
 
     return $token;
   }
 
   public function didMarkupText() {
     $engine = $this->getEngine();
 
     $metadata_key = self::KEY_RULE_EMBED_FILE;
     $metadata = $engine->getTextMetadata($metadata_key, array());
 
     if (!$metadata) {
       return;
     }
 
     $file_phids = array();
     foreach ($metadata as $phid => $bundles) {
       foreach ($bundles as $data) {
 
         $options = $data['options'];
         $meta    = $data['meta'];
 
         if (!$meta['viewable'] || $options['layout'] == 'link') {
           $link = id(new PhabricatorFileLinkView())
             ->setFilePHID($meta['phid'])
             ->setFileName($meta['name'])
             ->setFileDownloadURI($meta['dUri'])
             ->setFileViewURI($meta['uri'])
             ->setFileViewable($meta['viewable']);
           $embed = $link->render();
           $engine->overwriteStoredText($data['token'], $embed);
           continue;
         }
 
         require_celerity_resource('lightbox-attachment-css');
         $img = phutil_render_tag('img', $data['attrs']);
 
         $embed = javelin_render_tag(
           'a',
           array(
             'href'        => $meta['uri'],
             'class'       => $options['image_class'],
             'sigil'       => 'lightboxable',
             'mustcapture' => true,
             'meta'        => $meta,
           ),
           $img);
 
         $layout_class = null;
         switch ($options['layout']) {
           case 'right':
           case 'center':
           case 'inline':
           case 'left':
             $layout_class = 'phabricator-remarkup-embed-layout-'.
               $options['layout'];
             break;
           default:
             $layout_class = 'phabricator-remarkup-embed-layout-left';
             break;
         }
 
         if ($options['float']) {
           switch ($options['layout']) {
             case 'center':
             case 'inline':
               break;
             case 'right':
               $layout_class .= ' phabricator-remarkup-embed-float-right';
               break;
             case 'left':
             default:
               $layout_class .= ' phabricator-remarkup-embed-float-left';
               break;
           }
         }
 
         if ($layout_class) {
           $embed = phutil_render_tag(
             'div',
             array(
               'class' => $layout_class,
             ),
             $embed);
         }
 
         $engine->overwriteStoredText($data['token'], $embed);
       }
       $file_phids[] = $phid;
     }
     $engine->setTextMetadata(self::KEY_EMBED_FILE_PHIDS, $file_phids);
     $engine->setTextMetadata($metadata_key, array());
   }
 
 }