// This method takes an attributed string and outputs a GlyphLayout data
    // structure that contains the glyph number and location of each inidividual glyph
    void getGlyphLayout (const AttributedString& text, GlyphLayout& glyphLayout)
    {
        // For now we are creating the DirectWrite Factory, System Font Collection,
        // D2D Factory and GDI Render target every time we layout text.
        // This is inefficient and we may be loading and unloading libraries each layout.
        // These four things should be created once at application startup and be destroyed
        // when the application exits. I'm not sure where the best place to do this so
        // for now I will just use the inefficient method.

        IDWriteFactory* dwFactory = nullptr;
        HRESULT hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&dwFactory));

        IDWriteFontCollection* dwFontCollection = nullptr;
        hr = dwFactory->GetSystemFontCollection (&dwFontCollection);

        // To add color to text, we need to create a D2D render target
        // Since we are not actually rendering to a D2D context we create a temporary GDI render target
        ID2D1Factory *d2dFactory = nullptr;
        hr = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2dFactory);
        D2D1_RENDER_TARGET_PROPERTIES d2dRTProp = D2D1::RenderTargetProperties(
            D2D1_RENDER_TARGET_TYPE_SOFTWARE,
            D2D1::PixelFormat(
            DXGI_FORMAT_B8G8R8A8_UNORM,
            D2D1_ALPHA_MODE_IGNORE),
            0,
            0,
            D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE,
            D2D1_FEATURE_LEVEL_DEFAULT
            );
        ID2D1DCRenderTarget* d2dDCRT = nullptr;
        hr = d2dFactory->CreateDCRenderTarget (&d2dRTProp, &d2dDCRT);

        // Initially we set the paragraph up with a default font and then apply the attributed string ranges later
        Font defaultFont;
        const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *dwFontCollection);
        // We should probably be detecting the locale instead of hard coding it to en-us
        String localeName("en-us");

        // We multiply the font height by the size factor so we layout text at the correct size
        IDWriteTextFormat* dwTextFormat = nullptr;
        hr = dwFactory->CreateTextFormat (
            defaultFont.getTypefaceName().toWideCharPointer(),
            dwFontCollection,
            DWRITE_FONT_WEIGHT_REGULAR,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            defaultFont.getHeight() * defaultFontHeightToEmSizeFactor,
            localeName.toWideCharPointer(),
            &dwTextFormat
            );

        // Paragraph Attributes
        // Set Paragraph Alignment
        if (text.getTextAlignment() == AttributedString::left)
            dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING);
        if (text.getTextAlignment() == AttributedString::right)
            dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_TRAILING);
        if (text.getTextAlignment() == AttributedString::center)
            dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_CENTER);
        // DirectWrite cannot justify text, default to left alignment
        if (text.getTextAlignment() == AttributedString::justified)
            dwTextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING);
        // Set Word Wrap
        if (text.getWordWrap() == AttributedString::none)
            dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_NO_WRAP);
        if (text.getWordWrap() == AttributedString::byWord)
            dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP);
        // DirectWrite does not support wrapping by character, default to wrapping by word
        if (text.getWordWrap() == AttributedString::byChar)
            dwTextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP);
        // DirectWrite does not automatically set reading direction
        // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic)
        if (text.getReadingDirection() == AttributedString::rightToLeft)
            dwTextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);

        IDWriteTextLayout* dwTextLayout = nullptr;
        hr = dwFactory->CreateTextLayout (
            text.getText().toWideCharPointer(),
            text.getText().length(),
            dwTextFormat,
            glyphLayout.getWidth(),
            glyphLayout.getHeight(),
            &dwTextLayout
            );

        // Character Attributes
        int numCharacterAttributes = text.getCharAttributesSize();
        for (int i = 0; i < numCharacterAttributes; ++i)
        {
            Attr* attr = text.getCharAttribute (i);
            // Character Range Error Checking
            if (attr->range.getStart() > text.getText().length()) continue;
            if (attr->range.getEnd() > text.getText().length()) attr->range.setEnd (text.getText().length());
            if (attr->attribute == Attr::font)
            {
                AttrFont* attrFont = static_cast<AttrFont*>(attr);
                DWRITE_TEXT_RANGE dwRange;
                dwRange.startPosition = attrFont->range.getStart();
                dwRange.length = attrFont->range.getLength();
                dwTextLayout->SetFontFamilyName (attrFont->font.getTypefaceName().toWideCharPointer(), dwRange);
                // We multiply the font height by the size factor so we layout text at the correct size
                const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (attrFont->font, *dwFontCollection);
                dwTextLayout->SetFontSize (attrFont->font.getHeight() * fontHeightToEmSizeFactor, dwRange);
            }
            if (attr->attribute == Attr::foregroundColour)
            {
                AttrColour* attrColour = static_cast<AttrColour*>(attr);
                DWRITE_TEXT_RANGE dwRange;
                dwRange.startPosition = attrColour->range.getStart();
                dwRange.length = attrColour->range.getLength();
                ID2D1SolidColorBrush* d2dBrush = nullptr;
                d2dDCRT->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF(attrColour->colour.getFloatRed(),
                    attrColour->colour.getFloatGreen(), attrColour->colour.getFloatBlue(),
                    attrColour->colour.getFloatAlpha())), &d2dBrush);
                // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours
                dwTextLayout->SetDrawingEffect (d2dBrush, dwRange);
                safeRelease (&d2dBrush);
            }
        }

        UINT32 actualLineCount = 0;
        hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount);
        // Preallocate GlyphLayout Line Array
        glyphLayout.setNumLines (actualLineCount);
        HeapBlock <DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount);
        hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount);
        int location = 0;
        // Create GlyphLine structures for each line in the layout
        for (UINT32 i = 0; i < actualLineCount; ++i)
        {
            // Get string range
            Range<int> lineStringRange (location, (int) location + dwLineMetrics[i].length);
            location = dwLineMetrics[i].length;
            GlyphLine* glyphLine = new GlyphLine();
            glyphLine->setStringRange (lineStringRange);
            glyphLayout.addGlyphLine (glyphLine);
        }

        // To copy glyph data from DirectWrite into our own data structures we must create our
        // own CustomTextRenderer. Instead of passing the draw method an actual graphics context,
        // we pass it the GlyphLayout object that needs to be filled with glyphs.
        CustomDirectWriteTextRenderer* textRenderer = nullptr;
        textRenderer = new CustomDirectWriteTextRenderer();
        hr = dwTextLayout->Draw (
            &glyphLayout,
            textRenderer,
            glyphLayout.getX(),
            glyphLayout.getY()
            );

        safeRelease (&textRenderer);
        safeRelease (&dwTextLayout);
        safeRelease (&dwTextFormat);
        safeRelease (&d2dDCRT);
        safeRelease (&d2dFactory);
        safeRelease (&dwFontCollection);
        safeRelease (&dwFactory);
    }
    void setTextFormatProperties (const AttributedString& text, IDWriteTextFormat& format)
    {
        DWRITE_TEXT_ALIGNMENT alignment = DWRITE_TEXT_ALIGNMENT_LEADING;
        DWRITE_WORD_WRAPPING wrapType = DWRITE_WORD_WRAPPING_WRAP;

        switch (text.getJustification().getOnlyHorizontalFlags())
        {
            case Justification::left:                   break;
            case Justification::right:                  alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break;
            case Justification::horizontallyCentred:    alignment = DWRITE_TEXT_ALIGNMENT_CENTER; break;
            case Justification::horizontallyJustified:  break; // DirectWrite cannot justify text, default to left alignment
            default:                                    jassertfalse; break; // Illegal justification flags
        }

        switch (text.getWordWrap())
        {
            case AttributedString::none:      wrapType = DWRITE_WORD_WRAPPING_NO_WRAP; break;
            case AttributedString::byWord:    break;
            case AttributedString::byChar:    break; // DirectWrite doesn't support wrapping by character, default to word-wrap
            default:                          jassertfalse; break; // Illegal flags!
        }

        // DirectWrite does not automatically set reading direction
        // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic)
        if (text.getReadingDirection() == AttributedString::rightToLeft)
        {
            format.SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);

            switch (text.getJustification().getOnlyHorizontalFlags())
            {
                case Justification::left:      alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break;
                case Justification::right:     alignment = DWRITE_TEXT_ALIGNMENT_LEADING;  break;
                default: break;
            }
        }

        format.SetTextAlignment (alignment);
        format.SetWordWrapping (wrapType);
    }