From a2a846e47330cccd4925187c94f7417c92a6ea32 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 22 Feb 2014 17:26:27 +0000 Subject: [PATCH] Allow customizing bitmap handling in wxSVGFileDC. Provide a built-in alternative for using external files for the bitmaps in SVG: allow embedding them inside the SVG itself using "data:" URI. And also allow to define custom handlers to make the behaviour even more flexible. Closes #15968. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@75981 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 1 + include/wx/dcsvg.h | 45 ++++++++++++- interface/wx/dcsvg.h | 108 +++++++++++++++++++++++++++++++- src/common/dcsvg.cpp | 146 ++++++++++++++++++++++++++++++++----------- 4 files changed, 260 insertions(+), 40 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 9393c6ce65..1120a44832 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -26,6 +26,7 @@ All: All (GUI): - XRC handler for wxAuiToolBar added (Kinaou Hervé, David Hart). +- Add support for embedding bitmaps in generated SVG in wxSVGFileDC (iwbnwif). - Add support for sorting wxDataViewCtrl by multiple columns (Trigve). - Add wxHtmlWindow::SetDefaultHTMLCursor() (Jeff A. Marr). - Add default ctor and Create() to wxContextHelpButton (Hanmac). diff --git a/include/wx/dcsvg.h b/include/wx/dcsvg.h index c6485ff131..4f79708908 100644 --- a/include/wx/dcsvg.h +++ b/include/wx/dcsvg.h @@ -25,10 +25,43 @@ class WXDLLIMPEXP_FWD_BASE wxFileOutputStream; - - class WXDLLIMPEXP_FWD_CORE wxSVGFileDC; +// Base class for bitmap handlers used by wxSVGFileDC, used by the standard +// "embed" and "link" handlers below but can also be used to create a custom +// handler. +class WXDLLIMPEXP_CORE wxSVGBitmapHandler +{ +public: + // Write the representation of the given bitmap, appearing at the specified + // position, to the provided stream. + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const = 0; + + virtual ~wxSVGBitmapHandler() {} +}; + +// Predefined standard bitmap handler: creates a file, stores the bitmap in +// this file and uses the file URI in the generated SVG. +class WXDLLIMPEXP_CORE wxSVGBitmapFileHandler : public wxSVGBitmapHandler +{ +public: + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const; +}; + +// Predefined handler which embeds the bitmap (base64-encoding it) inside the +// generated SVG file. +class WXDLLIMPEXP_CORE wxSVGBitmapEmbedHandler : public wxSVGBitmapHandler +{ +public: + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const; +}; + class WXDLLIMPEXP_CORE wxSVGFileDCImpl : public wxDCImpl { public: @@ -94,6 +127,8 @@ public: virtual void* GetHandle() const { return NULL; } + void SetBitmapHandler(wxSVGBitmapHandler* handler); + private: virtual bool DoGetPixel(wxCoord, wxCoord, wxColour *) const { @@ -197,6 +232,7 @@ private: bool m_graphics_changed; // set by Set{Brush,Pen}() int m_width, m_height; double m_dpi; + wxSVGBitmapHandler* m_bmp_handler; // class to handle bitmaps // The clipping nesting level is incremented by every call to // SetClippingRegion() and reset when DestroyClippingRegion() is called. @@ -220,6 +256,11 @@ public: : wxDC(new wxSVGFileDCImpl(this, filename, width, height, dpi)) { } + + // wxSVGFileDC-specific methods: + + // Use a custom bitmap handler: takes ownership of the handler. + void SetBitmapHandler(wxSVGBitmapHandler* handler); }; #endif // wxUSE_SVG diff --git a/interface/wx/dcsvg.h b/interface/wx/dcsvg.h index 23ad44c6b4..ffaa3d82ae 100644 --- a/interface/wx/dcsvg.h +++ b/interface/wx/dcsvg.h @@ -21,9 +21,12 @@ is a write-only class. As the wxSVGFileDC is a vector format, raster operations like GetPixel() - are unlikely to be supported. However, the SVG specification allows for PNG - format raster files to be embedded in the SVG, and so bitmaps, icons and - blit operations in wxSVGFileDC are supported. + are unlikely to be supported. However, the SVG specification allows for + raster files to be embedded in the SVG, and so bitmaps, icons and blit + operations in wxSVGFileDC are supported. By default only PNG format bitmaps + are supported and these are saved as separate files in the same folder + as the SVG file, however it is possible to change this behaviour by + replacing the built in bitmap handler using wxSVGFileDC::SetBitmapHandler(). A more substantial SVG library (for reading and writing) is available at the wxArt2D website . @@ -31,6 +34,7 @@ @library{wxcore} @category{dc} */ + class wxSVGFileDC : public wxDC { public: @@ -60,6 +64,28 @@ public: */ void Clear(); + /** + Replaces the default bitmap handler with @a handler. + + By default, an object of wxSVGBitmapFileHandler class is used as bitmap + handler. You may want to replace it with an object of predefined + wxSVGBitmapEmbedHandler class to embed the bitmaps in the generated SVG + instead of storing them in separate files like this: + @code + mySVGFileDC->SetBitmapHandler(new wxSVGBitmapEmbedHandler()); + @endcode + + or derive your own bitmap handler class and use it if you need to + customize the bitmap handling further. + + @param handler The new bitmap handler. If non-NULL, this object takes + ownership of this handler and will delete it when it is not needed + any more. + + @since 3.1.0 + */ + void SetBitmapHandler(wxSVGBitmapHandler* handler); + /** Does the same as wxDC::SetLogicalFunction(), except that only wxCOPY is available. Trying to set one of the other values will fail. @@ -117,3 +143,79 @@ public: //@} }; +/** + Abstract base class for handling bitmaps inside a wxSVGFileDC. + + To use it you need to derive a new class from it and override + ProcessBitmap() to generate a properly a formed SVG image element (see + http://www.w3.org/TR/SVG/struct.html#ImageElement). + + Two example bitmap handlers are provided in wx/dcsvg.h. The first (default) + handler will create PNG files in the same folder as the SVG file and uses + links to them in the SVG. The second handler (wxSVGBitmapEmbedHandler) will + embed the PNG image in the SVG file using base 64 encoding. + + The handler can be changed by calling wxSVGFileDC::SetBitmapHandler(). + + @library{wxcore} + @category{dc} + + @since 3.1.0 +*/ +class wxSVGBitmapHandler +{ +public: + /** + Writes the bitmap representation as SVG to the given stream. + + The XML generated by this function will be inserted into the SVG file + inline with the XML generated by the main wxSVGFileDC class so it is + important that the XML is properly formed. + + @param bitmap A valid bitmap to add to SVG. + @param x Horizontal position of the bitmap. + @param y Vertical position of the bitmap. + @param stream The stream to write SVG contents to. + */ + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const = 0; +}; + +/** + Handler embedding bitmaps as base64-encoded PNGs into the SVG. + + @see wxSVGFileDC::SetBitmapHandler(). + + @library{wxcore} + @category{dc} + + @since 3.1.0 +*/ +class wxSVGBitmapEmbedHandler : public wxSVGBitmapHandler +{ +public: + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const; +}; + +/** + Handler saving a bitmap to an external file and linking to it from the SVG. + + This handler is used by default by wxSVGFileDC. + + @see wxSVGFileDC::SetBitmapHandler(). + + @library{wxcore} + @category{dc} + + @since 3.1.0 +*/ +class wxSVGBitmapFileHandler : public wxSVGBitmapHandler +{ +public: + virtual bool ProcessBitmap(const wxBitmap& bitmap, + wxCoord x, wxCoord y, + wxOutputStream& stream) const; +}; diff --git a/src/common/dcsvg.cpp b/src/common/dcsvg.cpp index 5c55a0b935..561791435b 100644 --- a/src/common/dcsvg.cpp +++ b/src/common/dcsvg.cpp @@ -23,9 +23,11 @@ #include "wx/image.h" #endif +#include "wx/base64.h" #include "wx/dcsvg.h" #include "wx/wfstream.h" #include "wx/filename.h" +#include "wx/mstream.h" #include "wx/private/markupparser.h" @@ -109,6 +111,105 @@ wxString wxBrushString(wxColour c, int style = wxBRUSHSTYLE_SOLID) } // anonymous namespace +// ---------------------------------------------------------------------------- +// wxSVGBitmapEmbedHandler +// ---------------------------------------------------------------------------- + +bool +wxSVGBitmapEmbedHandler::ProcessBitmap(const wxBitmap& bmp, + wxCoord x, wxCoord y, + wxOutputStream& stream) const +{ + static int sub_images = 0; + + if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL ) + wxImage::AddHandler(new wxPNGHandler); + + // write the bitmap as a PNG to a memory stream and Base64 encode + wxMemoryOutputStream mem; + bmp.ConvertToImage().SaveFile(mem, wxBITMAP_TYPE_PNG); + wxString data = wxBase64Encode(mem.GetOutputStreamBuffer()->GetBufferStart(), + mem.GetSize()); + + // write image meta information + wxString s; + s += wxString::Format(" "; // last line + } + + // write to the SVG file + const wxCharBuffer buf = s.utf8_str(); + stream.Write(buf, strlen((const char *)buf)); + + return stream.IsOk(); +} + +// ---------------------------------------------------------- +// wxSVGBitmapFileHandler +// ---------------------------------------------------------- + +bool +wxSVGBitmapFileHandler::ProcessBitmap(const wxBitmap& bmp, + wxCoord x, wxCoord y, + wxOutputStream& stream) const +{ + static int sub_images = 0; + + if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL ) + wxImage::AddHandler(new wxPNGHandler); + + // find a suitable file name + wxString sPNG; + do + { + sPNG = wxString::Format("image%d.png", sub_images++); + } + while (wxFile::Exists(sPNG)); + + if ( !bmp.SaveFile(sPNG, wxBITMAP_TYPE_PNG) ) + return false; + + // reference the bitmap from the SVG doc using only filename & ext + sPNG = sPNG.AfterLast(wxFileName::GetPathSeparator()); + + // reference the bitmap from the SVG doc + wxString s; + s += wxString::Format(" \n\n", sPNG); + + // write to the SVG file + const wxCharBuffer buf = s.utf8_str(); + stream.Write(buf, strlen((const char *)buf)); + + return stream.IsOk(); +} + +// ---------------------------------------------------------- +// wxSVGFileDC (specialisations) +// ---------------------------------------------------------- + +void wxSVGFileDC::SetBitmapHandler(wxSVGBitmapHandler* handler) +{ + ((wxSVGFileDCImpl*)GetImpl())->SetBitmapHandler(handler); +} + // ---------------------------------------------------------- // wxSVGFileDCImpl // ---------------------------------------------------------- @@ -150,6 +251,7 @@ void wxSVGFileDCImpl::Init (const wxString &filename, int Width, int Height, dou ////////////////////code here + m_bmp_handler = NULL; m_outfile = new wxFileOutputStream(filename); m_OK = m_outfile->IsOk(); if (m_OK) @@ -570,6 +672,11 @@ void wxSVGFileDCImpl::SetBackgroundMode( int mode ) m_backgroundMode = mode; } +void wxSVGFileDCImpl::SetBitmapHandler(wxSVGBitmapHandler* handler) +{ + delete m_bmp_handler; + m_bmp_handler = handler; +} void wxSVGFileDCImpl::SetBrush(const wxBrush& brush) { @@ -686,42 +793,11 @@ void wxSVGFileDCImpl::DoDrawBitmap(const class wxBitmap & bmp, wxCoord x, wxCoor { NewGraphicsIfNeeded(); - wxString sTmp, s, sPNG; - if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL ) - wxImage::AddHandler(new wxPNGHandler); + // If we don't have any bitmap handler yet, use the default one. + if ( !m_bmp_handler ) + m_bmp_handler = new wxSVGBitmapFileHandler(); -// create suitable file name - sTmp.Printf ( wxT("_image%d.png"), m_sub_images); - sPNG = m_filename.BeforeLast(wxT('.')) + sTmp; - while (wxFile::Exists(sPNG) ) - { - m_sub_images ++; - sTmp.Printf ( wxT("_image%d.png"), m_sub_images); - sPNG = m_filename.BeforeLast(wxT('.')) + sTmp; - } - -//create copy of bitmap (wxGTK doesn't like saving a constant bitmap) - wxBitmap myBitmap = bmp; -//save it - bool bPNG_OK = myBitmap.SaveFile(sPNG,wxBITMAP_TYPE_PNG); - -// reference the bitmap from the SVG doc -// only use filename & ext - sPNG = sPNG.AfterLast(wxFileName::GetPathSeparator()); - -// reference the bitmap from the SVG doc - int w = myBitmap.GetWidth(); - int h = myBitmap.GetHeight(); - sTmp.Printf ( wxT(" \n"), sPNG.c_str() ); - s += sTmp + wxT("Image from wxSVG ") + wxT("\n"); - - if (m_OK && bPNG_OK) - { - write(s); - } - m_OK = m_outfile->IsOk() && bPNG_OK; + m_bmp_handler->ProcessBitmap(bmp, x, y, *m_outfile); } void wxSVGFileDCImpl::write(const wxString &s)