Commit 6be27ae2 authored by Maciej Suminski's avatar Maciej Suminski

Glyphs and their bounding boxes are held in vectors instead of deque.

Moved scaling of font glyphs to the moment when they are created (eliminated a few multiplications unnecessary variables).
Changed some magic numbers into constants.
parent ca11855f
......@@ -30,9 +30,9 @@
using namespace KIGFX;
const double STROKE_FONT::LINE_HEIGHT_RATIO = 1.6;
const double STROKE_FONT::OVERBAR_HEIGHT = 0.45;
const double STROKE_FONT::BOLD_FACTOR = 1.3;
const double STROKE_FONT::HERSHEY_SCALE = 1.0 / 21.0;
STROKE_FONT::STROKE_FONT( GAL* aGal ) :
m_gal( aGal ),
......@@ -41,7 +41,6 @@ STROKE_FONT::STROKE_FONT( GAL* aGal ) :
m_mirrored( false )
{
// Default values
m_scaleFactor = 1.0 / 21.0;
m_glyphSize = VECTOR2D( 10.0, 10.0 );
m_verticalJustify = GR_TEXT_VJUSTIFY_BOTTOM;
m_horizontalJustify = GR_TEXT_HJUSTIFY_LEFT;
......@@ -57,6 +56,8 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe
{
m_glyphs.clear();
m_glyphBoundingBoxes.clear();
m_glyphs.resize( aNewStrokeFontSize );
m_glyphBoundingBoxes.resize( aNewStrokeFontSize );
for( int j = 0; j < aNewStrokeFontSize; j++ )
{
......@@ -82,8 +83,8 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe
if( i < 2 )
{
// The first two values contain the width of the char
glyphStartX = coordinate[0] - 'R';
glyphEndX = coordinate[1] - 'R';
glyphStartX = ( coordinate[0] - 'R' ) * HERSHEY_SCALE;
glyphEndX = ( coordinate[1] - 'R' ) * HERSHEY_SCALE;
glyphBoundingX = VECTOR2D( 0, glyphEndX - glyphStartX );
}
else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) )
......@@ -98,8 +99,8 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe
{
// Every coordinate description of the Hershey format has an offset,
// it has to be subtracted
point.x = (double) ( coordinate[0] - 'R' ) - glyphStartX;
point.y = (double) ( coordinate[1] - 'R' ) - 11.0;
point.x = (double) ( coordinate[0] - 'R' ) * HERSHEY_SCALE - glyphStartX;
point.y = (double) ( coordinate[1] - 'R' ) * HERSHEY_SCALE;
pointList.push_back( point );
}
......@@ -109,16 +110,22 @@ bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNe
if( pointList.size() > 0 )
glyph.push_back( pointList );
m_glyphs.push_back( glyph );
m_glyphs[j] = glyph;
// Compute the bounding box of the glyph
m_glyphBoundingBoxes.push_back( computeBoundingBox( glyph, glyphBoundingX ) );
m_glyphBoundingBoxes[j] = computeBoundingBox( glyph, glyphBoundingX );
}
return true;
}
int STROKE_FONT::getInterline() const
{
return ( m_glyphSize.y * 14 ) / 10 + m_gal->GetLineWidth();
}
BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLYPHBoundingX ) const
{
BOX2D boundingBox;
......@@ -145,115 +152,134 @@ BOX2D STROKE_FONT::computeBoundingBox( const GLYPH& aGLYPH, const VECTOR2D& aGLY
void STROKE_FONT::Draw( wxString aText, const VECTOR2D& aPosition, double aRotationAngle )
{
// By default overbar is turned off
m_overbar = false;
// Context needs to be saved before any transformations
m_gal->Save();
m_gal->Translate( aPosition );
// Single line height
int lineHeight = getInterline();
// The overall height of all lines of text
double textBlockHeight = lineHeight * ( linesCount( aText ) - 1 );
switch( m_verticalJustify )
{
case GR_TEXT_VJUSTIFY_CENTER:
m_gal->Translate( VECTOR2D( 0, -textBlockHeight / 2.0 ) );
break;
case GR_TEXT_VJUSTIFY_BOTTOM:
m_gal->Translate( VECTOR2D( 0, -textBlockHeight ) );
break;
case GR_TEXT_VJUSTIFY_TOP:
break;
default:
break;
}
m_gal->Rotate( -aRotationAngle );
m_gal->SetIsStroke( true );
m_gal->SetIsFill( false );
if( m_bold )
m_gal->SetLineWidth( m_gal->GetLineWidth() * BOLD_FACTOR );
// Split multiline strings into separate ones and draw them line by line
int newlinePos = aText.Find( '\n' );
int begin = 0;
int newlinePos = aText.find( '\n' );
if( newlinePos != wxNOT_FOUND )
while( newlinePos != wxNOT_FOUND )
{
VECTOR2D nextlinePosition = VECTOR2D( 0.0, m_glyphSize.y * LINE_HEIGHT_RATIO );
size_t length = newlinePos - begin;
drawSingleLineText( aText.Mid( begin, length ) );
m_gal->Translate( VECTOR2D( 0.0, lineHeight ) );
Draw( aText.Mid( newlinePos + 1 ), nextlinePosition, 0.0 );
aText = aText.Mid( 0, newlinePos );
begin = newlinePos + 1;
newlinePos = aText.find( '\n', begin + 1 );
}
// Draw the last (or the only one) line
drawSingleLineText( aText.Mid( begin ) );
m_gal->Restore();
}
void STROKE_FONT::drawSingleLineText( const wxString& aText )
{
// By default the overbar is turned off
m_overbar = false;
double xOffset;
VECTOR2D glyphSize( m_glyphSize );
// Compute the text size
VECTOR2D textsize = computeTextSize( aText );
VECTOR2D textSize = computeTextSize( aText );
m_gal->Save();
// Adjust the text position to the given alignment
switch( m_horizontalJustify )
{
case GR_TEXT_HJUSTIFY_CENTER:
m_gal->Translate( VECTOR2D( -textsize.x / 2.0, 0 ) );
m_gal->Translate( VECTOR2D( -textSize.x / 2.0, 0 ) );
break;
case GR_TEXT_HJUSTIFY_RIGHT:
if( !m_mirrored )
m_gal->Translate( VECTOR2D( -textsize.x, 0 ) );
m_gal->Translate( VECTOR2D( -textSize.x, 0 ) );
break;
case GR_TEXT_HJUSTIFY_LEFT:
if( m_mirrored )
m_gal->Translate( VECTOR2D( -textsize.x, 0 ) );
break;
default:
break;
}
switch( m_verticalJustify )
{
case GR_TEXT_VJUSTIFY_CENTER:
m_gal->Translate( VECTOR2D( 0, textsize.y / 2.0 ) );
break;
case GR_TEXT_VJUSTIFY_TOP:
m_gal->Translate( VECTOR2D( 0, textsize.y ) );
break;
case GR_TEXT_VJUSTIFY_BOTTOM:
m_gal->Translate( VECTOR2D( -textSize.x, 0 ) );
break;
default:
break;
}
double xOffset, glyphSizeX;
if( m_mirrored )
{
// In case of mirrored text invert the X scale of points and their X direction
// (m_glyphSize.x) and start drawing from the position where text normally should end
// (textsize.x)
xOffset = textsize.x;
glyphSizeX = -m_glyphSize.x;
}
else
// (textSize.x)
xOffset = textSize.x;
glyphSize.x = -m_glyphSize.x;
} else
{
xOffset = 0.0;
glyphSizeX = m_glyphSize.x;
}
double scaleY = m_scaleFactor * m_glyphSize.y;
double scaleX = m_scaleFactor * glyphSizeX;
m_gal->SetIsStroke( true );
m_gal->SetIsFill( false );
if( m_bold )
{
m_gal->SetLineWidth( m_gal->GetLineWidth() * 1.3 );
}
for( wxString::const_iterator chIt = aText.begin(); chIt != aText.end(); chIt++ )
{
// Toggle overbar
if( *chIt == '~' )
{
m_overbar = !m_overbar;
continue;
}
GLYPH_LIST::iterator glyphIt = m_glyphs.begin();
std::deque<BOX2D>::iterator bbIt = m_glyphBoundingBoxes.begin();
unsigned dd = *chIt - ' ';
if( dd >= m_glyphBoundingBoxes.size() )
if( dd >= m_glyphBoundingBoxes.size() || dd < 0 )
dd = '?' - ' ';
advance( glyphIt, dd );
advance( bbIt, dd );
GLYPH& glyph = m_glyphs[dd];
BOX2D& bbox = m_glyphBoundingBoxes[dd];
GLYPH glyph = *glyphIt;
if( m_overbar )
{
VECTOR2D startOverbar( xOffset, -getInterline() * OVERBAR_HEIGHT );
VECTOR2D endOverbar( xOffset + glyphSize.x * bbox.GetEnd().x,
-getInterline() * OVERBAR_HEIGHT );
m_gal->DrawLine( startOverbar, endOverbar );
}
for( GLYPH::iterator pointListIt = glyph.begin(); pointListIt != glyph.end();
pointListIt++ )
......@@ -263,7 +289,7 @@ void STROKE_FONT::Draw( wxString aText, const VECTOR2D& aPosition, double aRotat
for( std::deque<VECTOR2D>::iterator pointIt = pointListIt->begin();
pointIt != pointListIt->end(); pointIt++ )
{
VECTOR2D pointPos( pointIt->x * scaleX + xOffset, pointIt->y * scaleY );
VECTOR2D pointPos( pointIt->x * glyphSize.x + xOffset, pointIt->y * glyphSize.y );
if( m_italic )
{
......@@ -278,15 +304,7 @@ void STROKE_FONT::Draw( wxString aText, const VECTOR2D& aPosition, double aRotat
m_gal->DrawPolyline( pointListScaled );
}
if( m_overbar )
{
VECTOR2D startOverbar( xOffset, -textsize.y * 1.2 );
VECTOR2D endOverbar( xOffset + m_scaleFactor * glyphSizeX * bbIt->GetEnd().x,
-textsize.y * 1.2 );
m_gal->DrawLine( startOverbar, endOverbar );
}
xOffset += m_scaleFactor * glyphSizeX * bbIt->GetEnd().x;
xOffset += glyphSize.x * bbox.GetEnd().x;
}
m_gal->Restore();
......@@ -299,18 +317,19 @@ VECTOR2D STROKE_FONT::computeTextSize( const wxString& aText ) const
for( wxString::const_iterator chIt = aText.begin(); chIt != aText.end(); chIt++ )
{
wxASSERT_MSG( *chIt != '\n',
wxT( "This function is intended to work with single line strings" ) );
if( *chIt == '~' )
continue;
std::deque<BOX2D>::const_iterator bbIt = m_glyphBoundingBoxes.begin();
// Index in the bounding boxes table
unsigned dd = *chIt - ' ';
if( dd >= m_glyphBoundingBoxes.size() )
if( dd >= m_glyphBoundingBoxes.size() || dd < 0 )
dd = '?' - ' ';
advance( bbIt, dd );
result.x += m_scaleFactor * m_glyphSize.x * bbIt->GetEnd().x;
result.x += m_glyphSize.x * m_glyphBoundingBoxes[dd].GetEnd().x;
}
return result;
......
......@@ -39,7 +39,7 @@ namespace KIGFX
class GAL;
typedef std::deque< std::deque<VECTOR2D> > GLYPH;
typedef std::deque<GLYPH> GLYPH_LIST;
typedef std::vector<GLYPH> GLYPH_LIST;
/**
* @brief Class STROKE_FONT implements stroke font drawing.
......@@ -55,8 +55,6 @@ public:
/// Destructor
~STROKE_FONT();
// TODO Load font from a text file
/**
* @brief Load the new stroke font.
*
......@@ -75,16 +73,6 @@ public:
*/
void Draw( wxString aText, const VECTOR2D& aPosition, double aRotationAngle );
/**
* @brief Set the scale factor of the font for the glyph size.
*
* @param aScaleFactor is the scale factor of the font.
*/
inline void SetScaleFactor( const double aScaleFactor )
{
m_scaleFactor = aScaleFactor;
}
/**
* @brief Set the glyph size.
*
......@@ -158,13 +146,19 @@ public:
private:
GAL* m_gal; ///< Pointer to the GAL
GLYPH_LIST m_glyphs; ///< Glyph list
std::deque<BOX2D> m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs
double m_scaleFactor; ///< Scale factor for the glyph
std::vector<BOX2D> m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs
VECTOR2D m_glyphSize; ///< Size of the glyphs
EDA_TEXT_HJUSTIFY_T m_horizontalJustify; ///< Horizontal justification
EDA_TEXT_VJUSTIFY_T m_verticalJustify; ///< Vertical justification
bool m_bold, m_italic, m_mirrored, m_overbar; ///< Properties of text
/**
* @brief Returns a single line height using current settings.
*
* @return The line height.
*/
int getInterline() const;
/**
* @brief Compute the bounding box of a given glyph.
*
......@@ -174,6 +168,14 @@ private:
*/
BOX2D computeBoundingBox( const GLYPH& aGlyph, const VECTOR2D& aGlyphBoundingX ) const;
/**
* @brief Draws a single line of text. Multiline texts should be split before using the
* function.
*
* @param aText is the text to be drawn.
*/
void drawSingleLineText( const wxString& aText );
/**
* @brief Compute the size of a given text.
*
......@@ -182,7 +184,34 @@ private:
*/
VECTOR2D computeTextSize( const wxString& aText ) const;
static const double LINE_HEIGHT_RATIO;
/**
* @brief Returns number of lines for a given text.
*
* @param aText is the text to be checked.
* @return Number of lines of aText.
*/
unsigned int linesCount( const wxString& aText ) const
{
wxString::const_iterator it, itEnd;
unsigned int lines = 1;
for( it = aText.begin(), itEnd = aText.end(); it != itEnd; ++it )
{
if( *it == '\n' )
++lines;
}
return lines;
}
///> Factor that determines relative height of overbar.
static const double OVERBAR_HEIGHT;
///> Factor that determines relative line width for bold text.
static const double BOLD_FACTOR;
///> Scale factor for the glyph
static const double HERSHEY_SCALE;
};
} // namespace KIGFX
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment