Outline font support.
This commit is contained in:
parent
9c29cc945c
commit
9b406c1da4
|
@ -708,6 +708,15 @@ include_directories( SYSTEM ${PIXMAN_INCLUDE_DIR} )
|
|||
find_package( Boost 1.59.0 REQUIRED )
|
||||
include_directories( SYSTEM ${Boost_INCLUDE_DIR} )
|
||||
|
||||
#
|
||||
# Libraries required for outline font support.
|
||||
find_package( Freetype REQUIRED )
|
||||
include_directories( SYSTEM ${FREETYPE_INCLUDE_DIRS} )
|
||||
|
||||
find_package( HarfBuzz REQUIRED )
|
||||
include_directories( SYSTEM ${HarfBuzz_INCLUDE_DIRS} )
|
||||
|
||||
find_package( Fontconfig REQUIRED )
|
||||
|
||||
# Include MinGW resource compiler.
|
||||
include( MinGWResourceCompiler )
|
||||
|
|
|
@ -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 ()
|
|
@ -72,6 +72,10 @@ target_link_libraries( gal
|
|||
${PIXMAN_LIBRARIES}
|
||||
${OPENGL_LIBRARIES}
|
||||
${GDI_PLUS_LIBRARIES}
|
||||
# outline font support
|
||||
${FREETYPE_LIBRARIES}
|
||||
${HarfBuzz_LIBRARIES}
|
||||
${Fontconfig_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
|
@ -296,6 +300,10 @@ set( FONT_SRCS
|
|||
font/font.cpp
|
||||
font/glyph.cpp
|
||||
font/stroke_font.cpp
|
||||
font/outline_font.cpp
|
||||
font/outline_decomposer.cpp
|
||||
font/triangulate.cpp
|
||||
font/fontconfig.cpp
|
||||
)
|
||||
|
||||
set( COMMON_SRCS
|
||||
|
@ -493,6 +501,10 @@ target_link_libraries( common
|
|||
${CURL_LIBRARIES}
|
||||
${wxWidgets_LIBRARIES}
|
||||
${EXTRA_LIBS}
|
||||
# outline font support
|
||||
${FREETYPE_LIBRARIES}
|
||||
${HarfBuzz_LIBRARIES}
|
||||
${Fontconfig_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories( common
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <string_utils.h>
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <font/stroke_font.h>
|
||||
#include <font/outline_font.h>
|
||||
#include <trigo.h>
|
||||
#include <markup_parser.h>
|
||||
|
||||
|
@ -68,22 +69,18 @@ FONT* FONT::getDefaultFont()
|
|||
|
||||
FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic )
|
||||
{
|
||||
if( aFontName.empty() )
|
||||
if( aFontName.empty() || aFontName == _( "KiCad" ) )
|
||||
return getDefaultFont();
|
||||
|
||||
std::tuple<wxString, bool, bool> key = { aFontName, aBold, aItalic };
|
||||
|
||||
FONT* font = s_fontMap[key];
|
||||
|
||||
#if 0
|
||||
// FONT TODO: load a real font
|
||||
if( !font )
|
||||
font = OUTLINE_FONT::LoadFont( aFontName, aBold, aItalic );
|
||||
#else
|
||||
|
||||
if( !font )
|
||||
font = getDefaultFont();
|
||||
#endif
|
||||
|
||||
s_fontMap[key] = font;
|
||||
|
||||
|
@ -102,7 +99,7 @@ bool FONT::IsStroke( const wxString& aFontName )
|
|||
|
||||
return font && font->IsStroke();
|
||||
#else
|
||||
return aFontName == _( "Default Font" );
|
||||
return aFontName == _( "Default Font" ) || aFontName == wxT( "KiCad" );
|
||||
#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() );
|
||||
}
|
|
@ -116,3 +116,16 @@ void STROKE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin )
|
|||
point.x = originX - ( point.x - originX );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOX2D OUTLINE_GLYPH::BoundingBox()
|
||||
{
|
||||
BOX2I bbox = BBox();
|
||||
return BOX2D( bbox.GetOrigin(), bbox.GetSize() );
|
||||
}
|
||||
|
||||
|
||||
void OUTLINE_GLYPH::Mirror( const VECTOR2D& aMirrorOrigin )
|
||||
{
|
||||
SHAPE_POLY_SET::Mirror( true, false, aMirrorOrigin );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +44,7 @@
|
|||
#include <bezier_curves.h>
|
||||
#include <math/util.h> // for KiROUND
|
||||
#include <trace_helpers.h>
|
||||
#include <font/triangulate.h>
|
||||
|
||||
#include <wx/frame.h>
|
||||
|
||||
|
@ -2420,12 +2421,28 @@ void OPENGL_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal )
|
|||
for( const std::vector<VECTOR2D>& pointList : strokeGlyph )
|
||||
DrawPolyline( pointList );
|
||||
}
|
||||
#if 0 // FONT TODO
|
||||
else if( aGlyph.IsOutline() )
|
||||
{
|
||||
fillPolygonAsTriangles( aGlyph.GetPolylist() );
|
||||
const auto& outlineGlyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph );
|
||||
|
||||
fillPolygonAsTriangles( outlineGlyph );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void OPENGL_GAL::fillPolygonAsTriangles( const SHAPE_POLY_SET& aPolyList )
|
||||
{
|
||||
m_currentManager->Shader( SHADER_NONE );
|
||||
m_currentManager->Color( m_fillColor );
|
||||
|
||||
auto triangleCallback = [&]( int aPolygonIndex, const VECTOR2D& aVertex1,
|
||||
const VECTOR2D& aVertex2, const VECTOR2D& aVertex3,
|
||||
void* aCallbackData )
|
||||
{
|
||||
m_currentManager->Vertex( aVertex1.x, aVertex1.y, m_layerDepth );
|
||||
m_currentManager->Vertex( aVertex2.x, aVertex2.y, m_layerDepth );
|
||||
m_currentManager->Vertex( aVertex3.x, aVertex3.y, m_layerDepth );
|
||||
};
|
||||
|
||||
Triangulate( aPolyList, triangleCallback );
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -47,6 +47,21 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class OUTLINE_GLYPH : public GLYPH, public SHAPE_POLY_SET
|
||||
{
|
||||
public:
|
||||
OUTLINE_GLYPH() :
|
||||
SHAPE_POLY_SET()
|
||||
{}
|
||||
|
||||
bool IsOutline() const override { return true; }
|
||||
|
||||
BOX2D BoundingBox() override;
|
||||
|
||||
void Mirror( const VECTOR2D& aMirrorOrigin = VECTOR2D( 0, 0 ) ) override;
|
||||
};
|
||||
|
||||
|
||||
class STROKE_GLYPH : public GLYPH, public std::vector<std::vector<VECTOR2D>>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -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
|
|
@ -524,6 +524,8 @@ private:
|
|||
|
||||
VECTOR2D getScreenPixelSize() const;
|
||||
|
||||
void fillPolygonAsTriangles( const SHAPE_POLY_SET& aPolyList );
|
||||
|
||||
/**
|
||||
* Basic OpenGL initialization and feature checks.
|
||||
*
|
||||
|
|
|
@ -52,24 +52,27 @@
|
|||
"drc_exclusions": [],
|
||||
"meta": {
|
||||
"filename": "board_design_settings.json",
|
||||
"version": 1
|
||||
"version": 2
|
||||
},
|
||||
"rule_severities": {
|
||||
"annular_width": "error",
|
||||
"clearance": "error",
|
||||
"copper_edge_clearance": "error",
|
||||
"copper_sliver": "warning",
|
||||
"courtyards_overlap": "error",
|
||||
"diff_pair_gap_out_of_range": "error",
|
||||
"diff_pair_uncoupled_length_too_long": "error",
|
||||
"drill_out_of_range": "error",
|
||||
"duplicate_footprints": "warning",
|
||||
"extra_footprint": "warning",
|
||||
"footprint_type_mismatch": "error",
|
||||
"hole_clearance": "error",
|
||||
"hole_near_hole": "error",
|
||||
"invalid_outline": "error",
|
||||
"item_on_disabled_layer": "error",
|
||||
"items_not_allowed": "error",
|
||||
"length_out_of_range": "error",
|
||||
"lib_footprint_issues": "error",
|
||||
"malformed_courtyard": "error",
|
||||
"microvia_drill_out_of_range": "error",
|
||||
"missing_courtyard": "ignore",
|
||||
|
@ -79,9 +82,14 @@
|
|||
"padstack": "error",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_over_copper": "error",
|
||||
"silk_overlap": "error",
|
||||
"silk_over_copper": "warning",
|
||||
"silk_overlap": "warning",
|
||||
"skew_out_of_range": "error",
|
||||
"solder_mask_bridge": "error",
|
||||
"starved_thermal": "error",
|
||||
"text_height": "warning",
|
||||
"text_thickness": "warning",
|
||||
"through_hole_pad_without_hole": "error",
|
||||
"too_many_vias": "error",
|
||||
"track_dangling": "warning",
|
||||
"track_width": "error",
|
||||
|
@ -97,27 +105,29 @@
|
|||
"allow_microvias": false,
|
||||
"max_error": 0.005,
|
||||
"min_clearance": 0.0,
|
||||
"min_copper_edge_clearance": 0.01,
|
||||
"min_hole_clearance": 0.0,
|
||||
"min_copper_edge_clearance": 0.1016,
|
||||
"min_hole_clearance": 0.25,
|
||||
"min_hole_to_hole": 0.25,
|
||||
"min_microvia_diameter": 0.508,
|
||||
"min_microvia_drill": 0.2032,
|
||||
"min_resolved_spokes": 2,
|
||||
"min_silk_clearance": 0.0,
|
||||
"min_text_height": 0.7999999999999999,
|
||||
"min_text_thickness": 0.12,
|
||||
"min_through_hole_diameter": 0.508,
|
||||
"min_track_width": 0.2032,
|
||||
"min_via_annular_width": 0.049999999999999996,
|
||||
"min_via_diameter": 0.889,
|
||||
"solder_mask_clearance": 0.254,
|
||||
"solder_mask_min_width": 0.0,
|
||||
"solder_paste_clearance": 0.0,
|
||||
"solder_paste_margin_ratio": 0.0
|
||||
"solder_mask_to_copper_clearance": 0.0,
|
||||
"use_height_for_length_calcs": true
|
||||
},
|
||||
"track_widths": [],
|
||||
"via_dimensions": [],
|
||||
"zones_allow_external_fillets": false,
|
||||
"zones_use_no_outline": true
|
||||
},
|
||||
"layer_presets": []
|
||||
"layer_presets": [],
|
||||
"viewports": []
|
||||
},
|
||||
"boards": [],
|
||||
"cvpcb": {
|
||||
|
@ -176,7 +186,7 @@
|
|||
}
|
||||
],
|
||||
"meta": {
|
||||
"version": 0
|
||||
"version": 2
|
||||
},
|
||||
"net_colors": null
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue