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