Compiles, but I obviously have no idea how it works yet. Yes, eventually we should factor out common parts from the iOS and MacOSX code.
482 lines
15 KiB
C++
482 lines
15 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2000, 2010 Oracle and/or its affiliates.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
#include "ios/common.h"
|
|
#include "ios/salcoretextstyle.hxx"
|
|
#include "ios/salcoretextlayout.hxx"
|
|
#include "ios/salgdi.h"
|
|
|
|
|
|
CoreTextLayout::CoreTextLayout(IosSalGraphics* graphics, CoreTextStyleInfo* style) :
|
|
m_graphics(graphics),
|
|
m_style(style),
|
|
m_glyphs_count(-1),
|
|
m_chars_count(-1),
|
|
m_chars2glyphs(NULL),
|
|
m_glyphs2chars(NULL),
|
|
m_glyphs(NULL),
|
|
m_char_widths(NULL),
|
|
m_glyph_advances(NULL),
|
|
m_glyph_positions(NULL),
|
|
m_typesetter(NULL),
|
|
m_line(NULL),
|
|
m_has_bound_rec(false),
|
|
m_base_advance(0),
|
|
m_cached_width(0.0F),
|
|
m_current_run_index(0),
|
|
m_current_glyph_index(0),
|
|
m_current_glyphrun_index(0),
|
|
m_runs(NULL)
|
|
{
|
|
}
|
|
|
|
CoreTextLayout::~CoreTextLayout()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
void CoreTextLayout::AdjustLayout( ImplLayoutArgs& /*rArgs*/ )
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
msgs_debug(layout,"<--");
|
|
/* TODO */
|
|
}
|
|
|
|
void CoreTextLayout::Clean()
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
if (m_glyphs)
|
|
{
|
|
delete[] m_glyphs;
|
|
m_glyphs = NULL;
|
|
}
|
|
if (m_chars2glyphs)
|
|
{
|
|
delete[] m_chars2glyphs;
|
|
m_chars2glyphs = NULL;
|
|
}
|
|
if (m_glyphs2chars)
|
|
{
|
|
delete[] m_glyphs2chars;
|
|
m_glyphs2chars = NULL;
|
|
}
|
|
if (m_char_widths)
|
|
{
|
|
delete[] m_char_widths;
|
|
m_char_widths = NULL;
|
|
}
|
|
if (m_glyph_advances)
|
|
{
|
|
delete[] m_glyph_advances;
|
|
m_glyph_advances = NULL;
|
|
}
|
|
if (m_glyph_positions)
|
|
{
|
|
delete[] m_glyph_positions;
|
|
m_glyph_positions = NULL;
|
|
}
|
|
SafeCFRelease(m_typesetter);
|
|
SafeCFRelease(m_line);
|
|
m_has_bound_rec = false;
|
|
msgs_debug(layout,"<--");
|
|
}
|
|
|
|
void CoreTextLayout::DrawText( SalGraphics& rGraphics ) const
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
IosSalGraphics& gr = static_cast<IosSalGraphics&>(rGraphics);
|
|
if (m_chars_count <= 0 || !gr.CheckContext())
|
|
{
|
|
return;
|
|
}
|
|
CGContextSaveGState( gr.mrContext );
|
|
Point pos = GetDrawPosition(Point(0,0));
|
|
#if 0
|
|
msgs_debug(layout,"at pos (%ld, %ld)", 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(m_line, gr.mrContext);
|
|
#else
|
|
InitGIA();
|
|
msgs_debug(layout,"at- pos (%ld, %ld)", pos.X(), pos.Y());
|
|
CGFontRef cg_font = CTFontCopyGraphicsFont(m_style->GetFont(), NULL);
|
|
CGContextSetFont(gr.mrContext, cg_font);
|
|
CGContextSetFontSize(gr.mrContext, CTFontGetSize(m_style->GetFont()));
|
|
CGContextSetTextDrawingMode(gr.mrContext, kCGTextFill);
|
|
CGContextSetShouldAntialias( gr.mrContext, true );
|
|
if (m_style->GetColor())
|
|
{
|
|
CGContextSetFillColorWithColor(gr.mrContext, m_style->GetColor());
|
|
CGContextSetStrokeColorWithColor(gr.mrContext, m_style->GetColor());
|
|
}
|
|
else
|
|
{
|
|
CGContextSetRGBFillColor(gr.mrContext, 0.0, 0.0, 0.0, 1.0);
|
|
}
|
|
CFRelease(cg_font);
|
|
// CGContextSetTextPosition(gr.mrContext, pos.X(), pos.Y());
|
|
CGContextSetTextMatrix(gr.mrContext, CGAffineTransformMakeScale(1.0, -1.0));
|
|
CGContextSetShouldAntialias( gr.mrContext, !gr.mbNonAntialiasedText );
|
|
CGContextTranslateCTM(gr.mrContext, pos.X(), pos.Y());
|
|
// for(int i = 0; i < m_glyphs_count ; ++i)
|
|
// {
|
|
// msgs_debug(layout,"m_glyph=%p m_glyph_positions=%p count=%d", m_glyphs, m_glyph_positions, m_glyphs_count);
|
|
// msgs_debug(layout,"glyph[%d]=0x%x position(%g,%g)", i, m_glyphs[i], m_glyph_positions[i].x, m_glyph_positions[i].y);
|
|
CGContextShowGlyphs(gr.mrContext, m_glyphs, m_glyphs_count);
|
|
// CGContextShowGlyphsAtPositions(gr.mrContext, m_glyphs, m_glyph_positions, m_glyphs_count);
|
|
// CGContextShowGlyphsWidthAdvances(gr.mrContext, m_glyphs, m_glyph_advances, m_glyphs_count);
|
|
|
|
// CGContextShowGlyphsAtPoint(gr.mrContext, pos.X(), pos.Y(), m_glyphs, m_glyphs_count);
|
|
// }
|
|
#endif
|
|
// restore the original graphic context transformations
|
|
CGContextRestoreGState( gr.mrContext );
|
|
msgs_debug(layout,"<--");
|
|
|
|
}
|
|
|
|
// not needed. CoreText manage fallback directly
|
|
void CoreTextLayout::DropGlyph( int /*nStart*/ ) {}
|
|
|
|
long CoreTextLayout::FillDXArray( long* pDXArray ) const
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
// short circuit requests which don't need full details
|
|
if ( !pDXArray )
|
|
{
|
|
return GetTextWidth();
|
|
}
|
|
// check assumptions
|
|
DBG_ASSERT( !mnTrailingSpaceWidth, "CoreText::FillDXArray() with nTSW!=0" );
|
|
|
|
// initialize details about the resulting layout
|
|
InitGIA();
|
|
|
|
// distribute the widths among the string elements
|
|
long width = 0;
|
|
float scale = m_style->GetFontStretchFactor();
|
|
m_cached_width = 0;
|
|
|
|
for( int i = 0; i < m_chars_count; ++i )
|
|
{
|
|
// convert and adjust for accumulated rounding errors
|
|
m_cached_width += m_char_widths[i];
|
|
const long old_width = width;
|
|
width = round_to_long(m_cached_width * scale);
|
|
pDXArray[i] = width - old_width;
|
|
}
|
|
msgs_debug(layout," w=%ld <--", width);
|
|
return width;
|
|
}
|
|
|
|
bool CoreTextLayout::GetBoundRect( SalGraphics &rGraphics, Rectangle& rVCLRect ) const
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
IosSalGraphics& gr = static_cast<IosSalGraphics&>(rGraphics);
|
|
if ( !m_has_bound_rec )
|
|
{
|
|
CGRect bound_rect = CTLineGetImageBounds( m_line, gr.mrContext );
|
|
if ( !CGRectIsNull( bound_rect ) )
|
|
{
|
|
m_bound_rect = Rectangle(
|
|
Point( round_to_long(bound_rect.origin.x * m_style->GetFontStretchFactor()),
|
|
round_to_long(bound_rect.origin.y - bound_rect.size.height )),
|
|
Size( round_to_long(bound_rect.size.width * m_style->GetFontStretchFactor()), round_to_long(bound_rect.size.height)));
|
|
m_bound_rect.Justify();
|
|
}
|
|
m_has_bound_rec = true;
|
|
}
|
|
rVCLRect = m_bound_rect;
|
|
msgs_debug(layout,"<--");
|
|
return true;
|
|
}
|
|
|
|
void CoreTextLayout::GetCaretPositions( int max_index, long* caret_position) const
|
|
{
|
|
msgs_debug(layout,"max_index %d -->", max_index);
|
|
int local_max = max_index < m_chars_count * 2 ? max_index : m_chars_count;
|
|
for(int i = 0 ; i < max_index - 1; i+=2)
|
|
{
|
|
CGFloat primary, secondary;
|
|
primary = CTLineGetOffsetForStringIndex(m_line, i >> 1, &secondary);
|
|
caret_position[i] = round_to_long(m_base_advance + primary);
|
|
caret_position[i+1] = round_to_long(m_base_advance + secondary);
|
|
i += 2;
|
|
}
|
|
for(int i = local_max ; i < max_index ; ++i)
|
|
{
|
|
caret_position[i] = -1;
|
|
}
|
|
msgs_debug(layout,"<--");
|
|
}
|
|
|
|
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
|
|
{
|
|
msgs_debug(layout,"nLen=%d nStart=%d-->", nLen, nStart);
|
|
// get glyph measurements
|
|
InitGIA();
|
|
|
|
if ( nStart < 0 ) // first glyph requested?
|
|
{
|
|
nStart = 0;
|
|
m_current_run_index = 0;
|
|
m_current_glyph_index = 0;
|
|
m_current_glyphrun_index = 0;
|
|
}
|
|
else if (nStart >= m_glyphs_count)
|
|
{
|
|
m_current_run_index = 0;
|
|
m_current_glyph_index = 0;
|
|
m_current_glyphrun_index = 0;
|
|
return 0;
|
|
}
|
|
if (!m_runs)
|
|
{
|
|
m_runs = CTLineGetGlyphRuns(m_line);
|
|
}
|
|
CFIndex nb_runs = CFArrayGetCount( m_runs );
|
|
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex( m_runs, m_current_run_index );
|
|
CFIndex nb_glyphs = CTRunGetGlyphCount( run );
|
|
|
|
int i = 0;
|
|
bool first = true;
|
|
while(i < nLen)
|
|
{
|
|
if (m_current_glyphrun_index >= nb_glyphs)
|
|
{
|
|
m_current_run_index += 1;
|
|
if (m_current_run_index >= nb_runs)
|
|
{
|
|
break;
|
|
}
|
|
run = (CTRunRef)CFArrayGetValueAtIndex( m_runs, m_current_run_index );
|
|
nb_glyphs = CTRunGetGlyphCount( run );
|
|
m_current_glyphrun_index = 0;
|
|
}
|
|
if (first)
|
|
{
|
|
CGPoint first_pos;
|
|
CTRunGetPositions(run, CFRangeMake(m_current_glyphrun_index,1), &first_pos);
|
|
Point pos(first_pos.x, first_pos.y);
|
|
rPos = GetDrawPosition(pos);
|
|
msgs_debug(layout,"rPos(%ld, %ld)", rPos.X(),rPos.Y());
|
|
first = false;
|
|
}
|
|
pGlyphIDs[i] = m_glyphs[m_current_glyph_index];
|
|
if (pGlyphAdvances)
|
|
{
|
|
pGlyphAdvances[i] = m_glyph_advances[m_current_glyph_index];
|
|
}
|
|
if (pCharIndexes)
|
|
{
|
|
pCharIndexes[i] = m_glyphs2chars[m_current_glyph_index];
|
|
}
|
|
m_current_glyph_index += 1;
|
|
m_current_glyphrun_index += 1;
|
|
i += 1;
|
|
nStart += 1;
|
|
}
|
|
msgs_debug(layout,"i=%d <--", i);
|
|
return i;
|
|
}
|
|
|
|
int CoreTextLayout::GetTextBreak( long /*nMaxWidth*/, long /*nCharExtra*/, int /*nFactor*/ ) const
|
|
{
|
|
/* TODO */
|
|
return false;
|
|
}
|
|
|
|
long CoreTextLayout::GetTextWidth() const
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
|
|
CGRect bound_rect = CTLineGetImageBounds(m_line, m_graphics->GetContext());
|
|
long w = round_to_long(bound_rect.size.width * m_style->GetFontStretchFactor());
|
|
msgs_debug(layout,"w=%ld <--", w);
|
|
return w;
|
|
}
|
|
|
|
// not needed. CoreText manage fallback directly
|
|
void CoreTextLayout::InitFont() const
|
|
{
|
|
}
|
|
|
|
bool CoreTextLayout::InitGIA() const
|
|
{
|
|
msgs_debug(layout,"count=%d <--", m_chars_count);
|
|
|
|
if ( m_chars_count <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
if (m_glyphs)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
m_glyphs = new CGGlyph[m_glyphs_count];
|
|
m_char_widths = new int[ m_chars_count ];
|
|
m_chars2glyphs = new int[ m_chars_count ];
|
|
for( int i = 0; i < m_chars_count; ++i)
|
|
{
|
|
m_char_widths[i] = 0.0;
|
|
m_chars2glyphs[i] = -1;
|
|
}
|
|
m_glyphs2chars = new int[m_glyphs_count];
|
|
m_glyph_advances = new int[m_glyphs_count];
|
|
m_glyph_positions = new CGPoint[m_glyphs_count];
|
|
|
|
|
|
CFArrayRef runs = CTLineGetGlyphRuns( m_line );
|
|
CFIndex nb_runs = CFArrayGetCount( runs );
|
|
int p = 0;
|
|
for( CFIndex i = 0; i < nb_runs; ++i )
|
|
{
|
|
CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex( runs, i );
|
|
if ( run )
|
|
{
|
|
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 ), &m_glyph_positions[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 )
|
|
{
|
|
m_glyphs[ p ] = glyphs[ j ];
|
|
CFIndex k = indices[ j ];
|
|
m_glyphs2chars[p] = k;
|
|
m_chars2glyphs[k] = p;
|
|
|
|
if ( j < nb_glyphs - 1 )
|
|
{
|
|
m_char_widths[ k ] += m_glyph_positions[ p + 1 ].x - m_glyph_positions[ p ].x;
|
|
}
|
|
if ( p > 0)
|
|
{
|
|
m_glyph_advances[p - 1] = m_glyph_positions[ p ].x - m_glyph_positions[p - 1].x;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
msgs_debug(layout,"<--");
|
|
return true;
|
|
}
|
|
|
|
bool CoreTextLayout::LayoutText(ImplLayoutArgs& args)
|
|
{
|
|
msgs_debug(layout,"-->");
|
|
Clean();
|
|
m_style->SetColor();
|
|
/* retreive MinCharPos EndCharPos Flags and Orientation */
|
|
SalLayout::AdjustLayout(args);
|
|
m_chars_count = mnEndCharPos - mnMinCharPos;
|
|
|
|
/* don't layout emptty (or worse negative size) strings */
|
|
if (m_chars_count <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
/* c0 and c1 are construction objects */
|
|
CFStringRef c0 = CFStringCreateWithCharactersNoCopy( NULL, &(args.mpStr[args.mnMinCharPos]), m_chars_count, kCFAllocatorNull );
|
|
if ( !c0 )
|
|
{
|
|
Clean();
|
|
return false;
|
|
}
|
|
|
|
CFStringRef keys[6];
|
|
CFTypeRef values[6];
|
|
int nb_attributes = 0;
|
|
|
|
keys[nb_attributes]= kCTFontAttributeName;
|
|
values[nb_attributes] = m_style->GetFont();
|
|
nb_attributes += 1;
|
|
|
|
CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault,
|
|
(const void**)&keys,
|
|
(const void**)&values,
|
|
nb_attributes,
|
|
&kCFTypeDictionaryKeyCallBacks,
|
|
&kCFTypeDictionaryValueCallBacks);
|
|
|
|
|
|
CFAttributedStringRef string = CFAttributedStringCreate( NULL, c0, attributes );
|
|
CFRelease( c0 );
|
|
CFRelease( attributes );
|
|
if ( !string )
|
|
{
|
|
Clean();
|
|
return false;
|
|
}
|
|
m_typesetter = CTTypesetterCreateWithAttributedString(string);
|
|
CFRelease(string);
|
|
if (!m_typesetter)
|
|
{
|
|
Clean();
|
|
return false;
|
|
}
|
|
m_line = CTTypesetterCreateLine(m_typesetter, CFRangeMake(0, 0));
|
|
if (!m_line)
|
|
{
|
|
Clean();
|
|
return false;
|
|
}
|
|
m_glyphs_count = CTLineGetGlyphCount(m_line);
|
|
|
|
msgs_debug(layout,"glyph_count=%d <--", m_glyphs_count);
|
|
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*/ ) {}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|