office-gobmx/vcl/coretext/salcoretextlayout.cxx
Tor Lillqvist 91a3723922 Remove unnecessary code that was based on a wrong guess
Change-Id: Ia3e12d68c0eef56ba32b2c6062448874a52a8df3
2013-03-26 19:06:08 +02:00

646 lines
23 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <iostream>
#include <iomanip>
#include "coretext/common.h"
#include "coretext/salcoretextstyle.hxx"
#ifdef MACOSX
#include "coretext/salgdi.h"
#else
#include "headless/svpgdi.hxx"
#endif
class CoreTextLayout : public SalLayout
{
public:
CoreTextLayout( QuartzSalGraphics* graphics, CoreTextStyleInfo* style);
virtual ~CoreTextLayout();
virtual void AdjustLayout( ImplLayoutArgs& );
virtual void DrawText( SalGraphics& ) const;
virtual void DropGlyph( int nStart );
virtual long FillDXArray( sal_Int32* pDXArray ) const;
virtual bool GetBoundRect( SalGraphics&, Rectangle& ) const;
virtual void GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const;
virtual bool GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const;
virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
virtual long GetTextWidth() const;
virtual void InitFont() const;
virtual bool LayoutText( ImplLayoutArgs& );
virtual void MoveGlyph( int nStart, long nNewXPos );
virtual void Simplify( bool bIsBase );
private:
void InvalidateMeasurements();
bool InitGIA( ImplLayoutArgs &rArgs ) const;
QuartzSalGraphics* mpGraphics;
CoreTextStyleInfo* mpStyle;
int mnCharCount; // ==mnEndCharPos-mnMinCharPos
// cached details about the resulting layout
// mutable members since these details are all lazy initialized
mutable int mnGlyphCount; // glyph count
mutable CGGlyph* mpGlyphs; // glyphs
mutable int* mpCharWidths; // map relative charpos to charwidth
mutable int* mpChars2Glyphs; // map relative charpos to absolute glyphpos
mutable int* mpGlyphs2Chars; // map absolute glyphpos to absolute charpos
mutable int* mpGlyphAdvances; // glyph widths for the justified layout
mutable CGPoint* mpGlyphPositions;
mutable CTTypesetterRef mpTypesetter;
mutable CTLineRef mpLine;
mutable bool mbHasBoundRectangle;
mutable Rectangle maBoundRectangle;
// x-offset relative to layout origin
// currently only used in RTL-layouts
CGFloat mnBaseAdvance;
mutable CFIndex mnCurrentRunIndex;
mutable CFIndex mnCurrentGlyphIndex;
mutable CFIndex mnCurrentGlyphRunIndex;
mutable CFArrayRef mpRuns;
};
CoreTextLayout::CoreTextLayout(QuartzSalGraphics* graphics, CoreTextStyleInfo* style) :
mpGraphics(graphics),
mpStyle(style),
mnCharCount(-1),
mnGlyphCount(-1),
mpGlyphs(NULL),
mpCharWidths(NULL),
mpChars2Glyphs(NULL),
mpGlyphs2Chars(NULL),
mpGlyphAdvances(NULL),
mpGlyphPositions(NULL),
mpTypesetter(NULL),
mpLine(NULL),
mbHasBoundRectangle(false),
mnBaseAdvance(0),
mnCurrentRunIndex(0),
mnCurrentGlyphIndex(0),
mnCurrentGlyphRunIndex(0),
mpRuns(NULL)
{
SAL_INFO( "vcl.coretext.layout", "CoreTextLayout::CoreTextLayout() " << this );
}
CoreTextLayout::~CoreTextLayout()
{
InvalidateMeasurements();
SAL_INFO( "vcl.coretext.layout", "~CoreTextLayout(" << this << ")" );
}
std::ostream &operator <<(std::ostream& s, ImplLayoutArgs &rArgs)
{
#ifndef SAL_LOG_INFO
(void) rArgs;
return s;
#else
s << "ImplLayoutArgs{";
s << "Flags=";
if (rArgs.mnFlags == 0)
s << 0;
else {
bool need_or = false;
s << "{";
#define TEST(x) if (rArgs.mnFlags & SAL_LAYOUT_##x) { if (need_or) s << "|"; s << #x; need_or = true; }
TEST(BIDI_RTL);
TEST(BIDI_STRONG);
TEST(RIGHT_ALIGN);
TEST(KERNING_PAIRS);
TEST(KERNING_ASIAN);
TEST(VERTICAL);
TEST(COMPLEX_DISABLED);
TEST(ENABLE_LIGATURES);
TEST(SUBSTITUTE_DIGITS);
TEST(KASHIDA_JUSTIFICATON);
TEST(DISABLE_GLYPH_PROCESSING);
TEST(FOR_FALLBACK);
#undef TEST
s << "}";
}
s << ",Length=" << rArgs.mnLength;
s << ",MinCharPos=" << rArgs.mnMinCharPos;
s << ",EndCharPos=" << rArgs.mnEndCharPos;
s << ",Str=\"";
int lim = rArgs.mnLength;
if (lim > 10)
lim = 7;
for (int i = 0; i < lim; i++) {
if (rArgs.mpStr[i] == '\n')
s << "\\n";
else if (rArgs.mpStr[i] < ' ' || (rArgs.mpStr[i] >= 0x7F && rArgs.mpStr[i] <= 0xFF))
s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << (int) rArgs.mpStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
else if (rArgs.mpStr[i] < 0x7F)
s << (char) rArgs.mpStr[i];
else
s << "\\u" << std::hex << std::setw(4) << std::setfill('0') << (int) rArgs.mpStr[i] << std::setfill(' ') << std::setw(1) << std::dec;
}
if (rArgs.mnLength > lim)
s << "...";
s << "\"";
s << ",LayoutWidth=" << rArgs.mnLayoutWidth;
s << "}";
return s;
#endif
}
void CoreTextLayout::AdjustLayout( ImplLayoutArgs& rArgs )
{
SAL_INFO( "vcl.coretext.layout", "AdjustLayout(" << this << ",rArgs=" << rArgs << ")" );
InvalidateMeasurements();
SalLayout::AdjustLayout( rArgs );
mnCharCount = mnEndCharPos - mnMinCharPos;
InitGIA( rArgs );
}
void CoreTextLayout::InvalidateMeasurements()
{
SAL_INFO( "vcl.coretext.layout", "InvalidateMeasurements(" << this << ")" );
if( mpGlyphs ) {
delete[] mpGlyphs;
mpGlyphs = NULL;
}
if( mpChars2Glyphs ) {
delete[] mpChars2Glyphs;
mpChars2Glyphs = NULL;
}
if( mpGlyphs2Chars ) {
delete[] mpGlyphs2Chars;
mpGlyphs2Chars = NULL;
}
if( mpCharWidths ) {
delete[] mpCharWidths;
mpCharWidths = NULL;
}
if( mpGlyphAdvances ) {
delete[] mpGlyphAdvances;
mpGlyphAdvances = NULL;
}
if( mpGlyphPositions ) {
delete[] mpGlyphPositions;
mpGlyphPositions = NULL;
}
SafeCFRelease(mpTypesetter);
SafeCFRelease(mpLine);
mbHasBoundRectangle = false;
}
void CoreTextLayout::DrawText( SalGraphics& rGraphics ) const
{
SAL_INFO( "vcl.coretext.layout", "DrawText(" << this << ")" );
QuartzSalGraphics& gr = static_cast<QuartzSalGraphics&>(rGraphics);
if( mnCharCount <= 0 || !gr.CheckContext() )
return;
CGContextSaveGState( gr.mrContext );
Point pos = GetDrawPosition(Point(0,0));
#if 0
SAL_INFO( "vcl.coretext.layout", "at pos (" << pos.X() << "," << pos.Y() << ")" );
CGContextSetTextMatrix(gr.mrContext, CGAffineTransformMakeScale(1.0, -1.0));
CGContextSetShouldAntialias( gr.mrContext, !gr.mbNonAntialiasedText );
CGContextSetTextPosition(gr.mrContext, pos.X(), pos.Y());
CTLineDraw(mpLine, gr.mrContext);
#else
SAL_INFO( "vcl.coretext.layout", "at pos (" << pos.X() << "," << pos.Y() <<") ctfont=" << mpStyle->GetFont() );
CGFontRef cg_font = CTFontCopyGraphicsFont(mpStyle->GetFont(), NULL);
if( !cg_font ) {
SAL_INFO( "vcl.coretext.layout", "Error cg_font is NULL" );
return;
}
CGContextSetFont(gr.mrContext, cg_font);
CGContextSetFontSize(gr.mrContext, CTFontGetSize(mpStyle->GetFont()));
CGContextSetTextDrawingMode(gr.mrContext, kCGTextFill);
CGContextSetShouldAntialias( gr.mrContext, true );
if( mpStyle->GetColor() ) {
CGContextSetFillColorWithColor(gr.mrContext, mpStyle->GetColor());
CGContextSetStrokeColorWithColor(gr.mrContext, mpStyle->GetColor());
}
else {
CGContextSetRGBFillColor(gr.mrContext, 0.0, 0.0, 0.0, 1.0);
}
CFRelease(cg_font);
CGContextSetTextMatrix(gr.mrContext, CGAffineTransformMakeScale(1.0, -1.0));
CGContextSetShouldAntialias( gr.mrContext, !gr.mbNonAntialiasedText );
CGContextTranslateCTM(gr.mrContext, pos.X(), pos.Y());
CGContextShowGlyphs(gr.mrContext, mpGlyphs, mnGlyphCount);
#endif
// restore the original graphic context transformations
CGContextRestoreGState( gr.mrContext );
}
// not needed. CoreText manage fallback directly
void CoreTextLayout::DropGlyph( int /*nStart*/ )
{
}
long CoreTextLayout::FillDXArray( sal_Int32* pDXArray ) const
{
SAL_INFO( "vcl.coretext.layout", "FillDXArray(" << this << ")" );
// short circuit requests which don't need full details
if( !pDXArray ) {
SAL_INFO( "vcl.coretext.layout", "FillDXArray() returning GetTextWidth()" );
return GetTextWidth();
}
// distribute the widths among the string elements
long width = 0;
float scale = mpStyle->GetFontStretchFactor();
CGFloat accumulated_width = 0;
for( int i = 0; i < mnCharCount; ++i ) {
// convert and adjust for accumulated rounding errors
accumulated_width += mpCharWidths[i];
const long old_width = width;
width = round_to_long(accumulated_width * scale);
pDXArray[i] = width - old_width;
}
SAL_INFO( "vcl.coretext.layout", "FillDXArrar() returning " << width );
return width;
}
bool CoreTextLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rVCLRect ) const
{
SAL_INFO( "vcl.coretext.layout", "GetBoundRect(" << this << ")" );
if ( !mbHasBoundRectangle ) {
QuartzSalGraphics& gr = static_cast<QuartzSalGraphics&>(rGraphics);
CGRect bound_rect = CTLineGetImageBounds( mpLine, gr.mrContext );
if ( !CGRectIsNull( bound_rect ) ) {
maBoundRectangle = Rectangle(
Point( round_to_long(bound_rect.origin.x * mpStyle->GetFontStretchFactor()),
round_to_long(bound_rect.origin.y - bound_rect.size.height )),
Size( round_to_long(bound_rect.size.width * mpStyle->GetFontStretchFactor()), round_to_long(bound_rect.size.height)));
maBoundRectangle.Justify();
}
mbHasBoundRectangle = true;
}
rVCLRect = maBoundRectangle;
SAL_INFO( "vcl.coretext.layout", "GetBoundRect() returning with rVCLRect={" << rVCLRect << "}" );
return true;
}
void CoreTextLayout::GetCaretPositions( int max_index, sal_Int32* caret_position ) const
{
SAL_INFO( "vcl.coretext.layout", "GetCaretPositions(" << this << ",max_index=" << max_index << ")" );
int local_max = max_index < mnCharCount * 2 ? max_index : mnCharCount;
for( int i = 0 ; i < max_index - 1; i+=2 ) {
CGFloat primary, secondary;
primary = CTLineGetOffsetForStringIndex(mpLine, i >> 1, &secondary);
caret_position[i] = round_to_long(mnBaseAdvance + primary);
caret_position[i+1] = round_to_long(mnBaseAdvance + secondary);
i += 2;
}
for( int i = local_max ; i < max_index ; ++i ) {
caret_position[i] = -1;
}
}
bool CoreTextLayout::GetGlyphOutlines( SalGraphics&, PolyPolyVector& ) const
{
return false;
}
int CoreTextLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIDs, Point& rPos, int& nStart,
sal_Int32* pGlyphAdvances, int* pCharIndexes ) const
{
SAL_INFO( "vcl.coretext.layout", "GetNextGlyphs(" << this << ",nLen=" << nLen << ",nStart=" << nStart << ")" );
if( nStart < 0 ) { // first glyph requested?
nStart = 0;
mnCurrentRunIndex = 0;
mnCurrentGlyphIndex = 0;
mnCurrentGlyphRunIndex = 0;
}
else if( nStart >= mnGlyphCount ) {
mnCurrentRunIndex = 0;
mnCurrentGlyphIndex = 0;
mnCurrentGlyphRunIndex = 0;
return 0;
}
if( !mpRuns ) {
mpRuns = CTLineGetGlyphRuns(mpLine);
}
CFIndex nb_runs = CFArrayGetCount( mpRuns );
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex( mpRuns, mnCurrentRunIndex );
CFIndex nb_glyphs = CTRunGetGlyphCount( run );
int i = 0;
bool first = true;
while( i < nLen ) {
if( mnCurrentGlyphRunIndex >= nb_glyphs ) {
mnCurrentRunIndex += 1;
if( mnCurrentRunIndex >= nb_runs ) {
break;
}
run = (CTRunRef)CFArrayGetValueAtIndex( mpRuns, mnCurrentRunIndex );
nb_glyphs = CTRunGetGlyphCount( run );
mnCurrentGlyphRunIndex = 0;
}
if( first ) {
CGPoint first_pos;
CTRunGetPositions(run, CFRangeMake(mnCurrentGlyphRunIndex,1), &first_pos);
Point pos(first_pos.x, first_pos.y);
rPos = GetDrawPosition(pos);
SAL_INFO( "vcl.coretext.layout", "rPos(" << rPos.X() << "," << rPos.Y() << ")" );
first = false;
}
pGlyphIDs[i] = mpGlyphs[mnCurrentGlyphIndex];
if( pGlyphAdvances ) {
pGlyphAdvances[i] = mpGlyphAdvances[mnCurrentGlyphIndex];
}
if( pCharIndexes ) {
pCharIndexes[i] = mpGlyphs2Chars[mnCurrentGlyphIndex];
}
mnCurrentGlyphIndex += 1;
mnCurrentGlyphRunIndex += 1;
i += 1;
nStart += 1;
}
SAL_INFO( "vcl.coretext.layout", "GetNextGlyphs() returning " << i );
return i;
}
int CoreTextLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
{
SAL_INFO( "vcl.coretext.layout", "GetTextBreak(" << this << ",nMaxWidth=" << nMaxWidth << ",nCharExtra=" << nCharExtra << ",nFactor=" << nFactor << ")" );
if( !mpLine ) {
SAL_INFO( "vcl.coretext.layout", "GetTextBreak() returning STRING_LEN" );
return STRING_LEN;
}
// the semantics of the legacy use case (nCharExtra!=0) cannot be mapped to ATSUBreakLine()
if( nCharExtra != 0 )
{
#if 0
// prepare the measurement by layouting and measuring the un-expanded/un-condensed text
if( !InitGIA() )
return STRING_LEN;
// TODO: use a better way than by testing each the char position
ATSUTextMeasurement nATSUSumWidth = 0;
const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nMaxWidth / nFactor );
const ATSUTextMeasurement nATSUExtraWidth = Vcl2Fixed( nCharExtra ) / nFactor;
for( int i = 0; i < mnCharCount; ++i ) {
nATSUSumWidth += mpCharWidths[i];
if( nATSUSumWidth >= nATSUMaxWidth )
return (mnMinCharPos + i);
nATSUSumWidth += nATSUExtraWidth;
if( nATSUSumWidth >= nATSUMaxWidth )
if( i+1 < mnCharCount )
return (mnMinCharPos + i);
}
return STRING_LEN;
#endif
}
// get a quick overview on what could fit
const CGFloat nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
if( nPixelWidth <= 0 ) {
SAL_INFO( "vcl.coretext.layout", "GetTextBreak(): nPixelWidth=" << nPixelWidth << ", returning mnMinCharPos=" << mnMinCharPos );
return mnMinCharPos;
}
CFIndex nBreakPos = CTTypesetterSuggestLineBreak( mpTypesetter, 0, nPixelWidth ) + mnMinCharPos;
// upper layers expect STRING_LEN if everything fits
if( nBreakPos >= mnEndCharPos ) {
SAL_INFO( "vcl.coretext.layout", "GetTextBreak(): nBreakPos=" << nBreakPos << " >= mnEndCharPos=" << mnEndCharPos << ", returning STRING_LEN" );
return STRING_LEN;
}
SAL_INFO( "vcl.coretext.layout", "GetTextBreak() returning nBreakPos=" << nBreakPos );
return nBreakPos;
}
long CoreTextLayout::GetTextWidth() const
{
CGRect bound_rect = CTLineGetImageBounds(mpLine, mpGraphics->GetContext());
long w = round_to_long(bound_rect.size.width * mpStyle->GetFontStretchFactor());
SAL_INFO( "vcl.coretext.layout", "GetTextWidth(" << this << ") returning " << w );
return w;
}
// not needed. CoreText manage fallback directly
void CoreTextLayout::InitFont() const
{
SAL_INFO( "vcl.coretext.layout", "InitFont(" << this << ")" );
}
bool CoreTextLayout::InitGIA( ImplLayoutArgs& rArgs ) const
{
SAL_INFO( "vcl.coretext.layout", "InitGIA(" << this << "): " << mnCharCount << ":" << rArgs.mnMinCharPos << "--" << mnEndCharPos );
if( mnCharCount <= 0) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): mnCharCount is non-positive, returning false" );
return false;
}
if( mpGlyphs ) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): mpGlyphs is non-NULL, returning true" );
return true;
}
// Note that unlike the ATSUI code, we store only the part of the
// buffer addressed by mnMinCharPos--mnEndCharPos. Not the whole
// buffer. I.e. all indexing of the string as referenced to by
// mpTypesetter should be relative to mnMinCharPos.
CFStringRef string = CFStringCreateWithCharacters( NULL, &(rArgs.mpStr[rArgs.mnMinCharPos]), mnCharCount );
if ( !string ) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): CFStringCreateWithCharacter() returned NULL, returning false" );
return false;
}
CFStringRef keys[6];
CFTypeRef values[6];
keys[0] = kCTFontAttributeName;
values[0] = CFRetain( mpStyle->GetFont() );
CFDictionaryRef attributes = CFDictionaryCreate( kCFAllocatorDefault,
(const void**)&keys,
(const void**)&values,
1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
CFAttributedStringRef attributed_string = CFAttributedStringCreate( NULL, string, attributes );
CFRelease( string );
CFRelease( attributes );
if ( !attributed_string ) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): CFAttributedStringCreate() returned NULL, returning false" );
return false;
}
mpTypesetter = CTTypesetterCreateWithAttributedString( attributed_string );
CFRelease( attributed_string );
if( !mpTypesetter ) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): CTTypesetterCreateWithAttributedString() returned NULL, returning false" );
return false;
}
mpLine = CTTypesetterCreateLine( mpTypesetter, CFRangeMake( 0, 0 ) );
if( !mpLine ) {
SAL_INFO( "vcl.coretext.layout", "InitGIA(): CTTypesetterCreateLine() returned NULL, returning false" );
return false;
}
mnGlyphCount = CTLineGetGlyphCount( mpLine );
SAL_INFO( "vcl.coretext.layout", "InitGIA(): CTLineGetGlyphCount() returned " << mnGlyphCount );
mpGlyphs = new CGGlyph[ mnGlyphCount ];
mpCharWidths = new int[ mnCharCount ];
mpChars2Glyphs = new int[ mnCharCount ];
for( int i = 0; i < mnCharCount; ++i) {
mpCharWidths[i] = 0.0;
mpChars2Glyphs[i] = -1;
}
mpGlyphs2Chars = new int[ mnGlyphCount ];
mpGlyphAdvances = new int[ mnGlyphCount ];
mpGlyphPositions = new CGPoint[ mnGlyphCount ];
CFArrayRef runs = CTLineGetGlyphRuns( mpLine );
CFIndex nb_runs = CFArrayGetCount( runs );
int p = 0;
for( CFIndex i = 0; i < nb_runs; ++i ) {
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex( runs, i );
if( run ) {
std::ostringstream glyph_info_line;
CFIndex nb_glyphs = CTRunGetGlyphCount( run );
if( nb_glyphs ) {
CFRange text_range = CTRunGetStringRange( run );
if( text_range.location != kCFNotFound && text_range.length > 0 ) {
CFIndex indices[ nb_glyphs ];
CGGlyph glyphs[ nb_glyphs ];
CTRunGetStringIndices( run, CFRangeMake( 0, 0 ), indices );
CTRunGetGlyphs( run, CFRangeMake( 0, 0 ), glyphs );
CTRunGetPositions( run, CFRangeMake( 0, 0 ), &mpGlyphPositions[p] );
bool is_vertical_run = false;
CFDictionaryRef aDict = CTRunGetAttributes( run );
if ( aDict ) {
const CFBooleanRef aValue = (const CFBooleanRef)CFDictionaryGetValue( aDict, kCTVerticalFormsAttributeName );
is_vertical_run = (aValue == kCFBooleanTrue) ? true : false;
}
for (CFIndex j = 0 ; j < nb_glyphs; ++p, ++j ) {
assert ( p < mnGlyphCount );
mpGlyphs[ p ] = glyphs[ j ];
#ifdef SAL_LOG_INFO
if (j < 7)
glyph_info_line << " " << glyphs[j] << "@(" << mpGlyphPositions[p].x << "," << mpGlyphPositions[p].y << ")";
else if (j == 7)
glyph_info_line << "...";
#endif
CFIndex k = indices[ j ];
mpGlyphs2Chars[p] = k;
assert( k < mnCharCount );
mpChars2Glyphs[k] = p;
if ( j < nb_glyphs - 1 )
{
mpCharWidths[ k ] += mpGlyphPositions[ p + 1 ].x - mpGlyphPositions[ p ].x;
}
if( p > 0)
{
mpGlyphAdvances[p - 1] = mpGlyphPositions[ p ].x - mpGlyphPositions[p - 1].x;
}
}
}
}
SAL_INFO( "vcl.coretext.layout", " run " << run << " glyphs:" << glyph_info_line.str() );
}
}
SAL_INFO( "vcl.coretext.layout", "InitGIA() returning normally true" );
return true;
}
bool CoreTextLayout::LayoutText( ImplLayoutArgs& rArgs)
{
SAL_INFO( "vcl.coretext.layout", "LayoutText(" << this << ",rArgs=" << rArgs << ")" );
mpStyle->SetColor();
AdjustLayout( rArgs );
/* don't layout empty (or worse negative size) strings */
if(mnCharCount <= 0) {
SAL_INFO( "vcl.coretext.layout", "LayoutText(): mnCharCount non-positive, returning false!" );
return false;
}
SAL_INFO( "vcl.coretext.layout", "LayoutText() returning, mnGlyphCount=" << mnGlyphCount );
return true;
}
// not needed. CoreText manage fallback directly
void CoreTextLayout::MoveGlyph( int /*nStart*/, long /*nNewXPos*/ )
{
}
// not needed. CoreText manage fallback directly
void CoreTextLayout::Simplify( bool /*bIsBase*/ )
{
}
SalLayout* QuartzSalGraphics::GetTextLayout( ImplLayoutArgs&, int /*nFallbackLevel*/ )
{
CoreTextLayout* layout = new CoreTextLayout( this, m_style );
return layout;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */