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

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