committed by
Jeff Young
19 changed files with 1951 additions and 20 deletions
-
9CMakeLists.txt
-
103CMakeModules/FindFontconfig.cmake
-
187CMakeModules/FindHarfBuzz.cmake
-
12common/CMakeLists.txt
-
9common/font/font.cpp
-
133common/font/fontconfig.cpp
-
13common/font/glyph.cpp
-
325common/font/outline_decomposer.cpp
-
623common/font/outline_font.cpp
-
47common/font/triangulate.cpp
-
23common/gal/opengl/opengl_gal.cpp
-
51include/font/fontconfig.h
-
56include/font/fontinfo.h
-
15include/font/glyph.h
-
128include/font/outline_decomposer.h
-
166include/font/outline_font.h
-
37include/font/triangulate.h
-
2include/gal/opengl/opengl_gal.h
-
32qa/data/complex_hierarchy.kicad_pro
@ -0,0 +1,103 @@ |
|||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
|||
# file Copyright.txt or https://cmake.org/licensing for details. |
|||
|
|||
#[=======================================================================[.rst: |
|||
FindFontconfig |
|||
-------------- |
|||
|
|||
.. versionadded:: 3.14 |
|||
|
|||
Find Fontconfig headers and library. |
|||
|
|||
Imported Targets |
|||
^^^^^^^^^^^^^^^^ |
|||
|
|||
``Fontconfig::Fontconfig`` |
|||
The Fontconfig library, if found. |
|||
|
|||
Result Variables |
|||
^^^^^^^^^^^^^^^^ |
|||
|
|||
This will define the following variables in your project: |
|||
|
|||
``Fontconfig_FOUND`` |
|||
true if (the requested version of) Fontconfig is available. |
|||
``Fontconfig_VERSION`` |
|||
the version of Fontconfig. |
|||
``Fontconfig_LIBRARIES`` |
|||
the libraries to link against to use Fontconfig. |
|||
``Fontconfig_INCLUDE_DIRS`` |
|||
where to find the Fontconfig headers. |
|||
``Fontconfig_COMPILE_OPTIONS`` |
|||
this should be passed to target_compile_options(), if the |
|||
target is not used for linking |
|||
|
|||
#]=======================================================================] |
|||
|
|||
|
|||
# use pkg-config to get the directories and then use these values |
|||
# in the FIND_PATH() and FIND_LIBRARY() calls |
|||
find_package(PkgConfig QUIET) |
|||
pkg_check_modules(PKG_FONTCONFIG QUIET fontconfig) |
|||
set(Fontconfig_COMPILE_OPTIONS ${PKG_FONTCONFIG_CFLAGS_OTHER}) |
|||
set(Fontconfig_VERSION ${PKG_FONTCONFIG_VERSION}) |
|||
|
|||
find_path( Fontconfig_INCLUDE_DIR |
|||
NAMES |
|||
fontconfig/fontconfig.h |
|||
HINTS |
|||
${PKG_FONTCONFIG_INCLUDE_DIRS} |
|||
/usr/X11/include |
|||
) |
|||
|
|||
find_library( Fontconfig_LIBRARY |
|||
NAMES |
|||
fontconfig |
|||
PATHS |
|||
${PKG_FONTCONFIG_LIBRARY_DIRS} |
|||
) |
|||
|
|||
if (Fontconfig_INCLUDE_DIR AND NOT Fontconfig_VERSION) |
|||
file(STRINGS ${Fontconfig_INCLUDE_DIR}/fontconfig/fontconfig.h _contents REGEX "^#define[ \t]+FC_[A-Z]+[ \t]+[0-9]+$") |
|||
unset(Fontconfig_VERSION) |
|||
foreach(VPART MAJOR MINOR REVISION) |
|||
foreach(VLINE ${_contents}) |
|||
if(VLINE MATCHES "^#define[\t ]+FC_${VPART}[\t ]+([0-9]+)$") |
|||
set(Fontconfig_VERSION_PART "${CMAKE_MATCH_1}") |
|||
if(Fontconfig_VERSION) |
|||
string(APPEND Fontconfig_VERSION ".${Fontconfig_VERSION_PART}") |
|||
else() |
|||
set(Fontconfig_VERSION "${Fontconfig_VERSION_PART}") |
|||
endif() |
|||
endif() |
|||
endforeach() |
|||
endforeach() |
|||
endif () |
|||
|
|||
include(FindPackageHandleStandardArgs) |
|||
find_package_handle_standard_args(Fontconfig |
|||
FOUND_VAR |
|||
Fontconfig_FOUND |
|||
REQUIRED_VARS |
|||
Fontconfig_LIBRARY |
|||
Fontconfig_INCLUDE_DIR |
|||
VERSION_VAR |
|||
Fontconfig_VERSION |
|||
) |
|||
|
|||
|
|||
if(Fontconfig_FOUND AND NOT TARGET Fontconfig::Fontconfig) |
|||
add_library(Fontconfig::Fontconfig UNKNOWN IMPORTED) |
|||
set_target_properties(Fontconfig::Fontconfig PROPERTIES |
|||
IMPORTED_LOCATION "${Fontconfig_LIBRARY}" |
|||
INTERFACE_COMPILE_OPTIONS "${Fontconfig_COMPILE_OPTIONS}" |
|||
INTERFACE_INCLUDE_DIRECTORIES "${Fontconfig_INCLUDE_DIR}" |
|||
) |
|||
endif() |
|||
|
|||
mark_as_advanced(Fontconfig_LIBRARY Fontconfig_INCLUDE_DIR) |
|||
|
|||
if(Fontconfig_FOUND) |
|||
set(Fontconfig_LIBRARIES ${Fontconfig_LIBRARY}) |
|||
set(Fontconfig_INCLUDE_DIRS ${Fontconfig_INCLUDE_DIR}) |
|||
endif() |
|||
@ -0,0 +1,187 @@ |
|||
# Copyright (c) 2012, Intel Corporation |
|||
# Copyright (c) 2019 Sony Interactive Entertainment Inc. |
|||
# |
|||
# Redistribution and use in source and binary forms, with or without |
|||
# modification, are permitted provided that the following conditions are met: |
|||
# |
|||
# * Redistributions of source code must retain the above copyright notice, this |
|||
# list of conditions and the following disclaimer. |
|||
# * Redistributions in binary form must reproduce the above copyright notice, |
|||
# this list of conditions and the following disclaimer in the documentation |
|||
# and/or other materials provided with the distribution. |
|||
# * Neither the name of Intel Corporation nor the names of its contributors may |
|||
# be used to endorse or promote products derived from this software without |
|||
# specific prior written permission. |
|||
# |
|||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|||
# POSSIBILITY OF SUCH DAMAGE. |
|||
# |
|||
# Try to find Harfbuzz include and library directories. |
|||
# |
|||
# After successful discovery, this will set for inclusion where needed: |
|||
# HarfBuzz_INCLUDE_DIRS - containg the HarfBuzz headers |
|||
# HarfBuzz_LIBRARIES - containg the HarfBuzz library |
|||
|
|||
#[=======================================================================[.rst: |
|||
FindHarfBuzz |
|||
-------------- |
|||
|
|||
Find HarfBuzz headers and libraries. |
|||
|
|||
Imported Targets |
|||
^^^^^^^^^^^^^^^^ |
|||
|
|||
``HarfBuzz::HarfBuzz`` |
|||
The HarfBuzz library, if found. |
|||
|
|||
``HarfBuzz::ICU`` |
|||
The HarfBuzz ICU library, if found. |
|||
|
|||
Result Variables |
|||
^^^^^^^^^^^^^^^^ |
|||
|
|||
This will define the following variables in your project: |
|||
|
|||
``HarfBuzz_FOUND`` |
|||
true if (the requested version of) HarfBuzz is available. |
|||
``HarfBuzz_VERSION`` |
|||
the version of HarfBuzz. |
|||
``HarfBuzz_LIBRARIES`` |
|||
the libraries to link against to use HarfBuzz. |
|||
``HarfBuzz_INCLUDE_DIRS`` |
|||
where to find the HarfBuzz headers. |
|||
``HarfBuzz_COMPILE_OPTIONS`` |
|||
this should be passed to target_compile_options(), if the |
|||
target is not used for linking |
|||
|
|||
#]=======================================================================] |
|||
|
|||
find_package(PkgConfig QUIET) |
|||
pkg_check_modules(PC_HARFBUZZ QUIET harfbuzz) |
|||
set(HarfBuzz_COMPILE_OPTIONS ${PC_HARFBUZZ_CFLAGS_OTHER}) |
|||
set(HarfBuzz_VERSION ${PC_HARFBUZZ_CFLAGS_VERSION}) |
|||
|
|||
find_path(HarfBuzz_INCLUDE_DIR |
|||
NAMES hb.h |
|||
HINTS ${PC_HARFBUZZ_INCLUDEDIR} ${PC_HARFBUZZ_INCLUDE_DIRS} |
|||
PATH_SUFFIXES harfbuzz |
|||
) |
|||
|
|||
find_library(HarfBuzz_LIBRARY |
|||
NAMES ${HarfBuzz_NAMES} harfbuzz |
|||
HINTS ${PC_HARFBUZZ_LIBDIR} ${PC_HARFBUZZ_LIBRARY_DIRS} |
|||
) |
|||
|
|||
if (HarfBuzz_INCLUDE_DIR AND NOT HarfBuzz_VERSION) |
|||
if (EXISTS "${HarfBuzz_INCLUDE_DIR}/hb-version.h") |
|||
file(READ "${HarfBuzz_INCLUDE_DIR}/hb-version.h" _harfbuzz_version_content) |
|||
|
|||
string(REGEX MATCH "#define +HB_VERSION_STRING +\"([0-9]+\.[0-9]+\.[0-9]+)\"" _dummy "${_harfbuzz_version_content}") |
|||
set(HarfBuzz_VERSION "${CMAKE_MATCH_1}") |
|||
endif () |
|||
endif () |
|||
|
|||
if ("${HarfBuzz_FIND_VERSION}" VERSION_GREATER "${HarfBuzz_VERSION}") |
|||
message(FATAL_ERROR "Required version (" ${HarfBuzz_FIND_VERSION} ") is higher than found version (" ${HarfBuzz_VERSION} ")") |
|||
endif () |
|||
|
|||
# Find components |
|||
if (HarfBuzz_INCLUDE_DIR AND HarfBuzz_LIBRARY) |
|||
set(_HarfBuzz_REQUIRED_LIBS_FOUND ON) |
|||
set(HarfBuzz_LIBS_FOUND "HarfBuzz (required): ${HarfBuzz_LIBRARY}") |
|||
else () |
|||
set(_HarfBuzz_REQUIRED_LIBS_FOUND OFF) |
|||
set(HarfBuzz_LIBS_NOT_FOUND "HarfBuzz (required)") |
|||
endif () |
|||
|
|||
if ("ICU" IN_LIST HarfBuzz_FIND_COMPONENTS) |
|||
pkg_check_modules(PC_HARFBUZZ_ICU QUIET harfbuzz-icu) |
|||
set(HarfBuzz_ICU_COMPILE_OPTIONS ${PC_HARFBUZZ_ICU_CFLAGS_OTHER}) |
|||
|
|||
find_path(HarfBuzz_ICU_INCLUDE_DIR |
|||
NAMES hb-icu.h |
|||
HINTS ${PC_HARFBUZZ_ICU_INCLUDEDIR} ${PC_HARFBUZZ_ICU_INCLUDE_DIRS} |
|||
PATH_SUFFIXES harfbuzz |
|||
) |
|||
|
|||
find_library(HarfBuzz_ICU_LIBRARY |
|||
NAMES ${HarfBuzz_ICU_NAMES} harfbuzz-icu |
|||
HINTS ${PC_HARFBUZZ_ICU_LIBDIR} ${PC_HARFBUZZ_ICU_LIBRARY_DIRS} |
|||
) |
|||
|
|||
if (HarfBuzz_ICU_LIBRARY) |
|||
if (HarfBuzz_FIND_REQUIRED_ICU) |
|||
list(APPEND HarfBuzz_LIBS_FOUND "ICU (required): ${HarfBuzz_ICU_LIBRARY}") |
|||
else () |
|||
list(APPEND HarfBuzz_LIBS_FOUND "ICU (optional): ${HarfBuzz_ICU_LIBRARY}") |
|||
endif () |
|||
else () |
|||
if (HarfBuzz_FIND_REQUIRED_ICU) |
|||
set(_HarfBuzz_REQUIRED_LIBS_FOUND OFF) |
|||
list(APPEND HarfBuzz_LIBS_NOT_FOUND "ICU (required)") |
|||
else () |
|||
list(APPEND HarfBuzz_LIBS_NOT_FOUND "ICU (optional)") |
|||
endif () |
|||
endif () |
|||
endif () |
|||
|
|||
if (NOT HarfBuzz_FIND_QUIETLY) |
|||
if (HarfBuzz_LIBS_FOUND) |
|||
message(STATUS "Found the following HarfBuzz libraries:") |
|||
foreach (found ${HarfBuzz_LIBS_FOUND}) |
|||
message(STATUS " ${found}") |
|||
endforeach () |
|||
endif () |
|||
if (HarfBuzz_LIBS_NOT_FOUND) |
|||
message(STATUS "The following HarfBuzz libraries were not found:") |
|||
foreach (found ${HarfBuzz_LIBS_NOT_FOUND}) |
|||
message(STATUS " ${found}") |
|||
endforeach () |
|||
endif () |
|||
endif () |
|||
|
|||
include(FindPackageHandleStandardArgs) |
|||
find_package_handle_standard_args(HarfBuzz |
|||
FOUND_VAR HarfBuzz_FOUND |
|||
REQUIRED_VARS HarfBuzz_INCLUDE_DIR HarfBuzz_LIBRARY _HarfBuzz_REQUIRED_LIBS_FOUND |
|||
VERSION_VAR HarfBuzz_VERSION |
|||
) |
|||
|
|||
if (HarfBuzz_LIBRARY AND NOT TARGET HarfBuzz::HarfBuzz) |
|||
add_library(HarfBuzz::HarfBuzz UNKNOWN IMPORTED GLOBAL) |
|||
set_target_properties(HarfBuzz::HarfBuzz PROPERTIES |
|||
IMPORTED_LOCATION "${HarfBuzz_LIBRARY}" |
|||
INTERFACE_COMPILE_OPTIONS "${HarfBuzz_COMPILE_OPTIONS}" |
|||
INTERFACE_INCLUDE_DIRECTORIES "${HarfBuzz_INCLUDE_DIR}" |
|||
) |
|||
endif () |
|||
|
|||
if (HarfBuzz_ICU_LIBRARY AND NOT TARGET HarfBuzz::ICU) |
|||
add_library(HarfBuzz::ICU UNKNOWN IMPORTED GLOBAL) |
|||
set_target_properties(HarfBuzz::ICU PROPERTIES |
|||
IMPORTED_LOCATION "${HarfBuzz_ICU_LIBRARY}" |
|||
INTERFACE_COMPILE_OPTIONS "${HarfBuzz_ICU_COMPILE_OPTIONS}" |
|||
INTERFACE_INCLUDE_DIRECTORIES "${HarfBuzz_ICU_INCLUDE_DIR}" |
|||
) |
|||
endif () |
|||
|
|||
mark_as_advanced( |
|||
HarfBuzz_INCLUDE_DIR |
|||
HarfBuzz_ICU_INCLUDE_DIR |
|||
HarfBuzz_LIBRARY |
|||
HarfBuzz_ICU_LIBRARY |
|||
) |
|||
|
|||
if (HarfBuzz_FOUND) |
|||
set(HarfBuzz_LIBRARIES ${HarfBuzz_LIBRARY} ${HarfBuzz_ICU_LIBRARY}) |
|||
set(HarfBuzz_INCLUDE_DIRS ${HarfBuzz_INCLUDE_DIR} ${HarfBuzz_ICU_INCLUDE_DIR}) |
|||
endif () |
|||
@ -0,0 +1,133 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021-2022 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 <iostream>
|
|||
#include <sstream>
|
|||
#include <font/fontconfig.h>
|
|||
#include <pgm_base.h>
|
|||
#include <settings/settings_manager.h>
|
|||
|
|||
using namespace fontconfig; |
|||
|
|||
static FONTCONFIG* g_config = nullptr; |
|||
|
|||
inline static FcChar8* wxStringToFcChar8( const wxString& str ) |
|||
{ |
|||
wxScopedCharBuffer const fcBuffer = str.ToUTF8(); |
|||
return (FcChar8*) fcBuffer.data(); |
|||
} |
|||
|
|||
|
|||
FONTCONFIG::FONTCONFIG() |
|||
{ |
|||
m_config = FcInitLoadConfigAndFonts(); |
|||
|
|||
wxString configDirPath( Pgm().GetSettingsManager().GetUserSettingsPath() + wxT( "/fonts" ) ); |
|||
FcConfigAppFontAddDir( nullptr, wxStringToFcChar8( configDirPath ) ); |
|||
}; |
|||
|
|||
|
|||
FONTCONFIG& Fontconfig() |
|||
{ |
|||
if( !g_config ) |
|||
{ |
|||
FcInit(); |
|||
g_config = new FONTCONFIG(); |
|||
} |
|||
|
|||
return *g_config; |
|||
} |
|||
|
|||
|
|||
bool FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile ) |
|||
{ |
|||
FcPattern* pat = FcNameParse( wxStringToFcChar8( aFontName ) ); |
|||
FcConfigSubstitute( nullptr, pat, FcMatchPattern ); |
|||
FcDefaultSubstitute( pat ); |
|||
|
|||
FcResult r = FcResultNoMatch; |
|||
FcPattern* font = FcFontMatch( nullptr, pat, &r ); |
|||
|
|||
bool ok = false; |
|||
|
|||
if( font ) |
|||
{ |
|||
FcChar8* file = nullptr; |
|||
|
|||
if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch ) |
|||
{ |
|||
aFontFile = wxString::FromUTF8( (char*) file ); |
|||
ok = true; |
|||
} |
|||
|
|||
FcPatternDestroy( font ); |
|||
} |
|||
|
|||
FcPatternDestroy( pat ); |
|||
return ok; |
|||
} |
|||
|
|||
|
|||
void FONTCONFIG::ListFonts( std::vector<std::string>& aFonts ) |
|||
{ |
|||
if( m_fonts.empty() ) |
|||
{ |
|||
FcPattern* pat = FcPatternCreate(); |
|||
FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_STYLE, FC_LANG, FC_FILE, nullptr ); |
|||
FcFontSet* fs = FcFontList( nullptr, pat, os ); |
|||
|
|||
for( int i = 0; fs && i < fs->nfont; ++i ) |
|||
{ |
|||
FcPattern* font = fs->fonts[i]; |
|||
FcChar8* file; |
|||
FcChar8* style; |
|||
FcChar8* family; |
|||
|
|||
if( FcPatternGetString( font, FC_FILE, 0, &file ) == FcResultMatch |
|||
&& FcPatternGetString( font, FC_FAMILY, 0, &family ) == FcResultMatch |
|||
&& FcPatternGetString( font, FC_STYLE, 0, &style ) == FcResultMatch ) |
|||
{ |
|||
std::ostringstream s; |
|||
s << family; |
|||
|
|||
std::string theFile( (char*) file ); |
|||
std::string theFamily( (char*) family ); |
|||
std::string theStyle( (char*) style ); |
|||
FONTINFO fontInfo( theFile, theStyle, theFamily ); |
|||
|
|||
if( theFamily.length() > 0 && theFamily.front() == '.' ) |
|||
continue; |
|||
|
|||
auto it = m_fonts.find( theFamily ); |
|||
|
|||
if( it == m_fonts.end() ) |
|||
m_fonts.insert( std::pair<std::string, FONTINFO>( theFamily, fontInfo ) ); |
|||
else |
|||
it->second.Children().push_back( fontInfo ); |
|||
} |
|||
} |
|||
|
|||
if( fs ) |
|||
FcFontSetDestroy( fs ); |
|||
} |
|||
|
|||
for( const std::pair<const std::string, FONTINFO>& entry : m_fonts ) |
|||
aFonts.push_back( entry.second.Family() ); |
|||
} |
|||
@ -0,0 +1,325 @@ |
|||
/*
|
|||
* 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. |
|||
* |
|||
* Outline 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 <font/outline_decomposer.h>
|
|||
#include <bezier_curves.h>
|
|||
|
|||
using namespace KIFONT; |
|||
|
|||
OUTLINE_DECOMPOSER::OUTLINE_DECOMPOSER( FT_Outline& aOutline ) : |
|||
m_outline( aOutline ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
static VECTOR2D toVector2D( const FT_Vector* aFreeTypeVector ) |
|||
{ |
|||
return VECTOR2D( aFreeTypeVector->x, aFreeTypeVector->y ); |
|||
} |
|||
|
|||
|
|||
void OUTLINE_DECOMPOSER::newContour() |
|||
{ |
|||
CONTOUR contour; |
|||
contour.orientation = FT_Outline_Get_Orientation( &m_outline ); |
|||
m_contours->push_back( contour ); |
|||
} |
|||
|
|||
|
|||
void OUTLINE_DECOMPOSER::addContourPoint( const VECTOR2D& p ) |
|||
{ |
|||
// don't add repeated points
|
|||
if( m_contours->back().points.empty() || m_contours->back().points.back() != p ) |
|||
m_contours->back().points.push_back( p ); |
|||
} |
|||
|
|||
|
|||
int OUTLINE_DECOMPOSER::moveTo( const FT_Vector* aEndPoint, void* aCallbackData ) |
|||
{ |
|||
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData ); |
|||
|
|||
decomposer->m_lastEndPoint.x = aEndPoint->x; |
|||
decomposer->m_lastEndPoint.y = aEndPoint->y; |
|||
|
|||
decomposer->newContour(); |
|||
decomposer->addContourPoint( decomposer->m_lastEndPoint ); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int OUTLINE_DECOMPOSER::lineTo( const FT_Vector* aEndPoint, void* aCallbackData ) |
|||
{ |
|||
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData ); |
|||
|
|||
decomposer->m_lastEndPoint.x = aEndPoint->x; |
|||
decomposer->m_lastEndPoint.y = aEndPoint->y; |
|||
|
|||
decomposer->addContourPoint( decomposer->m_lastEndPoint ); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int OUTLINE_DECOMPOSER::quadraticTo( const FT_Vector* aControlPoint, const FT_Vector* aEndPoint, |
|||
void* aCallbackData ) |
|||
{ |
|||
return cubicTo( aControlPoint, nullptr, aEndPoint, aCallbackData ); |
|||
} |
|||
|
|||
|
|||
int OUTLINE_DECOMPOSER::cubicTo( const FT_Vector* aFirstControlPoint, |
|||
const FT_Vector* aSecondControlPoint, const FT_Vector* aEndPoint, |
|||
void* aCallbackData ) |
|||
{ |
|||
OUTLINE_DECOMPOSER* decomposer = static_cast<OUTLINE_DECOMPOSER*>( aCallbackData ); |
|||
|
|||
GLYPH_POINTS bezier; |
|||
bezier.push_back( decomposer->m_lastEndPoint ); |
|||
bezier.push_back( toVector2D( aFirstControlPoint ) ); |
|||
|
|||
if( aSecondControlPoint ) |
|||
{ |
|||
// aSecondControlPoint == nullptr for quadratic Beziers
|
|||
bezier.push_back( toVector2D( aSecondControlPoint ) ); |
|||
} |
|||
|
|||
bezier.push_back( toVector2D( aEndPoint ) ); |
|||
|
|||
GLYPH_POINTS result; |
|||
decomposer->approximateBezierCurve( result, bezier ); |
|||
|
|||
for( const VECTOR2D& p : result ) |
|||
decomposer->addContourPoint( p ); |
|||
|
|||
decomposer->m_lastEndPoint.x = aEndPoint->x; |
|||
decomposer->m_lastEndPoint.y = aEndPoint->y; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
void OUTLINE_DECOMPOSER::OutlineToSegments( CONTOURS* aContours ) |
|||
{ |
|||
m_contours = aContours; |
|||
|
|||
FT_Outline_Funcs callbacks; |
|||
|
|||
callbacks.move_to = moveTo; |
|||
callbacks.line_to = lineTo; |
|||
callbacks.conic_to = quadraticTo; |
|||
callbacks.cubic_to = cubicTo; |
|||
callbacks.shift = 0; |
|||
callbacks.delta = 0; |
|||
|
|||
FT_Error e = FT_Outline_Decompose( &m_outline, &callbacks, this ); |
|||
|
|||
if( e ) |
|||
{ |
|||
// TODO: handle error != 0
|
|||
} |
|||
|
|||
for( CONTOUR& c : *m_contours ) |
|||
c.winding = winding( c.points ); |
|||
} |
|||
|
|||
|
|||
// use converter in kimath
|
|||
bool OUTLINE_DECOMPOSER::approximateQuadraticBezierCurve( GLYPH_POINTS& aResult, |
|||
const GLYPH_POINTS& aBezier ) const |
|||
{ |
|||
// TODO: assert aBezier.size == 3
|
|||
|
|||
// BEZIER_POLY only handles cubic Bezier curves, even though the comments say otherwise...
|
|||
//
|
|||
// Quadratic to cubic Bezier conversion:
|
|||
// cpn = Cubic Bezier control points (n = 0..3, 4 in total)
|
|||
// qpn = Quadratic Bezier control points (n = 0..2, 3 in total)
|
|||
// cp0 = qp0, cp1 = qp0 + 2/3 * (qp1 - qp0), cp2 = qp2 + 2/3 * (qp1 - qp2), cp3 = qp2
|
|||
|
|||
static const double twoThirds = 2 / 3.0; |
|||
|
|||
GLYPH_POINTS cubic; |
|||
cubic.push_back( aBezier.at( 0 ) ); // cp0
|
|||
cubic.push_back( aBezier.at( 0 ) + twoThirds * ( aBezier.at( 1 ) - aBezier.at( 0 ) ) ); // cp1
|
|||
cubic.push_back( aBezier.at( 2 ) + twoThirds * ( aBezier.at( 1 ) - aBezier.at( 2 ) ) ); // cp2
|
|||
cubic.push_back( aBezier.at( 2 ) ); // cp3
|
|||
|
|||
return approximateCubicBezierCurve( aResult, cubic ); |
|||
} |
|||
|
|||
|
|||
bool OUTLINE_DECOMPOSER::approximateCubicBezierCurve( GLYPH_POINTS& aResult, |
|||
const GLYPH_POINTS& aCubicBezier ) const |
|||
{ |
|||
// TODO: assert aCubicBezier.size == 4
|
|||
|
|||
// TODO: find out what the minimum segment length should really be!
|
|||
static const int minimumSegmentLength = 50; |
|||
GLYPH_POINTS tmp; |
|||
BEZIER_POLY converter( aCubicBezier ); |
|||
converter.GetPoly( tmp, minimumSegmentLength ); |
|||
|
|||
for( unsigned int i = 0; i < tmp.size(); i++ ) |
|||
aResult.push_back( tmp.at( i ) ); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
bool OUTLINE_DECOMPOSER::approximateBezierCurve( GLYPH_POINTS& aResult, |
|||
const GLYPH_POINTS& aBezier ) const |
|||
{ |
|||
bool bezierIsCubic = ( aBezier.size() == 4 ); |
|||
|
|||
if( bezierIsCubic ) |
|||
return approximateCubicBezierCurve( aResult, aBezier ); |
|||
else |
|||
return approximateQuadraticBezierCurve( aResult, aBezier ); |
|||
} |
|||
|
|||
|
|||
int OUTLINE_DECOMPOSER::winding( const GLYPH_POINTS& aContour ) const |
|||
{ |
|||
// -1 == counterclockwise, 1 == clockwise
|
|||
|
|||
const int cw = 1; |
|||
const int ccw = -1; |
|||
|
|||
if( aContour.size() < 2 ) |
|||
{ |
|||
// zero or one points, so not a clockwise contour - in fact not a contour at all
|
|||
//
|
|||
// It could also be argued that a contour needs 3 extremum points at a minimum to be
|
|||
// considered a proper contour (ie. a glyph (subpart) outline, or a hole)
|
|||
return 0; |
|||
} |
|||
|
|||
unsigned int i_lowest_vertex; |
|||
double lowest_y = std::numeric_limits<double>::max(); |
|||
|
|||
for( unsigned int i = 0; i < aContour.size(); i++ ) |
|||
{ |
|||
VECTOR2D p = aContour[i]; |
|||
|
|||
if( p.y < lowest_y ) |
|||
{ |
|||
i_lowest_vertex = i; |
|||
lowest_y = p.y; |
|||
|
|||
// note: we should also check for p.y == lowest_y and then choose the point with
|
|||
// leftmost.x, but as p.x is a double, equality is a dubious concept; however
|
|||
// this should suffice in the general case
|
|||
} |
|||
} |
|||
|
|||
unsigned int i_prev_vertex; |
|||
unsigned int i_next_vertex; |
|||
|
|||
// TODO: this should be done with modulo arithmetic for clarity
|
|||
if( i_lowest_vertex == 0 ) |
|||
i_prev_vertex = aContour.size() - 1; |
|||
else |
|||
i_prev_vertex = i_lowest_vertex - 1; |
|||
|
|||
if( i_lowest_vertex == aContour.size() - 1 ) |
|||
i_next_vertex = 0; |
|||
else |
|||
i_next_vertex = i_lowest_vertex + 1; |
|||
|
|||
const VECTOR2D& lowest = aContour[i_lowest_vertex]; |
|||
VECTOR2D prev( aContour[i_prev_vertex] ); |
|||
|
|||
while( prev == lowest ) |
|||
{ |
|||
if( i_prev_vertex == 0 ) |
|||
i_prev_vertex = aContour.size() - 1; |
|||
else |
|||
i_prev_vertex--; |
|||
|
|||
if( i_prev_vertex == i_lowest_vertex ) |
|||
{ |
|||
// ERROR: degenerate contour (all points are equal)
|
|||
// TODO: signal error
|
|||
// for now let's just return something at random
|
|||
return cw; |
|||
} |
|||
|
|||
prev = aContour[i_prev_vertex]; |
|||
} |
|||
|
|||
VECTOR2D next( aContour[i_next_vertex] ); |
|||
|
|||
while( next == lowest ) |
|||
{ |
|||
if( i_next_vertex == aContour.size() - 1 ) |
|||
i_next_vertex = 0; |
|||
else |
|||
i_next_vertex++; |
|||
|
|||
if( i_next_vertex == i_lowest_vertex ) |
|||
{ |
|||
// ERROR: degenerate contour (all points are equal)
|
|||
// TODO: signal error
|
|||
// for now let's just return something at random
|
|||
return cw; |
|||
} |
|||
|
|||
next = aContour[i_next_vertex]; |
|||
} |
|||
|
|||
// winding is figured out based on the angle between the lowest
|
|||
// vertex and its neighbours
|
|||
//
|
|||
// prev.x < lowest.x && next.x > lowest.x -> ccw
|
|||
//
|
|||
// prev.x > lowest.x && next.x < lowest.x -> cw
|
|||
//
|
|||
// prev.x < lowest.x && next.x < lowest.x:
|
|||
// ?
|
|||
//
|
|||
// prev.x > lowest.x && next.x > lowest.x:
|
|||
// ?
|
|||
//
|
|||
if( prev.x < lowest.x && next.x > lowest.x ) |
|||
return ccw; |
|||
|
|||
if( prev.x > lowest.x && next.x < lowest.x ) |
|||
return cw; |
|||
|
|||
double prev_deltaX = prev.x - lowest.x; |
|||
double prev_deltaY = prev.y - lowest.y; |
|||
double next_deltaX = next.x - lowest.x; |
|||
double next_deltaY = next.y - lowest.y; |
|||
|
|||
double prev_atan = atan2( prev_deltaY, prev_deltaX ); |
|||
double next_atan = atan2( next_deltaY, next_deltaX ); |
|||
|
|||
if( prev_atan > next_atan ) |
|||
return ccw; |
|||
else |
|||
return cw; |
|||
} |
|||
@ -0,0 +1,623 @@ |
|||
/*
|
|||
* 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-2022 Kicad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* Outline 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 <limits>
|
|||
#include <pgm_base.h>
|
|||
#include <settings/settings_manager.h>
|
|||
#include <harfbuzz/hb-ft.h>
|
|||
#include <bezier_curves.h>
|
|||
#include <geometry/shape_poly_set.h>
|
|||
#include <eda_text.h>
|
|||
#include <font/outline_font.h>
|
|||
#include FT_GLYPH_H
|
|||
#include FT_BBOX_H
|
|||
#include <trigo.h>
|
|||
#include <font/fontconfig.h>
|
|||
|
|||
using namespace KIFONT; |
|||
|
|||
FT_Library OUTLINE_FONT::m_freeType = nullptr; |
|||
|
|||
OUTLINE_FONT::OUTLINE_FONT() : |
|||
m_faceSize( 16 ), |
|||
m_subscriptSize( 13 ) |
|||
{ |
|||
if( !m_freeType ) |
|||
{ |
|||
//FT_Error ft_error = FT_Init_FreeType( &m_freeType );
|
|||
// TODO: handle ft_error
|
|||
FT_Init_FreeType( &m_freeType ); |
|||
} |
|||
} |
|||
|
|||
|
|||
OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, bool aItalic ) |
|||
{ |
|||
OUTLINE_FONT* font = new OUTLINE_FONT(); |
|||
|
|||
wxString fontFile; |
|||
wxString qualifiedFontName = aFontName; |
|||
|
|||
if( aBold ) |
|||
qualifiedFontName << ":bold"; |
|||
|
|||
if( aItalic ) |
|||
qualifiedFontName << ":italic"; |
|||
|
|||
if( Fontconfig().FindFont( qualifiedFontName, fontFile ) ) |
|||
(void) font->loadFace( fontFile ); |
|||
else |
|||
(void) font->loadFontSimple( aFontName ); |
|||
|
|||
return font; |
|||
} |
|||
|
|||
|
|||
bool OUTLINE_FONT::loadFontSimple( const wxString& aFontFileName ) |
|||
{ |
|||
wxFileName fontFile( aFontFileName ); |
|||
wxString fileName = fontFile.GetFullPath(); |
|||
// TODO: handle ft_error properly (now we just return false if load does not succeed)
|
|||
FT_Error ft_error = loadFace( fileName ); |
|||
|
|||
if( ft_error ) |
|||
{ |
|||
// Try user dir
|
|||
fontFile.SetExt( "otf" ); |
|||
fontFile.SetPath( Pgm().GetSettingsManager().GetUserSettingsPath() + wxT( "/fonts" ) ); |
|||
fileName = fontFile.GetFullPath(); |
|||
|
|||
if( wxFile::Exists( fileName ) ) |
|||
{ |
|||
ft_error = loadFace( fileName ); |
|||
} |
|||
else |
|||
{ |
|||
fontFile.SetExt( "ttf" ); |
|||
fileName = fontFile.GetFullPath(); |
|||
|
|||
if( wxFile::Exists( fileName ) ) |
|||
ft_error = loadFace( fileName ); |
|||
} |
|||
} |
|||
|
|||
if( ft_error == FT_Err_Unknown_File_Format ) |
|||
{ |
|||
std::cerr << "The font file " << fileName << " could be opened and read, " |
|||
<< "but it appears that its font format is unsupported." << std::endl; |
|||
} |
|||
else if( ft_error ) |
|||
{ |
|||
std::cerr << "ft_error " << ft_error << std::endl; |
|||
return false; |
|||
} |
|||
else |
|||
{ |
|||
m_fontName = aFontFileName; |
|||
m_fontFileName = fileName; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
FT_Error OUTLINE_FONT::loadFace( const wxString& aFontFileName ) |
|||
{ |
|||
m_faceScaler = m_faceSize * 64; |
|||
m_subscriptFaceScaler = m_subscriptSize * 64; |
|||
|
|||
// TODO: check that going from wxString to char* with UTF-8
|
|||
// conversion for filename makes sense on any/all platforms
|
|||
FT_Error e = FT_New_Face( m_freeType, aFontFileName.mb_str( wxConvUTF8 ), 0, &m_face ); |
|||
|
|||
if( !e ) |
|||
{ |
|||
FT_Select_Charmap( m_face, FT_Encoding::FT_ENCODING_UNICODE ); |
|||
FT_Set_Char_Size( m_face, 0, m_faceScaler, 0, 0 ); |
|||
|
|||
e = FT_New_Face( m_freeType, aFontFileName.mb_str( wxConvUTF8 ), 0, &m_subscriptFace ); |
|||
|
|||
if( !e ) |
|||
{ |
|||
FT_Select_Charmap( m_subscriptFace, FT_Encoding::FT_ENCODING_UNICODE ); |
|||
FT_Set_Char_Size( m_subscriptFace, 0, m_subscriptFaceScaler, 0, 0 ); |
|||
|
|||
m_fontName = wxString( m_face->family_name ); |
|||
m_fontFileName = aFontFileName; |
|||
} |
|||
} |
|||
|
|||
return e; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Compute the boundary limits of aText (the bounding box of all shapes). |
|||
* |
|||
* @return a VECTOR2D giving the width and height of text. |
|||
*/ |
|||
VECTOR2D OUTLINE_FONT::StringBoundaryLimits( const KIGFX::GAL* aGal, const UTF8& aText, |
|||
const VECTOR2D& aGlyphSize, |
|||
double aGlyphThickness ) const |
|||
{ |
|||
hb_buffer_t* buf = hb_buffer_create(); |
|||
hb_buffer_add_utf8( buf, aText.c_str(), -1, 0, -1 ); |
|||
|
|||
// guess direction, script, and language based on contents
|
|||
hb_buffer_guess_segment_properties( buf ); |
|||
|
|||
unsigned int glyphCount; |
|||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); |
|||
hb_font_t* referencedFont = hb_ft_font_create_referenced( m_face ); |
|||
//hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
|||
|
|||
hb_ft_font_set_funcs( referencedFont ); |
|||
hb_shape( referencedFont, buf, nullptr, 0 ); |
|||
|
|||
int width = 0; |
|||
int height = m_face->size->metrics.height; |
|||
|
|||
FT_UInt previous; |
|||
|
|||
for( int i = 0; i < (int) glyphCount; i++ ) |
|||
{ |
|||
//hb_glyph_position_t& pos = glyphPos[i];
|
|||
int codepoint = glyphInfo[i].codepoint; |
|||
|
|||
if( i > 0 ) |
|||
{ |
|||
FT_Vector delta; |
|||
FT_Get_Kerning( m_face, previous, codepoint, FT_KERNING_DEFAULT, &delta ); |
|||
width += delta.x >> 6; |
|||
} |
|||
|
|||
FT_Load_Glyph( m_face, codepoint, FT_LOAD_NO_BITMAP ); |
|||
FT_GlyphSlot glyph = m_face->glyph; |
|||
|
|||
width += glyph->advance.x >> 6; |
|||
previous = codepoint; |
|||
} |
|||
|
|||
return VECTOR2D( width * m_faceScaler, height * m_faceScaler ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Compute the vertical position of an overbar. This is the distance between the text |
|||
* baseline and the overbar. |
|||
*/ |
|||
double OUTLINE_FONT::ComputeOverbarVerticalPosition( double aGlyphHeight ) const |
|||
{ |
|||
// TODO: dummy to make this compile! not used
|
|||
return aGlyphHeight; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* 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 OUTLINE_FONT::GetInterline( double aGlyphHeight, double aLineSpacing ) const |
|||
{ |
|||
if( GetFace()->units_per_EM ) |
|||
return ( aLineSpacing * aGlyphHeight * ( GetFace()->height / GetFace()->units_per_EM ) ); |
|||
else |
|||
return ( aLineSpacing * aGlyphHeight * INTERLINE_PITCH_RATIO ); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* Compute the X and Y size of a given text. The text is expected to be a single line. |
|||
*/ |
|||
VECTOR2D OUTLINE_FONT::ComputeTextLineSize( const KIGFX::GAL* aGal, const UTF8& aText ) const |
|||
{ |
|||
return StringBoundaryLimits( aGal, aText, aGal->GetGlyphSize(), 0.0 ); |
|||
} |
|||
|
|||
|
|||
static bool contourIsFilled( const CONTOUR& c ) |
|||
{ |
|||
switch( c.orientation ) |
|||
{ |
|||
case FT_ORIENTATION_TRUETYPE: return c.winding == 1; |
|||
case FT_ORIENTATION_POSTSCRIPT: return c.winding == -1; |
|||
default: return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
static bool contourIsHole( const CONTOUR& c ) |
|||
{ |
|||
return !contourIsFilled( c ); |
|||
} |
|||
|
|||
|
|||
BOX2I OUTLINE_FONT::getBoundingBox( const std::vector<std::unique_ptr<GLYPH>>& aGlyphs ) const |
|||
{ |
|||
int minX = INT_MAX; |
|||
int minY = INT_MAX; |
|||
int maxX = INT_MIN; |
|||
int maxY = INT_MIN; |
|||
|
|||
for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aGlyphs ) |
|||
{ |
|||
BOX2D bbox = glyph->BoundingBox(); |
|||
bbox.Normalize(); |
|||
|
|||
if( minX > bbox.GetX() ) |
|||
minX = bbox.GetX(); |
|||
|
|||
if( minY > bbox.GetY() ) |
|||
minY = bbox.GetY(); |
|||
|
|||
if( maxX < bbox.GetRight() ) |
|||
maxX = bbox.GetRight(); |
|||
|
|||
if( maxY < bbox.GetBottom() ) |
|||
maxY = bbox.GetBottom(); |
|||
} |
|||
|
|||
BOX2I ret; |
|||
ret.SetOrigin( minX, minY ); |
|||
ret.SetEnd( maxX, maxY ); |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
VECTOR2I OUTLINE_FONT::GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>& aGlyphs, |
|||
const EDA_TEXT* aText ) const |
|||
{ |
|||
wxArrayString strings; |
|||
std::vector<wxPoint> positions; |
|||
int n; |
|||
VECTOR2I ret; |
|||
std::vector<VECTOR2D> boundingBoxes; |
|||
TEXT_STYLE_FLAGS textStyle = 0; |
|||
|
|||
if( aText->IsItalic() ) |
|||
textStyle |= TEXT_STYLE::ITALIC; |
|||
|
|||
getLinePositions( aText->GetShownText(), aText->GetTextPos(), strings, positions, n, |
|||
boundingBoxes, aText->GetAttributes() ); |
|||
|
|||
for( int i = 0; i < n; i++ ) |
|||
{ |
|||
ret = drawMarkup( nullptr, aGlyphs, UTF8( strings.Item( i ) ), positions[i], |
|||
aText->GetTextSize(), aText->GetTextAngle(), textStyle ); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
|
|||
VECTOR2I OUTLINE_FONT::GetTextAsGlyphs( BOX2I* aBoundingBox, |
|||
std::vector<std::unique_ptr<GLYPH>>& aGlyphs, |
|||
const UTF8& aText, const VECTOR2D& aGlyphSize, |
|||
const wxPoint& aPosition, const EDA_ANGLE& aOrientation, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const |
|||
{ |
|||
hb_buffer_t* buf = hb_buffer_create(); |
|||
hb_buffer_add_utf8( buf, aText.c_str(), -1, 0, -1 ); |
|||
|
|||
// guess direction, script, and language based on contents
|
|||
hb_buffer_guess_segment_properties( buf ); |
|||
|
|||
unsigned int glyphCount; |
|||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); |
|||
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount ); |
|||
hb_font_t* referencedFont; |
|||
|
|||
//const double subscriptAndSuperscriptScaler = 0.5;
|
|||
VECTOR2D glyphSize = aGlyphSize; |
|||
FT_Face face = m_face; |
|||
int scaler = m_faceScaler; |
|||
|
|||
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) ) |
|||
{ |
|||
face = m_subscriptFace; |
|||
//scaler = m_subscriptFaceScaler;
|
|||
} |
|||
|
|||
referencedFont = hb_ft_font_create_referenced( face ); |
|||
hb_ft_font_set_funcs( referencedFont ); |
|||
hb_shape( referencedFont, buf, nullptr, 0 ); |
|||
|
|||
const VECTOR2D scaleFactor( -glyphSize.x / scaler, glyphSize.y / scaler ); |
|||
|
|||
VECTOR2I cursor( 0, 0 ); |
|||
VECTOR2I extentBottomLeft( INT_MAX, INT_MAX ); |
|||
VECTOR2I extentTopRight( INT_MIN, INT_MIN ); |
|||
VECTOR2I vBottomLeft( INT_MAX, INT_MAX ); |
|||
VECTOR2I vTopRight( INT_MIN, INT_MIN ); |
|||
|
|||
for( unsigned int i = 0; i < glyphCount; i++ ) |
|||
{ |
|||
hb_glyph_position_t& pos = glyphPos[i]; |
|||
int codepoint = glyphInfo[i].codepoint; |
|||
|
|||
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP ); |
|||
|
|||
FT_GlyphSlot faceGlyph = face->glyph; |
|||
|
|||
// contours is a collection of all outlines in the glyph;
|
|||
// example: glyph for 'o' generally contains 2 contours,
|
|||
// one for the glyph outline and one for the hole
|
|||
CONTOURS contours; |
|||
|
|||
OUTLINE_DECOMPOSER decomposer( faceGlyph->outline ); |
|||
decomposer.OutlineToSegments( &contours ); |
|||
|
|||
std::unique_ptr<OUTLINE_GLYPH> glyph = std::make_unique<OUTLINE_GLYPH>(); |
|||
std::vector<SHAPE_LINE_CHAIN> holes; |
|||
std::vector<SHAPE_LINE_CHAIN> outlines; |
|||
|
|||
for( CONTOUR& c : contours ) |
|||
{ |
|||
GLYPH_POINTS points = c.points; |
|||
SHAPE_LINE_CHAIN shape; |
|||
|
|||
VECTOR2D offset( aPosition ); |
|||
|
|||
if( IsSubscript( aTextStyle ) ) |
|||
offset.y += glyphSize.y * 0.1; |
|||
else if( IsSuperscript( aTextStyle ) ) |
|||
offset.y -= glyphSize.y * 0.2; |
|||
|
|||
for( const VECTOR2D& v : points ) |
|||
{ |
|||
// Save text extents
|
|||
if( vBottomLeft.x > v.x ) |
|||
vBottomLeft.x = v.x; |
|||
if( vBottomLeft.y > v.y ) |
|||
vBottomLeft.y = v.y; |
|||
if( vTopRight.x < v.x ) |
|||
vTopRight.x = v.x; |
|||
if( vTopRight.y < v.y ) |
|||
vTopRight.y = v.y; |
|||
|
|||
VECTOR2D pt( v.x, v.y ); |
|||
VECTOR2D ptC( pt.x + cursor.x, pt.y + cursor.y ); |
|||
wxPoint scaledPtOrig( -ptC.x * scaleFactor.x, -ptC.y * scaleFactor.y ); |
|||
wxPoint scaledPt( scaledPtOrig ); |
|||
RotatePoint( &scaledPt, aOrientation.AsRadians() ); |
|||
scaledPt.x += offset.x; |
|||
scaledPt.y += offset.y; |
|||
|
|||
if( extentBottomLeft.x > scaledPt.x ) |
|||
extentBottomLeft.x = scaledPt.x; |
|||
if( extentBottomLeft.y > scaledPt.y ) |
|||
extentBottomLeft.y = scaledPt.y; |
|||
if( extentTopRight.x < scaledPt.x ) |
|||
extentTopRight.x = scaledPt.x; |
|||
if( extentTopRight.y < scaledPt.y ) |
|||
extentTopRight.y = scaledPt.y; |
|||
|
|||
shape.Append( scaledPt.x, scaledPt.y ); |
|||
//ptListScaled.push_back( scaledPt );
|
|||
} |
|||
|
|||
if( contourIsHole( c ) ) |
|||
holes.push_back( std::move( shape ) ); |
|||
else |
|||
outlines.push_back( std::move( shape ) ); |
|||
} |
|||
|
|||
for( SHAPE_LINE_CHAIN& outline : outlines ) |
|||
{ |
|||
if( outline.PointCount() ) |
|||
{ |
|||
outline.SetClosed( true ); |
|||
glyph->AddOutline( outline ); |
|||
} |
|||
} |
|||
|
|||
int nthHole = 0; |
|||
|
|||
for( SHAPE_LINE_CHAIN& hole : holes ) |
|||
{ |
|||
if( hole.PointCount() ) |
|||
{ |
|||
hole.SetClosed( true ); |
|||
VECTOR2I firstPoint = hole.GetPoint( 0 ); |
|||
//SHAPE_SIMPLE *outlineForHole = nullptr;
|
|||
int nthOutline = -1; |
|||
int n = 0; |
|||
|
|||
for( SHAPE_LINE_CHAIN& outline : outlines ) |
|||
{ |
|||
if( outline.PointInside( firstPoint ) ) |
|||
{ |
|||
//outlineForHole = outline;
|
|||
nthOutline = n; |
|||
break; |
|||
} |
|||
|
|||
n++; |
|||
} |
|||
|
|||
if( nthOutline > -1 ) |
|||
glyph->AddHole( hole, n ); |
|||
} |
|||
|
|||
nthHole++; |
|||
} |
|||
|
|||
aGlyphs.push_back( std::move( glyph ) ); |
|||
|
|||
cursor.x += pos.x_advance; |
|||
cursor.y += pos.y_advance; |
|||
} |
|||
|
|||
VECTOR2I cursorEnd( cursor ); |
|||
|
|||
if( IsOverbar( aTextStyle ) ) |
|||
{ |
|||
std::unique_ptr<OUTLINE_GLYPH> overbarGlyph = std::make_unique<OUTLINE_GLYPH>(); |
|||
SHAPE_LINE_CHAIN overbar; |
|||
|
|||
int left = extentBottomLeft.x; |
|||
int right = extentTopRight.x; |
|||
int top = extentBottomLeft.y - 800; |
|||
int barHeight = -3200; |
|||
|
|||
overbar.Append( VECTOR2D( left, top ) ); |
|||
overbar.Append( VECTOR2D( right, top ) ); |
|||
overbar.Append( VECTOR2D( right, top + barHeight ) ); |
|||
overbar.Append( VECTOR2D( left, top + barHeight ) ); |
|||
overbar.SetClosed( true ); |
|||
|
|||
overbarGlyph->AddOutline( overbar ); |
|||
|
|||
aGlyphs.push_back( std::move( overbarGlyph ) ); |
|||
} |
|||
|
|||
hb_buffer_destroy( buf ); |
|||
|
|||
VECTOR2I cursorDisplacement( -cursorEnd.x * scaleFactor.x, cursorEnd.y * scaleFactor.y ); |
|||
|
|||
if( aBoundingBox ) |
|||
{ |
|||
aBoundingBox->SetOrigin( aPosition.x, aPosition.y ); |
|||
aBoundingBox->SetEnd( cursorDisplacement ); |
|||
} |
|||
|
|||
return VECTOR2I( aPosition.x + cursorDisplacement.x, aPosition.y + cursorDisplacement.y ); |
|||
} |
|||
|
|||
|
|||
VECTOR2D OUTLINE_FONT::getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const |
|||
{ |
|||
hb_buffer_t* buf = hb_buffer_create(); |
|||
hb_buffer_add_utf8( buf, aString.c_str(), -1, 0, -1 ); |
|||
|
|||
// guess direction, script, and language based on contents
|
|||
hb_buffer_guess_segment_properties( buf ); |
|||
|
|||
FT_Face face = m_face; |
|||
int scaler = m_faceScaler; |
|||
|
|||
if( IsSubscript( aTextStyle ) || IsSuperscript( aTextStyle ) ) |
|||
face = m_subscriptFace; |
|||
|
|||
hb_font_t* referencedFont = hb_ft_font_create_referenced( face ); |
|||
hb_ft_font_set_funcs( referencedFont ); |
|||
hb_shape( referencedFont, buf, nullptr, 0 ); |
|||
|
|||
unsigned int glyphCount; |
|||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); |
|||
//hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount );
|
|||
|
|||
VECTOR2D boundingBox( 0, 0 ); |
|||
|
|||
int xScaler = aGlyphSize.x / scaler; |
|||
int yScaler = aGlyphSize.y / scaler; |
|||
double maxHeight = 0.0; |
|||
|
|||
for( unsigned int i = 0; i < glyphCount; i++ ) |
|||
{ |
|||
//hb_glyph_position_t& pos = glyphPos[i];
|
|||
int codepoint = glyphInfo[i].codepoint; |
|||
|
|||
FT_Load_Glyph( face, codepoint, FT_LOAD_NO_BITMAP ); |
|||
|
|||
FT_GlyphSlot glyphSlot = face->glyph; |
|||
FT_Glyph glyph; |
|||
FT_BBox controlBox; |
|||
|
|||
FT_Get_Glyph( glyphSlot, &glyph ); |
|||
FT_Glyph_Get_CBox( glyph, FT_Glyph_BBox_Mode::FT_GLYPH_BBOX_UNSCALED, &controlBox ); |
|||
|
|||
double width = controlBox.xMax * xScaler; |
|||
boundingBox.x += width; |
|||
|
|||
double height = controlBox.yMax * yScaler; |
|||
if( height > maxHeight ) |
|||
maxHeight = height; |
|||
|
|||
FT_Done_Glyph( glyph ); |
|||
} |
|||
boundingBox.y = aGlyphSize.y; //maxHeight;
|
|||
|
|||
hb_buffer_destroy( buf ); |
|||
|
|||
return boundingBox; |
|||
} |
|||
|
|||
|
|||
#undef OUTLINEFONT_RENDER_AS_PIXELS
|
|||
#ifdef OUTLINEFONT_RENDER_AS_PIXELS
|
|||
/*
|
|||
* WIP: eeschema (and PDF output?) should use pixel rendering instead of linear segmentation |
|||
*/ |
|||
void OUTLINE_FONT::RenderToOpenGLCanvas( KIGFX::OPENGL_GAL& aGal, const UTF8& aString, |
|||
const VECTOR2D& aGlyphSize, const wxPoint& aPosition, |
|||
const EDA_ANGLE& aOrientation, bool aIsMirrored ) const |
|||
{ |
|||
hb_buffer_t* buf = hb_buffer_create(); |
|||
hb_buffer_add_utf8( buf, aString.c_str(), -1, 0, -1 ); |
|||
|
|||
// guess direction, script, and language based on contents
|
|||
hb_buffer_guess_segment_properties( buf ); |
|||
|
|||
unsigned int glyphCount; |
|||
hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( buf, &glyphCount ); |
|||
hb_glyph_position_t* glyphPos = hb_buffer_get_glyph_positions( buf, &glyphCount ); |
|||
hb_font_t* referencedFont = hb_ft_font_create_referenced( m_face ); |
|||
|
|||
hb_ft_font_set_funcs( referencedFont ); |
|||
hb_shape( referencedFont, buf, nullptr, 0 ); |
|||
|
|||
const double mirror_factor = ( aIsMirrored ? 1 : -1 ); |
|||
const double x_scaleFactor = mirror_factor * aGlyphSize.x / mScaler; |
|||
const double y_scaleFactor = aGlyphSize.y / mScaler; |
|||
|
|||
hb_position_t cursor_x = 0; |
|||
hb_position_t cursor_y = 0; |
|||
|
|||
for( unsigned int i = 0; i < glyphCount; i++ ) |
|||
{ |
|||
hb_glyph_position_t& pos = glyphPos[i]; |
|||
int codepoint = glyphInfo[i].codepoint; |
|||
|
|||
FT_Error e = FT_Load_Glyph( m_face, codepoint, FT_LOAD_DEFAULT ); |
|||
// TODO handle FT_Load_Glyph error
|
|||
|
|||
FT_Glyph glyph; |
|||
e = FT_Get_Glyph( m_face->glyph, &glyph ); |
|||
// TODO handle FT_Get_Glyph error
|
|||
|
|||
wxPoint pt( aPosition ); |
|||
pt.x += ( cursor_x >> 6 ) * x_scaleFactor; |
|||
pt.y += ( cursor_y >> 6 ) * y_scaleFactor; |
|||
|
|||
cursor_x += pos.x_advance; |
|||
cursor_y += pos.y_advance; |
|||
} |
|||
|
|||
hb_buffer_destroy( buf ); |
|||
} |
|||
#endif //OUTLINEFONT_RENDER_AS_PIXELS
|
|||
@ -0,0 +1,47 @@ |
|||
/*
|
|||
* 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 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 <limits>
|
|||
#include <font/triangulate.h>
|
|||
|
|||
void Triangulate( const SHAPE_POLY_SET& aPolylist, TRIANGULATE_CALLBACK aCallback, |
|||
void* aCallbackData ) |
|||
{ |
|||
SHAPE_POLY_SET polys( aPolylist ); |
|||
|
|||
polys.Fracture( SHAPE_POLY_SET::PM_FAST ); // TODO verify aFastMode
|
|||
polys.CacheTriangulation(); |
|||
|
|||
for( unsigned int i = 0; i < polys.TriangulatedPolyCount(); i++ ) |
|||
{ |
|||
const SHAPE_POLY_SET::TRIANGULATED_POLYGON* polygon = polys.TriangulatedPolygon( i ); |
|||
for ( size_t j = 0; j < polygon->GetTriangleCount(); j++ ) |
|||
{ |
|||
VECTOR2I a; |
|||
VECTOR2I b; |
|||
VECTOR2I c; |
|||
|
|||
polygon->GetTriangle( j, a, b, c ); |
|||
aCallback( i, a, b, c, aCallbackData ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,51 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021-2022 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/>. |
|||
*/ |
|||
|
|||
#ifndef KICAD_FONTCONFIG_H |
|||
#define KICAD_FONTCONFIG_H |
|||
|
|||
#include <fontconfig/fontconfig.h> |
|||
#include <wx/string.h> |
|||
#include <vector> |
|||
#include <map> |
|||
#include <font/fontinfo.h> |
|||
|
|||
namespace fontconfig |
|||
{ |
|||
|
|||
class FONTCONFIG |
|||
{ |
|||
public: |
|||
FONTCONFIG(); |
|||
|
|||
bool FindFont( const wxString& aFontName, wxString& aFontFile ); |
|||
|
|||
void ListFonts( std::vector<std::string>& aFonts ); |
|||
|
|||
private: |
|||
FcConfig* m_config; |
|||
std::map<std::string, FONTINFO> m_fonts; |
|||
}; |
|||
|
|||
} // namespace fontconfig |
|||
|
|||
fontconfig::FONTCONFIG& Fontconfig(); |
|||
|
|||
#endif //KICAD_FONTCONFIG_H |
|||
@ -0,0 +1,56 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021-2022 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/>. |
|||
*/ |
|||
|
|||
#ifndef FONT_FONTINFO_H |
|||
#define FONT_FONTINFO_H |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
namespace fontconfig |
|||
{ |
|||
|
|||
class FONTINFO |
|||
{ |
|||
public: |
|||
FONTINFO( std::string aFile, std::string aStyle, std::string aFamily ) : |
|||
m_file( aFile ), |
|||
m_style( aStyle ), |
|||
m_family( aFamily ) |
|||
{ |
|||
} |
|||
|
|||
const std::string& File() const { return m_file; } |
|||
const std::string& Style() const { return m_style; } |
|||
const std::string& Family() const { return m_family; } |
|||
|
|||
std::vector<FONTINFO>& Children() { return m_children; } |
|||
|
|||
private: |
|||
std::string m_file; |
|||
std::string m_style; |
|||
std::string m_family; |
|||
|
|||
std::vector<FONTINFO> m_children; |
|||
}; |
|||
|
|||
} // namespace fontconfig |
|||
|
|||
#endif //FONT_FONTINFO_H |
|||
@ -0,0 +1,128 @@ |
|||
/* |
|||
* 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. |
|||
* |
|||
* Outline 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 OUTLINE_DECOMPOSER_H |
|||
#define OUTLINE_DECOMPOSER_H |
|||
|
|||
#include <vector> |
|||
#include <freetype2/ft2build.h> |
|||
#include FT_FREETYPE_H |
|||
#include FT_OUTLINE_H |
|||
#include <math/box2.h> |
|||
#include <math/vector2d.h> |
|||
#include <font/glyph.h> |
|||
|
|||
namespace KIFONT |
|||
{ |
|||
typedef std::vector<VECTOR2D> GLYPH_POINTS; |
|||
typedef std::vector<GLYPH_POINTS> GLYPH_POINTS_LIST; |
|||
typedef std::vector<BOX2D> GLYPH_BOUNDING_BOX_LIST; |
|||
|
|||
typedef struct |
|||
{ |
|||
GLYPH_POINTS points; |
|||
int winding; |
|||
FT_Orientation orientation; |
|||
} CONTOUR; |
|||
|
|||
|
|||
typedef std::vector<CONTOUR> CONTOURS; |
|||
|
|||
|
|||
class OUTLINE_DECOMPOSER |
|||
{ |
|||
public: |
|||
OUTLINE_DECOMPOSER( FT_Outline& aOutline ); |
|||
|
|||
void OutlineToSegments( CONTOURS* aContours ); |
|||
|
|||
private: |
|||
void contourToSegmentsAndArcs( CONTOUR& aResult, unsigned int aContourIndex ) const; |
|||
|
|||
void newContour(); |
|||
|
|||
void addContourPoint( const VECTOR2D& p ); |
|||
|
|||
int approximateContour( const GLYPH_POINTS& aPoints, const std::vector<bool>& aPointOnCurve, |
|||
GLYPH_POINTS& aResult ) const; |
|||
|
|||
bool approximateBezierCurve( GLYPH_POINTS& result, const GLYPH_POINTS& bezier ) const; |
|||
bool approximateQuadraticBezierCurve( GLYPH_POINTS& result, const GLYPH_POINTS& bezier ) const; |
|||
bool approximateCubicBezierCurve( GLYPH_POINTS& result, const GLYPH_POINTS& bezier ) const; |
|||
|
|||
/** |
|||
* @return 1 if aContour is in clockwise order, -1 if it is in |
|||
* counterclockwise order, or 0 if the winding can't be |
|||
* determined. |
|||
*/ |
|||
int winding( const GLYPH_POINTS& aContour ) const; |
|||
|
|||
inline static const unsigned int onCurve( char aTags ) |
|||
{ |
|||
return aTags & 0x1; |
|||
} |
|||
|
|||
inline static const unsigned int thirdOrderBezierPoint( char aTags ) |
|||
{ |
|||
return onCurve( aTags ) ? 0 : aTags & 0x2; |
|||
} |
|||
|
|||
inline static const unsigned int secondOrderBezierPoint( char aTags ) |
|||
{ |
|||
return onCurve( aTags ) ? 0 : !thirdOrderBezierPoint( aTags ); |
|||
} |
|||
|
|||
inline static const unsigned int hasDropout( char aTags ) |
|||
{ |
|||
return aTags & 0x4; |
|||
} |
|||
|
|||
inline static const unsigned int dropoutMode( char aTags ) |
|||
{ |
|||
return hasDropout( aTags ) ? ( aTags & 0x38 ) : 0; |
|||
} |
|||
|
|||
// FT_Outline_Decompose callbacks |
|||
static int moveTo( const FT_Vector* aEndPoint, void* aCallbackData ); |
|||
|
|||
static int lineTo( const FT_Vector* aEndPoint, void* aCallbackData ); |
|||
|
|||
static int quadraticTo( const FT_Vector* aControlPoint, const FT_Vector* aEndPoint, |
|||
void* aCallbackData ); |
|||
|
|||
static int cubicTo( const FT_Vector* aFirstControlPoint, const FT_Vector* aSecondControlPoint, |
|||
const FT_Vector* aEndPoint, void* aCallbackData ); |
|||
|
|||
private: |
|||
FT_Outline& m_outline; |
|||
CONTOURS* m_contours; |
|||
|
|||
VECTOR2D m_lastEndPoint; |
|||
}; |
|||
|
|||
} //namespace KIFONT |
|||
|
|||
#endif // OUTLINE_DECOMPOSER_H |
|||
@ -0,0 +1,166 @@ |
|||
/* |
|||
* This program source code file is part of KICAD, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021-2022 Kicad Developers, see AUTHORS.txt for contributors. |
|||
* |
|||
* Outline 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 OUTLINE_FONT_H_ |
|||
#define OUTLINE_FONT_H_ |
|||
|
|||
#include <gal/graphics_abstraction_layer.h> |
|||
#include <geometry/shape_poly_set.h> |
|||
#include <freetype2/ft2build.h> |
|||
#include FT_FREETYPE_H |
|||
#include FT_OUTLINE_H |
|||
//#include <gal/opengl/opengl_freetype.h> |
|||
#include <harfbuzz/hb.h> |
|||
#include <font/font.h> |
|||
#include <font/glyph.h> |
|||
#include <font/outline_decomposer.h> |
|||
|
|||
namespace KIFONT |
|||
{ |
|||
/** |
|||
* Class OUTLINE_FONT implements outline font drawing. |
|||
*/ |
|||
class OUTLINE_FONT : public FONT |
|||
{ |
|||
public: |
|||
OUTLINE_FONT(); |
|||
|
|||
bool IsOutline() const override { return true; } |
|||
|
|||
bool IsBold() const override |
|||
{ |
|||
return m_face && ( m_face->style_flags & FT_STYLE_FLAG_BOLD ); |
|||
} |
|||
|
|||
bool IsItalic() const override |
|||
{ |
|||
return m_face && ( m_face->style_flags & FT_STYLE_FLAG_ITALIC ); |
|||
} |
|||
|
|||
/** |
|||
* Load an outline font. TrueType (.ttf) and OpenType (.otf) are supported. |
|||
* @param aFontFileName is the (platform-specific) fully qualified name of the font file |
|||
*/ |
|||
static OUTLINE_FONT* LoadFont( const wxString& aFontFileName, bool aBold, bool aItalic ); |
|||
|
|||
#if 0 |
|||
/** |
|||
* Draw a string. |
|||
* |
|||
* @param aGal |
|||
* @param aText is the text to be drawn. |
|||
* @param aPosition is the text position in world coordinates. |
|||
* @param aOrigin is the item origin |
|||
* @param aAttributes contains text attributes (angle, line spacing, ...) |
|||
* @return bounding box width/height |
|||
*/ |
|||
VECTOR2D Draw( KIGFX::GAL* aGal, const UTF8& aText, const VECTOR2D& aPosition, |
|||
const VECTOR2D& aOrigin, const TEXT_ATTRIBUTES& aAttributes ) const override; |
|||
#endif |
|||
|
|||
/** |
|||
* 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 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 = 0.0, 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 GetTextAsGlyphs( BOX2I* aBoundingBox, std::vector<std::unique_ptr<GLYPH>>& aGlyphs, |
|||
const UTF8& aText, const VECTOR2D& aGlyphSize, |
|||
const wxPoint& aPosition, const EDA_ANGLE& aAngle, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const override; |
|||
|
|||
/** |
|||
* Like GetTextAsGlyphs, but handles multiple lines. |
|||
* TODO: Combine with GetTextAsGlyphs, maybe with a boolean parameter, |
|||
* but it's possible a non-line-breaking version isn't even needed |
|||
* |
|||
* @param aGlyphs returns text glyphs |
|||
* @param aText the text item |
|||
*/ |
|||
VECTOR2I GetLinesAsGlyphs( std::vector<std::unique_ptr<GLYPH>>& aGlyphs, |
|||
const EDA_TEXT* aText ) const; |
|||
|
|||
const FT_Face& GetFace() const { return m_face; } |
|||
|
|||
#if 0 |
|||
void RenderToOpenGLCanvas( KIGFX::OPENGL_FREETYPE& aTarget, const UTF8& aString, |
|||
const VECTOR2D& aGlyphSize, const wxPoint& aPosition, |
|||
double aOrientation, bool aIsMirrored ) const; |
|||
#endif |
|||
|
|||
protected: |
|||
VECTOR2D getBoundingBox( const UTF8& aString, const VECTOR2D& aGlyphSize, |
|||
TEXT_STYLE_FLAGS aTextStyle ) const override; |
|||
|
|||
|
|||
FT_Error loadFace( const wxString& aFontFileName ); |
|||
|
|||
bool loadFontSimple( const wxString& aFontFileName ); |
|||
|
|||
BOX2I getBoundingBox( const std::vector<std::unique_ptr<GLYPH>>& aGlyphs ) const; |
|||
|
|||
private: |
|||
// FreeType variables |
|||
static FT_Library m_freeType; |
|||
FT_Face m_face; |
|||
const int m_faceSize; |
|||
FT_Face m_subscriptFace; |
|||
const int m_subscriptSize; |
|||
|
|||
int m_faceScaler; |
|||
int m_subscriptFaceScaler; |
|||
|
|||
// cache for glyphs converted to straight segments |
|||
// key is glyph index (FT_GlyphSlot field glyph_index) |
|||
std::map<unsigned int, GLYPH_POINTS_LIST> m_contourCache; |
|||
}; |
|||
|
|||
} //namespace KIFONT |
|||
|
|||
#endif // OUTLINE_FONT_H_ |
|||
@ -0,0 +1,37 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2021 Ola Rinta-Koski |
|||
* Copyright (C) 2021-2022 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/>. |
|||
*/ |
|||
|
|||
#ifndef TRIANGULATE_H |
|||
#define TRIANGULATE_H |
|||
|
|||
#include <math/vector2d.h> |
|||
#include <font/glyph.h> |
|||
#include <geometry/shape_poly_set.h> |
|||
#include <functional> |
|||
|
|||
|
|||
typedef std::function<void( int, const VECTOR2I& aPoint1, const VECTOR2I& aPoint2, |
|||
const VECTOR2I& aPoint3, void* aCallbackData )> |
|||
TRIANGULATE_CALLBACK; |
|||
|
|||
void Triangulate( const SHAPE_POLY_SET& aPolylist, TRIANGULATE_CALLBACK aCallback, |
|||
void* aCallbackData = nullptr ); |
|||
|
|||
#endif // TRIANGULATE_H |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue