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
This commit is contained in:
Vadim Zeitlin
2014-02-22 17:26:27 +00:00
parent c59b815f2c
commit a2a846e473
4 changed files with 260 additions and 40 deletions

View File

@@ -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).

View File

@@ -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

View File

@@ -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 <http://wxart2d.sourceforge.net/>.
@@ -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;
};

View File

@@ -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(" <image x=\"%d\" y=\"%d\" "
"width=\"%dpx\" height=\"%dpx\" "
"title=\"Image from wxSVG\"\n",
x, y, bmp.GetWidth(), bmp.GetHeight());
s += wxString::Format(" id=\"image%d\" "
"xlink:href=\"data:image/png;base64,\n",
sub_images++);
// Wrap Base64 encoded data on 76 columns boundary (same as Inkscape).
const unsigned WRAP = 76;
for ( size_t i = 0; i < data.size(); i += WRAP )
{
if (i < data.size() - WRAP)
s += data.Mid(i, WRAP) + "\n";
else
s += data.Mid(i, s.size() - i) + "\"\n/>"; // 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(" <image x=\"%d\" y=\"%d\" "
"width=\"%dpx\" height=\"%dpx\" "
"title=\"Image from wxSVG\"\n",
x, y, bmp.GetWidth(), bmp.GetHeight());
s += wxString::Format(" xlink:href=\"%s\">\n</image>\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(" <image x=\"%d\" y=\"%d\" width=\"%dpx\" height=\"%dpx\" "), x,y,w,h );
s += sTmp;
sTmp.Printf ( wxT(" xlink:href=\"%s\"> \n"), sPNG.c_str() );
s += sTmp + wxT("<title>Image from wxSVG</title> </image>") + 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)