mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +00:00
Remove TextLayout::createNativeLayout()
This commit is contained in:
parent
38f299a054
commit
51955453ef
8 changed files with 33 additions and 981 deletions
|
|
@ -2,6 +2,38 @@
|
|||
|
||||
# Version 8.0.0
|
||||
|
||||
## Change
|
||||
|
||||
As part of the Unicode upgrades TextLayout codepaths have been unified across
|
||||
all platforms. As a consequence the behaviour of TextLayout on Apple platforms
|
||||
will now be different in two regards:
|
||||
- With certain fonts, line spacing will now be different.
|
||||
- The AttributedString option WordWrap::byChar will no longer have an effect,
|
||||
just like it didn't have an effect on non-Apple platforms previously. Wrapping
|
||||
will now always happen on word boundaries.
|
||||
|
||||
Furthermore, the JUCE_USE_DIRECTWRITE compiler flag will no longer have any
|
||||
effect.
|
||||
|
||||
**Possible Issues**
|
||||
|
||||
User interfaces using TextLayout and the WordWrap::byChar option will have their
|
||||
appearance altered on Apple platforms. The line spacing will be different for
|
||||
certain fonts.
|
||||
|
||||
**Workaround**
|
||||
|
||||
There is no workaround.
|
||||
|
||||
**Rationale**
|
||||
|
||||
The new, unified codepath has better support for Unicode text in general. The
|
||||
font fallback mechanism, which previously was only available using the removed
|
||||
codepaths is now an integral part of the new approach. By removing the
|
||||
alternative codepaths, text layout and line spacing has become more consistent
|
||||
across the platforms.
|
||||
|
||||
|
||||
## Change
|
||||
|
||||
As part of the Unicode upgrades the vertical alignment logic of TextLayout has
|
||||
|
|
|
|||
|
|
@ -273,8 +273,7 @@ void TextLayout::createLayout (const AttributedString& text, float maxWidth, flo
|
|||
height = maxHeight;
|
||||
justification = text.getJustification();
|
||||
|
||||
if (! createNativeLayout (text))
|
||||
createStandardLayout (text);
|
||||
createStandardLayout (text);
|
||||
|
||||
recalculateSize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -271,7 +271,6 @@ private:
|
|||
Justification justification;
|
||||
|
||||
void createStandardLayout (const AttributedString&);
|
||||
bool createNativeLayout (const AttributedString&);
|
||||
|
||||
JUCE_LEAK_DETECTOR (TextLayout)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,519 +0,0 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE framework.
|
||||
Copyright (c) Raw Material Software Limited
|
||||
|
||||
JUCE is an open source framework subject to commercial or open source
|
||||
licensing.
|
||||
|
||||
By downloading, installing, or using the JUCE framework, or combining the
|
||||
JUCE framework with any other source code, object code, content or any other
|
||||
copyrightable work, you agree to the terms of the JUCE End User Licence
|
||||
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
||||
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
||||
do not agree to the terms of these agreements, we will not license the JUCE
|
||||
framework to you, and you must discontinue the installation or download
|
||||
process and cease use of the JUCE framework.
|
||||
|
||||
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
||||
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
||||
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
||||
|
||||
Or:
|
||||
|
||||
You may also use this code under the terms of the AGPLv3:
|
||||
https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
|
||||
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
||||
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_USE_DIRECTWRITE
|
||||
namespace DirectWriteTypeLayout
|
||||
{
|
||||
class CustomDirectWriteTextRenderer final : public ComBaseClassHelper<IDWriteTextRenderer>
|
||||
{
|
||||
public:
|
||||
CustomDirectWriteTextRenderer (IDWriteFontCollection& fonts, const AttributedString& as)
|
||||
: attributedString (as),
|
||||
fontCollection (fonts)
|
||||
{
|
||||
}
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (refId == __uuidof (IDWritePixelSnapping))
|
||||
return castToType<IDWritePixelSnapping> (result);
|
||||
|
||||
return ComBaseClassHelper<IDWriteTextRenderer>::QueryInterface (refId, result);
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
JUCE_COMRESULT IsPixelSnappingDisabled (void* /*clientDrawingContext*/, BOOL* isDisabled) noexcept override
|
||||
{
|
||||
*isDisabled = FALSE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX* matrix) noexcept override
|
||||
{
|
||||
matrix->m11 = 1.0f; matrix->m12 = 0.0f;
|
||||
matrix->m21 = 0.0f; matrix->m22 = 1.0f;
|
||||
matrix->dx = 0.0f; matrix->dy = 0.0f;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetPixelsPerDip (void*, FLOAT* pixelsPerDip) noexcept override
|
||||
{
|
||||
*pixelsPerDip = 1.0f;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT DrawUnderline (void*, FLOAT, FLOAT, DWRITE_UNDERLINE const*, IUnknown*) noexcept override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT DrawStrikethrough (void*, FLOAT, FLOAT, DWRITE_STRIKETHROUGH const*, IUnknown*) noexcept override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT DrawInlineObject (void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*) noexcept override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT DrawGlyphRun (void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE,
|
||||
DWRITE_GLYPH_RUN const* glyphRun, DWRITE_GLYPH_RUN_DESCRIPTION const* runDescription,
|
||||
IUnknown* clientDrawingEffect) noexcept override
|
||||
{
|
||||
const auto containsTextOrNewLines = [runDescription]
|
||||
{
|
||||
const String runString (runDescription->string, runDescription->stringLength);
|
||||
|
||||
if (runString.containsNonWhitespaceChars() || runString.containsAnyOf ("\n\r"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (! containsTextOrNewLines)
|
||||
return S_OK;
|
||||
|
||||
auto layout = static_cast<TextLayout*> (clientDrawingContext);
|
||||
|
||||
if (! (baselineOriginY >= -1.0e10f && baselineOriginY <= 1.0e10f))
|
||||
baselineOriginY = 0; // DirectWrite sometimes sends NaNs in this parameter
|
||||
|
||||
if (! approximatelyEqual (baselineOriginY, lastOriginY))
|
||||
{
|
||||
lastOriginY = baselineOriginY;
|
||||
++currentLine;
|
||||
|
||||
if (currentLine >= layout->getNumLines())
|
||||
{
|
||||
jassert (currentLine == layout->getNumLines());
|
||||
auto line = std::make_unique<TextLayout::Line>();
|
||||
line->lineOrigin = Point<float> (baselineOriginX, baselineOriginY);
|
||||
|
||||
layout->addLine (std::move (line));
|
||||
}
|
||||
}
|
||||
|
||||
auto& glyphLine = layout->getLine (currentLine);
|
||||
|
||||
DWRITE_FONT_METRICS dwFontMetrics;
|
||||
glyphRun->fontFace->GetMetrics (&dwFontMetrics);
|
||||
|
||||
glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, *glyphRun));
|
||||
glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, *glyphRun));
|
||||
|
||||
auto glyphRunLayout = new TextLayout::Run (Range<int> ((int) runDescription->textPosition,
|
||||
(int) (runDescription->textPosition + runDescription->stringLength)),
|
||||
(int) glyphRun->glyphCount);
|
||||
glyphLine.runs.add (glyphRunLayout);
|
||||
|
||||
glyphRun->fontFace->GetMetrics (&dwFontMetrics);
|
||||
auto totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent);
|
||||
auto fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight;
|
||||
|
||||
glyphRunLayout->font = getFontForRun (*glyphRun, glyphRun->fontEmSize / fontHeightToEmSizeFactor);
|
||||
glyphRunLayout->colour = getColourOf (static_cast<ID2D1SolidColorBrush*> (clientDrawingEffect));
|
||||
|
||||
auto lineOrigin = layout->getLine (currentLine).lineOrigin;
|
||||
auto x = baselineOriginX - lineOrigin.x;
|
||||
|
||||
auto extraKerning = glyphRunLayout->font.getExtraKerningFactor()
|
||||
* glyphRunLayout->font.getHeight();
|
||||
|
||||
for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
|
||||
{
|
||||
auto advance = glyphRun->glyphAdvances[i];
|
||||
|
||||
if ((glyphRun->bidiLevel & 1) != 0)
|
||||
x -= advance + extraKerning; // RTL text
|
||||
|
||||
glyphRunLayout->glyphs.add (TextLayout::Glyph (glyphRun->glyphIndices[i],
|
||||
Point<float> (x, baselineOriginY - lineOrigin.y),
|
||||
advance));
|
||||
|
||||
if ((glyphRun->bidiLevel & 1) == 0)
|
||||
x += advance + extraKerning; // LTR text
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
const AttributedString& attributedString;
|
||||
IDWriteFontCollection& fontCollection;
|
||||
int currentLine = -1;
|
||||
float lastOriginY = -10000.0f;
|
||||
|
||||
static float scaledFontSize (int n, const DWRITE_FONT_METRICS& metrics, const DWRITE_GLYPH_RUN& glyphRun) noexcept
|
||||
{
|
||||
return (std::abs ((float) n) / (float) metrics.designUnitsPerEm) * glyphRun.fontEmSize;
|
||||
}
|
||||
|
||||
static Colour getColourOf (ID2D1SolidColorBrush* d2dBrush) noexcept
|
||||
{
|
||||
if (d2dBrush == nullptr)
|
||||
return Colours::black;
|
||||
|
||||
const D2D1_COLOR_F colour (d2dBrush->GetColor());
|
||||
return Colour::fromFloatRGBA (colour.r, colour.g, colour.b, colour.a);
|
||||
}
|
||||
|
||||
Font getFontForRun (const DWRITE_GLYPH_RUN& glyphRun, float fontHeight)
|
||||
{
|
||||
for (int i = 0; i < attributedString.getNumAttributes(); ++i)
|
||||
{
|
||||
auto& font = attributedString.getAttribute (i).font;
|
||||
auto typeface = font.getTypefacePtr();
|
||||
|
||||
if (auto* wt = dynamic_cast<WindowsDirectWriteTypeface*> (typeface.get()))
|
||||
if (wt->getIDWriteFontFace() == glyphRun.fontFace)
|
||||
return font.withHeight (fontHeight);
|
||||
}
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
[[maybe_unused]] auto hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress());
|
||||
jassert (dwFont != nullptr);
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> dwFontFamily;
|
||||
hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress());
|
||||
|
||||
return Font (getFontFamilyName (dwFontFamily), getFontFaceName (dwFont), fontHeight);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomDirectWriteTextRenderer)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static float getFontHeightToEmSizeFactor (IDWriteFont& dwFont)
|
||||
{
|
||||
ComSmartPtr<IDWriteFontFace> dwFontFace;
|
||||
dwFont.CreateFontFace (dwFontFace.resetAndGetPointerAddress());
|
||||
|
||||
if (dwFontFace == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
DWRITE_FONT_METRICS dwFontMetrics;
|
||||
dwFontFace->GetMetrics (&dwFontMetrics);
|
||||
|
||||
const float totalHeight = (float) (dwFontMetrics.ascent + dwFontMetrics.descent);
|
||||
return dwFontMetrics.designUnitsPerEm / totalHeight;
|
||||
}
|
||||
|
||||
static 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 0:
|
||||
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 0:
|
||||
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);
|
||||
}
|
||||
|
||||
static void addAttributedRange (const AttributedString::Attribute& attr,
|
||||
IDWriteTextLayout& textLayout,
|
||||
CharPointer_UTF16 begin,
|
||||
CharPointer_UTF16 textPointer,
|
||||
const UINT32 textLen,
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
IDWriteFontCollection& fontCollection)
|
||||
{
|
||||
DWRITE_TEXT_RANGE range;
|
||||
range.startPosition = (UINT32) (textPointer.getAddress() - begin.getAddress());
|
||||
|
||||
if (textLen <= range.startPosition)
|
||||
return;
|
||||
|
||||
const auto wordEnd = jmin (textLen, (UINT32) ((textPointer + attr.range.getLength()).getAddress() - begin.getAddress()));
|
||||
range.length = wordEnd - range.startPosition;
|
||||
|
||||
{
|
||||
auto familyName = FontStyleHelpers::getConcreteFamilyName (attr.font);
|
||||
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex;
|
||||
fontCollection.FindFamilyName (familyName.toWideCharPointer(), &fontIndex, &fontFound);
|
||||
|
||||
if (! fontFound)
|
||||
fontIndex = 0;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> fontFamily;
|
||||
[[maybe_unused]] auto hr = fontCollection.GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress());
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
uint32 fontFacesCount = 0;
|
||||
fontFacesCount = fontFamily->GetFontCount();
|
||||
|
||||
for (int i = (int) fontFacesCount; --i >= 0;)
|
||||
{
|
||||
hr = fontFamily->GetFont ((UINT32) i, dwFont.resetAndGetPointerAddress());
|
||||
|
||||
if (attr.font.getTypefaceStyle() == getFontFaceName (dwFont))
|
||||
break;
|
||||
}
|
||||
|
||||
textLayout.SetFontFamilyName (familyName.toWideCharPointer(), range);
|
||||
textLayout.SetFontWeight (dwFont->GetWeight(), range);
|
||||
textLayout.SetFontStretch (dwFont->GetStretch(), range);
|
||||
textLayout.SetFontStyle (dwFont->GetStyle(), range);
|
||||
|
||||
auto fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
textLayout.SetFontSize (attr.font.getHeight() * fontHeightToEmSizeFactor, range);
|
||||
}
|
||||
|
||||
{
|
||||
auto col = attr.colour;
|
||||
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush;
|
||||
renderTarget.CreateSolidColorBrush (D2D1::ColorF (col.getFloatRed(),
|
||||
col.getFloatGreen(),
|
||||
col.getFloatBlue(),
|
||||
col.getFloatAlpha()),
|
||||
d2dBrush.resetAndGetPointerAddress());
|
||||
|
||||
// We need to call SetDrawingEffect with a legitimate brush to get DirectWrite to break text based on colours
|
||||
textLayout.SetDrawingEffect (d2dBrush, range);
|
||||
}
|
||||
}
|
||||
|
||||
static bool setupLayout (const AttributedString& text,
|
||||
float maxWidth,
|
||||
float maxHeight,
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
IDWriteFactory& directWriteFactory,
|
||||
IDWriteFontCollection& fontCollection,
|
||||
ComSmartPtr<IDWriteTextLayout>& textLayout)
|
||||
{
|
||||
// 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
|
||||
|
||||
Font defaultFont;
|
||||
BOOL fontFound = false;
|
||||
uint32 fontIndex;
|
||||
fontCollection.FindFamilyName (defaultFont.getTypefacePtr()->getName().toWideCharPointer(), &fontIndex, &fontFound);
|
||||
|
||||
if (! fontFound)
|
||||
fontIndex = 0;
|
||||
|
||||
ComSmartPtr<IDWriteFontFamily> dwFontFamily;
|
||||
auto hr = fontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress());
|
||||
|
||||
ComSmartPtr<IDWriteFont> dwFont;
|
||||
hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL,
|
||||
dwFont.resetAndGetPointerAddress());
|
||||
jassert (dwFont != nullptr);
|
||||
|
||||
auto defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont);
|
||||
|
||||
ComSmartPtr<IDWriteTextFormat> dwTextFormat;
|
||||
hr = directWriteFactory.CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), &fontCollection,
|
||||
DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
|
||||
defaultFont.getHeight() * defaultFontHeightToEmSizeFactor,
|
||||
L"en-us", dwTextFormat.resetAndGetPointerAddress());
|
||||
|
||||
setTextFormatProperties (text, *dwTextFormat);
|
||||
|
||||
{
|
||||
DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 };
|
||||
ComSmartPtr<IDWriteInlineObject> trimmingSign;
|
||||
hr = directWriteFactory.CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress());
|
||||
hr = dwTextFormat->SetTrimming (&trimming, trimmingSign);
|
||||
}
|
||||
|
||||
const auto beginPtr = text.getText().toUTF16();
|
||||
const auto textLen = (UINT32) (beginPtr.findTerminatingNull().getAddress() - beginPtr.getAddress());
|
||||
|
||||
hr = directWriteFactory.CreateTextLayout (beginPtr.getAddress(),
|
||||
textLen,
|
||||
dwTextFormat,
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
textLayout.resetAndGetPointerAddress());
|
||||
|
||||
if (FAILED (hr) || textLayout == nullptr)
|
||||
return false;
|
||||
|
||||
const auto numAttributes = text.getNumAttributes();
|
||||
auto rangePointer = beginPtr;
|
||||
|
||||
for (int i = 0; i < numAttributes; ++i)
|
||||
{
|
||||
const auto attribute = text.getAttribute (i);
|
||||
addAttributedRange (attribute, *textLayout, beginPtr, rangePointer, textLen, renderTarget, fontCollection);
|
||||
rangePointer += attribute.range.getLength();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void createLayout (TextLayout& layout,
|
||||
const AttributedString& text,
|
||||
IDWriteFactory& directWriteFactory,
|
||||
IDWriteFontCollection& fontCollection,
|
||||
ID2D1DCRenderTarget& renderTarget)
|
||||
{
|
||||
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
|
||||
|
||||
if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget,
|
||||
directWriteFactory, fontCollection, dwTextLayout))
|
||||
return;
|
||||
|
||||
UINT32 actualLineCount = 0;
|
||||
[[maybe_unused]] auto hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount);
|
||||
|
||||
layout.ensureStorageAllocated ((int) actualLineCount);
|
||||
|
||||
{
|
||||
auto textRenderer = becomeComSmartPtrOwner (new CustomDirectWriteTextRenderer (fontCollection, text));
|
||||
hr = dwTextLayout->Draw (&layout, textRenderer, 0, 0);
|
||||
}
|
||||
|
||||
HeapBlock<DWRITE_LINE_METRICS> dwLineMetrics (actualLineCount);
|
||||
hr = dwTextLayout->GetLineMetrics (dwLineMetrics, actualLineCount, &actualLineCount);
|
||||
int lastLocation = 0;
|
||||
auto numLines = jmin ((int) actualLineCount, layout.getNumLines());
|
||||
float yAdjustment = 0;
|
||||
auto extraLineSpacing = text.getLineSpacing();
|
||||
|
||||
for (int i = 0; i < numLines; ++i)
|
||||
{
|
||||
auto& line = layout.getLine (i);
|
||||
line.stringRange = Range<int> (lastLocation, lastLocation + (int) dwLineMetrics[i].length);
|
||||
line.lineOrigin.y += yAdjustment;
|
||||
yAdjustment += extraLineSpacing;
|
||||
lastLocation += (int) dwLineMetrics[i].length;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void drawToD2DContext (const AttributedString& text,
|
||||
const Rectangle<float>& area,
|
||||
ID2D1RenderTarget& renderTarget,
|
||||
IDWriteFactory& directWriteFactory,
|
||||
IDWriteFontCollection& fontCollection)
|
||||
{
|
||||
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
|
||||
|
||||
if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget,
|
||||
directWriteFactory, fontCollection, dwTextLayout))
|
||||
{
|
||||
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush;
|
||||
renderTarget.CreateSolidColorBrush (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f),
|
||||
d2dBrush.resetAndGetPointerAddress());
|
||||
|
||||
renderTarget.DrawTextLayout (D2D1::Point2F ((float) area.getX(), (float) area.getY()),
|
||||
dwTextLayout, d2dBrush, D2D1_DRAW_TEXT_OPTIONS_CLIP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool canAllTypefacesAndFontsBeUsedInLayout (const AttributedString& text)
|
||||
{
|
||||
auto numCharacterAttributes = text.getNumAttributes();
|
||||
|
||||
for (int i = 0; i < numCharacterAttributes; ++i)
|
||||
{
|
||||
const auto& font = text.getAttribute (i).font;
|
||||
auto typeface = font.getTypefacePtr();
|
||||
|
||||
if (font.getHorizontalScale() != 1.0f || dynamic_cast<WindowsDirectWriteTypeface*> (typeface.get()) == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool TextLayout::createNativeLayout ([[maybe_unused]] const AttributedString& text)
|
||||
{
|
||||
#if JUCE_USE_DIRECTWRITE
|
||||
if (! canAllTypefacesAndFontsBeUsedInLayout (text))
|
||||
return false;
|
||||
|
||||
SharedResourcePointer<Direct2DFactories> factories;
|
||||
|
||||
if (factories->d2dFactory != nullptr
|
||||
&& factories->directWriteFactory != nullptr
|
||||
&& factories->systemFonts != nullptr
|
||||
&& factories->directWriteRenderTarget != nullptr)
|
||||
{
|
||||
DirectWriteTypeLayout::createLayout (*this, text,
|
||||
*factories->directWriteFactory,
|
||||
*factories->systemFonts,
|
||||
*factories->directWriteRenderTarget);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
@ -855,11 +855,4 @@ void Typeface::scanFolderForFonts (const File&)
|
|||
// TODO(reuk)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool TextLayout::createNativeLayout ([[maybe_unused]] const AttributedString& text)
|
||||
{
|
||||
// TODO(reuk) Currently unimplemented
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -633,9 +633,4 @@ void Typeface::scanFolderForFonts (const File&)
|
|||
jassertfalse; // not currently available
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -104,11 +104,6 @@ StringArray Font::findAllTypefaceStyles (const String& family)
|
|||
return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct DefaultFontInfo
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,432 +35,6 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
static constexpr float referenceFontSize = 1024.0f;
|
||||
|
||||
CTFontRef getCTFontFromTypeface (const Font& f);
|
||||
|
||||
namespace CoreTextTypeLayout
|
||||
{
|
||||
static float getFontTotalHeight (CTFontRef font)
|
||||
{
|
||||
return std::abs ((float) CTFontGetAscent (font))
|
||||
+ std::abs ((float) CTFontGetDescent (font));
|
||||
}
|
||||
|
||||
static float getHeightToPointsFactor (CTFontRef font)
|
||||
{
|
||||
return (float) CTFontGetSize (font) / (float) getFontTotalHeight (font);
|
||||
}
|
||||
|
||||
static CFUniquePtr<CTFontRef> getFontWithPointSize (CTFontRef font, float pointSize)
|
||||
{
|
||||
return CFUniquePtr<CTFontRef> (CTFontCreateCopyWithAttributes (font, pointSize, nullptr, nullptr));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct Advances
|
||||
{
|
||||
Advances (CTRunRef run, CFIndex numGlyphs) : advances (CTRunGetAdvancesPtr (run))
|
||||
{
|
||||
if (advances == nullptr)
|
||||
{
|
||||
local.malloc (numGlyphs);
|
||||
CTRunGetAdvances (run, CFRangeMake (0, 0), local);
|
||||
advances = local;
|
||||
}
|
||||
}
|
||||
|
||||
const CGSize* advances;
|
||||
HeapBlock<CGSize> local;
|
||||
};
|
||||
|
||||
struct Glyphs
|
||||
{
|
||||
Glyphs (CTRunRef run, size_t numGlyphs) : glyphs (CTRunGetGlyphsPtr (run))
|
||||
{
|
||||
if (glyphs == nullptr)
|
||||
{
|
||||
local.malloc (numGlyphs);
|
||||
CTRunGetGlyphs (run, CFRangeMake (0, 0), local);
|
||||
glyphs = local;
|
||||
}
|
||||
}
|
||||
|
||||
const CGGlyph* glyphs;
|
||||
HeapBlock<CGGlyph> local;
|
||||
};
|
||||
|
||||
struct Positions
|
||||
{
|
||||
Positions (CTRunRef run, size_t numGlyphs) : points (CTRunGetPositionsPtr (run))
|
||||
{
|
||||
if (points == nullptr)
|
||||
{
|
||||
local.malloc (numGlyphs);
|
||||
CTRunGetPositions (run, CFRangeMake (0, 0), local);
|
||||
points = local;
|
||||
}
|
||||
}
|
||||
|
||||
const CGPoint* points;
|
||||
HeapBlock<CGPoint> local;
|
||||
};
|
||||
|
||||
struct LineInfo
|
||||
{
|
||||
LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex)
|
||||
{
|
||||
CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin);
|
||||
CTLineGetTypographicBounds (line, &ascent, &descent, &leading);
|
||||
}
|
||||
|
||||
CGPoint origin;
|
||||
CGFloat ascent, descent, leading;
|
||||
};
|
||||
|
||||
static CFUniquePtr<CTFontRef> getOrCreateFont (const Font& f)
|
||||
{
|
||||
if (auto ctf = getCTFontFromTypeface (f))
|
||||
{
|
||||
CFRetain (ctf);
|
||||
return CFUniquePtr<CTFontRef> (ctf);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static CTTextAlignment getTextAlignment (const AttributedString& text)
|
||||
{
|
||||
const auto flags = text.getJustification().getOnlyHorizontalFlags();
|
||||
|
||||
if (@available (macOS 10.8, *))
|
||||
{
|
||||
switch (flags)
|
||||
{
|
||||
case Justification::right: return kCTTextAlignmentRight;
|
||||
case Justification::horizontallyCentred: return kCTTextAlignmentCenter;
|
||||
case Justification::horizontallyJustified: return kCTTextAlignmentJustified;
|
||||
default: return kCTTextAlignmentLeft;
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
|
||||
|
||||
switch (flags)
|
||||
{
|
||||
case Justification::right: return kCTRightTextAlignment;
|
||||
case Justification::horizontallyCentred: return kCTCenterTextAlignment;
|
||||
case Justification::horizontallyJustified: return kCTJustifiedTextAlignment;
|
||||
default: return kCTLeftTextAlignment;
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
static CTLineBreakMode getLineBreakMode (const AttributedString& text)
|
||||
{
|
||||
switch (text.getWordWrap())
|
||||
{
|
||||
case AttributedString::none: return kCTLineBreakByClipping;
|
||||
case AttributedString::byChar: return kCTLineBreakByCharWrapping;
|
||||
case AttributedString::byWord:
|
||||
default: return kCTLineBreakByWordWrapping;
|
||||
}
|
||||
}
|
||||
|
||||
static CTWritingDirection getWritingDirection (const AttributedString& text)
|
||||
{
|
||||
switch (text.getReadingDirection())
|
||||
{
|
||||
case AttributedString::rightToLeft: return kCTWritingDirectionRightToLeft;
|
||||
case AttributedString::leftToRight: return kCTWritingDirectionLeftToRight;
|
||||
case AttributedString::natural:
|
||||
default: return kCTWritingDirectionNatural;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// A flatmap that properly retains/releases font refs
|
||||
class FontMap
|
||||
{
|
||||
public:
|
||||
void emplace (CTFontRef ctFontRef, Font value)
|
||||
{
|
||||
pairs.emplace (std::lower_bound (pairs.begin(), pairs.end(), ctFontRef), ctFontRef, std::move (value));
|
||||
}
|
||||
|
||||
const Font* find (CTFontRef ctFontRef) const
|
||||
{
|
||||
const auto iter = std::lower_bound (pairs.begin(), pairs.end(), ctFontRef);
|
||||
|
||||
if (iter == pairs.end())
|
||||
return nullptr;
|
||||
|
||||
if (iter->key.get() != ctFontRef)
|
||||
return nullptr;
|
||||
|
||||
return &iter->value;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Pair
|
||||
{
|
||||
Pair (CTFontRef ref, Font font) : key (ref), value (std::move (font)) { CFRetain (ref); }
|
||||
|
||||
bool operator< (CTFontRef other) const { return key.get() < other; }
|
||||
|
||||
CFUniquePtr<CTFontRef> key;
|
||||
Font value;
|
||||
};
|
||||
|
||||
std::vector<Pair> pairs;
|
||||
};
|
||||
|
||||
struct AttributedStringAndFontMap
|
||||
{
|
||||
CFUniquePtr<CFAttributedStringRef> string;
|
||||
FontMap fontMap;
|
||||
};
|
||||
|
||||
static AttributedStringAndFontMap createCFAttributedString (const AttributedString& text)
|
||||
{
|
||||
FontMap fontMap;
|
||||
|
||||
const detail::ColorSpacePtr rgbColourSpace { CGColorSpaceCreateWithName (kCGColorSpaceSRGB) };
|
||||
|
||||
auto attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0);
|
||||
CFUniquePtr<CFStringRef> cfText (text.getText().toCFString());
|
||||
|
||||
CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText.get());
|
||||
|
||||
const auto numCharacterAttributes = text.getNumAttributes();
|
||||
const auto attribStringLen = CFAttributedStringGetLength (attribString);
|
||||
const auto beginPtr = text.getText().toUTF16();
|
||||
auto currentPosition = beginPtr;
|
||||
|
||||
for (int i = 0; i < numCharacterAttributes; currentPosition += text.getAttribute (i).range.getLength(), ++i)
|
||||
{
|
||||
const auto& attr = text.getAttribute (i);
|
||||
const auto wordBegin = currentPosition.getAddress() - beginPtr.getAddress();
|
||||
|
||||
if (attribStringLen <= wordBegin)
|
||||
continue;
|
||||
|
||||
const auto wordEndAddress = (currentPosition + attr.range.getLength()).getAddress();
|
||||
const auto wordEnd = jmin (attribStringLen, (CFIndex) (wordEndAddress - beginPtr.getAddress()));
|
||||
const auto range = CFRangeMake (wordBegin, wordEnd - wordBegin);
|
||||
|
||||
if (auto ctFontRef = getOrCreateFont (attr.font))
|
||||
{
|
||||
ctFontRef = getFontWithPointSize (ctFontRef.get(), attr.font.getHeight() * getHeightToPointsFactor (ctFontRef.get()));
|
||||
fontMap.emplace (ctFontRef.get(), attr.font);
|
||||
|
||||
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef.get());
|
||||
|
||||
if (attr.font.isUnderlined())
|
||||
{
|
||||
auto underline = kCTUnderlineStyleSingle;
|
||||
|
||||
CFUniquePtr<CFNumberRef> numberRef (CFNumberCreate (nullptr, kCFNumberIntType, &underline));
|
||||
CFAttributedStringSetAttribute (attribString, range, kCTUnderlineStyleAttributeName, numberRef.get());
|
||||
}
|
||||
|
||||
auto extraKerning = attr.font.getExtraKerningFactor();
|
||||
|
||||
if (! approximatelyEqual (extraKerning, 0.0f))
|
||||
{
|
||||
extraKerning *= attr.font.getHeight();
|
||||
|
||||
CFUniquePtr<CFNumberRef> numberRef (CFNumberCreate (nullptr, kCFNumberFloatType, &extraKerning));
|
||||
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef.get());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto col = attr.colour;
|
||||
|
||||
const CGFloat components[] = { col.getFloatRed(),
|
||||
col.getFloatGreen(),
|
||||
col.getFloatBlue(),
|
||||
col.getFloatAlpha() };
|
||||
auto colour = CGColorCreate (rgbColourSpace.get(), components);
|
||||
|
||||
CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour);
|
||||
CGColorRelease (colour);
|
||||
}
|
||||
}
|
||||
|
||||
// Paragraph Attributes
|
||||
auto ctTextAlignment = getTextAlignment (text);
|
||||
auto ctLineBreakMode = getLineBreakMode (text);
|
||||
auto ctWritingDirection = getWritingDirection (text);
|
||||
CGFloat ctLineSpacing = text.getLineSpacing();
|
||||
|
||||
CTParagraphStyleSetting settings[] =
|
||||
{
|
||||
{ kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
|
||||
{ kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
|
||||
{ kCTParagraphStyleSpecifierBaseWritingDirection, sizeof (CTWritingDirection), &ctWritingDirection},
|
||||
{ kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
|
||||
};
|
||||
|
||||
CFUniquePtr<CTParagraphStyleRef> ctParagraphStyleRef (CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings)));
|
||||
CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
|
||||
kCTParagraphStyleAttributeName, ctParagraphStyleRef.get());
|
||||
return { CFUniquePtr<CFAttributedStringRef> (attribString), std::move (fontMap) };
|
||||
}
|
||||
|
||||
struct FramesetterAndFontMap
|
||||
{
|
||||
CFUniquePtr<CTFramesetterRef> framesetter;
|
||||
FontMap fontMap;
|
||||
};
|
||||
|
||||
static FramesetterAndFontMap createCTFramesetter (const AttributedString& text)
|
||||
{
|
||||
auto attribStringAndMap = createCFAttributedString (text);
|
||||
return { CFUniquePtr<CTFramesetterRef> (CTFramesetterCreateWithAttributedString (attribStringAndMap.string.get())),
|
||||
std::move (attribStringAndMap.fontMap) };
|
||||
}
|
||||
|
||||
static CFUniquePtr<CTFrameRef> createCTFrame (CTFramesetterRef framesetter, CGRect bounds)
|
||||
{
|
||||
auto path = CGPathCreateMutable();
|
||||
CGPathAddRect (path, nullptr, bounds);
|
||||
|
||||
CFUniquePtr<CTFrameRef> frame (CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr));
|
||||
CGPathRelease (path);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
struct FrameAndFontMap
|
||||
{
|
||||
CFUniquePtr<CTFrameRef> frame;
|
||||
FontMap fontMap;
|
||||
};
|
||||
|
||||
static FrameAndFontMap createCTFrame (const AttributedString& text, CGRect bounds)
|
||||
{
|
||||
auto framesetterAndMap = createCTFramesetter (text);
|
||||
return { createCTFrame (framesetterAndMap.framesetter.get(), bounds),
|
||||
std::move (framesetterAndMap.fontMap) };
|
||||
}
|
||||
|
||||
static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
|
||||
{
|
||||
auto boundsHeight = glyphLayout.getHeight();
|
||||
auto frameAndMap = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
|
||||
auto lines = CTFrameGetLines (frameAndMap.frame.get());
|
||||
auto numLines = CFArrayGetCount (lines);
|
||||
|
||||
glyphLayout.ensureStorageAllocated ((int) numLines);
|
||||
|
||||
for (CFIndex i = 0; i < numLines; ++i)
|
||||
{
|
||||
auto line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
|
||||
auto runs = CTLineGetGlyphRuns (line);
|
||||
auto numRuns = CFArrayGetCount (runs);
|
||||
|
||||
auto cfrlineStringRange = CTLineGetStringRange (line);
|
||||
auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length;
|
||||
Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
|
||||
|
||||
LineInfo lineInfo (frameAndMap.frame.get(), line, i);
|
||||
|
||||
auto glyphLine = std::make_unique<TextLayout::Line> (lineStringRange,
|
||||
Point<float> ((float) lineInfo.origin.x,
|
||||
(float) (boundsHeight - lineInfo.origin.y)),
|
||||
(float) lineInfo.ascent,
|
||||
(float) lineInfo.descent,
|
||||
(float) lineInfo.leading,
|
||||
(int) numRuns);
|
||||
|
||||
for (CFIndex j = 0; j < numRuns; ++j)
|
||||
{
|
||||
auto run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
|
||||
auto numGlyphs = CTRunGetGlyphCount (run);
|
||||
auto runStringRange = CTRunGetStringRange (run);
|
||||
|
||||
auto glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
|
||||
(int) (runStringRange.location + runStringRange.length - 1)),
|
||||
(int) numGlyphs);
|
||||
glyphLine->runs.add (glyphRun);
|
||||
|
||||
CFDictionaryRef runAttributes = CTRunGetAttributes (run);
|
||||
|
||||
CTFontRef ctRunFont;
|
||||
if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
|
||||
{
|
||||
glyphRun->font = [&]
|
||||
{
|
||||
if (auto* it = frameAndMap.fontMap.find (ctRunFont))
|
||||
return *it;
|
||||
|
||||
CFUniquePtr<CFStringRef> cfsFontName (CTFontCopyPostScriptName (ctRunFont));
|
||||
CFUniquePtr<CTFontRef> ctFontRef (CTFontCreateWithName (cfsFontName.get(), referenceFontSize, nullptr));
|
||||
|
||||
auto fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef.get());
|
||||
|
||||
CFUniquePtr<CFStringRef> cfsFontFamily ((CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute));
|
||||
CFUniquePtr<CFStringRef> cfsFontStyle ((CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute));
|
||||
|
||||
Font result (FontOptions { String::fromCFString (cfsFontFamily.get()),
|
||||
String::fromCFString (cfsFontStyle.get()),
|
||||
(float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor) });
|
||||
|
||||
auto isUnderlined = [&]
|
||||
{
|
||||
CFNumberRef underlineStyle;
|
||||
|
||||
if (CFDictionaryGetValueIfPresent (runAttributes, kCTUnderlineStyleAttributeName, (const void**) &underlineStyle))
|
||||
{
|
||||
if (CFGetTypeID (underlineStyle) == CFNumberGetTypeID())
|
||||
{
|
||||
int value = 0;
|
||||
CFNumberGetValue (underlineStyle, kCFNumberLongType, (void*) &value);
|
||||
|
||||
return value != 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
result.setUnderline (isUnderlined);
|
||||
return result;
|
||||
}();
|
||||
}
|
||||
|
||||
CGColorRef cgRunColor;
|
||||
|
||||
if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
|
||||
&& CGColorGetNumberOfComponents (cgRunColor) == 4)
|
||||
{
|
||||
auto* components = CGColorGetComponents (cgRunColor);
|
||||
|
||||
glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
|
||||
(float) components[1],
|
||||
(float) components[2],
|
||||
(float) components[3]);
|
||||
}
|
||||
|
||||
const Glyphs glyphs (run, (size_t) numGlyphs);
|
||||
const Advances advances (run, numGlyphs);
|
||||
const Positions positions (run, (size_t) numGlyphs);
|
||||
|
||||
for (CFIndex k = 0; k < numGlyphs; ++k)
|
||||
glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k],
|
||||
convertToPointFloat (positions.points[k]),
|
||||
(float) advances.advances[k].width));
|
||||
}
|
||||
|
||||
glyphLayout.addLine (std::move (glyphLine));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This symbol is available on all the platforms we support, but not declared in the CoreText headers on older platforms.
|
||||
extern "C" CTFontRef CTFontCreateForStringWithLanguage (CTFontRef currentFont,
|
||||
CFStringRef string,
|
||||
|
|
@ -729,16 +303,6 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreTextTypeface)
|
||||
};
|
||||
|
||||
CTFontRef getCTFontFromTypeface (const Font& f)
|
||||
{
|
||||
const auto typeface = f.getTypefacePtr();
|
||||
|
||||
if (auto* tf = dynamic_cast<CoreTextTypeface*> (typeface.get()))
|
||||
return tf->getFontRef();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
|
||||
{
|
||||
|
|
@ -849,10 +413,4 @@ Typeface::Ptr Font::Native::getDefaultPlatformTypefaceForFont (const Font& font)
|
|||
return Typeface::createSystemTypefaceFor (newFont);
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString& text)
|
||||
{
|
||||
CoreTextTypeLayout::createLayout (*this, text);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue