Aegisub/aegisub/src/ass_style.cpp
Niels Martin Hansen b6a63c15e6 Adjust throwing of exceptions to the new style documented in exception.h.
(Good thing nothing is catching these yet, and it's the only case of "new style" exceptions being used.)

Originally committed to SVN as r3365.
2009-08-05 20:52:44 +00:00

647 lines
15 KiB
C++

// Copyright (c) 2005, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$
/// @file ass_style.cpp
/// @brief Class for style definitions in subtitles
/// @ingroup subs_storage
///
////////////
// Includes
#include "config.h"
#include <wx/tokenzr.h>
#include <wx/intl.h>
#include "ass_style.h"
#include "utils.h"
#include <ctype.h>
/// @brief Constructors AssColor //////////////////////////
///
AssColor::AssColor () {
r=g=b=a=0;
}
/// @brief DOCME
/// @param color
///
AssColor::AssColor (wxColour &color) {
SetWXColor(color);
}
/// @brief Parse from SSA/ASS
/// @param value
/// @return
///
void AssColor::Parse(const wxString value) {
if (value.Len() > 0 && value[0] == _T('#')) {
// HTML colour
SetWXColor(wxColor(value));
return;
}
// Prepare
char c,ostr[12];
unsigned long outval;
int oindex=11;
bool ishex=false;
ostr[11]=0;
for(int i=value.Len()-1;i>=0&&oindex>=0;i--) {
c=value[i];
if (isxdigit(c) || c=='-') {
ostr[--oindex] = c;
if (c>='A') ishex = true;
}
else if (c == 'H' || c == 'h') ishex = true;
}
outval=strtoul(ostr+oindex,0,ishex?16:10);
r = outval & 0xFF;
g = (outval>>8) & 0xFF;
b = (outval>>16)& 0xFF;
a = (outval>>24)& 0xFF;
}
/// @brief Gets a wxColour
/// @return
///
wxColour AssColor::GetWXColor() {
return wxColour(r,g,b,255-a);
}
/// @brief Sets color from wx
/// @param color
///
void AssColor::SetWXColor(const wxColor &color) {
r = color.Red();
g = color.Green();
b = color.Blue();
//a = color.Alpha();
}
/// @brief Get formatted in ASS format
/// @param alpha
/// @param stripped
/// @param isStyle
/// @return
///
wxString AssColor::GetASSFormatted (bool alpha,bool stripped,bool isStyle) {
wxString work;
if (!stripped) work += _T("&H");
if (alpha) work += wxString::Format(_T("%02X"),a);
work += wxString::Format(_T("%02X%02X%02X"),b,g,r);
if (!stripped && !isStyle) work += _T("&");
return work;
}
/// @brief Get decimal formatted
/// @return
///
wxString AssColor::GetSSAFormatted () {
long color = (a<<24)+(b<<16)+(g<<8)+r;
wxString output=wxString::Format(_T("%i"),(long)color);
return output;
}
/// @brief Operators
/// @param col
/// @return
///
bool AssColor::operator==(AssColor &col) const {
return r==col.r && g==col.g && b==col.b && a==col.a;
}
/// @brief DOCME
/// @param col
/// @return
///
bool AssColor::operator!=(AssColor &col) const {
return r!=col.r || g!=col.g || b!=col.b || a!=col.a;
}
/// @brief Default Constructor AssStyle /////////////////////////
///
AssStyle::AssStyle() {
group = _T("[V4+ Styles]");
name = _T("Default");
font = _T("Arial");
fontsize = 20;
primary.r = 255;
primary.g = 255;
primary.b = 255;
primary.a = 0;
secondary.r = 255;
secondary.g = 0;
secondary.b = 0;
secondary.a = 0;
outline.r = 0;
outline.g = 0;
outline.b = 0;
outline.a = 0;
shadow.r = 0;
shadow.g = 0;
shadow.b = 0;
shadow.a = 0;
bold = false;
italic = false;
underline = false;
strikeout = false;
scalex = 100;
scaley = 100;
spacing = 0;
angle = 0.0;
borderstyle = 1;
outline_w = 2.0;
shadow_w = 2.0;
alignment = 2;
Margin[0] = 10;
Margin[1] = 10;
Margin[2] = 10;
Margin[3] = 10;
encoding = 1;
relativeTo = 1;
UpdateData();
}
/// @brief Constructor
/// @param _data
/// @param version
///
AssStyle::AssStyle(wxString _data,int version) {
Valid = Parse(_data,version);
if (!Valid) {
throw _T("[Error] Failed parsing line.");
}
UpdateData();
}
/// @brief Destructor
///
AssStyle::~AssStyle() {
}
/// @brief Parses value from ASS data
/// @param rawData
/// @param version
/// @return
///
bool AssStyle::Parse(wxString rawData,int version) {
// Tokenize
wxString temp;
long templ;
wxStringTokenizer tkn(rawData.Trim(false).Mid(6),_T(","),wxTOKEN_RET_EMPTY_ALL);
// Read name
if (!tkn.HasMoreTokens()) return false;
name = tkn.GetNextToken();
name.Trim(true);
name.Trim(false);
// Read font name
if (!tkn.HasMoreTokens()) return false;
font = tkn.GetNextToken();
font.Trim(true);
font.Trim(false);
// Read font size
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
fontsize = templ;
if (version != 0) {
// Read primary color
if (!tkn.HasMoreTokens()) return false;
primary.Parse(tkn.GetNextToken());
// Read secondary color
if (!tkn.HasMoreTokens()) return false;
secondary.Parse(tkn.GetNextToken());
// Read outline color
if (!tkn.HasMoreTokens()) return false;
outline.Parse(tkn.GetNextToken());
// Read shadow color
if (!tkn.HasMoreTokens()) return false;
shadow.Parse(tkn.GetNextToken());
}
else {
// Read primary color
if (!tkn.HasMoreTokens()) return false;
primary.Parse(tkn.GetNextToken());
// Read secondary color
if (!tkn.HasMoreTokens()) return false;
secondary.Parse(tkn.GetNextToken());
// Read and discard tertiary color
if (!tkn.HasMoreTokens()) return false;
tkn.GetNextToken();
// Read shadow/outline color
if (!tkn.HasMoreTokens()) return false;
outline.Parse(tkn.GetNextToken());
shadow = outline;
}
// Read bold
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
bold = (templ==0)?false:true;
// Read italics
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken(); temp.ToLong(&templ);
italic = (templ==0)?false:true;
if (version != 0) {
// Read underline
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
underline = (templ==0)?false:true;
// Read strikeout
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
strikeout = (templ==0)?false:true;
// Read scale x
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&scalex);
//scalex = templ;
// Read scale y
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&scaley);
//scaley = templ;
// Read spacing
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&spacing);
//spacing = templ;
// Read angle
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&angle);
}
else {
// SSA defaults
//shadow.a = 128; //Parsed
underline = false;
strikeout = false;
scalex = 100;
scaley = 100;
spacing = 0;
angle = 0.0;
}
// Read border style
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
borderstyle = templ;
// Read outline width
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&outline_w);
// Read shadow width
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToDouble(&shadow_w);
// Read alignment
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
if (version == 0) {
switch(templ) {
case 1: alignment = 1; break;
case 2: alignment = 2; break;
case 3: alignment = 3; break;
case 5: alignment = 7; break;
case 6: alignment = 8; break;
case 7: alignment = 9; break;
case 9: alignment = 4; break;
case 10: alignment = 5; break;
case 11: alignment = 6; break;
default: alignment = 2; break;
}
}
else alignment = templ;
// Read left margin
if (!tkn.HasMoreTokens()) return false;
SetMarginString(tkn.GetNextToken(),0);
// Read right margin
if (!tkn.HasMoreTokens()) return false;
SetMarginString(tkn.GetNextToken(),1);
// Read top margin
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
SetMarginString(temp,2);
// Read bottom margin
if (version == 2) {
if (!tkn.HasMoreTokens()) return false;
SetMarginString(tkn.GetNextToken(),3);
}
else SetMarginString(temp,3);
// Read alpha level
if (version == 0) {
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
}
// Read encoding
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
encoding = templ;
// Read relative to
if (version == 2) {
if (!tkn.HasMoreTokens()) return false;
temp = tkn.GetNextToken();
temp.ToLong(&templ);
relativeTo = templ;
}
// End
if (tkn.HasMoreTokens()) return false;
return true;
}
/// @brief Writes data back to ASS (v4+) format
///
void AssStyle::UpdateData() {
wxString final;
name.Replace(_T(","),_T(";"));
font.Replace(_T(","),_T(";"));
final = wxString::Format(_T("Style: %s,%s,%s,%s,%s,%s,%s,%d,%d,%d,%d,%s,%s,%s,%s,%d,%s,%s,%i,%i,%i,%i,%i"),
name.c_str(), font.c_str(), PrettyFloatD(fontsize).c_str(),
primary.GetASSFormatted(true,false,true).c_str(),
secondary.GetASSFormatted(true,false,true).c_str(),
outline.GetASSFormatted(true,false,true).c_str(),
shadow.GetASSFormatted(true,false,true).c_str(),
(bold? -1 : 0), (italic ? -1 : 0),
(underline?-1:0),(strikeout?-1:0),
PrettyFloatD(scalex).c_str(),PrettyFloatD(scaley).c_str(),PrettyFloatD(spacing).c_str(),PrettyFloatD(angle).c_str(),
borderstyle,PrettyFloatD(outline_w).c_str(),PrettyFloatD(shadow_w).c_str(),alignment,
Margin[0],Margin[1],Margin[2],encoding);
SetEntryData(final);
}
/// @brief Sets margin from a string
/// @param str
/// @param which
///
void AssStyle::SetMarginString(const wxString str,int which) {
if (which < 0 || which >= 4) throw Aegisub::InvalidMarginIdError();
if (!str.IsNumber()) throw _T("Invalid margin value");
long value;
str.ToLong(&value);
if (value < 0) value = 0;
else if (value > 9999) value = 9999;
Margin[which] = value;
}
/// @brief Gets string for margin
/// @param which
/// @return
///
wxString AssStyle::GetMarginString(int which) {
if (which < 0 || which >= 4) throw Aegisub::InvalidMarginIdError();
wxString result = wxString::Format(_T("%04i"),Margin[which]);
return result;
}
/// @brief Convert style to SSA string
/// @return
///
wxString AssStyle::GetSSAText() {
wxString output;
int align = 0;
switch (alignment) {
case 1: align = 1; break;
case 2: align = 2; break;
case 3: align = 3; break;
case 4: align = 9; break;
case 5: align = 10; break;
case 6: align = 11; break;
case 7: align = 5; break;
case 8: align = 6; break;
case 9: align = 7; break;
}
name.Replace(_T(","),_T(";"));
font.Replace(_T(","),_T(";"));
output = wxString::Format(_T("Style: %s,%s,%s,%s,%s,0,%s,%d,%d,%d,%s,%s,%d,%d,%d,%d,0,%i"),
name.c_str(), font.c_str(), PrettyFloatD(fontsize).c_str(),
primary.GetSSAFormatted().c_str(),
secondary.GetSSAFormatted().c_str(),
shadow.GetSSAFormatted().c_str(),
(bold? -1 : 0), (italic ? -1 : 0),
borderstyle,PrettyFloatD(outline_w).c_str(),PrettyFloatD(shadow_w).c_str(),align,
Margin[0],Margin[1],Margin[2],encoding);
return output;
}
/// @brief Clone
/// @return
///
AssEntry *AssStyle::Clone() const {
// Create clone
AssStyle *final = new AssStyle();
// Copy data
final->group = group;
final->SetStartMS(GetStartMS());
final->Valid = Valid;
final->alignment = alignment;
final->angle = angle;
final->bold = bold;
final->borderstyle = borderstyle;
final->encoding = encoding;
final->font = font;
final->fontsize = fontsize;
final->italic = italic;
final->Margin[0] = Margin[0];
final->Margin[1] = Margin[1];
final->Margin[2] = Margin[2];
final->Margin[3] = Margin[3];
final->name = name;
final->outline = outline;
final->outline_w = outline_w;
final->primary = primary;
final->scalex = scalex;
final->scaley = scaley;
final->secondary = secondary;
final->shadow = shadow;
final->shadow_w = shadow_w;
final->spacing = spacing;
final->strikeout = strikeout;
final->underline = underline;
final->relativeTo = relativeTo;
final->SetEntryData(const_cast<AssStyle*>(this)->GetEntryData());
// Return
return final;
}
/// @brief Equal to another style?
/// @param style
/// @return
///
bool AssStyle::IsEqualTo(AssStyle *style) {
// memcmp won't work because strings won't match
if (style->alignment != alignment ||
style->angle != angle ||
style->bold != bold ||
style->borderstyle != borderstyle ||
style->encoding != encoding ||
style->font != font ||
style->fontsize != fontsize ||
style->italic != italic ||
style->Margin[0] != Margin[0] ||
style->Margin[1] != Margin[1] ||
style->Margin[2] != Margin[2] ||
style->Margin[3] != Margin[3] ||
style->name != name ||
style->outline != outline ||
style->outline_w != outline_w ||
style->primary != primary ||
style->scalex != scalex ||
style->scaley != scaley ||
style->secondary != secondary ||
style->shadow != shadow ||
style->shadow_w != shadow_w ||
style->spacing != spacing ||
style->strikeout != strikeout ||
style->underline != underline ||
style->relativeTo != relativeTo)
return false;
else return true;
}
/// @brief Get a list of valid ASS encodings
/// @param encodingStrings
///
void AssStyle::GetEncodings(wxArrayString &encodingStrings) {
encodingStrings.Clear();
encodingStrings.Add(wxString(_T("0 - ")) + _("ANSI"));
encodingStrings.Add(wxString(_T("1 - ")) + _("Default"));
encodingStrings.Add(wxString(_T("2 - ")) + _("Symbol"));
encodingStrings.Add(wxString(_T("77 - ")) + _("Mac"));
encodingStrings.Add(wxString(_T("128 - ")) + _("Shift_JIS"));
encodingStrings.Add(wxString(_T("129 - ")) + _("Hangeul"));
encodingStrings.Add(wxString(_T("130 - ")) + _("Johab"));
encodingStrings.Add(wxString(_T("134 - ")) + _("GB2312"));
encodingStrings.Add(wxString(_T("136 - ")) + _("Chinese BIG5"));
encodingStrings.Add(wxString(_T("161 - ")) + _("Greek"));
encodingStrings.Add(wxString(_T("162 - ")) + _("Turkish"));
encodingStrings.Add(wxString(_T("163 - ")) + _("Vietnamese"));
encodingStrings.Add(wxString(_T("177 - ")) + _("Hebrew"));
encodingStrings.Add(wxString(_T("178 - ")) + _("Arabic"));
encodingStrings.Add(wxString(_T("186 - ")) + _("Baltic"));
encodingStrings.Add(wxString(_T("204 - ")) + _("Russian"));
encodingStrings.Add(wxString(_T("222 - ")) + _("Thai"));
encodingStrings.Add(wxString(_T("238 - ")) + _("East European"));
encodingStrings.Add(wxString(_T("255 - ")) + _("OEM"));
}