diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -10,7 +10,7 @@
     'conpherence.pkg.css' => 'e68cf1fa',
     'conpherence.pkg.js' => '15191c65',
     'core.pkg.css' => '1dd5fa4b',
-    'core.pkg.js' => 'b9b4a943',
+    'core.pkg.js' => '1ea38af8',
     'differential.pkg.css' => '113e692c',
     'differential.pkg.js' => 'f6d809c0',
     'diffusion.pkg.css' => 'a2d17c7d',
@@ -392,7 +392,7 @@
     'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'f01586dc',
     'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '1db13e70',
     'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
-    'rsrc/js/application/files/behavior-document-engine.js' => '194cbe53',
+    'rsrc/js/application/files/behavior-document-engine.js' => '9108ee1a',
     'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
     'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
     'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '191b4909',
@@ -508,7 +508,7 @@
     'rsrc/js/phui/behavior-phui-submenu.js' => 'a6f7a73b',
     'rsrc/js/phui/behavior-phui-tab-group.js' => '0a0b10e9',
     'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
-    'rsrc/js/phuix/PHUIXActionView.js' => 'ed18356a',
+    'rsrc/js/phuix/PHUIXActionView.js' => '8d4a8c72',
     'rsrc/js/phuix/PHUIXAutocomplete.js' => 'df1bbd34',
     'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
     'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
@@ -607,7 +607,7 @@
     'javelin-behavior-diffusion-jump-to' => '73d09eef',
     'javelin-behavior-diffusion-locate-file' => '6d3e1947',
     'javelin-behavior-diffusion-pull-lastmodified' => 'f01586dc',
-    'javelin-behavior-document-engine' => '194cbe53',
+    'javelin-behavior-document-engine' => '9108ee1a',
     'javelin-behavior-doorkeeper-tag' => '1db13e70',
     'javelin-behavior-drydock-live-operation-status' => '901935ef',
     'javelin-behavior-durable-column' => '2ae077e1',
@@ -864,7 +864,7 @@
     'phui-workcard-view-css' => 'cca5fa92',
     'phui-workpanel-view-css' => 'a3a63478',
     'phuix-action-list-view' => 'b5c256b8',
-    'phuix-action-view' => 'ed18356a',
+    'phuix-action-view' => '8d4a8c72',
     'phuix-autocomplete' => 'df1bbd34',
     'phuix-button-view' => '8a91e1ac',
     'phuix-dropdown-menu' => '04b2ae03',
@@ -983,11 +983,6 @@
     '191b4909' => array(
       'javelin-behavior',
     ),
-    '194cbe53' => array(
-      'javelin-behavior',
-      'javelin-dom',
-      'javelin-stratcom',
-    ),
     '1ad0a787' => array(
       'javelin-install',
       'javelin-reactor',
@@ -1619,6 +1614,11 @@
       'javelin-stratcom',
       'javelin-install',
     ),
+    '8d4a8c72' => array(
+      'javelin-install',
+      'javelin-dom',
+      'javelin-util',
+    ),
     '8e1baf68' => array(
       'phui-button-css',
     ),
@@ -1644,6 +1644,11 @@
       'javelin-stratcom',
       'javelin-vector',
     ),
+    '9108ee1a' => array(
+      'javelin-behavior',
+      'javelin-dom',
+      'javelin-stratcom',
+    ),
     '92b9ec77' => array(
       'javelin-behavior',
       'javelin-stratcom',
@@ -2125,11 +2130,6 @@
       'javelin-stratcom',
       'javelin-vector',
     ),
-    'ed18356a' => array(
-      'javelin-install',
-      'javelin-dom',
-      'javelin-util',
-    ),
     'edf8a145' => array(
       'javelin-behavior',
       'javelin-uri',
diff --git a/src/applications/files/controller/PhabricatorFileDocumentController.php b/src/applications/files/controller/PhabricatorFileDocumentController.php
--- a/src/applications/files/controller/PhabricatorFileDocumentController.php
+++ b/src/applications/files/controller/PhabricatorFileDocumentController.php
@@ -43,6 +43,16 @@
     $engine = $engines[$engine_key];
     $this->engine = $engine;
 
+    $encode_setting = $request->getStr('encode');
+    if (strlen($encode_setting)) {
+      $engine->setEncodingConfiguration($encode_setting);
+    }
+
+    $highlight_setting = $request->getStr('highlight');
+    if (strlen($highlight_setting)) {
+      $engine->setHighlightingConfiguration($highlight_setting);
+    }
+
     try {
       $content = $engine->newDocument($ref);
     } catch (Exception $ex) {
diff --git a/src/applications/files/controller/PhabricatorFileViewController.php b/src/applications/files/controller/PhabricatorFileViewController.php
--- a/src/applications/files/controller/PhabricatorFileViewController.php
+++ b/src/applications/files/controller/PhabricatorFileViewController.php
@@ -422,6 +422,16 @@
       $engine->setHighlightedLines(range($lines[0], $lines[1]));
     }
 
+    $encode_setting = $request->getStr('encode');
+    if (strlen($encode_setting)) {
+      $engine->setEncodingConfiguration($encode_setting);
+    }
+
+    $highlight_setting = $request->getStr('highlight');
+    if (strlen($highlight_setting)) {
+      $engine->setHighlightingConfiguration($highlight_setting);
+    }
+
     $views = array();
     foreach ($engines as $candidate_key => $candidate_engine) {
       $label = $candidate_engine->getViewAsLabel($ref);
@@ -443,6 +453,8 @@
         'engineURI' => $candidate_engine->getRenderURI($ref),
         'viewURI' => $view_uri,
         'loadingMarkup' => hsprintf('%s', $loading),
+        'canEncode' => $candidate_engine->canConfigureEncoding($ref),
+        'canHighlight' => $candidate_engine->CanConfigureHighlighting($ref),
       );
     }
 
@@ -474,6 +486,18 @@
       'viewKey' => $engine->getDocumentEngineKey(),
       'views' => $views,
       'standaloneURI' => $engine->getRenderURI($ref),
+      'encode' => array(
+        'icon' => 'fa-font',
+        'name' => pht('Change Text Encoding...'),
+        'uri' => '/services/encoding/',
+        'value' => $encode_setting,
+      ),
+      'highlight' => array(
+        'icon' => 'fa-lightbulb-o',
+        'name' => pht('Highlight As...'),
+        'uri' => '/services/highlight/',
+        'value' => $highlight_setting,
+      ),
     );
 
     $view_button = id(new PHUIButtonView())
diff --git a/src/applications/files/document/PhabricatorDocumentEngine.php b/src/applications/files/document/PhabricatorDocumentEngine.php
--- a/src/applications/files/document/PhabricatorDocumentEngine.php
+++ b/src/applications/files/document/PhabricatorDocumentEngine.php
@@ -5,6 +5,8 @@
 
   private $viewer;
   private $highlightedLines = array();
+  private $encodingConfiguration;
+  private $highlightingConfiguration;
 
   final public function setViewer(PhabricatorUser $viewer) {
     $this->viewer = $viewer;
@@ -28,6 +30,32 @@
     return $this->canRenderDocumentType($ref);
   }
 
+  public function canConfigureEncoding(PhabricatorDocumentRef $ref) {
+    return false;
+  }
+
+  public function canConfigureHighlighting(PhabricatorDocumentRef $ref) {
+    return false;
+  }
+
+  final public function setEncodingConfiguration($config) {
+    $this->encodingConfiguration = $config;
+    return $this;
+  }
+
+  final public function getEncodingConfiguration() {
+    return $this->encodingConfiguration;
+  }
+
+  final public function setHighlightingConfiguration($config) {
+    $this->highlightingConfiguration = $config;
+    return $this;
+  }
+
+  final public function getHighlightingConfiguration() {
+    return $this->highlightingConfiguration;
+  }
+
   public function shouldRenderAsync(PhabricatorDocumentRef $ref) {
     return false;
   }
diff --git a/src/applications/files/document/PhabricatorSourceDocumentEngine.php b/src/applications/files/document/PhabricatorSourceDocumentEngine.php
--- a/src/applications/files/document/PhabricatorSourceDocumentEngine.php
+++ b/src/applications/files/document/PhabricatorSourceDocumentEngine.php
@@ -9,6 +9,10 @@
     return pht('View as Source');
   }
 
+  public function canConfigureHighlighting(PhabricatorDocumentRef $ref) {
+    return true;
+  }
+
   protected function getDocumentIconIcon(PhabricatorDocumentRef $ref) {
     return 'fa-code';
   }
@@ -20,9 +24,16 @@
   protected function newDocumentContent(PhabricatorDocumentRef $ref) {
     $content = $this->loadTextData($ref);
 
-    $content = PhabricatorSyntaxHighlighter::highlightWithFilename(
-      $ref->getName(),
-      $content);
+    $highlighting = $this->getHighlightingConfiguration();
+    if ($highlighting !== null) {
+      $content = PhabricatorSyntaxHighlighter::highlightWithLanguage(
+        $highlighting,
+        $content);
+    } else {
+      $content = PhabricatorSyntaxHighlighter::highlightWithFilename(
+        $ref->getName(),
+        $content);
+    }
 
     return $this->newTextDocumentContent($content);
   }
diff --git a/src/applications/files/document/PhabricatorTextDocumentEngine.php b/src/applications/files/document/PhabricatorTextDocumentEngine.php
--- a/src/applications/files/document/PhabricatorTextDocumentEngine.php
+++ b/src/applications/files/document/PhabricatorTextDocumentEngine.php
@@ -3,10 +3,16 @@
 abstract class PhabricatorTextDocumentEngine
   extends PhabricatorDocumentEngine {
 
+  private $encodingMessage = null;
+
   protected function canRenderDocumentType(PhabricatorDocumentRef $ref) {
     return $ref->isProbablyText();
   }
 
+  public function canConfigureEncoding(PhabricatorDocumentRef $ref) {
+    return true;
+  }
+
   protected function newTextDocumentContent($content) {
     $lines = phutil_split_lines($content);
 
@@ -14,19 +20,69 @@
       ->setHighlights($this->getHighlightedLines())
       ->setLines($lines);
 
+    $message = null;
+    if ($this->encodingMessage !== null) {
+      $message = $this->newMessage($this->encodingMessage);
+    }
+
     $container = phutil_tag(
       'div',
       array(
         'class' => 'document-engine-text',
       ),
-      $view);
+      array(
+        $message,
+        $view,
+      ));
 
     return $container;
   }
 
   protected function loadTextData(PhabricatorDocumentRef $ref) {
     $content = $ref->loadData();
-    $content = phutil_utf8ize($content);
+
+    $encoding = $this->getEncodingConfiguration();
+    if ($encoding !== null) {
+      if (function_exists('mb_convert_encoding')) {
+        $content = mb_convert_encoding($content, 'UTF-8', $encoding);
+        $this->encodingMessage = pht(
+          'This document was converted from %s to UTF8 for display.',
+          $encoding);
+      } else {
+        $this->encodingMessage = pht(
+          'Unable to perform text encoding conversion: mbstring extension '.
+          'is not available.');
+      }
+    } else {
+      if (!phutil_is_utf8($content)) {
+        if (function_exists('mb_detect_encoding')) {
+          $try_encodings = array(
+            'JIS' => pht('JIS'),
+            'EUC-JP' => pht('EUC-JP'),
+            'SJIS' => pht('Shift JIS'),
+            'ISO-8859-1' => pht('ISO-8859-1 (Latin 1)'),
+          );
+
+          $guess = mb_detect_encoding($content, array_keys($try_encodings));
+          if ($guess) {
+            $content = mb_convert_encoding($content, 'UTF-8', $guess);
+            $this->encodingMessage = pht(
+              'This document is not UTF8. It was detected as %s and '.
+              'converted to UTF8 for display.',
+              idx($try_encodings, $guess, $guess));
+          }
+        }
+      }
+    }
+
+    if (!phutil_is_utf8($content)) {
+      $content = phutil_utf8ize($content);
+      $this->encodingMessage = pht(
+        'This document is not UTF8 and its text encoding could not be '.
+        'detected automatically. Use "Change Text Encoding..." to choose '.
+        'an encoding.');
+    }
+
     return $content;
   }
 
diff --git a/webroot/rsrc/js/application/files/behavior-document-engine.js b/webroot/rsrc/js/application/files/behavior-document-engine.js
--- a/webroot/rsrc/js/application/files/behavior-document-engine.js
+++ b/webroot/rsrc/js/application/files/behavior-document-engine.js
@@ -52,6 +52,61 @@
       });
     }
 
+    list.addItem(
+      new JX.PHUIXActionView()
+        .setDivider(true));
+
+    var encode_item = new JX.PHUIXActionView()
+      .setName(data.encode.name)
+      .setIcon(data.encode.icon);
+
+    var onencode = JX.bind(null, function(data, e) {
+      e.prevent();
+
+      if (encode_item.getDisabled()) {
+        return;
+      }
+
+      new JX.Workflow(data.encode.uri, {encoding: data.encode.value})
+        .setHandler(function(r) {
+          data.encode.value = r.encoding;
+          onview(data);
+        })
+        .start();
+
+      menu.close();
+
+    }, data);
+
+    encode_item.setHandler(onencode);
+
+    list.addItem(encode_item);
+
+    var highlight_item = new JX.PHUIXActionView()
+      .setName(data.highlight.name)
+      .setIcon(data.highlight.icon);
+
+    var onhighlight = JX.bind(null, function(data, e) {
+      e.prevent();
+
+      if (highlight_item.getDisabled()) {
+        return;
+      }
+
+      new JX.Workflow(data.highlight.uri, {highlight: data.highlight.value})
+        .setHandler(function(r) {
+          data.highlight.value = r.highlight;
+          onview(data);
+        })
+        .start();
+
+      menu.close();
+    }, data);
+
+    highlight_item.setHandler(onhighlight);
+
+    list.addItem(highlight_item);
+
     menu.setContent(list.getNode());
 
     menu.listen('open', function() {
@@ -61,6 +116,11 @@
         // Highlight the current rendering engine.
         var is_selected = (engine.spec.viewKey == data.viewKey);
         engine.view.setSelected(is_selected);
+
+        if (is_selected) {
+          encode_item.setDisabled(!engine.spec.canEncode);
+          highlight_item.setDisabled(!engine.spec.canHighlight);
+        }
       }
     });
 
@@ -68,13 +128,38 @@
     menu.open();
   }
 
+  function add_params(uri, data) {
+    uri = JX.$U(uri);
+
+    if (data.highlight.value) {
+      uri.setQueryParam('highlight', data.highlight.value);
+    }
+
+    if (data.encode.value) {
+      uri.setQueryParam('encode', data.encode.value);
+    }
+
+    return uri.toString();
+  }
+
   function onview(data, spec, immediate) {
+    if (!spec) {
+      for (var ii = 0; ii < data.views.length; ii++) {
+        if (data.views[ii].viewKey == data.viewKey) {
+          spec = data.views[ii];
+          break;
+        }
+      }
+    }
+
     data.sequence = (data.sequence || 0) + 1;
     var handler = JX.bind(null, onrender, data, data.sequence);
 
     data.viewKey = spec.viewKey;
 
-    new JX.Request(spec.engineURI, handler)
+    var uri = add_params(spec.engineURI, data);
+
+    new JX.Request(uri, handler)
       .send();
 
     if (data.loadingView) {
@@ -93,7 +178,9 @@
 
       // Replace the URI with the URI for the specific rendering the user
       // has selected.
-      JX.History.replace(spec.viewURI);
+
+      var view_uri = add_params(spec.viewURI, data);
+      JX.History.replace(view_uri);
     }
   }
 
@@ -134,13 +221,7 @@
   if (config && config.renderControlID) {
     var control = JX.$(config.renderControlID);
     var data = JX.Stratcom.getData(control);
-
-    for (var ii = 0; ii < data.views.length; ii++) {
-      if (data.views[ii].viewKey == data.viewKey) {
-        onview(data, data.views[ii], true);
-        break;
-      }
-    }
+    onview(data, null, true);
   }
 
 });
diff --git a/webroot/rsrc/js/phuix/PHUIXActionView.js b/webroot/rsrc/js/phuix/PHUIXActionView.js
--- a/webroot/rsrc/js/phuix/PHUIXActionView.js
+++ b/webroot/rsrc/js/phuix/PHUIXActionView.js
@@ -34,6 +34,10 @@
       return this;
     },
 
+    getDisabled: function() {
+      return this._disabled;
+    },
+
     setLabel: function(label) {
       this._label = label;
       JX.DOM.alterClass(