Imported Upstream version 1.3.0
[platform/upstream/harfbuzz.git] / src / hb-directwrite.cc
index 96d1870..09889d0 100644 (file)
 #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"
 
 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++)
+  virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize)
   {
-    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++)
-  {
-    *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;
 
+  // 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, NULL), 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, NULL, __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,90 +232,27 @@ _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;
 
   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 data;
 }
 
 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
@@ -327,7 +273,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
@@ -444,7 +390,8 @@ public:
 
   IFACEMETHODIMP GetLocaleName(uint32_t textPosition,
     uint32_t* textLength,
-    wchar_t const** localeName) {
+    wchar_t const** localeName)
+  {
     return S_OK;
   }
 
@@ -469,7 +416,8 @@ public:
   {
     SetCurrentRun(textPosition);
     SplitCurrentRun(textPosition);
-    while (textLength > 0) {
+    while (textLength > 0)
+    {
       Run *run = FetchNextRun(&textLength);
       run->mScript = *scriptAnalysis;
     }
@@ -502,10 +450,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 +472,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 +490,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;
@@ -600,32 +554,11 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
   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);
@@ -672,7 +605,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 ? 
@@ -688,6 +620,7 @@ _hb_directwrite_shape(hb_shape_plan_t    *shape_plan,
 
   TextAnalysis analysis(textString, textLength, NULL, readingDirection);
   TextAnalysis::Run *runHead;
+  HRESULT hr;
   hr = analysis.GenerateResults(analyzer, &runHead);
 
 #define FAIL(...) \
@@ -731,11 +664,11 @@ _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));
 
@@ -746,9 +679,7 @@ retry_getglyphs:
 
   if (unlikely (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER)))
   {
-    free (clusterMap);
     free (glyphIndices);
-    free (textProperties);
     free (glyphProperties);
 
     maxGlyphCount *= 2;
@@ -799,106 +730,109 @@ retry_getglyphs:
     return false;
   }
 
-#ifdef HB_DIRECTWRITE_EXPERIMENTAL_JUSTIFICATION
+  // TODO: get lineWith from somewhere
+  float lineWidth = 0;
 
-  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! */