39 changed files with 2009 additions and 1293 deletions
-
3common/CMakeLists.txt
-
29common/basic_gal.cpp
-
27common/drawing_sheet/ds_painter.cpp
-
77common/eda_text.cpp
-
404common/font/font.cpp
-
228common/font/glyph.cpp
-
305common/font/stroke_font.cpp
-
113common/gal/cairo/cairo_gal.cpp
-
82common/gal/graphics_abstraction_layer.cpp
-
29common/gal/opengl/opengl_gal.cpp
-
640common/gal/stroke_font.cpp
-
58common/gr_text.cpp
-
18common/markup_parser.cpp
-
3common/plotters/SVG_plotter.cpp
-
4common/preview_items/draw_context.cpp
-
1common/preview_items/selection_area.cpp
-
8eeschema/dialogs/dialog_pin_properties.cpp
-
2eeschema/dialogs/dialog_pin_properties.h
-
13eeschema/lib_pin.cpp
-
2eeschema/lib_symbol.h
-
293eeschema/sch_painter.cpp
-
6eeschema/sch_painter.h
-
8eeschema/sch_text.cpp
-
52include/basic_gal.h
-
10include/eda_text.h
-
179include/font/font.h
-
207include/font/glyph.h
-
118include/font/stroke_font.h
-
4include/gal/cairo/cairo_gal.h
-
73include/gal/graphics_abstraction_layer.h
-
4include/gal/opengl/opengl_gal.h
-
191include/gal/stroke_font.h
-
5include/gr_text.h
-
6include/markup_parser.h
-
19pcbnew/autorouter/ar_autoplacer.cpp
-
59pcbnew/pcb_painter.cpp
-
4pcbnew/pcb_painter.h
-
15pcbnew/tools/drawing_tool.cpp
-
3qa/pns/pns_log_viewer.cpp
@ -0,0 +1,228 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski <gitlab@rinta-koski.net> |
|||
* Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software: you can redistribute it and/or modify it |
|||
* under the terms of the GNU General Public License as published by the |
|||
* Free Software Foundation, either version 3 of the License, or (at your |
|||
* option) any later version. |
|||
* |
|||
* This program 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 |
|||
* General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License along |
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <vector>
|
|||
#include <font/glyph.h>
|
|||
|
|||
using namespace KIFONT; |
|||
|
|||
|
|||
STROKE_GLYPH::STROKE_GLYPH( const STROKE_GLYPH& aGlyph ) |
|||
{ |
|||
for( const std::vector<VECTOR2D>& pointList : aGlyph.m_pointLists ) |
|||
m_pointLists.push_back( pointList ); |
|||
} |
|||
|
|||
|
|||
void STROKE_GLYPH::AddPoint( const VECTOR2D& aPoint ) |
|||
{ |
|||
if( !m_penIsDown ) |
|||
{ |
|||
std::vector<VECTOR2D> v; |
|||
m_pointLists.push_back( v ); |
|||
m_penIsDown = true; |
|||
} |
|||
|
|||
m_pointLists.back().push_back( aPoint ); |
|||
} |
|||
|
|||
|
|||
void STROKE_GLYPH::RaisePen() |
|||
{ |
|||
if( m_penIsDown ) |
|||
m_pointLists.back().shrink_to_fit(); |
|||
|
|||
m_penIsDown = false; |
|||
} |
|||
|
|||
|
|||
void STROKE_GLYPH::Finalize() |
|||
{ |
|||
if( !m_pointLists.empty() && !m_pointLists.back().empty() ) |
|||
m_pointLists.back().shrink_to_fit(); |
|||
} |
|||
|
|||
|
|||
BOX2D STROKE_GLYPH::BoundingBox() |
|||
{ |
|||
if( m_boundingBox.GetWidth() == 0 && m_boundingBox.GetHeight() == 0 ) |
|||
{ |
|||
bool first = true; |
|||
|
|||
for( const std::vector<VECTOR2D>& pointList : m_pointLists ) |
|||
{ |
|||
for( const VECTOR2D& point : pointList ) |
|||
{ |
|||
if( first ) |
|||
{ |
|||
m_boundingBox.SetOrigin( point ); |
|||
first = false; |
|||
} |
|||
else |
|||
{ |
|||
m_boundingBox.Merge( point ); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return m_boundingBox; |
|||
} |
|||
|
|||
|
|||
std::shared_ptr<GLYPH> STROKE_GLYPH::Resize( const VECTOR2D& aGlyphSize ) const |
|||
{ |
|||
std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>( *this ); |
|||
|
|||
glyph->clearBoundingBox(); |
|||
|
|||
bool first = true; |
|||
|
|||
for( std::vector<VECTOR2D>& pointList : glyph->m_pointLists ) |
|||
{ |
|||
for( VECTOR2D& point : pointList ) |
|||
{ |
|||
point.x = point.x * aGlyphSize.x; |
|||
point.y = point.y * aGlyphSize.y; |
|||
|
|||
if( first ) |
|||
{ |
|||
glyph->m_boundingBox.SetOrigin( point ); |
|||
first = false; |
|||
} |
|||
else |
|||
{ |
|||
glyph->m_boundingBox.Merge( point ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return glyph; |
|||
} |
|||
|
|||
|
|||
std::shared_ptr<GLYPH> STROKE_GLYPH::Translate( const VECTOR2D& aOffset ) const |
|||
{ |
|||
auto glyph = std::make_shared<STROKE_GLYPH>( *this ); |
|||
|
|||
glyph->clearBoundingBox(); |
|||
|
|||
bool first = true; |
|||
|
|||
for( std::vector<VECTOR2D>& pointList : glyph->m_pointLists ) |
|||
{ |
|||
for( VECTOR2D& point : pointList ) |
|||
{ |
|||
point.x += aOffset.x; |
|||
point.y += aOffset.y; |
|||
|
|||
if( first ) |
|||
{ |
|||
glyph->m_boundingBox.SetOrigin( point ); |
|||
first = false; |
|||
} |
|||
else |
|||
{ |
|||
glyph->m_boundingBox.Merge( point ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return glyph; |
|||
} |
|||
|
|||
|
|||
std::shared_ptr<GLYPH> STROKE_GLYPH::Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, |
|||
double aTilt ) |
|||
{ |
|||
std::shared_ptr<STROKE_GLYPH> glyph = std::make_shared<STROKE_GLYPH>( *this ); |
|||
|
|||
glyph->clearBoundingBox(); |
|||
|
|||
bool first = true; |
|||
|
|||
for( std::vector<VECTOR2D>& pointList : glyph->m_pointLists ) |
|||
{ |
|||
for( VECTOR2D& point : pointList ) |
|||
{ |
|||
point.x *= aGlyphSize.x; |
|||
point.y *= aGlyphSize.y; |
|||
|
|||
if( aTilt ) |
|||
point.x -= point.y * aTilt; |
|||
|
|||
point.x += aOffset.x; |
|||
point.y += aOffset.y; |
|||
|
|||
if( first ) |
|||
{ |
|||
glyph->m_boundingBox.SetOrigin( point ); |
|||
first = false; |
|||
} |
|||
else |
|||
{ |
|||
glyph->m_boundingBox.Merge( point ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return glyph; |
|||
} |
|||
|
|||
|
|||
std::shared_ptr<GLYPH> STROKE_GLYPH::Mirror( bool aMirror, const VECTOR2D& aMirrorOrigin ) const |
|||
{ |
|||
// TODO figure out a way to not make a copy if aMirror is false
|
|||
auto glyph = std::make_shared<STROKE_GLYPH>( *this ); |
|||
|
|||
if( aMirror ) |
|||
glyph->Mirror( aMirrorOrigin ); |
|||
|
|||
return glyph; |
|||
} |
|||
|
|||
|
|||
void STROKE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin ) |
|||
{ |
|||
double originX = aMirrorOrigin.x; |
|||
|
|||
clearBoundingBox(); |
|||
|
|||
bool first = true; |
|||
|
|||
for( std::vector<VECTOR2D>& pointList : m_pointLists ) |
|||
{ |
|||
for( VECTOR2D& point : pointList ) |
|||
{ |
|||
if( first ) |
|||
{ |
|||
//originX = point.x;
|
|||
point.x = originX - ( point.x - originX ); |
|||
m_boundingBox.SetOrigin( point ); |
|||
first = false; |
|||
} |
|||
else |
|||
{ |
|||
point.x = originX - ( point.x - originX ); |
|||
m_boundingBox.Merge( point ); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,305 @@ |
|||
/*
|
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de |
|||
* Copyright (C) 2013 CERN |
|||
* @author Maciej Suminski <maciej.suminski@cern.ch> |
|||
* Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors. |
|||
* |
|||
* Stroke font class |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program 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 General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <gal/graphics_abstraction_layer.h>
|
|||
#include <wx/string.h>
|
|||
#include <wx/filename.h>
|
|||
#include <wx/textfile.h>
|
|||
#include <newstroke_font.h>
|
|||
#include <font/glyph.h>
|
|||
#include <font/stroke_font.h>
|
|||
#include <markup_parser.h>
|
|||
#include <geometry/shape_line_chain.h>
|
|||
#include <trigo.h>
|
|||
|
|||
using namespace KIFONT; |
|||
|
|||
|
|||
///< Factor that determines relative vertical position of the overbar.
|
|||
static constexpr double OVERBAR_POSITION_FACTOR = 1.33; |
|||
|
|||
///< Scale factor for a glyph
|
|||
static constexpr double STROKE_FONT_SCALE = 1.0 / 21.0; |
|||
|
|||
///< Tilt factor for italic style (the is is the scaling factor
|
|||
///< on dY relative coordinates to give a tilt shape
|
|||
static constexpr double ITALIC_TILT = 1.0 / 8; |
|||
|
|||
///< Factor that determines the pitch between 2 lines.
|
|||
static constexpr double INTERLINE_PITCH_RATIO = 1.62; // The golden mean
|
|||
|
|||
static constexpr int FONT_OFFSET = -10; |
|||
|
|||
|
|||
bool g_defaultFontInitialized = false; |
|||
GLYPH_LIST g_defaultFontGlyphs; ///< Glyph list
|
|||
std::vector<BOX2D>* g_defaultFontGlyphBoundingBoxes; ///< Bounding boxes of the glyphs
|
|||
|
|||
|
|||
STROKE_FONT::STROKE_FONT() : |
|||
m_glyphs( nullptr ), |
|||
m_glyphBoundingBoxes( nullptr ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
STROKE_FONT* STROKE_FONT::LoadFont( const wxString& aFontName ) |
|||
{ |
|||
if( aFontName.empty() ) |
|||
{ |
|||
STROKE_FONT* font = new STROKE_FONT(); |
|||
font->loadNewStrokeFont( newstroke_font, newstroke_font_bufsize ); |
|||
return font; |
|||
} |
|||
else |
|||
{ |
|||
// FONT TODO: support for other stroke fonts?
|
|||
return nullptr; |
|||
} |
|||
} |
|||
|
|||
void STROKE_FONT::loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) |
|||
{ |
|||
if( !g_defaultFontInitialized ) |
|||
{ |
|||
g_defaultFontGlyphs.reserve( aNewStrokeFontSize ); |
|||
|
|||
g_defaultFontGlyphBoundingBoxes = new std::vector<BOX2D>; |
|||
g_defaultFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize ); |
|||
|
|||
for( int j = 0; j < aNewStrokeFontSize; j++ ) |
|||
{ |
|||
auto glyph = std::make_shared<STROKE_GLYPH>(); |
|||
double glyphStartX = 0.0; |
|||
|
|||
std::vector<VECTOR2D>* pointList = nullptr; |
|||
|
|||
int i = 0; |
|||
|
|||
while( aNewStrokeFont[j][i] ) |
|||
{ |
|||
VECTOR2D point( 0.0, 0.0 ); |
|||
char coordinate[2] = { |
|||
0, |
|||
}; |
|||
|
|||
for( int k : { 0, 1 } ) |
|||
coordinate[k] = aNewStrokeFont[j][i + k]; |
|||
|
|||
if( i < 2 ) |
|||
{ |
|||
// The first two values contain the width of the char
|
|||
glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; |
|||
} |
|||
else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) |
|||
{ |
|||
glyph->RaisePen(); |
|||
} |
|||
else |
|||
{ |
|||
// In stroke font, coordinates values are coded as <value> + 'R', where
|
|||
// <value> is an ASCII char.
|
|||
// therefore every coordinate description of the Hershey format has an offset,
|
|||
// it has to be subtracted
|
|||
// Note:
|
|||
// * the stroke coordinates are stored in reduced form (-1.0 to +1.0),
|
|||
// and the actual size is stroke coordinate * glyph size
|
|||
// * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' )
|
|||
point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; |
|||
|
|||
// FONT_OFFSET is here for historical reasons, due to the way the stroke font
|
|||
// was built. It allows shapes coordinates like W M ... to be >= 0
|
|||
// Only shapes like j y have coordinates < 0
|
|||
point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE; |
|||
|
|||
if( !pointList ) |
|||
pointList = new std::vector<VECTOR2D>; |
|||
|
|||
glyph->AddPoint( point ); |
|||
} |
|||
|
|||
i += 2; |
|||
} |
|||
|
|||
glyph->Finalize(); |
|||
|
|||
// Compute the bounding box of the glyph
|
|||
g_defaultFontGlyphBoundingBoxes->emplace_back( glyph->BoundingBox() ); |
|||
g_defaultFontGlyphs.push_back( glyph ); |
|||
m_maxGlyphWidth = std::max( m_maxGlyphWidth, |
|||
g_defaultFontGlyphBoundingBoxes->back().GetWidth() ); |
|||
} |
|||
|
|||
g_defaultFontInitialized = true; |
|||
} |
|||
|
|||
m_glyphs = &g_defaultFontGlyphs; |
|||
m_glyphBoundingBoxes = g_defaultFontGlyphBoundingBoxes; |
|||
} |
|||
|
|||
|
|||
double STROKE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const |
|||
{ |
|||
// Do not add the glyph thickness to the interline. This makes bold text line-spacing
|
|||
// different from normal text, which is poor typography.
|
|||
return ( aGlyphHeight * aLineSpacing * INTERLINE_PITCH_RATIO ); |
|||
} |
|||
|
|||
|
|||
double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const |
|||
{ |
|||
return aGlyphHeight * OVERBAR_POSITION_FACTOR; |
|||
} |
|||
|
|||
|
|||
VECTOR2D STROKE_FONT::ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const |
|||
{ |
|||
//TODO default glyph size (and line width) is a guess
|
|||
VECTOR2D glyphSize = aGal ? aGal->GetGlyphSize() : VECTOR2D( 16.0, 16.0 ); |
|||
double lineWidth = aGal ? aGal->GetLineWidth() : 1.0; |
|||
return StringBoundaryLimits( aGal, aText, glyphSize, lineWidth ); |
|||
} |
|||
|
|||
|
|||
VECTOR2D STROKE_FONT::StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, |
|||
const VECTOR2D& aGlyphSize, |
|||
double aGlyphThickness ) const |
|||
{ |
|||
// TODO do we need to parse every time - have we already parsed?
|
|||
MARKUP::MARKUP_PARSER markupParser( aText ); |
|||
auto root = markupParser.Parse(); |
|||
|
|||
GLYPH_LIST glyphs; // ignored
|
|||
BOX2I boundingBox; |
|||
|
|||
(void) drawMarkup( &boundingBox, glyphs, root, VECTOR2D(), aGlyphSize, EDA_ANGLE::ANGLE_0, |
|||
false, 0 /* TODO: this should really include italic */ ); |
|||
|
|||
return boundingBox.GetSize(); |
|||
} |
|||
|
|||
|
|||
VECTOR2D STROKE_FONT::getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const |
|||
{ |
|||
// TODO: take glyph thickness into account!
|
|||
return StringBoundaryLimits( nullptr, aString, aGlyphSize, 0 ); |
|||
} |
|||
|
|||
|
|||
VECTOR2I STROKE_FONT::GetTextAsPolygon( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, |
|||
const UTF8& aText, const VECTOR2D& aGlyphSize, |
|||
const wxPoint& aPosition, const EDA_ANGLE& aAngle, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const |
|||
{ |
|||
wxPoint cursor( aPosition ); |
|||
VECTOR2D glyphSize( aGlyphSize ); |
|||
double tilt = ( aTextStyle & TEXT_STYLE::ITALIC ) ? ITALIC_TILT : 0.0; |
|||
|
|||
if( aTextStyle & TEXT_STYLE::SUBSCRIPT || aTextStyle & TEXT_STYLE::SUPERSCRIPT ) |
|||
{ |
|||
constexpr double subscriptSuperscriptMultiplier = 0.7; |
|||
glyphSize.x *= subscriptSuperscriptMultiplier; |
|||
glyphSize.y *= subscriptSuperscriptMultiplier; |
|||
|
|||
if( aTextStyle & TEXT_STYLE::SUBSCRIPT ) |
|||
{ |
|||
constexpr double subscriptVerticalMultiplier = 0.3; |
|||
cursor.y += glyphSize.y * subscriptVerticalMultiplier; |
|||
} |
|||
else |
|||
{ |
|||
constexpr double superscriptVerticalMultiplier = 0.5; |
|||
cursor.y -= glyphSize.y * superscriptVerticalMultiplier; |
|||
} |
|||
} |
|||
|
|||
for( UTF8::uni_iter i = aText.ubegin(), end = aText.uend(); i < end; ++i ) |
|||
{ |
|||
// Index into bounding boxes table
|
|||
int dd = (signed) *i - ' '; |
|||
|
|||
if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) |
|||
{ |
|||
switch( *i ) |
|||
{ |
|||
case '\t': |
|||
// TAB->SPACE
|
|||
dd = 0; |
|||
break; |
|||
default: |
|||
// everything else is turned into a '?'
|
|||
dd = '?' - ' '; |
|||
} |
|||
} |
|||
|
|||
if( dd == 0 ) |
|||
{ |
|||
// 'space' character - draw nothing, advance cursor position
|
|||
constexpr double spaceAdvance = 0.6; |
|||
cursor.x += glyphSize.x * spaceAdvance; |
|||
} |
|||
else |
|||
{ |
|||
constexpr double interGlyphSpaceMultiplier = 0.15; |
|||
double interGlyphSpace = glyphSize.x * interGlyphSpaceMultiplier; |
|||
STROKE_GLYPH* source = static_cast<STROKE_GLYPH*>( m_glyphs->at( dd ).get() ); |
|||
|
|||
aGlyphs.push_back( source->Transform( glyphSize, cursor, tilt ) ); |
|||
|
|||
cursor.x = aGlyphs.back()->BoundingBox().GetEnd().x + interGlyphSpace; |
|||
} |
|||
} |
|||
|
|||
VECTOR2D barOffset( 0.0, 0.0 ); |
|||
|
|||
if( aTextStyle & TEXT_STYLE::OVERBAR ) |
|||
{ |
|||
std::shared_ptr<STROKE_GLYPH> overbarGlyph = std::make_shared<STROKE_GLYPH>(); |
|||
|
|||
barOffset.y = ComputeOverbarVerticalPosition( glyphSize.y ); |
|||
|
|||
if( aTextStyle & TEXT_STYLE::ITALIC ) |
|||
barOffset.x = barOffset.y * ITALIC_TILT; |
|||
|
|||
overbarGlyph->AddPoint( VECTOR2D( aPosition.x + barOffset.x, cursor.y - barOffset.y ) ); |
|||
overbarGlyph->AddPoint( VECTOR2D( cursor.x + barOffset.x, cursor.y - barOffset.y ) ); |
|||
overbarGlyph->Finalize(); |
|||
|
|||
aGlyphs.push_back( overbarGlyph ); |
|||
} |
|||
|
|||
if( aBoundingBox ) |
|||
{ |
|||
aBoundingBox->SetOrigin( aPosition.x, aPosition.y ); |
|||
aBoundingBox->SetEnd( cursor.x + barOffset.x, cursor.y + std::max( glyphSize.y, barOffset.y ) ); |
|||
aBoundingBox->Normalize(); |
|||
} |
|||
|
|||
return VECTOR2I( cursor.x, aPosition.y ); |
|||
} |
@ -1,640 +0,0 @@ |
|||
/*
|
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de |
|||
* Copyright (C) 2013 CERN |
|||
* @author Maciej Suminski <maciej.suminski@cern.ch> |
|||
* Copyright (C) 2016-2021 Kicad Developers, see change_log.txt for contributors. |
|||
* |
|||
* Stroke font class |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program 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 General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|||
* or you may search the http://www.gnu.org website for the version 2 license,
|
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#include <gal/stroke_font.h>
|
|||
#include <gal/graphics_abstraction_layer.h>
|
|||
#include <math/util.h> // for KiROUND
|
|||
#include <wx/string.h>
|
|||
#include <gr_text.h>
|
|||
|
|||
|
|||
using namespace KIGFX; |
|||
|
|||
const double STROKE_FONT::INTERLINE_PITCH_RATIO = 1.61; |
|||
const double STROKE_FONT::OVERBAR_POSITION_FACTOR = 1.33; |
|||
const double STROKE_FONT::UNDERLINE_POSITION_FACTOR = 0.41; |
|||
const double STROKE_FONT::BOLD_FACTOR = 1.3; |
|||
const double STROKE_FONT::STROKE_FONT_SCALE = 1.0 / 21.0; |
|||
const double STROKE_FONT::ITALIC_TILT = 1.0 / 8; |
|||
|
|||
|
|||
GLYPH_LIST* g_newStrokeFontGlyphs = nullptr; ///< Glyph list
|
|||
std::vector<BOX2D>* g_newStrokeFontGlyphBoundingBoxes; ///< Bounding boxes of the glyphs
|
|||
|
|||
|
|||
STROKE_FONT::STROKE_FONT( GAL* aGal ) : |
|||
m_gal( aGal ), |
|||
m_glyphs( nullptr ), |
|||
m_glyphBoundingBoxes( nullptr ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
bool STROKE_FONT::LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ) |
|||
{ |
|||
if( g_newStrokeFontGlyphs ) |
|||
{ |
|||
m_glyphs = g_newStrokeFontGlyphs; |
|||
m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; |
|||
return true; |
|||
} |
|||
|
|||
g_newStrokeFontGlyphs = new GLYPH_LIST; |
|||
g_newStrokeFontGlyphs->reserve( aNewStrokeFontSize ); |
|||
|
|||
g_newStrokeFontGlyphBoundingBoxes = new std::vector<BOX2D>; |
|||
g_newStrokeFontGlyphBoundingBoxes->reserve( aNewStrokeFontSize ); |
|||
|
|||
for( int j = 0; j < aNewStrokeFontSize; j++ ) |
|||
{ |
|||
GLYPH* glyph = new GLYPH; |
|||
double glyphStartX = 0.0; |
|||
double glyphEndX = 0.0; |
|||
double glyphWidth = 0.0; |
|||
|
|||
std::vector<VECTOR2D>* pointList = nullptr; |
|||
|
|||
int strokes = 0; |
|||
int i = 0; |
|||
|
|||
while( aNewStrokeFont[j][i] ) |
|||
{ |
|||
|
|||
if( aNewStrokeFont[j][i] == ' ' && aNewStrokeFont[j][i+1] == 'R' ) |
|||
strokes++; |
|||
|
|||
i += 2; |
|||
} |
|||
|
|||
glyph->reserve( strokes + 1 ); |
|||
|
|||
i = 0; |
|||
|
|||
while( aNewStrokeFont[j][i] ) |
|||
{ |
|||
VECTOR2D point( 0.0, 0.0 ); |
|||
char coordinate[2] = { 0, }; |
|||
|
|||
for( int k : { 0, 1 } ) |
|||
coordinate[k] = aNewStrokeFont[j][i + k]; |
|||
|
|||
if( i < 2 ) |
|||
{ |
|||
// The first two values contain the width of the char
|
|||
glyphStartX = ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE; |
|||
glyphEndX = ( coordinate[1] - 'R' ) * STROKE_FONT_SCALE; |
|||
glyphWidth = glyphEndX - glyphStartX; |
|||
} |
|||
else if( ( coordinate[0] == ' ' ) && ( coordinate[1] == 'R' ) ) |
|||
{ |
|||
if( pointList ) |
|||
pointList->shrink_to_fit(); |
|||
|
|||
// Raise pen
|
|||
pointList = nullptr; |
|||
} |
|||
else |
|||
{ |
|||
// In stroke font, coordinates values are coded as <value> + 'R',
|
|||
// <value> is an ASCII char.
|
|||
// therefore every coordinate description of the Hershey format has an offset,
|
|||
// it has to be subtracted
|
|||
// Note:
|
|||
// * the stroke coordinates are stored in reduced form (-1.0 to +1.0),
|
|||
// and the actual size is stroke coordinate * glyph size
|
|||
// * a few shapes have a height slightly bigger than 1.0 ( like '{' '[' )
|
|||
point.x = (double) ( coordinate[0] - 'R' ) * STROKE_FONT_SCALE - glyphStartX; |
|||
#define FONT_OFFSET -10
|
|||
// FONT_OFFSET is here for historical reasons, due to the way the stroke font
|
|||
// was built. It allows shapes coordinates like W M ... to be >= 0
|
|||
// Only shapes like j y have coordinates < 0
|
|||
point.y = (double) ( coordinate[1] - 'R' + FONT_OFFSET ) * STROKE_FONT_SCALE; |
|||
|
|||
if( !pointList ) |
|||
{ |
|||
pointList = new std::vector<VECTOR2D>; |
|||
glyph->push_back( pointList ); |
|||
} |
|||
|
|||
pointList->push_back( point ); |
|||
} |
|||
|
|||
i += 2; |
|||
} |
|||
|
|||
if( pointList ) |
|||
pointList->shrink_to_fit(); |
|||
|
|||
// Compute the bounding box of the glyph
|
|||
g_newStrokeFontGlyphBoundingBoxes->emplace_back( computeBoundingBox( glyph, glyphWidth ) ); |
|||
g_newStrokeFontGlyphs->push_back( glyph ); |
|||
} |
|||
|
|||
m_glyphs = g_newStrokeFontGlyphs; |
|||
m_glyphBoundingBoxes = g_newStrokeFontGlyphBoundingBoxes; |
|||
return true; |
|||
} |
|||
|
|||
|
|||
// Static function:
|
|||
double STROKE_FONT::GetInterline( double aGlyphHeight ) |
|||
{ |
|||
// Do not add the glyph thickness to the interline. This makes bold text line-spacing
|
|||
// different from normal text, which is poor typography.
|
|||
return ( aGlyphHeight * INTERLINE_PITCH_RATIO ); |
|||
} |
|||
|
|||
|
|||
BOX2D STROKE_FONT::computeBoundingBox( const GLYPH* aGLYPH, double aGlyphWidth ) const |
|||
{ |
|||
VECTOR2D min( 0, 0 ); |
|||
VECTOR2D max( aGlyphWidth, 0 ); |
|||
|
|||
for( const std::vector<VECTOR2D>* pointList : *aGLYPH ) |
|||
{ |
|||
for( const VECTOR2D& point : *pointList ) |
|||
{ |
|||
min.y = std::min( min.y, point.y ); |
|||
max.y = std::max( max.y, point.y ); |
|||
} |
|||
} |
|||
|
|||
return BOX2D( min, max - min ); |
|||
} |
|||
|
|||
|
|||
void STROKE_FONT::Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ) |
|||
{ |
|||
if( aText.empty() ) |
|||
return; |
|||
|
|||
// Context needs to be saved before any transformations
|
|||
m_gal->Save(); |
|||
|
|||
m_gal->Translate( aPosition ); |
|||
m_gal->Rotate( -aRotationAngle ); |
|||
|
|||
// Single line height
|
|||
int lineHeight = KiROUND( GetInterline( m_gal->GetGlyphSize().y ) ); |
|||
int lineCount = linesCount( aText ); |
|||
const VECTOR2D& glyphSize = m_gal->GetGlyphSize(); |
|||
|
|||
// align the 1st line of text
|
|||
switch( m_gal->GetVerticalJustify() ) |
|||
{ |
|||
case GR_TEXT_V_ALIGN_TOP: |
|||
m_gal->Translate( VECTOR2D( 0, glyphSize.y ) ); |
|||
break; |
|||
|
|||
case GR_TEXT_V_ALIGN_CENTER: |
|||
m_gal->Translate( VECTOR2D( 0, glyphSize.y / 2.0 ) ); |
|||
break; |
|||
|
|||
case GR_TEXT_V_ALIGN_BOTTOM: |
|||
break; |
|||
|
|||
default: |
|||
break; |
|||
} |
|||
|
|||
if( lineCount > 1 ) |
|||
{ |
|||
switch( m_gal->GetVerticalJustify() ) |
|||
{ |
|||
case GR_TEXT_V_ALIGN_TOP: |
|||
break; |
|||
|
|||
case GR_TEXT_V_ALIGN_CENTER: |
|||
m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight / 2) ); |
|||
break; |
|||
|
|||
case GR_TEXT_V_ALIGN_BOTTOM: |
|||
m_gal->Translate( VECTOR2D(0, -( lineCount - 1 ) * lineHeight ) ); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
m_gal->SetIsStroke( true ); |
|||
//m_gal->SetIsFill( false );
|
|||
|
|||
if( m_gal->IsFontBold() ) |
|||
m_gal->SetLineWidth( m_gal->GetLineWidth() * BOLD_FACTOR ); |
|||
|
|||
// Split multiline strings into separate ones and draw them line by line
|
|||
size_t begin = 0; |
|||
size_t newlinePos = aText.find( '\n' ); |
|||
|
|||
while( newlinePos != aText.npos ) |
|||
{ |
|||
size_t length = newlinePos - begin; |
|||
|
|||
drawSingleLineText( aText.substr( begin, length ) ); |
|||
m_gal->Translate( VECTOR2D( 0.0, lineHeight ) ); |
|||
|
|||
begin = newlinePos + 1; |
|||
newlinePos = aText.find( '\n', begin ); |
|||
} |
|||
|
|||
// Draw the last (or the only one) line
|
|||
if( !aText.empty() ) |
|||
drawSingleLineText( aText.substr( begin ) ); |
|||
|
|||
m_gal->Restore(); |
|||
} |
|||
|
|||
|
|||
void STROKE_FONT::drawSingleLineText( const UTF8& aText ) |
|||
{ |
|||
double xOffset; |
|||
double yOffset; |
|||
VECTOR2D baseGlyphSize( m_gal->GetGlyphSize() ); |
|||
double overbar_italic_comp = computeOverbarVerticalPosition() * ITALIC_TILT; |
|||
|
|||
if( m_gal->IsTextMirrored() ) |
|||
overbar_italic_comp = -overbar_italic_comp; |
|||
|
|||
// Compute the text size
|
|||
VECTOR2D textSize = computeTextLineSize( aText ); |
|||
double half_thickness = m_gal->GetLineWidth()/2; |
|||
|
|||
// Context needs to be saved before any transformations
|
|||
m_gal->Save(); |
|||
|
|||
// First adjust: the text X position is corrected by half_thickness
|
|||
// because when the text with thickness is draw, its full size is textSize,
|
|||
// but the position of lines is half_thickness to textSize - half_thickness
|
|||
// so we must translate the coordinates by half_thickness on the X axis
|
|||
// to place the text inside the 0 to textSize X area.
|
|||
m_gal->Translate( VECTOR2D( half_thickness, 0 ) ); |
|||
|
|||
// Adjust the text position to the given horizontal justification
|
|||
switch( m_gal->GetHorizontalJustify() ) |
|||
{ |
|||
case GR_TEXT_H_ALIGN_CENTER: |
|||
m_gal->Translate( VECTOR2D( -textSize.x / 2.0, 0 ) ); |
|||
break; |
|||
|
|||
case GR_TEXT_H_ALIGN_RIGHT: |
|||
if( !m_gal->IsTextMirrored() ) |
|||
m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); |
|||
break; |
|||
|
|||
case GR_TEXT_H_ALIGN_LEFT: |
|||
if( m_gal->IsTextMirrored() ) |
|||
m_gal->Translate( VECTOR2D( -textSize.x, 0 ) ); |
|||
break; |
|||
|
|||
default: |
|||
break; |
|||
} |
|||
|
|||
if( m_gal->IsTextMirrored() ) |
|||
{ |
|||
// 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 - m_gal->GetLineWidth(); |
|||
baseGlyphSize.x = -baseGlyphSize.x; |
|||
} |
|||
else |
|||
{ |
|||
xOffset = 0.0; |
|||
} |
|||
|
|||
// The overbar is indented inward at the beginning of an italicized section, but
|
|||
// must not be indented on subsequent letters to ensure that the bar segments
|
|||
// overlap.
|
|||
bool lastHadOverbar = false; |
|||
int overbarDepth = -1; |
|||
int superSubDepth = -1; |
|||
int braceNesting = 0; |
|||
VECTOR2D glyphSize = baseGlyphSize; |
|||
|
|||
// Allocate only once (for performance)
|
|||
std::vector<VECTOR2D> ptListScaled; |
|||
int char_count = 0; |
|||
|
|||
yOffset = 0; |
|||
|
|||
for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) |
|||
{ |
|||
// Handle tabs as locked to the nearest 4th column (counting in space-widths).
|
|||
// The choice of spaces is somewhat arbitrary but sufficient for aligning text; while
|
|||
// it can produce tabs that go backwards when following wide characters, spacing in
|
|||
// widest-char-widths produces tab spacing that is much too wide (and would change the
|
|||
// layout of existing boards).
|
|||
if( *chIt == '\t' ) |
|||
{ |
|||
char_count = ( char_count / 4 + 1 ) * 4 - 1; |
|||
xOffset = baseGlyphSize.x * char_count; |
|||
|
|||
glyphSize = baseGlyphSize; |
|||
yOffset = 0; |
|||
} |
|||
else if( *chIt == '^' && superSubDepth == -1 ) |
|||
{ |
|||
UTF8::uni_iter lookahead = chIt; |
|||
|
|||
if( ++lookahead != end && *lookahead == '{' ) |
|||
{ |
|||
chIt = lookahead; |
|||
superSubDepth = braceNesting; |
|||
braceNesting++; |
|||
|
|||
glyphSize = baseGlyphSize * 0.8; |
|||
yOffset = -baseGlyphSize.y * 0.3; |
|||
continue; |
|||
} |
|||
} |
|||
else if( *chIt == '_' && superSubDepth == -1 ) |
|||
{ |
|||
UTF8::uni_iter lookahead = chIt; |
|||
|
|||
if( ++lookahead != end && *lookahead == '{' ) |
|||
{ |
|||
chIt = lookahead; |
|||
superSubDepth = braceNesting; |
|||
braceNesting++; |
|||
|
|||
glyphSize = baseGlyphSize * 0.8; |
|||
yOffset = baseGlyphSize.y * 0.1; |
|||
continue; |
|||
} |
|||
} |
|||
else if( *chIt == '~' && overbarDepth == -1 ) |
|||
{ |
|||
UTF8::uni_iter lookahead = chIt; |
|||
|
|||
if( ++lookahead != end && *lookahead == '{' ) |
|||
{ |
|||
chIt = lookahead; |
|||
overbarDepth = braceNesting; |
|||
braceNesting++; |
|||
continue; |
|||
} |
|||
} |
|||
else if( *chIt == '{' ) |
|||
{ |
|||
braceNesting++; |
|||
} |
|||
else if( *chIt == '}' ) |
|||
{ |
|||
if( braceNesting > 0 ) |
|||
braceNesting--; |
|||
|
|||
if( braceNesting == superSubDepth ) |
|||
{ |
|||
superSubDepth = -1; |
|||
|
|||
glyphSize = baseGlyphSize; |
|||
yOffset = 0; |
|||
continue; |
|||
} |
|||
|
|||
if( braceNesting == overbarDepth ) |
|||
{ |
|||
overbarDepth = -1; |
|||
continue; |
|||
} |
|||
} |
|||
|
|||
// Index into bounding boxes table
|
|||
int dd = (signed) *chIt - ' '; |
|||
|
|||
if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) |
|||
{ |
|||
int substitute = *chIt == '\t' ? ' ' : '?'; |
|||
dd = substitute - ' '; |
|||
} |
|||
|
|||
const GLYPH* glyph = m_glyphs->at( dd ); |
|||
const BOX2D& bbox = m_glyphBoundingBoxes->at( dd ); |
|||
|
|||
if( overbarDepth != -1 ) |
|||
{ |
|||
double overbar_start_x = xOffset; |
|||
double overbar_start_y = - computeOverbarVerticalPosition(); |
|||
double overbar_end_x = xOffset + glyphSize.x * bbox.GetEnd().x; |
|||
double overbar_end_y = overbar_start_y; |
|||
|
|||
if( !lastHadOverbar ) |
|||
{ |
|||
if( m_gal->IsFontItalic() ) |
|||
overbar_start_x += overbar_italic_comp; |
|||
|
|||
lastHadOverbar = true; |
|||
} |
|||
|
|||
VECTOR2D startOverbar( overbar_start_x, overbar_start_y ); |
|||
VECTOR2D endOverbar( overbar_end_x, overbar_end_y ); |
|||
|
|||
m_gal->DrawLine( startOverbar, endOverbar ); |
|||
} |
|||
else |
|||
{ |
|||
lastHadOverbar = false; |
|||
} |
|||
|
|||
if( m_gal->IsFontUnderlined() ) |
|||
{ |
|||
double vOffset = computeUnderlineVerticalPosition(); |
|||
VECTOR2D startUnderline( xOffset, - vOffset ); |
|||
VECTOR2D endUnderline( xOffset + glyphSize.x * bbox.GetEnd().x, - vOffset ); |
|||
|
|||
m_gal->DrawLine( startUnderline, endUnderline ); |
|||
} |
|||
|
|||
for( const std::vector<VECTOR2D>* ptList : *glyph ) |
|||
{ |
|||
int ptCount = 0; |
|||
ptListScaled.clear(); |
|||
|
|||
for( const VECTOR2D& pt : *ptList ) |
|||
{ |
|||
VECTOR2D scaledPt( pt.x * glyphSize.x + xOffset, pt.y * glyphSize.y + yOffset ); |
|||
|
|||
if( m_gal->IsFontItalic() ) |
|||
{ |
|||
// FIXME should be done other way - referring to the lowest Y value of point
|
|||
// because now italic fonts are translated a bit
|
|||
if( m_gal->IsTextMirrored() ) |
|||
scaledPt.x += scaledPt.y * STROKE_FONT::ITALIC_TILT; |
|||
else |
|||
scaledPt.x -= scaledPt.y * STROKE_FONT::ITALIC_TILT; |
|||
} |
|||
|
|||
ptListScaled.push_back( scaledPt ); |
|||
ptCount++; |
|||
} |
|||
|
|||
m_gal->DrawPolyline( &ptListScaled[0], ptCount ); |
|||
} |
|||
|
|||
char_count++; |
|||
xOffset += glyphSize.x * bbox.GetEnd().x; |
|||
} |
|||
|
|||
m_gal->Restore(); |
|||
} |
|||
|
|||
|
|||
double STROKE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const |
|||
{ |
|||
// Static method.
|
|||
return aGlyphHeight * OVERBAR_POSITION_FACTOR; |
|||
} |
|||
|
|||
|
|||
double STROKE_FONT::computeOverbarVerticalPosition() const |
|||
{ |
|||
// Compute the Y position of the overbar. This is the distance between
|
|||
// the text base line and the overbar axis.
|
|||
return ComputeOverbarVerticalPosition( m_gal->GetGlyphSize().y ); |
|||
} |
|||
|
|||
|
|||
double STROKE_FONT::computeUnderlineVerticalPosition() const |
|||
{ |
|||
// Compute the Y position of the underline. This is the distance between
|
|||
// the text base line and the underline axis.
|
|||
return - m_gal->GetGlyphSize().y * UNDERLINE_POSITION_FACTOR; |
|||
} |
|||
|
|||
|
|||
VECTOR2D STROKE_FONT::computeTextLineSize( const UTF8& aText ) const |
|||
{ |
|||
return ComputeStringBoundaryLimits( aText, m_gal->GetGlyphSize(), m_gal->GetLineWidth() ); |
|||
} |
|||
|
|||
|
|||
VECTOR2D STROKE_FONT::ComputeStringBoundaryLimits( const UTF8& aText, const VECTOR2D& aGlyphSize, |
|||
double aGlyphThickness ) const |
|||
{ |
|||
VECTOR2D string_bbox; |
|||
int line_count = 1; |
|||
double maxX = 0.0, curX = 0.0; |
|||
|
|||
double curScale = 1.0; |
|||
int overbarDepth = -1; |
|||
int superSubDepth = -1; |
|||
int braceNesting = 0; |
|||
|
|||
for( UTF8::uni_iter chIt = aText.ubegin(), end = aText.uend(); chIt < end; ++chIt ) |
|||
{ |
|||
if( *chIt == '\n' ) |
|||
{ |
|||
curX = 0.0; |
|||
maxX = std::max( maxX, curX ); |
|||
++line_count; |
|||
continue; |
|||
} |
|||
|
|||
// Handle tabs as locked to the nearest 4th column (counting in spaces)
|
|||
// The choice of spaces is somewhat arbitrary but sufficient for aligning text
|
|||
if( *chIt == '\t' ) |
|||
{ |
|||
double spaces = m_glyphBoundingBoxes->at( 0 ).GetEnd().x; |
|||
double addlSpace = 3.0 * spaces - std::fmod( curX, 4.0 * spaces ); |
|||
|
|||
// Add the remaining space (between 0 and 3 spaces)
|
|||
curX += addlSpace; |
|||
} |
|||
else if( (*chIt == '^' || *chIt == '_') && superSubDepth == -1 ) |
|||
{ |
|||
UTF8::uni_iter lookahead = chIt; |
|||
|
|||
if( ++lookahead != end && *lookahead == '{' ) |
|||
{ |
|||
// Process superscript
|
|||
chIt = lookahead; |
|||
superSubDepth = braceNesting; |
|||
braceNesting++; |
|||
|
|||
curScale = 0.8; |
|||
continue; |
|||
} |
|||
} |
|||
else if( *chIt == '~' && overbarDepth == -1 ) |
|||
{ |
|||
UTF8::uni_iter lookahead = chIt; |
|||
|
|||
if( ++lookahead != end && *lookahead == '{' ) |
|||
{ |
|||
chIt = lookahead; |
|||
overbarDepth = braceNesting; |
|||
braceNesting++; |
|||
continue; |
|||
} |
|||
} |
|||
else if( *chIt == '{' ) |
|||
{ |
|||
braceNesting++; |
|||
} |
|||
else if( *chIt == '}' ) |
|||
{ |
|||
if( braceNesting > 0 ) |
|||
braceNesting--; |
|||
|
|||
if( braceNesting == overbarDepth ) |
|||
{ |
|||
overbarDepth = -1; |
|||
continue; |
|||
} |
|||
|
|||
if( braceNesting == superSubDepth ) |
|||
{ |
|||
superSubDepth = -1; |
|||
|
|||
curScale = 1.0; |
|||
continue; |
|||
} |
|||
} |
|||
|
|||
// Index in the bounding boxes table
|
|||
int dd = (signed) *chIt - ' '; |
|||
|
|||
if( dd >= (int) m_glyphBoundingBoxes->size() || dd < 0 ) |
|||
{ |
|||
int substitute = *chIt == '\t' ? ' ' : '?'; |
|||
dd = substitute - ' '; |
|||
} |
|||
|
|||
const BOX2D& box = m_glyphBoundingBoxes->at( dd ); |
|||
curX += box.GetEnd().x * curScale; |
|||
} |
|||
|
|||
string_bbox.x = std::max( maxX, curX ) * aGlyphSize.x; |
|||
string_bbox.x += aGlyphThickness; |
|||
string_bbox.y = line_count * GetInterline( aGlyphSize.y ); |
|||
|
|||
// For italic correction, take in account italic tilt
|
|||
if( m_gal->IsFontItalic() ) |
|||
string_bbox.x += string_bbox.y * STROKE_FONT::ITALIC_TILT; |
|||
|
|||
return string_bbox; |
|||
} |
@ -0,0 +1,207 @@ |
|||
/* |
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021 Kicad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program 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 General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
|||
* or you may search the http://www.gnu.org website for the version 2 license, |
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#ifndef GLYPH_H |
|||
#define GLYPH_H |
|||
|
|||
#include <memory> |
|||
#include <math/box2.h> |
|||
#include <geometry/shape_poly_set.h> |
|||
#include <wx/debug.h> |
|||
|
|||
namespace KIFONT |
|||
{ |
|||
class GLYPH |
|||
{ |
|||
public: |
|||
virtual ~GLYPH() |
|||
{} |
|||
|
|||
virtual void AddPoint( const VECTOR2D& aPoint ) = 0; |
|||
|
|||
virtual void RaisePen() = 0; |
|||
|
|||
virtual void Finalize() = 0; |
|||
|
|||
virtual BOX2D BoundingBox() = 0; |
|||
|
|||
virtual std::shared_ptr<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const = 0; |
|||
|
|||
virtual std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const = 0; |
|||
|
|||
virtual std::shared_ptr<GLYPH> Mirror( bool aMirror, |
|||
const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const = 0; |
|||
|
|||
virtual void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) = 0; |
|||
|
|||
virtual const SHAPE_POLY_SET& GetPolylist() const = 0; |
|||
|
|||
virtual const std::vector<std::vector<VECTOR2D>>& GetPoints() const = 0; |
|||
|
|||
virtual bool IsOutline() const { return false; } |
|||
virtual bool IsStroke() const { return false; } |
|||
}; |
|||
|
|||
typedef std::vector<std::shared_ptr<GLYPH>> GLYPH_LIST; |
|||
|
|||
|
|||
class OUTLINE_GLYPH : public GLYPH |
|||
{ |
|||
public: |
|||
OUTLINE_GLYPH( const SHAPE_POLY_SET& poly ) |
|||
{ |
|||
m_polySet = poly; |
|||
} |
|||
|
|||
const SHAPE_POLY_SET& GetPolylist() const override { return m_polySet; } |
|||
|
|||
bool IsOutline() const override |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
// |
|||
void AddPoint( const VECTOR2D& aPoint ) override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
} |
|||
|
|||
void RaisePen() override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
} |
|||
|
|||
void Finalize() override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
} |
|||
|
|||
BOX2D BoundingBox() override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return BOX2D(); |
|||
} |
|||
|
|||
std::shared_ptr<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return nullptr; |
|||
} |
|||
|
|||
std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return nullptr; |
|||
} |
|||
|
|||
std::shared_ptr<GLYPH> Mirror( bool aMirror, |
|||
const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return nullptr; |
|||
} |
|||
|
|||
void Mirror( const VECTOR2D& aMirrorOrigin = VECTOR2D( 0, 0 ) ) override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
} |
|||
|
|||
const std::vector<std::vector<VECTOR2D>>& GetPoints() const override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return m_dummy; |
|||
} |
|||
|
|||
private: |
|||
SHAPE_POLY_SET m_polySet; |
|||
|
|||
// For unimplemented return values |
|||
std::vector<std::vector<VECTOR2D>> m_dummy; |
|||
}; |
|||
|
|||
|
|||
class STROKE_GLYPH : public GLYPH |
|||
{ |
|||
public: |
|||
STROKE_GLYPH() |
|||
{} |
|||
|
|||
STROKE_GLYPH( const STROKE_GLYPH& aGlyph ); |
|||
|
|||
void AddPoint( const VECTOR2D& aPoint ) override; |
|||
|
|||
void RaisePen() override; |
|||
|
|||
void Finalize() override; |
|||
|
|||
BOX2D BoundingBox() override; |
|||
|
|||
std::shared_ptr<GLYPH> Resize( const VECTOR2D& aGlyphSize ) const override; |
|||
|
|||
std::shared_ptr<GLYPH> Translate( const VECTOR2D& aOffset ) const override; |
|||
|
|||
std::shared_ptr<GLYPH> Transform( const VECTOR2D& aGlyphSize, const VECTOR2D& aOffset, |
|||
double aTilt ); |
|||
|
|||
std::shared_ptr<GLYPH> Mirror( bool aMirror, |
|||
const VECTOR2D& aMirrorOrigin = { 0, 0 } ) const override; |
|||
|
|||
void Mirror( const VECTOR2D& aMirrorOrigin = { 0, 0 } ) override; |
|||
|
|||
bool IsStroke() const override { return true; } |
|||
|
|||
// |
|||
const SHAPE_POLY_SET& GetPolylist() const override |
|||
{ |
|||
wxFAIL_MSG( "unimplemented" ); |
|||
return m_dummy; |
|||
} |
|||
|
|||
const std::vector<std::vector<VECTOR2D>>& GetPoints() const override { return m_pointLists; } |
|||
|
|||
private: |
|||
void clearBoundingBox() |
|||
{ |
|||
m_boundingBox.SetOrigin( 0, 0 ); |
|||
m_boundingBox.SetSize( 0, 0 ); |
|||
} |
|||
|
|||
private: |
|||
bool m_penIsDown = false; |
|||
std::vector<std::vector<VECTOR2D>> m_pointLists; |
|||
BOX2D m_boundingBox; |
|||
|
|||
// For unimplemented return values |
|||
SHAPE_POLY_SET m_dummy; |
|||
}; |
|||
|
|||
|
|||
typedef std::vector<VECTOR2D> GLYPH_POINTS; |
|||
typedef std::vector<GLYPH_POINTS> GLYPH_POINTS_LIST; |
|||
typedef std::vector<BOX2D> GLYPH_BOUNDING_BOX_LIST; |
|||
|
|||
|
|||
} // namespace KIFONT |
|||
|
|||
#endif // GLYPH_H |
@ -0,0 +1,118 @@ |
|||
/* |
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de |
|||
* Copyright (C) 2013 CERN |
|||
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* @author Maciej Suminski <maciej.suminski@cern.ch> |
|||
* |
|||
* Stroke font class |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program 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 General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
|||
* or you may search the http://www.gnu.org website for the version 2 license, |
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#ifndef STROKE_FONT_H |
|||
#define STROKE_FONT_H |
|||
|
|||
#include <map> |
|||
#include <deque> |
|||
#include <algorithm> |
|||
#include <utf8.h> |
|||
#include <math/box2.h> |
|||
#include <font/font.h> |
|||
|
|||
namespace KIGFX |
|||
{ |
|||
class GAL; |
|||
} |
|||
|
|||
namespace KIFONT |
|||
{ |
|||
/** |
|||
* Implement a stroke font drawing. |
|||
* |
|||
* A stroke font is composed of lines. |
|||
*/ |
|||
class STROKE_FONT : public FONT |
|||
{ |
|||
public: |
|||
STROKE_FONT(); |
|||
|
|||
bool IsStroke() const override { return true; } |
|||
|
|||
/** |
|||
* Load a stroke font. |
|||
* |
|||
* @param aFontName is the name of the font. If empty, the standard KiCad stroke font is |
|||
* loaded. |
|||
*/ |
|||
static STROKE_FONT* LoadFont( const wxString& aFontName ); |
|||
|
|||
/** |
|||
* Compute the boundary limits of aText (the bounding box of all shapes). |
|||
* |
|||
* @return a VECTOR2D giving the width and height of text. |
|||
*/ |
|||
VECTOR2D StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, |
|||
const VECTOR2D& aGlyphSize, |
|||
double aGlyphThickness ) const override; |
|||
|
|||
/** |
|||
* Compute the vertical position of an overbar. This is the distance between the text |
|||
* baseline and the overbar. |
|||
*/ |
|||
double ComputeOverbarVerticalPosition( double aGlyphHeight ) const override; |
|||
|
|||
/** |
|||
* Compute the distance (interline) between 2 lines of text (for multiline texts). This is |
|||
* the distance between baselines, not the space between line bounding boxes. |
|||
*/ |
|||
double GetInterline( double aGlyphHeight, double aLineSpacing = 1.0 ) const override; |
|||
|
|||
/** |
|||
* Compute the X and Y size of a given text. The text is expected to be a single line. |
|||
*/ |
|||
VECTOR2D ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const override; |
|||
|
|||
VECTOR2I GetTextAsPolygon( BOX2I* aBoundingBox, GLYPH_LIST& aGlyphs, const UTF8& aText, |
|||
const VECTOR2D& aGlyphSize, const wxPoint& aPosition, |
|||
const EDA_ANGLE& aAngle, TEXT_STYLE_FLAGS aTextStyle ) const override; |
|||
|
|||
protected: |
|||
VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const override; |
|||
|
|||
private: |
|||
/** |
|||
* Load the standard KiCad stroke font. |
|||
* |
|||
* @param aNewStrokeFont is the pointer to the font data. |
|||
* @param aNewStrokeFontSize is the size of the font data. |
|||
*/ |
|||
void loadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ); |
|||
|
|||
private: |
|||
const GLYPH_LIST* m_glyphs; ///< Glyph list |
|||
const GLYPH_BOUNDING_BOX_LIST* m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs |
|||
double m_maxGlyphWidth; |
|||
}; |
|||
|
|||
} //namespace KIFONT |
|||
|
|||
#endif // STROKE_FONT_H |
@ -1,191 +0,0 @@ |
|||
/* |
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de |
|||
* Copyright (C) 2013 CERN |
|||
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* @author Maciej Suminski <maciej.suminski@cern.ch> |
|||
* |
|||
* Stroke font class |
|||
* |
|||
* This program is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU General Public License |
|||
* as published by the Free Software Foundation; either version 2 |
|||
* of the License, or (at your option) any later version. |
|||
* |
|||
* This program 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 General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU General Public License |
|||
* along with this program; if not, you may find one here: |
|||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html |
|||
* or you may search the http://www.gnu.org website for the version 2 license, |
|||
* or you may write to the Free Software Foundation, Inc., |
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
|||
*/ |
|||
|
|||
#ifndef STROKE_FONT_H_ |
|||
#define STROKE_FONT_H_ |
|||
|
|||
#include <deque> |
|||
#include <algorithm> |
|||
|
|||
#include <utf8.h> |
|||
|
|||
#include <eda_text.h> |
|||
|
|||
#include <math/box2.h> |
|||
|
|||
namespace KIGFX |
|||
{ |
|||
class GAL; |
|||
|
|||
typedef std::vector<std::vector<VECTOR2D>*> GLYPH; |
|||
typedef std::vector<GLYPH*> GLYPH_LIST; |
|||
|
|||
/** |
|||
* Implement a stroke font drawing. |
|||
* |
|||
* A stroke font is composed of lines. |
|||
*/ |
|||
class STROKE_FONT |
|||
{ |
|||
friend class GAL; |
|||
|
|||
public: |
|||
/// Constructor |
|||
STROKE_FONT( GAL* aGal ); |
|||
|
|||
/** |
|||
* Load the new stroke font. |
|||
* |
|||
* @param aNewStrokeFont is the pointer to the font data. |
|||
* @param aNewStrokeFontSize is the size of the font data. |
|||
* @return True, if the font was successfully loaded, else false. |
|||
*/ |
|||
bool LoadNewStrokeFont( const char* const aNewStrokeFont[], int aNewStrokeFontSize ); |
|||
|
|||
/** |
|||
* Draw a string. |
|||
* |
|||
* @param aText is the text to be drawn. |
|||
* @param aPosition is the text position in world coordinates. |
|||
* @param aRotationAngle is the text rotation angle in radians. |
|||
*/ |
|||
void Draw( const UTF8& aText, const VECTOR2D& aPosition, double aRotationAngle ); |
|||
|
|||
/** |
|||
* Changes Graphics Abstraction Layer used for drawing items for a new one. |
|||
* |
|||
* @param aGal is the new GAL instance. |
|||
*/ |
|||
void SetGAL( GAL* aGal ) |
|||
{ |
|||
m_gal = aGal; |
|||
} |
|||
|
|||
/** |
|||
* Compute the boundary limits of aText (the bounding box of all shapes). |
|||
* |
|||
* The overbar and alignment are not taken in account, '~' characters are skipped. |
|||
* |
|||
* @return a VECTOR2D giving the width and height of text. |
|||
*/ |
|||
VECTOR2D ComputeStringBoundaryLimits( const UTF8& aText, const VECTOR2D& aGlyphSize, |
|||
double aGlyphThickness ) const; |
|||
|
|||
/** |
|||
* Compute the vertical position of an overbar, sometimes used in texts. |
|||
* |
|||
* This is the distance between the text base line and the overbar. |
|||
* |
|||
* @param aGlyphHeight is the height (vertical size) of the text. |
|||
* @return the relative position of the overbar axis. |
|||
*/ |
|||
double ComputeOverbarVerticalPosition( double aGlyphHeight ) const; |
|||
|
|||
/** |
|||
* Compute the distance (interline) between 2 lines of text (for multiline texts). |
|||
* |
|||
* @param aGlyphHeight is the height (vertical size) of the text. |
|||
* @return the interline. |
|||
*/ |
|||
static double GetInterline( double aGlyphHeight ); |
|||
|
|||
private: |
|||
/** |
|||
* Compute the X and Y size of a given text. The text is expected to be |
|||
* a only one line text. |
|||
* |
|||
* @param aText is the text string (one line). |
|||
* @return the text size. |
|||
*/ |
|||
VECTOR2D computeTextLineSize( const UTF8& aText ) const; |
|||
|
|||
/** |
|||
* Compute the vertical position of an overbar, sometimes used in texts. |
|||
* This is the distance between the text base line and the overbar. |
|||
* @return the relative position of the overbar axis. |
|||
*/ |
|||
double computeOverbarVerticalPosition() const; |
|||
double computeUnderlineVerticalPosition() const; |
|||
|
|||
/** |
|||
* Compute the bounding box of a given glyph. |
|||
* |
|||
* @param aGlyph is the glyph. |
|||
* @param aGlyphWidth is the x-component of the bounding box size. |
|||
* @return is the complete bounding box size. |
|||
*/ |
|||
BOX2D computeBoundingBox( const GLYPH* aGlyph, double aGlyphWidth ) const; |
|||
|
|||
/** |
|||
* Draw 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 UTF8& aText ); |
|||
|
|||
/** |
|||
* Returns number of lines for a given text. |
|||
* |
|||
* @param aText is the text to be checked. |
|||
* @return unsigned - The number of lines in aText. |
|||
*/ |
|||
inline unsigned linesCount( const UTF8& aText ) const |
|||
{ |
|||
if( aText.empty() ) |
|||
return 0; // std::count does not work well with empty strings |
|||
else |
|||
// aText.end() - 1 is to skip a newline character that is potentially at the end |
|||
return std::count( aText.begin(), aText.end() - 1, '\n' ) + 1; |
|||
} |
|||
|
|||
GAL* m_gal; ///< Pointer to the GAL |
|||
const GLYPH_LIST* m_glyphs; ///< Glyph list |
|||
const std::vector<BOX2D>* m_glyphBoundingBoxes; ///< Bounding boxes of the glyphs |
|||
|
|||
///< Factor that determines relative vertical position of the overbar. |
|||
static const double OVERBAR_POSITION_FACTOR; |
|||
static const double UNDERLINE_POSITION_FACTOR; |
|||
|
|||
///< Factor that determines relative line width for bold text. |
|||
static const double BOLD_FACTOR; |
|||
|
|||
///< Scale factor for a glyph |
|||
static const double STROKE_FONT_SCALE; |
|||
|
|||
///< Tilt factor for italic style (the is is the scaling factor |
|||
///< on dY relative coordinates to give a tilt shape |
|||
static const double ITALIC_TILT; |
|||
|
|||
///< Factor that determines the pitch between 2 lines. |
|||
static const double INTERLINE_PITCH_RATIO; |
|||
}; |
|||
} // namespace KIGFX |
|||
|
|||
#endif // STROKE_FONT_H_ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue