@@ -22,31 +22,156 @@ | |||||
============================================================================== | ============================================================================== | ||||
*/ | */ | ||||
AttributedString::Attribute::Attribute (Range<int> range_, Colour colour_) | |||||
: range (range_), colour (new Colour (colour_)) | |||||
namespace | |||||
{ | { | ||||
int getLength (const Array<AttributedString::Attribute>& atts) noexcept | |||||
{ | |||||
return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0; | |||||
} | |||||
void splitAttributeRanges (Array<AttributedString::Attribute>& atts, int position) | |||||
{ | |||||
for (int i = atts.size(); --i >= 0;) | |||||
{ | |||||
const AttributedString::Attribute& att = atts.getReference (i); | |||||
const int offset = position - att.range.getStart(); | |||||
if (offset >= 0) | |||||
{ | |||||
if (offset > 0 && position < att.range.getEnd()) | |||||
{ | |||||
atts.insert (i + 1, att); | |||||
atts.getReference (i).range.setEnd (position); | |||||
atts.getReference (i + 1).range.setStart (position); | |||||
} | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange) | |||||
{ | |||||
newRange = newRange.getIntersectionWith (Range<int> (0, getLength (atts))); | |||||
if (! newRange.isEmpty()) | |||||
{ | |||||
splitAttributeRanges (atts, newRange.getStart()); | |||||
splitAttributeRanges (atts, newRange.getEnd()); | |||||
} | |||||
return newRange; | |||||
} | |||||
void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts) | |||||
{ | |||||
for (int i = atts.size() - 1; --i >= 0;) | |||||
{ | |||||
AttributedString::Attribute& a1 = atts.getReference (i); | |||||
AttributedString::Attribute& a2 = atts.getReference (i + 1); | |||||
if (a1.colour == a2.colour && a1.font == a2.font) | |||||
{ | |||||
a1.range.setEnd (a2.range.getEnd()); | |||||
atts.remove (i + 1); | |||||
if (i < atts.size() - 1) | |||||
++i; | |||||
} | |||||
} | |||||
} | |||||
void appendRange (Array<AttributedString::Attribute>& atts, | |||||
int length, const Font* f, const Colour* c) | |||||
{ | |||||
if (atts.size() == 0) | |||||
{ | |||||
atts.add (AttributedString::Attribute (Range<int> (0, length), | |||||
f != nullptr ? *f : Font(), | |||||
c != nullptr ? *c : Colour (0xff000000))); | |||||
} | |||||
else | |||||
{ | |||||
const int start = getLength (atts); | |||||
atts.add (AttributedString::Attribute (Range<int> (start, start + length), | |||||
f != nullptr ? *f : atts.getReference (atts.size() - 1).font, | |||||
c != nullptr ? *c : atts.getReference (atts.size() - 1).colour)); | |||||
mergeAdjacentRanges (atts); | |||||
} | |||||
} | |||||
void applyFontAndColour (Array<AttributedString::Attribute>& atts, | |||||
Range<int> range, const Font* f, const Colour* c) | |||||
{ | |||||
range = splitAttributeRanges (atts, range); | |||||
for (int i = 0; i < atts.size(); ++i) | |||||
{ | |||||
AttributedString::Attribute& att = atts.getReference (i); | |||||
if (range.getStart() < att.range.getEnd()) | |||||
{ | |||||
if (range.getEnd() <= att.range.getStart()) | |||||
break; | |||||
if (c != nullptr) att.colour = *c; | |||||
if (f != nullptr) att.font = *f; | |||||
} | |||||
} | |||||
mergeAdjacentRanges (atts); | |||||
} | |||||
void truncate (Array<AttributedString::Attribute>& atts, int newLength) | |||||
{ | |||||
splitAttributeRanges (atts, newLength); | |||||
for (int i = atts.size(); --i >= 0;) | |||||
if (atts.getReference (i).range.getStart() >= newLength) | |||||
atts.remove (i); | |||||
} | |||||
} | } | ||||
AttributedString::Attribute::Attribute (Range<int> range_, const Font& font_) | |||||
: range (range_), font (new Font (font_)) | |||||
//============================================================================== | |||||
AttributedString::Attribute::Attribute() noexcept : colour (0xff000000) {} | |||||
AttributedString::Attribute::~Attribute() noexcept {} | |||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||||
AttributedString::Attribute::Attribute (Attribute&& other) noexcept | |||||
: range (other.range), | |||||
font (static_cast<Font&&> (other.font)), | |||||
colour (other.colour) | |||||
{ | |||||
} | |||||
AttributedString::Attribute& AttributedString::Attribute::operator= (Attribute&& other) noexcept | |||||
{ | { | ||||
range = other.range; | |||||
font = static_cast<Font&&> (other.font); | |||||
colour = other.colour; | |||||
return *this; | |||||
} | } | ||||
#endif | |||||
AttributedString::Attribute::Attribute (const Attribute& other) | |||||
AttributedString::Attribute::Attribute (const Attribute& other) noexcept | |||||
: range (other.range), | : range (other.range), | ||||
font (other.font.createCopy()), | |||||
colour (other.colour.createCopy()) | |||||
font (other.font), | |||||
colour (other.colour) | |||||
{ | { | ||||
} | } | ||||
AttributedString::Attribute::Attribute (const Attribute& other, const int offset) | |||||
: range (other.range + offset), | |||||
font (other.font.createCopy()), | |||||
colour (other.colour.createCopy()) | |||||
AttributedString::Attribute& AttributedString::Attribute::operator= (const Attribute& other) noexcept | |||||
{ | { | ||||
range = other.range; | |||||
font = other.font; | |||||
colour = other.colour; | |||||
return *this; | |||||
} | } | ||||
AttributedString::Attribute::~Attribute() {} | |||||
AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept | |||||
: range (r), font (f), colour (c) | |||||
{ | |||||
} | |||||
//============================================================================== | //============================================================================== | ||||
AttributedString::AttributedString() | AttributedString::AttributedString() | ||||
@@ -58,12 +183,12 @@ AttributedString::AttributedString() | |||||
} | } | ||||
AttributedString::AttributedString (const String& newString) | AttributedString::AttributedString (const String& newString) | ||||
: text (newString), | |||||
lineSpacing (0.0f), | |||||
: lineSpacing (0.0f), | |||||
justification (Justification::left), | justification (Justification::left), | ||||
wordWrap (AttributedString::byWord), | wordWrap (AttributedString::byWord), | ||||
readingDirection (AttributedString::natural) | readingDirection (AttributedString::natural) | ||||
{ | { | ||||
setText (newString); | |||||
} | } | ||||
AttributedString::AttributedString (const AttributedString& other) | AttributedString::AttributedString (const AttributedString& other) | ||||
@@ -71,9 +196,9 @@ AttributedString::AttributedString (const AttributedString& other) | |||||
lineSpacing (other.lineSpacing), | lineSpacing (other.lineSpacing), | ||||
justification (other.justification), | justification (other.justification), | ||||
wordWrap (other.wordWrap), | wordWrap (other.wordWrap), | ||||
readingDirection (other.readingDirection) | |||||
readingDirection (other.readingDirection), | |||||
attributes (other.attributes) | |||||
{ | { | ||||
attributes.addCopiesOf (other.attributes); | |||||
} | } | ||||
AttributedString& AttributedString::operator= (const AttributedString& other) | AttributedString& AttributedString::operator= (const AttributedString& other) | ||||
@@ -85,8 +210,7 @@ AttributedString& AttributedString::operator= (const AttributedString& other) | |||||
justification = other.justification; | justification = other.justification; | ||||
wordWrap = other.wordWrap; | wordWrap = other.wordWrap; | ||||
readingDirection = other.readingDirection; | readingDirection = other.readingDirection; | ||||
attributes.clear(); | |||||
attributes.addCopiesOf (other.attributes); | |||||
attributes = other.attributes; | |||||
} | } | ||||
return *this; | return *this; | ||||
@@ -99,7 +223,7 @@ AttributedString::AttributedString (AttributedString&& other) noexcept | |||||
justification (other.justification), | justification (other.justification), | ||||
wordWrap (other.wordWrap), | wordWrap (other.wordWrap), | ||||
readingDirection (other.readingDirection), | readingDirection (other.readingDirection), | ||||
attributes (static_cast<OwnedArray<Attribute>&&> (other.attributes)) | |||||
attributes (static_cast<Array<Attribute>&&> (other.attributes)) | |||||
{ | { | ||||
} | } | ||||
@@ -110,58 +234,61 @@ AttributedString& AttributedString::operator= (AttributedString&& other) noexcep | |||||
justification = other.justification; | justification = other.justification; | ||||
wordWrap = other.wordWrap; | wordWrap = other.wordWrap; | ||||
readingDirection = other.readingDirection; | readingDirection = other.readingDirection; | ||||
attributes = static_cast<OwnedArray<Attribute>&&> (other.attributes); | |||||
attributes = static_cast<Array<Attribute>&&> (other.attributes); | |||||
return *this; | return *this; | ||||
} | } | ||||
#endif | #endif | ||||
AttributedString::~AttributedString() {} | |||||
AttributedString::~AttributedString() noexcept {} | |||||
void AttributedString::setText (const String& other) | |||||
void AttributedString::setText (const String& newText) | |||||
{ | { | ||||
text = other; | |||||
const int newLength = newText.length(); | |||||
const int oldLength = getLength (attributes); | |||||
if (newLength > oldLength) | |||||
appendRange (attributes, newLength - oldLength, nullptr, nullptr); | |||||
else if (newLength < oldLength) | |||||
truncate (attributes, newLength); | |||||
text = newText; | |||||
} | } | ||||
void AttributedString::append (const String& textToAppend) | void AttributedString::append (const String& textToAppend) | ||||
{ | { | ||||
text += textToAppend; | text += textToAppend; | ||||
appendRange (attributes, textToAppend.length(), nullptr, nullptr); | |||||
} | } | ||||
void AttributedString::append (const String& textToAppend, const Font& font) | void AttributedString::append (const String& textToAppend, const Font& font) | ||||
{ | { | ||||
const int oldLength = text.length(); | |||||
const int newLength = textToAppend.length(); | |||||
text += textToAppend; | text += textToAppend; | ||||
setFont (Range<int> (oldLength, oldLength + newLength), font); | |||||
appendRange (attributes, textToAppend.length(), &font, nullptr); | |||||
} | } | ||||
void AttributedString::append (const String& textToAppend, Colour colour) | void AttributedString::append (const String& textToAppend, Colour colour) | ||||
{ | { | ||||
const int oldLength = text.length(); | |||||
const int newLength = textToAppend.length(); | |||||
text += textToAppend; | text += textToAppend; | ||||
setColour (Range<int> (oldLength, oldLength + newLength), colour); | |||||
appendRange (attributes, textToAppend.length(), nullptr, &colour); | |||||
} | } | ||||
void AttributedString::append (const String& textToAppend, const Font& font, Colour colour) | void AttributedString::append (const String& textToAppend, const Font& font, Colour colour) | ||||
{ | { | ||||
const int oldLength = text.length(); | |||||
const int newLength = textToAppend.length(); | |||||
text += textToAppend; | text += textToAppend; | ||||
setFont (Range<int> (oldLength, oldLength + newLength), font); | |||||
setColour (Range<int> (oldLength, oldLength + newLength), colour); | |||||
appendRange (attributes, textToAppend.length(), &font, &colour); | |||||
} | } | ||||
void AttributedString::append (const AttributedString& other) | void AttributedString::append (const AttributedString& other) | ||||
{ | { | ||||
const int originalLength = text.length(); | |||||
const int originalLength = getLength (attributes); | |||||
const int originalNumAtts = attributes.size(); | |||||
text += other.text; | text += other.text; | ||||
attributes.addArray (other.attributes); | |||||
for (int i = 0; i < other.attributes.size(); ++i) | |||||
attributes.add (new Attribute (*other.attributes.getUnchecked(i), originalLength)); | |||||
for (int i = originalNumAtts; i < attributes.size(); ++i) | |||||
attributes.getReference (i).range += originalLength; | |||||
mergeAdjacentRanges (attributes); | |||||
} | } | ||||
void AttributedString::clear() | void AttributedString::clear() | ||||
@@ -192,36 +319,30 @@ void AttributedString::setLineSpacing (const float newLineSpacing) noexcept | |||||
void AttributedString::setColour (Range<int> range, Colour colour) | void AttributedString::setColour (Range<int> range, Colour colour) | ||||
{ | { | ||||
attributes.add (new Attribute (range, colour)); | |||||
applyFontAndColour (attributes, range, nullptr, &colour); | |||||
} | } | ||||
void AttributedString::setColour (Colour colour) | |||||
void AttributedString::setFont (Range<int> range, const Font& font) | |||||
{ | { | ||||
for (int i = attributes.size(); --i >= 0;) | |||||
if (attributes.getUnchecked(i)->getColour() != nullptr) | |||||
attributes.remove (i); | |||||
setColour (Range<int> (0, text.length()), colour); | |||||
applyFontAndColour (attributes, range, &font, nullptr); | |||||
} | } | ||||
void AttributedString::setFont (Range<int> range, const Font& font) | |||||
void AttributedString::setColour (Colour colour) | |||||
{ | { | ||||
attributes.add (new Attribute (range, font)); | |||||
setColour (Range<int> (0, getLength (attributes)), colour); | |||||
} | } | ||||
void AttributedString::setFont (const Font& font) | void AttributedString::setFont (const Font& font) | ||||
{ | { | ||||
for (int i = attributes.size(); --i >= 0;) | |||||
if (attributes.getUnchecked(i)->getFont() != nullptr) | |||||
attributes.remove (i); | |||||
setFont (Range<int> (0, text.length()), font); | |||||
setFont (Range<int> (0, getLength (attributes)), font); | |||||
} | } | ||||
void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const | void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const | ||||
{ | { | ||||
if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer())) | if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer())) | ||||
{ | { | ||||
jassert (text.length() == getLength (attributes)); | |||||
if (! g.getInternalContext().drawTextLayout (*this, area)) | if (! g.getInternalContext().drawTextLayout (*this, area)) | ||||
{ | { | ||||
TextLayout layout; | TextLayout layout; | ||||
@@ -53,7 +53,7 @@ public: | |||||
#endif | #endif | ||||
/** Destructor. */ | /** Destructor. */ | ||||
~AttributedString(); | |||||
~AttributedString() noexcept; | |||||
//============================================================================== | //============================================================================== | ||||
/** Returns the complete text of this attributed string. */ | /** Returns the complete text of this attributed string. */ | ||||
@@ -150,36 +150,28 @@ public: | |||||
class JUCE_API Attribute | class JUCE_API Attribute | ||||
{ | { | ||||
public: | public: | ||||
/** Creates an attribute that changes the colour for a range of characters. | |||||
@see AttributedString::setColour() | |||||
*/ | |||||
Attribute (Range<int> range, Colour colour); | |||||
/** Creates an attribute that changes the font for a range of characters. | |||||
@see AttributedString::setFont() | |||||
*/ | |||||
Attribute (Range<int> range, const Font& font); | |||||
Attribute (const Attribute&); | |||||
~Attribute(); | |||||
/** If this attribute specifies a font, this returns it; otherwise it returns nullptr. */ | |||||
const Font* getFont() const noexcept { return font; } | |||||
/** If this attribute specifies a colour, this returns it; otherwise it returns nullptr. */ | |||||
const Colour* getColour() const noexcept { return colour; } | |||||
Attribute() noexcept; | |||||
~Attribute() noexcept; | |||||
Attribute (const Attribute&) noexcept; | |||||
Attribute& operator= (const Attribute&) noexcept; | |||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | |||||
Attribute (Attribute&&) noexcept; | |||||
Attribute& operator= (Attribute&&) noexcept; | |||||
#endif | |||||
/** Creates an attribute that specifies the font and colour for a range of characters. */ | |||||
Attribute (Range<int> range, const Font& font, Colour colour) noexcept; | |||||
/** The range of characters to which this attribute will be applied. */ | /** The range of characters to which this attribute will be applied. */ | ||||
const Range<int> range; | |||||
Range<int> range; | |||||
private: | |||||
ScopedPointer<Font> font; | |||||
ScopedPointer<Colour> colour; | |||||
/** The font for this range of characters. */ | |||||
Font font; | |||||
friend class AttributedString; | |||||
Attribute (const Attribute&, int); | |||||
Attribute& operator= (const Attribute&); | |||||
/** The colour for this range of characters. */ | |||||
Colour colour; | |||||
private: | |||||
JUCE_LEAK_DETECTOR (Attribute) | JUCE_LEAK_DETECTOR (Attribute) | ||||
}; | }; | ||||
@@ -189,7 +181,7 @@ public: | |||||
/** Returns one of the string's attributes. | /** Returns one of the string's attributes. | ||||
The index provided must be less than getNumAttributes(), and >= 0. | The index provided must be less than getNumAttributes(), and >= 0. | ||||
*/ | */ | ||||
const Attribute* getAttribute (int index) const noexcept { return attributes.getUnchecked (index); } | |||||
const Attribute& getAttribute (int index) const noexcept { return attributes.getReference (index); } | |||||
//============================================================================== | //============================================================================== | ||||
/** Adds a colour attribute for the specified range. */ | /** Adds a colour attribute for the specified range. */ | ||||
@@ -210,7 +202,7 @@ private: | |||||
Justification justification; | Justification justification; | ||||
WordWrap wordWrap; | WordWrap wordWrap; | ||||
ReadingDirection readingDirection; | ReadingDirection readingDirection; | ||||
OwnedArray<Attribute> attributes; | |||||
Array<Attribute> attributes; | |||||
JUCE_LEAK_DETECTOR (AttributedString) | JUCE_LEAK_DETECTOR (AttributedString) | ||||
}; | }; | ||||
@@ -287,29 +287,6 @@ void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& te | |||||
//============================================================================== | //============================================================================== | ||||
namespace TextLayoutHelpers | namespace TextLayoutHelpers | ||||
{ | { | ||||
struct FontAndColour | |||||
{ | |||||
FontAndColour (const Font* f) noexcept : font (f), colour (0xff000000) {} | |||||
const Font* font; | |||||
Colour colour; | |||||
bool operator!= (const FontAndColour& other) const noexcept | |||||
{ | |||||
return (font != other.font && *font != *other.font) || colour != other.colour; | |||||
} | |||||
}; | |||||
struct RunAttribute | |||||
{ | |||||
RunAttribute (const FontAndColour& fc, const Range<int> r) noexcept | |||||
: fontAndColour (fc), range (r) | |||||
{} | |||||
FontAndColour fontAndColour; | |||||
Range<int> range; | |||||
}; | |||||
struct Token | struct Token | ||||
{ | { | ||||
Token (const String& t, const Font& f, Colour c, const bool whitespace) | Token (const String& t, const Font& f, Colour c, const bool whitespace) | ||||
@@ -337,7 +314,6 @@ namespace TextLayoutHelpers | |||||
void createLayout (const AttributedString& text, TextLayout& layout) | void createLayout (const AttributedString& text, TextLayout& layout) | ||||
{ | { | ||||
tokens.ensureStorageAllocated (64); | |||||
layout.ensureStorageAllocated (totalLines); | layout.ensureStorageAllocated (totalLines); | ||||
addTextRuns (text); | addTextRuns (text); | ||||
@@ -465,10 +441,8 @@ namespace TextLayoutHelpers | |||||
return CharacterFunctions::isWhitespace (c) ? 2 : 1; | return CharacterFunctions::isWhitespace (c) ? 2 : 1; | ||||
} | } | ||||
void appendText (const AttributedString& text, const Range<int> stringRange, | |||||
const Font& font, Colour colour) | |||||
void appendText (const String& stringText, const Font& font, Colour colour) | |||||
{ | { | ||||
const String stringText (text.getText().substring (stringRange.getStart(), stringRange.getEnd())); | |||||
String::CharPointerType t (stringText.getCharPointer()); | String::CharPointerType t (stringText.getCharPointer()); | ||||
String currentString; | String currentString; | ||||
int lastCharType = 0; | int lastCharType = 0; | ||||
@@ -551,48 +525,15 @@ namespace TextLayoutHelpers | |||||
void addTextRuns (const AttributedString& text) | void addTextRuns (const AttributedString& text) | ||||
{ | { | ||||
Font defaultFont; | |||||
Array<RunAttribute> runAttributes; | |||||
const int numAttributes = text.getNumAttributes(); | |||||
tokens.ensureStorageAllocated (jmax (64, numAttributes)); | |||||
for (int i = 0; i < numAttributes; ++i) | |||||
{ | { | ||||
const int stringLength = text.getText().length(); | |||||
int rangeStart = 0; | |||||
FontAndColour lastFontAndColour (&defaultFont); | |||||
// Iterate through every character in the string | |||||
for (int i = 0; i < stringLength; ++i) | |||||
{ | |||||
FontAndColour newFontAndColour (&defaultFont); | |||||
const int numCharacterAttributes = text.getNumAttributes(); | |||||
for (int j = 0; j < numCharacterAttributes; ++j) | |||||
{ | |||||
const AttributedString::Attribute& attr = *text.getAttribute (j); | |||||
const AttributedString::Attribute& attr = text.getAttribute (i); | |||||
if (attr.range.contains (i)) | |||||
{ | |||||
if (const Font* f = attr.getFont()) newFontAndColour.font = f; | |||||
if (const Colour* c = attr.getColour()) newFontAndColour.colour = *c; | |||||
} | |||||
} | |||||
if (i > 0 && newFontAndColour != lastFontAndColour) | |||||
{ | |||||
runAttributes.add (RunAttribute (lastFontAndColour, Range<int> (rangeStart, i))); | |||||
rangeStart = i; | |||||
} | |||||
lastFontAndColour = newFontAndColour; | |||||
} | |||||
if (rangeStart < stringLength) | |||||
runAttributes.add (RunAttribute (lastFontAndColour, Range<int> (rangeStart, stringLength))); | |||||
} | |||||
for (int i = 0; i < runAttributes.size(); ++i) | |||||
{ | |||||
const RunAttribute& r = runAttributes.getReference(i); | |||||
appendText (text, r.range, *(r.fontAndColour.font), r.fontAndColour.colour); | |||||
appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()), | |||||
attr.font, attr.colour); | |||||
} | } | ||||
} | } | ||||
@@ -91,9 +91,9 @@ class LowLevelGraphicsContext; | |||||
#include "images/juce_ImageCache.h" | #include "images/juce_ImageCache.h" | ||||
#include "images/juce_ImageConvolutionKernel.h" | #include "images/juce_ImageConvolutionKernel.h" | ||||
#include "images/juce_ImageFileFormat.h" | #include "images/juce_ImageFileFormat.h" | ||||
#include "fonts/juce_AttributedString.h" | |||||
#include "fonts/juce_Typeface.h" | #include "fonts/juce_Typeface.h" | ||||
#include "fonts/juce_Font.h" | #include "fonts/juce_Font.h" | ||||
#include "fonts/juce_AttributedString.h" | |||||
#include "fonts/juce_GlyphArrangement.h" | #include "fonts/juce_GlyphArrangement.h" | ||||
#include "fonts/juce_TextLayout.h" | #include "fonts/juce_TextLayout.h" | ||||
#include "fonts/juce_CustomTypeface.h" | #include "fonts/juce_CustomTypeface.h" | ||||
@@ -229,7 +229,7 @@ namespace CoreTextTypeLayout | |||||
for (int i = 0; i < numCharacterAttributes; ++i) | for (int i = 0; i < numCharacterAttributes; ++i) | ||||
{ | { | ||||
const AttributedString::Attribute& attr = *text.getAttribute (i); | |||||
const AttributedString::Attribute& attr = text.getAttribute (i); | |||||
const int rangeStart = attr.range.getStart(); | const int rangeStart = attr.range.getStart(); | ||||
if (rangeStart >= attribStringLen) | if (rangeStart >= attribStringLen) | ||||
@@ -237,42 +237,40 @@ namespace CoreTextTypeLayout | |||||
CFRange range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart); | CFRange range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart); | ||||
if (const Font* const f = attr.getFont()) | |||||
if (CTFontRef ctFontRef = getOrCreateFont (attr.font)) | |||||
{ | { | ||||
if (CTFontRef ctFontRef = getOrCreateFont (*f)) | |||||
{ | |||||
ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); | |||||
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef); | |||||
ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef)); | |||||
float extraKerning = f->getExtraKerningFactor(); | |||||
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef); | |||||
if (extraKerning != 0.0f) | |||||
{ | |||||
extraKerning *= f->getHeight(); | |||||
float extraKerning = attr.font.getExtraKerningFactor(); | |||||
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning); | |||||
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef); | |||||
CFRelease (numberRef); | |||||
} | |||||
if (extraKerning != 0.0f) | |||||
{ | |||||
extraKerning *= attr.font.getHeight(); | |||||
CFRelease (ctFontRef); | |||||
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning); | |||||
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef); | |||||
CFRelease (numberRef); | |||||
} | } | ||||
CFRelease (ctFontRef); | |||||
} | } | ||||
if (const Colour* const col = attr.getColour()) | |||||
{ | { | ||||
const Colour col (attr.colour); | |||||
#if JUCE_IOS | #if JUCE_IOS | ||||
const CGFloat components[] = { col->getFloatRed(), | |||||
col->getFloatGreen(), | |||||
col->getFloatBlue(), | |||||
col->getFloatAlpha() }; | |||||
const CGFloat components[] = { col.getFloatRed(), | |||||
col.getFloatGreen(), | |||||
col.getFloatBlue(), | |||||
col.getFloatAlpha() }; | |||||
CGColorRef colour = CGColorCreate (rgbColourSpace, components); | CGColorRef colour = CGColorCreate (rgbColourSpace, components); | ||||
#else | #else | ||||
CGColorRef colour = CGColorCreateGenericRGB (col->getFloatRed(), | |||||
col->getFloatGreen(), | |||||
col->getFloatBlue(), | |||||
col->getFloatAlpha()); | |||||
CGColorRef colour = CGColorCreateGenericRGB (col.getFloatRed(), | |||||
col.getFloatGreen(), | |||||
col.getFloatBlue(), | |||||
col.getFloatAlpha()); | |||||
#endif | #endif | ||||
CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour); | CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour); | ||||
@@ -449,7 +447,7 @@ namespace CoreTextTypeLayout | |||||
CFDictionaryRef runAttributes = CTRunGetAttributes (run); | CFDictionaryRef runAttributes = CTRunGetAttributes (run); | ||||
CTFontRef ctRunFont; | CTFontRef ctRunFont; | ||||
if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont)) | |||||
if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont)) | |||||
{ | { | ||||
CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); | CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); | ||||
CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr); | CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr); | ||||
@@ -1176,17 +1174,16 @@ static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) | |||||
for (int i = 0; i < numCharacterAttributes; ++i) | for (int i = 0; i < numCharacterAttributes; ++i) | ||||
{ | { | ||||
if (const Font* const f = text.getAttribute (i)->getFont()) | |||||
Typeface* t = text.getAttribute(i).font.getTypeface(); | |||||
if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (t)) | |||||
{ | { | ||||
if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (f->getTypeface())) | |||||
{ | |||||
if (tf->isMemoryFont) | |||||
return false; | |||||
} | |||||
else if (dynamic_cast<CustomTypeface*> (f->getTypeface()) != nullptr) | |||||
{ | |||||
if (tf->isMemoryFont) | |||||
return false; | return false; | ||||
} | |||||
} | |||||
else if (dynamic_cast<CustomTypeface*> (t) != nullptr) | |||||
{ | |||||
return false; | |||||
} | } | ||||
} | } | ||||
@@ -22,7 +22,7 @@ | |||||
============================================================================== | ============================================================================== | ||||
*/ | */ | ||||
//================================================================================================== | |||||
#if JUCE_USE_DIRECTWRITE | #if JUCE_USE_DIRECTWRITE | ||||
namespace DirectWriteTypeLayout | namespace DirectWriteTypeLayout | ||||
{ | { | ||||
@@ -41,7 +41,7 @@ namespace DirectWriteTypeLayout | |||||
JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override | JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override | ||||
{ | { | ||||
if (refId == __uuidof (IDWritePixelSnapping)) | if (refId == __uuidof (IDWritePixelSnapping)) | ||||
return castToType <IDWritePixelSnapping> (result); | |||||
return castToType<IDWritePixelSnapping> (result); | |||||
return ComBaseClassHelper<IDWriteTextRenderer>::QueryInterface (refId, result); | return ComBaseClassHelper<IDWriteTextRenderer>::QueryInterface (refId, result); | ||||
} | } | ||||
@@ -54,12 +54,9 @@ namespace DirectWriteTypeLayout | |||||
JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX* matrix) override | JUCE_COMRESULT GetCurrentTransform (void*, DWRITE_MATRIX* matrix) 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; | |||||
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; | return S_OK; | ||||
} | } | ||||
@@ -172,10 +169,13 @@ namespace DirectWriteTypeLayout | |||||
Font getFontForRun (const DWRITE_GLYPH_RUN& glyphRun, float fontHeight) | Font getFontForRun (const DWRITE_GLYPH_RUN& glyphRun, float fontHeight) | ||||
{ | { | ||||
for (int i = 0; i < attributedString.getNumAttributes(); ++i) | for (int i = 0; i < attributedString.getNumAttributes(); ++i) | ||||
if (const Font* font = attributedString.getAttribute(i)->getFont()) | |||||
if (WindowsDirectWriteTypeface* wt = dynamic_cast<WindowsDirectWriteTypeface*> (font->getTypeface())) | |||||
if (wt->getIDWriteFontFace() == glyphRun.fontFace) | |||||
return font->withHeight (fontHeight); | |||||
{ | |||||
const Font& font = attributedString.getAttribute(i).font; | |||||
if (WindowsDirectWriteTypeface* wt = dynamic_cast<WindowsDirectWriteTypeface*> (font.getTypeface())) | |||||
if (wt->getIDWriteFontFace() == glyphRun.fontFace) | |||||
return font.withHeight (fontHeight); | |||||
} | |||||
ComSmartPtr<IDWriteFont> dwFont; | ComSmartPtr<IDWriteFont> dwFont; | ||||
HRESULT hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress()); | HRESULT hr = fontCollection.GetFontFromFontFace (glyphRun.fontFace, dwFont.resetAndGetPointerAddress()); | ||||
@@ -253,9 +253,8 @@ namespace DirectWriteTypeLayout | |||||
range.startPosition = attr.range.getStart(); | range.startPosition = attr.range.getStart(); | ||||
range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); | range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); | ||||
if (const Font* const font = attr.getFont()) | |||||
{ | { | ||||
const String familyName (FontStyleHelpers::getConcreteFamilyName (*font)); | |||||
const String familyName (FontStyleHelpers::getConcreteFamilyName (attr.font)); | |||||
BOOL fontFound = false; | BOOL fontFound = false; | ||||
uint32 fontIndex; | uint32 fontIndex; | ||||
@@ -275,7 +274,7 @@ namespace DirectWriteTypeLayout | |||||
{ | { | ||||
hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); | hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); | ||||
if (font->getTypefaceStyle() == getFontFaceName (dwFont)) | |||||
if (attr.font.getTypefaceStyle() == getFontFaceName (dwFont)) | |||||
break; | break; | ||||
} | } | ||||
@@ -285,16 +284,16 @@ namespace DirectWriteTypeLayout | |||||
textLayout.SetFontStyle (dwFont->GetStyle(), range); | textLayout.SetFontStyle (dwFont->GetStyle(), range); | ||||
const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont); | const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*dwFont); | ||||
textLayout.SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); | |||||
textLayout.SetFontSize (attr.font.getHeight() * fontHeightToEmSizeFactor, range); | |||||
} | } | ||||
if (const Colour* const colour = attr.getColour()) | |||||
{ | { | ||||
const Colour col (attr.colour); | |||||
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush; | ComSmartPtr<ID2D1SolidColorBrush> d2dBrush; | ||||
renderTarget.CreateSolidColorBrush (D2D1::ColorF (colour->getFloatRed(), | |||||
colour->getFloatGreen(), | |||||
colour->getFloatBlue(), | |||||
colour->getFloatAlpha()), | |||||
renderTarget.CreateSolidColorBrush (D2D1::ColorF (col.getFloatRed(), | |||||
col.getFloatGreen(), | |||||
col.getFloatBlue(), | |||||
col.getFloatAlpha()), | |||||
d2dBrush.resetAndGetPointerAddress()); | d2dBrush.resetAndGetPointerAddress()); | ||||
// We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours | // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours | ||||
@@ -353,7 +352,7 @@ namespace DirectWriteTypeLayout | |||||
const int numAttributes = text.getNumAttributes(); | const int numAttributes = text.getNumAttributes(); | ||||
for (int i = 0; i < numAttributes; ++i) | for (int i = 0; i < numAttributes; ++i) | ||||
addAttributedRange (*text.getAttribute (i), *textLayout, textLen, renderTarget, fontCollection); | |||||
addAttributedRange (text.getAttribute (i), *textLayout, textLen, renderTarget, fontCollection); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -419,9 +418,8 @@ static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) | |||||
const int numCharacterAttributes = text.getNumAttributes(); | const int numCharacterAttributes = text.getNumAttributes(); | ||||
for (int i = 0; i < numCharacterAttributes; ++i) | for (int i = 0; i < numCharacterAttributes; ++i) | ||||
if (const Font* const font = text.getAttribute (i)->getFont()) | |||||
if (dynamic_cast<WindowsDirectWriteTypeface*> (font->getTypeface()) == nullptr) | |||||
return false; | |||||
if (dynamic_cast<WindowsDirectWriteTypeface*> (text.getAttribute(i).font.getTypeface()) == nullptr) | |||||
return false; | |||||
return true; | return true; | ||||
} | } | ||||