SubtitleEdit/libse/SubtitleFormats/SubtitleFormat.cs

616 lines
24 KiB
C#
Raw Normal View History

2016-02-06 07:52:53 +01:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using Nikse.SubtitleEdit.Core.Interfaces;
2016-02-06 07:52:53 +01:00
namespace Nikse.SubtitleEdit.Core.SubtitleFormats
{
public abstract class SubtitleFormat
{
private static IList<SubtitleFormat> _allSubtitleFormats;
protected static readonly char[] SplitCharColon = { ':' };
/// <summary>
2018-04-28 10:00:21 +02:00
/// Text formats supported by Subtitle Edit
2016-02-06 07:52:53 +01:00
/// </summary>
public static IEnumerable<SubtitleFormat> AllSubtitleFormats
2016-02-06 07:52:53 +01:00
{
get
{
if (_allSubtitleFormats != null)
2019-01-19 14:40:37 +01:00
{
2016-02-06 07:52:53 +01:00
return _allSubtitleFormats;
2019-01-19 14:40:37 +01:00
}
2016-02-06 07:52:53 +01:00
_allSubtitleFormats = new List<SubtitleFormat>
{
new SubRip(),
new AbcIViewer(),
new AdobeAfterEffectsFTME(),
new AdobeEncore(),
new AdobeEncoreLineTabNewLine(),
new AdobeEncoreTabs(),
new AdobeEncoreWithLineNumbers(),
new AdobeEncoreWithLineNumbersNtsc(),
new AdvancedSubStationAlpha(),
new AQTitle(),
new AvidCaption(),
new AvidDvd(),
new AwsTranscribeJson(),
2016-02-06 07:52:53 +01:00
new BelleNuitSubtitler(),
2020-05-30 15:13:02 +02:00
new Bilibili(),
new Cappella(),
2016-02-06 07:52:53 +01:00
new CaptionAssistant(),
new Captionate(),
new CaptionateMs(),
new CaraokeXml(),
new Csv(),
new Csv2(),
new Csv3(),
2016-08-10 21:22:29 +02:00
new Csv4(),
2017-04-27 13:20:55 +02:00
new Csv5(),
2017-12-09 08:39:31 +01:00
new CsvNuendo(),
2018-03-04 10:24:04 +01:00
new DCinemaInterop(),
2016-02-06 07:52:53 +01:00
new DCinemaSmpte2007(),
new DCinemaSmpte2010(),
new DCinemaSmpte2014(),
2016-02-06 07:52:53 +01:00
new DigiBeta(),
new DvdStudioPro(),
new DvdStudioProSpaceOne(),
new DvdStudioProSpaceOneSemicolon(),
2016-02-06 07:52:53 +01:00
new DvdStudioProSpace(),
new DvdSubtitle(),
new DvdSubtitleSystem(),
new Ebu(),
2016-03-23 22:06:56 +01:00
new Edl(),
2016-02-06 07:52:53 +01:00
new Eeg708(),
new ElrPrint(),
new ESubXf(),
2016-02-06 07:52:53 +01:00
new F4Text(),
2019-12-08 10:20:19 +01:00
new EZTSubtitlesProject(),
2016-02-06 07:52:53 +01:00
new F4Rtf(),
new F4Xml(),
new FabSubtitler(),
new FilmEditXml(),
new FinalCutProXml(),
new FinalCutProXXml(),
new FinalCutProXmlGap(),
new FinalCutProXCM(),
new FinalCutProXml13(),
new FinalCutProXml14(),
new FinalCutProXml14Text(),
new FinalCutProXml15(),
new FinalCutProXml16(),
new FinalCutProXml17(),
2020-08-16 09:21:57 +02:00
new FinalCutProXml18(),
2016-02-06 07:52:53 +01:00
new FinalCutProTestXml(),
new FinalCutProTest2Xml(),
new FlashXml(),
new FLVCoreCuePoints(),
new Footage(),
new GooglePlayJson(),
2016-02-06 07:52:53 +01:00
new GpacTtxt(),
new Gremots(),
new HollyStarJson(),
2016-02-06 07:52:53 +01:00
new ImageLogicAutocaption(),
new InqScribe(),
2016-02-06 07:52:53 +01:00
new IssXml(),
new ItunesTimedText(),
new JacoSub(),
new JsonTed(),
2016-02-06 07:52:53 +01:00
new Json(),
new JsonType2(),
new JsonType3(),
new JsonType4(),
new JsonType5(),
new JsonType6(),
new JsonType7(),
2016-10-24 21:07:42 +02:00
new JsonType8(),
2019-09-10 15:39:12 +02:00
new JsonType8b(),
new JsonType9(),
new JsonType10(),
new JsonType11(),
2017-10-22 23:29:04 +02:00
new JsonType12(),
2018-03-15 18:23:23 +01:00
new JsonType13(),
2018-10-28 05:08:45 +01:00
new JsonType14(),
2019-04-30 19:11:42 +02:00
new JsonType15(),
new JsonType16(),
new JsonType17(),
2017-12-30 13:14:14 +01:00
new KanopyHtml(),
2018-01-27 13:50:44 +01:00
new LambdaCap(),
2016-02-06 07:52:53 +01:00
new Lrc(),
new MacSub(),
new MediaTransData(),
2016-02-06 07:52:53 +01:00
new MicroDvd(),
new MidwayInscriberCGX(),
new MPlayer2(),
2020-08-10 20:30:55 +02:00
new MsOfficeWorkbook(),
2016-02-06 07:52:53 +01:00
new NciTimedRollUpCaptions(),
2019-11-17 19:48:04 +01:00
new NetflixImsc11Japanese(),
new NetflixTimedText(),
new OgmChapters(),
2016-02-06 07:52:53 +01:00
new OpenDvt(),
new Oresme(),
new OresmeDocXDocument(),
2019-04-04 17:29:34 +02:00
new OtterAi(),
2017-07-11 15:36:04 +02:00
new Pe2(),
new PhoenixSubtitle(),
2016-02-06 07:52:53 +01:00
new PinnacleImpression(),
new PListCaption(),
2019-11-11 22:30:20 +01:00
new ProjectionSubtitleList(),
2016-02-06 07:52:53 +01:00
new QubeMasterImport(),
new QuickTimeText(),
new RealTime(),
new RhozetHarmonic(),
new Rtf1(),
new Rtf2(),
2016-02-06 07:52:53 +01:00
new Sami(),
new SamiAvDicPlayer(),
2016-02-06 07:52:53 +01:00
new SamiModern(),
new SamiYouTube(),
new Scenarist(),
new ScenaristClosedCaptions(),
new ScenaristClosedCaptionsDropFrame(),
new SmilTimesheetData(),
2017-02-22 15:37:48 +01:00
new SmpteTt2052(),
2016-02-06 07:52:53 +01:00
new SoftNiSub(),
new SoftNicolonSub(),
new SonyDVDArchitect(),
new SonyDVDArchitectExplicitDuration(),
new SonyDVDArchitectLineAndDuration(),
2016-03-29 18:22:46 +02:00
new SonyDVDArchitectLineDurationLength(),
2016-02-06 07:52:53 +01:00
new SonyDVDArchitectTabs(),
new SonyDVDArchitectWithLineNumbers(),
new Speechmatics(),
2016-02-06 07:52:53 +01:00
new Spruce(),
new SpruceWithSpace(),
new StructuredTitles(),
new SubStationAlpha(),
new SubtitleEditorProject(),
2016-09-27 18:46:29 +02:00
new SubUrbia(),
2016-02-06 07:52:53 +01:00
new SubViewer10(),
new SubViewer20(),
new SwiftInterchange2(),
new SwiftText(),
new SwiftTextLineNumber(),
new SwiftTextLineNOAndDur(),
new Tek(),
new TimeXml(),
new TimeXml2(),
new TimedText10(),
new TimedText200604(),
new TimedText200604CData(),
new TimedText200604Ooyala(),
2016-02-06 07:52:53 +01:00
new TimedText(),
new TitleExchangePro(),
new Titra(),
new TmpegEncText(),
new TmpegEncAW5(),
new TmpegEncXml(),
new TMPlayer(),
new TranscriberXml(),
new Tmx14(),
new TurboTitler(),
new TwentyThreeJson(),
new TwentyThreeJsonEmbed(),
new TwentyThreeJsonEmbedWebSrt(),
2016-02-06 07:52:53 +01:00
new UniversalSubtitleFormat(),
new UTSubtitleXml(),
new Utx(),
new UtxFrames(),
new UleadSubtitleFormat(),
new VocapiaSplit(),
new WebVTT(),
new WebVTTFileWithLineNumber(),
new Xif(),
new YouTubeAnnotations(),
new YouTubeSbv(),
new YouTubeTranscript(),
new YouTubeTranscriptOneLine(),
new ZeroG(),
// new Idx(),
new UnknownSubtitle1(),
new UnknownSubtitle2(),
new UnknownSubtitle3(),
new UnknownSubtitle4(),
new UnknownSubtitle5(),
new UnknownSubtitle6(),
new UnknownSubtitle7(),
new UnknownSubtitle8(),
new UnknownSubtitle9(),
new UnknownSubtitle10(),
new UnknownSubtitle11(),
new UnknownSubtitle12(),
new UnknownSubtitle13(),
new UnknownSubtitle14(),
new UnknownSubtitle15(),
new UnknownSubtitle16(),
new UnknownSubtitle17(),
new UnknownSubtitle18(),
new UnknownSubtitle19(),
new UnknownSubtitle20(),
new UnknownSubtitle21(),
new UnknownSubtitle22(),
new UnknownSubtitle23(),
new UnknownSubtitle24(),
new UnknownSubtitle25(),
new UnknownSubtitle26(),
new UnknownSubtitle27(),
new UnknownSubtitle28(),
new UnknownSubtitle29(),
new UnknownSubtitle30(),
new UnknownSubtitle31(),
new UnknownSubtitle32(),
new UnknownSubtitle33(),
new UnknownSubtitle34(),
new UnknownSubtitle35(),
new UnknownSubtitle36(),
new UnknownSubtitle37(),
new UnknownSubtitle38(),
new UnknownSubtitle39(),
new UnknownSubtitle40(),
new UnknownSubtitle41(),
new UnknownSubtitle42(),
new UnknownSubtitle43(),
new UnknownSubtitle44(),
new UnknownSubtitle45(),
new UnknownSubtitle46(),
new UnknownSubtitle47(),
new UnknownSubtitle48(),
new UnknownSubtitle49(),
new UnknownSubtitle50(),
new UnknownSubtitle51(),
new UnknownSubtitle52(),
new UnknownSubtitle53(),
new UnknownSubtitle54(),
new UnknownSubtitle55(),
new UnknownSubtitle56(),
new UnknownSubtitle57(),
new UnknownSubtitle58(),
new UnknownSubtitle59(),
new UnknownSubtitle60(),
new UnknownSubtitle61(),
new UnknownSubtitle62(),
new UnknownSubtitle63(),
new UnknownSubtitle64(),
new UnknownSubtitle65(),
new UnknownSubtitle66(),
new UnknownSubtitle67(),
new UnknownSubtitle68(),
new UnknownSubtitle69(),
new UnknownSubtitle70(),
new UnknownSubtitle71(),
new UnknownSubtitle72(),
new UnknownSubtitle73(),
new UnknownSubtitle74(),
new UnknownSubtitle75(),
new UnknownSubtitle76(),
new UnknownSubtitle77(),
new UnknownSubtitle78(),
new UnknownSubtitle79(),
new UnknownSubtitle80(),
2016-02-17 19:15:18 +01:00
new UnknownSubtitle81(),
2016-02-21 18:52:33 +01:00
new UnknownSubtitle82(),
new UnknownSubtitle83(),
new UnknownSubtitle84(),
2017-10-10 20:19:51 +02:00
new UnknownSubtitle85(),
new UnknownSubtitle86(),
2018-08-23 17:01:13 +02:00
new UnknownSubtitle87(),
2019-04-05 15:03:42 +02:00
new UnknownSubtitle88(),
2019-05-05 17:34:04 +02:00
new UnknownSubtitle89(),
2019-08-08 18:34:33 +02:00
new UnknownSubtitle90(),
2019-09-01 15:28:44 +02:00
new UnknownSubtitle91(),
2020-01-28 17:55:14 +01:00
new UnknownSubtitle92(),
new UnknownSubtitle93(),
2020-03-10 19:31:56 +01:00
new UnknownSubtitle94(),
2020-03-12 09:46:26 +01:00
new UnknownSubtitle95(),
2020-04-12 18:10:18 +02:00
new UnknownSubtitle96(),
new UnknownSubtitle97(),
new UnknownSubtitle98(),
2020-06-06 07:12:25 +02:00
new UnknownSubtitle99(),
2016-02-06 07:52:53 +01:00
};
string path = Configuration.PluginsDirectory;
if (Directory.Exists(path))
{
foreach (string pluginFileName in Directory.EnumerateFiles(path, "*.DLL"))
2016-02-06 07:52:53 +01:00
{
try
{
var assembly = System.Reflection.Assembly.Load(FileUtil.ReadAllBytesShared(pluginFileName));
foreach (var exportedType in assembly.GetExportedTypes())
2016-02-06 07:52:53 +01:00
{
try
2016-02-06 07:52:53 +01:00
{
object pluginObject = Activator.CreateInstance(exportedType);
if (pluginObject is SubtitleFormat po)
2016-02-06 07:52:53 +01:00
{
_allSubtitleFormats.Insert(1, po);
2016-02-06 07:52:53 +01:00
}
}
catch
{
// ignored
}
2016-02-06 07:52:53 +01:00
}
}
catch
{
2017-08-03 12:43:52 +02:00
// ignored
2016-02-06 07:52:53 +01:00
}
}
}
return _allSubtitleFormats;
}
}
protected int _errorCount;
2017-08-03 12:43:52 +02:00
public abstract string Extension
2016-02-06 07:52:53 +01:00
{
get;
}
2017-08-03 12:43:52 +02:00
public abstract string Name
2016-02-06 07:52:53 +01:00
{
get;
}
2017-08-03 12:43:52 +02:00
public virtual bool IsTimeBased => true;
2016-02-06 07:52:53 +01:00
2017-08-03 12:43:52 +02:00
public bool IsFrameBased => !IsTimeBased;
2016-02-06 07:52:53 +01:00
2017-08-03 12:43:52 +02:00
public string FriendlyName => $"{Name} ({Extension})";
2016-02-06 07:52:53 +01:00
2017-08-03 12:43:52 +02:00
public int ErrorCount => _errorCount;
public virtual bool IsMine(List<string> lines, string fileName)
2016-02-06 07:52:53 +01:00
{
2017-08-03 12:43:52 +02:00
var subtitle = new Subtitle();
var oldFrameRate = Configuration.Settings.General.CurrentFrameRate;
LoadSubtitle(subtitle, lines, fileName);
Configuration.Settings.General.CurrentFrameRate = oldFrameRate;
return subtitle.Paragraphs.Count > _errorCount;
2016-02-06 07:52:53 +01:00
}
2017-08-03 12:43:52 +02:00
public abstract string ToText(Subtitle subtitle, string title);
2016-02-06 07:52:53 +01:00
2017-08-03 12:43:52 +02:00
public abstract void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName);
2016-02-06 07:52:53 +01:00
2017-08-03 12:43:52 +02:00
public bool IsVobSubIndexFile => Extension.Equals(".idx", StringComparison.Ordinal);
2016-02-06 07:52:53 +01:00
public virtual void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat)
{
}
2017-08-03 12:43:52 +02:00
public virtual List<string> AlternateExtensions => new List<string>();
2016-02-06 07:52:53 +01:00
public static int MillisecondsToFrames(double milliseconds)
{
return (int)Math.Round(milliseconds / (TimeCode.BaseUnit / GetFrameForCalculation(Configuration.Settings.General.CurrentFrameRate)));
}
public static double GetFrameForCalculation(double frameRate)
{
if (Math.Abs(frameRate - 23.976) < 0.01)
{
return 24000.0 / 1001.0;
}
if (Math.Abs(frameRate - 29.97) < 0.01)
{
return 30000.0 / 1001.0;
}
if (Math.Abs(frameRate - 59.94) < 0.01)
{
return 60000.0 / 1001.0;
}
return frameRate;
2016-02-06 07:52:53 +01:00
}
public static int MillisecondsToFramesMaxFrameRate(double milliseconds)
{
int frames = (int)Math.Round(milliseconds / (TimeCode.BaseUnit / GetFrameForCalculation(Configuration.Settings.General.CurrentFrameRate)));
2016-02-06 07:52:53 +01:00
if (frames >= Configuration.Settings.General.CurrentFrameRate)
2019-01-19 14:40:37 +01:00
{
2016-02-06 07:52:53 +01:00
frames = (int)(Configuration.Settings.General.CurrentFrameRate - 0.01);
2019-01-19 14:40:37 +01:00
}
2016-02-06 07:52:53 +01:00
return frames;
}
public static int FramesToMilliseconds(double frames)
{
return (int)Math.Round(frames * (TimeCode.BaseUnit / GetFrameForCalculation(Configuration.Settings.General.CurrentFrameRate)));
2016-02-06 07:52:53 +01:00
}
public static int FramesToMillisecondsMax999(double frames)
{
int ms = (int)Math.Round(frames * (TimeCode.BaseUnit / GetFrameForCalculation(Configuration.Settings.General.CurrentFrameRate)));
2016-02-06 07:52:53 +01:00
return Math.Min(ms, 999);
}
2017-08-03 12:43:52 +02:00
public virtual bool HasStyleSupport => false;
2016-02-06 07:52:53 +01:00
public bool BatchMode { get; set; }
public double? BatchSourceFrameRate { get; set; }
2016-02-06 07:52:53 +01:00
public static string ToUtf8XmlString(XmlDocument xml, bool omitXmlDeclaration = false)
{
var settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = omitXmlDeclaration,
};
var result = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(result, settings))
{
xml.Save(xmlWriter);
}
return result.ToString().Replace(" encoding=\"utf-16\"", " encoding=\"utf-8\"").Trim();
}
2017-08-03 12:43:52 +02:00
public virtual bool IsTextBased => true;
2016-02-06 07:52:53 +01:00
2019-01-25 21:04:18 +01:00
protected static TimeCode DecodeTimeCodeFramesTwoParts(string[] tokens)
2016-02-06 07:52:53 +01:00
{
if (tokens == null)
2019-01-19 14:40:37 +01:00
{
return new TimeCode();
2019-01-19 14:40:37 +01:00
}
if (tokens.Length != 2)
2019-01-19 14:40:37 +01:00
{
throw new InvalidOperationException();
2019-01-19 14:40:37 +01:00
}
2019-01-25 21:04:18 +01:00
return new TimeCode(0, 0, int.Parse(tokens[0]), FramesToMillisecondsMax999(int.Parse(tokens[1])));
}
2016-02-06 07:52:53 +01:00
2019-01-25 21:04:18 +01:00
protected static TimeCode DecodeTimeCodeFramesFourParts(string[] tokens)
{
if (tokens == null)
2019-01-19 14:40:37 +01:00
{
return new TimeCode();
2019-01-19 14:40:37 +01:00
}
if (tokens.Length != 4)
2019-01-19 14:40:37 +01:00
{
throw new InvalidOperationException();
2019-01-19 14:40:37 +01:00
}
2019-01-25 21:04:18 +01:00
return new TimeCode(int.Parse(tokens[0]), int.Parse(tokens[1]), int.Parse(tokens[2]), FramesToMillisecondsMax999(int.Parse(tokens[3])));
2016-02-06 07:52:53 +01:00
}
2019-01-25 21:04:18 +01:00
protected static TimeCode DecodeTimeCodeMsFourParts(string[] tokens)
{
if (tokens == null)
{
return new TimeCode();
}
if (tokens.Length != 4)
{
throw new InvalidOperationException();
}
2019-01-25 21:04:18 +01:00
return new TimeCode(int.Parse(tokens[0]), int.Parse(tokens[1]), int.Parse(tokens[2]), int.Parse(tokens[3]));
}
2019-01-25 21:04:18 +01:00
protected static TimeCode DecodeTimeCodeFrames(string timestamp, char[] splitChars)
2016-02-06 07:52:53 +01:00
{
return DecodeTimeCodeFramesFourParts(timestamp.Split(splitChars, StringSplitOptions.RemoveEmptyEntries));
2016-02-06 07:52:53 +01:00
}
2018-04-28 10:00:21 +02:00
/// <summary>
/// Load subtitle type of 'formats' from file.
/// </summary>
/// <param name="formats">List of possible formats</param>
/// <param name="fileName">Name of subtitle file</param>
/// <param name="subtitle">Subtitle to load file into</param>
/// <returns>The format of the file, null of not format match found</returns>
public static SubtitleFormat LoadSubtitleFromFile(SubtitleFormat[] formats, string fileName, Subtitle subtitle)
{
2018-04-28 10:00:21 +02:00
if (formats == null || formats.Length == 0 || string.IsNullOrEmpty(fileName))
{
return null;
}
2018-04-28 10:00:21 +02:00
var list = new List<string>(File.ReadAllLines(fileName, LanguageAutoDetect.GetEncodingFromFile(fileName)));
foreach (var subtitleFormat in formats)
{
if (subtitleFormat.IsMine(list, fileName))
{
subtitleFormat.LoadSubtitle(subtitle, list, fileName);
return subtitleFormat;
}
}
return null;
}
2018-04-28 10:00:21 +02:00
/// <summary>
/// Load subtitle from a list of lines and a file name (the last can be null).
/// </summary>
/// <param name="lines">Text lines from subtitle file</param>
/// <param name="fileName">Optional file name</param>
/// <returns>Subtitle, null if format not recognized</returns>
public static Subtitle LoadSubtitleFromLines(List<string> lines, string fileName)
{
if (lines == null || lines.Count == 0)
{
return null;
}
var subtitle = new Subtitle();
foreach (var subtitleFormat in AllSubtitleFormats)
{
if (subtitleFormat.IsMine(lines, fileName))
{
subtitleFormat.LoadSubtitle(subtitle, lines, fileName);
return subtitle;
}
}
return null;
}
2019-03-17 14:29:46 +01:00
public static SubtitleFormat[] GetBinaryFormats(bool batchMode)
2018-04-28 10:00:21 +02:00
{
2019-03-17 10:32:22 +01:00
return new SubtitleFormat[]
2018-04-28 10:00:21 +02:00
{
2019-03-17 15:34:44 +01:00
new Ebu { BatchMode = batchMode }, new Pac { BatchMode = batchMode }, new PacUnicode(), new Cavena890 { BatchMode = batchMode },
new Spt(), new CheetahCaption(), new CheetahCaptionOld(), new TSB4(), new Chk(), new Ayato(), new CapMakerPlus(), new Ultech130(),
new NciCaption(), new AvidStl(), new WinCaps32(), new IsmtDfxp(), new Cavena890(), new Spt(), new Sptx(), new IaiSub(),
new ELRStudioClosedCaption(), new CaptionsInc(), new TimeLineMvt(), new Cmaft(), new Pns()
};
}
public static SubtitleFormat[] GetTextOtherFormats()
{
2019-03-17 10:32:22 +01:00
return new SubtitleFormat[]
{
2019-03-17 15:34:44 +01:00
new DlDd(), new Ted20(), new Captionate(), new TimeLineAscii(), new TimeLineFootageAscii(), new TimedTextImage(),
new FinalCutProImage(), new SpuImage(), new Dost(), new SeImageHtmlIndex(), new BdnXml(), new Wsb(),
new JsonTypeOnlyLoad1(), new TranscriptiveJson(), new KaraokeCdgCreatorText(), new VidIcelandic(),
2018-04-28 10:00:21 +02:00
};
}
public static SubtitleFormat FromName(string formatName, SubtitleFormat defaultFormat)
{
string trimmedFormatName = formatName.Trim();
foreach (var format in AllSubtitleFormats)
{
if (format.Name.Trim().Equals(trimmedFormatName, StringComparison.OrdinalIgnoreCase) ||
format.FriendlyName.Trim().Equals(trimmedFormatName, StringComparison.OrdinalIgnoreCase))
{
return format;
}
}
return defaultFormat;
}
public static SubtitleFormat BinaryPersistableFromName(string formatName, bool batchMode)
{
string trimmedFormatName = formatName.Trim();
foreach (var format in GetBinaryFormats(batchMode))
{
if (format is IBinaryPersistableSubtitle &&
format.Name.Trim().Equals(trimmedFormatName, StringComparison.OrdinalIgnoreCase) ||
format.FriendlyName.Trim().Equals(trimmedFormatName, StringComparison.OrdinalIgnoreCase))
{
return format;
}
}
return null;
}
2016-02-06 07:52:53 +01:00
}
}