Imported Upstream version 1.7.6
[platform/upstream/harfbuzz.git] / src / hb-directwrite.cc
index 96d1870..69a8aa2 100644 (file)
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
+#include "hb-private.hh"
+#include "hb-debug.hh"
 #define HB_SHAPER directwrite
 #include "hb-shaper-impl-private.hh"
 
-#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
-  #include <DWrite.h>
-#else
-  #include <DWrite_1.h>
-#endif
+#include <DWrite_1.h>
 
 #include "hb-directwrite.h"
 
-#include "hb-open-file-private.hh"
-#include "hb-ot-name-table.hh"
-#include "hb-ot-tag.h"
 
+HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, face)
+HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, font)
 
-#ifndef HB_DEBUG_DIRECTWRITE
-#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
-#endif
-
-HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, face)
-HB_SHAPER_DATA_ENSURE_DECLARE(directwrite, font)
 
 /*
-* shaper face data
-*/
-
-struct hb_directwrite_shaper_face_data_t {
-  HANDLE fh;
-  wchar_t face_name[LF_FACESIZE];
-};
+ * DirectWrite font stream helpers
+ */
 
-/* face_name should point to a wchar_t[LF_FACESIZE] object. */
-static void
-_hb_generate_unique_face_name(wchar_t *face_name, unsigned int *plen)
+// This is a font loader which provides only one font (unlike its original design).
+// For a better implementation which was also source of this
+// and DWriteFontFileStream, have a look at to NativeFontResourceDWrite.cpp in Mozilla
+class DWriteFontFileLoader : public IDWriteFontFileLoader
 {
-  /* We'll create a private name for the font from a UUID using a simple,
-  * somewhat base64-like encoding scheme */
-  const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
-  UUID id;
-  UuidCreate ((UUID*)&id);
-  ASSERT_STATIC (2 + 3 * (16 / 2) < LF_FACESIZE);
-  unsigned int name_str_len = 0;
-  face_name[name_str_len++] = 'F';
-  face_name[name_str_len++] = '_';
-  unsigned char *p = (unsigned char *)&id;
-  for (unsigned int i = 0; i < 16; i += 2)
-  {
-    /* Spread the 16 bits from two bytes of the UUID across three chars of face_name,
-    * using the bits in groups of 5,5,6 to select chars from enc.
-    * This will generate 24 characters; with the 'F_' prefix we already provided,
-    * the name will be 26 chars (plus the NUL terminator), so will always fit within
-    * face_name (LF_FACESIZE = 32). */
-    face_name[name_str_len++] = enc[p[i] >> 3];
-    face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f];
-    face_name[name_str_len++] = enc[p[i + 1] & 0x3f];
-  }
-  face_name[name_str_len] = 0;
-  if (plen)
-    *plen = name_str_len;
-}
+private:
+  IDWriteFontFileStream *mFontFileStream;
+public:
+  DWriteFontFileLoader (IDWriteFontFileStream *fontFileStream) {
+    mFontFileStream = fontFileStream;
+  }
 
-/* Destroys blob. */
-static hb_blob_t *
-_hb_rename_font(hb_blob_t *blob, wchar_t *new_name)
-{
-  /* Create a copy of the font data, with the 'name' table replaced by a
-   * table that names the font with our private F_* name created above.
-   * For simplicity, we just append a new 'name' table and update the
-   * sfnt directory; the original table is left in place, but unused.
-   *
-   * The new table will contain just 5 name IDs: family, style, unique,
-   * full, PS. All of them point to the same name data with our unique name.
-   */
+  // IUnknown interface
+  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
+  IFACEMETHOD_(ULONG, AddRef)() { return 1; }
+  IFACEMETHOD_(ULONG, Release)() { return 1; }
 
-  blob = OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (blob);
+  // IDWriteFontFileLoader methods
+  virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const* fontFileReferenceKey,
+    UINT32 fontFileReferenceKeySize,
+    OUT IDWriteFontFileStream** fontFileStream)
+  {
+    *fontFileStream = mFontFileStream;
+    return S_OK;
+  }
+};
 
-  unsigned int length, new_length, name_str_len;
-  const char *orig_sfnt_data = hb_blob_get_data (blob, &length);
+class DWriteFontFileStream : public IDWriteFontFileStream
+{
+private:
+  uint8_t *mData;
+  uint32_t mSize;
+public:
+  DWriteFontFileStream(uint8_t *aData, uint32_t aSize)
+  {
+    mData = aData;
+    mSize = aSize;
+  }
 
-  _hb_generate_unique_face_name (new_name, &name_str_len);
+  // IUnknown interface
+  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { return S_OK; }
+  IFACEMETHOD_(ULONG, AddRef)() { return 1; }
+  IFACEMETHOD_(ULONG, Release)() { return 1; }
 
-  static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 };
+  // IDWriteFontFileStream methods
+  virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
+    UINT64 fileOffset,
+    UINT64 fragmentSize,
+    OUT void** fragmentContext)
+  {
+    // We are required to do bounds checking.
+    if (fileOffset + fragmentSize > mSize) {
+      return E_FAIL;
+    }
 
-  unsigned int name_table_length = OT::name::min_size +
-    ARRAY_LENGTH(name_IDs) * OT::NameRecord::static_size +
-    name_str_len * 2; /* for name data in UTF16BE form */
-  unsigned int name_table_offset = (length + 3) & ~3;
+    // truncate the 64 bit fileOffset to size_t sized index into mData
+    size_t index = static_cast<size_t> (fileOffset);
 
-  new_length = name_table_offset + ((name_table_length + 3) & ~3);
-  void *new_sfnt_data = calloc(1, new_length);
-  if (!new_sfnt_data)
-  {
-    hb_blob_destroy (blob);
-    return NULL;
+    // We should be alive for the duration of this.
+    *fragmentStart = &mData[index];
+    *fragmentContext = nullptr;
+    return S_OK;
   }
 
-  memcpy(new_sfnt_data, orig_sfnt_data, length);
+  virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext) { }
 
-  OT::name &name = OT::StructAtOffset<OT::name> (new_sfnt_data, name_table_offset);
-  name.format.set (0);
-  name.count.set (ARRAY_LENGTH (name_IDs));
-  name.stringOffset.set (name.get_size());
-  for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++)
-  {
-    OT::NameRecord &record = name.nameRecord[i];
-    record.platformID.set(3);
-    record.encodingID.set(1);
-    record.languageID.set(0x0409u); /* English */
-    record.nameID.set(name_IDs[i]);
-    record.length.set(name_str_len * 2);
-    record.offset.set(0);
-  }
-
-  /* Copy string data from new_name, converting wchar_t to UTF16BE. */
-  unsigned char *p = &OT::StructAfter<unsigned char>(name);
-  for (unsigned int i = 0; i < name_str_len; i++)
+  virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize)
   {
-    *p++ = new_name[i] >> 8;
-    *p++ = new_name[i] & 0xff;
+    *fileSize = mSize;
+    return S_OK;
   }
 
-  /* Adjust name table entry to point to new name table */
-  const OT::OpenTypeFontFile &file = *(OT::OpenTypeFontFile *) (new_sfnt_data);
-  unsigned int face_count = file.get_face_count ();
-  for (unsigned int face_index = 0; face_index < face_count; face_index++)
+  virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime)
   {
-    /* Note: doing multiple edits (ie. TTC) can be unsafe.  There may be
-    * toe-stepping.  But we don't really care. */
-    const OT::OpenTypeFontFace &face = file.get_face (face_index);
-    unsigned int index;
-    if (face.find_table_index (HB_OT_TAG_name, &index))
-    {
-      OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index));
-      record.checkSum.set_for_data (&name, name_table_length);
-      record.offset.set (name_table_offset);
-      record.length.set (name_table_length);
-    }
-    else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */
-    {
-      free (new_sfnt_data);
-      hb_blob_destroy (blob);
-      return NULL;
-    }
+    return E_NOTIMPL;
   }
+};
 
-  /* The checkSumAdjustment field in the 'head' table is now wrong,
-  * but that doesn't actually seem to cause any problems so we don't
-  * bother. */
 
-  hb_blob_destroy (blob);
-  return hb_blob_create ((const char *)new_sfnt_data, new_length,
-    HB_MEMORY_MODE_WRITABLE, NULL, free);
-}
+/*
+* shaper face data
+*/
+
+struct hb_directwrite_shaper_face_data_t {
+  IDWriteFactory *dwriteFactory;
+  IDWriteFontFile *fontFile;
+  IDWriteFontFileStream *fontFileStream;
+  IDWriteFontFileLoader *fontFileLoader;
+  IDWriteFontFace *fontFace;
+  hb_blob_t *faceBlob;
+};
 
 hb_directwrite_shaper_face_data_t *
 _hb_directwrite_shaper_face_data_create(hb_face_t *face)
 {
   hb_directwrite_shaper_face_data_t *data =
-    (hb_directwrite_shaper_face_data_t *) calloc (1, sizeof (hb_directwrite_shaper_face_data_t));
+    (hb_directwrite_shaper_face_data_t *) malloc (sizeof (hb_directwrite_shaper_face_data_t));
   if (unlikely (!data))
-    return NULL;
+    return nullptr;
+
+  // TODO: factory and fontFileLoader should be cached separately
+  IDWriteFactory* dwriteFactory;
+  DWriteCreateFactory (
+    DWRITE_FACTORY_TYPE_SHARED,
+    __uuidof (IDWriteFactory),
+    (IUnknown**) &dwriteFactory
+  );
 
+  HRESULT hr;
   hb_blob_t *blob = hb_face_reference_blob (face);
-  if (unlikely (!hb_blob_get_length (blob)))
-    DEBUG_MSG(DIRECTWRITE, face, "Face has empty blob");
+  IDWriteFontFileStream *fontFileStream = new DWriteFontFileStream (
+    (uint8_t*) hb_blob_get_data (blob, nullptr), hb_blob_get_length (blob));
 
-  blob = _hb_rename_font (blob, data->face_name);
-  if (unlikely (!blob))
-  {
-    free(data);
-    return NULL;
+  IDWriteFontFileLoader *fontFileLoader = new DWriteFontFileLoader (fontFileStream);
+  dwriteFactory->RegisterFontFileLoader (fontFileLoader);
+
+  IDWriteFontFile *fontFile;
+  uint64_t fontFileKey = 0;
+  hr = dwriteFactory->CreateCustomFontFileReference (&fontFileKey, sizeof (fontFileKey),
+      fontFileLoader, &fontFile);
+
+#define FAIL(...) \
+  HB_STMT_START { \
+    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
+    return false; \
+  } HB_STMT_END;
+
+  if (FAILED (hr)) {
+    FAIL ("Failed to load font file from data!");
+    return false;
   }
 
-  DWORD num_fonts_installed;
-  data->fh = AddFontMemResourceEx ((void *)hb_blob_get_data(blob, NULL),
-    hb_blob_get_length (blob),
-    0, &num_fonts_installed);
-  if (unlikely (!data->fh))
-  {
-    DEBUG_MSG (DIRECTWRITE, face, "Face AddFontMemResourceEx() failed");
-    free (data);
-    return NULL;
+  BOOL isSupported;
+  DWRITE_FONT_FILE_TYPE fileType;
+  DWRITE_FONT_FACE_TYPE faceType;
+  UINT32 numberOfFaces;
+  hr = fontFile->Analyze (&isSupported, &fileType, &faceType, &numberOfFaces);
+  if (FAILED (hr) || !isSupported) {
+    FAIL ("Font file is not supported.");
+    return false;
   }
 
+#undef FAIL
+
+  IDWriteFontFace *fontFace;
+  dwriteFactory->CreateFontFace (faceType, 1, &fontFile, 0,
+    DWRITE_FONT_SIMULATIONS_NONE, &fontFace);
+
+  data->dwriteFactory = dwriteFactory;
+  data->fontFile = fontFile;
+  data->fontFileStream = fontFileStream;
+  data->fontFileLoader = fontFileLoader;
+  data->fontFace = fontFace;
+  data->faceBlob = blob;
+
   return data;
 }
 
 void
 _hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t *data)
 {
-  RemoveFontMemResourceEx(data->fh);
-  free(data);
+  if (data->fontFace)
+    data->fontFace->Release ();
+  if (data->fontFile)
+    data->fontFile->Release ();
+  if (data->dwriteFactory) {
+    if (data->fontFileLoader)
+      data->dwriteFactory->UnregisterFontFileLoader (data->fontFileLoader);
+    data->dwriteFactory->Release ();
+  }
+  if (data->fontFileLoader)
+    delete data->fontFileLoader;
+  if (data->fontFileStream)
+    delete data->fontFileStream;
+  if (data->faceBlob)
+    hb_blob_destroy (data->faceBlob);
+  if (data)
+    free (data);
 }
 
 
@@ -223,60 +226,17 @@ _hb_directwrite_shaper_face_data_destroy(hb_directwrite_shaper_face_data_t *data
  */
 
 struct hb_directwrite_shaper_font_data_t {
-  HDC hdc;
-  LOGFONTW log_font;
-  HFONT hfont;
 };
 
-static bool
-populate_log_font (LOGFONTW  *lf,
-       hb_font_t *font)
-{
-  memset (lf, 0, sizeof (*lf));
-  lf->lfHeight = -font->y_scale;
-  lf->lfCharSet = DEFAULT_CHARSET;
-
-  hb_face_t *face = font->face;
-  hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
-
-  memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName));
-
-  return true;
-}
-
 hb_directwrite_shaper_font_data_t *
 _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 {
-  if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return NULL;
+  if (unlikely (!hb_directwrite_shaper_face_data_ensure (font->face))) return nullptr;
 
   hb_directwrite_shaper_font_data_t *data =
-    (hb_directwrite_shaper_font_data_t *) calloc (1, sizeof (hb_directwrite_shaper_font_data_t));
+    (hb_directwrite_shaper_font_data_t *) malloc (sizeof (hb_directwrite_shaper_font_data_t));
   if (unlikely (!data))
-    return NULL;
-
-  data->hdc = GetDC (NULL);
-
-  if (unlikely (!populate_log_font (&data->log_font, font)))
-  {
-    DEBUG_MSG (DIRECTWRITE, font, "Font populate_log_font() failed");
-    _hb_directwrite_shaper_font_data_destroy (data);
-    return NULL;
-  }
-
-  data->hfont = CreateFontIndirectW (&data->log_font);
-  if (unlikely (!data->hfont))
-  {
-    DEBUG_MSG (DIRECTWRITE, font, "Font CreateFontIndirectW() failed");
-    _hb_directwrite_shaper_font_data_destroy (data);
-     return NULL;
-  }
-
-  if (!SelectObject (data->hdc, data->hfont))
-  {
-    DEBUG_MSG (DIRECTWRITE, font, "Font SelectObject() failed");
-    _hb_directwrite_shaper_font_data_destroy (data);
-     return NULL;
-  }
+    return nullptr;
 
   return data;
 }
@@ -284,29 +244,9 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font)
 void
 _hb_directwrite_shaper_font_data_destroy (hb_directwrite_shaper_font_data_t *data)
 {
-  if (data->hdc)
-    ReleaseDC (NULL, data->hdc);
-  if (data->hfont)
-    DeleteObject (data->hfont);
   free (data);
 }
 
-LOGFONTW *
-hb_directwrite_font_get_logfontw (hb_font_t *font)
-{
-  if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL;
-  hb_directwrite_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return &font_data->log_font;
-}
-
-HFONT
-hb_directwrite_font_get_hfont (hb_font_t *font)
-{
-  if (unlikely (!hb_directwrite_shaper_font_data_ensure (font))) return NULL;
-  hb_directwrite_shaper_font_data_t *font_data =  HB_SHAPER_DATA_GET (font);
-  return font_data->hfont;
-}
-
 
 /*
  * shaper shape_plan data
@@ -316,8 +256,10 @@ struct hb_directwrite_shaper_shape_plan_data_t {};
 
 hb_directwrite_shaper_shape_plan_data_t *
 _hb_directwrite_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan HB_UNUSED,
-               const hb_feature_t *user_features HB_UNUSED,
-               unsigned int        num_user_features HB_UNUSED)
+                                              const hb_feature_t *user_features HB_UNUSED,
+                                              unsigned int        num_user_features HB_UNUSED,
+                                              const int          *coords HB_UNUSED,
+                                              unsigned int        num_coords HB_UNUSED)
 {
   return (hb_directwrite_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
 }
@@ -327,7 +269,7 @@ _hb_directwrite_shaper_shape_plan_data_destroy (hb_directwrite_shaper_shape_plan
 {
 }
 
-// Most of here TextAnalysis is originally written by Bas Schouten for Mozilla project
+// Most of TextAnalysis is originally written by Bas Schouten for Mozilla project
 // but now is relicensed to MIT for HarfBuzz use
 class TextAnalysis
   : public IDWriteTextAnalysisSource, public IDWriteTextAnalysisSink
@@ -369,7 +311,7 @@ public:
     , mTextLength(textLength)
     , mLocaleName(localeName)
     , mReadingDirection(readingDirection)
-    , mCurrentRun(NULL) { };
+    , mCurrentRun(nullptr) { };
 
   ~TextAnalysis() {
     // delete runs, except mRunHead which is part of the TextAnalysis object
@@ -393,7 +335,7 @@ public:
     mRunHead.mTextLength = mTextLength;
     mRunHead.mBidiLevel =
       (mReadingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
-    mRunHead.nextRun = NULL;
+    mRunHead.nextRun = nullptr;
     mCurrentRun = &mRunHead;
 
     // Call each of the analyzers in sequence, recording their results.
@@ -412,7 +354,7 @@ public:
   {
     if (textPosition >= mTextLength) {
       // No text at this position, valid query though.
-      *textString = NULL;
+      *textString = nullptr;
       *textLength = 0;
     }
     else {
@@ -428,8 +370,8 @@ public:
   {
     if (textPosition == 0 || textPosition > mTextLength) {
       // Either there is no text before here (== 0), or this
-      // is an invalid position. The query is considered valid thouh.
-      *textString = NULL;
+      // is an invalid position. The query is considered valid though.
+      *textString = nullptr;
       *textLength = 0;
     }
     else {
@@ -444,7 +386,8 @@ public:
 
   IFACEMETHODIMP GetLocaleName(uint32_t textPosition,
     uint32_t* textLength,
-    wchar_t const** localeName) {
+    wchar_t const** localeName)
+  {
     return S_OK;
   }
 
@@ -454,7 +397,7 @@ public:
     OUT IDWriteNumberSubstitution** numberSubstitution)
   {
     // We do not support number substitution.
-    *numberSubstitution = NULL;
+    *numberSubstitution = nullptr;
     *textLength = mTextLength - textPosition;
 
     return S_OK;
@@ -469,7 +412,8 @@ public:
   {
     SetCurrentRun(textPosition);
     SplitCurrentRun(textPosition);
-    while (textLength > 0) {
+    while (textLength > 0)
+    {
       Run *run = FetchNextRun(&textLength);
       run->mScript = *scriptAnalysis;
     }
@@ -502,10 +446,12 @@ protected:
     Run *origRun = mCurrentRun;
     // Split the tail if needed (the length remaining is less than the
     // current run's size).
-    if (*textLength < mCurrentRun->mTextLength) {
-      SplitCurrentRun(mCurrentRun->mTextStart + *textLength);
+    if (*textLength < mCurrentRun->mTextLength)
+    {
+      SplitCurrentRun (mCurrentRun->mTextStart + *textLength);
     }
-    else {
+    else
+    {
       // Just advance the current run.
       mCurrentRun = mCurrentRun->nextRun;
     }
@@ -522,12 +468,14 @@ protected:
     // this will usually just return early. If not, find the
     // corresponding run for the text position.
 
-    if (mCurrentRun && mCurrentRun->ContainsTextPosition(textPosition)) {
+    if (mCurrentRun && mCurrentRun->ContainsTextPosition (textPosition))
+    {
       return;
     }
 
     for (Run *run = &mRunHead; run; run = run->nextRun) {
-      if (run->ContainsTextPosition(textPosition)) {
+      if (run->ContainsTextPosition (textPosition))
+      {
         mCurrentRun = run;
         return;
       }
@@ -538,13 +486,15 @@ protected:
 
   void SplitCurrentRun(uint32_t splitPosition)
   {
-    if (!mCurrentRun) {
+    if (!mCurrentRun)
+    {
       //NS_ASSERTION(false, "SplitCurrentRun called without current run.");
       // Shouldn't be calling this when no current run is set!
       return;
     }
     // Split the current run.
-    if (splitPosition <= mCurrentRun->mTextStart) {
+    if (splitPosition <= mCurrentRun->mTextStart)
+    {
       // No need to split, already the start of a run
       // or before it. Usually the first.
       return;
@@ -590,42 +540,22 @@ static inline uint32_t hb_uint32_swap (const uint32_t v)
  * shaper
  */
 
-hb_bool_t
-_hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
+static hb_bool_t
+_hb_directwrite_shape_full(hb_shape_plan_t    *shape_plan,
   hb_font_t          *font,
   hb_buffer_t        *buffer,
   const hb_feature_t *features,
-  unsigned int        num_features)
+  unsigned int        num_features,
+  float               lineWidth)
 {
   hb_face_t *face = font->face;
   hb_directwrite_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
   hb_directwrite_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
+  IDWriteFactory *dwriteFactory = face_data->dwriteFactory;
+  IDWriteFontFace *fontFace = face_data->fontFace;
 
-  // factory probably should be cached
-#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
-  IDWriteFactory* dwriteFactory;
-#else
-  IDWriteFactory1* dwriteFactory;
-#endif
-  DWriteCreateFactory (
-    DWRITE_FACTORY_TYPE_SHARED,
-    __uuidof (IDWriteFactory),
-    (IUnknown**) &dwriteFactory
-  );
-
-  IDWriteGdiInterop *gdiInterop;
-  dwriteFactory->GetGdiInterop (&gdiInterop);
-  IDWriteFontFace* fontFace;
-  gdiInterop->CreateFontFaceFromHdc (font_data->hdc, &fontFace);
-
-#ifndef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
   IDWriteTextAnalyzer* analyzer;
   dwriteFactory->CreateTextAnalyzer(&analyzer);
-#else
-  IDWriteTextAnalyzer* analyzer0;
-  dwriteFactory->CreateTextAnalyzer (&analyzer0);
-  IDWriteTextAnalyzer1* analyzer = (IDWriteTextAnalyzer1*) analyzer0;
-#endif
 
   unsigned int scratch_size;
   hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
@@ -653,7 +583,7 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
       textString[chars_len++] = 0xFFFDu;
     else {
       textString[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10);
-      textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1 << 10) - 1));
+      textString[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1));
     }
   }
 
@@ -672,7 +602,6 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
     }
   }
 
-  HRESULT hr;
   // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
 
   DWRITE_READING_DIRECTION readingDirection = buffer->props.direction ? 
@@ -686,13 +615,14 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
   */
   uint32_t textLength = buffer->len;
 
-  TextAnalysis analysis(textString, textLength, NULL, readingDirection);
+  TextAnalysis analysis(textString, textLength, nullptr, readingDirection);
   TextAnalysis::Run *runHead;
+  HRESULT hr;
   hr = analysis.GenerateResults(analyzer, &runHead);
 
 #define FAIL(...) \
   HB_STMT_START { \
-    DEBUG_MSG (DIRECTWRITE, NULL, __VA_ARGS__); \
+    DEBUG_MSG (DIRECTWRITE, nullptr, __VA_ARGS__); \
     return false; \
   } HB_STMT_END;
 
@@ -707,7 +637,7 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
   bool isRightToLeft = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
   const wchar_t localeName[20] = {0};
-  if (buffer->props.language != NULL)
+  if (buffer->props.language != nullptr)
   {
     mbstowcs ((wchar_t*) localeName,
       hb_language_to_string (buffer->props.language), 20);
@@ -731,24 +661,22 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
     (const DWRITE_TYPOGRAPHIC_FEATURES*) &singleFeatures;
   const uint32_t featureRangeLengths[] = { textLength };
 
+  uint16_t* clusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t));
+  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*)
+    malloc (textLength * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES));
 retry_getglyphs:
-  uint16_t* clusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
   uint16_t* glyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
-  DWRITE_SHAPING_TEXT_PROPERTIES* textProperties = (DWRITE_SHAPING_TEXT_PROPERTIES*)
-    malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_TEXT_PROPERTIES));
   DWRITE_SHAPING_GLYPH_PROPERTIES* glyphProperties = (DWRITE_SHAPING_GLYPH_PROPERTIES*)
     malloc (maxGlyphCount * sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES));
 
-  hr = analyzer->GetGlyphs (textString, textLength, fontFace, FALSE,
-    isRightToLeft, &runHead->mScript, localeName, NULL, &dwFeatures,
+  hr = analyzer->GetGlyphs (textString, textLength, fontFace, false,
+    isRightToLeft, &runHead->mScript, localeName, nullptr, &dwFeatures,
     featureRangeLengths, 1, maxGlyphCount, clusterMap, textProperties, glyphIndices,
     glyphProperties, &glyphCount);
 
   if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
   {
-    free (clusterMap);
     free (glyphIndices);
-    free (textProperties);
     free (glyphProperties);
 
     maxGlyphCount *= 2;
@@ -789,7 +717,7 @@ retry_getglyphs:
   hr = analyzer->GetGlyphPlacements (textString,
     clusterMap, textProperties, textLength, glyphIndices,
     glyphProperties, glyphCount, fontFace, fontEmSize,
-    FALSE, isRightToLeft, &runHead->mScript, localeName,
+    false, isRightToLeft, &runHead->mScript, localeName,
     &dwFeatures, featureRangeLengths, 1,
     glyphAdvances, glyphOffsets);
 
@@ -799,106 +727,106 @@ retry_getglyphs:
     return false;
   }
 
-#ifdef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
-
-  DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
-    (DWRITE_JUSTIFICATION_OPPORTUNITY*)
-    malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY));
-  hr = analyzer->GetJustificationOpportunities (fontFace, fontEmSize,
-    runHead->mScript, textLength, glyphCount, textString, clusterMap,
-    glyphProperties, justificationOpportunities);
+  IDWriteTextAnalyzer1* analyzer1;
+  analyzer->QueryInterface (&analyzer1);
 
-  if (FAILED (hr))
+  if (analyzer1 && lineWidth)
   {
-    FAIL ("Analyzer failed to get justification opportunities.");
-    return false;
-  }
 
-  // TODO: get lineWith from somewhere
-  float lineWidth = 60000;
+    DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
+      (DWRITE_JUSTIFICATION_OPPORTUNITY*)
+      malloc (maxGlyphCount * sizeof (DWRITE_JUSTIFICATION_OPPORTUNITY));
+    hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize,
+      runHead->mScript, textLength, glyphCount, textString, clusterMap,
+      glyphProperties, justificationOpportunities);
 
-  float* justifiedGlyphAdvances =
-    (float*) malloc (maxGlyphCount * sizeof (float));
-  DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
-    malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET));
-  hr = analyzer->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
-    glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets);
+    if (FAILED (hr))
+    {
+      FAIL ("Analyzer failed to get justification opportunities.");
+      return false;
+    }
 
-  if (FAILED (hr))
-  {
-    FAIL ("Analyzer failed to get justified glyph advances.");
-    return false;
-  }
+    float* justifiedGlyphAdvances =
+      (float*) malloc (maxGlyphCount * sizeof (float));
+    DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
+      malloc (glyphCount * sizeof (DWRITE_GLYPH_OFFSET));
+    hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
+      glyphAdvances, glyphOffsets, justifiedGlyphAdvances, justifiedGlyphOffsets);
 
-  DWRITE_SCRIPT_PROPERTIES scriptProperties;
-  hr = analyzer->GetScriptProperties (runHead->mScript, &scriptProperties);
-  if (FAILED (hr))
-  {
-    FAIL ("Analyzer failed to get script properties.");
-    return false;
-  }
-  uint32_t justificationCharacter = scriptProperties.justificationCharacter;
+    if (FAILED (hr))
+    {
+      FAIL("Analyzer failed to get justified glyph advances.");
+      return false;
+    }
 
-  // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
-  if (justificationCharacter != 32)
-  {
-retry_getjustifiedglyphs:
-    uint16_t* modifiedClusterMap = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
-    uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
-    float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float));
-    DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
-      malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET));
-    uint32_t actualGlyphsCount;
-    hr = analyzer->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
+    DWRITE_SCRIPT_PROPERTIES scriptProperties;
+    hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
+    if (FAILED (hr))
+    {
+      FAIL("Analyzer failed to get script properties.");
+      return false;
+    }
+    uint32_t justificationCharacter = scriptProperties.justificationCharacter;
+
+    // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
+    if (justificationCharacter != 32)
+    {
+      uint16_t* modifiedClusterMap = (uint16_t*) malloc (textLength * sizeof (uint16_t));
+    retry_getjustifiedglyphs:
+      uint16_t* modifiedGlyphIndices = (uint16_t*) malloc (maxGlyphCount * sizeof (uint16_t));
+      float* modifiedGlyphAdvances = (float*) malloc (maxGlyphCount * sizeof (float));
+      DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = (DWRITE_GLYPH_OFFSET*)
+        malloc (maxGlyphCount * sizeof (DWRITE_GLYPH_OFFSET));
+      uint32_t actualGlyphsCount;
+      hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
         textLength, glyphCount, maxGlyphCount, clusterMap, glyphIndices,
         glyphAdvances, justifiedGlyphAdvances, justifiedGlyphOffsets,
         glyphProperties, &actualGlyphsCount, modifiedClusterMap, modifiedGlyphIndices,
         modifiedGlyphAdvances, modifiedGlyphOffsets);
 
-    if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
-    {
-      maxGlyphCount = actualGlyphsCount;
-      free (modifiedClusterMap);
-      free (modifiedGlyphIndices);
-      free (modifiedGlyphAdvances);
-      free (modifiedGlyphOffsets);
+      if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
+      {
+        maxGlyphCount = actualGlyphsCount;
+        free (modifiedGlyphIndices);
+        free (modifiedGlyphAdvances);
+        free (modifiedGlyphOffsets);
 
-      maxGlyphCount = actualGlyphsCount;
+        maxGlyphCount = actualGlyphsCount;
 
-      goto retry_getjustifiedglyphs;
-    }
-    if (FAILED (hr))
-    {
-      FAIL ("Analyzer failed to get justified glyphs.");
-      return false;
-    }
+        goto retry_getjustifiedglyphs;
+      }
+      if (FAILED (hr))
+      {
+        FAIL ("Analyzer failed to get justified glyphs.");
+        return false;
+      }
 
-    free (clusterMap);
-    free (glyphIndices);
-    free (glyphAdvances);
-    free (glyphOffsets);
+      free (clusterMap);
+      free (glyphIndices);
+      free (glyphAdvances);
+      free (glyphOffsets);
 
-    glyphCount = actualGlyphsCount;
-    clusterMap = modifiedClusterMap;
-    glyphIndices = modifiedGlyphIndices;
-    glyphAdvances = modifiedGlyphAdvances;
-    glyphOffsets = modifiedGlyphOffsets;
+      glyphCount = actualGlyphsCount;
+      clusterMap = modifiedClusterMap;
+      glyphIndices = modifiedGlyphIndices;
+      glyphAdvances = modifiedGlyphAdvances;
+      glyphOffsets = modifiedGlyphOffsets;
 
-    free(justifiedGlyphAdvances);
-    free(justifiedGlyphOffsets);
-  }
-  else
-  {
-    free(glyphAdvances);
-    free(glyphOffsets);
+      free (justifiedGlyphAdvances);
+      free (justifiedGlyphOffsets);
+    }
+    else
+    {
+      free (glyphAdvances);
+      free (glyphOffsets);
 
-    glyphAdvances = justifiedGlyphAdvances;
-    glyphOffsets = justifiedGlyphOffsets;
-  }
+      glyphAdvances = justifiedGlyphAdvances;
+      glyphOffsets = justifiedGlyphOffsets;
+    }
 
-  free(justificationOpportunities);
+    free (justificationOpportunities);
 
-#endif
+  }
 
   /* Ok, we've got everything we need, now compose output buffer,
    * very, *very*, carefully! */
@@ -968,3 +896,36 @@ retry_getjustifiedglyphs:
   /* Wow, done! */
   return true;
 }
+
+hb_bool_t
+_hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
+  hb_font_t          *font,
+  hb_buffer_t        *buffer,
+  const hb_feature_t *features,
+  unsigned int        num_features)
+{
+  return _hb_directwrite_shape_full(shape_plan, font, buffer,
+    features, num_features, 0);
+}
+
+/*
+ * Public [experimental] API
+ */
+
+hb_bool_t
+hb_directwrite_shape_experimental_width(hb_font_t          *font,
+  hb_buffer_t        *buffer,
+  const hb_feature_t *features,
+  unsigned int        num_features,
+  float               width)
+{
+  static char *shapers = "directwrite";
+  hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face,
+    &buffer->props, features, num_features, &shapers);
+  hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
+    features, num_features, width);
+
+  buffer->unsafe_to_break_all ();
+
+  return res;
+}