Add ability to embed files in various elements

Schematics, symbols, boards and footprints all get the ability to store
files inside their file structures.  File lookups now have a
kicad-embed:// URI to allow various parts of KiCad to refer to files
stored in this manner.

kicad-embed://datasheet.pdf references the file named "datasheet.pdf"
embedded in the document.  Embeds are allowed in schematics, boards,
symbols and footprints.  Currently supported embeddings are Datasheets,
3D Models and drawingsheets

Fixes https://gitlab.com/kicad/code/kicad/-/issues/6918

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2376

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17827
This commit is contained in:
Seth Hillbrand 2024-05-16 09:15:40 -07:00
parent 8d0c629f37
commit 77797103f7
136 changed files with 4944 additions and 1148 deletions

View File

@ -202,12 +202,12 @@ S3D_CACHE::~S3D_CACHE()
SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, const wxString& aBasePath,
S3D_CACHE_ENTRY** aCachePtr )
S3D_CACHE_ENTRY** aCachePtr, const EMBEDDED_FILES* aEmbeddedFiles )
{
if( aCachePtr )
*aCachePtr = nullptr;
wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile, aBasePath );
wxString full3Dpath = m_FNResolver->ResolvePath( aModelFile, aBasePath, aEmbeddedFiles );
if( full3Dpath.empty() )
{
@ -272,9 +272,9 @@ SCENEGRAPH* S3D_CACHE::load( const wxString& aModelFile, const wxString& aBasePa
}
SCENEGRAPH* S3D_CACHE::Load( const wxString& aModelFile, const wxString& aBasePath )
SCENEGRAPH* S3D_CACHE::Load( const wxString& aModelFile, const wxString& aBasePath, const EMBEDDED_FILES* aEmbeddedFiles )
{
return load( aModelFile, aBasePath );
return load( aModelFile, aBasePath, nullptr, aEmbeddedFiles );
}
@ -631,10 +631,11 @@ void S3D_CACHE::ClosePlugins()
}
S3DMODEL* S3D_CACHE::GetModel( const wxString& aModelFileName, const wxString& aBasePath )
S3DMODEL* S3D_CACHE::GetModel( const wxString& aModelFileName, const wxString& aBasePath,
const EMBEDDED_FILES* aEmbeddedFiles )
{
S3D_CACHE_ENTRY* cp = nullptr;
SCENEGRAPH* sp = load( aModelFileName, aBasePath,&cp );
SCENEGRAPH* sp = load( aModelFileName, aBasePath, &cp, aEmbeddedFiles );
if( !sp )
return nullptr;

View File

@ -38,6 +38,7 @@
#include <project.h>
#include <wx/string.h>
class EMBEDDED_FILES;
class PGM_BASE;
class S3D_CACHE_ENTRY;
class SCENEGRAPH;
@ -93,9 +94,10 @@ public:
*
* @param aModelFile is the partial or full path to the model to be loaded.
* @param aBasePath is the path to search for any relative files
* @param aEmbeddedFiles is a pointer to the embedded files list.
* @return true if the model was successfully loaded, otherwise false.
*/
SCENEGRAPH* Load( const wxString& aModelFile, const wxString& aBasePath );
SCENEGRAPH* Load( const wxString& aModelFile, const wxString& aBasePath, const EMBEDDED_FILES* aEmbeddedFiles );
FILENAME_RESOLVER* GetResolver() noexcept;
@ -123,9 +125,11 @@ public:
* structure for display by a renderer.
*
* @param aModelFileName is the full path to the model to be loaded.
* @param aBasePath is the path to search for any relative files.
* @param aEmbeddedFiles is a pointer to the embedded files list.
* @return is a pointer to the render data or NULL if not available.
*/
S3DMODEL* GetModel( const wxString& aModelFileName, const wxString& aBasePath );
S3DMODEL* GetModel( const wxString& aModelFileName, const wxString& aBasePath, const EMBEDDED_FILES* aEmbeddedFiles );
/**
* Delete up old cache files in cache directory.
@ -165,7 +169,9 @@ private:
bool saveCacheData( S3D_CACHE_ENTRY* aCacheItem );
// the real load function (can supply a cache entry pointer to member functions)
SCENEGRAPH* load( const wxString& aModelFile, const wxString& aBasePath, S3D_CACHE_ENTRY** aCachePtr = nullptr );
SCENEGRAPH* load( const wxString& aModelFile, const wxString& aBasePath,
S3D_CACHE_ENTRY** aCachePtr = nullptr,
const EMBEDDED_FILES* aEmbeddedFiles = nullptr );
/// cache entries
std::list< S3D_CACHE_ENTRY* > m_CacheList;

View File

@ -156,7 +156,7 @@ void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
if( m_cacheManager )
{
const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString );
const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString, nullptr );
if( model )
Set3DModel( (const S3DMODEL &)*model );

View File

@ -66,7 +66,9 @@ public:
/**
* Set this model to be displayed.
*
* @param aModelPathName 3D model path name.
* N.B. This will not load a model from the internal cache. Only from on disk.
*
* @param aModelPathName 3D model path name. Must be a file on disk.
*/
void Set3DModel( const wxString& aModelPathName );

View File

@ -974,7 +974,7 @@ void RENDER_3D_OPENGL::load3dModels( REPORTER* aStatusReporter )
{
// It is not present, try get it from cache
const S3DMODEL* modelPtr =
m_boardAdapter.Get3dCacheManager()->GetModel( fp_model.m_Filename, footprintBasePath );
m_boardAdapter.Get3dCacheManager()->GetModel( fp_model.m_Filename, footprintBasePath, footprint );
// only add it if the return is not NULL
if( modelPtr )

View File

@ -1247,8 +1247,6 @@ void RENDER_3D_RAYTRACE_BASE::load3DModels( CONTAINER_3D& aDstContainer, bool aS
// Get the list of model files for this model
S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
auto sM = fp->Models().begin();
auto eM = fp->Models().end();
wxString libraryName = fp->GetFPID().GetLibNickname();
@ -1271,44 +1269,38 @@ void RENDER_3D_RAYTRACE_BASE::load3DModels( CONTAINER_3D& aDstContainer, bool aS
}
}
while( sM != eM )
for( FP_3DMODEL& model : fp->Models() )
{
if( ( static_cast<float>( sM->m_Opacity ) > FLT_EPSILON )
&& ( sM->m_Show && !sM->m_Filename.empty() ) )
// get it from cache
const S3DMODEL* modelPtr =
cacheMgr->GetModel( model.m_Filename, footprintBasePath, fp );
// only add it if the return is not NULL.
if( modelPtr )
{
// get it from cache
const S3DMODEL* modelPtr =
cacheMgr->GetModel( sM->m_Filename, footprintBasePath );
glm::mat4 modelMatrix = fpMatrix;
// only add it if the return is not NULL.
if( modelPtr )
{
glm::mat4 modelMatrix = fpMatrix;
modelMatrix = glm::translate( modelMatrix,
SFVEC3F( model.m_Offset.x, model.m_Offset.y, model.m_Offset.z ) );
modelMatrix = glm::translate( modelMatrix,
SFVEC3F( sM->m_Offset.x, sM->m_Offset.y, sM->m_Offset.z ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( model.m_Rotation.z / 180.0f ) * glm::pi<float>(),
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( sM->m_Rotation.z / 180.0f ) * glm::pi<float>(),
SFVEC3F( 0.0f, 0.0f, 1.0f ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( model.m_Rotation.y / 180.0f ) * glm::pi<float>(),
SFVEC3F( 0.0f, 1.0f, 0.0f ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( sM->m_Rotation.y / 180.0f ) * glm::pi<float>(),
SFVEC3F( 0.0f, 1.0f, 0.0f ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( model.m_Rotation.x / 180.0f ) * glm::pi<float>(),
SFVEC3F( 1.0f, 0.0f, 0.0f ) );
modelMatrix = glm::rotate( modelMatrix,
(float) -( sM->m_Rotation.x / 180.0f ) * glm::pi<float>(),
SFVEC3F( 1.0f, 0.0f, 0.0f ) );
modelMatrix = glm::scale( modelMatrix,
SFVEC3F( model.m_Scale.x, model.m_Scale.y, model.m_Scale.z ) );
modelMatrix = glm::scale( modelMatrix,
SFVEC3F( sM->m_Scale.x, sM->m_Scale.y, sM->m_Scale.z ) );
addModels( aDstContainer, modelPtr, modelMatrix, (float) sM->m_Opacity,
aSkipMaterialInformation, boardItem );
}
addModels( aDstContainer, modelPtr, modelMatrix, (float) model.m_Opacity,
aSkipMaterialInformation, boardItem );
}
++sM;
}
}
}

View File

@ -91,7 +91,6 @@ set(3D-VIEWER_SRCS
common_ogl/ogl_utils.cpp
3d_fastmath.cpp
3d_math.cpp
dialogs/3d_cache_dialogs.cpp
dialogs/appearance_controls_3D.cpp
dialogs/appearance_controls_3D_base.cpp
dialogs/dialog_select_3d_model_base.cpp

View File

@ -1,52 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015-2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
* Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <dialogs/dialog_configure_paths.h>
#include "3d_info.h"
#include "3d_cache.h"
#include "3d_cache_dialogs.h"
#include "dialog_select_3d_model.h"
bool S3D::Select3DModel( wxWindow* aParent, S3D_CACHE* aCache, wxString& prevModelSelectDir,
int& prevModelWildcard, FP_3DMODEL* aModel )
{
if( nullptr == aModel )
return false;
DIALOG_SELECT_3DMODEL dm( aParent, aCache, aModel, prevModelSelectDir, prevModelWildcard );
// Use QuasiModal so that Configure3DPaths (and its help window) will work
return dm.ShowQuasiModal() == wxID_OK;
}
bool S3D::Configure3DPaths( wxWindow* aParent, FILENAME_RESOLVER* aResolver )
{
DIALOG_CONFIGURE_PATHS dlg( aParent );
// Use QuasiModal so that HTML help window will work
return( dlg.ShowQuasiModal() == wxID_OK );
}

View File

@ -1,40 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Cirilo Bernardo <cirilo.bernardo@gmail.com>
*
* 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 CACHE_DIALOGS_3D_H
#define CACHE_DIALOGS_3D_H
#include <wx/window.h>
class S3D_CACHE;
class FILENAME_RESOLVER;
namespace S3D
{
bool Select3DModel( wxWindow* aParent, S3D_CACHE* aCache, wxString& prevModelSelectDir,
int& prevModelWildcard, FP_3DMODEL* aModel );
bool Configure3DPaths( wxWindow* aParent, FILENAME_RESOLVER* aResolver );
}
#endif // CACHE_DIALOGS_3D_H

View File

@ -29,9 +29,9 @@
#include "project.h"
#include "3d_cache/3d_info.h"
#include "3d_cache/3d_cache.h"
#include "3d_cache_dialogs.h"
#include <3d_model_viewer/eda_3d_model_viewer.h>
#include <common_ogl/ogl_attr_list.h>
#include <dialogs/dialog_configure_paths.h>
#include <filename_resolver.h>
#include <pcbnew/footprint.h>
#include <wx_filename.h>
@ -197,7 +197,9 @@ void DIALOG_SELECT_3DMODEL::SetRootDir( wxCommandEvent& event )
void DIALOG_SELECT_3DMODEL::Cfg3DPaths( wxCommandEvent& event )
{
if( S3D::Configure3DPaths( this, m_resolver ) )
DIALOG_CONFIGURE_PATHS dlg( this );
if( dlg.ShowQuasiModal() == wxID_OK )
updateDirChoiceList();
}

View File

@ -44,6 +44,11 @@ public:
void SetRootDir( wxCommandEvent& event ) override;
void Cfg3DPaths( wxCommandEvent& event ) override;
bool IsEmbedded3DModel() const
{
return m_EmbedModelCb->IsChecked();
}
private:
void updateDirChoiceList( void );

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf0)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -35,6 +35,12 @@ DIALOG_SELECT_3D_MODEL_BASE::DIALOG_SELECT_3D_MODEL_BASE( wxWindow* parent, wxWi
bSizerLeft->Add( m_FileTree, 1, wxALL|wxEXPAND, 5 );
wxBoxSizer* bSizer7;
bSizer7 = new wxBoxSizer( wxHORIZONTAL );
bSizerLeft->Add( bSizer7, 0, wxEXPAND, 5 );
m_panelLeft->SetSizer( bSizerLeft );
m_panelLeft->Layout();
@ -70,6 +76,15 @@ DIALOG_SELECT_3D_MODEL_BASE::DIALOG_SELECT_3D_MODEL_BASE( wxWindow* parent, wxWi
bSizerMain->Add( bSizerLower, 0, wxEXPAND, 5 );
wxBoxSizer* bSizer6;
bSizer6 = new wxBoxSizer( wxHORIZONTAL );
m_EmbedModelCb = new wxCheckBox( this, wxID_ANY, _("Embed Model"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer6->Add( m_EmbedModelCb, 0, wxALL, 5 );
bSizer6->Add( 0, 0, 1, wxEXPAND, 5 );
m_sdbSizer = new wxStdDialogButtonSizer();
m_sdbSizerOK = new wxButton( this, wxID_OK );
m_sdbSizer->AddButton( m_sdbSizerOK );
@ -77,7 +92,10 @@ DIALOG_SELECT_3D_MODEL_BASE::DIALOG_SELECT_3D_MODEL_BASE( wxWindow* parent, wxWi
m_sdbSizer->AddButton( m_sdbSizerCancel );
m_sdbSizer->Realize();
bSizerMain->Add( m_sdbSizer, 0, wxEXPAND|wxALL, 5 );
bSizer6->Add( m_sdbSizer, 0, wxEXPAND|wxALL, 5 );
bSizerMain->Add( bSizer6, 0, wxEXPAND, 5 );
this->SetSizer( bSizerMain );

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf0)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -26,6 +26,7 @@
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/checkbox.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
@ -47,6 +48,7 @@ class DIALOG_SELECT_3D_MODEL_BASE : public DIALOG_SHIM
wxStaticText* m_stDirChoice;
wxChoice* m_dirChoices;
wxButton* m_cfgPathsButt;
wxCheckBox* m_EmbedModelCb;
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;

View File

@ -92,6 +92,12 @@ public:
*/
void UpdateDummyFootprint( bool aRelaodRequired = true );
/**
* Get the dummy footprint that is used for previewing the 3D model.
* We use this to hold the temporary 3D model shapes.
*/
FOOTPRINT* GetDummyFootprint() const { return m_dummyFootprint; }
private:
/**
* Load 3D relevant settings from the user configuration

View File

@ -809,6 +809,11 @@ include_directories( SYSTEM ${GLM_INCLUDE_DIR} )
#
find_package(ZLIB REQUIRED)
#
# Find Zstd library, required
#
find_package(ZSTD REQUIRED)
#
# Find libcurl, required
#

View File

@ -145,6 +145,7 @@ namespace ${enum}
// these first few are negative special ones for syntax, and are
// inherited from DSNLEXER.
T_NONE = DSN_NONE,
T_BAR = DSN_BAR, // Also called pipe: '|'
T_COMMENT = DSN_COMMENT,
T_STRING_QUOTE = DSN_STRING_QUOTE,
T_QUOTE_DEF = DSN_QUOTE_DEF,

41
cmake/FindZSTD.cmake Normal file
View File

@ -0,0 +1,41 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find Facebook zstd library
# This will define
# ZSTD_FOUND
# ZSTD_INCLUDE_DIR
# ZSTD_LIBRARY
#
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
ZSTD DEFAULT_MSG
ZSTD_LIBRARY ZSTD_INCLUDE_DIR
)
if (ZSTD_FOUND)
message(STATUS "Found Zstd: ${ZSTD_LIBRARY}")
endif()
mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)

View File

@ -195,6 +195,8 @@ target_link_libraries( kicommon
nlohmann_json
fmt::fmt
CURL::libcurl
picosha2
zstd
${wxWidgets_LIBRARIES}
${LIBGIT2_LIBRARIES}
@ -267,6 +269,7 @@ target_include_directories( kicommon
$<TARGET_PROPERTY:pegtl,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:expected,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:kiapi,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:picosha2,INTERFACE_INCLUDE_DIRECTORIES>
)
add_dependencies( kicommon pegtl version_header )
@ -304,6 +307,7 @@ set( COMMON_DLG_SRCS
dialogs/dialog_configure_paths_base.cpp
dialogs/dialog_display_html_text_base.cpp
dialogs/dialog_edit_library_tables.cpp
dialogs/dialog_embed_files.cpp
dialogs/dialog_global_lib_table_config.cpp
dialogs/dialog_global_lib_table_config_base.cpp
dialogs/dialog_grid_settings.cpp
@ -338,6 +342,8 @@ set( COMMON_DLG_SRCS
dialogs/panel_color_settings.cpp
dialogs/panel_common_settings.cpp
dialogs/panel_common_settings_base.cpp
dialogs/panel_embedded_files.cpp
dialogs/panel_embedded_files_base.cpp
dialogs/panel_gal_display_options.cpp
dialogs/panel_hotkeys_editor.cpp
dialogs/panel_image_editor.cpp
@ -534,6 +540,7 @@ set( COMMON_SRCS
eda_shape.cpp
eda_text.cpp
eda_tools.cpp
embedded_files.cpp
env_paths.cpp
executable_names.cpp
filename_resolver.cpp
@ -589,6 +596,7 @@ set( COMMON_SRCS
tool/edit_constraints.cpp
tool/edit_points.cpp
tool/editor_conditions.cpp
tool/embed_tool.cpp
tool/grid_helper.cpp
tool/grid_menu.cpp
tool/picker_tool.cpp
@ -921,6 +929,17 @@ make_lexer_export(
kicommon.h
)
# auto-generate embedded files lexer and keywords
make_lexer_export(
kicommon
embedded_files.keywords
embedded_files_lexer.h
embedded_files_keywords.cpp
EMBEDDED_FILES_T
KICOMMON_API
kicommon.h
)
# This one gets made only when testing.
# to build it, first enable #define STAND_ALONE at top of dsnlexer.cpp
add_executable( dsntest EXCLUDE_FROM_ALL dsnlexer.cpp )

View File

@ -0,0 +1,78 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 <wx/button.h>
#include <wx/sizer.h>
#include <dialogs/dialog_embed_files.h>
DIALOG_EMBED_FILES::DIALOG_EMBED_FILES( wxWindow* aParent, const wxString& aTitle ) :
DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_contentPanel( nullptr )
{
// Construction delayed until after panel is installed
}
void DIALOG_EMBED_FILES::InstallPanel( wxPanel* aPanel )
{
m_contentPanel = aPanel;
// Now perform the body of the constructor
auto mainSizer = new wxBoxSizer( wxVERTICAL );
SetSizer( mainSizer );
mainSizer->Add( m_contentPanel, 1, wxEXPAND|wxLEFT|wxTOP|wxRIGHT, 5 );
m_contentPanel->SetMinSize( FromDIP( wxSize( 1000, 600 ) ) );
auto sdbSizer = new wxStdDialogButtonSizer();
auto sdbSizerOK = new wxButton( this, wxID_OK );
sdbSizer->AddButton( sdbSizerOK );
auto sdbSizerCancel = new wxButton( this, wxID_CANCEL );
sdbSizer->AddButton( sdbSizerCancel );
sdbSizer->Realize();
mainSizer->Add( sdbSizer, 0, wxALL|wxEXPAND, 5 );
SetupStandardButtons();
finishDialogSettings();
// On some windows manager (Unity, XFCE), this dialog is not always raised, depending
// on how the dialog is run.
Raise();
}
bool DIALOG_EMBED_FILES::TransferDataToWindow()
{
return m_contentPanel->TransferDataToWindow();
}
bool DIALOG_EMBED_FILES::TransferDataFromWindow()
{
/**
* N.B. *do not* call wxDialog::TransferDataFromWindow() in the dialog code.
*/
return m_contentPanel->TransferDataFromWindow();
}

View File

@ -26,6 +26,8 @@
#include <dialogs/dialog_page_settings.h>
#include <eda_draw_frame.h>
#include <eda_item.h>
#include <embedded_files.h>
#include <filename_resolver.h>
#include <gr_basic.h>
#include <kiface_base.h>
#include <macros.h>
@ -38,6 +40,7 @@
#include <drawing_sheet/ds_painter.h>
#include <string_utils.h>
#include <widgets/std_bitmap_button.h>
#include <widgets/filedlg_open_embed_file.h>
#include <wx/valgen.h>
#include <wx/tokenzr.h>
#include <wx/filedlg.h>
@ -75,7 +78,7 @@ static const wxString pageFmts[] =
// to be recognized in code
};
DIALOG_PAGES_SETTINGS::DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, double aIuPerMils,
DIALOG_PAGES_SETTINGS::DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, EMBEDDED_FILES* aEmbeddedFiles, double aIuPerMils,
const VECTOR2D& aMaxUserSizeMils ) :
DIALOG_PAGES_SETTINGS_BASE( aParent ),
m_parent( aParent ),
@ -83,6 +86,7 @@ DIALOG_PAGES_SETTINGS::DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, double aI
m_initialized( false ),
m_pageBitmap( nullptr ),
m_iuPerMils( aIuPerMils ),
m_embeddedFiles( aEmbeddedFiles ),
m_customSizeX( aParent, m_userSizeXLabel, m_userSizeXCtrl, m_userSizeXUnits ),
m_customSizeY( aParent, m_userSizeYLabel, m_userSizeYCtrl, m_userSizeYUnits )
{
@ -114,6 +118,10 @@ DIALOG_PAGES_SETTINGS::DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, double aI
m_staticTextTitleBlock->SetLabel( _( "Title Block" ) );
}
m_filenameResolver = new FILENAME_RESOLVER;
m_filenameResolver->SetProject( &Prj() );
m_filenameResolver->SetProgramBase( &Pgm() );
SetupStandardButtons();
Centre();
@ -467,7 +475,8 @@ bool DIALOG_PAGES_SETTINGS::SavePageSettings()
if( fileName != BASE_SCREEN::m_DrawingSheetFileName )
{
wxString fullFileName = DS_DATA_MODEL::ResolvePath( fileName, m_projectPath );
wxString fullFileName = m_filenameResolver->ResolvePath( fileName, m_projectPath, m_embeddedFiles );
BASE_SCREEN::m_DrawingSheetFileName = fileName;
@ -794,19 +803,30 @@ void DIALOG_PAGES_SETTINGS::OnWksFileSelection( wxCommandEvent& event )
}
// Display a file picker dialog
FILEDLG_OPEN_EMBED_FILE customize;
wxFileDialog fileDialog( this, _( "Drawing Sheet File" ), path, name,
FILEEXT::DrawingSheetFileWildcard(),
wxFD_DEFAULT_STYLE | wxFD_FILE_MUST_EXIST );
if( m_embeddedFiles )
fileDialog.SetCustomizeHook( customize );
if( fileDialog.ShowModal() != wxID_OK )
return;
wxString fileName = fileDialog.GetPath();
wxString shortFileName;
// Try to use a project-relative path first:
if( !m_projectPath.IsEmpty() && fileName.StartsWith( m_projectPath ) )
if( m_embeddedFiles && customize.GetEmbed() )
{
fn.Assign( fileName );
EMBEDDED_FILES::EMBEDDED_FILE* result = m_embeddedFiles->AddFile( fn, false );
shortFileName = result->GetLink();
fileName = m_embeddedFiles->GetTempFileName( result->name ).GetFullPath();
}
else if( !m_projectPath.IsEmpty() && fileName.StartsWith( m_projectPath ) )
{
// Try to use a project-relative path
fn = wxFileName( fileName );
fn.MakeRelativeTo( m_projectPath );
shortFileName = fn.GetFullPath();

View File

@ -0,0 +1,314 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <bitmaps.h>
#include <dialogs/panel_embedded_files.h>
#include <embedded_files.h>
#include <kidialog.h>
#include <widgets/std_bitmap_button.h>
#include <widgets/wx_grid.h>
#include <wx/clipbrd.h>
#include <wx/dirdlg.h>
#include <wx/ffile.h>
#include <wx/filedlg.h>
#include <wx/filename.h>
#include <wx/menu.h>
PANEL_EMBEDDED_FILES::PANEL_EMBEDDED_FILES( wxWindow* parent, EMBEDDED_FILES* aFiles ) :
PANEL_EMBEDDED_FILES_BASE( parent ),
m_files( aFiles )
{
m_localFiles = new EMBEDDED_FILES();
for( auto& [name, file] : m_files->EmbeddedFileMap() )
{
EMBEDDED_FILES::EMBEDDED_FILE* newFile = new EMBEDDED_FILES::EMBEDDED_FILE( *file );
m_localFiles->AddFile( newFile );
}
// Set up the standard buttons
m_delete_button->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
m_browse_button->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
m_files_grid->SetMargins( 0 - wxSYS_VSCROLL_X, 0 );
m_files_grid->EnableAlternateRowColors();
}
void PANEL_EMBEDDED_FILES::onSize( wxSizeEvent& event )
{
resizeGrid();
}
void PANEL_EMBEDDED_FILES::resizeGrid()
{
int panel_width = GetClientRect().GetWidth();
int first_width = m_files_grid->GetColSize( 0 );
int second_width = m_files_grid->GetColSize( 1 );
double ratio;
if( first_width + second_width > 0 )
ratio = (double)first_width / (double)( first_width + second_width );
else
ratio = 0.3;
m_files_grid->SetColSize( 0, panel_width * ratio );
m_files_grid->SetColSize( 1, panel_width * ( 1 - ratio ) );
Layout();
}
void PANEL_EMBEDDED_FILES::onGridRightClick( wxGridEvent& event )
{
wxMenu menu;
menu.Append( wxID_COPY, _( "Copy Embedded Reference" ) );
menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
[&]( wxCommandEvent& )
{
int row = event.GetRow();
if( row >= 0 && row < m_files_grid->GetNumberRows() )
{
wxString cellValue = m_files_grid->GetCellValue( row, 1 );
if( wxTheClipboard->Open() )
{
wxTheClipboard->SetData( new wxTextDataObject( cellValue ) );
wxTheClipboard->Close();
}
}
}, wxID_COPY );
PopupMenu( &menu );
}
bool PANEL_EMBEDDED_FILES::TransferDataToWindow()
{
m_files_grid->ClearGrid();
if( m_files_grid->GetNumberRows() > 0 )
m_files_grid->DeleteRows( 0, m_files_grid->GetNumberRows() );
int ii = 0;
for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
{
while( m_files_grid->GetNumberRows() < ii + 1 )
m_files_grid->AppendRows( 1 );
m_files_grid->SetCellValue( ii, 0, name );
m_files_grid->SetCellValue( ii, 1, file->GetLink() );
ii++;
}
m_cbEmbedFonts->SetValue( m_files->GetAreFontsEmbedded() );
resizeGrid();
return true;
}
bool PANEL_EMBEDDED_FILES::TransferDataFromWindow()
{
m_files->ClearEmbeddedFiles();
std::vector<EMBEDDED_FILES::EMBEDDED_FILE*> files;
for( auto it = m_localFiles->EmbeddedFileMap().begin(); it != m_localFiles->EmbeddedFileMap().end(); it++ )
files.push_back( it->second );
for( auto& file : files )
{
m_files->AddFile( file );
m_localFiles->RemoveFile( file->name, false );
}
m_files->SetAreFontsEmbedded( m_cbEmbedFonts->IsChecked() );
return true;
}
void PANEL_EMBEDDED_FILES::onAddEmbeddedFile( wxCommandEvent& event )
{
wxFileDialog fileDialog( this, _( "Select a file to embed" ), wxEmptyString, wxEmptyString,
_( "All files|*.*" ), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( fileDialog.ShowModal() == wxID_OK )
{
wxFileName fileName( fileDialog.GetPath() );
wxString name = fileName.GetFullName();
if( m_localFiles->HasFile( name ) )
{
wxString msg = wxString::Format( _( "File '%s' already exists." ),
name );
KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ),
wxOK | wxCANCEL | wxICON_WARNING );
errorDlg.SetOKLabel( _( "Overwrite" ) );
if( errorDlg.ShowModal() != wxID_OK )
return;
for( int ii = 0; ii < m_files_grid->GetNumberRows(); ii++ )
{
if( m_files_grid->GetCellValue( ii, 0 ) == name )
{
m_files_grid->DeleteRows( ii );
break;
}
}
}
EMBEDDED_FILES::EMBEDDED_FILE* result = m_localFiles->AddFile( fileName, true );
if( !result )
{
wxString msg = wxString::Format( _( "Failed to add file '%s'." ),
name );
KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
errorDlg.ShowModal();
return;
}
m_files_grid->AppendRows( 1 );
int ii = m_files_grid->GetNumberRows() - 1;
m_files_grid->SetCellValue( ii, 0, name );
m_files_grid->SetCellValue( ii, 1, result->GetLink() );
}
}
void PANEL_EMBEDDED_FILES::onDeleteEmbeddedFile( wxCommandEvent& event )
{
int row = m_files_grid->GetGridCursorRow();
if( row < 0 )
return;
wxString name = m_files_grid->GetCellValue( row, 0 );
m_localFiles->RemoveFile( name );
m_files_grid->DeleteRows( row );
if( row < m_files_grid->GetNumberRows() )
m_files_grid->SetGridCursor( row, 0 );
else if( m_files_grid->GetNumberRows() > 0 )
m_files_grid->SetGridCursor( m_files_grid->GetNumberRows() - 1, 0 );
}
void PANEL_EMBEDDED_FILES::onExportFiles( wxCommandEvent& event )
{
wxDirDialog dirDialog( this, _( "Select a directory to export files" ) );
if( dirDialog.ShowModal() != wxID_OK )
return;
wxString path = dirDialog.GetPath();
for( auto& [name, file] : m_localFiles->EmbeddedFileMap() )
{
wxFileName fileName( path, name );
if( fileName.FileExists() )
{
wxString msg = wxString::Format( _( "File '%s' already exists." ),
fileName.GetFullName() );
KIDIALOG errorDlg( m_parent, msg, _( "Confirmation" ),
wxOK | wxCANCEL | wxICON_WARNING );
errorDlg.SetOKCancelLabels( _( "Overwrite" ), _( "Skip" ) );
errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
if( errorDlg.ShowModal() != wxID_OK )
continue;
}
bool skip_file = false;
while( 1 )
{
if( !fileName.IsDirWritable() )
{
#ifndef __WXMAC__
wxString msg = wxString::Format( _( "Directory '%s' is not writable." ),
fileName.GetFullName() );
#else
wxString msg = wxString::Format( _( "Folder '%s' is not writable." ),
fileName.GetPath() );
#endif
// Don't set a 'do not show again' checkbox for this dialog
KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxYES_NO | wxCANCEL | wxICON_ERROR );
errorDlg.SetYesNoCancelLabels( _( "Retry" ), _( "Skip" ), _( "Cancel" ) );
int result = errorDlg.ShowModal();
if( result == wxID_CANCEL )
{
return;
}
else if( result == wxID_NO )
{
skip_file = true;
break;
}
}
else
{
break;
}
}
if( skip_file )
continue;
wxFFile ffile( fileName.GetFullPath(), wxT( "w" ) );
if( !ffile.IsOpened() )
{
wxString msg = wxString::Format( _( "Failed to open file '%s'." ),
fileName.GetFullName() );
KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
errorDlg.ShowModal();
continue;
}
if( !ffile.Write( file->decompressedData.data(), file->decompressedData.size() ) )
{
wxString msg = wxString::Format( _( "Failed to write file '%s'." ),
fileName.GetFullName() );
KIDIALOG errorDlg( m_parent, msg, _( "Error" ), wxOK | wxICON_ERROR );
errorDlg.ShowModal();
}
}
}

View File

@ -0,0 +1,58 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 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, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PANEL_EMBEDDED_FILES_H
#define PANEL_EMBEDDED_FILES_H
#include "panel_embedded_files_base.h"
class EMBEDDED_FILES;
class PANEL_EMBEDDED_FILES : public PANEL_EMBEDDED_FILES_BASE
{
public:
PANEL_EMBEDDED_FILES( wxWindow* parent, EMBEDDED_FILES* aFiles );
~PANEL_EMBEDDED_FILES() override {};
bool TransferDataFromWindow() override;
bool TransferDataToWindow() override;
bool GetEmbedFonts() const { return m_cbEmbedFonts->GetValue(); }
protected:
void onGridRightClick( wxGridEvent& event ) override;
void onAddEmbeddedFile( wxCommandEvent& event ) override;
void onDeleteEmbeddedFile( wxCommandEvent& event ) override;
void onExportFiles( wxCommandEvent& event ) override;
void onSize( wxSizeEvent& event ) override;
private:
void resizeGrid();
EMBEDDED_FILES* m_files;
EMBEDDED_FILES* m_localFiles;
};
#endif // PANEL_EMBEDDED_FILES_H

View File

@ -0,0 +1,118 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/std_bitmap_button.h"
#include "widgets/wx_grid.h"
#include "panel_embedded_files_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_EMBEDDED_FILES_BASE::PANEL_EMBEDDED_FILES_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{
wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* m_global_sizer;
m_global_sizer = new wxBoxSizer( wxVERTICAL );
m_files_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_files_grid->CreateGrid( 1, 2 );
m_files_grid->EnableEditing( false );
m_files_grid->EnableGridLines( true );
m_files_grid->EnableDragGridSize( false );
m_files_grid->SetMargins( 0, 0 );
// Columns
m_files_grid->SetColSize( 0, 440 );
m_files_grid->SetColSize( 1, 180 );
m_files_grid->EnableDragColMove( false );
m_files_grid->EnableDragColSize( true );
m_files_grid->SetColLabelValue( 0, _("Filename") );
m_files_grid->SetColLabelValue( 1, _("Internal Reference") );
m_files_grid->SetColLabelSize( 22 );
m_files_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_files_grid->AutoSizeRows();
m_files_grid->EnableDragRowSize( false );
m_files_grid->SetRowLabelSize( 0 );
m_files_grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_files_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
m_global_sizer->Add( m_files_grid, 5, wxALL|wxEXPAND, 5 );
bMainSizer->Add( m_global_sizer, 1, wxEXPAND, 5 );
wxBoxSizer* bButtonsSizer;
bButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
m_browse_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_browse_button->SetToolTip( _("Add embedded file") );
bButtonsSizer->Add( m_browse_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
bButtonsSizer->Add( 20, 0, 0, wxEXPAND, 5 );
m_delete_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_delete_button->SetToolTip( _("Remove embedded file") );
bButtonsSizer->Add( m_delete_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
bButtonsSizer->Add( 0, 0, 1, wxEXPAND, 5 );
m_export = new wxButton( this, wxID_ANY, _("&Export"), wxDefaultPosition, wxDefaultSize, 0 );
bButtonsSizer->Add( m_export, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
bMainSizer->Add( bButtonsSizer, 0, wxEXPAND|wxALL, 3 );
m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
bMainSizer->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxVERTICAL );
m_cbEmbedFonts = new wxCheckBox( this, wxID_ANY, _("Embed Fonts"), wxDefaultPosition, wxDefaultSize, 0 );
m_cbEmbedFonts->SetToolTip( _("Store a copy of all fonts used") );
bSizer4->Add( m_cbEmbedFonts, 0, wxALL, 5 );
bMainSizer->Add( bSizer4, 0, wxEXPAND, 5 );
this->SetSizer( bMainSizer );
this->Layout();
bMainSizer->Fit( this );
// Connect Events
this->Connect( wxEVT_SIZE, wxSizeEventHandler( PANEL_EMBEDDED_FILES_BASE::onSize ) );
m_files_grid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( PANEL_EMBEDDED_FILES_BASE::onGridRightClick ), NULL, this );
m_browse_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onAddEmbeddedFile ), NULL, this );
m_delete_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onDeleteEmbeddedFile ), NULL, this );
m_export->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onExportFiles ), NULL, this );
}
PANEL_EMBEDDED_FILES_BASE::~PANEL_EMBEDDED_FILES_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_SIZE, wxSizeEventHandler( PANEL_EMBEDDED_FILES_BASE::onSize ) );
m_files_grid->Disconnect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( PANEL_EMBEDDED_FILES_BASE::onGridRightClick ), NULL, this );
m_browse_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onAddEmbeddedFile ), NULL, this );
m_delete_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onDeleteEmbeddedFile ), NULL, this );
m_export->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_EMBEDDED_FILES_BASE::onExportFiles ), NULL, this );
}

View File

@ -0,0 +1,559 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="18"/>
<object class="Project" expanded="true">
<property name="code_generation">C++</property>
<property name="cpp_class_decoration"></property>
<property name="cpp_disconnect_events">1</property>
<property name="cpp_event_generation">connect</property>
<property name="cpp_help_provider">none</property>
<property name="cpp_namespace"></property>
<property name="cpp_precompiled_header"></property>
<property name="cpp_use_array_enum">0</property>
<property name="cpp_use_enum">0</property>
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="file">panel_embedded_files_base</property>
<property name="first_id">1000</property>
<property name="internationalize">1</property>
<property name="lua_skip_events">1</property>
<property name="lua_ui_table">UI</property>
<property name="name">panel_embedded_files</property>
<property name="path">.</property>
<property name="php_disconnect_events">0</property>
<property name="php_disconnect_mode">source_name</property>
<property name="php_skip_events">1</property>
<property name="python_disconnect_events">0</property>
<property name="python_disconnect_mode">source_name</property>
<property name="python_image_path_wrapper_function_name"></property>
<property name="python_indent_with_spaces"></property>
<property name="python_skip_events">1</property>
<property name="relative_path">1</property>
<property name="use_microsoft_bom">0</property>
<property name="use_native_eol">0</property>
<object class="Panel" expanded="true">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size">-1,-1</property>
<property name="name">PANEL_EMBEDDED_FILES_BASE</property>
<property name="pos"></property>
<property name="size">-1,-1</property>
<property name="subclass">; forward_declare</property>
<property name="tooltip"></property>
<property name="two_step_creation">0</property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property>
<event name="OnSize">onSize</event>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bMainSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="false">
<property name="minimum_size"></property>
<property name="name">m_global_sizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">5</property>
<object class="wxGrid" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="autosize_cols">0</property>
<property name="autosize_rows">1</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="cell_bg"></property>
<property name="cell_font"></property>
<property name="cell_horiz_alignment">wxALIGN_LEFT</property>
<property name="cell_text"></property>
<property name="cell_vert_alignment">wxALIGN_CENTER</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="col_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="col_label_size">22</property>
<property name="col_label_values">&quot;Filename&quot; &quot;Internal Reference&quot;</property>
<property name="col_label_vert_alignment">wxALIGN_CENTER</property>
<property name="cols">2</property>
<property name="column_sizes">440,180</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">1</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="drag_col_move">0</property>
<property name="drag_col_size">1</property>
<property name="drag_grid_size">0</property>
<property name="drag_row_size">0</property>
<property name="editing">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">0</property>
<property name="font"></property>
<property name="grid_line_color"></property>
<property name="grid_lines">1</property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label_bg"></property>
<property name="label_font"></property>
<property name="label_text"></property>
<property name="margin_height">0</property>
<property name="margin_width">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size">-1,-1</property>
<property name="moveable">0</property>
<property name="name">m_files_grid</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Fixed</property>
<property name="row_label_horiz_alignment">wxALIGN_CENTER</property>
<property name="row_label_size">0</property>
<property name="row_label_values"></property>
<property name="row_label_vert_alignment">wxALIGN_CENTER</property>
<property name="row_sizes"></property>
<property name="rows">1</property>
<property name="show">1</property>
<property name="size"></property>
<property name="subclass">WX_GRID; widgets/wx_grid.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnGridCellRightClick">onGridRightClick</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">3</property>
<property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="false">
<property name="minimum_size"></property>
<property name="name">bButtonsSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxTOP|wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Add Embedded File</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_browse_button</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size">-1,-1</property>
<property name="style"></property>
<property name="subclass">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Add embedded file</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onAddEmbeddedFile</event>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="spacer" expanded="false">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">20</property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxTOP|wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxBitmapButton" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Delete SelectedFile</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_delete_button</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size">-1,-1</property>
<property name="style"></property>
<property name="subclass">STD_BITMAP_BUTTON; widgets/std_bitmap_button.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Remove embedded file</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onDeleteEmbeddedFile</event>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="false">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
<property name="proportion">0</property>
<object class="wxButton" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="auth_needed">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="bitmap"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="current"></property>
<property name="default">0</property>
<property name="default_pane">0</property>
<property name="disabled"></property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="focus"></property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">&amp;Export</property>
<property name="margins"></property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_export</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="position"></property>
<property name="pressed"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onExportFiles</event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND | wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticline1</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxLI_HORIZONTAL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="true">
<property name="minimum_size"></property>
<property name="name">bSizer4</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="true">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="true">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Embed Fonts</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_cbEmbedFonts</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Store a copy of all fonts used</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
</object>
</object>
</object>
</object>
</object>
</wxFormBuilder_Project>

View File

@ -0,0 +1,64 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class STD_BITMAP_BUTTON;
class WX_GRID;
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/font.h>
#include <wx/grid.h>
#include <wx/gdicmn.h>
#include <wx/sizer.h>
#include <wx/bmpbuttn.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/button.h>
#include <wx/statline.h>
#include <wx/checkbox.h>
#include <wx/panel.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_EMBEDDED_FILES_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_EMBEDDED_FILES_BASE : public wxPanel
{
private:
protected:
WX_GRID* m_files_grid;
STD_BITMAP_BUTTON* m_browse_button;
STD_BITMAP_BUTTON* m_delete_button;
wxButton* m_export;
wxStaticLine* m_staticline1;
wxCheckBox* m_cbEmbedFonts;
// Virtual event handlers, override them in your derived class
virtual void onSize( wxSizeEvent& event ) { event.Skip(); }
virtual void onGridRightClick( wxGridEvent& event ) { event.Skip(); }
virtual void onAddEmbeddedFile( wxCommandEvent& event ) { event.Skip(); }
virtual void onDeleteEmbeddedFile( wxCommandEvent& event ) { event.Skip(); }
virtual void onExportFiles( wxCommandEvent& event ) { event.Skip(); }
public:
PANEL_EMBEDDED_FILES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_EMBEDDED_FILES_BASE();
};

View File

@ -145,33 +145,3 @@ DS_DATA_ITEM* DS_DATA_MODEL::GetItem( unsigned aIdx ) const
return nullptr;
}
const wxString DS_DATA_MODEL::ResolvePath( const wxString& aPath, const wxString& aProjectPath )
{
wxString fullFileName = ExpandEnvVarSubstitutions( aPath, nullptr );
if( fullFileName.IsEmpty() )
return fullFileName;
wxFileName fn = fullFileName;
if( fn.IsAbsolute() )
return fullFileName;
// the path is not absolute: search it in project path, and then in kicad valid paths
if( !aProjectPath.IsEmpty() )
{
fn.MakeAbsolute( aProjectPath );
if( wxFileExists( fn.GetFullPath() ) )
return fn.GetFullPath();
}
fn = fullFileName;
wxString name = Kiface().KifaceSearch().FindValidPath( fn.GetFullName() );
if( !name.IsEmpty() )
fullFileName = name;
return fullFileName;
}

View File

@ -276,6 +276,9 @@ const char* DSNLEXER::Syntax( int aTok )
case DSN_EOF:
ret = "end of input";
break;
case DSN_BAR:
ret = "|";
break;
default:
ret = "???";
}
@ -379,6 +382,15 @@ void DSNLEXER::NeedRIGHT()
}
void DSNLEXER::NeedBAR()
{
int tok = NextTok();
if( tok != DSN_BAR )
Expecting( DSN_BAR );
}
int DSNLEXER::NeedSYMBOL()
{
int tok = NextTok();
@ -452,7 +464,7 @@ inline bool isDigit( char cc )
///< @return true if @a cc is an s-expression separator character.
inline bool isSep( char cc )
{
return isSpace( cc ) || cc=='(' || cc==')';
return isSpace( cc ) || cc == '(' || cc == ')' || cc == '|';
}
@ -597,6 +609,14 @@ L_read:
goto exit;
}
if( *cur == '|' )
{
curText = *cur;
curTok = DSN_BAR;
head = cur+1;
goto exit;
}
// Non-specctraMode, understands and deciphers escaped \, \r, \n, and \".
// Strips off leading and trailing double quotes
if( !specctraMode )

View File

@ -25,6 +25,7 @@
#include <pgm_base.h>
#include <common.h>
#include <confirm.h>
#include <embedded_files.h>
#include <gestfich.h>
#include <settings/common_settings.h>
@ -58,7 +59,8 @@ static const wxFileTypeInfo EDAfallbacks[] =
};
bool GetAssociatedDocument( wxWindow* aParent, const wxString& aDocName, PROJECT* aProject, SEARCH_STACK* aPaths )
bool GetAssociatedDocument( wxWindow* aParent, const wxString& aDocName, PROJECT* aProject,
SEARCH_STACK* aPaths, EMBEDDED_FILES* aFiles )
{
wxString docname;
wxString fullfilename;
@ -74,8 +76,48 @@ bool GetAssociatedDocument( wxWindow* aParent, const wxString& aDocName, PROJECT
wxURI uri( docname );
wxLogNull logNo; // Disable log messages
if( uri.HasScheme() && wxLaunchDefaultBrowser( docname ) )
return true;
if( uri.HasScheme() )
{
wxString scheme = uri.GetScheme().Lower();
if( scheme != FILEEXT::KiCadUriPrefix )
{
if( wxLaunchDefaultBrowser( docname ) )
return true;
}
else
{
if( !aFiles )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "No EMBEDDED_FILES object provided for kicad_embed URI" ) );
return false;
}
if( !docname.starts_with( FILEEXT::KiCadUriPrefix + "://" ) )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "Invalid kicad_embed URI '%s'" ), docname );
return false;
}
docname = docname.Mid( 14 );
wxFileName temp_file = aFiles->GetTempFileName( docname );
if( !temp_file.IsOk() )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "Failed to get temp file '%s' for kicad_embed URI" ), docname );
return false;
}
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "Opening embedded file '%s' as '%s'" ),
docname, temp_file.GetFullPath() );
docname = temp_file.GetFullPath();
}
}
}
#ifdef __WINDOWS__

511
common/embedded_files.cpp Normal file
View File

@ -0,0 +1,511 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 <wx/base64.h>
#include <wx/debug.h>
#include <wx/file.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/mstream.h>
#include <wx/wfstream.h>
#include <map>
#include <memory>
#include <sstream>
#include <zstd.h>
#include <embedded_files.h>
#include <kiid.h>
#include <paths.h>
EMBEDDED_FILES::EMBEDDED_FILE* EMBEDDED_FILES::AddFile( const wxFileName& aName, bool aOverwrite )
{
if( HasFile( aName.GetFullName() ) )
{
if( !aOverwrite )
return m_files[aName.GetFullName()];
m_files.erase( aName.GetFullName() );
}
wxFFileInputStream file( aName.GetFullPath() );
wxMemoryBuffer buffer;
if( !file.IsOk() )
return nullptr;
wxFileOffset length = file.GetLength();
std::unique_ptr<EMBEDDED_FILE> efile = std::make_unique<EMBEDDED_FILE>();
efile->name = aName.GetFullName();
efile->decompressedData.resize( length );
wxString ext = aName.GetExt().Upper();
// Handle some common file extensions
if( ext == "STP" || ext == "STPZ" || ext == "STEP" || ext == "WRL" || ext == "WRZ" )
{
efile->type = EMBEDDED_FILE::FILE_TYPE::MODEL;
}
else if( ext == "WOFF" || ext == "WOFF2" || ext == "TTF" || ext == "OTF" )
{
efile->type = EMBEDDED_FILE::FILE_TYPE::FONT;
}
else if( ext == "PDF" )
{
efile->type = EMBEDDED_FILE::FILE_TYPE::DATASHEET;
}
else if( ext == "KICAD_WKS" )
{
efile->type = EMBEDDED_FILE::FILE_TYPE::WORKSHEET;
}
if( !efile->decompressedData.data() )
return nullptr;
char* data = efile->decompressedData.data();
wxFileOffset total_read = 0;
while( !file.Eof() && total_read < length )
{
file.Read( data, length - total_read );
size_t read = file.LastRead();
data += read;
total_read += read;
}
if( CompressAndEncode( *efile ) != RETURN_CODE::OK )
return nullptr;
efile->is_valid = true;
m_files[aName.GetFullName()] = efile.release();
return m_files[aName.GetFullName()];
}
void EMBEDDED_FILES::AddFile( EMBEDDED_FILE* aFile )
{
m_files.insert( { aFile->name, aFile } );
}
// Remove a file from the collection
void EMBEDDED_FILES::RemoveFile( const wxString& name, bool aErase )
{
auto it = m_files.find( name );
if( it != m_files.end() )
{
m_files.erase( it );
if( aErase )
delete it->second;
}
}
void EMBEDDED_FILES::ClearEmbeddedFonts()
{
for( auto it = m_files.begin(); it != m_files.end(); )
{
if( it->second->type == EMBEDDED_FILE::FILE_TYPE::FONT )
{
delete it->second;
it = m_files.erase( it );
}
else
{
++it;
}
}
}
// Write the collection of files to a disk file in the specified format
void EMBEDDED_FILES::WriteEmbeddedFiles( OUTPUTFORMATTER& aOut, int aNestLevel,
bool aWriteData ) const
{
ssize_t MIME_BASE64_LENGTH = 76;
aOut.Print( aNestLevel, "(embedded_files\n" );
for( const auto& [name, entry] : m_files )
{
const EMBEDDED_FILE& file = *entry;
aOut.Print( aNestLevel + 1, "(file\n" );
aOut.Print( aNestLevel + 2, "(name \"%s\")\n", file.name.c_str().AsChar() );
const char* type = nullptr;
switch( file.type )
{
case EMBEDDED_FILE::FILE_TYPE::DATASHEET:
type = "datasheet";
break;
case EMBEDDED_FILE::FILE_TYPE::FONT:
type = "font";
break;
case EMBEDDED_FILE::FILE_TYPE::MODEL:
type = "model";
break;
case EMBEDDED_FILE::FILE_TYPE::WORKSHEET:
type = "worksheet";
break;
default:
type = "other";
break;
}
aOut.Print( aNestLevel + 2, "(type %s)\n", type );
if( aWriteData )
{
aOut.Print( 2, "(data\n" );
size_t first = 0;
while( first < file.compressedEncodedData.length() )
{
ssize_t remaining = file.compressedEncodedData.length() - first;
int length = std::min( remaining, MIME_BASE64_LENGTH );
std::string_view view( file.compressedEncodedData.data() + first, length );
aOut.Print( aNestLevel + 3, "%1s%.*s%s\n", first ? "" : "|", length, view.data(),
remaining == length ? "|" : "" );
first += MIME_BASE64_LENGTH;
}
aOut.Print( aNestLevel + 2, ")\n" ); // Close data
}
aOut.Print( aNestLevel + 2, "(checksum \"%s\")\n", file.data_sha.c_str() );
aOut.Print( aNestLevel + 1, ")\n" ); // Close file
}
aOut.Print( aNestLevel, ")\n" ); // Close embedded_files
}
// Compress and Base64 encode data
EMBEDDED_FILES::RETURN_CODE EMBEDDED_FILES::CompressAndEncode( EMBEDDED_FILE& aFile )
{
std::vector<char> compressedData;
size_t estCompressedSize = ZSTD_compressBound( aFile.decompressedData.size() );
compressedData.resize( estCompressedSize );
size_t compressedSize = ZSTD_compress( compressedData.data(), estCompressedSize,
aFile.decompressedData.data(),
aFile.decompressedData.size(), 15 );
if( ZSTD_isError( compressedSize ) )
{
compressedData.clear();
return RETURN_CODE::OUT_OF_MEMORY;
}
const size_t dstLen = wxBase64EncodedSize( compressedSize );
aFile.compressedEncodedData.resize( dstLen );
size_t retval = wxBase64Encode( aFile.compressedEncodedData.data(), dstLen,
compressedData.data(), compressedSize );
if( retval != dstLen )
{
aFile.compressedEncodedData.clear();
return RETURN_CODE::OUT_OF_MEMORY;
}
picosha2::hash256_hex_string( aFile.decompressedData, aFile.data_sha );
return RETURN_CODE::OK;
}
// Decompress and Base64 decode data
EMBEDDED_FILES::RETURN_CODE EMBEDDED_FILES::DecompressAndDecode( EMBEDDED_FILE& aFile )
{
std::vector<char> compressedData;
size_t compressedSize = wxBase64DecodedSize( aFile.compressedEncodedData.size() );
if( compressedSize == 0 )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "%s:%s:%d\n * Base64DecodedSize failed for file '%s' with size %zu" ),
__FILE__, __FUNCTION__, __LINE__, aFile.name, aFile.compressedEncodedData.size() );
return RETURN_CODE::OUT_OF_MEMORY;
}
compressedData.resize( compressedSize );
void* compressed = compressedData.data();
// The return value from wxBase64Decode is the actual size of the decoded data avoiding
// the modulo 4 padding of the base64 encoding
compressedSize = wxBase64Decode( compressed, compressedSize, aFile.compressedEncodedData );
unsigned long long estDecompressedSize = ZSTD_getFrameContentSize( compressed, compressedSize );
if( estDecompressedSize > 1e9 ) // Limit to 1GB
return RETURN_CODE::OUT_OF_MEMORY;
if( estDecompressedSize == ZSTD_CONTENTSIZE_ERROR
|| estDecompressedSize == ZSTD_CONTENTSIZE_UNKNOWN )
{
return RETURN_CODE::OUT_OF_MEMORY;
}
aFile.decompressedData.resize( estDecompressedSize );
void* decompressed = aFile.decompressedData.data();
size_t decompressedSize = ZSTD_decompress( decompressed, estDecompressedSize,
compressed, compressedSize );
if( ZSTD_isError( decompressedSize ) )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "%s:%s:%d\n * ZSTD_decompress failed with error '%s'" ),
__FILE__, __FUNCTION__, __LINE__, ZSTD_getErrorName( decompressedSize ) );
aFile.decompressedData.clear();
return RETURN_CODE::OUT_OF_MEMORY;
}
aFile.decompressedData.resize( decompressedSize );
std::string new_sha;
picosha2::hash256_hex_string( aFile.decompressedData, new_sha );
if( new_sha != aFile.data_sha )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "%s:%s:%d\n * Checksum error in embedded file '%s'" ),
__FILE__, __FUNCTION__, __LINE__, aFile.name );
aFile.decompressedData.clear();
return RETURN_CODE::CHECKSUM_ERROR;
}
return RETURN_CODE::OK;
}
// Parsing method
void EMBEDDED_FILES_PARSER::ParseEmbedded( EMBEDDED_FILES* aFiles )
{
if( !aFiles )
THROW_PARSE_ERROR( "No embedded files object provided", CurSource(), CurLine(),
CurLineNumber(), CurOffset() );
using namespace EMBEDDED_FILES_T;
std::unique_ptr<EMBEDDED_FILES::EMBEDDED_FILE> file( nullptr );
for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_file )
Expecting( "file" );
if( file )
{
if( !file->compressedEncodedData.empty() )
{
EMBEDDED_FILES::DecompressAndDecode( *file );
if( !file->Validate() )
THROW_PARSE_ERROR( "Checksum error in embedded file " + file->name, CurSource(),
CurLine(), CurLineNumber(), CurOffset() );
}
aFiles->AddFile( file.release() );
}
file = std::unique_ptr<EMBEDDED_FILES::EMBEDDED_FILE>( nullptr );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_checksum:
NeedSYMBOLorNUMBER();
if( !IsSymbol( token ) )
Expecting( "checksum data" );
file->data_sha = CurStr();
NeedRIGHT();
break;
case T_data:
NeedBAR();
token = NextTok();
file->compressedEncodedData.reserve( 1 << 17 );
while( token != T_BAR )
{
if( !IsSymbol( token ) )
Expecting( "base64 file data" );
file->compressedEncodedData += CurStr();
token = NextTok();
}
file->compressedEncodedData.shrink_to_fit();
NeedRIGHT();
break;
case T_name:
if( file )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "Duplicate 'name' tag in embedded file %s" ), file->name );
}
NeedSYMBOLorNUMBER();
file = std::make_unique<EMBEDDED_FILES::EMBEDDED_FILE>();
file->name = CurStr();
NeedRIGHT();
break;
case T_type:
token = NextTok();
switch( token )
{
case T_datasheet:
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::DATASHEET;
break;
case T_font:
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT;
break;
case T_model:
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::MODEL;
break;
case T_worksheet:
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::WORKSHEET;
break;
case T_other:
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::OTHER;
break;
default:
Expecting( "datasheet, font, model, worksheet or other" );
break;
}
NeedRIGHT();
break;
default:
Expecting( "checksum, data or name" );
}
}
}
// Add the last file in the collection
if( file )
{
if( !file->compressedEncodedData.empty() )
{
EMBEDDED_FILES::DecompressAndDecode( *file );
if( !file->Validate() )
THROW_PARSE_ERROR( "Checksum error in embedded file " + file->name, CurSource(),
CurLine(), CurLineNumber(), CurOffset() );
}
aFiles->AddFile( file.release() );
}
}
wxFileName EMBEDDED_FILES::GetTempFileName( const wxString& aName ) const
{
wxFileName cacheFile;
auto it = m_files.find( aName );
if( it == m_files.end() )
return cacheFile;
cacheFile.AssignDir( PATHS::GetUserCachePath() );
cacheFile.AppendDir( wxT( "embed" ) );
if( !PATHS::EnsurePathExists( cacheFile.GetFullPath() ) )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "%s:%s:%d\n * failed to create embed cache directory '%s'" ),
__FILE__, __FUNCTION__, __LINE__, cacheFile.GetPath() );
cacheFile.SetPath( wxFileName::GetTempDir() );
}
wxFileName inputName( aName );
// Store the cache file name using the data SHA to allow for shared data between
// multiple projects using the same files as well as deconflicting files with the same name
cacheFile.SetName( "kicad_embedded_" + it->second->data_sha );
cacheFile.SetExt( inputName.GetExt() );
if( cacheFile.FileExists() && cacheFile.IsFileReadable() )
return cacheFile;
wxFFileOutputStream out( cacheFile.GetFullPath() );
if( !out.IsOk() )
{
cacheFile.Clear();
return cacheFile;
}
out.Write( it->second->decompressedData.data(), it->second->decompressedData.size() );
return cacheFile;
}
const std::vector<wxString>* EMBEDDED_FILES::GetFontFiles() const
{
return &m_fontFiles;
}
const std::vector<wxString>* EMBEDDED_FILES::UpdateFontFiles()
{
m_fontFiles.clear();
for( const auto& [name, entry] : m_files )
{
if( entry->type == EMBEDDED_FILE::FILE_TYPE::FONT )
m_fontFiles.push_back( GetTempFileName( name ).GetFullPath() );
}
return &m_fontFiles;
}

View File

@ -0,0 +1,13 @@
checksum
data
datasheet
embedded_files
embedded_fonts
file
font
model
name
other
reference
type
worksheet

View File

@ -27,10 +27,12 @@
#include <sstream>
#include <wx/log.h>
#include <wx/uri.h>
#include <pgm_base.h>
#include <trace_helpers.h>
#include <common.h>
#include <embedded_files.h>
#include <env_vars.h>
#include <filename_resolver.h>
#include <confirm.h>
@ -241,7 +243,8 @@ bool FILENAME_RESOLVER::UpdatePathList( const std::vector< SEARCH_PATH >& aPathL
}
wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxString& aWorkingPath )
wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxString& aWorkingPath,
const EMBEDDED_FILES* aFiles )
{
std::lock_guard<std::mutex> lock( mutex_resolver );
@ -260,6 +263,32 @@ wxString FILENAME_RESOLVER::ResolvePath( const wxString& aFileName, const wxStri
// for getenv().
tname = ExpandEnvVarSubstitutions( tname, m_project );
// Check to see if the file is a URI for an embedded file.
if( tname.StartsWith( FILEEXT::KiCadUriPrefix + "://" ) )
{
if( !aFiles )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "No EMBEDDED_FILES object provided for kicad_embed URI" ) );
return wxEmptyString;
}
wxString path = tname.Mid( 14 );
wxFileName temp_file = aFiles->GetTempFileName( path );
if( !temp_file.IsOk() )
{
wxLogTrace( wxT( "KICAD_EMBED" ),
wxT( "Failed to get temp file '%s' for kicad_embed URI" ), path );
return wxEmptyString;
}
wxLogTrace( wxT( "KICAD_EMBED" ), wxT( "Opening embedded file '%s' as '%s'" ),
tname, temp_file.GetFullPath() );
return temp_file.GetFullPath();
}
wxFileName tmpFN( tname );
// this case covers full paths, leading expanded vars, and paths relative to the current
@ -688,11 +717,23 @@ bool FILENAME_RESOLVER::ValidateFileName( const wxString& aFileName, bool& hasAl
// ALIAS:relative/path
// 2. ALIAS is a UTF string excluding wxT( "{}[]()%~<>\"='`;:.,&?/\\|$" )
// 3. The relative path must be a valid relative path for the platform
// 4. We allow a URI for embedded files, but only if it has a name
hasAlias = false;
if( aFileName.empty() )
return false;
if( aFileName.StartsWith( wxT( "file://" ) )
|| aFileName.StartsWith( FILEEXT::KiCadUriPrefix + "://" ) )
{
size_t prefixLength = aFileName.StartsWith( wxT( "file://" ) ) ? 7 : 14;
if( aFileName.length() > prefixLength && aFileName[prefixLength] != '/' )
return true;
else
return false;
}
wxString filename = aFileName;
wxString lpath;
size_t aliasStart = aFileName.StartsWith( ':' ) ? 1 : 0;

View File

@ -143,7 +143,7 @@ FONT* FONT::getDefaultFont()
}
FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic )
FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic, const std::vector<wxString>* aEmbeddedFiles )
{
if( aFontName.empty() || aFontName.StartsWith( KICAD_FONT_NAME ) )
return getDefaultFont();
@ -156,7 +156,7 @@ FONT* FONT::GetFont( const wxString& aFontName, bool aBold, bool aItalic )
font = s_fontMap[key];
if( !font )
font = OUTLINE_FONT::LoadFont( aFontName, aBold, aItalic );
font = OUTLINE_FONT::LoadFont( aFontName, aBold, aItalic, aEmbeddedFiles );
if( !font )
font = getDefaultFont();

View File

@ -27,6 +27,7 @@
#include <macros.h>
#include <cstdint>
#include <reporter.h>
#include <embedded_files.h>
#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
@ -196,14 +197,25 @@ std::string FONTCONFIG::getFamilyStringByLang( FONTCONFIG_PAT& aPat, const wxStr
}
FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString &aFontFile,
int& aFaceIndex, bool aBold, bool aItalic )
FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString& aFontName, wxString& aFontFile,
int& aFaceIndex, bool aBold, bool aItalic,
const std::vector<wxString>* aEmbeddedFiles )
{
FF_RESULT retval = FF_RESULT::FF_ERROR;
if( !g_fcInitSuccess )
return retval;
FcConfig* config = FcConfigGetCurrent();
if( aEmbeddedFiles )
{
for( const auto& file : *aEmbeddedFiles )
{
FcConfigAppFontAddFile( config, (const FcChar8*) file.c_str().AsChar() );
}
}
wxString qualifiedFontName = aFontName;
wxScopedCharBuffer const fcBuffer = qualifiedFontName.ToUTF8();
@ -218,11 +230,11 @@ FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString
FcPatternAddString( pat, FC_FAMILY, (FcChar8*) fcBuffer.data() );
FcConfigSubstitute( nullptr, pat, FcMatchPattern );
FcConfigSubstitute( config, pat, FcMatchPattern );
FcDefaultSubstitute( pat );
FcResult r = FcResultNoMatch;
FcPattern* font = FcFontMatch( nullptr, pat, &r );
FcPattern* font = FcFontMatch( config, pat, &r );
wxString fontName;
@ -342,18 +354,29 @@ FONTCONFIG::FF_RESULT FONTCONFIG::FindFont( const wxString &aFontName, wxString
}
void FONTCONFIG::ListFonts( std::vector<std::string>& aFonts, const std::string& aDesiredLang )
void FONTCONFIG::ListFonts( std::vector<std::string>& aFonts, const std::string& aDesiredLang,
const std::vector<wxString>* aEmbeddedFiles, bool aForce )
{
if( !g_fcInitSuccess )
return;
// be sure to cache bust if the language changed
if( m_fontInfoCache.empty() || m_fontCacheLastLang != aDesiredLang )
if( m_fontInfoCache.empty() || m_fontCacheLastLang != aDesiredLang || aForce )
{
FcConfig* config = FcConfigGetCurrent();
if( aEmbeddedFiles )
{
for( const auto& file : *aEmbeddedFiles )
{
FcConfigAppFontAddFile( config, (const FcChar8*) file.c_str().AsChar() );
}
}
FcPattern* pat = FcPatternCreate();
FcObjectSet* os = FcObjectSetBuild( FC_FAMILY, FC_FAMILYLANG, FC_STYLE, FC_LANG, FC_FILE,
FC_OUTLINE, nullptr );
FcFontSet* fs = FcFontList( nullptr, pat, os );
FcFontSet* fs = FcFontList( config, pat, os );
for( int i = 0; fs && i < fs->nfont; ++i )
{

View File

@ -29,6 +29,10 @@
#include <geometry/shape_poly_set.h>
#include <font/fontconfig.h>
#include <font/outline_font.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_TABLES_H
#include FT_GLYPH_H
#include FT_BBOX_H
#include <trigo.h>
@ -53,7 +57,33 @@ OUTLINE_FONT::OUTLINE_FONT() :
}
OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, bool aItalic )
OUTLINE_FONT::EMBEDDING_PERMISSION OUTLINE_FONT::GetEmbeddingPermission() const
{
TT_OS2* os2 = reinterpret_cast<TT_OS2*>( FT_Get_Sfnt_Table( m_face, FT_SFNT_OS2 ) );
// If this table isn't present, we can't assume anything
if( !os2 )
return EMBEDDING_PERMISSION::RESTRICTED;
if( os2->fsType == FT_FSTYPE_INSTALLABLE_EMBEDDING ) // This allows the font to be exported from KiCad
return EMBEDDING_PERMISSION::INSTALLABLE;
if( os2->fsType & FT_FSTYPE_BITMAP_EMBEDDING_ONLY ) // We don't support bitmap fonts, so this disables embedding
return EMBEDDING_PERMISSION::RESTRICTED;
if( os2->fsType & FT_FSTYPE_EDITABLE_EMBEDDING ) // This allows us to use the font in KiCad but not export
return EMBEDDING_PERMISSION::EDITABLE;
if( os2->fsType & FT_FSTYPE_PREVIEW_AND_PRINT_EMBEDDING ) // This is not actually supported by KiCad ATM(2024)
return EMBEDDING_PERMISSION::PRINT_PREVIEW_ONLY;
// Anything else that is not explicitly enabled we treat as restricted.
return EMBEDDING_PERMISSION::RESTRICTED;
}
OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, bool aItalic,
const std::vector<wxString>* aEmbeddedFiles )
{
std::unique_ptr<OUTLINE_FONT> font = std::make_unique<OUTLINE_FONT>();
@ -61,7 +91,9 @@ OUTLINE_FONT* OUTLINE_FONT::LoadFont( const wxString& aFontName, bool aBold, boo
int faceIndex;
using fc = fontconfig::FONTCONFIG;
fc::FF_RESULT retval = Fontconfig()->FindFont( aFontName, fontFile, faceIndex, aBold, aItalic );
fc::FF_RESULT retval = Fontconfig()->FindFont( aFontName, fontFile, faceIndex, aBold, aItalic,
aEmbeddedFiles );
if( retval == fc::FF_RESULT::FF_ERROR )
return nullptr;

View File

@ -149,30 +149,6 @@ ALTIUM_COMPOUND_FILE::DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe )
}
std::map<wxString, wxString> ALTIUM_COMPOUND_FILE::ListLibFootprints()
{
if( m_libFootprintDirNameCache.empty() )
cacheLibFootprintNames();
return m_libFootprintDirNameCache;
}
std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*>
ALTIUM_COMPOUND_FILE::FindLibFootprintDirName( const wxString& aFpUnicodeName )
{
if( m_libFootprintNameCache.empty() )
cacheLibFootprintNames();
auto it = m_libFootprintNameCache.find( aFpUnicodeName );
if( it == m_libFootprintNameCache.end() )
return { wxEmptyString, nullptr };
return { it->first, it->second };
}
const CFB::COMPOUND_FILE_ENTRY*
ALTIUM_COMPOUND_FILE::FindStreamSingleLevel( const CFB::COMPOUND_FILE_ENTRY* aEntry,
const std::string aName, const bool aIsStream ) const
@ -333,53 +309,6 @@ ALTIUM_COMPOUND_FILE::FindStream( const std::vector<std::string>& aStreamPath )
}
void ALTIUM_COMPOUND_FILE::cacheLibFootprintNames()
{
m_libFootprintDirNameCache.clear();
m_libFootprintNameCache.clear();
if( !m_reader )
return;
const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry();
if( !root )
return;
m_reader->EnumFiles( root, 1,
[this]( const CFB::COMPOUND_FILE_ENTRY* tentry, const CFB::utf16string& dir,
int level ) -> int
{
if( m_reader->IsStream( tentry ) )
return 0;
m_reader->EnumFiles( tentry, 1,
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string&, int ) -> int
{
std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen );
if( m_reader->IsStream( entry ) && fileName == L"Parameters" )
{
ALTIUM_BINARY_PARSER parametersReader( *this, entry );
std::map<wxString, wxString> parameterProperties =
parametersReader.ReadProperties();
wxString key = ALTIUM_PROPS_UTILS::ReadString(
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
wxString fpName = ALTIUM_PROPS_UTILS::ReadUnicodeString(
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
m_libFootprintDirNameCache[key] = fpName;
m_libFootprintNameCache[fpName] = tentry;
}
return 0;
} );
return 0;
} );
}
ALTIUM_BINARY_PARSER::ALTIUM_BINARY_PARSER( const ALTIUM_COMPOUND_FILE& aFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry )
{

View File

@ -38,6 +38,7 @@
#include <wx/mstream.h>
#include <wx/zstream.h>
#include <math/vector2d.h>
#include <math/vector3.h>
namespace CFB
{
@ -63,8 +64,11 @@ public:
const CFB::COMPOUND_FILE_ENTRY* m_pinsSymbolLineWidth;
};
class ALTIUM_COMPOUND_FILE
{
friend class ALTIUM_PCB_COMPOUND_FILE;
public:
/**
* Open a CFB file. Constructor might throw an IO_ERROR.
@ -90,10 +94,6 @@ public:
std::unique_ptr<ALTIUM_COMPOUND_FILE> DecodeIntLibStream( const CFB::COMPOUND_FILE_ENTRY& cfe );
std::map<wxString, wxString> ListLibFootprints();
std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*> FindLibFootprintDirName( const wxString& aFpUnicodeName );
const CFB::COMPOUND_FILE_ENTRY* FindStream( const std::vector<std::string>& aStreamPath ) const;
const CFB::COMPOUND_FILE_ENTRY* FindStream( const CFB::COMPOUND_FILE_ENTRY* aStart, const std::vector<std::string>& aStreamPath ) const;
@ -108,13 +108,8 @@ public:
private:
void cacheLibFootprintNames();
std::unique_ptr<CFB::CompoundFileReader> m_reader;
std::vector<char> m_buffer;
std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> m_libFootprintNameCache;
std::map<wxString, wxString> m_libFootprintDirNameCache;
};

View File

@ -106,6 +106,8 @@ edge_connector
edge_plating
edge_width
effects
embedded_fonts
embedded_files
enabled
end
epsilon_r

View File

@ -1184,6 +1184,26 @@ TOOL_ACTION ACTIONS::pluginsReload( TOOL_ACTION_ARGS()
.Tooltip( _( "Reload all python plugins and refresh plugin menus" ) )
.Icon( BITMAPS::reload ) );
// Embedding Files
TOOL_ACTION ACTIONS::embeddedFiles( TOOL_ACTION_ARGS()
.Name( "common.Embed.embededFile" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Embedded Files" ) )
.Tooltip( _( "Manage embedded files" ) ) );
TOOL_ACTION ACTIONS::removeFile( TOOL_ACTION_ARGS()
.Name( "common.Embed.removeFile" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Remove File" ) )
.Tooltip( _( "Remove an embedded file" ) ) );
TOOL_ACTION ACTIONS::extractFile( TOOL_ACTION_ARGS()
.Name( "common.Embed.extractFile" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Extract File" ) )
.Tooltip( _( "Extract an embedded file" ) ) );
// System-wide selection Events
const TOOL_EVENT EVENTS::PointSelectedEvent( TC_MESSAGE, TA_ACTION, "common.Interactive.pointSelected" );

View File

@ -0,0 +1,92 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 <dialogs/dialog_embed_files.h>
#include <dialogs/panel_embedded_files.h>
#include <eda_draw_frame.h>
#include <eda_item.h>
#include <embedded_files.h>
#include <tool/actions.h>
#include <wx/debug.h>
#include <wx/filedlg.h>
#include <tool/embed_tool.h>
EMBED_TOOL::EMBED_TOOL( const std::string& aName ) :
TOOL_INTERACTIVE( aName )
{
}
EMBED_TOOL::EMBED_TOOL() :
TOOL_INTERACTIVE( "common.Embed" )
{
}
bool EMBED_TOOL::Init()
{
m_files = getModel<EDA_ITEM>()->GetEmbeddedFiles();
return true;
}
void EMBED_TOOL::Reset( RESET_REASON aReason )
{
m_files = getModel<EDA_ITEM>()->GetEmbeddedFiles();
}
int EMBED_TOOL::AddFile( const TOOL_EVENT& aEvent )
{
wxString name = aEvent.Parameter<wxString>();
m_files->AddFile( name, false );
return 1;
}
int EMBED_TOOL::RemoveFile( const TOOL_EVENT& aEvent )
{
wxString name = aEvent.Parameter<wxString>();
m_files->RemoveFile( name );
return 1;
}
std::vector<wxString> EMBED_TOOL::GetFileList()
{
std::vector<wxString> list;
for( auto& [name, file] : m_files->EmbeddedFileMap() )
{
list.push_back( name );
}
return list;
}
void EMBED_TOOL::setTransitions()
{
Go( &EMBED_TOOL::AddFile, ACTIONS::embeddedFiles.MakeEvent() );
Go( &EMBED_TOOL::RemoveFile, ACTIONS::removeFile.MakeEvent() );
}

View File

@ -22,12 +22,14 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <wx/checkbox.h>
#include <wx/combo.h>
#include <wx/filedlg.h>
#include <wx/dirdlg.h>
#include <wx/textctrl.h>
#include <bitmaps.h>
#include <embedded_files.h>
#include <kiway.h>
#include <kiway_player.h>
#include <kiway_express.h>
@ -37,6 +39,7 @@
#include <env_paths.h>
#include <pgm_base.h>
#include <widgets/wx_grid.h>
#include <widgets/filedlg_open_embed_file.h>
#include <widgets/grid_text_button_helpers.h>
#include <eda_doc.h>
@ -323,16 +326,25 @@ void GRID_CELL_FPID_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
class TEXT_BUTTON_URL : public wxComboCtrl
{
public:
TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg, SEARCH_STACK* aSearchStack ) :
TEXT_BUTTON_URL( wxWindow* aParent, DIALOG_SHIM* aParentDlg, SEARCH_STACK* aSearchStack, EMBEDDED_FILES* aFiles ) :
wxComboCtrl( aParent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxTE_PROCESS_ENTER | wxBORDER_NONE ),
m_dlg( aParentDlg ),
m_searchStack( aSearchStack )
m_searchStack( aSearchStack ),
m_files( aFiles )
{
SetButtonBitmaps( KiBitmapBundle( BITMAPS::www ) );
UpdateButtonBitmaps();
// win32 fix, avoids drawing the "native dropdown caret"
Customize( wxCC_IFLAG_HAS_NONSTANDARD_BUTTON );
// Bind event to handle text changes
Bind(wxEVT_TEXT, &TEXT_BUTTON_URL::OnTextChange, this);
}
~TEXT_BUTTON_URL()
{
Unbind(wxEVT_TEXT, &TEXT_BUTTON_URL::OnTextChange, this);
}
protected:
@ -345,19 +357,59 @@ protected:
{
wxString filename = GetValue();
if( !filename.IsEmpty() && filename != wxT( "~" ) )
GetAssociatedDocument( m_dlg, GetValue(), &m_dlg->Prj(), m_searchStack );
if (filename.IsEmpty() || filename == wxT("~"))
{
FILEDLG_OPEN_EMBED_FILE customize;
wxFileDialog openFileDialog( this, _( "Open file" ), "", "", "All files (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
openFileDialog.SetCustomizeHook( customize );
if( openFileDialog.ShowModal() == wxID_OK )
{
filename = openFileDialog.GetPath();
wxFileName fn( filename );
if( customize.GetEmbed() )
{
EMBEDDED_FILES::EMBEDDED_FILE* result = m_files->AddFile( fn, false );
SetValue( result->GetLink() );
}
else
{
SetValue( "file://" + filename );
}
}
}
else
{
GetAssociatedDocument(m_dlg, GetValue(), &m_dlg->Prj(), m_searchStack, m_files);
}
}
void OnTextChange(wxCommandEvent& event)
{
UpdateButtonBitmaps();
event.Skip(); // Ensure that other handlers can process this event too
}
void UpdateButtonBitmaps()
{
if (GetValue().IsEmpty())
SetButtonBitmaps(KiBitmapBundle(BITMAPS::small_folder));
else
SetButtonBitmaps(KiBitmapBundle(BITMAPS::www));
}
DIALOG_SHIM* m_dlg;
SEARCH_STACK* m_searchStack;
EMBEDDED_FILES* m_files;
};
void GRID_CELL_URL_EDITOR::Create( wxWindow* aParent, wxWindowID aId,
wxEvtHandler* aEventHandler )
{
m_control = new TEXT_BUTTON_URL( aParent, m_dlg, m_searchStack );
m_control = new TEXT_BUTTON_URL( aParent, m_dlg, m_searchStack, m_files );
WX_GRID::CellEditorSetMargins( Combo() );
#if wxUSE_VALIDATORS

View File

@ -154,7 +154,7 @@ class WX_GRID_ALT_ROW_COLOR_PROVIDER : public wxGridCellAttrProvider
{
public:
WX_GRID_ALT_ROW_COLOR_PROVIDER( const wxColor& aBaseColor ) : wxGridCellAttrProvider(),
m_attrOdd( new wxGridCellAttr() )
m_attrEven( new wxGridCellAttr() )
{
UpdateColors( aBaseColor );
}
@ -165,7 +165,7 @@ public:
// Choose the default color, taking into account if the dark mode theme is enabled
wxColor rowColor = aBaseColor.ChangeLightness( KIPLATFORM::UI::IsDarkTheme() ? 105 : 95 );
m_attrOdd->SetBackgroundColour( rowColor );
m_attrEven->SetBackgroundColour( rowColor );
}
@ -174,20 +174,20 @@ public:
{
wxGridCellAttrPtr cellAttr( wxGridCellAttrProvider::GetAttr( row, col, kind ) );
// Just pass through the cell attribute on even rows
if( row % 2 )
// Just pass through the cell attribute on odd rows (start normal to allow for the header row)
if( !( row % 2 ) )
return cellAttr.release();
if( !cellAttr )
{
cellAttr = m_attrOdd;
cellAttr = m_attrEven;
}
else
{
if( !cellAttr->HasBackgroundColour() )
{
cellAttr = cellAttr->Clone();
cellAttr->SetBackgroundColour( m_attrOdd->GetBackgroundColour() );
cellAttr->SetBackgroundColour( m_attrEven->GetBackgroundColour() );
}
}
@ -195,7 +195,7 @@ public:
}
private:
wxGridCellAttrPtr m_attrOdd;
wxGridCellAttrPtr m_attrEven;
};

View File

@ -202,6 +202,8 @@ const std::string FILEEXT::XaoFileExtension( "xao" );
const wxString FILEEXT::GerberFileExtensionsRegex( "(gbr|gko|pho|(g[tb][alops])|(gm?\\d\\d*)|(gp[tb]))" );
const std::string FILEEXT::KiCadUriPrefix( "kicad-embed" );
bool FILEEXT::IsGerberFileExtension( const wxString& ext )
{

View File

@ -26,9 +26,9 @@
#include <schematic.h>
#include <eeschema_settings.h>
DIALOG_EESCHEMA_PAGE_SETTINGS::DIALOG_EESCHEMA_PAGE_SETTINGS( EDA_DRAW_FRAME* aParent,
DIALOG_EESCHEMA_PAGE_SETTINGS::DIALOG_EESCHEMA_PAGE_SETTINGS( EDA_DRAW_FRAME* aParent, EMBEDDED_FILES* aEmbeddedFiles,
VECTOR2I aMaxUserSizeMils ) :
DIALOG_PAGES_SETTINGS( aParent, schIUScale.IU_PER_MILS, aMaxUserSizeMils )
DIALOG_PAGES_SETTINGS( aParent, aEmbeddedFiles, schIUScale.IU_PER_MILS, aMaxUserSizeMils )
{
}

View File

@ -22,11 +22,13 @@
#include <dialogs/dialog_page_settings.h>
class EMBEDDED_FILES;
class DIALOG_EESCHEMA_PAGE_SETTINGS : public DIALOG_PAGES_SETTINGS
{
public:
DIALOG_EESCHEMA_PAGE_SETTINGS( EDA_DRAW_FRAME* aParent, VECTOR2I aMaxUserSizeMils );
DIALOG_EESCHEMA_PAGE_SETTINGS( EDA_DRAW_FRAME* aParent, EMBEDDED_FILES* aEmbeddedFiles, VECTOR2I aMaxUserSizeMils );
virtual ~DIALOG_EESCHEMA_PAGE_SETTINGS();
private:

View File

@ -108,7 +108,7 @@ DIALOG_LABEL_PROPERTIES::DIALOG_LABEL_PROPERTIES( SCH_EDIT_FRAME* aParent, SCH_L
m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
m_grid->SetTable( m_fields );
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this,
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, nullptr,
[&]( wxCommandEvent& aEvent )
{
OnAddField( aEvent );

View File

@ -40,6 +40,7 @@
#include <dialog_sim_model.h>
#include <panel_embedded_files.h>
#include <dialog_lib_symbol_properties.h>
#include <settings/settings_manager.h>
#include <symbol_editor_settings.h>
@ -63,11 +64,14 @@ DIALOG_LIB_SYMBOL_PROPERTIES::DIALOG_LIB_SYMBOL_PROPERTIES( SYMBOL_EDIT_FRAME* a
m_delayedFocusColumn( -1 ),
m_delayedFocusPage( -1 )
{
m_embeddedFiles = new PANEL_EMBEDDED_FILES( m_NoteBook, m_libEntry );
m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
// Give a bit more room for combobox editors
m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
m_fields = new FIELDS_GRID_TABLE( this, aParent, m_grid, m_libEntry );
m_grid->SetTable( m_fields );
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this,
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, aLibEntry,
[&]( wxCommandEvent& aEvent )
{
OnAddField( aEvent );
@ -79,7 +83,7 @@ DIALOG_LIB_SYMBOL_PROPERTIES::DIALOG_LIB_SYMBOL_PROPERTIES( SYMBOL_EDIT_FRAME* a
m_grid->ShowHideColumns( cfg->m_EditSymbolVisibleColumns );
wxGridCellAttr* attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ) ) );
attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ), aLibEntry ) );
m_grid->SetAttr( DATASHEET_FIELD, FDC_VALUE, attr );
m_SymbolNameCtrl->SetValidator( FIELD_VALIDATOR( VALUE_FIELD ) );
@ -257,6 +261,8 @@ bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataToWindow()
m_NoteBook->SetSelection( (unsigned) m_lastOpenedPage );
m_embeddedFiles->TransferDataToWindow();
return true;
}
@ -469,6 +475,7 @@ bool DIALOG_LIB_SYMBOL_PROPERTIES::TransferDataFromWindow()
// occurs.
m_Parent->SetShowDeMorgan( m_hasAlternateBodyStyles->GetValue() );
m_embeddedFiles->TransferDataFromWindow();
return true;
}

View File

@ -32,6 +32,7 @@
class SYMBOL_EDIT_FRAME;
class LIB_SYMBOL;
class PANEL_EMBEDDED_FILES;
class WX_GRID;
@ -93,6 +94,8 @@ public:
std::bitset<64> m_shownColumns;
wxSize m_size;
PANEL_EMBEDDED_FILES* m_embeddedFiles;
private:
static int m_lastOpenedPage; // To remember the last notebook selection

View File

@ -30,6 +30,7 @@
#include <erc/erc_item.h>
#include <panel_text_variables.h>
#include <panel_bom_presets.h>
#include <panel_embedded_files.h>
#include <project/project_file.h>
#include <project/net_settings.h>
#include <settings/settings_manager.h>
@ -121,6 +122,16 @@ DIALOG_SCHEMATIC_SETUP::DIALOG_SCHEMATIC_SETUP( SCH_EDIT_FRAME* aFrame ) :
return new PANEL_TEXT_VARIABLES( aParent, &Prj() );
}, _( "Text Variables" ) );
m_treebook->AddPage( new wxPanel( GetTreebook() ), _( "Schematic Data" ) );
m_embeddedFilesPage = m_treebook->GetPageCount();
m_treebook->AddLazySubPage(
[this]( wxWindow* aParent ) -> wxWindow*
{
return new PANEL_EMBEDDED_FILES( aParent, &m_frame->Schematic() );
}, _( "Embedded Files" ) );
for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
m_treebook->ExpandNode( i );

View File

@ -56,6 +56,7 @@ protected:
size_t m_busesPage;
size_t m_severitiesPage;
size_t m_netclassesPage;
size_t m_embeddedFilesPage;
};

View File

@ -67,7 +67,7 @@ DIALOG_SHEET_PROPERTIES::DIALOG_SHEET_PROPERTIES( SCH_EDIT_FRAME* aParent, SCH_S
m_grid->SetDefaultRowSize( m_grid->GetDefaultRowSize() + 4 );
m_grid->SetTable( m_fields );
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this,
m_grid->PushEventHandler( new FIELDS_GRID_TRICKS( m_grid, this, &aParent->Schematic(),
[&]( wxCommandEvent& aEvent )
{
OnAddField( aEvent );

View File

@ -343,7 +343,8 @@ void DIALOG_SYMBOL_FIELDS_TABLE::SetupColumnProperties( int aCol )
else if( m_dataModel->GetColFieldName( aCol ) == GetCanonicalFieldName( DATASHEET_FIELD ) )
{
// set datasheet column viewer button
attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ) ) );
attr->SetEditor( new GRID_CELL_URL_EDITOR( this, PROJECT_SCH::SchSearchS( &Prj() ),
&m_parent->Schematic() ) );
m_grid->SetColAttr( aCol, attr );
}
else if( m_dataModel->ColIsQuantity( aCol ) || m_dataModel->ColIsItemNumber( aCol ) )

View File

@ -334,7 +334,7 @@ DIALOG_SYMBOL_PROPERTIES::DIALOG_SYMBOL_PROPERTIES( SCH_EDIT_FRAME* aParent,
m_pinGrid->SetDefaultRowSize( m_pinGrid->GetDefaultRowSize() + 4 );
m_fieldsGrid->SetTable( m_fields );
m_fieldsGrid->PushEventHandler( new FIELDS_GRID_TRICKS( m_fieldsGrid, this,
m_fieldsGrid->PushEventHandler( new FIELDS_GRID_TRICKS( m_fieldsGrid, this, &aParent->Schematic(),
[&]( wxCommandEvent& aEvent )
{
OnAddField( aEvent );

View File

@ -26,6 +26,7 @@
#include <kiway.h>
#include <symbol_edit_frame.h>
#include <dialogs/panel_gal_display_options.h>
#include <filename_resolver.h>
#include <pgm_base.h>
#include <project/project_file.h>
#include <project/project_local_settings.h>
@ -69,14 +70,6 @@ bool SCH_EDIT_FRAME::LoadProjectSettings()
BASE_SCREEN::m_DrawingSheetFileName = settings.m_SchDrawingSheetFileName;
// Load the drawing sheet from the filename stored in BASE_SCREEN::m_DrawingSheetFileName.
// If empty, or not existing, the default drawing sheet is loaded.
wxString filename = DS_DATA_MODEL::ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
Prj().GetProjectPath() );
if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, nullptr ) )
ShowInfoBarError( _( "Error loading drawing sheet." ), true );
PROJECT_LOCAL_SETTINGS& localSettings = Prj().GetLocalSettings();
EE_SELECTION_TOOL* selTool = GetToolManager()->GetTool<EE_SELECTION_TOOL>();
@ -87,6 +80,26 @@ bool SCH_EDIT_FRAME::LoadProjectSettings()
}
void SCH_EDIT_FRAME::LoadDrawingSheet()
{
// Load the drawing sheet from the filename stored in BASE_SCREEN::m_DrawingSheetFileName.
// If empty, or not existing, the default drawing sheet is loaded.
SCHEMATIC_SETTINGS& settings = Schematic().Settings();
FILENAME_RESOLVER resolver;
resolver.SetProject( &Prj() );
resolver.SetProgramBase( &Pgm() );
wxString filename = resolver.ResolvePath( settings.m_SchDrawingSheetFileName,
Prj().GetProjectPath(),
Schematic().GetEmbeddedFiles() );
wxString msg;
if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( filename, &msg ) )
ShowInfoBarError( msg, true );
}
void SCH_EDIT_FRAME::ShowSchematicSetupDialog( const wxString& aInitialPage )
{
SCH_SCREENS screens( Schematic().Root() );
@ -177,15 +190,20 @@ void SCH_EDIT_FRAME::saveProjectSettings()
if( !BASE_SCREEN::m_DrawingSheetFileName.IsEmpty() )
{
wxFileName layoutfn( DS_DATA_MODEL::ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
Prj().GetProjectPath() ) );
FILENAME_RESOLVER resolve;
resolve.SetProject( &Prj() );
resolve.SetProgramBase( &Pgm() );
wxFileName layoutfn( resolve.ResolvePath( BASE_SCREEN::m_DrawingSheetFileName,
Prj().GetProjectPath(),
Schematic().GetEmbeddedFiles() ) );
bool success = true;
if( !layoutfn.IsAbsolute() )
success = layoutfn.MakeAbsolute( Prj().GetProjectPath() );
if( success && layoutfn.IsOk() && !layoutfn.FileExists() )
if( success && layoutfn.IsOk() && !layoutfn.FileExists() && layoutfn.HasName() )
{
if( layoutfn.DirExists() && layoutfn.IsDirWritable() )
DS_DATA_MODEL::GetTheInstance().Save( layoutfn.GetFullPath() );

View File

@ -37,6 +37,7 @@
#include <memory>
#include <connection_graph.h>
#include "eeschema_helpers.h"
#include <filename_resolver.h>
#include <kiway.h>
#include <sch_painter.h>
#include <locale_io.h>
@ -111,9 +112,14 @@ void EESCHEMA_JOBS_HANDLER::InitRenderSettings( SCH_RENDER_SETTINGS* aRenderSett
auto loadSheet =
[&]( const wxString& path ) -> bool
{
wxString absolutePath = DS_DATA_MODEL::ResolvePath( path,
aSch->Prj().GetProjectPath() );
wxString msg;
FILENAME_RESOLVER resolve;
resolve.SetProject( &aSch->Prj() );
resolve.SetProgramBase( &Pgm() );
wxString absolutePath = resolve.ResolvePath( path,
wxGetCwd(),
aSch->GetEmbeddedFiles() );
if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( absolutePath, &msg ) )
{

View File

@ -21,6 +21,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <embedded_files.h>
#include <kiway.h>
#include <kiway_player.h>
#include <dialog_shim.h>
@ -238,8 +239,16 @@ void FIELDS_GRID_TABLE::initGrid( WX_GRID* aGrid )
fpIdEditor->SetValidator( m_nonUrlValidator );
m_footprintAttr->SetEditor( fpIdEditor );
EMBEDDED_FILES* files = nullptr;
if( m_frame->GetFrameType() == FRAME_SCH )
files = m_frame->GetScreen()->Schematic();
else if( m_frame->GetFrameType() == FRAME_SCH_SYMBOL_EDITOR || m_frame->GetFrameType() == FRAME_SCH_VIEWER )
files = m_part;
m_urlAttr = new wxGridCellAttr;
GRID_CELL_URL_EDITOR* urlEditor = new GRID_CELL_URL_EDITOR( m_dialog, PROJECT_SCH::SchSearchS( &m_frame->Prj() ) );
GRID_CELL_URL_EDITOR* urlEditor =
new GRID_CELL_URL_EDITOR( m_dialog, PROJECT_SCH::SchSearchS( &m_frame->Prj() ), files );
urlEditor->SetValidator( m_urlValidator );
m_urlAttr->SetEditor( urlEditor );
@ -292,6 +301,9 @@ void FIELDS_GRID_TABLE::initGrid( WX_GRID* aGrid )
SCH_EDIT_FRAME* editFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
wxArrayString existingNetclasses;
wxArrayString fonts;
std::vector<std::string> fontNames;
if( editFrame )
{
// Load the combobox with existing existingNetclassNames
@ -302,15 +314,30 @@ void FIELDS_GRID_TABLE::initGrid( WX_GRID* aGrid )
for( const auto& [ name, netclass ] : settings->m_NetClasses )
existingNetclasses.push_back( name );
// We don't need to re-cache the embedded fonts when looking at symbols in the schematic editor
// because the fonts are all available in the schematic.
const std::vector<wxString>* fontFiles = nullptr;
if( m_frame->GetScreen() && m_frame->GetScreen()->Schematic() )
fontFiles = m_frame->GetScreen()->Schematic()->GetEmbeddedFiles()->GetFontFiles();
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
fontFiles, false );
}
else
{
const std::vector<wxString>* fontFiles = m_part->GetEmbeddedFiles()->UpdateFontFiles();
// If there are font files embedded, we want to re-cache our fonts for each symbol that we
// are looking at in the symbol editor.
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
fontFiles, !fontFiles->empty() );
}
m_netclassAttr = new wxGridCellAttr;
m_netclassAttr->SetEditor( new GRID_CELL_COMBOBOX( existingNetclasses ) );
wxArrayString fonts;
std::vector<std::string> fontNames;
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ) );
for( const std::string& name : fontNames )
fonts.Add( wxString( name ) );
@ -931,8 +958,9 @@ void FIELDS_GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
else if (event.GetId() == MYID_SHOW_DATASHEET )
{
wxString datasheet_uri = m_grid->GetCellValue( DATASHEET_FIELD, FDC_VALUE );
GetAssociatedDocument( m_dlg, datasheet_uri, &m_dlg->Prj(),
PROJECT_SCH::SchSearchS( &m_dlg->Prj() ) );
PROJECT_SCH::SchSearchS( &m_dlg->Prj() ), m_files );
}
else
{

View File

@ -31,22 +31,25 @@
class SCH_BASE_FRAME;
class DIALOG_SHIM;
class EMBEDDED_FILES;
class SCH_LABEL_BASE;
class FIELDS_GRID_TRICKS : public GRID_TRICKS
{
public:
FIELDS_GRID_TRICKS( WX_GRID* aGrid, DIALOG_SHIM* aDialog,
FIELDS_GRID_TRICKS( WX_GRID* aGrid, DIALOG_SHIM* aDialog, EMBEDDED_FILES* aFiles,
std::function<void( wxCommandEvent& )> aAddHandler ) :
GRID_TRICKS( aGrid, std::move( aAddHandler ) ),
m_dlg( aDialog )
m_dlg( aDialog ),
m_files( aFiles )
{}
protected:
void showPopupMenu( wxMenu& menu, wxGridEvent& aEvent ) override;
void doPopupSelection( wxCommandEvent& event ) override;
DIALOG_SHIM* m_dlg;
EMBEDDED_FILES* m_files;
};

View File

@ -517,6 +517,11 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
}
// After the schematic is successfully loaded, we load the drawing sheet.
// This allows us to use the drawing sheet embedded in the schematic (if any)
// instead of the default one.
LoadDrawingSheet();
schematic.PruneOrphanedSymbolInstances( Prj().GetProjectName(), sheetList );
schematic.PruneOrphanedSheetInstances( Prj().GetProjectName(), sheetList );

View File

@ -24,6 +24,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <font/outline_font.h>
#include <sch_draw_panel.h>
#include <plotters/plotter.h>
#include <sch_screen.h>
@ -1872,3 +1873,50 @@ double LIB_SYMBOL::Similarity( const SCH_ITEM& aOther ) const
return similarity;
}
EMBEDDED_FILES* LIB_SYMBOL::GetEmbeddedFiles()
{
return static_cast<EMBEDDED_FILES*>( this );
}
const EMBEDDED_FILES* LIB_SYMBOL::GetEmbeddedFiles() const
{
return static_cast<const EMBEDDED_FILES*>( this );
}
void LIB_SYMBOL::EmbedFonts()
{
using OUTLINE_FONT = KIFONT::OUTLINE_FONT;
using EMBEDDING_PERMISSION = OUTLINE_FONT::EMBEDDING_PERMISSION;
std::set<OUTLINE_FONT*> fonts;
for( SCH_ITEM& item : m_drawings )
{
if( item.Type() == SCH_TEXT_T )
{
auto* text = static_cast<SCH_TEXT*>( &item );
if( auto* font = text->GetFont(); font && !font->IsStroke() )
{
auto* outline = static_cast<OUTLINE_FONT*>( font );
auto permission = outline->GetEmbeddingPermission();
if( permission == EMBEDDING_PERMISSION::EDITABLE
|| permission == EMBEDDING_PERMISSION::INSTALLABLE )
{
fonts.insert( outline );
}
}
}
}
for( auto* font : fonts )
{
auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT;
}
}

View File

@ -27,6 +27,7 @@
#ifndef LIB_SYMBOL_H
#define LIB_SYMBOL_H
#include <embedded_files.h>
#include <symbol.h>
#include <sch_field.h>
#include <sch_pin.h>
@ -73,7 +74,7 @@ struct LIB_SYMBOL_UNIT
* A library symbol object is typically saved and loaded in a symbol library file (.lib).
* Library symbols are different from schematic symbols.
*/
class LIB_SYMBOL : public SYMBOL, public LIB_TREE_ITEM
class LIB_SYMBOL : public SYMBOL, public LIB_TREE_ITEM, public EMBEDDED_FILES
{
public:
LIB_SYMBOL( const wxString& aName, LIB_SYMBOL* aParent = nullptr,
@ -333,6 +334,11 @@ public:
return GetValueField().GetText();
}
EMBEDDED_FILES* GetEmbeddedFiles() override;
const EMBEDDED_FILES* GetEmbeddedFiles() const;
void EmbedFonts() override;
void RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction ) override;
/**

View File

@ -66,6 +66,7 @@
#include <tool/action_toolbar.h>
#include <tool/common_control.h>
#include <tool/common_tools.h>
#include <tool/embed_tool.h>
#include <tool/picker_tool.h>
#include <tool/properties_tool.h>
#include <tool/selection.h>
@ -368,6 +369,7 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
}
LoadProjectSettings();
LoadDrawingSheet();
view->SetLayerVisible( LAYER_ERC_ERR, cfg->m_Appearance.show_erc_errors );
view->SetLayerVisible( LAYER_ERC_WARN, cfg->m_Appearance.show_erc_warnings );
@ -524,6 +526,7 @@ void SCH_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new EE_POINT_EDITOR );
m_toolManager->RegisterTool( new SCH_NAVIGATE_TOOL );
m_toolManager->RegisterTool( new PROPERTIES_TOOL );
m_toolManager->RegisterTool( new EMBED_TOOL );
m_toolManager->InitTools();
// Run the selection tool, it is supposed to be always active

View File

@ -159,6 +159,11 @@ public:
*/
bool LoadProjectSettings();
/**
* Load the drawing sheet file.
*/
void LoadDrawingSheet();
void ShowSchematicSetupDialog( const wxString& aInitialPage = wxEmptyString );
void LoadSettings( APP_SETTINGS_BASE* aCfg ) override;

View File

@ -49,7 +49,8 @@
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220914 // Symbol unit display names.
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20220914 // Don't save property ID
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20230620 // ki_description -> Description Field
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20231120 // generator_version; V8 cleanups
//#define SEXPR_SYMBOL_LIB_FILE_VERSION 20231120 // generator_version; V8 cleanups
#define SEXPR_SYMBOL_LIB_FILE_VERSION 20240529 // Embedded Files
/**
* Schematic file version.
@ -105,4 +106,5 @@
//#define SEXPR_SCHEMATIC_FILE_VERSION 20231120 // generator_version; V8 cleanups
//#define SEXPR_SCHEMATIC_FILE_VERSION 20240101 // Tables.
//#define SEXPR_SCHEMATIC_FILE_VERSION 20240417 // Rule areas
#define SEXPR_SCHEMATIC_FILE_VERSION 20240602 // Sheet attributes
//#define SEXPR_SCHEMATIC_FILE_VERSION 20240602 // Sheet attributes
#define SEXPR_SCHEMATIC_FILE_VERSION 20240620 // Embedded Files

View File

@ -372,6 +372,14 @@ void SCH_IO_KICAD_SEXPR::Format( SCH_SHEET* aSheet )
wxCHECK( screen, /* void */ );
// If we've requested to embed the fonts in the schematic, do so.
// Otherwise, clear the embedded fonts from the schematic. Embedded
// fonts will be used if available
if( m_schematic->GetAreFontsEmbedded() )
m_schematic->EmbedFonts();
else
m_schematic->GetEmbeddedFiles()->ClearEmbeddedFonts();
m_out->Print( 0, "(kicad_sch (version %d) (generator \"eeschema\") (generator_version \"%s\")\n\n",
SEXPR_SCHEMATIC_FILE_VERSION, GetMajorMinorVersion().c_str().AsChar() );
@ -477,7 +485,7 @@ void SCH_IO_KICAD_SEXPR::Format( SCH_SHEET* aSheet )
case SCH_SHAPE_T:
saveShape( static_cast<SCH_SHAPE*>( item ), 1 );
break;
case SCH_RULE_AREA_T:
saveRuleArea( static_cast<SCH_RULE_AREA*>( item ), 1 );
break;
@ -509,6 +517,13 @@ void SCH_IO_KICAD_SEXPR::Format( SCH_SHEET* aSheet )
instances.emplace_back( aSheet->GetRootInstance() );
saveInstances( instances, 1 );
m_out->Print( 1, "(embedded_fonts %s)\n",
m_schematic->GetAreFontsEmbedded() ? "yes" : "no" );
// Save any embedded files
if( !m_schematic->GetEmbeddedFiles()->IsEmpty() )
m_schematic->WriteEmbeddedFiles( *m_out, 1, true );
}
m_out->Print( 0, ")\n" );
@ -527,6 +542,14 @@ void SCH_IO_KICAD_SEXPR::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSele
m_schematic = &aSchematic;
m_out = aFormatter;
// If we've requested to embed the fonts in the schematic, do so.
// Otherwise, clear the embedded fonts from the schematic. Embedded
// fonts will be used if available
if( m_schematic->GetAreFontsEmbedded() )
m_schematic->EmbedFonts();
else
m_schematic->GetEmbeddedFiles()->ClearEmbeddedFonts();
size_t i;
SCH_ITEM* item;
std::map<wxString, LIB_SYMBOL*> libSymbols;
@ -564,7 +587,7 @@ void SCH_IO_KICAD_SEXPR::Format( EE_SELECTION* aSelection, SCH_SHEET_PATH* aSele
for( const std::pair<const wxString, LIB_SYMBOL*>& libSymbol : libSymbols )
{
SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( libSymbol.second, *m_out, 1,
libSymbol.first );
libSymbol.first, false );
}
m_out->Print( 0, ")\n\n" );

View File

@ -130,7 +130,7 @@ void SCH_IO_KICAD_SEXPR_LIB_CACHE::Save( const std::optional<bool>& aOpt )
void SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
int aNestLevel, const wxString& aLibName )
int aNestLevel, const wxString& aLibName, bool aIncludeData )
{
wxCHECK_RET( aSymbol, "Invalid LIB_SYMBOL pointer." );
@ -138,6 +138,15 @@ void SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMAT
wxCHECK2( wxLocale::GetInfo( wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER ) == ".",
LOCALE_IO toggle );
// If we've requested to embed the fonts in the symbol, do so.
// Otherwise, clear the embedded fonts from the symbol. Embedded
// fonts will be used if available
if( aSymbol->GetAreFontsEmbedded() )
aSymbol->EmbedFonts();
else
aSymbol->GetEmbeddedFiles()->ClearEmbeddedFonts();
int nextFreeFieldId = MANDATORY_FIELDS;
std::vector<SCH_FIELD*> fields;
std::string name = aFormatter.Quotew( aSymbol->GetLibId().GetLibItemName().wx_str() );
@ -262,6 +271,12 @@ void SCH_IO_KICAD_SEXPR_LIB_CACHE::SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMAT
aFormatter.Print( aNestLevel + 1, ")\n" );
}
aFormatter.Print( aNestLevel + 1, "(embedded_fonts %s)\n",
aSymbol->GetAreFontsEmbedded() ? "yes" : "no" );
if( !aSymbol->EmbeddedFileMap().empty() )
aSymbol->WriteEmbeddedFiles( aFormatter, aNestLevel + 1, aIncludeData );
}
else
{

View File

@ -51,8 +51,8 @@ public:
void DeleteSymbol( const wxString& aName ) override;
static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter,
int aNestLevel = 0, const wxString& aLibName = wxEmptyString );
static void SaveSymbol( LIB_SYMBOL* aSymbol, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0,
const wxString& aLibName = wxEmptyString, bool aIncludeData = true );
void SetFileFormatVersionAtLoad( int aVersion ) { m_fileFormatVersionAtLoad = aVersion; }
int GetFileFormatVersionAtLoad() const { return m_fileFormatVersionAtLoad; }

View File

@ -31,6 +31,7 @@
#include <fmt/format.h>
#define wxUSE_BASE64 1
#include <wx/base64.h>
#include <wx/log.h>
#include <wx/mstream.h>
#include <wx/tokenzr.h>
@ -39,6 +40,8 @@
#include <sch_pin.h>
#include <math/util.h> // KiROUND, Clamp
#include <font/font.h>
#include <font/fontconfig.h>
#include <pgm_base.h>
#include <string_utils.h>
#include <sch_bitmap.h>
#include <sch_bus_entry.h>
@ -274,6 +277,13 @@ LIB_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::ParseSymbol( LIB_SYMBOL_MAP& aSymbolLibMa
}
}
for( auto& [text, params] : m_fontTextMap )
{
text->SetFont( KIFONT::FONT::GetFont( std::get<0>( params ), std::get<1>( params ),
std::get<2>( params ),
newSymbol->GetEmbeddedFiles()->UpdateFontFiles() ) );
}
return newSymbol;
}
@ -534,6 +544,31 @@ LIB_SYMBOL* SCH_IO_KICAD_SEXPR_PARSER::parseLibSymbol( LIB_SYMBOL_MAP& aSymbolLi
symbol->AddDrawItem( item, false );
break;
case T_embedded_fonts:
{
symbol->SetAreFontsEmbedded( parseBool() );
NeedRIGHT();
break;
}
case T_embedded_files:
{
EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
embeddedFilesParser.SyncLineReaderWith( *this );
try
{
embeddedFilesParser.ParseEmbedded( symbol->GetEmbeddedFiles() );
}
catch( const IO_ERROR& e )
{
wxLogError( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
break;
}
default:
Expecting( "pin_names, pin_numbers, arc, bezier, circle, pin, polyline, "
"rectangle, or text" );
@ -742,7 +777,7 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseEDA_TEXT( EDA_TEXT* aText, bool aConvertOve
}
if( !faceName.IsEmpty() )
aText->SetFont( KIFONT::FONT::GetFont( faceName, bold, italic ) );
m_fontTextMap[aText] = { faceName, bold, italic };
break;
@ -2755,10 +2790,48 @@ void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopya
parseBusAlias( screen );
break;
case T_embedded_fonts:
{
SCHEMATIC* schematic = aSheet->Schematic();
if( !schematic )
THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
CurLineNumber(), CurOffset() );
schematic->GetEmbeddedFiles()->SetAreFontsEmbedded( parseBool() );
NeedRIGHT();
break;
}
case T_embedded_files:
{
SCHEMATIC* schematic = aSheet->Schematic();
if( !schematic )
THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
CurLineNumber(), CurOffset() );
EMBEDDED_FILES_PARSER embeddedFilesParser( reader );
embeddedFilesParser.SyncLineReaderWith( *this );
try
{
embeddedFilesParser.ParseEmbedded( schematic->GetEmbeddedFiles() );
}
catch( const PARSE_ERROR& e )
{
wxLogError( e.What() );
}
SyncLineReaderWith( embeddedFilesParser );
break;
}
default:
Expecting( "symbol, paper, page, title_block, bitmap, sheet, junction, no_connect, "
"bus_entry, line, bus, text, label, class_label, global_label, "
"hierarchical_label, symbol_instances, rule_area, or bus_alias" );
Expecting( "bitmap, bus, bus_alias, bus_entry, class_label, embedded_files, global_label, "
"hierarchical_label, junction, label, line, no_connect, page, paper, rule_area, "
"sheet, symbol, symbol_instances, text, title_block" );
}
}
@ -2771,6 +2844,26 @@ void SCH_IO_KICAD_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopya
}
screen->UpdateLocalLibSymbolLinks();
screen->FixupEmbeddedData();
SCHEMATIC* schematic = aSheet->Schematic();
if( !schematic )
THROW_PARSE_ERROR( _( "No schematic object" ), CurSource(), CurLine(),
CurLineNumber(), CurOffset() );
for( auto& [text, params] : m_fontTextMap )
{
text->SetFont( KIFONT::FONT::GetFont( std::get<0>( params ), std::get<1>( params ),
std::get<2>( params ),
schematic->GetEmbeddedFiles()->UpdateFontFiles() ) );
}
// When loading the schematic, take a moment to cache the fonts so that the font
// picker can show the embedded fonts immediately.
std::vector<std::string> fontNames;
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
schematic->GetEmbeddedFiles()->GetFontFiles(), true );
if( m_requiredVersion < 20200828 )
screen->SetLegacySymbolInstanceData();

View File

@ -244,6 +244,8 @@ private:
std::set<KIID> m_uuids;
std::map<EDA_TEXT*, std::tuple<wxString, bool, bool>> m_fontTextMap;
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
const LINE_READER* m_lineReader; // for progress reporting
unsigned m_lastProgressLine;

View File

@ -1488,6 +1488,28 @@ void SCH_SCREEN::AddLibSymbol( LIB_SYMBOL* aLibSymbol )
}
void SCH_SCREEN::FixupEmbeddedData()
{
SCHEMATIC* schematic = Schematic();
for( auto& [name, libSym] : m_libSymbols )
{
for( auto& [filename, embeddedFile] : libSym->EmbeddedFileMap() )
{
EMBEDDED_FILES::EMBEDDED_FILE* file = schematic->GetEmbeddedFile( filename );
if( file )
{
embeddedFile->compressedEncodedData = file->compressedEncodedData;
embeddedFile->decompressedData = file->decompressedData;
embeddedFile->data_sha = file->data_sha;
embeddedFile->is_valid = file->is_valid;
}
}
}
}
void SCH_SCREEN::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
{
m_aliases.insert( aAlias );

View File

@ -490,6 +490,13 @@ public:
*/
void AddLibSymbol( LIB_SYMBOL* aLibSymbol );
/**
* After loading a file from disk, the library symbols do not yet contain the full
* data for their embedded files, only a reference. This iterates over all lib symbols
* in the schematic and updates the library symbols with the full data.
*/
void FixupEmbeddedData();
/**
* Add a bus alias definition (and transfers ownership of the pointer).
*/

View File

@ -24,18 +24,21 @@
#include <core/kicad_algo.h>
#include <ee_collectors.h>
#include <erc/erc_settings.h>
#include <sch_marker.h>
#include <font/outline_font.h>
#include <netlist_exporter_spice.h>
#include <project.h>
#include <project/project_file.h>
#include <project/net_settings.h>
#include <project/project_file.h>
#include <schematic.h>
#include <sch_junction.h>
#include <sch_label.h>
#include <sch_line.h>
#include <sch_marker.h>
#include <sch_screen.h>
#include <sim/spice_settings.h>
#include <sch_label.h>
#include <sim/spice_value.h>
#include <netlist_exporter_spice.h>
#include <wx/log.h>
bool SCHEMATIC::m_IsSchematicExists = false;
@ -846,3 +849,59 @@ void SCHEMATIC::ResolveERCExclusionsPostUpdate()
RootScreen()->Append( marker );
}
}
EMBEDDED_FILES* SCHEMATIC::GetEmbeddedFiles()
{
return static_cast<EMBEDDED_FILES*>( this );
}
const EMBEDDED_FILES* SCHEMATIC::GetEmbeddedFiles() const
{
return static_cast<const EMBEDDED_FILES*>( this );
}
void SCHEMATIC::EmbedFonts()
{
std::set<KIFONT::OUTLINE_FONT*> fonts;
SCH_SHEET_LIST sheetList = BuildUnorderedSheetList();
for( const SCH_SHEET_PATH& sheet : sheetList )
{
for( SCH_ITEM* item : sheet.LastScreen()->Items() )
{
if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
{
KIFONT::FONT* font = text->GetFont();
if( !font || font->IsStroke() )
continue;
using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
|| outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
{
fonts.insert( outline );
}
}
}
}
for( KIFONT::OUTLINE_FONT* font : fonts )
{
auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
if( !file )
{
wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
continue;
}
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT;
}
}

View File

@ -21,6 +21,7 @@
#define KICAD_SCHEMATIC_H
#include <eda_item.h>
#include <embedded_files.h>
#include <sch_sheet_path.h>
#include <schematic_settings.h>
@ -71,7 +72,7 @@ public:
* Right now, Eeschema can have only one schematic open at a time, but this could change.
* Please keep this possibility in mind when adding to this object.
*/
class SCHEMATIC : public SCHEMATIC_IFACE, public EDA_ITEM
class SCHEMATIC : public SCHEMATIC_IFACE, public EDA_ITEM, public EMBEDDED_FILES
{
public:
SCHEMATIC( PROJECT* aPrj );
@ -161,6 +162,9 @@ public:
std::vector<SCH_MARKER*> ResolveERCExclusions();
EMBEDDED_FILES* GetEmbeddedFiles() override;
const EMBEDDED_FILES* GetEmbeddedFiles() const;
/**
* Return a pointer to a bus alias object for the given label, or null if one
* doesn't exist.
@ -305,6 +309,11 @@ public:
*/
void RemoveAllListeners();
/**
* Embed fonts in the schematic
*/
void EmbedFonts() override;
/**
* True if a SCHEMATIC exists, false if not
*/

View File

@ -16,6 +16,7 @@ bus_alias
bus_entry
cells
center
checksum
circle
clock
clock_low
@ -38,12 +39,15 @@ do_not_autoplace
dot
edge_clock_high
effects
embedded_fonts
embedded_files
end
extends
external
exclude_from_sim
face
fields_autoplaced
file
fill
font
footprint

View File

@ -51,6 +51,7 @@
#include <tool/common_control.h>
#include <tool/common_tools.h>
#include <tool/editor_conditions.h>
#include <tool/embed_tool.h>
#include <tool/library_editor_control.h>
#include <tool/picker_tool.h>
#include <tool/properties_tool.h>
@ -395,6 +396,7 @@ void SYMBOL_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new LIBRARY_EDITOR_CONTROL );
m_toolManager->RegisterTool( new SYMBOL_EDITOR_CONTROL );
m_toolManager->RegisterTool( new PROPERTIES_TOOL );
m_toolManager->RegisterTool( new EMBED_TOOL );
m_toolManager->InitTools();
// Run the selection tool, it is supposed to be always active

View File

@ -496,6 +496,7 @@ int EE_INSPECTION_TOOL::RunSimulation( const TOOL_EVENT& aEvent )
int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
{
wxString datasheet;
EMBEDDED_FILES* files = nullptr;
if( m_frame->IsType( FRAME_SCH_SYMBOL_EDITOR ) )
{
@ -505,6 +506,7 @@ int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
return 0;
datasheet = symbol->GetDatasheetField().GetText();
files = symbol;
}
else if( m_frame->IsType( FRAME_SCH_VIEWER ) )
{
@ -514,6 +516,7 @@ int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
return 0;
datasheet = entry->GetDatasheetField().GetText();
files = entry;
}
else if( m_frame->IsType( FRAME_SCH ) )
{
@ -528,6 +531,7 @@ int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
// Use GetShownText() to resolve any text variables, but don't allow adding extra text
// (ie: the field name)
datasheet = field->GetShownText( &symbol->Schematic()->CurrentSheet(), false );
files = symbol->Schematic();
}
if( datasheet.IsEmpty() || datasheet == wxS( "~" ) )
@ -537,7 +541,7 @@ int EE_INSPECTION_TOOL::ShowDatasheet( const TOOL_EVENT& aEvent )
else
{
GetAssociatedDocument( m_frame, datasheet, &m_frame->Prj(),
PROJECT_SCH::SchSearchS( &m_frame->Prj() ) );
PROJECT_SCH::SchSearchS( &m_frame->Prj() ), files );
}
return 0;

View File

@ -200,7 +200,7 @@ int SCH_EDITOR_CONTROL::PageSetup( const TOOL_EVENT& aEvent )
undoCmd.SetDescription( _( "Page Settings" ) );
m_frame->SaveCopyInUndoList( undoCmd, UNDO_REDO::PAGESETTINGS, false, false );
DIALOG_EESCHEMA_PAGE_SETTINGS dlg( m_frame, VECTOR2I( MAX_PAGE_SIZE_EESCHEMA_MILS,
DIALOG_EESCHEMA_PAGE_SETTINGS dlg( m_frame, m_frame->Schematic().GetEmbeddedFiles(), VECTOR2I( MAX_PAGE_SIZE_EESCHEMA_MILS,
MAX_PAGE_SIZE_EESCHEMA_MILS ) );
dlg.SetWksFileName( BASE_SCREEN::m_DrawingSheetFileName );

View File

@ -87,7 +87,7 @@ void SCH_NAVIGATE_TOOL::HypertextCommand( const wxString& href )
menu.Append( 1, wxString::Format( _( "Open %s" ), href ) );
if( m_frame->GetPopupMenuSelectionFromUser( menu ) == 1 )
GetAssociatedDocument( m_frame, href, &m_frame->Prj() );
GetAssociatedDocument( m_frame, href, &m_frame->Prj(), nullptr, &m_frame->Schematic() );
}
}

View File

@ -30,6 +30,8 @@
#include <properties/property_mgr.h>
#include <sch_commit.h>
#include <sch_edit_frame.h>
#include <symbol_edit_frame.h>
#include <symbol_viewer_frame.h>
#include <schematic.h>
#include <settings/color_settings.h>
#include <string_utils.h>
@ -218,10 +220,31 @@ void SCH_PROPERTIES_PANEL::OnLanguageChanged( wxCommandEvent& aEvent )
void SCH_PROPERTIES_PANEL::updateFontList()
{
wxPGChoices fonts;
const std::vector<wxString>* fontFiles = nullptr;
if( m_frame->GetFrameType() == FRAME_SCH && m_frame->GetScreen() && m_frame->GetScreen()->Schematic() )
{
fontFiles = m_frame->GetScreen()->Schematic()->GetEmbeddedFiles()->GetFontFiles();
}
else if( m_frame->GetFrameType() == FRAME_SCH_SYMBOL_EDITOR )
{
SYMBOL_EDIT_FRAME* symbolFrame = static_cast<SYMBOL_EDIT_FRAME*>( m_frame );
if( symbolFrame->GetCurSymbol() )
fontFiles = symbolFrame->GetCurSymbol()->GetEmbeddedFiles()->UpdateFontFiles();
}
else if( m_frame->GetFrameType() == FRAME_SCH_VIEWER )
{
SYMBOL_VIEWER_FRAME* symbolFrame = static_cast<SYMBOL_VIEWER_FRAME*>( m_frame );
if( symbolFrame->GetSelectedSymbol() )
fontFiles = symbolFrame->GetSelectedSymbol()->GetEmbeddedFiles()->UpdateFontFiles();
}
// Regnerate font names
std::vector<std::string> fontNames;
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ) );
Fontconfig()->ListFonts( fontNames, std::string( Pgm().GetLanguageTag().utf8_str() ),
fontFiles );
fonts.Add( _( "Default Font" ), -1 );
fonts.Add( KICAD_FONT_NAME, -2 );

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 DIALOG_EMBED_FILES_H
#define DIALOG_EMBED_FILES_H
#include <dialog_shim.h>
#include <wx/panel.h>
class DIALOG_EMBED_FILES : public DIALOG_SHIM
{
public:
DIALOG_EMBED_FILES( wxWindow* aParent, const wxString& aTitle );
void InstallPanel( wxPanel* aPanel );
bool TransferDataToWindow() override;
bool TransferDataFromWindow() override;
protected:
wxPanel* m_contentPanel;
};
#endif //DIALOG_EMBED_FILES_H

View File

@ -29,6 +29,8 @@
class DS_DATA_MODEL;
class EDA_DRAW_FRAME;
class BASE_SCREEN;
class EMBEDDED_FILES;
class FILENAME_RESOLVER;
/*!
* DIALOG_PAGES_SETTINGS class declaration
@ -37,8 +39,8 @@ class BASE_SCREEN;
class DIALOG_PAGES_SETTINGS: public DIALOG_PAGES_SETTINGS_BASE
{
public:
DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, double aIuPerMils,
const VECTOR2D& aMaxUserSizeMils );
DIALOG_PAGES_SETTINGS( EDA_DRAW_FRAME* aParent, EMBEDDED_FILES* aEmbeddedFiles,
double aIuPerMils, const VECTOR2D& aMaxUserSizeMils );
virtual ~DIALOG_PAGES_SETTINGS();
const wxString GetWksFileName()
@ -130,7 +132,9 @@ protected:
TITLE_BLOCK m_tb; /// Temporary title block (basic inscriptions).
DS_DATA_MODEL* m_drawingSheet; // the alternate and temporary drawing sheet shown by the
// dialog when the initial one is replaced by a new one
double m_iuPerMils;
double m_iuPerMils;
EMBEDDED_FILES* m_embeddedFiles; // the embedded files reference from the parent
FILENAME_RESOLVER* m_filenameResolver;
private:
UNIT_BINDER m_customSizeX;

View File

@ -57,7 +57,8 @@ struct KICOMMON_API KEYWORD
*/
enum DSN_SYNTAX_T
{
DSN_NONE = -11,
DSN_NONE = -12,
DSN_BAR = -11, // Also called pipe '|'
DSN_COMMENT = -10,
DSN_STRING_QUOTE = -9,
DSN_QUOTE_DEF = -8,
@ -381,6 +382,13 @@ public:
*/
void NeedRIGHT();
/**
* Call #NextTok() and then verifies that the token read in is a #DSN_BAR.
*
* @throw IO_ERROR if the next token is not a #DSN_BAR
*/
void NeedBAR();
/**
* Return the C string representation of a #DSN_T value.
*/

View File

@ -31,6 +31,8 @@
#ifndef __INCLUDE__EDA_DOC_H__
#define __INCLUDE__EDA_DOC_H__ 1
class EMBEDDED_FILES;
/**
* Open a document (file) with the suitable browser.
*
@ -43,7 +45,7 @@
* @param aPaths Additional paths to search for local disk datasheet files
*/
bool GetAssociatedDocument( wxWindow* aParent, const wxString& aDocName, PROJECT* aProject,
SEARCH_STACK* aPaths = nullptr );
SEARCH_STACK* aPaths = nullptr, EMBEDDED_FILES* aFiles = nullptr );
#endif /* __INCLUDE__EDA_DOC_H__ */

View File

@ -52,6 +52,7 @@ enum class INSPECT_RESULT
class UNITS_PROVIDER;
class EDA_DRAW_FRAME;
class MSG_PANEL_ITEM;
class EMBEDDED_FILES;
namespace google { namespace protobuf { class Any; } }
@ -440,6 +441,8 @@ public:
virtual void ViewGetLayers( int aLayers[], int& aCount ) const override;
virtual EMBEDDED_FILES* GetEmbeddedFiles() { return nullptr; }
#if defined(DEBUG)
/**

243
include/embedded_files.h Normal file
View File

@ -0,0 +1,243 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 EMBEDDED_FILES_H
#define EMBEDDED_FILES_H
#include <map>
#include <wx/string.h>
#include <wx/filename.h>
#include <embedded_files_lexer.h>
#include <wildcards_and_files_ext.h>
#include <richio.h>
#include <picosha2.h>
class EMBEDDED_FILES
{
public:
struct EMBEDDED_FILE
{
enum class FILE_TYPE
{
FONT,
MODEL,
WORKSHEET,
DATASHEET,
OTHER
};
EMBEDDED_FILE() :
type( FILE_TYPE::OTHER ),
is_valid( false )
{}
bool Validate()
{
std::string new_sha;
picosha2::hash256_hex_string( decompressedData, new_sha );
is_valid = ( new_sha == data_sha );
return is_valid;
}
wxString GetLink() const
{
return wxString::Format( "%s://%s", FILEEXT::KiCadUriPrefix, name );
}
wxString name;
FILE_TYPE type;
std::string compressedEncodedData;
std::vector<char> decompressedData;
std::string data_sha;
bool is_valid;
};
enum class RETURN_CODE : int
{
OK, // Success
FILE_NOT_FOUND, // File not found on disk
PERMISSIONS_ERROR, // Could not read/write file
FILE_ALREADY_EXISTS, // File already exists in the collection
OUT_OF_MEMORY, // Could not allocate memory
CHECKSUM_ERROR, // Checksum in file does not match data
};
EMBEDDED_FILES() = default;
~EMBEDDED_FILES()
{
for( auto& file : m_files )
delete file.second;
}
/**
* Loads a file from disk and adds it to the collection.
* @param aName is the name of the file to load.
* @param aOverwrite is true if the file should be overwritten if it already exists.
*/
EMBEDDED_FILE* AddFile( const wxFileName& aName, bool aOverwrite );
/**
* Appends a file to the collection.
*/
void AddFile( EMBEDDED_FILE* aFile );
/**
* Removes a file from the collection and frees the memory.
* @param aName is the name of the file to remove.
*/
void RemoveFile( const wxString& name, bool aErase = true );
/**
* Output formatter for the embedded files.
* @param aOut is the output formatter.
* @param aNestLevel is the current indentation level.
* @param aWriteData is true if the actual data should be written. This is false when writing an element
* that is already embedded in a file that itself has embedded files (boards, schematics, etc.)
*/
void WriteEmbeddedFiles( OUTPUTFORMATTER& aOut, int aNestLevel, bool aWriteData ) const;
/**
* Returns the link for an embedded file.
* @param aFile is the file to get the link for.
* @return the link for the file to be used in a hyperlink.
*/
wxString GetEmbeddedFileLink( const EMBEDDED_FILE& aFile ) const
{
return aFile.GetLink();
}
bool HasFile( const wxString& name ) const
{
wxFileName fileName( name );
return m_files.find( fileName.GetFullName() ) != m_files.end();
}
bool IsEmpty() const
{
return m_files.empty();
}
/**
* Helper function to get a list of fonts for fontconfig to add to the library.
*
* This is neccesary because EMBEDDED_FILES lives in common at the moment and
* fontconfig is in libkicommon. This will create the cache files in the KiCad
* cache directory (if they do not already exist) and return the temp files names
*/
const std::vector<wxString>* UpdateFontFiles();
/**
* If we just need the cached version of the font files, we can use this function which
* is const and will not update the font files.
*/
const std::vector<wxString>* GetFontFiles() const;
/**
* Removes all embedded fonts from the collection
*/
void ClearEmbeddedFonts();
/**
* Takes data from the #decompressedData buffer and compresses it using ZSTD
* into the #compressedEncodedData buffer. The data is then Base64 encoded.
*
* This call is used when adding a new file to the collection from disk
*/
static RETURN_CODE CompressAndEncode( EMBEDDED_FILE& aFile );
/**
* Takes data from the #compressedEncodedData buffer and Base64 decodes it.
* The data is then decompressed using ZSTD and stored in the #decompressedData buffer.
*
* This call is used when loading the embedded files using the parsers.
*/
static RETURN_CODE DecompressAndDecode( EMBEDDED_FILE& aFile );
/**
* Returns the embedded file with the given name or nullptr if it does not exist.
*/
EMBEDDED_FILE* GetEmbeddedFile( const wxString& aName ) const
{
auto it = m_files.find( aName );
return it == m_files.end() ? nullptr : it->second;
}
const std::map<wxString, EMBEDDED_FILE*>& EmbeddedFileMap() const
{
return m_files;
}
wxFileName GetTempFileName( const wxString& aName ) const;
wxFileName GetTempFileName( EMBEDDED_FILE* aFile ) const;
void ClearEmbeddedFiles( bool aDeleteFiles = true )
{
for( auto& file : m_files )
{
if( aDeleteFiles )
delete file.second;
}
m_files.clear();
}
virtual void EmbedFonts() {};
void SetAreFontsEmbedded( bool aEmbedFonts )
{
m_embedFonts = aEmbedFonts;
}
bool GetAreFontsEmbedded() const
{
return m_embedFonts;
}
protected:
bool m_embedFonts; // If set, fonts will be embedded in the element on save
// Otherwise, font files embedded in the element will be
// removed on save
private:
std::map<wxString, EMBEDDED_FILE*> m_files;
std::vector<wxString> m_fontFiles;
};
class EMBEDDED_FILES_PARSER : public EMBEDDED_FILES_LEXER
{
public:
EMBEDDED_FILES_PARSER( LINE_READER* aReader ) :
EMBEDDED_FILES_LEXER( aReader )
{
}
void ParseEmbedded( EMBEDDED_FILES* aFiles );
};
#endif // EMBEDDED_FILES_H

View File

@ -36,6 +36,7 @@
class PROJECT;
class PGM_BASE;
class EMBEDDED_FILES;
struct SEARCH_PATH
{
@ -99,8 +100,10 @@ public:
*
* @param aFileName The configured file path to resolve
* @param aWorkingPath The current working path for relative path resolutions
* @param aFiles The embedded files object to use for embedded file resolution
*/
wxString ResolvePath( const wxString& aFileName, const wxString& aWorkingPath );
wxString ResolvePath( const wxString& aFileName, const wxString& aWorkingPath,
const EMBEDDED_FILES* aFiles );
/**
* Produce a relative path based on the existing search directories or returns the same path

View File

@ -141,7 +141,7 @@ public:
virtual bool IsItalic() const { return false; }
static FONT* GetFont( const wxString& aFontName = wxEmptyString, bool aBold = false,
bool aItalic = false );
bool aItalic = false, const std::vector<wxString>* aEmbeddedFiles = nullptr );
static bool IsStroke( const wxString& aFontName );
const wxString& GetName() const { return m_fontName; };

View File

@ -59,14 +59,18 @@ public:
*
* A return value of false indicates a serious error in the font system.
*/
FF_RESULT FindFont( const wxString& aFontName, wxString& aFontFile, int& aFaceIndex, bool aBold, bool aItalic );
FF_RESULT FindFont( const wxString& aFontName, wxString& aFontFile, int& aFaceIndex, bool aBold,
bool aItalic, const std::vector<wxString>* aEmbeddedFiles = nullptr );
/**
* List the current available font families.
*
* @param aDesiredLang The desired language of font name to report back if available, otherwise it will fallback
* @param aEmbeddedFiles A list of embedded to use for searching fonts, if nullptr, this is not used
* @param aForce If true, force rebuilding the font cache
*/
void ListFonts( std::vector<std::string>& aFonts, const std::string& aDesiredLang );
void ListFonts( std::vector<std::string>& aFonts, const std::string& aDesiredLang,
const std::vector<wxString>* aEmbeddedFiles = nullptr, bool aForce = false );
/**
* Set the reporter to use for reporting font substitution warnings.

View File

@ -36,10 +36,11 @@
#endif
#include FT_FREETYPE_H
#include FT_OUTLINE_H
//#include <gal/opengl/opengl_freetype.h>
#include <font/font.h>
#include <font/glyph.h>
#include <font/outline_decomposer.h>
#include <embedded_files.h>
#include <mutex>
@ -51,6 +52,16 @@ namespace KIFONT
class GAL_API OUTLINE_FONT : public FONT
{
public:
enum class EMBEDDING_PERMISSION
{
INSTALLABLE,
EDITABLE,
PRINT_PREVIEW_ONLY,
RESTRICTED,
INVALID
};
OUTLINE_FONT();
bool IsOutline() const override { return true; }
@ -75,11 +86,16 @@ public:
m_fakeItal = true;
}
const wxString& GetFileName() const { return m_fontFileName; }
EMBEDDING_PERMISSION GetEmbeddingPermission() const;
/**
* 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 );
static OUTLINE_FONT* LoadFont( const wxString& aFontFileName, bool aBold, bool aItalic,
const std::vector<wxString>* aEmbeddedFiles );
/**
* Compute the distance (interline) between 2 lines of text (for multiline texts). This is

View File

@ -228,6 +228,11 @@ public:
// API
static TOOL_ACTION pluginsReload;
// Embedding Files
static TOOL_ACTION embeddedFiles;
static TOOL_ACTION extractFile;
static TOOL_ACTION removeFile;
///< Cursor control event types
enum CURSOR_EVENT_TYPE
{

57
include/tool/embed_tool.h Normal file
View File

@ -0,0 +1,57 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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 EMBED_TOOL_H
#define EMBED_TOOL_H
#include <tool/tool_interactive.h>
class wxFileName;
class EMBEDDED_FILES;
class EMBED_TOOL : public TOOL_INTERACTIVE
{
public:
EMBED_TOOL();
EMBED_TOOL( const std::string& aName );
virtual ~EMBED_TOOL() = default;
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
/// @copydoc TOOL_INTERACTIVE::Reset()
void Reset( RESET_REASON aReason ) override;
int AddFile( const TOOL_EVENT& aEvent );
int RemoveFile( const TOOL_EVENT& aEvent );
std::vector<wxString> GetFileList();
protected:
///< @copydoc TOOL_INTERACTIVE::setTransitions();
void setTransitions() override;
private:
EMBEDDED_FILES* m_files;
};
#endif /* EMBED_TOOL_H */

View File

@ -0,0 +1,55 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 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_FILEDLG_OPEN_EMBED_FILE_H
#define KICAD_FILEDLG_OPEN_EMBED_FILE_H
#include <wx/wx.h>
#include <wx/filedlgcustomize.h>
class FILEDLG_OPEN_EMBED_FILE : public wxFileDialogCustomizeHook
{
public:
FILEDLG_OPEN_EMBED_FILE( bool aDefaultEmbed = true ) :
m_embed( aDefaultEmbed )
{};
virtual void AddCustomControls( wxFileDialogCustomize& customizer ) override
{
m_cb = customizer.AddCheckBox( _( "Embed File" ) );
m_cb->SetValue( m_embed );
}
virtual void TransferDataFromCustomControls() override
{
m_embed = m_cb->GetValue();
}
bool GetEmbed() const { return m_embed; }
private:
bool m_embed;
wxFileDialogCheckBox* m_cb = nullptr;
wxDECLARE_NO_COPY_CLASS( FILEDLG_OPEN_EMBED_FILE );
};
#endif //KICAD_FILEDLG_OPEN_EMBED_FILE_H

View File

@ -35,6 +35,7 @@
class wxGrid;
class WX_GRID;
class DIALOG_SHIM;
class EMBEDDED_FILES;
class GRID_CELL_TEXT_BUTTON : public wxGridCellEditor
@ -118,8 +119,9 @@ protected:
class GRID_CELL_URL_EDITOR : public GRID_CELL_TEXT_BUTTON
{
public:
GRID_CELL_URL_EDITOR( DIALOG_SHIM* aParent, SEARCH_STACK* aSearchStack = nullptr ) :
m_dlg( aParent ), m_searchStack( aSearchStack )
GRID_CELL_URL_EDITOR( DIALOG_SHIM* aParent, SEARCH_STACK* aSearchStack = nullptr,
EMBEDDED_FILES* aFiles = nullptr ) :
m_dlg( aParent ), m_searchStack( aSearchStack ), m_files( aFiles )
{ }
wxGridCellEditor* Clone() const override
@ -132,6 +134,7 @@ public:
protected:
DIALOG_SHIM* m_dlg;
SEARCH_STACK* m_searchStack;
EMBEDDED_FILES* m_files;
};

View File

@ -192,6 +192,8 @@ public:
static const wxString GerberFileExtensionsRegex;
static const std::string KiCadUriPrefix;
/**
* @}
*/

View File

@ -90,7 +90,7 @@ int PL_EDITOR_CONTROL::PageSetup( const TOOL_EVENT& aEvent )
{
m_frame->SaveCopyInUndoList();
DIALOG_PAGES_SETTINGS dlg( m_frame, drawSheetIUScale.IU_PER_MILS,
DIALOG_PAGES_SETTINGS dlg( m_frame, nullptr, drawSheetIUScale.IU_PER_MILS,
VECTOR2I( MAX_PAGE_SIZE_EESCHEMA_MILS,
MAX_PAGE_SIZE_EESCHEMA_MILS ) );
dlg.SetWksFileName( m_frame->GetCurrentFileName() );

View File

@ -38,6 +38,7 @@
#include <connectivity/connectivity_data.h>
#include <convert_shape_list_to_polygon.h>
#include <footprint.h>
#include <font/outline_font.h>
#include <lset.h>
#include <pcb_base_frame.h>
#include <pcb_track.h>
@ -82,7 +83,8 @@ BOARD::BOARD() :
m_project( nullptr ),
m_userUnits( EDA_UNITS::MILLIMETRES ),
m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
m_NetInfo( this )
m_NetInfo( this ),
m_embedFonts( false )
{
// A too small value do not allow connecting 2 shapes (i.e. segments) not exactly connected
// A too large value do not allow safely connecting 2 shapes like very short segments.
@ -957,6 +959,26 @@ void BOARD::CacheTriangulation( PROGRESS_REPORTER* aReporter, const std::vector<
}
void BOARD::FixupEmbeddedData()
{
for( FOOTPRINT* footprint : m_footprints )
{
for( auto& [filename, embeddedFile] : footprint->EmbeddedFileMap() )
{
EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFile( filename );
if( file )
{
embeddedFile->compressedEncodedData = file->compressedEncodedData;
embeddedFile->decompressedData = file->decompressedData;
embeddedFile->data_sha = file->data_sha;
embeddedFile->is_valid = file->is_valid;
}
}
}
}
void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
{
if( aBoardItem == nullptr )
@ -2498,6 +2520,56 @@ bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
}
EMBEDDED_FILES* BOARD::GetEmbeddedFiles()
{
if( IsFootprintHolder() )
return static_cast<EMBEDDED_FILES*>( GetFirstFootprint() );
return static_cast<EMBEDDED_FILES*>( this );
}
const EMBEDDED_FILES* BOARD::GetEmbeddedFiles() const
{
if( IsFootprintHolder() )
return static_cast<const EMBEDDED_FILES*>( GetFirstFootprint() );
return static_cast<const EMBEDDED_FILES*>( this );
}
void BOARD::EmbedFonts()
{
std::set<KIFONT::OUTLINE_FONT*> fonts;
for( BOARD_ITEM* item : Drawings() )
{
if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
{
KIFONT::FONT* font = text->GetFont();
if( !font || font->IsStroke() )
continue;
using EMBEDDING_PERMISSION = KIFONT::OUTLINE_FONT::EMBEDDING_PERMISSION;
auto* outline = static_cast<KIFONT::OUTLINE_FONT*>( font );
if( outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::EDITABLE
|| outline->GetEmbeddingPermission() == EMBEDDING_PERMISSION::INSTALLABLE )
{
fonts.insert( outline );
}
}
}
for( KIFONT::OUTLINE_FONT* font : fonts )
{
auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
file->type = EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT;
}
}
const std::vector<PAD*> BOARD::GetPads() const
{
std::vector<PAD*> allPads;

View File

@ -27,6 +27,7 @@
#include <board_item_container.h>
#include <board_stackup_manager/board_stackup.h>
#include <embedded_files.h>
#include <common.h> // Needed for stl hash extensions
#include <convert_shape_list_to_polygon.h> // for OUTLINE_ERROR_HANDLER
#include <hash.h>
@ -284,7 +285,7 @@ enum class BOARD_USE
/**
* Information pertinent to a Pcbnew printed circuit board.
*/
class BOARD : public BOARD_ITEM_CONTAINER
class BOARD : public BOARD_ITEM_CONTAINER, public EMBEDDED_FILES
{
public:
static inline bool ClassOf( const EDA_ITEM* aItem )
@ -426,6 +427,13 @@ public:
*/
void FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems );
/**
* After loading a file from disk, the footprints do not yet contain the full
* data for their embedded files, only a reference. This iterates over all footprints
* in the board and updates them with the full embedded data.
*/
void FixupEmbeddedData();
void CacheTriangulation( PROGRESS_REPORTER* aReporter = nullptr,
const std::vector<ZONE*>& aZones = {} );
@ -1246,6 +1254,14 @@ public:
bool LegacyTeardrops() const { return m_legacyTeardrops; }
void SetLegacyTeardrops( bool aFlag ) { m_legacyTeardrops = aFlag; }
EMBEDDED_FILES* GetEmbeddedFiles() override;
const EMBEDDED_FILES* GetEmbeddedFiles() const;
/**
* Finds all fonts used in the board and embeds them in the file if permissions allow
*/
void EmbedFonts() override;
// --------- Item order comparators ---------
struct cmp_items
@ -1359,6 +1375,8 @@ private:
NETINFO_LIST m_NetInfo; // net info list (name, design constraints...
std::vector<BOARD_LISTENER*> m_listeners;
bool m_embedFonts;
};

View File

@ -31,6 +31,7 @@
#include <dialog_import_settings.h>
#include <pcb_io/pcb_io.h>
#include <pcb_io/pcb_io_mgr.h>
#include <panel_embedded_files.h>
#include <dialogs/panel_setup_severities.h>
#include <dialogs/panel_setup_rules.h>
#include <dialogs/panel_setup_teardrops.h>
@ -203,6 +204,14 @@ DIALOG_BOARD_SETUP::DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame ) :
board->GetDesignSettings().m_DRCSeverities );
}, _( "Violation Severity" ) );
m_treebook->AddPage( new wxPanel( GetTreebook() ), _( "Board Data" ) );
m_embeddedFilesPage = m_treebook->GetPageCount();
m_treebook->AddLazySubPage(
[this]( wxWindow* aParent ) -> wxWindow*
{
return new PANEL_EMBEDDED_FILES( aParent, m_frame->GetBoard() );
}, _( "Embedded Files" ) );
for( size_t i = 0; i < m_treebook->GetPageCount(); ++i )
m_treebook->ExpandNode( i );

View File

@ -73,6 +73,7 @@ private:
size_t m_netclassesPage;
size_t m_customRulesPage;
size_t m_severitiesPage;
size_t m_embeddedFilesPage;
};

View File

@ -42,8 +42,8 @@
#include <widgets/text_ctrl_eval.h>
#include <widgets/std_bitmap_button.h>
#include <settings/settings_manager.h>
#include <panel_embedded_files.h>
#include <panel_fp_properties_3d_model.h>
#include <dialogs/3d_cache_dialogs.h>
#include <dialogs/panel_preview_3d_model.h>
#include <dialog_footprint_properties.h>
@ -75,6 +75,9 @@ DIALOG_FOOTPRINT_PROPERTIES::DIALOG_FOOTPRINT_PROPERTIES( PCB_EDIT_FRAME* aParen
m_3dPanel = new PANEL_FP_PROPERTIES_3D_MODEL( m_frame, m_footprint, this, m_NoteBook );
m_NoteBook->AddPage( m_3dPanel, _("3D Models"), false );
m_embeddedFiles = new PANEL_EMBEDDED_FILES( m_NoteBook, m_footprint );
m_NoteBook->AddPage( m_embeddedFiles, _( "Embedded Files" ) );
// Configure display origin transforms
m_posX.SetCoordType( ORIGIN_TRANSFORMS::ABS_X_COORD );
m_posY.SetCoordType( ORIGIN_TRANSFORMS::ABS_Y_COORD );
@ -261,6 +264,9 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataToWindow()
if( !m_3dPanel->TransferDataToWindow() )
return false;
if( !m_embeddedFiles->TransferDataToWindow() )
return false;
// Footprint Fields
for( PCB_FIELD* srcField : m_footprint->GetFields() )
{
@ -494,6 +500,9 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataFromWindow()
if( !m_3dPanel->TransferDataFromWindow() )
return false;
if( !m_embeddedFiles->TransferDataFromWindow() )
return false;
auto view = m_frame->GetCanvas()->GetView();
BOARD_COMMIT commit( m_frame );
commit.Modify( m_footprint );

View File

@ -36,6 +36,7 @@
class PCB_EDIT_FRAME;
class PANEL_FP_PROPERTIES_3D_MODEL;
class PANEL_EMBEDDED_FILES;
class DIALOG_FOOTPRINT_PROPERTIES: public DIALOG_FOOTPRINT_PROPERTIES_BASE
{
@ -106,6 +107,7 @@ private:
wxSize m_gridSize;
wxSize m_lastRequestedSize;
PANEL_EMBEDDED_FILES* m_embeddedFiles;
};

Some files were not shown because too many files have changed in this diff Show More