1 //////////////////////////////////////////////////////////////////////////////
2 // Name:        SVGDocument.cpp
3 // Purpose:     wxSVGDocument - SVG render & data holder class
4 // Author:      Alex Thuering
5 // Created:     2005/01/17
6 // RCS-ID:      $Id: SVGDocument.cpp,v 1.52 2016/05/16 21:08:52 ntalex Exp $
7 // Copyright:   (c) 2005 Alex Thuering
8 // Licence:     wxWindows licence
9 //////////////////////////////////////////////////////////////////////////////
10 
11 #include "SVGDocument.h"
12 
13 #ifdef HAVE_LIBSKIA
14 #include "skia/SVGCanvasSkia.h"
15 #define WX_SVG_CANVAS wxSVGCanvasSkia
16 #else //USE_RENDER_CAIRO
17 #include "cairo/SVGCanvasCairo.h"
18 #define WX_SVG_CANVAS wxSVGCanvasCairo
19 #endif
20 
21 #include <wx/log.h>
22 
IMPLEMENT_ABSTRACT_CLASS(wxSVGDocument,wxSvgXmlDocument)23 IMPLEMENT_ABSTRACT_CLASS(wxSVGDocument, wxSvgXmlDocument)
24 
25 wxSVGDocument::wxSVGDocument(const wxSVGDocument& doc): wxSvgXmlDocument(doc) {
26 	Init();
27 }
28 
~wxSVGDocument()29 wxSVGDocument::~wxSVGDocument() {
30 	delete m_canvas;
31 }
32 
Load(const wxString & filename,const wxString & encoding)33 bool wxSVGDocument::Load(const wxString& filename, const wxString& encoding) {
34 	bool result = wxSvgXmlDocument::Load(filename, encoding);
35 	if (result) {
36 		m_path =  wxPathOnly(filename);
37 	}
38 	SetCurrentTime(0);
39 	return result;
40 }
41 
Load(wxInputStream & stream,const wxString & encoding)42 bool wxSVGDocument::Load(wxInputStream& stream, const wxString& encoding) {
43 	return wxSvgXmlDocument::Load(stream, encoding);
44 }
45 
Init()46 void wxSVGDocument::Init() {
47 	m_canvas = new WX_SVG_CANVAS;
48 	m_scale = 1;
49 	m_scaleY = -1; // == m_scale
50 	m_time = 0;
51 }
52 
GetTitle()53 wxString wxSVGDocument::GetTitle() {
54 	wxSVGElement* elem = (wxSVGElement*) GetRootElement()->GetChildren();
55 	while (elem) {
56 		if (elem->GetType() == wxSVGXML_ELEMENT_NODE && elem->GetDtd() == wxSVG_TITLE_ELEMENT) {
57 			if (elem->GetChildren() && elem->GetFirstChild()->GetType() == wxSVGXML_TEXT_NODE) {
58 				return elem->GetFirstChild()->GetContent();
59 			}
60 		}
61 		elem = (wxSVGElement*) elem->GetNext();
62 	}
63 	return wxT("");
64 }
65 
SetTitle(const wxString & title)66 void wxSVGDocument::SetTitle(const wxString& title) {
67 	wxSVGTitleElement* titleElem = NULL;
68 	wxSVGElement* elem = (wxSVGElement*) GetRootElement()->GetChildren();
69 	while (elem) {
70 		if (elem->GetType() == wxSVGXML_ELEMENT_NODE && elem->GetDtd() == wxSVG_TITLE_ELEMENT) {
71 			titleElem = (wxSVGTitleElement*) elem;
72 		}
73 		elem = (wxSVGElement*) elem->GetNext();
74 	}
75 	if (titleElem == NULL) {
76 		titleElem = new wxSVGTitleElement;
77 		GetRootElement()->AppendChild(titleElem);
78 	}
79 	if (titleElem->GetChildren() && elem->GetFirstChild()->GetType() == wxSVGXML_TEXT_NODE)
80 		elem->GetFirstChild()->SetContent(title);
81 	else
82 		elem->AddChild(new wxSvgXmlNode(wxSVGXML_TEXT_NODE, wxEmptyString, title));
83 }
84 
GetElementById(const wxString & id)85 wxSVGElement* wxSVGDocument::GetElementById(const wxString& id) {
86 	return GetRootElement() ? (wxSVGElement*) GetRootElement()->GetElementById(id) : NULL;
87 }
88 
CreateElement(const wxString & tagName)89 wxSvgXmlElement* wxSVGDocument::CreateElement(const wxString& tagName) {
90 	return CreateElementNS(wxT(""), tagName);
91 }
92 
93 #include "SVGDocument_CreateElement.cpp"
94 
GetDuration(wxSVGElement * parent)95 double wxSVGDocument::GetDuration(wxSVGElement* parent) {
96 	double result = 0;
97 	wxSVGElement* elem = (wxSVGElement*) parent->GetChildren();
98 	while (elem) {
99 		if (elem->GetType() == wxSVGXML_ELEMENT_NODE) {
100 			double duration = 0;
101 			switch (elem->GetDtd()) {
102 				case wxSVG_ANIMATE_ELEMENT:
103 				case wxSVG_ANIMATECOLOR_ELEMENT:
104 				case wxSVG_ANIMATEMOTION_ELEMENT:
105 				case wxSVG_ANIMATETRANSFORM_ELEMENT:
106 					duration = ((wxSVGAnimateElement*) elem)->GetBegin() + ((wxSVGAnimateElement*) elem)->GetDur();
107 					break;
108 				case wxSVG_VIDEO_ELEMENT:
109 					duration = ((wxSVGVideoElement*) elem)->GetBegin() + ((wxSVGVideoElement*) elem)->GetDuration();
110 					break;
111 				case wxSVG_IMAGE_ELEMENT: {
112 					wxSVGCanvasImage* canvasItem = (wxSVGCanvasImage*) ((wxSVGImageElement*) elem)->GetCanvasItem();
113 					if (canvasItem != NULL && canvasItem->GetSvgImage() != NULL) {
114 						duration = GetDuration(canvasItem->GetSvgImage());
115 						if (result < duration) {
116 							result = duration;
117 						}
118 					}
119 					break;
120 				}
121 				default:
122 					break;
123 			}
124 			if (result < duration) {
125 				result = duration;
126 			}
127 			if (elem->GetChildren()) {
128 				duration = GetDuration(elem);
129 				if (result < duration) {
130 					result = duration;
131 				}
132 			}
133 		}
134 		elem = (wxSVGElement*) elem->GetNext();
135 	}
136 	return result;
137 }
138 
GetDuration()139 double wxSVGDocument::GetDuration() {
140 	return GetDuration(GetRootElement());
141 }
142 
ApplyAnimation(wxSVGElement * parent,wxSVGSVGElement * ownerSVGElement)143 void wxSVGDocument::ApplyAnimation(wxSVGElement* parent, wxSVGSVGElement* ownerSVGElement) {
144 	wxSVGElement* elem = (wxSVGElement*) parent->GetChildren();
145 	while (elem) {
146 		if (elem->GetType() == wxSVGXML_ELEMENT_NODE) {
147 			if (elem->GetDtd() == wxSVG_IMAGE_ELEMENT) {
148 				wxSVGCanvasImage* canvasItem = (wxSVGCanvasImage*) ((wxSVGImageElement*) elem)->GetCanvasItem();
149 				if (canvasItem != NULL && canvasItem->GetSvgImage() != NULL) {
150 					wxSVGSVGElement* svgImage = canvasItem->GetSvgImage((wxSVGDocument*) elem->GetOwnerDocument());
151 					ApplyAnimation(svgImage, svgImage);
152 				}
153 			}
154 			switch (elem->GetDtd()) {
155 				case wxSVG_ANIMATE_ELEMENT:
156 					((wxSVGAnimateElement*) elem)->SetOwnerSVGElement(ownerSVGElement);
157 					((wxSVGAnimateElement*) elem)->ApplyAnimation();
158 					break;
159 				case wxSVG_ANIMATECOLOR_ELEMENT:
160 					((wxSVGAnimateMotionElement*) elem)->SetOwnerSVGElement(ownerSVGElement);
161 					((wxSVGAnimateMotionElement*) elem)->ApplyAnimation();
162 					break;
163 				case wxSVG_ANIMATEMOTION_ELEMENT:
164 					((wxSVGAnimateMotionElement*) elem)->SetOwnerSVGElement(ownerSVGElement);
165 					((wxSVGAnimateMotionElement*) elem)->ApplyAnimation();
166 					break;
167 				case wxSVG_ANIMATETRANSFORM_ELEMENT:
168 					((wxSVGAnimateTransformElement*) elem)->SetOwnerSVGElement(ownerSVGElement);
169 					((wxSVGAnimateTransformElement*) elem)->ApplyAnimation();
170 					break;
171 				case wxSVG_SVG_ELEMENT:
172 					ApplyAnimation(elem, (wxSVGSVGElement*) elem);
173 					break;
174 				default:
175 					ApplyAnimation(elem, ownerSVGElement);
176 					break;
177 			}
178 		}
179 		elem = (wxSVGElement*) elem->GetNext();
180 	}
181 }
182 
SetCurrentTime(double seconds)183 void wxSVGDocument::SetCurrentTime(double seconds) {
184 	m_time = seconds;
185 	// animation
186 	if (GetRootElement())
187 		ApplyAnimation(GetRootElement(), GetRootElement());
188 }
189 
190 /** Renders SVG to bitmap image */
Render(int width,int height,const wxSVGRect * rect,bool preserveAspectRatio,bool alpha,wxProgressDialog * progressDlg)191 wxImage wxSVGDocument::Render(int width, int height, const wxSVGRect* rect, bool preserveAspectRatio, bool alpha,
192 		wxProgressDialog* progressDlg) {
193 	if (!GetRootElement())
194 		return wxImage();
195 
196 	m_screenCTM = wxSVGMatrix();
197 
198 	if (GetRootElement()->GetWidth().GetBaseVal().GetUnitType() == wxSVG_LENGTHTYPE_UNKNOWN)
199 		GetRootElement()->SetWidth(wxSVGLength(wxSVG_LENGTHTYPE_PERCENTAGE, 100));
200 	if (GetRootElement()->GetHeight().GetBaseVal().GetUnitType() == wxSVG_LENGTHTYPE_UNKNOWN)
201 		GetRootElement()->SetHeight(wxSVGLength(wxSVG_LENGTHTYPE_PERCENTAGE, 100));
202 
203 	if (width == -1 || height == -1) {
204 		width = (int) GetRootElement()->GetWidth().GetAnimVal();
205 		height = (int) GetRootElement()->GetHeight().GetAnimVal();
206 		if (width <= 0 || height <= 0) {
207 			width = (int) GetRootElement()->GetViewBox().GetAnimVal().GetWidth();
208 			height = (int) GetRootElement()->GetViewBox().GetAnimVal().GetHeight();
209 		}
210 	}
211 
212 	if (GetRootElement()->GetWidth().GetAnimVal().GetUnitType() == wxSVG_LENGTHTYPE_PERCENTAGE) {
213 		wxSVGAnimatedLength l = GetRootElement()->GetWidth();
214 		l.GetBaseVal().ToViewportWidth(width);
215 		if (l.GetBaseVal() != ((const wxSVGAnimatedLength&) l).GetAnimVal())
216 			l.GetAnimVal().ToViewportWidth(width);
217 		GetRootElement()->SetWidth(l);
218 	}
219 	if (GetRootElement()->GetHeight().GetAnimVal().GetUnitType() == wxSVG_LENGTHTYPE_PERCENTAGE) {
220 		wxSVGAnimatedLength l = GetRootElement()->GetHeight();
221 		l.GetBaseVal().ToViewportHeight(height);
222 		if (l.GetBaseVal() != ((const wxSVGAnimatedLength&) l).GetAnimVal())
223 			l.GetAnimVal().ToViewportHeight(height);
224 		GetRootElement()->SetHeight(l);
225 	}
226 
227 	// scale it to fit in
228 	m_scale = 1;
229 	m_scaleY = -1; // == m_scale
230 	if (GetRootElement()->GetWidth().GetAnimVal() > 0 && GetRootElement()->GetHeight().GetAnimVal() > 0) {
231 		if (preserveAspectRatio) {
232 			m_scale = width / GetRootElement()->GetWidth().GetAnimVal();
233 			if (m_scale > height / GetRootElement()->GetHeight().GetAnimVal())
234 				m_scale = height / GetRootElement()->GetHeight().GetAnimVal();
235 			m_screenCTM = m_screenCTM.Scale(m_scale);
236 
237 			width = (int) (m_scale * GetRootElement()->GetWidth().GetAnimVal());
238 			height = (int) (m_scale * GetRootElement()->GetHeight().GetAnimVal());
239 		} else {
240 			m_scale = width / GetRootElement()->GetWidth().GetAnimVal();
241 			m_scaleY = height / GetRootElement()->GetHeight().GetAnimVal();
242 			m_screenCTM = m_screenCTM.ScaleNonUniform(m_scale, m_scaleY);
243 		}
244 	}
245 
246 	// render only rect if specified
247 	if (rect && !rect->IsEmpty()) {
248 		m_screenCTM = m_screenCTM.Translate(-rect->GetX(), -rect->GetY());
249 		if (rect->GetWidth() * GetScaleX() < width)
250 			width = (int) (rect->GetWidth() * GetScaleX());
251 		if (rect->GetHeight() * GetScaleY() < height)
252 			height = (int) (rect->GetHeight() * GetScaleY());
253 	}
254 
255 	// render
256 	m_canvas->Init(width, height, alpha);
257 	if (!alpha)
258 		m_canvas->Clear(*wxWHITE);
259 	GetCanvas()->RenderElement(GetRootElement(), rect, &m_screenCTM, &GetRootElement()->GetStyle(), NULL, NULL,
260 			progressDlg);
261 
262 	return m_canvas->GetImage();
263 }
264