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:
@@ -26,6 +26,7 @@ All:
|
|||||||
All (GUI):
|
All (GUI):
|
||||||
|
|
||||||
- XRC handler for wxAuiToolBar added (Kinaou Hervé, David Hart).
|
- 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 support for sorting wxDataViewCtrl by multiple columns (Trigve).
|
||||||
- Add wxHtmlWindow::SetDefaultHTMLCursor() (Jeff A. Marr).
|
- Add wxHtmlWindow::SetDefaultHTMLCursor() (Jeff A. Marr).
|
||||||
- Add default ctor and Create() to wxContextHelpButton (Hanmac).
|
- Add default ctor and Create() to wxContextHelpButton (Hanmac).
|
||||||
|
@@ -25,10 +25,43 @@
|
|||||||
|
|
||||||
class WXDLLIMPEXP_FWD_BASE wxFileOutputStream;
|
class WXDLLIMPEXP_FWD_BASE wxFileOutputStream;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WXDLLIMPEXP_FWD_CORE wxSVGFileDC;
|
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
|
class WXDLLIMPEXP_CORE wxSVGFileDCImpl : public wxDCImpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -94,6 +127,8 @@ public:
|
|||||||
|
|
||||||
virtual void* GetHandle() const { return NULL; }
|
virtual void* GetHandle() const { return NULL; }
|
||||||
|
|
||||||
|
void SetBitmapHandler(wxSVGBitmapHandler* handler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool DoGetPixel(wxCoord, wxCoord, wxColour *) const
|
virtual bool DoGetPixel(wxCoord, wxCoord, wxColour *) const
|
||||||
{
|
{
|
||||||
@@ -197,6 +232,7 @@ private:
|
|||||||
bool m_graphics_changed; // set by Set{Brush,Pen}()
|
bool m_graphics_changed; // set by Set{Brush,Pen}()
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
double m_dpi;
|
double m_dpi;
|
||||||
|
wxSVGBitmapHandler* m_bmp_handler; // class to handle bitmaps
|
||||||
|
|
||||||
// The clipping nesting level is incremented by every call to
|
// The clipping nesting level is incremented by every call to
|
||||||
// SetClippingRegion() and reset when DestroyClippingRegion() is called.
|
// SetClippingRegion() and reset when DestroyClippingRegion() is called.
|
||||||
@@ -220,6 +256,11 @@ public:
|
|||||||
: wxDC(new wxSVGFileDCImpl(this, filename, width, height, dpi))
|
: 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
|
#endif // wxUSE_SVG
|
||||||
|
@@ -21,9 +21,12 @@
|
|||||||
is a write-only class.
|
is a write-only class.
|
||||||
|
|
||||||
As the wxSVGFileDC is a vector format, raster operations like GetPixel()
|
As the wxSVGFileDC is a vector format, raster operations like GetPixel()
|
||||||
are unlikely to be supported. However, the SVG specification allows for PNG
|
are unlikely to be supported. However, the SVG specification allows for
|
||||||
format raster files to be embedded in the SVG, and so bitmaps, icons and
|
raster files to be embedded in the SVG, and so bitmaps, icons and blit
|
||||||
blit operations in wxSVGFileDC are supported.
|
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
|
A more substantial SVG library (for reading and writing) is available at
|
||||||
the wxArt2D website <http://wxart2d.sourceforge.net/>.
|
the wxArt2D website <http://wxart2d.sourceforge.net/>.
|
||||||
@@ -31,6 +34,7 @@
|
|||||||
@library{wxcore}
|
@library{wxcore}
|
||||||
@category{dc}
|
@category{dc}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class wxSVGFileDC : public wxDC
|
class wxSVGFileDC : public wxDC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -60,6 +64,28 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Clear();
|
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
|
Does the same as wxDC::SetLogicalFunction(), except that only wxCOPY is
|
||||||
available. Trying to set one of the other values will fail.
|
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;
|
||||||
|
};
|
||||||
|
@@ -23,9 +23,11 @@
|
|||||||
#include "wx/image.h"
|
#include "wx/image.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "wx/base64.h"
|
||||||
#include "wx/dcsvg.h"
|
#include "wx/dcsvg.h"
|
||||||
#include "wx/wfstream.h"
|
#include "wx/wfstream.h"
|
||||||
#include "wx/filename.h"
|
#include "wx/filename.h"
|
||||||
|
#include "wx/mstream.h"
|
||||||
|
|
||||||
#include "wx/private/markupparser.h"
|
#include "wx/private/markupparser.h"
|
||||||
|
|
||||||
@@ -109,6 +111,105 @@ wxString wxBrushString(wxColour c, int style = wxBRUSHSTYLE_SOLID)
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // 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
|
// wxSVGFileDCImpl
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
@@ -150,6 +251,7 @@ void wxSVGFileDCImpl::Init (const wxString &filename, int Width, int Height, dou
|
|||||||
|
|
||||||
////////////////////code here
|
////////////////////code here
|
||||||
|
|
||||||
|
m_bmp_handler = NULL;
|
||||||
m_outfile = new wxFileOutputStream(filename);
|
m_outfile = new wxFileOutputStream(filename);
|
||||||
m_OK = m_outfile->IsOk();
|
m_OK = m_outfile->IsOk();
|
||||||
if (m_OK)
|
if (m_OK)
|
||||||
@@ -570,6 +672,11 @@ void wxSVGFileDCImpl::SetBackgroundMode( int mode )
|
|||||||
m_backgroundMode = mode;
|
m_backgroundMode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wxSVGFileDCImpl::SetBitmapHandler(wxSVGBitmapHandler* handler)
|
||||||
|
{
|
||||||
|
delete m_bmp_handler;
|
||||||
|
m_bmp_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
void wxSVGFileDCImpl::SetBrush(const wxBrush& brush)
|
void wxSVGFileDCImpl::SetBrush(const wxBrush& brush)
|
||||||
{
|
{
|
||||||
@@ -686,42 +793,11 @@ void wxSVGFileDCImpl::DoDrawBitmap(const class wxBitmap & bmp, wxCoord x, wxCoor
|
|||||||
{
|
{
|
||||||
NewGraphicsIfNeeded();
|
NewGraphicsIfNeeded();
|
||||||
|
|
||||||
wxString sTmp, s, sPNG;
|
// If we don't have any bitmap handler yet, use the default one.
|
||||||
if ( wxImage::FindHandler(wxBITMAP_TYPE_PNG) == NULL )
|
if ( !m_bmp_handler )
|
||||||
wxImage::AddHandler(new wxPNGHandler);
|
m_bmp_handler = new wxSVGBitmapFileHandler();
|
||||||
|
|
||||||
// create suitable file name
|
m_bmp_handler->ProcessBitmap(bmp, x, y, *m_outfile);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxSVGFileDCImpl::write(const wxString &s)
|
void wxSVGFileDCImpl::write(const wxString &s)
|
||||||
|
Reference in New Issue
Block a user