1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        TextSub.cpp
3 // Purpose:     The class to store a DVD TextSub parameters
4 // Author:      Alex Thuering
5 // Created:	    09.04.2011
6 // RCS-ID:      $Id: TextSub.cpp,v 1.7 2016/04/19 23:09:52 ntalex Exp $
7 // Copyright:   (c) Alex Thuering
8 // Licence:     GPL
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "TextSub.h"
12 #include "Utils.h"
13 #include "Config.h"
14 #include <wxVillaLib/utils.h>
15 #include <wxSVGXML/svgxmlhelpr.h>
16 #include <wx/sstream.h>
17 
18 #include <fontconfig/fontconfig.h>
19 #define FONTS_CONF wxFindDataFile(wxT("fonts.conf"))
20 
21 FontMap TextSub::s_fonts;
22 
TextSub(wxString filename)23 TextSub::TextSub(wxString filename) {
24 	m_filename = filename;
25 	m_force = false;
26 	m_characterSet = s_config.GetSubtitlesCharacterSet();
27 	m_fontFamily = s_config.GetSubtitlesFontFamily();
28 	m_fontStyle = s_config.GetSubtitlesFontStyle();
29 	m_fontSize = s_config.GetSubtitlesFontSize();
30 	m_fillColour = s_config.GetSubtitlesFillColour();
31 	m_outlineColour = s_config.GetSubtitlesOutlineColour();
32     m_outlineThickness = s_config.GetSubtitlesOutlineThickness();
33     m_shadowColour = s_config.GetSubtitlesShadowColour();
34     m_shadowOffset = wxPoint(s_config.GetSubtitlesShadowOffsetX(), s_config.GetSubtitlesShadowOffsetY());
35 	m_alignment = (wxAlignment) s_config.GetSubtitlesAlignment();
36 	m_leftMargin = s_config.GetSubtitlesLeftMargin();
37 	m_rightMargin = s_config.GetSubtitlesRightMargin();
38 	m_topMargin = s_config.GetSubtitlesTopMargin();
39 	m_bottomMargin = s_config.GetSubtitlesBottomMargin();
40 	m_subtitleFps = 25;
41 	m_movieFps = 25;
42 	m_movieSize = wxSize(720, 574);
43 	m_aspectRatio = arAUTO;
44 }
45 
GetFontFile()46 wxString TextSub::GetFontFile() {
47 	map<wxString, wxString>& fonts = GetFontMap()[m_fontFamily];
48 	if (fonts.find(m_fontStyle) != fonts.end())
49 		return fonts[m_fontStyle];
50 	if (fonts.find(wxT("Normal")) != fonts.end())
51 		return fonts[wxT("Normal")];
52 	if (fonts.find(wxT("Standard")) != fonts.end())
53 		return fonts[wxT("Standard")];
54 	return fonts.begin() != fonts.end() ? fonts.begin()->second : wxT("");
55 }
56 
ToString(const wxColour & colour,DVDFileType type)57 wxString ToString(const wxColour& colour, DVDFileType type) {
58 	if (!colour.IsOk())
59 		return type == DVDAUTHOR_XML ? wxT("rgba(0,0,0,0)") : wxT("#00000000");
60 	return wxString::Format(type == DVDAUTHOR_XML ? wxT("rgba(%d,%d,%d,%d)") : wxT("#%02x%02x%02x%02x"),
61 			colour.Red(), colour.Green(), colour.Blue(), colour.Alpha());
62 }
63 
GetXML(DVDFileType type)64 wxSvgXmlNode* TextSub::GetXML(DVDFileType type) {
65 	wxSvgXmlNode* textsubNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, _T("textsub"));
66 	TextSub* defaults = new TextSub(wxT(""));
67 	textsubNode->AddProperty(_T("filename"), m_filename);
68 	if (type == DVDAUTHOR_XML || m_characterSet != defaults->m_characterSet)
69 		textsubNode->AddProperty(_T("characterset"), m_characterSet);
70 	if (m_force)
71 		textsubNode->AddProperty(_T("force"), _T("yes"));
72 
73 	// font
74 	if (type == DVDAUTHOR_XML) {
75 		if (GetFontFile().length() > 0)
76 			textsubNode->AddProperty(wxT("font"), GetFontFile());
77 	} else {
78 		if (m_fontFamily != defaults->m_fontFamily)
79 			textsubNode->AddProperty(wxT("fontFamily"), m_fontFamily);
80 		if (m_fontStyle != defaults->m_fontStyle)
81 			textsubNode->AddProperty(wxT("fontStyle"), m_fontStyle);
82 	}
83 	if (m_fontSize != defaults->m_fontSize)
84 		textsubNode->AddProperty(_T("fontsize"), wxString::Format(wxT("%g"), m_fontSize));
85 
86 	// colours
87 	textsubNode->AddProperty(wxT("fill-color"), ToString(m_fillColour, type));
88 	textsubNode->AddProperty(wxT("outline-color"), ToString(m_outlineColour, type));
89 	textsubNode->AddProperty(wxT("outline-thickness"), wxString::Format(wxT("%f"), m_outlineThickness));
90 	textsubNode->AddProperty(wxT("shadow-color"), ToString(m_shadowColour, type));
91 	if (m_shadowOffset != defaults->m_shadowOffset)
92 		textsubNode->AddProperty(wxT("shadow-offset"),
93 				wxString::Format(wxT("%d,%d"), m_shadowOffset.x, m_shadowOffset.y));
94 
95 	// alignment
96 	wxString hAlignment = m_alignment & wxALIGN_CENTER_HORIZONTAL ? wxT("center")
97 			: m_alignment & wxALIGN_RIGHT ? wxT("right") : wxT("left");
98 	if ((type == DVDAUTHOR_XML && hAlignment != wxT("left"))
99 			|| (type == DVDSTYLER_XML && hAlignment != wxT("center")))
100 		textsubNode->AddProperty(wxT("horizontal-alignment"), hAlignment);
101 	wxString vAlignment = m_alignment & wxALIGN_CENTER_VERTICAL ? wxT("center")
102 				: m_alignment & wxALIGN_BOTTOM ? wxT("bottom") : wxT("top");
103 	if (vAlignment != wxT("bottom"))
104 		textsubNode->AddProperty(wxT("vertical-alignment"), vAlignment);
105 
106 	// margin
107 	if (m_leftMargin != defaults->m_leftMargin)
108 		textsubNode->AddProperty(wxT("left-margin"), wxString::Format(wxT("%d"), m_leftMargin));
109 	if (m_rightMargin != defaults->m_rightMargin)
110 		textsubNode->AddProperty(wxT("right-margin"), wxString::Format(wxT("%d"), m_rightMargin));
111 	if (m_topMargin != defaults->m_topMargin)
112 		textsubNode->AddProperty(wxT("top-margin"), wxString::Format(wxT("%d"), m_topMargin));
113 	if (m_bottomMargin != defaults->m_bottomMargin)
114 		textsubNode->AddProperty(wxT("bottom-margin"), wxString::Format(wxT("%d"), m_bottomMargin));
115 
116 	// fps
117 	if (m_subtitleFps != defaults->m_subtitleFps)
118 		textsubNode->AddProperty(wxT("subtitle-fps"), wxString::Format(wxT("%g"), m_subtitleFps));
119 
120 	// movie values
121 	if (type == DVDAUTHOR_XML) {
122 		if (m_movieFps != defaults->m_movieFps)
123 			textsubNode->AddProperty(wxT("movie-fps"), wxString::Format(wxT("%g"), m_movieFps));
124 		if (m_movieSize.x != defaults->m_movieSize.x)
125 			textsubNode->AddProperty(wxT("movie-width"), wxString::Format(wxT("%d"), m_movieSize.x));
126 		if (m_movieSize.y != defaults->m_movieSize.y)
127 			textsubNode->AddProperty(wxT("movie-height"), wxString::Format(wxT("%d"), m_movieSize.y));
128 		if (m_aspectRatio != arAUTO)
129 			textsubNode->AddProperty(wxT("aspect"), m_aspectRatio == ar4_3 ? wxT("4:3") : wxT("16:9"));
130 	}
131 	delete defaults;
132 	return textsubNode;
133 }
134 
AsString()135 wxString TextSub::AsString() {
136 	wxStringOutputStream stream;
137 	wxSvgXmlDocument doc;
138 	doc.SetRoot(GetXML(DVDSTYLER_XML));
139 	doc.Save(stream);
140 	return stream.GetString();
141 }
142 
PutXML(wxSvgXmlNode * node)143 bool TextSub::PutXML(wxSvgXmlNode* node) {
144 	wxString val;
145 	long lval;
146 	long lval2;
147 	double dval;
148 
149 	if (!node->GetPropVal(wxT("filename"), &m_filename))
150 		return false;
151 	node->GetPropVal(wxT("characterset"), &m_characterSet);
152 
153 	m_force = node->GetPropVal(wxT("force"), &val) && val == wxT("yes");
154 
155 	// font
156 	node->GetPropVal(wxT("fontFamily"), &m_fontFamily);
157 	node->GetPropVal(wxT("fontStyle"), &m_fontStyle);
158 	if (node->GetPropVal(wxT("fontsize"), &val) && val.ToDouble(&dval))
159 		m_fontSize = dval;
160 
161 	// colours
162 	if (node->GetPropVal(wxT("fill-color"), &val) && val.length() >= 7 && val.GetChar(0) == wxT('#'))
163 		m_fillColour = String2Colour(val);
164 	if (node->GetPropVal(wxT("outline-color"), &val) && val.length() >= 7 && val.GetChar(0) == wxT('#'))
165 		m_outlineColour = String2Colour(val);
166 	if (node->GetPropVal(wxT("outline-thickness"), &val) && val.ToDouble(&dval))
167 		m_outlineThickness = dval;
168 	if (node->GetPropVal(wxT("shadow-color"), &val) && val.length() >= 7 && val.GetChar(0) == wxT('#'))
169 		m_shadowColour = String2Colour(val);
170 	if (node->GetPropVal(wxT("shadow-offset"), &val) && val.Find(wxT(',')) > 0
171 			&& val.Mid(0, val.Find(wxT(','))).ToLong(&lval) && val.Mid(val.Find(wxT(',')) + 1).ToLong(&lval2))
172 		m_shadowOffset = wxPoint(lval, lval2);
173 
174 	// alignment
175 	m_alignment = wxALIGN_CENTER_HORIZONTAL;
176 	if (node->GetPropVal(wxT("horizontal-alignment"), &val)) {
177 		if (val == wxT("left"))
178 			m_alignment = wxALIGN_LEFT;
179 		else if (val == wxT("right"))
180 			m_alignment = wxALIGN_RIGHT;
181 	}
182 	if (node->GetPropVal(wxT("vertical-alignment"), &val)) {
183 		if (val == wxT("center"))
184 			m_alignment = (wxAlignment) (m_alignment | wxALIGN_CENTER_VERTICAL);
185 		else if (val == wxT("top"))
186 			m_alignment = (wxAlignment) (m_alignment | wxALIGN_TOP);
187 		else
188 			m_alignment = (wxAlignment) (m_alignment | wxALIGN_BOTTOM);
189 	} else
190 		m_alignment = (wxAlignment) (m_alignment | wxALIGN_BOTTOM);
191 
192 	// margin
193 	if (node->GetPropVal(wxT("left-margin"), &val) && val.ToLong(&lval))
194 		m_leftMargin = lval;
195 	if (node->GetPropVal(wxT("right-margin"), &val) && val.ToLong(&lval))
196 		m_rightMargin = lval;
197 	if (node->GetPropVal(wxT("top-margin"), &val) && val.ToLong(&lval))
198 		m_topMargin = lval;
199 	if (node->GetPropVal(wxT("bottom-margin"), &val) && val.ToLong(&lval))
200 		m_bottomMargin = lval;
201 
202 	// fps
203 	if (node->GetPropVal(wxT("subtitle-fps"), &val) && val.ToDouble(&dval))
204 		m_subtitleFps = dval;
205 
206 	return true;
207 }
208 
SaveSpumux(const wxString & filename,VideoFormat videoFormat)209 bool TextSub::SaveSpumux(const wxString& filename, VideoFormat videoFormat) {
210 	wxSvgXmlDocument xml;
211 	wxSvgXmlNode* root = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, wxT("subpictures"));
212 	root->AddProperty(wxT("format"), isNTSC(videoFormat) ? wxT("NTSC") : wxT("PAL"));
213 	wxSvgXmlNode* streamNode = new wxSvgXmlNode(wxSVGXML_ELEMENT_NODE, wxT("stream"));
214 	streamNode->AddChild(GetXML(DVDAUTHOR_XML));
215 	root->AddChild(streamNode);
216 	xml.SetRoot(root);
217 	return xml.Save(filename);
218 }
219 
220 /**
221  * Returns font map (font family -> font style -> font filename)
222  */
GetFontMap()223 FontMap& TextSub::GetFontMap() {
224 	if (s_fonts.size() > 0)
225 		return s_fonts;
226 #ifdef __WXMSW__
227 	wxString fontConfigFile;
228 	if (!wxGetEnv(wxT("FONTCONFIG_FILE"), &fontConfigFile)) {
229 		// load config and fonts
230 		FcConfig* fc = FcConfigCreate ();
231 		if (fc == NULL) {
232 			wxLogError(wxString(wxT("SubtitlePropDlg::UpdateFontList(): ")) + wxString(wxT("FcConfigCreate failed.")));
233 			return s_fonts;
234 		}
235 		FcConfigParseAndLoad(fc, (const FcChar8*) (const char*) FONTS_CONF.ToUTF8(), FcTrue);
236 		FcConfigBuildFonts(fc);
237 		FcConfigSetCurrent(fc);
238 	}
239 #endif
240 	FcPattern* pattern = FcPatternBuild(NULL, FC_OUTLINE, FcTypeBool, 1, FC_SCALABLE, FcTypeBool, 1, NULL);
241 	FcObjectSet* objectSet = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FULLNAME, FC_FILE, FC_INDEX, NULL);
242 	FcFontSet* fontSet = FcFontList(0, pattern, objectSet);
243 	FcObjectSetDestroy(objectSet);
244 	FcPatternDestroy(pattern);
245 	if (fontSet == NULL)
246 		return s_fonts;
247 	for (int fi = 0; fi < fontSet->nfont; fi++) {
248 		// font file
249 		FcChar8* str;
250 		if (FcPatternGetString(fontSet->fonts[fi], FC_FILE, 0, &str) != FcResultMatch)
251 			continue;
252 		wxString fontFilename = wxString::FromUTF8((char*) str);
253 
254 		// font file index
255 		int fontFileIndex = 0;
256 		FcPatternGetInteger(fontSet->fonts[fi], FC_INDEX, 0, &fontFileIndex);
257 		if (fontFileIndex > 0)
258 			continue;
259 		// family name
260 		if (FcPatternGetString(fontSet->fonts[fi], FC_FAMILY, 0, &str) != FcResultMatch)
261 			continue;
262 		wxString fontFamily = wxString::FromUTF8((char*) str);
263 		// style name
264 		if (FcPatternGetString(fontSet->fonts[fi], FC_STYLE, 0, &str) != FcResultMatch)
265 			continue;
266 		wxString fontStyle = wxString::FromUTF8((char*) str);
267 		s_fonts[fontFamily][fontStyle] = fontFilename;
268 	}
269 	FcFontSetDestroy(fontSet);
270 	return s_fonts;
271 }
272 
273 /** Returns true, if font map is initialized */
IsFontMapInitialized()274 bool TextSub::IsFontMapInitialized() {
275 	return s_fonts.size() > 0;
276 }
277