diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 37d7532f46..580fc890a2 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -67,6 +67,7 @@ set( GAL_SRCS # Cairo GAL gal/cairo/cairo_gal.cpp gal/cairo/cairo_compositor.cpp + gal/cairo/cairo_print.cpp ) set( LEGACY_GAL_SRCS diff --git a/common/gal/cairo/cairo_print.cpp b/common/gal/cairo/cairo_print.cpp new file mode 100644 index 0000000000..4ef673fdb5 --- /dev/null +++ b/common/gal/cairo/cairo_print.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2018 CERN + * Author: Maciej Suminski <maciej.suminski@cern.ch> + * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 <gal/cairo/cairo_print.h> + +#include <stdexcept> +#include <wx/dcclient.h> +#include <wx/dcgraph.h> +#include <wx/dcmemory.h> +#include <wx/dcprint.h> + +#ifdef __WXMSW__ +#include <windows.h> +#include <gdiplus.h> +#include <cairo-win32.h> +#include <wx/msw/enhmeta.h> +#endif /* __WXMSW__ */ + +#ifdef __WXMAC__ +#include <ApplicationServices/ApplicationServices.h> +#include <cairo-quartz.h> +#endif /* __WXMAC__ */ + +using namespace KIGFX; + +CAIRO_PRINT_CTX::CAIRO_PRINT_CTX( wxDC* aDC ) + : m_gcdc( nullptr ), m_ctx( nullptr ), m_surface( nullptr ) +{ + if( wxPrinterDC* printerDC = dynamic_cast<wxPrinterDC*>( aDC ) ) + m_gcdc = new wxGCDC( *printerDC ); + else if( wxMemoryDC* memoryDC = dynamic_cast<wxMemoryDC*>( aDC ) ) + m_gcdc = new wxGCDC( *memoryDC ); + else if( wxWindowDC* windowDC = dynamic_cast<wxWindowDC*>( aDC ) ) + m_gcdc = new wxGCDC( *windowDC ); +#ifdef __WXMSW__ + else if( wxEnhMetaFileDC* enhMFDC = dynamic_cast<wxEnhMetaFileDC*>( aDC ) ) + m_gcdc = new wxGCDC( *enhMFDC ); +#endif /* __WXMSW__ */ + else + throw std::runtime_error( "Unhandled wxDC type" ); + + wxGraphicsContext* gctx = m_gcdc->GetGraphicsContext(); + + if( !gctx ) + throw std::runtime_error( "Could not get the Graphics Context" ); + +#ifdef __WXGTK__ + m_ctx = static_cast<cairo_t*>( gctx->GetNativeContext() ); + m_surface = cairo_get_target( m_ctx ); + // Magic value: apparently for linux, all printers are 72 DPI + m_dpi = 72.0; +#endif /* __WXGTK__ */ + +#ifdef __WXMSW__ + Gdiplus::Graphics* g = static_cast<Gdiplus::Graphics*>( gctx->GetNativeContext() ); + m_hdc = g->GetHDC(); + m_surface = cairo_win32_printing_surface_create( static_cast<HDC>( m_hdc ) ); + m_ctx = cairo_create( m_surface ); + m_dpi = GetDeviceCaps( static_cast<HDC>( m_hdc ), LOGPIXELSX ); +#endif /* __WXMSW__ */ + +#ifdef __WXMAC__ + wxSize size = m_gcdc->GetSize(); + CGContextRef cg = (CGContextRef) gctx->GetNativeContext(); + m_surface = cairo_quartz_surface_create_for_cg_context( cg, size.x, size.y ); + m_ctx = cairo_create( m_surface ); + wxASSERT( aDC->GetPPI().x == aDC->GetPPI().y ); + m_dpi = aDC->GetPPI().x; +#endif /* __WXMAC__ */ + + if( !m_ctx || cairo_status( m_ctx ) != CAIRO_STATUS_SUCCESS ) + throw std::runtime_error( "Could not create Cairo context" ); + + if( !m_surface || cairo_surface_status( m_surface ) != CAIRO_STATUS_SUCCESS ) + throw std::runtime_error( "Could not create Cairo surface" ); + + cairo_reference( m_ctx ); + cairo_surface_reference( m_surface ); +} + + +CAIRO_PRINT_CTX::~CAIRO_PRINT_CTX() +{ +#ifdef __WXMSW__ + cairo_surface_show_page( m_surface ); + wxGraphicsContext* gctx = m_gcdc->GetGraphicsContext(); + Gdiplus::Graphics* g = static_cast<Gdiplus::Graphics*>( gctx->GetNativeContext() ); + g->ReleaseHDC( static_cast<HDC>( m_hdc ) ); +#endif /* __WXMSW__ */ + + cairo_surface_destroy( m_surface ); + cairo_destroy( m_ctx ); + delete m_gcdc; +} + + +CAIRO_PRINT_GAL::CAIRO_PRINT_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, + cairo_t* aContext, cairo_surface_t* aSurface ) + : CAIRO_GAL_BASE( aDisplayOptions ) +{ + cairo_reference( aContext ); + cairo_surface_reference( aSurface ); + context = currentContext = aContext; + surface = aSurface; + m_clearColor = COLOR4D( 1.0, 1.0, 1.0, 1.0 ); + resetContext(); +} + + +void CAIRO_PRINT_GAL::ComputeWorldScreenMatrix() +{ + // worldUnitLength = inch per integer + worldUnitLength = 1e-9 /* 1 nm */ / 0.0254 /* 1 inch in meters */; + worldScale = screenDPI * worldUnitLength * zoomFactor; + + const auto paperSizeIU = VECTOR2D( m_nativePaperSize.y, m_nativePaperSize.x ) /* inches */ * 0.0254 * 1e9 /* 1 inch in nm */; + const auto paperSizeIUTransposed = VECTOR2D( paperSizeIU.y, paperSizeIU.x ); + + MATRIX3x3D scale, translation, flip, rotate, lookat; + + scale.SetIdentity(); + translation.SetIdentity(); + flip.SetIdentity(); + rotate.SetIdentity(); + lookat.SetIdentity(); + + if( m_hasNativeLandscapeRotation ) + { + translation.SetTranslation( 0.5 / GetZoomFactor() * paperSizeIUTransposed ); + } + else + { + if( isLandscape() ) + { + translation.SetTranslation( 0.5 / GetZoomFactor() * paperSizeIU ); + rotate.SetRotation( 90.0 * M_PI / 180.0 ); + } + else + { + translation.SetTranslation( 0.5 / GetZoomFactor() * paperSizeIUTransposed ); + } + } + + scale.SetScale( VECTOR2D( worldScale, worldScale ) ); + flip.SetScale( VECTOR2D( globalFlipX ? -1.0 : 1.0, globalFlipY ? -1.0 : 1.0 ) ); + lookat.SetTranslation( -lookAtPoint ); + + worldScreenMatrix = scale * translation * flip * rotate * lookat; + screenWorldMatrix = worldScreenMatrix.Inverse(); +} + + +void CAIRO_PRINT_GAL::SetNativePaperSize( const VECTOR2D& aSize, bool aHasNativeLandscapeRotation ) +{ + m_nativePaperSize = aSize; + m_hasNativeLandscapeRotation = aHasNativeLandscapeRotation; +} + + +void CAIRO_PRINT_GAL::SetSheetSize( const VECTOR2D& aSize ) +{ + // Convert aSize (inches) to pixels + SetScreenSize( VECTOR2I( std::ceil( aSize.x * screenDPI ) * 2, + std::ceil( aSize.y * screenDPI ) * 2 ) ); +} diff --git a/include/gal/cairo/cairo_print.h b/include/gal/cairo/cairo_print.h new file mode 100644 index 0000000000..90d2b665da --- /dev/null +++ b/include/gal/cairo/cairo_print.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 CERN + * Author: Maciej Suminski <maciej.suminski@cern.ch> + * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> + * + * 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 _CAIRO_PRINT_H_ +#define _CAIRO_PRINT_H_ + +#include <gal/cairo/cairo_gal.h> + +class wxDC; +class wxGCDC; + +namespace KIGFX +{ +/** + * CAIRO_PRINT_CTX provides a Cairo context created from wxPrintDC. + * It allows one to prepare printouts using the Cairo library and let wxWidgets handle the rest. + */ +class CAIRO_PRINT_CTX +{ +public: + CAIRO_PRINT_CTX( wxDC* aDC ); + ~CAIRO_PRINT_CTX(); + + cairo_t* GetContext() const + { + return m_ctx; + } + + cairo_surface_t* GetSurface() const + { + return m_surface; + } + + double GetNativeDPI() const + { + return m_dpi; + } + + bool HasNativeLandscapeRotation() const + { +#ifdef __WXGTK__ + return false; +#else + return true; +#endif /* not __WXGTK__ */ + } + +private: + wxGCDC* m_gcdc; + cairo_t* m_ctx; + cairo_surface_t* m_surface; + +#ifdef __WXMSW__ + ///> DC handle on Windows + void* m_hdc; // the real type is HDC, but do not pull in extra headers +#endif /* __WXMSW__ */ + + double m_dpi; +}; + + +class CAIRO_PRINT_GAL : public CAIRO_GAL_BASE +{ +public: + CAIRO_PRINT_GAL( GAL_DISPLAY_OPTIONS& aDisplayOptions, + cairo_t* aContext, cairo_surface_t* aSurface ); + + void ComputeWorldScreenMatrix() override; + + /** + * @param aSize is the printing sheet size expressed in inches. + * @param aRotateIfLandscape true if the platform requires 90 degrees + * rotation in order to print in landscape format. + */ + void SetNativePaperSize( const VECTOR2D& aSize, bool aRotateIfLandscape ); + + /** + * @param aSize is the schematics sheet size expressed in inches. + */ + void SetSheetSize( const VECTOR2D& aSize ); + +private: + ///> Returns true if page orientation is landscape + bool isLandscape() const + { + return m_nativePaperSize.x > m_nativePaperSize.y; + } + + ///> Printout size + VECTOR2D m_nativePaperSize; + + ///> Flag indicating whether the platform rotates page automatically or + ///> GAL needs to handle it in the transformation matrix + bool m_hasNativeLandscapeRotation; + +}; +} // namespace KIGFX + +#endif /* _CAIRO_PRINT_H_ */