mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-13 17:53:11 +02:00
Make printing consistent with plotting.
How you ask? Plot first -> then read the PDF and print directly to the platform-specific printers. Roundabout? Yes. Better than wx printing? Darnbetcha. Also allows multi-size page printing and proper margins and print preview Fixes https://gitlab.com/kicad/code/kicad/-/issues/1831
This commit is contained in:
parent
911e1630a8
commit
23c1a68da8
@ -783,6 +783,9 @@ find_package( CURL REQUIRED )
|
||||
if( UNIX AND NOT APPLE )
|
||||
find_package( SPNAV REQUIRED )
|
||||
include_directories( SYSTEM ${SPNAV_INCLUDE_DIR} )
|
||||
|
||||
find_package( Poppler REQUIRED )
|
||||
include_directories( SYSTEM ${POPPLER_INCLUDE_DIRS} )
|
||||
endif()
|
||||
|
||||
#
|
||||
|
277
cmake/ECMFindModuleHelpers.cmake
Normal file
277
cmake/ECMFindModuleHelpers.cmake
Normal file
@ -0,0 +1,277 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
ECMFindModuleHelpers
|
||||
--------------------
|
||||
|
||||
Helper macros for find modules: ``ecm_find_package_version_check()``,
|
||||
``ecm_find_package_parse_components()`` and
|
||||
``ecm_find_package_handle_library_components()``.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_version_check(<name>)
|
||||
|
||||
Prints warnings if the CMake version or the project's required CMake version
|
||||
is older than that required by extra-cmake-modules.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_parse_components(<name>
|
||||
RESULT_VAR <variable>
|
||||
KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
[SKIP_DEPENDENCY_HANDLING])
|
||||
|
||||
This macro will populate <variable> with a list of components found in
|
||||
<name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||
list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print
|
||||
an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||
``return()``.
|
||||
|
||||
The order of components in <variable> is guaranteed to match the order they
|
||||
are listed in the ``KNOWN_COMPONENTS`` argument.
|
||||
|
||||
If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable
|
||||
<name>_<component>_component_deps will be checked for dependent components.
|
||||
If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
dependencies will also be added to <variable>.
|
||||
|
||||
::
|
||||
|
||||
ecm_find_package_handle_library_components(<name>
|
||||
COMPONENTS <component> [<component> [...]]
|
||||
[SKIP_DEPENDENCY_HANDLING])
|
||||
[SKIP_PKG_CONFIG])
|
||||
|
||||
Creates an imported library target for each component. The operation of this
|
||||
macro depends on the presence of a number of CMake variables.
|
||||
|
||||
The <name>_<component>_lib variable should contain the name of this library,
|
||||
and <name>_<component>_header variable should contain the name of a header
|
||||
file associated with it (whatever relative path is normally passed to
|
||||
'#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||
which subdirectory of the include path the headers will be found in.
|
||||
``ecm_find_package_components()`` will then search for the library
|
||||
and include directory (creating appropriate cache variables) and create an
|
||||
imported library target named <name>::<component>.
|
||||
|
||||
Additional variables can be used to provide additional information:
|
||||
|
||||
If ``SKIP_PKG_CONFIG``, the <name>_<component>_pkg_config variable is set, and
|
||||
pkg-config is found, the pkg-config module given by
|
||||
<name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
library and header file. It will also be used to set
|
||||
<name>_<component>_VERSION.
|
||||
|
||||
Note that if version information is found via pkg-config,
|
||||
<name>_<component>_FIND_VERSION can be set to require a particular version
|
||||
for each component.
|
||||
|
||||
If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property
|
||||
of the imported target for <component> will be set to contain the imported
|
||||
targets for the components listed in <name>_<component>_component_deps.
|
||||
<component>_FOUND will also be set to ``FALSE`` if any of the components in
|
||||
<name>_<component>_component_deps are not found. This requires the components
|
||||
in <name>_<component>_component_deps to be listed before <component> in the
|
||||
``COMPONENTS`` argument.
|
||||
|
||||
The following variables will be set:
|
||||
|
||||
``<name>_TARGETS``
|
||||
the imported targets
|
||||
``<name>_LIBRARIES``
|
||||
the found libraries
|
||||
``<name>_INCLUDE_DIRS``
|
||||
the combined required include directories for the components
|
||||
``<name>_DEFINITIONS``
|
||||
the "other" CFLAGS provided by pkg-config, if any
|
||||
``<name>_VERSION``
|
||||
the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
has this variable set (note that components are searched for in the order
|
||||
they are passed to the macro), although if it is already set, it will not
|
||||
be altered
|
||||
|
||||
.. note::
|
||||
These variables are never cleared, so if
|
||||
``ecm_find_package_handle_library_components()`` is called multiple times with
|
||||
different components (typically because of multiple ``find_package()`` calls) then
|
||||
``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
call (although no duplicates).
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 3.16.0)
|
||||
message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_parse_components module_name)
|
||||
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_RESULT_VAR)
|
||||
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(${module_name}_FIND_COMPONENTS)
|
||||
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||
|
||||
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||
# Make sure deps are included
|
||||
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||
endif()
|
||||
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||
|
||||
# This makes sure components are listed in the same order as
|
||||
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||
set(${ECM_FPPC_RESULT_VAR})
|
||||
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||
endif()
|
||||
endforeach()
|
||||
# if there are any left, they are unknown components
|
||||
if(ecm_fppc_requestedComps)
|
||||
set(ecm_fppc_msgType STATUS)
|
||||
if(${module_name}_FIND_REQUIRED)
|
||||
set(ecm_fppc_msgType FATAL_ERROR)
|
||||
endif()
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_handle_library_components module_name)
|
||||
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fpwc_oneValueArgs)
|
||||
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPWC_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package(PkgConfig QUIET)
|
||||
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||
set(ecm_fpwc_dep_vars)
|
||||
set(ecm_fpwc_dep_targets)
|
||||
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||
endif()
|
||||
|
||||
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||
)
|
||||
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||
if(NOT ${module_name}_VERSION)
|
||||
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||
endif()
|
||||
|
||||
set(FPHSA_NAME_MISMATCHED 1)
|
||||
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||
FOUND_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||
REQUIRED_VARS
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
${ecm_fpwc_dep_vars}
|
||||
VERSION_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||
)
|
||||
unset(FPHSA_NAME_MISMATCHED)
|
||||
|
||||
mark_as_advanced(
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||
list(APPEND ${module_name}_LIBRARIES
|
||||
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||
set(${module_name}_DEFINITIONS
|
||||
${${module_name}_DEFINITIONS}
|
||||
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${module_name}_TARGETS
|
||||
"${module_name}::${ecm_fpwc_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(${module_name}_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||
endif()
|
||||
if(${module_name}_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(${module_name}_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||
endif()
|
||||
if(${module_name}_TARGETS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||
endif()
|
||||
endmacro()
|
132
cmake/FindPoppler.cmake
Normal file
132
cmake/FindPoppler.cmake
Normal file
@ -0,0 +1,132 @@
|
||||
# SPDX-FileCopyrightText: 2015 Alex Richardson <arichardson.kde@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindPoppler
|
||||
-----------
|
||||
|
||||
Try to find Poppler.
|
||||
|
||||
This is a component-based find module, which makes use of the COMPONENTS
|
||||
and OPTIONAL_COMPONENTS arguments to find_module. The following components
|
||||
are available::
|
||||
|
||||
Core Cpp Qt5 Qt4 Glib
|
||||
|
||||
If no components are specified, this module will act as though all components
|
||||
were passed to OPTIONAL_COMPONENTS.
|
||||
|
||||
This module will define the following variables, independently of the
|
||||
components searched for or found:
|
||||
|
||||
``Poppler_FOUND``
|
||||
TRUE if (the requested version of) Poppler is available
|
||||
``Poppler_VERSION``
|
||||
Found Poppler version
|
||||
``Poppler_TARGETS``
|
||||
A list of all targets imported by this module (note that there may be more
|
||||
than the components that were requested)
|
||||
``Poppler_LIBRARIES``
|
||||
This can be passed to target_link_libraries() instead of the imported
|
||||
targets
|
||||
``Poppler_INCLUDE_DIRS``
|
||||
This should be passed to target_include_directories() if the targets are
|
||||
not used for linking
|
||||
``Poppler_DEFINITIONS``
|
||||
This should be passed to target_compile_options() if the targets are not
|
||||
used for linking
|
||||
|
||||
For each searched-for components, ``Poppler_<component>_FOUND`` will be set to
|
||||
TRUE if the corresponding Poppler library was found, and FALSE otherwise. If
|
||||
``Poppler_<component>_FOUND`` is TRUE, the imported target
|
||||
``Poppler::<component>`` will be defined. This module will also attempt to
|
||||
determine ``Poppler_*_VERSION`` variables for each imported target, although
|
||||
``Poppler_VERSION`` should normally be sufficient.
|
||||
|
||||
In general we recommend using the imported targets, as they are easier to use
|
||||
and provide more control. Bear in mind, however, that if any target is in the
|
||||
link interface of an exported library, it must be made available by the
|
||||
package config file.
|
||||
|
||||
Since 5.19
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)
|
||||
|
||||
ecm_find_package_version_check(Poppler)
|
||||
|
||||
set(Poppler_known_components
|
||||
Cpp
|
||||
Qt4
|
||||
Qt5
|
||||
Qt6
|
||||
Glib
|
||||
)
|
||||
foreach(_comp ${Poppler_known_components})
|
||||
string(TOLOWER "${_comp}" _lc_comp)
|
||||
set(Poppler_${_comp}_component_deps "Core")
|
||||
set(Poppler_${_comp}_pkg_config "poppler-${_lc_comp}")
|
||||
set(Poppler_${_comp}_lib "poppler-${_lc_comp}")
|
||||
set(Poppler_${_comp}_header_subdir "poppler/${_lc_comp}")
|
||||
endforeach()
|
||||
set(Poppler_known_components Core ${Poppler_known_components})
|
||||
|
||||
set(Poppler_Core_component_deps "")
|
||||
set(Poppler_Core_pkg_config "poppler")
|
||||
# poppler-config.h header is only installed with --enable-xpdf-headers
|
||||
# fall back to using any header from a submodule with a path to make it work in that case too
|
||||
set(Poppler_Core_header "poppler-config.h" "cpp/poppler-version.h" "qt6/poppler-qt6.h" "qt5/poppler-qt5.h" "qt4/poppler-qt4.h" "glib/poppler.h")
|
||||
set(Poppler_Core_header_subdir "poppler")
|
||||
set(Poppler_Core_lib "poppler")
|
||||
|
||||
set(Poppler_Cpp_header "poppler-version.h")
|
||||
set(Poppler_Qt6_header "poppler-qt6.h")
|
||||
set(Poppler_Qt5_header "poppler-qt5.h")
|
||||
set(Poppler_Qt4_header "poppler-qt4.h")
|
||||
set(Poppler_Glib_header "poppler.h")
|
||||
|
||||
ecm_find_package_parse_components(Poppler
|
||||
RESULT_VAR Poppler_components
|
||||
KNOWN_COMPONENTS ${Poppler_known_components}
|
||||
)
|
||||
ecm_find_package_handle_library_components(Poppler
|
||||
COMPONENTS ${Poppler_components}
|
||||
)
|
||||
|
||||
# If pkg-config didn't provide us with version information,
|
||||
# try to extract it from poppler-version.h or poppler-config.h
|
||||
if(NOT Poppler_VERSION)
|
||||
find_file(Poppler_VERSION_HEADER
|
||||
NAMES "poppler-config.h" "cpp/poppler-version.h"
|
||||
HINTS ${Poppler_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${Poppler_Core_header_subdir}
|
||||
)
|
||||
mark_as_advanced(Poppler_VERSION_HEADER)
|
||||
if(Poppler_VERSION_HEADER)
|
||||
file(READ ${Poppler_VERSION_HEADER} _poppler_version_header_contents)
|
||||
string(REGEX REPLACE
|
||||
"^.*[ \t]+POPPLER_VERSION[ \t]+\"([0-9d.]*)\".*$"
|
||||
"\\1"
|
||||
Poppler_VERSION
|
||||
"${_poppler_version_header_contents}"
|
||||
)
|
||||
unset(_poppler_version_header_contents)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(Poppler
|
||||
FOUND_VAR
|
||||
Poppler_FOUND
|
||||
REQUIRED_VARS
|
||||
Poppler_LIBRARIES
|
||||
VERSION_VAR
|
||||
Poppler_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(Poppler PROPERTIES
|
||||
DESCRIPTION "A PDF rendering library"
|
||||
URL "https://poppler.freedesktop.org/"
|
||||
)
|
@ -818,6 +818,10 @@ target_link_libraries( common
|
||||
${Fontconfig_LIBRARIES}
|
||||
)
|
||||
|
||||
if( UNIX AND NOT APPLE )
|
||||
target_link_libraries( common ${Poppler_LIBRARIES} )
|
||||
endif()
|
||||
|
||||
if( KICAD_USE_SENTRY )
|
||||
target_link_libraries( common
|
||||
sentry )
|
||||
|
@ -84,6 +84,7 @@ static const wxChar ExtraZoneDisplayModes[] = wxT( "ExtraZoneDisplayModes" );
|
||||
static const wxChar MinPlotPenWidth[] = wxT( "MinPlotPenWidth" );
|
||||
static const wxChar DebugZoneFiller[] = wxT( "DebugZoneFiller" );
|
||||
static const wxChar DebugPDFWriter[] = wxT( "DebugPDFWriter" );
|
||||
static const wxChar UsePdfPrint[] = wxT( "UsePdfPrint" );
|
||||
static const wxChar SmallDrillMarkSize[] = wxT( "SmallDrillMarkSize" );
|
||||
static const wxChar HotkeysDumper[] = wxT( "HotkeysDumper" );
|
||||
static const wxChar DrawBoundingBoxes[] = wxT( "DrawBoundingBoxes" );
|
||||
@ -245,6 +246,7 @@ ADVANCED_CFG::ADVANCED_CFG()
|
||||
|
||||
m_DebugZoneFiller = false;
|
||||
m_DebugPDFWriter = false;
|
||||
m_UsePdfPrint = false;
|
||||
m_SmallDrillMarkSize = 0.35;
|
||||
m_HotkeysDumper = false;
|
||||
m_DrawBoundingBoxes = false;
|
||||
@ -456,6 +458,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
|
||||
m_entries.push_back( std::make_unique<PARAM_CFG_BOOL>( true, AC_KEYS::DebugPDFWriter,
|
||||
&m_DebugPDFWriter, m_DebugPDFWriter ) );
|
||||
|
||||
m_entries.push_back( std::make_unique<PARAM_CFG_BOOL>( true, AC_KEYS::UsePdfPrint,
|
||||
&m_UsePdfPrint, m_UsePdfPrint ) );
|
||||
|
||||
m_entries.push_back( std::make_unique<PARAM_CFG_DOUBLE>( true, AC_KEYS::SmallDrillMarkSize,
|
||||
&m_SmallDrillMarkSize, m_SmallDrillMarkSize,
|
||||
0.0, 3.0 ) );
|
||||
|
171
common/dialogs/dialog_generate_database_connection.cpp
Normal file
171
common/dialogs/dialog_generate_database_connection.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 KiCad Developers
|
||||
*
|
||||
* 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_generate_database_connection.h>
|
||||
|
||||
#include <wx/choice.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#include <database/database_connection.h>
|
||||
#include <vector>
|
||||
|
||||
DIALOG_GENERATE_DATABASE_CONNECTION::DIALOG_GENERATE_DATABASE_CONNECTION( wxWindow* aParent ) :
|
||||
DIALOG_SHIM( aParent, wxID_ANY, _( "Generate Database Connection" ), wxDefaultPosition,
|
||||
wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
|
||||
{
|
||||
wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_dsnChoice = new wxChoice( this, wxID_ANY );
|
||||
std::vector<std::string> dsns;
|
||||
DATABASE_CONNECTION::ListDataSources( dsns );
|
||||
|
||||
for( const std::string& d : dsns )
|
||||
m_dsnChoice->Append( d );
|
||||
|
||||
m_dsnChoice->Append( _( "Custom" ) );
|
||||
|
||||
topSizer->Add( new wxStaticText( this, wxID_ANY, _( "Data Source Name" ) ), 0, wxALL, 5 );
|
||||
topSizer->Add( m_dsnChoice, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
|
||||
|
||||
wxFlexGridSizer* grid = new wxFlexGridSizer( 2, 2, 5, 5 );
|
||||
grid->AddGrowableCol( 1, 1 );
|
||||
|
||||
grid->Add( new wxStaticText( this, wxID_ANY, _( "Username" ) ), 0, wxALIGN_CENTER_VERTICAL );
|
||||
m_userCtrl = new wxTextCtrl( this, wxID_ANY );
|
||||
grid->Add( m_userCtrl, 1, wxEXPAND );
|
||||
|
||||
grid->Add( new wxStaticText( this, wxID_ANY, _( "Password" ) ), 0, wxALIGN_CENTER_VERTICAL );
|
||||
m_passCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_PASSWORD );
|
||||
grid->Add( m_passCtrl, 1, wxEXPAND );
|
||||
|
||||
grid->Add( new wxStaticText( this, wxID_ANY, _( "Timeout" ) ), 0, wxALIGN_CENTER_VERTICAL );
|
||||
m_timeoutCtrl = new wxSpinCtrl( this, wxID_ANY );
|
||||
m_timeoutCtrl->SetRange( 0, 999 );
|
||||
m_timeoutCtrl->SetValue( DATABASE_CONNECTION::DEFAULT_TIMEOUT );
|
||||
grid->Add( m_timeoutCtrl, 0, wxEXPAND );
|
||||
|
||||
grid->Add( new wxStaticText( this, wxID_ANY, _( "Connection String" ) ), 0,
|
||||
wxALIGN_CENTER_VERTICAL );
|
||||
m_connStrCtrl = new wxTextCtrl( this, wxID_ANY );
|
||||
grid->Add( m_connStrCtrl, 1, wxEXPAND );
|
||||
|
||||
topSizer->Add( grid, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
|
||||
|
||||
m_testButton = new wxButton( this, wxID_ANY, _( "Test Connection" ) );
|
||||
topSizer->Add( m_testButton, 0, wxLEFT | wxBOTTOM, 5 );
|
||||
|
||||
topSizer->Add( new wxStaticText( this, wxID_ANY, _( "Tables" ) ), 0, wxLEFT | wxRIGHT, 5 );
|
||||
m_tableChoice = new wxChoice( this, wxID_ANY );
|
||||
m_tableChoice->Enable( false );
|
||||
topSizer->Add( m_tableChoice, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
|
||||
|
||||
SetSizerAndFit( topSizer );
|
||||
|
||||
m_dsnChoice->Bind( wxEVT_CHOICE, &DIALOG_GENERATE_DATABASE_CONNECTION::OnDSNChanged, this );
|
||||
m_testButton->Bind( wxEVT_BUTTON, &DIALOG_GENERATE_DATABASE_CONNECTION::OnTest, this );
|
||||
|
||||
UpdateControls();
|
||||
SetupStandardButtons();
|
||||
}
|
||||
|
||||
DATABASE_SOURCE DIALOG_GENERATE_DATABASE_CONNECTION::GetSource() const
|
||||
{
|
||||
DATABASE_SOURCE src;
|
||||
src.type = DATABASE_SOURCE_TYPE::ODBC;
|
||||
|
||||
int sel = m_dsnChoice->GetSelection();
|
||||
if( sel != wxNOT_FOUND && sel < (int) m_dsnChoice->GetCount() - 1 )
|
||||
{
|
||||
src.dsn = m_dsnChoice->GetString( sel ).ToStdString();
|
||||
src.username = m_userCtrl->GetValue().ToStdString();
|
||||
src.password = m_passCtrl->GetValue().ToStdString();
|
||||
src.timeout = m_timeoutCtrl->GetValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
src.connection_string = m_connStrCtrl->GetValue().ToStdString();
|
||||
src.timeout = DATABASE_CONNECTION::DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
void DIALOG_GENERATE_DATABASE_CONNECTION::OnDSNChanged( wxCommandEvent& aEvent )
|
||||
{
|
||||
UpdateControls();
|
||||
}
|
||||
|
||||
void DIALOG_GENERATE_DATABASE_CONNECTION::UpdateControls()
|
||||
{
|
||||
bool custom = m_dsnChoice->GetSelection() == (int) m_dsnChoice->GetCount() - 1;
|
||||
|
||||
m_userCtrl->Enable( !custom );
|
||||
m_passCtrl->Enable( !custom );
|
||||
m_timeoutCtrl->Enable( !custom );
|
||||
m_connStrCtrl->Enable( custom );
|
||||
}
|
||||
|
||||
void DIALOG_GENERATE_DATABASE_CONNECTION::OnTest( wxCommandEvent& aEvent )
|
||||
{
|
||||
m_tableChoice->Clear();
|
||||
|
||||
std::unique_ptr<DATABASE_CONNECTION> conn;
|
||||
|
||||
if( m_dsnChoice->GetSelection() != (int) m_dsnChoice->GetCount() - 1 )
|
||||
{
|
||||
wxString dsn = m_dsnChoice->GetStringSelection();
|
||||
wxString user = m_userCtrl->GetValue();
|
||||
wxString pass = m_passCtrl->GetValue();
|
||||
int timeout = m_timeoutCtrl->GetValue();
|
||||
|
||||
conn = std::make_unique<DATABASE_CONNECTION>( dsn.ToStdString(), user.ToStdString(), pass.ToStdString(),
|
||||
timeout, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
conn = std::make_unique<DATABASE_CONNECTION>( m_connStrCtrl->GetValue().ToStdString(),
|
||||
DATABASE_CONNECTION::DEFAULT_TIMEOUT, false );
|
||||
}
|
||||
|
||||
if( !conn->Connect() )
|
||||
{
|
||||
wxMessageBox( _( "Unable to connect to database" ), _( "Database Error" ), wxOK | wxICON_ERROR,
|
||||
this );
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> tables;
|
||||
|
||||
if( conn->GetTables( tables ) )
|
||||
{
|
||||
for( const std::string& t : tables )
|
||||
m_tableChoice->Append( t );
|
||||
|
||||
if( !tables.empty() )
|
||||
m_tableChoice->SetSelection( 0 );
|
||||
|
||||
m_tableChoice->Enable( true );
|
||||
}
|
||||
}
|
@ -75,6 +75,9 @@
|
||||
#include <python_manager.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <winrt/base.h>
|
||||
#endif
|
||||
/**
|
||||
* Current list of languages supported by KiCad.
|
||||
*
|
||||
@ -177,6 +180,10 @@ void PGM_BASE::Destroy()
|
||||
APP_MONITOR::SENTRY::Instance()->Cleanup();
|
||||
|
||||
m_pgm_checker.reset();
|
||||
|
||||
#ifdef _MSC_VER
|
||||
winrt::uninit_apartment();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -441,6 +448,10 @@ bool PGM_BASE::InitPgm( bool aHeadless, bool aSkipPyInit, bool aIsUnitTest )
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
winrt::init_apartment(winrt::apartment_type::single_threaded);
|
||||
#endif
|
||||
|
||||
m_settings_manager = std::make_unique<SETTINGS_MANAGER>( aHeadless );
|
||||
m_background_jobs_monitor = std::make_unique<BACKGROUND_JOBS_MONITOR>();
|
||||
m_notifications_manager = std::make_unique<NOTIFICATIONS_MANAGER>();
|
||||
|
@ -31,12 +31,15 @@
|
||||
#include <settings/settings_manager.h>
|
||||
#include <wx/print.h>
|
||||
#include <wx/printdlg.h>
|
||||
#include <wx/filename.h>
|
||||
#include "dialog_print.h"
|
||||
|
||||
|
||||
#include <dialogs/panel_printer_list.h>
|
||||
|
||||
#include <advanced_config.h>
|
||||
#include <printing.h>
|
||||
#include <sch_plotter.h>
|
||||
|
||||
#include "sch_printout.h"
|
||||
|
||||
@ -113,6 +116,14 @@ DIALOG_PRINT::DIALOG_PRINT( SCH_EDIT_FRAME* aParent ) :
|
||||
m_sdbSizerApply->Hide();
|
||||
#endif
|
||||
|
||||
// New printing subsystem has print preview on all platforms
|
||||
#if defined( _MSC_VER )
|
||||
if( ADVANCED_CFG::GetCfg().m_UsePdfPrint )
|
||||
{
|
||||
m_sdbSizerApply->Hide();
|
||||
}
|
||||
#endif
|
||||
|
||||
m_sdbSizerOK->SetFocus();
|
||||
|
||||
Layout();
|
||||
@ -298,6 +309,51 @@ bool DIALOG_PRINT::TransferDataFromWindow()
|
||||
|
||||
SavePrintOptions();
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
if( ADVANCED_CFG::GetCfg().m_UsePdfPrint )
|
||||
{
|
||||
EESCHEMA_SETTINGS* cfg = m_parent->eeconfig();
|
||||
|
||||
SCH_RENDER_SETTINGS renderSettings( *m_parent->GetRenderSettings() );
|
||||
renderSettings.m_ShowHiddenPins = false;
|
||||
renderSettings.m_ShowHiddenFields = false;
|
||||
|
||||
COLOR_SETTINGS* cs = ::GetColorSettings( cfg->m_Printing.use_theme
|
||||
? cfg->m_Printing.color_theme
|
||||
: cfg->m_ColorTheme );
|
||||
renderSettings.LoadColors( cs );
|
||||
|
||||
SCH_PLOT_OPTS plotOpts;
|
||||
plotOpts.m_plotDrawingSheet = cfg->m_Printing.title_block;
|
||||
plotOpts.m_blackAndWhite = cfg->m_Printing.monochrome;
|
||||
plotOpts.m_useBackgroundColor = cfg->m_Printing.background;
|
||||
plotOpts.m_theme = cfg->m_Printing.use_theme ? cfg->m_Printing.color_theme
|
||||
: cfg->m_ColorTheme;
|
||||
|
||||
wxFileName tmp = wxFileName::CreateTempFileName( wxS( "eeschema_print" ) );
|
||||
wxRemoveFile( tmp.GetFullPath() );
|
||||
tmp.SetExt( wxS( "pdf" ) );
|
||||
plotOpts.m_outputFile = tmp.GetFullPath();
|
||||
|
||||
SCH_PLOTTER plotter( m_parent );
|
||||
|
||||
Pgm().m_Printing = true;
|
||||
plotter.Plot( PLOT_FORMAT::PDF, plotOpts, &renderSettings, nullptr );
|
||||
Pgm().m_Printing = false;
|
||||
|
||||
KIPLATFORM::PRINTING::PRINT_RESULT result =
|
||||
KIPLATFORM::PRINTING::PrintPDF( TO_UTF8( plotter.GetLastOutputFilePath() ) );
|
||||
|
||||
if( result != KIPLATFORM::PRINTING::PRINT_RESULT::OK &&
|
||||
result != KIPLATFORM::PRINTING::PRINT_RESULT::CANCELLED )
|
||||
{
|
||||
DisplayError( this, KIPLATFORM::PRINTING::PrintResultToString( result ) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int sheet_count = m_parent->Schematic().Root().CountSheets();
|
||||
|
||||
wxPrintData& data = m_parent->GetPageSetupData().GetPrintData();
|
||||
|
@ -322,6 +322,15 @@ public:
|
||||
*/
|
||||
bool m_DebugPDFWriter;
|
||||
|
||||
/**
|
||||
* Use legacy wxWidgets-based printing.
|
||||
*
|
||||
* Setting name: "UsePdfPrint"
|
||||
* Valid values: 0 or 1
|
||||
* Default value: 0
|
||||
*/
|
||||
bool m_UsePdfPrint;
|
||||
|
||||
/**
|
||||
* The diameter of the drill marks on print and plot outputs (in mm) when the "Drill marks"
|
||||
* option is set to "Small mark".
|
||||
|
58
include/dialogs/dialog_generate_database_connection.h
Normal file
58
include/dialogs/dialog_generate_database_connection.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 KiCad Developers
|
||||
*
|
||||
* 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_GENERATE_DATABASE_CONNECTION_H
|
||||
#define DIALOG_GENERATE_DATABASE_CONNECTION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <dialog_shim.h>
|
||||
#include <database/database_lib_settings.h>
|
||||
|
||||
class wxChoice;
|
||||
class wxTextCtrl;
|
||||
class wxSpinCtrl;
|
||||
class wxButton;
|
||||
|
||||
/**
|
||||
* Dialog for generating database connection settings.
|
||||
*/
|
||||
class DIALOG_GENERATE_DATABASE_CONNECTION : public DIALOG_SHIM
|
||||
{
|
||||
public:
|
||||
DIALOG_GENERATE_DATABASE_CONNECTION( wxWindow* aParent );
|
||||
|
||||
DATABASE_SOURCE GetSource() const;
|
||||
|
||||
private:
|
||||
void OnDSNChanged( wxCommandEvent& aEvent );
|
||||
void OnTest( wxCommandEvent& aEvent );
|
||||
void UpdateControls();
|
||||
|
||||
private:
|
||||
wxChoice* m_dsnChoice;
|
||||
wxTextCtrl* m_userCtrl;
|
||||
wxTextCtrl* m_passCtrl;
|
||||
wxSpinCtrl* m_timeoutCtrl;
|
||||
wxTextCtrl* m_connStrCtrl;
|
||||
wxButton* m_testButton;
|
||||
wxChoice* m_tableChoice;
|
||||
};
|
||||
|
||||
#endif // DIALOG_GENERATE_DATABASE_CONNECTION_H
|
@ -51,6 +51,7 @@ if( APPLE )
|
||||
os/apple/policy.mm
|
||||
os/apple/secrets.mm
|
||||
os/apple/sysinfo.cpp
|
||||
os/apple/printing.mm
|
||||
)
|
||||
|
||||
set( PLATFORM_LIBS
|
||||
@ -58,6 +59,7 @@ if( APPLE )
|
||||
"-framework AppKit"
|
||||
"-framework CoreData"
|
||||
"-framework Foundation"
|
||||
"-framework PDFKit"
|
||||
)
|
||||
elseif( WIN32 )
|
||||
list( APPEND PLATFORM_SRCS
|
||||
@ -68,6 +70,7 @@ elseif( WIN32 )
|
||||
os/windows/policy.cpp
|
||||
os/windows/secrets.cpp
|
||||
os/windows/sysinfo.cpp
|
||||
os/windows/printing.cpp
|
||||
)
|
||||
|
||||
set( PLATFORM_LIBS
|
||||
@ -75,6 +78,7 @@ elseif( WIN32 )
|
||||
"winhttp"
|
||||
"wintrust"
|
||||
"imm32"
|
||||
"windowsapp"
|
||||
)
|
||||
elseif( UNIX )
|
||||
list( APPEND PLATFORM_SRCS
|
||||
@ -85,6 +89,7 @@ elseif( UNIX )
|
||||
os/unix/policy.cpp
|
||||
os/unix/secrets.cpp
|
||||
os/unix/sysinfo.cpp
|
||||
os/unix/printing.cpp
|
||||
)
|
||||
|
||||
# Detect the secret library and configure it
|
||||
|
60
libs/kiplatform/include/printing.h
Normal file
60
libs/kiplatform/include/printing.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 KIPLATFORM_PRINTING_H_
|
||||
#define KIPLATFORM_PRINTING_H_
|
||||
|
||||
#include <string>
|
||||
#include <wx/translation.h>
|
||||
|
||||
namespace KIPLATFORM
|
||||
{
|
||||
namespace PRINTING
|
||||
{
|
||||
enum class PRINT_RESULT
|
||||
{
|
||||
OK = 0,
|
||||
CANCELLED,
|
||||
FILE_NOT_FOUND,
|
||||
FAILED_TO_LOAD,
|
||||
FAILED_TO_PRINT,
|
||||
UNSUPPORTED,
|
||||
UNKNOWN_ERROR
|
||||
};
|
||||
|
||||
inline const wxString PrintResultToString( PRINT_RESULT aResult )
|
||||
{
|
||||
switch( aResult )
|
||||
{
|
||||
case PRINT_RESULT::OK: return _( "Success" );
|
||||
case PRINT_RESULT::CANCELLED: return _( "Cancelled" );
|
||||
case PRINT_RESULT::FILE_NOT_FOUND: return _( "File not found" );
|
||||
case PRINT_RESULT::FAILED_TO_LOAD: return _( "Failed to load PDF" );
|
||||
case PRINT_RESULT::FAILED_TO_PRINT:return _( "Failed to print" );
|
||||
case PRINT_RESULT::UNSUPPORTED: return _( "Unsupported" );
|
||||
default: return _( "Unknown error" );
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_RESULT PrintPDF( const std::string& aFile );
|
||||
}
|
||||
} // namespace KIPLATFORM
|
||||
|
||||
#endif // KIPLATFORM_PRINTING_H_
|
||||
|
96
libs/kiplatform/os/apple/printing.mm
Normal file
96
libs/kiplatform/os/apple/printing.mm
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 <printing.h>
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <PDFKit/PDFKit.h>
|
||||
|
||||
namespace KIPLATFORM
|
||||
{
|
||||
namespace PRINTING
|
||||
{
|
||||
PRINT_RESULT PrintPDF( const std::string& aFile, bool fit_to_page)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSString* path = [NSString stringWithUTF8String:aFile.c_str()];
|
||||
|
||||
if( ![[NSFileManager defaultManager] isReadableFileAtPath:path] )
|
||||
return PRINT_RESULT::FILE_NOT_FOUND;
|
||||
|
||||
NSURL* url = [NSURL fileURLWithPath:path];
|
||||
PDFDocument* document = [[PDFDocument alloc] initWithURL:url];
|
||||
|
||||
if( !document || [document pageCount] == 0 )
|
||||
{
|
||||
[document release];
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
}
|
||||
|
||||
NSPrintInfo* printInfo = [NSPrintInfo sharedPrintInfo];
|
||||
|
||||
PDFPrintScalingMode scalingMode = fit_to_page ?
|
||||
kPDFPrintPageScaleDownToFit : kPDFPrintPageScaleNone;
|
||||
|
||||
NSPrintOperation* op = [document printOperationForPrintInfo:printInfo
|
||||
scalingMode:scalingMode
|
||||
autoRotate:YES];
|
||||
|
||||
if( !op )
|
||||
{
|
||||
[document release];
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
|
||||
[op setShowsPrintPanel:YES];
|
||||
[op setShowsProgressPanel:YES];
|
||||
|
||||
BOOL success = [op runOperation];
|
||||
|
||||
PRINT_RESULT result;
|
||||
|
||||
if (success)
|
||||
{
|
||||
result = PRINT_RESULT::OK;
|
||||
} else
|
||||
{
|
||||
// Check if operation was cancelled (uncertain that this works)
|
||||
NSPrintInfo* info = [op printInfo];
|
||||
NSDictionary* settings = [info printSettings];
|
||||
|
||||
if ([[settings objectForKey:NSPrintJobDisposition] isEqualToString:NSPrintCancelJob]) {
|
||||
result = PRINT_RESULT::CANCELLED;
|
||||
} else {
|
||||
result = PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
}
|
||||
|
||||
[document release];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_RESULT PrintPDF(const std::string& aFile)
|
||||
{
|
||||
return PrintPDF(aFile, true);
|
||||
}
|
||||
|
||||
} // namespace PRINTING
|
||||
} // namespace KIPLATFORM
|
231
libs/kiplatform/os/unix/printing.cpp
Normal file
231
libs/kiplatform/os/unix/printing.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 <printing.h>
|
||||
|
||||
#include <poppler/glib/poppler.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct PrintData
|
||||
{
|
||||
PopplerDocument* doc;
|
||||
bool fit_to_page;
|
||||
|
||||
PrintData( PopplerDocument* d ) :
|
||||
doc( d ),
|
||||
fit_to_page( true )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void draw_page( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr, gpointer user_data )
|
||||
{
|
||||
PrintData* print_data = static_cast<PrintData*>( user_data );
|
||||
|
||||
if( !print_data || !print_data->doc )
|
||||
return;
|
||||
|
||||
PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
|
||||
|
||||
if( !page ) return;
|
||||
|
||||
auto cleanup_page = std::unique_ptr<PopplerPage, decltype( &g_object_unref )>( page, &g_object_unref );
|
||||
|
||||
cairo_t* cr = gtk_print_context_get_cairo_context( context );
|
||||
|
||||
if( !cr ) return;
|
||||
|
||||
// Get page dimensions
|
||||
double page_width, page_height;
|
||||
poppler_page_get_size( page, &page_width, &page_height );
|
||||
|
||||
// Get print context dimensions
|
||||
double print_width = gtk_print_context_get_width( context );
|
||||
double print_height = gtk_print_context_get_height( context );
|
||||
|
||||
cairo_save( cr );
|
||||
auto cleanup_cairo = std::unique_ptr<cairo_t, decltype( &cairo_restore )>( cr, &cairo_restore );
|
||||
|
||||
if( print_data->fit_to_page )
|
||||
{
|
||||
// Calculate scaling to fit page while maintaining aspect ratio
|
||||
double scale_x = print_width / page_width;
|
||||
double scale_y = print_height / page_height;
|
||||
double scale = std::min( scale_x, scale_y );
|
||||
|
||||
// Center the page
|
||||
double scaled_width = page_width * scale;
|
||||
double scaled_height = page_height * scale;
|
||||
double offset_x = ( print_width - scaled_width ) / 2.0;
|
||||
double offset_y = ( print_height - scaled_height ) / 2.0;
|
||||
|
||||
// Apply transformations
|
||||
cairo_translate( cr, offset_x, offset_y );
|
||||
cairo_scale( cr, scale, scale );
|
||||
}
|
||||
|
||||
// Set white background
|
||||
cairo_set_source_rgb( cr, 1.0, 1.0, 1.0 );
|
||||
cairo_paint( cr );
|
||||
|
||||
// Render the page
|
||||
poppler_page_render( page, cr );
|
||||
}
|
||||
|
||||
void begin_print_callback( GtkPrintOperation* operation, GtkPrintContext* context, gpointer user_data )
|
||||
{
|
||||
PrintData* print_data = static_cast<PrintData*>( user_data );
|
||||
if( !print_data || !print_data->doc )
|
||||
{
|
||||
gtk_print_operation_cancel( operation );
|
||||
return;
|
||||
}
|
||||
|
||||
int num_pages = poppler_document_get_n_pages( print_data->doc );
|
||||
if( num_pages <= 0 )
|
||||
{
|
||||
gtk_print_operation_cancel( operation );
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_print_operation_set_n_pages( operation, num_pages );
|
||||
}
|
||||
|
||||
void request_page_setup_callback( GtkPrintOperation* operation, GtkPrintContext* context, gint page_nr,
|
||||
GtkPageSetup* setup, gpointer user_data )
|
||||
{
|
||||
PrintData* print_data = static_cast<PrintData*>( user_data );
|
||||
|
||||
if( !print_data || !print_data->doc )
|
||||
return;
|
||||
|
||||
PopplerPage* page = poppler_document_get_page( print_data->doc, page_nr );
|
||||
|
||||
if( !page )
|
||||
return;
|
||||
|
||||
// Get page dimensions to determine orientation
|
||||
double page_width, page_height;
|
||||
poppler_page_get_size( page, &page_width, &page_height );
|
||||
|
||||
// Set orientation based on page dimensions
|
||||
GtkPageOrientation orientation =
|
||||
( page_width > page_height ) ? GTK_PAGE_ORIENTATION_LANDSCAPE : GTK_PAGE_ORIENTATION_PORTRAIT;
|
||||
gtk_page_setup_set_orientation( setup, orientation );
|
||||
|
||||
g_object_unref( page );
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace KIPLATFORM
|
||||
{
|
||||
namespace PRINTING
|
||||
{
|
||||
PRINT_RESULT PrintPDF( const std::string& aFile, bool fit_to_page )
|
||||
{
|
||||
// Check file accessibility
|
||||
if( access( aFile.c_str(), R_OK ) != 0 )
|
||||
return PRINT_RESULT::FILE_NOT_FOUND;
|
||||
|
||||
// Create file URI
|
||||
gchar* uri = g_filename_to_uri( aFile.c_str(), NULL, NULL );
|
||||
if( !uri )
|
||||
return PRINT_RESULT::FILE_NOT_FOUND;
|
||||
|
||||
// Load the PDF document
|
||||
GError* error = NULL;
|
||||
PopplerDocument* doc = poppler_document_new_from_file( uri, NULL, &error );
|
||||
g_free( uri );
|
||||
|
||||
if( error )
|
||||
{
|
||||
g_error_free( error );
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
}
|
||||
|
||||
if( !doc )
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
|
||||
auto cleanup_doc = std::unique_ptr<PopplerDocument, decltype(&g_object_unref)>(doc, &g_object_unref);
|
||||
|
||||
// Check if document has pages
|
||||
int num_pages = poppler_document_get_n_pages( doc );
|
||||
|
||||
if( num_pages <= 0 )
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
|
||||
// Create print data
|
||||
PrintData print_data( doc );
|
||||
print_data.fit_to_page = fit_to_page;
|
||||
|
||||
// Create print operation
|
||||
GtkPrintOperation* op = gtk_print_operation_new();
|
||||
|
||||
if( !op )
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
auto cleanup_op = std::unique_ptr<GtkPrintOperation, decltype( &g_object_unref )>( op, &g_object_unref );
|
||||
|
||||
// Set up print operation properties
|
||||
gtk_print_operation_set_use_full_page( op, FALSE );
|
||||
gtk_print_operation_set_unit( op, GTK_UNIT_POINTS );
|
||||
|
||||
// Connect callbacks
|
||||
g_signal_connect( op, "begin-print", G_CALLBACK( begin_print_callback ), &print_data );
|
||||
g_signal_connect( op, "draw-page", G_CALLBACK( draw_page ), &print_data );
|
||||
g_signal_connect( op, "request-page-setup", G_CALLBACK( request_page_setup_callback ), &print_data );
|
||||
|
||||
// Run the print operation
|
||||
error = NULL;
|
||||
GtkPrintOperationResult result =
|
||||
gtk_print_operation_run( op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, &error );
|
||||
|
||||
// Handle errors and determine result
|
||||
PRINT_RESULT return_result;
|
||||
|
||||
if( error )
|
||||
{
|
||||
g_error_free( error );
|
||||
return_result = PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( result )
|
||||
{
|
||||
case GTK_PRINT_OPERATION_RESULT_APPLY: return_result = PRINT_RESULT::OK; break;
|
||||
case GTK_PRINT_OPERATION_RESULT_CANCEL: return_result = PRINT_RESULT::CANCELLED; break;
|
||||
case GTK_PRINT_OPERATION_RESULT_ERROR:
|
||||
default: return_result = PRINT_RESULT::FAILED_TO_PRINT; break;
|
||||
}
|
||||
}
|
||||
|
||||
return return_result;
|
||||
}
|
||||
|
||||
PRINT_RESULT PrintPDF( const std::string& aFile )
|
||||
{
|
||||
return PrintPDF( aFile, true );
|
||||
}
|
||||
|
||||
} // namespace PRINTING
|
||||
} // namespace KIPLATFORM
|
422
libs/kiplatform/os/windows/printing.cpp
Normal file
422
libs/kiplatform/os/windows/printing.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 <printing.h>
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
#include <windows.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <roapi.h>
|
||||
|
||||
#include <winrt/base.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Graphics.Printing.h>
|
||||
#include <winrt/Windows.UI.Xaml.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Media.h>
|
||||
#include <winrt/Windows.UI.Xaml.Printing.h>
|
||||
#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Data.Pdf.h>
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
|
||||
#include <winrt/base.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Graphics.Printing.h>
|
||||
#include <winrt/Windows.UI.Xaml.h>
|
||||
#include <winrt/Windows.UI.Xaml.Controls.h>
|
||||
#include <winrt/Windows.UI.Xaml.Media.Imaging.h>
|
||||
#include <winrt/Windows.UI.Xaml.Printing.h>
|
||||
#include <winrt/Windows.UI.Xaml.Hosting.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Data.Pdf.h>
|
||||
#include <winrt/Windows.Graphics.Imaging.h>
|
||||
|
||||
using namespace winrt;
|
||||
|
||||
// Manual declaration of IPrintManagerInterop to avoid missing header
|
||||
MIDL_INTERFACE("C5435A42-8D43-4E7B-A68A-EF311E392087")
|
||||
IPrintManagerInterop : public ::IInspectable
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetForWindow(
|
||||
/* [in] */ HWND appWindow,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [iid_is][retval][out] */ void **printManager) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE ShowPrintUIForWindowAsync(
|
||||
/* [in] */ HWND appWindow,
|
||||
/* [retval][out] */ void **operation) = 0;
|
||||
};
|
||||
|
||||
// Manual declaration of IDesktopWindowXamlSourceNative to avoid missing header
|
||||
MIDL_INTERFACE("3cbcf1bf-2f76-4e9c-96ab-e84b37972554")
|
||||
IDesktopWindowXamlSourceNative : public ::IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE AttachToWindow(
|
||||
/* [in] */ HWND parentWnd) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE get_WindowHandle(
|
||||
/* [retval][out] */ HWND *hWnd) = 0;
|
||||
};
|
||||
|
||||
static inline std::pair<uint32_t, uint32_t> DpToPixels( winrt::Windows::Data::Pdf::PdfPage const& page, double dpi )
|
||||
{
|
||||
const auto s = page.Size(); // DIPs (1 DIP = 1/96 inch)
|
||||
const double scale = dpi / 96.0;
|
||||
uint32_t w = static_cast<uint32_t>( std::max( 1.0, std::floor( s.Width * scale + 0.5 ) ) );
|
||||
uint32_t h = static_cast<uint32_t>( std::max( 1.0, std::floor( s.Height * scale + 0.5 ) ) );
|
||||
return { w, h };
|
||||
}
|
||||
|
||||
// Helper class to manage image with its associated stream
|
||||
struct ManagedImage
|
||||
{
|
||||
winrt::Windows::UI::Xaml::Controls::Image image;
|
||||
winrt::Windows::Storage::Streams::InMemoryRandomAccessStream stream;
|
||||
|
||||
ManagedImage() = default;
|
||||
ManagedImage(winrt::Windows::UI::Xaml::Controls::Image img, winrt::Windows::Storage::Streams::InMemoryRandomAccessStream str) : image(img), stream(str) {}
|
||||
|
||||
ManagedImage(ManagedImage&& other) noexcept
|
||||
: image(std::move(other.image)), stream(std::move(other.stream)) {}
|
||||
|
||||
ManagedImage& operator=(ManagedImage&& other) noexcept {
|
||||
if (this != &other) {
|
||||
image = std::move(other.image);
|
||||
stream = std::move(other.stream);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Render one page to a XAML Image using RenderToStreamAsync
|
||||
// dpi: e.g., 300 for preview; 600 for print
|
||||
// Returns a ManagedImage that keeps the stream alive
|
||||
static ManagedImage RenderPdfPageToImage( winrt::Windows::Data::Pdf::PdfDocument const& pdf, uint32_t pageIndex, double dpi )
|
||||
{
|
||||
auto page = pdf.GetPage( pageIndex );
|
||||
|
||||
if( !page )
|
||||
return {};
|
||||
|
||||
auto [pxW, pxH] = DpToPixels( page, dpi );
|
||||
|
||||
winrt::Windows::Data::Pdf::PdfPageRenderOptions opts;
|
||||
opts.DestinationWidth( pxW );
|
||||
opts.DestinationHeight( pxH );
|
||||
|
||||
winrt::Windows::Storage::Streams::InMemoryRandomAccessStream stream;
|
||||
|
||||
try
|
||||
{
|
||||
page.RenderToStreamAsync( stream, opts ).get(); // sync for simplicity
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Use a BitmapImage that sources directly from the stream
|
||||
winrt::Windows::UI::Xaml::Media::Imaging::BitmapImage bmp;
|
||||
|
||||
try
|
||||
{
|
||||
stream.Seek(0);
|
||||
bmp.SetSourceAsync( stream ).get();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::Image img;
|
||||
img.Source( bmp );
|
||||
img.Stretch( winrt::Windows::UI::Xaml::Media::Stretch::Uniform );
|
||||
|
||||
// Return both image and stream to keep stream alive
|
||||
return ManagedImage{ img, stream };
|
||||
}
|
||||
|
||||
|
||||
namespace KIPLATFORM {
|
||||
namespace PRINTING {
|
||||
|
||||
class WIN_PDF_PRINTER
|
||||
{
|
||||
public:
|
||||
WIN_PDF_PRINTER( HWND hwndOwner, winrt::Windows::Data::Pdf::PdfDocument const& pdf ) :
|
||||
m_hwnd( hwndOwner ),
|
||||
m_pdf( pdf )
|
||||
{
|
||||
}
|
||||
|
||||
PRINT_RESULT Run()
|
||||
{
|
||||
if( !m_pdf )
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
|
||||
// Create hidden XAML Island host
|
||||
m_xamlSource = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource();
|
||||
auto native = m_xamlSource.as<IDesktopWindowXamlSourceNative>();
|
||||
|
||||
if( !native )
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
RECT rc{ 0, 0, 100, 100 }; // Use larger minimum size
|
||||
m_host = ::CreateWindowExW( 0, L"STATIC", L"", WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
||||
rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, m_hwnd, nullptr,
|
||||
::GetModuleHandleW( nullptr ), nullptr );
|
||||
|
||||
auto cleanup_guard = std::unique_ptr<void, std::function<void( void* )>>
|
||||
( (void*) 1, [this]( void* ){ this->cleanup(); } );
|
||||
|
||||
if( !m_host )
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
if( FAILED( native->AttachToWindow( m_host ) ) )
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
m_root = winrt::Windows::UI::Xaml::Controls::Grid();
|
||||
m_xamlSource.Content( m_root );
|
||||
|
||||
m_printDoc = winrt::Windows::UI::Xaml::Printing::PrintDocument();
|
||||
m_docSrc = m_printDoc.DocumentSource();
|
||||
m_pageCount = std::max<uint32_t>( 1, m_pdf.PageCount() );
|
||||
|
||||
m_paginateToken = m_printDoc.Paginate(
|
||||
[this]( winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Printing::PaginateEventArgs const& e )
|
||||
{
|
||||
m_printDoc.SetPreviewPageCount( m_pageCount, winrt::Windows::UI::Xaml::Printing::PreviewPageCountType::Final );
|
||||
} );
|
||||
|
||||
m_getPreviewToken = m_printDoc.GetPreviewPage(
|
||||
[this]( winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Printing::GetPreviewPageEventArgs const& e )
|
||||
{
|
||||
const uint32_t index = e.PageNumber() - 1; // 1-based from system
|
||||
auto managedImg = RenderPdfPageToImage( m_pdf, index, /*dpi*/ 300.0 );
|
||||
if( managedImg.image )
|
||||
{
|
||||
// Store the managed image to keep stream alive
|
||||
m_previewImages[index] = std::move(managedImg);
|
||||
m_printDoc.SetPreviewPage( e.PageNumber(), m_previewImages[index].image );
|
||||
}
|
||||
} );
|
||||
|
||||
m_addPagesToken = m_printDoc.AddPages(
|
||||
[this]( winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::Printing::AddPagesEventArgs const& e )
|
||||
{
|
||||
for( uint32_t i = 0; i < m_pageCount; ++i )
|
||||
{
|
||||
auto managedImg = RenderPdfPageToImage( m_pdf, i, /*dpi*/ 600.0 );
|
||||
if( managedImg.image )
|
||||
{
|
||||
// Store the managed image to keep stream alive
|
||||
m_printImages[i] = std::move(managedImg);
|
||||
m_printDoc.AddPage( m_printImages[i].image );
|
||||
}
|
||||
}
|
||||
m_printDoc.AddPagesComplete();
|
||||
} );
|
||||
|
||||
try
|
||||
{
|
||||
auto factory = winrt::get_activation_factory<winrt::Windows::Graphics::Printing::PrintManager>();
|
||||
auto pmInterop = factory.as<IPrintManagerInterop>();
|
||||
|
||||
winrt::Windows::Graphics::Printing::PrintManager printManager{ nullptr };
|
||||
|
||||
if( FAILED( pmInterop->GetForWindow( m_hwnd,
|
||||
winrt::guid_of<winrt::Windows::Graphics::Printing::PrintManager>(),
|
||||
winrt::put_abi( printManager ) ) ) )
|
||||
{
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
|
||||
// Now we have the WinRT PrintManager directly
|
||||
m_rtPM = printManager;
|
||||
m_taskRequestedToken = m_rtPM.PrintTaskRequested(
|
||||
[this]( winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Graphics::Printing::PrintTaskRequestedEventArgs const& e )
|
||||
{
|
||||
auto task = e.Request().CreatePrintTask( L"KiCad PDF Print",
|
||||
[this]( winrt::Windows::Graphics::Printing::PrintTaskSourceRequestedArgs const& sourceRequestedArgs )
|
||||
{
|
||||
// Supply document source for preview
|
||||
sourceRequestedArgs.SetSource( m_docSrc );
|
||||
} );
|
||||
} );
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> asyncOp{ nullptr };
|
||||
|
||||
// Immediately wait for results to keep this in thread
|
||||
if( FAILED( pmInterop->ShowPrintUIForWindowAsync( m_hwnd, winrt::put_abi(asyncOp) ) ) )
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
bool shown = false;
|
||||
|
||||
try
|
||||
{
|
||||
shown = asyncOp.GetResults();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
|
||||
return shown ? PRINT_RESULT::OK : PRINT_RESULT::CANCELLED;
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void cleanup()
|
||||
{
|
||||
// Clear image containers first to release streams
|
||||
m_previewImages.clear();
|
||||
m_printImages.clear();
|
||||
|
||||
if( m_rtPM )
|
||||
{
|
||||
m_rtPM.PrintTaskRequested( m_taskRequestedToken );
|
||||
m_rtPM = nullptr;
|
||||
}
|
||||
|
||||
if( m_printDoc )
|
||||
{
|
||||
m_printDoc.AddPages( m_addPagesToken );
|
||||
m_printDoc.GetPreviewPage( m_getPreviewToken );
|
||||
m_printDoc.Paginate( m_paginateToken );
|
||||
}
|
||||
|
||||
m_docSrc = nullptr;
|
||||
m_printDoc = nullptr;
|
||||
m_root = nullptr;
|
||||
|
||||
if( m_host )
|
||||
{
|
||||
::DestroyWindow( m_host );
|
||||
m_host = nullptr;
|
||||
}
|
||||
|
||||
m_xamlSource = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
HWND m_hwnd{};
|
||||
winrt::Windows::Data::Pdf::PdfDocument m_pdf{ nullptr };
|
||||
|
||||
winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource m_xamlSource{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Controls::Grid m_root{ nullptr };
|
||||
winrt::Windows::UI::Xaml::Printing::PrintDocument m_printDoc{ nullptr };
|
||||
winrt::Windows::Graphics::Printing::IPrintDocumentSource m_docSrc{ nullptr };
|
||||
|
||||
uint32_t m_pageCount{ 0 };
|
||||
winrt::Windows::Graphics::Printing::PrintManager m_rtPM{ nullptr };
|
||||
winrt::event_token m_taskRequestedToken{};
|
||||
|
||||
winrt::event_token m_paginateToken{};
|
||||
winrt::event_token m_getPreviewToken{};
|
||||
winrt::event_token m_addPagesToken{};
|
||||
|
||||
HWND m_host{ nullptr };
|
||||
|
||||
// Store managed images to keep streams alive
|
||||
std::map<uint32_t, ManagedImage> m_previewImages;
|
||||
std::map<uint32_t, ManagedImage> m_printImages;
|
||||
};
|
||||
|
||||
|
||||
static std::wstring Utf8ToWide( std::string const& s )
|
||||
{
|
||||
if( s.empty() ) return {};
|
||||
|
||||
int len = MultiByteToWideChar( CP_UTF8, 0, s.data(), (int) s.size(), nullptr, 0 );
|
||||
std::wstring out( len, L'\0' );
|
||||
|
||||
MultiByteToWideChar( CP_UTF8, 0, s.data(), (int) s.size(), out.data(), len );
|
||||
return out;
|
||||
}
|
||||
|
||||
PRINT_RESULT PrintPDF(std::string const& aFile )
|
||||
{
|
||||
// Validate path
|
||||
DWORD attrs = GetFileAttributesA( aFile.c_str() );
|
||||
|
||||
if( attrs == INVALID_FILE_ATTRIBUTES )
|
||||
return PRINT_RESULT::FILE_NOT_FOUND;
|
||||
|
||||
// Load PDF via Windows.Data.Pdf
|
||||
winrt::Windows::Data::Pdf::PdfDocument pdf{ nullptr };
|
||||
|
||||
try
|
||||
{
|
||||
auto path = Utf8ToWide( aFile );
|
||||
auto file = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync( winrt::hstring( path ) ).get();
|
||||
pdf = winrt::Windows::Data::Pdf::PdfDocument::LoadFromFileAsync( file ).get();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
}
|
||||
|
||||
if( !pdf || pdf.PageCount() == 0 ) return PRINT_RESULT::FAILED_TO_LOAD;
|
||||
|
||||
HWND hwndOwner = ::GetActiveWindow();
|
||||
if( !hwndOwner ) hwndOwner = ::GetForegroundWindow();
|
||||
if( !hwndOwner ) return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
|
||||
try
|
||||
{
|
||||
WIN_PDF_PRINTER printer( hwndOwner, pdf );
|
||||
return printer.Run();
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return PRINT_RESULT::FAILED_TO_PRINT;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace PRINTING
|
||||
} // namespace KIPLATFORM
|
||||
|
||||
#else
|
||||
|
||||
namespace KIPLATFORM
|
||||
{
|
||||
namespace PRINTING
|
||||
{
|
||||
PRINT_RESULT PrintPDF( std::string const& )
|
||||
{
|
||||
return PRINT_RESULT::UNSUPPORTED;
|
||||
}
|
||||
} // namespace PRINTING
|
||||
} // namespace KIPLATFORM
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user